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
27Ball::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
55void 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
67void 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
89void 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
100void 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
109void 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
175BallState Ball::currentState()
176{
177 return state;
178}
179
180QList<QGraphicsItem*> Ball::infoItems() const
181{
182 return QList<QGraphicsItem*>() << label;
183}
184
185void Ball::setName(const QString &name)
186{
187 label->setText(name);
188}
189
190void Ball::setVisible(bool yes)
191{
192 EllipticalCanvasItem::setVisible(yes);
193 setState(state);
194}
195
196Kolf::Overlay* Ball::createOverlay()
197{
198 return new Kolf::Overlay(this, this);
199}
200