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 | #include "ball.h" |
21 | #include "game.h" |
22 | #include "overlay.h" |
23 | #include "shape.h" |
24 | |
25 | #include <QApplication> |
26 | |
27 | Ball::Ball(QGraphicsItem* parent, b2World* world) |
28 | : EllipticalCanvasItem(true, QLatin1String("ball" ), parent, world) |
29 | { |
30 | const int diameter = 8; |
31 | setSize(QSizeF(diameter, diameter)); |
32 | setZBehavior(CanvasItem::IsRaisedByStrut, 10); |
33 | |
34 | setData(0, Rtti_NoCollision); |
35 | m_doDetect = true; |
36 | setBeginningOfHole(false); |
37 | m_collisionId = 0; |
38 | m_addStroke = false; |
39 | m_placeOnGround = false; |
40 | m_forceStillGoing = false; |
41 | frictionMultiplier = 1.0; |
42 | |
43 | QFont font(QApplication::font()); |
44 | font.setPixelSize(12); |
45 | label = new QGraphicsSimpleTextItem(QString(), this); |
46 | label->setFont(font); |
47 | label->setBrush(Qt::white); |
48 | label->setPos(5, 5); |
49 | label->setVisible(false); |
50 | |
51 | // this sets z |
52 | setState(Stopped); |
53 | } |
54 | |
55 | void Ball::setState(BallState newState) |
56 | { |
57 | state = newState; |
58 | if (state == Holed || !EllipticalCanvasItem::isVisible()) |
59 | setSimulationType(CanvasItem::NoSimulation); |
60 | else |
61 | setSimulationType(CanvasItem::DynamicSimulation); |
62 | |
63 | if (state != Stopped) |
64 | setBeginningOfHole(false); |
65 | } |
66 | |
67 | void Ball::friction() |
68 | { |
69 | if (state == Stopped || state == Holed || !isVisible()) |
70 | { |
71 | setVelocity(QPointF()); |
72 | return; |
73 | } |
74 | const double subtractAmount = .027 * frictionMultiplier; |
75 | Vector velocity = this->velocity(); |
76 | if (velocity.magnitude() <= subtractAmount) |
77 | { |
78 | state = Stopped; |
79 | setVelocity(QPointF()); |
80 | game->timeout(); |
81 | return; |
82 | } |
83 | velocity.setMagnitude(velocity.magnitude() - subtractAmount); |
84 | setVelocity(velocity); |
85 | |
86 | frictionMultiplier = 1.0; |
87 | } |
88 | |
89 | void Ball::moveBy(double dx, double dy) |
90 | { |
91 | EllipticalCanvasItem::moveBy(dx, dy); |
92 | |
93 | if (game && !game->isPaused()) |
94 | collisionDetect(); |
95 | |
96 | if ((dx || dy) && game && game->curBall() == this) |
97 | game->ballMoved(); |
98 | } |
99 | |
100 | void Ball::endSimulation() |
101 | { |
102 | CanvasItem::endSimulation(); |
103 | if (state == Stopped) |
104 | if (!qFuzzyIsNull(Vector(velocity()).magnitude())) |
105 | //ball was resting, but received some momentum from collision with other ball |
106 | setState(Rolling); |
107 | } |
108 | |
109 | void Ball::collisionDetect() |
110 | { |
111 | if (!isVisible() || state == Holed || !m_doDetect) |
112 | return; |
113 | |
114 | // do friction every other time |
115 | m_collisionId = (m_collisionId + 1) % 2; |
116 | if (m_collisionId == 1 && !velocity().isNull()) |
117 | friction(); |
118 | |
119 | const double initialVelocity = Vector(velocity()).magnitude(); |
120 | const double minSpeed = .06; |
121 | |
122 | QList<QGraphicsItem *> items = collidingItems(); |
123 | |
124 | bool doTerrainCollisions = true; |
125 | foreach (QGraphicsItem* item, items) |
126 | { |
127 | if (item->data(0) == Rtti_NoCollision || item->data(0) == Rtti_Putter) |
128 | continue; |
129 | |
130 | if (!isVisible() || state == Holed) |
131 | return; |
132 | |
133 | CanvasItem *citem = dynamic_cast<CanvasItem *>(item); |
134 | if (citem) |
135 | { |
136 | if (!citem->terrainCollisions()) |
137 | { |
138 | //do collision |
139 | const bool allowTerrainCollisions = citem->collision(this); |
140 | doTerrainCollisions = doTerrainCollisions && allowTerrainCollisions; |
141 | } |
142 | break; |
143 | } |
144 | } |
145 | |
146 | if (doTerrainCollisions) |
147 | { |
148 | foreach (QGraphicsItem* item, items) |
149 | { |
150 | CanvasItem *citem = dynamic_cast<CanvasItem *>(item); |
151 | if (citem && citem->terrainCollisions()) |
152 | { |
153 | // slopes return false |
154 | // as only one should be processed |
155 | // however that might not always be true |
156 | if (!citem->collision(this)) |
157 | { |
158 | break; |
159 | } |
160 | } |
161 | } |
162 | } |
163 | |
164 | const double currentVelocity = Vector(velocity()).magnitude(); |
165 | const double velocityChange = qAbs(initialVelocity - currentVelocity); |
166 | |
167 | if(currentVelocity < minSpeed && velocityChange < minSpeed && currentVelocity) |
168 | { |
169 | //cutoff low velocities |
170 | setVelocity(Vector()); |
171 | setState(Stopped); |
172 | } |
173 | } |
174 | |
175 | BallState Ball::currentState() |
176 | { |
177 | return state; |
178 | } |
179 | |
180 | QList<QGraphicsItem*> Ball::infoItems() const |
181 | { |
182 | return QList<QGraphicsItem*>() << label; |
183 | } |
184 | |
185 | void Ball::setName(const QString &name) |
186 | { |
187 | label->setText(name); |
188 | } |
189 | |
190 | void Ball::setVisible(bool yes) |
191 | { |
192 | EllipticalCanvasItem::setVisible(yes); |
193 | setState(state); |
194 | } |
195 | |
196 | Kolf::Overlay* Ball::createOverlay() |
197 | { |
198 | return new Kolf::Overlay(this, this); |
199 | } |
200 | |