/ball.cpp

http://cutefootball.googlecode.com/ · C++ · 304 lines · 225 code · 40 blank · 39 comment · 43 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. #include "ball.h"
  22. #include "team.h"
  23. #include "soundeffects.h"
  24. #include "Player.h"
  25. #include "pitchscene.h"
  26. #include <cassert> // assert
  27. #include <QDebug>
  28. Ball::Ball(Pitch* pitch)
  29. : QObject(),
  30. QGraphicsPixmapItem(QPixmap(QString(":/images/ball.png")),NULL),
  31. m_pitch(pitch),
  32. m_destination(QPointF(0,0)),
  33. step_(0),
  34. m_ballOwner(NULL),
  35. m_lastPlayerToTouchBall(NULL),
  36. m_positionLocked(false),
  37. m_requiredNextAction(MWindow::NoAction),
  38. m_requiredNextActionPlayer(NULL)
  39. {
  40. m_ballOwnerTimer = new QTimer(this);
  41. m_ballOwnerTimer->setInterval(250);
  42. m_ballOwnerTimer->start();
  43. QSize pixmapSize = pixmap().size();
  44. pixmapSize.scale(QSizeF(20,20).toSize(), Qt::KeepAspectRatio);
  45. setTransformOriginPoint(boundingRect().center());
  46. setZValue(Pitch::ZBall);
  47. m_animation = new QGraphicsItemAnimation(this);
  48. m_animationTimeLine = new QTimeLine(1000, this);
  49. m_animationTimeLine->setFrameRange(0, 40);
  50. m_animation->setItem(this);
  51. m_animation->setTimeLine(m_animationTimeLine);
  52. connect(m_animationTimeLine, SIGNAL(frameChanged(int)), this, SLOT(updateBall(int)));
  53. connect(m_ballOwnerTimer, SIGNAL(timeout()), this, SLOT(updateBallOwner()));
  54. }
  55. Ball::~Ball()
  56. {
  57. if (m_ballOwnerTimer)
  58. m_ballOwnerTimer->stop();
  59. delete m_ballOwnerTimer;
  60. delete m_animation;
  61. delete m_animationTimeLine;
  62. }
  63. void Ball::updateBallOwner()
  64. {
  65. // find a player who is near the ball.
  66. // that player, now controls the ball
  67. QList<QGraphicsItem*> list = m_pitch->scene()->collidingItems(this,
  68. Qt::IntersectsItemBoundingRect);
  69. foreach(QGraphicsItem* i, list) {
  70. Player* p = qgraphicsitem_cast<Player*>(i);
  71. if (p) {
  72. if ( p == m_ballOwner ) {
  73. p->setHasBall(true);
  74. setBallOwner(p);
  75. break;
  76. }
  77. if ( m_ballOwner )
  78. m_ballOwner->setHasBall(false);
  79. setBallOwner(p);
  80. p->setHasBall(true);
  81. }
  82. }
  83. }
  84. QRectF Ball::boundingRect() const
  85. {
  86. return QRectF(-9, -9, 9, 9);
  87. }
  88. void Ball::paint(QPainter *painter,
  89. const QStyleOptionGraphicsItem *option,
  90. QWidget *widget)
  91. {
  92. // Draw QGraphicsPixmapItem face
  93. painter->drawPixmap(boundingRect().toRect(), pixmap());
  94. }
  95. QPainterPath Ball::shape() const
  96. {
  97. QPainterPath path;
  98. path.addRect(-10, -10,
  99. 10, 10);
  100. return path;
  101. }
  102. void Ball::moveBall(MWindow::Action action, int speed)
  103. {
  104. if (m_positionLocked) {
  105. qDebug() << "moveBall positionLocked";
  106. return;
  107. }
  108. if (m_requiredNextAction != MWindow::NoAction) {
  109. if (action == m_requiredNextAction ) {
  110. qDebug() << "action accepted";
  111. setRequiredNextAction(MWindow::NoAction,NULL,NULL);
  112. } else {
  113. qDebug() << "moveBall specialAction required -> return";
  114. return;
  115. }
  116. }
  117. if (!m_ballOwner) {
  118. qDebug() << "moveBall no ball owner";
  119. assert(0);
  120. return;
  121. }
  122. // make the move
  123. switch(action)
  124. {
  125. case MWindow::North:
  126. case MWindow::NorthEast:
  127. case MWindow::East:
  128. case MWindow::SouthEast:
  129. case MWindow::South:
  130. case MWindow::SouthWest:
  131. case MWindow::West:
  132. case MWindow::NorthWest:
  133. {
  134. m_angle = (qreal)action;
  135. QLineF ballTrajectory;
  136. ballTrajectory.setP1(pos());
  137. ballTrajectory.setAngle(m_angle);
  138. ballTrajectory.setLength(m_ballOwner->speed());
  139. // qDebug() << "Ball::moveBall " << m_angle << " deg, speed=" << m_ballOwner->speed();
  140. // qDebug() << "Ball::moveBall start " << ballTrajectory.p1() << " end " << ballTrajectory.p2();
  141. // qDebug() << "Ball::moveBall dx " << ballTrajectory.dx() << " dy " << ballTrajectory.dy();
  142. setRotation((step_%4)*90.0);
  143. moveBy(ballTrajectory.dx(), ballTrajectory.dy());
  144. }
  145. break;
  146. case MWindow::Shot:
  147. // TODO shootBall(speed);
  148. break;
  149. default:
  150. break;
  151. }
  152. }
  153. void Ball::kickBall(MWindow::Action action, QPointF destination)
  154. {
  155. if (m_positionLocked)
  156. return;
  157. if ( m_requiredNextAction != MWindow::NoAction) {
  158. qDebug() << "Ball::kickBall m_requiredNextAction case";
  159. if (action == m_requiredNextAction) {
  160. qDebug() << "action accepted";
  161. setRequiredNextAction(MWindow::NoAction,NULL,NULL);
  162. } else
  163. return;
  164. }
  165. Team* team(NULL);
  166. if ( m_ballOwner ) {
  167. m_ballOwner->setHasBall(false);
  168. team = m_ballOwner->team();
  169. }
  170. setNoBallOwner();
  171. // calculate the difference between present and destination
  172. QLineF line(pos(),destination);
  173. m_animationTimeLine->setDuration(line.length()*7);
  174. QPointF tmp(pos());
  175. m_animationTimeLine->stop();
  176. qreal stepX = (destination.x() - tmp.x()) / 40.0;
  177. qreal stepY = (destination.y() - tmp.y()) / 40.0;
  178. QPointF delta(stepX,stepY);
  179. for (int i = 0; i <= 40; ++i) {
  180. // QPointF newPos = tmp + QPointF(stepX,stepY);
  181. m_animation->setPosAt(i / 40.0, tmp + delta);
  182. // Rotation in animations does not seem to work
  183. // animation_->setRotationAt(i / 40.0, i*90.0);
  184. tmp = tmp + delta;
  185. }
  186. m_animationTimeLine->start();
  187. if (action == MWindow::Shot)
  188. emit shot(team, destination);
  189. else if (action == MWindow::Pass)
  190. emit pass(team, destination);
  191. emit soundEvent(SoundEffects::BallKick);
  192. }
  193. void Ball::updateBall(int frame)
  194. {
  195. // qDebug() << "Ball::updateBall" << frame;
  196. // animation may no longer be running due to a goal
  197. if ( (m_animationTimeLine->state() == QTimeLine::Running) && !m_positionLocked ) {
  198. QPointF newPos = m_animation->posAt(frame/40.0);
  199. if (!m_pitch->footballPitch().contains(newPos))
  200. qDebug() << "Ball::updateBall XXX error" << frame;
  201. setPos(newPos);
  202. // Rotation in animations does not seem to work
  203. // setRotation(animation_->rotationAt(frame/40.0));
  204. }
  205. }
  206. QVariant Ball::itemChange(GraphicsItemChange change, const QVariant &value)
  207. {
  208. if (change == ItemPositionChange && scene() ) {
  209. m_pitch->centerOn();
  210. if (m_positionLocked)
  211. return m_lastPos;
  212. step_++;
  213. // Ball is either on the pitch or in a goal, otherwise return last position
  214. // value is the new position.
  215. QPointF newPos = value.toPointF();
  216. QRectF pitchRect = m_pitch->footballPitch();
  217. // has a goal been scored?
  218. if (m_pitch->m_topGoal->contains(newPos)
  219. || m_pitch->m_bottomGoal->contains(newPos)) {
  220. setFlag(QGraphicsItem::ItemSendsGeometryChanges, false);
  221. m_positionLocked = true;
  222. qDebug() << "Ball::itemChange goal scored";
  223. emit goalScored(m_pitch->m_topGoal->contains(newPos));
  224. emit soundEvent(SoundEffects::Goal);
  225. emit soundEvent(SoundEffects::Whistle);
  226. setNoBallOwner();
  227. m_animationTimeLine->stop();
  228. m_lastPos = newPos;
  229. return newPos;
  230. }
  231. if (!pitchRect.contains(newPos) && !(m_pitch->m_topGoal->contains(newPos)
  232. || m_pitch->m_bottomGoal->contains(newPos))) {
  233. qreal dx = newPos.x() - m_lastPos.x();
  234. qreal dy = newPos.y() - m_lastPos.y();
  235. //qDebug() << "Ball::itemChange not in pitch, dx" << dx << "," << dy;
  236. m_animationTimeLine->stop();
  237. emit soundEvent(SoundEffects::BallRebound);
  238. return QPointF(m_lastPos.x() - dx*1.5,m_lastPos.y()-dy*1.5);
  239. } else {
  240. m_lastPos = newPos;
  241. return newPos;
  242. }
  243. }
  244. return QGraphicsItem::itemChange(change, value);
  245. }
  246. void Ball::setBallOwner(Player* p)
  247. {
  248. if (p) {
  249. if (m_requiredNextActionPlayer != NULL &&
  250. p != m_requiredNextActionPlayer ) {
  251. qDebug() << p->name() << " not allowed to be ball owner";
  252. return;
  253. }
  254. m_ballOwner = p;
  255. m_lastPlayerToTouchBall = p;
  256. m_velocity = p->speed();
  257. }
  258. }
  259. void Ball::setRequiredNextAction(MWindow::Action a, Team* t, Player* p)
  260. {
  261. qDebug() << "Ball::setRequiredNextAction";
  262. m_requiredNextAction = a;
  263. m_requiredNextActionPlayer = p;
  264. }