From 6bd6be0835d8884854f27559b7d84119f6fdae17 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 24 May 2014 01:56:51 +0300 Subject: [PATCH] Object class handler is now a proper "handler" - Some changes in interfaces - Fixed some missing fields in serialization - Moved object names to new handler --- client/CGameInfo.h | 4 +- client/mapHandler.cpp | 2 +- lib/CDefObjInfoHandler.cpp | 147 +++++++++++++++++++++++++++++++++--- lib/CDefObjInfoHandler.h | 94 +++++++++++++++++++---- lib/CGameState.cpp | 54 +++---------- lib/CGeneralTextHandler.cpp | 1 - lib/CGeneralTextHandler.h | 1 - lib/CObjectConstructor.cpp | 28 +++++++ lib/CObjectConstructor.h | 2 +- lib/CObjectHandler.cpp | 51 ++++++++++--- lib/CObjectHandler.h | 9 ++- lib/CObjectWithReward.cpp | 10 +-- lib/CObjectWithReward.h | 10 ++- lib/NetPacksLib.cpp | 4 +- lib/VCMI_Lib.h | 4 +- lib/rmg/CMapGenerator.cpp | 2 +- 16 files changed, 322 insertions(+), 101 deletions(-) diff --git a/client/CGameInfo.h b/client/CGameInfo.h index f56429555..d0393568e 100644 --- a/client/CGameInfo.h +++ b/client/CGameInfo.h @@ -23,7 +23,7 @@ class CBuildingHandler; class CObjectHandler; class CSoundHandler; class CMusicHandler; -class CObjectTypesHandler; +class CObjectClassesHandler; class CTownHandler; class CGeneralTextHandler; class CConsoleHandler; @@ -57,7 +57,7 @@ public: ConstTransitivePtr creh; ConstTransitivePtr spellh; ConstTransitivePtr objh; - ConstTransitivePtr objtypeh; + ConstTransitivePtr objtypeh; CGeneralTextHandler * generaltexth; CMapHandler * mh; CTownHandler * townh; diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index 01ab922ff..efb2cfc01 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -1073,7 +1073,7 @@ void CMapHandler::getTerrainDescr( const int3 &pos, std::string & out, bool terN } if(t.hasFavourableWinds()) - out = CGI->generaltexth->names[Obj::FAVORABLE_WINDS]; + out = CGI->objtypeh->getObjectName(Obj::FAVORABLE_WINDS); else if(terName) out = CGI->generaltexth->terrainNames[t.terType]; } diff --git a/lib/CDefObjInfoHandler.cpp b/lib/CDefObjInfoHandler.cpp index b75ba812d..a100a6456 100644 --- a/lib/CDefObjInfoHandler.cpp +++ b/lib/CDefObjInfoHandler.cpp @@ -11,6 +11,8 @@ #include "CModHandler.h" #include "JsonNode.h" +#include "CObjectConstructor.h" + /* * CDefObjInfoHandler.cpp, part of VCMI engine * @@ -350,29 +352,153 @@ CDefObjInfoHandler::CDefObjInfoHandler() readTextFile("Data/Heroes.txt"); } */ -void CObjectTypesHandler::init() -{ +CObjectClassesHandler::CObjectClassesHandler() +{ + // list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code + handlerConstructors["configurable"] = std::make_shared; + +#define SET_HANDLER(STRING, TYPENAME) handlerConstructors[STRING] = std::make_shared > + + SET_HANDLER("", CGObjectInstance); + SET_HANDLER("generic", CGObjectInstance); + + SET_HANDLER("market", CGMarket); + SET_HANDLER("bank", CBank); + SET_HANDLER("cartographer", CCartographer); + SET_HANDLER("artifact", CGArtifact); + SET_HANDLER("blackMarket", CGBlackMarket); + SET_HANDLER("boat", CGBoat); + SET_HANDLER("bonusingObject", CGBonusingObject); + SET_HANDLER("borderGate", CGBorderGate); + SET_HANDLER("borderGuard", CGBorderGuard); + SET_HANDLER("monster", CGCreature); + SET_HANDLER("denOfThieves", CGDenOfthieves); + SET_HANDLER("dwelling", CGDwelling); + SET_HANDLER("event", CGEvent); + SET_HANDLER("garrison", CGGarrison); + SET_HANDLER("hero", CGHeroInstance); + SET_HANDLER("heroPlaceholder", CGHeroPlaceholder); + SET_HANDLER("keymaster", CGKeymasterTent); + SET_HANDLER("lighthouse", CGLighthouse); + SET_HANDLER("magi", CGMagi); + SET_HANDLER("magicSpring", CGMagicSpring); + SET_HANDLER("magicWell", CGMagicWell); + SET_HANDLER("market", CGMarket); + SET_HANDLER("mine", CGMine); + SET_HANDLER("obelisk", CGObelisk); + SET_HANDLER("observatory", CGObservatory); + SET_HANDLER("onceVisitable", CGOnceVisitable); + SET_HANDLER("pandora", CGPandoraBox); + SET_HANDLER("pickable", CGPickable); + SET_HANDLER("pyramid", CGPyramid); + SET_HANDLER("questGuard", CGQuestGuard); + SET_HANDLER("resource", CGResource); + SET_HANDLER("scholar", CGScholar); + SET_HANDLER("seerHut", CGSeerHut); + SET_HANDLER("shipyard", CGShipyard); + SET_HANDLER("shrine", CGShrine); + SET_HANDLER("sign", CGSignBottle); + SET_HANDLER("siren", CGSirens); + SET_HANDLER("teleport", CGTeleport); + SET_HANDLER("town", CGTownInstance); + SET_HANDLER("university", CGUniversity); + SET_HANDLER("oncePerHero", CGVisitableOPH); + SET_HANDLER("oncePerWeek", CGVisitableOPW); + SET_HANDLER("witch", CGWitchHut); + +#undef SET_HANDLER } -TObjectTypeHandler CObjectTypesHandler::getHandlerFor(si32 type, si32 subtype) const +static std::vector readTextFile(std::string path) { - if (objectTypes.count(type)) + //TODO +} + +std::vector CObjectClassesHandler::loadLegacyData(size_t dataSize) +{ + std::vector ret(dataSize);// create storage for 256 objects + + auto parseFile = [&](std::string filename) { - if (objectTypes.at(type).count(subtype)) - return objectTypes.at(type).at(subtype); + auto entries = readTextFile(filename); + for (JsonNode & entry : entries) + { + si32 id = entry["basebase"].Float(); + si32 subid = entry["base"].Float(); + + entry.Struct().erase("basebase"); + entry.Struct().erase("base"); + + if (ret[id].Vector().size() <= subid) + ret[id].Vector().resize(subid+1); + ret[id]["legacyTypes"].Vector()[subid][entry["animation"].String()].swap(entry); + } + }; + + parseFile("Data/Objects.txt"); + parseFile("Data/Heroes.txt"); + + CLegacyConfigParser parser("Data/ObjNames.txt"); + for (size_t i=0; i<256; i++) + { + ret[i]["name"].String() = parser.readString(); + parser.endLine(); + } + return ret; +} + +CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json) +{ + auto obj = new ObjectContainter(); + obj->name = json["name"].String(); + obj->handlerName = json["handler"].String(); + obj->base = json["base"]; + for (auto entry : json["types"].Struct()) + { + auto handler = handlerConstructors.at(obj->handlerName)(); + handler->init(entry.second); + } + 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, size_t index) +{ +} + +std::vector CObjectClassesHandler::getDefaultAllowed() const +{ + return std::vector(); //TODO? +} + +TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const +{ + if (objects.count(type)) + { + if (objects.at(type)->objects.count(subtype)) + return objects.at(type)->objects.at(subtype); } assert(0); // FIXME: throw error? return nullptr; } -void AObjectTypeHandler::init(si32 type, si32 subtype) +std::string CObjectClassesHandler::getObjectName(si32 type) const +{ + assert(objects.count(type)); + return objects.at(type)->name; +} + +void AObjectTypeHandler::setType(si32 type, si32 subtype) { this->type = type; this->subtype = subtype; } -void AObjectTypeHandler::load(const JsonNode & input) +void AObjectTypeHandler::init(const JsonNode & input) { for (auto entry : input["templates"].Struct()) { @@ -416,7 +542,7 @@ std::vector AObjectTypeHandler::getTemplates(si32 terrainType) c return filtered.empty() ? ret : filtered; } -ObjectTemplate AObjectTypeHandler::selectTemplate(si32 terrainType, CGObjectInstance * object) const +boost::optional AObjectTypeHandler::getOverride(si32 terrainType, const CGObjectInstance * object) const { std::vector ret = getTemplates(terrainType); for (auto & tmpl : ret) @@ -424,6 +550,5 @@ ObjectTemplate AObjectTypeHandler::selectTemplate(si32 terrainType, CGObjectInst if (objectFilter(object, tmpl)) return tmpl; } - // FIXME: no matches found. Warn? Ask for torches? Die? - return ret.front(); + return boost::optional(); } diff --git a/lib/CDefObjInfoHandler.h b/lib/CDefObjInfoHandler.h index 3c314af89..6911eaed5 100644 --- a/lib/CDefObjInfoHandler.h +++ b/lib/CDefObjInfoHandler.h @@ -2,6 +2,7 @@ #include "GameConstants.h" #include "../lib/ConstTransitivePtr.h" +#include "IHandlerBase.h" /* * CDefObjInfoHandler.h, part of VCMI engine @@ -105,15 +106,17 @@ class AObjectTypeHandler si32 type; si32 subtype; + JsonNode base; /// describes base template + std::vector templates; protected: - void init(si32 type, si32 subtype); - - /// loads templates from Json structure using fields "base" and "templates" - void load(const JsonNode & input); + void setType(si32 type, si32 subtype); virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const; public: + /// loads templates from Json structure using fields "base" and "templates" + virtual void init(const JsonNode & input); + void addTemplate(const ObjectTemplate & templ); /// returns all templates, without any filters @@ -122,35 +125,96 @@ public: /// returns all templates that can be placed on specific terrain type std::vector getTemplates(si32 terrainType) const; - /// returns template suitable for object. If returned template is not equal to current one - /// it must be replaced with this one (and properly updated on all clients) - ObjectTemplate selectTemplate(si32 terrainType, CGObjectInstance * object) 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) + boost::optional getOverride(si32 terrainType, const CGObjectInstance * object) const; + /// Creates object and set up core properties (like ID/subID). Object is NOT initialized + /// to allow creating objects before game start (e.g. map loading) virtual CGObjectInstance * create(ObjectTemplate tmpl) const = 0; + /// Configures object properties. Should be re-entrable, resetting state of the object if necessarily virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; + /// Returns object configuration, if available. Othervice returns NULL virtual const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const = 0; + + template void serialize(Handler &h, const int version) + { + h & type & subtype & templates; + } +}; + +template +class CDefaultObjectTypeHandler : public AObjectTypeHandler +{ + CGObjectInstance * create(ObjectTemplate tmpl) const + { + auto obj = new ObjectType(); + obj->ID = tmpl.id; + obj->subID = tmpl.subid; + obj->appearance = tmpl; + return obj; + } + + virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const + { + } + + virtual const IObjectInfo * getObjectInfo(ObjectTemplate tmpl) const + { + return nullptr; + } }; typedef std::shared_ptr TObjectTypeHandler; -class CObjectTypesHandler +class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase { - /// list of object handlers, each of them handles only one type - std::map > objectTypes; + /// Small internal structure that contains information on specific group of objects + /// (creating separate entity is overcomplicating at least at this point) + struct ObjectContainter + { + si32 id; + std::string name; // human-readable name + std::string handlerName; // ID of handler that controls this object, shoul be determined using hadlerConstructor map + + JsonNode base; + std::map objects; + + template void serialize(Handler &h, const int version) + { + h & base & objects; + } + }; + + /// list of object handlers, each of them handles only one type + std::map objects; + + /// map that is filled during contruction with all known handlers. Not serializeable + std::map > handlerConstructors; + + ObjectContainter * loadFromJson(const JsonNode & json); public: - void init(); + CObjectClassesHandler(); + + virtual std::vector loadLegacyData(size_t dataSize); + + virtual void loadObject(std::string scope, std::string name, const JsonNode & data); + virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index); + + virtual void afterLoadFinalization(){}; + + virtual std::vector getDefaultAllowed() const; /// returns handler for specified object (ID-based). ObjectHandler keeps ownership TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const; + std::string getObjectName(si32 type) const; + template void serialize(Handler &h, const int version) { - //h & objects; - if (!h.saving) - init(); // TODO: implement serialization + h & objects; } }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 1447c65cd..fa83a1bf9 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -166,6 +166,10 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst { dst = VLC->arth->artifacts[ser]->EventText(); } + else if (type == OBJ_NAMES) + { + dst = VLC->objtypeh->getObjectName(ser); + } else { std::vector *vec; @@ -177,9 +181,6 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst case XTRAINFO_TXT: vec = &VLC->generaltexth->xtrainfo; break; - case OBJ_NAMES: - vec = &VLC->generaltexth->names; - break; case RES_NAMES: vec = &VLC->generaltexth->restypes; break; @@ -651,58 +652,25 @@ void CGameState::randomizeObject(CGObjectInstance *cur) std::pair ran = pickObject(cur); if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything { - if(cur->ID==Obj::TOWN) //town - set def - { - const TerrainTile &tile = map->getTile(cur->visitablePos()); - CGTownInstance *t = dynamic_cast(cur); - t->town = VLC->townh->factions[t->subID]->town; - t->appearance = VLC->objtypeh->getHandlerFor(Obj::TOWN, t->subID)->selectTemplate(tile.terType, t); - t->updateAppearance(); - } + if(cur->ID==Obj::TOWN) + cur->setType(cur->ID, cur->subID); // update def, if necessary return; } else if(ran.first==Obj::HERO)//special code for hero { CGHeroInstance *h = dynamic_cast(cur); - if(!h) {logGlobal->warnStream()<<"Wrong random hero at "<pos; return;} - cur->ID = ran.first; - cur->subID = ran.second; - h->type = VLC->heroh->heroes[ran.second]; - h->portrait = h->type->imageIndex; - h->randomizeArmy(h->type->heroClass->faction); + cur->setType(ran.first, ran.second); map->heroesOnMap.push_back(h); - return; //TODO: maybe we should do something with definfo? + return; } else if(ran.first==Obj::TOWN)//special code for town { - const TerrainTile &tile = map->getTile(cur->visitablePos()); CGTownInstance *t = dynamic_cast(cur); - if(!t) {logGlobal->warnStream()<<"Wrong random town at "<pos; return;} - cur->ID = ran.first; - cur->subID = ran.second; - //FIXME: copy-pasted from above - t->town = VLC->townh->factions[t->subID]->town; - t->appearance = VLC->objtypeh->getHandlerFor(Obj::TOWN, t->subID)->selectTemplate(tile.terType, t); - t->updateAppearance(); - - t->randomizeArmy(t->subID); + cur->setType(ran.first, ran.second); map->towns.push_back(t); return; } - else - { - if (ran.first != cur->appearance.id || - ran.second != cur->appearance.subid) - { - const TerrainTile &tile = map->getTile(cur->visitablePos()); - cur->appearance = VLC->objtypeh->getHandlerFor(ran.first, ran.second)->selectTemplate(tile.terType, cur); - } - } - //we have to replace normal random object - cur->ID = ran.first; - cur->subID = ran.second; - map->removeBlockVisTiles(cur, true); //recalculate blockvis tiles - picked object might have different than random placeholder - map->addBlockVisTiles(cur); + cur->setType(ran.first, ran.second); } int CGameState::getDate(Date::EDateType mode) const @@ -1078,7 +1046,7 @@ void CGameState::randomizeMapObjects() if(!obj) continue; randomizeObject(obj); - obj->hoverName = VLC->generaltexth->names[obj->ID]; + obj->hoverName = VLC->objtypeh->getObjectName(obj->ID); //handle Favouring Winds - mark tiles under it if(obj->ID == Obj::FAVORABLE_WINDS) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index e2321551b..6cd415dc8 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -287,7 +287,6 @@ CGeneralTextHandler::CGeneralTextHandler() readToVector("DATA/HEROSCRN.TXT", heroscrn); readToVector("DATA/TENTCOLR.TXT", tentColors); readToVector("DATA/SKILLLEV.TXT", levels); - readToVector("DATA/OBJNAMES.TXT", names); localizedTexts = JsonNode(ResourceID("config/translate.json", EResType::TEXT)); diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index 3107900e8..269331b0e 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -112,7 +112,6 @@ public: std::vector victoryConditions; //objects - std::vector names; //vector of objects; i-th object in vector has subnumber i std::vector creGens; //names of creatures' generators std::vector creGens4; //names of multiple creatures' generators std::vector advobtxt; diff --git a/lib/CObjectConstructor.cpp b/lib/CObjectConstructor.cpp index 6838f9c95..a011167fe 100644 --- a/lib/CObjectConstructor.cpp +++ b/lib/CObjectConstructor.cpp @@ -138,8 +138,32 @@ void CRandomRewardObjectInfo::init(const JsonNode & objectConfig) void CRandomRewardObjectInfo::configureObject(CObjectWithReward * object, CRandomGenerator & rng) const { + std::map thrownDice; + for (const JsonNode & reward : parameters["rewards"].Vector()) { + if (!reward["appearChance"].isNull()) + { + JsonNode chance = reward["appearChance"]; + si32 diceID = chance["dice"].Float(); + + if (thrownDice.count(diceID) == 0) + thrownDice[diceID] = rng.getIntRange(1, 100)(); + + if (!chance["min"].isNull()) + { + int min = chance["min"].Float(); + if (min > thrownDice[diceID]) + continue; + } + if (!chance["max"].isNull()) + { + int max = chance["max"].Float(); + if (max < thrownDice[diceID]) + continue; + } + } + const JsonNode & limiter = reward["limiter"]; CVisitInfo info; // load limiter @@ -172,6 +196,9 @@ void CRandomRewardObjectInfo::configureObject(CObjectWithReward * object, CRando info.reward.artifacts = loadArtifacts(reward["artifacts"], rng); info.reward.spells = loadSpells(reward["spells"], rng); info.reward.creatures = loadCreatures(reward["creatures"], rng); + + info.message = loadMessage(reward["message"]); + info.selectChance = loadValue(reward["selectChance"], rng); } object->onSelect = loadMessage(parameters["onSelectMessage"]); @@ -241,6 +268,7 @@ CObjectWithRewardConstructor::CObjectWithRewardConstructor() void CObjectWithRewardConstructor::init(const JsonNode & config) { + AObjectTypeHandler::init(config); objectInfo.init(config); } diff --git a/lib/CObjectConstructor.h b/lib/CObjectConstructor.h index 27a88c22f..8c080b44b 100644 --- a/lib/CObjectConstructor.h +++ b/lib/CObjectConstructor.h @@ -47,7 +47,7 @@ class CObjectWithRewardConstructor : public AObjectTypeHandler public: CObjectWithRewardConstructor(); - void init(const JsonNode & config); + void init(const JsonNode & config) override; CGObjectInstance * create(ObjectTemplate tmpl) const override; diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index f4c118f95..885d5c28e 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -372,6 +372,19 @@ bool CGObjectInstance::operator<(const CGObjectInstance & cmp) const //screen p return false; } +void CGObjectInstance::setType(si32 ID, si32 subID) +{ + const TerrainTile &tile = cb->gameState()->map->getTile(visitablePos()); + + this->ID = Obj(ID); + this->subID = subID; + this->appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(tile.terType).front(); + + //recalculate blockvis tiles - new appearance might have different blockmap than before + cb->gameState()->map->removeBlockVisTiles(this, true); + cb->gameState()->map->addBlockVisTiles(this); +} + void CGObjectInstance::initObj() { switch(ID) @@ -456,7 +469,7 @@ int3 CGObjectInstance::getVisitableOffset() const void CGObjectInstance::getNameVis( std::string &hname ) const { const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer()); - hname = VLC->generaltexth->names[ID]; + hname = VLC->objtypeh->getObjectName(ID); if(h) { const bool visited = h->hasBonusFrom(Bonus::OBJECT,ID); @@ -720,6 +733,15 @@ void CGHeroInstance::initHero(HeroTypeID SUBID) initHero(); } +void CGHeroInstance::setType(si32 ID, si32 subID) +{ + assert(ID == Obj::HERO); // just in case + CGObjectInstance::setType(ID, subID); + type = VLC->heroh->heroes[subID]; + portrait = type->imageIndex; + randomizeArmy(type->heroClass->faction); +} + void CGHeroInstance::initHero() { assert(validTypes(true)); @@ -913,7 +935,7 @@ const std::string & CGHeroInstance::getHoverText() const return hoverName; } else - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); return hoverName; } @@ -2497,6 +2519,15 @@ std::vector CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode return IMarket::availableItemsIds(mode); } +void CGTownInstance::setType(si32 ID, si32 subID) +{ + assert(ID == Obj::TOWN); // just in case + CGObjectInstance::setType(ID, subID); + town = VLC->townh->factions[subID]->town; + randomizeArmy(subID); + updateAppearance(); +} + void CGTownInstance::updateAppearance() { if (!hasFort()) @@ -4126,10 +4157,10 @@ const std::string & CGSeerHut::getHoverText() const boost::algorithm::replace_first(hoverName,"%s", seerName); } else //just seer hut - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); break; case Obj::QUEST_GUARD: - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); break; default: logGlobal->debugStream() << "unrecognized quest object"; @@ -4448,7 +4479,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const const std::string & CGWitchHut::getHoverText() const { - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); if(wasVisited(cb->getLocalPlayer())) { hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s) @@ -4907,7 +4938,7 @@ void CGShrine::initObj() const std::string & CGShrine::getHoverText() const { - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); if(wasVisited(cb->getCurrentPlayer())) //TODO: use local player, not current { hoverName += "\n" + VLC->generaltexth->allTexts[355]; // + (learn %s) @@ -5509,7 +5540,7 @@ const std::string& CGKeys::getHoverText() const const std::string CGKeys::getName() const { std::string name; - name = VLC->generaltexth->tentColors[subID] + " " + VLC->generaltexth->names[ID]; + name = VLC->generaltexth->tentColors[subID] + " " + VLC->objtypeh->getObjectName(ID); return name; } @@ -5545,7 +5576,7 @@ void CGBorderGuard::getVisitText (MetaString &text, std::vector &comp void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const { if (!onHover) - text << VLC->generaltexth->tentColors[subID] << " " << VLC->generaltexth->names[Obj::KEYMASTER]; + text << VLC->generaltexth->tentColors[subID] << " " << VLC->objtypeh->getObjectName(Obj::KEYMASTER); } bool CGBorderGuard::checkQuest (const CGHeroInstance * h) const @@ -5945,7 +5976,7 @@ void CGObelisk::initObj() const std::string & CGObelisk::getHoverText() const { bool visited = wasVisited(cb->getLocalPlayer()); - hoverName = VLC->generaltexth->names[ID] + " " + visitedTxt(visited); + hoverName = VLC->objtypeh->getObjectName(ID) + " " + visitedTxt(visited); return hoverName; } @@ -5998,7 +6029,7 @@ void CGLighthouse::initObj() const std::string & CGLighthouse::getHoverText() const { - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); //TODO: owned by %s player return hoverName; } diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index cf6a305be..102b46f28 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -234,6 +234,8 @@ public: //CGObjectInstance& operator=(const CGObjectInstance & right); virtual const std::string & getHoverText() const; + void setType(si32 ID, si32 subID); + ///IObjectInterface void initObj() override; void onHeroVisit(const CGHeroInstance * h) const override; @@ -355,7 +357,7 @@ public: //std::vector artifacts; //hero's artifacts from bag //std::map artifWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 std::set spells; //known spells (spell IDs) - std::set visitedObjects; + std::set visitedObjects; struct DLL_LINKAGE Patrol { @@ -422,7 +424,7 @@ public: h & static_cast(*this); h & static_cast(*this); h & exp & level & name & biography & portrait & mana & secSkills & movement - & sex & inTownGarrison & spells & patrol & moveDir & skillsInfo & visitedObjects; + & sex & inTownGarrison & spells & patrol & moveDir & skillsInfo & visitedObjects; h & visitedTown & boat; h & type & specialty & commander; BONUS_TREE_DESERIALIZATION_FIX @@ -474,6 +476,8 @@ public: ////////////////////////////////////////////////////////////////////////// + void setType(si32 ID, si32 subID); + void initHero(); void initHero(HeroTypeID SUBID); @@ -698,6 +702,7 @@ public: bool allowsTrade(EMarketMode::EMarketMode mode) const; std::vector availableItemsIds(EMarketMode::EMarketMode mode) const; + void setType(si32 ID, si32 subID); void updateAppearance(); ////////////////////////////////////////////////////////////////////////// diff --git a/lib/CObjectWithReward.cpp b/lib/CObjectWithReward.cpp index ef6f119d3..21ab6347d 100644 --- a/lib/CObjectWithReward.cpp +++ b/lib/CObjectWithReward.cpp @@ -121,7 +121,6 @@ void CObjectWithReward::onHeroVisit(const CGHeroInstance *h) const else iw.text = onVisited; cb->showInfoDialog(&iw); - onRewardGiven(h); // FIXME: dummy call to properly act on empty objects (e.g. Floatsam that must be removed after visit) break; } case 1: // one reward. Just give it with message @@ -141,7 +140,7 @@ void CObjectWithReward::onHeroVisit(const CGHeroInstance *h) const case SELECT_FIRST: // give first available grantRewardWithMessage(rewards[0]); break; - case SELECT_RANDOM: // select one randomly + case SELECT_RANDOM: // select one randomly //TODO: use weights grantRewardWithMessage(rewards[cb->gameState()->getRandomGenerator().nextInt(rewards.size()-1)]); break; } @@ -376,7 +375,7 @@ static std::string & visitedTxt(const bool visited) const std::string & CObjectWithReward::getHoverText() const { const CGHeroInstance *h = cb->getSelectedHero(cb->getCurrentPlayer()); - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); if(visitMode != VISIT_UNLIMITED) { bool visited = wasVisited(cb->getCurrentPlayer()); @@ -975,7 +974,7 @@ const std::string & CGVisitableOPH::getHoverText() const default: throw std::runtime_error("Wrong CGVisitableOPH object ID!\n"); } - hoverName = VLC->generaltexth->names[ID]; + hoverName = VLC->objtypeh->getObjectName(ID); if(pom >= 0) hoverName += ("\n" + VLC->generaltexth->xtrainfo[pom]); const CGHeroInstance *h = cb->getSelectedHero (cb->getCurrentPlayer()); @@ -1015,7 +1014,6 @@ void CGVisitableOPW::initObj() soundID = soundBase::GENIE; onEmpty.addTxt(MetaString::ADVOB_TXT, 169); // 3-6 of any resource but wood and gold - // this is UGLY. TODO: find better way to describe this for (int resID = Res::MERCURY; resID < Res::GOLD; resID++) { for (int val = 3; val <=6; val++) @@ -1045,7 +1043,7 @@ void CGVisitableOPW::initObj() void CGMagicSpring::initObj() { - CVisitInfo visit; // TODO: "player above max mana" limiter + CVisitInfo visit; // TODO: "player above max mana" limiter. Use logical expressions for limiters? visit.reward.manaPercentage = 200; visit.message.addTxt(MetaString::ADVOB_TXT, 74); info.push_back(visit); // two rewards, one for each entrance diff --git a/lib/CObjectWithReward.h b/lib/CObjectWithReward.h index 877e649c5..cadc7a581 100644 --- a/lib/CObjectWithReward.h +++ b/lib/CObjectWithReward.h @@ -119,6 +119,7 @@ public: template void serialize(Handler &h, const int version) { h & resources & extraComponents & removeObject; + h & manaPercentage & movePercentage; h & gainedExp & gainedLevels & manaDiff & movePoints; h & primary & secondary & bonuses; h & artifacts & spells & creatures; @@ -134,6 +135,9 @@ public: /// Message that will be displayed on granting of this reward, if not empty MetaString message; + /// Chance for this reward to be selected in case of random choice + si32 selectChance; + /// How many times this reward has been granted since last reset si32 numOfGrants; @@ -143,7 +147,7 @@ public: template void serialize(Handler &h, const int version) { - h & limiter & reward & message; + h & limiter & reward & message & selectChance & numOfGrants; } }; @@ -229,8 +233,8 @@ public: template void serialize(Handler &h, const int version) { h & static_cast(*this); - h & info & canRefuse; - h & onSelect & onVisited & onEmpty; + h & info & canRefuse & resetDuration; + h & onSelect & onVisited & onEmpty & visitMode; h & soundID & selectMode & selectedReward; } diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 8ee35e925..9afc62ecb 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -623,9 +623,9 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs ) o->subID = subID; o->pos = pos; const TerrainTile &t = gs->map->getTile(pos); - o->appearance = VLC->objtypeh->getHandlerFor(o->ID, o->subID)->selectTemplate(t.terType, o); + o->appearance = VLC->objtypeh->getHandlerFor(o->ID, o->subID)->getTemplates(t.terType).front(); id = o->id = ObjectInstanceID(gs->map->objects.size()); - o->hoverName = VLC->generaltexth->names[ID]; + o->hoverName = VLC->objtypeh->getObjectName(ID); gs->map->objects.push_back(o); gs->map->addBlockVisTiles(o); diff --git a/lib/VCMI_Lib.h b/lib/VCMI_Lib.h index cf728fe55..3cd5e60f6 100644 --- a/lib/VCMI_Lib.h +++ b/lib/VCMI_Lib.h @@ -16,7 +16,7 @@ class CCreatureHandler; class CSpellHandler; class CBuildingHandler; class CObjectHandler; -class CObjectTypesHandler; +class CObjectClassesHandler; class CTownHandler; class CGeneralTextHandler; class CModHandler; @@ -42,7 +42,7 @@ public: CCreatureHandler * creh; CSpellHandler * spellh; CObjectHandler * objh; - CObjectTypesHandler * objtypeh; + CObjectClassesHandler * objtypeh; CTownHandler * townh; CGeneralTextHandler * generaltexth; CModHandler * modh; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 771887765..d0c9308b0 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -166,7 +166,7 @@ void CMapGenerator::genTowns() } town->subID = townId; town->tempOwner = owner; - town->appearance = VLC->objtypeh->getHandlerFor(town->ID, town->subID)->selectTemplate(map->getTile(townPos[side]).terType, town); + town->appearance = VLC->objtypeh->getHandlerFor(town->ID, town->subID)->getTemplates(map->getTile(townPos[side]).terType).front(); town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); editManager->insertObject(town, int3(townPos[side].x, townPos[side].y + (i / 2) * 5, 0));