PageRenderTime 74ms CodeModel.GetById 15ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 0ms

/ball.cpp

http://cutefootball.googlecode.com/
C++ | 304 lines | 225 code | 40 blank | 39 comment | 42 complexity | cb1547447c3022e85e53499d33dbf3ca MD5 | raw file
  1/*
  2 * Copyright 2010,2011 Timothy Rochford
  3 *
  4 *    This file is part of CuteFootball.
  5 *
  6 *    CuteFootball is free software: you can redistribute it and/or modify
  7 *    it under the terms of the Lesser GNU General Public License as published by
  8 *    the Free Software Foundation, either version 3 of the License, or
  9 *    (at your option) any later version.
 10 *
 11 *    CuteFootball is distributed in the hope that it will be useful,
 12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14 *    Lesser GNU General Public License for more details.
 15 *
 16 *    You should have received a copy of the Lesser GNU General Public License
 17 *    along with CuteFootball.  If not, see <http://www.gnu.org/licenses/>.
 18 *
 19 */
 20#include <QtGui>
 21
 22#include "ball.h"
 23#include "team.h"
 24#include "soundeffects.h"
 25#include "Player.h"
 26#include "pitchscene.h"
 27
 28#include <cassert> // assert
 29
 30#include <QDebug>
 31
 32Ball::Ball(Pitch* pitch)
 33    : QObject(),
 34    QGraphicsPixmapItem(QPixmap(QString(":/images/ball.png")),NULL),
 35    m_pitch(pitch),
 36    m_destination(QPointF(0,0)),
 37    step_(0),
 38    m_ballOwner(NULL),
 39    m_lastPlayerToTouchBall(NULL),
 40    m_positionLocked(false),
 41    m_requiredNextAction(MWindow::NoAction),
 42    m_requiredNextActionPlayer(NULL)
 43{
 44    m_ballOwnerTimer = new QTimer(this);
 45    m_ballOwnerTimer->setInterval(250);
 46    m_ballOwnerTimer->start();
 47
 48    QSize pixmapSize = pixmap().size();
 49    pixmapSize.scale(QSizeF(20,20).toSize(), Qt::KeepAspectRatio);
 50
 51    setTransformOriginPoint(boundingRect().center());
 52    setZValue(Pitch::ZBall);
 53
 54    m_animation = new QGraphicsItemAnimation(this);
 55    m_animationTimeLine = new QTimeLine(1000, this);
 56    m_animationTimeLine->setFrameRange(0, 40);
 57
 58    m_animation->setItem(this);
 59    m_animation->setTimeLine(m_animationTimeLine);
 60    connect(m_animationTimeLine, SIGNAL(frameChanged(int)), this, SLOT(updateBall(int)));
 61    connect(m_ballOwnerTimer, SIGNAL(timeout()), this, SLOT(updateBallOwner()));
 62}
 63
 64Ball::~Ball()
 65{
 66    if (m_ballOwnerTimer)
 67        m_ballOwnerTimer->stop();
 68    delete m_ballOwnerTimer;
 69    delete m_animation;
 70    delete m_animationTimeLine;
 71}
 72
 73void Ball::updateBallOwner()
 74{
 75    // find a player who is near the ball.
 76    // that player, now controls the ball
 77    QList<QGraphicsItem*> list = m_pitch->scene()->collidingItems(this,
 78                                     Qt::IntersectsItemBoundingRect);
 79
 80    foreach(QGraphicsItem* i, list) {
 81        Player* p = qgraphicsitem_cast<Player*>(i);
 82        if (p) {
 83            if ( p == m_ballOwner ) {
 84                p->setHasBall(true);
 85                setBallOwner(p);
 86                break;
 87            }
 88            if ( m_ballOwner )
 89                 m_ballOwner->setHasBall(false);
 90            setBallOwner(p);
 91            p->setHasBall(true);
 92        }
 93    }
 94}
 95
 96QRectF Ball::boundingRect() const
 97{
 98    return QRectF(-9, -9, 9, 9);
 99}
100
101void Ball::paint(QPainter *painter,
102                 const QStyleOptionGraphicsItem *option,
103                 QWidget *widget)
104{
105    // Draw QGraphicsPixmapItem face
106    painter->drawPixmap(boundingRect().toRect(), pixmap());
107}
108
109QPainterPath Ball::shape() const
110{
111    QPainterPath path;
112    path.addRect(-10, -10,
113                 10, 10);
114    return path;
115}
116
117void Ball::moveBall(MWindow::Action action, int speed)
118{
119    if (m_positionLocked) {
120        qDebug() << "moveBall positionLocked";
121        return;
122    }
123    if (m_requiredNextAction != MWindow::NoAction) {
124        if (action ==  m_requiredNextAction ) {
125            qDebug() << "action accepted";
126            setRequiredNextAction(MWindow::NoAction,NULL,NULL);
127        } else {
128            qDebug() << "moveBall specialAction required -> return";
129            return;
130        }
131    }
132
133    if (!m_ballOwner) {
134        qDebug() << "moveBall no ball owner";
135        assert(0);
136        return;
137    }
138
139    // make the move
140    switch(action)
141    {
142    case MWindow::North:
143    case MWindow::NorthEast:
144    case MWindow::East:
145    case MWindow::SouthEast:
146    case MWindow::South:
147    case MWindow::SouthWest:
148    case MWindow::West:
149    case MWindow::NorthWest:
150        {
151        m_angle = (qreal)action;
152        QLineF ballTrajectory;
153        ballTrajectory.setP1(pos());
154        ballTrajectory.setAngle(m_angle);
155        ballTrajectory.setLength(m_ballOwner->speed());
156
157//        qDebug() << "Ball::moveBall " << m_angle << " deg, speed=" << m_ballOwner->speed();
158//        qDebug() << "Ball::moveBall start " << ballTrajectory.p1() << " end " << ballTrajectory.p2();
159//        qDebug() << "Ball::moveBall dx " << ballTrajectory.dx() << " dy " << ballTrajectory.dy();
160
161        setRotation((step_%4)*90.0);
162        moveBy(ballTrajectory.dx(), ballTrajectory.dy());
163        }
164        break;
165    case MWindow::Shot:
166        // TODO shootBall(speed);
167        break;
168    default:
169        break;
170    }
171}
172
173void Ball::kickBall(MWindow::Action action, QPointF destination)
174{
175    if (m_positionLocked)
176        return;
177    if ( m_requiredNextAction != MWindow::NoAction) {
178        qDebug() << "Ball::kickBall m_requiredNextAction case";
179        if (action ==  m_requiredNextAction) {
180            qDebug() << "action accepted";
181            setRequiredNextAction(MWindow::NoAction,NULL,NULL);
182        } else
183            return;
184    }
185
186    Team* team(NULL);
187    if ( m_ballOwner ) {
188        m_ballOwner->setHasBall(false);
189        team = m_ballOwner->team();
190    }
191    setNoBallOwner();
192
193    // calculate the difference between present and destination
194    QLineF line(pos(),destination);
195
196    m_animationTimeLine->setDuration(line.length()*7);
197    QPointF tmp(pos());
198
199    m_animationTimeLine->stop();
200
201    qreal stepX = (destination.x() - tmp.x()) / 40.0;
202    qreal stepY = (destination.y() - tmp.y()) / 40.0;
203    QPointF delta(stepX,stepY);
204
205    for (int i = 0; i <= 40; ++i) {
206//        QPointF newPos = tmp + QPointF(stepX,stepY);
207        m_animation->setPosAt(i / 40.0, tmp + delta);
208        // Rotation in animations does not seem to work
209        // animation_->setRotationAt(i / 40.0, i*90.0);
210        tmp = tmp + delta;
211    }
212    m_animationTimeLine->start();
213
214    if (action == MWindow::Shot)
215        emit shot(team, destination);
216    else if (action == MWindow::Pass)
217        emit pass(team, destination);
218    emit soundEvent(SoundEffects::BallKick);
219}
220
221void Ball::updateBall(int frame)
222{
223  //  qDebug() << "Ball::updateBall" << frame;
224    // animation may no longer be running due to a goal
225    if ( (m_animationTimeLine->state() == QTimeLine::Running) && !m_positionLocked ) {
226        QPointF newPos = m_animation->posAt(frame/40.0);
227        if (!m_pitch->footballPitch().contains(newPos))
228            qDebug() << "Ball::updateBall XXX error" << frame;
229        setPos(newPos);
230        // Rotation in animations does not seem to work
231        // setRotation(animation_->rotationAt(frame/40.0));
232    }
233}
234
235QVariant Ball::itemChange(GraphicsItemChange change, const QVariant &value)
236 {
237     if (change == ItemPositionChange && scene() ) {
238
239         m_pitch->centerOn();
240
241         if (m_positionLocked)
242             return m_lastPos;
243
244         step_++;
245
246         // Ball is either on the pitch or in a goal, otherwise return last position
247         // value is the new position.
248         QPointF newPos = value.toPointF();
249         QRectF pitchRect = m_pitch->footballPitch();
250
251         // has a goal been scored?
252         if (m_pitch->m_topGoal->contains(newPos)
253             || m_pitch->m_bottomGoal->contains(newPos)) {
254             setFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
255             m_positionLocked = true;
256             qDebug() << "Ball::itemChange goal scored";
257             emit goalScored(m_pitch->m_topGoal->contains(newPos));
258             emit soundEvent(SoundEffects::Goal);
259             emit soundEvent(SoundEffects::Whistle);
260             setNoBallOwner();
261             m_animationTimeLine->stop();
262             m_lastPos = newPos;
263             return newPos;
264         }
265
266        if (!pitchRect.contains(newPos) && !(m_pitch->m_topGoal->contains(newPos)
267                                             || m_pitch->m_bottomGoal->contains(newPos))) {
268            qreal dx = newPos.x() - m_lastPos.x();
269            qreal dy = newPos.y() - m_lastPos.y();
270            //qDebug() << "Ball::itemChange not in pitch, dx" << dx << "," << dy;
271            m_animationTimeLine->stop();
272            emit soundEvent(SoundEffects::BallRebound);
273            return QPointF(m_lastPos.x() - dx*1.5,m_lastPos.y()-dy*1.5);
274        } else {
275            m_lastPos = newPos;
276            return newPos;
277        }
278    }
279    return QGraphicsItem::itemChange(change, value);
280 }
281
282void Ball::setBallOwner(Player* p)
283{
284    if (p) {
285
286        if (m_requiredNextActionPlayer != NULL &&
287            p != m_requiredNextActionPlayer ) {
288            qDebug() << p->name() << " not allowed to be ball owner";
289            return;
290        }
291
292        m_ballOwner = p;
293        m_lastPlayerToTouchBall = p;
294
295        m_velocity = p->speed();
296    }
297}
298
299void Ball::setRequiredNextAction(MWindow::Action a, Team* t, Player* p)
300{
301    qDebug() << "Ball::setRequiredNextAction";
302    m_requiredNextAction = a;
303    m_requiredNextActionPlayer = p;
304}