From 39bb6d5f3961ceacb7b4165b0ad61ad0b3a95db9 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:00:02 +0300 Subject: [PATCH] IMarket now able to store artifacts --- lib/gameState/CGameState.cpp | 22 ++--- lib/gameState/CGameStateCampaign.cpp | 4 +- .../CommonConstructors.cpp | 2 +- lib/mapObjects/CGMarket.cpp | 5 -- lib/mapObjects/CGMarket.h | 4 - lib/mapObjects/CGTownInstance.cpp | 86 ++++++++++++------- lib/mapObjects/CGTownInstance.h | 4 +- lib/mapObjects/IMarket.cpp | 27 ++++++ lib/mapObjects/IMarket.h | 13 ++- lib/mapping/MapFormatH3M.cpp | 12 +-- lib/mapping/MapReaderH3M.cpp | 4 +- lib/mapping/MapReaderH3M.h | 2 +- lib/networkPacks/NetPacksLib.cpp | 4 +- lib/rmg/modificators/TownPlacer.cpp | 8 +- 14 files changed, 123 insertions(+), 74 deletions(-) diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 7598801b6..79fcf5022 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -804,10 +804,10 @@ void CGameState::initTowns() //init buildings if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings { - vti->builtBuildings.erase(BuildingID::DEFAULT); - vti->builtBuildings.insert(BuildingID::VILLAGE_HALL); + vti->removeBuilding(BuildingID::DEFAULT); + vti->addBuilding(BuildingID::VILLAGE_HALL); if(vti->tempOwner != PlayerColor::NEUTRAL) - vti->builtBuildings.insert(BuildingID::TAVERN); + vti->addBuilding(BuildingID::TAVERN); auto definesBuildingsChances = VLC->settings()->getVector(EGameSettings::TOWNS_STARTING_DWELLING_CHANCES); @@ -815,32 +815,32 @@ void CGameState::initTowns() { if((getRandomGenerator().nextInt(1,100) <= definesBuildingsChances[i])) { - vti->builtBuildings.insert(basicDwellings[i]); + vti->addBuilding(basicDwellings[i]); } } } // village hall must always exist - vti->builtBuildings.insert(BuildingID::VILLAGE_HALL); + vti->addBuilding(BuildingID::VILLAGE_HALL); //init hordes for (int i = 0; i < vti->town->creatures.size(); i++) { if (vstd::contains(vti->builtBuildings, hordes[i])) //if we have horde for this level { - vti->builtBuildings.erase(hordes[i]);//remove old ID + vti->removeBuilding(hordes[i]);//remove old ID if (vti->getTown()->hordeLvl.at(0) == i)//if town first horde is this one { - vti->builtBuildings.insert(BuildingID::HORDE_1);//add it + vti->addBuilding(BuildingID::HORDE_1);//add it //if we have upgraded dwelling as well if (vstd::contains(vti->builtBuildings, upgradedDwellings[i])) - vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well + vti->addBuilding(BuildingID::HORDE_1_UPGR);//add it as well } if (vti->getTown()->hordeLvl.at(1) == i)//if town second horde is this one { - vti->builtBuildings.insert(BuildingID::HORDE_2); + vti->addBuilding(BuildingID::HORDE_2); if (vstd::contains(vti->builtBuildings, upgradedDwellings[i])) - vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR); + vti->addBuilding(BuildingID::HORDE_2_UPGR); } } } @@ -853,7 +853,7 @@ void CGameState::initTowns() }); if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED) - vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour) + vti->removeBuilding(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour) //Early check for #1444-like problems for([[maybe_unused]] const auto & building : vti->builtBuildings) diff --git a/lib/gameState/CGameStateCampaign.cpp b/lib/gameState/CGameStateCampaign.cpp index b5a30ceff..cebd9dda0 100644 --- a/lib/gameState/CGameStateCampaign.cpp +++ b/lib/gameState/CGameStateCampaign.cpp @@ -664,10 +664,10 @@ void CGameStateCampaign::initTowns() if (newBuilding == BuildingID::NONE) break; - if (town->builtBuildings.count(newBuilding) != 0) + if(town->hasBuilt(newBuilding)) break; - town->builtBuildings.insert(newBuilding); + town->addBuilding(newBuilding); auto building = town->town->buildings.at(newBuilding); newBuilding = building->upgrade; diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 3053da13a..3d2f12be1 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -248,7 +248,7 @@ CGMarket * MarketInstanceConstructor::createObject(IGameCallback * cb) const void MarketInstanceConstructor::initializeObject(CGMarket * market) const { - market->marketModes = marketModes; + market->addMarketMode(marketModes); market->marketEfficiency = marketEfficiency; if(auto univercity = dynamic_cast(market)) diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 8b6cf877e..2de4ef257 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -38,11 +38,6 @@ int CGMarket::getMarketEfficiency() const return marketEfficiency; } -bool CGMarket::allowsTrade(EMarketMode mode) const -{ - return marketModes.count(mode); -} - int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const { return -1; diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index 44a342066..0c69354b6 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -18,8 +18,6 @@ VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket { public: - - std::set marketModes; int marketEfficiency; CGMarket(IGameCallback *cb); @@ -29,7 +27,6 @@ public: ///IMarket int getMarketEfficiency() const override; - bool allowsTrade(EMarketMode mode) const override; int availableUnits(EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited std::vector availableItemsIds(EMarketMode mode) const override; @@ -37,7 +34,6 @@ public: { h & static_cast(*this); h & static_cast(*this); - h & marketModes; h & marketEfficiency; } }; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 64eddc418..f6148ac3e 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -690,35 +690,6 @@ int CGTownInstance::getMarketEfficiency() const return marketCount; } -bool CGTownInstance::allowsTrade(EMarketMode mode) const -{ - switch(mode) - { - case EMarketMode::RESOURCE_RESOURCE: - case EMarketMode::RESOURCE_PLAYER: - return hasBuilt(BuildingID::MARKETPLACE); - - case EMarketMode::ARTIFACT_RESOURCE: - case EMarketMode::RESOURCE_ARTIFACT: - return hasBuilt(BuildingSubID::ARTIFACT_MERCHANT); - - case EMarketMode::CREATURE_RESOURCE: - return hasBuilt(BuildingSubID::FREELANCERS_GUILD); - - case EMarketMode::CREATURE_UNDEAD: - return hasBuilt(BuildingSubID::CREATURE_TRANSFORMER); - - case EMarketMode::RESOURCE_SKILL: - return hasBuilt(BuildingSubID::MAGIC_UNIVERSITY); - case EMarketMode::CREATURE_EXP: - case EMarketMode::ARTIFACT_EXP: - return false; - default: - assert(0); - return false; - } -} - std::vector CGTownInstance::availableItemsIds(EMarketMode mode) const { if(mode == EMarketMode::RESOURCE_ARTIFACT) @@ -962,6 +933,55 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) c return false; } +void CGTownInstance::addBuilding(const BuildingID & buildingID) +{ + if(buildingID == BuildingID::NONE) + return; + + builtBuildings.insert(buildingID); + marketBuildingModeMapper(buildingID, [this](const EMarketMode mode) {addMarketMode(mode);}); +} + +void CGTownInstance::removeBuilding(const BuildingID & buildingID) +{ + if(!vstd::contains(builtBuildings, buildingID)) + return; + + builtBuildings.erase(buildingID); + marketBuildingModeMapper(buildingID, [this](const EMarketMode mode) {removeMarketMode(mode);}); +} + +void CGTownInstance::marketBuildingModeMapper(const BuildingID & buildingID, const std::function & func) +{ + const auto townType = (*VLC->townh)[getFaction()]->town; + if(townType->buildings.find(buildingID) == townType->buildings.end()) + return; + + // TODO how to remove hardcoded buildings? + if(buildingID == BuildingID::MARKETPLACE) + { + func(EMarketMode::RESOURCE_RESOURCE); + func(EMarketMode::RESOURCE_PLAYER); + } + else if(townType->buildings.at(buildingID)->subId == BuildingSubID::ARTIFACT_MERCHANT) + { + func(EMarketMode::ARTIFACT_RESOURCE); + func(EMarketMode::RESOURCE_ARTIFACT); + } + else if(townType->buildings.at(buildingID)->subId == BuildingSubID::FREELANCERS_GUILD) + { + func(EMarketMode::CREATURE_RESOURCE); + } + else if(townType->buildings.at(buildingID)->subId == BuildingSubID::CREATURE_TRANSFORMER) + { + func(EMarketMode::CREATURE_UNDEAD); + } + else if(townType->buildings.at(buildingID)->subId == BuildingSubID::MAGIC_UNIVERSITY) + { + func(EMarketMode::RESOURCE_SKILL); + } +} + TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const { if (vstd::contains(town->buildings, buildingID)) @@ -1132,23 +1152,23 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler) { handler.serializeLIC("buildings", buildingsLIC); - builtBuildings.insert(BuildingID::VILLAGE_HALL); + addBuilding(BuildingID::VILLAGE_HALL); if(buildingsLIC.none.empty() && buildingsLIC.all.empty()) { - builtBuildings.insert(BuildingID::DEFAULT); + addBuilding(BuildingID::DEFAULT); bool hasFort = false; handler.serializeBool("hasFort",hasFort); if(hasFort) - builtBuildings.insert(BuildingID::FORT); + addBuilding(BuildingID::FORT); } else { for(const si32 item : buildingsLIC.none) forbiddenBuildings.insert(BuildingID(item)); for(const si32 item : buildingsLIC.all) - builtBuildings.insert(BuildingID(item)); + addBuilding(BuildingID(item)); } } } diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index e57e430b7..332b45d02 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -153,7 +153,6 @@ public: EGeneratorState shipyardStatus() const override; const IObjectInterface * getObject() const override; int getMarketEfficiency() const override; //=market count - bool allowsTrade(EMarketMode mode) const override; std::vector availableItemsIds(EMarketMode mode) const override; void updateAppearance(); @@ -175,6 +174,8 @@ public: //checks if building is constructed and town has same subID bool hasBuilt(const BuildingID & buildingID) const; bool hasBuilt(const BuildingID & buildingID, FactionID townID) const; + void addBuilding(const BuildingID & buildingID); + void removeBuilding(const BuildingID & buildingID); TResources getBuildingCost(const BuildingID & buildingID) const; TResources dailyIncome() const; //calculates daily income of this town @@ -227,6 +228,7 @@ protected: void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; void serializeJsonOptions(JsonSerializeFormat & handler) override; void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override; + void marketBuildingModeMapper(const BuildingID & buildingID, const std::function & func); private: FactionID randomizeFaction(vstd::RNG & rand); diff --git a/lib/mapObjects/IMarket.cpp b/lib/mapObjects/IMarket.cpp index 469008a78..fab8a4c2d 100644 --- a/lib/mapObjects/IMarket.cpp +++ b/lib/mapObjects/IMarket.cpp @@ -20,6 +20,11 @@ VCMI_LIB_NAMESPACE_BEGIN +bool IMarket::allowsTrade(const EMarketMode mode) const +{ + return marketModes.count(mode) > 0; +} + bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const { switch(mode) @@ -135,6 +140,28 @@ int IMarket::availableUnits(const EMarketMode mode, const int marketItemSerial) } } +void IMarket::addMarketMode(const EMarketMode mode) +{ + marketModes.insert(mode); + + if(mode == EMarketMode::ARTIFACT_EXP) + altarArtifacts = std::make_shared(); +} + +void IMarket::addMarketMode(const std::set & modes) +{ + for(const auto & mode : modes) + addMarketMode(mode); +} + +void IMarket::removeMarketMode(const EMarketMode mode) +{ + marketModes.erase(mode); + + if(mode == EMarketMode::ARTIFACT_EXP) + altarArtifacts.reset(); +} + std::vector IMarket::availableItemsIds(const EMarketMode mode) const { std::vector ret; diff --git a/lib/mapObjects/IMarket.h b/lib/mapObjects/IMarket.h index d26cc0b99..fa64e81c4 100644 --- a/lib/mapObjects/IMarket.h +++ b/lib/mapObjects/IMarket.h @@ -25,22 +25,27 @@ public: }; virtual int getMarketEfficiency() const = 0; - virtual bool allowsTrade(const EMarketMode mode) const = 0; + virtual bool allowsTrade(const EMarketMode mode) const; virtual int availableUnits(const EMarketMode mode, const int marketItemSerial) const; //-1 if unlimited virtual std::vector availableItemsIds(const EMarketMode mode) const; + void addMarketMode(const EMarketMode mode); + void addMarketMode(const std::set & modes); + void removeMarketMode(const EMarketMode mode); bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units std::vector availableModes() const; template void serialize(Handler & h) { - if(h.loadingGamestate) - { - } + h & marketModes; + + if(h.loadingGamestate && vstd::contains(marketModes, EMarketMode::ARTIFACT_EXP)) + altarArtifacts = std::make_shared(); } private: std::shared_ptr altarArtifacts; + std::set marketModes; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 18b51e1b6..e067f0d44 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -2198,18 +2198,20 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt bool hasCustomBuildings = reader->readBool(); if(hasCustomBuildings) { - reader->readBitmaskBuildings(object->builtBuildings, faction); - reader->readBitmaskBuildings(object->forbiddenBuildings, faction); + object->subID = faction.value(); + for(const auto & building : reader->readBitmaskBuildings(faction)) + object->addBuilding(building); + object->forbiddenBuildings = reader->readBitmaskBuildings(faction); } // Standard buildings else { bool hasFort = reader->readBool(); if(hasFort) - object->builtBuildings.insert(BuildingID::FORT); + object->addBuilding(BuildingID::FORT); //means that set of standard building should be included - object->builtBuildings.insert(BuildingID::DEFAULT); + object->addBuilding(BuildingID::DEFAULT); } if(features.levelAB) @@ -2257,7 +2259,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt reader->skipZero(17); // New buildings - reader->readBitmaskBuildings(event.buildings, faction); + event.buildings = reader->readBitmaskBuildings(faction); event.creatures.resize(7); for(int i = 0; i < 7; ++i) diff --git a/lib/mapping/MapReaderH3M.cpp b/lib/mapping/MapReaderH3M.cpp index a2fec4e04..d911a1058 100644 --- a/lib/mapping/MapReaderH3M.cpp +++ b/lib/mapping/MapReaderH3M.cpp @@ -257,9 +257,10 @@ PlayerColor MapReaderH3M::readPlayer32() return PlayerColor(value); } -void MapReaderH3M::readBitmaskBuildings(std::set & dest, std::optional faction) +std::set MapReaderH3M::readBitmaskBuildings(std::optional faction) { std::set h3m; + std::set dest; readBitmask(h3m, features.buildingsBytes, features.buildingsCount, false); for (auto const & h3mEntry : h3m) @@ -269,6 +270,7 @@ void MapReaderH3M::readBitmaskBuildings(std::set & dest, std::option if (mapped != BuildingID::NONE) // artifact merchant may be set in random town, but not present in actual town dest.insert(mapped); } + return dest; } void MapReaderH3M::readBitmaskFactions(std::set & dest, bool invert) diff --git a/lib/mapping/MapReaderH3M.h b/lib/mapping/MapReaderH3M.h index d6312a80d..b0c83797b 100644 --- a/lib/mapping/MapReaderH3M.h +++ b/lib/mapping/MapReaderH3M.h @@ -48,7 +48,7 @@ public: PlayerColor readPlayer(); PlayerColor readPlayer32(); - void readBitmaskBuildings(std::set & dest, std::optional faction); + std::set readBitmaskBuildings(std::optional faction); void readBitmaskFactions(std::set & dest, bool invert); void readBitmaskPlayers(std::set & dest, bool invert); void readBitmaskResources(std::set & dest, bool invert); diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 6946b11ab..8bddb478e 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1332,7 +1332,7 @@ void NewStructures::applyGs(CGameState *gs) for(const auto & id : bid) { assert(t->town->buildings.at(id) != nullptr); - t->builtBuildings.insert(id); + t->addBuilding(id); } t->updateAppearance(); t->built = built; @@ -1344,7 +1344,7 @@ void RazeStructures::applyGs(CGameState *gs) CGTownInstance *t = gs->getTown(tid); for(const auto & id : bid) { - t->builtBuildings.erase(id); + t->removeBuilding(id); t->updateAppearance(); } diff --git a/lib/rmg/modificators/TownPlacer.cpp b/lib/rmg/modificators/TownPlacer.cpp index 035536d29..0a3e412c4 100644 --- a/lib/rmg/modificators/TownPlacer.cpp +++ b/lib/rmg/modificators/TownPlacer.cpp @@ -79,8 +79,8 @@ void TownPlacer::placeTowns(ObjectManager & manager) CGTownInstance * town = dynamic_cast(townFactory->create(map.mapInstance->cb, nullptr)); town->tempOwner = player; - town->builtBuildings.insert(BuildingID::FORT); - town->builtBuildings.insert(BuildingID::DEFAULT); + town->addBuilding(BuildingID::FORT); + town->addBuilding(BuildingID::DEFAULT); for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town @@ -203,8 +203,8 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player town->tempOwner = player; if (hasFort) - town->builtBuildings.insert(BuildingID::FORT); - town->builtBuildings.insert(BuildingID::DEFAULT); + town->addBuilding(BuildingID::FORT); + town->addBuilding(BuildingID::DEFAULT); for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town town->possibleSpells.push_back(spellID);