1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-10 22:31:40 +02:00

Add specific objects and configure their frequency / value

This commit is contained in:
Tomasz Zieliński
2024-09-12 21:04:27 +02:00
parent bfe75a6a02
commit 4b263b6d41
6 changed files with 177 additions and 101 deletions

View File

@@ -40,6 +40,8 @@
#include "../texts/CGeneralTextHandler.h"
#include "../texts/CLegacyConfigParser.h"
#include <vstd/StringUtils.h>
VCMI_LIB_NAMESPACE_BEGIN
CObjectClassesHandler::CObjectClassesHandler()
@@ -396,6 +398,9 @@ CompoundMapObjectID CObjectClassesHandler::getCompoundIdentifier(const std::stri
if(id)
{
if (subtype.empty())
return CompoundMapObjectID(id.value(), 0);
const auto & object = objects.at(id.value());
std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
@@ -410,45 +415,32 @@ CompoundMapObjectID CObjectClassesHandler::getCompoundIdentifier(const std::stri
CompoundMapObjectID CObjectClassesHandler::getCompoundIdentifier(const std::string & objectName) const
{
// FIXME: Crash with no further log
// TODO: Use existing utilities for parsing id:
// CIdentifierStorage::ObjectCallback::fromNameAndType
// TODO: Use existing utilities for parsing id?
JsonNode node(objectName);
auto modScope = node.getModScope();
std::string subtype = "object"; //Default for objects with no subIds
std::string type;
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
auto scopeAndFullName = vstd::splitStringToPair(objectName, ':');
logGlobal->debug("scopeAndFullName: %s, %s", scopeAndFullName.first, scopeAndFullName.second);
if(firstColon != std::string::npos)
auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');
logGlobal->debug("typeAndName: %s, %s", typeAndName.first, typeAndName.second);
auto nameAndSubtype = vstd::splitStringToPair(typeAndName.second, '.');
logGlobal->debug("nameAndSubtype: %s, %s", nameAndSubtype.first, nameAndSubtype.second);
if (!nameAndSubtype.first.empty())
{
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);
}
type = nameAndSubtype.first;
subtype = nameAndSubtype.second;
}
else
{
if(lastDot != std::string::npos)
{
type = objectName.substr(0, lastDot);
subtype = objectName.substr(lastDot + 1);
}
else
{
type = objectName;
}
type = typeAndName.second;
}
return getCompoundIdentifier(scope, type, subtype);
return getCompoundIdentifier(scopeAndFullName.first, type, subtype);
}
std::set<MapObjectID> CObjectClassesHandler::knownObjects() const

View File

@@ -46,6 +46,8 @@ public:
/// H3 ID/subID of this object
MapObjectID id;
MapObjectSubID subid;
// TODO: get compound id
/// print priority, objects with higher priority will be print first, below everything else
si32 printPriority;
/// animation file that should be used to display object

View File

@@ -50,6 +50,15 @@ ObjectInfo & ObjectInfo::operator=(const ObjectInfo & other)
return *this;
}
void ObjectInfo::setAllTemplates(MapObjectID type, MapObjectSubID subtype)
{
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
if(!templHandler)
return;
templates = templHandler->getTemplates();
}
void ObjectInfo::setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrainType)
{
auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
@@ -68,6 +77,20 @@ void ObjectConfig::addBannedObject(const CompoundMapObjectID & objid)
logGlobal->info("Banned object of type %d.%d", objid.primaryID, objid.secondaryID);
}
void ObjectConfig::addCustomObject(const ObjectInfo & object, const CompoundMapObjectID & objid)
{
// FIXME: Need id / subId
// FIXME: Add templates and possibly other info
customObjects.push_back(object);
auto & lastObject = customObjects.back();
lastObject.setAllTemplates(objid.primaryID, objid.secondaryID);
assert(lastObject.templates.size() > 0);
auto temp = lastObject.templates.front();
logGlobal->info("Added custom object of type %d.%d", temp->id, temp->subid);
}
void ObjectConfig::serializeJson(JsonSerializeFormat & handler)
{
// TODO: We need serializer utility for list of enum values
@@ -87,73 +110,105 @@ void ObjectConfig::serializeJson(JsonSerializeFormat & handler)
(EObjectCategory::QUEST_ARTIFACT, "questArtifact")
(EObjectCategory::SEER_HUT, "seerHut");
auto categories = handler.enterArray("bannedCategories");
if (handler.saving)
{
for (const auto& category : bannedObjectCategories)
{
auto str = OBJECT_CATEGORY_STRINGS.left.at(category);
categories.serializeString(categories.size(), str);
}
}
else
{
std::vector<std::string> categoryNames;
categories.serializeArray(categoryNames);
for (const auto & categoryName : categoryNames)
// TODO: Separate into individual methods to enforce RAII destruction?
{
auto categories = handler.enterArray("bannedCategories");
if (handler.saving)
{
auto it = OBJECT_CATEGORY_STRINGS.right.find(categoryName);
if (it != OBJECT_CATEGORY_STRINGS.right.end())
for (const auto& category : bannedObjectCategories)
{
bannedObjectCategories.push_back(it->second);
auto str = OBJECT_CATEGORY_STRINGS.left.at(category);
categories.serializeString(categories.size(), str);
}
}
}
auto bannedObjectData = handler.enterArray("bannedObjects");
if (handler.saving)
{
// FIXME: Do we even need to serialize / store banned objects?
/*
for (const auto & object : bannedObjects)
else
{
// TODO: Translate id back to string?
std::vector<std::string> categoryNames;
categories.serializeArray(categoryNames);
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())
for (const auto & categoryName : categoryNames)
{
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)
auto it = OBJECT_CATEGORY_STRINGS.right.find(categoryName);
if (it != OBJECT_CATEGORY_STRINGS.right.end())
{
addBannedObject(objid);
bannedObjectCategories.push_back(it->second);
}
}
}
}
// FIXME: Doesn't seem to use this field at all
{
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
{
std::vector<std::string> objectNames;
bannedObjectData.serializeArray(objectNames);
for (const auto & objectName : objectNames)
{
VLC->objtypeh->resolveObjectCompoundId(objectName,
[this](CompoundMapObjectID objid)
{
addBannedObject(objid);
}
);
}
}
}
auto commonObjectData = handler.getCurrent()["commonObjects"].Vector();
if (handler.saving)
{
//TODO?
}
else
{
for (const auto & objectConfig : commonObjectData)
{
auto objectName = objectConfig["id"].String();
auto rmg = objectConfig["rmg"].Struct();
// TODO: Use common code with default rmg config
auto objectValue = rmg["value"].Integer();
auto objectProbability = rmg["rarity"].Integer();
auto objectMaxPerZone = rmg["zoneLimit"].Integer();
VLC->objtypeh->resolveObjectCompoundId(objectName,
[this, objectValue, objectProbability, objectMaxPerZone](CompoundMapObjectID objid)
{
ObjectInfo object;
// TODO: Configure basic generateObject function
object.value = objectValue;
object.probability = objectProbability;
object.maxPerZone = objectMaxPerZone;
addCustomObject(object, objid);
}
);

View File

@@ -32,6 +32,7 @@ struct DLL_LINKAGE ObjectInfo
std::function<CGObjectInstance *()> generateObject;
std::function<void(CGObjectInstance *)> destroyObject;
void setAllTemplates(MapObjectID type, MapObjectSubID subtype);
void setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrain);
//bool matchesId(const CompoundMapObjectID & id) const;
@@ -61,7 +62,7 @@ public:
};
void addBannedObject(const CompoundMapObjectID & objid);
void addCustomObject(const ObjectInfo & object);
void addCustomObject(const ObjectInfo & object, const CompoundMapObjectID & objid);
void clearBannedObjects();
void clearCustomObjects();
const std::vector<CompoundMapObjectID> & getBannedObjects() const;

View File

@@ -50,7 +50,7 @@ void TreasurePlacer::process()
// Get default objects
addAllPossibleObjects();
// Override with custom objects
objects.patchWithZoneConfig(zone);
objects.patchWithZoneConfig(zone, this);
auto * m = zone.getModificator<ObjectManager>();
if(m)
@@ -65,6 +65,8 @@ void TreasurePlacer::init()
DEPENDENCY_ALL(PrisonHeroPlacer);
DEPENDENCY(RoadPlacer);
// FIXME: Starting zones get Pandoras with neutral creatures
// Add all native creatures
for(auto const & cre : VLC->creh->objects)
{
@@ -79,7 +81,6 @@ void TreasurePlacer::init()
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);
@@ -126,14 +127,11 @@ void TreasurePlacer::addCommonObjects()
//Skip objects with per-map limit here
continue;
}
setBasicProperties(oi, CompoundMapObjectID(primaryID, secondaryID));
oi.generateObject = [this, primaryID, secondaryID]() -> CGObjectInstance *
{
return VLC->objtypeh->getHandlerFor(primaryID, secondaryID)->create(map.mapInstance->cb, nullptr);
};
oi.value = rmgInfo.value;
oi.probability = rmgInfo.rarity;
oi.setTemplates(primaryID, secondaryID, zone.getTerrainType());
oi.maxPerZone = rmgInfo.zoneLimit;
if(!oi.templates.empty())
addObjectToRandomPool(oi);
@@ -142,6 +140,15 @@ void TreasurePlacer::addCommonObjects()
}
}
void TreasurePlacer::setBasicProperties(ObjectInfo & oi, CompoundMapObjectID objid) const
{
oi.generateObject = [this, objid]() -> CGObjectInstance *
{
return VLC->objtypeh->getHandlerFor(objid)->create(map.mapInstance->cb, nullptr);
};
oi.setTemplates(objid.primaryID, objid.secondaryID, zone.getTerrainType());
}
void TreasurePlacer::addPrisons()
{
//Generate Prison on water only if it has a template
@@ -1116,7 +1123,7 @@ void TreasurePlacer::ObjectPool::updateObject(MapObjectID id, MapObjectSubID sub
customObjects[CompoundMapObjectID(id, subid)] = info;
}
void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone, TreasurePlacer * tp)
{
// FIXME: Wycina wszystkie obiekty poza pandorami i dwellami :?
@@ -1145,18 +1152,27 @@ void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
if (categoriesSet.count(category))
{
logGlobal->info("Removing object %s from possible objects", oi.templates.front()->stringID);
/* FIXME:
Removing object normal from possible objects
Removing object base from possible objects
Removing object default from possible objects
*/
return true;
}
return false;
});
vstd::erase_if(possibleObjects, [&zone](const ObjectInfo & object)
auto bannedObjects = zone.getBannedObjects();
auto bannedObjectsSet = std::set<CompoundMapObjectID>(bannedObjects.begin(), bannedObjects.end());
vstd::erase_if(possibleObjects, [&bannedObjectsSet](const ObjectInfo & object)
{
for (const auto & templ : object.templates)
{
CompoundMapObjectID key(templ->id, templ->subid);
if (vstd::contains(zone.getBannedObjects(), key))
if (bannedObjectsSet.count(key))
{
// FIXME: Stopped working, nothing is banned
logGlobal->info("Banning object %s from possible objects", templ->stringID);
return true;
}
}
@@ -1164,6 +1180,15 @@ void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
});
auto configuredObjects = zone.getConfiguredObjects();
// FIXME: Access TreasurePlacer from ObjectPool
for (auto & object : configuredObjects)
{
auto temp = object.templates.front();
tp->setBasicProperties(object, CompoundMapObjectID(temp->id, temp->subid));
addObject(object);
logGlobal->info("Added custom object of type %d.%d", temp->id, temp->subid);
}
// TODO: Overwrite or add to possibleObjects
// FIXME: Protect with mutex as well?

View File

@@ -34,6 +34,7 @@ public:
void createTreasures(ObjectManager & manager);
void addObjectToRandomPool(const ObjectInfo& oi);
void setBasicProperties(ObjectInfo & oi, CompoundMapObjectID objid) const;
// TODO: Can be defaulted to addAllPossibleObjects, but then each object will need to be configured
void addCommonObjects();
@@ -69,7 +70,7 @@ protected:
void addObject(const ObjectInfo & info);
void updateObject(MapObjectID id, MapObjectSubID subid, ObjectInfo info);
std::vector<ObjectInfo> & getPossibleObjects();
void patchWithZoneConfig(const Zone & zone);
void patchWithZoneConfig(const Zone & zone, TreasurePlacer * tp);
void sortPossibleObjects();
void discardObjectsAboveValue(ui32 value);