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 "objects.h" |
21 | #include "ball.h" |
22 | #include "game.h" |
23 | #include "tagaro/board.h" |
24 | |
25 | #include <QFormLayout> |
26 | #include <QTimer> |
27 | #include <KConfigGroup> |
28 | #include <KNumInput> |
29 | #include <KRandom> |
30 | |
31 | //BEGIN Kolf::BlackHole |
32 | |
33 | Kolf::BlackHole::BlackHole(QGraphicsItem* parent, b2World* world) |
34 | : EllipticalCanvasItem(true, QLatin1String("black_hole" ), parent, world) |
35 | , m_minSpeed(3.0) |
36 | , m_maxSpeed(5.0) |
37 | , m_runs(0) |
38 | , m_exitDeg(0) |
39 | , m_exitItem(new QGraphicsLineItem(0, -15, 0, 15, Kolf::findBoard(this))) |
40 | , m_directionItem(new ArrowItem(Kolf::findBoard(this))) |
41 | , m_infoLine(new QGraphicsLineItem(this)) |
42 | { |
43 | setSize(QSizeF(16, 18)); |
44 | setZBehavior(CanvasItem::IsRaisedByStrut, 4); |
45 | setSimulationType(CanvasItem::NoSimulation); |
46 | |
47 | const QColor myColor((QRgb)(KRandom::random() % 0x01000000)); |
48 | ellipseItem()->setBrush(myColor); |
49 | m_exitItem->setPen(QPen(myColor, 6)); |
50 | m_directionItem->setPen(myColor); |
51 | m_infoLine->setPen(QPen(myColor, 2)); |
52 | |
53 | setExitPos(QPointF(300, 100)); |
54 | m_directionItem->setVisible(false); |
55 | m_infoLine->setVisible(false); |
56 | moveBy(0, 0); //initializes line item |
57 | } |
58 | |
59 | Kolf::BlackHole::~BlackHole() |
60 | { |
61 | //these items are not direct childs |
62 | delete m_directionItem; |
63 | delete m_exitItem; |
64 | } |
65 | |
66 | double Kolf::BlackHole::minSpeed() const |
67 | { |
68 | return m_minSpeed; |
69 | } |
70 | |
71 | void Kolf::BlackHole::setMinSpeed(double news) |
72 | { |
73 | m_minSpeed = news; |
74 | m_directionItem->setLength(10.0 + 2.5 * (m_minSpeed + m_maxSpeed)); |
75 | propagateUpdate(); |
76 | } |
77 | |
78 | double Kolf::BlackHole::maxSpeed() const |
79 | { |
80 | return m_maxSpeed; |
81 | } |
82 | |
83 | void Kolf::BlackHole::setMaxSpeed(double news) |
84 | { |
85 | m_maxSpeed = news; |
86 | m_directionItem->setLength(10.0 + 2.5 * (m_minSpeed + m_maxSpeed)); |
87 | propagateUpdate(); |
88 | } |
89 | |
90 | QList<QGraphicsItem*> Kolf::BlackHole::infoItems() const |
91 | { |
92 | return QList<QGraphicsItem*>() << m_infoLine << m_directionItem; |
93 | } |
94 | |
95 | Kolf::Overlay* Kolf::BlackHole::createOverlay() |
96 | { |
97 | return new Kolf::BlackHoleOverlay(this); |
98 | } |
99 | |
100 | Config* Kolf::BlackHole::config(QWidget* parent) |
101 | { |
102 | return new Kolf::BlackHoleConfig(this, parent); |
103 | } |
104 | |
105 | void Kolf::BlackHole::moveBy(double dx, double dy) |
106 | { |
107 | EllipticalCanvasItem::moveBy(dx, dy); |
108 | m_infoLine->setLine(QLineF(QPointF(), m_exitItem->pos() - pos())); |
109 | propagateUpdate(); |
110 | } |
111 | |
112 | int Kolf::BlackHole::curExitDeg() const |
113 | { |
114 | return m_exitDeg; |
115 | } |
116 | |
117 | void Kolf::BlackHole::setExitDeg(int newdeg) |
118 | { |
119 | m_exitDeg = newdeg; |
120 | m_exitItem->setRotation(-newdeg); |
121 | m_directionItem->setAngle(-deg2rad(newdeg)); |
122 | propagateUpdate(); |
123 | } |
124 | |
125 | QPointF Kolf::BlackHole::exitPos() const |
126 | { |
127 | return m_exitItem->pos(); |
128 | } |
129 | |
130 | void Kolf::BlackHole::setExitPos(const QPointF& exitPos) |
131 | { |
132 | m_exitItem->setPos(exitPos); |
133 | m_directionItem->setPos(exitPos); |
134 | moveBy(0, 0); //updates line item, and calls propagateUpdate() |
135 | } |
136 | |
137 | Vector Kolf::BlackHole::exitDirection() const |
138 | { |
139 | return m_directionItem->vector(); |
140 | } |
141 | |
142 | void Kolf::BlackHole::shotStarted() |
143 | { |
144 | m_runs = 0; |
145 | } |
146 | |
147 | bool Kolf::BlackHole::collision(Ball* ball) |
148 | { |
149 | //miss if speed too high |
150 | const double speed = Vector(ball->velocity()).magnitude(); |
151 | if (speed > 3.75) |
152 | return true; |
153 | // is center of ball in cup? |
154 | if (!contains(ball->pos() - pos())) |
155 | return true; |
156 | // warp through blackhole at most 10 times per shot |
157 | if (m_runs > 10 && game && game->isInPlay()) |
158 | return true; |
159 | |
160 | playSound("blackholeputin" ); |
161 | |
162 | const double diff = m_maxSpeed - m_minSpeed; |
163 | const double newSpeed = m_minSpeed + speed / 3.75 * diff; |
164 | |
165 | ball->setVelocity(Vector()); |
166 | ball->setState(Stopped); |
167 | ball->setVisible(false); |
168 | ball->setForceStillGoing(true); |
169 | |
170 | const double distance = Vector(pos() - m_exitItem->pos()).magnitude(); |
171 | BlackHoleTimer* timer = new BlackHoleTimer(ball, newSpeed, distance * 2.5 - newSpeed * 35 + 500); |
172 | |
173 | connect(timer, SIGNAL(eject(Ball*,double)), this, SLOT(eject(Ball*,double))); |
174 | connect(timer, SIGNAL(halfway()), this, SLOT(halfway())); |
175 | |
176 | playSound("blackhole" ); |
177 | return false; |
178 | } |
179 | |
180 | Kolf::BlackHoleTimer::BlackHoleTimer(Ball* ball, double speed, int msec) |
181 | : m_speed(speed), m_ball(ball) |
182 | { |
183 | QTimer::singleShot(msec, this, SLOT(emitEject())); |
184 | QTimer::singleShot(msec / 2, this, SIGNAL(halfway())); |
185 | } |
186 | |
187 | void Kolf::BlackHoleTimer::emitEject() |
188 | { |
189 | emit eject(m_ball, m_speed); |
190 | deleteLater(); |
191 | } |
192 | |
193 | void Kolf::BlackHole::eject(Ball* ball, double speed) |
194 | { |
195 | ball->setVisible(true); |
196 | //place ball 10 units after exit, and set exit velocity |
197 | const Vector direction = Vector::fromMagnitudeDirection(1, -deg2rad(m_exitDeg)); |
198 | ball->setPos(m_exitItem->pos() + 10 * direction); |
199 | ball->setVelocity(speed * direction); |
200 | |
201 | ball->setForceStillGoing(false); |
202 | ball->setState(Rolling); |
203 | |
204 | m_runs++; |
205 | |
206 | playSound("blackholeeject" ); |
207 | } |
208 | |
209 | void Kolf::BlackHole::halfway() |
210 | { |
211 | playSound("blackhole" ); |
212 | } |
213 | |
214 | void Kolf::BlackHole::load(KConfigGroup* cfgGroup) |
215 | { |
216 | setExitPos(cfgGroup->readEntry("exit" , exitPos().toPoint())); |
217 | setExitDeg(cfgGroup->readEntry("exitDeg" , m_exitDeg)); |
218 | setMinSpeed(cfgGroup->readEntry("minspeed" , m_minSpeed)); |
219 | setMaxSpeed(cfgGroup->readEntry("maxspeed" , m_maxSpeed)); |
220 | } |
221 | |
222 | void Kolf::BlackHole::save(KConfigGroup* cfgGroup) |
223 | { |
224 | cfgGroup->writeEntry("exit" , m_exitItem->pos().toPoint()); |
225 | cfgGroup->writeEntry("exitDeg" , m_exitDeg); |
226 | cfgGroup->writeEntry("minspeed" , m_minSpeed); |
227 | cfgGroup->writeEntry("maxspeed" , m_maxSpeed); |
228 | } |
229 | |
230 | //END Kolf::BlackHole |
231 | //BEGIN Kolf::BlackHoleConfig |
232 | |
233 | Kolf::BlackHoleConfig::BlackHoleConfig(BlackHole* blackHole, QWidget* parent) |
234 | : Config(parent) |
235 | , m_blackHole(blackHole) |
236 | { |
237 | QFormLayout* layout = new QFormLayout(this); |
238 | |
239 | KIntSpinBox* deg = new KIntSpinBox(this); |
240 | deg->setRange(0, 359); |
241 | deg->setSingleStep(10); |
242 | deg->setSuffix(ki18np(" degree" , " degrees" )); |
243 | deg->setValue(m_blackHole->curExitDeg()); |
244 | deg->setWrapping(true); |
245 | layout->addRow(i18n("Exiting ball angle:" ), deg); |
246 | connect(deg, SIGNAL(valueChanged(int)), this, SLOT(degChanged(int))); |
247 | |
248 | KDoubleNumInput* min = new KDoubleNumInput(this); |
249 | min->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); |
250 | min->setRange(0, 8); |
251 | min->setValue(m_blackHole->minSpeed()); |
252 | layout->addRow(i18n("Minimum exit speed:" ), min); |
253 | connect(min, SIGNAL(valueChanged(double)), this, SLOT(minChanged(double))); |
254 | |
255 | KDoubleNumInput* max = new KDoubleNumInput(this); |
256 | max->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); |
257 | max->setRange(0, 8); |
258 | max->setValue(m_blackHole->maxSpeed()); |
259 | layout->addRow(i18n("Maximum exit speed:" ), max); |
260 | connect(max, SIGNAL(valueChanged(double)), this, SLOT(maxChanged(double))); |
261 | } |
262 | |
263 | void Kolf::BlackHoleConfig::degChanged(int newdeg) |
264 | { |
265 | m_blackHole->setExitDeg(newdeg); |
266 | changed(); |
267 | } |
268 | |
269 | void Kolf::BlackHoleConfig::minChanged(double news) |
270 | { |
271 | m_blackHole->setMinSpeed(news); |
272 | changed(); |
273 | } |
274 | |
275 | void Kolf::BlackHoleConfig::maxChanged(double news) |
276 | { |
277 | m_blackHole->setMaxSpeed(news); |
278 | changed(); |
279 | } |
280 | |
281 | //END Kolf::BlackHoleConfig |
282 | //BEGIN Kolf::BlackHoleOverlay |
283 | |
284 | Kolf::BlackHoleOverlay::BlackHoleOverlay(Kolf::BlackHole* blackHole) |
285 | : Kolf::Overlay(blackHole, blackHole) |
286 | , m_exitIndicator(new QGraphicsLineItem(this)) |
287 | , m_exitHandle(new Kolf::OverlayHandle(Kolf::OverlayHandle::SquareShape, this)) |
288 | , m_speedHandle(new Kolf::OverlayHandle(Kolf::OverlayHandle::SquareShape, this)) |
289 | { |
290 | addHandle(m_exitIndicator); |
291 | addHandle(m_exitHandle); |
292 | addHandle(m_speedHandle); |
293 | connect(m_exitHandle, SIGNAL(moveRequest(QPointF)), this, SLOT(moveHandle(QPointF))); |
294 | connect(m_speedHandle, SIGNAL(moveRequest(QPointF)), this, SLOT(moveHandle(QPointF))); |
295 | } |
296 | |
297 | void Kolf::BlackHoleOverlay::update() |
298 | { |
299 | Kolf::Overlay::update(); |
300 | Kolf::BlackHole* blackHole = dynamic_cast<Kolf::BlackHole*>(qitem()); |
301 | m_exitHandle->setPos(blackHole->exitPos() - blackHole->pos()); |
302 | m_speedHandle->setPos(m_exitHandle->pos() + blackHole->exitDirection()); |
303 | m_exitIndicator->setLine(QLineF(m_exitHandle->pos(), m_speedHandle->pos())); |
304 | const qreal exitAngle = -deg2rad(blackHole->curExitDeg()); |
305 | m_exitHandle->setRotation(exitAngle); |
306 | m_speedHandle->setRotation(exitAngle); |
307 | } |
308 | |
309 | void Kolf::BlackHoleOverlay::moveHandle(const QPointF& handleScenePos) |
310 | { |
311 | Kolf::BlackHole* blackHole = dynamic_cast<Kolf::BlackHole*>(qitem()); |
312 | if (sender() == m_exitHandle) |
313 | blackHole->setExitPos(handleScenePos); |
314 | else if (sender() == m_speedHandle) |
315 | { |
316 | //this modifies only exit direction, not speed |
317 | Vector dir = handleScenePos - blackHole->exitPos(); |
318 | blackHole->setExitDeg(-rad2deg(dir.direction())); |
319 | } |
320 | } |
321 | |
322 | //END Kolf::BlackHoleOverlay |
323 | //BEGIN Kolf::Cup |
324 | |
325 | Kolf::Cup::Cup(QGraphicsItem* parent, b2World* world) |
326 | : EllipticalCanvasItem(false, QLatin1String("cup" ), parent, world) |
327 | { |
328 | const int diameter = 16; |
329 | setSize(QSizeF(diameter, diameter)); |
330 | setZBehavior(CanvasItem::IsRaisedByStrut, 4); |
331 | setSimulationType(CanvasItem::NoSimulation); |
332 | } |
333 | |
334 | Kolf::Overlay* Kolf::Cup::createOverlay() |
335 | { |
336 | return new Kolf::Overlay(this, this); |
337 | } |
338 | |
339 | bool Kolf::Cup::collision(Ball* ball) |
340 | { |
341 | //miss if speed too high |
342 | const double speed = Vector(ball->velocity()).magnitude(); |
343 | if (speed > 3.75) |
344 | return true; |
345 | //miss if center of ball not inside cup |
346 | if (!contains(ball->pos() - pos())) |
347 | return true; |
348 | //place ball in hole |
349 | ball->setState(Holed); |
350 | playSound("holed" ); |
351 | ball->setPos(pos()); |
352 | ball->setVelocity(Vector()); |
353 | return false; |
354 | } |
355 | |
356 | //END Kolf::Cup |
357 | |
358 | #include "objects.moc" |
359 | |