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
|
"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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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()
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
@ -584,6 +590,32 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
|
|||||||
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");
|
||||||
if(handler.saving)
|
if(handler.saving)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
@ -1744,12 +1744,12 @@ void CRmgTemplateZone::initTerrainType ()
|
|||||||
{
|
{
|
||||||
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
|
||||||
|
if(int extraRes = gen->getConfig().mineExtraResources)
|
||||||
|
{
|
||||||
for(auto * mine : createdMines)
|
for(auto * mine : createdMines)
|
||||||
{
|
{
|
||||||
for(int rc = gen->rand.nextInt(1, 3); rc > 0; --rc)
|
for(int rc = gen->rand.nextInt(1, extraRes); rc > 0; --rc)
|
||||||
{
|
{
|
||||||
auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
|
auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
|
||||||
resourse->amount = CGResource::RANDOM_AMOUNT;
|
resourse->amount = CGResource::RANDOM_AMOUNT;
|
||||||
addNearbyObject(resourse, mine);
|
addNearbyObject(resourse, mine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2072,22 +2074,19 @@ 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;
|
|
||||||
for(const auto & offset : offsets)
|
|
||||||
{
|
|
||||||
if(candidateTile+offset == position)
|
|
||||||
{
|
{
|
||||||
|
auto candidateTile = position - outOffset;
|
||||||
std::set<int3> tilesBlockedAbsolute;
|
std::set<int3> tilesBlockedAbsolute;
|
||||||
|
|
||||||
//check space under object
|
//check space under object
|
||||||
bool allClear = true;
|
bool allClear = true;
|
||||||
for(const auto & objectTileOffset : tilesBlockedByObject)
|
for(const auto & objectTileOffset : tilesBlockedByObject)
|
||||||
@ -2101,40 +2100,36 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!allClear) //cannot place shipyard anyway
|
if(!allClear) //cannot place shipyard anyway
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
//prepare temporary map
|
//prepare temporary map
|
||||||
for(auto& blockedPos : tilesBlockedAbsolute)
|
for(auto& blockedPos : tilesBlockedAbsolute)
|
||||||
gen->setOccupied(blockedPos, ETileType::USED);
|
gen->setOccupied(blockedPos, ETileType::USED);
|
||||||
|
|
||||||
//check if position is accessible
|
|
||||||
|
//check if boarding position is accessible
|
||||||
gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v)
|
gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v)
|
||||||
{
|
{
|
||||||
if(!gen->isBlocked(v) && gen->getZoneID(v)==id)
|
if(!gen->isBlocked(v) && gen->getZoneID(v)==id)
|
||||||
{
|
{
|
||||||
//make sure that it's possible to create path to boarding position
|
//make sure that it's possible to create path to boarding position
|
||||||
if(crunchPath(v, findClosestTile(freePaths, v), false, nullptr))
|
if(connectWithCenter(v, false, false))
|
||||||
shipAccessCandidates.insert(v);
|
shipAccessCandidates.insert(v);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//check if we can connect shipyard entrance with path
|
||||||
|
if(!connectWithCenter(candidateTile + shipyard->getVisitableOffset(), false))
|
||||||
|
shipAccessCandidates.clear();
|
||||||
|
|
||||||
//rollback temporary map
|
//rollback temporary map
|
||||||
for(auto& blockedPos : tilesBlockedAbsolute)
|
for(auto& blockedPos : tilesBlockedAbsolute)
|
||||||
gen->setOccupied(blockedPos, ETileType::POSSIBLE);
|
gen->setOccupied(blockedPos, ETileType::POSSIBLE);
|
||||||
|
|
||||||
if(!shipAccessCandidates.empty())
|
if(!shipAccessCandidates.empty() && isAccessibleFromSomewhere(shipyard->appearance, candidateTile))
|
||||||
{
|
|
||||||
foundTargetPosition = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break; //no need to check other offsets as we already found position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(foundTargetPosition && 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,6 +2141,8 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(tryToPlaceObjectAndConnectToPath(shipyard, targetTile)==EObjectPlacingResult::SUCCESS)
|
||||||
|
{
|
||||||
placeObject(shipyard, targetTile);
|
placeObject(shipyard, targetTile);
|
||||||
guardObject(shipyard, guardStrength, false, true);
|
guardObject(shipyard, guardStrength, false, true);
|
||||||
|
|
||||||
@ -2157,8 +2154,11 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
|
|||||||
return true;
|
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,16 +2391,6 @@ bool CRmgTemplateZone::fill()
|
|||||||
|
|
||||||
addAllPossibleObjects();
|
addAllPossibleObjects();
|
||||||
|
|
||||||
if(type==ETemplateZoneType::WATER)
|
|
||||||
{
|
|
||||||
initFreeTiles();
|
|
||||||
connectLater();
|
|
||||||
createRequiredObjects();
|
|
||||||
fractalize();
|
|
||||||
createTreasures();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//zone center should be always clear to allow other tiles to connect
|
//zone center should be always clear to allow other tiles to connect
|
||||||
initFreeTiles();
|
initFreeTiles();
|
||||||
connectLater(); //ideally this should work after fractalize, but fails
|
connectLater(); //ideally this should work after fractalize, but fails
|
||||||
@ -2403,9 +2398,6 @@ bool CRmgTemplateZone::fill()
|
|||||||
placeMines();
|
placeMines();
|
||||||
createRequiredObjects();
|
createRequiredObjects();
|
||||||
createTreasures();
|
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());
|
||||||
}
|
}
|
||||||
@ -2835,7 +2828,7 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CTreasurePileInfo &info, ui32 desir
|
|||||||
{
|
{
|
||||||
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,10 +2906,8 @@ 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 *
|
||||||
{
|
{
|
||||||
@ -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,7 +3019,7 @@ 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);
|
||||||
}
|
}
|
||||||
@ -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);
|
||||||
|
|
||||||
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user