#include "realizing.h" #include #include #include #include #include "game.h" #include "consts.h" #include "animation.h" #include "components/realizable.h" #include "components/mappable.h" #include "components/animatable.h" #include "components/playable.h" #include "components/ponderable.h" #include "components/transformable.h" #include "components/automatable.h" #include "systems/mapping.h" #include "systems/animating.h" #include "systems/pondering.h" #include "systems/automating.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; } // TODO: neither the XML doc nor any of the emplaced entities are properly // destroyed if this method throws an exception. EntityManager::id_type RealizingSystem::initSingleton( std::string worldFile, std::string prototypeFile) { id_type world = game_.getEntityManager().emplaceEntity(); auto& realizable = game_.getEntityManager(). emplaceComponent(world); game_.getSystemManager().getSystem(). initScriptEngine(realizable.scriptEngine); realizable.worldFile = worldFile; realizable.prototypeFile = prototypeFile; auto& mapping = game_.getSystemManager().getSystem(); xmlChar* key = nullptr; // Create a mapping between prototype names and the XML trees defining them. xmlDocPtr protoXml = xmlParseFile(prototypeFile.c_str()); if (protoXml == nullptr) { throw std::invalid_argument("Cannot find prototypes file"); } xmlNodePtr protoTop = xmlDocGetRootElement(protoXml); if (protoTop == nullptr) { throw std::invalid_argument("Error parsing prototypes file"); } if (xmlStrcmp(protoTop->name, reinterpret_cast("entities"))) { throw std::invalid_argument("Error parsing prototypes file"); } std::map prototypes; for (xmlNodePtr node = protoTop->xmlChildrenNode; node != nullptr; node = node->next) { if (!xmlStrcmp(node->name, reinterpret_cast("entity"))) { key = getProp(node, "id"); std::string prototypeId = reinterpret_cast(key); xmlFree(key); prototypes[prototypeId] = node; } } // Create entities from the world definition. xmlDocPtr doc = xmlParseFile(worldFile.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"); } key = getProp(top, "startx"); realizable.startingPos.x() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "starty"); realizable.startingPos.y() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "startmap"); realizable.startingMapId = atoi(reinterpret_cast(key)); xmlFree(key); for (xmlNodePtr node = top->xmlChildrenNode; node != nullptr; node = node->next) { if (!xmlStrcmp(node->name, reinterpret_cast("map"))) { id_type map = game_.getEntityManager().emplaceEntity(); auto& mappable = game_.getEntityManager(). emplaceComponent(map, Texture("res/tiles.png"), Texture("res/font.bmp")); key = getProp(node, "id"); mappable.mapId = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(node, "title"); mappable.title = reinterpret_cast(key); xmlFree(key); for (xmlNodePtr mapNode = node->xmlChildrenNode; mapNode != nullptr; mapNode = mapNode->next) { if (!xmlStrcmp( mapNode->name, reinterpret_cast("environment"))) { key = xmlNodeGetContent(mapNode); if (key == nullptr) { throw std::invalid_argument("Error parsing world file"); } mappable.tiles.clear(); mappable.tiles.push_back(atoi(strtok( reinterpret_cast(key), ",\n"))); for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) { mappable.tiles.push_back(atoi(strtok(nullptr, ",\n"))); } xmlFree(key); } else if (!xmlStrcmp( mapNode->name, reinterpret_cast("entity"))) { id_type mapObject = game_.getEntityManager().emplaceEntity(); key = getProp(mapNode, "type"); std::string prototypeId = reinterpret_cast(key); xmlFree(key); xmlNodePtr prototypeNode = prototypes[prototypeId]; // Set the coordinates from the object definition. auto& transformable = game_.getEntityManager(). emplaceComponent(mapObject); key = getProp(mapNode, "x"); transformable.origPos.x() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(mapNode, "y"); transformable.origPos.y() = atoi(reinterpret_cast(key)); xmlFree(key); // Set the sprite and size using the prototype definition. key = getProp(prototypeNode, "sprite"); std::string spritePath = reinterpret_cast(key); xmlFree(key); key = getProp(prototypeNode, "width"); transformable.origSize.w() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(prototypeNode, "height"); transformable.origSize.h() = atoi(reinterpret_cast(key)); xmlFree(key); AnimationSet objectAnim( spritePath.c_str(), transformable.origSize.w(), transformable.origSize.h(), 1); objectAnim.emplaceAnimation("static", 0, 1, 1); auto& animatable = game_.getEntityManager(). emplaceComponent( mapObject, std::move(objectAnim)); animatable.origAnimation = "static"; // Create a physics body. game_.getSystemManager().getSystem(). initializeBody(mapObject, PonderableComponent::Type::vacuumed); if (prototypeId == "movplat") { auto& automatable = game_.getEntityManager(). emplaceComponent(mapObject); realizable.scriptEngine.script_file( "res/platform.lua");//, } mappable.objects.push_back(mapObject); } else if (!xmlStrcmp( mapNode->name, reinterpret_cast("adjacent"))) { key = getProp(mapNode, "type"); std::string adjTypeStr(reinterpret_cast(key)); xmlFree(key); MappableComponent::Adjacent::Type adjType; if (adjTypeStr == "wall") { adjType = MappableComponent::Adjacent::Type::wall; } else if (adjTypeStr == "wrap") { adjType = MappableComponent::Adjacent::Type::wrap; } else if (adjTypeStr == "warp") { adjType = MappableComponent::Adjacent::Type::warp; } else if (adjTypeStr == "reverseWarp") { adjType = MappableComponent::Adjacent::Type::reverse; } else { throw std::logic_error("Invalid adjacency type"); } key = getProp(mapNode, "map"); size_t adjMapId = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(mapNode, "dir"); std::string adjDir(reinterpret_cast(key)); xmlFree(key); if (adjDir == "left") { mappable.leftAdjacent = {adjType, adjMapId}; } else if (adjDir == "right") { mappable.rightAdjacent = {adjType, adjMapId}; } else if (adjDir == "up") { mappable.upAdjacent = {adjType, adjMapId}; } else if (adjDir == "down") { mappable.downAdjacent = {adjType, adjMapId}; } else { throw std::logic_error("Invalid adjacency direction"); } } } mapping.generateBoundaries(map); realizable.maps.insert(map); realizable.entityByMapId[mappable.mapId] = map; } } xmlFreeDoc(doc); xmlFreeDoc(protoXml); loadMap(realizable.entityByMapId[realizable.startingMapId]); return world; } EntityManager::id_type RealizingSystem::getSingleton() const { std::set result = game_.getEntityManager().getEntitiesWithComponents< RealizableComponent>(); if (result.empty()) { throw std::logic_error("No realizable entity found"); } else if (result.size() > 1) { throw std::logic_error("Multiple realizable entities found"); } return *std::begin(result); } void RealizingSystem::loadMap(id_type mapEntity) { id_type world = getSingleton(); auto& realizable = game_.getEntityManager(). getComponent(world); auto& animating = game_.getSystemManager().getSystem(); auto& pondering = game_.getSystemManager().getSystem(); auto& automating = game_.getSystemManager().getSystem(); std::set players = game_.getEntityManager().getEntitiesWithComponents< PlayableComponent>(); if (realizable.hasActiveMap) { id_type oldMap = realizable.activeMap; auto& oldMappable = game_.getEntityManager(). getComponent(oldMap); // Deactivate any map objects from the old map. for (id_type prototype : oldMappable.objects) { leaveActiveMap(prototype); } // Deactivate players that were on the old map. for (id_type player : players) { auto& playable = game_.getEntityManager(). getComponent(player); if (playable.mapId == oldMap) { leaveActiveMap(player); } } } realizable.hasActiveMap = true; realizable.activeMap = mapEntity; auto& mappable = game_.getEntityManager(). getComponent(mapEntity); // Initialize the new map's objects. for (id_type prototype : mappable.objects) { if (game_.getEntityManager(). hasComponent(prototype)) { auto& transformable = game_.getEntityManager(). getComponent(prototype); transformable.pos = transformable.origPos; transformable.size = transformable.origSize; } if (game_.getEntityManager().hasComponent(prototype)) { animating.initPrototype(prototype); } if (game_.getEntityManager().hasComponent(prototype)) { pondering.initPrototype(prototype); } if (game_.getEntityManager().hasComponent(prototype)) { automating.initPrototype(prototype); } enterActiveMap(prototype); } // Activate any players on the map. for (id_type player : players) { auto& playable = game_.getEntityManager(). getComponent(player); if (playable.mapId == mapEntity) { enterActiveMap(player); } } } void RealizingSystem::enterActiveMap(id_type entity) { if (game_.getEntityManager().hasComponent(entity)) { auto& animatable = game_.getEntityManager(). getComponent(entity); animatable.active = true; } if (game_.getEntityManager().hasComponent(entity)) { auto& ponderable = game_.getEntityManager(). getComponent(entity); ponderable.active = true; } if (game_.getEntityManager().hasComponent(entity)) { auto& automatable = game_.getEntityManager(). getComponent(entity); automatable.active = true; } } void RealizingSystem::leaveActiveMap(id_type entity) { if (game_.getEntityManager().hasComponent(entity)) { auto& animatable = game_.getEntityManager(). getComponent(entity); animatable.active = false; } if (game_.getEntityManager().hasComponent(entity)) { auto& ponderable = game_.getEntityManager(). getComponent(entity); ponderable.active = false; } if (game_.getEntityManager().hasComponent(entity)) { auto& automatable = game_.getEntityManager(). getComponent(entity); automatable.active = false; } }