diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 8dbd73f7e..10038bf1d 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -2212,7 +2212,8 @@ void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition) return; } - auto costBase = TResources(GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]); + ResourceSet costBase; + costBase.resolveFromJson(GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]); auto costExponent = GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float(); auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent); diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 1b29fc0d8..e31d22a4f 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -623,7 +623,7 @@ std::shared_ptr CCreatureHandler::loadFromJson(const std::string & sc JsonDeserializer handler(nullptr, node); cre->serializeJson(handler); - cre->cost = ResourceSet(node["cost"]); + cre->cost.resolveFromJson(node["cost"]); LIBRARY->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]); LIBRARY->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]); diff --git a/lib/ResourceSet.cpp b/lib/ResourceSet.cpp index 2731ab723..99fecc29a 100644 --- a/lib/ResourceSet.cpp +++ b/lib/ResourceSet.cpp @@ -20,19 +20,27 @@ VCMI_LIB_NAMESPACE_BEGIN ResourceSet::ResourceSet() { - container.resize(LIBRARY->resourceTypeHandler->getAllObjects().size()); + resizeContainer(); }; -ResourceSet::ResourceSet(const JsonNode & node) -{ - container.resize(LIBRARY->resourceTypeHandler->getAllObjects().size()); - for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) - container[i] = static_cast(node[i.toResource()->getJsonKey()].Float()); -} - ResourceSet::ResourceSet(const ResourceSet& rhs) : container(rhs.container) // vector copy constructor { + resizeContainer(); +} + +void ResourceSet::resizeContainer() +{ + container.resize(LIBRARY->resourceTypeHandler->getAllObjects().size()); +} + +void ResourceSet::resolveFromJson(const JsonNode & node) +{ + for(auto & n : node.Struct()) + LIBRARY->identifiers()->requestIdentifier(n.second.getModScope(), "resource", n.first, [n, this](int32_t identifier) + { + (*this)[identifier] = static_cast(n.second.Float()); + }); } void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName) diff --git a/lib/ResourceSet.h b/lib/ResourceSet.h index 816974539..a77beb7f8 100644 --- a/lib/ResourceSet.h +++ b/lib/ResourceSet.h @@ -27,16 +27,18 @@ class ResourceSet { private: std::vector container = {}; + DLL_LINKAGE void resizeContainer(); public: - // read resources set from json. Format example: { "gold": 500, "wood":5 } - DLL_LINKAGE ResourceSet(const JsonNode & node); DLL_LINKAGE ResourceSet(); DLL_LINKAGE ResourceSet(const ResourceSet& rhs); + DLL_LINKAGE void resolveFromJson(const JsonNode & node); + #define scalarOperator(OPSIGN) \ ResourceSet& operator OPSIGN ## =(const TResource &rhs) \ { \ + resizeContainer(); \ for(auto i = 0; i < container.size(); i++) \ container.at(i) OPSIGN ## = rhs; \ \ @@ -46,6 +48,7 @@ public: #define vectorOperator(OPSIGN) \ ResourceSet& operator OPSIGN ## =(const ResourceSet &rhs) \ { \ + resizeContainer(); \ for(auto i = 0; i < container.size(); i++) \ container.at(i) OPSIGN ## = rhs[i]; \ \ @@ -85,24 +88,31 @@ public: // Array-like interface TResource & operator[](GameResID index) { + resizeContainer(); return operator[](index.getNum()); } const TResource & operator[](GameResID index) const { + if (index.getNum() >= container.size()) { + static const TResource defaultValue{}; + return defaultValue; + } return operator[](index.getNum()); } TResource & operator[](size_t index) { + resizeContainer(); return container.at(index); } const TResource & operator[](size_t index) const { - if(index >= container.size()) - logGlobal->error("Try to access resource which is not existing! Maybe new resources in mod not marked as modType=Resources?"); - + if (index >= container.size()) { + static const TResource defaultValue{}; + return defaultValue; + } return container.at(index); } diff --git a/lib/entities/faction/CTownHandler.cpp b/lib/entities/faction/CTownHandler.cpp index f59ba46e0..8ea1e9a0c 100644 --- a/lib/entities/faction/CTownHandler.cpp +++ b/lib/entities/faction/CTownHandler.cpp @@ -281,8 +281,8 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons LIBRARY->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]); ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE); - ret->resources = TResources(source["cost"]); - ret->produce = TResources(source["produce"]); + ret->resources.resolveFromJson(source["cost"]); + ret->produce.resolveFromJson(source["produce"]); ret->manualHeroVisit = source["manualHeroVisit"].Bool(); ret->upgradeReplacesBonuses = source["upgradeReplacesBonuses"].Bool(); diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index b264dc90a..57b198686 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -397,7 +397,7 @@ void CGameState::initDifficulty() auto setDifficulty = [this](PlayerState & state, const JsonNode & json) { //set starting resources - state.resources = TResources(json["resources"]); + state.resources.resolveFromJson(json["resources"]); //handicap const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(state.color); diff --git a/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp b/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp index 56dd9656e..ad629ce7e 100644 --- a/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp @@ -40,7 +40,7 @@ void FlaggableInstanceConstructor::initTypeData(const JsonNode & config) } } - dailyIncome = ResourceSet(config["dailyIncome"]); + dailyIncome.resolveFromJson(config["dailyIncome"]); } void FlaggableInstanceConstructor::initializeObject(FlaggableMapObject * flaggable) const diff --git a/lib/modding/ModManager.cpp b/lib/modding/ModManager.cpp index cf5339098..01e961197 100644 --- a/lib/modding/ModManager.cpp +++ b/lib/modding/ModManager.cpp @@ -807,10 +807,7 @@ void ModDependenciesResolver::tryAddMods(TModList modsToResolve, const ModsStora { resolvedOnCurrentTreeLevel.insert(*it); // Not to the resolvedModIDs, so current node children will be resolved on the next iteration assert(!vstd::contains(sortedValidMods, *it)); - if(storage.getMod(*it).getValue("modType").String() == "Resources") // Resources needs to load before core to make it possible to override core elements with new resources and for correct init of ResourceSet - sortedValidMods.insert(sortedValidMods.begin(), *it); - else - sortedValidMods.push_back(*it); + sortedValidMods.push_back(*it); it = modsToResolve.erase(it); continue; } diff --git a/mapeditor/mapsettings/eventsettings.cpp b/mapeditor/mapsettings/eventsettings.cpp index e800e5162..63a287863 100644 --- a/mapeditor/mapsettings/eventsettings.cpp +++ b/mapeditor/mapsettings/eventsettings.cpp @@ -53,7 +53,9 @@ TResources resourcesFromVariant(const QVariant & v) JsonNode vJson; for(auto r : v.toMap().keys()) vJson[r.toStdString()].Integer() = v.toMap().value(r).toInt(); - return TResources(vJson); + ResourceSet res; + res.resolveFromJson(vJson); + return res; } QVariant toVariant(std::vector objects) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5694725c8..722c601d6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2293,7 +2293,8 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool return true; } - auto costBase = TResources(gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]); + ResourceSet costBase; + costBase.resolveFromJson(gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]); auto costExponent = gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float(); auto cost = costBase * std::pow(t->spellResearchAcceptedCounter + 1, costExponent);