File indexing completed on 2024-05-12 04:04:30

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 }