From 6eac3c84f6717457cb220d804b0915a1b5f603fd Mon Sep 17 00:00:00 2001 From: oss Date: Mon, 16 Mar 2020 00:57:39 +0300 Subject: [PATCH] Global refactoring; add level builder --- src/cell.cpp | 106 +++++++++++++++++++++++++++++++------------------ src/cell.h | 71 +++++++++++++++++++++++---------- src/entity.cpp | 24 +++++------ src/entity.h | 14 +++---- src/game.cpp | 86 ++++++++------------------------------- src/game.h | 2 +- src/hero.cpp | 12 +++--- src/hero.h | 2 +- src/level.cpp | 94 ++++++++++++++++++++++++++++++++++++++++--- src/level.h | 17 ++++++-- 10 files changed, 261 insertions(+), 167 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 7f362bd..e7ffffd 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -3,10 +3,10 @@ #include "hero.h" #include "level.h" -#define UNUSED(expr) (void)(expr); +#define UNUSED(expr) (void)(expr) -Cell::Cell(coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Entity(cell_x, cell_y), +Cell::Cell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Entity(cell_row, cell_col), cell_color(color) {} @@ -20,8 +20,8 @@ sf::Color Cell::color() const noexcept /////////////////////////////////////// -PassableCell::PassableCell(coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color) +PassableCell::PassableCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color) {} PassableCell::~PassableCell() @@ -29,17 +29,21 @@ PassableCell::~PassableCell() bool PassableCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { - UNUSED(hero) - UNUSED(level) + UNUSED(hero), UNUSED(level); // Hero just moves on. return true; } +Cell *PassableCell::getDefaultInstance() +{ + return new PassableCell(); +} + /////////////////////////////////////// -WaterCell::WaterCell(coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color) +WaterCell::WaterCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color) {} WaterCell::~WaterCell() @@ -48,19 +52,21 @@ WaterCell::~WaterCell() bool WaterCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { // Try to use one charge to place a bridge - if (hero->useCharge()) { - level->placeBridge(pos_x, pos_y); - return true; - } + if (hero->useCharge()) + level->placeBridge(entity_row, entity_col); - // If hero doesn't have enough charges, we move Hero back return false; } +Cell *WaterCell::getDefaultInstance() +{ + return new WaterCell(); +} + /////////////////////////////////////// -WallCell::WallCell(coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color) +WallCell::WallCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color) {} WallCell::~WallCell() @@ -68,17 +74,21 @@ WallCell::~WallCell() bool WallCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { - UNUSED(hero) - UNUSED(level) + UNUSED(hero), UNUSED(level); // Hero never passes this cell. return false; } +Cell *WallCell::getDefaultInstance() +{ + return new WallCell(); +} + /////////////////////////////////////// -ChargeCell::ChargeCell(coordinate cell_x, coordinate cell_y, int has_charges, const sf::Color &color) : - Cell(cell_x, cell_y, color), +ChargeCell::ChargeCell(coordinate cell_row, coordinate cell_col, int has_charges, const sf::Color &color) : + Cell(cell_row, cell_col, color), cell_charges(has_charges) {} @@ -89,15 +99,20 @@ bool ChargeCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { // Hero picks up the charge; remove it from the map hero->refillCharges(cell_charges); - level->removeCharge(pos_x, pos_y); + level->removeCharge(entity_row, entity_col); return true; } +Cell *ChargeCell::getDefaultInstance() +{ + return new ChargeCell(); +} + /////////////////////////////////////// -ExitCell::ExitCell(coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color) +ExitCell::ExitCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color) {} ExitCell::~ExitCell() @@ -105,19 +120,24 @@ ExitCell::~ExitCell() bool ExitCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { - UNUSED(level) + UNUSED(level); // Level is over. hero->reachExit(); return true; } +Cell *ExitCell::getDefaultInstance() +{ + return new ExitCell(); +} + /////////////////////////////////////// -TeleportCell::TeleportCell(coordinate cell_x, coordinate cell_y, coordinate new_cell_x, coordinate new_cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color), - new_x(new_cell_x), - new_y(new_cell_y) +TeleportCell::TeleportCell(coordinate cell_row, coordinate cell_col, coordinate new_cell_row, coordinate new_cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color), + new_x(new_cell_row), + new_y(new_cell_col) {} TeleportCell::~TeleportCell() @@ -125,19 +145,24 @@ TeleportCell::~TeleportCell() bool TeleportCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { - UNUSED(level) + UNUSED(level); // Hero jumps into teleport! hero->setPosition(new_x, new_y); return true; } +Cell *TeleportCell::getDefaultInstance() +{ + return new TeleportCell(); +} + /////////////////////////////////////// -TriggerCell::TriggerCell(std::vector &&cells_to_change, coordinate cell_x, coordinate cell_y, const sf::Color &color) : - Cell(cell_x, cell_y, color) +TriggerCell::TriggerCell(/*std::vector &&cells_to_change,*/ coordinate cell_row, coordinate cell_col, const sf::Color &color) : + Cell(cell_row, cell_col, color) { - cells = std::move(cells_to_change); + //cells = std::move(cells_to_change); } TriggerCell::~TriggerCell() @@ -145,20 +170,25 @@ TriggerCell::~TriggerCell() bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { - UNUSED(hero) + UNUSED(hero); Map &map = level->mapArray(); // We replace needed cells with the ones that the trigger provides. - for (CellPtr &cell : cells) + for (Cell *cell : cells) { - const coordinate &y = cell->y(); - const coordinate &x = cell->x(); + const coordinate &row = cell->row(); + const coordinate &col = cell->col(); - map[x][y].release(); - map[x][y] = std::move(cell); + delete map[row][col]; + map[row][col] = cell; } // It's an impassable object, so player can't move to here. return false; } + +Cell *TriggerCell::getDefaultInstance() +{ + return new TriggerCell(); +} diff --git a/src/cell.h b/src/cell.h index e5bddf5..f2b6391 100644 --- a/src/cell.h +++ b/src/cell.h @@ -16,6 +16,18 @@ const sf::Color Blue = sf::Color(0, 255, 255); const sf::Color Gray = sf::Color(125, 125, 125); } +enum CELL_TYPES { + PASSABLE_CELL = 0, + WATER_CELL, + WALL_CELL, + CHARGE_CELL, + EXIT_CELL, + TELEPORT_CELL, + TRIGGER_CELL, + + N_CELLS +}; + /////////////////////////////////////// class Hero; @@ -24,7 +36,6 @@ class Cell; using HeroPtr = std::unique_ptr; using LevelPtr = std::unique_ptr; -using CellPtr = std::unique_ptr; /////////////////////////////////////// @@ -35,8 +46,8 @@ protected: sf::Color cell_color; public: - Cell(coordinate cell_x = 0, - coordinate cell_y = 0, + Cell(coordinate cell_row = 0, + coordinate cell_col = 0, const sf::Color &color = palette::White); virtual ~Cell() override; @@ -45,6 +56,8 @@ public: /// Determine if Hero can move onto this cell or not virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0; + + virtual Cell *getDefaultInstance() = 0; }; /////////////////////////////////////// @@ -53,13 +66,15 @@ public: class PassableCell : public Cell { public: - PassableCell(coordinate cell_x = 0, - coordinate cell_y = 0, // Brown + PassableCell(coordinate cell_row = 0, + coordinate cell_col = 0, // Brown const sf::Color &color = palette::Brown); virtual ~PassableCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -68,13 +83,15 @@ public: class WaterCell : public Cell { public: - WaterCell(coordinate cell_x = 0, - coordinate cell_y = 0, + WaterCell(coordinate cell_row = 0, + coordinate cell_col = 0, const sf::Color &color = palette::Blue); virtual ~WaterCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -83,13 +100,15 @@ public: class WallCell : public Cell { public: - WallCell(coordinate cell_x = 0, - coordinate cell_y = 0, // Gray - const sf::Color &color = ); + WallCell(coordinate cell_row = 0, + coordinate cell_col = 0, // Gray + const sf::Color &color = palette::Gray); virtual ~WallCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -101,14 +120,16 @@ private: int cell_charges; public: - ChargeCell(coordinate cell_x = 0, - coordinate cell_y = 0, + ChargeCell(coordinate cell_row = 0, + coordinate cell_col = 0, int has_charges = 1, const sf::Color &color = sf::Color::Green); virtual ~ChargeCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -117,13 +138,15 @@ public: class ExitCell : public Cell { public: - ExitCell(coordinate cell_x = 0, - coordinate cell_y = 0, + ExitCell(coordinate cell_row = 0, + coordinate cell_col = 0, const sf::Color &color = sf::Color::Red); virtual ~ExitCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -135,15 +158,17 @@ private: coordinate new_x, new_y; public: - TeleportCell(coordinate cell_x = 0, - coordinate cell_y = 0, - coordinate new_cell_x = 0, - coordinate new_cell_y = 0, // Purple + TeleportCell(coordinate cell_row = 0, + coordinate cell_col = 0, + coordinate new_cell_row = 0, + coordinate new_cell_col = 0, // Purple const sf::Color &color = sf::Color(128, 0, 128)); virtual ~TeleportCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; /////////////////////////////////////// @@ -153,17 +178,19 @@ class TriggerCell : public Cell { private: // Vector of cells to place on map - std::vector cells; + std::vector cells; public: - TriggerCell(std::vector &&cells_to_change, - coordinate cell_x = 0, - coordinate cell_y = 0, // Pink + TriggerCell(//std::vector &&cells_to_change, + coordinate cell_row = 0, + coordinate cell_col = 0, // Pink const sf::Color &color = sf::Color(255, 192, 203)); virtual ~TriggerCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; + + virtual Cell *getDefaultInstance() override; }; #endif // CELL_H diff --git a/src/entity.cpp b/src/entity.cpp index 33eeb7a..9d54a2e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1,31 +1,31 @@ #include "entity.h" -Entity::Entity(coordinate _x, coordinate _y) : - pos_x(_x), pos_y(_y) +Entity::Entity(coordinate _row, coordinate _col) : + entity_row(_row), entity_col(_col) {} Entity::~Entity() {} /// Get current Entity position -void Entity::position(coordinate &x, coordinate &y) const noexcept +void Entity::position(coordinate &row, coordinate &col) const noexcept { - x = pos_x; - y = pos_y; + row = entity_row; + col = entity_col; } -void Entity::setPosition(coordinate x, coordinate y) +void Entity::setPosition(coordinate row, coordinate col) { - pos_x = x; - pos_y = y; + entity_row = row; + entity_col = col; } -coordinate Entity::x() const noexcept +coordinate Entity::row() const noexcept { - return pos_x; + return entity_row; } -coordinate Entity::y() const noexcept +coordinate Entity::col() const noexcept { - return pos_y; + return entity_col; } diff --git a/src/entity.h b/src/entity.h index f6dda32..eca8cc1 100644 --- a/src/entity.h +++ b/src/entity.h @@ -1,30 +1,30 @@ #ifndef ENTITY_H #define ENTITY_H -using coordinate = unsigned int; +using coordinate = unsigned long; /// Interface representing entity which can be placed on the map class Entity { protected: - coordinate pos_x, pos_y; + coordinate entity_row, entity_col; public: - Entity(coordinate _x = 0, coordinate _y = 0); + Entity(coordinate _row = 0, coordinate _col = 0); virtual ~Entity() = 0; /// Get current Entity position - void position(coordinate &x, coordinate &y) const noexcept; + void position(coordinate &row, coordinate &col) const noexcept; /// Set Entity position explicitly - void setPosition(coordinate x, coordinate y); + void setPosition(coordinate row, coordinate col); /// Get current x of the Entity position - coordinate x() const noexcept; + coordinate row() const noexcept; /// Get current y of the Entity position - coordinate y() const noexcept; + coordinate col() const noexcept; }; #endif // ENTITY_H diff --git a/src/game.cpp b/src/game.cpp index abb9997..cba684a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -8,7 +8,7 @@ constexpr int cell_width = 60; constexpr int cell_height = 35; constexpr int cell_deviation = 25; -constexpr int window_side = cell_width * side; +constexpr int window_side = cell_width * 4; Game::Game() { @@ -18,11 +18,11 @@ Game::Game() // Generate level level = std::make_unique(); - main_window.create(sf::VideoMode(window_side, window_side), "SFML-Test Application", sf::Style::Default); + main_window.create(sf::VideoMode(window_side * 2, window_side * 2), "SFML-Test Application", sf::Style::Default); main_window.setActive(); current_level = 1; - loadLevel(current_level); + //loadLevel(current_level); } int Game::run() @@ -54,7 +54,6 @@ int Game::run() } //////////////////////////////////////////////////// - Direction Game::getDirection(sf::Keyboard::Key &key) const { switch (key) @@ -93,24 +92,20 @@ void Game::onMoving(sf::Keyboard::Key &key) return; // Save the initial coordinates - coordinate initial_x, initial_y; - hero->position(initial_x, initial_y); + coordinate initial_row, initial_col; + hero->position(initial_row, initial_col); // Try to move hero hero->move(direction); // Save the new coordinates after moving - coordinate attempt_x, attempt_y; - hero->position(attempt_x, attempt_y); + coordinate attempt_row, attempt_col; + hero->position(attempt_row, attempt_col); ////////////////////////// - if (!level->mapArray()[attempt_x][attempt_y]->onMovingTo(hero, level)) - hero->setPosition(initial_x, initial_y); - - if (hero->onExit()) - loadLevel(++current_level); - + if (!level->mapArray()[attempt_row][attempt_col]->onMovingTo(hero, level)) + hero->setPosition(initial_row, initial_col); } void Game::renderMap() @@ -142,22 +137,22 @@ void Game::renderMap() text.setString("Available bridge cells: " + std::to_string(hero->charges())); // Where is hero - coordinate hero_x, hero_y; - hero->position(hero_x, hero_y); + coordinate hero_row, hero_col; + hero->position(hero_row, hero_col); // Draw map from 2D array - for (coordinate x = 0; x < side; ++x) + for (coordinate x = 0; x < level->width(); ++x) { - shift = side * cell_deviation; + shift = static_cast(level->width()) * cell_deviation; - for (coordinate y = 0; y < side; ++y) + for (coordinate y = 0; y < level->height(); ++y) { convex_brush.setPosition(shift + painter_x, painter_y); - convex_brush.setFillColor(map[x][y]->color()); + convex_brush.setFillColor(map[y][x]->color()); main_window.draw(convex_brush); - if (hero_x == x && hero_y == y) + if (hero_row == y && hero_col == x) { // Place the hero sprite convex_brush.setFillColor(sf::Color::White); @@ -176,52 +171,3 @@ void Game::renderMap() main_window.draw(text); } - -void Game::loadLevel(int level_index) -{ - Map &map = level->mapArray(); - - // Fill the level with water - for (coordinate x = 0; x < side; ++x) - for (coordinate y = 0; y < side; ++y) - map[x][y] = std::make_unique(x, y); - - std::vector trigger_cells; - trigger_cells.emplace_back(std::make_unique(0, 0)); - trigger_cells.emplace_back(std::make_unique(1, 0)); - trigger_cells.emplace_back(std::make_unique(0, 1)); - - switch (level_index) - { - case 1: - // Hardcoding is temporary! - hero->setPosition(1, 1); - hero->setCharges(2); - - level->setDefaultGroundColor(sf::Color(165, 42, 42)); - - map[0][0] = std::make_unique(0, 0); - map[0][1] = std::make_unique(0, 1); - map[1][0] = std::make_unique(1, 0); - map[1][1] = std::make_unique(1, 1); - map[1][2] = std::make_unique(1, 2); - map[1][3] = std::make_unique(1, 3); - map[1][4] = std::make_unique(1, 4); - map[2][1] = std::make_unique(2, 1, 6, 6); - map[2][2] = std::make_unique(2, 2); - map[3][2] = std::make_unique(3, 2); - map[3][3] = std::make_unique(3, 3); - map[6][3] = std::make_unique(6, 3); - map[6][4] = std::make_unique(6, 4); - map[6][5] = std::make_unique(6, 5); - map[6][6] = std::make_unique(6, 6); - map[7][6] = std::make_unique(7, 6); - map[9][6] = std::make_unique(9, 6); - map[8][7] = std::make_unique(8, 7); - map[2][3] = std::make_unique(2, 3, 5); - map[3][3] = std::make_unique(std::move(trigger_cells), 3, 3); - break; - default: - main_window.close(); - } -} diff --git a/src/game.h b/src/game.h index e6d50eb..75d5136 100644 --- a/src/game.h +++ b/src/game.h @@ -33,7 +33,7 @@ private: void renderMap(); /// Prepare map and hero for a game level - void loadLevel(int level_index = 1); + //void loadLevel(int level_index = 1); public: explicit Game(); diff --git a/src/hero.cpp b/src/hero.cpp index 6f6b6ec..2fe5c9d 100644 --- a/src/hero.cpp +++ b/src/hero.cpp @@ -1,7 +1,7 @@ #include "hero.h" -Hero::Hero(coordinate position_x, coordinate position_y, int initial_charges) : - Entity(position_x, position_y), +Hero::Hero(coordinate row, coordinate col, int initial_charges) : + Entity(row, col), hero_charges(initial_charges), on_exit(false) {} @@ -37,13 +37,13 @@ void Hero::move(const Direction &direction) switch (direction) { case Direction::Up: - --pos_y; break; + --entity_row; break; case Direction::Down: - ++pos_y; break; + ++entity_row; break; case Direction::Left: - --pos_x; break; + --entity_col; break; case Direction::Right: - ++pos_x; break; + ++entity_col; break; case Direction::None: break; } diff --git a/src/hero.h b/src/hero.h index 9bacbe3..b0b0596 100644 --- a/src/hero.h +++ b/src/hero.h @@ -20,7 +20,7 @@ private: bool on_exit; public: - explicit Hero(coordinate position_x = 0, coordinate position_y = 0, int initial_charges = 0); + explicit Hero(coordinate row = 0, coordinate col = 0, int initial_charges = 0); /// Add more charges for hero to use void refillCharges(int append_charges); diff --git a/src/level.cpp b/src/level.cpp index de9fbf2..70045e3 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1,16 +1,98 @@ #include "level.h" -Level::Level() -{} +#include +#include -void Level::placeBridge(coordinate x, coordinate y) +void Level::prepareCellInstances() { - map[x][y] = std::make_unique(x, y, sf::Color::Black); + default_cells[PASSABLE_CELL] = new PassableCell(); + default_cells[WATER_CELL] = new WaterCell(); + default_cells[WALL_CELL] = new WallCell(); + default_cells[CHARGE_CELL] = new ChargeCell(); + default_cells[EXIT_CELL] = new ExitCell(); + default_cells[TELEPORT_CELL] = new TeleportCell(); + default_cells[TRIGGER_CELL] = new TriggerCell(); } -void Level::removeCharge(coordinate x, coordinate y) +void Level::readMap(std::ifstream &file) { - map[x][y] = std::make_unique(x, y, color_ground); + int i; + for (coordinate j = 0; j < map.size(); ++j) + { + for (coordinate k = 0; k < map[j].size(); ++k) + { + file >> i; + map[j][k] = default_cells[i]->getDefaultInstance(); + map[j][k]->setPosition(j, k); + } + } +} + +Level::Level(const std::string &map_file) +{ + prepareCellInstances(); + + std::ifstream file; + file.open(map_file); + + std::string cur_line; + while (getline(file, cur_line)) + { + // need fix; see std::string.compare + if (strstr(cur_line.data(), "size") != NULL) + { + file >> level_width >> level_height; + map.resize(level_height); + for (Row &row : map) + row.resize(level_width); + } + else if (strstr(cur_line.data(), "map") != NULL) + { + readMap(file); + } + else if (strstr(cur_line.data(), "teleport") != NULL) + { + coordinate src_row, src_col; + coordinate dest_row, dest_col; + + file >> src_row >> src_col >> dest_row >> dest_col; + // reinterpret_cast(map[src_row][src_col])->setDestination(dest_row, dest_col); + } + } +} + +Level::~Level() +{ + for (Cell *cell : default_cells) + delete cell; + + for (Row &row : map) + for (Cell *cell : row) + delete cell; +} + +size_t Level::width() const +{ + return level_width; +} + +size_t Level::height() const +{ + return level_height; +} + +void Level::placeBridge(coordinate row, coordinate col) +{ + Cell *buf = map[row][col]; + map[row][col] = new PassableCell(row, col, sf::Color::Black); + delete buf; +} + +void Level::removeCharge(coordinate row, coordinate col) +{ + Cell *buf = map[row][col]; + map[row][col] = new PassableCell(row, col, color_ground); + delete buf; } Map& Level::mapArray() diff --git a/src/level.h b/src/level.h index a9c0530..4683bab 100644 --- a/src/level.h +++ b/src/level.h @@ -4,10 +4,10 @@ #include #include "cell.h" -constexpr coordinate side = 32; +const std::string default_file_name = "test_map"; -using Row = std::array; -using Map = std::array; +using Row = std::vector; +using Map = std::vector; /// Abstraction over 2D array to quickly get access to level cells class Level @@ -15,9 +15,18 @@ class Level private: Map map; sf::Color color_ground; + size_t level_width, level_height; + std::array default_cells; + + void prepareCellInstances(); + void readMap(std::ifstream &file); public: - Level(); + Level(const std::string &map_file = default_file_name); + ~Level(); + + size_t width() const; + size_t height() const; /// Place a bridge cell void placeBridge(coordinate x, coordinate y);