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
Ball(QGraphicsItem * parent,b2World * world)27 Ball::Ball(QGraphicsItem* parent, b2World* world)
28 : EllipticalCanvasItem(true, QStringLiteral("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
setState(BallState newState)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
friction()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
moveBy(double dx,double dy)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
endSimulation()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
collisionDetect()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 const QList<QGraphicsItem *> items = collidingItems();
123
124 bool doTerrainCollisions = true;
125 for (QGraphicsItem* item : items) {
126 if (item->data(0) == Rtti_NoCollision || item->data(0) == Rtti_Putter)
127 {
128 if (item->data(0) == Rtti_NoCollision)
129 game->playSound(Sound::Wall);
130 continue;
131 }
132
133 if (!isVisible() || state == Holed)
134 return;
135
136 CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
137 if (citem)
138 {
139 if (!citem->terrainCollisions())
140 {
141 //do collision
142 const bool allowTerrainCollisions = citem->collision(this);
143 doTerrainCollisions = doTerrainCollisions && allowTerrainCollisions;
144 }
145 break;
146 }
147 }
148
149 if (doTerrainCollisions)
150 {
151 for (QGraphicsItem* item : items) {
152 CanvasItem *citem = dynamic_cast<CanvasItem *>(item);
153 if (citem && citem->terrainCollisions())
154 {
155 // slopes return false
156 // as only one should be processed
157 // however that might not always be true
158 if (!citem->collision(this))
159 {
160 break;
161 }
162 }
163 }
164 }
165
166 const double currentVelocity = Vector(velocity()).magnitude();
167 const double velocityChange = qAbs(initialVelocity - currentVelocity);
168
169 if(currentVelocity < minSpeed && velocityChange < minSpeed && currentVelocity)
170 {
171 //cutoff low velocities
172 setVelocity(Vector());
173 setState(Stopped);
174 }
175 }
176
currentState()177 BallState Ball::currentState()
178 {
179 return state;
180 }
181
infoItems() const182 QList<QGraphicsItem*> Ball::infoItems() const
183 {
184 return QList<QGraphicsItem*>() << label;
185 }
186
setName(const QString & name)187 void Ball::setName(const QString &name)
188 {
189 label->setText(name);
190 }
191
setVisible(bool yes)192 void Ball::setVisible(bool yes)
193 {
194 EllipticalCanvasItem::setVisible(yes);
195 setState(state);
196 }
197
createOverlay()198 Kolf::Overlay* Ball::createOverlay()
199 {
200 return new Kolf::Overlay(this, this);
201 }
202