/* 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