diff --git a/build-debug/sfml-test b/build-debug/sfml-test index a1933ef..3707f2c 100755 Binary files a/build-debug/sfml-test and b/build-debug/sfml-test differ diff --git a/src/cell.cpp b/src/cell.cpp index 95de48c..401a75d 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -46,7 +46,7 @@ bool PassableCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -CellPtr PassableCell::getDefaultInstance() const +CellPtr PassableCell::clone() const { return std::make_unique(); } @@ -69,7 +69,7 @@ bool WaterCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return false; } -CellPtr WaterCell::getDefaultInstance() const +CellPtr WaterCell::clone() const { return std::make_unique(); } @@ -94,7 +94,7 @@ bool WallCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return false; } -CellPtr WallCell::getDefaultInstance() const +CellPtr WallCell::clone() const { return std::make_unique(); } @@ -120,7 +120,7 @@ bool ChargeCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -CellPtr ChargeCell::getDefaultInstance() const +CellPtr ChargeCell::clone() const { return std::make_unique(); } @@ -143,7 +143,7 @@ bool ExitCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -CellPtr ExitCell::getDefaultInstance() const +CellPtr ExitCell::clone() const { return std::make_unique(); } @@ -174,7 +174,7 @@ void TeleportCell::setDestination(coordinate new_cell_row, coordinate new_cell_c new_col = new_cell_col; } -CellPtr TeleportCell::getDefaultInstance() const +CellPtr TeleportCell::clone() const { return std::make_unique(); } @@ -182,6 +182,8 @@ CellPtr TeleportCell::getDefaultInstance() const /////////////////////////////////////// +const std::vector TriggerCell::cells_to_cast { PASSABLE_CELL, WATER_CELL, WALL_CELL, EXIT_CELL }; + TriggerCell::TriggerCell(/*std::vector &&cells_to_change,*/ coordinate cell_row, coordinate cell_col, const sf::Color &color) : Cell(cell_row, cell_col, color) { @@ -194,27 +196,31 @@ TriggerCell::TriggerCell(/*std::vector &&cells_to_change,*/ coordinate TriggerCell::~TriggerCell() {} +void TriggerCell::addTarget(CellPtr &&cell) +{ + UNUSED(cell); + //cells.emplace_back(cell); +} + bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level) { UNUSED(hero); - Map &map = level->mapArray(); - // We replace needed cells with the ones that the trigger provides. for (CellPtr &cell : cells) { const coordinate &row = cell->row(); const coordinate &col = cell->col(); - map[row][col].release(); - map[row][col] = std::move(cell); + level->getCellAt(row, col).release(); + level->getCellAt(row, col) = std::move(cell); } // It's an impassable object, so player can't move to here. return false; } -CellPtr TriggerCell::getDefaultInstance() const +CellPtr TriggerCell::clone() const { return std::make_unique(); } diff --git a/src/cell.h b/src/cell.h index 3228fa7..675bbbc 100644 --- a/src/cell.h +++ b/src/cell.h @@ -21,14 +21,14 @@ const sf::Color Pink = sf::Color(255, 192, 203); const sf::Color Black = sf::Color( 0, 0, 0); } -enum CELL_TYPES { +enum CELL_TYPE { PASSABLE_CELL = 0, - WATER_CELL, - WALL_CELL, - CHARGE_CELL, - EXIT_CELL, - TELEPORT_CELL, - TRIGGER_CELL, + WATER_CELL = 1, + WALL_CELL = 2, + CHARGE_CELL = 3, + EXIT_CELL = 4, + TELEPORT_CELL = 5, + TRIGGER_CELL = 6, N_CELLS }; @@ -69,7 +69,7 @@ public: /// Determine if Hero can move onto this cell or not virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0; - virtual CellPtr getDefaultInstance() const = 0; + virtual CellPtr clone() const = 0; }; /////////////////////////////////////// @@ -86,7 +86,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -103,7 +103,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -120,7 +120,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -141,7 +141,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -158,7 +158,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -182,7 +182,7 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; /////////////////////////////////////// @@ -191,10 +191,13 @@ public: class TriggerCell final : public Cell { private: - // Vector of cells to place on map + // Vector of cells to place on map std::vector cells; public: + // Vector of cell types you can cast in to + static const std::vector cells_to_cast; + TriggerCell(//std::vector &&cells_to_change, coordinate cell_row = 0, coordinate cell_col = 0, @@ -202,9 +205,11 @@ public: virtual ~TriggerCell() override; + void addTarget(CellPtr &&cell); + virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual CellPtr getDefaultInstance() const override; + virtual CellPtr clone() const override; }; #endif // CELL_H diff --git a/src/game.cpp b/src/game.cpp index 89e10f1..75f7e4f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -21,8 +21,8 @@ Game::Game() main_window.create(sf::VideoMode(window_side * 3, window_side * 3), "SFML-Test Application", sf::Style::Default); main_window.setActive(); - level->mapArray()[0][0]->setHeightShift(15); - level->mapArray()[0][1]->setHeightShift(10); + level->getCellAt(0, 0)->setHeightShift(15); + level->getCellAt(0, 1)->setHeightShift(10); current_level = 1; //loadLevel(current_level); @@ -107,14 +107,12 @@ void Game::onMoving(sf::Keyboard::Key &key) ////////////////////////// - if (!level->mapArray()[attempt_row][attempt_col]->onMovingTo(hero, level)) + if (!level->getCellAt(attempt_row, attempt_col)->onMovingTo(hero, level)) hero->setPosition(initial_row, initial_col); } void Game::renderMap() { - const Map &map = level->mapArray(); - float painter_x = 60, painter_y = 60; float horizontal_shift = 0, vertical_shift = 0; @@ -145,12 +143,11 @@ void Game::renderMap() // Draw map from 2D array for (coordinate x = 0; x < level->cols(); ++x) - { + { horizontal_shift = static_cast(level->cols()) * cell_deviation; - for (coordinate y = 0; y < level->rows(); ++y) { - vertical_shift = static_cast(map[y][x]->heightShift()); + vertical_shift = static_cast(level->getCellAt(y, x)->heightShift()); // If cell has any height value, we should draw walls for it if (vertical_shift > 0) @@ -166,7 +163,9 @@ void Game::renderMap() convex_wall_brush.setPoint(5, sf::Vector2f(cell_width, cell_height)); convex_wall_brush.setOutlineThickness(0); - sf::Color wall_color(sf::Uint8(map[y][x]->color().r - 40), sf::Uint8(map[y][x]->color().g - 40), sf::Uint8(map[y][x]->color().b - 40)); + sf::Color wall_color(sf::Uint8(level->getCellAt(y, x)->color().r - 40), + sf::Uint8(level->getCellAt(y, x)->color().g - 40), + sf::Uint8(level->getCellAt(y, x)->color().b - 40)); convex_wall_brush.setFillColor(wall_color); convex_wall_brush.setPosition(painter_x + horizontal_shift, painter_y); @@ -180,7 +179,7 @@ void Game::renderMap() float final_y = painter_y - vertical_shift; convex_brush.setPosition(final_x, final_y); - convex_brush.setFillColor(map[y][x]->color()); + convex_brush.setFillColor(level->getCellAt(y, x)->color()); main_window.draw(convex_brush); diff --git a/src/level.cpp b/src/level.cpp index 7926d34..ad56305 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1,103 +1,143 @@ #include "level.h" +#include #include -#include - -void Level::prepareCellInstances() -{ - 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::readMap(std::ifstream &file) -{ - 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); - } - } -} +#include template // [D]erived - [B]ase -std::unique_ptr static_unique_pointer_cast (std::unique_ptr&& old) +std::unique_ptr static_unique_pointer_cast(std::unique_ptr&& old) { - return std::unique_ptr{static_cast(old.release())}; + return std::unique_ptr{static_cast(old.release())}; } -Level::Level(const std::string &map_file) +void Level::Map::init(const std::string &map_file_name) { - prepareCellInstances(); + prepareDefaultCells(); std::ifstream file; - file.open(map_file); + file.open(map_file_name); std::string cur_line; + std::istringstream sstr; + SECTION cur_section = SECTION::NONE; while (getline(file, cur_line)) { - // need fix; see std::string.compare - if (cur_line.compare(0, 4, "size") == 0) + if (map_section.find(cur_line) != map_section.end()) { - file >> level_rows >> level_cols; - map.resize(level_rows); - for (Row &row : map) - row.resize(level_cols); + cur_section = map_section[cur_line]; + continue; } - else if (cur_line.compare(0, 3, "map") == 0) - { - readMap(file); - } - else if (cur_line.compare(0, 8, "teleport") == 0) + + sstr.str(cur_line); + switch (cur_section) { - coordinate src_row, src_col; - coordinate dest_row, dest_col; + case SECTION::SIZE: + readMapSize(sstr); + break; + + case SECTION::MAP: + readMapRow(sstr); + break; + + case SECTION::TELEPORT: + readTeleport(sstr); + break; + + case SECTION::TRIGGER: + readTrigger(sstr); + break; - file >> src_row >> src_col >> dest_row >> dest_col; - auto teleport_cell = static_unique_pointer_cast(std::move(map[src_row][src_col])); - teleport_cell->setDestination(dest_row, dest_col); - map[src_row][src_col] = std::move(teleport_cell); + default: + break; } } + + file.close(); +} + +void Level::Map::prepareDefaultCells() +{ + default_cells[PASSABLE_CELL] = std::make_unique(); + default_cells[WATER_CELL] = std::make_unique(); + default_cells[WALL_CELL] = std::make_unique(); + default_cells[CHARGE_CELL] = std::make_unique(); + default_cells[EXIT_CELL] = std::make_unique(); + default_cells[TELEPORT_CELL] = std::make_unique(); + default_cells[TRIGGER_CELL] = std::make_unique(); +} + +void Level::Map::readMapSize(std::istringstream &sstr) +{ + sstr >> rows >> cols; + data.reserve(rows * cols); +} + +void Level::Map::readMapRow(std::istringstream &sstr) +{ + data.push_back(Row()); + for (int cell_type; sstr >> cell_type;) + { + data.back().push_back(default_cells[cell_type]->clone()); + data.back().back()->setPosition(data.size()-1, data.back().size()-1); + } } -Level::~Level() +void Level::Map::readTeleport(std::istringstream &sstr) { - for (Cell *cell : default_cells) - delete cell; + coordinate src_row, src_col; + coordinate dest_row, dest_col; + + sstr >> src_row >> src_col >> dest_row >> dest_col; + auto teleport_cell = static_unique_pointer_cast(std::move(data[src_row][src_col])); + teleport_cell->setDestination(dest_row, dest_col); + data[src_row][src_col] = std::move(teleport_cell); +} + +void Level::Map::readTrigger(std::istringstream &sstr) +{ + coordinate src_row, src_col; + coordinate dest_row, dest_col; + int cell_type; + + sstr >> src_row >> src_col >> dest_row >> dest_col >> cell_type; + + if (std::find(TriggerCell::cells_to_cast.begin(), TriggerCell::cells_to_cast.end(), cell_type) == + TriggerCell::cells_to_cast.end()) + return ; + + auto trigger_cell = static_unique_pointer_cast(std::move(data[src_row][src_col])); + trigger_cell->addTarget(default_cells[cell_type]->clone()); + data[src_row][src_col] = std::move(trigger_cell); +} + +Level::Level(const std::string &map_file_name) +{ + map.init(map_file_name); } size_t Level::rows() const { - return level_rows; + return map.rows; } size_t Level::cols() const { - return level_cols; + return map.cols; } -void Level::placeBridge(coordinate row, coordinate col) +CellPtr &Level::getCellAt(coordinate row, coordinate col) { - map[row][col] = std::make_unique(row, col, palette::Black); + return map.data[row][col]; } -void Level::removeCharge(coordinate row, coordinate col) +void Level::placeBridge(coordinate row, coordinate col) { - map[row][col] = std::make_unique(row, col, color_ground); + map.data[row][col] = std::make_unique(row, col, palette::Black); } -Map& Level::mapArray() +void Level::removeCharge(coordinate row, coordinate col) { - return map; + map.data[row][col] = std::make_unique(row, col, color_ground); } sf::Color Level::defaultGroundColor() diff --git a/src/level.h b/src/level.h index bd0e725..fec0b9e 100644 --- a/src/level.h +++ b/src/level.h @@ -2,38 +2,74 @@ #define LEVEL_H #include -#include "cell.h" +#include -const std::string default_file_name = "test_map"; +#include "cell.h" -using Row = std::vector; -using Map = std::vector; +// Very desirable to create module for default values +const std::string default_map_file_name = "test_map"; /// Abstraction over 2D array to quickly get access to level cells class Level { private: + struct Map + { + using Row = std::vector; + using Matrix = std::vector; + + enum class SECTION + { + SIZE, + MAP, + TELEPORT, + TRIGGER, + NONE + }; + + std::map map_section = + { + { "size", SECTION::SIZE }, + { "map", SECTION::MAP }, + { "teleport", SECTION::TELEPORT }, + { "trigger", SECTION::TRIGGER }, + { "", SECTION::NONE } + }; + + Matrix data; + size_t rows, cols; + std::array default_cells; + + void init(const std::string &map_file_name = default_map_file_name); + + /// Prepare prototypes of default cells + void prepareDefaultCells(); + + /// Map file section readers + void readMapSize(std::istringstream &sstr); + void readMapRow(std::istringstream &sstr); + void readTeleport(std::istringstream &sstr); + void readTrigger(std::istringstream &sstr); + }; + Map map; sf::Color color_ground; - size_t level_rows, level_cols; - std::array default_cells; - - void prepareCellInstances(); - void readMap(std::ifstream &file); public: - Level(const std::string &map_file = default_file_name); - ~Level(); + Level(const std::string &map_file_name = default_map_file_name); + /// Number of map rows size_t rows() const; + + /// Number of map columns size_t cols() const; + /// Get cell at position row, col + CellPtr &getCellAt(coordinate row, coordinate col); + /// Place a bridge cell void placeBridge(coordinate x, coordinate y); - /// Get the 2D array of level map - Map& mapArray(); - /// Replace a charge cell with a ground cell void removeCharge(coordinate x, coordinate y); diff --git a/src/sfml-test.pro b/src/sfml-test.pro index 16853d5..41f77ac 100644 --- a/src/sfml-test.pro +++ b/src/sfml-test.pro @@ -3,7 +3,7 @@ CONFIG += c++17 CONFIG -= console app_bundle CONFIG -= qt -QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O2 -g +QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O0 -g SOURCES += \ cell.cpp \