/pitch.cpp

http://cutefootball.googlecode.com/ · C++ · 463 lines · 355 code · 71 blank · 37 comment · 44 complexity · 237fdad4f37d43780d6248f55db8f079 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 <QtGlobal>
  22. #include <QDebug>
  23. #include <QGraphicsLineItem>
  24. #include <QGraphicsEllipseItem>
  25. #include <QGraphicsItemAnimation>
  26. #include <QStatusBar>
  27. #include "pitch.h"
  28. #include "ball.h"
  29. #include "Player.h"
  30. #include "team.h"
  31. #include "goalkeeper.h"
  32. #include "screengraphics.h"
  33. #include "game.h"
  34. #include "soundEffects.h"
  35. #include "settingsFrame.h"
  36. #include "soccerutils.h"
  37. #include "pitchscene.h"
  38. Pitch::Pitch(const QRectF& footballGroundRect,
  39. QGraphicsView* view,
  40. SoundEffects* se,
  41. settingsFrame* settingsFrame,
  42. QStatusBar* bar)
  43. : QObject(),
  44. m_scene(new PitchScene(footballGroundRect, this)),
  45. m_view(view),
  46. m_motionTimer(NULL),
  47. m_bottomGoal(NULL),
  48. m_topGoal(NULL),
  49. m_settingsFrame(settingsFrame),
  50. m_soundEffects(se),
  51. m_teamMgr(new TeamManager),
  52. m_bar(bar)
  53. {
  54. m_view->scale(1.4,1.4);
  55. m_view->setScene(m_scene);
  56. m_scene->setInputMethod(m_settingsFrame->inputMethod());
  57. m_ball = new Ball(this);
  58. m_motionTimer = new QTimer(this);
  59. m_motionTimer->setInterval(KGameRefreshRate);
  60. m_gameFSM = new QStateMachine(this);
  61. m_game = new Game(*m_gameFSM, *this);
  62. connect(m_gameFSM, SIGNAL(started()), this, SLOT(gameStarted()));
  63. connect(m_gameFSM, SIGNAL(finished()), this, SLOT(gameStopped()));
  64. connect(m_gameFSM, SIGNAL(stopped()), this, SLOT(gameStopped()));
  65. layoutPitch();
  66. m_soundEffects->soundEnabled(m_settingsFrame->soundEnabled());
  67. m_teamMgr->createTeams();
  68. connect(m_motionTimer, SIGNAL(timeout()), m_scene, SLOT(advance()));
  69. connect(m_motionTimer, SIGNAL(timeout()), m_scene, SLOT(update()));
  70. connect(m_motionTimer, SIGNAL(timeout()), this, SLOT(hasBallCheck()));
  71. connect(m_motionTimer, SIGNAL(timeout()), this, SLOT(selectNearestPlayer()));
  72. connect(m_settingsFrame, SIGNAL(soundChanged(bool)), m_soundEffects, SLOT(soundEnabled(bool)));
  73. connect(m_settingsFrame, SIGNAL(inputMethodChanged(settingsFrame::InputMethod)),
  74. m_scene, SLOT(setInputMethod(settingsFrame::InputMethod)));
  75. // connect(m_settingsFrame, SIGNAL(inputMethodChanged(settingsFrame::InputMethod)),
  76. // m_screenButtonsLabel, SLOT(setInputMethod(settingsFrame::InputMethod)));
  77. }
  78. Pitch::~Pitch()
  79. {
  80. delete m_statusBarInfo;
  81. delete m_scene;
  82. if (m_motionTimer->isActive())
  83. m_motionTimer->stop();
  84. delete m_motionTimer;
  85. delete m_gameFSM;
  86. delete m_ball;
  87. delete m_teamMgr;
  88. }
  89. void Pitch::centerOn()
  90. {
  91. m_view->centerOn(m_centerOn);
  92. }
  93. void Pitch::centerOn(QGraphicsItem *item)
  94. {
  95. m_centerOn = item;
  96. m_view->centerOn(m_centerOn);
  97. }
  98. void Pitch::selectNearestPlayer()
  99. {
  100. Player *p = selectNearestPlayer(m_homeTeam);
  101. if (p)
  102. m_scene->setFocusItem(p);
  103. }
  104. Player* Pitch::selectNearestPlayer(Team* team)
  105. {
  106. qreal nearest = 0xffff;
  107. Player *nearestPlayer(NULL);
  108. // nobody has ball, select the closest hometeam
  109. foreach (Player *p, m_players) {
  110. if (p->team()== team) {
  111. // dont select the goal keeper
  112. if (p->m_role == Player::GoalKeeper)
  113. continue;
  114. if (!nearestPlayer)
  115. nearestPlayer = p;
  116. const int dx = p->pos().x() - m_ball->pos().x();
  117. const int dy = p->pos().y() - m_ball->pos().y();
  118. if ( (qAbs(dx*dx)+qAbs(dy*dy)) < nearest) {
  119. nearestPlayer = p;
  120. nearest = qAbs(qAbs(dx*dx)+qAbs(dy*dy));
  121. }
  122. }
  123. }
  124. return nearestPlayer;
  125. }
  126. void Pitch::gameStop()
  127. {
  128. qDebug() << "Pitch::gameStop";
  129. if (m_gameFSM->isRunning())
  130. m_gameFSM->stop();
  131. }
  132. void Pitch::gameStarted()
  133. {
  134. m_motionTimer->start();
  135. emit gameInProgress(true);
  136. }
  137. void Pitch::gameStopped()
  138. {
  139. qDebug() << "Pitch::gameStopped";
  140. m_statusBarInfo->removeWidgets();
  141. const QString initialStateName = static_cast<GameHalf*>(m_gameFSM->initialState())->objectName();
  142. if ( initialStateName == m_game->firstHalfState()->objectName()
  143. && extraTimeAllowed() && extraTime() ) {
  144. m_gameFSM->setInitialState(m_game->extraTimeFirstHalfState());
  145. m_gameFSM->start();
  146. } else if (initialStateName == m_game->extraTimeFirstHalfState()->objectName() && extraTime() ) {
  147. m_gameFSM->setInitialState(m_game->penaltiesState());
  148. m_gameFSM->start();
  149. } else {
  150. m_scene->removeItem(m_ball);
  151. foreach(Player *p, m_players)
  152. m_scene->removeItem(p);
  153. m_players.clear();
  154. m_motionTimer->stop();
  155. emit gameInProgress(false);
  156. }
  157. }
  158. void Pitch::setPiece(Team* originatingTeam, SetPiece s, QPointF foulLocation)
  159. {
  160. m_soundEffects->soundEvent(SoundEffects::Whistle);
  161. switch(s) {
  162. case Pitch::Foul:
  163. // TODO fouls are disabled
  164. //emit foul(originatingTeam, foulLocation);
  165. break;
  166. case Pitch::KickOff:
  167. emit kickOff(originatingTeam);
  168. break;
  169. default:
  170. break;
  171. }
  172. }
  173. void Pitch::layoutPitch()
  174. {
  175. const int KPitchBoundaryWidth = 40;
  176. // create the pitch
  177. m_footballPitch = QRectF(KPitchBoundaryWidth, KPitchBoundaryWidth,
  178. m_scene->width()-(KPitchBoundaryWidth*2), m_scene->height()-(KPitchBoundaryWidth*2));
  179. // center mark
  180. m_centerMark = QPointF((m_scene->width()/2.0)-4,(m_scene->height()/2.0)-4);
  181. // center circle
  182. m_centerCircle.addEllipse(m_centerMark,40.0,40.0);
  183. // half statistics frame
  184. m_statusBarInfo = new ScreenGraphics(*this, *m_bar);
  185. // create the goals
  186. m_bottomGoal = new QRectF( (m_scene->width() / 2)-60,
  187. m_scene->height()-KPitchBoundaryWidth,
  188. 120,
  189. KPitchBoundaryWidth/2);
  190. m_topGoal = new QRectF((m_scene->width() / 2)-60,KPitchBoundaryWidth/2,120,KPitchBoundaryWidth/2);
  191. // penalty areas
  192. QRectF penaltyRectF((m_scene->width() / 2)-80, 0, 160, KPitchBoundaryWidth*2 );
  193. m_topPenaltyArea.moveTo(penaltyRectF.center());
  194. m_topPenaltyArea.arcTo(penaltyRectF,180.0,180.0);
  195. m_topPenaltyArea.closeSubpath();
  196. QRectF penaltyRectF2((m_scene->width() / 2)-80, m_scene->height()-(KPitchBoundaryWidth*2), 160, KPitchBoundaryWidth*2);
  197. m_bottomPenaltyArea.moveTo(penaltyRectF2.center());
  198. m_bottomPenaltyArea.arcTo(penaltyRectF2,180.0,-180.0);
  199. m_bottomPenaltyArea.closeSubpath();
  200. m_entrancePoint = QPointF(0, (m_scene->sceneRect().height())/2);
  201. // divide the pitch into areas
  202. // makes it easier for computer based movement
  203. QPointF tlTopHalf = m_footballPitch.topLeft();
  204. const qreal w = m_footballPitch.width();
  205. const qreal h = m_footballPitch.height();
  206. for (int row = 0; row < KRow; row++) {
  207. for (int col = 0; col < KColumn; col++) {
  208. qreal tlx = (w / KColumn) * col;
  209. qreal tly = (h / KRow ) * row;
  210. QPointF tl(tlTopHalf + QPointF(tlx,tly));
  211. QPointF br(tl + QPointF(w/KColumn,h/KRow));
  212. m_pitchArea[row][col] = QRectF(tl, br);
  213. }
  214. }
  215. }
  216. void Pitch::pause()
  217. {
  218. qDebug() << "pause";
  219. m_motionTimer->stop();
  220. emit pauseGameClock();
  221. if ( m_game->currentState() )
  222. m_game->currentState()->pauseGameClock();
  223. }
  224. void Pitch::continueGame()
  225. {
  226. qDebug() << "continueGame";
  227. m_motionTimer->start();
  228. emit continueGameClock();
  229. if ( m_game->currentState() )
  230. m_game->currentState()->continueGameClock();
  231. }
  232. void Pitch::updateDisplayTime(int timeLeftMs)
  233. {
  234. if ( m_gameFSM->isRunning() ) {
  235. QTime tmp(0,0,0,0);
  236. tmp = tmp.addMSecs(timeLeftMs);
  237. m_statusBarInfo->update(tmp.toString(QString("m:ss")));
  238. //m_screenButtonsLabel->refresh();
  239. } else {
  240. qDebug() << "updateDisplayTime FSM not running";
  241. }
  242. }
  243. void Pitch::hasBallCheck()
  244. {
  245. // which team has the ball?
  246. Player* p = m_ball->lastPlayerToTouchBall();
  247. if ( p ) {
  248. m_homeTeam->setHasBall(p->team()== m_homeTeam);
  249. m_awayTeam->setHasBall(p->team()== m_awayTeam);
  250. }
  251. }
  252. void Pitch::countShots(Team* team, QPointF /* dest */)
  253. {
  254. if (team)
  255. team->setShots(team->shots() + 1);
  256. }
  257. void Pitch::newGame(int homeTeam, int awayTeam)
  258. {
  259. m_game->setHalfLength(m_settingsFrame->gameLengthMinutes());
  260. m_homeTeam = m_teamMgr->at(homeTeam);
  261. m_homeTeam->newGame();
  262. m_awayTeam = m_teamMgr->at(awayTeam);
  263. m_awayTeam->newGame();
  264. createTeamPlayers(m_homeTeam);
  265. createTeamPlayers(m_awayTeam);
  266. connect(m_ball, SIGNAL(shot(Team*,QPointF)), this, SLOT(countShots(Team*,QPointF)));
  267. connect(m_ball, SIGNAL(goalScored(bool)), m_awayTeam, SLOT(goalScored(bool)));
  268. connect(m_ball, SIGNAL(goalScored(bool)), m_homeTeam, SLOT(goalScored(bool)));
  269. connect(m_ball, SIGNAL(soundEvent(SoundEffects::GameSound)),
  270. m_soundEffects, SLOT(soundEvent(SoundEffects::GameSound)));
  271. m_soundEffects->soundEvent(SoundEffects::CrowdNoise);
  272. m_game->setTeamToKickOff(m_awayTeam);
  273. m_statusBarInfo->setTeams(m_homeTeam, m_awayTeam);
  274. m_gameFSM->start();
  275. }
  276. void Pitch::createTeamPlayers(Team *team)
  277. {
  278. bool isHomeTeam(false);
  279. if (team == m_homeTeam)
  280. isHomeTeam = true;
  281. QPointF startPos(0, m_scene->sceneRect().height()/2);
  282. // for each team in the directory
  283. QString f(":/teams/");
  284. f.append(team->fullName());
  285. f.append(".txt");
  286. QFile file(f);
  287. if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  288. return;
  289. int i = 0;
  290. while (!file.atEnd()) {
  291. QByteArray line = file.readLine();
  292. if (line.contains('#'))
  293. continue;
  294. QList<QByteArray> nameAndPosition = line.split(',');
  295. QString name = nameAndPosition.at(0).simplified();
  296. Player::Role r = mapPositionStringToRole(nameAndPosition.at(1).simplified());
  297. QString hairColorString = nameAndPosition.at(2).simplified();
  298. QColor hairColor(hairColorString);
  299. QString skinColorStr = nameAndPosition.at(3).simplified();
  300. QColor skinColor(skinColorMapping(skinColorStr));
  301. Player *pl(NULL);
  302. if (r == Player::GoalKeeper) {
  303. pl = new GoalKeeper(
  304. name,
  305. i+1,
  306. this,
  307. team,
  308. hairColor,
  309. skinColor);
  310. } else {
  311. qreal speed;
  312. if (m_settingsFrame->inputMethod() != settingsFrame::Keyboard) {
  313. if (isHomeTeam)
  314. speed += 0.5;
  315. else
  316. speed -= 0.5;
  317. } else
  318. speed = team->speed();
  319. pl = new Player(
  320. name,
  321. i+1,
  322. !isHomeTeam,
  323. this,
  324. team,
  325. speed,
  326. r,
  327. hairColor,
  328. skinColor);
  329. }
  330. pl->createPixmaps();
  331. pl->setPos(startPos);
  332. m_players.append(pl);
  333. m_scene->addItem(pl);
  334. i++;
  335. }
  336. file.close();
  337. }
  338. void Pitch::setPlayerDefendZone(Player *p)
  339. {
  340. bool nToS(false);
  341. if (p->team()->getDirection() == Team::NorthToSouth)
  342. nToS = true;
  343. QRectF zone;
  344. switch (p->m_role)
  345. {
  346. case Player::GoalKeeper:
  347. // goal keepers dont have a defend zone
  348. break;
  349. default:
  350. zone = p->m_startPositionRectF;
  351. break;
  352. }
  353. p->m_defendZone = zone;
  354. }
  355. void Pitch::setPlayerStartPositions(Team *team)
  356. {
  357. bool nToS(false);
  358. if (team->getDirection() == Team::NorthToSouth)
  359. nToS = true;
  360. QMap<int,QRectF> startPositions;
  361. startPositions.insert(Player::GoalKeeper,
  362. m_pitchArea[nToS ? 0 : 7][2]);
  363. startPositions.insert(Player::LeftDefence,
  364. m_pitchArea[nToS ? 1 : 6][0]);
  365. startPositions.insert(Player::LeftCentralDefence,
  366. m_pitchArea[nToS ? 1 : 6][1]);
  367. startPositions.insert(Player::CentralDefence,
  368. m_pitchArea[nToS ? 1 : 6][2]);
  369. startPositions.insert(Player::RightCentralDefence,
  370. m_pitchArea[nToS ? 1 : 6][3]);
  371. startPositions.insert(Player::RightDefence,
  372. m_pitchArea[nToS ? 1 : 6][4]);
  373. startPositions.insert(Player::LeftMidfield,
  374. m_pitchArea[nToS ? 2 : 5][0]);
  375. startPositions.insert(Player::CentralMidfield,
  376. m_pitchArea[nToS ? 2 : 5][2]);
  377. startPositions.insert(Player::RightMidfield,
  378. m_pitchArea[nToS ? 2 : 5][4]);
  379. startPositions.insert(Player::LeftAttack,
  380. m_pitchArea[nToS ? 3 : 4][0]);
  381. startPositions.insert(Player::CentralAttack,
  382. m_pitchArea[nToS ? 2 : 5][2]);
  383. startPositions.insert(Player::RightAttack,
  384. m_pitchArea[nToS ? 3 : 4][4]);
  385. foreach (Player *p, m_players) {
  386. if (p->team() == team) {
  387. p->setHasBall(false);
  388. p->m_startPositionRectF = startPositions[p->m_role];
  389. setPlayerDefendZone(p);
  390. }
  391. }
  392. }