| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtCore module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:BSD$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** BSD License Usage | 
| 18 | ** Alternatively, you may use this file under the terms of the BSD license | 
| 19 | ** as follows: | 
| 20 | ** | 
| 21 | ** "Redistribution and use in source and binary forms, with or without | 
| 22 | ** modification, are permitted provided that the following conditions are | 
| 23 | ** met: | 
| 24 | **   * Redistributions of source code must retain the above copyright | 
| 25 | **     notice, this list of conditions and the following disclaimer. | 
| 26 | **   * Redistributions in binary form must reproduce the above copyright | 
| 27 | **     notice, this list of conditions and the following disclaimer in | 
| 28 | **     the documentation and/or other materials provided with the | 
| 29 | **     distribution. | 
| 30 | **   * Neither the name of The Qt Company Ltd nor the names of its | 
| 31 | **     contributors may be used to endorse or promote products derived | 
| 32 | **     from this software without specific prior written permission. | 
| 33 | ** | 
| 34 | ** | 
| 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | 
| 46 | ** | 
| 47 | ** $QT_END_LICENSE$ | 
| 48 | ** | 
| 49 | ****************************************************************************/ | 
| 50 |  | 
| 51 | //Own | 
| 52 | #include "states.h" | 
| 53 | #include "graphicsscene.h" | 
| 54 | #include "boat.h" | 
| 55 | #include "submarine.h" | 
| 56 | #include "torpedo.h" | 
| 57 | #include "animationmanager.h" | 
| 58 | #include "progressitem.h" | 
| 59 | #include "textinformationitem.h" | 
| 60 |  | 
| 61 | //Qt | 
| 62 | #include <QFinalState> | 
| 63 | #include <QGraphicsView> | 
| 64 | #include <QKeyEventTransition> | 
| 65 | #include <QMessageBox> | 
| 66 | #include <QRandomGenerator> | 
| 67 | #include <QStateMachine> | 
| 68 |  | 
| 69 | PlayState::PlayState(GraphicsScene *scene, QState *parent) | 
| 70 |     : QState(parent), scene(scene), machine(nullptr), | 
| 71 |       currentLevel(0), score(0) | 
| 72 | { | 
| 73 | } | 
| 74 |  | 
| 75 | PlayState::~PlayState() | 
| 76 | { | 
| 77 |     delete machine; | 
| 78 | } | 
| 79 |  | 
| 80 | void PlayState::onEntry(QEvent *) | 
| 81 | { | 
| 82 |     //We are now playing? | 
| 83 |     if (machine) { | 
| 84 |         machine->stop(); | 
| 85 |         //we hide the information | 
| 86 |         scene->textInformationItem->hide(); | 
| 87 |         scene->clearScene(); | 
| 88 |         currentLevel = 0; | 
| 89 |         score = 0; | 
| 90 |         delete machine; | 
| 91 |     } | 
| 92 |  | 
| 93 |     machine = new QStateMachine; | 
| 94 |  | 
| 95 |     //This state is when player is playing | 
| 96 |     LevelState *levelState = new LevelState(scene, this, machine); | 
| 97 |  | 
| 98 |     //This state is when the player is actually playing but the game is not paused | 
| 99 |     QState *playingState = new QState(levelState); | 
| 100 |     levelState->setInitialState(playingState); | 
| 101 |  | 
| 102 |     //This state is when the game is paused | 
| 103 |     PauseState *pauseState = new PauseState(scene, levelState); | 
| 104 |  | 
| 105 |     //We have one view, it receive the key press event | 
| 106 |     QKeyEventTransition *pressPplay = new QKeyEventTransition(scene->views().at(i: 0), QEvent::KeyPress, Qt::Key_P); | 
| 107 |     pressPplay->setTargetState(pauseState); | 
| 108 |     QKeyEventTransition *pressPpause = new QKeyEventTransition(scene->views().at(i: 0), QEvent::KeyPress, Qt::Key_P); | 
| 109 |     pressPpause->setTargetState(playingState); | 
| 110 |  | 
| 111 |     //Pause "P" is triggered, the player pause the game | 
| 112 |     playingState->addTransition(transition: pressPplay); | 
| 113 |  | 
| 114 |     //To get back playing when the game has been paused | 
| 115 |     pauseState->addTransition(transition: pressPpause); | 
| 116 |  | 
| 117 |     //This state is when player have lost | 
| 118 |     LostState *lostState = new LostState(scene, this, machine); | 
| 119 |  | 
| 120 |     //This state is when player have won | 
| 121 |     WinState *winState = new WinState(scene, this, machine); | 
| 122 |  | 
| 123 |     //The boat has been destroyed then the game is finished | 
| 124 |     levelState->addTransition(obj: scene->boat, signal: &Boat::boatExecutionFinished,target: lostState); | 
| 125 |  | 
| 126 |     //This transition check if we won or not | 
| 127 |     WinTransition *winTransition = new WinTransition(scene, this, winState); | 
| 128 |  | 
| 129 |     //The boat has been destroyed then the game is finished | 
| 130 |     levelState->addTransition(transition: winTransition); | 
| 131 |  | 
| 132 |     //This state is an animation when the score changed | 
| 133 |     UpdateScoreState *scoreState = new UpdateScoreState(levelState); | 
| 134 |  | 
| 135 |     //This transition update the score when a submarine die | 
| 136 |     UpdateScoreTransition *scoreTransition = new UpdateScoreTransition(scene, this, levelState); | 
| 137 |     scoreTransition->setTargetState(scoreState); | 
| 138 |  | 
| 139 |     //The boat has been destroyed then the game is finished | 
| 140 |     playingState->addTransition(transition: scoreTransition); | 
| 141 |  | 
| 142 |     //We go back to play state | 
| 143 |     scoreState->addTransition(target: playingState); | 
| 144 |  | 
| 145 |     //We start playing!!! | 
| 146 |     machine->setInitialState(levelState); | 
| 147 |  | 
| 148 |     //Final state | 
| 149 |     QFinalState *finalState = new QFinalState(machine); | 
| 150 |  | 
| 151 |     //This transition is triggered when the player press space after completing a level | 
| 152 |     CustomSpaceTransition *spaceTransition = new CustomSpaceTransition(scene->views().at(i: 0), this, QEvent::KeyPress, Qt::Key_Space); | 
| 153 |     spaceTransition->setTargetState(levelState); | 
| 154 |     winState->addTransition(transition: spaceTransition); | 
| 155 |  | 
| 156 |     //We lost we should reach the final state | 
| 157 |     lostState->addTransition(obj: lostState, signal: &QState::finished, target: finalState); | 
| 158 |  | 
| 159 |     machine->start(); | 
| 160 | } | 
| 161 |  | 
| 162 | LevelState::LevelState(GraphicsScene *scene, PlayState *game, QState *parent) : QState(parent), scene(scene), game(game) | 
| 163 | { | 
| 164 | } | 
| 165 | void LevelState::onEntry(QEvent *) | 
| 166 | { | 
| 167 |     initializeLevel(); | 
| 168 | } | 
| 169 |  | 
| 170 | void LevelState::initializeLevel() | 
| 171 | { | 
| 172 |     //we re-init the boat | 
| 173 |     scene->boat->setPos(ax: scene->width()/2, ay: scene->sealLevel() - scene->boat->size().height()); | 
| 174 |     scene->boat->setCurrentSpeed(0); | 
| 175 |     scene->boat->setCurrentDirection(Boat::None); | 
| 176 |     scene->boat->setBombsLaunched(0); | 
| 177 |     scene->boat->show(); | 
| 178 |     scene->setFocusItem(item: scene->boat, focusReason: Qt::OtherFocusReason); | 
| 179 |     scene->boat->run(); | 
| 180 |  | 
| 181 |     scene->progressItem->setScore(game->score); | 
| 182 |     scene->progressItem->setLevel(game->currentLevel + 1); | 
| 183 |  | 
| 184 |     const GraphicsScene::LevelDescription currentLevelDescription = scene->levelsData.value(akey: game->currentLevel); | 
| 185 |     for (const QPair<int,int> &subContent : currentLevelDescription.submarines) { | 
| 186 |  | 
| 187 |         GraphicsScene::SubmarineDescription submarineDesc = scene->submarinesData.at(i: subContent.first); | 
| 188 |  | 
| 189 |         for (int j = 0; j < subContent.second; ++j ) { | 
| 190 |             SubMarine *sub = new SubMarine(submarineDesc.type, submarineDesc.name, submarineDesc.points); | 
| 191 |             scene->addItem(submarine: sub); | 
| 192 |             int random = QRandomGenerator::global()->bounded(highest: 15) + 1; | 
| 193 |             qreal x = random == 13 || random == 5 ? 0 : scene->width() - sub->size().width(); | 
| 194 |             qreal y = scene->height() -(QRandomGenerator::global()->bounded(highest: 150) + 1) - sub->size().height(); | 
| 195 |             sub->setPos(ax: x,ay: y); | 
| 196 |             sub->setCurrentDirection(x == 0 ? SubMarine::Right : SubMarine::Left); | 
| 197 |             sub->setCurrentSpeed(QRandomGenerator::global()->bounded(highest: 3) + 1); | 
| 198 |         } | 
| 199 |     } | 
| 200 | } | 
| 201 |  | 
| 202 | /** Pause State */ | 
| 203 | PauseState::PauseState(GraphicsScene *scene, QState *parent) : QState(parent), scene(scene) | 
| 204 | { | 
| 205 | } | 
| 206 |  | 
| 207 | void PauseState::onEntry(QEvent *) | 
| 208 | { | 
| 209 |     AnimationManager::self()->pauseAll(); | 
| 210 |     scene->boat->setEnabled(false); | 
| 211 | } | 
| 212 | void PauseState::onExit(QEvent *) | 
| 213 | { | 
| 214 |     AnimationManager::self()->resumeAll(); | 
| 215 |     scene->boat->setEnabled(true); | 
| 216 |     scene->boat->setFocus(); | 
| 217 | } | 
| 218 |  | 
| 219 | /** Lost State */ | 
| 220 | LostState::LostState(GraphicsScene *scene, PlayState *game, QState *parent) : QState(parent), scene(scene), game(game) | 
| 221 | { | 
| 222 | } | 
| 223 |  | 
| 224 | void LostState::onEntry(QEvent *) | 
| 225 | { | 
| 226 |     //The message to display | 
| 227 |     QString message = QString("You lose on level %1. Your score is %2." ).arg(a: game->currentLevel+1).arg(a: game->score); | 
| 228 |  | 
| 229 |     //We set the level back to 0 | 
| 230 |     game->currentLevel = 0; | 
| 231 |  | 
| 232 |     //We set the score back to 0 | 
| 233 |     game->score = 0; | 
| 234 |  | 
| 235 |     //We clear the scene | 
| 236 |     scene->clearScene(); | 
| 237 |  | 
| 238 |     //We inform the player | 
| 239 |     scene->textInformationItem->setMessage(message); | 
| 240 |     scene->textInformationItem->show(); | 
| 241 | } | 
| 242 |  | 
| 243 | void LostState::onExit(QEvent *) | 
| 244 | { | 
| 245 |     //we hide the information | 
| 246 |     scene->textInformationItem->hide(); | 
| 247 | } | 
| 248 |  | 
| 249 | /** Win State */ | 
| 250 | WinState::WinState(GraphicsScene *scene, PlayState *game, QState *parent) : QState(parent), scene(scene), game(game) | 
| 251 | { | 
| 252 | } | 
| 253 |  | 
| 254 | void WinState::onEntry(QEvent *) | 
| 255 | { | 
| 256 |     //We clear the scene | 
| 257 |     scene->clearScene(); | 
| 258 |  | 
| 259 |     QString message; | 
| 260 |     if (scene->levelsData.size() - 1 != game->currentLevel) { | 
| 261 |         message = QString("You win the level %1. Your score is %2.\nPress Space to continue." ).arg(a: game->currentLevel+1).arg(a: game->score); | 
| 262 |         //We increment the level number | 
| 263 |         game->currentLevel++; | 
| 264 |     } else { | 
| 265 |         message = QString("You finish the game on level %1. Your score is %2." ).arg(a: game->currentLevel+1).arg(a: game->score); | 
| 266 |         //We set the level back to 0 | 
| 267 |         game->currentLevel = 0; | 
| 268 |         //We set the score back to 0 | 
| 269 |         game->score = 0; | 
| 270 |     } | 
| 271 |  | 
| 272 |     //We inform the player | 
| 273 |     scene->textInformationItem->setMessage(message); | 
| 274 |     scene->textInformationItem->show(); | 
| 275 | } | 
| 276 |  | 
| 277 | void WinState::onExit(QEvent *) | 
| 278 | { | 
| 279 |     //we hide the information | 
| 280 |     scene->textInformationItem->hide(); | 
| 281 | } | 
| 282 |  | 
| 283 | /** UpdateScore State */ | 
| 284 | UpdateScoreState::UpdateScoreState(QState *parent) : QState(parent) | 
| 285 | { | 
| 286 | } | 
| 287 |  | 
| 288 | /** Win transition */ | 
| 289 | UpdateScoreTransition::UpdateScoreTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) | 
| 290 |     : QSignalTransition(scene, &GraphicsScene::subMarineDestroyed), | 
| 291 |       game(game), scene(scene) | 
| 292 | { | 
| 293 |     setTargetState(target); | 
| 294 | } | 
| 295 |  | 
| 296 | bool UpdateScoreTransition::eventTest(QEvent *event) | 
| 297 | { | 
| 298 |     if (!QSignalTransition::eventTest(event)) | 
| 299 |         return false; | 
| 300 |     QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(event); | 
| 301 |     game->score += se->arguments().at(i: 0).toInt(); | 
| 302 |     scene->progressItem->setScore(game->score); | 
| 303 |     return true; | 
| 304 | } | 
| 305 |  | 
| 306 | /** Win transition */ | 
| 307 | WinTransition::WinTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) | 
| 308 |     : QSignalTransition(scene, &GraphicsScene::allSubMarineDestroyed), | 
| 309 |       game(game), scene(scene) | 
| 310 | { | 
| 311 |     setTargetState(target); | 
| 312 | } | 
| 313 |  | 
| 314 | bool WinTransition::eventTest(QEvent *event) | 
| 315 | { | 
| 316 |     if (!QSignalTransition::eventTest(event)) | 
| 317 |         return false; | 
| 318 |     QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(event); | 
| 319 |     game->score += se->arguments().at(i: 0).toInt(); | 
| 320 |     scene->progressItem->setScore(game->score); | 
| 321 |     return true; | 
| 322 | } | 
| 323 |  | 
| 324 | /** Space transition */ | 
| 325 | CustomSpaceTransition::CustomSpaceTransition(QWidget *widget, PlayState *game, QEvent::Type type, int key) | 
| 326 |     : QKeyEventTransition(widget, type, key), game(game) | 
| 327 | { | 
| 328 | } | 
| 329 |  | 
| 330 | bool CustomSpaceTransition::eventTest(QEvent *event) | 
| 331 | { | 
| 332 |     if (!QKeyEventTransition::eventTest(event)) | 
| 333 |         return false; | 
| 334 |     return (game->currentLevel != 0); | 
| 335 | } | 
| 336 |  |