#ifndef _H_XMLLOADER_H #define _H_XMLLOADER_H #include #include #include #include #include #include #include #include #include #include struct Tile; // Except for the player, these definitions are mostly overrides of the monster // data struct EntityData { bool isPlayer; bool animated; int x = 0, y = 0; float hp; std::string monsterDef; std::string graphic; std::string weapon = "gun/pistol"; std::string script; }; struct MapData { std::string name; struct TileSet { int startID = 1; std::string path; }; std::vector tileSets; int width = 0, height = 0; float tileSize = 32.f; // 3D array, 0: layer, 1: y, 2: x // Holding tile startID + ids, 0 is an empty tile std::vector>> tiles; }; struct TileSetData { std::string name; std::string type; std::string file; int width = 0, height = 0; int columns = 0; int tileCount = 0; float tileSize = 64.f; struct TileData { int id = 0; std::string type; bool walkable = true; struct ObjectData { int id = 0; std::string name; glm::vec2 pos; glm::vec2 size; bool collidable = false; bool pickup = false; }; std::vector> objects; }; std::vector> tiles; }; struct SceneData { std::string id; std::string type; std::string bgFile; const MapData *map; std::vector entities; }; // Store our script as a string so we only need to read the file at boot struct ScriptData { std::string id; std::string fileName; std::string script; }; struct WeaponData { std::string id; float fireSpeed = 250.0f; int clipSize = 21; int maxAmmo = 512; std::string script = ""; std::string graphic; bool animated = false; float sizeX = 50.f, sizeY = 50.f; float offsetX = 0.f, offsetY = 0.f; float bulletSizeX = 50.f, bulletSizeY = 50.f; std::string bulletGraphic; bool bulletAnimated = false; float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f; float modMin = 0.5f, modMax = 1.0f; }; // ID is the new tactic I've decided on. // Each animationable object will be given a prefix // denoting their type and object so / // This prefix will be used to look up the animation on // the animation map struct AnimationData { std::string id; std::string spriteAtlas; bool directional = false; bool oneShot; float FPS = 1.f; float frameSize; }; // This holds a lot of defaults, but the scene can // override a large portion of the definitions here struct MonsterData { std::string id; std::string anim; std::string weapon; bool aggressive = false; std::string behaviour; float hp; }; struct SoundData { std::string id; // // std::string path; bool spatial; }; class XMLLoader { public: XMLLoader() {} bool loadScenes(const char *sceneFolder); bool loadWeapons(const char *weaponFolder); bool loadAnimations(const char *animationFolder); bool loadTileSets(const char *tileSetFolder); bool loadMaps(const char *mapFolder); bool loadMonsters(const char *monsterFolder); // luaLoader is the function we use to load the lua file. // This will be defined in our resource manager bool loadScripts(const char *scriptFolder, std::function luaLoader); bool loadSoundEffects(const char *soundFolder); const SceneData *getSceneData(const std::string &id) const { try { return scenes.at(id).get(); } catch (std::exception &) { return nullptr; } } const ScriptData *getScriptData(const std::string &id) const { try { return scripts.at(id).get(); } catch (std::exception &) { return nullptr; } } const MapData *getMapData(const std::string &name) const { try { return maps.at(name).get(); } catch (std::exception &) { return nullptr; } } const WeaponData *getWeaponData(const std::string &id) const { try { return weapons.at(id).get(); } catch (std::exception &) { return nullptr; } } const AnimationData *getAnimationData(const std::string &id) const { try { return animations.at(id).get(); } catch (std::exception &) { return nullptr; } } const TileSetData *getTileSetData(const std::string &name) const { try { return tileSets.at(name).get(); } catch (std::exception &) { return nullptr; } } const SoundData *getSoundData(const std::string &id) const { try { return sounds.at(id).get(); } catch (std::exception &) { return nullptr; } } // return a full set of animations, may need further optimization. // one idea is when loading animations we create a seperate map that holds // each set by their reference, so we can just do a simple, hash table lookup. std::vector getAnimationSet(const std::string &prefix) const { std::vector animSet; animSet.reserve(animations.size()); for (const auto &[id, anim] : animations) { if (id.starts_with(prefix)) animSet.push_back(anim.get()); } animSet.shrink_to_fit(); return animSet; } void clearData() { scenes.clear(); weapons.clear(); animations.clear(); maps.clear(); tileSets.clear(); } protected: bool loadXmlScene(const char *xmlFile, SceneData *out); bool loadEntityData(const char *xmlFile, SceneData *out); bool loadTile(tinyxml2::XMLElement *tileElement, TileSetData::TileData *out); bool loadObject(tinyxml2::XMLElement *objectElement, TileSetData::TileData::ObjectData *out); private: std::unordered_map> scenes; std::unordered_map> scripts; std::unordered_map> weapons; std::unordered_map> animations; std::unordered_map> monsters; std::unordered_map> maps; std::unordered_map> tileSets; std::unordered_map> sounds; }; #endif // _H_XMLLOADER_H