nsnake
Classic snake game for the terminal
Loading...
Searching...
No Matches
Game.cpp
1#include <Game/Game.hpp>
2#include <Config/Globals.hpp>
3#include <Misc/Utils.hpp>
4#include <Interface/LayoutGame.hpp>
5#include <Flow/InputManager.hpp>
6#include <Game/BoardParser.hpp>
7
8#include <stdlib.h>
9
10// Options of the Pause Menu
11enum NamesToEasilyIdentifyTheMenuItemsInsteadOfRawNumbers
12{
13 RESUME, RESTART, QUIT_MENU, QUIT_GAME
14};
15
16Game::Game():
17 scores(NULL),
18 currentScore(NULL),
19 layout(NULL),
20 gameOver(false),
21 isPaused(false),
22 showPauseMenu(false),
23 showHelp(false),
24 pauseMenu(NULL),
25 player(NULL),
26 board(NULL),
27 fruits(NULL)
28{ }
29Game::~Game()
30{
31 SAFE_DELETE(this->layout);
32 SAFE_DELETE(this->scores);
33 SAFE_DELETE(this->currentScore);
34 SAFE_DELETE(this->pauseMenu);
35 SAFE_DELETE(this->player);
36 SAFE_DELETE(this->board);
37 SAFE_DELETE(this->fruits);
38}
39void Game::start(std::string levelName)
40{
41 // Cleaning things from the previous game (if any)
42 SAFE_DELETE(this->layout);
43 SAFE_DELETE(this->scores);
44 SAFE_DELETE(this->currentScore);
45 SAFE_DELETE(this->pauseMenu);
46 SAFE_DELETE(this->player);
47 SAFE_DELETE(this->board);
48 SAFE_DELETE(this->fruits);
49
50 this->userAskedToQuit = false;
51 this->userAskedToGoToMenu = false;
52 this->gameOver = false;
53 this->isPaused = false;
54
55 this->scores = new ScoreFile(levelName);
56 // will load the scores on `GameStateGame`
57
58 this->currentScore = new ScoreEntry();
59 this->currentScore->level = levelName;
60 this->currentScore->speed = Globals::Game::starting_speed;
61 this->currentScore->fruits = Globals::Game::fruits_at_once;
62 this->currentScore->random_walls = Globals::Game::random_walls;
63 this->currentScore->teleport = Globals::Game::teleport;
64 this->currentScore->board_size = Globals::Game::board_size;
65 this->currentScore->board_scroll_delay = Globals::Game::board_scroll_delay;
66 this->currentScore->board_scroll_left = Globals::Game::board_scroll_left;
67 this->currentScore->board_scroll_right = Globals::Game::board_scroll_right;
68 this->currentScore->board_scroll_up = Globals::Game::board_scroll_up;
69 this->currentScore->board_scroll_down = Globals::Game::board_scroll_down;
70
71 // Defaults to large
72 int boardw = Board::large_width;
73 int boardh = Board::large_height;
74
75 if (Globals::Game::board_size == Globals::Game::SMALL)
76 {
77 boardw = Board::small_width;
78 boardh = Board::small_height;
79 }
80 else if (Globals::Game::board_size == Globals::Game::MEDIUM)
81 {
82 boardw = Board::medium_width;
83 boardh = Board::medium_height;
84 }
85
86 if (! levelName.empty())
87 this->board = BoardParser::load(levelName);
88
89 else
90 {
91 // If no level name is specified, silently
92 // fall back to a default one.
93
94 this->board = new Board(boardw,
95 boardh,
96 ((Globals::Game::teleport) ?
97 Board::TELEPORT :
98 Board::SOLID));
99 }
100
101 // the player!
102 this->player = new Player(this->board->getStartX(),
103 this->board->getStartY());
104
105 if (Globals::Game::random_walls)
106 this->board->randomlyFillExceptBy(this->player->getX(),
107 this->player->getY());
108
109 // fruits beibeh
110 this->fruits = new FruitManager(Globals::Game::fruits_at_once);
111 this->fruits->update(this->player, this->board);
112
113 // Finally, the interface
114 //
115 // NOTE: It depends on the `currentScore` level name!
116 // Do not initialize it before!
117 this->layout = new LayoutGame(this, 80, 24);
118
119 // Creating the menu and adding each item
120 this->pauseMenu = new Menu(1,
121 1,
122 this->layout->pause->getW() - 2,
123 this->layout->pause->getH() - 2);
124
125 MenuItem* item;
126
127 item = new MenuItem("Resume", RESUME);
128 this->pauseMenu->add(item);
129
130 item = new MenuItem("Restart", RESTART);
131 this->pauseMenu->add(item);
132
133 this->pauseMenu->addBlank();
134
135 item = new MenuItem("Quit to Main Menu", QUIT_MENU);
136 this->pauseMenu->add(item);
137
138 item = new MenuItem("Quit Game", QUIT_GAME);
139 this->pauseMenu->add(item);
140
141 // Starting timers
142 this->timerSnake.start();
143 this->timerBoard.start();
144 this->timer.start();
145}
146void Game::handleInput()
147{
148 if (InputManager::noKeyPressed())
149 return;
150
151 // The only two absolute inputs are to quit and pause.
152 // Others depend if the game is paused or not.
153 if (InputManager::isPressed("quit"))
154 {
155 this->userAskedToQuit = true;
156 }
157 else if (InputManager::isPressed("pause"))
158 {
159 (this->isPaused) ?
160 this->pause(false) :
161 this->pause(true);
162
163 return;
164 }
165 else if (InputManager::isPressed('\n') ||
166 InputManager::isPressed(KEY_ENTER))
167 {
168 if (! this->isPaused)
169 {
170 this->pause(true);
171 return;
172 // This needs to be here otherwise
173 // ENTER goes to the menu and immediately
174 // unpauses the game.
175 }
176 }
177 else if (InputManager::isPressed("help"))
178 {
179 // Toggling Pause and Help window
180 if (this->isPaused)
181 {
182 this->showHelp = false;
183 this->timer.unpause();
184 this->timerSnake.unpause();
185 this->timerBoard.unpause();
186 }
187 else
188 {
189 this->showHelp = true;
190 this->timer.pause();
191 this->timerSnake.pause();
192 this->timerBoard.pause();
193 }
194 }
195
196 // Other keys are not used when paused.
197 if (this->isPaused || this->showHelp)
198 {
199 this->pauseMenu->handleInput();
200 return;
201 }
202
203 if (InputManager::isPressed("left"))
204 {
205 this->player->move(Player::LEFT);
206 }
207 else if (InputManager::isPressed("right"))
208 {
209 this->player->move(Player::RIGHT);
210 }
211 else if (InputManager::isPressed("up"))
212 {
213 this->player->move(Player::UP);
214 }
215 else if (InputManager::isPressed("down"))
216 {
217 this->player->move(Player::DOWN);
218 }
219}
220void Game::update()
221{
222 if (this->gameOver)
223 return;
224
225 // If we're paused, only handle the menu.
226 if (this->isPaused)
227 {
228 if (this->pauseMenu->willQuit())
229 {
230 int option = this->pauseMenu->currentID();
231
232 switch(option)
233 {
234 case RESUME:
235 this->pause(false);
236 break;
237
238 case RESTART:
239 this->start(Globals::Game::current_level);
240 return;
241
242 case QUIT_MENU:
243 this->userAskedToGoToMenu = true;
244 break;
245
246 case QUIT_GAME:
247 this->userAskedToQuit = true;
248 break;
249 }
250 this->pauseMenu->reset();
251 }
252 return;
253 }
254
255 // Forcing Snake to move if enough time has passed
256 // (time based on current level)
257 this->timerSnake.pause();
258 int delta = this->getDelay(this->currentScore->speed);
259
260 if (this->timerSnake.delta_ms() >= delta)
261 {
262 // Checking if on the previous frame
263 // the Snake died.
264 if (! this->player->isAlive())
265 {
266 this->gameOver = true;
267
268 // Check the return value and warns the player
269 // if he just beat the high score
270 this->scores->handle(this->currentScore);
271 }
272 else
273 {
274 // Actually move the player
275 this->player->update(this->board);
276
277 while (this->fruits->eatenFruit(this->player))
278 {
279 this->player->increase();
280
281 // Score formula is kinda random and
282 // scattered all over this file.
283 // TODO: Center it all on the Score class.
284 this->currentScore->points += this->currentScore->speed * 2;
285 }
286
287 this->fruits->update(this->player, this->board);
288 }
289 this->timerSnake.start();
290 }
291 else
292 this->timerSnake.unpause();
293
294 // Hey, can we scroll the Board?
295 // If yes, on which direction should we do it?
296 this->timerBoard.pause();
297 delta = Globals::Game::board_scroll_delay;
298
299 if (this->timerBoard.delta_ms() >= delta)
300 {
301 if (Globals::Game::board_scroll_up) this->board->scrollUp();
302 if (Globals::Game::board_scroll_down) this->board->scrollDown();
303 if (Globals::Game::board_scroll_left) this->board->scrollLeft();
304 if (Globals::Game::board_scroll_right) this->board->scrollRight();
305
306 this->timerBoard.start();
307 }
308 else
309 this->timerBoard.unpause();
310}
311void Game::draw()
312{
313 this->layout->draw(this->pauseMenu);
314}
315bool Game::isOver()
316{
317 return (this->gameOver);
318}
320{
321 return this->userAskedToQuit;
322}
324{
325 return this->userAskedToGoToMenu;
326}
327int Game::getDelay(int speed)
328{
329 // returning delay in milliseconds
330 if (speed < 1) return 800;
331
332 switch (speed)
333 {
334 case 1: return 800;
335 case 2: return 600;
336 case 3: return 500;
337 case 4: return 300;
338 case 5: return 200;
339 case 6: return 150;
340 case 7: return 125;
341 case 8: return 100;
342 case 9: return 80;
343 case 10: return 50;
344 }
345 return 50;
346}
347void Game::pause(bool option)
348{
349 if (option)
350 {
351 if (this->isPaused)
352 return;
353
354 this->isPaused = true;
355 this->showPauseMenu = true;
356 this->timer.pause();
357 this->timerSnake.pause();
358 }
359 else
360 {
361 if (! this->isPaused)
362 return;
363
364 this->isPaused = false;
365 this->showPauseMenu = false;
366 this->timer.unpause();
367 this->timerSnake.unpause();
368 }
369}
370
static Board * load(std::string filename)
Loads and parses level with name.
A level where the snake runs and eats fruits.
Definition Board.hpp:33
void randomlyFillExceptBy(int x, int y)
Places random walls all over the Board except by #x and #y, allowing the Player to move a little bit ...
Definition Board.cpp:83
Controls how many Fruits are there and how they're spawned.
void update(Player *player, Board *board)
Updates internal fruits, adding them to the #board and making sure it doesn't touch #player.
bool eatenFruit(Player *player)
Tells if the #player has eaten a fruit this frame.
bool gameOver
If the game is over (board is full of blocks).
Definition Game.hpp:72
void start(std::string levelName="")
Starts game, optionally loading a level.
Definition Game.cpp:39
int getDelay(int speed)
Returns how much time (millisseconds) we need to wait for a specific #speed.
Definition Game.cpp:327
ScoreEntry * currentScore
Current score for this level.
Definition Game.hpp:66
Menu * pauseMenu
Menu that's shown only when the user presses Pause.
Definition Game.hpp:101
bool isPaused
If the game is paused.
Definition Game.hpp:90
bool showPauseMenu
If it's showing the pause menu.
Definition Game.hpp:94
bool showHelp
If it's showing the help screen.
Definition Game.hpp:98
ScoreFile * scores
All the current level's score.
Definition Game.hpp:59
bool willQuit()
If we'll quit the game right away.
Definition Game.cpp:319
Timer timerSnake
Timer that tells when to move the player, based on the current speed).
Definition Game.hpp:79
bool willReturnToMenu()
If we'll return to the main menu.
Definition Game.cpp:323
Timer timerBoard
Timer that tells when to scroll the board, if this game setting is activated.
Definition Game.hpp:83
Window * pause
Contains the pause menu.
List of selectable items.
Definition Menu.hpp:29
bool willQuit()
Tells if the user selected an item that quits the menu.
Definition Menu.cpp:330
void handleInput()
Makes the menu react to input, as seen on the global InputManager.
Definition Menu.cpp:183
int currentID()
Returns the user-specified id of the selected item.
Definition Menu.cpp:343
void reset()
Makes the menu able to be selected again.
Definition Menu.cpp:424
int getX()
Returns the head's x position.
Definition Player.cpp:25
Stores points the player made on the game.
Definition ScoreFile.hpp:88
bool handle(ScoreEntry *score)
Checks if #score is the highest score and make it so.
void pause()
Temporarily stops the timer.
Definition Timer.cpp:34
void start()
Sets a starting point for the timer.
Definition Timer.cpp:26
void unpause()
Restarts the timer if it was paused.
Definition Timer.cpp:43
suseconds_t delta_ms()
Returns the milisseconds part of the timer's difference.
Definition Timer.cpp:75
Simplest type of item possible, with a label and user-defined id.
Definition MenuItem.hpp:12
A single entry on the high-score file.
Definition ScoreFile.hpp:28
bool random_walls
If random walls were spawned on this level.
Definition ScoreFile.hpp:43
std::string level
On which level the user made this score.
Definition ScoreFile.hpp:37
int fruits
How many fruits at once were allowed on this level.
Definition ScoreFile.hpp:40
bool teleport
If teleport was enabled on this level.
Definition ScoreFile.hpp:46
unsigned int speed
Under which game speed the score was made.
Definition ScoreFile.hpp:33
unsigned int points
How many points the user got.
Definition ScoreFile.hpp:30
Globals::Game::BoardSize board_size
How large was the game board on this score.
Definition ScoreFile.hpp:52