mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Distribute limited objects evenly in zones with matching terrain
This commit is contained in:
parent
a88e1dc1f6
commit
18a87d1ec0
@ -101,6 +101,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/rmg/Zone.cpp
|
||||
${MAIN_LIB_DIR}/rmg/Functions.cpp
|
||||
${MAIN_LIB_DIR}/rmg/ObjectManager.cpp
|
||||
${MAIN_LIB_DIR}/rmg/ObjectDistributor.cpp
|
||||
${MAIN_LIB_DIR}/rmg/RoadPlacer.cpp
|
||||
${MAIN_LIB_DIR}/rmg/TreasurePlacer.cpp
|
||||
${MAIN_LIB_DIR}/rmg/RmgMap.cpp
|
||||
@ -338,6 +339,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/rmg/Zone.h
|
||||
${MAIN_LIB_DIR}/rmg/Functions.h
|
||||
${MAIN_LIB_DIR}/rmg/ObjectManager.h
|
||||
${MAIN_LIB_DIR}/rmg/ObjectDistributor.h
|
||||
${MAIN_LIB_DIR}/rmg/RoadPlacer.h
|
||||
${MAIN_LIB_DIR}/rmg/TreasurePlacer.h
|
||||
${MAIN_LIB_DIR}/rmg/RmgMap.h
|
||||
|
@ -508,7 +508,11 @@ void AObjectTypeHandler::init(const JsonNode & input)
|
||||
if (!input["rmg"].isNull())
|
||||
{
|
||||
rmgInfo.value = static_cast<ui32>(input["rmg"]["value"].Float());
|
||||
rmgInfo.mapLimit = loadJsonOrMax(input["rmg"]["mapLimit"]);
|
||||
|
||||
const JsonNode & mapLimit = input["rmg"]["mapLimit"];
|
||||
if (!mapLimit.isNull())
|
||||
rmgInfo.mapLimit.reset(static_cast<ui32>(mapLimit.Float()));
|
||||
|
||||
rmgInfo.zoneLimit = loadJsonOrMax(input["rmg"]["zoneLimit"]);
|
||||
rmgInfo.rarity = static_cast<ui32>(input["rmg"]["rarity"].Float());
|
||||
} // else block is not needed - set in constructor
|
||||
|
@ -43,7 +43,7 @@ struct DLL_LINKAGE RandomMapInfo
|
||||
ui32 value;
|
||||
|
||||
/// How many of such objects can be placed on map, 0 = object can not be placed by RMG
|
||||
ui32 mapLimit;
|
||||
boost::optional<ui32> mapLimit;
|
||||
|
||||
/// How many of such objects can be placed in one zone, 0 = unplaceable
|
||||
ui32 zoneLimit;
|
||||
@ -53,11 +53,12 @@ struct DLL_LINKAGE RandomMapInfo
|
||||
|
||||
RandomMapInfo():
|
||||
value(0),
|
||||
mapLimit(0),
|
||||
zoneLimit(0),
|
||||
rarity(0)
|
||||
{}
|
||||
|
||||
void setMapLimit(ui32 val) { mapLimit.reset(val); }
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & value;
|
||||
|
116
lib/rmg/ObjectDistributor.cpp
Normal file
116
lib/rmg/ObjectDistributor.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* ObjectDistributor.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "ObjectDistributor.h"
|
||||
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "RmgMap.h"
|
||||
#include "CMapGenerator.h"
|
||||
#include "TreasurePlacer.h"
|
||||
#include "TownPlacer.h"
|
||||
#include "TerrainPainter.h"
|
||||
#include "../mapObjects/CObjectClassesHandler.h"
|
||||
#include "../mapObjects/MapObjects.h"
|
||||
#include "Functions.h"
|
||||
#include "RmgObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
void ObjectDistributor::process()
|
||||
{
|
||||
//Firts call will add objects to ALL zones, once they were added skip it
|
||||
if (zone.getModificator<TreasurePlacer>()->getPossibleObjectsSize() == 0)
|
||||
{
|
||||
ObjectDistributor::distributeLimitedObjects();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectDistributor::init()
|
||||
{
|
||||
DEPENDENCY(TownPlacer);
|
||||
DEPENDENCY(TerrainPainter);
|
||||
POSTFUNCTION(TreasurePlacer);
|
||||
}
|
||||
|
||||
void ObjectDistributor::distributeLimitedObjects()
|
||||
{
|
||||
//FIXME: Must be called after TerrainPainter::process()
|
||||
|
||||
ObjectInfo oi;
|
||||
auto zones = map.getZones();
|
||||
|
||||
for (auto primaryID : VLC->objtypeh->knownObjects())
|
||||
{
|
||||
for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
|
||||
if (!handler->isStaticObject() && handler->getRMGInfo().value)
|
||||
{
|
||||
auto rmgInfo = handler->getRMGInfo();
|
||||
|
||||
//Skip objects which don't have global per-map limit here
|
||||
if (rmgInfo.mapLimit)
|
||||
{
|
||||
|
||||
//Count all zones where this object can be placed
|
||||
std::vector<std::shared_ptr<Zone>> matchingZones;
|
||||
|
||||
//TODO: Are all terrains initialized at this point? Including water?
|
||||
for (const auto& it : zones)
|
||||
{
|
||||
if (!handler->getTemplates(it.second->getTerrainType()).empty())
|
||||
{
|
||||
matchingZones.push_back(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Also check if the object value is within zone max value
|
||||
|
||||
size_t numZones = matchingZones.size();
|
||||
if (!numZones)
|
||||
continue;
|
||||
|
||||
auto rmgInfo = handler->getRMGInfo();
|
||||
|
||||
for (auto& zone : matchingZones)
|
||||
{
|
||||
auto temp = handler->getTemplates(zone->getTerrainType()).front();
|
||||
oi.generateObject = [temp]() -> CGObjectInstance *
|
||||
{
|
||||
return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
|
||||
};
|
||||
|
||||
oi.value = rmgInfo.value;
|
||||
oi.probability = rmgInfo.rarity;
|
||||
oi.templ = temp;
|
||||
|
||||
//Rounding up will make sure all possible objects are exhausted
|
||||
uint32_t mapLimit = rmgInfo.mapLimit.get();
|
||||
uint32_t maxPerZone = std::ceil(float(mapLimit) / numZones);
|
||||
|
||||
//But not more than zone limit
|
||||
oi.maxPerZone = std::min(maxPerZone, rmgInfo.zoneLimit);
|
||||
numZones--;
|
||||
|
||||
rmgInfo.setMapLimit(mapLimit - oi.maxPerZone);
|
||||
//Don't add objects with 0 count remaining
|
||||
if (oi.maxPerZone)
|
||||
{
|
||||
zone->getModificator<TreasurePlacer>()->addObjectToRandomPool(oi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
32
lib/rmg/ObjectDistributor.h
Normal file
32
lib/rmg/ObjectDistributor.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ObjectDistributor.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Zone.h"
|
||||
#include "RmgObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGObjectInstance;
|
||||
class ObjectTemplate;
|
||||
|
||||
class ObjectDistributor : public Modificator
|
||||
{
|
||||
void distributeLimitedObjects();
|
||||
|
||||
public:
|
||||
MODIFICATOR(ObjectDistributor);
|
||||
|
||||
void process() override;
|
||||
void init() override;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -21,6 +21,7 @@
|
||||
#include "TreasurePlacer.h"
|
||||
#include "ConnectionsPlacer.h"
|
||||
#include "TownPlacer.h"
|
||||
#include "ObjectDistributor.h"
|
||||
#include "WaterAdopter.h"
|
||||
#include "WaterProxy.h"
|
||||
#include "WaterRoutes.h"
|
||||
@ -118,6 +119,7 @@ void RmgMap::addModificators()
|
||||
auto zone = z.second;
|
||||
|
||||
zone->addModificator<ObjectManager>();
|
||||
zone->addModificator<ObjectDistributor>();
|
||||
zone->addModificator<TreasurePlacer>();
|
||||
zone->addModificator<ObstaclePlacer>();
|
||||
zone->addModificator<TerrainPainter>();
|
||||
|
@ -46,6 +46,11 @@ void TreasurePlacer::setQuestArtZone(Zone * otherZone)
|
||||
questArtZone = otherZone;
|
||||
}
|
||||
|
||||
void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
|
||||
{
|
||||
possibleObjects.push_back(oi);
|
||||
}
|
||||
|
||||
void TreasurePlacer::addAllPossibleObjects()
|
||||
{
|
||||
ObjectInfo oi;
|
||||
@ -59,6 +64,14 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
|
||||
if(!handler->isStaticObject() && handler->getRMGInfo().value)
|
||||
{
|
||||
auto rmgInfo = handler->getRMGInfo();
|
||||
if (rmgInfo.mapLimit)
|
||||
{
|
||||
//Skip objects with per-map limit here
|
||||
continue;
|
||||
}
|
||||
|
||||
//TODO: Also check if the object value is within zone max value
|
||||
for(const auto & temp : handler->getTemplates())
|
||||
{
|
||||
if(temp->canBePlacedAt(zone.getTerrainType()))
|
||||
@ -67,15 +80,12 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
{
|
||||
return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp);
|
||||
};
|
||||
auto rmgInfo = handler->getRMGInfo();
|
||||
oi.value = rmgInfo.value;
|
||||
oi.probability = rmgInfo.rarity;
|
||||
oi.templ = temp;
|
||||
oi.maxPerZone = rmgInfo.zoneLimit;
|
||||
vstd::amin(oi.maxPerZone, rmgInfo.mapLimit / numZones); //simple, but should distribute objects evenly on large maps
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
|
||||
break; //Place only one template per zone
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +126,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.value = generator.getConfig().prisonValues[i];
|
||||
oi.probability = 30;
|
||||
oi.maxPerZone = generator.getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos.
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//all following objects are unlimited
|
||||
@ -172,7 +182,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
};
|
||||
|
||||
oi.templ = tmplate;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,7 +211,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::SPELL_SCROLL, 0, zone.getTerrainType());
|
||||
oi.value = generator.getConfig().scrollValues[i];
|
||||
oi.probability = 30;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//pandora box with gold
|
||||
@ -217,7 +227,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = i * generator.getConfig().pandoraMultiplierGold;
|
||||
oi.probability = 5;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//pandora box with experience
|
||||
@ -233,7 +243,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = i * generator.getConfig().pandoraMultiplierExperience;
|
||||
oi.probability = 20;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//pandora box with creatures
|
||||
@ -286,7 +296,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = static_cast<ui32>((2 * (creature->AIValue) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) / 3);
|
||||
oi.probability = 3;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//Pandora with 12 spells of certain level
|
||||
@ -315,7 +325,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = (i + 1) * generator.getConfig().pandoraMultiplierSpells; //5000 - 15000
|
||||
oi.probability = 2;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
//Pandora with 15 spells of certain school
|
||||
@ -344,7 +354,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = generator.getConfig().pandoraSpellSchool;
|
||||
oi.probability = 2;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
// Pandora box with 60 random spells
|
||||
@ -372,7 +382,7 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
|
||||
oi.value = generator.getConfig().pandoraSpell60;
|
||||
oi.probability = 2;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
|
||||
//seer huts with creatures or generic rewards
|
||||
|
||||
@ -438,14 +448,14 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
generator.banQuestArt(artid);
|
||||
|
||||
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
|
||||
|
||||
return obj;
|
||||
};
|
||||
oi.setTemplate(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
|
||||
oi.value = static_cast<ui32>(((2 * (creature->AIValue) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->faction)) / map.getTotalZoneCount())) - 4000) / 3);
|
||||
oi.probability = 3;
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
|
||||
static int seerLevels = std::min(generator.getConfig().questValues.size(), generator.getConfig().questRewardValues.size());
|
||||
@ -474,12 +484,12 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
|
||||
generator.banQuestArt(artid);
|
||||
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
|
||||
oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
|
||||
{
|
||||
@ -497,16 +507,21 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
|
||||
generator.banQuestArt(artid);
|
||||
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->possibleObjects.push_back(generateArtInfo(artid));
|
||||
this->questArtZone->getModificator<TreasurePlacer>()->addObjectToRandomPool(generateArtInfo(artid));
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
possibleObjects.push_back(oi);
|
||||
addObjectToRandomPool(oi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t TreasurePlacer::getPossibleObjectsSize() const
|
||||
{
|
||||
return possibleObjects.size();
|
||||
}
|
||||
|
||||
bool TreasurePlacer::isGuardNeededForTreasure(int value)
|
||||
{
|
||||
return zone.getType() != ETemplateZoneType::WATER && value > minGuardedValue;
|
||||
|
@ -45,8 +45,11 @@ public:
|
||||
void createTreasures(ObjectManager & manager);
|
||||
|
||||
void setQuestArtZone(Zone * otherZone);
|
||||
void addObjectToRandomPool(const ObjectInfo& oi);
|
||||
void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects
|
||||
|
||||
size_t getPossibleObjectsSize() const;
|
||||
|
||||
protected:
|
||||
bool isGuardNeededForTreasure(int value);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user