/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ultima/ultima4/ultima4.h" #include "ultima/ultima4/map/annotation.h" #include "ultima/ultima4/map/city.h" #include "ultima/ultima4/controllers/combat_controller.h" #include "ultima/ultima4/map/dungeon.h" #include "ultima/ultima4/map/map.h" #include "ultima/ultima4/map/maploader.h" #include "ultima/ultima4/map/mapmgr.h" #include "ultima/ultima4/game/item.h" #include "ultima/ultima4/game/moongate.h" #include "ultima/ultima4/game/person.h" #include "ultima/ultima4/game/portal.h" #include "ultima/ultima4/map/shrine.h" #include "ultima/ultima4/map/tilemap.h" #include "ultima/ultima4/map/tileset.h" #include "ultima/ultima4/map/xml_map.h" #include "ultima/ultima4/core/types.h" #include "ultima/ultima4/core/config.h" namespace Ultima { namespace Ultima4 { MapMgr *MapMgr::_instance = nullptr; MapMgr *MapMgr::getInstance() { if (_instance == nullptr) _instance = new MapMgr(); return _instance; } void MapMgr::destroy() { if (_instance != nullptr) { delete _instance; _instance = nullptr; } } MapMgr::MapMgr() { const Config *config = Config::getInstance(); Map *map; Std::vector maps = config->getElement("maps").getChildren(); for (const auto &i : maps) { map = initMapFromConf(i); // Map actually gets loaded later, when it's needed registerMap(map); } } MapMgr::~MapMgr() { for (auto *i : _mapList) delete i; } void MapMgr::unloadMap(MapId id) { delete _mapList[id]; const Config *config = Config::getInstance(); Std::vector maps = config->getElement("maps").getChildren(); for (const auto &i : maps) { if (id == static_cast(i.getInt("id"))) { Map *map = initMapFromConf(i); _mapList[id] = map; break; } } } Map *MapMgr::initMap(Map::Type type) { Map *map; switch (type) { case Map::WORLD: map = new Map(); break; case Map::COMBAT: map = new CombatMap(); break; case Map::SHRINE: map = new Shrine(); break; case Map::DUNGEON: map = new Dungeon(); break; case Map::CITY: map = new City(); break; case Map::XML: map = new XMLMap(); break; default: error("Error: invalid map type used"); break; } return map; } Map *MapMgr::get(MapId id) { // if the map hasn't been loaded yet, load it! if (!_mapList[id]->_data.size()) { MapLoader *loader = g_mapLoaders->getLoader(_mapList[id]->_type); if (loader == nullptr) error("can't load map of type \"%d\"", _mapList[id]->_type); loader->load(_mapList[id]); } return _mapList[id]; } void MapMgr::registerMap(Map *map) { if (_mapList.size() <= map->_id) _mapList.resize(map->_id + 1, nullptr); if (_mapList[map->_id] != nullptr) error("Error: A map with id '%d' already exists", map->_id); _mapList[map->_id] = map; } Map *MapMgr::initMapFromConf(const ConfigElement &mapConf) { Map *map; static const char *const mapTypeEnumStrings[] = { "world", "city", "shrine", "combat", "dungeon", "xml", nullptr }; static const char *const borderBehaviorEnumStrings[] = { "wrap", "exit", "fixed", nullptr }; map = initMap(static_cast(mapConf.getEnum("type", mapTypeEnumStrings))); if (!map) return nullptr; map->_id = static_cast(mapConf.getInt("id")); map->_type = static_cast(mapConf.getEnum("type", mapTypeEnumStrings)); map->_fname = mapConf.getString("fname"); map->_width = mapConf.getInt("width"); map->_height = mapConf.getInt("height"); map->_levels = mapConf.getInt("levels"); map->_chunkWidth = mapConf.getInt("chunkwidth"); map->_chunkHeight = mapConf.getInt("chunkheight"); map->_offset = mapConf.getInt("offset"); map->_borderBehavior = static_cast(mapConf.getEnum("borderbehavior", borderBehaviorEnumStrings)); if (isCombatMap(map)) { CombatMap *cm = dynamic_cast(map); assert(cm); cm->setContextual(mapConf.getBool("contextual")); } if (mapConf.getBool("showavatar")) map->_flags |= SHOW_AVATAR; if (mapConf.getBool("nolineofsight")) map->_flags |= NO_LINE_OF_SIGHT; if (mapConf.getBool("firstperson")) map->_flags |= FIRST_PERSON; map->_music = static_cast(mapConf.getInt("music")); map->_tileSet = g_tileSets->get(mapConf.getString("tileset")); map->_tileMap = g_tileMaps->get(mapConf.getString("tilemap")); Std::vector children = mapConf.getChildren(); for (const auto &i : children) { if (i.getName() == "city") { City *city = dynamic_cast(map); assert(city); initCityFromConf(i, city); } else if (i.getName() == "shrine") { Shrine *shrine = dynamic_cast(map); assert(shrine); initShrineFromConf(i, shrine); } else if (i.getName() == "dungeon") { Dungeon *dungeon = dynamic_cast(map); assert(dungeon); initDungeonFromConf(i, dungeon); } else if (i.getName() == "portal") map->_portals.push_back(initPortalFromConf(i)); else if (i.getName() == "moongate") createMoongateFromConf(i); else if (i.getName() == "compressedchunk") map->_compressedChunks.push_back(initCompressedChunkFromConf(i)); else if (i.getName() == "label") map->_labels[i.getString("name")] = MapCoords(i.getInt("x"), i.getInt("y"), i.getInt("z", 0)); else if (i.getName() == "tiles" && map->_type == Map::XML) static_cast(map)->_tilesText = i.getNode()->firstChild()->text(); } return map; } void MapMgr::initCityFromConf(const ConfigElement &cityConf, City *city) { city->_name = cityConf.getString("name"); city->_type = cityConf.getString("type"); city->_tlkFname = cityConf.getString("tlk_fname"); Std::vector children = cityConf.getChildren(); for (const auto &i : children) { if (i.getName() == "personrole") city->_personRoles.push_back(initPersonRoleFromConf(i)); } } PersonRole *MapMgr::initPersonRoleFromConf(const ConfigElement &personRoleConf) { PersonRole *personrole; static const char *const roleEnumStrings[] = { "companion", "weaponsvendor", "armorvendor", "foodvendor", "tavernkeeper", "reagentsvendor", "healer", "innkeeper", "guildvendor", "horsevendor", "lordbritish", "hawkwind", nullptr }; personrole = new PersonRole(); personrole->_role = personRoleConf.getEnum("role", roleEnumStrings) + NPC_TALKER_COMPANION; personrole->_id = personRoleConf.getInt("id"); return personrole; } Portal *MapMgr::initPortalFromConf(const ConfigElement &portalConf) { Portal *portal; portal = new Portal(); portal->_portalConditionsMet = nullptr; portal->_retroActiveDest = nullptr; portal->_coords = MapCoords( portalConf.getInt("x"), portalConf.getInt("y"), portalConf.getInt("z", 0)); portal->_destid = static_cast(portalConf.getInt("destmapid")); portal->_start.x = static_cast(portalConf.getInt("startx")); portal->_start.y = static_cast(portalConf.getInt("starty")); portal->_start.z = static_cast(portalConf.getInt("startlevel", 0)); Common::String prop = portalConf.getString("action"); if (prop == "none") portal->_triggerAction = ACTION_NONE; else if (prop == "enter") portal->_triggerAction = ACTION_ENTER; else if (prop == "klimb") portal->_triggerAction = ACTION_KLIMB; else if (prop == "descend") portal->_triggerAction = ACTION_DESCEND; else if (prop == "exit_north") portal->_triggerAction = ACTION_EXIT_NORTH; else if (prop == "exit_east") portal->_triggerAction = ACTION_EXIT_EAST; else if (prop == "exit_south") portal->_triggerAction = ACTION_EXIT_SOUTH; else if (prop == "exit_west") portal->_triggerAction = ACTION_EXIT_WEST; else error("unknown trigger_action: %s", prop.c_str()); prop = portalConf.getString("condition"); if (!prop.empty()) { if (prop == "shrine") portal->_portalConditionsMet = &shrineCanEnter; else if (prop == "abyss") portal->_portalConditionsMet = &Items::isAbyssOpened; else error("unknown portalConditionsMet: %s", prop.c_str()); } portal->_saveLocation = portalConf.getBool("savelocation"); portal->_message = portalConf.getString("message"); prop = portalConf.getString("transport"); if (prop == "foot") portal->_portalTransportRequisites = TRANSPORT_FOOT; else if (prop == "footorhorse") portal->_portalTransportRequisites = TRANSPORT_FOOT_OR_HORSE; else error("unknown transport: %s", prop.c_str()); portal->_exitPortal = portalConf.getBool("exits"); // Used as a shortcut for specifying the display tile // for new/fan maps being added to the overworld portal->_tile = portalConf.exists("tile") ? portalConf.getInt("tile") : -1; Std::vector children = portalConf.getChildren(); for (const auto &i : children) { if (i.getName() == "retroActiveDest") { portal->_retroActiveDest = new PortalDestination(); portal->_retroActiveDest->_coords = MapCoords( i.getInt("x"), i.getInt("y"), i.getInt("z", 0)); portal->_retroActiveDest->_mapid = static_cast(i.getInt("mapid")); } } return portal; } void MapMgr::initShrineFromConf(const ConfigElement &shrineConf, Shrine *shrine) { static const char *const virtues[] = {"Honesty", "Compassion", "Valor", "Justice", "Sacrifice", "Honor", "Spirituality", "Humility", nullptr}; shrine->setVirtue(static_cast(shrineConf.getEnum("virtue", virtues))); shrine->setMantra(shrineConf.getString("mantra")); } void MapMgr::initDungeonFromConf(const ConfigElement &dungeonConf, Dungeon *dungeon) { dungeon->_nRooms = dungeonConf.getInt("rooms"); dungeon->_rooms = nullptr; dungeon->_roomMaps = nullptr; dungeon->_name = dungeonConf.getString("name"); } void MapMgr::createMoongateFromConf(const ConfigElement &moongateConf) { int phase = moongateConf.getInt("phase"); Coords coords(moongateConf.getInt("x"), moongateConf.getInt("y")); g_moongates->add(phase, coords); } int MapMgr::initCompressedChunkFromConf(const ConfigElement &compressedChunkConf) { return compressedChunkConf.getInt("index"); } } // End of namespace Ultima4 } // End of namespace Ultima