From 8142a9c87a13cecc7a3698e877f24d89f128c074 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sat, 21 Apr 2018 14:50:52 -0400 Subject: Started working on prototype objects --- CMakeLists.txt | 3 ++ src/collision.h | 1 + src/components/mappable.h | 6 ++++ src/components/playable.h | 2 ++ src/components/ponderable.h | 44 ++++++++++++++++++++--- src/game.cpp | 4 ++- src/game.h | 9 ++++- src/map.h | 66 ++++++++++++++++++++++++++++++++-- src/prototype.cpp | 55 ++++++++++++++++++++++++++++ src/prototype.h | 45 +++++++++++++++++++++++ src/prototype_manager.cpp | 71 ++++++++++++++++++++++++++++++++++++ src/prototype_manager.h | 23 ++++++++++++ src/systems/mapping.cpp | 14 ++++++++ src/systems/playing.cpp | 33 ++++++++++++++++- src/systems/playing.h | 2 ++ src/systems/pondering.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++--- src/systems/pondering.h | 9 ++++- src/world.cpp | 67 +++++++++++++++++++++++++++------- src/xml.cpp | 13 +++++++ src/xml.h | 8 +++++ 20 files changed, 533 insertions(+), 30 deletions(-) create mode 100644 src/prototype.cpp create mode 100644 src/prototype.h create mode 100644 src/prototype_manager.cpp create mode 100644 src/prototype_manager.h create mode 100644 src/xml.cpp create mode 100644 src/xml.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 34246ad..0309cd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,9 @@ add_executable(Aromatherapy src/world.cpp src/util.cpp src/collision.cpp + src/prototype.cpp + src/prototype_manager.cpp + src/xml.cpp src/renderer/renderer.cpp src/renderer/mesh.cpp src/renderer/shader.cpp diff --git a/src/collision.h b/src/collision.h index e5371f8..0f62b98 100644 --- a/src/collision.h +++ b/src/collision.h @@ -15,6 +15,7 @@ public: platform, adjacency, warp, + event, danger }; diff --git a/src/components/mappable.h b/src/components/mappable.h index 633cdf4..e26326d 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h @@ -125,6 +125,11 @@ public: font_ = std::move(v); } + inline std::map& getInstances() + { + return instances_; + } + private: size_t mapId_ = -1; @@ -135,6 +140,7 @@ private: asc_boundaries_type downBoundaries_; Texture tileset_; Texture font_; + std::map instances_; }; #endif /* end of include guard: MAPPABLE_H_0B0316FB */ diff --git a/src/components/playable.h b/src/components/playable.h index 86a7ee7..0d91929 100644 --- a/src/components/playable.h +++ b/src/components/playable.h @@ -18,6 +18,8 @@ public: int checkpointMapId = -1; double checkpointX = 0; double checkpointY = 0; + bool checkpointObjectActivated = false; + size_t checkpointObjectIndex = 0; }; diff --git a/src/components/ponderable.h b/src/components/ponderable.h index 78af25f..da509a2 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -2,22 +2,44 @@ #define TANGIBLE_H_746DB3EE #include "component.h" +#include +#include + +class Game; class PonderableComponent : public Component { public: - enum class Type { + enum class BodyType { vacuumed, freefalling }; - PonderableComponent(Type type) : type_(type) + enum class ColliderType { + player, + event + }; + + static const size_t COLLIDER_TYPES = 2; + + PonderableComponent( + BodyType bodyType, + ColliderType colliderType) : + bodyType_(bodyType), + colliderType_(colliderType) + { + } + + using event_callback_type = std::function; + + inline BodyType getBodyType() const { + return bodyType_; } - inline Type getType() const + inline ColliderType getColliderType() const { - return type_; + return colliderType_; } inline double getVelocityX() const @@ -90,16 +112,28 @@ public: collidable_ = v; } + inline const event_callback_type& getEventCallback(ColliderType v) const + { + return eventCallbacks_[static_cast(v)]; + } + + inline void setEventCallback(ColliderType v, event_callback_type callback) + { + eventCallbacks_[static_cast(v)] = std::move(callback); + } + private: double velX_ = 0.0; double velY_ = 0.0; double accelX_ = 0.0; double accelY_ = 0.0; - Type type_ = Type::vacuumed; + BodyType bodyType_; + ColliderType colliderType_; bool grounded_ = false; bool frozen_ = false; bool collidable_ = true; + std::array eventCallbacks_; }; #endif /* end of include guard: TANGIBLE_H_746DB3EE */ diff --git a/src/game.cpp b/src/game.cpp index 3da23a3..5259739 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -28,7 +28,9 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) game.systemManager_.input(key, action); } -Game::Game() : world_("res/maps.xml") +Game::Game() : + world_("res/maps.xml"), + prototypes_("res/entities.xml") { systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); diff --git a/src/game.h b/src/game.h index 43e08da..2dd7f91 100644 --- a/src/game.h +++ b/src/game.h @@ -5,6 +5,7 @@ #include "system_manager.h" #include "world.h" #include "renderer/renderer.h" +#include "prototype_manager.h" class Game { public: @@ -28,11 +29,16 @@ public: return systemManager_; } - inline const World& getWorld() + inline const World& getWorld() const { return world_; } + inline const PrototypeManager& getPrototypeManager() const + { + return prototypes_; + } + friend void key_callback( GLFWwindow* window, int key, @@ -46,6 +52,7 @@ private: EntityManager entityManager_; SystemManager systemManager_; World world_; + PrototypeManager prototypes_; bool shouldQuit_ = false; }; diff --git a/src/map.h b/src/map.h index 6fe1e62..0e11b9d 100644 --- a/src/map.h +++ b/src/map.h @@ -4,7 +4,6 @@ #include #include #include -#include #include class Map { @@ -44,6 +43,59 @@ public: int mapId_; }; + class Object { + public: + + Object( + std::string type, + double x, + double y, + size_t index, + std::map items) : + type_(std::move(type)), + x_(x), + y_(y), + index_(index), + items_(std::move(items)) + { + } + + inline const std::string& getType() const + { + return type_; + } + + inline double getX() const + { + return x_; + } + + inline double getY() const + { + return y_; + } + + inline size_t getIndex() const + { + return index_; + } + + inline const std::map& getItems() const + { + return items_; + } + + private: + + std::string type_; + double x_; + double y_; + size_t index_; + std::map items_; + }; + + using object_storage_type = std::list; + Map( int id, std::vector tiles, @@ -51,14 +103,16 @@ public: Adjacent leftAdjacent, Adjacent rightAdjacent, Adjacent upAdjacent, - Adjacent downAdjacent) : + Adjacent downAdjacent, + object_storage_type objects) : id_(id), tiles_(std::move(tiles)), title_(std::move(title)), leftAdjacent_(std::move(leftAdjacent)), rightAdjacent_(std::move(rightAdjacent)), upAdjacent_(std::move(upAdjacent)), - downAdjacent_(std::move(downAdjacent)) + downAdjacent_(std::move(downAdjacent)), + objects_(std::move(objects)) { } @@ -97,6 +151,11 @@ public: return downAdjacent_; } + inline const object_storage_type& getObjects() const + { + return objects_; + } + private: int id_; @@ -106,6 +165,7 @@ private: Adjacent rightAdjacent_; Adjacent upAdjacent_; Adjacent downAdjacent_; + object_storage_type objects_; }; #endif /* end of include guard: MAP_H_74055FC0 */ diff --git a/src/prototype.cpp b/src/prototype.cpp new file mode 100644 index 0000000..a4a0e80 --- /dev/null +++ b/src/prototype.cpp @@ -0,0 +1,55 @@ +#include "prototype.h" +#include "game.h" +#include "animation.h" +#include "components/animatable.h" +#include "components/transformable.h" +#include "components/ponderable.h" +#include "systems/pondering.h" +#include "systems/playing.h" + +id_type Prototype::instantiate( + Game& game, + const Map::Object& object) const +{ + id_type entity = game.getEntityManager().emplaceEntity(); + + AnimationSet entityGraphics(spritePath_.c_str(), w_, h_, 1); + entityGraphics.emplaceAnimation("default", 0, 1, 1); + + game.getEntityManager().emplaceComponent( + entity, + std::move(entityGraphics), + "default"); + + game.getEntityManager().emplaceComponent( + entity, + object.getX(), + object.getY(), + w_, + h_); + + game.getSystemManager().getSystem().initializeBody( + entity, + PonderableComponent::BodyType::vacuumed, + PonderableComponent::ColliderType::event); + + auto& ponderable = game.getEntityManager(). + getComponent(entity); + + switch (action_) + { + case Action::save: + { + ponderable.setEventCallback(PonderableComponent::ColliderType::player, + [] (Game& game) { + auto& playing = game.getSystemManager().getSystem(); + + playing.save(); + }); + + break; + } + } + + return entity; +} diff --git a/src/prototype.h b/src/prototype.h new file mode 100644 index 0000000..03df00f --- /dev/null +++ b/src/prototype.h @@ -0,0 +1,45 @@ +#ifndef PROTOTYPE_H_BB208E0F +#define PROTOTYPE_H_BB208E0F + +#include +#include +#include "map.h" +#include "entity_manager.h" + +class Game; + +class Prototype { +public: + + using id_type = EntityManager::id_type; + + enum class Action { + none, + save + }; + + Prototype( + int w, + int h, + std::string spritePath, + Action action) : + w_(w), + h_(h), + spritePath_(std::move(spritePath)), + action_(action) + { + } + + id_type instantiate( + Game& game, + const Map::Object& object) const; + +private: + + int w_; + int h_; + std::string spritePath_; + Action action_; +}; + +#endif /* end of include guard: PROTOTYPE_H_BB208E0F */ diff --git a/src/prototype_manager.cpp b/src/prototype_manager.cpp new file mode 100644 index 0000000..250aa59 --- /dev/null +++ b/src/prototype_manager.cpp @@ -0,0 +1,71 @@ +#include "prototype_manager.h" +#include +#include +#include "xml.h" + +PrototypeManager::PrototypeManager(std::string path) +{ + xmlDocPtr doc = xmlParseFile(path.c_str()); + if (doc == nullptr) + { + throw std::invalid_argument("Cannot find prototypes file"); + } + + xmlNodePtr top = xmlDocGetRootElement(doc); + if (top == nullptr) + { + throw std::invalid_argument("Error parsing prototypes file"); + } + + if (xmlStrcmp(top->name, reinterpret_cast("entities"))) + { + throw std::invalid_argument("Error parsing prototypes file"); + } + + xmlChar* key = nullptr; + + for (xmlNodePtr node = top->xmlChildrenNode; + node != nullptr; + node = node->next) + { + if (!xmlStrcmp(node->name, reinterpret_cast("entity"))) + { + key = getProp(node, "id"); + std::string prototypeName(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "sprite"); + std::string spritePath(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "width"); + int width = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "height"); + int height = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "action"); + std::string actionStr(reinterpret_cast(key)); + xmlFree(key); + + Prototype::Action pAction = Prototype::Action::none; + if (actionStr == "save") + { + pAction = Prototype::Action::save; + } + + prototypes_.emplace( + std::piecewise_construct, + std::tie(prototypeName), + std::tie( + width, + height, + spritePath, + pAction)); + } + } + + xmlFreeDoc(doc); +} diff --git a/src/prototype_manager.h b/src/prototype_manager.h new file mode 100644 index 0000000..3fadd23 --- /dev/null +++ b/src/prototype_manager.h @@ -0,0 +1,23 @@ +#ifndef PROTOTYPE_MANAGER_H_83CECCA6 +#define PROTOTYPE_MANAGER_H_83CECCA6 + +#include +#include +#include "prototype.h" + +class PrototypeManager { +public: + + PrototypeManager(std::string path); + + inline const Prototype& getPrototype(std::string name) const + { + return prototypes_.at(name); + } + +private: + + std::map prototypes_; +}; + +#endif /* end of include guard: PROTOTYPE_MANAGER_H_83CECCA6 */ diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index a3a17ec..c7c2f9d 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -195,4 +195,18 @@ void MappingSystem::loadMap(size_t mapId) MappableComponent::Boundary::Type::danger); } } + + for (const Map::Object& object : map.getObjects()) + { + const Prototype& prototype = game_.getPrototypeManager(). + getPrototype(object.getType()); + + id_type emplacedObject = prototype.instantiate( + game_, + object.getX(), + object.getY(), + object.getItems()); + + mappable.getInstances()[object.getIndex()] = emplacedObject; + } } diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp index 40d9706..5077f8a 100644 --- a/src/systems/playing.cpp +++ b/src/systems/playing.cpp @@ -5,6 +5,7 @@ #include "components/playable.h" #include "components/controllable.h" #include "components/orientable.h" +#include "components/mappable.h" #include "systems/mapping.h" #include "systems/pondering.h" #include "systems/orienting.h" @@ -84,7 +85,8 @@ void PlayingSystem::initPlayer() game_.getSystemManager().getSystem().initializeBody( player, - PonderableComponent::Type::freefalling); + PonderableComponent::BodyType::freefalling, + PonderableComponent::ColliderType::player); game_.getEntityManager().emplaceComponent(player); game_.getEntityManager().emplaceComponent(player); @@ -173,3 +175,32 @@ void PlayingSystem::die() }); } } + +void PlayingSystem::save() +{ + playSound("res/Pickup_Coin23.wav", 0.25); + + auto players = game_.getEntityManager().getEntitiesWithComponents< + TransformableComponent, + PlayableComponent>(); + + auto maps = game_.getEntityManager().getEntitiesWithComponents< + MappableComponent>(); + + auto& mappable = game_.getEntityManager(). + getComponent(*maps.begin()); + + for (id_type player : players) + { + auto& transformable = game_.getEntityManager(). + getComponent(player); + + auto& playable = game_.getEntityManager(). + getComponent(player); + + playable.checkpointMapId = mappable.getMapId(); + playable.checkpointX = transformable.getX(); + playable.checkpointY = transformable.getY(); + playable.checkpointObjectActivated = false; + } +} diff --git a/src/systems/playing.h b/src/systems/playing.h index ff16808..d7f5072 100644 --- a/src/systems/playing.h +++ b/src/systems/playing.h @@ -23,6 +23,8 @@ public: void die(); + void save(); + }; #endif /* end of include guard: PLAYING_H_70A54F7D */ diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 02d5cfc..9115988 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -39,7 +39,7 @@ void PonderingSystem::tick(double dt) ponderable.setVelocityY( ponderable.getVelocityY() + ponderable.getAccelY() * dt); - if ((ponderable.getType() == PonderableComponent::Type::freefalling) + if ((ponderable.getBodyType() == PonderableComponent::BodyType::freefalling) && (ponderable.getVelocityY() > TERMINAL_VELOCITY)) { ponderable.setVelocityY(TERMINAL_VELOCITY); @@ -58,7 +58,7 @@ void PonderingSystem::tick(double dt) std::priority_queue collisions; - // Find collisions + // Find map collisions for (id_type mapEntity : maps) { auto& mappable = game_.getEntityManager(). @@ -366,6 +366,51 @@ void PonderingSystem::tick(double dt) } } + // Find body collisions + for (id_type body : entities) + { + // Can't collide with self + if (body == entity) + { + continue; + } + + // Make sure the body is collidable + auto& colliderPonderable = + game_.getEntityManager().getComponent(body); + + if (!colliderPonderable.isCollidable()) + { + continue; + } + + // Test if the body was already colliding + auto& colliderTransformable = + game_.getEntityManager().getComponent(body); + + if ((oldRight > colliderTransformable.getX()) + && (oldX < colliderTransformable.getX() + colliderTransformable.getW()) + && (oldBottom > colliderTransformable.getY()) + && (oldY < colliderTransformable.getY() + colliderTransformable.getH())) + { + continue; + } + + // Test if there is a new collision + if (!((newX + transformable.getW() > colliderTransformable.getX()) + && (newX < colliderTransformable.getX() + colliderTransformable.getW()) + && (newY + transformable.getH() > colliderTransformable.getY()) + && (newY < + colliderTransformable.getY() + colliderTransformable.getH()))) + { + continue; + } + + // Process the collision + processBodyCollision(entity, body); + processBodyCollision(body, entity); + } + // Move transformable.setX(newX); transformable.setY(newY); @@ -399,13 +444,46 @@ void PonderingSystem::tick(double dt) void PonderingSystem::initializeBody( id_type entity, - PonderableComponent::Type type) + PonderableComponent::BodyType bodyType, + PonderableComponent::ColliderType colliderType) { auto& ponderable = game_.getEntityManager(). - emplaceComponent(entity, type); + emplaceComponent(entity, bodyType, colliderType); - if (type == PonderableComponent::Type::freefalling) + if (bodyType == PonderableComponent::BodyType::freefalling) { ponderable.setAccelY(NORMAL_GRAVITY); } } + +void PonderingSystem::processBodyCollision(id_type body, id_type collider) +{ + auto& bodyPonderable = game_.getEntityManager(). + getComponent(body); + + auto& colliderPonderable = game_.getEntityManager(). + getComponent(collider); + + switch (colliderPonderable.getColliderType()) + { + case PonderableComponent::ColliderType::event: + { + auto& callback = colliderPonderable. + getEventCallback(bodyPonderable.getColliderType()); + + if (callback) + { + callback(game_); + } + + break; + } + + default: + { + // Do nothing. + + break; + } + } +} diff --git a/src/systems/pondering.h b/src/systems/pondering.h index d70525b..7e342df 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h @@ -14,7 +14,14 @@ public: void tick(double dt); - void initializeBody(id_type entity, PonderableComponent::Type type); + void initializeBody( + id_type entity, + PonderableComponent::BodyType bodyType, + PonderableComponent::ColliderType colliderType); + +private: + + void processBodyCollision(id_type body, id_type collider); }; diff --git a/src/world.cpp b/src/world.cpp index 3b6bd41..d239067 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -1,19 +1,8 @@ #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; -} +#include "xml.h" World::World(std::string filename) { @@ -67,6 +56,7 @@ World::World(std::string filename) Map::Adjacent rightAdj; Map::Adjacent upAdj; Map::Adjacent downAdj; + std::list objects; for (xmlNodePtr mapNode = node->xmlChildrenNode; mapNode != nullptr; @@ -77,6 +67,10 @@ World::World(std::string filename) reinterpret_cast("environment"))) { key = xmlNodeGetContent(mapNode); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing XML file"); + } mapTiles.clear(); mapTiles.push_back(atoi(strtok(reinterpret_cast(key), ",\n"))); @@ -86,6 +80,52 @@ World::World(std::string filename) } xmlFree(key); + } else if (!xmlStrcmp( + mapNode->name, + reinterpret_cast("entity"))) + { + key = getProp(mapNode, "type"); + std::string entType(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(mapNode, "x"); + int entX = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(mapNode, "y"); + int entY = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(mapNode, "index"); + int entIndex = atoi(reinterpret_cast(key)); + xmlFree(key); + + std::map items; + + for (xmlNodePtr entityNode = mapNode->xmlChildrenNode; + entityNode != nullptr; + entityNode = entityNode->next) + { + key = getProp(entityNode, "id"); + std::string itemId(reinterpret_cast(key)); + xmlFree(key); + + key = xmlNodeGetContent(entityNode); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing XML file"); + } + + items[itemId] = atoi(reinterpret_cast(key)); + xmlFree(key); + } + + objects.emplace_back( + std::move(entType), + entX, + entY, + entIndex, + std::move(items)); } else if (!xmlStrcmp( mapNode->name, reinterpret_cast("adjacent"))) @@ -147,7 +187,8 @@ World::World(std::string filename) leftAdj, rightAdj, upAdj, - downAdj)); + downAdj, + std::move(objects))); } } diff --git a/src/xml.cpp b/src/xml.cpp new file mode 100644 index 0000000..f6f60ea --- /dev/null +++ b/src/xml.cpp @@ -0,0 +1,13 @@ +#include "xml.h" +#include + +xmlChar* getProp(xmlNodePtr node, const char* attr) +{ + xmlChar* key = xmlGetProp(node, reinterpret_cast(attr)); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing XML file"); + } + + return key; +} diff --git a/src/xml.h b/src/xml.h new file mode 100644 index 0000000..8ecf639 --- /dev/null +++ b/src/xml.h @@ -0,0 +1,8 @@ +#ifndef XML_H_E5E95685 +#define XML_H_E5E95685 + +#include + +xmlChar* getProp(xmlNodePtr node, const char* attr); + +#endif /* end of include guard: XML_H_E5E95685 */ -- cgit 1.4.1