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:
parent
5c1a66ab69
commit
5054ee011a
59
config/randomMap.json
Normal file
59
config/randomMap.json
Normal 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]
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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<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()
|
||||
{
|
||||
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()
|
||||
|
@ -52,11 +52,31 @@ public:
|
||||
class DLL_LINKAGE CMapGenerator
|
||||
{
|
||||
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>>;
|
||||
|
||||
explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
|
||||
~CMapGenerator(); // required due to std::unique_ptr
|
||||
|
||||
const Config & getConfig() const;
|
||||
|
||||
mutable std::unique_ptr<CMap> map;
|
||||
CRandomGenerator rand;
|
||||
|
||||
@ -111,6 +131,7 @@ public:
|
||||
private:
|
||||
int randomSeed;
|
||||
CMapGenOptions& mapGenOptions;
|
||||
Config config;
|
||||
|
||||
std::vector<rmg::ZoneConnection> 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();
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include <vstd/ContainerUtils.h>
|
||||
#include <boost/bimap.hpp>
|
||||
#include "CRmgTemplate.h"
|
||||
|
||||
#include "../mapping/CMap.h"
|
||||
@ -359,7 +360,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
|
||||
rawStrength = static_cast<decltype(rawStrength)>(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<EWaterContent::EWaterContent> & CRmgTemplate::getWaterContentAllowed() const
|
||||
{
|
||||
return allowedWaterContent;
|
||||
}
|
||||
|
||||
void CRmgTemplate::setId(const std::string & value)
|
||||
{
|
||||
id = value;
|
||||
@ -584,6 +590,32 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
|
||||
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");
|
||||
if(handler.saving)
|
||||
|
@ -181,6 +181,7 @@ public:
|
||||
|
||||
bool matchesSize(const int3 & value) const;
|
||||
bool isWaterContentAllowed(EWaterContent::EWaterContent waterContent) const;
|
||||
const std::set<EWaterContent::EWaterContent> & getWaterContentAllowed() const;
|
||||
|
||||
void setId(const std::string & value);
|
||||
const std::string & getName() const;
|
||||
|
@ -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<int>(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<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;
|
||||
|
||||
for(const auto & mineInfo : mines)
|
||||
@ -1783,23 +1781,27 @@ 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<int3> offsets;
|
||||
std::vector<int3> outOffsets;
|
||||
auto tilesBlockedByObject = shipyard->getBlockedOffsets();
|
||||
tilesBlockedByObject.insert(shipyard->getVisitableOffset());
|
||||
shipyard->getOutOffsets(offsets);
|
||||
shipyard->getOutOffsets(outOffsets);
|
||||
|
||||
int3 targetTile(-1, -1, -1);
|
||||
std::set<int3> shipAccessCandidates;
|
||||
|
||||
for(auto& candidateTile : possibleTiles)
|
||||
for(const auto & outOffset : outOffsets)
|
||||
{
|
||||
bool foundTargetPosition = false;
|
||||
for(const auto & offset : offsets)
|
||||
auto candidateTile = position - outOffset;
|
||||
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;
|
||||
//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<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);
|
||||
}
|
||||
|
||||
@ -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<int> & 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);
|
||||
|
Loading…
Reference in New Issue
Block a user