PageRenderTime 60ms CodeModel.GetById 19ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/pitch.cpp

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