1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-09-16 09:26:28 +02:00

Implemented (basic) object construction and placement

* contain refactoring of RMG and class handling (will be more)
This commit is contained in:
AlexVinS
2015-11-14 16:50:29 +03:00
parent 9cc3dae5fe
commit 2d5a366e6c
14 changed files with 334 additions and 209 deletions

View File

@@ -207,6 +207,28 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
artifacts.push_back(object); artifacts.push_back(object);
VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ.setMeta(scope);
templ["animation"].String() = object->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
});
registerObject(scope, "artifact", object->identifier, object->id); registerObject(scope, "artifact", object->identifier, object->id);
} }
@@ -219,6 +241,27 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
assert(artifacts[index] == nullptr); // ensure that this id was not loaded before assert(artifacts[index] == nullptr); // ensure that this id was not loaded before
artifacts[index] = object; artifacts[index] = object;
VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::ARTIFACT, object->id.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ.setMeta(scope);
templ["animation"].String() = object->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, object->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, object->id);
});
registerObject(scope, "artifact", object->identifier, object->id); registerObject(scope, "artifact", object->identifier, object->id);
} }
@@ -280,16 +323,16 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
void CArtHandler::addSlot(CArtifact * art, const std::string & slotID) void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
{ {
static const std::vector<ArtifactPosition> miscSlots = static const std::vector<ArtifactPosition> miscSlots =
{ {
ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5 ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5
}; };
static const std::vector<ArtifactPosition> ringSlots = static const std::vector<ArtifactPosition> ringSlots =
{ {
ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING
}; };
if (slotID == "MISC") if (slotID == "MISC")
{ {
vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots); vstd::concatenate(art->possibleSlots[ArtBearer::HERO], miscSlots);
@@ -323,7 +366,7 @@ void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node)
CArtifact::EartClass CArtHandler::stringToClass(std::string className) CArtifact::EartClass CArtHandler::stringToClass(std::string className)
{ {
static const std::map<std::string, CArtifact::EartClass> artifactClassMap = static const std::map<std::string, CArtifact::EartClass> artifactClassMap =
{ {
{"TREASURE", CArtifact::ART_TREASURE}, {"TREASURE", CArtifact::ART_TREASURE},
{"MINOR", CArtifact::ART_MINOR}, {"MINOR", CArtifact::ART_MINOR},
{"MAJOR", CArtifact::ART_MAJOR}, {"MAJOR", CArtifact::ART_MAJOR},
@@ -694,24 +737,6 @@ void CArtHandler::afterLoadFinalization()
bonus->sid = art->id; bonus->sid = art->id;
} }
} }
for (CArtifact * art : artifacts)
{
VLC->objtypeh->loadSubObject(art->Name(), JsonNode(), Obj::ARTIFACT, art->id.num);
if (!art->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = art->advMapDef;
// add new template.
// Necessary for objects added via mods that don't have any templates in H3
VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-art like lock)
if (VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::ARTIFACT, art->id);
}
} }
CArtifactInstance::CArtifactInstance() CArtifactInstance::CArtifactInstance()

View File

@@ -166,12 +166,12 @@ static void AddAbility(CCreature *cre, const JsonVector &ability_vec)
} }
nsf->type = it->second; nsf->type = it->second;
JsonUtils::parseTypedBonusShort(ability_vec,nsf); JsonUtils::parseTypedBonusShort(ability_vec,nsf);
nsf->source = Bonus::CREATURE_ABILITY; nsf->source = Bonus::CREATURE_ABILITY;
nsf->sid = cre->idNumber; nsf->sid = cre->idNumber;
cre->addNewBonus(nsf); cre->addNewBonus(nsf);
} }
@@ -363,6 +363,24 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
creatures.push_back(object); creatures.push_back(object);
VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = object->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
});
registerObject(scope, "creature", name, object->idNumber); registerObject(scope, "creature", name, object->idNumber);
for(auto node : data["extraNames"].Vector()) for(auto node : data["extraNames"].Vector())
@@ -385,6 +403,24 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
assert(creatures[index] == nullptr); // ensure that this id was not loaded before assert(creatures[index] == nullptr); // ensure that this id was not loaded before
creatures[index] = object; creatures[index] = object;
VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
{
JsonNode conf;
conf.setMeta(scope);
VLC->objtypeh->loadSubObject(object->identifier, conf, Obj::MONSTER, object->idNumber.num);
if (!object->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = object->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, object->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, object->idNumber.num);
});
registerObject(scope, "creature", name, object->idNumber); registerObject(scope, "creature", name, object->idNumber);
for(auto & node : data["extraNames"].Vector()) for(auto & node : data["extraNames"].Vector())
{ {
@@ -1124,20 +1160,7 @@ void CCreatureHandler::buildBonusTreeForTiers()
void CCreatureHandler::afterLoadFinalization() void CCreatureHandler::afterLoadFinalization()
{ {
for (CCreature * crea : creatures)
{
VLC->objtypeh->loadSubObject(crea->nameSing, JsonNode(), Obj::MONSTER, crea->idNumber.num);
if (!crea->advMapDef.empty())
{
JsonNode templ;
templ["animation"].String() = crea->advMapDef;
VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber)->addTemplate(templ);
}
// object does not have any templates - this is not usable object (e.g. pseudo-creature like Arrow Tower)
if (VLC->objtypeh->getHandlerFor(Obj::MONSTER, crea->idNumber.num)->getTemplates().empty())
VLC->objtypeh->removeSubObject(Obj::MONSTER, crea->idNumber.num);
}
} }
void CCreatureHandler::deserializationFix() void CCreatureHandler::deserializationFix()

View File

@@ -205,6 +205,13 @@ void CHeroClassHandler::loadObject(std::string scope, std::string name, const Js
VLC->objtypeh->loadSubObject(name, classConf, index, object->id); VLC->objtypeh->loadSubObject(name, classConf, index, object->id);
}); });
// VLC->modh->identifiers.requestIdentifier(scope, "object", "prison", [=](si32 index)
// {
// JsonNode conf;
// conf.setMeta(scope);
// VLC->objtypeh->loadSubObject(name, conf, index, object->id);
// });
VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id); VLC->modh->identifiers.registerObject(scope, "heroClass", name, object->id);
} }

View File

@@ -101,9 +101,9 @@ void CIdentifierStorage::requestIdentifier(std::string scope, std::string type,
void CIdentifierStorage::requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)>& callback) void CIdentifierStorage::requestIdentifier(std::string scope, std::string fullName, const std::function<void(si32)>& callback)
{ {
auto scopeAndFullName = splitString(fullName, ':'); auto scopeAndFullName = splitString(fullName, ':');
auto typeAndName = splitString(scopeAndFullName.second, '.'); auto typeAndName = splitString(scopeAndFullName.second, '.');
requestIdentifier(ObjectCallback(scope, scopeAndFullName.first, typeAndName.first, typeAndName.second, callback, false)); requestIdentifier(ObjectCallback(scope, scopeAndFullName.first, typeAndName.first, typeAndName.second, callback, false));
} }
@@ -186,6 +186,7 @@ void CIdentifierStorage::registerObject(std::string scope, std::string type, std
checkIdentifier(fullID); checkIdentifier(fullID);
registeredObjects.insert(std::make_pair(fullID, data)); registeredObjects.insert(std::make_pair(fullID, data));
logGlobal->traceStream() << scope << "::" << fullID;
} }
std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request) std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request)
@@ -331,11 +332,11 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
{ {
ModInfo & modInfo = modData[modName]; ModInfo & modInfo = modData[modName];
bool result = true; bool result = true;
auto performValidate = [&,this](JsonNode & data, const std::string & name){ auto performValidate = [&,this](JsonNode & data, const std::string & name){
handler->beforeValidate(data); handler->beforeValidate(data);
if (validate) if (validate)
result &= JsonUtils::validate(data, "vcmi:" + objectName, name); result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
}; };
// apply patches // apply patches
@@ -355,7 +356,7 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
if (originalData.size() > index) if (originalData.size() > index)
{ {
JsonUtils::merge(originalData[index], data); JsonUtils::merge(originalData[index], data);
performValidate(originalData[index],name); performValidate(originalData[index],name);
handler->loadObject(modName, name, originalData[index], index); handler->loadObject(modName, name, originalData[index], index);

View File

@@ -74,7 +74,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("pandora", CGPandoraBox); SET_HANDLER("pandora", CGPandoraBox);
SET_HANDLER("pickable", CGPickable); SET_HANDLER("pickable", CGPickable);
SET_HANDLER("prison", CGHeroInstance); SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("questGuard", CGQuestGuard); SET_HANDLER("questGuard", CGQuestGuard);
SET_HANDLER("resource", CGResource); SET_HANDLER("resource", CGResource);
SET_HANDLER("scholar", CGScholar); SET_HANDLER("scholar", CGScholar);
@@ -148,17 +147,18 @@ si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID)
return defaultID; // some H3M objects loaded, first modded found return defaultID; // some H3M objects loaded, first modded found
} }
void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContainter * obj) void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj)
{ {
if (!handlerConstructors.count(obj->handlerName)) if (!handlerConstructors.count(obj->handlerName))
{ {
logGlobal->errorStream() << "Handler with name " << obj->handlerName << " was not found!"; logGlobal->errorStream() << "Handler with name " << obj->handlerName << " was not found!";
return; return;
} }
si32 id = selectNextID(entry["index"], obj->objects, 1000); si32 id = selectNextID(entry["index"], obj->subObjects, 1000);
auto handler = handlerConstructors.at(obj->handlerName)(); auto handler = handlerConstructors.at(obj->handlerName)();
handler->setType(obj->id, id); handler->setType(obj->id, id);
handler->setTypeName(obj->identifier, identifier);
if (customNames.count(obj->id) && customNames.at(obj->id).size() > id) if (customNames.count(obj->id) && customNames.at(obj->id).size() > id)
handler->init(entry, customNames.at(obj->id).at(id)); handler->init(entry, customNames.at(obj->id).at(id));
@@ -175,50 +175,49 @@ void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContai
legacyTemplates.erase(range.first, range.second); legacyTemplates.erase(range.first, range.second);
} }
logGlobal->debugStream() << "Loaded object " << obj->id << ":" << id; logGlobal->debugStream() << "Loaded object " << obj->identifier << "(" << obj->id << ")" << ":" << identifier << "(" << id << ")" ;
assert(!obj->objects.count(id)); // DO NOT override assert(!obj->subObjects.count(id)); // DO NOT override
obj->objects[id] = handler; obj->subObjects[id] = handler;
obj->subIds[identifier] = id;//todo: scope
} }
CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json) CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json, const std::string & name)
{ {
auto obj = new ObjectContainter(); auto obj = new ObjectContainter();
obj->identifier = name;
obj->name = json["name"].String(); obj->name = json["name"].String();
obj->handlerName = json["handler"].String(); obj->handlerName = json["handler"].String();
obj->base = json["base"]; obj->base = json["base"];
obj->id = selectNextID(json["index"], objects, 256); obj->id = selectNextID(json["index"], objects, 256);
for (auto entry : json["types"].Struct()) for (auto entry : json["types"].Struct())
{ {
loadObjectEntry(entry.second, obj); loadObjectEntry(entry.first, entry.second, obj);
} }
return obj; return obj;
} }
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data) void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{ {
auto object = loadFromJson(data); auto object = loadFromJson(data, name);
objects[object->id] = object; objects[object->id] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id); VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
} }
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{ {
auto object = loadFromJson(data); auto object = loadFromJson(data, name);
assert(objects[index] == nullptr); // ensure that this id was not loaded before assert(objects[index] == nullptr); // ensure that this id was not loaded before
objects[index] = object; objects[index] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id); VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
} }
void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID) void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID)
{ {
config.setType(JsonNode::DATA_STRUCT); // ensure that input is not NULL config.setType(JsonNode::DATA_STRUCT); // ensure that input is not NULL
assert(objects.count(ID)); assert(objects.count(ID));
if (subID) if (subID)
{ {
assert(objects.at(ID)->objects.count(subID.get()) == 0); assert(objects.at(ID)->subObjects.count(subID.get()) == 0);
assert(config["index"].isNull()); assert(config["index"].isNull());
config["index"].Float() = subID.get(); config["index"].Float() = subID.get();
} }
@@ -227,14 +226,14 @@ void CObjectClassesHandler::loadSubObject(std::string name, JsonNode config, si3
JsonUtils::inherit(config, objects.at(ID)->base); JsonUtils::inherit(config, objects.at(ID)->base);
config.setMeta(oldMeta); config.setMeta(oldMeta);
loadObjectEntry(config, objects[ID]); loadObjectEntry(identifier, config, objects[ID]);
} }
void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID) void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID)
{ {
assert(objects.count(ID)); assert(objects.count(ID));
assert(objects.at(ID)->objects.count(subID)); assert(objects.at(ID)->subObjects.count(subID));
objects.at(ID)->objects.erase(subID); objects.at(ID)->subObjects.erase(subID); //TODO: cleanup string id map
} }
std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
@@ -246,14 +245,28 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
{ {
if (objects.count(type)) if (objects.count(type))
{ {
if (objects.at(type)->objects.count(subtype)) if (objects.at(type)->subObjects.count(subtype))
return objects.at(type)->objects.at(subtype); return objects.at(type)->subObjects.at(subtype);
} }
logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype; logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
assert(0); // FIXME: throw error? throw std::runtime_error("Object type handler not found");
return nullptr; return nullptr;
} }
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string type, std::string subtype) const
{
boost::optional<si32> id = VLC->modh->identifiers.getIdentifier("core", "object", type, false);
if(id)
{
si32 subId = objects.at(id.get())->subIds.at(subtype);
return objects.at(id.get())->subObjects.at(subId);
}
logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
throw std::runtime_error("Object type handler not found");
return nullptr;
}
std::set<si32> CObjectClassesHandler::knownObjects() const std::set<si32> CObjectClassesHandler::knownObjects() const
{ {
std::set<si32> ret; std::set<si32> ret;
@@ -270,7 +283,7 @@ std::set<si32> CObjectClassesHandler::knownSubObjects(si32 primaryID) const
if (objects.count(primaryID)) if (objects.count(primaryID))
{ {
for (auto entry : objects.at(primaryID)->objects) for (auto entry : objects.at(primaryID)->subObjects)
ret.insert(entry.first); ret.insert(entry.first);
} }
return ret; return ret;
@@ -292,7 +305,7 @@ void CObjectClassesHandler::afterLoadFinalization()
{ {
for (auto entry : objects) for (auto entry : objects)
{ {
for (auto obj : entry.second->objects) for (auto obj : entry.second->subObjects)
{ {
obj.second->afterLoadFinalization(); obj.second->afterLoadFinalization();
if (obj.second->getTemplates().empty()) if (obj.second->getTemplates().empty())
@@ -331,6 +344,12 @@ void AObjectTypeHandler::setType(si32 type, si32 subtype)
this->subtype = subtype; this->subtype = subtype;
} }
void AObjectTypeHandler::setTypeName(std::string type, std::string subtype)
{
this->typeName = type;
this->subTypeName = subtype;
}
static ui32 loadJsonOrMax(const JsonNode & input) static ui32 loadJsonOrMax(const JsonNode & input)
{ {
if (input.isNull()) if (input.isNull())
@@ -377,6 +396,14 @@ bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemp
return false; // by default there are no overrides return false; // by default there are no overrides
} }
void AObjectTypeHandler::preInitObject(CGObjectInstance * obj) const
{
obj->ID = Obj(type);
obj->subID = subtype;
obj->typeName = typeName;
obj->subTypeName = subTypeName;
}
void AObjectTypeHandler::initTypeData(const JsonNode & input) void AObjectTypeHandler::initTypeData(const JsonNode & input)
{ {
// empty implementation for overrides // empty implementation for overrides

View File

@@ -104,11 +104,14 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
si32 type; si32 type;
si32 subtype; si32 subtype;
std::string typeName;
std::string subTypeName;
JsonNode base; /// describes base template JsonNode base; /// describes base template
std::vector<ObjectTemplate> templates; std::vector<ObjectTemplate> templates;
protected: protected:
void preInitObject(CGObjectInstance * obj) const;
virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const; virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
/// initialization for classes that inherit this one /// initialization for classes that inherit this one
@@ -117,6 +120,7 @@ public:
virtual ~AObjectTypeHandler(){} virtual ~AObjectTypeHandler(){}
void setType(si32 type, si32 subtype); void setType(si32 type, si32 subtype);
void setTypeName(std::string type, std::string subtype);
/// loads generic data from Json structure and passes it towards type-specific constructors /// 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, boost::optional<std::string> name = boost::optional<std::string>());
@@ -149,12 +153,16 @@ public:
/// This should set remaining properties, including randomized or depending on map /// This should set remaining properties, including randomized or depending on map
virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0;
/// Returns object configuration, if available. Othervice returns NULL /// Returns object configuration, if available. Otherwise returns NULL
virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const = 0; virtual std::unique_ptr<IObjectInfo> getObjectInfo(ObjectTemplate tmpl) const = 0;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & type & subtype & templates & rmgInfo & objectName; h & type & subtype & templates & rmgInfo & objectName;
if(version >= 755)
{
h & typeName & subTypeName;
}
} }
}; };
@@ -167,16 +175,21 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
struct ObjectContainter struct ObjectContainter
{ {
si32 id; si32 id;
std::string identifier;
std::string name; // human-readable name std::string name; // human-readable name
std::string handlerName; // ID of handler that controls this object, shoul be determined using hadlerConstructor map std::string handlerName; // ID of handler that controls this object, should be determined using handlerConstructor map
JsonNode base; JsonNode base;
std::map<si32, TObjectTypeHandler> objects; std::map<si32, TObjectTypeHandler> subObjects;
std::map<std::string, si32> subIds;//full id from core scope -> subtype
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & name & handlerName & base & objects; h & name & handlerName & base & subObjects;
if(version >= 755)
{
h & identifier & subIds;
}
} }
}; };
@@ -194,8 +207,8 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
/// format: customNames[primaryID][secondaryID] -> name /// format: customNames[primaryID][secondaryID] -> name
std::map<si32, std::vector<std::string>> customNames; std::map<si32, std::vector<std::string>> customNames;
void loadObjectEntry(const JsonNode & entry, ObjectContainter * obj); void loadObjectEntry(const std::string & identifier, const JsonNode & entry, ObjectContainter * obj);
ObjectContainter * loadFromJson(const JsonNode & json); ObjectContainter * loadFromJson(const JsonNode & json, const std::string & name);
public: public:
CObjectClassesHandler(); CObjectClassesHandler();
@@ -204,7 +217,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) override;
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
void loadSubObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>()); void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
void removeSubObject(si32 ID, si32 subID); void removeSubObject(si32 ID, si32 subID);
void beforeValidate(JsonNode & object) override; void beforeValidate(JsonNode & object) override;
@@ -218,10 +231,11 @@ public:
/// returns handler for specified object (ID-based). ObjectHandler keeps ownership /// returns handler for specified object (ID-based). ObjectHandler keeps ownership
TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const; TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const;
TObjectTypeHandler getHandlerFor(std::string type, std::string subtype) const;
std::string getObjectName(si32 type) const; std::string getObjectName(si32 type) const;
std::string getObjectName(si32 type, si32 subtype) const; std::string getObjectName(si32 type, si32 subtype) const;
/// Returns handler string describing the handler (for use in client) /// Returns handler string describing the handler (for use in client)
std::string getObjectHandlerName(si32 type) const; std::string getObjectHandlerName(si32 type) const;

View File

@@ -326,7 +326,14 @@ bool CGObjectInstance::passableFor(PlayerColor color) const
void CGObjectInstance::writeJson(JsonNode & json, bool withState) const void CGObjectInstance::writeJson(JsonNode & json, bool withState) const
{ {
logGlobal->debugStream() <<"Save: [" << pos << "] " << id << " " << ID << " " << subID << " " << typeName << " " << subTypeName;
json.setType(JsonNode::DATA_STRUCT); json.setType(JsonNode::DATA_STRUCT);
json["type"].String() = typeName;
json["subType"].String() = subTypeName;
json["x"].Float() = pos.x;
json["y"].Float() = pos.y;
json["l"].Float() = pos.z;
appearance.writeJson(json["template"]); appearance.writeJson(json["template"]);
writeJsonOptions(json["options"]); writeJsonOptions(json["options"]);
@@ -341,10 +348,16 @@ void CGObjectInstance::readJson(const JsonNode & json, bool withState)
logGlobal->error("Invalid object instance data"); logGlobal->error("Invalid object instance data");
return; return;
} }
pos.x = json["x"].Float();
pos.y = json["y"].Float();
pos.z = json["l"].Float();
appearance.readJson(json["template"]); appearance.readJson(json["template"]);
readJsonOptions(json["options"]); readJsonOptions(json["options"]);
if(withState) if(withState)
readJsonState(json["state"]); readJsonState(json["state"]);
logGlobal->debugStream() <<"Load: [" << pos << "] " << id << " " << ID << " " << subID << " " << typeName << " " << subTypeName;
} }
void CGObjectInstance::writeJsonOptions(JsonNode & json) const void CGObjectInstance::writeJsonOptions(JsonNode & json) const

View File

@@ -113,6 +113,9 @@ public:
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object /// If true hero can visit this object only from neighbouring tiles and can't stand on this object
bool blockVisit; bool blockVisit;
std::string typeName;
std::string subTypeName;
CGObjectInstance(); CGObjectInstance();
~CGObjectInstance(); ~CGObjectInstance();
@@ -171,6 +174,11 @@ public:
{ {
h & pos & ID & subID & id & tempOwner & blockVisit & appearance; h & pos & ID & subID & id & tempOwner & blockVisit & appearance;
//definfo is handled by map serializer //definfo is handled by map serializer
if(version >= 755)
{
h & typeName & subTypeName;
}
} }
///Entry point of Json serialization ///Entry point of Json serialization

View File

@@ -188,6 +188,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
CGObjectInstance * CRewardableConstructor::create(ObjectTemplate tmpl) const CGObjectInstance * CRewardableConstructor::create(ObjectTemplate tmpl) const
{ {
auto ret = new CRewardableObject(); auto ret = new CRewardableObject();
preInitObject(ret);
ret->appearance = tmpl; ret->appearance = tmpl;
return ret; return ret;
} }

View File

@@ -31,8 +31,7 @@ protected:
ObjectType * createTyped(ObjectTemplate tmpl) const ObjectType * createTyped(ObjectTemplate tmpl) const
{ {
auto obj = new ObjectType(); auto obj = new ObjectType();
obj->ID = tmpl.id; preInitObject(obj);
obj->subID = tmpl.subid;
obj->appearance = tmpl; obj->appearance = tmpl;
return obj; return obj;
} }

View File

@@ -284,6 +284,13 @@ void CMapLoaderJson::readMap()
map->initTerrain(); map->initTerrain();
readTerrain(); readTerrain();
readObjects(); readObjects();
// Calculate blocked / visitable positions
for(auto & elem : map->objects)
{
map->addBlockVisTiles(elem);
}
map->calculateGuardingGreaturePositions();
} }
void CMapLoaderJson::readHeader() void CMapLoaderJson::readHeader()
@@ -569,6 +576,7 @@ CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, const
void CMapLoaderJson::MapObjectLoader::construct() void CMapLoaderJson::MapObjectLoader::construct()
{ {
//TODO:consider move to ObjectTypeHandler
//find type handler //find type handler
std::string typeName = configuration["type"].String(), subTypeName = configuration["subType"].String(); std::string typeName = configuration["type"].String(), subTypeName = configuration["subType"].String();
if(typeName.empty()) if(typeName.empty())
@@ -584,13 +592,16 @@ void CMapLoaderJson::MapObjectLoader::construct()
si32 type = owner->getIdentifier("object", typeName); si32 type = owner->getIdentifier("object", typeName);
// VLC->objtypeh->getHandlerFor() handler = VLC->objtypeh->getHandlerFor(typeName, subTypeName);
//TODO:MapObjectLoader::construct()
instance = handler->create(ObjectTemplate());
instance->id = ObjectInstanceID(owner->map->objects.size());
owner->map->objects.push_back(instance);
} }
void CMapLoaderJson::MapObjectLoader::configure() void CMapLoaderJson::MapObjectLoader::configure()
{ {
//TODO:MapObjectLoader::configure() instance->readJson(configuration, false);
} }
void CMapLoaderJson::readObjects() void CMapLoaderJson::readObjects()

View File

@@ -32,7 +32,7 @@ void CMapGenerator::foreachDirectNeighbour(const int3& pos, std::function<void(i
int3 n = pos + dir; int3 n = pos + dir;
if(map->isInTheMap(n)) if(map->isInTheMap(n))
foo(n); foo(n);
} }
} }
@@ -189,7 +189,7 @@ void CMapGenerator::addPlayerInfo()
playerCount = mapGenOptions->getCompOnlyPlayerCount(); playerCount = mapGenOptions->getCompOnlyPlayerCount();
teamCount = mapGenOptions->getCompOnlyTeamCount(); teamCount = mapGenOptions->getCompOnlyTeamCount();
} }
if(playerCount == 0) if(playerCount == 0)
{ {
continue; continue;
@@ -259,7 +259,7 @@ void CMapGenerator::genZones()
} }
void CMapGenerator::fillZones() void CMapGenerator::fillZones()
{ {
//init native town count with 0 //init native town count with 0
for (auto faction : VLC->townh->getAllowedFactions()) for (auto faction : VLC->townh->getAllowedFactions())
zonesPerFaction[faction] = 0; zonesPerFaction[faction] = 0;
@@ -495,8 +495,8 @@ void CMapGenerator::createConnections()
zoneA->addMonster (this, guardPos, connection.getGuardStrength(), false, true); zoneA->addMonster (this, guardPos, connection.getGuardStrength(), false, true);
//zones can make paths only in their own area //zones can make paths only in their own area
zoneA->crunchPath(this, guardPos, posA, true, zoneA->getFreePaths()); //make connection towards our zone center zoneA->crunchPath(this, guardPos, posA, true, zoneA->getFreePaths()); //make connection towards our zone center
zoneB->crunchPath(this, guardPos, posB, true, zoneB->getFreePaths()); //make connection towards other zone center zoneB->crunchPath(this, guardPos, posB, true, zoneB->getFreePaths()); //make connection towards other zone center
zoneA->addRoadNode(guardPos); zoneA->addRoadNode(guardPos);
zoneB->addRoadNode(guardPos); zoneB->addRoadNode(guardPos);
break; //we're done with this connection break; //we're done with this connection
@@ -504,7 +504,7 @@ void CMapGenerator::createConnections()
} }
} }
else //create subterranean gates between two zones else //create subterranean gates between two zones
{ {
//find point on the path between zones //find point on the path between zones
float3 offset (posB.x - posA.x, posB.y - posA.y, 0); float3 offset (posB.x - posA.x, posB.y - posA.y, 0);
@@ -563,15 +563,14 @@ void CMapGenerator::createConnections()
} }
if (!guardPos.valid()) if (!guardPos.valid())
{ {
auto teleport1 = new CGMonolith; auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, getNextMonlithIndex());
teleport1->ID = Obj::MONOLITH_TWO_WAY; auto teleport1 = factory->create(ObjectTemplate());
teleport1->subID = getNextMonlithIndex();
auto teleport2 = new CGMonolith(*teleport1); auto teleport2 = factory->create(ObjectTemplate());
zoneA->addRequiredObject (teleport1, connection.getGuardStrength()); zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
zoneB->addRequiredObject (teleport2, connection.getGuardStrength()); zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
} }
} }
} }
@@ -590,7 +589,7 @@ void CMapGenerator::addHeaderInfo()
void CMapGenerator::checkIsOnMap(const int3& tile) const void CMapGenerator::checkIsOnMap(const int3& tile) const
{ {
if (!map->isInTheMap(tile)) if (!map->isInTheMap(tile))
throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile)); throw rmgException(boost::to_string(boost::format("Tile %s is outside the map") % tile));
} }
@@ -633,8 +632,8 @@ bool CMapGenerator::isUsed(const int3 &tile) const
bool CMapGenerator::isRoad(const int3& tile) const bool CMapGenerator::isRoad(const int3& tile) const
{ {
checkIsOnMap(tile); checkIsOnMap(tile);
return tiles[tile.x][tile.y][tile.z].isRoad(); return tiles[tile.x][tile.y][tile.z].isRoad();
} }
void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state) void CMapGenerator::setOccupied(const int3 &tile, ETileType::ETileType state)
@@ -648,7 +647,7 @@ void CMapGenerator::setRoad(const int3& tile, ERoadType::ERoadType roadType)
{ {
checkIsOnMap(tile); checkIsOnMap(tile);
tiles[tile.x][tile.y][tile.z].setRoadType(roadType); tiles[tile.x][tile.y][tile.z].setRoadType(roadType);
} }

View File

@@ -86,7 +86,7 @@ void CRmgTemplateZone::CTownInfo::setCastleDensity(int value)
castleDensity = value; castleDensity = value;
} }
CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), terrain(ETerrainType::WRONG),roadType(ERoadType::NO_ROAD) CTileInfo::CTileInfo():nearestObjectDistance(INT_MAX), terrain(ETerrainType::WRONG),roadType(ERoadType::NO_ROAD)
{ {
occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages occupied = ETileType::POSSIBLE; //all tiles are initially possible to place objects or passages
} }
@@ -638,7 +638,7 @@ do not leave zone border
} }
auto lastDistance = distance; auto lastDistance = distance;
auto processNeighbours = [this, gen, &currentPos, dst, &distance, &result, &end, clearedTiles](int3 &pos) auto processNeighbours = [this, gen, &currentPos, dst, &distance, &result, &end, clearedTiles](int3 &pos)
{ {
if (!result) //not sure if lambda is worth it... if (!result) //not sure if lambda is worth it...
@@ -672,19 +672,19 @@ do not leave zone border
} }
} }
}; };
if (onlyStraight) if (onlyStraight)
gen->foreachDirectNeighbour (currentPos, processNeighbours); gen->foreachDirectNeighbour (currentPos, processNeighbours);
else else
gen->foreach_neighbour (currentPos,processNeighbours); gen->foreach_neighbour (currentPos,processNeighbours);
int3 anotherPos(-1, -1, -1); int3 anotherPos(-1, -1, -1);
if (!(result || distance < lastDistance)) //we do not advance, use more advanced pathfinding algorithm? if (!(result || distance < lastDistance)) //we do not advance, use more advanced pathfinding algorithm?
{ {
//try any nearby tiles, even if its not closer than current //try any nearby tiles, even if its not closer than current
float lastDistance = 2 * distance; //start with significantly larger value float lastDistance = 2 * distance; //start with significantly larger value
auto processNeighbours2 = [this, gen, &currentPos, dst, &lastDistance, &anotherPos, &end, clearedTiles](int3 &pos) auto processNeighbours2 = [this, gen, &currentPos, dst, &lastDistance, &anotherPos, &end, clearedTiles](int3 &pos)
{ {
if (currentPos.dist2dSQ(dst) < lastDistance) //try closest tiles from all surrounding unused tiles if (currentPos.dist2dSQ(dst) < lastDistance) //try closest tiles from all surrounding unused tiles
@@ -700,13 +700,13 @@ do not leave zone border
} }
} }
} }
}; };
if (onlyStraight) if (onlyStraight)
gen->foreachDirectNeighbour(currentPos, processNeighbours2); gen->foreachDirectNeighbour(currentPos, processNeighbours2);
else else
gen->foreach_neighbour(currentPos, processNeighbours2); gen->foreach_neighbour(currentPos, processNeighbours2);
if (anotherPos.valid()) if (anotherPos.valid())
{ {
if (clearedTiles) if (clearedTiles)
@@ -878,7 +878,7 @@ bool CRmgTemplateZone::connectPath(CMapGenerator* gen, const int3& src, bool onl
if (onlyStraight) if (onlyStraight)
gen->foreachDirectNeighbour(currentNode, foo); gen->foreachDirectNeighbour(currentNode, foo);
else else
gen->foreach_neighbour(currentNode, foo); gen->foreach_neighbour(currentNode, foo);
} }
@@ -997,8 +997,8 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5}; static const float multiplier1[] = {0.5, 0.75, 1.0, 1.5, 1.5};
static const float multiplier2[] = {0.5, 0.75, 1.0, 1.0, 1.5}; static const float multiplier2[] = {0.5, 0.75, 1.0, 1.0, 1.5};
int strength1 = std::max(0.f, (strength - value1[monsterStrength]) * multiplier1[monsterStrength]); int strength1 = std::max(0.f, (strength - value1[monsterStrength]) * multiplier1[monsterStrength]);
int strength2 = std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]); int strength2 = std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]);
strength = strength1 + strength2; strength = strength1 + strength2;
if (strength < 2000) if (strength < 2000)
@@ -1031,10 +1031,9 @@ bool CRmgTemplateZone::addMonster(CMapGenerator* gen, int3 &pos, si32 strength,
amount = strength / VLC->creh->creatures[creId]->AIValue; amount = strength / VLC->creh->creatures[creId]->AIValue;
} }
auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);
auto guard = new CGCreature(); auto guard = (CGCreature *) guardFactory->create(ObjectTemplate());
guard->ID = Obj::MONSTER;
guard->subID = creId;
guard->character = CGCreature::HOSTILE; guard->character = CGCreature::HOSTILE;
auto hlp = new CStackInstance(creId, amount); auto hlp = new CStackInstance(creId, amount);
//will be set during initialization //will be set during initialization
@@ -1132,7 +1131,7 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
info.occupiedPositions.insert(visitablePos); info.occupiedPositions.insert(visitablePos);
currentValue += oi.value; currentValue += oi.value;
treasures[info.nextTreasurePos] = object; treasures[info.nextTreasurePos] = object;
//now find place for next object //now find place for next object
@@ -1313,19 +1312,23 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
{ {
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
auto town = new CGTownInstance(); si32 subType = townType;
town->ID = Obj::TOWN;
if (this->townsAreSameType) if(totalTowns>0)
town->subID = townType;
else
{ {
if (townTypes.size()) if(!this->townsAreSameType)
town->subID = *RandomGeneratorUtil::nextItem(townTypes, gen->rand); {
else if (townTypes.size())
town->subID = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed subType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
else
subType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
}
} }
auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
auto town = (CGTownInstance *) townFactory->create(ObjectTemplate());
town->ID = Obj::TOWN;
town->tempOwner = player; town->tempOwner = player;
if (hasFort) if (hasFort)
town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::FORT);
@@ -1337,10 +1340,8 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
town->possibleSpells.push_back(spell->id); town->possibleSpells.push_back(spell->id);
} }
if (!totalTowns) if (totalTowns <= 0)
{ {
//first town in zone sets the facton of entire zone
town->subID = townType;
//register MAIN town of zone //register MAIN town of zone
gen->registerZone(town->subID); gen->registerZone(town->subID);
//first town in zone goes in the middle //first town in zone goes in the middle
@@ -1376,10 +1377,9 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
randomizeTownType(gen); randomizeTownType(gen);
} }
auto town = new CGTownInstance(); auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, townType);
town->ID = Obj::TOWN;
town->subID = townType; CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate());
town->tempOwner = player; town->tempOwner = player;
town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::FORT);
town->builtBuildings.insert(BuildingID::DEFAULT); town->builtBuildings.insert(BuildingID::DEFAULT);
@@ -1490,20 +1490,25 @@ void CRmgTemplateZone::paintZoneTerrain (CMapGenerator* gen, ETerrainType terrai
bool CRmgTemplateZone::placeMines (CMapGenerator* gen) bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
{ {
std::vector<Res::ERes> required_mines;
required_mines.push_back(Res::ERes::WOOD);
required_mines.push_back(Res::ERes::ORE);
static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE}; static const Res::ERes woodOre[] = {Res::ERes::WOOD, Res::ERes::ORE};
static const Res::ERes preciousResources[] = {Res::ERes::GEMS, Res::ERes::CRYSTAL, Res::ERes::MERCURY, Res::ERes::SULFUR}; static const Res::ERes preciousResources[] = {Res::ERes::GEMS, Res::ERes::CRYSTAL, Res::ERes::MERCURY, Res::ERes::SULFUR};
std::array<TObjectTypeHandler, 7> factory =
{
VLC->objtypeh->getHandlerFor(Obj::MINE, 0),
VLC->objtypeh->getHandlerFor(Obj::MINE, 1),
VLC->objtypeh->getHandlerFor(Obj::MINE, 2),
VLC->objtypeh->getHandlerFor(Obj::MINE, 3),
VLC->objtypeh->getHandlerFor(Obj::MINE, 4),
VLC->objtypeh->getHandlerFor(Obj::MINE, 5),
VLC->objtypeh->getHandlerFor(Obj::MINE, 6)
};
for (const auto & res : woodOre) for (const auto & res : woodOre)
{ {
for (int i = 0; i < mines[res]; i++) for (int i = 0; i < mines[res]; i++)
{ {
auto mine = new CGMine(); auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(res);
mine->producedResource = res; mine->producedResource = res;
mine->producedQuantity = mine->defaultResProduction(); mine->producedQuantity = mine->defaultResProduction();
if (!i) if (!i)
@@ -1516,9 +1521,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
{ {
for (int i = 0; i < mines[res]; i++) for (int i = 0; i < mines[res]; i++)
{ {
auto mine = new CGMine(); auto mine = (CGMine *) factory.at(static_cast<si32>(res))->create(ObjectTemplate());
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(res);
mine->producedResource = res; mine->producedResource = res;
mine->producedQuantity = mine->defaultResProduction(); mine->producedQuantity = mine->defaultResProduction();
addRequiredObject(mine, 3500); addRequiredObject(mine, 3500);
@@ -1526,9 +1529,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
} }
for (int i = 0; i < mines[Res::GOLD]; i++) for (int i = 0; i < mines[Res::GOLD]; i++)
{ {
auto mine = new CGMine(); auto mine = (CGMine *) factory.at(Res::GOLD)->create(ObjectTemplate());
mine->ID = Obj::MINE;
mine->subID = static_cast<si32>(Res::GOLD);
mine->producedResource = Res::GOLD; mine->producedResource = Res::GOLD;
mine->producedQuantity = mine->defaultResProduction(); mine->producedQuantity = mine->defaultResProduction();
addRequiredObject(mine, 7000); addRequiredObject(mine, 7000);
@@ -1540,7 +1541,7 @@ bool CRmgTemplateZone::placeMines (CMapGenerator* gen)
bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen) bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
{ {
logGlobal->traceStream() << "Creating required objects"; logGlobal->traceStream() << "Creating required objects";
for(const auto &object : requiredObjects) for(const auto &object : requiredObjects)
{ {
auto obj = object.first; auto obj = object.first;
@@ -1577,16 +1578,16 @@ bool CRmgTemplateZone::createRequiredObjects(CMapGenerator* gen)
break; break;
} }
placeObject(gen, obj, pos); placeObject(gen, obj, pos);
guardObject (gen, obj, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY), true); guardObject (gen, obj, object.second, (obj->ID == Obj::MONOLITH_TWO_WAY), true);
//paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones //paths to required objects constitute main paths of zone. otherwise they just may lead to middle and create dead zones
} }
for (const auto &obj : closeObjects) for (const auto &obj : closeObjects)
{ {
std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end()); //new tiles vector after each object has been placed std::vector<int3> tiles(possibleTiles.begin(), possibleTiles.end()); //new tiles vector after each object has been placed
// smallest distance to zone center, greatest distance to nearest object // smallest distance to zone center, greatest distance to nearest object
auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool auto isCloser = [this, gen](const int3 & lhs, const int3 & rhs) -> bool
{ {
@@ -1675,7 +1676,7 @@ void CRmgTemplateZone::createTreasures(CMapGenerator* gen)
return !gen->isPossible(tile); return !gen->isPossible(tile);
}); });
int3 treasureTilePos; int3 treasureTilePos;
//If we are able to place at least one object with value lower than minGuardedValue, it's ok //If we are able to place at least one object with value lower than minGuardedValue, it's ok
do do
@@ -1722,11 +1723,11 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
std::vector<obstaclePair> possibleObstacles; std::vector<obstaclePair> possibleObstacles;
//get all possible obstacles for this terrain //get all possible obstacles for this terrain
for (auto primaryID : VLC->objtypeh->knownObjects()) for (auto primaryID : VLC->objtypeh->knownObjects())
{ {
for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID)) for (auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
{ {
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID); auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
if (handler->isStaticObject()) if (handler->isStaticObject())
{ {
for (auto temp : handler->getTemplates()) for (auto temp : handler->getTemplates())
@@ -1735,7 +1736,7 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp); obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp);
} }
} }
} }
} }
for (auto o : obstaclesBySize) for (auto o : obstaclesBySize)
{ {
@@ -1789,13 +1790,13 @@ void CRmgTemplateZone::createObstacles2(CMapGenerator* gen)
void CRmgTemplateZone::connectRoads(CMapGenerator* gen) void CRmgTemplateZone::connectRoads(CMapGenerator* gen)
{ {
logGlobal->debug("Started building roads"); logGlobal->debug("Started building roads");
std::set<int3> roadNodesCopy(roadNodes); std::set<int3> roadNodesCopy(roadNodes);
std::set<int3> processed; std::set<int3> processed;
while(!roadNodesCopy.empty()) while(!roadNodesCopy.empty())
{ {
int3 node = *roadNodesCopy.begin(); int3 node = *roadNodesCopy.begin();
roadNodesCopy.erase(node); roadNodesCopy.erase(node);
int3 cross(-1, -1, -1); int3 cross(-1, -1, -1);
@@ -1818,13 +1819,13 @@ void CRmgTemplateZone::connectRoads(CMapGenerator* gen)
processed.insert(cross); //don't draw road starting at end point which is already connected processed.insert(cross); //don't draw road starting at end point which is already connected
vstd::erase_if_present(roadNodesCopy, cross); vstd::erase_if_present(roadNodesCopy, cross);
} }
processed.insert(node); processed.insert(node);
} }
drawRoads(gen); drawRoads(gen);
logGlobal->debug("Finished building roads"); logGlobal->debug("Finished building roads");
} }
void CRmgTemplateZone::drawRoads(CMapGenerator* gen) void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
@@ -1832,7 +1833,7 @@ void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
std::vector<int3> tiles; std::vector<int3> tiles;
for (auto tile : roads) for (auto tile : roads)
{ {
if(gen->map->isInTheMap(tile)) if(gen->map->isInTheMap(tile))
tiles.push_back (tile); tiles.push_back (tile);
} }
for (auto tile : roadNodes) for (auto tile : roadNodes)
@@ -1841,8 +1842,8 @@ void CRmgTemplateZone::drawRoads(CMapGenerator* gen)
tiles.push_back(tile); tiles.push_back(tile);
} }
gen->editManager->getTerrainSelection().setSelection(tiles); gen->editManager->getTerrainSelection().setSelection(tiles);
gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand); gen->editManager->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
} }
@@ -1852,7 +1853,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
//zone center should be always clear to allow other tiles to connect //zone center should be always clear to allow other tiles to connect
gen->setOccupied(this->getPos(), ETileType::FREE); gen->setOccupied(this->getPos(), ETileType::FREE);
freePaths.insert(pos); freePaths.insert(pos);
addAllPossibleObjects (gen); addAllPossibleObjects (gen);
@@ -1861,7 +1862,7 @@ bool CRmgTemplateZone::fill(CMapGenerator* gen)
placeMines(gen); placeMines(gen);
createRequiredObjects(gen); createRequiredObjects(gen);
createTreasures(gen); createTreasures(gen);
logGlobal->infoStream() << boost::format ("Zone %d filled successfully") %id; logGlobal->infoStream() << boost::format ("Zone %d filled successfully") %id;
return true; return true;
} }
@@ -2040,7 +2041,7 @@ void CRmgTemplateZone::checkAndPlaceObject(CMapGenerator* gen, CGObjectInstance*
auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType); auto templates = VLC->objtypeh->getHandlerFor(object->ID, object->subID)->getTemplates(terrainType);
if (templates.empty()) if (templates.empty())
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %object->ID %object->subID %pos %terrainType)); throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %object->ID %object->subID %pos %terrainType));
object->appearance = templates.front(); object->appearance = templates.front();
} }
@@ -2059,7 +2060,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
points.insert(pos + object->getVisitableOffset()); points.insert(pos + object->getVisitableOffset());
points.insert(pos); points.insert(pos);
for(auto p : points) for(auto p : points)
{ {
if (gen->map->isInTheMap(p)) if (gen->map->isInTheMap(p))
{ {
gen->setOccupied(p, ETileType::USED); gen->setOccupied(p, ETileType::USED);
@@ -2068,7 +2069,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
if (updateDistance) if (updateDistance)
{ {
for(auto tile : possibleTiles) //don't need to mark distance for not possible tiles for(auto tile : possibleTiles) //don't need to mark distance for not possible tiles
{ {
si32 d = pos.dist2dSQ(tile); //optimization, only relative distance is interesting si32 d = pos.dist2dSQ(tile); //optimization, only relative distance is interesting
gen->setNearestObjectDistance(tile, std::min<float>(d, gen->getNearestObjectDistance(tile))); gen->setNearestObjectDistance(tile, std::min<float>(d, gen->getNearestObjectDistance(tile)));
} }
@@ -2080,7 +2081,7 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
logGlobal->warnStream() << boost::format("Placed Seer Hut at %s, quest artifact %d is %s") % object->pos % artid % VLC->arth->artifacts[artid]->Name(); logGlobal->warnStream() << boost::format("Placed Seer Hut at %s, quest artifact %d is %s") % object->pos % artid % VLC->arth->artifacts[artid]->Name();
} }
switch (object->ID) switch (object->ID)
{ {
case Obj::TOWN: case Obj::TOWN:
@@ -2093,10 +2094,10 @@ void CRmgTemplateZone::placeObject(CMapGenerator* gen, CGObjectInstance* object,
addRoadNode(object->visitablePos()); addRoadNode(object->visitablePos());
} }
break; break;
default: default:
break; break;
} }
} }
void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard) void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance* object, const int3 &pos, si32 str, bool zoneGuard)
@@ -2107,9 +2108,8 @@ void CRmgTemplateZone::placeAndGuardObject(CMapGenerator* gen, CGObjectInstance*
void CRmgTemplateZone::placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength) void CRmgTemplateZone::placeSubterraneanGate(CMapGenerator* gen, int3 pos, si32 guardStrength)
{ {
auto gate = new CGSubterraneanGate; auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0);
gate->ID = Obj::SUBTERRANEAN_GATE; auto gate = factory->create(ObjectTemplate());
gate->subID = 0;
placeObject (gen, gate, pos, true); placeObject (gen, gate, pos, true);
addToConnectLater (getAccessibleOffset (gen, gate->appearance, pos)); //guard will be placed on accessibleOffset addToConnectLater (getAccessibleOffset (gen, gate->appearance, pos)); //guard will be placed on accessibleOffset
guardObject (gen, gate, guardStrength, true); guardObject (gen, gate, guardStrength, true);
@@ -2123,7 +2123,7 @@ std::vector<int3> CRmgTemplateZone::getAccessibleOffsets (CMapGenerator* gen, CG
auto tilesBlockedByObject = object->getBlockedPos(); //absolue value, as object is already placed auto tilesBlockedByObject = object->getBlockedPos(); //absolue value, as object is already placed
gen->foreach_neighbour(visitable, [&](int3& pos) gen->foreach_neighbour(visitable, [&](int3& pos)
{ {
if (gen->isPossible(pos) || gen->isFree(pos)) if (gen->isPossible(pos) || gen->isFree(pos))
{ {
@@ -2166,7 +2166,7 @@ bool CRmgTemplateZone::guardObject(CMapGenerator* gen, CGObjectInstance* object,
if (!gen->isFree(pos)) if (!gen->isFree(pos))
gen->setOccupied(pos, ETileType::BLOCKED); gen->setOccupied(pos, ETileType::BLOCKED);
} }
gen->foreach_neighbour (guardTile, [&](int3& pos) gen->foreach_neighbour (guardTile, [&](int3& pos)
{ {
if (gen->isPossible(pos)) if (gen->isPossible(pos))
gen->setOccupied (pos, ETileType::FREE); gen->setOccupied (pos, ETileType::FREE);
@@ -2303,9 +2303,8 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CMapGenerator* gen, CTreasurePileIn
{ {
oi.generateObject = [minValue]() -> CGObjectInstance * oi.generateObject = [minValue]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
obj->resources[Res::GOLD] = minValue; obj->resources[Res::GOLD] = minValue;
return obj; return obj;
}; };
@@ -2393,9 +2392,6 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i, gen, this]() -> CGObjectInstance * oi.generateObject = [i, gen, this]() -> CGObjectInstance *
{ {
auto obj = new CGHeroInstance;
obj->ID = Obj::PRISON;
std::vector<ui32> possibleHeroes; std::vector<ui32> possibleHeroes;
for (int j = 0; j < gen->map->allowedHeroes.size(); j++) for (int j = 0; j < gen->map->allowedHeroes.size(); j++)
{ {
@@ -2404,6 +2400,10 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
} }
auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, gen->rand); auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, gen->rand);
auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0);
auto obj = (CGHeroInstance *) factory->create(ObjectTemplate());
obj->subID = hid; //will be initialized later obj->subID = hid; //will be initialized later
obj->exp = prisonExp[i]; obj->exp = prisonExp[i];
obj->setOwner(PlayerColor::NEUTRAL); obj->setOwner(PlayerColor::NEUTRAL);
@@ -2471,9 +2471,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i, gen]() -> CGObjectInstance * oi.generateObject = [i, gen]() -> CGObjectInstance *
{ {
auto obj = new CGArtifact(); auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0);
obj->ID = Obj::SPELL_SCROLL; auto obj = (CGArtifact *) factory->create(ObjectTemplate());
obj->subID = 0;
std::vector<SpellID> out; std::vector<SpellID> out;
for (auto spell : VLC->spellh->objects) //spellh size appears to be greater (?) for (auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
@@ -2499,9 +2498,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i]() -> CGObjectInstance * oi.generateObject = [i]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
obj->resources[Res::GOLD] = i * 5000; obj->resources[Res::GOLD] = i * 5000;
return obj; return obj;
}; };
@@ -2516,9 +2514,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i]() -> CGObjectInstance * oi.generateObject = [i]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
obj->gainedExp = i * 5000; obj->gainedExp = i * 5000;
return obj; return obj;
}; };
@@ -2564,9 +2561,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance * oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
auto stack = new CStackInstance(creature, creaturesAmount); auto stack = new CStackInstance(creature, creaturesAmount);
obj->creatures.putStack(SlotID(0), stack); obj->creatures.putStack(SlotID(0), stack);
return obj; return obj;
@@ -2582,9 +2578,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i, gen]() -> CGObjectInstance * oi.generateObject = [i, gen]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
std::vector <CSpell *> spells; std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects) for (auto spell : VLC->spellh->objects)
@@ -2612,9 +2607,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
{ {
oi.generateObject = [i,gen]() -> CGObjectInstance * oi.generateObject = [i,gen]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
std::vector <CSpell *> spells; std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects) for (auto spell : VLC->spellh->objects)
@@ -2642,9 +2636,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [gen]() -> CGObjectInstance * oi.generateObject = [gen]() -> CGObjectInstance *
{ {
auto obj = new CGPandoraBox(); auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0);
obj->ID = Obj::PANDORAS_BOX; auto obj = (CGPandoraBox *) factory->create(ObjectTemplate());
obj->subID = 0;
std::vector <CSpell *> spells; std::vector <CSpell *> spells;
for (auto spell : VLC->spellh->objects) for (auto spell : VLC->spellh->objects)
@@ -2716,9 +2709,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [creature, creaturesAmount, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance * oi.generateObject = [creature, creaturesAmount, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{ {
auto obj = new CGSeerHut(); auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
obj->ID = Obj::SEER_HUT; auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->subID = randomAppearance;
obj->rewardType = CGSeerHut::CREATURE; obj->rewardType = CGSeerHut::CREATURE;
obj->rID = creature->idNumber; obj->rID = creature->idNumber;
obj->rVal = creaturesAmount; obj->rVal = creaturesAmount;
@@ -2752,9 +2744,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance * oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{ {
auto obj = new CGSeerHut(); auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
obj->ID = Obj::SEER_HUT; auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->subID = randomAppearance;
obj->rewardType = CGSeerHut::EXPERIENCE; obj->rewardType = CGSeerHut::EXPERIENCE;
obj->rID = 0; //unitialized? obj->rID = 0; //unitialized?
obj->rVal = seerExpGold[i]; obj->rVal = seerExpGold[i];
@@ -2774,9 +2766,8 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance * oi.generateObject = [i, randomAppearance, gen, this, generateArtInfo]() -> CGObjectInstance *
{ {
auto obj = new CGSeerHut(); auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance);
obj->ID = Obj::SEER_HUT; auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
obj->subID = randomAppearance;
obj->rewardType = CGSeerHut::RESOURCES; obj->rewardType = CGSeerHut::RESOURCES;
obj->rID = Res::GOLD; obj->rID = Res::GOLD;
obj->rVal = seerExpGold[i]; obj->rVal = seerExpGold[i];

View File

@@ -152,6 +152,12 @@ void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectI
{ {
BOOST_CHECK_EQUAL(actual->getStringId(), expected->getStringId()); BOOST_CHECK_EQUAL(actual->getStringId(), expected->getStringId());
BOOST_CHECK_EQUAL(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison BOOST_CHECK_EQUAL(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison
std::string actualFullID = boost::to_string(boost::format("%s(%d)|%s(%d)") % actual->typeName % actual->id % actual->subTypeName % actual->subID);
std::string expectedFullID = boost::to_string(boost::format("%s(%d)|%s(%d)") % expected->typeName % expected->id % expected->subTypeName % expected->subID);
BOOST_CHECK_EQUAL(actualFullID, expectedFullID);
BOOST_CHECK_EQUAL(actual->pos, expected->pos);
} }
void MapComparer::compareObjects() void MapComparer::compareObjects()