/* * Copyright (C) 2011-2016 OpenDungeons Team * * 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 "utils/ConfigManager.h" #include "entities/CreatureDefinition.h" #include "entities/Tile.h" #include "entities/Weapon.h" #include "game/Skill.h" #include "gamemap/TileSet.h" #include "spawnconditions/SpawnCondition.h" #include "utils/Helper.h" #include "utils/LogManager.h" #include #include const std::vector EMPTY_SPAWNPOOL; const std::string EMPTY_STRING; const Ogre::ColourValue DEFAULT_SEAT_COLOURVALUE; const std::string ConfigManager::DEFAULT_TILESET_NAME = "Default"; const std::string ConfigManager::DEFAULT_KEEPER_VOICE = "Default"; const std::string ConfigManager::DefaultWorkerCreatureDefinition = "DefaultWorker"; template<> ConfigManager* Ogre::Singleton::msSingleton = nullptr; ConfigManager::ConfigManager(const std::string& configPath, const std::string& userConfigPath, const std::string& soundPath) : mNetworkPort(0), mClientConnectionTimeout(5000), mBaseSpawnPoint(10), mCreatureDeathCounter(10), mMaxCreaturesPerSeatAbsolute(30), mMaxCreaturesPerSeatDefault(10), mCreatureBaseMood(1000), mCreatureMoodHappy(1200), mCreatureMoodUpset(0), mCreatureMoodAngry(-1000), mCreatureMoodFurious(-2000), mSlapDamagePercent(15), mSlapEffectDuration(15), mTimePayDay(300), mNbTurnsFuriousMax(120), mMaxManaPerSeat(250000.0), mClaimingWallPenalty(0.8), mDigCoefGold(5.0), mDigCoefGem(1.0), mDigCoefClaimedWall(0.5), mNbTurnsKoCreatureAttacked(10), mCreatureDefinitionDefaultWorker(nullptr), mNbWorkersDigSameFaceTile(2), mNbWorkersClaimSameTile(1) { // TODO: it might be better to go through the creature definitions and try to pickup the first worker we can find mCreatureDefinitionDefaultWorker = new CreatureDefinition(DefaultWorkerCreatureDefinition, CreatureDefinition::CreatureJob::Worker, "Kobold.mesh"); if(!loadGlobalConfig(configPath)) { OD_LOG_ERR("Couldn't read loadCreatureDefinitions"); exit(1); } std::string fileName = configPath + mFilenameCreatureDefinition; if(!loadCreatureDefinitions(fileName)) { OD_LOG_ERR("Couldn't read loadCreatureDefinitions"); exit(1); } fileName = configPath + mFilenameEquipmentDefinition; if(!loadEquipements(fileName)) { OD_LOG_ERR("Couldn't read loadEquipements"); exit(1); } fileName = configPath + mFilenameSpawnConditions; if(!loadSpawnConditions(fileName)) { OD_LOG_ERR("Couldn't read loadSpawnConditions"); exit(1); } fileName = configPath + mFilenameFactions; if(!loadFactions(fileName)) { OD_LOG_ERR("Couldn't read loadFactions"); exit(1); } fileName = configPath + mFilenameRooms; if(!loadRooms(fileName)) { OD_LOG_ERR("Couldn't read loadRooms"); exit(1); } fileName = configPath + mFilenameTraps; if(!loadTraps(fileName)) { OD_LOG_ERR("Couldn't read loadTraps"); exit(1); } fileName = configPath + mFilenameSpells; if(!loadSpellConfig(fileName)) { OD_LOG_ERR("Couldn't read loadSpellConfig"); exit(1); } fileName = configPath + mFilenameSkills; if(!loadSkills(fileName)) { OD_LOG_ERR("Couldn't read loadSkills"); exit(1); } fileName = configPath + mFilenameTilesets; if(!loadTilesets(fileName)) { OD_LOG_ERR("Couldn't read loadTilesets"); exit(1); } // Reserve space in any case. mUserConfig.resize(Config::Ctg::TOTAL); if (!userConfigPath.empty()) loadUserConfig(userConfigPath); loadKeeperVoices(soundPath); } ConfigManager::~ConfigManager() { for(auto pair : mCreatureDefs) { delete pair.second; } if(mCreatureDefinitionDefaultWorker != nullptr) delete mCreatureDefinitionDefaultWorker; for(const Weapon* def : mWeapons) { delete def; } mWeapons.clear(); for(std::pair> p : mCreatureSpawnConditions) { for(const SpawnCondition* spawnCondition : p.second) { delete spawnCondition; } } mCreatureSpawnConditions.clear(); for(std::pair p : mTileSets) { delete p.second; } mTileSets.clear(); } bool ConfigManager::loadGlobalConfig(const std::string& configPath) { std::stringstream configFile; std::string fileName = configPath + "global.cfg"; if(!Helper::readFileWithoutComments(fileName, configFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; uint32_t paramsOk = 0; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[SeatColors]") { if(!loadGlobalConfigSeatColors(configFile)) break; paramsOk |= 1; continue; } if(nextParam == "[ConfigFiles]") { if(!loadGlobalConfigDefinitionFiles(configFile)) break; paramsOk |= 2; continue; } if(nextParam == "[GameConfig]") { if(!loadGlobalGameConfig(configFile)) break; paramsOk |= 4; continue; } } if(paramsOk != 0x07) { OD_LOG_ERR("Not enough parameters for config file paramsOk=" + Helper::toString(paramsOk)); return false; } return true; } bool ConfigManager::loadGlobalConfigDefinitionFiles(std::stringstream& configFile) { std::string nextParam; uint32_t filesOk = 0; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[/ConfigFiles]") break; if(nextParam != "[ConfigFile]") { OD_LOG_ERR("Wrong parameter read nextParam=" + nextParam); return false; } uint32_t paramsOk = 0; std::string type; std::string fileName; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[/ConfigFile]") { break; } if(nextParam == "Type") { configFile >> type; paramsOk |= 0x01; continue; } if(nextParam == "Filename") { configFile >> fileName; paramsOk |= 0x02; continue; } } if(paramsOk != 0x03) { OD_LOG_ERR("Missing parameters paramsOk=" + Helper::toString(paramsOk)); return false; } if(type == "Creatures") { mFilenameCreatureDefinition = fileName; filesOk |= 1; } else if(type == "Equipments") { mFilenameEquipmentDefinition = fileName; filesOk |= 2; } else if(type == "SpawnConditions") { mFilenameSpawnConditions = fileName; filesOk |= 4; } else if(type == "Factions") { mFilenameFactions = fileName; filesOk |= 8; } else if(type == "Rooms") { mFilenameRooms = fileName; filesOk |= 0x10; } else if(type == "Traps") { mFilenameTraps = fileName; filesOk |= 0x20; } else if(type == "Spells") { mFilenameSpells = fileName; filesOk |= 0x40; } else if(type == "Skills") { mFilenameSkills = fileName; filesOk |= 0x080; } else if(type == "Tilesets") { mFilenameTilesets = fileName; filesOk |= 0x100; } } if(filesOk != 0x1FF) { OD_LOG_ERR("Missing parameter file filesOk=" + Helper::toString(filesOk)); return false; } return true; } bool ConfigManager::loadGlobalConfigSeatColors(std::stringstream& configFile) { std::string nextParam; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[/SeatColors]") break; if(nextParam != "[SeatColor]") { OD_LOG_ERR("Wrong parameter read nextParam=" + nextParam); return false; } uint32_t paramsOk = 0; std::string id; Ogre::ColourValue colourValue; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[/SeatColor]") { break; } if(nextParam == "ID") { configFile >> id; paramsOk |= 0x01; continue; } if(nextParam == "ColorR") { configFile >> nextParam; double v = Helper::toDouble(nextParam); if(v < 0.0 || v > 1.0) { OD_LOG_ERR("Wrong parameter read nextParam=" + nextParam); return false; } colourValue.r = v; paramsOk |= 0x02; continue; } if(nextParam == "ColorG") { configFile >> nextParam; double v = Helper::toDouble(nextParam); if(v < 0.0 || v > 1.0) { OD_LOG_ERR("Wrong parameter read nextParam=" + nextParam); return false; } colourValue.g = v; paramsOk |= 0x04; continue; } if(nextParam == "ColorB") { configFile >> nextParam; double v = Helper::toDouble(nextParam); if(v < 0.0 || v > 1.0) { OD_LOG_ERR("Wrong parameter read nextParam=" + nextParam); return false; } colourValue.b = v; paramsOk |= 0x08; continue; } } if(paramsOk != 0x0F) { OD_LOG_ERR("Missing parameters paramsOk=" + Helper::toString(paramsOk)); return false; } mSeatColors[id] = colourValue; } return true; } bool ConfigManager::loadGlobalGameConfig(std::stringstream& configFile) { std::string nextParam; uint32_t paramsOk = 0; while(configFile.good()) { if(!(configFile >> nextParam)) break; if(nextParam == "[/GameConfig]") break; if(nextParam == "NetworkPort") { configFile >> nextParam; mNetworkPort = Helper::toUInt32(nextParam); paramsOk |= 1; } if(nextParam == "ClientConnectionTimeout") { configFile >> nextParam; mClientConnectionTimeout = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "CreatureDeathCounter") { configFile >> nextParam; mCreatureDeathCounter = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "MaxCreaturesPerSeatAbsolute") { configFile >> nextParam; mMaxCreaturesPerSeatAbsolute = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "MaxCreaturesPerSeatDefault") { configFile >> nextParam; mMaxCreaturesPerSeatDefault = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "SlapDamagePercent") { configFile >> nextParam; mSlapDamagePercent = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "SlapEffectDuration") { configFile >> nextParam; mSlapEffectDuration = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "TimePayDay") { configFile >> nextParam; mTimePayDay = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "NbTurnsFuriousMax") { configFile >> nextParam; mNbTurnsFuriousMax = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "MaxManaPerSeat") { configFile >> nextParam; mMaxManaPerSeat = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "ClaimingWallPenalty") { configFile >> nextParam; mClaimingWallPenalty = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "DigCoefGold") { configFile >> nextParam; mDigCoefGold = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "DigCoefGem") { configFile >> nextParam; mDigCoefGem = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "DigCoefClaimedWall") { configFile >> nextParam; mDigCoefClaimedWall = Helper::toDouble(nextParam); // Not mandatory } if(nextParam == "CreatureBaseMood") { configFile >> nextParam; mCreatureBaseMood = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "CreatureMoodHappy") { configFile >> nextParam; mCreatureMoodHappy = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "CreatureMoodUpset") { configFile >> nextParam; mCreatureMoodUpset = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "CreatureMoodAngry") { configFile >> nextParam; mCreatureMoodAngry = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "CreatureMoodFurious") { configFile >> nextParam; mCreatureMoodFurious = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "NbWorkersDigSameFaceTile") { configFile >> nextParam; mNbWorkersDigSameFaceTile = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "NbWorkersClaimSameTile") { configFile >> nextParam; mNbWorkersClaimSameTile = Helper::toUInt32(nextParam); // Not mandatory } if(nextParam == "NbTurnsKoCreatureAttacked") { configFile >> nextParam; mNbTurnsKoCreatureAttacked = Helper::toInt(nextParam); // Not mandatory } if(nextParam == "MainMenuMusic") { std::string line; std::getline(configFile, line); std::vector elements = Helper::split(line, '\t', true); if (elements.empty()) { OD_LOG_WRN("Invalid MainMenuMusic : " + line); continue; } mMainMenuMusic = elements[0]; // Not mandatory } if(nextParam == "MasterServerUrl") { configFile >> mMasterServerUrl; // Not mandatory } } if(paramsOk != 0x01) { OD_LOG_ERR("Missing parameters paramsOk=" + Helper::toString(paramsOk)); return false; } return true; } bool ConfigManager::loadCreatureDefinitions(const std::string& fileName) { OD_LOG_INF("Load creature definition file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[CreatureDefinitions]") { OD_LOG_ERR("Invalid Creature classes start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/CreatureDefinitions]") break; if (nextParam == "[/Creature]") continue; // Seek the [Creature] tag if (nextParam != "[Creature]") { OD_LOG_ERR("Invalid Creature classes start format. Line was " + nextParam); return false; } // Load the creature definition until a [/Creature] tag is found CreatureDefinition* creatureDef = CreatureDefinition::load(defFile, mCreatureDefs); if (creatureDef == nullptr) { OD_LOG_ERR("Invalid Creature classes start format"); return false; } mCreatureDefs.emplace(creatureDef->getClassName(), creatureDef); } return true; } bool ConfigManager::loadEquipements(const std::string& fileName) { OD_LOG_INF("Load weapon definition file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[EquipmentDefinitions]") { OD_LOG_ERR("Invalid weapon start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/EquipmentDefinitions]") break; if (nextParam == "[/Equipment]") continue; if (nextParam != "[Equipment]") { OD_LOG_ERR("Invalid Weapon definition format. Line was " + nextParam); return false; } // Load the definition Weapon* weapon = Weapon::load(defFile); if (weapon == nullptr) { OD_LOG_ERR("Invalid Weapon definition format"); return false; } mWeapons.push_back(weapon); } return true; } bool ConfigManager::loadSpawnConditions(const std::string& fileName) { OD_LOG_INF("Load creature spawn conditions file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[SpawnConditions]") { OD_LOG_ERR("Invalid creature spawn condition start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/SpawnConditions]") break; if (nextParam == "[/SpawnCondition]") continue; if (nextParam == "BaseSpawnPoint") { defFile >> nextParam; mBaseSpawnPoint = Helper::toUInt32(nextParam); continue; } if (nextParam != "[SpawnCondition]") { OD_LOG_ERR("Invalid creature spawn condition format. Line was " + nextParam); return false; } if(!(defFile >> nextParam)) break; if (nextParam != "CreatureClass") { OD_LOG_ERR("Invalid creature spawn condition format. Line was " + nextParam); return false; } defFile >> nextParam; const CreatureDefinition* creatureDefinition = getCreatureDefinition(nextParam); if(creatureDefinition == nullptr) { OD_LOG_ERR("nextParam=" + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/SpawnCondition]") break; if (nextParam != "[Condition]") { OD_LOG_ERR("Invalid creature spawn condition format. nextParam=" + nextParam); return false; } // Load the definition SpawnCondition* def = SpawnCondition::load(defFile); if (def == nullptr) { OD_LOG_ERR("Invalid creature spawn condition format"); return false; } mCreatureSpawnConditions[creatureDefinition].push_back(def); } } return true; } bool ConfigManager::loadFactions(const std::string& fileName) { OD_LOG_INF("Load factions file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[Factions]") { OD_LOG_ERR("Invalid factions start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Factions]") break; if (nextParam != "[Faction]") { OD_LOG_ERR("Invalid faction. Line was " + nextParam); return false; } std::string factionName; std::string workerClass; while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Faction]") break; if (nextParam == "[/Factions]") break; if (nextParam == "Name") { defFile >> factionName; continue; } if(factionName.empty()) { OD_LOG_ERR("Empty or missing faction name is not allowed"); return false; } if (nextParam == "WorkerClass") { defFile >> workerClass; continue; } if(workerClass.empty()) { OD_LOG_ERR("Empty or missing WorkerClass name is not allowed"); return false; } if (nextParam != "[SpawnPool]") { OD_LOG_ERR("Invalid faction. Line was " + nextParam); return false; } mFactions.push_back(factionName); mFactionDefaultWorkerClass[factionName] = workerClass; // The first faction in the config file is also used for the rogue seat if(mDefaultWorkerRogue.empty()) mDefaultWorkerRogue = workerClass; while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/SpawnPool]") break; if (nextParam == "[/Faction]") break; if (nextParam == "[/Factions]") break; // We check if the creature definition exists const CreatureDefinition* creatureDefinition = getCreatureDefinition(nextParam); if(creatureDefinition == nullptr) { OD_LOG_ERR("factionName=" + factionName + ", class=" + nextParam); continue; } mFactionSpawnPool[factionName].push_back(nextParam); } } } return true; } bool ConfigManager::loadRooms(const std::string& fileName) { OD_LOG_INF("Load Rooms file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[Rooms]") { OD_LOG_ERR("Invalid factions start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Rooms]") break; defFile >> mRoomsConfig[nextParam]; } return true; } bool ConfigManager::loadTraps(const std::string& fileName) { OD_LOG_INF("Load traps file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[Traps]") { OD_LOG_ERR("Invalid Traps start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Traps]") break; defFile >> mTrapsConfig[nextParam]; } return true; } bool ConfigManager::loadSpellConfig(const std::string& fileName) { OD_LOG_INF("Load Spell config file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[Spells]") { OD_LOG_ERR("Invalid Spells start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Spells]") break; defFile >> mSpellConfig[nextParam]; } return true; } bool ConfigManager::loadSkills(const std::string& fileName) { OD_LOG_INF("Load Skills file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; // Read in the creature class descriptions defFile >> nextParam; if (nextParam != "[Skills]") { OD_LOG_ERR("Invalid Skills start format. Line was " + nextParam); return false; } while(defFile.good()) { if(!(defFile >> nextParam)) break; if (nextParam == "[/Skills]") break; defFile >> mSkillPoints[nextParam]; } return true; } bool ConfigManager::loadTilesets(const std::string& fileName) { OD_LOG_INF("Load Tilesets file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_ERR("Couldn't read " + fileName); return false; } std::string nextParam; defFile >> nextParam; if (nextParam != "[Tilesets]") { OD_LOG_ERR("Invalid Tilesets start format. Line was " + nextParam); return false; } while(true) { defFile >> nextParam; if (nextParam == "[/Tilesets]") break; if (nextParam == "[/Tileset]") continue; if (nextParam != "[Tileset]") { OD_LOG_ERR("Expecting TileSet tag but got=" + nextParam); return false; } defFile >> nextParam; if (nextParam != "Name") { OD_LOG_ERR("Expecting Name tag but got=" + nextParam); return false; } std::string tileSetName; defFile >> tileSetName; TileSet* tileSet = new TileSet(); mTileSets[tileSetName] = tileSet; defFile >> nextParam; if(nextParam != "[TileLink]") { OD_LOG_ERR("Expecting TileLink tag but got=" + nextParam); return false; } while(true) { defFile >> nextParam; if(nextParam == "[/TileLink]") break; TileVisual tileVisual1 = Tile::tileVisualFromString(nextParam); if(tileVisual1 == TileVisual::nullTileVisual) { OD_LOG_ERR("Wrong TileVisual1 in tileset=" + nextParam); return false; } defFile >> nextParam; TileVisual tileVisual2 = Tile::tileVisualFromString(nextParam); if(tileVisual2 == TileVisual::nullTileVisual) { OD_LOG_ERR("Wrong TileVisual2 in tileset=" + nextParam); return false; } tileSet->addTileLink(tileVisual1, tileVisual2); } if(!loadTilesetValues(defFile, TileVisual::goldGround, tileSet->configureTileValues(TileVisual::goldGround))) return false; if(!loadTilesetValues(defFile, TileVisual::goldFull, tileSet->configureTileValues(TileVisual::goldFull))) return false; if(!loadTilesetValues(defFile, TileVisual::dirtGround, tileSet->configureTileValues(TileVisual::dirtGround))) return false; if(!loadTilesetValues(defFile, TileVisual::dirtFull, tileSet->configureTileValues(TileVisual::dirtFull))) return false; if(!loadTilesetValues(defFile, TileVisual::rockGround, tileSet->configureTileValues(TileVisual::rockGround))) return false; if(!loadTilesetValues(defFile, TileVisual::rockFull, tileSet->configureTileValues(TileVisual::rockFull))) return false; if(!loadTilesetValues(defFile, TileVisual::waterGround, tileSet->configureTileValues(TileVisual::waterGround))) return false; if(!loadTilesetValues(defFile, TileVisual::lavaGround, tileSet->configureTileValues(TileVisual::lavaGround))) return false; if(!loadTilesetValues(defFile, TileVisual::claimedGround, tileSet->configureTileValues(TileVisual::claimedGround))) return false; if(!loadTilesetValues(defFile, TileVisual::claimedFull, tileSet->configureTileValues(TileVisual::claimedFull))) return false; if(!loadTilesetValues(defFile, TileVisual::gemGround, tileSet->configureTileValues(TileVisual::gemGround))) return false; if(!loadTilesetValues(defFile, TileVisual::gemFull, tileSet->configureTileValues(TileVisual::gemFull))) return false; } // At least the default tileset should be defined if(mTileSets.count(DEFAULT_TILESET_NAME) <= 0) { OD_LOG_ERR("No tileset defined with name=" + DEFAULT_TILESET_NAME); return false; } return true; } bool ConfigManager::loadTilesetValues(std::istream& defFile, TileVisual tileVisual, std::vector& tileValues) { std::string nextParam; std::string beginTag = "[" + Tile::tileVisualToString(tileVisual) + "]"; std::string endTag = "[/" + Tile::tileVisualToString(tileVisual) + "]"; defFile >> nextParam; if (nextParam != beginTag) { OD_LOG_ERR("Expecting " + beginTag + " tag but got=" + nextParam); return false; } while(true) { std::string indexStr; defFile >> indexStr; if(indexStr == endTag) return true; boost::dynamic_bitset<> x(indexStr); uint32_t index = static_cast(x.to_ulong()); std::string meshName; defFile >> meshName; std::string materialName; defFile >> materialName; if(materialName.compare("''") == 0) materialName.clear(); double rotX; defFile >> rotX; double rotY; defFile >> rotY; double rotZ; defFile >> rotZ; if(index >= tileValues.size()) { OD_LOG_ERR("Tileset index too high in tileset=" + endTag + ", index=" + indexStr); return false; } tileValues[index] = TileSetValue(meshName, materialName, rotX, rotY, rotZ); } } void ConfigManager::loadUserConfig(const std::string& fileName) { if (fileName.empty()) return; mFilenameUserCfg = fileName; OD_LOG_INF("Load user config file: " + fileName); std::stringstream defFile; if(!Helper::readFileWithoutComments(fileName, defFile)) { OD_LOG_INF("Couldn't read " + fileName); return; } mUserConfig.clear(); mUserConfig.resize(Config::Ctg::TOTAL); std::string nextParam; defFile >> nextParam; if (nextParam != "[Configuration]") { OD_LOG_WRN("Invalid User configuration start format. Line was " + nextParam); return; } std::string value; Config::Ctg category = Config::Ctg::NONE; while(defFile.good()) { if (!(defFile >> nextParam)) { break; } else if (nextParam == "[/Configuration]") { break; } else if (nextParam == "[Audio]") { category = Config::Ctg::AUDIO; continue; } else if (nextParam == "[Video]") { category = Config::Ctg::VIDEO; continue; } else if (nextParam == "[Input]") { category = Config::Ctg::INPUT; continue; } else if (nextParam == "[Game]") { category = Config::Ctg::GAME; continue; } else if (nextParam == "[/Audio]" || nextParam == "[/Video]" || nextParam == "[/Input]" || nextParam == "[/Game]") { category = Config::Ctg::NONE; continue; } else if (!nextParam.empty()) { std::string line; std::getline(defFile, line); // Make sure to cut the line only when encountering a tab. line = nextParam + line; std::vector elements = Helper::split(line, '\t'); if (elements.size() != 2) { OD_LOG_WRN("Invalid parameter line: " + line); continue; } if (category == Config::Ctg::NONE) { OD_LOG_WRN("Parameter set in unknown category. Will be ignored: " + elements[0] + ": " + elements[1]); continue; } mUserConfig[ category ][ elements[0] ] = elements[1]; } else { continue; } } } bool ConfigManager::saveUserConfig() { if (mFilenameUserCfg.empty()) { OD_LOG_ERR("Can't save config. The user config file isn't set."); return false; } std::ofstream userFile(mFilenameUserCfg.c_str(), std::ifstream::out); if (!userFile.is_open()) { OD_LOG_ERR("Couldn't open user config for writing: " + mFilenameUserCfg); return false; } // Split config in categories. std::map& mAudioUserConfig = mUserConfig.at(Config::Ctg::AUDIO); std::map& mVideoUserConfig = mUserConfig.at(Config::Ctg::VIDEO); std::map& mInputUserConfig = mUserConfig.at(Config::Ctg::INPUT); std::map& mGameUserConfig = mUserConfig.at(Config::Ctg::GAME); userFile << "[Configuration]" << std::endl; userFile << "[Audio]" << std::endl; for(std::pair audio : mAudioUserConfig) userFile << audio.first << "\t" << audio.second << std::endl; userFile << "[/Audio]" << std::endl; userFile << "[Video]" << std::endl; for(std::pair video : mVideoUserConfig) userFile << video.first << "\t" << video.second << std::endl; userFile << "[/Video]" << std::endl; userFile << "[Input]" << std::endl; for(std::pair input : mInputUserConfig) userFile << input.first << "\t" << input.second << std::endl; userFile << "[/Input]" << std::endl; userFile << "[Game]" << std::endl; for(std::pair input : mGameUserConfig) userFile << input.first << "\t" << input.second << std::endl; userFile << "[/Game]" << std::endl; userFile << "[/Configuration]" << std::endl; userFile.close(); return true; } const std::string ConfigManager::getUserValue(Config::Ctg category, const std::string& param, const std::string& defaultValue, bool triggerError) const { if (category >= mUserConfig.size()) { OD_LOG_ERR("User configuration categories uninitialized!"); return defaultValue; } auto& userCfg = mUserConfig[category]; auto it = userCfg.find(param); if(it == userCfg.end()) { if (triggerError) OD_LOG_ERR("Unknown parameter param=" + param); return defaultValue; } return it->second; } const std::string& ConfigManager::getRoomConfigString(const std::string& param) const { auto it = mRoomsConfig.find(param); if(it == mRoomsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return EMPTY_STRING; } return it->second; } uint32_t ConfigManager::getRoomConfigUInt32(const std::string& param) const { auto it = mRoomsConfig.find(param); if(it == mRoomsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toUInt32(it->second); } int32_t ConfigManager::getRoomConfigInt32(const std::string& param) const { auto it = mRoomsConfig.find(param); if(it == mRoomsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toInt(it->second); } double ConfigManager::getRoomConfigDouble(const std::string& param) const { auto it = mRoomsConfig.find(param); if(it == mRoomsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0.0; } return Helper::toDouble(it->second); } const std::string& ConfigManager::getTrapConfigString(const std::string& param) const { auto it = mTrapsConfig.find(param); if(it == mTrapsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return EMPTY_STRING; } return it->second; } uint32_t ConfigManager::getTrapConfigUInt32(const std::string& param) const { auto it = mTrapsConfig.find(param); if(it == mTrapsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toUInt32(it->second); } int32_t ConfigManager::getTrapConfigInt32(const std::string& param) const { auto it = mTrapsConfig.find(param); if(it == mTrapsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toInt(it->second); } double ConfigManager::getTrapConfigDouble(const std::string& param) const { auto it = mTrapsConfig.find(param); if(it == mTrapsConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0.0; } return Helper::toDouble(it->second); } const std::string& ConfigManager::getSpellConfigString(const std::string& param) const { auto it = mSpellConfig.find(param); if(it == mSpellConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return EMPTY_STRING; } return it->second; } uint32_t ConfigManager::getSpellConfigUInt32(const std::string& param) const { auto it = mSpellConfig.find(param); if(it == mSpellConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toUInt32(it->second); } int32_t ConfigManager::getSpellConfigInt32(const std::string& param) const { auto it = mSpellConfig.find(param); if(it == mSpellConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0; } return Helper::toInt(it->second); } double ConfigManager::getSpellConfigDouble(const std::string& param) const { auto it = mSpellConfig.find(param); if(it == mSpellConfig.end()) { OD_LOG_ERR("Unknown parameter param=" + param); return 0.0; } return Helper::toDouble(it->second); } int32_t ConfigManager::getSkillPoints(const std::string& res) const { auto it = mSkillPoints.find(res); if(it == mSkillPoints.end()) { OD_LOG_ERR("Unknown parameter res=" + res); return 0; } return it->second; } const CreatureDefinition* ConfigManager::getCreatureDefinition(const std::string& name) const { auto it = mCreatureDefs.find(name); if(it != mCreatureDefs.end()) { return it->second; } return nullptr; } const Weapon* ConfigManager::getWeapon(const std::string& name) const { for(const Weapon* def : mWeapons) { if(name.compare(def->getName()) == 0) return def; } return nullptr; } const Ogre::ColourValue& ConfigManager::getColorFromId(const std::string& id) const { auto it = mSeatColors.find(id); if(it == mSeatColors.end()) return DEFAULT_SEAT_COLOURVALUE; return it->second; } const std::vector& ConfigManager::getCreatureSpawnConditions(const CreatureDefinition* def) const { auto it = mCreatureSpawnConditions.find(def); if(it == mCreatureSpawnConditions.end()) return SpawnCondition::EMPTY_SPAWNCONDITIONS; return it->second; } const std::vector& ConfigManager::getFactionSpawnPool(const std::string& faction) const { auto it = mFactionSpawnPool.find(faction); if(it == mFactionSpawnPool.end()) return EMPTY_SPAWNPOOL; return it->second; } const std::string& ConfigManager::getFactionWorkerClass(const std::string& faction) const { auto it = mFactionDefaultWorkerClass.find(faction); if(it == mFactionDefaultWorkerClass.end()) return EMPTY_STRING; return it->second; } const TileSet* ConfigManager::getTileSet(const std::string& tileSetName) const { if(tileSetName.empty()) return mTileSets.at(DEFAULT_TILESET_NAME); auto it = mTileSets.find(tileSetName); if(it == mTileSets.end()) { OD_LOG_ERR("Cannot find requested tileset name=" + tileSetName); // We return the default tileset return mTileSets.at(DEFAULT_TILESET_NAME); } return it->second; } bool ConfigManager::initVideoConfig(Ogre::Root& ogreRoot) { // Also creates the config entry if it doesn't exist in config yet. std::string rendererName = getVideoValue(Config::RENDERER, std::string(), false); // Try the default OpenGL renderer first, if empty. if (rendererName.empty()) rendererName = "OpenGL Rendering Subsystem"; Ogre::RenderSystem* renderSystem = ogreRoot.getRenderSystemByName(rendererName); bool sameRenderer = true; if (renderSystem == nullptr) { const Ogre::RenderSystemList& renderers = ogreRoot.getAvailableRenderers(); if(renderers.empty()) { OD_LOG_ERR("No valid renderer found. Exiting..."); return false; } renderSystem = *renderers.begin(); OD_LOG_INF("No OpenGL renderer found. Using the first available: " + renderSystem->getName()); sameRenderer = false; } ogreRoot.setRenderSystem(renderSystem); Ogre::ConfigOptionMap& options = renderSystem->getConfigOptions(); // If the renderer was changed, we need to reset the video options. if (sameRenderer == false) { mUserConfig[Config::Ctg::VIDEO].clear(); for (std::pair option : options) { std::string optionName = option.first; Ogre::ConfigOption& values = option.second; // We don't store options that are immutable or empty if (values.immutable || values.possibleValues.empty()) continue; setVideoValue(optionName, values.currentValue); } } else { std::vector optionsToRemove; // The renderer system was initialized. Let's load its new option values. std::map& mVideoUserConfig = mUserConfig[Config::Ctg::VIDEO]; for (std::pair setting : mVideoUserConfig) { // Check the option exists. if (options.find(setting.first) == options.end()) { optionsToRemove.push_back(setting.first); continue; } // Check the desired option value exists. Ogre::ConfigOption& values = options.find(setting.first)->second; bool valueIsPossible = false; for (std::string value : values.possibleValues) { if (setting.second == value) { valueIsPossible = true; break; } } if (!valueIsPossible) { optionsToRemove.push_back(setting.first); continue; } renderSystem->setConfigOption(setting.first, setting.second); } // Removes now invalid options from the video options. for (std::string option : optionsToRemove) mVideoUserConfig.erase(option); } return true; } void ConfigManager::loadKeeperVoices(const std::string& soundPath) { std::vector directories; std::string parentPath = soundPath + "Relative"; if(!Helper::fillDirList(parentPath, directories, false)) { OD_LOG_ERR("Error while loading sounds in directory=" + parentPath); return; } for(const std::string& directory : directories) { // We add the keeper voice OD_LOG_INF("Keeper voice found=" + directory); mKeeperVoices.push_back(directory); } if(mKeeperVoices.empty()) { OD_LOG_ERR("No keeper voice found. Relative sounds will not work"); return; } if(std::find(mKeeperVoices.begin(), mKeeperVoices.end(), ConfigManager::DEFAULT_KEEPER_VOICE) == mKeeperVoices.end()) { OD_LOG_ERR("No default keeper voice found"); } }