From a59fcafb2e81f3cb40ff320b106030e8fed4bd66 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 2 Nov 2023 18:38:53 -0400 Subject: Modernized C++ a bit (and removed global highscores) --- CMakeLists.txt | 8 +- gamestate.cpp | 953 +++++++++++++++++++++++++++----------------------------- gamestate.h | 13 +- highscore.cpp | 14 - highscore.h | 21 +- hs_state.cpp | 276 ++++++++++++++++ hs_state.h | 64 ++++ hslist.cpp | 969 +++++---------------------------------------------------- hslist.h | 124 +------- mazeoflife.cpp | 75 +++-- mazeoflife.h | 21 ++ sdl.h | 102 ++++++ state.h | 12 +- titlestate.cpp | 182 +++++------ titlestate.h | 39 ++- util.cpp | 32 +- util.h | 13 +- 17 files changed, 1235 insertions(+), 1683 deletions(-) delete mode 100644 highscore.cpp create mode 100644 hs_state.cpp create mode 100644 hs_state.h create mode 100644 sdl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 136dadc..e44357f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.1) project (mazeoflife) +#set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -sUSE_SDL=2 -sUSE_SDL_NET=2 -sUSE_SDL_TTF=2") + set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) find_package(SDL2 REQUIRED) @@ -13,7 +15,7 @@ include_directories( ${SDL2_NET_INCLUDE_DIRS} ) -add_executable(mazeoflife highscore.cpp hslist.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) -set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 11) +add_executable(mazeoflife hslist.cpp hs_state.cpp mazeoflife.cpp util.cpp titlestate.cpp gamestate.cpp) +set_property(TARGET mazeoflife PROPERTY CXX_STANDARD 17) set_property(TARGET mazeoflife PROPERTY CXX_STANDARD_REQUIRED ON) target_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 @@ #include #include #include +#include +#include #include #include #include @@ -16,61 +18,13 @@ #include #include "highscore.h" +#include "hs_state.h" #include "hslist.h" #include "mazeoflife.h" #include "titlestate.h" #include "util.h" -class GameBoard { - public: - GameBoard(int level, int playerx, int playery); - void tick(int playerx, int playery); - void render(SDL_Renderer* renderer, int level) const; - bool isObstructed(int x, int y) const; - bool operator<(const GameBoard& other) const; - - using coord = std::tuple; - - private: - void initialize(int level); - bool solve(int playerx, int playery) const; - std::string dump() const; - - using board_type = std::bitset; - - void incrementIfNeighbor(int x, int y, const board_type& temp, int playerx, - int playery, int& tick) const; - - bool applyNeighbors(int x, int y, const board_type& temp, int playerx, - int playery) const; - - board_type blocks; - board_type updateable; - int oldx; - int oldy; -}; - -class LoadGameState : public State { - public: - LoadGameState(int level); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); - - private: - int level; -}; - -class PlayGameState : public State { - public: - PlayGameState(int level, GameBoard* board, int playerx, int playery); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); - - private: - bool move(int x, int y); - int level; - GameBoard* board; - int playerx; - int playery; -}; +using board_type = std::bitset; void setRendererAliveColor(SDL_Renderer* renderer, int level) { switch ((level / 10) % 5) { @@ -112,8 +66,8 @@ void setRendererDeadColor(SDL_Renderer* renderer, int level) { } } -void GameBoard::incrementIfNeighbor(int x, int y, const board_type& temp, - int playerx, int playery, int& tick) const { +void incrementIfNeighbor(int x, int y, const board_type& temp, int playerx, + int playery, int& tick) { int nx = x; int ny = y; @@ -127,545 +81,566 @@ void GameBoard::incrementIfNeighbor(int x, int y, const board_type& temp, } } -State* GameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { - return new LoadGameState(0); -} - -LoadGameState::LoadGameState(int m_level) { level = m_level; } +bool applyNeighbors(int x, int y, const board_type& temp, int playerx, + int playery) { + int neighbors = 0; -State* LoadGameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { - char* wintitle = new char[50]; - sprintf(wintitle, "Maze Of Life - Level %d", level); - SDL_SetWindowTitle(window, wintitle); + incrementIfNeighbor(x - 1, y - 1, temp, playerx, playery, neighbors); + incrementIfNeighbor(x - 1, y, temp, playerx, playery, neighbors); + incrementIfNeighbor(x - 1, y + 1, temp, playerx, playery, neighbors); + incrementIfNeighbor(x, y - 1, temp, playerx, playery, neighbors); + incrementIfNeighbor(x, y + 1, temp, playerx, playery, neighbors); + incrementIfNeighbor(x + 1, y - 1, temp, playerx, playery, neighbors); + incrementIfNeighbor(x + 1, y, temp, playerx, playery, neighbors); + incrementIfNeighbor(x + 1, y + 1, temp, playerx, playery, neighbors); - // Randomly place the player in a corner - int playerx, playery; - switch (rand() % 4) { - case 0: - playerx = 1; - playery = 1; - break; - case 1: - playerx = 1; - playery = HEIGHT - 2; - break; - case 2: - playerx = WIDTH - 2; - playery = HEIGHT - 2; - break; - case 3: - playerx = WIDTH - 2; - playery = 1; - break; + if (temp[x + y * WIDTH]) { + return ((neighbors >= 1) && (neighbors <= 4)); + } else { + return (neighbors == 3); } +} - // Display the level number - setRendererDeadColor(renderer, level); - SDL_RenderClear(renderer); - - TTF_Font* font = loadFont(100); - SDL_Color fontColor = {0, 0, 0, 0}; - char levelnum[8]; - sprintf(levelnum, "%d", level); - SDL_Surface* dispsurf = TTF_RenderText_Solid(font, levelnum, fontColor); - SDL_Texture* disptext = SDL_CreateTextureFromSurface(renderer, dispsurf); - SDL_FreeSurface(dispsurf); - - SDL_Rect pos; - SDL_QueryTexture(disptext, NULL, NULL, &pos.w, &pos.h); - pos.x = 240 - (pos.w / 2); - pos.y = 240 - (pos.h / 2); - - SDL_RenderCopy(renderer, disptext, NULL, &pos); - SDL_RenderPresent(renderer); +class GameBoard { + public: + GameBoard(Game& game, int level, int playerx, int playery) { + for (;;) { + initialize(game, level); + updateable_.set(); + oldx_ = playerx; + oldy_ = playery; + + for (int i = 0; i < 50; i++) { + tick(playerx, playery); + } - // Do 50 gens of Conway - GameBoard* board = new GameBoard(level, playerx, playery); + if (solve(playerx, playery)) { + break; + } else { + std::cout << "Impossible board: " << playerx << "," << playery << "," + << dump() << std::endl; + } + } + } - // Wait a bit - SDL_Delay(500); + void tick(int playerx, int playery) { + board_type temp{blocks_}; + board_type tempdateable{updateable_}; + if ((playerx != oldx_) || (playery != oldy_)) { + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + int tdx = oldx_ + dx; + int tdy = oldy_ + dy; + wrap(tdx, tdy); + tempdateable.set(tdx + tdy * WIDTH); + + tdx = playerx + dx; + tdy = playery + dy; + wrap(tdx, tdy); + tempdateable.set(tdx + tdy * WIDTH); + } + } + } - // Start the level - return new PlayGameState(level, board, playerx, playery); -} + oldx_ = playerx; + oldy_ = playery; -PlayGameState::PlayGameState(int m_level, GameBoard* m_board, int m_playerx, - int m_playery) { - level = m_level; - board = m_board; - playerx = m_playerx; - playery = m_playery; -} + updateable_.reset(); -State* PlayGameState::operator()(SDL_Window* window, SDL_Renderer* renderer) { - SDL_Event e; + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + if (((x == 15) && (y == 15)) || (!tempdateable[x + y * WIDTH])) { + continue; + } - for (;;) { - // Tick board - board->tick(playerx, playery); + blocks_[x + y * WIDTH] = applyNeighbors(x, y, temp, playerx, playery); - // Paint board - board->render(renderer, level); + if (temp[x + y * WIDTH] != blocks_[x + y * WIDTH]) { + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + int tdx = x + dx; + int tdy = y + dy; + wrap(tdx, tdy); + updateable_.set(tdx + tdy * WIDTH); + } + } + } + } + } + } - // Paint event + void render(SDL_Renderer* renderer, int level) const { SDL_Rect block; block.w = 16; block.h = 16; - block.x = 15 * 16; - block.y = 15 * 16; - SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); - SDL_RenderFillRect(renderer, &block); - - // Paint player - block.x = playerx * 16; - block.y = playery * 16; - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - SDL_RenderFillRect(renderer, &block); - - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - switch (e.key.keysym.sym) { - case SDLK_LEFT: - if (move(playerx - 1, playery)) { - return new LoadGameState(level + 1); - } else { - break; - } - case SDLK_RIGHT: - if (move(playerx + 1, playery)) { - return new LoadGameState(level + 1); - } else { - break; - } + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + block.x = x * 16; + block.y = y * 16; - case SDLK_UP: - if (move(playerx, playery - 1)) { - return new LoadGameState(level + 1); - } else { - break; - } - - case SDLK_DOWN: - if (move(playerx, playery + 1)) { - return new LoadGameState(level + 1); - } else { - break; - } - - case SDLK_ESCAPE: - SDL_SetWindowTitle(window, ""); - - std::ifstream exists(getDataFile()); - if (exists) { - FILE* hslist = fopen(getDataFile(), "r"); - int scores; - Highscore* h; - - fscanf(hslist, "%d%*c", &scores); + if (blocks_[x + y * WIDTH]) { + setRendererAliveColor(renderer, level); + } else { + setRendererDeadColor(renderer, level); + } - if (scores < 10) { - fclose(hslist); + SDL_RenderFillRect(renderer, &block); + } + } + } - return new EnterHighscoreState(level); - } else { - for (int i = 0; i < scores; i++) { - int namelen; - char namelens[4]; - char* name = (char*)calloc(25, sizeof(char)); - int score; + bool isObstructed(int x, int y) const { + return blocks_[x + y * WIDTH] || (x == 15 && y == 15); + } - fscanf(hslist, "%d", &namelen); - sprintf(namelens, "%%%dc", namelen); - fscanf(hslist, namelens, name); - fscanf(hslist, "%d%*c", &score); + bool operator<(const GameBoard& other) const { + for (int i = WIDTH * HEIGHT - 1; i >= 0; i--) { + if (blocks_[i] ^ other.blocks_[i]) { + return other.blocks_[i]; + } + } - h = new Highscore(name, score); - } + return false; + } - fclose(hslist); + using coord = std::tuple; - if (h->getLevel() < level) { - return new EnterHighscoreState(level); - } else { - return new DisplayAndReturnLocalHighscoreListState(); - } - } - } else { - return new EnterHighscoreState(level); + private: + void initialize(Game& game, int level) { + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + blocks_[x + y * WIDTH] = false; + + switch (level / 10 + 1) { + case 1: + if ((x > 13) && (x < 17) && (y > 13) && (y < 17)) { + blocks_[x + y * WIDTH] = + std::bernoulli_distribution(0.5)(game.rng); } + break; + case 2: + case 3: + if ((x > 12) && (x < 18) && (y > 12) && (y < 18)) { + blocks_[x + y * WIDTH] = + std::bernoulli_distribution(0.5)(game.rng); + } + break; + case 4: + case 5: + if ((x > 11) && (x < 19) && (y > 11) && (y < 19)) { + blocks_[x + y * WIDTH] = + std::bernoulli_distribution(0.5)(game.rng); + } + break; + default: + blocks_[x + y * WIDTH] = std::bernoulli_distribution(0.5)(game.rng); } } } - SDL_Delay(5); + blocks_[15 + 15 * WIDTH] = false; } -} -bool PlayGameState::move(int x, int y) { - wrap(x, y); + bool solve(int playerx, int playery) const { + std::deque> search; + std::unordered_map done; - // Are we at the event? - if ((x == 15) && (y == 15)) { - return true; - } + // Assume that the player will not move while the board is changing, so tick + // the board until it either stops changing, or it reaches a state that it + // has already been in (in the case of alternating systems). + { + GameBoard original = *this; - // Can we even go there? - if (!board->isObstructed(x, y)) { - playerx = x; - playery = y; - } - - return false; -} + std::unordered_set pastStates; + pastStates.insert(original.blocks_); -GameBoard::GameBoard(int level, int playerx, int playery) { - for (;;) { - initialize(level); - updateable.set(); - oldx = playerx; - oldy = playery; + while (original.updateable_.any()) { + original.tick(playerx, playery); - for (int i = 0; i < 50; i++) { - tick(playerx, playery); - } - - if (solve(playerx, playery)) { - break; - } else { - std::cout << "Impossible board: " << playerx << "," << playery << "," - << dump() << std::endl; - } - } -} + if (pastStates.count(original.blocks_)) { + break; + } -void GameBoard::tick(int playerx, int playery) { - board_type temp{blocks}; - board_type tempdateable{updateable}; - if ((playerx != oldx) || (playery != oldy)) { - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - int tdx = oldx + dx; - int tdy = oldy + dy; - wrap(tdx, tdy); - tempdateable.set(tdx + tdy * WIDTH); - - tdx = playerx + dx; - tdy = playery + dy; - wrap(tdx, tdy); - tempdateable.set(tdx + tdy * WIDTH); + pastStates.insert(original.blocks_); } + + search.emplace_front(std::move(original), coord{playerx, playery}, 0); } - } - oldx = playerx; - oldy = playery; + // Use breadth first search to find a solution. + bool exists = false; + while (!search.empty()) { + auto cur = std::move(search.front()); + search.pop_front(); - updateable.reset(); + GameBoard& cbr = std::get<0>(cur); + coord& cpl = std::get<1>(cur); + int cns = std::get<2>(cur); - for (int y = 0; y < HEIGHT; y++) { - for (int x = 0; x < WIDTH; x++) { - if (((x == 15) && (y == 15)) || (!tempdateable[x + y * WIDTH])) { + // If it has been over 100 generations, give up. + if (cns > 100) { continue; } - blocks[x + y * WIDTH] = applyNeighbors(x, y, temp, playerx, playery); + int cplx = std::get<0>(cpl); + int cply = std::get<1>(cpl); - if (temp[x + y * WIDTH] != blocks[x + y * WIDTH]) { - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - int tdx = x + dx; - int tdy = y + dy; - wrap(tdx, tdy); - updateable.set(tdx + tdy * WIDTH); - } - } + // If this section of this board state has already been checked, skip it. + if (done.count(cbr.blocks_) && + done.at(cbr.blocks_)[cplx + cply * WIDTH]) { + continue; } - } - } -} - -bool GameBoard::applyNeighbors(int x, int y, const board_type& temp, - int playerx, int playery) const { - int neighbors = 0; - incrementIfNeighbor(x - 1, y - 1, temp, playerx, playery, neighbors); - incrementIfNeighbor(x - 1, y, temp, playerx, playery, neighbors); - incrementIfNeighbor(x - 1, y + 1, temp, playerx, playery, neighbors); - incrementIfNeighbor(x, y - 1, temp, playerx, playery, neighbors); - incrementIfNeighbor(x, y + 1, temp, playerx, playery, neighbors); - incrementIfNeighbor(x + 1, y - 1, temp, playerx, playery, neighbors); - incrementIfNeighbor(x + 1, y, temp, playerx, playery, neighbors); - incrementIfNeighbor(x + 1, y + 1, temp, playerx, playery, neighbors); + // Use a flood fill to find a set of positions accessible to the player + // without modifying the board state, as well as a set of positions + // adjacent to the flood that /will/ modify the board state. + board_type flood; + std::deque front; + front.push_front(cpl); + flood[cplx + cply * WIDTH] = true; + + std::set edges; + + while (!front.empty()) { + coord frontLoc = std::move(front.front()); + front.pop_front(); + + // Iterate over the positions 4-adjacent to the current one. + for (coord& fc : std::list{ + {std::get<0>(frontLoc) - 1, std::get<1>(frontLoc)}, + {std::get<0>(frontLoc) + 1, std::get<1>(frontLoc)}, + {std::get<0>(frontLoc), std::get<1>(frontLoc) - 1}, + {std::get<0>(frontLoc), std::get<1>(frontLoc) + 1}, + }) { + wrap(std::get<0>(fc), std::get<1>(fc)); + int fcx = std::get<0>(fc); + int fcy = std::get<1>(fc); + + // If this position is already in the flood, skip it. + if (flood[fcx + fcy * WIDTH]) { + continue; + } - if (temp[x + y * WIDTH]) { - return ((neighbors >= 1) && (neighbors <= 4)); - } else { - return (neighbors == 3); - } -} + // If the player could not move into this position, skip it. + if (cbr.isObstructed(fcx, fcy)) { + continue; + } -void GameBoard::render(SDL_Renderer* renderer, int level) const { - SDL_Rect block; - block.w = 16; - block.h = 16; + // If this position is adjacent to the event, then the board is + // solvable. + if (((fcx == 15) && ((fcy == 14) || (fcy == 16))) || + ((fcy == 15) && ((fcx == 14) || (fcx == 16)))) { + exists = true; + break; + } - for (int y = 0; y < HEIGHT; y++) { - for (int x = 0; x < WIDTH; x++) { - block.x = x * 16; - block.y = y * 16; + // Check if the player moving would cause any positions 8-adjacent to + // the start or end positions to change. This is more efficient than + // copying the board state and then running tick. + bool changed = false; + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + if (dx == 0 && dy == 0) { + continue; + } - if (blocks[x + y * WIDTH]) { - setRendererAliveColor(renderer, level); - } else { - setRendererDeadColor(renderer, level); - } + int cpldx = cplx + dx; + int cpldy = cply + dy; + wrap(cpldx, cpldy); - SDL_RenderFillRect(renderer, &block); - } - } -} + if (cbr.isObstructed(cpldx, cpldy) != + applyNeighbors(cpldx, cpldy, cbr.blocks_, fcx, fcy)) { + changed = true; + break; + } -bool GameBoard::isObstructed(int x, int y) const { - return blocks[x + y * WIDTH] || (x == 15 && y == 15); -} + int fcxdx = fcx + dx; + int fcydy = fcy + dy; + wrap(fcxdx, fcydy); -void GameBoard::initialize(int level) { - for (int y = 0; y < HEIGHT; y++) { - for (int x = 0; x < WIDTH; x++) { - blocks[x + y * WIDTH] = false; + if (cbr.isObstructed(fcxdx, fcydy) != + applyNeighbors(fcxdx, fcydy, cbr.blocks_, fcx, fcy)) { + changed = true; + break; + } + } - switch (level / 10 + 1) { - case 1: - if ((x > 13) && (x < 17) && (y > 13) && (y < 17)) { - blocks[x + y * WIDTH] = rand() % 2; - } - break; - case 2: - case 3: - if ((x > 12) && (x < 18) && (y > 12) && (y < 18)) { - blocks[x + y * WIDTH] = rand() % 2; + if (changed) { + break; + } } - break; - case 4: - case 5: - if ((x > 11) && (x < 19) && (y > 11) && (y < 19)) { - blocks[x + y * WIDTH] = rand() % 2; + + // If moving to this position would change the board state, add it to + // the set of edges; otherwise, add it to the flood and the flood + // front. + if (changed) { + edges.insert(fc); + } else { + flood[fcx + fcy * WIDTH] = true; + front.push_back(fc); } + } + + if (exists) { break; - default: - blocks[x + y * WIDTH] = rand() % 2; + } } - } - } - blocks[15 + 15 * WIDTH] = false; -} + if (exists) { + break; + } -bool GameBoard::operator<(const GameBoard& other) const { - for (int i = WIDTH * HEIGHT - 1; i >= 0; i--) { - if (blocks[i] ^ other.blocks[i]) { - return other.blocks[i]; - } - } + // Add the flood to the set of checked positions for this board state. + done[cbr.blocks_] |= flood; - return false; -} + // Add the edges to the search queue. + for (const coord& newLoc : edges) { + GameBoard nextState1 = cbr; + nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc)); -bool GameBoard::solve(int playerx, int playery) const { - std::deque> search; - std::unordered_map done; + // Assume that the player will not move while the board is changing, so + // tick the board until it either stops changing, or it reaches a state + // that it has already been in (in the case of alternating systems). + std::unordered_set pastStates; + pastStates.insert(nextState1.blocks_); - // Assume that the player will not move while the board is changing, so tick - // the board until it either stops changing, or it reaches a state that it has - // already been in (in the case of alternating systems). - { - GameBoard original = *this; + while (nextState1.updateable_.any()) { + nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc)); - std::unordered_set pastStates; - pastStates.insert(original.blocks); + if (pastStates.count(nextState1.blocks_)) { + break; + } - while (original.updateable.any()) { - original.tick(playerx, playery); + pastStates.insert(nextState1.blocks_); + } - if (pastStates.count(original.blocks)) { - break; + if (!done.count(nextState1.blocks_) || + !done.at(nextState1.blocks_)[std::get<0>(newLoc) + + std::get<1>(newLoc) * WIDTH]) { + search.emplace_back(std::move(nextState1), newLoc, cns + 1); + } } + } - pastStates.insert(original.blocks); + return exists; + } + + std::string dump() const { + std::stringstream output; + output << std::hex; + for (int i = 0; i < WIDTH * HEIGHT / 4; i++) { + int chunk = (8 * blocks_[i * 4]) + (4 * blocks_[i * 4 + 1]) + + (2 * blocks_[i * 4 + 2]) + blocks_[i * 4 + 3]; + output << chunk; } - search.emplace_front(std::move(original), coord{playerx, playery}, 0); + return output.str(); } - // Use breadth first search to find a solution. - bool exists = false; - while (!search.empty()) { - auto cur = std::move(search.front()); - search.pop_front(); + board_type blocks_; + board_type updateable_; + int oldx_; + int oldy_; +}; - GameBoard& cbr = std::get<0>(cur); - coord& cpl = std::get<1>(cur); - int cns = std::get<2>(cur); +std::unique_ptr startNewLevel(int level, + std::unique_ptr board, + int playerx, int playery); - // If it has been over 100 generations, give up. - if (cns > 100) { - continue; +class LoadGameState : public State { + public: + LoadGameState(int level) : level_(level) {} + + std::unique_ptr operator()(Game& game) { + std::ostringstream wintitle; + wintitle << "Maze Of Life - Level " << level_; + SDL_SetWindowTitle(game.window.get(), wintitle.str().c_str()); + + // Randomly place the player in a corner + int playerx, playery; + switch (std::uniform_int_distribution(0, 3)(game.rng)) { + case 0: { + playerx = 1; + playery = 1; + break; + } + case 1: { + playerx = 1; + playery = HEIGHT - 2; + break; + } + case 2: { + playerx = WIDTH - 2; + playery = HEIGHT - 2; + break; + } + case 3: { + playerx = WIDTH - 2; + playery = 1; + break; + } } - int cplx = std::get<0>(cpl); - int cply = std::get<1>(cpl); + // Display the level number + setRendererDeadColor(game.renderer.get(), level_); + SDL_RenderClear(game.renderer.get()); - // If this section of this board state has already been checked, skip it. - if (done.count(cbr.blocks) && done.at(cbr.blocks)[cplx + cply * WIDTH]) { - continue; - } + font_ptr font = loadFont(100); + SDL_Color fontColor = {0, 0, 0, 0}; + std::string levelnum = std::to_string(level_); + surface_ptr dispsurf = surface_ptr( + TTF_RenderText_Solid(font.get(), levelnum.c_str(), fontColor)); + texture_ptr disptext = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), dispsurf.get())); - // Use a flood fill to find a set of positions accessible to the player - // without modifying the board state, as well as a set of positions adjacent - // to the flood that /will/ modify the board state. - board_type flood; - std::deque front; - front.push_front(cpl); - flood[cplx + cply * WIDTH] = true; - - std::set edges; - - while (!front.empty()) { - coord frontLoc = std::move(front.front()); - front.pop_front(); - - // Iterate over the positions 4-adjacent to the current one. - for (coord& fc : std::list{ - {std::get<0>(frontLoc) - 1, std::get<1>(frontLoc)}, - {std::get<0>(frontLoc) + 1, std::get<1>(frontLoc)}, - {std::get<0>(frontLoc), std::get<1>(frontLoc) - 1}, - {std::get<0>(frontLoc), std::get<1>(frontLoc) + 1}, - }) { - wrap(std::get<0>(fc), std::get<1>(fc)); - int fcx = std::get<0>(fc); - int fcy = std::get<1>(fc); - - // If this position is already in the flood, skip it. - if (flood[fcx + fcy * WIDTH]) { - continue; - } + SDL_Rect pos; + SDL_QueryTexture(disptext.get(), NULL, NULL, &pos.w, &pos.h); + pos.x = 240 - (pos.w / 2); + pos.y = 240 - (pos.h / 2); - // If the player could not move into this position, skip it. - if (cbr.isObstructed(fcx, fcy)) { - continue; - } + SDL_RenderCopy(game.renderer.get(), disptext.get(), NULL, &pos); + SDL_RenderPresent(game.renderer.get()); - // If this position is adjacent to the event, then the board is - // solvable. - if (((fcx == 15) && ((fcy == 14) || (fcy == 16))) || - ((fcy == 15) && ((fcx == 14) || (fcx == 16)))) { - exists = true; - break; - } + // Do 50 gens of Conway + std::unique_ptr board = + std::make_unique(game, level_, playerx, playery); - // Check if the player moving would cause any positions 8-adjacent to - // the start or end positions to change. This is more efficient than - // copying the board state and then running tick. - bool changed = false; - for (int dy = -1; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++) { - if (dx == 0 && dy == 0) { - continue; - } + // Wait a bit + SDL_Delay(500); - int cpldx = cplx + dx; - int cpldy = cply + dy; - wrap(cpldx, cpldy); + // Start the level + return startNewLevel(level_, std::move(board), playerx, playery); + } - if (cbr.isObstructed(cpldx, cpldy) != - applyNeighbors(cpldx, cpldy, cbr.blocks, fcx, fcy)) { - changed = true; - break; - } + private: + int level_; +}; - int fcxdx = fcx + dx; - int fcydy = fcy + dy; - wrap(fcxdx, fcydy); +class PlayGameState : public State { + public: + PlayGameState(int level, std::unique_ptr board, int playerx, + int playery) + : level_(level), + board_(std::move(board)), + playerx_(playerx), + playery_(playery) {} - if (cbr.isObstructed(fcxdx, fcydy) != - applyNeighbors(fcxdx, fcydy, cbr.blocks, fcx, fcy)) { - changed = true; - break; - } + std::unique_ptr operator()(Game& game) { + SDL_Event e; + + // Tick board + board_->tick(playerx_, playery_); + + // Paint board + board_->render(game.renderer.get(), level_); + + // Paint event + SDL_Rect block; + block.w = 16; + block.h = 16; + block.x = 15 * 16; + block.y = 15 * 16; + SDL_SetRenderDrawColor(game.renderer.get(), 0, 0, 255, 255); + SDL_RenderFillRect(game.renderer.get(), &block); + + // Paint player + block.x = playerx_ * 16; + block.y = playery_ * 16; + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255); + SDL_RenderFillRect(game.renderer.get(), &block); + + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + + return nullptr; + } else if (e.type == SDL_KEYDOWN) { + bool trymove = false; + int to_x = playerx_; + int to_y = playery_; + + switch (e.key.keysym.sym) { + case SDLK_LEFT: { + trymove = true; + to_x--; + break; } + case SDLK_RIGHT: { + trymove = true; + to_x++; + break; + } + case SDLK_UP: { + trymove = true; + to_y--; + break; + } + case SDLK_DOWN: { + trymove = true; + to_y++; + break; + } + case SDLK_ESCAPE: { + SDL_SetWindowTitle(game.window.get(), ""); + + std::unique_ptr hslist = + HighscoreList::GetLocalHighscores(); + if (hslist->addHighscore(Highscore("", level_)) <= 10) { + return std::make_unique(game, level_); + } else { + return std::make_unique( + game); + } - if (changed) { break; } } - // If moving to this position would change the board state, add it to - // the set of edges; otherwise, add it to the flood and the flood front. - if (changed) { - edges.insert(fc); - } else { - flood[fcx + fcy * WIDTH] = true; - front.push_back(fc); + if (trymove && move(to_x, to_y)) { + return std::make_unique(level_ + 1); } } - - if (exists) { - break; - } - } - - if (exists) { - break; } - // Add the flood to the set of checked positions for this board state. - done[cbr.blocks] |= flood; - - // Add the edges to the search queue. - for (const coord& newLoc : edges) { - GameBoard nextState1 = cbr; - nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc)); - - // Assume that the player will not move while the board is changing, so - // tick the board until it either stops changing, or it reaches a state - // that it has already been in (in the case of alternating systems). - std::unordered_set pastStates; - pastStates.insert(nextState1.blocks); + SDL_Delay(5); - while (nextState1.updateable.any()) { - nextState1.tick(std::get<0>(newLoc), std::get<1>(newLoc)); + return nullptr; + } - if (pastStates.count(nextState1.blocks)) { - break; - } + private: + bool move(int x, int y) { + wrap(x, y); - pastStates.insert(nextState1.blocks); - } + // Are we at the event? + if ((x == 15) && (y == 15)) { + return true; + } - if (!done.count(nextState1.blocks) || - !done.at(nextState1.blocks)[std::get<0>(newLoc) + - std::get<1>(newLoc) * WIDTH]) { - search.emplace_back(std::move(nextState1), newLoc, cns + 1); - } + // Can we even go there? + if (!board_->isObstructed(x, y)) { + playerx_ = x; + playery_ = y; } + + return false; } - return exists; -} + int level_; + std::unique_ptr board_; + int playerx_; + int playery_; +}; -std::string GameBoard::dump() const { - std::stringstream output; - output << std::hex; - for (int i = 0; i < WIDTH * HEIGHT / 4; i++) { - int chunk = (8 * blocks[i * 4]) + (4 * blocks[i * 4 + 1]) + - (2 * blocks[i * 4 + 2]) + blocks[i * 4 + 3]; - output << chunk; - } +std::unique_ptr startNewLevel(int level, + std::unique_ptr board, + int playerx, int playery) { + return std::make_unique(level, std::move(board), playerx, + playery); +} - return output.str(); +std::unique_ptr GameState::operator()(Game&) { + return std::make_unique(0); } diff --git a/gamestate.h b/gamestate.h index 32e5d07..9b0b52c 100644 --- a/gamestate.h +++ b/gamestate.h @@ -1,13 +1,14 @@ -#include - -#include "state.h" - #ifndef GAMESTATE_H #define GAMESTATE_H +#include + +#include "mazeoflife.h" +#include "state.h" + class GameState : public State { public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + std::unique_ptr operator()(Game& game); }; -#endif \ No newline at end of file +#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 @@ -#include "highscore.h" - -Highscore::Highscore(char* name, int level) { - this->name = name; - this->level = level; -} - -char* Highscore::getName() { return name; } - -int Highscore::getLevel() { return level; } - -void Highscore::setRank(int rank) { this->rank = rank; } - -int 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 @@ #ifndef HIGHSCORE_H #define HIGHSCORE_H +#include + class Highscore { public: - Highscore(char* name, int level); - char* getName(); - int getLevel(); - void setRank(int rank); - int getRank(); + Highscore() = default; + + Highscore(std::string name, int level) : name_(name), level_(level) {} + + const std::string getName() const { return name_; } + int getLevel() const { return level_; } + void setRank(int rank) { rank_ = rank; } + int getRank() const { return rank_; } private: - char* name; - int level; - int rank; + std::string name_; + int level_; + int rank_; }; #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 @@ +#include "hs_state.h" + +#include + +#include "gamestate.h" +#include "titlestate.h" +#include "util.h" + +DisplayLocalHighscoreListState::DisplayLocalHighscoreListState(Game& game) { + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); + + std::unique_ptr lhl = HighscoreList::GetLocalHighscores(); + + surface_ptr list_s = lhl->render(); + SDL_Color fontColor = {0, 0, 0, 0}; + font_ptr font = loadFont(40); + surface_ptr title = surface_ptr( + TTF_RenderText_Blended(font.get(), "Highscore List", fontColor)); + SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; + SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace); + + surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_rtm.bmp")); + SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; + SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace); + + list_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get())); +} + +std::unique_ptr DisplayLocalHighscoreListState::operator()(Game& game) { + SDL_Event e; + + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255); + SDL_RenderClear(game.renderer.get()); + SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), 137, 449); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if (e.key.keysym.sym == SDLK_RETURN) { + return std::make_unique(game); + } + } + } + + return nullptr; +} + +DisplayAndReturnLocalHighscoreListState:: + DisplayAndReturnLocalHighscoreListState(Game& game) { + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); + + std::unique_ptr lhl = HighscoreList::GetLocalHighscores(); + surface_ptr list_s = lhl->render(); + SDL_Color fontColor = {0, 0, 0, 0}; + font_ptr font = loadFont(40); + surface_ptr title = surface_ptr( + TTF_RenderText_Blended(font.get(), "Highscore List", fontColor)); + SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; + SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace); + + surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_paartm.bmp")); + SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; + SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace); + + list_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get())); +} + +std::unique_ptr DisplayAndReturnLocalHighscoreListState::operator()( + Game& game) { + SDL_Event e; + + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255); + SDL_RenderClear(game.renderer.get()); + SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 52 : 225, + 447); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) { + selection_--; + } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) { + selection_++; + } else if (e.key.keysym.sym == SDLK_RETURN) { + switch (selection_) { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(game); + } + } + } + } + + return nullptr; +} + +EnterHighscoreState::EnterHighscoreState(Game& game, int level) + : level_(level) { + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); + + // Render highscore list + std::unique_ptr lhl = HighscoreList::GetLocalHighscores(); + newpos_ = lhl->addHighscore(Highscore("", level_)); + + surface_ptr list_s = lhl->render(); + + SDL_Color fontColor = {0, 0, 0, 0}; + font_ptr bigfont = loadFont(40); + surface_ptr title = surface_ptr( + TTF_RenderText_Blended(bigfont.get(), "New Highscore!", fontColor)); + SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; + SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace); + + font_ptr font = loadFont(25); + surface_ptr text = surface_ptr( + TTF_RenderText_Blended(font.get(), "Enter Your Name", fontColor)); + SDL_Rect oSpace = {240 - (text->w / 2), 440, text->w, text->h}; + SDL_BlitSurface(text.get(), NULL, list_s.get(), &oSpace); + + list_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get())); + + int posw, posh; + std::ostringstream posstr; + posstr << newpos_ << ":"; + std::string pos = posstr.str(); + + surface_ptr newName_s = + surface_ptr(TTF_RenderText_Blended(font.get(), " ", fontColor)); + TTF_SizeText(bigfont.get(), pos.c_str(), &posw, &posh); + rntSpace_.x = posw; + rntSpace_.y = newpos_ * 40 + ((posh / 2) - (newName_s->h / 2)); + rntSpace_.w = newName_s->w; + rntSpace_.h = newName_s->h; + newName_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get())); +} + +std::unique_ptr EnterHighscoreState::operator()(Game& game) { + SDL_Event e; + + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255); + SDL_RenderClear(game.renderer.get()); + + SDL_Rect eSpace = {0, newpos_ * 40, 480, 40}; + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255); + SDL_RenderFillRect(game.renderer.get(), &eSpace); + + SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL); + SDL_RenderCopy(game.renderer.get(), newName_.get(), NULL, &rntSpace_); + + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_BACKSPACE) && (lp_ > 0)) { + --lp_; + hsname_.pop_back(); + + SDL_Color fontColor = {0, 0, 0, 0}; + std::ostringstream name; + name << " " << hsname_; + + font_ptr font = loadFont(25); + surface_ptr newName_s = surface_ptr( + TTF_RenderText_Blended(font.get(), name.str().c_str(), fontColor)); + rntSpace_.w = newName_s->w; + rntSpace_.h = newName_s->h; + newName_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get())); + } else if ((e.key.keysym.sym == SDLK_RETURN) && !hsname_.empty()) { + std::unique_ptr lhl = + HighscoreList::GetLocalHighscores(); + Highscore h2(hsname_, level_); + lhl->addHighscore(h2); + lhl->writeToFile(); + + return std::make_unique(game, h2); + } + } else if (e.type == SDL_TEXTINPUT) { + if (((*e.text.text & 0xFF80) == 0) && + (*e.text.text >= 32 && *e.text.text < 127) && (lp_ < 25)) { + lp_++; + hsname_.push_back(*e.text.text & 0x7f); + + SDL_Color fontColor = {0, 0, 0, 0}; + std::ostringstream name; + name << " " << hsname_; + + font_ptr font = loadFont(25); + surface_ptr newName_s = surface_ptr( + TTF_RenderText_Blended(font.get(), name.str().c_str(), fontColor)); + rntSpace_.w = newName_s->w; + rntSpace_.h = newName_s->h; + newName_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), newName_s.get())); + } + } + } + + return nullptr; +} + +NewHighscoreState::NewHighscoreState(Game& game, Highscore h) : h_(h) { + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); + + // Render highscore list + std::unique_ptr lhl = HighscoreList::GetLocalHighscores(); + surface_ptr list_s = lhl->render(); + + SDL_Color fontColor = {0, 0, 0, 0}; + font_ptr bigfont = loadFont(40); + surface_ptr title = surface_ptr( + TTF_RenderText_Blended(bigfont.get(), "New Highscore!", fontColor)); + SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; + SDL_BlitSurface(title.get(), NULL, list_s.get(), &tSpace); + + surface_ptr options_s = surface_ptr(SDL_LoadBMP("resources/hlo_paartm.bmp")); + SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; + SDL_BlitSurface(options_s.get(), NULL, list_s.get(), &oSpace); + + list_ = texture_ptr( + SDL_CreateTextureFromSurface(game.renderer.get(), list_s.get())); +} + +std::unique_ptr NewHighscoreState::operator()(Game& game) { + SDL_Event e; + + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 255, 255); + SDL_RenderClear(game.renderer.get()); + + SDL_Rect eSpace = {0, h_.getRank() * 40, 480, 40}; + SDL_SetRenderDrawColor(game.renderer.get(), 255, 255, 0, 255); + SDL_RenderFillRect(game.renderer.get(), &eSpace); + + SDL_RenderCopy(game.renderer.get(), list_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 52 : 225, + 447); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) { + selection_--; + } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) { + selection_++; + } else if (e.key.keysym.sym == SDLK_RETURN) { + switch (selection_) { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(game); + } + } + } + } + + return nullptr; +} 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 @@ +#ifndef HS_STATE_H_C824D037 +#define HS_STATE_H_C824D037 + +#include + +#include "hslist.h" +#include "mazeoflife.h" +#include "sdl.h" +#include "state.h" + +class DisplayLocalHighscoreListState : public State { + public: + explicit DisplayLocalHighscoreListState(Game& game); + + std::unique_ptr operator()(Game& game); + + private: + texture_ptr list_; + texture_ptr pointer_; +}; + +class DisplayAndReturnLocalHighscoreListState : public State { + public: + explicit DisplayAndReturnLocalHighscoreListState(Game& game); + + std::unique_ptr operator()(Game& game); + + private: + texture_ptr list_; + texture_ptr pointer_; + int selection_ = 0; +}; + +class EnterHighscoreState : public State { + public: + EnterHighscoreState(Game& game, int level); + + std::unique_ptr operator()(Game& game); + + private: + int level_; + int lp_ = 0; + std::string hsname_; + texture_ptr newName_; + texture_ptr pointer_; + texture_ptr list_; + SDL_Rect rntSpace_; + int newpos_; +}; + +class NewHighscoreState : public State { + public: + NewHighscoreState(Game& game, Highscore h); + + std::unique_ptr operator()(Game& game); + + private: + Highscore h_; + texture_ptr pointer_; + texture_ptr list_; + int selection_ = 0; +}; + +#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 @@ #include #include +#include #include #include "gamestate.h" #include "titlestate.h" #include "util.h" -// We want to be able to sort Highscore objects in descending score order -struct hslist_comp { - bool operator()(Highscore* lhs, Highscore* rhs) const { - return (lhs->getLevel() > rhs->getLevel()); - } -} hslist_comp_i; +HighscoreList::HighscoreList(std::vector hslist) : hslist_(hslist) { + resetRanks(); +} -// resetRanks : sets the rank of all Highscore objects in a hslist_t to their -// (one-based) index in the list -void resetRanks(hslist_t in) { +void HighscoreList::resetRanks() { int i = 1; - for (hslist_t::iterator it = in.begin(); it != in.end(); ++it, ++i) { - ((Highscore*)*it)->setRank(i); + for (Highscore& hs : hslist_) { + hs.setRank(i++); } } -SDL_Surface* HighscoreList::render() { - SDL_Surface* tmp = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); +surface_ptr HighscoreList::render() { + surface_ptr tmp = + surface_ptr(SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0)); Uint32 bgColor = SDL_MapRGB(tmp->format, 255, 255, 255); - SDL_FillRect(tmp, NULL, bgColor); - SDL_SetColorKey(tmp, SDL_TRUE, bgColor); - TTF_Font* posFont = loadFont(40); - TTF_Font* dataFont = loadFont(25); + SDL_FillRect(tmp.get(), NULL, bgColor); + SDL_SetColorKey(tmp.get(), SDL_TRUE, bgColor); + font_ptr posFont = loadFont(40); + font_ptr dataFont = loadFont(25); SDL_Color fontColor = {0, 0, 0, 0}; - int i = 0; - for (hslist_t::iterator it = hslist.begin(); it != hslist.end(); ++it, ++i) { - Highscore* h = *it; - + for (const Highscore& h : hslist_) { int posw, posh; - char pos[4]; // 2 max characters in rank plus the colon at the end, plus - // terminator - sprintf(pos, "%d:", h->getRank()); - TTF_SizeText(posFont, pos, &posw, &posh); - SDL_Rect posSpace = {0, (i + 1) * 40, posw, posh}; - SDL_BlitSurface(TTF_RenderText_Blended(posFont, pos, fontColor), NULL, tmp, - &posSpace); + + std::ostringstream posstr; + posstr << h.getRank() << ":"; + + std::string pos = posstr.str(); + TTF_SizeText(posFont.get(), pos.c_str(), &posw, &posh); + SDL_Rect posSpace = {0, h.getRank() * 40, posw, posh}; + SDL_BlitSurface( + TTF_RenderText_Blended(posFont.get(), pos.c_str(), fontColor), NULL, + tmp.get(), &posSpace); + + std::ostringstream namestr; + namestr << " " << h.getName(); int namew, nameh; - char name[27]; // 25 max characters in username plus the space at the - // beginning, plus terminator - sprintf(name, " %s", h->getName()); - TTF_SizeText(dataFont, name, &namew, &nameh); - SDL_Rect nameSpace = {posw, (i + 1) * 40 + ((posh / 2) - (nameh / 2)), + std::string name = namestr.str(); + TTF_SizeText(dataFont.get(), name.c_str(), &namew, &nameh); + SDL_Rect nameSpace = {posw, h.getRank() * 40 + ((posh / 2) - (nameh / 2)), namew, nameh}; - SDL_BlitSurface(TTF_RenderText_Blended(dataFont, name, fontColor), NULL, - tmp, &nameSpace); + SDL_BlitSurface( + TTF_RenderText_Blended(dataFont.get(), name.c_str(), fontColor), NULL, + tmp.get(), &nameSpace); int lvlw, lvlh; - char lvl[11]; // 10 max characters in level (based off the fact that 2^32-1 - // is 10 characters long, and is the highest int), plus - // terminator - sprintf(lvl, "%d", h->getLevel()); - TTF_SizeText(dataFont, lvl, &lvlw, &lvlh); - SDL_Rect lvlSpace = {480 - lvlw, (i + 1) * 40 + ((posh / 2) - (nameh / 2)), - lvlw, lvlh}; - SDL_BlitSurface(TTF_RenderText_Blended(dataFont, lvl, fontColor), NULL, tmp, - &lvlSpace); + std::string lvl = std::to_string(h.getLevel()); + TTF_SizeText(dataFont.get(), lvl.c_str(), &lvlw, &lvlh); + SDL_Rect lvlSpace = { + 480 - lvlw, h.getRank() * 40 + ((posh / 2) - (nameh / 2)), lvlw, lvlh}; + SDL_BlitSurface( + TTF_RenderText_Blended(dataFont.get(), lvl.c_str(), fontColor), NULL, + tmp.get(), &lvlSpace); } return tmp; } -hslist_t HighscoreList::getLocalHighscores() { - hslist_t temp; +std::unique_ptr HighscoreList::GetLocalHighscores() { + std::vector hslist; - std::ifstream exists(getDataFile()); - if (exists) { - FILE* hslist = fopen(getDataFile(), "r"); - int scores; - fscanf(hslist, "%d%*c", &scores); + std::ifstream hsstream(getDataFile()); + if (hsstream) { + int num_scores = 0; + hsstream >> num_scores; - for (int i = 0; i < scores; i++) { - int namelen; - char namelens[4]; - char* name = (char*)calloc(25, sizeof(char)); + for (int i = 0; i < num_scores; i++) { + std::string name; int score; - fscanf(hslist, "%d", &namelen); - sprintf(namelens, "%%%dc", namelen); - fscanf(hslist, namelens, name); - fscanf(hslist, "%d%*c", &score); - - Highscore* h = new Highscore(name, score); - h->setRank(i + 1); + hsstream >> name; + hsstream >> score; - temp.push_back(h); + hslist.emplace_back(name, score); } - - fclose(hslist); } - return temp; + return std::unique_ptr(new HighscoreList(hslist)); } -hslist_t HighscoreList::getGlobalHighscores() { - IPaddress ipaddress; - - if (SDLNet_ResolveHost(&ipaddress, "other.fourisland.com", 80) == -1) { - printf("Could not resolve host \"other.fourisland.com\": %s\n", - SDLNet_GetError()); - throw 1; - } - - TCPsocket tcpsock = SDLNet_TCP_Open(&ipaddress); - if (!tcpsock) { - printf("Could not connect to host \"other.fourisland.com\": %s\n", - SDLNet_GetError()); - throw 2; - } - - const char* headers = - "GET /mol/hslist.php HTTP/1.1\nHost: other.fourisland.com\nUser-Agent: " - "Maze Of Life v3.0\nAccept: text/plain\nKeep-Alive: 300\nConnection: " - "keep-alive\n\n"; - if (SDLNet_TCP_Send(tcpsock, headers, strlen(headers) + 1) < - strlen(headers)) { - printf("Connection closed by peer: %s\n", SDLNet_GetError()); - throw 3; - } - - std::stringstream download(std::stringstream::in | std::stringstream::out); - char hslist[1024]; - SDLNet_TCP_Recv(tcpsock, hslist, 1024); - download << hslist; - SDLNet_TCP_Close(tcpsock); - - char temps[256]; - download.getline(temps, 256); - while (strlen(temps) != 1) { - download.getline(temps, 256); - } - - hslist_t temp; - int scores; - download.getline(temps, 256); - if (sscanf(temps, "%d%*c", &scores) != 1) { - printf("Recieved data is of an invalid format: %s\n", temps); - throw 4; - } - - for (int i = 0; i < scores; i++) { - int namelen; - char namelens[13]; - char* name = (char*)calloc(25, sizeof(char)); - int score; - download.getline(temps, 256); - - if (sscanf(temps, "%d", &namelen) != 1) { - printf("Recieved data is of an invalid format (1-%d): %s\n", i, temps); - throw 4; - } - - sprintf(namelens, "%%*d%%%dc", namelen); - - if (sscanf(temps, namelens, name) != 1) { - printf("Recieved data is of an invalid format (2-%d): %s\n", i, temps); - throw 4; - } - - sprintf(namelens, "%%*d%%*%dc%%d", namelen); - - if (sscanf(temps, namelens, &score) != 1) { - printf("Recieved data is of an invalid format (3-%d): %s\n", i, temps); - throw 4; - } - - Highscore* h = new Highscore(name, score); - h->setRank(i + 1); - - temp.push_back(h); - } - - return temp; -} - -LocalHighscoreList::LocalHighscoreList() { - this->hslist = getLocalHighscores(); -} - -int LocalHighscoreList::addHighscore(Highscore* h) { - hslist.push_back(h); - std::sort(hslist.begin(), hslist.end(), hslist_comp_i); - resetRanks(hslist); - - if (hslist.size() > 10) { - hslist.resize(10); - } - - return h->getRank(); -} - -void LocalHighscoreList::writeHighscores() { - FILE* hsfile = fopen(getDataFile(), "w"); - fprintf(hsfile, "%d ", (int)this->hslist.size()); - - for (hslist_t::iterator it = hslist.begin(); it != this->hslist.end(); it++) { - Highscore* h = *it; - - fprintf(hsfile, "%d%s%d ", (int)strlen(h->getName()), h->getName(), - h->getLevel()); - } - - fclose(hsfile); -} - -GlobalHighscoreList::GlobalHighscoreList() { - fail = false; - - try { - this->hslist = getGlobalHighscores(); - } catch (int e) { - fail = true; - } -} - -GlobalHighscoreList::GlobalHighscoreList(Highscore* h) { - fail = false; - - try { - IPaddress ipaddress; - - if (SDLNet_ResolveHost(&ipaddress, "other.fourisland.com", 80) == -1) { - printf("Could not resolve host \"other.fourisland.com\": %s\n", - SDLNet_GetError()); - throw 1; - } - - TCPsocket tcpsock = SDLNet_TCP_Open(&ipaddress); - if (!tcpsock) { - printf("Could not connect to host \"other.fourisland.com\": %s\n", - SDLNet_GetError()); - throw 2; - } - - char body[256]; - sprintf(body, "name=%s&level=%d", h->getName(), h->getLevel()); - char headers[256]; - sprintf( - headers, - "POST /mol/hslist.php?add HTTP/1.1\nHost: " - "other.fourisland.com\nUser-Agent: Maze Of Life v2.0\nAccept: " - "text/plain\nKeep-Alive: 300\nConnection: keep-alive\nContent-Type: " - "application/x-www-form-urlencoded\nContent-Length: %d\n\n%s\n", - (int)strlen(body), body); - if (SDLNet_TCP_Send(tcpsock, headers, strlen(headers) + 1) < - strlen(headers)) { - printf("Connection closed by peer: %s\n", SDLNet_GetError()); - throw 3; - } - - std::stringstream download(std::stringstream::in | std::stringstream::out); - char hslist[1024]; - SDLNet_TCP_Recv(tcpsock, hslist, 1024); - download << hslist; - SDLNet_TCP_Close(tcpsock); - - char temps[256]; - download.getline(temps, 256); - while (strlen(temps) != 1) { - download.getline(temps, 256); - } - - int rank; - download.getline(temps, 256); - if (sscanf(temps, "%d%*c", &rank) != 1) { - printf("Recieved data is of an invalid format: %s\n", temps); - throw 4; - } - - this->hslist = getGlobalHighscores(); - - if (this->hslist.empty()) { - printf( - "Global Highscore List cannot be empty after adding a score to " - "it.\n"); - throw 5; - } - - if (rank > 10) { - h->setRank(rank); - - this->hslist[9] = h; - } else { - this->hslist.push_back(h); - std::sort(this->hslist.begin(), this->hslist.end(), hslist_comp_i); - resetRanks(this->hslist); - - if (this->hslist.size() > 10) { - this->hslist.resize(10); - } - } - } catch (int e) { - fail = true; - } -} - -SDL_Surface* GlobalHighscoreList::render() { - if (fail) { - SDL_Surface* tmp = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); - Uint32 bgColor = SDL_MapRGB(tmp->format, 255, 255, 255); - SDL_FillRect(tmp, NULL, bgColor); - SDL_SetColorKey(tmp, SDL_TRUE, bgColor); - TTF_Font* dataFont = loadFont(25); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* text = TTF_RenderText_Blended( - dataFont, "Error retrieving highscores", fontColor); - SDL_Rect tSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w, - text->h}; - SDL_BlitSurface(text, NULL, tmp, &tSpace); - - return tmp; - } else { - return super::render(); - } -} - -bool GlobalHighscoreList::didFail() { return fail; } - -State* ChooseHighscoreListState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* background = loadImage(renderer, "resources/chl.bmp"); - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; - SDL_Event e; - - for (;;) { - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, background, NULL, NULL); - applyTexture(renderer, pointer, 127, - selection == 0 ? 306 : (selection == 1 ? 336 : 396)); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_UP) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection != 2)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new DisplayLocalHighscoreListState(); - case 1: - return new DisplayGlobalHighscoreListState(); - case 2: - return new TitleState(); - } - } - } - } - } -} - -State* DisplayLocalHighscoreListState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - - LocalHighscoreList* lhl = new LocalHighscoreList(); - SDL_Surface* list_s = lhl->render(); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - SDL_Event e; - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, 137, 449); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if (e.key.keysym.sym == SDLK_RETURN) { - return new ChooseHighscoreListState(); - } - } - } - } -} - -State* DisplayAndReturnLocalHighscoreListState::operator()( - SDL_Window* window, SDL_Renderer* renderer) { - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - - LocalHighscoreList* lhl = new LocalHighscoreList(); - SDL_Surface* list_s = lhl->render(); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_paartm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - int selection = 0; - SDL_Event e; - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, selection == 0 ? 52 : 225, 447); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new GameState(); - case 1: - return new TitleState(); - } - } - } - } - } -} - -State* DisplayGlobalHighscoreListState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - - // Display loading message - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - - SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); - Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255); - SDL_FillRect(list_s, NULL, bgColor); - SDL_SetColorKey(list_s, SDL_TRUE, bgColor); - TTF_Font* dataFont = loadFont(25); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* text = - TTF_RenderText_Blended(dataFont, "Fetching highscores....", fontColor); - SDL_Rect aSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w, - text->h}; - SDL_BlitSurface(text, NULL, list_s, &aSpace); - SDL_FreeSurface(text); - - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - m = SDL_CreateMutex(); - - // Start downloading scores - SDL_CreateThread(&LoadHighscoreList, "LoadHighscoreList", this); - - // Parse keyboard events - SDL_Event e; - - for (;;) { - if (SDL_LockMutex(m) == 0) { - if (lhl != NULL) { - SDL_Surface* list_s = lhl->render(); - - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_rtm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - lhl = NULL; - } - - SDL_UnlockMutex(m); - } - - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, 137, 449); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - SDL_DestroyMutex(m); - - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if (e.key.keysym.sym == SDLK_RETURN) { - SDL_DestroyMutex(m); - - return new ChooseHighscoreListState(); - } - } - } - } -} - -int DisplayGlobalHighscoreListState::LoadHighscoreList(void* pParam) { - DisplayGlobalHighscoreListState* parent = - ((DisplayGlobalHighscoreListState*)pParam); - if (SDL_LockMutex(parent->m) == 0) { - parent->lhl = new GlobalHighscoreList(); - - SDL_UnlockMutex(parent->m); - } else { - printf("Couldn't lock mutex: %s\n", SDL_GetError()); - } -} - -EnterHighscoreState::EnterHighscoreState(int level) { this->level = level; } - -State* EnterHighscoreState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - - // Render highscore list - LocalHighscoreList* lhl = new LocalHighscoreList(); - char* emp = new char[1]; - emp[0] = 0; - Highscore* h = new Highscore(emp, level); - int newpos = lhl->addHighscore(h); - - SDL_Surface* list_s = lhl->render(); - - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "New Highscore!", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - this->lp = 0; - this->hsname = (char*)calloc(25, sizeof(char)); - - SDL_Surface* text = - TTF_RenderText_Blended(loadFont(25), "Enter Your Name", fontColor); - SDL_Rect oSpace = {240 - (text->w / 2), 440, text->w, text->h}; - SDL_BlitSurface(text, NULL, list_s, &oSpace); - SDL_FreeSurface(text); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - int selection = 0; - SDL_Event e; - - int posw, posh; - char pos[3]; // 2 max characters in rank plus the colon at the end - sprintf(pos, "%d:", newpos); - char name[26]; // 25 max characters in username plus the space at the - // beginning - sprintf(name, " %s", hsname); - SDL_Surface* newName_s = - TTF_RenderText_Blended(loadFont(25), name, fontColor); - TTF_SizeText(loadFont(40), pos, &posw, &posh); - SDL_Rect rntSpace; - rntSpace.x = posw; - rntSpace.y = newpos * 40 + ((posh / 2) - (newName_s->h / 2)); - rntSpace.w = newName_s->w; - rntSpace.h = newName_s->h; - newName = SDL_CreateTextureFromSurface(renderer, newName_s); - SDL_FreeSurface(newName_s); - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - - SDL_Rect eSpace = {0, newpos * 40, 480, 40}; - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - SDL_RenderFillRect(renderer, &eSpace); - - SDL_RenderCopy(renderer, list, NULL, NULL); - SDL_RenderCopy(renderer, newName, NULL, &rntSpace); - - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_BACKSPACE) && (lp > 0)) { - hsname[--lp] = 0; - - SDL_Color fontColor = {0, 0, 0, 0}; - char name[26]; // 25 max characters in username plus the space at the - // beginning - sprintf(name, " %s", hsname); - newName_s = TTF_RenderText_Blended(loadFont(25), name, fontColor); - rntSpace.w = newName_s->w; - rntSpace.h = newName_s->h; - newName = SDL_CreateTextureFromSurface(renderer, newName_s); - SDL_FreeSurface(newName_s); - } else if ((e.key.keysym.sym == SDLK_RETURN) && (hsname[0] != 0)) { - lhl = new LocalHighscoreList(); - Highscore* h2 = new Highscore(hsname, level); - lhl->addHighscore(h2); - lhl->writeHighscores(); - - return new NewHighscoreState(h2); - } - } else if (e.type == SDL_TEXTINPUT) { - if (((*e.text.text & 0xFF80) == 0) && - (*e.text.text >= 32 && *e.text.text < 127) && (lp < 25)) { - hsname[lp++] = *e.text.text & 0x7f; - hsname[lp] = 0; - - SDL_Color fontColor = {0, 0, 0, 0}; - char name[26]; // 25 max characters in username plus the space at the - // beginning - sprintf(name, " %s", hsname); - newName_s = TTF_RenderText_Blended(loadFont(25), name, fontColor); - rntSpace.w = newName_s->w; - rntSpace.h = newName_s->h; - newName = SDL_CreateTextureFromSurface(renderer, newName_s); - SDL_FreeSurface(newName_s); - } - } - } - } -} - -NewHighscoreState::NewHighscoreState(Highscore* h) { this->h = h; } - -State* NewHighscoreState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - - // Render highscore list - LocalHighscoreList* lhl = new LocalHighscoreList(); - SDL_Surface* list_s = lhl->render(); - - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "New Highscore!", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_passartm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - int selection = 0; - SDL_Event e; - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - - SDL_Rect eSpace = {0, h->getRank() * 40, 480, 40}; - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - SDL_RenderFillRect(renderer, &eSpace); - - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, - selection == 0 ? 13 : (selection == 1 ? 138 : 284), 448); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 2)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new GameState(); - case 1: - return new SubmitHighscoreState(h); - case 2: - return new TitleState(); - } - } - } - } - } -} - -SubmitHighscoreState::SubmitHighscoreState(Highscore* h) { this->h = h; } - -State* SubmitHighscoreState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); - Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255); - SDL_FillRect(list_s, NULL, bgColor); - SDL_SetColorKey(list_s, SDL_TRUE, bgColor); - TTF_Font* dataFont = loadFont(25); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* text = - TTF_RenderText_Blended(dataFont, "Sending highscore....", fontColor); - SDL_Rect aSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w, - text->h}; - SDL_BlitSurface(text, NULL, list_s, &aSpace); - SDL_FreeSurface(text); - - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - // Start submitting score - m = SDL_CreateMutex(); - SDL_CreateThread(&SubmitHighscore, "SubmitHighscore", this); - - SDL_Event e; - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, list, NULL, NULL); - SDL_RenderPresent(renderer); - - if (SDL_LockMutex(m) == 0) { - if (lhl != NULL) { - SDL_UnlockMutex(m); - SDL_DestroyMutex(m); - - if (lhl->didFail()) { - return new FailedSubmittingHighscoreState(h); - } else { - return new SubmittedHighscoreState(lhl, h); - } - } else { - SDL_UnlockMutex(m); - } - } - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - SDL_DestroyMutex(m); +int HighscoreList::addHighscore(Highscore h) { + int i = 1; - return NULL; - } + for (const Highscore& cur_h : hslist_) { + if (h.getLevel() > cur_h.getLevel()) { + break; } + i++; } -} - -int SubmitHighscoreState::SubmitHighscore(void* pParam) { - SubmitHighscoreState* parent = (SubmitHighscoreState*)pParam; - if (SDL_LockMutex(parent->m) == 0) { - parent->lhl = new GlobalHighscoreList(parent->h); - - SDL_UnlockMutex(parent->m); - } else { - printf("Could not lock mutex: %s\n", SDL_GetError()); - } -} - -FailedSubmittingHighscoreState::FailedSubmittingHighscoreState(Highscore* h) { - this->h = h; -} - -State* FailedSubmittingHighscoreState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Surface* list_s = SDL_CreateRGBSurface(0, 480, 480, 32, 0, 0, 0, 0); - Uint32 bgColor = SDL_MapRGB(list_s->format, 255, 255, 255); - SDL_FillRect(list_s, NULL, bgColor); - SDL_SetColorKey(list_s, SDL_TRUE, bgColor); - TTF_Font* dataFont = loadFont(25); - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* text = TTF_RenderText_Blended( - dataFont, "Error submitting highscores", fontColor); - SDL_Rect tSpace = {240 - (text->w / 2), 240 - (text->h / 2), text->w, - text->h}; - SDL_BlitSurface(text, NULL, list_s, &tSpace); - SDL_FreeSurface(text); - - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect aSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &aSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_passartm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; - SDL_Event e; - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, - selection == 0 ? 13 : (selection == 1 ? 138 : 284), 448); - SDL_RenderPresent(renderer); + hslist_.push_back(h); + std::sort(hslist_.begin(), hslist_.end(), + [](const Highscore& lhs, const Highscore& rhs) { + return lhs.getLevel() > rhs.getLevel(); + }); + resetRanks(); - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 2)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new GameState(); - case 1: - return new SubmitHighscoreState(h); - case 2: - return new TitleState(); - } - } - } - } + if (hslist_.size() > 10) { + hslist_.resize(10); } -} -SubmittedHighscoreState::SubmittedHighscoreState(GlobalHighscoreList* lhl, - Highscore* h) { - this->lhl = lhl; - this->h = h; + return i; } -State* SubmittedHighscoreState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Surface* list_s = lhl->render(); - - SDL_Color fontColor = {0, 0, 0, 0}; - SDL_Surface* title = - TTF_RenderText_Blended(loadFont(40), "Highscore List", fontColor); - SDL_Rect tSpace = {240 - (title->w / 2), 0, title->w, title->h}; - SDL_BlitSurface(title, NULL, list_s, &tSpace); - SDL_FreeSurface(title); - - SDL_Surface* options_s = SDL_LoadBMP("resources/hlo_paartm.bmp"); - SDL_Rect oSpace = {0, 440, options_s->w, options_s->h}; - SDL_BlitSurface(options_s, NULL, list_s, &oSpace); - SDL_FreeSurface(options_s); - - SDL_Texture* list = SDL_CreateTextureFromSurface(renderer, list_s); - SDL_FreeSurface(list_s); - - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; - SDL_Event e; +void HighscoreList::writeToFile() { + std::ofstream hsfile(getDataFile()); + hsfile << hslist_.size() << std::endl; - int newpos = h->getRank(); - if (newpos > 10) { - newpos = 10; - } - - for (;;) { - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderClear(renderer); - - SDL_Rect eSpace = {0, newpos * 40, 480, 40}; - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - SDL_RenderFillRect(renderer, &eSpace); - - SDL_RenderCopy(renderer, list, NULL, NULL); - applyTexture(renderer, pointer, selection == 0 ? 52 : 225, 447); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new GameState(); - case 1: - return new TitleState(); - } - } - } - } + for (const Highscore& h : hslist_) { + hsfile << h.getName() << std::endl << h.getLevel() << std::endl; } } diff --git a/hslist.h b/hslist.h index efffa9d..0636183 100644 --- a/hslist.h +++ b/hslist.h @@ -1,130 +1,34 @@ +#ifndef HSLIST_H +#define HSLIST_H + #include +#include #include #include "highscore.h" +#include "sdl.h" #include "state.h" -#ifndef HSLIST_H -#define HSLIST_H - -typedef std::vector hslist_t; - -void resetRanks(hslist_t in); - class HighscoreList { public: - SDL_Surface* render(); - - protected: - hslist_t getLocalHighscores(); - hslist_t getGlobalHighscores(); - - hslist_t hslist; -}; - -class LocalHighscoreList : public HighscoreList { - public: - LocalHighscoreList(); - int addHighscore(Highscore* h); - void writeHighscores(); -}; - -class GlobalHighscoreList : public HighscoreList { - public: - GlobalHighscoreList(); - GlobalHighscoreList(Highscore* h); - SDL_Surface* render(); - bool didFail(); - - private: - typedef HighscoreList super; - - protected: - bool fail; -}; - -class ChooseHighscoreListState : public State { - public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); -}; - -class DisplayLocalHighscoreListState : public State { - public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); -}; - -class DisplayAndReturnLocalHighscoreListState : public State { - public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); -}; - -class DisplayGlobalHighscoreListState : public State { - public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); - - protected: - SDL_Surface* list_s; - SDL_Texture* list; - GlobalHighscoreList* lhl; - SDL_mutex* m; - - private: - static int LoadHighscoreList(void* pParam); -}; - -class EnterHighscoreState : public State { - public: - EnterHighscoreState(int level); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); - - private: - int level; - int lp; - char* hsname; - SDL_Texture* newName; -}; - -class NewHighscoreState : public State { - public: - NewHighscoreState(Highscore* h); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + static std::unique_ptr GetLocalHighscores(); + static std::unique_ptr GetGlobalHighscores(); - private: - Highscore* h; -}; + surface_ptr render(); -class SubmitHighscoreState : public State { - public: - SubmitHighscoreState(Highscore* h); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + const std::vector& getList() const { return hslist_; } - protected: - Highscore* h; - SDL_mutex* m; - GlobalHighscoreList* lhl; + int addHighscore(Highscore h); - private: - static int SubmitHighscore(void* pParam); -}; - -class FailedSubmittingHighscoreState : public State { - public: - FailedSubmittingHighscoreState(Highscore* h); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + void writeToFile(); private: - Highscore* h; -}; + explicit HighscoreList(std::vector hslist); -class SubmittedHighscoreState : public State { - public: - SubmittedHighscoreState(GlobalHighscoreList* lhl, Highscore* h); - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + void resetRanks(); - private: - GlobalHighscoreList* lhl; - Highscore* h; + std::vector hslist_; }; #endif diff --git a/mazeoflife.cpp b/mazeoflife.cpp index 952fbdb..3afff78 100644 --- a/mazeoflife.cpp +++ b/mazeoflife.cpp @@ -1,5 +1,9 @@ #include "mazeoflife.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + #include #include #include @@ -7,53 +11,58 @@ #include #include #include +#include +#include "sdl.h" #include "state.h" #include "titlestate.h" -int main(int argc, char* argv[]) { - srand(time(NULL)); +static void main_loop(void* arg) { + Game& game = *static_cast(arg); - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) { - printf("Could not initialize SDL: %s.\n", SDL_GetError()); - exit(-1); + std::unique_ptr next_state = (*game.state)(game); + if (next_state) { + game.state = std::move(next_state); } - if (TTF_Init() == -1) { - printf("Could not initialize SDL_ttf: %s.\n", TTF_GetError()); - exit(-1); + if (game.should_quit) { +#ifdef __EMSCRIPTEN__ + emscripten_cancel_main_loop(); +#else + exit(0); +#endif } +} - if (SDLNet_Init() == -1) { - printf("Cound not initalize SDL_net: %s.\n", SDLNet_GetError()); - exit(-1); - } +int main(int, char**) { + Game game; + + std::random_device randomEngine; + game.rng = std::mt19937(randomEngine()); - SDL_Window* window = - SDL_CreateWindow("Maze of Life", 100, 100, 480, 480, SDL_WINDOW_SHOWN); - if (window == NULL) { - std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl; - return 1; + game.window = window_ptr( + SDL_CreateWindow("Maze of Life", 100, 100, 480, 480, SDL_WINDOW_SHOWN)); + if (!game.window) { + throw sdl_error(); } SDL_Surface* icon = SDL_LoadBMP("resources/icon.bmp"); - SDL_SetWindowIcon(window, icon); + SDL_SetWindowIcon(game.window.get(), icon); - SDL_Renderer* renderer = SDL_CreateRenderer( - window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == NULL) { - std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl; - return 1; + game.renderer = renderer_ptr( + SDL_CreateRenderer(game.window.get(), -1, + SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC)); + if (!game.renderer) { + throw sdl_error(); } - State* state = new TitleState(); - while (state != NULL) { - state = (*state)(window, renderer); - } + game.state = std::make_unique(game); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDLNet_Quit(); - TTF_Quit(); - SDL_Quit(); -} \ No newline at end of file +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop_arg(main_loop, &game, 0, 1); +#else + for (;;) { + main_loop(&game); + } +#endif +} diff --git a/mazeoflife.h b/mazeoflife.h index 3cc6d6d..8a31d07 100644 --- a/mazeoflife.h +++ b/mazeoflife.h @@ -1,7 +1,28 @@ #ifndef MAZEOFLIFE_H #define MAZEOFLIFE_H +#include +#include + +#include "sdl.h" +#include "state.h" + const int WIDTH = 30; const int HEIGHT = 30; +struct Game { + std::mt19937 rng; + + sdl_wrapper sdl; + ttf_wrapper ttf; + net_wrapper net; + + window_ptr window; + renderer_ptr renderer; + + std::unique_ptr state; + + bool should_quit = false; +}; + #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 @@ +#ifndef SDL_H_A2226476 +#define SDL_H_A2226476 + +#include +#include +#include + +#include + +class sdl_error : public std::logic_error { + public: + sdl_error() : std::logic_error(SDL_GetError()) {} +}; + +class ttf_error : public std::logic_error { + public: + ttf_error() : std::logic_error(TTF_GetError()) {} +}; + +class net_error : public std::logic_error { + public: + net_error() : std::logic_error(SDLNet_GetError()) {} +}; + +class sdl_wrapper { + public: + sdl_wrapper() { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) { + sdl_error ex; + SDL_Quit(); + + throw ex; + } + } + + ~sdl_wrapper() { SDL_Quit(); } +}; + +class ttf_wrapper { + public: + ttf_wrapper() { + if (TTF_Init() == -1) { + ttf_error ex; + TTF_Quit(); + + throw ex; + } + } + + ~ttf_wrapper() { TTF_Quit(); } +}; + +class net_wrapper { + public: + net_wrapper() { + if (SDLNet_Init() == -1) { + net_error ex; + SDLNet_Quit(); + + throw ex; + } + } + + ~net_wrapper() { SDLNet_Quit(); } +}; + +class window_deleter { + public: + void operator()(SDL_Window* ptr) { SDL_DestroyWindow(ptr); } +}; + +using window_ptr = std::unique_ptr; + +class renderer_deleter { + public: + void operator()(SDL_Renderer* ptr) { SDL_DestroyRenderer(ptr); } +}; + +using renderer_ptr = std::unique_ptr; + +class surface_deleter { + public: + void operator()(SDL_Surface* ptr) { SDL_FreeSurface(ptr); } +}; + +using surface_ptr = std::unique_ptr; + +class texture_deleter { + public: + void operator()(SDL_Texture* ptr) { SDL_DestroyTexture(ptr); } +}; + +using texture_ptr = std::unique_ptr; + +class font_deleter { + public: + void operator()(TTF_Font* ptr) { TTF_CloseFont(ptr); } +}; + +using font_ptr = std::unique_ptr; + +#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 @@ -#include - #ifndef STATE_H #define STATE_H +#include + +class Game; + class State { public: - virtual State* operator()(SDL_Window* window, SDL_Renderer* renderer) { - return NULL; - }; + virtual std::unique_ptr operator()(Game& game) { return {}; }; }; -#endif \ No newline at end of file +#endif diff --git a/titlestate.cpp b/titlestate.cpp index 166a55b..b68e8fa 100644 --- a/titlestate.cpp +++ b/titlestate.cpp @@ -1,116 +1,124 @@ #include "titlestate.h" #include "gamestate.h" -#include "hslist.h" +#include "hs_state.h" #include "util.h" -State* TitleState::operator()(SDL_Window* window, SDL_Renderer* renderer) { - SDL_Texture* background = loadImage(renderer, "resources/title.bmp"); - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; +TitleState::TitleState(Game& game) { + background_ = loadImage(game.renderer.get(), "resources/title.bmp"); + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); +} + +std::unique_ptr TitleState::operator()(Game& game) { SDL_Event e; - for (;;) { - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, background, NULL, NULL); - applyTexture(renderer, pointer, 136, - selection == 0 - ? 316 - : (selection == 1 ? 350 : (selection == 2 ? 381 : 417))); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_UP) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection != 3)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new GameState(); - case 1: - return new HowToPlayState(); - case 2: - return new ChooseHighscoreListState(); - case 3: - return NULL; + SDL_RenderClear(game.renderer.get()); + SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), 136, + selection_ == 0 + ? 316 + : (selection_ == 1 ? 350 : (selection_ == 2 ? 381 : 417))); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_UP) && (selection_ != 0)) { + selection_--; + } else if ((e.key.keysym.sym == SDLK_DOWN) && (selection_ != 3)) { + selection_++; + } else if (e.key.keysym.sym == SDLK_RETURN) { + switch (selection_) { + case 0: + return std::make_unique(); + case 1: + return std::make_unique(game); + case 2: + return std::make_unique(game); + case 3: { + game.should_quit = true; + break; } } } } } + + return nullptr; } -State* HowToPlayState::operator()(SDL_Window* window, SDL_Renderer* renderer) { - SDL_Texture* background = loadImage(renderer, "resources/htp1.bmp"); - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; +HowToPlayState::HowToPlayState(Game& game) { + background_ = loadImage(game.renderer.get(), "resources/htp1.bmp"); + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); +} + +std::unique_ptr HowToPlayState::operator()(Game& game) { SDL_Event e; - for (;;) { - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, background, NULL, NULL); - applyTexture(renderer, pointer, selection == 0 ? 74 : 216, 430); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new HowToPlayPageTwoState(); - - break; - case 1: - return new TitleState(); - } + SDL_RenderClear(game.renderer.get()); + SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 74 : 216, + 430); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) { + selection_--; + } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) { + selection_++; + } else if (e.key.keysym.sym == SDLK_RETURN) { + switch (selection_) { + case 0: + return std::make_unique(game); + case 1: + return std::make_unique(game); } } } } + + return nullptr; +} + +HowToPlayPageTwoState::HowToPlayPageTwoState(Game& game) { + background_ = loadImage(game.renderer.get(), "resources/htp2.bmp"); + pointer_ = loadImage(game.renderer.get(), "resources/pointer.bmp"); } -State* HowToPlayPageTwoState::operator()(SDL_Window* window, - SDL_Renderer* renderer) { - SDL_Texture* background = loadImage(renderer, "resources/htp2.bmp"); - SDL_Texture* pointer = loadImage(renderer, "resources/pointer.bmp"); - int selection = 0; +std::unique_ptr HowToPlayPageTwoState::operator()(Game& game) { SDL_Event e; - for (;;) { - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, background, NULL, NULL); - applyTexture(renderer, pointer, selection == 0 ? 45 : 238, 430); - SDL_RenderPresent(renderer); - - while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { - return NULL; - } else if (e.type == SDL_KEYDOWN) { - if ((e.key.keysym.sym == SDLK_LEFT) && (selection != 0)) { - selection--; - } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection != 1)) { - selection++; - } else if (e.key.keysym.sym == SDLK_RETURN) { - switch (selection) { - case 0: - return new HowToPlayState(); - - break; - case 1: - return new TitleState(); - } + SDL_RenderClear(game.renderer.get()); + SDL_RenderCopy(game.renderer.get(), background_.get(), NULL, NULL); + applyTexture(game.renderer.get(), pointer_.get(), selection_ == 0 ? 45 : 238, + 430); + SDL_RenderPresent(game.renderer.get()); + + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + game.should_quit = true; + break; + } else if (e.type == SDL_KEYDOWN) { + if ((e.key.keysym.sym == SDLK_LEFT) && (selection_ != 0)) { + selection_--; + } else if ((e.key.keysym.sym == SDLK_RIGHT) && (selection_ != 1)) { + selection_++; + } else if (e.key.keysym.sym == SDLK_RETURN) { + switch (selection_) { + case 0: + return std::make_unique(game); + case 1: + return std::make_unique(game); } } } } + + return nullptr; } diff --git a/titlestate.h b/titlestate.h index e989659..22c99f5 100644 --- a/titlestate.h +++ b/titlestate.h @@ -1,23 +1,46 @@ -#include - -#include "state.h" - #ifndef TITLESTATE_H #define TITLESTATE_H +#include + +#include "mazeoflife.h" +#include "sdl.h" +#include "state.h" + class TitleState : public State { public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + explicit TitleState(Game& game); + + std::unique_ptr operator()(Game& game); + + private: + texture_ptr background_; + texture_ptr pointer_; + int selection_ = 0; }; class HowToPlayState : public State { public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + explicit HowToPlayState(Game& game); + + std::unique_ptr operator()(Game& game); + + private: + texture_ptr background_; + texture_ptr pointer_; + int selection_ = 0; }; class HowToPlayPageTwoState : public State { public: - State* operator()(SDL_Window* window, SDL_Renderer* renderer); + explicit HowToPlayPageTwoState(Game& game); + + std::unique_ptr operator()(Game& game); + + private: + texture_ptr background_; + texture_ptr pointer_; + int selection_ = 0; }; -#endif \ No newline at end of file +#endif diff --git a/util.cpp b/util.cpp index 693d0ad..5947620 100644 --- a/util.cpp +++ b/util.cpp @@ -1,8 +1,10 @@ #include "util.h" +#include #include #include "mazeoflife.h" +#include "sdl.h" void wrap(int& x, int& y) { if (x < 0) { @@ -18,38 +20,32 @@ void wrap(int& x, int& y) { } } -TTF_Font* loadFont(int size) { - TTF_Font* tmpfont = TTF_OpenFont("resources/mono.ttf", size); - - if (tmpfont == NULL) { - printf("Unable to load font: %s\n", TTF_GetError()); - exit(1); +font_ptr loadFont(int size) { + font_ptr font = font_ptr(TTF_OpenFont("resources/mono.ttf", size)); + if (!font) { + throw ttf_error(); } - return tmpfont; + return font; } -const char* getDataFile() { +std::string getDataFile() { #ifdef WINDOWS char* dir = getenv("USERPROFILE"); #else char* dir = getenv("HOME"); #endif - return (std::string(dir) + "/.molhslist").c_str(); + return std::string(std::filesystem::path(dir) / ".molhslist"); } -SDL_Texture* loadImage(SDL_Renderer* renderer, std::string file) { - SDL_Surface* surface = SDL_LoadBMP(file.c_str()); - if (surface == NULL) { - std::cout << SDL_GetError() << std::endl; - return NULL; +texture_ptr loadImage(SDL_Renderer* renderer, std::string file) { + surface_ptr surface = surface_ptr(SDL_LoadBMP(file.c_str())); + if (!surface) { + throw sdl_error(); } - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); - SDL_FreeSurface(surface); - - return texture; + return texture_ptr(SDL_CreateTextureFromSurface(renderer, surface.get())); } void 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 @@ +#ifndef UTIL_H +#define UTIL_H + #include #include +#include #include -#ifndef UTIL_H -#define UTIL_H +#include "sdl.h" void wrap(int& x, int& y); -TTF_Font* loadFont(int size); -const char* getDataFile(); -SDL_Texture* loadImage(SDL_Renderer* renderer, std::string file); +font_ptr loadFont(int size); +std::string getDataFile(); +texture_ptr loadImage(SDL_Renderer* renderer, std::string file); void applyTexture(SDL_Renderer* renderer, SDL_Texture* tex, int x, int y); #endif \ No newline at end of file -- cgit 1.4.1