From d808bd6412f546344555f9212e304a005c1afd2c Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:08:18 +0200 Subject: [PATCH] introduce toResourceType --- AI/Nullkiller/AIUtility.cpp | 2 +- AI/Nullkiller/Goals/AbstractGoal.cpp | 4 +- AI/VCAI/Goals/AbstractGoal.cpp | 4 +- client/adventureMap/AdventureMapWidget.cpp | 2 +- client/mainmenu/CStatisticScreen.cpp | 24 ++++++------ client/widgets/markets/TradePanels.cpp | 2 +- client/windows/CCastleInterface.cpp | 2 +- include/vcmi/ResourceType.h | 25 ++++++++++++ include/vcmi/ResourceTypeService.h | 25 ++++++++++++ include/vcmi/Services.h | 2 + lib/CMakeLists.txt | 2 + lib/GameLibrary.cpp | 5 +++ lib/GameLibrary.h | 1 + lib/ResourceSet.cpp | 6 +-- lib/constants/EntityIdentifiers.cpp | 14 ++++++- lib/constants/EntityIdentifiers.h | 6 +++ lib/entities/ResourceTypeHandler.cpp | 38 ++++++++----------- lib/entities/ResourceTypeHandler.h | 37 ++++++------------ lib/gameState/GameStatistics.cpp | 12 +++--- lib/json/JsonRandom.cpp | 2 +- .../CommonConstructors.cpp | 2 +- lib/mapObjects/CGResource.cpp | 2 +- lib/mapObjects/CQuest.cpp | 2 +- lib/mapObjects/IMarket.cpp | 10 ++--- lib/mapObjects/MiscObjects.cpp | 6 +-- lib/rmg/CRmgTemplate.cpp | 2 +- lib/texts/MetaString.cpp | 4 +- mapeditor/inspector/towneventdialog.cpp | 4 +- mapeditor/mapsettings/eventsettings.cpp | 2 +- mapeditor/mapsettings/timedevent.cpp | 4 +- .../graphicelements/CardItem.cpp | 4 +- mapeditor/templateeditor/mineselector.cpp | 2 +- server/processors/NewTurnProcessor.cpp | 2 +- test/mock/mock_Services.h | 1 + 34 files changed, 160 insertions(+), 102 deletions(-) create mode 100644 include/vcmi/ResourceType.h create mode 100644 include/vcmi/ResourceTypeService.h diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index 4d5a5a6c5..bf6b1536e 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -471,7 +471,7 @@ int32_t getArtifactBonusScoreImpl(const std::shared_ptr & bonus) case BonusType::UNDEAD_RAISE_PERCENTAGE: return bonus->val * 400; case BonusType::GENERATE_RESOURCE: - return bonus->val * LIBRARY->resourceTypeHandler->getById(bonus->subtype.as())->getPrice() * 10; + return bonus->val * bonus->subtype.as().toResource()->getPrice() * 10; case BonusType::SPELL_DURATION: return bonus->val * 200; case BonusType::MAGIC_RESISTANCE: diff --git a/AI/Nullkiller/Goals/AbstractGoal.cpp b/AI/Nullkiller/Goals/AbstractGoal.cpp index be5893082..6f92d1ec8 100644 --- a/AI/Nullkiller/Goals/AbstractGoal.cpp +++ b/AI/Nullkiller/Goals/AbstractGoal.cpp @@ -44,13 +44,13 @@ std::string AbstractGoal::toString() const switch(goalType) { case COLLECT_RES: - desc = "COLLECT RESOURCE " + LIBRARY->resourceTypeHandler->getById(resID)->getJsonKey() + " (" + std::to_string(value) + ")"; + desc = "COLLECT RESOURCE " + GameResID(resID).toResource()->getJsonKey() + " (" + std::to_string(value) + ")"; break; case TRADE: { auto obj = cb->getObjInstance(ObjectInstanceID(objid)); if (obj) - desc = (boost::format("TRADE %d of %s at %s") % value % LIBRARY->resourceTypeHandler->getById(resID)->getJsonKey() % obj->getObjectName()).str(); + desc = (boost::format("TRADE %d of %s at %s") % value % GameResID(resID).toResource()->getJsonKey() % obj->getObjectName()).str(); } break; case GATHER_TROOPS: diff --git a/AI/VCAI/Goals/AbstractGoal.cpp b/AI/VCAI/Goals/AbstractGoal.cpp index 0c8a2bc89..8e9811a62 100644 --- a/AI/VCAI/Goals/AbstractGoal.cpp +++ b/AI/VCAI/Goals/AbstractGoal.cpp @@ -57,13 +57,13 @@ std::string AbstractGoal::name() const //TODO: virtualize case BUILD_STRUCTURE: return "BUILD STRUCTURE"; case COLLECT_RES: - desc = "COLLECT RESOURCE " + LIBRARY->resourceTypeHandler->getById(resID)->getJsonKey() + " (" + std::to_string(value) + ")"; + desc = "COLLECT RESOURCE " + GameResID(resID).toResource()->getJsonKey() + " (" + std::to_string(value) + ")"; break; case TRADE: { auto obj = cb->getObjInstance(ObjectInstanceID(objid)); if (obj) - desc = (boost::format("TRADE %d of %s at %s") % value % LIBRARY->resourceTypeHandler->getById(resID)->getJsonKey() % obj->getObjectName()).str(); + desc = (boost::format("TRADE %d of %s at %s") % value % GameResID(resID).toResource()->getJsonKey() % obj->getObjectName()).str(); } break; case GATHER_TROOPS: diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index b989bd4a4..6cc5216c7 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -288,7 +288,7 @@ std::shared_ptr AdventureMapWidget::buildResourceDateBar(const JsonN for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++) //TODO: configurable resource support { - const auto & node = input[LIBRARY->resourceTypeHandler->getById(i)->getJsonKey()]; + const auto & node = input[GameResID(i).toResource()->getJsonKey()]; if(node.isNull()) continue; diff --git a/client/mainmenu/CStatisticScreen.cpp b/client/mainmenu/CStatisticScreen.cpp index fa80a6f68..629d1683d 100644 --- a/client/mainmenu/CStatisticScreen.cpp +++ b/client/mainmenu/CStatisticScreen.cpp @@ -80,7 +80,7 @@ void CStatisticScreen::onSelectButton() auto possibleRes = std::vector{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS}; std::vector resourceText; for(const auto & res : possibleRes) - resourceText.emplace_back(MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(res)->getNameTextID()).toString()); + resourceText.emplace_back(MetaString::createFromTextID(res.toResource()->getNameTextID()).toString()); ENGINE->windows().createAndPushWindow(resourceText, [this, content, possibleRes](int index) { @@ -170,7 +170,7 @@ std::shared_ptr CStatisticScreen::getContent(Content c, EGameResID r case CHART_RESOURCES: plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.resources[res]; }); - return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(res)->getNameTextID()).toString(), plotData, icons, 0); + return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(res.toResource()->getNameTextID()).toString(), plotData, icons, 0); case CHART_INCOME: plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.income; }); @@ -194,7 +194,7 @@ std::shared_ptr CStatisticScreen::getContent(Content c, EGameResID r case CHART_NUMBER_OF_MINES: plotData = extractData(statistic, [res](StatisticDataSetEntry val) -> float { return val.numMines[res]; }); - return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(res)->getNameTextID()).toString(), plotData, icons, 0); + return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(res.toResource()->getNameTextID()).toString(), plotData, icons, 0); case CHART_ARMY_STRENGTH: plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.armyStrength; }); @@ -206,11 +206,11 @@ std::shared_ptr CStatisticScreen::getContent(Content c, EGameResID r case CHART_RESOURCES_SPENT_ARMY: plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForArmy[res]; }); - return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(res)->getNameTextID()).toString(), plotData, icons, 0); + return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(res.toResource()->getNameTextID()).toString(), plotData, icons, 0); case CHART_RESOURCES_SPENT_BUILDINGS: plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForBuildings[res]; }); - return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(res)->getNameTextID()).toString(), plotData, icons, 0); + return std::make_shared(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + MetaString::createFromTextID(res.toResource()->getNameTextID()).toString(), plotData, icons, 0); case CHART_MAP_EXPLORED: plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.mapExploredRatio; }); @@ -331,43 +331,43 @@ OverviewPanel::OverviewPanel(Rect position, std::string title, const StatisticDa } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::GOLD)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::GOLD).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::GOLD]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::WOOD)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::WOOD).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::WOOD]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::MERCURY)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::MERCURY).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::MERCURY]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::ORE)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::ORE).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::ORE]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::SULFUR)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::SULFUR).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::SULFUR]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::CRYSTAL)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::CRYSTAL).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::CRYSTAL]); } }, { - LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(EGameResID::GEMS)->getNameTextID()).toString(), [this](PlayerColor color){ + LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + MetaString::createFromTextID(GameResID(EGameResID::GEMS).toResource()->getNameTextID()).toString(), [this](PlayerColor color){ auto val = playerDataFilter(color).back(); return std::to_string(val.tradeVolume[EGameResID::GEMS]); } diff --git a/client/widgets/markets/TradePanels.cpp b/client/widgets/markets/TradePanels.cpp index 56d8d3998..58fe1119b 100644 --- a/client/widgets/markets/TradePanels.cpp +++ b/client/widgets/markets/TradePanels.cpp @@ -176,7 +176,7 @@ void CTradeableItem::hover(bool on) ENGINE->statusbar()->write(LIBRARY->artifacts()->getByIndex(id)->getNameTranslated()); break; case EType::RESOURCE: - ENGINE->statusbar()->write(MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(id)->getNameTextID()).toString()); + ENGINE->statusbar()->write(MetaString::createFromTextID(GameResID(id).toResource()->getNameTextID()).toString()); break; case EType::PLAYER: ENGINE->statusbar()->write(LIBRARY->generaltexth->capColors[id]); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 6cc31ff79..d1b5ee1be 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1127,7 +1127,7 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID: else //Mystic Pond produced something; { descr += "\n\n" + hasProduced; - boost::algorithm::replace_first(descr,"%s",MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(town->bonusValue.first)->getNameTextID()).toString()); + boost::algorithm::replace_first(descr,"%s",MetaString::createFromTextID(GameResID(town->bonusValue.first).toResource()->getNameTextID()).toString()); boost::algorithm::replace_first(descr,"%d",std::to_string(town->bonusValue.second)); } } diff --git a/include/vcmi/ResourceType.h b/include/vcmi/ResourceType.h new file mode 100644 index 000000000..bfe9a0aeb --- /dev/null +++ b/include/vcmi/ResourceType.h @@ -0,0 +1,25 @@ +/* + * ResourceType.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "Entity.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class GameResID; + +class DLL_LINKAGE ResourceType : public EntityT +{ + virtual int getPrice() const = 0; +}; + + +VCMI_LIB_NAMESPACE_END diff --git a/include/vcmi/ResourceTypeService.h b/include/vcmi/ResourceTypeService.h new file mode 100644 index 000000000..4788bc243 --- /dev/null +++ b/include/vcmi/ResourceTypeService.h @@ -0,0 +1,25 @@ +/* + * ResourceTypeService.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +#include "EntityService.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class GameResID; +class ResourceType; + +class DLL_LINKAGE ResourceTypeService : public EntityServiceT +{ +public: +}; + +VCMI_LIB_NAMESPACE_END diff --git a/include/vcmi/Services.h b/include/vcmi/Services.h index 9ca566e48..45ce55f9a 100644 --- a/include/vcmi/Services.h +++ b/include/vcmi/Services.h @@ -19,6 +19,7 @@ class CreatureService; class FactionService; class HeroClassService; class HeroTypeService; +class ResourceTypeService; class SkillService; class JsonNode; class BattleFieldService; @@ -52,6 +53,7 @@ public: virtual const FactionService * factions() const = 0; virtual const HeroClassService * heroClasses() const = 0; virtual const HeroTypeService * heroTypes() const = 0; + virtual const ResourceTypeService * resources() const = 0; #if SCRIPTING_ENABLED virtual const scripting::Service * scripts() const = 0; #endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cd058104c..074421588 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -428,6 +428,8 @@ set(lib_MAIN_HEADERS ../include/vcmi/HeroClassService.h ../include/vcmi/HeroType.h ../include/vcmi/HeroTypeService.h + ../include/vcmi/ResourceType.h + ../include/vcmi/ResourceTypeService.h ../include/vcmi/Metatype.h ../include/vcmi/Player.h ../include/vcmi/ServerCallback.h diff --git a/lib/GameLibrary.cpp b/lib/GameLibrary.cpp index c2c90ae18..029acb560 100644 --- a/lib/GameLibrary.cpp +++ b/lib/GameLibrary.cpp @@ -75,6 +75,11 @@ const HeroTypeService * GameLibrary::heroTypes() const return heroh.get(); } +const ResourceTypeService * GameLibrary::resources() const +{ + return resourceTypeHandler.get(); +} + #if SCRIPTING_ENABLED const scripting::Service * GameLibrary::scripts() const { diff --git a/lib/GameLibrary.h b/lib/GameLibrary.h index 58d7c94c6..2218a0817 100644 --- a/lib/GameLibrary.h +++ b/lib/GameLibrary.h @@ -60,6 +60,7 @@ public: const FactionService * factions() const override; const HeroClassService * heroClasses() const override; const HeroTypeService * heroTypes() const override; + const ResourceTypeService * resources() const override; #if SCRIPTING_ENABLED const scripting::Service * scripts() const override; #endif diff --git a/lib/ResourceSet.cpp b/lib/ResourceSet.cpp index c47e0cfe1..10f435538 100644 --- a/lib/ResourceSet.cpp +++ b/lib/ResourceSet.cpp @@ -23,7 +23,7 @@ ResourceSet::ResourceSet() = default; ResourceSet::ResourceSet(const JsonNode & node) { for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) - container[i] = static_cast(node[LIBRARY->resourceTypeHandler->getById(i)->getJsonKey()].Float()); + container[i] = static_cast(node[i.toResource()->getJsonKey()].Float()); } void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName) @@ -38,7 +38,7 @@ void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string if(idx == EGameResID::MITHRIL) continue; - handler.serializeInt(LIBRARY->resourceTypeHandler->getById(idx)->getJsonKey(), this->operator[](idx), 0); + handler.serializeInt(idx.toResource()->getJsonKey(), this->operator[](idx), 0); } } @@ -102,7 +102,7 @@ TResourceCap ResourceSet::marketValue() const { TResourceCap total = 0; for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) - total += static_cast(LIBRARY->resourceTypeHandler->getById(i)->getPrice()) * static_cast(operator[](i)); + total += static_cast(i.toResource()->getPrice()) * static_cast(operator[](i)); return total; } diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index 51ddc52ab..35158f6e5 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -399,6 +401,16 @@ const HeroType * HeroTypeID::toEntity(const Services * services) const return services->heroTypes()->getByIndex(num); } +const Resource * GameResID::toResource() const +{ + return dynamic_cast(toEntity(LIBRARY)); +} + +const ResourceType * GameResID::toEntity(const Services * services) const +{ + return services->resources()->getByIndex(num); +} + si32 SpellID::decode(const std::string & identifier) { if (identifier == "preset") @@ -629,7 +641,7 @@ si32 GameResID::decode(const std::string & identifier) std::string GameResID::encode(const si32 index) { - return LIBRARY->resourceTypeHandler->getById(index)->getJsonKey(); + return GameResID(index).toResource()->getJsonKey(); } si32 BuildingTypeUniqueID::decode(const std::string & identifier) diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index 7387f0988..9852eb0fd 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -25,6 +25,9 @@ class CHero; class CHeroClass; class HeroClass; class HeroTypeService; +class Resource; +class ResourceType; +class ResourceTypeService; class CFaction; class Faction; class Skill; @@ -1079,6 +1082,9 @@ public: static si32 decode(const std::string & identifier); static std::string encode(const si32 index); static std::string entityType(); + + const Resource * toResource() const; + const ResourceType * toEntity(const Services * services) const; }; class DLL_LINKAGE BuildingTypeUniqueID : public Identifier diff --git a/lib/entities/ResourceTypeHandler.cpp b/lib/entities/ResourceTypeHandler.cpp index 75ea7d3ef..b52e6020b 100644 --- a/lib/entities/ResourceTypeHandler.cpp +++ b/lib/entities/ResourceTypeHandler.cpp @@ -18,17 +18,17 @@ VCMI_LIB_NAMESPACE_BEGIN -std::string resources::ResourceType::getNameTextID() const +std::string Resource::getNameTextID() const { return TextIdentifier( "resources", modScope, identifier, "name" ).get(); } -std::string resources::ResourceType::getNameTranslated() const +std::string Resource::getNameTranslated() const { return LIBRARY->generaltexth->translate(getNameTextID()); } -void resources::ResourceType::registerIcons(const IconRegistar & cb) const +void Resource::registerIcons(const IconRegistar & cb) const { cb(getIconIndex(), 0, "SMALRES", iconSmall); cb(getIconIndex(), 0, "RESOURCE", iconMedium); @@ -42,36 +42,28 @@ std::vector ResourceTypeHandler::loadLegacyData() return std::vector(8, JsonNode(JsonMap())); } -std::shared_ptr ResourceTypeHandler::loadObjectImpl(std::string scope, std::string name, const JsonNode & data, size_t index) +std::shared_ptr ResourceTypeHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) { - auto ret = std::make_shared(); + auto ret = std::make_shared(); ret->id = GameResID(index); ret->modScope = scope; - ret->identifier = name; + ret->identifier = identifier; - ret->price = data["price"].Integer(); - ret->iconSmall = data["images"]["small"].String(); - ret->iconMedium = data["images"]["medium"].String(); - ret->iconLarge = data["images"]["large"].String(); + ret->price = json["price"].Integer(); + ret->iconSmall = json["images"]["small"].String(); + ret->iconMedium = json["images"]["medium"].String(); + ret->iconLarge = json["images"]["large"].String(); - LIBRARY->generaltexth->registerString(scope, ret->getNameTextID(), data["name"]); + LIBRARY->generaltexth->registerString(scope, ret->getNameTextID(), json["name"]); return ret; } -/// loads single object into game. Scope is namespace of this object, same as name of source mod -void ResourceTypeHandler::loadObject(std::string scope, std::string name, const JsonNode & data) +const std::vector & ResourceTypeHandler::getTypeNames() const { - objects.push_back(loadObjectImpl(scope, name, data, objects.size())); - registerObject(scope, "resources", name, objects.back()->getIndex()); -} - -void ResourceTypeHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) -{ - assert(objects[index] == nullptr); // ensure that this id was not loaded before - objects[index] = loadObjectImpl(scope, name, data, index); - registerObject(scope, "resources", name, objects[index]->getIndex()); + static const std::vector types = { "resources" }; + return types; } std::vector ResourceTypeHandler::getAllObjects() const @@ -79,7 +71,7 @@ std::vector ResourceTypeHandler::getAllObjects() const std::vector result; for (const auto & resource : objects) - result.push_back(resource->id); + result.push_back(resource->getId()); return result; } diff --git a/lib/entities/ResourceTypeHandler.h b/lib/entities/ResourceTypeHandler.h index 26fb2a15c..66d7f2701 100644 --- a/lib/entities/ResourceTypeHandler.h +++ b/lib/entities/ResourceTypeHandler.h @@ -12,6 +12,8 @@ #include #include +#include +#include #include "../constants/EntityIdentifiers.h" #include "../IHandlerBase.h" #include "../filesystem/ResourcePath.h" @@ -20,12 +22,9 @@ VCMI_LIB_NAMESPACE_BEGIN class ResourceTypeHandler; -namespace resources +class DLL_LINKAGE Resource : public ResourceType { - -class DLL_LINKAGE ResourceType : public EntityT -{ - friend class VCMI_LIB_WRAP_NAMESPACE(ResourceTypeHandler); + friend class ResourceTypeHandler; GameResID id; //backlink @@ -38,10 +37,7 @@ class DLL_LINKAGE ResourceType : public EntityT std::string modScope; public: - int getPrice() const - { - return price; - } + int getPrice() const override { return price; } std::string getJsonKey() const override { return identifier; } int32_t getIndex() const override { return id.getNum(); } @@ -53,27 +49,18 @@ public: std::string getNameTranslated() const override; }; -} - -class DLL_LINKAGE ResourceTypeHandler : public IHandlerBase +class DLL_LINKAGE ResourceTypeHandler : public CHandlerBase { - std::shared_ptr loadObjectImpl(std::string scope, std::string name, const JsonNode & data, size_t index); public: + std::shared_ptr loadFromJson(const std::string & scope, + const JsonNode & json, + const std::string & identifier, + size_t index) override; + + const std::vector & getTypeNames() const override; std::vector loadLegacyData() override; - /// loads single object into game. Scope is namespace of this object, same as name of source mod - 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; - std::vector getAllObjects() const; - - const resources::ResourceType * getById(GameResID index) const - { - return objects.at(index).get(); - } - -private: - std::vector> objects; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/gameState/GameStatistics.cpp b/lib/gameState/GameStatistics.cpp index 42aeb0dae..cbfe9cf13 100644 --- a/lib/gameState/GameStatistics.cpp +++ b/lib/gameState/GameStatistics.cpp @@ -108,7 +108,7 @@ void StatisticDataSetEntry::serializeJson(JsonSerializeFormat & handler) auto zonesData = handler.enterStruct("numMines"); for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects()) if(idx != GameResID::MITHRIL) - handler.serializeInt(LIBRARY->resourceTypeHandler->getById(idx)->getJsonKey(), numMines[idx], 0); + handler.serializeInt(idx.toResource()->getJsonKey(), numMines[idx], 0); } handler.serializeInt("score", score); handler.serializeInt("maxHeroLevel", maxHeroLevel); @@ -193,15 +193,15 @@ std::string StatisticDataSet::toCsv(std::string sep) const ss << "EventDefeatedStrongestHero" << sep; ss << "MovementPointsUsed"; for(auto & resource : resources) - ss << sep << LIBRARY->resourceTypeHandler->getById(resource)->getJsonKey(); + ss << sep << resource.toResource()->getJsonKey(); for(auto & resource : resources) - ss << sep << LIBRARY->resourceTypeHandler->getById(resource)->getJsonKey() + "Mines"; + ss << sep << resource.toResource()->getJsonKey() + "Mines"; for(auto & resource : resources) - ss << sep << LIBRARY->resourceTypeHandler->getById(resource)->getJsonKey() + "SpentResourcesForArmy"; + ss << sep << resource.toResource()->getJsonKey() + "SpentResourcesForArmy"; for(auto & resource : resources) - ss << sep << LIBRARY->resourceTypeHandler->getById(resource)->getJsonKey() + "SpentResourcesForBuildings"; + ss << sep << resource.toResource()->getJsonKey() + "SpentResourcesForBuildings"; for(auto & resource : resources) - ss << sep << LIBRARY->resourceTypeHandler->getById(resource)->getJsonKey() + "TradeVolume"; + ss << sep << resource.toResource()->getJsonKey() + "TradeVolume"; ss << "\r\n"; for(auto & entry : data) diff --git a/lib/json/JsonRandom.cpp b/lib/json/JsonRandom.cpp index 9f44a4f81..5ee9d9c15 100644 --- a/lib/json/JsonRandom.cpp +++ b/lib/json/JsonRandom.cpp @@ -301,7 +301,7 @@ JsonRandom::JsonRandom(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer) for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) { - ret[i] = loadValue(value[LIBRARY->resourceTypeHandler->getById(i)->getJsonKey()], variables); + ret[i] = loadValue(value[i.toResource()->getJsonKey()], variables); } return ret; } diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index f3c0dbcfa..241d715b9 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -61,7 +61,7 @@ bool ResourceInstanceConstructor::hasNameTextID() const std::string ResourceInstanceConstructor::getNameTextID() const { - return LIBRARY->resourceTypeHandler->getById(resourceType)->getNameTextID(); + return resourceType.toResource()->getNameTextID(); } GameResID ResourceInstanceConstructor::getResourceType() const diff --git a/lib/mapObjects/CGResource.cpp b/lib/mapObjects/CGResource.cpp index 68c6733cf..52a318f44 100644 --- a/lib/mapObjects/CGResource.cpp +++ b/lib/mapObjects/CGResource.cpp @@ -51,7 +51,7 @@ GameResID CGResource::resourceID() const std::string CGResource::getHoverText(PlayerColor player) const { - return MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(resourceID())->getNameTextID()).toString(); + return MetaString::createFromTextID(resourceID().toResource()->getNameTextID()).toString(); } void CGResource::pickRandomObject(IGameRandomizer & gameRandomizer) diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 45e6cccaa..2e98300df 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -376,7 +376,7 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects()) if(idx != GameResID::MITHRIL) - handler.serializeInt(LIBRARY->resourceTypeHandler->getById(idx)->getJsonKey(), mission.resources[idx], 0); + handler.serializeInt(idx.toResource()->getJsonKey(), mission.resources[idx], 0); } if(missionType == "Hero") diff --git a/lib/mapObjects/IMarket.cpp b/lib/mapObjects/IMarket.cpp index 690fac6dc..414d57de5 100644 --- a/lib/mapObjects/IMarket.cpp +++ b/lib/mapObjects/IMarket.cpp @@ -33,8 +33,8 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) { double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5); - double r = LIBRARY->resourceTypeHandler->getById(id1)->getPrice(); //value of given resource - double g = LIBRARY->resourceTypeHandler->getById(id2)->getPrice() / effectiveness; //value of wanted resource + double r = GameResID(id1).toResource()->getPrice(); //value of given resource + double g = GameResID(id2).toResource()->getPrice() / effectiveness; //value of wanted resource if(r>g) //if given resource is more expensive than wanted { @@ -54,7 +54,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)]; double r = LIBRARY->creatures()->getByIndex(id1)->getRecruitCost(EGameResID::GOLD); //value of given creature in gold - double g = LIBRARY->resourceTypeHandler->getById(id2)->getPrice() / effectiveness; //value of wanted resource + double g = GameResID(id2).toResource()->getPrice() / effectiveness; //value of wanted resource if(r>g) //if given resource is more expensive than wanted { @@ -75,7 +75,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) case EMarketMode::RESOURCE_ARTIFACT: { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); - double r = LIBRARY->resourceTypeHandler->getById(id1)->getPrice(); //value of offered resource + double r = GameResID(id1).toResource()->getPrice(); //value of offered resource double g = LIBRARY->artifacts()->getByIndex(id2)->getPrice() / effectiveness; //value of bought artifact in gold if(id1 != 6) //non-gold prices are doubled @@ -89,7 +89,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); double r = LIBRARY->artifacts()->getByIndex(id1)->getPrice() * effectiveness; - double g = LIBRARY->resourceTypeHandler->getById(id2)->getPrice(); + double g = GameResID(id2).toResource()->getPrice(); // if(id2 != 6) //non-gold prices are doubled // r /= 2; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index aefed18a7..c4c7fe057 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -165,7 +165,7 @@ std::string CGMine::getHoverText(PlayerColor player) const std::string hoverName = CArmedInstance::getHoverText(player); if (tempOwner != PlayerColor::NEUTRAL) - hoverName += "\n(" + MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(producedResource.getNum())->getNameTextID()).toString() + ")"; + hoverName += "\n(" + MetaString::createFromTextID(producedResource.toResource()->getNameTextID()).toString() + ")"; if(stacksCount()) { @@ -239,7 +239,7 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) { JsonNode node; for(const auto & resID : abandonedMineResources) - node.Vector().emplace_back(LIBRARY->resourceTypeHandler->getById(resID)->getJsonKey()); + node.Vector().emplace_back(resID.toResource()->getJsonKey()); handler.serializeRaw("possibleResources", node, std::nullopt); } @@ -254,7 +254,7 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) { std::vector resNames; for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects()) - resNames.push_back(LIBRARY->resourceTypeHandler->getById(res)->getJsonKey()); + resNames.push_back(res.toResource()->getJsonKey()); int raw_res = vstd::find_pos(resNames, s); if(raw_res < 0) logGlobal->error("Invalid resource name: %s", s); diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index 1dee90b47..1cc33e347 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -537,7 +537,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler) for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects()) if(idx != GameResID::MITHRIL) - handler.serializeInt(LIBRARY->resourceTypeHandler->getById(idx)->getJsonKey(), mines[idx], 0); + handler.serializeInt(idx.toResource()->getJsonKey(), mines[idx], 0); } handler.serializeStruct("customObjects", objectConfig); diff --git a/lib/texts/MetaString.cpp b/lib/texts/MetaString.cpp index 90256ec8c..60e037a61 100644 --- a/lib/texts/MetaString.cpp +++ b/lib/texts/MetaString.cpp @@ -379,7 +379,7 @@ void MetaString::appendName(const CreatureID & id, TQuantity count) void MetaString::appendName(const GameResID& id) { - appendTextID(LIBRARY->resourceTypeHandler->getById(id)->getNameTextID()); + appendTextID(id.toResource()->getNameTextID()); } void MetaString::appendNameSingular(const CreatureID & id) @@ -424,7 +424,7 @@ void MetaString::replaceName(const SpellID & id) void MetaString::replaceName(const GameResID& id) { - replaceTextID(LIBRARY->resourceTypeHandler->getById(id)->getNameTextID()); + replaceTextID(id.toResource()->getNameTextID()); } void MetaString::replaceNameSingular(const CreatureID & id) diff --git a/mapeditor/inspector/towneventdialog.cpp b/mapeditor/inspector/towneventdialog.cpp index 56fa31de6..2ed52009c 100644 --- a/mapeditor/inspector/towneventdialog.cpp +++ b/mapeditor/inspector/towneventdialog.cpp @@ -93,7 +93,7 @@ void TownEventDialog::initResources() item->setText(name); ui->resourcesTable->setItem(i, 0, item); - int val = resourcesMap.value(QString::fromStdString(LIBRARY->resourceTypeHandler->getById(i)->getJsonKey())).toInt(); + int val = resourcesMap.value(QString::fromStdString(i.toResource()->getJsonKey())).toInt(); auto * edit = new QSpinBox(ui->resourcesTable); edit->setMaximum(i == GameResID::GOLD ? MAXIMUM_GOLD_CHANGE : MAXIMUM_RESOURCE_CHANGE); edit->setMinimum(i == GameResID::GOLD ? -MAXIMUM_GOLD_CHANGE : -MAXIMUM_RESOURCE_CHANGE); @@ -232,7 +232,7 @@ QVariantMap TownEventDialog::resourcesToVariant() auto res = params.value("resources").toMap(); for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) { - auto itemType = QString::fromStdString(LIBRARY->resourceTypeHandler->getById(i)->getJsonKey()); + auto itemType = QString::fromStdString(i.toResource()->getJsonKey()); auto * itemQty = static_cast (ui->resourcesTable->cellWidget(i, 1)); res[itemType] = QVariant::fromValue(itemQty->value()); diff --git a/mapeditor/mapsettings/eventsettings.cpp b/mapeditor/mapsettings/eventsettings.cpp index 858e69438..e800e5162 100644 --- a/mapeditor/mapsettings/eventsettings.cpp +++ b/mapeditor/mapsettings/eventsettings.cpp @@ -44,7 +44,7 @@ QVariant toVariant(const TResources & resources) { QVariantMap result; for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) - result[QString::fromStdString(LIBRARY->resourceTypeHandler->getById(i)->getJsonKey())] = QVariant::fromValue(resources[i]); + result[QString::fromStdString(i.toResource()->getJsonKey())] = QVariant::fromValue(resources[i]); return result; } diff --git a/mapeditor/mapsettings/timedevent.cpp b/mapeditor/mapsettings/timedevent.cpp index cdc288a82..879af496e 100644 --- a/mapeditor/mapsettings/timedevent.cpp +++ b/mapeditor/mapsettings/timedevent.cpp @@ -53,7 +53,7 @@ TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent) MetaString str; str.appendName(GameResID(i)); auto name = QString::fromStdString(str.toString()); - int val = params.value("resources").toMap().value(QString::fromStdString(LIBRARY->resourceTypeHandler->getById(i)->getJsonKey())).toInt(); + int val = params.value("resources").toMap().value(QString::fromStdString(i.toResource()->getJsonKey())).toInt(); ui->resources->setItem(i, 0, new QTableWidgetItem(name)); auto nval = new QTableWidgetItem(QString::number(val)); nval->setFlags(nval->flags() | Qt::ItemIsEditable); @@ -98,7 +98,7 @@ void TimedEvent::on_TimedEvent_finished(int result) auto res = target->data(Qt::UserRole).toMap().value("resources").toMap(); for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects()) { - auto itemType = QString::fromStdString(LIBRARY->resourceTypeHandler->getById(i)->getJsonKey()); + auto itemType = QString::fromStdString(i.toResource()->getJsonKey()); auto * itemQty = ui->resources->item(i, 1); res[itemType] = QVariant::fromValue(itemQty->text().toInt()); } diff --git a/mapeditor/templateeditor/graphicelements/CardItem.cpp b/mapeditor/templateeditor/graphicelements/CardItem.cpp index 884ee6cbc..2e5059d05 100644 --- a/mapeditor/templateeditor/graphicelements/CardItem.cpp +++ b/mapeditor/templateeditor/graphicelements/CardItem.cpp @@ -151,11 +151,11 @@ int CardItem::getId() void CardItem::setResAmount(GameResID res, int val) { - auto textElem = getElementById(doc, "text" + QString::fromStdString(LIBRARY->resourceTypeHandler->getById(res)->getJsonKey())); + auto textElem = getElementById(doc, "text" + QString::fromStdString(res.toResource()->getJsonKey())); textElem.setAttribute("style", textElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";")); textElem.firstChild().setNodeValue(val ? QString::number(val) : ""); - auto iconElem = getElementById(doc, "icon" + QString::fromStdString(LIBRARY->resourceTypeHandler->getById(res)->getJsonKey())); + auto iconElem = getElementById(doc, "icon" + QString::fromStdString(res.toResource()->getJsonKey())); iconElem.setAttribute("opacity", val ? "1.0" : "0.1"); } diff --git a/mapeditor/templateeditor/mineselector.cpp b/mapeditor/templateeditor/mineselector.cpp index 9a731aa1a..3e394ed7d 100644 --- a/mapeditor/templateeditor/mineselector.cpp +++ b/mapeditor/templateeditor/mineselector.cpp @@ -35,7 +35,7 @@ MineSelector::MineSelector(std::map & mines) : ui->tableWidgetMines->setHorizontalHeaderLabels({tr("Resource"), tr("Mines")}); for (int row = 0; row < resourcesToShow.size(); ++row) { - auto name = MetaString::createFromTextID(LIBRARY->resourceTypeHandler->getById(resourcesToShow[row].getNum())->getNameTextID()).toString(); + auto name = MetaString::createFromTextID(resourcesToShow[row].toResource()->getNameTextID()).toString(); auto label = new QLabel(QString::fromStdString(name)); label->setAlignment(Qt::AlignCenter); ui->tableWidgetMines->setCellWidget(row, 0, label); diff --git a/server/processors/NewTurnProcessor.cpp b/server/processors/NewTurnProcessor.cpp index b277256b4..8f1634620 100644 --- a/server/processors/NewTurnProcessor.cpp +++ b/server/processors/NewTurnProcessor.cpp @@ -259,7 +259,7 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne // Distribute weekly bonuses over 7 days, depending on the current day of the week for (GameResID i : LIBRARY->resourceTypeHandler->getAllObjects()) { - const std::string & name = LIBRARY->resourceTypeHandler->getById(i)->getJsonKey(); + const std::string & name = i.toResource()->getJsonKey(); int64_t weeklyBonus = difficultyConfig[name].Integer(); int64_t dayOfWeek = gameHandler->gameState().getDate(Date::DAY_OF_WEEK); int64_t dailyIncome = incomeHandicapped[i]; diff --git a/test/mock/mock_Services.h b/test/mock/mock_Services.h index cd6159141..0a7e5b201 100644 --- a/test/mock/mock_Services.h +++ b/test/mock/mock_Services.h @@ -21,6 +21,7 @@ public: MOCK_CONST_METHOD0(factions, const FactionService *()); MOCK_CONST_METHOD0(heroClasses, const HeroClassService *()); MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *()); + MOCK_CONST_METHOD0(resources, const ResourceTypeService *()); #if SCRIPTING_ENABLED MOCK_CONST_METHOD0(scripts, const scripting::Service *()); #endif