summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-11-02 18:38:53 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-11-02 18:38:53 -0400
commita59fcafb2e81f3cb40ff320b106030e8fed4bd66 (patch)
tree7e7396a9422814365a5f903a53d7391d3e7b22fd
parent45d6e635c880a7fae8711fba366519dd314d9faf (diff)
downloadmazeoflife-a59fcafb2e81f3cb40ff320b106030e8fed4bd66.tar.gz
mazeoflife-a59fcafb2e81f3cb40ff320b106030e8fed4bd66.tar.bz2
mazeoflife-a59fcafb2e81f3cb40ff320b106030e8fed4bd66.zip
Modernized C++ a bit (and removed global highscores) HEAD master
-rw-r--r--CMakeLists.txt8
-rw-r--r--gamestate.cpp953
-rw-r--r--gamestate.h13
-rw-r--r--highscore.cpp14
-rw-r--r--highscore.h21
-rw-r--r--hs_state.cpp276
-rw-r--r--hs_state.h64
-rw-r--r--hslist.cpp969
-rw-r--r--hslist.h124
-rw-r--r--mazeoflife.cpp75
-rw-r--r--mazeoflife.h21
-rw-r--r--sdl.h102
-rw-r--r--state.h12
-rw-r--r--titlestate.cpp182
-rw-r--r--titlestate.h39
-rw-r--r--util.cpp32
-rw-r--r--util.h13
17 files changed, 1235 insertions, 1683 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 136dadc..e44357f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,6 +1,8 @@
1cmake_minimum_required (VERSION 2.6) 1cmake_minimum_required (VERSION 3.1)
2project (mazeoflife) 2project (mazeoflife)
3 3
4#set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -sUSE_SDL=2 -sUSE_SDL_NET=2 -sUSE_SDL_TTF=2")
5
4set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 6set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
5 7
6find_package(SDL2 REQUIRED) 8find_package(SDL2 REQUIRED)
@@ -13,7 +15,7 @@ include_directories(
13 ${SDL2_NET_INCLUDE_DIRS} 15 ${SDL2_NET_INCLUDE_DIRS}
14) 16)
15 17
16add_executable(mazeoflife highscore.cpp hslist.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) 18add_executable(mazeoflife hslist.cpp hs_state.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp)
17set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 11) 19set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 17)
18set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON) 20set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON)
19target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY} ${SDL2_NET_LIBRARIES}) 21target_link_libraries(mazeoflife ${SDL2_LIBRARY} ${SDL2_TTF_LIBRARY} ${SDL2_NET_LIBRARIES})
diff --git a/gamestate.cpp b/gamestate.cpp index e77ff76..953d35b 100644 --- a/gamestate.cpp +++ b/gamestate.cpp
@@ -8,6 +8,8 @@
8#include <fstream> 8#include <fstream>
9#include <iostream> 9#include <iostream>
10#include <list> 10#include <list>
11#include <memory>
12#include <random>
11#include <set> 13#include <set>
12#include <sstream> 14#include <sstream>
13#include <tuple> 15#include <tuple>
@@ -16,61 +18,13 @@
16#include <vector> 18#include <vector>
17 19
18#include "highscore.h" 20#include "highscore.h"
21#include "hs_state.h"
19#include "hslist.h" 22#include "hslist.h"
20#include "mazeoflife.h" 23#include "mazeoflife.h"
21#include "titlestate.h" 24#include "titlestate.h"
22#include "util.h" 25#include "util.h"
23 26
24class GameBoard { 27using board_type = std::bitset<WIDTH * HEIGHT>;
25 public:
26 GameBoard(int level, int playerx, int playery);
27 void tick(int playerx, int playery);
28 void render(SDL_Renderer* renderer, int level) const;
29 bool isObstructed(int x, int y) const;
30 bool operator<(const GameBoard& other) const;
31
32 using coord = std::tuple<int, int>;
33
34 private:
35 void initialize(int level);
36 bool solve(int playerx, int playery) const;
37 std::string dump() const;
38
39 using board_type = std::bitset<WIDTH * HEIGHT>;
40
41 void incrementIfNeighbor(int x, int y, const board_type& temp, int playerx,
42 int playery, int& tick) const;
43
44 bool applyNeighbors(int x, int y, const board_type& temp, int playerx,
45 int playery) const;
46
47 board_type blocks;
48 board_type updateable;
49 int oldx;
50 int oldy;
51};
52
53class LoadGameState : public State {
54 public:
55 LoadGameState(int level);
56 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
57
58 private:
59 int level;
60};
61
62class PlayGameState : public State {
63 public:
64 PlayGameState(int level, GameBoard* board, int playerx, int playery);
65 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
66
67 private:
68 bool move(int x, int y);
69 int level;
70 GameBoard* board;
71 int playerx;
72 int playery;
73};
74 28
75void setRendererAliveColor(SDL_Renderer* renderer, int level) { 29void setRendererAliveColor(SDL_Renderer* renderer, int level) {
76 switch ((level / 10) % 5) { 30 switch ((level / 10) % 5) {
@@ -112,8 +66,8 @@ void setRendererDeadColor(SDL_Renderer* renderer, int level) {
112 } 66 }
113} 67}
114 68
115void GameBoard::incrementIfNeighbor(int x, int y, const board_type& temp, 69void incrementIfNeighbor(int x, int y, const board_type& temp, int playerx,
116 int playerx, int playery, int& tick) const { 70 int playery, int& tick) {
117 int nx = x; 71 int nx = x;
118 int ny = y; 72 int ny = y;
119 73
@@ -127,545 +81,566 @@ void GameBoard::incrementIfNeighbor(int x, int y, const board_type& temp,
127 } 81 }
128} 82}
129 83
130State* GameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { 84bool applyNeighbors(int x, int y, const board_type& temp, int playerx,
131 return new LoadGameState(0); 85 int playery) {
132} 86 int neighbors = 0;
133
134LoadGameState::LoadGameState(int m_level) { level = m_level; }
135 87
136State* LoadGameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { 88 incrementIfNeighbor(x - 1, y - 1, temp, playerx, playery, neighbors);
137 char* wintitle = new char[50]; 89 incrementIfNeighbor(x - 1, y, temp, playerx, playery, neighbors);
138 sprintf(wintitle, "Maze Of Life - Level %d", level); 90 incrementIfNeighbor(x - 1, y + 1, temp, playerx, playery, neighbors);
139 SDL_SetWindowTitle(window, wintitle); 91 incrementIfNeighbor(x, y - 1, temp, playerx, playery, neighbors);
92 incrementIfNeighbor(x, y + 1, temp, playerx, playery, neighbors);
93 incrementIfNeighbor(x + 1, y - 1, temp, playerx, playery, neighbors);
94 incrementIfNeighbor(x + 1, y, temp, playerx, playery, neighbors);
95 incrementIfNeighbor(x + 1, y + 1, temp, playerx, playery, neighbors);
140 96
141 // Randomly place the player in a corner 97 if (temp[x + y * WIDTH]) {
142 int playerx, playery; 98 return ((neighbors >= 1) && (neighbors <= 4));
143 switch (rand() % 4) { 99 } else {
144 case 0: 100 return (neighbors == 3);
145 playerx = 1;
146 playery = 1;
147 break;
148 case 1:
149 playerx = 1;
150 playery = HEIGHT - 2;
151 break;
152 case 2:
153 playerx = WIDTH - 2;
154 playery = HEIGHT - 2;
155 break;
156 case 3:
157 playerx = WIDTH - 2;
158 playery = 1;
159 break;
160 } 101 }
102}
161 103
162 // Display the level number 104class GameBoard {
163 setRendererDeadColor(renderer, level); 105 public:
164 SDL_RenderClear(renderer); 106 GameBoard(Game& game, int level, int playerx, int playery) {
165 107 for (;;) {
166 TTF_Font* font = loadFont(100); 108 initialize(game, level);
167 SDL_Color fontColor = {0, 0, 0, 0}; 109 updateable_.set();
168 char levelnum[8]; 110 oldx_ = playerx;
169 sprintf(levelnum, "%d", level); 111 oldy_ = playery;
170 SDL_Surface* dispsurf = TTF_RenderText_Solid(font, levelnum, fontColor); 112
171 SDL_Texture* disptext = SDL_CreateTextureFromSurface(renderer, dispsurf); 113 for (int i = 0; i < 50; i++) {
172 SDL_FreeSurface(dispsurf); 114 tick(playerx, playery);
173 115 }
174 SDL_Rect pos;
175 SDL_QueryTexture(disptext, NULL, NULL, &pos.w, &pos.h);
176 pos.x = 240 - (pos.w / 2);
177 pos.y = 240 - (pos.h / 2);
178
179 SDL_RenderCopy(renderer, disptext, NULL, &pos);
180 SDL_RenderPresent(renderer);
181 116
182 // Do 50 gens of Conway 117 if (solve(playerx, playery)) {
183 GameBoard* board = new GameBoard(level, playerx, playery); 118 break;
119 } else {
120 std::cout << "Impossible board: " << playerx << "," << playery << ","
121 << dump() << std::endl;
122 }
123 }
124 }
184 125
185 // Wait a bit 126 void tick(int playerx, int playery) {
186 SDL_Delay(500); 127 board_type temp{blocks_};
128 board_type tempdateable{updateable_};
129 if ((playerx != oldx_) || (playery != oldy_)) {
130 for (int dy = -1; dy <= 1; dy++) {
131 for (int dx = -1; dx <= 1; dx++) {
132 int tdx = oldx_ + dx;
133 int tdy = oldy_ + dy;
134 wrap(tdx, tdy);
135 tempdateable.set(tdx + tdy * WIDTH);
136
137 tdx = playerx + dx;
138 tdy = playery + dy;
139 wrap(tdx, tdy);
140 tempdateable.set(tdx + tdy * WIDTH);
141 }
142 }
143 }
187 144
188 // Start the level 145 oldx_ = playerx;
189 return new PlayGameState(level, board, playerx, playery); 146 oldy_ = playery;
190}
191 147
192PlayGameState::PlayGameState(int m_level, GameBoard* m_board, int m_playerx, 148 updateable_.reset();
193 int m_playery) {
194 level = m_level;
195 board = m_board;
196 playerx = m_playerx;
197 playery = m_playery;
198}
199 149
200State* PlayGameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { 150 for (int y = 0; y < HEIGHT; y++) {
201 SDL_Event e; 151 for (int x = 0; x < WIDTH; x++) {
152 if (((x == 15) && (y == 15)) || (!tempdateable[x + y * WIDTH])) {
153 continue;
154 }
202 155
203 for (;;) { 156 blocks_[x + y * WIDTH] = applyNeighbors(x, y, temp, playerx, playery);
204 // Tick board
205 board->tick(playerx, playery);
206 157
207 // Paint board 158 if (temp[x + y * WIDTH] != blocks_[x + y * WIDTH]) {
208 board->render(renderer, level); 159 for (int dy = -1; dy <= 1; dy++) {
160 for (int dx = -1; dx <= 1; dx++) {
161 int tdx = x + dx;
162 int tdy = y + dy;
163 wrap(tdx, tdy);
164 updateable_.set(tdx + tdy * WIDTH);
165 }
166 }
167 }
168 }
169 }
170 }
209 171
210 // Paint event 172 void render(SDL_Renderer* renderer, int level) const {
211 SDL_Rect block; 173 SDL_Rect block;
212 block.w = 16; 174 block.w = 16;
213 block.h = 16; 175 block.h = 16;
214 block.x = 15 * 16;
215 block.y = 15 * 16;
216 SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
217 SDL_RenderFillRect(renderer, &block);
218
219 // Paint player
220 block.x = playerx * 16;
221 block.y = playery * 16;
222 SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
223 SDL_RenderFillRect(renderer, &block);
224
225 SDL_RenderPresent(renderer);
226
227 while (SDL_PollEvent(&e)) {
228 if (e.type == SDL_QUIT) {
229 return NULL;
230 } else if (e.type == SDL_KEYDOWN) {
231 switch (e.key.keysym.sym) {
232 case SDLK_LEFT:
233 if (move(playerx - 1, playery)) {
234 return new LoadGameState(level + 1);
235 } else {
236 break;
237 }
238 176
239 case SDLK_RIGHT: 177 for (int y = 0; y < HEIGHT; y++) {
240 if (move(playerx + 1, playery)) { 178 for (int x = 0; x < WIDTH; x++) {
241 return new LoadGameState(level + 1); 179 block.x = x * 16;
242 } else { 180 block.y = y * 16;
243 break;
244 }
245 181
246 case SDLK_UP: 182 if (blocks_[x + y * WIDTH]) {
247 if (move(playerx, playery - 1)) { 183 setRendererAliveColor(renderer, level);
248 return new LoadGameState(level + 1); 184 } else {
249 } else { 185 setRendererDeadColor(renderer, level);
250 break; 186 }
251 }
252
253 case SDLK_DOWN:
254 if (move(playerx, playery + 1)) {
255 return new LoadGameState(level + 1);
256 } else {
257 break;
258 }
259
260 case SDLK_ESCAPE:
261 SDL_SetWindowTitle(window, "");
262
263 std::ifstream exists(getDataFile());
264 if (exists) {
265 FILE* hslist = fopen(getDataFile(), "r");
266 int scores;
267 Highscore* h;
268
269 fscanf(hslist, "%d%*c", &scores);
270 187
271 if (scores < 10) { 188 SDL_RenderFillRect(renderer, &block);
272 fclose(hslist); 189 }
190 }
191 }
273 192
274 return new EnterHighscoreState(level); 193 bool isObstructed(int x, int y) const {
275 } else { 194 return blocks_[x + y * WIDTH] || (x == 15 && y == 15);
276 for (int i = 0; i < scores; i++) { 195 }
277 int namelen;
278 char namelens[4];
279 char* name = (char*)calloc(25, sizeof(char));
280 int score;
281 196
282 fscanf(hslist, "%d", &namelen); 197 bool operator<(const GameBoard& other) const {
283 sprintf(namelens, "%%%dc", namelen); 198 for (int i = WIDTH * HEIGHT - 1; i >= 0; i--) {
284 fscanf(hslist, namelens, name); 199 if (blocks_[i] ^ other.blocks_[i]) {
285 fscanf(hslist, "%d%*c", &score); 200 return other.blocks_[i];
201 }
202 }
286 203
287 h = new Highscore(name, score); 204 return false;
288 } 205 }
289 206
290 fclose(hslist); 207 using coord = std::tuple<int, int>;
291 208
292 if (h->getLevel() < level) { 209 private:
293 return new EnterHighscoreState(level); 210 void initialize(Game& game, int level) {
294 } else { 211 for (int y = 0; y < HEIGHT; y++) {
295 return new DisplayAndReturnLocalHighscoreListState(); 212 for (int x = 0; x < WIDTH; x++) {
296 } 213 blocks_[x + y * WIDTH] = false;
297 } 214
298 } else { 215 switch (level / 10 + 1) {
299 return new EnterHighscoreState(level); 216 case 1:
217 if ((x > 13) && (x < 17) && (y > 13) && (y < 17)) {
218 blocks_[x + y * WIDTH] =
219 std::bernoulli_distribution(0.5)(game.rng);
300 } 220 }
221 break;
222 case 2:
223 case 3:
224 if ((x > 12) && (x < 18) && (y > 12) && (y < 18)) {
225 blocks_[x + y * WIDTH] =
226 std::bernoulli_distribution(0.5)(game.rng);
227 }
228 break;
229 case 4:
230 case 5:
231 if ((x > 11) && (x < 19) && (y > 11) && (y < 19)) {
232 blocks_[x + y * WIDTH] =
233 std::bernoulli_distribution(0.5)(game.rng);
234 }
235 break;
236 default:
237 blocks_[x + y * WIDTH] = std::bernoulli_distribution(0.5)(game.rng);
301 } 238 }
302 } 239 }
303 } 240 }
304 241
305 SDL_Delay(5); 242 blocks_[15 + 15 * WIDTH] = false;
306 } 243 }
307}
308 244
309bool PlayGameState::move(int x, int y) { 245 bool solve(int playerx, int playery) const {
310 wrap(x, y); 246 std::deque<std::tuple<GameBoard, coord, int>> search;
247 std::unordered_map<board_type, board_type> done;
311 248
312 // Are we at the event? 249 // Assume that the player will not move while the board is changing, so tick
313 if ((x == 15) && (y == 15)) { 250 // the board until it either stops changing, or it reaches a state that it
314 return true; 251 // has already been in (in the case of alternating systems).
315 } 252 {
253 GameBoard original = *this;
316 254
317 // Can we even go there? 255 std::unordered_set<board_type> pastStates;
318 if (!board->isObstructed(x, y)) { 256 pastStates.insert(original.blocks_);
319 playerx = x;
320 playery = y;
321 }
322
323 return false;
324}
325 257
326GameBoard::GameBoard(int level, int playerx, int playery) { 258 while (original.updateable_.any()) {
327 for (;;) { 259 original.tick(playerx, playery);
328 initialize(level);
329 updateable.set();
330 oldx = playerx;
331 oldy = playery;
332 260
333 for (int i = 0; i < 50; i++) { 261 if (pastStates.count(original.blocks_)) {
334 tick(playerx, playery); 262 break;
335 } 263 }
336
337 if (solve(playerx, playery)) {
338 break;
339 } else {
340 std::cout << "Impossible board: " << playerx << "," << playery << ","
341 << dump() << std::endl;
342 }
343 }
344}
345 264
346void GameBoard::tick(int playerx, int playery) { 265 pastStates.insert(original.blocks_);
347 board_type temp{blocks};
348 board_type tempdateable{updateable};
349 if ((playerx != oldx) || (playery != oldy)) {
350 for (int dy = -1; dy <= 1; dy++) {
351 for (int dx = -1; dx <= 1; dx++) {
352 int tdx = oldx + dx;
353 int tdy = oldy + dy;
354 wrap(tdx, tdy);
355 tempdateable.set(tdx + tdy * WIDTH);
356
357 tdx = playerx + dx;
358 tdy = playery + dy;
359 wrap(tdx, tdy);
360 tempdateable.set(tdx + tdy * WIDTH);
361 } 266 }
267
268 search.emplace_front(std::move(original), coord{playerx, playery}, 0);
362 } 269 }
363 }
364 270
365 oldx = playerx; 271 // Use breadth first search to find a solution.
366 oldy = playery; 272 bool exists = false;
273 while (!search.empty()) {
274 auto cur = std::move(search.front());
275 search.pop_front();
367 276
368 updateable.reset(); 277 GameBoard& cbr = std::get<0>(cur);
278 coord& cpl = std::get<1>(cur);
279 int cns = std::get<2>(cur);
369 280
370 for (int y = 0; y < HEIGHT; y++) { 281 // If it has been over 100 generations, give up.
371 for (int x = 0; x < WIDTH; x++) { 282 if (cns > 100) {
372 if (((x == 15) && (y == 15)) || (!tempdateable[x + y * WIDTH])) {
373 continue; 283 continue;
374 } 284 }
375 285
376 blocks[x + y * WIDTH] = applyNeighbors(x, y, temp, playerx, playery); 286 int cplx = std::get<0>(cpl);
287 int cply = std::get<1>(cpl);
377 288
378 if (temp[x + y * WIDTH] != blocks[x + y * WIDTH]) { 289 // If this section of this board state has already been checked, skip it.
379 for (int dy = -1; dy <= 1; dy++) { 290 if (done.count(cbr.blocks_) &&
380 for (int dx = -1; dx <= 1; dx++) { 291 done.at(cbr.blocks_)[cplx + cply * WIDTH]) {
381 int tdx = x + dx; 292 continue;
382 int tdy = y + dy;
383 wrap(tdx, tdy);
384 updateable.set(tdx + tdy * WIDTH);
385 }
386 }
387 } 293 }
388 }
389 }
390}
391
392bool GameBoard::applyNeighbors(int x, int y, const board_type& temp,
393 int playerx, int playery) const {
394 int neighbors = 0;
395 294
396 incrementIfNeighbor(x - 1, y - 1, temp, playerx, playery, neighbors); 295 // Use a flood fill to find a set of positions accessible to the player
397 incrementIfNeighbor(x - 1, y, temp, playerx, playery, neighbors); 296 // without modifying the board state, as well as a set of positions
398 incrementIfNeighbor(x - 1, y + 1, temp, playerx, playery, neighbors); 297 // adjacent to the flood that /will/ modify the board state.
399 incrementIfNeighbor(x, y - 1, temp, playerx, playery, neighbors); 298 board_type flood;
400 incrementIfNeighbor(x, y + 1, temp, playerx, playery, neighbors); 299 std::deque<coord> front;
401 incrementIfNeighbor(x + 1, y - 1, temp, playerx, playery, neighbors); 300 front.push_front(cpl);
402 incrementIfNeighbor(x + 1, y, temp, playerx, playery, neighbors); 301 flood[cplx + cply * WIDTH] = true;
403 incrementIfNeighbor(x + 1, y + 1, temp, playerx, playery, neighbors); 302
303 std::set<coord> edges;
304
305 while (!front.empty()) {
306 coord frontLoc = std::move(front.front());
307 front.pop_front();
308
309 // Iterate over the positions 4-adjacent to the current one.
310 for (coord& fc : std::list<coord>{
311 {std::get<0>(frontLoc) - 1, std::get<1>(frontLoc)},
312 {std::get<0>(frontLoc) + 1, std::get<1>(frontLoc)},
313 {std::get<0>(frontLoc), std::get<1>(frontLoc) - 1},
314 {std::get<0>(frontLoc), std::get<1>(frontLoc) + 1},
315 }) {
316 wrap(std::get<0>(fc), std::get<1>(fc));
317 int fcx = std::get<0>(fc);
318 int fcy = std::get<1>(fc);
319
320 // If this position is already in the flood, skip it.
321 if (flood[fcx + fcy * WIDTH]) {
322 continue;
323 }
404 324
405 if (temp[x + y * WIDTH]) { 325 // If the player could not move into this position, skip it.
406 return ((neighbors >= 1) && (neighbors <= 4)); 326 if (cbr.isObstructed(fcx, fcy)) {
407 } else { 327 continue;
408 return (neighbors == 3); 328 }
409 }
410}
411 329
412void GameBoard::render(SDL_Renderer* renderer, int level) const { 330 // If this position is adjacent to the event, then the board is
413 SDL_Rect block; 331 // solvable.
414 block.w = 16; 332 if (((fcx == 15) && ((fcy == 14) || (fcy == 16))) ||
415 block.h = 16; 333 ((fcy == 15) && ((fcx == 14) || (fcx == 16)))) {
334 exists = true;
335 break;
336 }
416 337
417 for (int y = 0; y < HEIGHT; y++) { 338 // Check if the player moving would cause any positions 8-adjacent to
418 for (int x = 0; x < WIDTH; x++) { 339 // the start or end positions to change. This is more efficient than
419 block.x = x * 16; 340 // copying the board state and then running tick.
420 block.y = y * 16; 341 bool changed = false;
342 for (int dy = -1; dy <= 1; dy++) {
343 for (int dx = -1; dx <= 1; dx++) {
344 if (dx == 0 && dy == 0) {
345 continue;
346 }
421 347
422 if (blocks[x + y * WIDTH]) { 348 int cpldx = cplx + dx;
423 setRendererAliveColor(renderer, level); 349 int cpldy = cply + dy;
424 } else { 350 wrap(cpldx, cpldy);
425 setRendererDeadColor(renderer, level);
426 }
427 351
428 SDL_RenderFillRect(renderer, &block); 352 if (cbr.isObstructed(cpldx, cpldy) !=
429 } 353 applyNeighbors(cpldx, cpldy, cbr.blocks_, fcx, fcy)) {
430 } 354 changed = true;
431} 355 break;
356 }
432 357
433bool GameBoard::isObstructed(int x, int y) const { 358 int fcxdx = fcx + dx;
434 return blocks[x + y * WIDTH] || (x == 15 && y == 15); 359 int fcydy = fcy + dy;
435} 360 wrap(fcxdx, fcydy);
436 361
437void GameBoard::initialize(int level) { 362 if (cbr.isObstructed(fcxdx, fcydy) !=
438 for (int y = 0; y < HEIGHT; y++) { 363 applyNeighbors(fcxdx, fcydy, cbr.blocks_, fcx, fcy)) {
439 for (int x = 0; x < WIDTH; x++) { 364 changed = true;
440 blocks[x + y * WIDTH] = false; 365 break;
366 }
367 }
441 368
442 switch (level / 10 + 1) { 369 if (changed) {
443 case 1: 370 break;
444 if ((x > 13) && (x < 17) && (y > 13) && (y < 17)) { 371 }
445 blocks[x + y * WIDTH] = rand() % 2;
446 }
447 break;
448 case 2:
449 case 3:
450 if ((x > 12) && (x < 18) && (y > 12) && (y < 18)) {
451 blocks[x + y * WIDTH] = rand() % 2;
452 } 372 }
453 break; 373
454 case 4: 374 // If moving to this position would change the board state, add it to
455 case 5: 375 // the set of edges; otherwise, add it to the flood and the flood
456 if ((x > 11) && (x < 19) && (y > 11) && (y < 19)) { 376 // front.
457 blocks[x + y * WIDTH] = rand() % 2; 377 if (changed) {
378 edges.insert(fc);
379 } else {
380 flood[fcx + fcy * WIDTH] = true;
381 front.push_back(fc);
458 } 382 }
383 }
384
385 if (exists) {
459 break; 386 break;
460 default: 387 }
461 blocks[x + y * WIDTH] = rand() % 2;
462 } 388 }
463 }
464 }
465 389
466 blocks[15 + 15 * WIDTH] = false; 390 if (exists) {
467} 391 break;
392 }
468 393
469bool GameBoard::operator<(const GameBoard& other) const { 394 // Add the flood to the set of checked positions for this board state.
470 for (int i = WIDTH * HEIGHT - 1; i >= 0; i--) { 395 done[cbr.blocks_] |= flood;
471 if (blocks[i] ^ other.blocks[i]) {
472 return other.blocks[i];
473 }
474 }
475 396
476 return false; 397 // Add the edges to the search queue.
477} 398 for (const coord& newLoc : edges) {
399 GameBoard nextState1 = cbr;
400 nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc));
478 401
479bool GameBoard::solve(int playerx, int playery) const { 402 // Assume that the player will not move while the board is changing, so
480 std::deque<std::tuple<GameBoard, coord, int>> search; 403 // tick the board until it either stops changing, or it reaches a state
481 std::unordered_map<board_type, board_type> done; 404 // that it has already been in (in the case of alternating systems).
405 std::unordered_set<board_type> pastStates;
406 pastStates.insert(nextState1.blocks_);
482 407
483 // Assume that the player will not move while the board is changing, so tick 408 while (nextState1.updateable_.any()) {
484 // the board until it either stops changing, or it reaches a state that it has 409 nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc));
485 // already been in (in the case of alternating systems).
486 {
487 GameBoard original = *this;
488 410
489 std::unordered_set<board_type> pastStates; 411 if (pastStates.count(nextState1.blocks_)) {
490 pastStates.insert(original.blocks); 412 break;
413 }
491 414
492 while (original.updateable.any()) { 415 pastStates.insert(nextState1.blocks_);
493 original.tick(playerx, playery); 416 }
494 417
495 if (pastStates.count(original.blocks)) { 418 if (!done.count(nextState1.blocks_) ||
496 break; 419 !done.at(nextState1.blocks_)[std::get<0>(newLoc) +
420 std::get<1>(newLoc) * WIDTH]) {
421 search.emplace_back(std::move(nextState1), newLoc, cns + 1);
422 }
497 } 423 }
424 }
498 425
499 pastStates.insert(original.blocks); 426 return exists;
427 }
428
429 std::string dump() const {
430 std::stringstream output;
431 output << std::hex;
432 for (int i = 0; i < WIDTH * HEIGHT / 4; i++) {
433 int chunk = (8 * blocks_[i * 4]) + (4 * blocks_[i * 4 + 1]) +
434 (2 * blocks_[i * 4 + 2]) + blocks_[i * 4 + 3];
435 output << chunk;
500 } 436 }
501 437
502 search.emplace_front(std::move(original), coord{playerx, playery}, 0); 438 return output.str();
503 } 439 }
504 440
505 // Use breadth first search to find a solution. 441 board_type blocks_;
506 bool exists = false; 442 board_type updateable_;
507 while (!search.empty()) { 443 int oldx_;
508 auto cur = std::move(search.front()); 444 int oldy_;
509 search.pop_front(); 445};
510 446
511 GameBoard& cbr = std::get<0>(cur); 447std::unique_ptr<State> startNewLevel(int level,
512 coord& cpl = std::get<1>(cur); 448 std::unique_ptr<GameBoard> board,
513 int cns = std::get<2>(cur); 449 int playerx, int playery);
514 450
515 // If it has been over 100 generations, give up. 451class LoadGameState : public State {
516 if (cns > 100) { 452 public:
517 continue; 453 LoadGameState(int level) : level_(level) {}
454
455 std::unique_ptr<State> operator()(Game& game) {
456 std::ostringstream wintitle;
457 wintitle << "Maze Of Life - Level " << level_;
458 SDL_SetWindowTitle(game.window.get(), wintitle.str().c_str());
459
460 // Randomly place the player in a corner
461 int playerx, playery;
462 switch (std::uniform_int_distribution(0, 3)(game.rng)) {
463 case 0: {
464 playerx = 1;
465 playery = 1;
466 break;
467 }
468 case 1: {
469 playerx = 1;
470 playery = HEIGHT - 2;
471 break;
472 }
473 case 2: {
474 playerx = WIDTH - 2;
475 playery = HEIGHT - 2;
476 break;
477 }
478 case 3: {
479 playerx = WIDTH - 2;
480 playery = 1;
481 break;
482 }
518 } 483 }
519 484
520 int cplx = std::get<0>(cpl); 485 // Display the level number
521 int cply = std::get<1>(cpl); 486 setRendererDeadColor(game.renderer.get(), level_);
487 SDL_RenderClear(game.renderer.get());
522 488
523 // If this section of this board state has already been checked, skip it. 489 font_ptr font = loadFont(100);
524 if (done.count(cbr.blocks) && done.at(cbr.blocks)[cplx + cply * WIDTH]) { 490 SDL_Color fontColor = {0, 0, 0, 0};
525 continue; 491 std::string levelnum = std::to_string(level_);
526 } 492 surface_ptr dispsurf = surface_ptr(
493 TTF_RenderText_Solid(font.get(), levelnum.c_str(), fontColor));
494 texture_ptr disptext = texture_ptr(
495 SDL_CreateTextureFromSurface(game.renderer.get(), dispsurf.get()));
527 496
528 // Use a flood fill to find a set of positions accessible to the player 497 SDL_Rect pos;
529 // without modifying the board state, as well as a set of positions adjacent 498 SDL_QueryTexture(disptext.get(), NULL, NULL, &pos.w, &pos.h);
530 // to the flood that /will/ modify the board state. 499 pos.x = 240 - (pos.w / 2);
531 board_type flood; 500 pos.y = 240 - (pos.h / 2);
532 std::deque<coord> front;
533 front.push_front(cpl);
534 flood[cplx + cply * WIDTH] = true;
535
536 std::set<coord> edges;
537
538 while (!front.empty()) {
539 coord frontLoc = std::move(front.front());
540 front.pop_front();
541
542 // Iterate over the positions 4-adjacent to the current one.
543 for (coord& fc : std::list<coord>{
544 {std::get<0>(frontLoc) - 1, std::get<1>(frontLoc)},
545 {std::get<0>(frontLoc) + 1, std::get<1>(frontLoc)},
546 {std::get<0>(frontLoc), std::get<1>(frontLoc) - 1},
547 {std::get<0>(frontLoc), std::get<1>(frontLoc) + 1},
548 }) {
549 wrap(std::get<0>(fc), std::get<1>(fc));
550 int fcx = std::get<0>(fc);
551 int fcy = std::get<1>(fc);
552
553 // If this position is already in the flood, skip it.
554 if (flood[fcx + fcy * WIDTH]) {
555 continue;
556 }
557 501
558 // If the player could not move into this position, skip it. 502 SDL_RenderCopy(game.renderer.get(), disptext.get(), NULL, &pos);
559 if (cbr.isObstructed(fcx, fcy)) { 503 SDL_RenderPresent(game.renderer.get());
560 continue;
561 }
562 504
563 // If this position is adjacent to the event, then the board is 505 // Do 50 gens of Conway
564 // solvable. 506 std::unique_ptr<GameBoard> board =
565 if (((fcx == 15) && ((fcy == 14) || (fcy == 16))) || 507 std::make_unique<GameBoard>(game, level_, playerx, playery);
566 ((fcy == 15) && ((fcx == 14) || (fcx == 16)))) {
567 exists = true;
568 break;
569 }
570 508
571 // Check if the player moving would cause any positions 8-adjacent to 509 // Wait a bit
572 // the start or end positions to change. This is more efficient than 510 SDL_Delay(500);
573 // copying the board state and then running tick.
574 bool changed = false;
575 for (int dy = -1; dy <= 1; dy++) {
576 for (int dx = -1; dx <= 1; dx++) {
577 if (dx == 0 && dy == 0) {
578 continue;
579 }
580 511
581 int cpldx = cplx + dx; 512 // Start the level
582 int cpldy = cply + dy; 513 return startNewLevel(level_, std::move(board), playerx, playery);
583 wrap(cpldx, cpldy); 514 }
584 515
585 if (cbr.isObstructed(cpldx, cpldy) != 516 private:
586 applyNeighbors(cpldx, cpldy, cbr.blocks, fcx, fcy)) { 517 int level_;
587 changed = true; 518};
588 break;
589 }
590 519
591 int fcxdx = fcx + dx; 520class PlayGameState : public State {
592 int fcydy = fcy + dy; 521 public:
593 wrap(fcxdx, fcydy); 522 PlayGameState(int level, std::unique_ptr<GameBoard> board, int playerx,
523 int playery)
524 : level_(level),
525 board_(std::move(board)),
526 playerx_(playerx),
527 playery_(playery) {}
594 528
595 if (cbr.isObstructed(fcxdx, fcydy) != 529 std::unique_ptr<State> operator()(Game& game) {
596 applyNeighbors(fcxdx, fcydy, cbr.blocks, fcx, fcy)) { 530 SDL_Event e;
597 changed = true; 531
598 break; 532 // Tick board
599 } 533 board_->tick(playerx_, playery_);
534
535 // Paint board
536 board_->render(game.renderer.get(), level_);
537
538 // Paint event
539 SDL_Rect block;
540 block.w = 16;
541 block.h = 16;
542 block.x = 15 * 16;
543 block.y = 15 * 16;
544 SDL_SetRenderDrawColor(game.renderer.get(), 0, 0, 255, 255);
545 SDL_RenderFillRect(game.renderer.get(), &block);
546
547 // Paint player
548 block.x = playerx_ * 16;
549 block.y = playery_ * 16;
550 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255);
551 SDL_RenderFillRect(game.renderer.get(), &block);
552
553 SDL_RenderPresent(game.renderer.get());
554
555 while (SDL_PollEvent(&e)) {
556 if (e.type == SDL_QUIT) {
557 game.should_quit = true;
558
559 return nullptr;
560 } else if (e.type == SDL_KEYDOWN) {
561 bool trymove = false;
562 int to_x = playerx_;
563 int to_y = playery_;
564
565 switch (e.key.keysym.sym) {
566 case SDLK_LEFT: {
567 trymove = true;
568 to_x--;
569 break;
600 } 570 }
571 case SDLK_RIGHT: {
572 trymove = true;
573 to_x++;
574 break;
575 }
576 case SDLK_UP: {
577 trymove = true;
578 to_y--;
579 break;
580 }
581 case SDLK_DOWN: {
582 trymove = true;
583 to_y++;
584 break;
585 }
586 case SDLK_ESCAPE: {
587 SDL_SetWindowTitle(game.window.get(), "");
588
589 std::unique_ptr<HighscoreList> hslist =
590 HighscoreList::GetLocalHighscores();
591 if (hslist->addHighscore(Highscore("", level_)) <= 10) {
592 return std::make_unique<EnterHighscoreState>(game, level_);
593 } else {
594 return std::make_unique<DisplayAndReturnLocalHighscoreListState>(
595 game);
596 }
601 597
602 if (changed) {
603 break; 598 break;
604 } 599 }
605 } 600 }
606 601
607 // If moving to this position would change the board state, add it to 602 if (trymove && move(to_x, to_y)) {
608 // the set of edges; otherwise, add it to the flood and the flood front. 603 return std::make_unique<LoadGameState>(level_ + 1);
609 if (changed) {
610 edges.insert(fc);
611 } else {
612 flood[fcx + fcy * WIDTH] = true;
613 front.push_back(fc);
614 } 604 }
615 } 605 }
616
617 if (exists) {
618 break;
619 }
620 }
621
622 if (exists) {
623 break;
624 } 606 }
625 607
626 // Add the flood to the set of checked positions for this board state. 608 SDL_Delay(5);
627 done[cbr.blocks] |= flood;
628
629 // Add the edges to the search queue.
630 for (const coord& newLoc : edges) {
631 GameBoard nextState1 = cbr;
632 nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc));
633
634 // Assume that the player will not move while the board is changing, so
635 // tick the board until it either stops changing, or it reaches a state
636 // that it has already been in (in the case of alternating systems).
637 std::unordered_set<board_type> pastStates;
638 pastStates.insert(nextState1.blocks);
639 609
640 while (nextState1.updateable.any()) { 610 return nullptr;
641 nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc)); 611 }
642 612
643 if (pastStates.count(nextState1.blocks)) { 613 private:
644 break; 614 bool move(int x, int y) {
645 } 615 wrap(x, y);
646 616
647 pastStates.insert(nextState1.blocks); 617 // Are we at the event?
648 } 618 if ((x == 15) && (y == 15)) {
619 return true;
620 }
649 621
650 if (!done.count(nextState1.blocks) || 622 // Can we even go there?
651 !done.at(nextState1.blocks)[std::get<0>(newLoc) + 623 if (!board_->isObstructed(x, y)) {
652 std::get<1>(newLoc) * WIDTH]) { 624 playerx_ = x;
653 search.emplace_back(std::move(nextState1), newLoc, cns + 1); 625 playery_ = y;
654 }
655 } 626 }
627
628 return false;
656 } 629 }
657 630
658 return exists; 631 int level_;
659} 632 std::unique_ptr<GameBoard> board_;
633 int playerx_;
634 int playery_;
635};
660 636
661std::string GameBoard::dump() const { 637std::unique_ptr<State> startNewLevel(int level,
662 std::stringstream output; 638 std::unique_ptr<GameBoard> board,
663 output << std::hex; 639 int playerx, int playery) {
664 for (int i = 0; i < WIDTH * HEIGHT / 4; i++) { 640 return std::make_unique<PlayGameState>(level, std::move(board), playerx,
665 int chunk = (8 * blocks[i * 4]) + (4 * blocks[i * 4 + 1]) + 641 playery);
666 (2 * blocks[i * 4 + 2]) + blocks[i * 4 + 3]; 642}
667 output << chunk;
668 }
669 643
670 return output.str(); 644std::unique_ptr<State> GameState::operator()(Game&) {
645 return std::make_unique<LoadGameState>(0);
671} 646}
diff --git a/gamestate.h b/gamestate.h index 32e5d07..9b0b52c 100644 --- a/gamestate.h +++ b/gamestate.h
@@ -1,13 +1,14 @@
1#include <SDL.h>
2
3#include "state.h"
4
5#ifndef GAMESTATE_H 1#ifndef GAMESTATE_H
6#define GAMESTATE_H 2#define GAMESTATE_H
7 3
4#include <memory>
5
6#include "mazeoflife.h"
7#include "state.h"
8
8class GameState : public State { 9class GameState : public State {
9 public: 10 public:
10 State* operator()(SDL_Window* window, SDL_Renderer* renderer); 11 std::unique_ptr<State> operator()(Game& game);
11}; 12};
12 13
13#endif \ No newline at end of file 14#endif
diff --git a/highscore.cpp b/highscore.cpp deleted file mode 100644 index eefe058..0000000 --- a/highscore.cpp +++ /dev/null
@@ -1,14 +0,0 @@
1#include "highscore.h"
2
3Highscore::Highscore(char* name, int level) {
4 this->name = name;
5 this->level = level;
6}
7
8char* Highscore::getName() { return name; }
9
10int Highscore::getLevel() { return level; }
11
12void Highscore::setRank(int rank) { this->rank = rank; }
13
14int Highscore::getRank() { return rank; }
diff --git a/highscore.h b/highscore.h index f7ecacc..a3e531b 100644 --- a/highscore.h +++ b/highscore.h
@@ -1,18 +1,23 @@
1#ifndef HIGHSCORE_H 1#ifndef HIGHSCORE_H
2#define HIGHSCORE_H 2#define HIGHSCORE_H
3 3
4#include <string>
5
4class Highscore { 6class Highscore {
5 public: 7 public:
6 Highscore(char* name, int level); 8 Highscore() = default;
7 char* getName(); 9
8 int getLevel(); 10 Highscore(std::string name, int level) : name_(name), level_(level) {}
9 void setRank(int rank); 11
10 int getRank(); 12 const std::string getName() const { return name_; }
13 int getLevel() const { return level_; }
14 void setRank(int rank) { rank_ = rank; }
15 int getRank() const { return rank_; }
11 16
12 private: 17 private:
13 char* name; 18 std::string name_;
14 int level; 19 int level_;
15 int rank; 20 int rank_;
16}; 21};
17 22
18#endif 23#endif
diff --git a/hs_state.cpp b/hs_state.cpp new file mode 100644 index 0000000..f6aab86 --- /dev/null +++ b/hs_state.cpp
@@ -0,0 +1,276 @@
1#include "hs_state.h"
2
3#include <sstream>
4
5#include "gamestate.h"
6#include "titlestate.h"
7#include "util.h"
8
9DisplayLocalHighscoreListState::DisplayLocalHighscoreListState(Game& game) {
10 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
11
12 std::unique_ptr<HighscoreList> lhl = HighscoreList::GetLocalHighscores();
13
14 surface_ptr list_s = lhl->render();
15 SDL_Color fontColor = {0, 0, 0, 0};
16 font_ptr font = loadFont(40);
17 surface_ptr title = surface_ptr(
18 TTF_RenderText_Blended(font.get(), "Highscore List", fontColor));
19 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
20 SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace);
21
22 surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_rtm.bmp"));
23 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
24 SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace);
25
26 list_ = texture_ptr(
27 SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get()));
28}
29
30std::unique_ptr<State> DisplayLocalHighscoreListState::operator()(Game& game) {
31 SDL_Event e;
32
33 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255);
34 SDL_RenderClear(game.renderer.get());
35 SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL);
36 applyTexture(game.renderer.get(), pointer_.get(), 137, 449);
37 SDL_RenderPresent(game.renderer.get());
38
39 while (SDL_PollEvent(&e)) {
40 if (e.type == SDL_QUIT) {
41 game.should_quit = true;
42 break;
43 } else if (e.type == SDL_KEYDOWN) {
44 if (e.key.keysym.sym == SDLK_RETURN) {
45 return std::make_unique<TitleState>(game);
46 }
47 }
48 }
49
50 return nullptr;
51}
52
53DisplayAndReturnLocalHighscoreListState::
54 DisplayAndReturnLocalHighscoreListState(Game& game) {
55 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
56
57 std::unique_ptr<HighscoreList> lhl = HighscoreList::GetLocalHighscores();
58 surface_ptr list_s = lhl->render();
59 SDL_Color fontColor = {0, 0, 0, 0};
60 font_ptr font = loadFont(40);
61 surface_ptr title = surface_ptr(
62 TTF_RenderText_Blended(font.get(), "Highscore List", fontColor));
63 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
64 SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace);
65
66 surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_paartm.bmp"));
67 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
68 SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace);
69
70 list_ = texture_ptr(
71 SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get()));
72}
73
74std::unique_ptr<State> DisplayAndReturnLocalHighscoreListState::operator()(
75 Game& game) {
76 SDL_Event e;
77
78 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255);
79 SDL_RenderClear(game.renderer.get());
80 SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL);
81 applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 52 : 225,
82 447);
83 SDL_RenderPresent(game.renderer.get());
84
85 while (SDL_PollEvent(&e)) {
86 if (e.type == SDL_QUIT) {
87 game.should_quit = true;
88 break;
89 } else if (e.type == SDL_KEYDOWN) {
90 if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) {
91 selection_--;
92 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) {
93 selection_++;
94 } else if (e.key.keysym.sym == SDLK_RETURN) {
95 switch (selection_) {
96 case 0:
97 return std::make_unique<GameState>();
98 case 1:
99 return std::make_unique<TitleState>(game);
100 }
101 }
102 }
103 }
104
105 return nullptr;
106}
107
108EnterHighscoreState::EnterHighscoreState(Game& game, int level)
109 : level_(level) {
110 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
111
112 // Render highscore list
113 std::unique_ptr<HighscoreList> lhl = HighscoreList::GetLocalHighscores();
114 newpos_ = lhl->addHighscore(Highscore("", level_));
115
116 surface_ptr list_s = lhl->render();
117
118 SDL_Color fontColor = {0, 0, 0, 0};
119 font_ptr bigfont = loadFont(40);
120 surface_ptr title = surface_ptr(
121 TTF_RenderText_Blended(bigfont.get(), "New Highscore!", fontColor));
122 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
123 SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace);
124
125 font_ptr font = loadFont(25);
126 surface_ptr text = surface_ptr(
127 TTF_RenderText_Blended(font.get(), "Enter Your Name", fontColor));
128 SDL_Rect oSpace = {240 - (text->w / 2), 440, text->w, text->h};
129 SDL_BlitSurface(text.get(), NULL, list_s.get(), &oSpace);
130
131 list_ = texture_ptr(
132 SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get()));
133
134 int posw, posh;
135 std::ostringstream posstr;
136 posstr << newpos_ << ":";
137 std::string pos = posstr.str();
138
139 surface_ptr newName_s =
140 surface_ptr(TTF_RenderText_Blended(font.get(), " ", fontColor));
141 TTF_SizeText(bigfont.get(), pos.c_str(), &posw, &posh);
142 rntSpace_.x = posw;
143 rntSpace_.y = newpos_ * 40 + ((posh / 2) - (newName_s->h / 2));
144 rntSpace_.w = newName_s->w;
145 rntSpace_.h = newName_s->h;
146 newName_ = texture_ptr(
147 SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get()));
148}
149
150std::unique_ptr<State> EnterHighscoreState::operator()(Game& game) {
151 SDL_Event e;
152
153 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255);
154 SDL_RenderClear(game.renderer.get());
155
156 SDL_Rect eSpace = {0, newpos_ * 40, 480, 40};
157 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255);
158 SDL_RenderFillRect(game.renderer.get(), &eSpace);
159
160 SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL);
161 SDL_RenderCopy(game.renderer.get(), newName_.get(), NULL, &rntSpace_);
162
163 SDL_RenderPresent(game.renderer.get());
164
165 while (SDL_PollEvent(&e)) {
166 if (e.type == SDL_QUIT) {
167 game.should_quit = true;
168 break;
169 } else if (e.type == SDL_KEYDOWN) {
170 if ((e.key.keysym.sym == SDLK_BACKSPACE) && (lp_ > 0)) {
171 --lp_;
172 hsname_.pop_back();
173
174 SDL_Color fontColor = {0, 0, 0, 0};
175 std::ostringstream name;
176 name << " " << hsname_;
177
178 font_ptr font = loadFont(25);
179 surface_ptr newName_s = surface_ptr(
180 TTF_RenderText_Blended(font.get(), name.str().c_str(), fontColor));
181 rntSpace_.w = newName_s->w;
182 rntSpace_.h = newName_s->h;
183 newName_ = texture_ptr(
184 SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get()));
185 } else if ((e.key.keysym.sym == SDLK_RETURN) && !hsname_.empty()) {
186 std::unique_ptr<HighscoreList> lhl =
187 HighscoreList::GetLocalHighscores();
188 Highscore h2(hsname_, level_);
189 lhl->addHighscore(h2);
190 lhl->writeToFile();
191
192 return std::make_unique<NewHighscoreState>(game, h2);
193 }
194 } else if (e.type == SDL_TEXTINPUT) {
195 if (((*e.text.text & 0xFF80) == 0) &&
196 (*e.text.text >= 32 && *e.text.text < 127) && (lp_ < 25)) {
197 lp_++;
198 hsname_.push_back(*e.text.text & 0x7f);
199
200 SDL_Color fontColor = {0, 0, 0, 0};
201 std::ostringstream name;
202 name << " " << hsname_;
203
204 font_ptr font = loadFont(25);
205 surface_ptr newName_s = surface_ptr(
206 TTF_RenderText_Blended(font.get(), name.str().c_str(), fontColor));
207 rntSpace_.w = newName_s->w;
208 rntSpace_.h = newName_s->h;
209 newName_ = texture_ptr(
210 SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get()));
211 }
212 }
213 }
214
215 return nullptr;
216}
217
218NewHighscoreState::NewHighscoreState(Game& game, Highscore h) : h_(h) {
219 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
220
221 // Render highscore list
222 std::unique_ptr<HighscoreList> lhl = HighscoreList::GetLocalHighscores();
223 surface_ptr list_s = lhl->render();
224
225 SDL_Color fontColor = {0, 0, 0, 0};
226 font_ptr bigfont = loadFont(40);
227 surface_ptr title = surface_ptr(
228 TTF_RenderText_Blended(bigfont.get(), "New Highscore!", fontColor));
229 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
230 SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace);
231
232 surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_paartm.bmp"));
233 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
234 SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace);
235
236 list_ = texture_ptr(
237 SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get()));
238}
239
240std::unique_ptr<State> NewHighscoreState::operator()(Game& game) {
241 SDL_Event e;
242
243 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255);
244 SDL_RenderClear(game.renderer.get());
245
246 SDL_Rect eSpace = {0, h_.getRank() * 40, 480, 40};
247 SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255);
248 SDL_RenderFillRect(game.renderer.get(), &eSpace);
249
250 SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL);
251 applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 52 : 225,
252 447);
253 SDL_RenderPresent(game.renderer.get());
254
255 while (SDL_PollEvent(&e)) {
256 if (e.type == SDL_QUIT) {
257 game.should_quit = true;
258 break;
259 } else if (e.type == SDL_KEYDOWN) {
260 if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) {
261 selection_--;
262 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) {
263 selection_++;
264 } else if (e.key.keysym.sym == SDLK_RETURN) {
265 switch (selection_) {
266 case 0:
267 return std::make_unique<GameState>();
268 case 1:
269 return std::make_unique<TitleState>(game);
270 }
271 }
272 }
273 }
274
275 return nullptr;
276}
diff --git a/hs_state.h b/hs_state.h new file mode 100644 index 0000000..64ecac0 --- /dev/null +++ b/hs_state.h
@@ -0,0 +1,64 @@
1#ifndef HS_STATE_H_C824D037
2#define HS_STATE_H_C824D037
3
4#include <memory>
5
6#include "hslist.h"
7#include "mazeoflife.h"
8#include "sdl.h"
9#include "state.h"
10
11class DisplayLocalHighscoreListState : public State {
12 public:
13 explicit DisplayLocalHighscoreListState(Game& game);
14
15 std::unique_ptr<State> operator()(Game& game);
16
17 private:
18 texture_ptr list_;
19 texture_ptr pointer_;
20};
21
22class DisplayAndReturnLocalHighscoreListState : public State {
23 public:
24 explicit DisplayAndReturnLocalHighscoreListState(Game& game);
25
26 std::unique_ptr<State> operator()(Game& game);
27
28 private:
29 texture_ptr list_;
30 texture_ptr pointer_;
31 int selection_ = 0;
32};
33
34class EnterHighscoreState : public State {
35 public:
36 EnterHighscoreState(Game& game, int level);
37
38 std::unique_ptr<State> operator()(Game& game);
39
40 private:
41 int level_;
42 int lp_ = 0;
43 std::string hsname_;
44 texture_ptr newName_;
45 texture_ptr pointer_;
46 texture_ptr list_;
47 SDL_Rect rntSpace_;
48 int newpos_;
49};
50
51class NewHighscoreState : public State {
52 public:
53 NewHighscoreState(Game& game, Highscore h);
54
55 std::unique_ptr<State> operator()(Game& game);
56
57 private:
58 Highscore h_;
59 texture_ptr pointer_;
60 texture_ptr list_;
61 int selection_ = 0;
62};
63
64#endif /* end of include guard: HS_STATE_H_C824D037 */
diff --git a/hslist.cpp b/hslist.cpp index 5eb9b6c..9ae2aa4 100644 --- a/hslist.cpp +++ b/hslist.cpp
@@ -5,946 +5,123 @@
5 5
6#include <algorithm> 6#include <algorithm>
7#include <fstream> 7#include <fstream>
8#include <memory>
8#include <sstream> 9#include <sstream>
9 10
10#include "gamestate.h" 11#include "gamestate.h"
11#include "titlestate.h" 12#include "titlestate.h"
12#include "util.h" 13#include "util.h"
13 14
14// We want to be able to sort Highscore objects in descending score order 15HighscoreList::HighscoreList(std::vector<Highscore> hslist) : hslist_(hslist) {
15struct hslist_comp { 16 resetRanks();
16 bool operator()(Highscore* lhs, Highscore* rhs) const { 17}
17 return (lhs->getLevel() > rhs->getLevel());
18 }
19} hslist_comp_i;
20 18
21// resetRanks : sets the rank of all Highscore objects in a hslist_t to their 19void HighscoreList::resetRanks() {
22// (one-based) index in the list
23void resetRanks(hslist_t in) {
24 int i = 1; 20 int i = 1;
25 for (hslist_t::iterator it = in.begin(); it != in.end(); ++it, ++i) { 21 for (Highscore& hs : hslist_) {
26 ((Highscore*)*it)->setRank(i); 22 hs.setRank(i++);
27 } 23 }
28} 24}
29 25
30SDL_Surface* HighscoreList::render() { 26surface_ptr HighscoreList::render() {
31 SDL_Surface* tmp = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); 27 surface_ptr tmp =
28 surface_ptr(SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0));
32 Uint32 bgColor = SDL_MapRGB(tmp->format, 255, 255, 255); 29 Uint32 bgColor = SDL_MapRGB(tmp->format, 255, 255, 255);
33 SDL_FillRect(tmp, NULL, bgColor); 30 SDL_FillRect(tmp.get(), NULL, bgColor);
34 SDL_SetColorKey(tmp, SDL_TRUE, bgColor); 31 SDL_SetColorKey(tmp.get(), SDL_TRUE, bgColor);
35 TTF_Font* posFont = loadFont(40); 32 font_ptr posFont = loadFont(40);
36 TTF_Font* dataFont = loadFont(25); 33 font_ptr dataFont = loadFont(25);
37 SDL_Color fontColor = {0, 0, 0, 0}; 34 SDL_Color fontColor = {0, 0, 0, 0};
38 35
39 int i = 0; 36 for (const Highscore& h : hslist_) {
40 for (hslist_t::iterator it = hslist.begin(); it != hslist.end(); ++it, ++i) {
41 Highscore* h = *it;
42
43 int posw, posh; 37 int posw, posh;
44 char pos[4]; // 2 max characters in rank plus the colon at the end, plus 38
45 // terminator 39 std::ostringstream posstr;
46 sprintf(pos, "%d:", h->getRank()); 40 posstr << h.getRank() << ":";
47 TTF_SizeText(posFont, pos, &posw, &posh); 41
48 SDL_Rect posSpace = {0, (i + 1) * 40, posw, posh}; 42 std::string pos = posstr.str();
49 SDL_BlitSurface(TTF_RenderText_Blended(posFont, pos, fontColor), NULL, tmp, 43 TTF_SizeText(posFont.get(), pos.c_str(), &posw, &posh);
50 &posSpace); 44 SDL_Rect posSpace = {0, h.getRank() * 40, posw, posh};
45 SDL_BlitSurface(
46 TTF_RenderText_Blended(posFont.get(), pos.c_str(), fontColor), NULL,
47 tmp.get(), &posSpace);
48
49 std::ostringstream namestr;
50 namestr << " " << h.getName();
51 51
52 int namew, nameh; 52 int namew, nameh;
53 char name[27]; // 25 max characters in username plus the space at the 53 std::string name = namestr.str();
54 // beginning, plus terminator 54 TTF_SizeText(dataFont.get(), name.c_str(), &namew, &nameh);
55 sprintf(name, " %s", h->getName()); 55 SDL_Rect nameSpace = {posw, h.getRank() * 40 + ((posh / 2) - (nameh / 2)),
56 TTF_SizeText(dataFont, name, &namew, &nameh);
57 SDL_Rect nameSpace = {posw, (i + 1) * 40 + ((posh / 2) - (nameh / 2)),
58 namew, nameh}; 56 namew, nameh};
59 SDL_BlitSurface(TTF_RenderText_Blended(dataFont, name, fontColor), NULL, 57 SDL_BlitSurface(
60 tmp, &nameSpace); 58 TTF_RenderText_Blended(dataFont.get(), name.c_str(), fontColor), NULL,
59 tmp.get(), &nameSpace);
61 60
62 int lvlw, lvlh; 61 int lvlw, lvlh;
63 char lvl[11]; // 10 max characters in level (based off the fact that 2^32-1 62 std::string lvl = std::to_string(h.getLevel());
64 // is 10 characters long, and is the highest int), plus 63 TTF_SizeText(dataFont.get(), lvl.c_str(), &lvlw, &lvlh);
65 // terminator 64 SDL_Rect lvlSpace = {
66 sprintf(lvl, "%d", h->getLevel()); 65 480 - lvlw, h.getRank() * 40 + ((posh / 2) - (nameh / 2)), lvlw, lvlh};
67 TTF_SizeText(dataFont, lvl, &lvlw, &lvlh); 66 SDL_BlitSurface(
68 SDL_Rect lvlSpace = {480 - lvlw, (i + 1) * 40 + ((posh / 2) - (nameh / 2)), 67 TTF_RenderText_Blended(dataFont.get(), lvl.c_str(), fontColor), NULL,
69 lvlw, lvlh}; 68 tmp.get(), &lvlSpace);
70 SDL_BlitSurface(TTF_RenderText_Blended(dataFont, lvl, fontColor), NULL, tmp,
71 &lvlSpace);
72 } 69 }
73 70
74 return tmp; 71 return tmp;
75} 72}
76 73
77hslist_t HighscoreList::getLocalHighscores() { 74std::unique_ptr<HighscoreList> HighscoreList::GetLocalHighscores() {
78 hslist_t temp; 75 std::vector<Highscore> hslist;
79 76
80 std::ifstream exists(getDataFile()); 77 std::ifstream hsstream(getDataFile());
81 if (exists) { 78 if (hsstream) {
82 FILE* hslist = fopen(getDataFile(), "r"); 79 int num_scores = 0;
83 int scores; 80 hsstream >> num_scores;
84 fscanf(hslist, "%d%*c", &scores);
85 81
86 for (int i = 0; i < scores; i++) { 82 for (int i = 0; i < num_scores; i++) {
87 int namelen; 83 std::string name;
88 char namelens[4];
89 char* name = (char*)calloc(25, sizeof(char));
90 int score; 84 int score;
91 85
92 fscanf(hslist, "%d", &namelen); 86 hsstream >> name;
93 sprintf(namelens, "%%%dc", namelen); 87 hsstream >> score;
94 fscanf(hslist, namelens, name);
95 fscanf(hslist, "%d%*c", &score);
96
97 Highscore* h = new Highscore(name, score);
98 h->setRank(i + 1);
99 88
100 temp.push_back(h); 89 hslist.emplace_back(name, score);
101 } 90 }
102
103 fclose(hslist);
104 } 91 }
105 92
106 return temp; 93 return std::unique_ptr<HighscoreList>(new HighscoreList(hslist));
107} 94}
108 95
109hslist_t HighscoreList::getGlobalHighscores() { 96int HighscoreList::addHighscore(Highscore h) {
110 IPaddress ipaddress; 97 int i = 1;
111
112 if (SDLNet_ResolveHost(&ipaddress, "other.fourisland.com", 80) == -1) {
113 printf("Could not resolve host \"other.fourisland.com\": %s\n",
114 SDLNet_GetError());
115 throw 1;
116 }
117
118 TCPsocket tcpsock = SDLNet_TCP_Open(&ipaddress);
119 if (!tcpsock) {
120 printf("Could not connect to host \"other.fourisland.com\": %s\n",
121 SDLNet_GetError());
122 throw 2;
123 }
124
125 const char* headers =
126 "GET /mol/hslist.php HTTP/1.1\nHost: other.fourisland.com\nUser-Agent: "
127 "Maze Of Life v3.0\nAccept: text/plain\nKeep-Alive: 300\nConnection: "
128 "keep-alive\n\n";
129 if (SDLNet_TCP_Send(tcpsock, headers, strlen(headers) + 1) <
130 strlen(headers)) {
131 printf("Connection closed by peer: %s\n", SDLNet_GetError());
132 throw 3;
133 }
134
135 std::stringstream download(std::stringstream::in | std::stringstream::out);
136 char hslist[1024];
137 SDLNet_TCP_Recv(tcpsock, hslist, 1024);
138 download << hslist;
139 SDLNet_TCP_Close(tcpsock);
140
141 char temps[256];
142 download.getline(temps, 256);
143 while (strlen(temps) != 1) {
144 download.getline(temps, 256);
145 }
146
147 hslist_t temp;
148 int scores;
149 download.getline(temps, 256);
150 if (sscanf(temps, "%d%*c", &scores) != 1) {
151 printf("Recieved data is of an invalid format: %s\n", temps);
152 throw 4;
153 }
154
155 for (int i = 0; i < scores; i++) {
156 int namelen;
157 char namelens[13];
158 char* name = (char*)calloc(25, sizeof(char));
159 int score;
160 download.getline(temps, 256);
161
162 if (sscanf(temps, "%d", &namelen) != 1) {
163 printf("Recieved data is of an invalid format (1-%d): %s\n", i, temps);
164 throw 4;
165 }
166
167 sprintf(namelens, "%%*d%%%dc", namelen);
168
169 if (sscanf(temps, namelens, name) != 1) {
170 printf("Recieved data is of an invalid format (2-%d): %s\n", i, temps);
171 throw 4;
172 }
173
174 sprintf(namelens, "%%*d%%*%dc%%d", namelen);
175
176 if (sscanf(temps, namelens, &score) != 1) {
177 printf("Recieved data is of an invalid format (3-%d): %s\n", i, temps);
178 throw 4;
179 }
180
181 Highscore* h = new Highscore(name, score);
182 h->setRank(i + 1);
183
184 temp.push_back(h);
185 }
186
187 return temp;
188}
189
190LocalHighscoreList::LocalHighscoreList() {
191 this->hslist = getLocalHighscores();
192}
193
194int LocalHighscoreList::addHighscore(Highscore* h) {
195 hslist.push_back(h);
196 std::sort(hslist.begin(), hslist.end(), hslist_comp_i);
197 resetRanks(hslist);
198
199 if (hslist.size() > 10) {
200 hslist.resize(10);
201 }
202
203 return h->getRank();
204}
205
206void LocalHighscoreList::writeHighscores() {
207 FILE* hsfile = fopen(getDataFile(), "w");
208 fprintf(hsfile, "%d ", (int)this->hslist.size());
209
210 for (hslist_t::iterator it = hslist.begin(); it != this->hslist.end(); it++) {
211 Highscore* h = *it;
212
213 fprintf(hsfile, "%d%s%d ", (int)strlen(h->getName()), h->getName(),
214 h->getLevel());
215 }
216
217 fclose(hsfile);
218}
219
220GlobalHighscoreList::GlobalHighscoreList() {
221 fail = false;
222
223 try {
224 this->hslist = getGlobalHighscores();
225 } catch (int e) {
226 fail = true;
227 }
228}
229
230GlobalHighscoreList::GlobalHighscoreList(Highscore* h) {
231 fail = false;
232
233 try {
234 IPaddress ipaddress;
235
236 if (SDLNet_ResolveHost(&ipaddress, "other.fourisland.com", 80) == -1) {
237 printf("Could not resolve host \"other.fourisland.com\": %s\n",
238 SDLNet_GetError());
239 throw 1;
240 }
241
242 TCPsocket tcpsock = SDLNet_TCP_Open(&ipaddress);
243 if (!tcpsock) {
244 printf("Could not connect to host \"other.fourisland.com\": %s\n",
245 SDLNet_GetError());
246 throw 2;
247 }
248
249 char body[256];
250 sprintf(body, "name=%s&level=%d", h->getName(), h->getLevel());
251 char headers[256];
252 sprintf(
253 headers,
254 "POST /mol/hslist.php?add HTTP/1.1\nHost: "
255 "other.fourisland.com\nUser-Agent: Maze Of Life v2.0\nAccept: "
256 "text/plain\nKeep-Alive: 300\nConnection: keep-alive\nContent-Type: "
257 "application/x-www-form-urlencoded\nContent-Length: %d\n\n%s\n",
258 (int)strlen(body), body);
259 if (SDLNet_TCP_Send(tcpsock, headers, strlen(headers) + 1) <
260 strlen(headers)) {
261 printf("Connection closed by peer: %s\n", SDLNet_GetError());
262 throw 3;
263 }
264
265 std::stringstream download(std::stringstream::in | std::stringstream::out);
266 char hslist[1024];
267 SDLNet_TCP_Recv(tcpsock, hslist, 1024);
268 download << hslist;
269 SDLNet_TCP_Close(tcpsock);
270
271 char temps[256];
272 download.getline(temps, 256);
273 while (strlen(temps) != 1) {
274 download.getline(temps, 256);
275 }
276
277 int rank;
278 download.getline(temps, 256);
279 if (sscanf(temps, "%d%*c", &rank) != 1) {
280 printf("Recieved data is of an invalid format: %s\n", temps);
281 throw 4;
282 }
283
284 this->hslist = getGlobalHighscores();
285
286 if (this->hslist.empty()) {
287 printf(
288 "Global Highscore List cannot be empty after adding a score to "
289 "it.\n");
290 throw 5;
291 }
292
293 if (rank > 10) {
294 h->setRank(rank);
295
296 this->hslist[9] = h;
297 } else {
298 this->hslist.push_back(h);
299 std::sort(this->hslist.begin(), this->hslist.end(), hslist_comp_i);
300 resetRanks(this->hslist);
301
302 if (this->hslist.size() > 10) {
303 this->hslist.resize(10);
304 }
305 }
306 } catch (int e) {
307 fail = true;
308 }
309}
310
311SDL_Surface* GlobalHighscoreList::render() {
312 if (fail) {
313 SDL_Surface* tmp = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0);
314 Uint32 bgColor = SDL_MapRGB(tmp->format, 255, 255, 255);
315 SDL_FillRect(tmp, NULL, bgColor);
316 SDL_SetColorKey(tmp, SDL_TRUE, bgColor);
317 TTF_Font* dataFont = loadFont(25);
318 SDL_Color fontColor = {0, 0, 0, 0};
319 SDL_Surface* text = TTF_RenderText_Blended(
320 dataFont, "Error retrieving highscores", fontColor);
321 SDL_Rect tSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w,
322 text->h};
323 SDL_BlitSurface(text, NULL, tmp, &tSpace);
324
325 return tmp;
326 } else {
327 return super::render();
328 }
329}
330
331bool GlobalHighscoreList::didFail() { return fail; }
332
333State* ChooseHighscoreListState::operator()(SDL_Window* window,
334 SDL_Renderer* renderer) {
335 SDL_Texture* background = loadImage(renderer, "resources/chl.bmp");
336 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
337 int selection = 0;
338 SDL_Event e;
339
340 for (;;) {
341 SDL_RenderClear(renderer);
342 SDL_RenderCopy(renderer, background, NULL, NULL);
343 applyTexture(renderer, pointer, 127,
344 selection == 0 ? 306 : (selection == 1 ? 336 : 396));
345 SDL_RenderPresent(renderer);
346
347 while (SDL_PollEvent(&e)) {
348 if (e.type == SDL_QUIT) {
349 return NULL;
350 } else if (e.type == SDL_KEYDOWN) {
351 if ((e.key.keysym.sym == SDLK_UP) && (selection != 0)) {
352 selection--;
353 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection != 2)) {
354 selection++;
355 } else if (e.key.keysym.sym == SDLK_RETURN) {
356 switch (selection) {
357 case 0:
358 return new DisplayLocalHighscoreListState();
359 case 1:
360 return new DisplayGlobalHighscoreListState();
361 case 2:
362 return new TitleState();
363 }
364 }
365 }
366 }
367 }
368}
369
370State* DisplayLocalHighscoreListState::operator()(SDL_Window* window,
371 SDL_Renderer* renderer) {
372 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
373
374 LocalHighscoreList* lhl = new LocalHighscoreList();
375 SDL_Surface* list_s = lhl->render();
376 SDL_Color fontColor = {0, 0, 0, 0};
377 SDL_Surface* title =
378 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
379 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
380 SDL_BlitSurface(title, NULL, list_s, &tSpace);
381 SDL_FreeSurface(title);
382
383 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp");
384 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
385 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
386 SDL_FreeSurface(options_s);
387
388 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
389 SDL_FreeSurface(list_s);
390
391 SDL_Event e;
392
393 for (;;) {
394 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
395 SDL_RenderClear(renderer);
396 SDL_RenderCopy(renderer, list, NULL, NULL);
397 applyTexture(renderer, pointer, 137, 449);
398 SDL_RenderPresent(renderer);
399
400 while (SDL_PollEvent(&e)) {
401 if (e.type == SDL_QUIT) {
402 return NULL;
403 } else if (e.type == SDL_KEYDOWN) {
404 if (e.key.keysym.sym == SDLK_RETURN) {
405 return new ChooseHighscoreListState();
406 }
407 }
408 }
409 }
410}
411
412State* DisplayAndReturnLocalHighscoreListState::operator()(
413 SDL_Window* window, SDL_Renderer* renderer) {
414 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
415
416 LocalHighscoreList* lhl = new LocalHighscoreList();
417 SDL_Surface* list_s = lhl->render();
418 SDL_Color fontColor = {0, 0, 0, 0};
419 SDL_Surface* title =
420 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
421 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
422 SDL_BlitSurface(title, NULL, list_s, &tSpace);
423 SDL_FreeSurface(title);
424
425 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_paartm.bmp");
426 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
427 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
428 SDL_FreeSurface(options_s);
429
430 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
431 SDL_FreeSurface(list_s);
432
433 int selection = 0;
434 SDL_Event e;
435
436 for (;;) {
437 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
438 SDL_RenderClear(renderer);
439 SDL_RenderCopy(renderer, list, NULL, NULL);
440 applyTexture(renderer, pointer, selection == 0 ? 52 : 225, 447);
441 SDL_RenderPresent(renderer);
442
443 while (SDL_PollEvent(&e)) {
444 if (e.type == SDL_QUIT) {
445 return NULL;
446 } else if (e.type == SDL_KEYDOWN) {
447 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) {
448 selection--;
449 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) {
450 selection++;
451 } else if (e.key.keysym.sym == SDLK_RETURN) {
452 switch (selection) {
453 case 0:
454 return new GameState();
455 case 1:
456 return new TitleState();
457 }
458 }
459 }
460 }
461 }
462}
463
464State* DisplayGlobalHighscoreListState::operator()(SDL_Window* window,
465 SDL_Renderer* renderer) {
466 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
467
468 // Display loading message
469 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
470 SDL_RenderClear(renderer);
471
472 SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0);
473 Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255);
474 SDL_FillRect(list_s, NULL, bgColor);
475 SDL_SetColorKey(list_s, SDL_TRUE, bgColor);
476 TTF_Font* dataFont = loadFont(25);
477 SDL_Color fontColor = {0, 0, 0, 0};
478 SDL_Surface* text =
479 TTF_RenderText_Blended(dataFont, "Fetching highscores....", fontColor);
480 SDL_Rect aSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w,
481 text->h};
482 SDL_BlitSurface(text, NULL, list_s, &aSpace);
483 SDL_FreeSurface(text);
484
485 SDL_Surface* title =
486 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
487 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
488 SDL_BlitSurface(title, NULL, list_s, &tSpace);
489 SDL_FreeSurface(title);
490
491 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp");
492 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
493 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
494 SDL_FreeSurface(options_s);
495
496 list = SDL_CreateTextureFromSurface(renderer, list_s);
497 SDL_FreeSurface(list_s);
498
499 m = SDL_CreateMutex();
500
501 // Start downloading scores
502 SDL_CreateThread(&LoadHighscoreList, "LoadHighscoreList", this);
503
504 // Parse keyboard events
505 SDL_Event e;
506
507 for (;;) {
508 if (SDL_LockMutex(m) == 0) {
509 if (lhl != NULL) {
510 SDL_Surface* list_s = lhl->render();
511
512 SDL_Color fontColor = {0, 0, 0, 0};
513 SDL_Surface* title =
514 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
515 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
516 SDL_BlitSurface(title, NULL, list_s, &tSpace);
517 SDL_FreeSurface(title);
518
519 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp");
520 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
521 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
522 SDL_FreeSurface(options_s);
523
524 list = SDL_CreateTextureFromSurface(renderer, list_s);
525 SDL_FreeSurface(list_s);
526
527 lhl = NULL;
528 }
529
530 SDL_UnlockMutex(m);
531 }
532
533 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
534 SDL_RenderClear(renderer);
535 SDL_RenderCopy(renderer, list, NULL, NULL);
536 applyTexture(renderer, pointer, 137, 449);
537 SDL_RenderPresent(renderer);
538
539 while (SDL_PollEvent(&e)) {
540 if (e.type == SDL_QUIT) {
541 SDL_DestroyMutex(m);
542
543 return NULL;
544 } else if (e.type == SDL_KEYDOWN) {
545 if (e.key.keysym.sym == SDLK_RETURN) {
546 SDL_DestroyMutex(m);
547
548 return new ChooseHighscoreListState();
549 }
550 }
551 }
552 }
553}
554
555int DisplayGlobalHighscoreListState::LoadHighscoreList(void* pParam) {
556 DisplayGlobalHighscoreListState* parent =
557 ((DisplayGlobalHighscoreListState*)pParam);
558 if (SDL_LockMutex(parent->m) == 0) {
559 parent->lhl = new GlobalHighscoreList();
560
561 SDL_UnlockMutex(parent->m);
562 } else {
563 printf("Couldn't lock mutex: %s\n", SDL_GetError());
564 }
565}
566
567EnterHighscoreState::EnterHighscoreState(int level) { this->level = level; }
568
569State* EnterHighscoreState::operator()(SDL_Window* window,
570 SDL_Renderer* renderer) {
571 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
572
573 // Render highscore list
574 LocalHighscoreList* lhl = new LocalHighscoreList();
575 char* emp = new char[1];
576 emp[0] = 0;
577 Highscore* h = new Highscore(emp, level);
578 int newpos = lhl->addHighscore(h);
579
580 SDL_Surface* list_s = lhl->render();
581
582 SDL_Color fontColor = {0, 0, 0, 0};
583 SDL_Surface* title =
584 TTF_RenderText_Blended(loadFont(40), "New Highscore!", fontColor);
585 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
586 SDL_BlitSurface(title, NULL, list_s, &tSpace);
587 SDL_FreeSurface(title);
588
589 this->lp = 0;
590 this->hsname = (char*)calloc(25, sizeof(char));
591
592 SDL_Surface* text =
593 TTF_RenderText_Blended(loadFont(25), "Enter Your Name", fontColor);
594 SDL_Rect oSpace = {240 - (text->w / 2), 440, text->w, text->h};
595 SDL_BlitSurface(text, NULL, list_s, &oSpace);
596 SDL_FreeSurface(text);
597
598 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
599 SDL_FreeSurface(list_s);
600
601 int selection = 0;
602 SDL_Event e;
603
604 int posw, posh;
605 char pos[3]; // 2 max characters in rank plus the colon at the end
606 sprintf(pos, "%d:", newpos);
607 char name[26]; // 25 max characters in username plus the space at the
608 // beginning
609 sprintf(name, " %s", hsname);
610 SDL_Surface* newName_s =
611 TTF_RenderText_Blended(loadFont(25), name, fontColor);
612 TTF_SizeText(loadFont(40), pos, &posw, &posh);
613 SDL_Rect rntSpace;
614 rntSpace.x = posw;
615 rntSpace.y = newpos * 40 + ((posh / 2) - (newName_s->h / 2));
616 rntSpace.w = newName_s->w;
617 rntSpace.h = newName_s->h;
618 newName = SDL_CreateTextureFromSurface(renderer, newName_s);
619 SDL_FreeSurface(newName_s);
620
621 for (;;) {
622 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
623 SDL_RenderClear(renderer);
624
625 SDL_Rect eSpace = {0, newpos * 40, 480, 40};
626 SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
627 SDL_RenderFillRect(renderer, &eSpace);
628
629 SDL_RenderCopy(renderer, list, NULL, NULL);
630 SDL_RenderCopy(renderer, newName, NULL, &rntSpace);
631
632 SDL_RenderPresent(renderer);
633
634 while (SDL_PollEvent(&e)) {
635 if (e.type == SDL_QUIT) {
636 return NULL;
637 } else if (e.type == SDL_KEYDOWN) {
638 if ((e.key.keysym.sym == SDLK_BACKSPACE) && (lp > 0)) {
639 hsname[--lp] = 0;
640
641 SDL_Color fontColor = {0, 0, 0, 0};
642 char name[26]; // 25 max characters in username plus the space at the
643 // beginning
644 sprintf(name, " %s", hsname);
645 newName_s = TTF_RenderText_Blended(loadFont(25), name, fontColor);
646 rntSpace.w = newName_s->w;
647 rntSpace.h = newName_s->h;
648 newName = SDL_CreateTextureFromSurface(renderer, newName_s);
649 SDL_FreeSurface(newName_s);
650 } else if ((e.key.keysym.sym == SDLK_RETURN) && (hsname[0] != 0)) {
651 lhl = new LocalHighscoreList();
652 Highscore* h2 = new Highscore(hsname, level);
653 lhl->addHighscore(h2);
654 lhl->writeHighscores();
655
656 return new NewHighscoreState(h2);
657 }
658 } else if (e.type == SDL_TEXTINPUT) {
659 if (((*e.text.text & 0xFF80) == 0) &&
660 (*e.text.text >= 32 && *e.text.text < 127) && (lp < 25)) {
661 hsname[lp++] = *e.text.text & 0x7f;
662 hsname[lp] = 0;
663
664 SDL_Color fontColor = {0, 0, 0, 0};
665 char name[26]; // 25 max characters in username plus the space at the
666 // beginning
667 sprintf(name, " %s", hsname);
668 newName_s = TTF_RenderText_Blended(loadFont(25), name, fontColor);
669 rntSpace.w = newName_s->w;
670 rntSpace.h = newName_s->h;
671 newName = SDL_CreateTextureFromSurface(renderer, newName_s);
672 SDL_FreeSurface(newName_s);
673 }
674 }
675 }
676 }
677}
678
679NewHighscoreState::NewHighscoreState(Highscore* h) { this->h = h; }
680
681State* NewHighscoreState::operator()(SDL_Window* window,
682 SDL_Renderer* renderer) {
683 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
684
685 // Render highscore list
686 LocalHighscoreList* lhl = new LocalHighscoreList();
687 SDL_Surface* list_s = lhl->render();
688
689 SDL_Color fontColor = {0, 0, 0, 0};
690 SDL_Surface* title =
691 TTF_RenderText_Blended(loadFont(40), "New Highscore!", fontColor);
692 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
693 SDL_BlitSurface(title, NULL, list_s, &tSpace);
694 SDL_FreeSurface(title);
695
696 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_passartm.bmp");
697 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
698 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
699 SDL_FreeSurface(options_s);
700
701 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
702 SDL_FreeSurface(list_s);
703
704 int selection = 0;
705 SDL_Event e;
706
707 for (;;) {
708 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
709 SDL_RenderClear(renderer);
710
711 SDL_Rect eSpace = {0, h->getRank() * 40, 480, 40};
712 SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
713 SDL_RenderFillRect(renderer, &eSpace);
714
715 SDL_RenderCopy(renderer, list, NULL, NULL);
716 applyTexture(renderer, pointer,
717 selection == 0 ? 13 : (selection == 1 ? 138 : 284), 448);
718 SDL_RenderPresent(renderer);
719
720 while (SDL_PollEvent(&e)) {
721 if (e.type == SDL_QUIT) {
722 return NULL;
723 } else if (e.type == SDL_KEYDOWN) {
724 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) {
725 selection--;
726 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 2)) {
727 selection++;
728 } else if (e.key.keysym.sym == SDLK_RETURN) {
729 switch (selection) {
730 case 0:
731 return new GameState();
732 case 1:
733 return new SubmitHighscoreState(h);
734 case 2:
735 return new TitleState();
736 }
737 }
738 }
739 }
740 }
741}
742
743SubmitHighscoreState::SubmitHighscoreState(Highscore* h) { this->h = h; }
744
745State* SubmitHighscoreState::operator()(SDL_Window* window,
746 SDL_Renderer* renderer) {
747 SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0);
748 Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255);
749 SDL_FillRect(list_s, NULL, bgColor);
750 SDL_SetColorKey(list_s, SDL_TRUE, bgColor);
751 TTF_Font* dataFont = loadFont(25);
752 SDL_Color fontColor = {0, 0, 0, 0};
753 SDL_Surface* text =
754 TTF_RenderText_Blended(dataFont, "Sending highscore....", fontColor);
755 SDL_Rect aSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w,
756 text->h};
757 SDL_BlitSurface(text, NULL, list_s, &aSpace);
758 SDL_FreeSurface(text);
759
760 SDL_Surface* title =
761 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
762 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
763 SDL_BlitSurface(title, NULL, list_s, &tSpace);
764 SDL_FreeSurface(title);
765
766 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
767 SDL_FreeSurface(list_s);
768
769 // Start submitting score
770 m = SDL_CreateMutex();
771 SDL_CreateThread(&SubmitHighscore, "SubmitHighscore", this);
772
773 SDL_Event e;
774
775 for (;;) {
776 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
777 SDL_RenderClear(renderer);
778 SDL_RenderCopy(renderer, list, NULL, NULL);
779 SDL_RenderPresent(renderer);
780
781 if (SDL_LockMutex(m) == 0) {
782 if (lhl != NULL) {
783 SDL_UnlockMutex(m);
784 SDL_DestroyMutex(m);
785
786 if (lhl->didFail()) {
787 return new FailedSubmittingHighscoreState(h);
788 } else {
789 return new SubmittedHighscoreState(lhl, h);
790 }
791 } else {
792 SDL_UnlockMutex(m);
793 }
794 }
795
796 while (SDL_PollEvent(&e)) {
797 if (e.type == SDL_QUIT) {
798 SDL_DestroyMutex(m);
799 98
800 return NULL; 99 for (const Highscore& cur_h : hslist_) {
801 } 100 if (h.getLevel() > cur_h.getLevel()) {
101 break;
802 } 102 }
103 i++;
803 } 104 }
804}
805
806int SubmitHighscoreState::SubmitHighscore(void* pParam) {
807 SubmitHighscoreState* parent = (SubmitHighscoreState*)pParam;
808 if (SDL_LockMutex(parent->m) == 0) {
809 parent->lhl = new GlobalHighscoreList(parent->h);
810
811 SDL_UnlockMutex(parent->m);
812 } else {
813 printf("Could not lock mutex: %s\n", SDL_GetError());
814 }
815}
816
817FailedSubmittingHighscoreState::FailedSubmittingHighscoreState(Highscore* h) {
818 this->h = h;
819}
820
821State* FailedSubmittingHighscoreState::operator()(SDL_Window* window,
822 SDL_Renderer* renderer) {
823 SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0);
824 Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255);
825 SDL_FillRect(list_s, NULL, bgColor);
826 SDL_SetColorKey(list_s, SDL_TRUE, bgColor);
827 TTF_Font* dataFont = loadFont(25);
828 SDL_Color fontColor = {0, 0, 0, 0};
829 SDL_Surface* text = TTF_RenderText_Blended(
830 dataFont, "Error submitting highscores", fontColor);
831 SDL_Rect tSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w,
832 text->h};
833 SDL_BlitSurface(text, NULL, list_s, &tSpace);
834 SDL_FreeSurface(text);
835
836 SDL_Surface* title =
837 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
838 SDL_Rect aSpace = {240 - (title->w / 2), 0, title->w, title->h};
839 SDL_BlitSurface(title, NULL, list_s, &aSpace);
840 SDL_FreeSurface(title);
841
842 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_passartm.bmp");
843 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
844 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
845 SDL_FreeSurface(options_s);
846
847 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
848 SDL_FreeSurface(list_s);
849
850 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
851 int selection = 0;
852 SDL_Event e;
853 105
854 for (;;) { 106 hslist_.push_back(h);
855 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); 107 std::sort(hslist_.begin(), hslist_.end(),
856 SDL_RenderClear(renderer); 108 [](const Highscore& lhs, const Highscore& rhs) {
857 SDL_RenderCopy(renderer, list, NULL, NULL); 109 return lhs.getLevel() > rhs.getLevel();
858 applyTexture(renderer, pointer, 110 });
859 selection == 0 ? 13 : (selection == 1 ? 138 : 284), 448); 111 resetRanks();
860 SDL_RenderPresent(renderer);
861 112
862 while (SDL_PollEvent(&e)) { 113 if (hslist_.size() > 10) {
863 if (e.type == SDL_QUIT) { 114 hslist_.resize(10);
864 return NULL;
865 } else if (e.type == SDL_KEYDOWN) {
866 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) {
867 selection--;
868 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 2)) {
869 selection++;
870 } else if (e.key.keysym.sym == SDLK_RETURN) {
871 switch (selection) {
872 case 0:
873 return new GameState();
874 case 1:
875 return new SubmitHighscoreState(h);
876 case 2:
877 return new TitleState();
878 }
879 }
880 }
881 }
882 } 115 }
883}
884 116
885SubmittedHighscoreState::SubmittedHighscoreState(GlobalHighscoreList* lhl, 117 return i;
886 Highscore* h) {
887 this->lhl = lhl;
888 this->h = h;
889} 118}
890 119
891State* SubmittedHighscoreState::operator()(SDL_Window* window, 120void HighscoreList::writeToFile() {
892 SDL_Renderer* renderer) { 121 std::ofstream hsfile(getDataFile());
893 SDL_Surface* list_s = lhl->render(); 122 hsfile << hslist_.size() << std::endl;
894
895 SDL_Color fontColor = {0, 0, 0, 0};
896 SDL_Surface* title =
897 TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor);
898 SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h};
899 SDL_BlitSurface(title, NULL, list_s, &tSpace);
900 SDL_FreeSurface(title);
901
902 SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_paartm.bmp");
903 SDL_Rect oSpace = {0, 440, options_s->w, options_s->h};
904 SDL_BlitSurface(options_s, NULL, list_s, &oSpace);
905 SDL_FreeSurface(options_s);
906
907 SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s);
908 SDL_FreeSurface(list_s);
909
910 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
911 int selection = 0;
912 SDL_Event e;
913 123
914 int newpos = h->getRank(); 124 for (const Highscore& h : hslist_) {
915 if (newpos > 10) { 125 hsfile << h.getName() << std::endl << h.getLevel() << std::endl;
916 newpos = 10;
917 }
918
919 for (;;) {
920 SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
921 SDL_RenderClear(renderer);
922
923 SDL_Rect eSpace = {0, newpos * 40, 480, 40};
924 SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
925 SDL_RenderFillRect(renderer, &eSpace);
926
927 SDL_RenderCopy(renderer, list, NULL, NULL);
928 applyTexture(renderer, pointer, selection == 0 ? 52 : 225, 447);
929 SDL_RenderPresent(renderer);
930
931 while (SDL_PollEvent(&e)) {
932 if (e.type == SDL_QUIT) {
933 return NULL;
934 } else if (e.type == SDL_KEYDOWN) {
935 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) {
936 selection--;
937 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) {
938 selection++;
939 } else if (e.key.keysym.sym == SDLK_RETURN) {
940 switch (selection) {
941 case 0:
942 return new GameState();
943 case 1:
944 return new TitleState();
945 }
946 }
947 }
948 }
949 } 126 }
950} 127}
diff --git a/hslist.h b/hslist.h index efffa9d..0636183 100644 --- a/hslist.h +++ b/hslist.h
@@ -1,130 +1,34 @@
1#ifndef HSLIST_H
2#define HSLIST_H
3
1#include <SDL.h> 4#include <SDL.h>
2 5
6#include <memory>
3#include <vector> 7#include <vector>
4 8
5#include "highscore.h" 9#include "highscore.h"
10#include "sdl.h"
6#include "state.h" 11#include "state.h"
7 12
8#ifndef HSLIST_H
9#define HSLIST_H
10
11typedef std::vector<Highscore*> hslist_t;
12
13void resetRanks(hslist_t in);
14
15class HighscoreList { 13class HighscoreList {
16 public: 14 public:
17 SDL_Surface* render(); 15 static std::unique_ptr<HighscoreList> GetLocalHighscores();
18 16 static std::unique_ptr<HighscoreList> GetGlobalHighscores();
19 protected:
20 hslist_t getLocalHighscores();
21 hslist_t getGlobalHighscores();
22
23 hslist_t hslist;
24};
25
26class LocalHighscoreList : public HighscoreList {
27 public:
28 LocalHighscoreList();
29 int addHighscore(Highscore* h);
30 void writeHighscores();
31};
32
33class GlobalHighscoreList : public HighscoreList {
34 public:
35 GlobalHighscoreList();
36 GlobalHighscoreList(Highscore* h);
37 SDL_Surface* render();
38 bool didFail();
39
40 private:
41 typedef HighscoreList super;
42
43 protected:
44 bool fail;
45};
46
47class ChooseHighscoreListState : public State {
48 public:
49 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
50};
51
52class DisplayLocalHighscoreListState : public State {
53 public:
54 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
55};
56
57class DisplayAndReturnLocalHighscoreListState : public State {
58 public:
59 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
60};
61
62class DisplayGlobalHighscoreListState : public State {
63 public:
64 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
65
66 protected:
67 SDL_Surface* list_s;
68 SDL_Texture* list;
69 GlobalHighscoreList* lhl;
70 SDL_mutex* m;
71
72 private:
73 static int LoadHighscoreList(void* pParam);
74};
75
76class EnterHighscoreState : public State {
77 public:
78 EnterHighscoreState(int level);
79 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
80
81 private:
82 int level;
83 int lp;
84 char* hsname;
85 SDL_Texture* newName;
86};
87
88class NewHighscoreState : public State {
89 public:
90 NewHighscoreState(Highscore* h);
91 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
92 17
93 private: 18 surface_ptr render();
94 Highscore* h;
95};
96 19
97class SubmitHighscoreState : public State { 20 const std::vector<Highscore>& getList() const { return hslist_; }
98 public:
99 SubmitHighscoreState(Highscore* h);
100 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
101 21
102 protected: 22 int addHighscore(Highscore h);
103 Highscore* h;
104 SDL_mutex* m;
105 GlobalHighscoreList* lhl;
106 23
107 private: 24 void writeToFile();
108 static int SubmitHighscore(void* pParam);
109};
110
111class FailedSubmittingHighscoreState : public State {
112 public:
113 FailedSubmittingHighscoreState(Highscore* h);
114 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
115 25
116 private: 26 private:
117 Highscore* h; 27 explicit HighscoreList(std::vector<Highscore> hslist);
118};
119 28
120class SubmittedHighscoreState : public State { 29 void resetRanks();
121 public:
122 SubmittedHighscoreState(GlobalHighscoreList* lhl, Highscore* h);
123 State* operator()(SDL_Window* window, SDL_Renderer* renderer);
124 30
125 private: 31 std::vector<Highscore> hslist_;
126 GlobalHighscoreList* lhl;
127 Highscore* h;
128}; 32};
129 33
130#endif 34#endif
diff --git a/mazeoflife.cpp b/mazeoflife.cpp index 952fbdb..3afff78 100644 --- a/mazeoflife.cpp +++ b/mazeoflife.cpp
@@ -1,5 +1,9 @@
1#include "mazeoflife.h" 1#include "mazeoflife.h"
2 2
3#ifdef __EMSCRIPTEN__
4#include <emscripten.h>
5#endif
6
3#include <SDL.h> 7#include <SDL.h>
4#include <SDL_net.h> 8#include <SDL_net.h>
5#include <SDL_ttf.h> 9#include <SDL_ttf.h>
@@ -7,53 +11,58 @@
7#include <cstdlib> 11#include <cstdlib>
8#include <ctime> 12#include <ctime>
9#include <iostream> 13#include <iostream>
14#include <memory>
10 15
16#include "sdl.h"
11#include "state.h" 17#include "state.h"
12#include "titlestate.h" 18#include "titlestate.h"
13 19
14int main(int argc, char* argv[]) { 20static void main_loop(void* arg) {
15 srand(time(NULL)); 21 Game& game = *static_cast<Game*>(arg);
16 22
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) { 23 std::unique_ptr<State> next_state = (*game.state)(game);
18 printf("Could not initialize SDL: %s.\n", SDL_GetError()); 24 if (next_state) {
19 exit(-1); 25 game.state = std::move(next_state);
20 } 26 }
21 27
22 if (TTF_Init() == -1) { 28 if (game.should_quit) {
23 printf("Could not initialize SDL_ttf: %s.\n", TTF_GetError()); 29#ifdef __EMSCRIPTEN__
24 exit(-1); 30 emscripten_cancel_main_loop();
31#else
32 exit(0);
33#endif
25 } 34 }
35}
26 36
27 if (SDLNet_Init() == -1) { 37int main(int, char**) {
28 printf("Cound not initalize SDL_net: %s.\n", SDLNet_GetError()); 38 Game game;
29 exit(-1); 39
30 } 40 std::random_device randomEngine;
41 game.rng = std::mt19937(randomEngine());
31 42
32 SDL_Window* window = 43 game.window = window_ptr(
33 SDL_CreateWindow("Maze of Life", 100, 100, 480, 480, SDL_WINDOW_SHOWN); 44 SDL_CreateWindow("Maze of Life", 100, 100, 480, 480, SDL_WINDOW_SHOWN));
34 if (window == NULL) { 45 if (!game.window) {
35 std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; 46 throw sdl_error();
36 return 1;
37 } 47 }
38 48
39 SDL_Surface* icon = SDL_LoadBMP("resources/icon.bmp"); 49 SDL_Surface* icon = SDL_LoadBMP("resources/icon.bmp");
40 SDL_SetWindowIcon(window, icon); 50 SDL_SetWindowIcon(game.window.get(), icon);
41 51
42 SDL_Renderer* renderer = SDL_CreateRenderer( 52 game.renderer = renderer_ptr(
43 window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 53 SDL_CreateRenderer(game.window.get(), -1,
44 if (renderer == NULL) { 54 SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
45 std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl; 55 if (!game.renderer) {
46 return 1; 56 throw sdl_error();
47 } 57 }
48 58
49 State* state = new TitleState(); 59 game.state = std::make_unique<TitleState>(game);
50 while (state != NULL) {
51 state = (*state)(window, renderer);
52 }
53 60
54 SDL_DestroyRenderer(renderer); 61#ifdef __EMSCRIPTEN__
55 SDL_DestroyWindow(window); 62 emscripten_set_main_loop_arg(main_loop, &game, 0, 1);
56 SDLNet_Quit(); 63#else
57 TTF_Quit(); 64 for (;;) {
58 SDL_Quit(); 65 main_loop(&game);
59} \ No newline at end of file 66 }
67#endif
68}
diff --git a/mazeoflife.h b/mazeoflife.h index 3cc6d6d..8a31d07 100644 --- a/mazeoflife.h +++ b/mazeoflife.h
@@ -1,7 +1,28 @@
1#ifndef MAZEOFLIFE_H 1#ifndef MAZEOFLIFE_H
2#define MAZEOFLIFE_H 2#define MAZEOFLIFE_H
3 3
4#include <memory>
5#include <random>
6
7#include "sdl.h"
8#include "state.h"
9
4const int WIDTH = 30; 10const int WIDTH = 30;
5const int HEIGHT = 30; 11const int HEIGHT = 30;
6 12
13struct Game {
14 std::mt19937 rng;
15
16 sdl_wrapper sdl;
17 ttf_wrapper ttf;
18 net_wrapper net;
19
20 window_ptr window;
21 renderer_ptr renderer;
22
23 std::unique_ptr<State> state;
24
25 bool should_quit = false;
26};
27
7#endif 28#endif
diff --git a/sdl.h b/sdl.h new file mode 100644 index 0000000..46d1fa4 --- /dev/null +++ b/sdl.h
@@ -0,0 +1,102 @@
1#ifndef SDL_H_A2226476
2#define SDL_H_A2226476
3
4#include <SDL.h>
5#include <SDL_net.h>
6#include <SDL_ttf.h>
7
8#include <memory>
9
10class sdl_error : public std::logic_error {
11 public:
12 sdl_error() : std::logic_error(SDL_GetError()) {}
13};
14
15class ttf_error : public std::logic_error {
16 public:
17 ttf_error() : std::logic_error(TTF_GetError()) {}
18};
19
20class net_error : public std::logic_error {
21 public:
22 net_error() : std::logic_error(SDLNet_GetError()) {}
23};
24
25class sdl_wrapper {
26 public:
27 sdl_wrapper() {
28 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
29 sdl_error ex;
30 SDL_Quit();
31
32 throw ex;
33 }
34 }
35
36 ~sdl_wrapper() { SDL_Quit(); }
37};
38
39class ttf_wrapper {
40 public:
41 ttf_wrapper() {
42 if (TTF_Init() == -1) {
43 ttf_error ex;
44 TTF_Quit();
45
46 throw ex;
47 }
48 }
49
50 ~ttf_wrapper() { TTF_Quit(); }
51};
52
53class net_wrapper {
54 public:
55 net_wrapper() {
56 if (SDLNet_Init() == -1) {
57 net_error ex;
58 SDLNet_Quit();
59
60 throw ex;
61 }
62 }
63
64 ~net_wrapper() { SDLNet_Quit(); }
65};
66
67class window_deleter {
68 public:
69 void operator()(SDL_Window* ptr) { SDL_DestroyWindow(ptr); }
70};
71
72using window_ptr = std::unique_ptr<SDL_Window, window_deleter>;
73
74class renderer_deleter {
75 public:
76 void operator()(SDL_Renderer* ptr) { SDL_DestroyRenderer(ptr); }
77};
78
79using renderer_ptr = std::unique_ptr<SDL_Renderer, renderer_deleter>;
80
81class surface_deleter {
82 public:
83 void operator()(SDL_Surface* ptr) { SDL_FreeSurface(ptr); }
84};
85
86using surface_ptr = std::unique_ptr<SDL_Surface, surface_deleter>;
87
88class texture_deleter {
89 public:
90 void operator()(SDL_Texture* ptr) { SDL_DestroyTexture(ptr); }
91};
92
93using texture_ptr = std::unique_ptr<SDL_Texture, texture_deleter>;
94
95class font_deleter {
96 public:
97 void operator()(TTF_Font* ptr) { TTF_CloseFont(ptr); }
98};
99
100using font_ptr = std::unique_ptr<TTF_Font, font_deleter>;
101
102#endif /* end of include guard: SDL_H_A2226476 */
diff --git a/state.h b/state.h index 0d50ab3..845c544 100644 --- a/state.h +++ b/state.h
@@ -1,13 +1,13 @@
1#include <SDL.h>
2
3#ifndef STATE_H 1#ifndef STATE_H
4#define STATE_H 2#define STATE_H
5 3
4#include <memory>
5
6class Game;
7
6class State { 8class State {
7 public: 9 public:
8 virtual State* operator()(SDL_Window* window, SDL_Renderer* renderer) { 10 virtual std::unique_ptr<State> operator()(Game& game) { return {}; };
9 return NULL;
10 };
11}; 11};
12 12
13#endif \ No newline at end of file 13#endif
diff --git a/titlestate.cpp b/titlestate.cpp index 166a55b..b68e8fa 100644 --- a/titlestate.cpp +++ b/titlestate.cpp
@@ -1,116 +1,124 @@
1#include "titlestate.h" 1#include "titlestate.h"
2 2
3#include "gamestate.h" 3#include "gamestate.h"
4#include "hslist.h" 4#include "hs_state.h"
5#include "util.h" 5#include "util.h"
6 6
7State* TitleState::operator()(SDL_Window* window, SDL_Renderer* renderer) { 7TitleState::TitleState(Game& game) {
8 SDL_Texture* background = loadImage(renderer, "resources/title.bmp"); 8 background_ = loadImage(game.renderer.get(), "resources/title.bmp");
9 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); 9 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
10 int selection = 0; 10}
11
12std::unique_ptr<State> TitleState::operator()(Game& game) {
11 SDL_Event e; 13 SDL_Event e;
12 14
13 for (;;) { 15 SDL_RenderClear(game.renderer.get());
14 SDL_RenderClear(renderer); 16 SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL);
15 SDL_RenderCopy(renderer, background, NULL, NULL); 17 applyTexture(game.renderer.get(), pointer_.get(), 136,
16 applyTexture(renderer, pointer, 136, 18 selection_ == 0
17 selection == 0 19 ? 316
18 ? 316 20 : (selection_ == 1 ? 350 : (selection_ == 2 ? 381 : 417)));
19 : (selection == 1 ? 350 : (selection == 2 ? 381 : 417))); 21 SDL_RenderPresent(game.renderer.get());
20 SDL_RenderPresent(renderer); 22
21 23 while (SDL_PollEvent(&e)) {
22 while (SDL_PollEvent(&e)) { 24 if (e.type == SDL_QUIT) {
23 if (e.type == SDL_QUIT) { 25 game.should_quit = true;
24 return NULL; 26 break;
25 } else if (e.type == SDL_KEYDOWN) { 27 } else if (e.type == SDL_KEYDOWN) {
26 if ((e.key.keysym.sym == SDLK_UP) && (selection != 0)) { 28 if ((e.key.keysym.sym == SDLK_UP) && (selection_ != 0)) {
27 selection--; 29 selection_--;
28 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection != 3)) { 30 } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection_ != 3)) {
29 selection++; 31 selection_++;
30 } else if (e.key.keysym.sym == SDLK_RETURN) { 32 } else if (e.key.keysym.sym == SDLK_RETURN) {
31 switch (selection) { 33 switch (selection_) {
32 case 0: 34 case 0:
33 return new GameState(); 35 return std::make_unique<GameState>();
34 case 1: 36 case 1:
35 return new HowToPlayState(); 37 return std::make_unique<HowToPlayState>(game);
36 case 2: 38 case 2:
37 return new ChooseHighscoreListState(); 39 return std::make_unique<DisplayLocalHighscoreListState>(game);
38 case 3: 40 case 3: {
39 return NULL; 41 game.should_quit = true;
42 break;
40 } 43 }
41 } 44 }
42 } 45 }
43 } 46 }
44 } 47 }
48
49 return nullptr;
45} 50}
46 51
47State* HowToPlayState::operator()(SDL_Window* window, SDL_Renderer* renderer) { 52HowToPlayState::HowToPlayState(Game& game) {
48 SDL_Texture* background = loadImage(renderer, "resources/htp1.bmp"); 53 background_ = loadImage(game.renderer.get(), "resources/htp1.bmp");
49 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); 54 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
50 int selection = 0; 55}
56
57std::unique_ptr<State> HowToPlayState::operator()(Game& game) {
51 SDL_Event e; 58 SDL_Event e;
52 59
53 for (;;) { 60 SDL_RenderClear(game.renderer.get());
54 SDL_RenderClear(renderer); 61 SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL);
55 SDL_RenderCopy(renderer, background, NULL, NULL); 62 applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 74 : 216,
56 applyTexture(renderer, pointer, selection == 0 ? 74 : 216, 430); 63 430);
57 SDL_RenderPresent(renderer); 64 SDL_RenderPresent(game.renderer.get());
58 65
59 while (SDL_PollEvent(&e)) { 66 while (SDL_PollEvent(&e)) {
60 if (e.type == SDL_QUIT) { 67 if (e.type == SDL_QUIT) {
61 return NULL; 68 game.should_quit = true;
62 } else if (e.type == SDL_KEYDOWN) { 69 break;
63 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { 70 } else if (e.type == SDL_KEYDOWN) {
64 selection--; 71 if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) {
65 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { 72 selection_--;
66 selection++; 73 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) {
67 } else if (e.key.keysym.sym == SDLK_RETURN) { 74 selection_++;
68 switch (selection) { 75 } else if (e.key.keysym.sym == SDLK_RETURN) {
69 case 0: 76 switch (selection_) {
70 return new HowToPlayPageTwoState(); 77 case 0:
71 78 return std::make_unique<HowToPlayPageTwoState>(game);
72 break; 79 case 1:
73 case 1: 80 return std::make_unique<TitleState>(game);
74 return new TitleState();
75 }
76 } 81 }
77 } 82 }
78 } 83 }
79 } 84 }
85
86 return nullptr;
87}
88
89HowToPlayPageTwoState::HowToPlayPageTwoState(Game& game) {
90 background_ = loadImage(game.renderer.get(), "resources/htp2.bmp");
91 pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp");
80} 92}
81 93
82State* HowToPlayPageTwoState::operator()(SDL_Window* window, 94std::unique_ptr<State> HowToPlayPageTwoState::operator()(Game& game) {
83 SDL_Renderer* renderer) {
84 SDL_Texture* background = loadImage(renderer, "resources/htp2.bmp");
85 SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp");
86 int selection = 0;
87 SDL_Event e; 95 SDL_Event e;
88 96
89 for (;;) { 97 SDL_RenderClear(game.renderer.get());
90 SDL_RenderClear(renderer); 98 SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL);
91 SDL_RenderCopy(renderer, background, NULL, NULL); 99 applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 45 : 238,
92 applyTexture(renderer, pointer, selection == 0 ? 45 : 238, 430); 100 430);
93 SDL_RenderPresent(renderer); 101 SDL_RenderPresent(game.renderer.get());
94 102
95 while (SDL_PollEvent(&e)) { 103 while (SDL_PollEvent(&e)) {
96 if (e.type == SDL_QUIT) { 104 if (e.type == SDL_QUIT) {
97 return NULL; 105 game.should_quit = true;
98 } else if (e.type == SDL_KEYDOWN) { 106 break;
99 if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { 107 } else if (e.type == SDL_KEYDOWN) {
100 selection--; 108 if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) {
101 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { 109 selection_--;
102 selection++; 110 } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) {
103 } else if (e.key.keysym.sym == SDLK_RETURN) { 111 selection_++;
104 switch (selection) { 112 } else if (e.key.keysym.sym == SDLK_RETURN) {
105 case 0: 113 switch (selection_) {
106 return new HowToPlayState(); 114 case 0:
107 115 return std::make_unique<HowToPlayState>(game);
108 break; 116 case 1:
109 case 1: 117 return std::make_unique<TitleState>(game);
110 return new TitleState();
111 }
112 } 118 }
113 } 119 }
114 } 120 }
115 } 121 }
122
123 return nullptr;
116} 124}
diff --git a/titlestate.h b/titlestate.h index e989659..22c99f5 100644 --- a/titlestate.h +++ b/titlestate.h
@@ -1,23 +1,46 @@
1#include <SDL.h>
2
3#include "state.h"
4
5#ifndef TITLESTATE_H 1#ifndef TITLESTATE_H
6#define TITLESTATE_H 2#define TITLESTATE_H
7 3
4#include <memory>
5
6#include "mazeoflife.h"
7#include "sdl.h"
8#include "state.h"
9
8class TitleState : public State { 10class TitleState : public State {
9 public: 11 public:
10 State* operator()(SDL_Window* window, SDL_Renderer* renderer); 12 explicit TitleState(Game& game);
13
14 std::unique_ptr<State> operator()(Game& game);
15
16 private:
17 texture_ptr background_;
18 texture_ptr pointer_;
19 int selection_ = 0;
11}; 20};
12 21
13class HowToPlayState : public State { 22class HowToPlayState : public State {
14 public: 23 public:
15 State* operator()(SDL_Window* window, SDL_Renderer* renderer); 24 explicit HowToPlayState(Game& game);
25
26 std::unique_ptr<State> operator()(Game& game);
27
28 private:
29 texture_ptr background_;
30 texture_ptr pointer_;
31 int selection_ = 0;
16}; 32};
17 33
18class HowToPlayPageTwoState : public State { 34class HowToPlayPageTwoState : public State {
19 public: 35 public:
20 State* operator()(SDL_Window* window, SDL_Renderer* renderer); 36 explicit HowToPlayPageTwoState(Game& game);
37
38 std::unique_ptr<State> operator()(Game& game);
39
40 private:
41 texture_ptr background_;
42 texture_ptr pointer_;
43 int selection_ = 0;
21}; 44};
22 45
23#endif \ No newline at end of file 46#endif
diff --git a/util.cpp b/util.cpp index 693d0ad..5947620 100644 --- a/util.cpp +++ b/util.cpp
@@ -1,8 +1,10 @@
1#include "util.h" 1#include "util.h"
2 2
3#include <filesystem>
3#include <iostream> 4#include <iostream>
4 5
5#include "mazeoflife.h" 6#include "mazeoflife.h"
7#include "sdl.h"
6 8
7void wrap(int& x, int& y) { 9void wrap(int& x, int& y) {
8 if (x < 0) { 10 if (x < 0) {
@@ -18,38 +20,32 @@ void wrap(int& x, int& y) {
18 } 20 }
19} 21}
20 22
21TTF_Font* loadFont(int size) { 23font_ptr loadFont(int size) {
22 TTF_Font* tmpfont = TTF_OpenFont("resources/mono.ttf", size); 24 font_ptr font = font_ptr(TTF_OpenFont("resources/mono.ttf", size));
23 25 if (!font) {
24 if (tmpfont == NULL) { 26 throw ttf_error();
25 printf("Unable to load font: %s\n", TTF_GetError());
26 exit(1);
27 } 27 }
28 28
29 return tmpfont; 29 return font;
30} 30}
31 31
32const char* getDataFile() { 32std::string getDataFile() {
33#ifdef WINDOWS 33#ifdef WINDOWS
34 char* dir = getenv("USERPROFILE"); 34 char* dir = getenv("USERPROFILE");
35#else 35#else
36 char* dir = getenv("HOME"); 36 char* dir = getenv("HOME");
37#endif 37#endif
38 38
39 return (std::string(dir) + "/.molhslist").c_str(); 39 return std::string(std::filesystem::path(dir) / ".molhslist");
40} 40}
41 41
42SDL_Texture* loadImage(SDL_Renderer* renderer, std::string file) { 42texture_ptr loadImage(SDL_Renderer* renderer, std::string file) {
43 SDL_Surface* surface = SDL_LoadBMP(file.c_str()); 43 surface_ptr surface = surface_ptr(SDL_LoadBMP(file.c_str()));
44 if (surface == NULL) { 44 if (!surface) {
45 std::cout << SDL_GetError() << std::endl; 45 throw sdl_error();
46 return NULL;
47 } 46 }
48 47
49 SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); 48 return texture_ptr(SDL_CreateTextureFromSurface(renderer, surface.get()));
50 SDL_FreeSurface(surface);
51
52 return texture;
53} 49}
54 50
55void applyTexture(SDL_Renderer* renderer, SDL_Texture* tex, int x, int y) { 51void applyTexture(SDL_Renderer* renderer, SDL_Texture* tex, int x, int y) {
diff --git a/util.h b/util.h index 836f065..062dfe6 100644 --- a/util.h +++ b/util.h
@@ -1,15 +1,18 @@
1#ifndef UTIL_H
2#define UTIL_H
3
1#include <SDL.h> 4#include <SDL.h>
2#include <SDL_ttf.h> 5#include <SDL_ttf.h>
3 6
7#include <memory>
4#include <string> 8#include <string>
5 9
6#ifndef UTIL_H 10#include "sdl.h"
7#define UTIL_H
8 11
9void wrap(int& x, int& y); 12void wrap(int& x, int& y);
10TTF_Font* loadFont(int size); 13font_ptr loadFont(int size);
11const char* getDataFile(); 14std::string getDataFile();
12SDL_Texture* loadImage(SDL_Renderer* renderer, std::string file); 15texture_ptr loadImage(SDL_Renderer* renderer, std::string file);
13void applyTexture(SDL_Renderer* renderer, SDL_Texture* tex, int x, int y); 16void applyTexture(SDL_Renderer* renderer, SDL_Texture* tex, int x, int y);
14 17
15#endif \ No newline at end of file 18#endif \ No newline at end of file