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