1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

First version that works - banning objects is possible

This commit is contained in:
Tomasz Zieliński 2024-08-21 20:16:41 +02:00
parent 7fe9c473b4
commit 85ee859b6e
9 changed files with 612 additions and 104 deletions

View File

@ -181,6 +181,7 @@ set(lib_MAIN_SRCS
rmg/TileInfo.cpp
rmg/Zone.cpp
rmg/Functions.cpp
rmg/ObjectInfo.cpp
rmg/RmgMap.cpp
rmg/PenroseTiling.cpp
rmg/modificators/Modificator.cpp
@ -583,6 +584,7 @@ set(lib_MAIN_HEADERS
rmg/RmgMap.h
rmg/float3.h
rmg/Functions.h
rmg/ObjectInfo.h
rmg/PenroseTiling.h
rmg/modificators/Modificator.h
rmg/modificators/ObjectManager.h

View File

@ -382,6 +382,78 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(CompoundMapObjectID comp
return getHandlerFor(compoundIdentifier.primaryID, compoundIdentifier.secondaryID);
}
CompoundMapObjectID CObjectClassesHandler::getCompoundIdentifier(const std::string & scope, const std::string & type, const std::string & subtype) const
{
std::optional<si32> id;
if (scope.empty())
{
id = VLC->identifiers()->getIdentifier("object", type);
}
else
{
id = VLC->identifiers()->getIdentifier(scope, "object", type);
}
if(id)
{
const auto & object = objects.at(id.value());
std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
if (subID)
return CompoundMapObjectID(id.value(), subID.value());
}
std::string errorString = "Failed to get id for object of type " + type + "." + subtype;
logGlobal->error(errorString);
throw std::runtime_error(errorString);
}
CompoundMapObjectID CObjectClassesHandler::getCompoundIdentifier(const std::string & objectName) const
{
// FIXME: Crash with no further log
//"core:object.creatureBank.experimentalShop",
//"core:object.creatureBank.wolfRiderPicket",
//"core:object.creatureBank.demonDomain"
// TODO: Use existing utilities for parsing id?
JsonNode node(objectName);
auto modScope = node.getModScope();
std::string scope, type, subtype;
size_t firstColon = objectName.find(':');
size_t lastDot = objectName.find_last_of('.');
// TODO: Ignore object class, there should not be objects with same names within one scope anyway
if(firstColon != std::string::npos)
{
scope = objectName.substr(0, firstColon);
if(lastDot != std::string::npos && lastDot > firstColon)
{
type = objectName.substr(firstColon + 1, lastDot - firstColon - 1);
subtype = objectName.substr(lastDot + 1);
}
else
{
type = objectName.substr(firstColon + 1);
}
}
else
{
if(lastDot != std::string::npos)
{
type = objectName.substr(0, lastDot);
subtype = objectName.substr(lastDot + 1);
}
else
{
type = objectName;
}
}
return getCompoundIdentifier(scope, type, subtype);
}
std::set<MapObjectID> CObjectClassesHandler::knownObjects() const
{
std::set<MapObjectID> ret;
@ -451,6 +523,18 @@ void CObjectClassesHandler::afterLoadFinalization()
logGlobal->warn("No templates found for %s:%s", entry->getJsonKey(), obj->getJsonKey());
}
}
for(auto & entry : objectIdHandlers)
{
// Call function for each object id
entry.second(entry.first);
}
}
void CObjectClassesHandler::resolveObjectCompoundId(const std::string & id, std::function<void(CompoundMapObjectID)> callback)
{
auto compoundId = getCompoundIdentifier(id);
objectIdHandlers.push_back(std::make_pair(compoundId, callback));
}
void CObjectClassesHandler::generateExtraMonolithsForRMG(ObjectClass * container)

View File

@ -74,6 +74,8 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase, boost::noncopyabl
/// map that is filled during construction with all known handlers. Not serializeable due to usage of std::function
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
std::vector<std::pair<CompoundMapObjectID, std::function<void(CompoundMapObjectID)>>> objectIdHandlers;
/// container with H3 templates, used only during loading, no need to serialize it
using TTemplatesContainer = std::multimap<std::pair<MapObjectID, MapObjectSubID>, std::shared_ptr<const ObjectTemplate>>;
TTemplatesContainer legacyTemplates;
@ -110,15 +112,19 @@ public:
TObjectTypeHandler getHandlerFor(MapObjectID type, MapObjectSubID subtype) const;
TObjectTypeHandler getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const;
TObjectTypeHandler getHandlerFor(CompoundMapObjectID compoundIdentifier) const;
CompoundMapObjectID getCompoundIdentifier(const std::string & scope, const std::string & type, const std::string & subtype) const;
CompoundMapObjectID getCompoundIdentifier(const std::string & objectName) const;
std::string getObjectName(MapObjectID type, MapObjectSubID subtype) const;
SObjectSounds getObjectSounds(MapObjectID type, MapObjectSubID subtype) const;
void resolveObjectCompoundId(const std::string & id, std::function<void(CompoundMapObjectID)> callback);
/// Returns handler string describing the handler (for use in client)
std::string getObjectHandlerName(MapObjectID type) const;
std::string getJsonKey(MapObjectID type) const;
};
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@ -102,6 +102,7 @@ void ZoneOptions::CTownInfo::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("castles", castleCount, 0);
handler.serializeInt("townDensity", townDensity, 0);
handler.serializeInt("castleDensity", castleDensity, 0);
handler.serializeInt("sourceZone", sourceZone, NO_ZONE);
}
ZoneOptions::ZoneOptions():
@ -398,6 +399,8 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], mines[idx], 0);
}
}
handler.serializeStruct("customObjects", objectConfig);
}
ZoneConnection::ZoneConnection():
@ -837,6 +840,7 @@ void CRmgTemplate::afterLoad()
allowedWaterContent.erase(EWaterContent::RANDOM);
}
// TODO: Allow any integer size which does not match enum, as well
void CRmgTemplate::serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName)
{
static const std::map<std::string, int3> sizeMapping =
@ -905,5 +909,87 @@ void CRmgTemplate::serializePlayers(JsonSerializeFormat & handler, CPlayerCountR
value.fromString(encodedValue);
}
void ZoneOptions::ObjectConfig::addBannedObject(const CompoundMapObjectID & objid)
{
// FIXME: We do not need to store the object info, just the id
bannedObjects.push_back(objid);
logGlobal->info("Banned object of type %d.%d", objid.primaryID, objid.secondaryID);
}
void ZoneOptions::ObjectConfig::serializeJson(JsonSerializeFormat & handler)
{
// TODO: Implement'
auto bannedObjectData = handler.enterArray("bannedObjects");
if (handler.saving)
{
// FIXME: Do we even need to serialize / store banned objects?
/*
for (const auto & object : bannedObjects)
{
// TODO: Translate id back to string?
JsonNode node;
node.String() = VLC->objtypeh->getHandlerFor(object.primaryID, object.secondaryID);
// TODO: Check if AI-generated code is right
}
// handler.serializeRaw("bannedObjects", node, std::nullopt);
*/
}
else
{
/*
auto zonesData = handler.enterStruct("zones");
for(const auto & idAndZone : zonesData->getCurrent().Struct())
{
auto guard = handler.enterStruct(idAndZone.first);
auto zone = std::make_shared<ZoneOptions>();
zone->setId(decodeZoneId(idAndZone.first));
zone->serializeJson(handler);
zones[zone->getId()] = zone;
}
*/
std::vector<std::string> objectNames;
bannedObjectData.serializeArray(objectNames);
for (const auto & objectName : objectNames)
{
VLC->objtypeh->resolveObjectCompoundId(objectName,
[this](CompoundMapObjectID objid)
{
addBannedObject(objid);
}
);
}
}
}
const std::vector<CompoundMapObjectID> & ZoneOptions::getBannedObjects() const
{
return objectConfig.getBannedObjects();
}
const std::vector<ObjectInfo> & ZoneOptions::getCustomObjects() const
{
return objectConfig.getCustomObjects();
}
const std::vector<CompoundMapObjectID> & ZoneOptions::ObjectConfig::getBannedObjects() const
{
return bannedObjects;
}
const std::vector<ObjectInfo> & ZoneOptions::ObjectConfig::getCustomObjects() const
{
return customObjects;
}
VCMI_LIB_NAMESPACE_END

View File

@ -13,10 +13,13 @@
#include "../int3.h"
#include "../GameConstants.h"
#include "../ResourceSet.h"
#include "ObjectInfo.h"
#include "../mapObjectConstructors/CObjectClassesHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class JsonSerializeFormat;
struct CompoundMapObjectID;
enum class ETemplateZoneType
{
@ -131,8 +134,37 @@ public:
int castleCount;
int townDensity;
int castleDensity;
// TODO: Copy from another zone once its randomized
TRmgTemplateZoneId sourceZone = NO_ZONE;
};
// TODO: Store config for custom objects to spawn in this zone
// TODO: Read custom object config from zone file
class DLL_LINKAGE ObjectConfig
{
public:
//ObjectConfig() = default;
void addBannedObject(const CompoundMapObjectID & objid);
void addCustomObject(const ObjectInfo & object);
void clearBannedObjects();
void clearCustomObjects();
const std::vector<CompoundMapObjectID> & getBannedObjects() const;
const std::vector<ObjectInfo> & getCustomObjects() const;
// TODO: Separate serializer
void serializeJson(JsonSerializeFormat & handler);
private:
// TODO: Add convenience method for banning objects by name
std::vector<CompoundMapObjectID> bannedObjects;
// TODO: In what format should I store custom objects?
// Need to convert map serialization format to ObjectInfo
std::vector<ObjectInfo> customObjects;
};
// TODO: Allow to copy all custom objects config from another zone
ZoneOptions();
TRmgTemplateZoneId getId() const;
@ -182,12 +214,17 @@ public:
bool areTownsSameType() const;
bool isMatchTerrainToTown() const;
const std::vector<CompoundMapObjectID> & getBannedObjects() const;
const std::vector<ObjectInfo> & getCustomObjects() const;
protected:
TRmgTemplateZoneId id;
ETemplateZoneType type;
int size;
ui32 maxTreasureValue;
std::optional<int> owner;
ObjectConfig objectConfig;
CTownInfo playerTowns;
CTownInfo neutralTowns;
bool matchTerrainToTown;
@ -276,6 +313,10 @@ private:
std::set<TerrainId> inheritTerrainType(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
std::map<TResource, ui16> inheritMineTypes(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
std::vector<CTreasureInfo> inheritTreasureInfo(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
// TODO: Copy custom object settings
// TODO: Copy town type after source town is actually randomized
void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName);
void serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & value, const std::string & fieldName);
};

71
lib/rmg/ObjectInfo.cpp Normal file
View File

@ -0,0 +1,71 @@
/*
* ObjectInfo.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 "ObjectInfo.h"
#include "../VCMI_Lib.h"
#include "../mapObjectConstructors/CObjectClassesHandler.h"
#include "../mapObjectConstructors/AObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
ObjectInfo::ObjectInfo():
destroyObject([](CGObjectInstance * obj){}),
maxPerZone(std::numeric_limits<ui32>::max())
{
}
ObjectInfo::ObjectInfo(const ObjectInfo & other)
{
templates = other.templates;
value = other.value;
probability = other.probability;
maxPerZone = other.maxPerZone;
generateObject = other.generateObject;
destroyObject = other.destroyObject;
}
ObjectInfo & ObjectInfo::operator=(const ObjectInfo & other)
{
if (this == &other)
return *this;
templates = other.templates;
value = other.value;
probability = other.probability;
maxPerZone = other.maxPerZone;
generateObject = other.generateObject;
destroyObject = other.destroyObject;
return *this;
}
void ObjectInfo::setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrainType)
{
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
if(!templHandler)
return;
templates = templHandler->getTemplates(terrainType);
}
/*
bool ObjectInfo::matchesId(const CompoundMapObjectID & id) const
{
for (const auto & templ : templates)
{
if (id.primaryID == templ->id && id.secondaryID == templ->subid)
return true;
}
return false;
}
*/
VCMI_LIB_NAMESPACE_END

40
lib/rmg/ObjectInfo.h Normal file
View File

@ -0,0 +1,40 @@
/*
* ObjectInfo.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 "../mapObjects/ObjectTemplate.h"
#include "../constants/EntityIdentifiers.h"
VCMI_LIB_NAMESPACE_BEGIN
struct CompoundMapObjectID;
class CGObjectInstance;
struct DLL_LINKAGE ObjectInfo
{
ObjectInfo();
ObjectInfo(const ObjectInfo & other);
ObjectInfo & operator=(const ObjectInfo & other);
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
ui32 value = 0;
ui16 probability = 0;
ui32 maxPerZone = 1;
//ui32 maxPerMap; //unused
std::function<CGObjectInstance *()> generateObject;
std::function<void(CGObjectInstance *)> destroyObject;
void setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrain);
//bool matchesId(const CompoundMapObjectID & id) const;
};
VCMI_LIB_NAMESPACE_END

View File

@ -37,15 +37,19 @@
VCMI_LIB_NAMESPACE_BEGIN
ObjectInfo::ObjectInfo():
destroyObject([](CGObjectInstance * obj){})
{
}
void TreasurePlacer::process()
{
if (zone.getMaxTreasureValue() == 0)
{
//No treasures at all
return;
}
// Get default objects
addAllPossibleObjects();
// Override with custom objects
objects.patchWithZoneConfig(zone);
auto * m = zone.getModificator<ObjectManager>();
if(m)
createTreasures(*m);
@ -58,15 +62,52 @@ void TreasurePlacer::init()
DEPENDENCY(ConnectionsPlacer);
DEPENDENCY_ALL(PrisonHeroPlacer);
DEPENDENCY(RoadPlacer);
// Add all native creatures
for(auto const & cre : VLC->creh->objects)
{
if(!cre->special && cre->getFaction() == zone.getTownType())
{
creatures.push_back(cre.get());
}
}
tierValues = generator.getConfig().pandoraCreatureValues;
}
void TreasurePlacer::addObjectToRandomPool(const ObjectInfo& oi)
{
// FIXME: It is never the case - objects must be erased or badly copied after this
if (oi.templates.empty())
{
logGlobal->error("Attempt to add ObjectInfo with no templates! Value: %d", oi.value);
return;
}
if (!oi.generateObject)
{
logGlobal->error("Attempt to add ObjectInfo with no generateObject function! Value: %d", oi.value);
return;
}
if (!oi.maxPerZone)
{
logGlobal->warn("Attempt to add ObjectInfo with 0 maxPerZone! Value: %d", oi.value);
return;
}
RecursiveLock lock(externalAccessMutex);
possibleObjects.push_back(oi);
objects.addObject(oi);
}
void TreasurePlacer::addAllPossibleObjects()
{
addCommonObjects();
addDwellings();
addPandoraBoxes();
addSeerHuts();
addPrisons();
addScrolls();
}
void TreasurePlacer::addCommonObjects()
{
ObjectInfo oi;
@ -97,7 +138,10 @@ void TreasurePlacer::addAllPossibleObjects()
}
}
}
}
void TreasurePlacer::addPrisons()
{
//Generate Prison on water only if it has a template
auto prisonTemplates = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0)->getTemplates(zone.getTerrainType());
if (!prisonTemplates.empty())
@ -157,21 +201,14 @@ void TreasurePlacer::addAllPossibleObjects()
addObjectToRandomPool(oi);
}
}
}
void TreasurePlacer::addDwellings()
{
if(zone.getType() == ETemplateZoneType::WATER)
return;
//all following objects are unlimited
oi.maxPerZone = std::numeric_limits<ui32>::max();
std::vector<const CCreature *> creatures; //native creatures for this zone
for(auto const & cre : VLC->creh->objects)
{
if(!cre->special && cre->getFaction() == zone.getTownType())
{
creatures.push_back(cre.get());
}
}
ObjectInfo oi;
//dwellings
auto dwellingTypes = {Obj::CREATURE_GENERATOR1, Obj::CREATURE_GENERATOR4};
@ -214,7 +251,15 @@ void TreasurePlacer::addAllPossibleObjects()
}
}
}
}
void TreasurePlacer::addScrolls()
{
if(zone.getType() == ETemplateZoneType::WATER)
return;
ObjectInfo oi;
for(int i = 0; i < generator.getConfig().scrollValues.size(); i++)
{
oi.generateObject = [i, this]() -> CGObjectInstance *
@ -239,6 +284,22 @@ void TreasurePlacer::addAllPossibleObjects()
addObjectToRandomPool(oi);
}
}
void TreasurePlacer::addPandoraBoxes()
{
if(zone.getType() == ETemplateZoneType::WATER)
return;
addPandoraBoxesWithGold();
addPandoraBoxesWithExperience();
addPandoraBoxesWithCreatures();
addPandoraBoxesWithSpells();
}
void TreasurePlacer::addPandoraBoxesWithGold()
{
ObjectInfo oi;
//pandora box with gold
for(int i = 1; i < 5; i++)
{
@ -260,7 +321,11 @@ void TreasurePlacer::addAllPossibleObjects()
if(!oi.templates.empty())
addObjectToRandomPool(oi);
}
}
void TreasurePlacer::addPandoraBoxesWithExperience()
{
ObjectInfo oi;
//pandora box with experience
for(int i = 1; i < 5; i++)
{
@ -282,43 +347,12 @@ void TreasurePlacer::addAllPossibleObjects()
if(!oi.templates.empty())
addObjectToRandomPool(oi);
}
//pandora box with creatures
const std::vector<int> & tierValues = generator.getConfig().pandoraCreatureValues;
auto creatureToCount = [tierValues](const CCreature * creature) -> int
{
if(!creature->getAIValue() || tierValues.empty()) //bug #2681
return 0; //this box won't be generated
//Follow the rules from https://heroes.thelazy.net/index.php/Pandora%27s_Box
}
int actualTier = creature->getLevel() > tierValues.size() ?
tierValues.size() - 1 :
creature->getLevel() - 1;
float creaturesAmount = std::floor((static_cast<float>(tierValues[actualTier])) / creature->getAIValue());
if (creaturesAmount < 1)
{
return 0;
}
else if(creaturesAmount <= 5)
{
//No change
}
else if(creaturesAmount <= 12)
{
creaturesAmount = std::ceil(creaturesAmount / 2) * 2;
}
else if(creaturesAmount <= 50)
{
creaturesAmount = std::round(creaturesAmount / 5) * 5;
}
else
{
creaturesAmount = std::round(creaturesAmount / 10) * 10;
}
return static_cast<int>(creaturesAmount);
};
void TreasurePlacer::addPandoraBoxesWithCreatures()
{
ObjectInfo oi;
//pandora box with creatures
for(auto * creature : creatures)
{
@ -344,7 +378,11 @@ void TreasurePlacer::addAllPossibleObjects()
if(!oi.templates.empty())
addObjectToRandomPool(oi);
}
}
void TreasurePlacer::addPandoraBoxesWithSpells()
{
ObjectInfo oi;
//Pandora with 12 spells of certain level
for(int i = 1; i <= GameConstants::SPELL_LEVELS; i++)
{
@ -441,9 +479,14 @@ void TreasurePlacer::addAllPossibleObjects()
oi.probability = 2;
if(!oi.templates.empty())
addObjectToRandomPool(oi);
}
void TreasurePlacer::addSeerHuts()
{
//Seer huts with creatures or generic rewards
ObjectInfo oi;
if(zone.getConnectedZoneIds().size()) //Unlikely, but...
{
auto * qap = zone.getModificator<QuestArtifactPlacer>();
@ -588,12 +631,6 @@ void TreasurePlacer::addAllPossibleObjects()
}
}
size_t TreasurePlacer::getPossibleObjectsSize() const
{
RecursiveLock lock(externalAccessMutex);
return possibleObjects.size();
}
void TreasurePlacer::setMaxPrisons(size_t count)
{
RecursiveLock lock(externalAccessMutex);
@ -606,6 +643,40 @@ size_t TreasurePlacer::getMaxPrisons() const
return maxPrisons;
}
int TreasurePlacer::creatureToCount(const CCreature * creature) const
{
if(!creature->getAIValue() || tierValues.empty()) //bug #2681
return 0; //this box won't be generated
//Follow the rules from https://heroes.thelazy.net/index.php/Pandora%27s_Box
int actualTier = creature->getLevel() > tierValues.size() ?
tierValues.size() - 1 :
creature->getLevel() - 1;
float creaturesAmount = std::floor((static_cast<float>(tierValues[actualTier])) / creature->getAIValue());
if (creaturesAmount < 1)
{
return 0;
}
else if(creaturesAmount <= 5)
{
//No change
}
else if(creaturesAmount <= 12)
{
creaturesAmount = std::ceil(creaturesAmount / 2) * 2;
}
else if(creaturesAmount <= 50)
{
creaturesAmount = std::round(creaturesAmount / 5) * 5;
}
else
{
creaturesAmount = std::round(creaturesAmount / 10) * 10;
}
return static_cast<int>(creaturesAmount);
};
bool TreasurePlacer::isGuardNeededForTreasure(int value)
{// no guard in a zone with "monsters: none" and for small treasures; water zones cen get monster strength ZONE_NONE elsewhere if needed
return zone.monsterStrength != EMonsterStrength::ZONE_NONE && value > minGuardedValue;
@ -623,6 +694,7 @@ std::vector<ObjectInfo*> TreasurePlacer::prepareTreasurePile(const CTreasureInfo
bool hasLargeObject = false;
while(currentValue <= static_cast<int>(desiredValue) - 100) //no objects with value below 100 are available
{
// FIXME: Pointer might be invalidated after this
auto * oi = getRandomObject(desiredValue, currentValue, !hasLargeObject);
if(!oi) //fail
break;
@ -674,12 +746,21 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
accessibleArea.add(int3());
}
auto * object = oi->generateObject();
if(oi->templates.empty())
CGObjectInstance * object = nullptr;
if (oi->generateObject)
{
logGlobal->warn("Deleting randomized object with no templates: %s", object->getObjectName());
oi->destroyObject(object);
delete object;
object = oi->generateObject();
if(oi->templates.empty())
{
logGlobal->warn("Deleting randomized object with no templates: %s", object->getObjectName());
oi->destroyObject(object);
delete object;
continue;
}
}
else
{
logGlobal->error("ObjectInfo has no generateObject function! Templates: %d", oi->templates.size());
continue;
}
@ -785,7 +866,7 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu
ui32 maxVal = desiredValue - currentValue;
ui32 minValue = static_cast<ui32>(0.25f * (desiredValue - currentValue));
for(ObjectInfo & oi : possibleObjects) //copy constructor turned out to be costly
for(ObjectInfo & oi : objects.getPossibleObjects()) //copy constructor turned out to be costly
{
if(oi.value > maxVal)
break; //this assumes values are sorted in ascending order
@ -859,24 +940,19 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
boost::sort(treasureInfo, valueComparator);
//sort treasures by ascending value so we can stop checking treasures with too high value
boost::sort(possibleObjects, [](const ObjectInfo& oi1, const ObjectInfo& oi2) -> bool
{
return oi1.value < oi2.value;
});
objects.sortPossibleObjects();
const size_t size = zone.area()->getTilesVector().size();
int totalDensity = 0;
// FIXME: No need to use iterator here
for (auto t = treasureInfo.begin(); t != treasureInfo.end(); t++)
{
std::vector<rmg::Object> treasures;
//discard objects with too high value to be ever placed
vstd::erase_if(possibleObjects, [t](const ObjectInfo& oi) -> bool
{
return oi.value > t->max;
});
objects.discardObjectsAboveValue(t->max);
totalDensity += t->density;
@ -895,7 +971,11 @@ void TreasurePlacer::createTreasures(ObjectManager& manager)
continue;
}
int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0, [](int v, const ObjectInfo* oi) {return v + oi->value; });
int value = std::accumulate(treasurePileInfos.begin(), treasurePileInfos.end(), 0,
[](int v, const ObjectInfo* oi)
{
return v + oi->value;
});
const ui32 maxPileGenerationAttempts = 2;
for (ui32 attempt = 0; attempt < maxPileGenerationAttempts; attempt++)
@ -1016,13 +1096,88 @@ char TreasurePlacer::dump(const int3 & t)
return Modificator::dump(t);
}
void ObjectInfo::setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrainType)
void TreasurePlacer::ObjectPool::addObject(const ObjectInfo & info)
{
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
if(!templHandler)
return;
templates = templHandler->getTemplates(terrainType);
possibleObjects.push_back(info);
}
void TreasurePlacer::ObjectPool::updateObject(MapObjectID id, MapObjectSubID subid, ObjectInfo info)
{
/*
Handle separately:
- Dwellings
- Prisons
- Seer huts (quests)
- Pandora Boxes
*/
// FIXME: This will drop all templates
customObjects[CompoundMapObjectID(id, subid)] = info;
}
void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
{
// FIXME: Wycina wszystkie obiekty poza pandorami i dwellami :?
// Copy standard objects if they are not already modified
/*
for (const auto & object : possibleObjects)
{
for (const auto & templ : object.templates)
{
// FIXME: Objects with same temmplates (Pandora boxes) are not added
CompoundMapObjectID key(templ->id, templ->subid);
if (!vstd::contains(customObjects, key))
{
customObjects[key] = object;
}
}
}
*/
vstd::erase_if(possibleObjects, [&zone](const ObjectInfo & object)
{
for (const auto & templ : object.templates)
{
CompoundMapObjectID key(templ->id, templ->subid);
if (vstd::contains(zone.getBannedObjects(), key))
{
return true;
}
}
return false;
});
// Now copy back modified list
// FIXME: Protect with mutex as well?
/*
possibleObjects.clear();
for (const auto & customObject : customObjects)
{
addObject(customObject.second);
}
*/
// TODO: Consider adding custom Pandora boxes with arbitrary content
}
std::vector<ObjectInfo> & TreasurePlacer::ObjectPool::getPossibleObjects()
{
return possibleObjects;
}
void TreasurePlacer::ObjectPool::sortPossibleObjects()
{
boost::sort(possibleObjects, [](const ObjectInfo& oi1, const ObjectInfo& oi2) -> bool
{
return oi1.value < oi2.value;
});
}
void TreasurePlacer::ObjectPool::discardObjectsAboveValue(ui32 value)
{
vstd::erase_if(possibleObjects, [value](const ObjectInfo& oi) -> bool
{
return oi.value > value;
});
}
VCMI_LIB_NAMESPACE_END

View File

@ -9,6 +9,8 @@
*/
#pragma once
#include "../ObjectInfo.h"
#include "../Zone.h"
#include "../../mapObjects/ObjectTemplate.h"
@ -18,21 +20,7 @@ class CGObjectInstance;
class ObjectManager;
class RmgMap;
class CMapGenerator;
struct ObjectInfo
{
ObjectInfo();
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
ui32 value = 0;
ui16 probability = 0;
ui32 maxPerZone = 1;
//ui32 maxPerMap; //unused
std::function<CGObjectInstance *()> generateObject;
std::function<void(CGObjectInstance *)> destroyObject;
void setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrain);
};
struct CompoundMapObjectID;
class TreasurePlacer: public Modificator
{
@ -45,11 +33,26 @@ public:
void createTreasures(ObjectManager & manager);
void addObjectToRandomPool(const ObjectInfo& oi);
void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects
size_t getPossibleObjectsSize() const;
// TODO: Can be defaulted to addAllPossibleObjects, but then each object will need to be configured
void addCommonObjects();
void addDwellings();
void addPandoraBoxes();
void addPandoraBoxesWithGold();
void addPandoraBoxesWithExperience();
void addPandoraBoxesWithCreatures();
void addPandoraBoxesWithSpells();
void addSeerHuts();
void addPrisons();
void addScrolls();
void addAllPossibleObjects(); //add objects, including zone-specific, to possibleObjects
// TODO: Read custom object config from zone file
/// Get all objects for this terrain
void setMaxPrisons(size_t count);
size_t getMaxPrisons() const;
int creatureToCount(const CCreature * creature) const;
protected:
bool isGuardNeededForTreasure(int value);
@ -59,7 +62,24 @@ protected:
rmg::Object constructTreasurePile(const std::vector<ObjectInfo*> & treasureInfos, bool densePlacement = false);
protected:
std::vector<ObjectInfo> possibleObjects;
class ObjectPool
{
public:
void addObject(const ObjectInfo & info);
void updateObject(MapObjectID id, MapObjectSubID subid, ObjectInfo info);
std::vector<ObjectInfo> & getPossibleObjects();
void patchWithZoneConfig(const Zone & zone);
void sortPossibleObjects();
void discardObjectsAboveValue(ui32 value);
private:
std::vector<ObjectInfo> possibleObjects;
std::map<CompoundMapObjectID, ObjectInfo> customObjects;
} objects;
// TODO: Need to nagivate and update these
int minGuardedValue = 0;
rmg::Area treasureArea;
@ -67,6 +87,9 @@ protected:
rmg::Area guards;
size_t maxPrisons;
std::vector<const CCreature *> creatures; //native creatures for this zone
std::vector<int> tierValues;
};
VCMI_LIB_NAMESPACE_END