diff options
-rw-r--r-- | include/components.hpp | 248 | ||||
-rw-r--r-- | include/player.hpp | 1 | ||||
-rw-r--r-- | include/world.hpp | 2 | ||||
-rw-r--r-- | src/components.cpp | 20 | ||||
-rw-r--r-- | src/player.cpp | 43 | ||||
-rw-r--r-- | src/world.cpp | 173 | ||||
-rw-r--r-- | xml/arena.xml | 6 | ||||
-rw-r--r-- | xml/entities.xml | 2 |
8 files changed, 306 insertions, 189 deletions
diff --git a/include/components.hpp b/include/components.hpp index 3169a6d..541f0f8 100644 --- a/include/components.hpp +++ b/include/components.hpp @@ -11,6 +11,7 @@ #include <string> #include <vector> +#include <error.hpp> #include <events.hpp> #include <inventory.hpp> #include <random.hpp> @@ -22,19 +23,52 @@ using namespace tinyxml2; /** + * @class Component + * @brief A base class for all components, insures all components have similar + * base functionalities. + */ +class Component : public entityx::Component<Component> { +public: + /** + * Constructs the component from the two given XML tags. + * + * Components can get information from two places: where the entity is defined + * (it's implementation, e.g. in town.xml) or from the tag's definition (e.g. entities.xml). + * The definition tag should be used for default values. + * + * @param imp tag for the implementation of the entity + * @param def tag for the definition of the component + */ + virtual void fromXML(XMLElement* imp, XMLElement* def) = 0; +}; + +/** * @struct Position * @brief Stores the position of an entity on the xy plane. */ -struct Position { +struct Position : public Component { /** * Constructor that sets the position of the object, if nothing is passed it will default to 0. * @param x The x position the object will be placed at. * @param y the y position the object will be placed at. */ Position(float x = 0.0f, float y = 0.0f): x(x), y(y) {} + Position(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } float x; /**< The x position in the world */ float y; /**< The y position in the world */ + + void fromXML(XMLElement* imp, XMLElement* def) final { + vec2 c; + if (imp->Attribute("position") != nullptr) + c = imp->StrAttribute("position"); + else + c = def->StrAttribute("value"); + + x = c.x, y = c.y; + } }; /** @@ -42,17 +76,33 @@ struct Position { * @brief Store an entities velocity. * This allows the entity to move throughout the world. */ -struct Direction { +struct Direction : public Component { /** * Constructor that sets the velocity, if no position is passed, it defaults to (0,0). * @param x The velocity of the object on the x axis. * @param y The velocity of the object on the y axis. */ Direction(float x = 0.0f, float y = 0.0f): x(x), y(y), grounded(false) {} + Direction(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } float x; /**< Velocity the object is moving in the x direction, this is added to the position */ float y; /**< Velocity the object is moving in the y direction, this is added to the position */ bool grounded; + + void fromXML(XMLElement* imp, XMLElement* def) final { + vec2 c; + if (imp->Attribute("direction") != nullptr) { + c = imp->StrAttribute("direction"); + } else if (def->Attribute("value") != nullptr) { + c = def->StrAttribute("value"); + } else { + c = vec2(0, 0); + } + + x = c.x, y = c.y, grounded = false; + } }; /** @@ -60,14 +110,24 @@ struct Direction { * @brief Allows and entity to react to gravity and frictions. * When an entity inherits this component it will react with gravity and move with friction. */ -struct Physics { +struct Physics : public Component { /** * Constructor that sets the gravity constant, if not specified it becomes 0. * @param g The non default gravity constant. */ Physics(float g = 1.0f): g(g) {} + Physics(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } float g; /**< The gravity constant, how fast the object falls */ + + void fromXML(XMLElement* imp, XMLElement* def) final { + if (imp->QueryFloatAttribute("gravity", &g) != XML_NO_ERROR) { + if (def->QueryFloatAttribute("value", &g) != XML_NO_ERROR) + g = 1.0f; + } + } }; /** @@ -75,36 +135,77 @@ struct Physics { * @brief Places an entity without physics on the ground. * This is used so we don't have to update the physics of a non-moving object every loop. */ -struct Grounded { - //TODO possibly make a way to change this - bool grounded = false; +struct Grounded : public Component { + Grounded(bool g = false) + : grounded(g) {} + Grounded(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } + + bool grounded; + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + (void)def; + grounded = false; + } }; /** * @struct Health * @brief Gives and entity health and stuff. */ -struct Health { +struct Health : public Component { /** - * Constructor that sets the variables, with 0 health as default. + * Constructor that sets the variables, with 1 health as default. */ Health(int m = 1, int h = 0) : health(h != 0 ? h : m), maxHealth(m) {} + Health(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } int health; /**< The current amount of health */ int maxHealth; /**< The maximum amount of health */ + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + (void)def; + // TODO + health = maxHealth = 1; + } }; -struct Portal { - Portal(std::string tf = "") : toFile(tf) {} +struct Portal : public Component { + Portal(std::string tf = "") + : toFile(tf) {} + Portal(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } std::string toFile; + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)def; + toFile = imp->StrAttribute("inside"); + } }; -struct Name { - Name(std::string n = "") : name(n) {} +struct Name : public Component { + Name(std::string n = "") + : name(n) {} + Name(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } std::string name; + + void fromXML(XMLElement* imp, XMLElement* def) final { + auto n = imp->Attribute("name"); + + // TODO check def's nullness + name = n != nullptr ? n : def->Attribute("value"); + } }; struct ItemDrop { @@ -119,22 +220,36 @@ struct ItemDrop { * @brief Allows an entity to collide with other objects. * When an entity has this component it can collide with the world and other objects. */ -struct Solid { +struct Solid : public Component { /** * Constructor that sets the entities dimensions based on what is passed. * @param w The desired width of the entity. * @param h The desired height of the entity. */ - Solid(float w = 0.0f, float h = 0.0f): width(w), height(h) {offset = 0.0f; passable = true; } - //Solid(float w = 0.0f, float h = 0.0f, vec2 offset = 0.0f): width(w), height(h), offset(offset) {passable = true; } + Solid(float w = 0.0f, float h = 0.0f) + : width(w), height(h), offset(0), passable(true) {} + Solid(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } - void Passable(bool v) {passable = v;} - bool Passable(void) {return passable;} + void Passable(bool v) { passable = v; } + bool Passable(void) { return passable; } float width; /**< The width of the entity in units */ float height; /**< The height of the entity in units */ vec2 offset; /**< This allows us to make the hitbox in any spot */ bool passable; /**< This determines whether or not one can pass by the entity */ + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + vec2 c; + if (def->Attribute("value") != nullptr) + c = def->StrAttribute("value"); + else + c = vec2(0, 0); + + width = c.x, height = c.y, offset = 0, passable = true; + } }; struct SpriteData { @@ -190,9 +305,12 @@ std::vector<Frame> developFrame(XMLElement*); * @brief If an entity is visible we want to be able to see it. * Each entity is given a sprite, a sprite can consist of manu frames or pieces to make one. */ -struct Sprite { +struct Sprite : public Component { Sprite(bool left = false) : faceLeft(left) {} + Sprite(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } Frame getSprite() { return sprite; @@ -259,6 +377,13 @@ struct Sprite { Frame sprite; bool faceLeft; + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + auto frames = developFrame(def); + if (!frames.empty()) + sprite = frames.at(0); + } }; /** @@ -330,7 +455,7 @@ struct Limb { }; //TODO kill clyne -struct Animate { +struct Animate : public Component { // COMMENT std::vector<Limb> limb; // COMMENT @@ -340,6 +465,10 @@ struct Animate { index = 0; } + Animate(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } + // COMMENT void firstFrame(unsigned int updateType, Frame &sprite) { @@ -359,30 +488,96 @@ struct Animate { } } } + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + + auto animx = def->FirstChildElement(); + uint limbid = 0; + float limbupdate = 0; + uint limbupdatetype = 0; + + while (animx != nullptr) { + if (std::string(animx->Name()) == "movement") { + limbupdatetype = 1; + + auto limbx = animx->FirstChildElement(); + while (limbx != nullptr) { + if (std::string(limbx->Name()) == "limb") { + auto frames = developFrame(limbx); + limb.push_back(Limb()); + auto& newLimb = limb.back(); + newLimb.updateType = limbupdatetype; + + if (limbx->QueryUnsignedAttribute("id", &limbid) == XML_NO_ERROR) + newLimb.limbID = limbid; + if (limbx->QueryFloatAttribute("update", &limbupdate) == XML_NO_ERROR) + newLimb.updateRate = limbupdate; + + // place our newly developed frames in the entities animation stack + for (auto &f : frames) { + newLimb.addFrame(f); + for (auto &fr : newLimb.frame) { + for (auto &sd : fr) + sd.first.limb = limbid; + } + } + } + + limbx = limbx->NextSiblingElement(); + } + } + + animx = animx->NextSiblingElement(); + } + } }; /** * @struct Visible * @brief If an entity is visible we want to be able to draw it. */ -struct Visible { +struct Visible : public Component { /** * @brief Decide what layer to draw the entity on. * When stuff is drawn, it is drawn on a "layer". This layer gives more of a 3D effect to the world. * @param z The desired "layer" of the entity. */ Visible(float z = 0.0f): z(z) {} + Visible(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } float z; /**< The value along the z axis the entity will be drawn on */ + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + if (def->QueryFloatAttribute("value", &z) != XML_NO_ERROR) + z = 0; + } }; -struct Dialog { +struct Dialog : public Component { Dialog(int idx = 0) : index(idx), rindex((idx == 9999) ? randGet() : idx), talking(false) {} + Dialog(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } int index; int rindex; bool talking; + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)def; + bool hasDialog; + if (imp->QueryBoolAttribute("hasDialog", &hasDialog) != XML_NO_ERROR) + hasDialog = false; + + index = hasDialog ? 0 : 9999; + rindex = (index == 9999) ? randGet() : index; + talking = false; + } }; // movement styles @@ -412,11 +607,20 @@ struct Wander { /** * Causes the entity to get mad at the player, charge and fight. */ -struct Aggro { +struct Aggro : public Component { Aggro(const std::string& a) : arena(a) {} + Aggro(XMLElement* imp, XMLElement* def) { + fromXML(imp, def); + } std::string arena; + + void fromXML(XMLElement* imp, XMLElement* def) final { + (void)imp; + // TODO null check..?, imp given + arena = def->StrAttribute("arena"); + } }; /** diff --git a/include/player.hpp b/include/player.hpp index 56886e3..2d232ab 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -38,6 +38,7 @@ public: * Creates the player, adding it to the entity system. */ void create(void); + inline auto getId(void) const { return player.id(); } /** * Configures events for use with the entity system. diff --git a/include/world.hpp b/include/world.hpp index b4986e1..e47f78f 100644 --- a/include/world.hpp +++ b/include/world.hpp @@ -177,6 +177,8 @@ public: bool save(void); void load(const std::string& file); + + void fight(entityx::Entity entity); }; #endif // WORLD_HPP_ diff --git a/src/components.cpp b/src/components.cpp index a66a1db..19ba0fb 100644 --- a/src/components.cpp +++ b/src/components.cpp @@ -22,10 +22,11 @@ static std::vector<std::string> randomDialog (readFileA("assets/dialog_en-us")); void MovementSystem::update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) { - std::string arena; + bool fight = false; + entityx::Entity toFight; (void)ev; - en.each<Position, Direction>([&arena, dt](entityx::Entity entity, Position &position, Direction &direction) { + en.each<Position, Direction>([&](entityx::Entity entity, Position &position, Direction &direction) { position.x += direction.x * dt; position.y += direction.y * dt; @@ -52,11 +53,12 @@ void MovementSystem::update(entityx::EntityManager &en, entityx::EventManager &e if (entity.has_component<Aggro>()) { auto ppos = game::engine.getSystem<PlayerSystem>()->getPosition(); if (ppos.x > position.x && ppos.x < position.x + entity.component<Solid>()->width) { - auto& h = entity.component<Health>()->health; - if (h > 0) { - arena = entity.component<Aggro>()->arena; - h = 0; - } + //auto& h = entity.component<Health>()->health; + //if (h > 0) { + fight = true; + toFight = entity; + // h = 0; + //} } else direction.x = (ppos.x > position.x) ? .05 : -.05; } else if (entity.has_component<Wander>()) { @@ -72,10 +74,10 @@ void MovementSystem::update(entityx::EntityManager &en, entityx::EventManager &e } }); - if (!arena.empty()) { + if (fight) { ui::toggleWhiteFast(); ui::waitForCover(); - game::engine.getSystem<WorldSystem>()->load(arena); + game::engine.getSystem<WorldSystem>()->fight(toFight); ui::toggleWhiteFast(); } } diff --git a/src/player.cpp b/src/player.cpp index cd939bb..bcde388 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -88,48 +88,7 @@ void PlayerSystem::create(void) // handle player animation xmld.Parse(animationXML); - auto entan = player.assign<Animate>(); - auto animx = xmld.FirstChildElement()->FirstChildElement(); - - unsigned int limbid = 0; - float limbupdate = 0; - unsigned int limbupdatetype = 0; - - while (animx) { - std::string animType = animx->Name(); - std::cout << animx->Name() << std::endl; - if (animType == "movement") { - limbupdatetype = 1; - auto limbx = animx->FirstChildElement(); - while (limbx) { - std::string limbHopefully = limbx->Name(); - if (limbHopefully == "limb") { - auto frames = developFrame(limbx); - - entan->limb.push_back(Limb()); - entan->limb.back().updateType = limbupdatetype; - - if (limbx->QueryUnsignedAttribute("id", &limbid) == XML_NO_ERROR) { - entan->limb.back().limbID = limbid; - } - if (limbx->QueryFloatAttribute("update", &limbupdate) == XML_NO_ERROR) { - entan->limb.back().updateRate = limbupdate; - } - - // place our newly developed frames in the entities animation stack - for (auto &f : frames) { - entan->limb.back().addFrame(f); - for (auto &fr : entan->limb.back().frame) { - for (auto &sd : fr) - sd.first.limb = limbid; - } - } - } - limbx = limbx->NextSiblingElement(); - } - } - animx = animx->NextSiblingElement(); - } + player.assign<Animate>(nullptr, xmld.FirstChildElement()); } void PlayerSystem::configure(entityx::EventManager &ev) diff --git a/src/world.cpp b/src/world.cpp index b0e28a3..5d6202d 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -153,6 +153,16 @@ bool WorldSystem::save(void) return true; } +static std::vector<entityx::Entity::Id> savedEntities; + +void WorldSystem::fight(entityx::Entity entity) +{ + savedEntities.clear(); + savedEntities.emplace_back(entity.id()); + load(entity.component<Aggro>()->arena); + entity.remove<Aggro>(); +} + void WorldSystem::load(const std::string& file) { auto& render = *game::engine.getSystem<RenderSystem>(); @@ -216,8 +226,21 @@ void WorldSystem::load(const std::string& file) world.toLeft = world.toRight = ""; currentXMLFile = file; - game::entities.reset(); - game::engine.getSystem<PlayerSystem>()->create(); + + //game::entities.reset(); + if (!savedEntities.empty()) { + savedEntities.emplace_back(game::engine.getSystem<PlayerSystem>()->getId()); + game::entities.each<Position>( + [&](entityx::Entity entity, Position& p) { + (void)p; + if (std::find(savedEntities.begin(), savedEntities.end(), entity.id()) + == savedEntities.end()) + entity.destroy(); + }, true); + } else { + game::entities.reset(); + game::engine.getSystem<PlayerSystem>()->create(); + } // iterate through tags while (wxml != nullptr) { @@ -294,121 +317,41 @@ void WorldSystem::load(const std::string& file) while (abcd) { std::string tname = abcd->Name(); - if (tname == "Position") { - vec2 coords; - - if (wxml->Attribute("position") != nullptr) { - coords = wxml->StrAttribute("position"); - } else { - coords = abcd->StrAttribute("value"); - } - - float cdat[2] = {coords.x, coords.y}; - entity.assign<Position>(cdat[0], cdat[1]); - } else if (tname == "Visible") { - entity.assign<Visible>(abcd->FloatAttribute("value")); - } else if (tname == "Sprite") { - auto sprite = entity.assign<Sprite>(); - auto sprx = abcd; - auto frames = developFrame(sprx); - if (frames.size() > 0) - sprite->sprite = frames.at(0); - } else if (tname == "Portal") { - entity.assign<Portal>(wxml->StrAttribute("inside")); - } else if (tname == "Solid") { - vec2 dim; - - if (abcd->Attribute("value") != nullptr) - dim = abcd->StrAttribute("value"); - else - dim = entity.component<Sprite>()->getSpriteSize(); - - float cdat[2] = {dim.x, dim.y}; - entity.assign<Solid>(cdat[0], cdat[1]); - } else if (tname == "Direction") { - vec2 dir; - - if (wxml->Attribute("direction") != nullptr) { - dir = wxml->StrAttribute("direction"); - } else if (wxml->Attribute("value") != nullptr) { - dir = wxml->StrAttribute("value"); - } else { - dir = vec2(0,0); + if (tname == "Position") + entity.assign<Position>(wxml, abcd); + else if (tname == "Visible") + entity.assign<Visible>(wxml, abcd); + else if (tname == "Sprite") + entity.assign<Sprite>(wxml, abcd); + else if (tname == "Portal") + entity.assign<Portal>(wxml, abcd); + else if (tname == "Solid") { + auto solid = entity.assign<Solid>(wxml, abcd); + if (solid->width == 0 && solid->height == 0) { + auto dim = entity.component<Sprite>()->getSpriteSize(); + entity.remove<Solid>(); + entity.assign<Solid>(dim.x, dim.y); } - - float cdat[2] = {dir.x, dir.y}; - entity.assign<Direction>(cdat[0], cdat[1]); - } else if (tname == "Physics") { - float g; - - if (wxml->Attribute("gravity") != nullptr) { - g = wxml->FloatAttribute("gravity"); - } else if (wxml->Attribute("value") != nullptr) { - g = wxml->FloatAttribute("value"); - } else { - g = 1.0f; - } - - entity.assign<Physics>(g); - } else if (tname == "Name") { - auto name = wxml->Attribute("name"); - entity.assign<Name>( name != nullptr ? name : abcd->Attribute("value")); - } else if (tname == "Dialog") { - entity.assign<Dialog>((wxml->BoolAttribute("hasDialog") ? 0 : 9999)); - } else if (tname == "Grounded") { - entity.assign<Grounded>(); - } else if (tname == "Wander") { + } else if (tname == "Direction") + entity.assign<Direction>(wxml, abcd); + else if (tname == "Physics") + entity.assign<Physics>(wxml, abcd); + else if (tname == "Name") + entity.assign<Name>(wxml, abcd); + else if (tname == "Dialog") + entity.assign<Dialog>(wxml, abcd); + else if (tname == "Grounded") + entity.assign<Grounded>(); // no need to pass xmls... + else if (tname == "Wander") entity.assign<Wander>(); - } else if (tname == "Hop" ) { + else if (tname == "Hop") entity.assign<Hop>(); - } else if (tname == "Health") { - entity.assign<Health>(); - } else if (tname == "Aggro" ) { - entity.assign<Aggro>(abcd->Attribute("arena")); - } else if (tname == "Animation") { - auto entan = entity.assign<Animate>(); - auto animx = abcd->FirstChildElement(); - - uint limbid = 0; - float limbupdate = 0; - uint limbupdatetype = 0; - - while (animx) { - std::string animType = animx->Name(); - if (animType == "movement") { - limbupdatetype = 1; - auto limbx = animx->FirstChildElement(); - while (limbx) { - std::string limbHopefully = limbx->Name(); - if (limbHopefully == "limb") { - auto frames = developFrame(limbx); - - entan->limb.push_back(Limb()); - entan->limb.back().updateType = limbupdatetype; - - if (limbx->QueryUnsignedAttribute("id", &limbid) == XML_NO_ERROR) { - entan->limb.back().limbID = limbid; - } - if (limbx->QueryFloatAttribute("update", &limbupdate) == XML_NO_ERROR) { - entan->limb.back().updateRate = limbupdate; - } - - // place our newly developed frames in the entities animation stack - for (auto &f : frames) { - entan->limb.back().addFrame(f); - for (auto &fr : entan->limb.back().frame) { - for (auto &sd : fr) - sd.first.limb = limbid; - } - } - } - limbx = limbx->NextSiblingElement(); - } - } - - animx = animx->NextSiblingElement(); - } - } + else if (tname == "Health") + entity.assign<Health>(wxml, abcd); + else if (tname == "Aggro") + entity.assign<Aggro>(wxml, abcd); + else if (tname == "Animation") + entity.assign<Animate>(wxml, abcd); abcd = abcd->NextSiblingElement(); } @@ -906,7 +849,7 @@ void WorldSystem::render(void) iStart = std::clamp(static_cast<int>(pOffset - (SCREEN_WIDTH / 2 / game::HLINE) - GROUND_HILLINESS), 0, static_cast<int>(world.data.size())); iEnd = std::clamp(static_cast<int>(pOffset + (SCREEN_WIDTH / 2 / game::HLINE) + 2), - 0, static_cast<int>(world.data.size())); + 0, static_cast<int>(world.data.size() - GROUND_HILLINESS)); // draw the dirt waitToSwap = true; diff --git a/xml/arena.xml b/xml/arena.xml new file mode 100644 index 0000000..24da9b0 --- /dev/null +++ b/xml/arena.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<World> + <style background="0" bgm="assets/music/embark.wav" folder="assets/style/classic/"/> + <generation width="200"/> + <time>6000</time> +</World> diff --git a/xml/entities.xml b/xml/entities.xml index 0d3c80b..5e33b92 100644 --- a/xml/entities.xml +++ b/xml/entities.xml @@ -67,7 +67,7 @@ <Physics /> <Name value="SKIRL" /> <Wander /> - <Aggro arena="bobshouse.xml" /> + <Aggro arena="arena.xml" /> </skirl> <structure> |