From 5054ee011a166242c86ddd77e9c0c2e3edea5eb5 Mon Sep 17 00:00:00 2001 From: Nordsoft91 Date: Sun, 5 Jun 2022 09:02:58 +0300 Subject: [PATCH] Rmg water support (#751) * Roads added to shipyard * Load general rmg parameters from config * Fix issue with default zone guard * Move magic numbers related to balance to randomMap.json --- config/randomMap.json | 59 +++++++++ config/schemas/template.json | 10 ++ lib/rmg/CMapGenOptions.cpp | 2 +- lib/rmg/CMapGenerator.cpp | 85 ++++++++++++- lib/rmg/CMapGenerator.h | 23 ++++ lib/rmg/CRmgTemplate.cpp | 34 ++++- lib/rmg/CRmgTemplate.h | 1 + lib/rmg/CRmgTemplateZone.cpp | 237 +++++++++++++++++------------------ 8 files changed, 323 insertions(+), 128 deletions(-) create mode 100644 config/randomMap.json diff --git a/config/randomMap.json b/config/randomMap.json new file mode 100644 index 000000000..72de50821 --- /dev/null +++ b/config/randomMap.json @@ -0,0 +1,59 @@ +{ + "terrain" : + { + "undergroundAllow" : ["lava"], //others to be replaced by subterranena + "groundProhibit" : ["subterranean"] //to be replaced by dirt + }, + "waterZone" : + { + "treasure" : + [ + { "min" : 2000, "max" : 6000, "density" : 1 }, + { "min" : 100, "max" : 1000, "density" : 5 } + ], + "shipyard" : + { + "value" : 3500 + } + }, + "mines" : + { + "value" : + { + "wood" : 1500, + "ore" : 1500, + "gems" : 3500, + "crystal" : 3500, + "mercury" : 3500, + "sulfur" : 3500, + "gold" : 7000 + }, + "extraResourcesLimit" : 3 + }, + "minGuardStrength" : 2000, + "defaultRoadType" : "cobblestone_road", + "treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit + "prisons" : + { + "experience" : [0, 5000, 15000, 90000, 500000], + "value" : [2500, 5000, 10000, 20000, 30000], + }, + "scrolls" : + { + "value" : [500, 2000, 3000, 4000, 5000], + }, + "pandoras" : + { + "valueMultiplierGold" : 5000, + "valueMultiplierExperience" : 6000, + "valueMultiplierSpells" : 2500, + "valueSpellSchool" : 15000, + "valueSpell60" : 30000, + "creaturesValue" : [5000, 7000, 9000, 12000, 16000, 21000, 27000] + }, + "quests" : + { + "value" : [2000, 5333, 8666, 12000], + "rewardValue" : [5000, 10000, 15000, 20000] + } +} \ No newline at end of file diff --git a/config/schemas/template.json b/config/schemas/template.json index 1f0464589..6b6081bba 100644 --- a/config/schemas/template.json +++ b/config/schemas/template.json @@ -60,6 +60,12 @@ "minimum" : 0 } } + }, + "waterContent": + { + "enum": ["none", "normal", "islands"], + "additionalProperties" : false, + "type": "string" } }, @@ -73,6 +79,10 @@ "type": "array", "items":{"$ref" : "#/definitions/connection"} }, + "allowedWaterContent": { + "type": "array", + "items": {"$ref" : "#/definitions/waterContent"} + }, "required" : ["zones", "connections"], "additionalProperties" : false } diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 2adfab190..ba71ace26 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -246,7 +246,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand) if(waterContent == EWaterContent::RANDOM) { - waterContent = static_cast(rand.nextInt(EWaterContent::NONE, EWaterContent::ISLANDS)); + waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand); } if(monsterStrength == EMonsterStrength::RANDOM) { diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 637cc26b8..f8440c651 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -62,10 +62,89 @@ CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) : zonesTotal(0), tiles(nullptr), prisonsRemaining(0), monolithIndex(0) { + loadConfig(); rand.setSeed(this->randomSeed); mapGenOptions.finalize(rand); } +void CMapGenerator::loadConfig() +{ + static std::map terrainMap + { + {"dirt", ETerrainType::DIRT}, + {"sand", ETerrainType::SAND}, + {"grass", ETerrainType::GRASS}, + {"snow", ETerrainType::SNOW}, + {"swamp", ETerrainType::SWAMP}, + {"subterranean", ETerrainType::SUBTERRANEAN}, + {"lava", ETerrainType::LAVA}, + {"rough", ETerrainType::ROUGH} + }; + static const std::map resMap + { + {"wood", Res::ERes::WOOD}, + {"ore", Res::ERes::ORE}, + {"gems", Res::ERes::GEMS}, + {"crystal", Res::ERes::CRYSTAL}, + {"mercury", Res::ERes::MERCURY}, + {"sulfur", Res::ERes::SULFUR}, + {"gold", Res::ERes::GOLD}, + }; + static std::map roadTypeMap + { + {"dirt_road", ERoadType::DIRT_ROAD}, + {"gravel_road", ERoadType::GRAVEL_ROAD}, + {"cobblestone_road", ERoadType::COBBLESTONE_ROAD} + }; + static const ResourceID path("config/randomMap.json"); + JsonNode randomMapJson(path); + for(auto& s : randomMapJson["terrain"]["undergroundAllow"].Vector()) + { + if(!s.isNull()) + config.terrainUndergroundAllowed.push_back(terrainMap[s.String()]); + } + for(auto& s : randomMapJson["terrain"]["groundProhibit"].Vector()) + { + if(!s.isNull()) + config.terrainGroundProhibit.push_back(terrainMap[s.String()]); + } + config.shipyardGuard = randomMapJson["waterZone"]["shipyard"]["value"].Integer(); + for(auto & treasure : randomMapJson["waterZone"]["treasure"].Vector()) + { + config.waterTreasure.emplace_back(treasure["min"].Integer(), treasure["max"].Integer(), treasure["density"].Integer()); + } + for(auto& s : resMap) + { + config.mineValues[s.second] = randomMapJson["mines"]["value"][s.first].Integer(); + } + config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer(); + config.minGuardStrength = randomMapJson["minGuardStrength"].Integer(); + config.defaultRoadType = roadTypeMap[randomMapJson["defaultRoadType"].String()]; + config.treasureValueLimit = randomMapJson["treasureValueLimit"].Integer(); + for(auto & i : randomMapJson["prisons"]["experience"].Vector()) + config.prisonExperience.push_back(i.Integer()); + for(auto & i : randomMapJson["prisons"]["value"].Vector()) + config.prisonValues.push_back(i.Integer()); + for(auto & i : randomMapJson["scrolls"]["value"].Vector()) + config.scrollValues.push_back(i.Integer()); + for(auto & i : randomMapJson["pandoras"]["creaturesValue"].Vector()) + config.pandoraCreatureValues.push_back(i.Integer()); + for(auto & i : randomMapJson["quests"]["value"].Vector()) + config.questValues.push_back(i.Integer()); + for(auto & i : randomMapJson["quests"]["rewardValue"].Vector()) + config.questRewardValues.push_back(i.Integer()); + config.pandoraMultiplierGold = randomMapJson["pandoras"]["valueMultiplierGold"].Integer(); + config.pandoraMultiplierExperience = randomMapJson["pandoras"]["valueMultiplierExperience"].Integer(); + config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer(); + config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer(); + config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer(); +} + +const CMapGenerator::Config & CMapGenerator::getConfig() const +{ + return config; +} + void CMapGenerator::initTiles() { map->initTerrain(); @@ -309,8 +388,10 @@ void CMapGenerator::genZones() void CMapGenerator::createWaterTreasures() { //add treasures on water - getZoneWater().second->addTreasureInfo(CTreasureInfo{100, 1000, 5}); - getZoneWater().second->addTreasureInfo(CTreasureInfo{2000, 6000, 1}); + for(auto & treasureInfo : getConfig().waterTreasure) + { + getZoneWater().second->addTreasureInfo(treasureInfo); + } } void CMapGenerator::prepareWaterTiles() diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index bff4a3487..3469af69f 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -52,11 +52,31 @@ public: class DLL_LINKAGE CMapGenerator { public: + struct Config + { + std::vector terrainUndergroundAllowed; + std::vector terrainGroundProhibit; + std::vector waterTreasure; + int shipyardGuard; + int mineExtraResources; + std::map mineValues; + int minGuardStrength; + ERoadType::ERoadType defaultRoadType; + int treasureValueLimit; + std::vector prisonExperience, prisonValues; + std::vector scrollValues; + int pandoraMultiplierGold, pandoraMultiplierExperience, pandoraMultiplierSpells, pandoraSpellSchool, pandoraSpell60; + std::vector pandoraCreatureValues; + std::vector questValues, questRewardValues; + }; + using Zones = std::map>; explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr)); ~CMapGenerator(); // required due to std::unique_ptr + const Config & getConfig() const; + mutable std::unique_ptr map; CRandomGenerator rand; @@ -111,6 +131,7 @@ public: private: int randomSeed; CMapGenOptions& mapGenOptions; + Config config; std::vector connectionsLeft; Zones zones; @@ -129,6 +150,8 @@ private: void checkIsOnMap(const int3 &tile) const; //throws /// Generation methods + void loadConfig(); + std::string getMapDescription() const; void initPrisonsRemaining(); diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 1774b941f..35ce71dc7 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include +#include #include "CRmgTemplate.h" #include "../mapping/CMap.h" @@ -359,7 +360,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) rawStrength = static_cast(zoneMonsterStrength); rawStrength++; } - handler.serializeEnum("monsters", rawStrength, STRENGTH); + handler.serializeEnum("monsters", rawStrength, EMonsterStrength::ZONE_NORMAL + 1, STRENGTH); if(!handler.saving) { rawStrength--; @@ -443,6 +444,11 @@ bool CRmgTemplate::isWaterContentAllowed(EWaterContent::EWaterContent waterConte return waterContent == EWaterContent::EWaterContent::RANDOM || allowedWaterContent.count(waterContent); } +const std::set & CRmgTemplate::getWaterContentAllowed() const +{ + return allowedWaterContent; +} + void CRmgTemplate::setId(const std::string & value) { id = value; @@ -583,6 +589,32 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler) auto connectionsData = handler.enterArray("connections"); connectionsData.serializeStruct(connections); } + + { + boost::bimap enc; + enc.insert({EWaterContent::NONE, "none"}); + enc.insert({EWaterContent::NORMAL, "normal"}); + enc.insert({EWaterContent::ISLANDS, "islands"}); + JsonNode node; + if(handler.saving) + { + node.setType(JsonNode::JsonType::DATA_VECTOR); + for(auto wc : allowedWaterContent) + { + JsonNode n; + n.String() = enc.left.at(wc); + node.Vector().push_back(n); + } + } + handler.serializeRaw("allowedWaterContent", node, boost::none); + if(!handler.saving) + { + for(auto wc : node.Vector()) + { + allowedWaterContent.insert(enc.right.at(std::string(wc.String()))); + } + } + } { auto zonesData = handler.enterStruct("zones"); diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 94af9017f..0053e57c8 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -181,6 +181,7 @@ public: bool matchesSize(const int3 & value) const; bool isWaterContentAllowed(EWaterContent::EWaterContent waterContent) const; + const std::set & getWaterContentAllowed() const; void setId(const std::string & value); const std::string & getName() const; diff --git a/lib/rmg/CRmgTemplateZone.cpp b/lib/rmg/CRmgTemplateZone.cpp index 78bb96b85..f359ed28a 100644 --- a/lib/rmg/CRmgTemplateZone.cpp +++ b/lib/rmg/CRmgTemplateZone.cpp @@ -641,7 +641,7 @@ void CRmgTemplateZone::waterConnection(CRmgTemplateZone& dst) if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns) { - coastTile = dst.createShipyard(lake.tiles, 3500); + coastTile = dst.createShipyard(lake.tiles, gen->getConfig().shipyardGuard); if(!coastTile.valid()) { coastTile = makeBoat(dst.getId(), lake.tiles); @@ -1266,7 +1266,7 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin int strength2 = static_cast(std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength])); strength = strength1 + strength2; - if (strength < 2000) + if (strength < gen->getConfig().minGuardStrength) return false; //no guard at all CreatureID creId = CreatureID::NONE; @@ -1742,14 +1742,14 @@ void CRmgTemplateZone::initTerrainType () //TODO: allow new types of terrain? { - if (isUnderground()) + if(isUnderground()) { - if (terrainType != ETerrainType::LAVA) + if(!vstd::contains(gen->getConfig().terrainUndergroundAllowed, terrainType)) terrainType = ETerrainType::SUBTERRANEAN; } else { - if (terrainType == ETerrainType::SUBTERRANEAN) + if(vstd::contains(gen->getConfig().terrainGroundProhibit, terrainType)) terrainType = ETerrainType::DIRT; } } @@ -1767,8 +1767,6 @@ void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType) bool CRmgTemplateZone::placeMines () { using namespace Res; - static const std::map mineValue{{ERes::WOOD, 1500}, {ERes::ORE, 1500}, {ERes::GEMS, 3500}, {ERes::CRYSTAL, 3500}, {ERes::MERCURY, 3500}, {ERes::SULFUR, 3500}, {ERes::GOLD, 7000}}; - std::vector createdMines; for(const auto & mineInfo : mines) @@ -1783,22 +1781,26 @@ bool CRmgTemplateZone::placeMines () createdMines.push_back(mine); if(!i && (res == ERes::WOOD || res == ERes::ORE)) - addCloseObject(mine, mineValue.at(res)); //only first woor&ore mines are close + addCloseObject(mine, gen->getConfig().mineValues.at(res)); //only first wood&ore mines are close else - addRequiredObject(mine, mineValue.at(res)); + addRequiredObject(mine, gen->getConfig().mineValues.at(res)); } } //create extra resources - for(auto * mine : createdMines) + if(int extraRes = gen->getConfig().mineExtraResources) { - for(int rc = gen->rand.nextInt(1, 3); rc > 0; --rc) + for(auto * mine : createdMines) { - auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate()); - resourse->amount = CGResource::RANDOM_AMOUNT; - addNearbyObject(resourse, mine); + for(int rc = gen->rand.nextInt(1, extraRes); rc > 0; --rc) + { + auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate()); + resourse->amount = CGResource::RANDOM_AMOUNT; + addNearbyObject(resourse, mine); + } } } + return true; } @@ -2072,69 +2074,62 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength) shipyard->tempOwner = PlayerColor::NEUTRAL; setTemplateForObject(shipyard); - std::vector offsets; + std::vector outOffsets; auto tilesBlockedByObject = shipyard->getBlockedOffsets(); tilesBlockedByObject.insert(shipyard->getVisitableOffset()); - shipyard->getOutOffsets(offsets); + shipyard->getOutOffsets(outOffsets); int3 targetTile(-1, -1, -1); std::set shipAccessCandidates; - for(auto& candidateTile : possibleTiles) + for(const auto & outOffset : outOffsets) { - bool foundTargetPosition = false; - for(const auto & offset : offsets) + auto candidateTile = position - outOffset; + std::set tilesBlockedAbsolute; + + //check space under object + bool allClear = true; + for(const auto & objectTileOffset : tilesBlockedByObject) { - if(candidateTile+offset == position) + auto objectTile = candidateTile + objectTileOffset; + tilesBlockedAbsolute.insert(objectTile); + if(!gen->map->isInTheMap(objectTile) || !gen->isPossible(objectTile) || gen->getZoneID(objectTile)!=id) { - std::set tilesBlockedAbsolute; - //check space under object - bool allClear = true; - for(const auto & objectTileOffset : tilesBlockedByObject) - { - auto objectTile = candidateTile + objectTileOffset; - tilesBlockedAbsolute.insert(objectTile); - if(!gen->map->isInTheMap(objectTile) || !gen->isPossible(objectTile) || gen->getZoneID(objectTile)!=id) - { - allClear = false; - break; - } - } - if(!allClear) //cannot place shipyard anyway - break; - - //prepare temporary map - for(auto& blockedPos : tilesBlockedAbsolute) - gen->setOccupied(blockedPos, ETileType::USED); - - //check if position is accessible - gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v) - { - if(!gen->isBlocked(v) && gen->getZoneID(v)==id) - { - //make sure that it's possible to create path to boarding position - if(crunchPath(v, findClosestTile(freePaths, v), false, nullptr)) - shipAccessCandidates.insert(v); - } - }); - - //rollback temporary map - for(auto& blockedPos : tilesBlockedAbsolute) - gen->setOccupied(blockedPos, ETileType::POSSIBLE); - - if(!shipAccessCandidates.empty()) - { - foundTargetPosition = true; - } - - break; //no need to check other offsets as we already found position + allClear = false; + break; } } + if(!allClear) //cannot place shipyard anyway + continue; - if(foundTargetPosition && isAccessibleFromSomewhere(shipyard->appearance, candidateTile)) + //prepare temporary map + for(auto& blockedPos : tilesBlockedAbsolute) + gen->setOccupied(blockedPos, ETileType::USED); + + + //check if boarding position is accessible + gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v) + { + if(!gen->isBlocked(v) && gen->getZoneID(v)==id) + { + //make sure that it's possible to create path to boarding position + if(connectWithCenter(v, false, false)) + shipAccessCandidates.insert(v); + } + }); + + //check if we can connect shipyard entrance with path + if(!connectWithCenter(candidateTile + shipyard->getVisitableOffset(), false)) + shipAccessCandidates.clear(); + + //rollback temporary map + for(auto& blockedPos : tilesBlockedAbsolute) + gen->setOccupied(blockedPos, ETileType::POSSIBLE); + + if(!shipAccessCandidates.empty() && isAccessibleFromSomewhere(shipyard->appearance, candidateTile)) { targetTile = candidateTile; - break; + break; //no need to check other offsets as we already found position } shipAccessCandidates.clear(); //invalidate positions @@ -2146,19 +2141,24 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength) return false; } - placeObject(shipyard, targetTile); - guardObject(shipyard, guardStrength, false, true); - - for(auto& accessPosition : shipAccessCandidates) + if(tryToPlaceObjectAndConnectToPath(shipyard, targetTile)==EObjectPlacingResult::SUCCESS) { - if(connectPath(accessPosition, false)) + placeObject(shipyard, targetTile); + guardObject(shipyard, guardStrength, false, true); + + for(auto& accessPosition : shipAccessCandidates) { - gen->setOccupied(accessPosition, ETileType::FREE); - return true; + if(connectPath(accessPosition, false)) + { + gen->setOccupied(accessPosition, ETileType::FREE); + return true; + } } } - throw rmgException("Cannot find path to shipyard boarding position"); + logGlobal->warn("Cannot find path to shipyard boarding position"); + delete shipyard; + return false; } void CRmgTemplateZone::createTreasures() @@ -2206,7 +2206,12 @@ void CRmgTemplateZone::createTreasures() //optimization - don't check tiles which are not allowed vstd::erase_if(possibleTiles, [this](const int3 &tile) -> bool { - return (!gen->isPossible(tile)) || gen->getZoneID(tile)!=getId(); + //for water area we sholdn't place treasures close to coast + for(auto & lake : lakes) + if(vstd::contains(lake.distance, tile) && lake.distance[tile] < 2) + return true; + + return !gen->isPossible(tile) || gen->getZoneID(tile)!=getId(); }); @@ -2376,7 +2381,7 @@ void CRmgTemplateZone::drawRoads() } gen->getEditManager()->getTerrainSelection().setSelection(tiles); - gen->getEditManager()->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand); + gen->getEditManager()->drawRoad(gen->getConfig().defaultRoadType, &gen->rand); } @@ -2386,26 +2391,13 @@ bool CRmgTemplateZone::fill() addAllPossibleObjects(); - if(type==ETemplateZoneType::WATER) - { - initFreeTiles(); - connectLater(); - createRequiredObjects(); - fractalize(); - createTreasures(); - } - else - { - //zone center should be always clear to allow other tiles to connect - initFreeTiles(); - connectLater(); //ideally this should work after fractalize, but fails - fractalize(); - placeMines(); - createRequiredObjects(); - createTreasures(); - } - - gen->dump(false); + //zone center should be always clear to allow other tiles to connect + initFreeTiles(); + connectLater(); //ideally this should work after fractalize, but fails + fractalize(); + placeMines(); + createRequiredObjects(); + createTreasures(); logGlobal->info("Zone %d filled successfully", id); return true; @@ -2613,6 +2605,7 @@ void CRmgTemplateZone::placeObject(CGObjectInstance* object, const int3 &pos, bo case Obj::MONOLITH_ONE_WAY_ENTRANCE: case Obj::MONOLITH_ONE_WAY_EXIT: case Obj::SUBTERRANEAN_GATE: + case Obj::SHIPYARD: { addRoadNode(object->visitablePos()); } @@ -2831,11 +2824,11 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CTreasurePileInfo &info, ui32 desir } } - if (thresholds.empty()) + if(thresholds.empty()) { ObjectInfo oi; //Generate pandora Box with gold if the value is extremely high - if (minValue > 20000) //we don't have object valuable enough + if(minValue > gen->getConfig().treasureValueLimit) //we don't have object valuable enough { oi.generateObject = [minValue]() -> CGObjectInstance * { @@ -2913,17 +2906,15 @@ void CRmgTemplateZone::addAllPossibleObjects() //prisons //levels 1, 5, 10, 20, 30 - static int prisonExp[] = { 0, 5000, 15000, 90000, 500000 }; - static int prisonValues[] = { 2500, 5000, 10000, 20000, 30000 }; - - for (int i = 0; i < 5; i++) + static int prisonsLevels = std::min(gen->getConfig().prisonExperience.size(), gen->getConfig().prisonValues.size()); + for(int i = 0; i < prisonsLevels; i++) { oi.generateObject = [i, this]() -> CGObjectInstance * { std::vector possibleHeroes; - for (int j = 0; j < gen->map->allowedHeroes.size(); j++) + for(int j = 0; j < gen->map->allowedHeroes.size(); j++) { - if (gen->map->allowedHeroes[j]) + if(gen->map->allowedHeroes[j]) possibleHeroes.push_back(j); } @@ -2933,7 +2924,7 @@ void CRmgTemplateZone::addAllPossibleObjects() obj->subID = hid; //will be initialized later - obj->exp = prisonExp[i]; + obj->exp = gen->getConfig().prisonExperience[i]; obj->setOwner(PlayerColor::NEUTRAL); gen->map->allowedHeroes[hid] = false; //ban this hero gen->decreasePrisonsRemaining(); @@ -2942,7 +2933,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PRISON, 0, terrainType); - oi.value = prisonValues[i]; + oi.value = gen->getConfig().prisonValues[i]; oi.probability = 30; oi.maxPerZone = gen->getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos. possibleObjects.push_back(oi); @@ -3008,9 +2999,7 @@ void CRmgTemplateZone::addAllPossibleObjects() } } - static const int scrollValues[] = { 500, 2000, 3000, 4000, 5000 }; - - for (int i = 0; i < 5; i++) + for(int i = 0; i < gen->getConfig().scrollValues.size(); i++) { oi.generateObject = [i, this]() -> CGObjectInstance * { @@ -3030,13 +3019,13 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::SPELL_SCROLL, 0, terrainType); - oi.value = scrollValues[i]; + oi.value = gen->getConfig().scrollValues[i]; oi.probability = 30; possibleObjects.push_back(oi); } //pandora box with gold - for (int i = 1; i < 5; i++) + for(int i = 1; i < 5; i++) { oi.generateObject = [i]() -> CGObjectInstance * { @@ -3046,7 +3035,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); - oi.value = i * 5000; + oi.value = i * gen->getConfig().pandoraMultiplierGold; oi.probability = 5; possibleObjects.push_back(oi); } @@ -3062,20 +3051,22 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); - oi.value = i * 6000; + oi.value = i * gen->getConfig().pandoraMultiplierExperience; oi.probability = 20; possibleObjects.push_back(oi); } //pandora box with creatures - static const int tierValues[] = { 5000, 7000, 9000, 12000, 16000, 21000, 27000 }; + const std::vector & tierValues = gen->getConfig().pandoraCreatureValues; - auto creatureToCount = [](CCreature * creature) -> int + auto creatureToCount = [&tierValues](CCreature * creature) -> int { if (!creature->AIValue) //bug #2681 return 0; //this box won't be generated - int actualTier = creature->level > 7 ? 6 : creature->level - 1; + int actualTier = creature->level > tierValues.size() ? + tierValues.size() - 1 : + creature->level - 1; float creaturesAmount = ((float)tierValues[actualTier]) / creature->AIValue; if (creaturesAmount <= 5) { @@ -3142,7 +3133,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); - oi.value = (i + 1) * 2500; //5000 - 15000 + oi.value = (i + 1) * gen->getConfig().pandoraMultiplierSpells; //5000 - 15000 oi.probability = 2; possibleObjects.push_back(oi); } @@ -3171,7 +3162,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); - oi.value = 15000; + oi.value = gen->getConfig().pandoraSpellSchool; oi.probability = 2; possibleObjects.push_back(oi); } @@ -3199,7 +3190,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); - oi.value = 30000; + oi.value = gen->getConfig().pandoraSpell60; oi.probability = 2; possibleObjects.push_back(oi); @@ -3240,7 +3231,7 @@ void CRmgTemplateZone::addAllPossibleObjects() return artInfo; }; - for (int i = 0; i < std::min((int)creatures.size(), questArtsRemaining - genericSeerHuts); i++) + for(int i = 0; i < std::min((int)creatures.size(), questArtsRemaining - genericSeerHuts); i++) { auto creature = creatures[i]; int creaturesAmount = creatureToCount(creature); @@ -3276,15 +3267,13 @@ void CRmgTemplateZone::addAllPossibleObjects() possibleObjects.push_back(oi); } - static int seerExpGold[] = { 5000, 10000, 15000, 20000 }; - static int seerValues[] = { 2000, 5333, 8666, 12000 }; - - for (int i = 0; i < 4; i++) //seems that code for exp and gold reward is similiar + static int seerLevels = std::min(gen->getConfig().questValues.size(), gen->getConfig().questRewardValues.size()); + for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar { int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand); oi.setTemplate(Obj::SEER_HUT, randomAppearance, terrainType); - oi.value = seerValues[i]; + oi.value = gen->getConfig().questValues[i]; oi.probability = 10; oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * @@ -3294,7 +3283,7 @@ void CRmgTemplateZone::addAllPossibleObjects() obj->rewardType = CGSeerHut::EXPERIENCE; obj->rID = 0; //unitialized? - obj->rVal = seerExpGold[i]; + obj->rVal = gen->getConfig().questRewardValues[i]; obj->quest->missionType = CQuest::MISSION_ART; ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand); @@ -3317,7 +3306,7 @@ void CRmgTemplateZone::addAllPossibleObjects() auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); obj->rewardType = CGSeerHut::RESOURCES; obj->rID = Res::GOLD; - obj->rVal = seerExpGold[i]; + obj->rVal = gen->getConfig().questRewardValues[i]; obj->quest->missionType = CQuest::MISSION_ART; ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);