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#include <KDebug>
29class b2Body;
30class b2World;
31
32class Ball;
33class KConfigGroup;
34class KolfGame;
35
36namespace Kolf
37{
38 class EllipseShape;
39 class Overlay;
40 class Shape;
41}
42
43enum RttiCodes { Rtti_NoCollision = 1001, Rtti_DontPlaceOn = 1002, Rtti_Putter = 1004 };
44
45class CanvasItem
46{
47public:
48 CanvasItem(b2World* world);
49 virtual ~CanvasItem();
50 ///load your settings from the KConfigGroup, which represents a course.
51 virtual void load(KConfigGroup *) {}
52 ///save your settings.
53 virtual void save(KConfigGroup *cfg);
54 ///called for information when shot started
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.
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'.
61 virtual Config *config(QWidget *parent) { return new DefaultConfig(parent); }
62 ///Returns other items that should be moveable (besides this one of course).
63 virtual QList<QGraphicsItem *> moveableItems() const { return QList<QGraphicsItem *>(); }
64
65 void setId(int newId) { m_id = newId; }
66 int curId() const { return m_id; }
67
68 ///Play a sound (e.g. playSound("wall") plays kdedir/share/apps/kolf/sounds/wall.wav). Optionally, specify \a vol to be between 0-1, for no sound to full volume, respectively.
69 void playSound(const QString &file, double vol = 1);
70
71 ///Called on ball's collision. Return if terrain collidingItems should be processed.
72 virtual bool collision(Ball *ball) { Q_UNUSED(ball) return false; }
73
74 ///Reimplement if you want extra items to have access to the game object. playSound() relies on having this.
75 virtual void setGame(KolfGame *game) { this->game = game; }
76
77 QString name() const { return m_name; }
78 void setName(const QString &newname) { m_name = newname; }
79 virtual void setSize(const QSizeF&) {}
80
81 virtual void moveBy(double dx, double dy);
82
83 //The following is needed temporarily while CanvasItem is not a QGraphicsItem by itself.
84 void setPosition(const QPointF& pos) { const QPointF diff = pos - getPosition(); moveBy(diff.x(), diff.y()); }
85 virtual QPointF getPosition() const = 0;
86
87 enum ZBehavior { FixedZValue = 0, IsStrut = 1, IsRaisedByStrut = 2 };
88 ///This specifies how the object is Z-ordered.
89 ///\li FixedZValue: No special behavior.
90 ///\li IsStrut: This item is a vertical strut. It raises certain
91 /// items when they move on top of it. Its zValue is \a zValueStep.
92 ///\li IsRaisedByStrut: This item can be raised by struts underneath
93 /// it. \a zValueStep is the amount by which the zValue is raised
94 /// then. (i.e. \a zValue is relative to the strut)
95 //TODO: account for overlapping struts
96 void setZBehavior(ZBehavior behavior, qreal zValue);
97 ///Struts are normally found by collision detection. This method
98 ///configures a static strut for this item (on a semantic basis;
99 ///e.g. the RectangleItem is the static strut for its walls).
100 void setStaticStrut(CanvasItem* citem);
101 void updateZ(QGraphicsItem* self);
102 void moveItemsOnStrut(const QPointF& posDiff);
103 static bool mayCollide(CanvasItem* citem1, CanvasItem* citem2);
104protected:
105 friend class Kolf::Overlay; //for delivery of Kolf::Overlay::stateChanged signal
106 ///pointer to main KolfGame
107 KolfGame *game;
108private:
109 QString m_name;
110 int m_id;
111 CanvasItem::ZBehavior m_zBehavior;
112 qreal m_zValue;
113 CanvasItem* m_strut;
114 CanvasItem* m_staticStrut;
115 QList<CanvasItem*> m_struttedItems;
116
117//AFTER THIS LINE follows what I have inserted during the refactoring
118 public:
119 enum SimulationFlag
120 {
121 CollisionFlag = 1 << 0,
122 KinematicSimulationFlag = 1 << 1,
123 DynamicSimulationFlag = 1 << 2
124 };
125 enum SimulationType
126 {
127 ///The object is immovable.
128 NoSimulation = 0,
129 ///The object is immovable, but other objects can interact with it.
130 CollisionSimulation = CollisionFlag,
131 ///The object moves according to its kinematic state.
132 KinematicSimulation = CollisionSimulation | KinematicSimulationFlag,
133 ///This object collides with the shapes of other objects, and forces
134 ///can act on it.
135 DynamicSimulation = KinematicSimulation | DynamicSimulationFlag
136 };
137
138 b2World* world() const;
139 QList<Kolf::Shape*> shapes() const { return m_shapes; }
140 Kolf::Overlay* overlay(bool createIfNecessary = true);
141 ///@return items inside this CanvasItem which shall only be shown when
142 ///the user toggles additional info. Hide these items by default!
143 virtual QList<QGraphicsItem*> infoItems() const { return QList<QGraphicsItem*>(); }
144
145 ///This is the velocity used by the physics engine: In each time step,
146 ///the position of this canvas item changes by the value of this property.
147 QPointF velocity() const;
148 void setVelocity(const QPointF& velocity);
149 protected:
150 void addShape(Kolf::Shape* shape);
151 ///Configure how this object will participate in physical simulation.
152 void setSimulationType(CanvasItem::SimulationType type);
153
154 friend class ::KolfGame; //for the following two methods
155 ///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.
156 virtual void startSimulation();
157 ///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.
158 virtual void endSimulation();
159
160 ///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.
161 ///@warning Do not actually call this function from subclass implementations. Use overlay() instead.
162 virtual Kolf::Overlay* createOverlay() { return 0; } //TODO: make this pure virtual when all CanvasItems are QGraphicsItems and implement createOverlay() (and then disallow createOverlay() == 0)
163 ///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).
164 void propagateUpdate();
165 private:
166 friend class Kolf::Shape; //for access to m_body
167 b2Body* m_body;
168
169 Kolf::Overlay* m_overlay;
170 QList<Kolf::Shape*> m_shapes;
171 CanvasItem::SimulationType m_simulationType;
172};
173
174//WARNING: pos() is at center (not at top-left edge of bounding rect!)
175class EllipticalCanvasItem : public Tagaro::SpriteObjectItem, public CanvasItem
176{
177 public:
178 EllipticalCanvasItem(bool withEllipse, const QString& spriteKey, QGraphicsItem* parent, b2World* world);
179 QGraphicsEllipseItem* ellipseItem() const { return m_ellipseItem; }
180
181 virtual bool contains(const QPointF& point) const;
182 virtual QPainterPath shape() const;
183
184 QRectF rect() const;
185 double width() const { return Tagaro::SpriteObjectItem::size().width(); }
186 double height() const { return Tagaro::SpriteObjectItem::size().height(); }
187
188 virtual void setSize(const QSizeF& size);
189 void setSize(qreal width, qreal height) { setSize(QSizeF(width, height)); }
190 virtual void moveBy(double x, double y);
191
192 void saveSize(KConfigGroup* group);
193 void loadSize(KConfigGroup* group);
194
195 virtual QPointF getPosition() const { return QGraphicsItem::pos(); }
196 private:
197 QGraphicsEllipseItem* m_ellipseItem;
198 Kolf::EllipseShape* m_shape;
199};
200
201class ArrowItem : public QGraphicsPathItem
202{
203 public:
204 ArrowItem(QGraphicsItem* parent);
205
206 qreal angle() const;
207 void setAngle(qreal angle);
208 qreal length() const;
209 void setLength(qreal length);
210 bool isReversed() const;
211 void setReversed(bool reversed);
212
213 Vector vector() const;
214 private:
215 void updatePath();
216 qreal m_angle, m_length;
217 bool m_reversed;
218};
219
220#endif
221