diff --git a/README.md b/README.md index 947a083..3a1fa9a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,6 @@ Just playing around with [sfml](https://www.sfml-dev.org/), since I have nothing - Run `./sfml-test` command from your build destination. ### How to build: ### -- Except any C++ compiler which supports C++14 and higher, you also need to have SFML installed and accessible. +- Except any C++ compiler which supports C++17 and higher, you also need to have SFML installed and accessible. - Build the project (my sources work with qmake) - Move the `/src/font` folder into the folder of your build destination. diff --git a/build-debug/sfml-test b/build-debug/sfml-test index 1e3af72..a1933ef 100755 Binary files a/build-debug/sfml-test and b/build-debug/sfml-test differ diff --git a/build-debug/test_map b/build-debug/test_map new file mode 100644 index 0000000..c4c0b65 --- /dev/null +++ b/build-debug/test_map @@ -0,0 +1,10 @@ +size +7 7 +map +2 2 2 2 2 2 2 +2 0 0 0 0 0 2 +2 0 0 1 0 0 2 +2 0 0 1 1 0 2 +2 1 1 2 2 2 2 +2 0 0 0 0 0 2 +2 2 2 2 2 2 2 diff --git a/build-release/sfml-test b/build-release/sfml-test index 159da13..4ebf91f 100755 Binary files a/build-release/sfml-test and b/build-release/sfml-test differ diff --git a/build-release/test_map b/build-release/test_map new file mode 100644 index 0000000..c4c0b65 --- /dev/null +++ b/build-release/test_map @@ -0,0 +1,10 @@ +size +7 7 +map +2 2 2 2 2 2 2 +2 0 0 0 0 0 2 +2 0 0 1 0 0 2 +2 0 0 1 1 0 2 +2 1 1 2 2 2 2 +2 0 0 0 0 0 2 +2 2 2 2 2 2 2 diff --git a/src/cell.cpp b/src/cell.cpp index e7ffffd..95de48c 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -7,7 +7,8 @@ Cell::Cell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : Entity(cell_row, cell_col), - cell_color(color) + cell_color(color), + height_shift(0) // ground by default {} Cell::~Cell() @@ -18,6 +19,16 @@ sf::Color Cell::color() const noexcept return cell_color; } +void Cell::setHeightShift(coordinate shift_by_y) +{ + height_shift = shift_by_y; +} + +coordinate Cell::heightShift() const +{ + return height_shift; +} + /////////////////////////////////////// PassableCell::PassableCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : @@ -35,9 +46,9 @@ bool PassableCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -Cell *PassableCell::getDefaultInstance() +CellPtr PassableCell::getDefaultInstance() const { - return new PassableCell(); + return std::make_unique(); } /////////////////////////////////////// @@ -58,16 +69,19 @@ bool WaterCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return false; } -Cell *WaterCell::getDefaultInstance() +CellPtr WaterCell::getDefaultInstance() const { - return new WaterCell(); + return std::make_unique(); } /////////////////////////////////////// WallCell::WallCell(coordinate cell_row, coordinate cell_col, const sf::Color &color) : Cell(cell_row, cell_col, color) -{} +{ + // walls are a bit higher than ground and water + height_shift = 10; +} WallCell::~WallCell() {} @@ -80,9 +94,9 @@ bool WallCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return false; } -Cell *WallCell::getDefaultInstance() +CellPtr WallCell::getDefaultInstance() const { - return new WallCell(); + return std::make_unique(); } /////////////////////////////////////// @@ -90,7 +104,9 @@ Cell *WallCell::getDefaultInstance() 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) -{} +{ + height_shift = 5; // charges are a bit higher than ground and water, but lower than walls +} ChargeCell::~ChargeCell() {} @@ -104,9 +120,9 @@ bool ChargeCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -Cell *ChargeCell::getDefaultInstance() +CellPtr ChargeCell::getDefaultInstance() const { - return new ChargeCell(); + return std::make_unique(); } /////////////////////////////////////// @@ -127,17 +143,17 @@ bool ExitCell::onMovingTo(HeroPtr &hero, LevelPtr &level) return true; } -Cell *ExitCell::getDefaultInstance() +CellPtr ExitCell::getDefaultInstance() const { - return new ExitCell(); + return std::make_unique(); } /////////////////////////////////////// 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) + new_row(new_cell_row), + new_col(new_cell_col) {} TeleportCell::~TeleportCell() @@ -148,21 +164,31 @@ bool TeleportCell::onMovingTo(HeroPtr &hero, LevelPtr &level) UNUSED(level); // Hero jumps into teleport! - hero->setPosition(new_x, new_y); + hero->setPosition(new_row, new_col); return true; } -Cell *TeleportCell::getDefaultInstance() +void TeleportCell::setDestination(coordinate new_cell_row, coordinate new_cell_col) { - return new TeleportCell(); + new_row = new_cell_row; + new_col = new_cell_col; } +CellPtr TeleportCell::getDefaultInstance() const +{ + return std::make_unique(); +} + + /////////////////////////////////////// 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); + + // triggers are a bit higher than ground and water, but lower than walls + height_shift = 5; } TriggerCell::~TriggerCell() @@ -175,20 +201,20 @@ bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level) Map &map = level->mapArray(); // We replace needed cells with the ones that the trigger provides. - for (Cell *cell : cells) + for (CellPtr &cell : cells) { const coordinate &row = cell->row(); const coordinate &col = cell->col(); - delete map[row][col]; - map[row][col] = cell; + map[row][col].release(); + map[row][col] = std::move(cell); } // It's an impassable object, so player can't move to here. return false; } -Cell *TriggerCell::getDefaultInstance() +CellPtr TriggerCell::getDefaultInstance() const { - return new TriggerCell(); + return std::make_unique(); } diff --git a/src/cell.h b/src/cell.h index f2b6391..3228fa7 100644 --- a/src/cell.h +++ b/src/cell.h @@ -10,10 +10,15 @@ namespace palette { -const sf::Color Brown = sf::Color(165, 42, 42); -const sf::Color White = sf::Color(255, 255, 255); -const sf::Color Blue = sf::Color(0, 255, 255); -const sf::Color Gray = sf::Color(125, 125, 125); +const sf::Color Brown = sf::Color(165, 42, 42); +const sf::Color White = sf::Color(255, 255, 255); +const sf::Color Blue = sf::Color( 0, 255, 255); +const sf::Color Gray = sf::Color(125, 125, 125); +const sf::Color Green = sf::Color( 0, 255, 0); +const sf::Color Red = sf::Color(250, 0, 0); +const sf::Color Purple = sf::Color(128, 0, 128); +const sf::Color Pink = sf::Color(255, 192, 203); +const sf::Color Black = sf::Color( 0, 0, 0); } enum CELL_TYPES { @@ -36,6 +41,7 @@ class Cell; using HeroPtr = std::unique_ptr; using LevelPtr = std::unique_ptr; +using CellPtr = std::unique_ptr; /////////////////////////////////////// @@ -44,6 +50,7 @@ class Cell : public Entity { protected: sf::Color cell_color; + coordinate height_shift; public: Cell(coordinate cell_row = 0, @@ -53,34 +60,39 @@ public: virtual ~Cell() override; sf::Color color() const noexcept; + + /// "shift_by_y" indicates the height of current cell + /// Height is a shift of y coordinate on the scene, relatively to the ground (which is 0) + void setHeightShift(coordinate shift_by_y); + coordinate heightShift() const; /// Determine if Hero can move onto this cell or not virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0; - virtual Cell *getDefaultInstance() = 0; + virtual CellPtr getDefaultInstance() const = 0; }; /////////////////////////////////////// /// Any cell where Hero is free to move -class PassableCell : public Cell +class PassableCell final : public Cell { public: PassableCell(coordinate cell_row = 0, - coordinate cell_col = 0, // Brown + coordinate cell_col = 0, const sf::Color &color = palette::Brown); virtual ~PassableCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which requires Hero to spend a charge for bridge to move on -class WaterCell : public Cell +class WaterCell final : public Cell { public: WaterCell(coordinate cell_row = 0, @@ -91,30 +103,30 @@ public: virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which is impossible to move on -class WallCell : public Cell +class WallCell final : public Cell { public: WallCell(coordinate cell_row = 0, - coordinate cell_col = 0, // Gray + coordinate cell_col = 0, const sf::Color &color = palette::Gray); virtual ~WallCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which gives hero a charge -class ChargeCell : public Cell +class ChargeCell final : public Cell { private: int cell_charges; @@ -123,74 +135,76 @@ public: ChargeCell(coordinate cell_row = 0, coordinate cell_col = 0, int has_charges = 1, - const sf::Color &color = sf::Color::Green); + const sf::Color &color = palette::Green); virtual ~ChargeCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which moves hero to next level -class ExitCell : public Cell +class ExitCell final : public Cell { public: ExitCell(coordinate cell_row = 0, coordinate cell_col = 0, - const sf::Color &color = sf::Color::Red); + const sf::Color &color = palette::Red); virtual ~ExitCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which teleports hero to following coordinates -class TeleportCell : public Cell +class TeleportCell final : public Cell { private: - coordinate new_x, new_y; + coordinate new_row, new_col; public: 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)); + coordinate new_cell_col = 0, + const sf::Color &color = palette::Purple); virtual ~TeleportCell() override; + + void setDestination(coordinate new_cell_row, coordinate new_cell_col); virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; /////////////////////////////////////// /// A cell which replaces and changes other map cells when activated -class TriggerCell : public Cell +class TriggerCell final : 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_row = 0, - coordinate cell_col = 0, // Pink - const sf::Color &color = sf::Color(255, 192, 203)); + coordinate cell_col = 0, + const sf::Color &color = palette::Pink); virtual ~TriggerCell() override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; - virtual Cell *getDefaultInstance() override; + virtual CellPtr getDefaultInstance() const override; }; #endif // CELL_H diff --git a/src/game.cpp b/src/game.cpp index 4915b98..89e10f1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -18,9 +18,12 @@ Game::Game() // Generate level level = std::make_unique(); - main_window.create(sf::VideoMode(window_side * 2, window_side * 2), "SFML-Test Application", sf::Style::Default); + 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); + current_level = 1; //loadLevel(current_level); } @@ -112,8 +115,8 @@ void Game::renderMap() { const Map &map = level->mapArray(); - float painter_x = 0, painter_y = 0; - float shift = 0; + float painter_x = 60, painter_y = 60; + float horizontal_shift = 0, vertical_shift = 0; // Brush for cell sprites sf::ConvexShape convex_brush; @@ -122,7 +125,7 @@ void Game::renderMap() convex_brush.setPoint(1, sf::Vector2f(cell_deviation + cell_width, 0.f)); convex_brush.setPoint(2, sf::Vector2f(cell_width, cell_height)); convex_brush.setPoint(3, sf::Vector2f(0.f, cell_height)); - convex_brush.setFillColor(sf::Color::Blue); + convex_brush.setFillColor(palette::Blue); convex_brush.setOutlineThickness(0); convex_brush.setPosition(painter_x, painter_y); @@ -131,7 +134,7 @@ void Game::renderMap() sf::Font font; font.loadFromFile("font/VeraMono.ttf"); text.setFont(font); - text.setFillColor(sf::Color::White); + text.setFillColor(palette::White); text.setCharacterSize(25); text.setPosition(50, 350); text.setString("Available bridge cells: " + std::to_string(hero->charges())); @@ -143,29 +146,58 @@ void Game::renderMap() // Draw map from 2D array for (coordinate x = 0; x < level->cols(); ++x) { - shift = static_cast(level->cols()) * cell_deviation; + horizontal_shift = static_cast(level->cols()) * cell_deviation; for (coordinate y = 0; y < level->rows(); ++y) { - convex_brush.setPosition(shift + painter_x, painter_y); + vertical_shift = static_cast(map[y][x]->heightShift()); + + // If cell has any height value, we should draw walls for it + if (vertical_shift > 0) + { + // Brush for vertical walls + sf::ConvexShape convex_wall_brush; + convex_wall_brush.setPointCount(6); + convex_wall_brush.setPoint(0, sf::Vector2f(cell_deviation + cell_width, -vertical_shift)); + convex_wall_brush.setPoint(1, sf::Vector2f(cell_deviation + cell_width, 0.f)); + convex_wall_brush.setPoint(2, sf::Vector2f(cell_width, cell_height)); + convex_wall_brush.setPoint(3, sf::Vector2f(0.f, cell_height)); + convex_wall_brush.setPoint(4, sf::Vector2f(0.f, cell_height - vertical_shift)); + 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)); + convex_wall_brush.setFillColor(wall_color); + + convex_wall_brush.setPosition(painter_x + horizontal_shift, painter_y); + + main_window.draw(convex_wall_brush); + } + + // Draw the top surface of the cell itself + + float final_x = painter_x + horizontal_shift; + float final_y = painter_y - vertical_shift; + + convex_brush.setPosition(final_x, final_y); convex_brush.setFillColor(map[y][x]->color()); - + main_window.draw(convex_brush); if (hero_row == y && hero_col == x) { - // Place the hero sprite - convex_brush.setFillColor(sf::Color::White); + // Draw the hero sprite + convex_brush.setFillColor(palette::White); main_window.draw(convex_brush); } - // Move painter to next cell of row + // Move painter to next cell of current column painter_y += cell_height; - shift -= cell_deviation; + horizontal_shift -= cell_deviation; } - // Move painter to next row of the map - painter_y = 0; + // Move painter to next column + painter_y = 60; painter_x += cell_width; } diff --git a/src/game.h b/src/game.h index 75d5136..778c6fa 100644 --- a/src/game.h +++ b/src/game.h @@ -15,8 +15,8 @@ class Game { private: // Game entities - std::unique_ptr hero; - std::unique_ptr level; + HeroPtr hero; + LevelPtr level; int current_level; diff --git a/src/level.cpp b/src/level.cpp index 84fbc2d..7926d34 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -28,6 +28,12 @@ void Level::readMap(std::ifstream &file) } } +template // [D]erived - [B]ase +std::unique_ptr static_unique_pointer_cast (std::unique_ptr&& old) +{ + return std::unique_ptr{static_cast(old.release())}; +} + Level::Level(const std::string &map_file) { prepareCellInstances(); @@ -56,7 +62,9 @@ Level::Level(const std::string &map_file) 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); + 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); } } } @@ -65,10 +73,6 @@ Level::~Level() { for (Cell *cell : default_cells) delete cell; - - for (Row &row : map) - for (Cell *cell : row) - delete cell; } size_t Level::rows() const @@ -83,16 +87,12 @@ size_t Level::cols() const void Level::placeBridge(coordinate row, coordinate col) { - Cell *buf = map[row][col]; - map[row][col] = new PassableCell(row, col, sf::Color::Black); - delete buf; + map[row][col] = std::make_unique(row, col, palette::Black); } void Level::removeCharge(coordinate row, coordinate col) { - Cell *buf = map[row][col]; - map[row][col] = new PassableCell(row, col, color_ground); - delete buf; + map[row][col] = std::make_unique(row, col, color_ground); } Map& Level::mapArray() diff --git a/src/level.h b/src/level.h index 600a9bb..bd0e725 100644 --- a/src/level.h +++ b/src/level.h @@ -6,7 +6,7 @@ const std::string default_file_name = "test_map"; -using Row = std::vector; +using Row = std::vector; using Map = std::vector; /// Abstraction over 2D array to quickly get access to level cells