File indexing completed on 2025-02-02 07:28:20
0001 /* 0002 Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu> 0003 Copyright 2010 Stefan Majewsky <majewsky@gmx.net> 0004 0005 This program is free software; you can redistribute it and/or modify 0006 it under the terms of the GNU General Public License as published by 0007 the Free Software Foundation; either version 2 of the License, or 0008 (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0018 */ 0019 0020 #include "ball.h" 0021 #include "game.h" 0022 #include "overlay.h" 0023 #include "shape.h" 0024 0025 #include <QApplication> 0026 0027 Ball::Ball(QGraphicsItem* parent, b2World* world) 0028 : EllipticalCanvasItem(true, QStringLiteral("ball"), parent, world) 0029 { 0030 const int diameter = 8; 0031 setSize(QSizeF(diameter, diameter)); 0032 setZBehavior(CanvasItem::IsRaisedByStrut, 10); 0033 0034 setData(0, Rtti_NoCollision); 0035 m_doDetect = true; 0036 setBeginningOfHole(false); 0037 m_collisionId = 0; 0038 m_addStroke = false; 0039 m_placeOnGround = false; 0040 m_forceStillGoing = false; 0041 frictionMultiplier = 1.0; 0042 0043 QFont font(QApplication::font()); 0044 font.setPixelSize(12); 0045 label = new QGraphicsSimpleTextItem(QString(), this); 0046 label->setFont(font); 0047 label->setBrush(Qt::white); 0048 label->setPos(5, 5); 0049 label->setVisible(false); 0050 0051 // this sets z 0052 setState(Stopped); 0053 } 0054 0055 void Ball::setState(BallState newState) 0056 { 0057 state = newState; 0058 if (state == Holed || !EllipticalCanvasItem::isVisible()) 0059 setSimulationType(CanvasItem::NoSimulation); 0060 else 0061 setSimulationType(CanvasItem::DynamicSimulation); 0062 0063 if (state != Stopped) 0064 setBeginningOfHole(false); 0065 } 0066 0067 void Ball::friction() 0068 { 0069 if (state == Stopped || state == Holed || !isVisible()) 0070 { 0071 setVelocity(QPointF()); 0072 return; 0073 } 0074 const double subtractAmount = .027 * frictionMultiplier; 0075 Vector velocity = this->velocity(); 0076 if (velocity.magnitude() <= subtractAmount) 0077 { 0078 state = Stopped; 0079 setVelocity(QPointF()); 0080 game->timeout(); 0081 return; 0082 } 0083 velocity.setMagnitude(velocity.magnitude() - subtractAmount); 0084 setVelocity(velocity); 0085 0086 frictionMultiplier = 1.0; 0087 } 0088 0089 void Ball::moveBy(double dx, double dy) 0090 { 0091 EllipticalCanvasItem::moveBy(dx, dy); 0092 0093 if (game && !game->isPaused()) 0094 collisionDetect(); 0095 0096 if ((dx || dy) && game && game->curBall() == this) 0097 game->ballMoved(); 0098 } 0099 0100 void Ball::endSimulation() 0101 { 0102 CanvasItem::endSimulation(); 0103 if (state == Stopped) { 0104 if (!qFuzzyIsNull(Vector(velocity()).magnitude())) { 0105 //ball was resting, but received some momentum from collision with other ball 0106 setState(Rolling); 0107 } 0108 } else if (state == Rolling) { 0109 if (qFuzzyIsNull(Vector(velocity()).magnitude())) { 0110 //ball was moving, but stopped moving from collision with other ball 0111 setState(Stopped); 0112 } 0113 } 0114 } 0115 0116 void Ball::collisionDetect() 0117 { 0118 if (!isVisible() || state == Holed || !m_doDetect) 0119 return; 0120 0121 // do friction every other time 0122 m_collisionId = (m_collisionId + 1) % 2; 0123 if (m_collisionId == 1 && !velocity().isNull()) 0124 friction(); 0125 0126 const double initialVelocity = Vector(velocity()).magnitude(); 0127 const double minSpeed = .06; 0128 0129 const QList<QGraphicsItem *> items = collidingItems(); 0130 0131 bool doTerrainCollisions = true; 0132 for (QGraphicsItem* item : items) { 0133 if (item->data(0) == Rtti_NoCollision || item->data(0) == Rtti_Putter) 0134 { 0135 if (item->data(0) == Rtti_NoCollision) 0136 game->playSound(Sound::Wall); 0137 continue; 0138 } 0139 0140 if (!isVisible() || state == Holed) 0141 return; 0142 0143 CanvasItem *citem = dynamic_cast<CanvasItem *>(item); 0144 if (citem) 0145 { 0146 if (!citem->terrainCollisions()) 0147 { 0148 //do collision 0149 const bool allowTerrainCollisions = citem->collision(this); 0150 doTerrainCollisions = doTerrainCollisions && allowTerrainCollisions; 0151 } 0152 break; 0153 } 0154 } 0155 0156 if (doTerrainCollisions) 0157 { 0158 for (QGraphicsItem* item : items) { 0159 CanvasItem *citem = dynamic_cast<CanvasItem *>(item); 0160 if (citem && citem->terrainCollisions()) 0161 { 0162 // slopes return false 0163 // as only one should be processed 0164 // however that might not always be true 0165 if (!citem->collision(this)) 0166 { 0167 break; 0168 } 0169 } 0170 } 0171 } 0172 0173 const double currentVelocity = Vector(velocity()).magnitude(); 0174 const double velocityChange = qAbs(initialVelocity - currentVelocity); 0175 0176 if(currentVelocity < minSpeed && velocityChange < minSpeed && currentVelocity) 0177 { 0178 //cutoff low velocities 0179 setVelocity(Vector()); 0180 setState(Stopped); 0181 } 0182 } 0183 0184 BallState Ball::currentState() 0185 { 0186 return state; 0187 } 0188 0189 QList<QGraphicsItem*> Ball::infoItems() const 0190 { 0191 return QList<QGraphicsItem*>() << label; 0192 } 0193 0194 void Ball::setName(const QString &name) 0195 { 0196 label->setText(name); 0197 } 0198 0199 void Ball::setVisible(bool yes) 0200 { 0201 EllipticalCanvasItem::setVisible(yes); 0202 setState(state); 0203 } 0204 0205 Kolf::Overlay* Ball::createOverlay() 0206 { 0207 return new Kolf::Overlay(this, this); 0208 }