| 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 | |