From 77be863f4f15d2481a64e4e8dadb4060a6e4e590 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 11 Feb 2018 12:34:52 -0500 Subject: Implemented map rendering and basic collision Only wall and platform collision currently works, and map edges are not currently implemented. --- src/components/mappable.h | 146 ++++++++++++++++++++++++++ src/components/ponderable.h | 23 +++- src/consts.h | 3 + src/direction.h | 8 +- src/entity_manager.h | 20 +++- src/game.cpp | 14 ++- src/game.h | 9 +- src/map.h | 45 ++++++++ src/systems/controlling.cpp | 22 ++-- src/systems/mapping.cpp | 143 +++++++++++++++++++++++++ src/systems/mapping.h | 19 ++++ src/systems/pondering.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++-- src/systems/pondering.h | 16 +++ src/world.cpp | 99 ++++++++++++++++++ src/world.h | 41 ++++++++ 15 files changed, 825 insertions(+), 31 deletions(-) create mode 100644 src/components/mappable.h create mode 100644 src/map.h create mode 100644 src/systems/mapping.cpp create mode 100644 src/systems/mapping.h create mode 100644 src/world.cpp create mode 100644 src/world.h (limited to 'src') diff --git a/src/components/mappable.h b/src/components/mappable.h new file mode 100644 index 0000000..7530919 --- /dev/null +++ b/src/components/mappable.h @@ -0,0 +1,146 @@ +#ifndef MAPPABLE_H_0B0316FB +#define MAPPABLE_H_0B0316FB + +#include +#include "component.h" +#include "renderer.h" +#include "map.h" + +class MappableComponent : public Component { +public: + + class Boundary { + public: + + enum class Type { + wall, + wrap, + teleport, + reverse, + platform, + danger + }; + + Boundary( + double axis, + double lower, + double upper, + Type type) : + axis_(axis), + lower_(lower), + upper_(upper), + type_(type) + { + } + + inline double getAxis() const + { + return axis_; + } + + inline double getLower() const + { + return lower_; + } + + inline double getUpper() const + { + return upper_; + } + + inline Type getType() const + { + return type_; + } + + private: + + double axis_; + double lower_; + double upper_; + Type type_; + }; + + MappableComponent( + Texture tileset, + Texture font) : + tileset_(std::move(tileset)), + font_(std::move(font)) + { + } + + using asc_boundaries_type = + std::multimap< + double, + Boundary, + std::less>; + + using desc_boundaries_type = + std::multimap< + double, + Boundary, + std::greater>; + + inline size_t getMapId() const + { + return mapId_; + } + + inline void setMapId(size_t v) + { + mapId_ = v; + } + + inline desc_boundaries_type& getLeftBoundaries() + { + return leftBoundaries_; + } + + inline asc_boundaries_type& getRightBoundaries() + { + return rightBoundaries_; + } + + inline desc_boundaries_type& getUpBoundaries() + { + return upBoundaries_; + } + + inline asc_boundaries_type& getDownBoundaries() + { + return downBoundaries_; + } + + inline const Texture& getTileset() const + { + return tileset_; + } + + inline void setTileset(Texture v) + { + tileset_ = std::move(v); + } + + inline const Texture& getFont() const + { + return font_; + } + + inline void setFont(Texture v) + { + font_ = std::move(v); + } + +private: + + size_t mapId_ = -1; + + desc_boundaries_type leftBoundaries_; + asc_boundaries_type rightBoundaries_; + desc_boundaries_type upBoundaries_; + asc_boundaries_type downBoundaries_; + Texture tileset_; + Texture font_; +}; + +#endif /* end of include guard: MAPPABLE_H_0B0316FB */ diff --git a/src/components/ponderable.h b/src/components/ponderable.h index dfbf908..ac759b6 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -6,13 +6,27 @@ class PonderableComponent : public Component { public: - enum class state { + enum class Type { + vacuumed, + freefalling + }; + + enum class State { grounded, jumping, falling, dropping }; + PonderableComponent(Type type) : type_(type) + { + } + + inline Type getType() const + { + return type_; + } + inline double getVelocityX() const { return velX_; @@ -53,12 +67,12 @@ public: accelY_ = v; } - inline state getState() const + inline State getState() const { return state_; } - inline void setState(state arg) + inline void setState(State arg) { state_ = arg; } @@ -69,7 +83,8 @@ private: double velY_ = 0.0; double accelX_ = 0.0; double accelY_ = 0.0; - state state_ = state::grounded; + Type type_ = Type::vacuumed; + State state_ = State::grounded; }; #endif /* end of include guard: TANGIBLE_H_746DB3EE */ diff --git a/src/consts.h b/src/consts.h index 4595719..a6c9985 100644 --- a/src/consts.h +++ b/src/consts.h @@ -7,6 +7,9 @@ const int GAME_WIDTH = 320; const int GAME_HEIGHT = 200; const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1; +const int WALL_GAP = 6; +const int TILESET_COLS = 8; +const int FONT_COLS = 16; const int FRAMES_PER_SECOND = 60; const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND; diff --git a/src/direction.h b/src/direction.h index 32d6b41..9a4c801 100644 --- a/src/direction.h +++ b/src/direction.h @@ -2,10 +2,10 @@ #define DIRECTION_H_9C49EAFD enum class Direction { - Left, - Right, - Up, - Down + left, + right, + up, + down }; #endif /* end of include guard: DIRECTION_H_9C49EAFD */ diff --git a/src/entity_manager.h b/src/entity_manager.h index 9068fe3..7fd82fc 100644 --- a/src/entity_manager.h +++ b/src/entity_manager.h @@ -106,7 +106,7 @@ public: { if ((entity >= entities.size()) || slotAvailable[entity]) { - throw std::invalid_argument("Cannot delete non-existent entity"); + throw std::invalid_argument("Cannot get non-existent entity"); } EntityData& data = entities[entity]; @@ -138,7 +138,7 @@ public: { if ((entity >= entities.size()) || slotAvailable[entity]) { - throw std::invalid_argument("Cannot delete non-existent entity"); + throw std::invalid_argument("Cannot get non-existent entity"); } EntityData& data = entities[entity]; @@ -167,7 +167,7 @@ public: { if ((entity >= entities.size()) || slotAvailable[entity]) { - throw std::invalid_argument("Cannot delete non-existent entity"); + throw std::invalid_argument("Cannot get non-existent entity"); } EntityData& data = entities[entity]; @@ -181,6 +181,20 @@ public: return *dynamic_cast(data.components[componentType].get()); } + template + bool hasComponent(id_type entity) + { + if ((entity >= entities.size()) || slotAvailable[entity]) + { + throw std::invalid_argument("Cannot get non-existent entity"); + } + + EntityData& data = entities[entity]; + std::type_index componentType = typeid(T); + + return data.components.count(componentType); + } + template std::set getEntitiesWithComponents() { diff --git a/src/game.cpp b/src/game.cpp index dfe700e..7cbe7e0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -8,6 +8,7 @@ #include "systems/controlling.h" #include "systems/pondering.h" #include "systems/animating.h" +#include "systems/mapping.h" #include "animation.h" #include "renderer.h" #include "consts.h" @@ -26,10 +27,14 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) game.systemManager_.input(key, action); } -Game::Game(GLFWwindow* window) : window_(window) +Game::Game( + GLFWwindow* window) : + window_(window), + world_("res/maps.xml") { systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); + systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); int player = entityManager_.emplaceEntity(); @@ -49,11 +54,16 @@ Game::Game(GLFWwindow* window) : window_(window) player, 203, 44, 10, 12); + systemManager_.getSystem().initializeBody( + player, + PonderableComponent::Type::freefalling); + entityManager_.emplaceComponent(player); - entityManager_.emplaceComponent(player); entityManager_.emplaceComponent(player); entityManager_.emplaceComponent(player); + systemManager_.getSystem().loadMap(world_.getStartingMapId()); + glfwSwapInterval(1); glfwSetWindowUserPointer(window_, this); glfwSetKeyCallback(window_, key_callback); diff --git a/src/game.h b/src/game.h index 7bd038e..346d67e 100644 --- a/src/game.h +++ b/src/game.h @@ -4,6 +4,7 @@ #include "renderer.h" #include "entity_manager.h" #include "system_manager.h" +#include "world.h" class Game { public: @@ -22,6 +23,11 @@ public: return systemManager_; } + inline const World& getWorld() + { + return world_; + } + friend void key_callback( GLFWwindow* window, int key, @@ -31,9 +37,10 @@ public: private: + GLFWwindow* const window_; EntityManager entityManager_; SystemManager systemManager_; - GLFWwindow* const window_; + World world_; bool shouldQuit_ = false; }; diff --git a/src/map.h b/src/map.h new file mode 100644 index 0000000..9177870 --- /dev/null +++ b/src/map.h @@ -0,0 +1,45 @@ +#ifndef MAP_H_74055FC0 +#define MAP_H_74055FC0 + +#include +#include +#include +#include +#include + +class Map { +public: + + Map( + int id, + std::vector tiles, + std::string title) : + id_(id), + tiles_(std::move(tiles)), + title_(std::move(title)) + { + } + + inline size_t getId() const + { + return id_; + } + + inline const std::vector& getTiles() const + { + return tiles_; + } + + inline const std::string& getTitle() const + { + return title_; + } + +private: + + int id_; + std::vector tiles_; + std::string title_; +}; + +#endif /* end of include guard: MAP_H_74055FC0 */ diff --git a/src/systems/controlling.cpp b/src/systems/controlling.cpp index 3647ff8..fa09d11 100644 --- a/src/systems/controlling.cpp +++ b/src/systems/controlling.cpp @@ -123,7 +123,7 @@ void ControllingSystem::walkLeft(id_type entity) auto& animating = game_.getSystemManager().getSystem(); - if (ponderable.getState() == PonderableComponent::state::grounded) + if (ponderable.getState() == PonderableComponent::State::grounded) { animating.startAnimation(entity, "walkingLeft"); } else { @@ -141,7 +141,7 @@ void ControllingSystem::walkRight(id_type entity) auto& animating = game_.getSystemManager().getSystem(); - if (ponderable.getState() == PonderableComponent::state::grounded) + if (ponderable.getState() == PonderableComponent::State::grounded) { animating.startAnimation(entity, "walkingRight"); } else { @@ -156,7 +156,7 @@ void ControllingSystem::stopWalking(id_type entity) ponderable.setVelocityX(0); - if (ponderable.getState() == PonderableComponent::state::grounded) + if (ponderable.getState() == PonderableComponent::State::grounded) { auto& animating = game_.getSystemManager().getSystem(); @@ -173,13 +173,13 @@ void ControllingSystem::jump(id_type entity) { auto& ponderable = game_.getEntityManager().getComponent(entity); - if (ponderable.getState() == PonderableComponent::state::grounded) + if (ponderable.getState() == PonderableComponent::State::grounded) { playSound("res/Randomize87.wav", 0.25); ponderable.setVelocityY(JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3)); ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3)); - ponderable.setState(PonderableComponent::state::jumping); + ponderable.setState(PonderableComponent::State::jumping); } } @@ -187,10 +187,10 @@ void ControllingSystem::stopJumping(id_type entity) { auto& ponderable = game_.getEntityManager().getComponent(entity); - if (ponderable.getState() == PonderableComponent::state::jumping) + if (ponderable.getState() == PonderableComponent::State::jumping) { ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233)); - ponderable.setState(PonderableComponent::state::falling); + ponderable.setState(PonderableComponent::State::falling); } } @@ -199,12 +199,12 @@ void ControllingSystem::drop(id_type entity, bool start) auto& droppable = game_.getEntityManager().getComponent(entity); auto& ponderable = game_.getEntityManager().getComponent(entity); - if (start && (ponderable.getState() == PonderableComponent::state::grounded)) + if (start && (ponderable.getState() == PonderableComponent::State::grounded)) { - ponderable.setState(PonderableComponent::state::dropping); - } else if ((!start) && (ponderable.getState() == PonderableComponent::state::dropping)) + ponderable.setState(PonderableComponent::State::dropping); + } else if ((!start) && (ponderable.getState() == PonderableComponent::State::dropping)) { - ponderable.setState(PonderableComponent::state::grounded); + ponderable.setState(PonderableComponent::State::grounded); } droppable.setDroppable(start); } diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp new file mode 100644 index 0000000..8723e16 --- /dev/null +++ b/src/systems/mapping.cpp @@ -0,0 +1,143 @@ +#include "mapping.h" +#include "components/mappable.h" +#include "game.h" +#include "consts.h" + +#include + +template +inline void addBoundary( + Storage& boundaries, + int axis, + int lower, + int upper, + MappableComponent::Boundary::Type type) +{ + boundaries.emplace(std::piecewise_construct, + std::tie(axis), + std::tie(axis, lower, upper, type)); +} + +void MappingSystem::render(Texture& texture) +{ + auto entities = game_.getEntityManager().getEntitiesWithComponents< + MappableComponent>(); + + for (id_type entity : entities) + { + auto& mappable = game_.getEntityManager(). + getComponent(entity); + + const Map& map = game_.getWorld().getMap(mappable.getMapId()); + + for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) + { + int x = i % MAP_WIDTH; + int y = i / MAP_WIDTH; + int tile = map.getTiles()[i]; + + if (tile > 0) + { + Rectangle dst { + x * TILE_WIDTH, + y * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + Rectangle src { + (tile % TILESET_COLS) * TILE_WIDTH, + (tile / TILESET_COLS) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + texture.blit(mappable.getTileset(), std::move(src), std::move(dst)); + } + } + + int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (map.getTitle().size() / 2); + for (size_t i = 0; i < map.getTitle().size(); i++) + { + Rectangle src { + (map.getTitle()[i] % FONT_COLS) * TILE_WIDTH, + (map.getTitle()[i] / FONT_COLS) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + Rectangle dst { + (startX + static_cast(i)) * TILE_WIDTH, + 24 * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + texture.blit(mappable.getFont(), std::move(src), std::move(dst)); + } + } +} + +void MappingSystem::loadMap(size_t mapId) +{ + id_type mapEntity = game_.getEntityManager().emplaceEntity(); + + auto& mappable = game_.getEntityManager(). + emplaceComponent(mapEntity, + Texture("res/tiles.png"), + Texture("res/font.bmp")); + + mappable.setMapId(mapId); + + const Map& map = game_.getWorld().getMap(mappable.getMapId()); + + for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) + { + size_t x = i % MAP_WIDTH; + size_t y = i / MAP_WIDTH; + int tile = map.getTiles()[i]; + + if ((tile >= 5) && (tile <= 7)) + { + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x + 1) * TILE_WIDTH, + MappableComponent::Boundary::Type::platform); + } else if ((tile > 0) && (tile < 28)) + { + addBoundary( + mappable.getRightBoundaries(), + x * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getLeftBoundaries(), + (x+1) * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getUpBoundaries(), + (y+1) * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::wall); + } else if (tile == 42) + { + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::danger); + } + } +} diff --git a/src/systems/mapping.h b/src/systems/mapping.h new file mode 100644 index 0000000..53d054b --- /dev/null +++ b/src/systems/mapping.h @@ -0,0 +1,19 @@ +#ifndef MAPPING_H_33FC2294 +#define MAPPING_H_33FC2294 + +#include "system.h" + +class MappingSystem : public System { +public: + + MappingSystem(Game& game) : System(game) + { + } + + void render(Texture& texture); + + void loadMap(size_t mapId); + +}; + +#endif /* end of include guard: MAPPING_H_33FC2294 */ diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index e40db1d..26a6f56 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -2,6 +2,8 @@ #include "game.h" #include "components/ponderable.h" #include "components/transformable.h" +#include "components/droppable.h" +#include "consts.h" void PonderingSystem::tick(double dt) { @@ -9,17 +11,251 @@ void PonderingSystem::tick(double dt) PonderableComponent, TransformableComponent>(); + auto maps = game_.getEntityManager().getEntitiesWithComponents< + MappableComponent>(); + for (id_type entity : entities) { - auto& transformable = game_.getEntityManager().getComponent(entity); - auto& ponderable = game_.getEntityManager().getComponent(entity); + auto& transformable = game_.getEntityManager(). + getComponent(entity); + + auto& ponderable = game_.getEntityManager(). + getComponent(entity); // Accelerate - ponderable.setVelocityX(ponderable.getVelocityX() + ponderable.getAccelX() * dt); - ponderable.setVelocityY(ponderable.getVelocityY() + ponderable.getAccelY() * dt); + ponderable.setVelocityX( + ponderable.getVelocityX() + ponderable.getAccelX() * dt); + + ponderable.setVelocityY( + ponderable.getVelocityY() + ponderable.getAccelY() * dt); + + const double oldX = transformable.getX(); + const double oldY = transformable.getY(); + const double oldRight = oldX + transformable.getW(); + const double oldBottom = oldY + transformable.getH(); + + double newX = oldX + ponderable.getVelocityX() * dt; + double newY = oldY + ponderable.getVelocityY() * dt; + + if (ponderable.getVelocityY() > 0.0) + { + ponderable.setState(PonderableComponent::State::falling); + } + + for (id_type mapEntity : maps) + { + auto& mappable = game_.getEntityManager(). + getComponent(mapEntity); + + if (newX < oldX) + { + for (auto it = mappable.getLeftBoundaries().lower_bound(oldX); + (it != std::end(mappable.getLeftBoundaries())) && (it->first >= newX); + it++) + { + if ((oldBottom > it->second.getLower()) + && (oldY < it->second.getUpper())) + { + // We have a collision! + processCollision( + entity, + Direction::left, + newX, + newY, + it->first, + it->second.getType()); + + break; + } + } + } else if (newX > oldX) + { + for (auto it = mappable.getRightBoundaries().lower_bound(oldRight); + (it != std::end(mappable.getRightBoundaries())) + && (it->first <= (newX + transformable.getW())); + it++) + { + if ((oldBottom > it->second.getLower()) + && (oldY < it->second.getUpper())) + { + // We have a collision! + processCollision( + entity, + Direction::right, + newX, + newY, + it->first, + it->second.getType()); + + break; + } + } + } + + if (newY < oldY) + { + for (auto it = mappable.getUpBoundaries().lower_bound(oldY); + (it != std::end(mappable.getUpBoundaries())) && (it->first >= newY); + it++) + { + if ((oldRight > it->second.getLower()) + && (oldX < it->second.getUpper())) + { + // We have a collision! + processCollision( + entity, + Direction::up, + newX, + newY, + it->first, + it->second.getType()); + + break; + } + } + } else if (newY > oldY) + { + for (auto it = mappable.getDownBoundaries().lower_bound(oldBottom); + (it != std::end(mappable.getDownBoundaries())) + && (it->first <= (newY + transformable.getH())); + it++) + { + if ((oldRight > it->second.getLower()) + && (oldX < it->second.getUpper())) + { + // We have a collision! + processCollision( + entity, + Direction::down, + newX, + newY, + it->first, + it->second.getType()); + + break; + } + } + } + } // Move - transformable.setX(transformable.getX() + ponderable.getVelocityX() * dt); - transformable.setY(transformable.getY() + ponderable.getVelocityY() * dt); + transformable.setX(newX); + transformable.setY(newY); + } +} + +void PonderingSystem::initializeBody( + id_type entity, + PonderableComponent::Type type) +{ + auto& ponderable = game_.getEntityManager(). + emplaceComponent(entity, type); + + if (type == PonderableComponent::Type::freefalling) + { + ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233)); + ponderable.setState(PonderableComponent::State::falling); + } +} + +void PonderingSystem::processCollision( + id_type entity, + Direction dir, + double& newX, + double& newY, + int axis, + MappableComponent::Boundary::Type type) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + auto& transformable = game_.getEntityManager(). + getComponent(entity); + + switch (type) + { + case MappableComponent::Boundary::Type::wall: + { + switch (dir) + { + case Direction::left: + { + newX = axis; + ponderable.setVelocityX(0.0); + + break; + } + + case Direction::right: + { + newX = axis - transformable.getW(); + ponderable.setVelocityX(0.0); + + break; + } + + case Direction::up: + { + newY = axis; + ponderable.setVelocityY(0.0); + + break; + } + + case Direction::down: + { + newY = axis - transformable.getH(); + ponderable.setVelocityY(0.0); + + if (ponderable.getState() == PonderableComponent::State::falling) + { + ponderable.setState(PonderableComponent::State::grounded); + } + + break; + } + } + + break; + } + + case MappableComponent::Boundary::Type::platform: + { + if (game_.getEntityManager().hasComponent(entity)) + { + auto& droppable = game_.getEntityManager(). + getComponent(entity); + + if (droppable.isDroppable()) + { + droppable.setDroppable(false); + } else { + newY = axis - transformable.getH(); + ponderable.setVelocityY(0.0); + + if (ponderable.getState() == PonderableComponent::State::falling) + { + ponderable.setState(PonderableComponent::State::grounded); + } + } + } else { + newY = axis - transformable.getH(); + ponderable.setVelocityY(0.0); + + if (ponderable.getState() == PonderableComponent::State::falling) + { + ponderable.setState(PonderableComponent::State::grounded); + } + } + + break; + } + + default: + { + // Not yet implemented. + + break; + } } } diff --git a/src/systems/pondering.h b/src/systems/pondering.h index 44e7600..a16622b 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h @@ -2,6 +2,9 @@ #define PONDERING_H_F2530E0E #include "system.h" +#include "components/mappable.h" +#include "components/ponderable.h" +#include "direction.h" class PonderingSystem : public System { public: @@ -11,6 +14,19 @@ public: } void tick(double dt); + + void initializeBody(id_type entity, PonderableComponent::Type type); + +private: + + void processCollision( + id_type entity, + Direction dir, + double& newX, + double& newY, + int axis, + MappableComponent::Boundary::Type type); + }; #endif /* end of include guard: PONDERING_H_F2530E0E */ diff --git a/src/world.cpp b/src/world.cpp new file mode 100644 index 0000000..9b1e4f6 --- /dev/null +++ b/src/world.cpp @@ -0,0 +1,99 @@ +#include "world.h" +#include +#include +#include +#include "consts.h" + +inline xmlChar* getProp(xmlNodePtr node, const char* attr) +{ + xmlChar* key = xmlGetProp(node, reinterpret_cast(attr)); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing world file"); + } + + return key; +} + +World::World(std::string filename) +{ + xmlDocPtr doc = xmlParseFile(filename.c_str()); + if (doc == nullptr) + { + throw std::invalid_argument("Cannot find world file"); + } + + xmlNodePtr top = xmlDocGetRootElement(doc); + if (top == nullptr) + { + throw std::invalid_argument("Error parsing world file"); + } + + if (xmlStrcmp(top->name, reinterpret_cast("world"))) + { + throw std::invalid_argument("Error parsing world file"); + } + + xmlChar* key = nullptr; + + key = getProp(top, "startx"); + startX_ = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(top, "starty"); + startY_ = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(top, "startmap"); + startMap_ = atoi(reinterpret_cast(key)); + xmlFree(key); + + for (xmlNodePtr node = top->xmlChildrenNode; + node != nullptr; + node = node->next) + { + if (!xmlStrcmp(node->name, reinterpret_cast("map"))) + { + key = getProp(node, "id"); + size_t mapId = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "title"); + std::string mapTitle(reinterpret_cast(key)); + xmlFree(key); + + std::vector mapTiles; + + for (xmlNodePtr mapNode = node->xmlChildrenNode; + mapNode != nullptr; + mapNode = mapNode->next) + { + if (!xmlStrcmp( + mapNode->name, + reinterpret_cast("environment"))) + { + key = xmlNodeGetContent(mapNode); + + mapTiles.clear(); + mapTiles.push_back(atoi(strtok(reinterpret_cast(key), ",\n"))); + for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) + { + mapTiles.push_back(atoi(strtok(nullptr, ",\n"))); + } + + xmlFree(key); + } + } + + maps_.emplace( + std::piecewise_construct, + std::forward_as_tuple(mapId), + std::forward_as_tuple( + mapId, + std::move(mapTiles), + std::move(mapTitle))); + } + } + + xmlFreeDoc(doc); +} diff --git a/src/world.h b/src/world.h new file mode 100644 index 0000000..b88adf4 --- /dev/null +++ b/src/world.h @@ -0,0 +1,41 @@ +#ifndef WORLD_H_153C698B +#define WORLD_H_153C698B + +#include +#include +#include "map.h" + +class World { +public: + + explicit World(std::string filename); + + inline const Map& getMap(size_t id) const + { + return maps_.at(id); + } + + inline size_t getStartingMapId() const + { + return startMap_; + } + + inline int getStartingX() const + { + return startX_; + } + + inline int getStartingY() const + { + return startY_; + } + +private: + + std::map maps_; + size_t startMap_; + int startX_; + int startY_; +}; + +#endif /* end of include guard: WORLD_H_153C698B */ -- cgit 1.4.1