1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Refactoring of ObjectClassesHandler

This commit is contained in:
Ivan Savenko
2022-12-31 15:01:19 +02:00
parent c5f72ccdc7
commit 6c472339ce
14 changed files with 237 additions and 314 deletions

View File

@@ -32,10 +32,6 @@ MapObjectsEvaluator::MapObjectsEvaluator()
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = handler->getAiValue().get();
}
else if(VLC->objtypeh->getObjGroupAiValue(primaryID) != boost::none) //if value is not initialized - fallback to default value for this object family if it exists
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = VLC->objtypeh->getObjGroupAiValue(primaryID).get();
}
else //some default handling when aiValue not found, objects that require advanced properties (unavailable from handler) get their value calculated in getObjectValue
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0;

View File

@@ -1371,7 +1371,7 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
if(t.hasFavorableWinds())
{
out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS);
out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS, 0);
return;
}
const TerrainTile2 & tt = ttiles[pos.z][pos.x][pos.y];

View File

@@ -2,6 +2,7 @@
"creatureBank" : {
"index" :16,
"handler": "bank",
"lastReservedIndex" : 6,
"base" : {
"sounds" : {
"visit" : ["ROGUE"]

View File

@@ -2,6 +2,7 @@
"creatureGeneratorCommon" : {
"index" :17,
"handler": "dwelling",
"lastReservedIndex" : 79,
"base" : {
"base" : {
"visitableFrom" : [ "---", "+++", "+++" ],
@@ -528,6 +529,7 @@
"creatureGeneratorSpecial" : {
"index" :20,
"handler": "dwelling",
"lastReservedIndex" : 1,
"types" : {
"elementalConflux" : {
"index" : 0,

View File

@@ -230,6 +230,7 @@
"subterraneanGate" : {
"index" :103,
"handler" : "subterraneanGate",
"lastReservedIndex" : 1,
"base" : {
"sounds" : {
"ambient" : ["LOOPGATE"],
@@ -559,6 +560,7 @@
"witchHut" : {
"index" :113,
"handler" : "witch",
"lastReservedIndex" : 1,
"base" : {
"sounds" : {
"visit" : ["GAZEBO"]

View File

@@ -23,8 +23,8 @@
"hero" : {
"index" :34,
"handler": "hero",
"defaultAiValue" : 7500,
"base" : {
"aiValue" : 7500,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VVV", "VAV"]
@@ -54,6 +54,7 @@
"resource" : {
"index" :79,
"handler": "resource",
"lastReservedIndex" : 7,
"base" : {
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
@@ -76,8 +77,8 @@
"town" : {
"index" :98,
"handler": "town",
"defaultAiValue" : 20000,
"base" : {
"aiValue" : 20000,
"filters" : {
// village image - fort not present
"village" : [ "noneOf", [ "fort" ] ],
@@ -106,8 +107,9 @@
"boat" : {
"index" :8,
"handler": "boat",
"defaultAiValue" : 0,
"lastReservedIndex" : 2,
"base" : {
"aiValue" : 0,
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VVV", "VAV" ]
@@ -124,8 +126,9 @@
"borderGuard" : {
"index" :9,
"handler": "borderGuard",
"defaultAiValue" : 0,
"lastReservedIndex" : 7,
"base" : {
"aiValue" : 0,
"sounds" : {
"visit" : ["CAVEHEAD"],
"removal" : [ "PICKUP01", "PICKUP02", "PICKUP03", "PICKUP04", "PICKUP05", "PICKUP06", "PICKUP07" ]
@@ -145,8 +148,9 @@
"borderGate" : {
"index" :212,
"handler": "borderGate",
"defaultAiValue" : 0,
"lastReservedIndex" : 7,
"base" : {
"aiValue" : 0,
"sounds" : {
"visit" : ["CAVEHEAD"]
}
@@ -165,8 +169,9 @@
"keymasterTent" : {
"index" :10,
"handler": "keymaster",
"defaultAiValue" : 10000,
"lastReservedIndex" : 7,
"base" : {
"aiValue" : 10000,
"sounds" : {
"visit" : ["CAVEHEAD"]
}
@@ -186,8 +191,9 @@
"seerHut" : {
"index" :83,
"handler": "seerHut",
"defaultAiValue" : 10000,
"lastReservedIndex" : 2,
"base" : {
"aiValue" : 10000,
"base" : {
"visitableFrom" : [ "---", "+++", "+++" ],
"mask" : [ "VVV", "VVV", "VAV" ]
@@ -207,6 +213,7 @@
"cartographer" : {
"index" :13,
"handler": "cartographer",
"lastReservedIndex" : 2,
"base" : {
"sounds" : {
"visit" : ["LIGHTHOUSE"]
@@ -223,6 +230,7 @@
"mine" : {
"index" :53,
"handler": "mine",
"lastReservedIndex" : 7,
"base" : {
"sounds" : {
"visit" : ["FLAGMINE"]
@@ -320,8 +328,8 @@
"abandonedMine" : {
"index" :220,
"handler": "mine",
"defaultAiValue" : 3500,
"base" : {
"aiValue" : 3500,
"sounds" : {
"ambient" : ["LOOPCAVE"]
}
@@ -334,8 +342,9 @@
"garrisonHorizontal": {
"index" :33,
"handler": "garrison",
"defaultAiValue" : 0,
"lastReservedIndex" : 1,
"base" : {
"aiValue" : 0,
"sounds" : {
"visit" : ["MILITARY"]
}
@@ -358,8 +367,9 @@
"garrisonVertical" : {
"index" :219,
"handler": "garrison",
"defaultAiValue" : 0,
"lastReservedIndex" : 1,
"base" : {
"aiValue" : 0,
"sounds" : {
"visit" : ["MILITARY"]
}
@@ -384,6 +394,7 @@
"monolithOneWayEntrance" : {
"index" :43,
"handler": "monolith",
"lastReservedIndex" : 7,
"base" : {
"sounds" : {
"ambient" : ["LOOPMON1"],
@@ -404,6 +415,7 @@
"monolithOneWayExit" : {
"index" :44,
"handler": "monolith",
"lastReservedIndex" : 7,
"base" : {
"sounds" : { "ambient" : ["LOOPMON1"] }
},
@@ -421,6 +433,7 @@
"monolithTwoWay" : {
"index" :45,
"handler": "monolith",
"lastReservedIndex" : 7,
"base" : {
"sounds" : {
"ambient" : ["LOOPMON2"],
@@ -441,7 +454,9 @@
// subtype: level
"randomDwellingLvl" : {
"index" :217, "handler": "randomDwelling",
"index" :217,
"handler": "randomDwelling",
"lastReservedIndex" : 6,
"types" : {
"objectLvl1" : { "index" : 0},
"objectLvl2" : { "index" : 1},
@@ -457,6 +472,7 @@
"randomDwellingFaction" : {
"index" :218,
"handler": "randomDwelling",
"lastReservedIndex" : 8,
"types" : {
"objectCastle" : { "index" : 0},
"objectRampart" : { "index" : 1},

View File

@@ -10,46 +10,9 @@
"index": {
"type":"number"
},
"name": {
"type":"string"
},
"defaultAiValue": {
"type":"number"
},
"handler": {
"type":"string"
},
"sounds": {
"type":"object",
"additionalProperties" : false,
"description": "Sounds used by this object",
"properties" : {
"ambient": {
"type":"array",
"description": "Background sound of an object",
"items": {
"type": "string"
}
},
"visit": {
"type":"array",
"description": "Sound that played on object visit",
"items": {
"type": "string"
}
},
"removal": {
"type":"array",
"description": "Sound that played on object removal",
"items": {
"type": "string"
}
}
}
},
"base": {
"type" : "object"
},

View File

@@ -129,7 +129,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
}
else if(type == OBJ_NAMES)
{
dst = VLC->objtypeh->getObjectName(ser);
dst = VLC->objtypeh->getObjectName(ser, 0);
}
else if(type == SEC_SKILL_NAME)
{

View File

@@ -92,7 +92,7 @@ CObjectClassesHandler::CObjectClassesHandler()
CObjectClassesHandler::~CObjectClassesHandler()
{
for(auto p : objects)
delete p.second;
delete p;
}
std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
@@ -118,190 +118,149 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
CLegacyConfigParser namesParser("Data/ObjNames.txt");
for (size_t i=0; i<256; i++)
{
ret[i]["name"].String() = namesParser.readString();
ret[i]["base"]["name"].String() = namesParser.readString();
namesParser.endLine();
}
JsonNode cregen1;
JsonNode cregen4;
CLegacyConfigParser cregen1Parser("data/crgen1");
do
customNames[Obj::CREATURE_GENERATOR1].push_back(cregen1Parser.readString());
{
JsonNode subObject;
subObject["name"].String() = cregen1Parser.readString();
cregen1.Vector().push_back(subObject);
}
while(cregen1Parser.endLine());
CLegacyConfigParser cregen4Parser("data/crgen4");
do
customNames[Obj::CREATURE_GENERATOR4].push_back(cregen4Parser.readString());
{
JsonNode subObject;
subObject["name"].String() = cregen4Parser.readString();
cregen4.Vector().push_back(subObject);
}
while(cregen4Parser.endLine());
ret[Obj::CREATURE_GENERATOR1]["subObjects"] = cregen1;
ret[Obj::CREATURE_GENERATOR4]["subObjects"] = cregen4;
return ret;
}
/// selects preferred ID (or subID) for new object
template<typename Map>
si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 fixedObjectsBound)
void CObjectClassesHandler::loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj)
{
assert(fixedObjectsBound > 0);
if(fixedID.isNull())
{
auto lastID = map.empty() ? 0 : map.rbegin()->first;
return lastID < fixedObjectsBound ? fixedObjectsBound : lastID + 1;
}
auto id = static_cast<si32>(fixedID.Float());
if(id >= fixedObjectsBound)
logGlobal->error("Getting next ID overflowed: %d >= %d", id, fixedObjectsBound);
auto object = loadSubObjectFromJson(scope, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), identifier), entry, obj, obj->objects.size());
return id;
assert(object);
obj->objects.push_back(object);
registerObject(scope, obj->getIdentifier(), identifier, object->subtype);
}
void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj, bool isSubobject)
void CObjectClassesHandler::loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index)
{
static const si32 fixedObjectsBound = 1000; // legacy value for backward compatibilitty
static const si32 fixedSubobjectsBound = 10000000; // large enough arbitrary value to avoid ID-collisions
si32 usedBound = fixedObjectsBound;
//TODO: load name for subobjects
auto object = loadSubObjectFromJson(scope, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), identifier), entry, obj, index);
assert(object);
assert(obj->objects[index] == nullptr); // ensure that this id was not loaded before
obj->objects[index] = object;
registerObject(scope, obj->getIdentifier(), identifier, object->subtype);
}
TObjectTypeHandler CObjectClassesHandler::loadSubObjectFromJson(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index)
{
if(!handlerConstructors.count(obj->handlerName))
{
logGlobal->error("Handler with name %s was not found!", obj->handlerName);
return;
}
const auto convertedId = VLC->modh->normalizeIdentifier(entry.meta, CModHandler::scopeBuiltin(), identifier);
const auto & entryIndex = entry["index"];
bool useSelectNextID = !isSubobject || entryIndex.isNull();
if(useSelectNextID && isSubobject)
{
usedBound = fixedSubobjectsBound;
logGlobal->error("Subobject index is Null. convertedId = '%s' obj->id = %d", convertedId, obj->id);
}
si32 id = useSelectNextID ? selectNextID(entryIndex, obj->subObjects, usedBound)
: (si32)entryIndex.Float();
auto handler = handlerConstructors.at(obj->handlerName)();
handler->setType(obj->id, id);
handler->setTypeName(obj->getIdentifier(), convertedId);
if (customNames.count(obj->id) && customNames.at(obj->id).size() > id)
handler->init(entry, customNames.at(obj->id).at(id));
else
handler->init(entry);
//if (handler->getTemplates().empty())
{
auto range = legacyTemplates.equal_range(std::make_pair(obj->id, id));
for (auto & templ : boost::make_iterator_range(range.first, range.second))
{
handler->addTemplate(templ.second);
}
legacyTemplates.erase(range.first, range.second);
return nullptr;
}
logGlobal->debug("Loaded object %s(%d)::%s(%d)", obj->getIdentifier(), obj->id, convertedId, id);
auto createdObject = handlerConstructors.at(obj->handlerName)();
createdObject->setType(obj->id, index);
createdObject->setTypeName(obj->getIdentifier(), identifier);
createdObject->init(entry);
//some mods redefine content handlers in the decoration.json in such way:
//"core:sign" : { "types" : { "forgeSign" : { ...
static const std::vector<std::string> breakersRMG
auto range = legacyTemplates.equal_range(std::make_pair(obj->id, index));
for (auto & templ : boost::make_iterator_range(range.first, range.second))
{
"hota.hota decorations:hotaPandoraBox"
, "hota.hota decorations:hotaSubterreanGate"
};
const bool isExistingKey = obj->subObjects.count(id) > 0;
const bool isBreaker = std::any_of(breakersRMG.begin(), breakersRMG.end(),
[&handler](const std::string & str)
{
return str.compare(handler->subTypeName) == 0;
});
const bool passedHandler = !isExistingKey && !isBreaker;
createdObject->addTemplate(templ.second);
}
legacyTemplates.erase(range.first, range.second);
if(passedHandler)
{
obj->subObjects[id] = handler;
obj->subIds[convertedId] = id;
}
else if(isExistingKey) //It's supposed that fan mods handlers are not overridden by default handlers
{
logGlobal->trace("Handler '%s' has not been overridden with handler '%s' in object %s(%d)::%s(%d)",
obj->subObjects[id]->subTypeName, obj->handlerName, obj->getIdentifier(), obj->id, convertedId, id);
}
else
{
logGlobal->warn("Handler '%s' for object %s(%d)::%s(%d) has not been activated as RMG breaker",
obj->handlerName, obj->getIdentifier(), obj->id, convertedId, id);
}
logGlobal->debug("Loaded object %s(%d)::%s(%d)", obj->getIdentifier(), obj->id, identifier, index);
return createdObject;
}
std::string CObjectClassesHandler::ObjectContainter::getIdentifier() const
std::string ObjectClass::getIdentifier() const
{
return modScope + ":" + identifier;
return identifier + "Object";
}
std::string CObjectClassesHandler::ObjectContainter::getNameTextID() const
ObjectClass * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index)
{
return TextIdentifier ("object", modScope, identifier, "name").get();
}
std::string CObjectClassesHandler::ObjectContainter::getNameTranslated() const
{
return VLC->generaltexth->translate(getNameTextID());
}
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name)
{
auto obj = new ObjectContainter(scope, name);
static const si32 fixedObjectsBound = 256; //Legacy value for backward compatibility
VLC->generaltexth->registerString( obj->getNameTextID(), json["name"].String());
auto obj = new ObjectClass(scope, name);
obj->handlerName = json["handler"].String();
obj->base = json["base"];
obj->id = selectNextID(json["index"], objects, fixedObjectsBound);
obj->id = index;
if(json["defaultAiValue"].isNull())
obj->groupDefaultAiValue = boost::none;
else
obj->groupDefaultAiValue = static_cast<boost::optional<si32>>(json["defaultAiValue"].Integer());
for (auto entry : json["types"].Struct())
loadObjectEntry(entry.first, entry.second, obj);
obj->objects.resize(json["lastReservedIndex"].Float() + 1);
auto originalData = json["subObjects"].Vector();
for (auto subData : json["types"].Struct())
{
if (!subData.second["index"].isNull())
{
size_t subIndex = subData.second["index"].Integer();
if (subIndex < originalData.size())
{
JsonUtils::merge(originalData[subIndex], subData.second);
std::swap(originalData[subIndex], subData.second);
}
loadSubObject(scope, subData.first, subData.second, obj, subIndex);
}
else
loadSubObject(scope, subData.first, subData.second, obj);
}
return obj;
}
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(scope, data, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name));
objects[object->id] = object;
auto object = loadFromJson(scope, data, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), objects.size());
objects.push_back(object);
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
}
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(scope, data, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name));
auto object = loadFromJson(scope, data, VLC->modh->normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), index);
assert(objects[(si32)index] == nullptr); // ensure that this id was not loaded before
objects[(si32)index] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
}
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID)
void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID)
{
static const bool isSubObject = true;
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL
assert(objects.count(ID));
if (subID)
{
assert(objects.at(ID)->subObjects.count(subID.get()) == 0);
assert(config["index"].isNull());
config["index"].Float() = subID.get();
config["index"].setMeta(config.meta);
}
assert(ID < objects.size());
assert(objects[ID]);
JsonUtils::inherit(config, objects.at(ID)->base);
loadObjectEntry(identifier, config, objects[ID], isSubObject);
loadSubObject(config.meta, identifier, config, objects[ID], subID);
}
void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)
{
assert(objects.count(ID));
assert(objects.at(ID)->subObjects.count(subID));
objects.at(ID)->subObjects.erase(subID); //TODO: cleanup string id map
assert(ID < objects.size());
assert(objects[ID]);
assert(subID < objects[ID]->objects.size());
objects[ID]->objects[subID] = nullptr;
}
std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
@@ -311,14 +270,11 @@ std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const
{
if (objects.count(type))
{
if (objects.at(type)->subObjects.count(subtype))
return objects.at(type)->subObjects.at(subtype);
}
std::string errorString = "Failed to find object of type " + std::to_string(type) + "::" + std::to_string(subtype);
logGlobal->error(errorString);
throw std::runtime_error(errorString);
assert(type < objects.size());
assert(objects[type]);
assert(subtype < objects[type]->objects.size());
return objects.at(type)->objects.at(subtype);
}
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string scope, std::string type, std::string subtype) const
@@ -326,14 +282,13 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string scope, std::
boost::optional<si32> id = VLC->modh->identifiers.getIdentifier(scope, "object", type, false);
if(id)
{
auto object = objects.at(id.get());
if(object->subIds.count(subtype))
{
si32 subId = object->subIds.at(subtype);
auto object = objects[id.get()];
boost::optional<si32> subID = VLC->modh->identifiers.getIdentifier(scope, object->getIdentifier(), subtype, false);
return object->subObjects.at(subId);
}
if (subID)
return object->objects[subID.get()];
}
std::string errorString = "Failed to find object of type " + type + "::" + subtype;
logGlobal->error(errorString);
throw std::runtime_error(errorString);
@@ -349,20 +304,23 @@ std::set<si32> CObjectClassesHandler::knownObjects() const
std::set<si32> ret;
for (auto entry : objects)
ret.insert(entry.first);
if (entry)
ret.insert(entry->id);
return ret;
}
std::set<si32> CObjectClassesHandler::knownSubObjects(si32 primaryID) const
{
assert(primaryID < objects.size());
assert(objects[primaryID]);
std::set<si32> ret;
if (objects.count(primaryID))
{
for (auto entry : objects.at(primaryID)->subObjects)
ret.insert(entry.first);
}
for (auto entry : objects.at(primaryID)->objects)
if (entry)
ret.insert(entry->subtype);
return ret;
}
@@ -380,16 +338,19 @@ void CObjectClassesHandler::afterLoadFinalization()
{
for(auto entry : objects)
{
for(auto obj : entry.second->subObjects)
if (!entry)
continue;
for(auto obj : entry->objects)
{
obj.second->afterLoadFinalization();
if(obj.second->getTemplates().empty())
logGlobal->warn("No templates found for %d:%d", entry.first, obj.first);
obj->afterLoadFinalization();
if(obj->getTemplates().empty())
logGlobal->warn("No templates found for %s:%s", entry->getIdentifier(), obj->getIdentifier());
}
}
//duplicate existing two-way portals to make reserve for RMG
auto& portalVec = objects[Obj::MONOLITH_TWO_WAY]->subObjects;
auto& portalVec = objects[Obj::MONOLITH_TWO_WAY]->objects;
size_t portalCount = portalVec.size();
size_t currentIndex = portalCount;
while(portalVec.size() < 100)
@@ -399,39 +360,18 @@ void CObjectClassesHandler::afterLoadFinalization()
}
}
std::string CObjectClassesHandler::getObjectName(si32 type) const
{
if (objects.count(type))
return objects.at(type)->getNameTranslated();
logGlobal->error("Access to non existing object of type %d", type);
return "";
}
std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const
{
if (knownSubObjects(type).count(subtype))
{
auto name = getHandlerFor(type, subtype)->getCustomName();
if (name)
return name.get();
}
return getObjectName(type);
}
SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type) const
{
if(objects.count(type))
return objects.at(type)->sounds;
logGlobal->error("Access to non existing object of type %d", type);
return SObjectSounds();
return getHandlerFor(type, subtype)->getNameTranslated();
}
SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) const
{
if(knownSubObjects(type).count(subtype))
return getHandlerFor(type, subtype)->getSounds();
else
return getObjectSounds(type);
assert(type < objects.size());
assert(objects[type]);
assert(subtype < objects[type]->objects.size());
return getHandlerFor(type, subtype)->getSounds();
}
std::string CObjectClassesHandler::getObjectHandlerName(si32 type) const
@@ -439,11 +379,6 @@ std::string CObjectClassesHandler::getObjectHandlerName(si32 type) const
return objects.at(type)->handlerName;
}
boost::optional<si32> CObjectClassesHandler::getObjGroupAiValue(si32 primaryID) const
{
return objects.at(primaryID)->groupDefaultAiValue;
}
AObjectTypeHandler::AObjectTypeHandler():
type(-1), subtype(-1)
{
@@ -466,6 +401,21 @@ void AObjectTypeHandler::setTypeName(std::string type, std::string subtype)
this->subTypeName = subtype;
}
std::string AObjectTypeHandler::getIdentifier() const
{
return modName + ":" + subTypeName;
}
std::string AObjectTypeHandler::getTypeName()
{
return typeName;
}
std::string AObjectTypeHandler::getSubTypeName()
{
return subTypeName;
}
static ui32 loadJsonOrMax(const JsonNode & input)
{
if (input.isNull())
@@ -474,7 +424,7 @@ static ui32 loadJsonOrMax(const JsonNode & input)
return static_cast<ui32>(input.Float());
}
void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::string> name)
void AObjectTypeHandler::init(const JsonNode & input)
{
base = input["base"];
@@ -506,10 +456,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
}
}
if (input["name"].isNull())
objectName = name;
else
objectName.reset(input["name"].String());
VLC->generaltexth->registerString(getNameTextID(), input["name"].String());
for(const JsonNode & node : input["sounds"]["ambient"].Vector())
sounds.ambient.push_back(node.String());
@@ -551,9 +498,14 @@ void AObjectTypeHandler::initTypeData(const JsonNode & input)
// empty implementation for overrides
}
boost::optional<std::string> AObjectTypeHandler::getCustomName() const
std::string AObjectTypeHandler::getNameTextID() const
{
return objectName;
return TextIdentifier( typeName, modName, subTypeName, "name" ).get();
}
std::string AObjectTypeHandler::getNameTranslated() const
{
return VLC->generaltexth->translate(getNameTextID());
}
SObjectSounds AObjectTypeHandler::getSounds() const

View File

@@ -136,12 +136,13 @@ public:
class CGObjectInstance;
/// Class responsible for creation of objects of specific type & subtype
class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
{
RandomMapInfo rmgInfo;
/// Human-readable name of this object, used for objects like banks and dwellings, if set
boost::optional<std::string> objectName;
/// Text ID for human-readable name of this object, used for objects like banks and dwellings, if set
boost::optional<std::string> nameTextID;
JsonNode base; /// describes base template
@@ -150,7 +151,6 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
SObjectSounds sounds;
boost::optional<si32> aiValue;
boost::optional<std::string> battlefield;
protected:
@@ -159,9 +159,10 @@ protected:
/// initialization for classes that inherit this one
virtual void initTypeData(const JsonNode & input);
public:
std::string modName;
std::string typeName;
std::string subTypeName;
public:
si32 type;
si32 subtype;
@@ -171,11 +172,20 @@ public:
void setType(si32 type, si32 subtype);
void setTypeName(std::string type, std::string subtype);
std::string getTypeName();
std::string getSubTypeName();
/// loads generic data from Json structure and passes it towards type-specific constructors
void init(const JsonNode & input, boost::optional<std::string> name = boost::optional<std::string>());
void init(const JsonNode & input);
/// returns full form of identifier of this object in form of modName:objectName
std::string getIdentifier() const;
/// returns objet's name in form of translatable text ID
std::string getNameTextID() const;
std::string getNameTranslated() const;
/// Returns object-specific name, if set
boost::optional<std::string> getCustomName() const;
SObjectSounds getSounds() const;
void addTemplate(std::shared_ptr<const ObjectTemplate> templ);
@@ -191,9 +201,6 @@ public:
BattleField getBattlefield() const;
/// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle)
/// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server)
const RandomMapInfo & getRMGInfo();
boost::optional<si32> getAiValue() const;
@@ -219,7 +226,8 @@ public:
h & subtype;
h & templates;
h & rmgInfo;
h & objectName;
h & nameTextID;
h & modName;
h & typeName;
h & subTypeName;
h & sounds;
@@ -230,52 +238,42 @@ public:
typedef std::shared_ptr<AObjectTypeHandler> TObjectTypeHandler;
/// Class responsible for creation of adventure map objects of specific type
class DLL_LINKAGE ObjectClass
{
std::string modScope;
std::string identifier;
public:
ObjectClass() = default;
ObjectClass(const std::string & modScope, const std::string & identifier):
identifier(identifier),
modScope(modScope)
{}
si32 id;
std::string handlerName; // ID of handler that controls this object, should be determined using handlerConstructor map
JsonNode base;
std::vector<TObjectTypeHandler> objects;
std::string getIdentifier() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & handlerName;
h & base;
h & objects;
h & identifier;
h & modScope;
}
};
/// Main class responsible for creation of all adventure map objects
class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
{
/// Small internal structure that contains information on specific group of objects
/// (creating separate entity is overcomplicating at least at this point)
class DLL_LINKAGE ObjectContainter
{
std::string identifier;
std::string modScope;
public:
ObjectContainter() = default;
ObjectContainter(const std::string & modScope, const std::string & identifier):
identifier(identifier),
modScope(modScope)
{}
si32 id;
std::string handlerName; // ID of handler that controls this object, should be determined using handlerConstructor map
JsonNode base;
std::map<si32, TObjectTypeHandler> subObjects;
std::map<std::string, si32> subIds;//full id from core scope -> subtype
SObjectSounds sounds;
boost::optional<si32> groupDefaultAiValue;
std::string getIdentifier() const;
std::string getNameTextID() const; // {scope, "objects", name, "name"}
std::string getNameTranslated() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & handlerName;
h & base;
h & subObjects;
h & identifier;
h & modScope;
h & subIds;
h & sounds;
h & groupDefaultAiValue;
}
};
/// list of object handlers, each of them handles only one type
std::map<si32, ObjectContainter * > objects;
std::vector<ObjectClass * > objects;
/// map that is filled during contruction with all known handlers. Not serializeable due to usage of std::function
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
@@ -284,12 +282,12 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
typedef std::multimap<std::pair<si32, si32>, std::shared_ptr<const ObjectTemplate>> TTemplatesContainer;
TTemplatesContainer legacyTemplates;
/// contains list of custom names for H3 objects (e.g. Dwellings), used to load H3 data
/// format: customNames[primaryID][secondaryID] -> name
std::map<si32, std::vector<std::string>> customNames;
TObjectTypeHandler loadSubObjectFromJson(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index);
void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj, bool isSubobject = false);
ObjectContainter * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name);
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj);
void loadSubObject(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index);
ObjectClass * loadFromJson(const std::string & scope, const JsonNode & json, const std::string & name, size_t index);
public:
CObjectClassesHandler();
@@ -300,7 +298,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID);
void removeSubObject(si32 ID, si32 subID);
void beforeValidate(JsonNode & object) override;
@@ -317,17 +315,13 @@ public:
TObjectTypeHandler getHandlerFor(std::string scope, std::string type, std::string subtype) const;
TObjectTypeHandler getHandlerFor(CompoundMapObjectID compoundIdentifier) const;
std::string getObjectName(si32 type) const;
std::string getObjectName(si32 type, si32 subtype) const;
SObjectSounds getObjectSounds(si32 type) const;
SObjectSounds getObjectSounds(si32 type, si32 subtype) const;
/// Returns handler string describing the handler (for use in client)
std::string getObjectHandlerName(si32 type) const;
boost::optional<si32> getObjGroupAiValue(si32 primaryID) const; //default AI value of objects belonging to particular primaryID
template <typename Handler> void serialize(Handler &h, const int version)
{
h & objects;

View File

@@ -1171,7 +1171,7 @@ void CGBorderGuard::getVisitText (MetaString &text, std::vector<Component> &comp
void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const
{
if (!onHover)
text << VLC->generaltexth->tentColors[subID] << " " << VLC->objtypeh->getObjectName(Obj::KEYMASTER);
text << VLC->generaltexth->tentColors[subID] << " " << VLC->objtypeh->getObjectName(Obj::KEYMASTER, subID);
}
bool CGBorderGuard::checkQuest(const CGHeroInstance * h) const

View File

@@ -172,7 +172,7 @@ namespace TriggeredEventsDetail
{
si32 subtype = *subtypes.begin();
auto handler = VLC->objtypeh->getHandlerFor(type, subtype);
identifier = handler->typeName;
identifier = handler->getTypeName();
}
}
break;
@@ -1141,7 +1141,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
pos.z = static_cast<si32>(configuration["l"].Float());
//special case for grail
if(typeName == "grail")
if(typeName == "grail")
{
owner->map->grailPos = pos;

View File

@@ -477,10 +477,7 @@ void MainWindow::addGroupIntoCatalog(const std::string & groupName, bool useCust
if(staticOnly && !factory->isStaticObject())
continue;
auto subGroupName = QString::fromStdString(factory->subTypeName);
auto customName = factory->getCustomName();
if(customName)
subGroupName = tr(customName->c_str());
auto subGroupName = QString::fromStdString(factory->getNameTranslated());
auto * itemType = new QStandardItem(subGroupName);
for(int templateId = 0; templateId < templates.size(); ++templateId)

View File

@@ -106,8 +106,8 @@ void MapController::repairMap()
if(obj->ID != Obj::HERO && obj->ID != Obj::PRISON && (obj->typeName.empty() || obj->subTypeName.empty()))
{
auto handler = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID);
obj->typeName = handler->typeName;
obj->subTypeName = handler->subTypeName;
obj->typeName = handler->getTypeName();
obj->subTypeName = handler->getSubTypeName();
}
//fix flags
if(obj->getOwner() == PlayerColor::UNFLAGGABLE)