1 /*
2     Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu>
3     Copyright 2010 Stefan Majewsky <majewsky@gmx.net>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 #ifndef KOLF_CANVASITEM_H
21 #define KOLF_CANVASITEM_H
22 
23 #include <config.h>
24 #include "vector.h"
25 #include "tagaro/spriteobjectitem.h"
26 
27 #include <QGraphicsRectItem>
28 
29 class b2Body;
30 class b2World;
31 
32 class Ball;
33 class KConfigGroup;
34 class KolfGame;
35 
36 namespace Kolf
37 {
38 	class EllipseShape;
39 	class Overlay;
40 	class Shape;
41 }
42 
43 enum RttiCodes { Rtti_NoCollision = 1001, Rtti_DontPlaceOn = 1002, Rtti_Putter = 1004 };
44 
45 class CanvasItem
46 {
47 public:
48 	explicit CanvasItem(b2World* world);
49 	virtual ~CanvasItem();
50 	///load your settings from the KConfigGroup, which represents a course.
load(KConfigGroup *)51 	virtual void load(KConfigGroup *) {}
52 	///save your settings.
53 	virtual void save(KConfigGroup *cfg);
54 	///called for information when shot started
shotStarted()55 	virtual void shotStarted() {}
56 	///called when the edit mode has been changed.
57 	virtual void editModeChanged(bool editing);
58 	///Returns whether all items of this type of item (based on data()) that are "colliding" (ie, in the same spot) with ball should get collision() called.
terrainCollisions()59 	virtual bool terrainCollisions() const { return false; }
60 	///Returns a Config that can be used to configure this item by the user. The default implementation returns one that says 'No configuration options'.
config(QWidget * parent)61 	virtual Config *config(QWidget *parent) { return new DefaultConfig(parent); }
62 	///Returns other items that should be movable (besides this one of course).
moveableItems()63 	virtual QList<QGraphicsItem *> moveableItems() const { return QList<QGraphicsItem *>(); }
64 
setId(int newId)65 	void setId(int newId) { m_id = newId; }
curId()66 	int curId() const { return m_id; }
67 
68 	///Called on ball's collision. Return if terrain collidingItems should be processed.
collision(Ball * ball)69 	virtual bool collision(Ball *ball) { Q_UNUSED(ball) return false; }
70 
71 	///Reimplement if you want extra items to have access to the game object.
setGame(KolfGame * game)72 	virtual void setGame(KolfGame *game) { this->game = game; }
73 
name()74 	QString name() const { return m_name; }
setName(const QString & newname)75 	void setName(const QString &newname) { m_name = newname; }
setSize(const QSizeF &)76 	virtual void setSize(const QSizeF&) {}
77 
78 	virtual void moveBy(double dx, double dy);
79 
80 	//The following is needed temporarily while CanvasItem is not a QGraphicsItem by itself.
setPosition(const QPointF & pos)81 	void setPosition(const QPointF& pos) { const QPointF diff = pos - getPosition(); moveBy(diff.x(), diff.y()); }
82 	virtual QPointF getPosition() const = 0;
83 
84 	enum ZBehavior { FixedZValue = 0, IsStrut = 1, IsRaisedByStrut = 2 };
85 	///This specifies how the object is Z-ordered.
86 	///\li FixedZValue: No special behavior.
87 	///\li IsStrut: This item is a vertical strut. It raises certain
88 	///    items when they move on top of it. Its zValue is \a zValueStep.
89 	///\li IsRaisedByStrut: This item can be raised by struts underneath
90 	///    it. \a zValueStep is the amount by which the zValue is raised
91 	///    then. (i.e. \a zValue is relative to the strut)
92 	//TODO: account for overlapping struts
93 	void setZBehavior(ZBehavior behavior, qreal zValue);
94 	///Struts are normally found by collision detection. This method
95 	///configures a static strut for this item (on a semantic basis;
96 	///e.g. the RectangleItem is the static strut for its walls).
97 	void setStaticStrut(CanvasItem* citem);
98 	void updateZ(QGraphicsItem* self);
99 	void moveItemsOnStrut(const QPointF& posDiff);
100 	static bool mayCollide(CanvasItem* citem1, CanvasItem* citem2);
101 protected:
102 	friend class Kolf::Overlay; //for delivery of Kolf::Overlay::stateChanged signal
103 	///pointer to main KolfGame
104 	KolfGame *game;
105 private:
106 	QString m_name;
107 	int m_id;
108 	CanvasItem::ZBehavior m_zBehavior;
109 	qreal m_zValue;
110 	CanvasItem* m_strut;
111 	CanvasItem* m_staticStrut;
112 	QList<CanvasItem*> m_struttedItems;
113 
114 //AFTER THIS LINE follows what I have inserted during the refactoring
115 	public:
116 		enum SimulationFlag
117 		{
118 			CollisionFlag = 1 << 0,
119 			KinematicSimulationFlag = 1 << 1,
120 			DynamicSimulationFlag = 1 << 2
121 		};
122 		enum SimulationType
123 		{
124 			///The object is immovable.
125 			NoSimulation = 0,
126 			///The object is immovable, but other objects can interact with it.
127 			CollisionSimulation = CollisionFlag,
128 			///The object moves according to its kinematic state.
129 			KinematicSimulation = CollisionSimulation | KinematicSimulationFlag,
130 			///This object collides with the shapes of other objects, and forces
131 			///can act on it.
132 			DynamicSimulation = KinematicSimulation | DynamicSimulationFlag
133 		};
134 
135 		b2World* world() const;
shapes()136 		QList<Kolf::Shape*> shapes() const { return m_shapes; }
137 		Kolf::Overlay* overlay(bool createIfNecessary = true);
138 		///@return items inside this CanvasItem which shall only be shown when
139 		///the user toggles additional info. Hide these items by default!
infoItems()140 		virtual QList<QGraphicsItem*> infoItems() const { return QList<QGraphicsItem*>(); }
141 
142 		///This is the velocity used by the physics engine: In each time step,
143 		///the position of this canvas item changes by the value of this property.
144 		QPointF velocity() const;
145 		void setVelocity(const QPointF& velocity);
146 	protected:
147 		void addShape(Kolf::Shape* shape);
148 		///Configure how this object will participate in physical simulation.
149 		void setSimulationType(CanvasItem::SimulationType type);
150 
151 		friend class ::KolfGame; //for the following two methods
152 		///The physics engine calls this method to prepare the object for the following simulation step. Subclass implementations have to call the base implementation just before returning.
153 		virtual void startSimulation();
154 		///The physics engine calls this method after calculating the next frame, to let the objects update their representation. Subclass implementations have to call the base implementation before anything else.
155 		virtual void endSimulation();
156 
157 		///Creates the optimal overlay for this object. The implementation does not have to propagate its properties to the overlay, as the overlay is updated just after it has been created.
158 		///@warning Do not actually call this function from subclass implementations. Use overlay() instead.
createOverlay()159 		virtual Kolf::Overlay* createOverlay() { return nullptr; } //TODO: make this pure virtual when all CanvasItems are QGraphicsItems and implement createOverlay() (and then disallow createOverlay() == 0)
160 		///This function should be called whenever the value of an object's property changes. This will most prominently cause the overlay to be updated (if it exists).
161 		void propagateUpdate();
162 	private:
163 		friend class Kolf::Shape; //for access to m_body
164 		b2Body* m_body;
165 
166 		Kolf::Overlay* m_overlay;
167 		QList<Kolf::Shape*> m_shapes;
168 		CanvasItem::SimulationType m_simulationType;
169 };
170 
171 //WARNING: pos() is at center (not at top-left edge of bounding rect!)
172 class EllipticalCanvasItem : public Tagaro::SpriteObjectItem, public CanvasItem
173 {
174 	public:
175 		EllipticalCanvasItem(bool withEllipse, const QString& spriteKey, QGraphicsItem* parent, b2World* world);
ellipseItem()176 		QGraphicsEllipseItem* ellipseItem() const { return m_ellipseItem; }
177 
178 		bool contains(const QPointF& point) const override;
179 		QPainterPath shape() const override;
180 
181 		QRectF rect() const;
width()182 		double width() const { return Tagaro::SpriteObjectItem::size().width(); }
height()183 		double height() const { return Tagaro::SpriteObjectItem::size().height(); }
184 
185 		void setSize(const QSizeF& size) override;
setSize(qreal width,qreal height)186 		void setSize(qreal width, qreal height) { setSize(QSizeF(width, height)); }
187 		void moveBy(double x, double y) override;
188 
189 		void saveSize(KConfigGroup* group);
190 		void loadSize(KConfigGroup* group);
191 
getPosition()192 		QPointF getPosition() const override { return QGraphicsItem::pos(); }
193 	private:
194 		QGraphicsEllipseItem* m_ellipseItem;
195 		Kolf::EllipseShape* m_shape;
196 };
197 
198 class ArrowItem : public QGraphicsPathItem
199 {
200 	public:
201 		explicit ArrowItem(QGraphicsItem* parent);
202 
203 		qreal angle() const;
204 		void setAngle(qreal angle);
205 		qreal length() const;
206 		void setLength(qreal length);
207 		bool isReversed() const;
208 		void setReversed(bool reversed);
209 
210 		Vector vector() const;
211 	private:
212 		void updatePath();
213 		qreal m_angle, m_length;
214 		bool m_reversed;
215 };
216 
217 #endif
218