1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

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
This commit is contained in:
Nordsoft91 2022-06-05 09:02:58 +03:00 committed by Andrii Danylchenko
parent 5c1a66ab69
commit 5054ee011a
8 changed files with 323 additions and 128 deletions

59
config/randomMap.json Normal file
View File

@ -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]
}
}

View File

@ -60,6 +60,12 @@
"minimum" : 0 "minimum" : 0
} }
} }
},
"waterContent":
{
"enum": ["none", "normal", "islands"],
"additionalProperties" : false,
"type": "string"
} }
}, },
@ -73,6 +79,10 @@
"type": "array", "type": "array",
"items":{"$ref" : "#/definitions/connection"} "items":{"$ref" : "#/definitions/connection"}
}, },
"allowedWaterContent": {
"type": "array",
"items": {"$ref" : "#/definitions/waterContent"}
},
"required" : ["zones", "connections"], "required" : ["zones", "connections"],
"additionalProperties" : false "additionalProperties" : false
} }

View File

@ -246,7 +246,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
if(waterContent == EWaterContent::RANDOM) if(waterContent == EWaterContent::RANDOM)
{ {
waterContent = static_cast<EWaterContent::EWaterContent>(rand.nextInt(EWaterContent::NONE, EWaterContent::ISLANDS)); waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand);
} }
if(monsterStrength == EMonsterStrength::RANDOM) if(monsterStrength == EMonsterStrength::RANDOM)
{ {

View File

@ -62,10 +62,89 @@ CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
zonesTotal(0), tiles(nullptr), prisonsRemaining(0), zonesTotal(0), tiles(nullptr), prisonsRemaining(0),
monolithIndex(0) monolithIndex(0)
{ {
loadConfig();
rand.setSeed(this->randomSeed); rand.setSeed(this->randomSeed);
mapGenOptions.finalize(rand); mapGenOptions.finalize(rand);
} }
void CMapGenerator::loadConfig()
{
static std::map<std::string, ETerrainType> 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<std::string, Res::ERes> 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<std::string, ERoadType::ERoadType> 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() void CMapGenerator::initTiles()
{ {
map->initTerrain(); map->initTerrain();
@ -309,8 +388,10 @@ void CMapGenerator::genZones()
void CMapGenerator::createWaterTreasures() void CMapGenerator::createWaterTreasures()
{ {
//add treasures on water //add treasures on water
getZoneWater().second->addTreasureInfo(CTreasureInfo{100, 1000, 5}); for(auto & treasureInfo : getConfig().waterTreasure)
getZoneWater().second->addTreasureInfo(CTreasureInfo{2000, 6000, 1}); {
getZoneWater().second->addTreasureInfo(treasureInfo);
}
} }
void CMapGenerator::prepareWaterTiles() void CMapGenerator::prepareWaterTiles()

View File

@ -52,11 +52,31 @@ public:
class DLL_LINKAGE CMapGenerator class DLL_LINKAGE CMapGenerator
{ {
public: public:
struct Config
{
std::vector<ETerrainType> terrainUndergroundAllowed;
std::vector<ETerrainType> terrainGroundProhibit;
std::vector<CTreasureInfo> waterTreasure;
int shipyardGuard;
int mineExtraResources;
std::map<Res::ERes, int> mineValues;
int minGuardStrength;
ERoadType::ERoadType defaultRoadType;
int treasureValueLimit;
std::vector<int> prisonExperience, prisonValues;
std::vector<int> scrollValues;
int pandoraMultiplierGold, pandoraMultiplierExperience, pandoraMultiplierSpells, pandoraSpellSchool, pandoraSpell60;
std::vector<int> pandoraCreatureValues;
std::vector<int> questValues, questRewardValues;
};
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>; using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>;
explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr)); explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
~CMapGenerator(); // required due to std::unique_ptr ~CMapGenerator(); // required due to std::unique_ptr
const Config & getConfig() const;
mutable std::unique_ptr<CMap> map; mutable std::unique_ptr<CMap> map;
CRandomGenerator rand; CRandomGenerator rand;
@ -111,6 +131,7 @@ public:
private: private:
int randomSeed; int randomSeed;
CMapGenOptions& mapGenOptions; CMapGenOptions& mapGenOptions;
Config config;
std::vector<rmg::ZoneConnection> connectionsLeft; std::vector<rmg::ZoneConnection> connectionsLeft;
Zones zones; Zones zones;
@ -129,6 +150,8 @@ private:
void checkIsOnMap(const int3 &tile) const; //throws void checkIsOnMap(const int3 &tile) const; //throws
/// Generation methods /// Generation methods
void loadConfig();
std::string getMapDescription() const; std::string getMapDescription() const;
void initPrisonsRemaining(); void initPrisonsRemaining();

View File

@ -10,6 +10,7 @@
#include "StdInc.h" #include "StdInc.h"
#include <vstd/ContainerUtils.h> #include <vstd/ContainerUtils.h>
#include <boost/bimap.hpp>
#include "CRmgTemplate.h" #include "CRmgTemplate.h"
#include "../mapping/CMap.h" #include "../mapping/CMap.h"
@ -359,7 +360,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
rawStrength = static_cast<decltype(rawStrength)>(zoneMonsterStrength); rawStrength = static_cast<decltype(rawStrength)>(zoneMonsterStrength);
rawStrength++; rawStrength++;
} }
handler.serializeEnum("monsters", rawStrength, STRENGTH); handler.serializeEnum("monsters", rawStrength, EMonsterStrength::ZONE_NORMAL + 1, STRENGTH);
if(!handler.saving) if(!handler.saving)
{ {
rawStrength--; rawStrength--;
@ -443,6 +444,11 @@ bool CRmgTemplate::isWaterContentAllowed(EWaterContent::EWaterContent waterConte
return waterContent == EWaterContent::EWaterContent::RANDOM || allowedWaterContent.count(waterContent); return waterContent == EWaterContent::EWaterContent::RANDOM || allowedWaterContent.count(waterContent);
} }
const std::set<EWaterContent::EWaterContent> & CRmgTemplate::getWaterContentAllowed() const
{
return allowedWaterContent;
}
void CRmgTemplate::setId(const std::string & value) void CRmgTemplate::setId(const std::string & value)
{ {
id = value; id = value;
@ -583,6 +589,32 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
auto connectionsData = handler.enterArray("connections"); auto connectionsData = handler.enterArray("connections");
connectionsData.serializeStruct(connections); connectionsData.serializeStruct(connections);
} }
{
boost::bimap<EWaterContent::EWaterContent, std::string> 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"); auto zonesData = handler.enterStruct("zones");

View File

@ -181,6 +181,7 @@ public:
bool matchesSize(const int3 & value) const; bool matchesSize(const int3 & value) const;
bool isWaterContentAllowed(EWaterContent::EWaterContent waterContent) const; bool isWaterContentAllowed(EWaterContent::EWaterContent waterContent) const;
const std::set<EWaterContent::EWaterContent> & getWaterContentAllowed() const;
void setId(const std::string & value); void setId(const std::string & value);
const std::string & getName() const; const std::string & getName() const;

View File

@ -641,7 +641,7 @@ void CRmgTemplateZone::waterConnection(CRmgTemplateZone& dst)
if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns) 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()) if(!coastTile.valid())
{ {
coastTile = makeBoat(dst.getId(), lake.tiles); coastTile = makeBoat(dst.getId(), lake.tiles);
@ -1266,7 +1266,7 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin
int strength2 = static_cast<int>(std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength])); int strength2 = static_cast<int>(std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]));
strength = strength1 + strength2; strength = strength1 + strength2;
if (strength < 2000) if (strength < gen->getConfig().minGuardStrength)
return false; //no guard at all return false; //no guard at all
CreatureID creId = CreatureID::NONE; CreatureID creId = CreatureID::NONE;
@ -1742,14 +1742,14 @@ void CRmgTemplateZone::initTerrainType ()
//TODO: allow new types of terrain? //TODO: allow new types of terrain?
{ {
if (isUnderground()) if(isUnderground())
{ {
if (terrainType != ETerrainType::LAVA) if(!vstd::contains(gen->getConfig().terrainUndergroundAllowed, terrainType))
terrainType = ETerrainType::SUBTERRANEAN; terrainType = ETerrainType::SUBTERRANEAN;
} }
else else
{ {
if (terrainType == ETerrainType::SUBTERRANEAN) if(vstd::contains(gen->getConfig().terrainGroundProhibit, terrainType))
terrainType = ETerrainType::DIRT; terrainType = ETerrainType::DIRT;
} }
} }
@ -1767,8 +1767,6 @@ void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType)
bool CRmgTemplateZone::placeMines () bool CRmgTemplateZone::placeMines ()
{ {
using namespace Res; using namespace Res;
static const std::map<ERes, int> mineValue{{ERes::WOOD, 1500}, {ERes::ORE, 1500}, {ERes::GEMS, 3500}, {ERes::CRYSTAL, 3500}, {ERes::MERCURY, 3500}, {ERes::SULFUR, 3500}, {ERes::GOLD, 7000}};
std::vector<CGMine*> createdMines; std::vector<CGMine*> createdMines;
for(const auto & mineInfo : mines) for(const auto & mineInfo : mines)
@ -1783,22 +1781,26 @@ bool CRmgTemplateZone::placeMines ()
createdMines.push_back(mine); createdMines.push_back(mine);
if(!i && (res == ERes::WOOD || res == ERes::ORE)) 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 else
addRequiredObject(mine, mineValue.at(res)); addRequiredObject(mine, gen->getConfig().mineValues.at(res));
} }
} }
//create extra resources //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()); for(int rc = gen->rand.nextInt(1, extraRes); rc > 0; --rc)
resourse->amount = CGResource::RANDOM_AMOUNT; {
addNearbyObject(resourse, mine); auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
resourse->amount = CGResource::RANDOM_AMOUNT;
addNearbyObject(resourse, mine);
}
} }
} }
return true; return true;
} }
@ -2072,69 +2074,62 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
shipyard->tempOwner = PlayerColor::NEUTRAL; shipyard->tempOwner = PlayerColor::NEUTRAL;
setTemplateForObject(shipyard); setTemplateForObject(shipyard);
std::vector<int3> offsets; std::vector<int3> outOffsets;
auto tilesBlockedByObject = shipyard->getBlockedOffsets(); auto tilesBlockedByObject = shipyard->getBlockedOffsets();
tilesBlockedByObject.insert(shipyard->getVisitableOffset()); tilesBlockedByObject.insert(shipyard->getVisitableOffset());
shipyard->getOutOffsets(offsets); shipyard->getOutOffsets(outOffsets);
int3 targetTile(-1, -1, -1); int3 targetTile(-1, -1, -1);
std::set<int3> shipAccessCandidates; std::set<int3> shipAccessCandidates;
for(auto& candidateTile : possibleTiles) for(const auto & outOffset : outOffsets)
{ {
bool foundTargetPosition = false; auto candidateTile = position - outOffset;
for(const auto & offset : offsets) std::set<int3> 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<int3> tilesBlockedAbsolute; allClear = false;
//check space under object break;
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
} }
} }
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; targetTile = candidateTile;
break; break; //no need to check other offsets as we already found position
} }
shipAccessCandidates.clear(); //invalidate positions shipAccessCandidates.clear(); //invalidate positions
@ -2146,19 +2141,24 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
return false; return false;
} }
placeObject(shipyard, targetTile); if(tryToPlaceObjectAndConnectToPath(shipyard, targetTile)==EObjectPlacingResult::SUCCESS)
guardObject(shipyard, guardStrength, false, true);
for(auto& accessPosition : shipAccessCandidates)
{ {
if(connectPath(accessPosition, false)) placeObject(shipyard, targetTile);
guardObject(shipyard, guardStrength, false, true);
for(auto& accessPosition : shipAccessCandidates)
{ {
gen->setOccupied(accessPosition, ETileType::FREE); if(connectPath(accessPosition, false))
return true; {
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() void CRmgTemplateZone::createTreasures()
@ -2206,7 +2206,12 @@ void CRmgTemplateZone::createTreasures()
//optimization - don't check tiles which are not allowed //optimization - don't check tiles which are not allowed
vstd::erase_if(possibleTiles, [this](const int3 &tile) -> bool 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()->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(); addAllPossibleObjects();
if(type==ETemplateZoneType::WATER) //zone center should be always clear to allow other tiles to connect
{ initFreeTiles();
initFreeTiles(); connectLater(); //ideally this should work after fractalize, but fails
connectLater(); fractalize();
createRequiredObjects(); placeMines();
fractalize(); createRequiredObjects();
createTreasures(); 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);
logGlobal->info("Zone %d filled successfully", id); logGlobal->info("Zone %d filled successfully", id);
return true; 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_ENTRANCE:
case Obj::MONOLITH_ONE_WAY_EXIT: case Obj::MONOLITH_ONE_WAY_EXIT:
case Obj::SUBTERRANEAN_GATE: case Obj::SUBTERRANEAN_GATE:
case Obj::SHIPYARD:
{ {
addRoadNode(object->visitablePos()); addRoadNode(object->visitablePos());
} }
@ -2831,11 +2824,11 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CTreasurePileInfo &info, ui32 desir
} }
} }
if (thresholds.empty()) if(thresholds.empty())
{ {
ObjectInfo oi; ObjectInfo oi;
//Generate pandora Box with gold if the value is extremely high //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 * oi.generateObject = [minValue]() -> CGObjectInstance *
{ {
@ -2913,17 +2906,15 @@ void CRmgTemplateZone::addAllPossibleObjects()
//prisons //prisons
//levels 1, 5, 10, 20, 30 //levels 1, 5, 10, 20, 30
static int prisonExp[] = { 0, 5000, 15000, 90000, 500000 }; static int prisonsLevels = std::min(gen->getConfig().prisonExperience.size(), gen->getConfig().prisonValues.size());
static int prisonValues[] = { 2500, 5000, 10000, 20000, 30000 }; for(int i = 0; i < prisonsLevels; i++)
for (int i = 0; i < 5; i++)
{ {
oi.generateObject = [i, this]() -> CGObjectInstance * oi.generateObject = [i, this]() -> CGObjectInstance *
{ {
std::vector<ui32> possibleHeroes; std::vector<ui32> 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); possibleHeroes.push_back(j);
} }
@ -2933,7 +2924,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
obj->subID = hid; //will be initialized later obj->subID = hid; //will be initialized later
obj->exp = prisonExp[i]; obj->exp = gen->getConfig().prisonExperience[i];
obj->setOwner(PlayerColor::NEUTRAL); obj->setOwner(PlayerColor::NEUTRAL);
gen->map->allowedHeroes[hid] = false; //ban this hero gen->map->allowedHeroes[hid] = false; //ban this hero
gen->decreasePrisonsRemaining(); gen->decreasePrisonsRemaining();
@ -2942,7 +2933,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PRISON, 0, terrainType); oi.setTemplate(Obj::PRISON, 0, terrainType);
oi.value = prisonValues[i]; oi.value = gen->getConfig().prisonValues[i];
oi.probability = 30; oi.probability = 30;
oi.maxPerZone = gen->getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos. oi.maxPerZone = gen->getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos.
possibleObjects.push_back(oi); 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 < gen->getConfig().scrollValues.size(); i++)
for (int i = 0; i < 5; i++)
{ {
oi.generateObject = [i, this]() -> CGObjectInstance * oi.generateObject = [i, this]() -> CGObjectInstance *
{ {
@ -3030,13 +3019,13 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::SPELL_SCROLL, 0, terrainType); oi.setTemplate(Obj::SPELL_SCROLL, 0, terrainType);
oi.value = scrollValues[i]; oi.value = gen->getConfig().scrollValues[i];
oi.probability = 30; oi.probability = 30;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
//pandora box with gold //pandora box with gold
for (int i = 1; i < 5; i++) for(int i = 1; i < 5; i++)
{ {
oi.generateObject = [i]() -> CGObjectInstance * oi.generateObject = [i]() -> CGObjectInstance *
{ {
@ -3046,7 +3035,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 5000; oi.value = i * gen->getConfig().pandoraMultiplierGold;
oi.probability = 5; oi.probability = 5;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
@ -3062,20 +3051,22 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = i * 6000; oi.value = i * gen->getConfig().pandoraMultiplierExperience;
oi.probability = 20; oi.probability = 20;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
//pandora box with creatures //pandora box with creatures
static const int tierValues[] = { 5000, 7000, 9000, 12000, 16000, 21000, 27000 }; const std::vector<int> & tierValues = gen->getConfig().pandoraCreatureValues;
auto creatureToCount = [](CCreature * creature) -> int auto creatureToCount = [&tierValues](CCreature * creature) -> int
{ {
if (!creature->AIValue) //bug #2681 if (!creature->AIValue) //bug #2681
return 0; //this box won't be generated 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; float creaturesAmount = ((float)tierValues[actualTier]) / creature->AIValue;
if (creaturesAmount <= 5) if (creaturesAmount <= 5)
{ {
@ -3142,7 +3133,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); 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; oi.probability = 2;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
@ -3171,7 +3162,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 15000; oi.value = gen->getConfig().pandoraSpellSchool;
oi.probability = 2; oi.probability = 2;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
@ -3199,7 +3190,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return obj; return obj;
}; };
oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType); oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
oi.value = 30000; oi.value = gen->getConfig().pandoraSpell60;
oi.probability = 2; oi.probability = 2;
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
@ -3240,7 +3231,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
return artInfo; 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]; auto creature = creatures[i];
int creaturesAmount = creatureToCount(creature); int creaturesAmount = creatureToCount(creature);
@ -3276,15 +3267,13 @@ void CRmgTemplateZone::addAllPossibleObjects()
possibleObjects.push_back(oi); possibleObjects.push_back(oi);
} }
static int seerExpGold[] = { 5000, 10000, 15000, 20000 }; static int seerLevels = std::min(gen->getConfig().questValues.size(), gen->getConfig().questRewardValues.size());
static int seerValues[] = { 2000, 5333, 8666, 12000 }; for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar
for (int i = 0; i < 4; i++) //seems that code for exp and gold reward is similiar
{ {
int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand); int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand);
oi.setTemplate(Obj::SEER_HUT, randomAppearance, terrainType); oi.setTemplate(Obj::SEER_HUT, randomAppearance, terrainType);
oi.value = seerValues[i]; oi.value = gen->getConfig().questValues[i];
oi.probability = 10; oi.probability = 10;
oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
@ -3294,7 +3283,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
obj->rewardType = CGSeerHut::EXPERIENCE; obj->rewardType = CGSeerHut::EXPERIENCE;
obj->rID = 0; //unitialized? obj->rID = 0; //unitialized?
obj->rVal = seerExpGold[i]; obj->rVal = gen->getConfig().questRewardValues[i];
obj->quest->missionType = CQuest::MISSION_ART; obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand); ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);
@ -3317,7 +3306,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->rewardType = CGSeerHut::RESOURCES; obj->rewardType = CGSeerHut::RESOURCES;
obj->rID = Res::GOLD; obj->rID = Res::GOLD;
obj->rVal = seerExpGold[i]; obj->rVal = gen->getConfig().questRewardValues[i];
obj->quest->missionType = CQuest::MISSION_ART; obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand); ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);