diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 55d8a59b7..a4613db1b 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -9,6 +9,7 @@ */ #include "StdInc.h" +#include "../../lib/ArtifactUtils.h" #include "../../lib/UnlockGuard.h" #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" @@ -663,7 +664,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vectorheroManager->getHeroRole(hero) != HeroRole::MAIN || nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE)) { @@ -995,21 +996,21 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance for(auto p : h->artifactsWorn) { if(p.second.artifact) - allArtifacts.push_back(ArtifactLocation(h, p.first)); + allArtifacts.push_back(ArtifactLocation(h->id, p.first)); } } for(auto slot : h->artifactsInBackpack) - allArtifacts.push_back(ArtifactLocation(h, h->getArtPos(slot.artifact))); + allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact))); if(otherh) { for(auto p : otherh->artifactsWorn) { if(p.second.artifact) - allArtifacts.push_back(ArtifactLocation(otherh, p.first)); + allArtifacts.push_back(ArtifactLocation(otherh->id, p.first)); } for(auto slot : otherh->artifactsInBackpack) - allArtifacts.push_back(ArtifactLocation(otherh, otherh->getArtPos(slot.artifact))); + allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact))); } //we give stuff to one hero or another, depending on giveStuffToFirstHero @@ -1021,13 +1022,13 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance for(auto location : allArtifacts) { - if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST) + if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot)) continue; //don't reequip artifact we already wear if(location.slot == ArtifactPosition::MACH4) // don't attempt to move catapult continue; - auto s = location.getSlot(); + auto s = cb->getHero(location.artHolder)->getSlot(location.slot); if(!s || s->locked) //we can't move locks continue; auto artifact = s->artifact; @@ -1038,9 +1039,9 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance bool emptySlotFound = false; for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType())) { - ArtifactLocation destLocation(target, slot); - if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move + if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move { + ArtifactLocation destLocation(target->id, slot); cb->swapArtifacts(location, destLocation); //just put into empty slot emptySlotFound = true; changeMade = true; @@ -1054,11 +1055,11 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance auto otherSlot = target->getSlot(slot); if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one { - ArtifactLocation destLocation(target, slot); //if that artifact is better than what we have, pick it - if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move + if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move { - cb->swapArtifacts(location, ArtifactLocation(target, target->getArtPos(otherSlot->artifact))); + ArtifactLocation destLocation(target->id, slot); + cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact))); changeMade = true; break; } diff --git a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp index 386e71779..8a6edc8c7 100644 --- a/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/BuildAnalyzer.cpp @@ -318,7 +318,7 @@ bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const { for(auto tdi : developmentInfos) { - if(tdi.town->subID == alignment && tdi.town->hasBuilt(bid)) + if(tdi.town->getFaction() == alignment && tdi.town->hasBuilt(bid)) return true; } diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.cpp b/AI/Nullkiller/Behaviors/StartupBehavior.cpp index 84abb41fe..3a7f59f72 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.cpp +++ b/AI/Nullkiller/Behaviors/StartupBehavior.cpp @@ -71,7 +71,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown) for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects()) { - if((obj->ID == Obj::RESOURCE && obj->subID == GameResID(EGameResID::GOLD)) + if((obj->ID == Obj::RESOURCE && dynamic_cast(obj)->resourceID() == EGameResID::GOLD) || obj->ID == Obj::TREASURE_CHEST || obj->ID == Obj::CAMPFIRE || obj->ID == Obj::WATER_WHEEL) diff --git a/AI/Nullkiller/Engine/FuzzyHelper.cpp b/AI/Nullkiller/Engine/FuzzyHelper.cpp index e550c47ba..5252f89c3 100644 --- a/AI/Nullkiller/Engine/FuzzyHelper.cpp +++ b/AI/Nullkiller/Engine/FuzzyHelper.cpp @@ -24,7 +24,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) { //this one is not fuzzy anymore, just calculate weighted average - auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance); + auto objectInfo = bank->getObjectHandler()->getObjectInfo(bank->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); @@ -161,10 +161,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj) } case Obj::PYRAMID: { - if(obj->subID == 0) - return estimateBankDanger(dynamic_cast(obj)); - else - return 0; + return estimateBankDanger(dynamic_cast(obj)); } default: return 0; diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 98ce1a86f..be3d87845 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -122,7 +122,7 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer { //Fixme: unused variable hero - auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance); + auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); auto resources = bankInfo->getPossibleResourcesReward(); TResources result = TResources(); @@ -139,7 +139,7 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero) { - auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance); + auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); auto creatures = bankInfo->getPossibleCreaturesReward(); uint64_t result = 0; @@ -467,14 +467,20 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons switch(target->ID) { case Obj::MINE: - return target->subID == GameResID(EGameResID::GOLD) + { + auto mine = dynamic_cast(target); + return mine->producedResource == EGameResID::GOLD ? 0.5f - : 0.4f * getTotalResourceRequirementStrength(target->subID) + 0.1f * getResourceRequirementStrength(target->subID); + : 0.4f * getTotalResourceRequirementStrength(mine->producedResource) + 0.1f * getResourceRequirementStrength(mine->producedResource); + } case Obj::RESOURCE: - return target->subID == GameResID(EGameResID::GOLD) + { + auto resource = dynamic_cast(target); + return resource->resourceID() == EGameResID::GOLD ? 0 - : 0.2f * getTotalResourceRequirementStrength(target->subID) + 0.4f * getResourceRequirementStrength(target->subID); + : 0.2f * getTotalResourceRequirementStrength(resource->resourceID()) + 0.4f * getResourceRequirementStrength(resource->resourceID()); + } case Obj::CREATURE_BANK: { @@ -626,12 +632,14 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG const int dailyIncomeMultiplier = 5; const float enemyArmyEliminationGoldRewardRatio = 0.2f; const int32_t heroEliminationBonus = GameConstants::HERO_GOLD_COST / 2; - auto isGold = target->subID == GameResID(EGameResID::GOLD); // TODO: other resorces could be sold but need to evaluate market power switch(target->ID) { case Obj::RESOURCE: - return isGold ? 600 : 100; + { + auto * res = dynamic_cast(target); + return res->resourceID() == GameResID::GOLD ? 600 : 100; + } case Obj::TREASURE_CHEST: return 1500; case Obj::WATER_WHEEL: @@ -640,7 +648,10 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG return dailyIncomeMultiplier * estimateTownIncome(ai->cb.get(), target, hero); case Obj::MINE: case Obj::ABANDONED_MINE: - return dailyIncomeMultiplier * (isGold ? 1000 : 75); + { + auto * mine = dynamic_cast(target); + return dailyIncomeMultiplier * (mine->producedResource == GameResID::GOLD ? 1000 : 75); + } case Obj::MYSTICAL_GARDEN: case Obj::WINDMILL: return 100; @@ -1005,7 +1016,7 @@ public: uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const { - if(ai->buildAnalyzer->hasAnyBuilding(town->subID, bi.id)) + if(ai->buildAnalyzer->hasAnyBuilding(town->getFaction(), bi.id)) return 0; auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID); diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 64cf46d28..2b0fe415e 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -26,7 +26,6 @@ using crint3 = const int3 &; using crstring = const std::string &; using dwellingContent = std::pair>; -const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1; const int ACTUAL_RESOURCE_COUNT = 7; const int ALLOWED_ROAMING_HEROES = 8; diff --git a/AI/VCAI/FuzzyEngines.cpp b/AI/VCAI/FuzzyEngines.cpp index 91ba40139..f4702f3a7 100644 --- a/AI/VCAI/FuzzyEngines.cpp +++ b/AI/VCAI/FuzzyEngines.cpp @@ -406,7 +406,7 @@ float VisitObjEngine::evaluate(Goals::VisitObj & goal) else { MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0); - logGlobal->error("AI met object type it doesn't know - ID: " + std::to_string(obj->ID) + ", subID: " + std::to_string(obj->subID) + " - adding to database with value " + std::to_string(objValue)); + logGlobal->error("AI met object type it doesn't know - ID: %d, subID: %d - adding to database with value %d ", obj->ID, obj->subID, objValue); } setSharedFuzzyVariables(goal); diff --git a/AI/VCAI/FuzzyHelper.cpp b/AI/VCAI/FuzzyHelper.cpp index c125e2315..a47cbafa0 100644 --- a/AI/VCAI/FuzzyHelper.cpp +++ b/AI/VCAI/FuzzyHelper.cpp @@ -66,7 +66,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank) { //this one is not fuzzy anymore, just calculate weighted average - auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance); + auto objectInfo = bank->getObjectHandler()->getObjectInfo(bank->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); @@ -324,15 +324,8 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai) case Obj::DRAGON_UTOPIA: case Obj::SHIPWRECK: //shipwreck case Obj::DERELICT_SHIP: //derelict ship - // case Obj::PYRAMID: - return estimateBankDanger(dynamic_cast(obj)); case Obj::PYRAMID: - { - if(obj->subID == 0) - return estimateBankDanger(dynamic_cast(obj)); - else - return 0; - } + return estimateBankDanger(dynamic_cast(obj)); default: return 0; } diff --git a/AI/VCAI/Goals/CollectRes.cpp b/AI/VCAI/Goals/CollectRes.cpp index 27e168fa6..d4513dbeb 100644 --- a/AI/VCAI/Goals/CollectRes.cpp +++ b/AI/VCAI/Goals/CollectRes.cpp @@ -43,10 +43,10 @@ TGoalVec CollectRes::getAllPossibleSubgoals() return resID == GameResID(EGameResID::GOLD); break; case Obj::RESOURCE: - return obj->subID == resID; + return dynamic_cast(obj)->resourceID() == GameResID(resID); break; case Obj::MINE: - return (obj->subID == resID && + return (dynamic_cast(obj)->producedResource == GameResID(resID) && (cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines break; case Obj::CAMPFIRE: diff --git a/AI/VCAI/Goals/GatherTroops.cpp b/AI/VCAI/Goals/GatherTroops.cpp index 4af9e7434..ed4e1a6c0 100644 --- a/AI/VCAI/Goals/GatherTroops.cpp +++ b/AI/VCAI/Goals/GatherTroops.cpp @@ -88,7 +88,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals() } auto creature = VLC->creatures()->getByIndex(objid); - if(t->subID == creature->getFaction()) //TODO: how to force AI to build unupgraded creatures? :O + if(t->getFaction() == creature->getFaction()) //TODO: how to force AI to build unupgraded creatures? :O { auto tryFindCreature = [&]() -> std::optional> { diff --git a/AI/VCAI/Pathfinding/PathfindingManager.cpp b/AI/VCAI/Pathfinding/PathfindingManager.cpp index 3b56951ed..c983150bc 100644 --- a/AI/VCAI/Pathfinding/PathfindingManager.cpp +++ b/AI/VCAI/Pathfinding/PathfindingManager.cpp @@ -190,7 +190,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet if(isBlockedBorderGate(firstTileToGet)) { //FIXME: this way we'll not visit gate and activate quest :? - return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->subID)); + return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->getObjTypeIndex())); } auto topObj = cb->getTopObj(firstTileToGet); diff --git a/AI/VCAI/ResourceManager.cpp b/AI/VCAI/ResourceManager.cpp index 8a97e430f..b69c4e9bc 100644 --- a/AI/VCAI/ResourceManager.cpp +++ b/AI/VCAI/ResourceManager.cpp @@ -59,19 +59,7 @@ TResources ResourceManager::estimateIncome() const if (obj->ID == Obj::MINE) { auto mine = dynamic_cast(obj); - switch (mine->producedResource.toEnum()) - { - case EGameResID::WOOD: - case EGameResID::ORE: - ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION; - break; - case EGameResID::GOLD: - ret[EGameResID::GOLD] += GOLD_MINE_PRODUCTION; - break; - default: - ret[obj->subID] += RESOURCE_MINE_PRODUCTION; - break; - } + ret += mine->dailyIncome(); } } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 1099fd3cb..19313633b 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -14,6 +14,7 @@ #include "BuildingManager.h" #include "Goals/Goals.h" +#include "../../lib/ArtifactUtils.h" #include "../../lib/UnlockGuard.h" #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" @@ -1166,21 +1167,21 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot for(auto p : h->artifactsWorn) { if(p.second.artifact) - allArtifacts.push_back(ArtifactLocation(h, p.first)); + allArtifacts.push_back(ArtifactLocation(h->id, p.first)); } } for(auto slot : h->artifactsInBackpack) - allArtifacts.push_back(ArtifactLocation(h, h->getArtPos(slot.artifact))); + allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.artifact))); if(otherh) { for(auto p : otherh->artifactsWorn) { if(p.second.artifact) - allArtifacts.push_back(ArtifactLocation(otherh, p.first)); + allArtifacts.push_back(ArtifactLocation(otherh->id, p.first)); } for(auto slot : otherh->artifactsInBackpack) - allArtifacts.push_back(ArtifactLocation(otherh, otherh->getArtPos(slot.artifact))); + allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.artifact))); } //we give stuff to one hero or another, depending on giveStuffToFirstHero @@ -1195,10 +1196,10 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot if(location.slot == ArtifactPosition::MACH4 || location.slot == ArtifactPosition::SPELLBOOK) continue; // don't attempt to move catapult and spellbook - if(location.relatedObj() == target && location.slot < ArtifactPosition::AFTER_LAST) + if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot)) continue; //don't reequip artifact we already wear - auto s = location.getSlot(); + auto s = cb->getHero(location.artHolder)->getSlot(location.slot); if(!s || s->locked) //we can't move locks continue; auto artifact = s->artifact; @@ -1209,9 +1210,9 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot bool emptySlotFound = false; for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType())) { - ArtifactLocation destLocation(target, slot); - if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move + if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move { + ArtifactLocation destLocation(target->id, slot); cb->swapArtifacts(location, destLocation); //just put into empty slot emptySlotFound = true; changeMade = true; @@ -1225,11 +1226,11 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot auto otherSlot = target->getSlot(slot); if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one { - ArtifactLocation destLocation(target, slot); //if that artifact is better than what we have, pick it - if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move + if(compareArtifacts(artifact, otherSlot->artifact) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move { - cb->swapArtifacts(location, ArtifactLocation(target, target->getArtPos(otherSlot->artifact))); + ArtifactLocation destLocation(target->id, slot); + cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->artifact))); changeMade = true; break; } @@ -1759,11 +1760,11 @@ void VCAI::addVisitableObj(const CGObjectInstance * obj) CGTeleport::addToChannel(knownTeleportChannels, teleportObj); } -const CGObjectInstance * VCAI::lookForArt(int aid) const +const CGObjectInstance * VCAI::lookForArt(ArtifactID aid) const { for(const CGObjectInstance * obj : ai->visitableObjs) { - if(obj->ID == Obj::ARTIFACT && obj->subID == aid) + if(obj->ID == Obj::ARTIFACT && dynamic_cast(obj)->getArtifact() == aid) return obj; } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 8231003d9..5f327e5c3 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -251,7 +251,7 @@ public: void retrieveVisitableObjs(); virtual std::vector getFlaggedObjects() const; - const CGObjectInstance * lookForArt(int aid) const; + const CGObjectInstance * lookForArt(ArtifactID aid) const; bool isAccessible(const int3 & pos) const; HeroPtr getHeroWithGrail() const; diff --git a/CCallback.cpp b/CCallback.cpp index 753c443f0..78c8fe884 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -265,7 +265,7 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn assert(townOrTavern); assert(hero); - HireHero pack(HeroTypeID(hero->subID), townOrTavern->id); + HireHero pack(hero->getHeroType(), townOrTavern->id); pack.player = *player; sendRequest(&pack); } diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 760010a25..409b0f30e 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -293,7 +293,7 @@ void CPlayerInterface::yourTurn(QueryID queryID) std::string msg = CGI->generaltexth->allTexts[13]; boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name); std::vector> cmp; - cmp.push_back(std::make_shared(CComponent::flag, playerID.getNum(), 0)); + cmp.push_back(std::make_shared(ComponentType::FLAG, playerID)); showInfoDialog(msg, cmp); } else @@ -326,7 +326,7 @@ void CPlayerInterface::acceptTurn(QueryID queryID) auto playerColor = *cb->getPlayerID(); std::vector components; - components.emplace_back(Component::EComponentType::FLAG, playerColor.getNum(), 0, 0); + components.emplace_back(ComponentType::FLAG, playerColor); MetaString text; const auto & optDaysWithoutCastle = cb->getPlayerState(playerColor)->daysWithoutCastle; @@ -1228,7 +1228,7 @@ void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, con text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated()); // Picture of assembled artifact at bottom. - auto sc = std::make_shared(CComponent::artifact, assembledArtifact->getIndex(), 0); + auto sc = std::make_shared(ComponentType::ARTIFACT, assembledArtifact->getId()); scs.push_back(sc); } else @@ -1441,7 +1441,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start) std::string msg = CGI->generaltexth->translate("vcmi.adventureMap.playerAttacked"); boost::replace_first(msg, "%s", cb->getStartInfo()->playerInfos.find(playerID)->second.name); std::vector> cmp; - cmp.push_back(std::make_shared(CComponent::flag, playerID.getNum(), 0)); + cmp.push_back(std::make_shared(ComponentType::FLAG, playerID)); makingTurn = true; //workaround for stiff showInfoDialog implementation showInfoDialog(msg, cmp); makingTurn = false; @@ -1753,8 +1753,7 @@ void CPlayerInterface::requestReturningToMainMenu(bool won) void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) { - auto hero = std::visit(HeroObjectRetriever(), al.artHolder); - if(hero) + if(auto hero = cb->getHero(al.artHolder)) { auto art = hero->getArt(al.slot); if(art == nullptr) @@ -1770,15 +1769,13 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) void CPlayerInterface::artifactPut(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto hero = std::visit(HeroObjectRetriever(), al.artHolder); - adventureInt->onHeroChanged(hero); + adventureInt->onHeroChanged(cb->getHero(al.artHolder)); } void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto hero = std::visit(HeroObjectRetriever(), al.artHolder); - adventureInt->onHeroChanged(hero); + adventureInt->onHeroChanged(cb->getHero(al.artHolder)); for(auto artWin : GH.windows().findWindows()) artWin->artifactRemoved(al); @@ -1789,8 +1786,7 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto hero = std::visit(HeroObjectRetriever(), dst.artHolder); - adventureInt->onHeroChanged(hero); + adventureInt->onHeroChanged(cb->getHero(dst.artHolder)); bool redraw = true; // If a bulk transfer has arrived, then redrawing only the last art movement. @@ -1815,8 +1811,7 @@ void CPlayerInterface::bulkArtMovementStart(size_t numOfArts) void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto hero = std::visit(HeroObjectRetriever(), al.artHolder); - adventureInt->onHeroChanged(hero); + adventureInt->onHeroChanged(cb->getHero(al.artHolder)); for(auto artWin : GH.windows().findWindows()) artWin->artifactAssembled(al); @@ -1825,8 +1820,7 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; - auto hero = std::visit(HeroObjectRetriever(), al.artHolder); - adventureInt->onHeroChanged(hero); + adventureInt->onHeroChanged(cb->getHero(al.artHolder)); for(auto artWin : GH.windows().findWindows()) artWin->artifactDisassembled(al); diff --git a/client/HeroMovementController.cpp b/client/HeroMovementController.cpp index 681b8ea4c..81a625d12 100644 --- a/client/HeroMovementController.cpp +++ b/client/HeroMovementController.cpp @@ -359,7 +359,7 @@ void HeroMovementController::moveOnce(const CGHeroInstance * h, const CGPath & p { stopMovementSound(); logGlobal->trace("Requesting hero teleportation to %s", nextNode.coord.toString()); - LOCPLINT->cb->moveHero(h, nextCoord, false); + LOCPLINT->cb->moveHero(h, h->pos, false); return; } else diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 26183016b..0a7861a3f 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -267,14 +267,14 @@ void ApplyClientNetPackVisitor::visitBulkSmartRebalanceStacks(BulkSmartRebalance void ApplyClientNetPackVisitor::visitPutArtifact(PutArtifact & pack) { - callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactPut, pack.al); + callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactPut, pack.al); if(pack.askAssemble) - callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::askToAssembleArtifact, pack.al); + callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::askToAssembleArtifact, pack.al); } void ApplyClientNetPackVisitor::visitEraseArtifact(EraseArtifact & pack) { - callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactRemoved, pack.al); + callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactRemoved, pack.al); } void ApplyClientNetPackVisitor::visitMoveArtifact(MoveArtifact & pack) @@ -286,9 +286,9 @@ void ApplyClientNetPackVisitor::visitMoveArtifact(MoveArtifact & pack) callInterfaceIfPresent(cl, player, &IGameEventsReceiver::askToAssembleArtifact, pack.dst); }; - moveArtifact(pack.src.owningPlayer()); - if(pack.src.owningPlayer() != pack.dst.owningPlayer()) - moveArtifact(pack.dst.owningPlayer()); + moveArtifact(cl.getOwner(pack.src.artHolder)); + if(cl.getOwner(pack.src.artHolder) != cl.getOwner(pack.dst.artHolder)) + moveArtifact(cl.getOwner(pack.dst.artHolder)); cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings } @@ -306,8 +306,8 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack) } }; - auto srcOwner = std::get>(pack.srcArtHolder)->tempOwner; - auto dstOwner = std::get>(pack.dstArtHolder)->tempOwner; + auto srcOwner = cl.getOwner(pack.srcArtHolder); + auto dstOwner = cl.getOwner(pack.dstArtHolder); // Begin a session of bulk movement of arts. It is not necessary but useful for the client optimization. callInterfaceIfPresent(cl, srcOwner, &IGameEventsReceiver::bulkArtMovementStart, pack.artsPack0.size() + pack.artsPack1.size()); @@ -321,14 +321,14 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack) void ApplyClientNetPackVisitor::visitAssembledArtifact(AssembledArtifact & pack) { - callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactAssembled, pack.al); + callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactAssembled, pack.al); cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings } void ApplyClientNetPackVisitor::visitDisassembledArtifact(DisassembledArtifact & pack) { - callInterfaceIfPresent(cl, pack.al.owningPlayer(), &IGameEventsReceiver::artifactDisassembled, pack.al); + callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactDisassembled, pack.al); cl.invalidatePaths(); // hero might have equipped/unequipped Angel Wings } @@ -604,7 +604,7 @@ void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack) void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack) { CGHeroInstance *h = gs.map->heroesOnMap.back(); - if(h->subID != pack.hid) + if(h->getHeroType() != pack.hid) { logNetwork->error("Something wrong with hero recruited!"); } diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index 360dd4946..c583f461f 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -376,47 +376,51 @@ void CInfoBar::pushComponents(const std::vector & components, std::st std::array, int>, 10> reward_map; for(const auto & c : components) { - switch(c.id) + switch(c.type) { - case Component::EComponentType::PRIM_SKILL: - case Component::EComponentType::EXPERIENCE: + case ComponentType::PRIM_SKILL: + case ComponentType::EXPERIENCE: + case ComponentType::LEVEL: + case ComponentType::MANA: reward_map.at(0).first.push_back(c); reward_map.at(0).second = 8; //At most 8, cannot be more break; - case Component::EComponentType::SEC_SKILL: + case ComponentType::SEC_SKILL: reward_map.at(1).first.push_back(c); reward_map.at(1).second = 4; //At most 4 break; - case Component::EComponentType::SPELL: + case ComponentType::SPELL: reward_map.at(2).first.push_back(c); reward_map.at(2).second = 4; //At most 4 break; - case Component::EComponentType::ARTIFACT: + case ComponentType::ARTIFACT: + case ComponentType::SPELL_SCROLL: reward_map.at(3).first.push_back(c); reward_map.at(3).second = 4; //At most 4, too long names break; - case Component::EComponentType::CREATURE: + case ComponentType::CREATURE: reward_map.at(4).first.push_back(c); reward_map.at(4).second = 4; //At most 4, too long names break; - case Component::EComponentType::RESOURCE: + case ComponentType::RESOURCE: + case ComponentType::RESOURCE_PER_DAY: reward_map.at(5).first.push_back(c); reward_map.at(5).second = 7; //At most 7 break; - case Component::EComponentType::MORALE: - case Component::EComponentType::LUCK: + case ComponentType::MORALE: + case ComponentType::LUCK: reward_map.at(6).first.push_back(c); reward_map.at(6).second = 2; //At most 2 - 1 for morale + 1 for luck break; - case Component::EComponentType::BUILDING: + case ComponentType::BUILDING: reward_map.at(7).first.push_back(c); reward_map.at(7).second = 1; //At most 1 - only large icons available AFAIK break; - case Component::EComponentType::HERO_PORTRAIT: + case ComponentType::HERO_PORTRAIT: reward_map.at(8).first.push_back(c); reward_map.at(8).second = 1; //I do not think than we even can get more than 1 hero break; - case Component::EComponentType::FLAG: + case ComponentType::FLAG: reward_map.at(9).first.push_back(c); reward_map.at(9).second = 1; //I do not think than we even can get more than 1 player in notification break; diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 7e476340c..f3adbe037 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -905,7 +905,7 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS const spells::Caster * BattleActionsController::getCurrentSpellcaster() const { if (heroSpellToCast) - return owner.getActiveHero(); + return owner.currentHero(); else return owner.stacksController->getActiveStack(); } diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 0bb6e0bd2..0c6e7e809 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -27,6 +27,7 @@ #include "../../CCallback.h" #include "../../lib/battle/CObstacleInstance.h" #include "../../lib/ObstacleHandler.h" +#include "../../lib/serializer/JsonDeserializer.h" BattleObstacleController::BattleObstacleController(BattleInterface & owner): owner(owner), @@ -77,7 +78,14 @@ void BattleObstacleController::obstacleRemoved(const std::vectorpreload(); auto first = animation->getImage(0, 0); @@ -88,7 +96,7 @@ void BattleObstacleController::obstacleRemoved(const std::vector if we know how to blit obstacle, let's blit the effect in the same place Point whereTo = getObstaclePosition(first, obstacle); //AFAIK, in H3 there is no sound of obstacle removal - owner.stacksController->addNewAnim(new EffectAnimation(owner, AnimationPath::fromJson(obstacle["appearAnimation"]), whereTo, obstacle["position"].Integer(), 0, true)); + owner.stacksController->addNewAnim(new EffectAnimation(owner, animationPath, whereTo, obstacle["position"].Integer(), 0, true)); obstacleAnimations.erase(oi.id); //so when multiple obstacles are removed, they show up one after another diff --git a/client/lobby/CBonusSelection.cpp b/client/lobby/CBonusSelection.cpp index 2566bf075..dabc75028 100644 --- a/client/lobby/CBonusSelection.cpp +++ b/client/lobby/CBonusSelection.cpp @@ -161,7 +161,7 @@ void CBonusSelection::createBonusesIcons() break; case CampaignBonusType::BUILDING: { - int faction = -1; + FactionID faction; for(auto & elem : CSH->si->playerInfos) { if(elem.second.isControlledByHuman()) diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index 4d7f8987e..51feeaff3 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -209,7 +209,6 @@ void InfoCard::changeSelection() return; labelSaveDate->setText(mapInfo->date); - labelMapSize->setText(std::to_string(mapInfo->mapHeader->width) + "x" + std::to_string(mapInfo->mapHeader->height)); mapName->setText(mapInfo->getNameTranslated()); mapDescription->setText(mapInfo->getDescriptionTranslated()); @@ -220,8 +219,11 @@ void InfoCard::changeSelection() if(SEL->screenType == ESelectionScreen::campaignList) return; - iconsMapSizes->setFrame(mapInfo->getMapSizeIconId()); const CMapHeader * header = mapInfo->mapHeader.get(); + + labelMapSize->setText(std::to_string(header->width) + "x" + std::to_string(header->height)); + iconsMapSizes->setFrame(mapInfo->getMapSizeIconId()); + iconsVictoryCondition->setFrame(header->victoryIconIndex); labelVictoryConditionText->setText(header->victoryMessage.toString()); iconsLossCondition->setFrame(header->defeatIconIndex); diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index 72e3817f3..e62bf26c4 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -571,7 +571,7 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow() for(auto & elem : town->creatures) { if(!elem.empty()) - components.push_back(std::make_shared(CComponent::creature, elem.front(), 0, CComponent::tiny)); + components.push_back(std::make_shared(ComponentType::CREATURE, elem.front(), 0, CComponent::tiny)); } boxAssociatedCreatures = std::make_shared(components, Rect(10, 140, pos.w - 20, 140)); } diff --git a/client/mainmenu/CHighScoreScreen.cpp b/client/mainmenu/CHighScoreScreen.cpp index de76d62b5..b7e7508a2 100644 --- a/client/mainmenu/CHighScoreScreen.cpp +++ b/client/mainmenu/CHighScoreScreen.cpp @@ -252,7 +252,7 @@ int CHighScoreInputScreen::addEntry(std::string text) { auto sortFunctor = [](const JsonNode & left, const JsonNode & right) { if(left["points"].Integer() == right["points"].Integer()) - return left["posFlag"].Integer() > right["posFlag"].Integer(); + return left["posFlag"].Bool() > right["posFlag"].Bool(); return left["points"].Integer() > right["points"].Integer(); }; @@ -325,7 +325,6 @@ void CHighScoreInputScreen::deactivate() { CCS->videoh->close(); CCS->soundh->stopSound(videoSoundHandle); - CIntObject::deactivate(); } void CHighScoreInputScreen::clickPressed(const Point & cursorPosition) @@ -361,7 +360,7 @@ void CHighScoreInputScreen::keyPressed(EShortcut key) } CHighScoreInput::CHighScoreInput(std::string playerName, std::function readyCB) - : CWindowObject(0, ImagePath::builtin("HIGHNAME")), ready(readyCB) + : CWindowObject(NEEDS_ANIMATED_BACKGROUND, ImagePath::builtin("HIGHNAME")), ready(readyCB) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; diff --git a/client/mapView/MapRendererContext.cpp b/client/mapView/MapRendererContext.cpp index d3c896029..07b5575eb 100644 --- a/client/mapView/MapRendererContext.cpp +++ b/client/mapView/MapRendererContext.cpp @@ -427,11 +427,7 @@ size_t MapRendererWorldViewContext::overlayImageIndex(const int3 & coordinates) if(!object->visitableAt(coordinates.x, coordinates.y)) continue; - ObjectPosInfo info; - info.pos = coordinates; - info.id = object->ID; - info.subId = object->subID; - info.owner = object->tempOwner; + ObjectPosInfo info(object); size_t iconIndex = selectOverlayImageForObject(info); diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 8077c3438..662f41ccc 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -204,11 +204,19 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const CAnimation::CAnimation(const AnimationPath & Name): name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")), - preloaded(false), - defFile() + preloaded(false) { if(CResourceHandler::get()->existsResource(name)) - defFile = std::make_shared(name); + { + try + { + defFile = std::make_shared(name); + } + catch ( const std::runtime_error & e) + { + logAnim->error("Def file %s failed to load! Reason: %s", Name.getOriginalName(), e.what()); + } + } init(); diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index a3b72d320..c4440aaef 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -49,7 +49,7 @@ void CArtPlace::setInternals(const CArtifactInstance * artInst) if(settings["general"]["enableUiEnhancements"].Bool()) { imageIndex = spellID.num; - if(baseType != CComponent::spell) + if(component.type != ComponentType::SPELL_SCROLL) { image->setScale(Point(pos.w, 34)); image->setAnimationPath(AnimationPath::builtin("spellscr"), imageIndex); @@ -57,21 +57,20 @@ void CArtPlace::setInternals(const CArtifactInstance * artInst) } } // Add spell component info (used to provide a pic in r-click popup) - baseType = CComponent::spell; - type = spellID; + component.type = ComponentType::SPELL_SCROLL; + component.subType = spellID; } else { - if(settings["general"]["enableUiEnhancements"].Bool() && baseType != CComponent::artifact) + if(settings["general"]["enableUiEnhancements"].Bool() && component.type != ComponentType::ARTIFACT) { image->setScale(Point()); image->setAnimationPath(AnimationPath::builtin("artifact"), imageIndex); image->moveTo(Point(pos.x, pos.y)); } - baseType = CComponent::artifact; - type = artInst->getTypeId(); + component.type = ComponentType::ARTIFACT; + component.subType = artInst->getTypeId(); } - bonusValue = 0; image->enable(); text = artInst->getDescription(); } @@ -121,10 +120,11 @@ void CCommanderArtPlace::returnArtToHeroCallback() } else { - ArtifactLocation src(commanderOwner->commander.get(), artifactPos); - ArtifactLocation dst(commanderOwner, freeSlot); + ArtifactLocation src(commanderOwner->id, artifactPos); + src.creature = SlotID::COMMANDER_SLOT_PLACEHOLDER; + ArtifactLocation dst(commanderOwner->id, freeSlot); - if(ourArt->canBePutAt(dst, true)) + if(ourArt->canBePutAt(commanderOwner, freeSlot, true)) { LOCPLINT->cb->swapArtifacts(src, dst); setArtifact(nullptr); diff --git a/client/widgets/CArtifactsOfHeroAltar.cpp b/client/widgets/CArtifactsOfHeroAltar.cpp index 737b05f9c..5c33ee05c 100644 --- a/client/widgets/CArtifactsOfHeroAltar.cpp +++ b/client/widgets/CArtifactsOfHeroAltar.cpp @@ -72,7 +72,7 @@ void CArtifactsOfHeroAltar::pickUpArtifact(CHeroArtPlace & artPlace) if(ArtifactUtils::isSlotBackpack(pickedArtFromSlot)) pickedArtFromSlot = curHero->getSlotByInstance(art); assert(pickedArtFromSlot != ArtifactPosition::PRE_FIRST); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, pickedArtFromSlot), ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, pickedArtFromSlot), ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } } @@ -89,7 +89,7 @@ void CArtifactsOfHeroAltar::pickedArtMoveToAltar(const ArtifactPosition & slot) if(ArtifactUtils::isSlotBackpack(slot) || ArtifactUtils::isSlotEquipment(slot) || slot == ArtifactPosition::TRANSITION_POS) { assert(curHero->getSlot(slot)->getArt()); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, slot), ArtifactLocation(curHero, pickedArtFromSlot)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, slot), ArtifactLocation(curHero->id, pickedArtFromSlot)); pickedArtFromSlot = ArtifactPosition::PRE_FIRST; } } diff --git a/client/widgets/CArtifactsOfHeroBackpack.cpp b/client/widgets/CArtifactsOfHeroBackpack.cpp index f95d5cf65..6a487799c 100644 --- a/client/widgets/CArtifactsOfHeroBackpack.cpp +++ b/client/widgets/CArtifactsOfHeroBackpack.cpp @@ -80,8 +80,8 @@ void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, co void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace) { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), - ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot), + ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } void CArtifactsOfHeroBackpack::scrollBackpack(int offset) diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp index 769da2258..319c40e2f 100644 --- a/client/widgets/CArtifactsOfHeroBase.cpp +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -39,11 +39,11 @@ void CArtifactsOfHeroBase::putBackPickedArtifact() auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId()); if(slot == ArtifactPosition::PRE_FIRST) { - LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } else { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero, slot)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS), ArtifactLocation(curHero->id, slot)); } } if(putBackPickedArtCallback) @@ -178,7 +178,7 @@ void CArtifactsOfHeroBase::scrollBackpackForArtSet(int offset, const CArtifactSe void CArtifactsOfHeroBase::markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved) { for(auto artPlace : artWorn) - artPlace.second->selectSlot(art->artType->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved)); + artPlace.second->selectSlot(art->canBePutAt(curHero, artPlace.second->slot, assumeDestRemoved)); } void CArtifactsOfHeroBase::unmarkSlots() diff --git a/client/widgets/CArtifactsOfHeroKingdom.cpp b/client/widgets/CArtifactsOfHeroKingdom.cpp index 67e702b5f..14fd222f4 100644 --- a/client/widgets/CArtifactsOfHeroKingdom.cpp +++ b/client/widgets/CArtifactsOfHeroKingdom.cpp @@ -13,6 +13,7 @@ #include "Buttons.h" #include "../CPlayerInterface.h" +#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../CCallback.h" #include "../../lib/networkPacks/ArtifactLocation.h" @@ -56,7 +57,7 @@ void CArtifactsOfHeroKingdom::swapArtifacts(const ArtifactLocation & srcLoc, con void CArtifactsOfHeroKingdom::pickUpArtifact(CHeroArtPlace & artPlace) { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), - ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot), + ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } diff --git a/client/widgets/CArtifactsOfHeroMain.cpp b/client/widgets/CArtifactsOfHeroMain.cpp index 46b5329ca..b4ac981b0 100644 --- a/client/widgets/CArtifactsOfHeroMain.cpp +++ b/client/widgets/CArtifactsOfHeroMain.cpp @@ -11,6 +11,7 @@ #include "CArtifactsOfHeroMain.h" #include "../CPlayerInterface.h" +#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../CCallback.h" #include "../../lib/networkPacks/ArtifactLocation.h" @@ -36,6 +37,6 @@ void CArtifactsOfHeroMain::swapArtifacts(const ArtifactLocation & srcLoc, const void CArtifactsOfHeroMain::pickUpArtifact(CHeroArtPlace & artPlace) { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero, artPlace.slot), - ArtifactLocation(curHero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(curHero->id, artPlace.slot), + ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS)); } diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 12844ceb3..b3f725746 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -39,41 +39,35 @@ #include "../../lib/CArtHandler.h" #include "../../lib/CArtifactInstance.h" -CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font): - perDay(false) +CComponent::CComponent(ComponentType Type, ComponentSubType Subtype, std::optional Val, ESize imageSize, EFonts font) { init(Type, Subtype, Val, imageSize, font, ""); } -CComponent::CComponent(Etype Type, int Subtype, std::string Val, ESize imageSize, EFonts font): - perDay(false) +CComponent::CComponent(ComponentType Type, ComponentSubType Subtype, const std::string & Val, ESize imageSize, EFonts font) { - init(Type, Subtype, 0, imageSize, font, Val); + init(Type, Subtype, std::nullopt, imageSize, font, Val); } CComponent::CComponent(const Component & c, ESize imageSize, EFonts font) - : perDay(false) { - if(c.id == Component::EComponentType::RESOURCE && c.when==-1) - perDay = true; - - init((Etype)c.id, c.subtype, c.val, imageSize, font); + init(c.type, c.subType, c.value, imageSize, font, ""); } -void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts fnt, std::string ValText) +void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optional Val, ESize imageSize, EFonts fnt, const std::string & ValText) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); addUsedEvents(SHOW_POPUP); - compType = Type; - subtype = Subtype; - val = Val; - valText = ValText; + data.type = Type; + data.subType = Subtype; + data.value = Val; + + customSubtitle = ValText; size = imageSize; font = fnt; - assert(compType < typeInvalid); assert(size < sizeInvalid); setSurface(getFileName()[size], (int)getIndex()); @@ -94,7 +88,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts if (size < small) max = 30; - if(Type == Etype::resource && !valText.empty()) + if(Type == ComponentType::RESOURCE && !ValText.empty()) max = 80; std::vector textLines = CMessage::breakText(getSubtitle(), std::max(max, pos.w), font); @@ -113,153 +107,209 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts } } -std::vector CComponent::getFileName() +std::vector CComponent::getFileName() const { - static const std::string primSkillsArr [] = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"}; - static const std::string secSkillsArr [] = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"}; - static const std::string resourceArr [] = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"}; - static const std::string creatureArr [] = {"CPRSMALL", "CPRSMALL", "CPRSMALL", "TWCRPORT"}; - static const std::string artifactArr[] = {"Artifact", "Artifact", "Artifact", "Artifact"}; - static const std::string spellsArr [] = {"SpellInt", "SpellInt", "SpellInt", "SPELLSCR"}; - static const std::string moraleArr [] = {"IMRL22", "IMRL30", "IMRL42", "imrl82"}; - static const std::string luckArr [] = {"ILCK22", "ILCK30", "ILCK42", "ilck82"}; - static const std::string heroArr [] = {"PortraitsSmall", "PortraitsSmall", "PortraitsSmall", "PortraitsLarge"}; - static const std::string flagArr [] = {"CREST58", "CREST58", "CREST58", "CREST58"}; + static const std::array primSkillsArr = {"PSKIL32", "PSKIL32", "PSKIL42", "PSKILL"}; + static const std::array secSkillsArr = {"SECSK32", "SECSK32", "SECSKILL", "SECSK82"}; + static const std::array resourceArr = {"SMALRES", "RESOURCE", "RESOURCE", "RESOUR82"}; + static const std::array creatureArr = {"CPRSMALL", "CPRSMALL", "CPRSMALL", "TWCRPORT"}; + static const std::array artifactArr = {"Artifact", "Artifact", "Artifact", "Artifact"}; + static const std::array spellsArr = {"SpellInt", "SpellInt", "SpellInt", "SPELLSCR"}; + static const std::array moraleArr = {"IMRL22", "IMRL30", "IMRL42", "imrl82"}; + static const std::array luckArr = {"ILCK22", "ILCK30", "ILCK42", "ilck82"}; + static const std::array heroArr = {"PortraitsSmall", "PortraitsSmall", "PortraitsSmall", "PortraitsLarge"}; + static const std::array flagArr = {"CREST58", "CREST58", "CREST58", "CREST58"}; - auto gen = [](const std::string * arr) -> std::vector + auto gen = [](const std::array & arr) -> std::vector { return { AnimationPath::builtin(arr[0]), AnimationPath::builtin(arr[1]), AnimationPath::builtin(arr[2]), AnimationPath::builtin(arr[3]) }; }; - switch(compType) + switch(data.type) { - case primskill: return gen(primSkillsArr); - case secskill: return gen(secSkillsArr); - case resource: return gen(resourceArr); - case creature: return gen(creatureArr); - case artifact: return gen(artifactArr); - case experience: return gen(primSkillsArr); - case spell: return gen(spellsArr); - case morale: return gen(moraleArr); - case luck: return gen(luckArr); - case building: return std::vector(4, (*CGI->townh)[subtype]->town->clientInfo.buildingsIcons); - case hero: return gen(heroArr); - case flag: return gen(flagArr); + case ComponentType::PRIM_SKILL: + case ComponentType::EXPERIENCE: + case ComponentType::MANA: + case ComponentType::LEVEL: + return gen(primSkillsArr); + case ComponentType::SEC_SKILL: + return gen(secSkillsArr); + case ComponentType::RESOURCE: + case ComponentType::RESOURCE_PER_DAY: + return gen(resourceArr); + case ComponentType::CREATURE: + return gen(creatureArr); + case ComponentType::ARTIFACT: + return gen(artifactArr); + case ComponentType::SPELL_SCROLL: + case ComponentType::SPELL: + return gen(spellsArr); + case ComponentType::MORALE: + return gen(moraleArr); + case ComponentType::LUCK: + return gen(luckArr); + case ComponentType::BUILDING: + return std::vector(4, (*CGI->townh)[data.subType.as().getFaction()]->town->clientInfo.buildingsIcons); + case ComponentType::HERO_PORTRAIT: + return gen(heroArr); + case ComponentType::FLAG: + return gen(flagArr); + default: + assert(0); + return {}; } - assert(0); - return {}; } -size_t CComponent::getIndex() +size_t CComponent::getIndex() const { - switch(compType) + switch(data.type) { - case primskill: return subtype; - case secskill: return subtype*3 + 3 + val - 1; - case resource: return subtype; - case creature: return CGI->creatures()->getByIndex(subtype)->getIconIndex(); - case artifact: return CGI->artifacts()->getByIndex(subtype)->getIconIndex(); - case experience: return 4; - case spell: return (size < large) ? subtype + 1 : subtype; - case morale: return val+3; - case luck: return val+3; - case building: return val; - case hero: return CGI->heroTypes()->getByIndex(subtype)->getIconIndex(); - case flag: return subtype; + case ComponentType::PRIM_SKILL: + return data.subType.getNum(); + case ComponentType::EXPERIENCE: + case ComponentType::LEVEL: + return 4; // for whatever reason, in H3 experience icon is located in primary skills icons + case ComponentType::MANA: + return 5; // for whatever reason, in H3 mana points icon is located in primary skills icons + case ComponentType::SEC_SKILL: + return data.subType.getNum() * 3 + 3 + data.value.value_or(0) - 1; + case ComponentType::RESOURCE: + case ComponentType::RESOURCE_PER_DAY: + return data.subType.getNum(); + case ComponentType::CREATURE: + return CGI->creatures()->getById(data.subType.as())->getIconIndex(); + case ComponentType::ARTIFACT: + return CGI->artifacts()->getById(data.subType.as())->getIconIndex(); + case ComponentType::SPELL_SCROLL: + case ComponentType::SPELL: + return (size < large) ? data.subType.getNum() + 1 : data.subType.getNum(); + case ComponentType::MORALE: + return data.value.value_or(0) + 3; + case ComponentType::LUCK: + return data.value.value_or(0) + 3; + case ComponentType::BUILDING: + return data.subType.as().getBuilding(); + case ComponentType::HERO_PORTRAIT: + return CGI->heroTypes()->getById(data.subType.as())->getIconIndex(); + case ComponentType::FLAG: + return data.subType.getNum(); + default: + assert(0); + return 0; } - assert(0); - return 0; } -std::string CComponent::getDescription() +std::string CComponent::getDescription() const { - switch(compType) + switch(data.type) { - case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill - : CGI->generaltexth->allTexts[149]; //mana - case secskill: return CGI->skillh->getByIndex(subtype)->getDescriptionTranslated(val); - case resource: return CGI->generaltexth->allTexts[242]; - case creature: return ""; - case artifact: - { - auto artID = ArtifactID(subtype); - auto description = VLC->arth->objects[artID]->getDescriptionTranslated(); - if(artID == ArtifactID::SPELL_SCROLL) + case ComponentType::PRIM_SKILL: + return CGI->generaltexth->arraytxt[2+data.subType.getNum()]; + case ComponentType::EXPERIENCE: + case ComponentType::LEVEL: + return CGI->generaltexth->allTexts[241]; + case ComponentType::MANA: + return CGI->generaltexth->allTexts[149]; + case ComponentType::SEC_SKILL: + return CGI->skillh->getByIndex(data.subType.getNum())->getDescriptionTranslated(data.value.value_or(0)); + case ComponentType::RESOURCE: + case ComponentType::RESOURCE_PER_DAY: + return CGI->generaltexth->allTexts[242]; + case ComponentType::CREATURE: + return ""; + case ComponentType::ARTIFACT: + return VLC->artifacts()->getById(data.subType.as())->getDescriptionTranslated(); + case ComponentType::SPELL_SCROLL: { - ArtifactUtils::insertScrrollSpellName(description, SpellID(val)); + auto description = VLC->arth->objects[ArtifactID::SPELL_SCROLL]->getDescriptionTranslated(); + ArtifactUtils::insertScrrollSpellName(description, data.subType.as()); + return description; } - return description; - } - case experience: return CGI->generaltexth->allTexts[241]; - case spell: return (*CGI->spellh)[subtype]->getDescriptionTranslated(val); - case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)]; - case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)]; - case building: return (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]->getDescriptionTranslated(); - case hero: return ""; - case flag: return ""; - } - assert(0); - return ""; -} - -std::string CComponent::getSubtitle() -{ - if(!perDay) - return getSubtitleInternal(); - - std::string ret = CGI->generaltexth->allTexts[3]; - boost::replace_first(ret, "%d", getSubtitleInternal()); - return ret; -} - -std::string CComponent::getSubtitleInternal() -{ - //FIXME: some of these are horrible (e.g creature) - switch(compType) - { - case primskill: return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387])); - case secskill: return CGI->generaltexth->levels[val-1] + "\n" + CGI->skillh->getByIndex(subtype)->getNameTranslated(); - case resource: return valText.empty() ? std::to_string(val) : valText; - case creature: + case ComponentType::SPELL: + return VLC->spells()->getById(data.subType.as())->getDescriptionTranslated(data.value.value_or(0)); + case ComponentType::MORALE: + return CGI->generaltexth->heroscrn[ 4 - (data.value.value_or(0)>0) + (data.value.value_or(0)<0)]; + case ComponentType::LUCK: + return CGI->generaltexth->heroscrn[ 7 - (data.value.value_or(0)>0) + (data.value.value_or(0)<0)]; + case ComponentType::BUILDING: { - auto creature = CGI->creh->getByIndex(subtype); - if ( val ) - return std::to_string(val) + " " + (val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated()); + auto index = data.subType.as(); + return (*CGI->townh)[index.getFaction()]->town->buildings[index.getBuilding()]->getDescriptionTranslated(); + } + case ComponentType::HERO_PORTRAIT: + return ""; + case ComponentType::FLAG: + return ""; + default: + assert(0); + return ""; + } +} + +std::string CComponent::getSubtitle() const +{ + if (!customSubtitle.empty()) + return customSubtitle; + + switch(data.type) + { + case ComponentType::PRIM_SKILL: + if (data.value) + return boost::str(boost::format("%+d %s") % data.value.value_or(0) % CGI->generaltexth->primarySkillNames[data.subType.getNum()]); else - return val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated(); - } - case artifact: return CGI->artifacts()->getByIndex(subtype)->getNameTranslated(); - case experience: + return CGI->generaltexth->primarySkillNames[data.subType.getNum()]; + case ComponentType::EXPERIENCE: + return std::to_string(data.value.value_or(0)); + case ComponentType::LEVEL: { - if(subtype == 1) //+1 level - tree of knowledge - { - std::string level = CGI->generaltexth->allTexts[442]; - boost::replace_first(level, "1", std::to_string(val)); - return level; - } + std::string level = CGI->generaltexth->allTexts[442]; + boost::replace_first(level, "1", std::to_string(data.value.value_or(0))); + return level; + } + case ComponentType::MANA: + return boost::str(boost::format("%+d %s") % data.value.value_or(0) % CGI->generaltexth->allTexts[387]); + case ComponentType::SEC_SKILL: + return CGI->generaltexth->levels[data.value.value_or(0)-1] + "\n" + CGI->skillh->getById(data.subType.as())->getNameTranslated(); + case ComponentType::RESOURCE: + return std::to_string(data.value.value_or(0)); + case ComponentType::RESOURCE_PER_DAY: + return boost::str(boost::format(CGI->generaltexth->allTexts[3]) % data.value.value_or(0)); + case ComponentType::CREATURE: + { + auto creature = CGI->creh->getById(data.subType.as()); + if(data.value) + return std::to_string(*data.value) + " " + (*data.value > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated()); else - { - return std::to_string(val); //amount of experience OR level required for seer hut; - } + return creature->getNamePluralTranslated(); } - case spell: return CGI->spells()->getByIndex(subtype)->getNameTranslated(); - case morale: return ""; - case luck: return ""; - case building: - { - auto building = (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]; - if(!building) + case ComponentType::ARTIFACT: + return CGI->artifacts()->getById(data.subType.as())->getNameTranslated(); + case ComponentType::SPELL_SCROLL: + case ComponentType::SPELL: + return CGI->spells()->getById(data.subType.as())->getNameTranslated(); + case ComponentType::MORALE: + return ""; + case ComponentType::LUCK: + return ""; + case ComponentType::BUILDING: { - logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[subtype]->town->faction->getNameTranslated(), val); - return (boost::format("Missing building #%d") % val).str(); + auto index = data.subType.as(); + auto building = (*CGI->townh)[index.getFaction()]->town->buildings[index.getBuilding()]; + if(!building) + { + logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[index.getFaction()]->town->faction->getNameTranslated(), index.getBuilding().getNum()); + return (boost::format("Missing building #%d") % index.getBuilding().getNum()).str(); + } + return building->getNameTranslated(); } - return building->getNameTranslated(); - } - case hero: return ""; - case flag: return CGI->generaltexth->capColors[subtype]; + case ComponentType::HERO_PORTRAIT: + return ""; + case ComponentType::FLAG: + return CGI->generaltexth->capColors[data.subType.as().getNum()]; + default: + assert(0); + return ""; } - logGlobal->error("Invalid CComponent type: %d", (int)compType); - return ""; } void CComponent::setSurface(const AnimationPath & defName, int imgPos) @@ -299,7 +349,7 @@ CSelectableComponent::CSelectableComponent(const Component &c, std::function OnSelect): +CSelectableComponent::CSelectableComponent(ComponentType Type, ComponentSubType Sub, int Val, ESize imageSize, std::function OnSelect): CComponent(Type,Sub,Val, imageSize),onSelect(OnSelect) { setRedrawParent(true); diff --git a/client/widgets/CComponent.h b/client/widgets/CComponent.h index 3f94137ff..f41ea3264 100644 --- a/client/widgets/CComponent.h +++ b/client/widgets/CComponent.h @@ -12,6 +12,7 @@ #include "../gui/CIntObject.h" #include "../render/EFont.h" #include "../../lib/filesystem/ResourcePath.h" +#include "../../lib/networkPacks/Component.h" VCMI_LIB_NAMESPACE_BEGIN @@ -26,11 +27,6 @@ class CLabel; class CComponent : public virtual CIntObject { public: - enum Etype - { - primskill, secskill, resource, creature, artifact, experience, spell, morale, luck, building, hero, flag, typeInvalid - }; - //NOTE: not all types have exact these sizes or have less than 4 of them. In such cases closest one will be used enum ESize { @@ -44,29 +40,24 @@ public: private: std::vector> lines; - size_t getIndex(); - std::vector getFileName(); + size_t getIndex() const; + std::vector getFileName() const; void setSurface(const AnimationPath & defName, int imgPos); - std::string getSubtitleInternal(); - void init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font = FONT_SMALL, std::string ValText=""); + void init(ComponentType Type, ComponentSubType Subtype, std::optional Val, ESize imageSize, EFonts font, const std::string & ValText); public: std::shared_ptr image; - - Etype compType; //component type + Component data; + std::string customSubtitle; ESize size; //component size. EFonts font; //Font size of label - int subtype; //type-dependant subtype. See getSomething methods for details - int val; // value \ strength \ amount of component. See getSomething methods for details - std::string valText; // value instead of amount; currently only for resource - bool perDay; // add "per day" text to subtitle - std::string getDescription(); - std::string getSubtitle(); + std::string getDescription() const; + std::string getSubtitle() const; - CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large, EFonts font = FONT_SMALL); - CComponent(Etype Type, int Subtype, std::string Val, ESize imageSize=large, EFonts font = FONT_SMALL); + CComponent(ComponentType Type, ComponentSubType Subtype, std::optional Val = std::nullopt, ESize imageSize=large, EFonts font = FONT_SMALL); + CComponent(ComponentType Type, ComponentSubType Subtype, const std::string & Val, ESize imageSize=large, EFonts font = FONT_SMALL); CComponent(const Component &c, ESize imageSize=large, EFonts font = FONT_SMALL); void showPopupWindow(const Point & cursorPosition) override; //call-in @@ -86,7 +77,7 @@ public: void clickPressed(const Point & cursorPosition) override; //call-in void clickDouble(const Point & cursorPosition) override; //call-in - CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize=large, std::function OnSelect = nullptr); + CSelectableComponent(ComponentType Type, ComponentSubType Sub, int Val, ESize imageSize=large, std::function OnSelect = nullptr); CSelectableComponent(const Component & c, std::function OnSelect = nullptr); }; diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 3942da8a0..8c7a78697 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -196,16 +196,18 @@ bool CGarrisonSlot::highlightOrDropArtifact() artSelected = true; if (myStack) // try dropping the artifact only if the slot isn't empty { - ArtifactLocation src(srcHero, ArtifactPosition::TRANSITION_POS); - ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT); - if(pickedArtInst->canBePutAt(dst, true)) + ArtifactLocation src(srcHero->id, ArtifactPosition::TRANSITION_POS); + ArtifactLocation dst(getObj()->id, ArtifactPosition::CREATURE_SLOT); + dst.creature = getSlot(); + + if(pickedArtInst->canBePutAt(myStack, ArtifactPosition::CREATURE_SLOT, true)) { //equip clicked stack - if(dst.getArt()) + if(auto dstArt = myStack->getArt(ArtifactPosition::CREATURE_SLOT)) { //creature can wear only one active artifact //if we are placing a new one, the old one will be returned to the hero's backpack - LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, - ArtifactUtils::getArtBackpackPosition(srcHero, dst.getArt()->getTypeId()))); + LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero->id, + ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId()))); } LOCPLINT->cb->swapArtifacts(src, dst); } diff --git a/client/widgets/CWindowWithArtifacts.cpp b/client/widgets/CWindowWithArtifacts.cpp index 8c6d2b7c9..bf5ca043e 100644 --- a/client/widgets/CWindowWithArtifacts.cpp +++ b/client/widgets/CWindowWithArtifacts.cpp @@ -95,7 +95,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst if(artPlace.getArt()->getTypeId() == ArtifactID::CATAPULT) { // The Catapult must be equipped - std::vector> catapult(1, std::make_shared(CComponent::artifact, 3, 0)); + std::vector> catapult(1, std::make_shared(ComponentType::ARTIFACT, ArtifactID(ArtifactID::CATAPULT))); LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); return false; } @@ -122,8 +122,8 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst if(pickedArtInst) { - auto srcLoc = ArtifactLocation(heroPickedArt, ArtifactPosition::TRANSITION_POS); - auto dstLoc = ArtifactLocation(hero, artPlace.slot); + auto srcLoc = ArtifactLocation(heroPickedArt->id, ArtifactPosition::TRANSITION_POS); + auto dstLoc = ArtifactLocation(hero->id, artPlace.slot); if(ArtifactUtils::isSlotBackpack(artPlace.slot)) { @@ -141,7 +141,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst } } // Check if artifact transfer is possible - else if(pickedArtInst->canBePutAt(dstLoc, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID)) + else if(pickedArtInst->canBePutAt(hero, artPlace.slot, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID)) { isTransferAllowed = true; } @@ -270,7 +270,7 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const // we have a different artifact may look surprising... but it's valid. auto pickedArtInst = std::get(curState.value()); - assert(!pickedArtInst || destLoc.isHolder(std::get(curState.value()))); + assert(!pickedArtInst || destLoc.artHolder == std::get(curState.value())->id); auto artifactMovedBody = [this, withRedraw, &destLoc, &pickedArtInst](auto artSetWeak) -> void { @@ -316,7 +316,7 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const } // Make sure the status bar is updated so it does not display old text - if(destLoc.getHolderArtSet() == hero) + if(destLoc.artHolder == hero->id) { if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot)) artPlace->hover(true); diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 4d5eff1d5..0e8d8c353 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -95,16 +95,16 @@ void LRClickableAreaWTextComp::clickPressed(const Point & cursorPosition) LOCPLINT->showInfoDialog(text, comp); } -LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, int BaseType) - : LRClickableAreaWText(Pos), baseType(BaseType), bonusValue(-1) +LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, ComponentType BaseType) + : LRClickableAreaWText(Pos) { - type = -1; + component.type = BaseType; } std::shared_ptr LRClickableAreaWTextComp::createComponent() const { - if(baseType >= 0) - return std::make_shared(CComponent::Etype(baseType), type, bonusValue); + if(component.type != ComponentType::NONE) + return std::make_shared(component); else return std::shared_ptr(); } @@ -164,17 +164,11 @@ void CHeroArea::hover(bool on) void LRClickableAreaOpenTown::clickPressed(const Point & cursorPosition) { if(town) - { LOCPLINT->openTownWindow(town); - if ( type == 2 ) - LOCPLINT->castleInt->builds->buildingClicked(BuildingID::VILLAGE_HALL); - else if ( type == 3 && town->fortLevel() ) - LOCPLINT->castleInt->builds->buildingClicked(BuildingID::FORT); - } } LRClickableAreaOpenTown::LRClickableAreaOpenTown(const Rect & Pos, const CGTownInstance * Town) - : LRClickableAreaWTextComp(Pos, -1), town(Town) + : LRClickableAreaWTextComp(Pos), town(Town) { } @@ -542,20 +536,21 @@ void MoraleLuckBox::set(const AFactionMember * node) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); - const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:} + const std::array textId = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:} const int noneTxtId = 108; //Russian version uses same text for neutral morale\luck - const int neutralDescr[] = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat. - const int componentType[] = {CComponent::luck, CComponent::morale}; - const int hoverTextBase[] = {7, 4}; + const std::array neutralDescr = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat. + const std::array componentType = {ComponentType::LUCK, ComponentType::MORALE}; + const std::array hoverTextBase = {7, 4}; TConstBonusListPtr modifierList = std::make_shared(); - bonusValue = 0; + + component.value = 0; if(node) - bonusValue = morale ? node->moraleValAndBonusList(modifierList) : node->luckValAndBonusList(modifierList); + component.value = morale ? node->moraleValAndBonusList(modifierList) : node->luckValAndBonusList(modifierList); - int mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good + int mrlt = (component.value>0)-(component.value<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt]; - baseType = componentType[morale]; + component.type = componentType[morale]; text = CGI->generaltexth->arraytxt[textId[morale]]; boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]); @@ -563,19 +558,19 @@ void MoraleLuckBox::set(const AFactionMember * node) || node->getBonusBearer()->hasBonusOfType(BonusType::NON_LIVING))) { text += CGI->generaltexth->arraytxt[113]; //unaffected by morale - bonusValue = 0; + component.value = 0; } else if(morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_MORALE)) { auto noMorale = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_MORALE)); text += "\n" + noMorale->Description(); - bonusValue = 0; + component.value = 0; } else if (!morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK)) { auto noLuck = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_LUCK)); text += "\n" + noLuck->Description(); - bonusValue = 0; + component.value = 0; } else { @@ -595,7 +590,7 @@ void MoraleLuckBox::set(const AFactionMember * node) else imageName = morale ? "IMRL42" : "ILCK42"; - image = std::make_shared(AnimationPath::builtin(imageName), bonusValue + 3); + image = std::make_shared(AnimationPath::builtin(imageName), *component.value + 3); image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2));//center icon } @@ -603,7 +598,6 @@ MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small) : morale(Morale), small(Small) { - bonusValue = 0; pos = r + pos.topLeft(); defActions = 255-DISPOSE; } diff --git a/client/widgets/MiscWidgets.h b/client/widgets/MiscWidgets.h index 021193e9a..28e306614 100644 --- a/client/widgets/MiscWidgets.h +++ b/client/widgets/MiscWidgets.h @@ -10,6 +10,7 @@ #pragma once #include "../gui/CIntObject.h" +#include "../../lib/networkPacks/Component.h" VCMI_LIB_NAMESPACE_BEGIN @@ -202,13 +203,12 @@ private: class LRClickableAreaWTextComp: public LRClickableAreaWText { public: - int type; - int baseType; - int bonusValue; + Component component; + void clickPressed(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; - LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1); + LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), ComponentType baseType = ComponentType::NONE); std::shared_ptr createComponent() const; }; @@ -263,4 +263,4 @@ class SimpleLine : public CIntObject public: SimpleLine(Point pos1, Point pos2, ColorRGBA color); void showAll(Canvas & to) override; -}; \ No newline at end of file +}; diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index a0bc94cc8..5bce62993 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -157,7 +157,7 @@ void CBuildingRect::showPopupWindow(const Point & cursorPosition) if (bid < BuildingID::DWELL_FIRST) { CRClickPopup::createAndPush(CInfoWindow::genText(bld->getNameTranslated(), bld->getDescriptionTranslated()), - std::make_shared(CComponent::building, bld->town->faction->getIndex(), bld->bid)); + std::make_shared(ComponentType::BUILDING, BuildingTypeUniqueID(bld->town->faction->getId(), bld->bid))); } else { @@ -860,7 +860,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID) void CCastleBuildings::enterBuilding(BuildingID building) { - std::vector> comps(1, std::make_shared(CComponent::building, town->subID, building)); + std::vector> comps(1, std::make_shared(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building))); LOCPLINT->showInfoDialog( town->town->buildings.find(building)->second->getDescriptionTranslated(), comps); } @@ -920,7 +920,7 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow() void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID upgrades) { - std::vector> comps(1, std::make_shared(CComponent::building,town->subID,building)); + std::vector> comps(1, std::make_shared(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building))); std::string descr = town->town->buildings.find(building)->second->getDescriptionTranslated(); std::string hasNotProduced; std::string hasProduced; @@ -969,9 +969,9 @@ void CCastleBuildings::enterMagesGuild() { const StartInfo *si = LOCPLINT->cb->getStartInfo(); // it would be nice to find a way to move this hack to config/mapOverrides.json - if(si && si->campState && // We're in campaign, + if(si && si->campState && // We're in campaign, (si->campState->getFilename() == "DATA/YOG.H3C") && // which is "Birth of a Barbarian", - (hero->subID == 45)) // and the hero is Yog (based on Solmyr) + (hero->getHeroType() == 45)) // and the hero is Yog (based on Solmyr) { // "Yog has given up magic in all its forms..." LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[736]); @@ -986,7 +986,7 @@ void CCastleBuildings::enterMagesGuild() CFunctionList onYes = [this](){ openMagesGuild(); }; CFunctionList onNo = onYes; onYes += [hero](){ LOCPLINT->cb->buyArtifact(hero, ArtifactID::SPELLBOOK); }; - std::vector> components(1, std::make_shared(CComponent::artifact,ArtifactID::SPELLBOOK,0)); + std::vector> components(1, std::make_shared(ComponentType::ARTIFACT, ArtifactID(ArtifactID::SPELLBOOK))); LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[214], onYes, onNo, components); } @@ -1129,7 +1129,7 @@ void CCreaInfo::showPopupWindow(const Point & cursorPosition) if (showAvailable) GH.windows().createAndPushWindow(GH.screenDimensions().x / 2, GH.screenDimensions().y / 2, town, level); else - CRClickPopup::createAndPush(genGrowthText(), std::make_shared(CComponent::creature, creature->getId())); + CRClickPopup::createAndPush(genGrowthText(), std::make_shared(ComponentType::CREATURE, creature->getId())); } bool CCreaInfo::getShowAvailable() @@ -1180,7 +1180,7 @@ void CTownInfo::showPopupWindow(const Point & cursorPosition) { if(building) { - auto c = std::make_shared(CComponent::building, building->town->faction->getIndex(), building->bid); + auto c = std::make_shared(ComponentType::BUILDING, BuildingTypeUniqueID(building->town->faction->getId(), building->bid)); CRClickPopup::createAndPush(CInfoWindow::genText(building->getNameTranslated(), building->getDescriptionTranslated()), c); } } @@ -1526,15 +1526,24 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin //Create components for all required resources std::vector> components; - for(int i = 0; iresources[i]) { - std::string text = std::to_string(building->resources[i]); - int resAfterBuy = LOCPLINT->cb->getResourceAmount(GameResID(i)) - building->resources[i]; - if(resAfterBuy < 0 && state != EBuildingState::ALREADY_PRESENT && settings["general"]["enableUiEnhancements"].Bool()) - text = "{H3Red|" + std::to_string(LOCPLINT->cb->getResourceAmount(GameResID(i))) + "}" + " / " + text; - components.push_back(std::make_shared(CComponent::resource, i, text, CComponent::small)); + MetaString message; + int resourceAmount = LOCPLINT->cb->getResourceAmount(i); + bool canAfford = resourceAmount >= building->resources[i]; + + if(!canAfford && state != EBuildingState::ALREADY_PRESENT && settings["general"]["enableUiEnhancements"].Bool()) + { + message.appendRawString("{H3Red|%d}/%d"); + message.replaceNumber(resourceAmount); + } + else + message.appendRawString("%d"); + + message.replaceNumber(building->resources[i]); + components.push_back(std::make_shared(ComponentType::RESOURCE, i, message.toString(), CComponent::small)); } } @@ -1889,12 +1898,12 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell) void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition) { - LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared(CComponent::spell, spell->id)); + LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared(ComponentType::SPELL, spell->id)); } void CMageGuildScreen::Scroll::showPopupWindow(const Point & cursorPosition) { - CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared(CComponent::spell, spell->id)); + CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared(ComponentType::SPELL, spell->id)); } void CMageGuildScreen::Scroll::hover(bool on) @@ -1935,7 +1944,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art cancelText.appendTextID("core.genrltxt.596"); cancelText.replaceTextID(creature->getNameSingularTextID()); - std::string costString = std::to_string(aid.toArtifact(CGI->artifacts())->getPrice()); + std::string costString = std::to_string(aid.toEntity(CGI)->getPrice()); title = std::make_shared(165, 28, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, titleString.toString()); costText = std::make_shared(165, 218, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]); diff --git a/client/windows/CCreatureWindow.cpp b/client/windows/CCreatureWindow.cpp index bece268cf..f6e073f2c 100644 --- a/client/windows/CCreatureWindow.cpp +++ b/client/windows/CCreatureWindow.cpp @@ -325,7 +325,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset) std::vector> resComps; for(TResources::nziterator i(totalCost); i.valid(); i++) { - resComps.push_back(std::make_shared(CComponent::resource, i->resType, (int)i->resVal)); + resComps.push_back(std::make_shared(ComponentType::RESOURCE, i->resType, i->resVal)); } if(LOCPLINT->cb->getResourceAmount().canAfford(totalCost)) @@ -582,10 +582,10 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s const CCommanderInstance * commander = parent->info->commander; expRankIcon = std::make_shared(AnimationPath::builtin("PSKIL42"), 4, 0, pos.x, pos.y); - auto area = std::make_shared(Rect(pos.x, pos.y, 44, 44), CComponent::experience); + auto area = std::make_shared(Rect(pos.x, pos.y, 44, 44), ComponentType::EXPERIENCE); expArea = area; area->text = CGI->generaltexth->allTexts[2]; - area->bonusValue = commander->getExpRank(); + area->component.value = commander->getExpRank(); boost::replace_first(area->text, "%d", std::to_string(commander->getExpRank())); boost::replace_first(area->text, "%d", std::to_string(CGI->heroh->reqExp(commander->getExpRank() + 1))); boost::replace_first(area->text, "%d", std::to_string(commander->experience)); @@ -611,8 +611,8 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s if(art) { parent->stackArtifactIcon = std::make_shared(AnimationPath::builtin("ARTIFACT"), art->artType->getIconIndex(), 0, pos.x, pos.y); - parent->stackArtifactHelp = std::make_shared(Rect(pos, Point(44, 44)), CComponent::artifact); - parent->stackArtifactHelp->type = art->artType->getId(); + parent->stackArtifactHelp = std::make_shared(Rect(pos, Point(44, 44)), ComponentType::ARTIFACT); + parent->stackArtifactHelp->component.subType = art->artType->getId(); if(parent->info->owner) { @@ -974,11 +974,12 @@ void CStackWindow::removeStackArtifact(ArtifactPosition pos) const auto slot = ArtifactUtils::getArtBackpackPosition(info->owner, art->getTypeId()); if(slot != ArtifactPosition::PRE_FIRST) { - LOCPLINT->cb->swapArtifacts(ArtifactLocation(info->stackNode, pos), ArtifactLocation(info->owner, slot)); + auto artLoc = ArtifactLocation(info->owner->id, pos); + artLoc.creature = info->stackNode->armyObj->findStack(info->stackNode); + LOCPLINT->cb->swapArtifacts(artLoc, ArtifactLocation(info->owner->id, slot)); stackArtifactButton.reset(); stackArtifactHelp.reset(); stackArtifactIcon.reset(); redraw(); } } - diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 7d05fe38a..e4e93337d 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -119,9 +119,9 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero) for(int v = 0; v < GameConstants::PRIMARY_SKILLS; ++v) { - auto area = std::make_shared(Rect(30 + 70 * v, 109, 42, 64), CComponent::primskill); + auto area = std::make_shared(Rect(30 + 70 * v, 109, 42, 64), ComponentType::PRIM_SKILL); area->text = CGI->generaltexth->arraytxt[2+v]; - area->type = v; + area->component.subType = PrimarySkill(v); area->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % CGI->generaltexth->primarySkillNames[v]); primSkillAreas.push_back(area); @@ -154,7 +154,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero) for(int i = 0; i < std::min(hero->secSkills.size(), 8u); ++i) { Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), 136, 42); - secSkillAreas.push_back(std::make_shared(r, CComponent::secskill)); + secSkillAreas.push_back(std::make_shared(r, ComponentType::SEC_SKILL)); secSkillImages.push_back(std::make_shared(secSkills, 0, 0, r.x, r.y)); int x = (i % 2) ? 212 : 68; @@ -232,20 +232,21 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) //primary skills support for(size_t g=0; gbonusValue = curHero->getPrimSkillLevel(static_cast(g)); - primSkillValues[g]->setText(std::to_string(primSkillAreas[g]->bonusValue)); + int value = curHero->getPrimSkillLevel(static_cast(g)); + primSkillAreas[g]->component.value = value; + primSkillValues[g]->setText(std::to_string(value)); } //secondary skills support for(size_t g=0; g< secSkillAreas.size(); ++g) { - int skill = curHero->secSkills[g].first; - int level = curHero->getSecSkillLevel(SecondarySkill(curHero->secSkills[g].first)); + SecondarySkill skill = curHero->secSkills[g].first; + int level = curHero->getSecSkillLevel(skill); std::string skillName = CGI->skillh->getByIndex(skill)->getNameTranslated(); std::string skillValue = CGI->generaltexth->levels[level-1]; - secSkillAreas[g]->type = skill; - secSkillAreas[g]->bonusValue = level; + secSkillAreas[g]->component.subType = skill; + secSkillAreas[g]->component.value = level; secSkillAreas[g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level); secSkillAreas[g]->hoverText = boost::str(boost::format(heroscrn[21]) % skillValue % skillName); secSkillImages[g]->setFrame(skill*3 + level + 2); @@ -334,20 +335,11 @@ void CHeroWindow::commanderWindow() if(pickedArtInst) { const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->commander, pickedArtInst->getTypeId()); - if(freeSlot < ArtifactPosition::COMMANDER_AFTER_LAST) //we don't want to put it in commander's backpack! + if(vstd::contains(ArtifactUtils::commanderSlots(), freeSlot)) // We don't want to put it in commander's backpack! { - ArtifactLocation src(hero, ArtifactPosition::TRANSITION_POS); - ArtifactLocation dst(curHero->commander.get(), freeSlot); - - if(pickedArtInst->canBePutAt(dst, true)) - { //equip clicked stack - if(dst.getArt()) - { - LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(hero, - ArtifactUtils::getArtBackpackPosition(hero, pickedArtInst->getTypeId()))); - } - LOCPLINT->cb->swapArtifacts(src, dst); - } + ArtifactLocation dst(curHero->id, freeSlot); + dst.creature = SlotID::COMMANDER_SLOT_PLACEHOLDER; + LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS), dst); } } else diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 85d157bbe..4be0b5569 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -256,7 +256,7 @@ void InfoBoxAbstractHeroData::prepareMessage(std::string & text, std::shared_ptr break; case HERO_PRIMARY_SKILL: text = CGI->generaltexth->arraytxt[2+getSubID()]; - comp = std::make_shared(CComponent::primskill, getSubID(), (int)getValue()); + comp = std::make_shared(ComponentType::PRIM_SKILL, PrimarySkill(getSubID()), getValue()); break; case HERO_MANA: text = CGI->generaltexth->allTexts[149]; @@ -271,7 +271,7 @@ void InfoBoxAbstractHeroData::prepareMessage(std::string & text, std::shared_ptr if(value) { text = CGI->skillh->getByIndex(subID)->getDescriptionTranslated((int)value); - comp = std::make_shared(CComponent::secskill, subID, (int)value); + comp = std::make_shared(ComponentType::SEC_SKILL, SecondarySkill(subID), (int)value); } break; } diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index bc43ea654..5fb2f1a8d 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -558,7 +558,7 @@ void CSpellWindow::SpellArea::clickPressed(const Point & cursorPosition) //battle spell on adv map or adventure map spell during combat => display infowindow, not cast if((combatSpell ^ inCombat) || inCastle) { - std::vector> hlp(1, std::make_shared(CComponent::spell, mySpell->id, 0)); + std::vector> hlp(1, std::make_shared(ComponentType::SPELL, mySpell->id)); LOCPLINT->showInfoDialog(mySpell->getDescriptionTranslated(schoolLevel), hlp); } else if(combatSpell) @@ -614,7 +614,7 @@ void CSpellWindow::SpellArea::showPopupWindow(const Point & cursorPosition) boost::algorithm::replace_first(dmgInfo, "%d", std::to_string(causedDmg)); } - CRClickPopup::createAndPush(mySpell->getDescriptionTranslated(schoolLevel) + dmgInfo, std::make_shared(CComponent::spell, mySpell->id)); + CRClickPopup::createAndPush(mySpell->getDescriptionTranslated(schoolLevel) + dmgInfo, std::make_shared(ComponentType::SPELL, mySpell->id)); } } diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 8a1d3672a..2d9da6c4c 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -191,8 +191,8 @@ void CTradeWindow::CTradeableItem::clickPressed(const Point & cursorPosition) const auto hero = aw->arts->getHero(); const auto slot = hero->getSlotByInstance(art); assert(slot != ArtifactPosition::PRE_FIRST); - LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slot), - ArtifactLocation(hero, ArtifactPosition::TRANSITION_POS)); + LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, slot), + ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS)); aw->arts->pickedArtFromSlot = slot; aw->arts->artifactsOnAltar.erase(art); setID(-1); @@ -667,10 +667,10 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->getNameTranslated(); break; case EMarketMode::RESOURCE_ARTIFACT: - title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); + title = (*CGI->townh)[o->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); break; case EMarketMode::ARTIFACT_RESOURCE: - title = (*CGI->townh)[o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); + title = (*CGI->townh)[o->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated(); // create image that copies part of background containing slot MISC_1 into position of slot MISC_5 // this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing diff --git a/client/windows/CWindowObject.cpp b/client/windows/CWindowObject.cpp index c21c1a219..231213219 100644 --- a/client/windows/CWindowObject.cpp +++ b/client/windows/CWindowObject.cpp @@ -39,7 +39,8 @@ CWindowObject::CWindowObject(int options_, const ImagePath & imageName, Point ce options(options_), background(createBg(imageName, options & PLAYER_COLORED)) { - assert(parent == nullptr); //Safe to remove, but windows should not have parent + if(!(options & NEEDS_ANIMATED_BACKGROUND)) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet) + assert(parent == nullptr); //Safe to remove, but windows should not have parent defActions = 255-DISPOSE; @@ -60,7 +61,8 @@ CWindowObject::CWindowObject(int options_, const ImagePath & imageName): options(options_), background(createBg(imageName, options_ & PLAYER_COLORED)) { - assert(parent == nullptr); //Safe to remove, but windows should not have parent + if(!(options & NEEDS_ANIMATED_BACKGROUND)) //currently workaround for highscores (currently uses window as normal control, because otherwise videos are not played in background yet) + assert(parent == nullptr); //Safe to remove, but windows should not have parent defActions = 255-DISPOSE; diff --git a/client/windows/CWindowObject.h b/client/windows/CWindowObject.h index dd49f107b..4746f52c8 100644 --- a/client/windows/CWindowObject.h +++ b/client/windows/CWindowObject.h @@ -38,7 +38,8 @@ public: PLAYER_COLORED=1, //background will be player-colored RCLICK_POPUP=2, // window will behave as right-click popup BORDERED=4, // window will have border if current resolution is bigger than size of window - SHADOW_DISABLED=8 //this window won't display any shadow + SHADOW_DISABLED=8, //this window won't display any shadow + NEEDS_ANIMATED_BACKGROUND=16 //there are videos in the background that have to be played }; /* diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index dd06d2dab..34824e619 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -401,7 +401,7 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std std::vector> comps; for(auto & skill : skills) { - auto comp = std::make_shared(CComponent::secskill, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium); + auto comp = std::make_shared(ComponentType::SEC_SKILL, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium); comp->onChoose = std::bind(&CLevelWindow::close, this); comps.push_back(comp); } @@ -693,9 +693,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, else primSkillAreas[g]->pos = Rect(Point(pos.x + 329, pos.y + 19 + 36 * g), Point(140, 32)); primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g]; - primSkillAreas[g]->type = g; - primSkillAreas[g]->bonusValue = 0; - primSkillAreas[g]->baseType = 0; + primSkillAreas[g]->component = Component( ComponentType::PRIM_SKILL, PrimarySkill(g)); primSkillAreas[g]->hoverText = CGI->generaltexth->heroscrn[1]; boost::replace_first(primSkillAreas[g]->hoverText, "%s", CGI->generaltexth->primarySkillNames[g]); } @@ -708,14 +706,11 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, //secondary skill's clickable areas for(int g=0; gsecSkills.size(); ++g) { - int skill = hero->secSkills[g].first, - level = hero->secSkills[g].second; // <1, 3> + SecondarySkill skill = hero->secSkills[g].first; + int level = hero->secSkills[g].second; // <1, 3> secSkillAreas[b].push_back(std::make_shared()); secSkillAreas[b][g]->pos = Rect(Point(pos.x + 32 + g * 36 + b * 454 , pos.y + (qeLayout ? 83 : 88)), Point(32, 32) ); - secSkillAreas[b][g]->baseType = 1; - - secSkillAreas[b][g]->type = skill; - secSkillAreas[b][g]->bonusValue = level; + secSkillAreas[b][g]->component = Component(ComponentType::SEC_SKILL, skill, level); secSkillAreas[b][g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level); secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21]; @@ -1082,7 +1077,7 @@ void CUniversityWindow::CItem::clickPressed(const Point & cursorPosition) void CUniversityWindow::CItem::showPopupWindow(const Point & cursorPosition) { - CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared(CComponent::secskill, ID, 1)); + CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared(ComponentType::SEC_SKILL, ID, 1)); } void CUniversityWindow::CItem::hover(bool on) diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 5580c5a36..07b47b8eb 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -375,7 +375,7 @@ class CUniversityWindow : public CStatusbarWindow std::shared_ptr name; std::shared_ptr level; public: - int ID;//id of selected skill + SecondarySkill ID;//id of selected skill CUniversityWindow * parent; void showAll(Canvas & to) override; diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 4654082d6..85515c7c1 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -686,8 +686,8 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods - COMMAND ${CMAKE_COMMAND} -E copy_directory ${MAIN_LIB_DIR}/../config ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config - COMMAND ${CMAKE_COMMAND} -E copy_directory ${MAIN_LIB_DIR}/../Mods ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../config ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../Mods ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods ) endif() diff --git a/cmake_modules/create_link.cmake b/cmake_modules/create_link.cmake new file mode 100644 index 000000000..c5ea762ad --- /dev/null +++ b/cmake_modules/create_link.cmake @@ -0,0 +1,14 @@ + +#message(${CMAKE_ARGV0}) # cmake.exe +#message(${CMAKE_ARGV1}) # -P +#message(${CMAKE_ARGV2}) # thisfilename +#message(${CMAKE_ARGV3}) # existing +#message(${CMAKE_ARGV4}) # linkname +if (WIN32) + file(TO_NATIVE_PATH ${CMAKE_ARGV3} existing_native) + file(TO_NATIVE_PATH ${CMAKE_ARGV4} linkname_native) + execute_process(COMMAND cmd.exe /c RD /Q "${linkname_native}") + execute_process(COMMAND cmd.exe /c mklink /J "${linkname_native}" "${existing_native}") +else() + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_ARGV3} ${CMAKE_ARGV4}) +endif() diff --git a/docs/players/Installation_iOS.md b/docs/players/Installation_iOS.md index 46d88c99c..a57eef464 100644 --- a/docs/players/Installation_iOS.md +++ b/docs/players/Installation_iOS.md @@ -16,6 +16,10 @@ have the following options: - [Create signing assets on macOS from terminal](https://github.com/kambala-decapitator/xcode-auto-signing-assets). In the command replace `your.bundle.id` with something like `com.MY-NAME.vcmi`. After that use the above signer tool. - [Sign from any OS](https://github.com/indygreg/PyOxidizer/tree/main/tugger-code-signing). You'd still need to find a way to create signing assets (private key and provisioning profile) though. +To install the signed ipa on your device, you can use Xcode or Apple Configurator (available on the Mac App Store for free). The latter also allows installing ipa from the command line, here's an example that assumes you have only 1 device connected to your Mac and the signed ipa is on your desktop: + + /Applications/Apple\ Configurator.app/Contents/MacOS/cfgutil install-app ~/Desktop/vcmi.ipa + ## Step 2: Installing Heroes III data files Note: if you don't need in-game videos, you can omit downloading/copying file VIDEO.VID from data folder - it will save your time and space. The same applies to the Mp3 directory. diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index f21ad852b..5ab52821f 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -174,12 +174,11 @@ if(APPLE_IOS) else() set(RESOURCES_DESTINATION ${DATA_DIR}/launcher) - # Copy to build directory for easier debugging + # Link to build directory for easier debugging add_custom_command(TARGET vcmilauncher POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/translation - + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/translation ) install(TARGETS vcmilauncher DESTINATION ${BIN_DIR}) diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index 5ac6648c2..e0c6bb635 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -90,7 +90,9 @@ void CSettingsView::loadSettings() ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String())); ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String())); ui->comboBoxEnemyAI->setCurrentText(QString::fromStdString(settings["server"]["enemyAI"].String())); + ui->comboBoxEnemyPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["playerAI"].String())); + ui->comboBoxAlliedPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["alliedAI"].String())); ui->spinBoxNetworkPort->setValue(settings["server"]["port"].Integer()); diff --git a/lib/ArtifactUtils.cpp b/lib/ArtifactUtils.cpp index 92da87763..85a72bf66 100644 --- a/lib/ArtifactUtils.cpp +++ b/lib/ArtifactUtils.cpp @@ -74,6 +74,49 @@ DLL_LINKAGE const std::vector & ArtifactUtils::constituentWorn return positions; } +DLL_LINKAGE const std::vector & ArtifactUtils::allWornSlots() +{ + static const std::vector positions = + { + ArtifactPosition::HEAD, + ArtifactPosition::SHOULDERS, + ArtifactPosition::NECK, + ArtifactPosition::RIGHT_HAND, + ArtifactPosition::LEFT_HAND, + ArtifactPosition::TORSO, + ArtifactPosition::RIGHT_RING, + ArtifactPosition::LEFT_RING, + ArtifactPosition::FEET, + ArtifactPosition::MISC1, + ArtifactPosition::MISC2, + ArtifactPosition::MISC3, + ArtifactPosition::MISC4, + ArtifactPosition::MISC5, + ArtifactPosition::MACH1, + ArtifactPosition::MACH2, + ArtifactPosition::MACH3, + ArtifactPosition::MACH4, + ArtifactPosition::SPELLBOOK + }; + + return positions; +} + +DLL_LINKAGE const std::vector & ArtifactUtils::commanderSlots() +{ + static const std::vector positions = + { + ArtifactPosition::COMMANDER1, + ArtifactPosition::COMMANDER2, + ArtifactPosition::COMMANDER3, + ArtifactPosition::COMMANDER4, + ArtifactPosition::COMMANDER5, + ArtifactPosition::COMMANDER6 + }; + + return positions; +} + DLL_LINKAGE bool ArtifactUtils::isArtRemovable(const std::pair & slot) { return slot.second.artifact @@ -214,7 +257,7 @@ DLL_LINKAGE void ArtifactUtils::insertScrrollSpellName(std::string & description if(sid.getNum() >= 0) { if(nameStart != std::string::npos && nameEnd != std::string::npos) - description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toSpell(VLC->spells())->getNameTranslated()); + description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toEntity(VLC->spells())->getNameTranslated()); } } diff --git a/lib/ArtifactUtils.h b/lib/ArtifactUtils.h index 4b30f946d..b4af3b401 100644 --- a/lib/ArtifactUtils.h +++ b/lib/ArtifactUtils.h @@ -31,6 +31,8 @@ namespace ArtifactUtils // TODO: Make this constexpr when the toolset is upgraded DLL_LINKAGE const std::vector & unmovableSlots(); DLL_LINKAGE const std::vector & constituentWornSlots(); + DLL_LINKAGE const std::vector & allWornSlots(); + DLL_LINKAGE const std::vector & commanderSlots(); DLL_LINKAGE bool isArtRemovable(const std::pair & slot); DLL_LINKAGE bool checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, const ArtifactID & artID, const ArtifactPosition & slot); DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot); diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 413a51bb2..a912854b6 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -167,7 +167,7 @@ bool CArtifact::isBig() const bool CArtifact::isTradable() const { - switch(id) + switch(id.toEnum()) { case ArtifactID::SPELLBOOK: case ArtifactID::GRAIL: @@ -693,8 +693,8 @@ void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander) a->possibleSlots[ArtBearer::HERO].clear(); a->possibleSlots[ArtBearer::CREATURE].clear(); } - for (int i = ArtifactPosition::COMMANDER1; i <= ArtifactPosition::COMMANDER6; ++i) - a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(i)); + for(const auto & slot : ArtifactUtils::commanderSlots()) + a->possibleSlots[ArtBearer::COMMANDER].push_back(ArtifactPosition(slot)); } bool CArtHandler::legalArtifact(const ArtifactID & id) @@ -975,9 +975,9 @@ const ArtSlotInfo * CArtifactSet::getSlot(const ArtifactPosition & pos) const } if(vstd::contains(artifactsWorn, pos)) return &artifactsWorn.at(pos); - if(pos >= ArtifactPosition::AFTER_LAST ) + if(ArtifactUtils::isSlotBackpack(pos)) { - int backpackPos = (int)pos - ArtifactPosition::BACKPACK_START; + auto backpackPos = pos - ArtifactPosition::BACKPACK_START; if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size()) return nullptr; else @@ -1080,9 +1080,9 @@ void CArtifactSet::serializeJsonArtifacts(JsonSerializeFormat & handler, const s void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map) { - for(ArtifactPosition ap = ArtifactPosition::HEAD; ap < ArtifactPosition::AFTER_LAST; ap.advance(1)) + for(const auto & slot : ArtifactUtils::allWornSlots()) { - serializeJsonSlot(handler, ap, map); + serializeJsonSlot(handler, slot, map); } std::vector backpackTemp; diff --git a/lib/CArtifactInstance.cpp b/lib/CArtifactInstance.cpp index d80373251..c790ca379 100644 --- a/lib/CArtifactInstance.cpp +++ b/lib/CArtifactInstance.cpp @@ -155,9 +155,9 @@ void CArtifactInstance::setId(ArtifactInstanceID id) this->id = id; } -bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const +bool CArtifactInstance::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const { - return artType->canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved); + return artType->canBePutAt(artSet, slot, assumeDestRemoved); } bool CArtifactInstance::isCombined() const @@ -165,15 +165,15 @@ bool CArtifactInstance::isCombined() const return artType->isCombined(); } -void CArtifactInstance::putAt(const ArtifactLocation & al) +void CArtifactInstance::putAt(CArtifactSet & set, const ArtifactPosition slot) { - auto placementMap = al.getHolderArtSet()->putArtifact(al.slot, this); + auto placementMap = set.putArtifact(slot, this); addPlacementMap(placementMap); } -void CArtifactInstance::removeFrom(const ArtifactLocation & al) +void CArtifactInstance::removeFrom(CArtifactSet & set, const ArtifactPosition slot) { - al.getHolderArtSet()->removeArtifact(al.slot); + set.removeArtifact(slot); for(auto & part : partsInfo) { if(part.slot != ArtifactPosition::PRE_FIRST) @@ -181,10 +181,10 @@ void CArtifactInstance::removeFrom(const ArtifactLocation & al) } } -void CArtifactInstance::move(const ArtifactLocation & src, const ArtifactLocation & dst) +void CArtifactInstance::move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot) { - removeFrom(src); - putAt(dst); + removeFrom(srcSet, srcSlot); + putAt(dstSet, dstSlot); } void CArtifactInstance::deserializationFix() diff --git a/lib/CArtifactInstance.h b/lib/CArtifactInstance.h index 2c87adf08..d72397afe 100644 --- a/lib/CArtifactInstance.h +++ b/lib/CArtifactInstance.h @@ -84,11 +84,12 @@ public: ArtifactInstanceID getId() const; void setId(ArtifactInstanceID id); - bool canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved = false) const; + bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE, + bool assumeDestRemoved = false) const; bool isCombined() const; - void putAt(const ArtifactLocation & al); - void removeFrom(const ArtifactLocation & al); - void move(const ArtifactLocation & src, const ArtifactLocation & dst); + void putAt(CArtifactSet & set, const ArtifactPosition slot); + void removeFrom(CArtifactSet & set, const ArtifactPosition slot); + void move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot); void deserializationFix(); template void serialize(Handler & h, const int version) diff --git a/lib/CBuildingHandler.cpp b/lib/CBuildingHandler.cpp index 6db07ac15..be986ee8b 100644 --- a/lib/CBuildingHandler.cpp +++ b/lib/CBuildingHandler.cpp @@ -12,7 +12,7 @@ VCMI_LIB_NAMESPACE_BEGIN -BuildingID CBuildingHandler::campToERMU(int camp, int townType, const std::set & builtBuildings) +BuildingID CBuildingHandler::campToERMU(int camp, FactionID townType, const std::set & builtBuildings) { static const std::vector campToERMU = { @@ -47,13 +47,13 @@ BuildingID CBuildingHandler::campToERMU(int camp, int townType, const std::set 1) + if(hordeLvlsPerTType[townType.getNum()].size() > 1) { - BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType][1]); + BuildingID dwellingID(BuildingID::DWELL_UP_FIRST + hordeLvlsPerTType[townType.getNum()][1]); if(vstd::contains(builtBuildings, dwellingID)) //if upgraded dwelling is built return BuildingID::HORDE_2_UPGR; diff --git a/lib/CBuildingHandler.h b/lib/CBuildingHandler.h index a0aab5615..059b9f087 100644 --- a/lib/CBuildingHandler.h +++ b/lib/CBuildingHandler.h @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE CBuildingHandler { public: - static BuildingID campToERMU(int camp, int townType, const std::set & builtBuildings); + static BuildingID campToERMU(int camp, FactionID townType, const std::set & builtBuildings); }; VCMI_LIB_NAMESPACE_END diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index eb03049ce..54c3083da 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -263,7 +263,7 @@ bool CCreature::isDoubleWide() const */ bool CCreature::isGood () const { - return VLC->factions()->getByIndex(faction)->getAlignment() == EAlignment::GOOD; + return VLC->factions()->getById(faction)->getAlignment() == EAlignment::GOOD; } /** @@ -272,7 +272,7 @@ bool CCreature::isGood () const */ bool CCreature::isEvil () const { - return VLC->factions()->getByIndex(faction)->getAlignment() == EAlignment::EVIL; + return VLC->factions()->getById(faction)->getAlignment() == EAlignment::EVIL; } si32 CCreature::maxAmount(const TResources &res) const //how many creatures can be bought @@ -388,7 +388,7 @@ void CCreature::serializeJson(JsonSerializeFormat & handler) if(handler.updating) { cost.serializeJson(handler, "cost"); - handler.serializeInt("faction", faction);//TODO: unify with deferred resolve + handler.serializeId("faction", faction); } handler.serializeInt("level", level); @@ -1350,34 +1350,23 @@ CCreatureHandler::~CCreatureHandler() CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier) const { - int r = 0; - if(tier == -1) //pick any allowed creature + std::vector allowed; + for(const auto & creature : objects) { - do - { - r = (*RandomGeneratorUtil::nextItem(objects, rand))->getId(); - } while (objects[r] && objects[r]->special); // find first "not special" creature + if(creature->special) + continue; + + if (creature->level == tier || tier == -1) + allowed.push_back(creature->getId()); } - else + + if(allowed.empty()) { - assert(vstd::iswithin(tier, 1, 7)); - std::vector allowed; - for(const auto & creature : objects) - { - if(!creature->special && creature->level == tier) - allowed.push_back(creature->getId()); - } - - if(allowed.empty()) - { - logGlobal->warn("Cannot pick a random creature of tier %d!", tier); - return CreatureID::NONE; - } - - return *RandomGeneratorUtil::nextItem(allowed, rand); + logGlobal->warn("Cannot pick a random creature of tier %d!", tier); + return CreatureID::NONE; } - assert (r >= 0); //should always be, but it crashed - return CreatureID(r); + + return *RandomGeneratorUtil::nextItem(allowed, rand); } diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 34849d337..1a75578cc 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -458,7 +458,7 @@ const CStackInstance & CCreatureSet::getStack(const SlotID & slot) const return *getStackPtr(slot); } -const CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const +CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const { if(hasStackAtSlot(slot)) return stacks.find(slot)->second; @@ -870,7 +870,7 @@ ArtBearer::ArtBearer CStackInstance::bearerType() const CStackInstance::ArtPlacementMap CStackInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art) { assert(!getArt(pos)); - assert(art->artType->canBePutAt(this, pos)); + assert(art->canBePutAt(this, pos)); attachTo(*art); return CArtifactSet::putArtifact(pos, art); @@ -1017,6 +1017,11 @@ const Creature * CStackBasicDescriptor::getType() const return type; } +CreatureID CStackBasicDescriptor::getId() const +{ + return type->getId(); +} + TQuantity CStackBasicDescriptor::getCount() const { return count; diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 634e62ba4..9b3cad26c 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -39,6 +39,7 @@ public: virtual ~CStackBasicDescriptor() = default; const Creature * getType() const; + CreatureID getId() const; TQuantity getCount() const; virtual void setType(const CCreature * c); @@ -253,7 +254,7 @@ public: void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all. const CStackInstance & getStack(const SlotID & slot) const; //stack must exist - const CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr + CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr const CCreature * getCreature(const SlotID & slot) const; //workaround of map issue; int getStackCount(const SlotID & slot) const; TExpType getStackExperience(const SlotID & slot) const; diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 5ab149426..44de7bee3 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -16,6 +16,7 @@ #include "gameState/TavernHeroesPool.h" #include "gameState/QuestInfo.h" #include "mapObjects/CGHeroInstance.h" +#include "networkPacks/ArtifactLocation.h" #include "CGeneralTextHandler.h" #include "StartInfo.h" // for StartInfo #include "battle/BattleInfo.h" // for BattleInfo @@ -966,6 +967,22 @@ const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid return gs->map->objects[oid.num]; } +CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const +{ + auto hero = const_cast(getHero(loc.artHolder)); + if(loc.creature.has_value()) + { + if(loc.creature.value() == SlotID::COMMANDER_SLOT_PLACEHOLDER) + return hero->commander; + else + return hero->getStackPtr(loc.creature.value()); + } + else + { + return hero; + } +} + std::vector CGameInfoCallback::getVisibleTeleportObjects(std::vector ids, PlayerColor player) const { vstd::erase_if(ids, [&](const ObjectInstanceID & id) -> bool diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index e1a19d5c3..794719187 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -40,6 +40,8 @@ class CGameState; class PathfinderConfig; struct TurnTimerInfo; +struct ArtifactLocation; +class CArtifactSet; class CArmedInstance; class CGObjectInstance; class CGHeroInstance; @@ -174,6 +176,7 @@ public: virtual int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg virtual const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const; virtual const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const; + virtual CArtifactSet * getArtSet(const ArtifactLocation & loc) const; //virtual const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const; //objects diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 8bb900e12..904880616 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -125,15 +125,17 @@ SecondarySkill CHeroClass::chooseSecSkill(const std::set & possi { int totalProb = 0; for(const auto & possible : possibles) - { - totalProb += secSkillProbability[possible]; - } + if (secSkillProbability.count(possible) != 0) + totalProb += secSkillProbability.at(possible); + if (totalProb != 0) // may trigger if set contains only banned skills (0 probability) { auto ran = rand.nextInt(totalProb - 1); for(const auto & possible : possibles) { - ran -= secSkillProbability[possible]; + if (secSkillProbability.count(possible) != 0) + ran -= secSkillProbability.at(possible); + if(ran < 0) { return possible; @@ -151,7 +153,7 @@ bool CHeroClass::isMagicHero() const EAlignment CHeroClass::getAlignment() const { - return VLC->factions()->getByIndex(faction)->getAlignment(); + return VLC->factions()->getById(faction)->getAlignment(); } int32_t CHeroClass::getIndex() const @@ -209,7 +211,7 @@ CHeroClass::CHeroClass(): void CHeroClassHandler::fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass, PrimarySkill pSkill) const { - const auto & skillName = NPrimarySkill::names[static_cast(pSkill)]; + const auto & skillName = NPrimarySkill::names[pSkill.getNum()]; auto currentPrimarySkillValue = static_cast(node["primarySkills"][skillName].Integer()); //minimal value is 0 for attack and defense and 1 for spell power and knowledge auto primarySkillLegalMinimum = (pSkill == PrimarySkill::ATTACK || pSkill == PrimarySkill::DEFENSE) ? 0 : 1; @@ -271,8 +273,6 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js int probability = static_cast(skillPair.second.Integer()); VLC->identifiers()->requestIdentifier(skillPair.second.meta, "skill", skillPair.first, [heroClass, probability](si32 skillID) { - if(heroClass->secSkillProbability.size() <= skillID) - heroClass->secSkillProbability.resize(skillID + 1, -1); // -1 = override with default later heroClass->secSkillProbability[skillID] = probability; }); } @@ -369,11 +369,11 @@ void CHeroClassHandler::afterLoadFinalization() auto chance = static_cast(heroClass->defaultTavernChance * faction->town->defaultTavernChance); heroClass->selectionProbability[faction->getId()] = static_cast(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it } + // set default probabilities for gaining secondary skills where not loaded previously - heroClass->secSkillProbability.resize(VLC->skillh->size(), -1); for(int skillID = 0; skillID < VLC->skillh->size(); skillID++) { - if(heroClass->secSkillProbability[skillID] < 0) + if(heroClass->secSkillProbability.count(skillID) == 0) { const CSkill * skill = (*VLC->skillh)[SecondarySkill(skillID)]; logMod->trace("%s: no probability for %s, using default", heroClass->identifier, skill->getJsonKey()); diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index fbd5c24b3..dacb15928 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -157,7 +157,7 @@ public: std::vector primarySkillLowLevel; // probability (%) of getting point of primary skill when getting level std::vector primarySkillHighLevel;// same for high levels (> 10) - std::vector secSkillProbability; //probabilities of gaining secondary skills (out of 112), in id order + std::map secSkillProbability; //probabilities of gaining secondary skills (out of 112), in id order std::map selectionProbability; //probability of selection in towns @@ -201,12 +201,6 @@ public: h & imageBattleFemale; h & imageMapMale; h & imageMapFemale; - - if(!h.saving) - { - for(int & i : secSkillProbability) - vstd::amax(i, 0); - } } EAlignment getAlignment() const; }; diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index 82338746c..1d467b846 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -120,7 +120,7 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInf DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill) { - out << "Skill(" << (int)skill.id << "," << skill.identifier << "): ["; + out << "Skill(" << skill.id.getNum() << "," << skill.identifier << "): ["; for(int i=0; i < skill.levels.size(); i++) out << (i ? "," : "") << skill.levels[i]; return out << "]"; @@ -233,7 +233,7 @@ CSkill * CSkillHandler::loadFromJson(const std::string & scope, const JsonNode & skillAtLevel.iconMedium = levelNode["images"]["medium"].String(); skillAtLevel.iconLarge = levelNode["images"]["large"].String(); } - logMod->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id); + logMod->debug("loaded secondary skill %s(%d)", identifier, skill->id.getNum()); return skill; } @@ -262,23 +262,4 @@ std::vector CSkillHandler::getDefaultAllowed() const return allowedSkills; } -si32 CSkillHandler::decodeSkill(const std::string & identifier) -{ - auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "skill", identifier); - if(rawId) - return rawId.value(); - else - return -1; -} - -std::string CSkillHandler::encodeSkill(const si32 index) -{ - return (*VLC->skillh)[SecondarySkill(index)]->identifier; -} - -std::string CSkillHandler::encodeSkillWithType(const si32 index) -{ - return ModUtility::makeFullIdentifier("", "skill", encodeSkill(index)); -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/CSkillHandler.h b/lib/CSkillHandler.h index d8f84a3dd..83fe729bb 100644 --- a/lib/CSkillHandler.h +++ b/lib/CSkillHandler.h @@ -111,11 +111,6 @@ public: std::vector getDefaultAllowed() const override; - ///json serialization helpers - static si32 decodeSkill(const std::string & identifier); - static std::string encodeSkill(const si32 index); - static std::string encodeSkillWithType(const si32 index); - template void serialize(Handler & h, const int version) { h & objects; diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index a33205fb2..d9541d545 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -686,7 +686,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons //MODS COMPATIBILITY FOR 0.96 if(!ret->produce.nonZero()) { - switch (ret->bid) { + switch (ret->bid.toEnum()) { break; case BuildingID::VILLAGE_HALL: ret->produce[EGameResID::GOLD] = 500; break; case BuildingID::TOWN_HALL : ret->produce[EGameResID::GOLD] = 1000; break; case BuildingID::CITY_HALL : ret->produce[EGameResID::GOLD] = 2000; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index a2b7f8d3d..25f70c0e2 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -148,11 +148,11 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set & tiles, std: void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector & out, CRandomGenerator & rand) const { for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]); + out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE).toArtifact()); for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)]); + out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR).toArtifact()); - out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]); + out.push_back(VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR).toArtifact()); } void CPrivilegedInfoCallback::getAllowedSpells(std::vector & out, std::optional level) diff --git a/lib/JsonRandom.cpp b/lib/JsonRandom.cpp index 2e78df61f..e0c4fcc33 100644 --- a/lib/JsonRandom.cpp +++ b/lib/JsonRandom.cpp @@ -113,7 +113,7 @@ namespace JsonRandom if (value.empty() || value[0] != '@') return PrimarySkill(*VLC->identifiers()->getIdentifier(modScope, "primarySkill", value)); else - return PrimarySkill(loadVariable("primarySkill", value, variables, static_cast(PrimarySkill::NONE))); + return PrimarySkill(loadVariable("primarySkill", value, variables, PrimarySkill::NONE.getNum())); } /// Method that allows type-specific object filtering @@ -322,7 +322,7 @@ namespace JsonRandom for(const auto & pair : value.Struct()) { PrimarySkill id = decodeKey(pair.second.meta, pair.first, variables); - ret[static_cast(id)] += loadValue(pair.second, rng, variables); + ret[id.getNum()] += loadValue(pair.second, rng, variables); } } if(value.isVector()) @@ -333,7 +333,7 @@ namespace JsonRandom PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng); defaultSkills.erase(skillID); - ret[static_cast(skillID)] += loadValue(element, rng, variables); + ret[skillID.getNum()] += loadValue(element, rng, variables); } } return ret; diff --git a/lib/MetaString.cpp b/lib/MetaString.cpp index 93665a0e9..bd02386f2 100644 --- a/lib/MetaString.cpp +++ b/lib/MetaString.cpp @@ -114,35 +114,35 @@ std::string MetaString::getLocalString(const std::pair & txt) c { case EMetaText::ART_NAMES: { - const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts()); + const auto * art = ArtifactID(ser).toEntity(VLC); if(art) return art->getNameTranslated(); return "#!#"; } case EMetaText::ART_DESCR: { - const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts()); + const auto * art = ArtifactID(ser).toEntity(VLC); if(art) return art->getDescriptionTranslated(); return "#!#"; } case EMetaText::ART_EVNTS: { - const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts()); + const auto * art = ArtifactID(ser).toEntity(VLC); if(art) return art->getEventTranslated(); return "#!#"; } case EMetaText::CRE_PL_NAMES: { - const auto * cre = CreatureID(ser).toCreature(VLC->creatures()); + const auto * cre = CreatureID(ser).toEntity(VLC); if(cre) return cre->getNamePluralTranslated(); return "#!#"; } case EMetaText::CRE_SING_NAMES: { - const auto * cre = CreatureID(ser).toCreature(VLC->creatures()); + const auto * cre = CreatureID(ser).toEntity(VLC); if(cre) return cre->getNameSingularTranslated(); return "#!#"; @@ -157,7 +157,7 @@ std::string MetaString::getLocalString(const std::pair & txt) c } case EMetaText::SPELL_NAME: { - const auto * spell = SpellID(ser).toSpell(VLC->spells()); + const auto * spell = SpellID(ser).toEntity(VLC); if(spell) return spell->getNameTranslated(); return "#!#"; diff --git a/lib/ResourceSet.cpp b/lib/ResourceSet.cpp index e9fa85243..ab786c7fd 100644 --- a/lib/ResourceSet.cpp +++ b/lib/ResourceSet.cpp @@ -25,19 +25,6 @@ ResourceSet::ResourceSet(const JsonNode & node) container[i] = static_cast(node[GameConstants::RESOURCE_NAMES[i]].Float()); } -ResourceSet::ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal, - TResource gems, TResource gold, TResource mithril) -{ - container[GameResID(EGameResID::WOOD)] = wood; - container[GameResID(EGameResID::MERCURY)] = mercury; - container[GameResID(EGameResID::ORE)] = ore; - container[GameResID(EGameResID::SULFUR)] = sulfur; - container[GameResID(EGameResID::CRYSTAL)] = crystal; - container[GameResID(EGameResID::GEMS)] = gems; - container[GameResID(EGameResID::GOLD)] = gold; - container[GameResID(EGameResID::MITHRIL)] = mithril; -} - void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName) { if(handler.saving && !nonZero()) diff --git a/lib/ResourceSet.h b/lib/ResourceSet.h index 676ee3730..ad844b230 100644 --- a/lib/ResourceSet.h +++ b/lib/ResourceSet.h @@ -26,12 +26,11 @@ class ResourceSet; class ResourceSet { private: - std::array container; + std::array container = {}; public: // read resources set from json. Format example: { "gold": 500, "wood":5 } DLL_LINKAGE ResourceSet(const JsonNode & node); - DLL_LINKAGE ResourceSet(TResource wood = 0, TResource mercury = 0, TResource ore = 0, TResource sulfur = 0, TResource crystal = 0, - TResource gems = 0, TResource gold = 0, TResource mithril = 0); + DLL_LINKAGE ResourceSet() = default; #define scalarOperator(OPSIGN) \ diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 28fbf09ee..2e8412d0a 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1155,7 +1155,7 @@ std::pair CBattleInfoCallback::getNearestStack( BattleHex CBattleInfoCallback::getAvaliableHex(const CreatureID & creID, ui8 side, int initialPos) const { - bool twoHex = VLC->creh->objects[creID]->isDoubleWide(); + bool twoHex = VLC->creatures()->getById(creID)->isDoubleWide(); int pos; if (initialPos > -1) @@ -1663,7 +1663,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c || !(spellID.toSpell()->canBeCast(this, spells::Mode::CREATURE_ACTIVE, subject))) continue; - switch (spellID) + switch (spellID.toEnum()) { case SpellID::SHIELD: case SpellID::FIRE_SHIELD: // not if all enemy units are shooters diff --git a/lib/battle/SideInBattle.cpp b/lib/battle/SideInBattle.cpp index 25fe84127..bfdcafb95 100644 --- a/lib/battle/SideInBattle.cpp +++ b/lib/battle/SideInBattle.cpp @@ -18,7 +18,7 @@ void SideInBattle::init(const CGHeroInstance * Hero, const CArmedInstance * Army hero = Hero; armyObject = Army; - switch(armyObject->ID) + switch(armyObject->ID.toEnum()) { case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR2: diff --git a/lib/battle/Unit.cpp b/lib/battle/Unit.cpp index 7e0c9be2d..1355ba089 100644 --- a/lib/battle/Unit.cpp +++ b/lib/battle/Unit.cpp @@ -218,7 +218,7 @@ int Unit::getRawSurrenderCost() const void UnitInfo::serializeJson(JsonSerializeFormat & handler) { handler.serializeInt("count", count); - handler.serializeId("type", type, CreatureID::NONE); + handler.serializeId("type", type, CreatureID(CreatureID::NONE)); handler.serializeInt("side", side); handler.serializeInt("position", position); handler.serializeBool("summoned", summoned); diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index fb45cbb46..39c0c1c44 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -100,13 +100,13 @@ std::string Bonus::Description(std::optional customValue) const switch(source) { case BonusSource::ARTIFACT: - str << sid.as().toArtifact(VLC->artifacts())->getNameTranslated(); + str << sid.as().toEntity(VLC)->getNameTranslated(); break; case BonusSource::SPELL_EFFECT: - str << sid.as().toSpell(VLC->spells())->getNameTranslated(); + str << sid.as().toEntity(VLC)->getNameTranslated(); break; case BonusSource::CREATURE_ABILITY: - str << sid.as().toCreature(VLC->creatures())->getNamePluralTranslated(); + str << sid.as().toEntity(VLC)->getNamePluralTranslated(); break; case BonusSource::SECONDARY_SKILL: str << VLC->skills()->getById(sid.as())->getNameTranslated(); diff --git a/lib/bonuses/Limiters.cpp b/lib/bonuses/Limiters.cpp index a2461b1e7..b0e031368 100644 --- a/lib/bonuses/Limiters.cpp +++ b/lib/bonuses/Limiters.cpp @@ -114,7 +114,7 @@ CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature & creature_, bool Inc void CCreatureTypeLimiter::setCreature(const CreatureID & id) { - creature = VLC->creh->objects[id]; + creature = id.toCreature(); } std::string CCreatureTypeLimiter::toString() const @@ -317,7 +317,7 @@ ILimiter::EDecision FactionLimiter::limit(const BonusLimitationContext &context) std::string FactionLimiter::toString() const { boost::format fmt("FactionLimiter(faction=%s)"); - fmt % VLC->factions()->getByIndex(faction)->getJsonKey(); + fmt % VLC->factions()->getById(faction)->getJsonKey(); return fmt.str(); } @@ -326,7 +326,7 @@ JsonNode FactionLimiter::toJsonNode() const JsonNode root(JsonNode::JsonType::DATA_STRUCT); root["type"].String() = "FACTION_LIMITER"; - root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getByIndex(faction)->getJsonKey())); + root["parameters"].Vector().push_back(JsonUtils::stringNode(VLC->factions()->getById(faction)->getJsonKey())); return root; } diff --git a/lib/campaign/CampaignState.cpp b/lib/campaign/CampaignState.cpp index 082ddf7ee..d23ffd314 100644 --- a/lib/campaign/CampaignState.cpp +++ b/lib/campaign/CampaignState.cpp @@ -73,13 +73,13 @@ ImagePath CampaignRegions::getBackgroundName() const Point CampaignRegions::getPosition(CampaignScenarioID which) const { - auto const & region = regions[static_cast(which)]; + auto const & region = regions[which.getNum()]; return Point(region.xpos, region.ypos); } ImagePath CampaignRegions::getNameFor(CampaignScenarioID which, int colorIndex, std::string type) const { - auto const & region = regions[static_cast(which)]; + auto const & region = regions[which.getNum()]; static const std::string colors[2][8] = { @@ -267,24 +267,23 @@ void CampaignState::setCurrentMapAsConquered(std::vector heroe return a->getHeroStrength() > b->getHeroStrength(); }); - logGlobal->info("Scenario %d of campaign %s (%s) has been completed", static_cast(*currentMap), getFilename(), getNameTranslated()); + logGlobal->info("Scenario %d of campaign %s (%s) has been completed", currentMap->getNum(), getFilename(), getNameTranslated()); mapsConquered.push_back(*currentMap); auto reservedHeroes = getReservedHeroes(); for (auto * hero : heroes) { - HeroTypeID heroType(hero->subID); JsonNode node = CampaignState::crossoverSerialize(hero); - if (reservedHeroes.count(heroType)) + if (reservedHeroes.count(hero->getHeroType())) { - logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->subID, hero->getNameTranslated()); - globalHeroPool[heroType] = node; + logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->getHeroType(), hero->getNameTranslated()); + globalHeroPool[hero->getHeroType()] = node; } else { - logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->subID, hero->getNameTranslated()); + logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->getHeroType(), hero->getNameTranslated()); scenarioHeroPool[*currentMap].push_back(node); } } @@ -321,7 +320,7 @@ std::unique_ptr CampaignState::getMap(CampaignScenarioID scenarioId) const CMapService mapService; std::string scenarioName = getFilename().substr(0, getFilename().find('.')); boost::to_lower(scenarioName); - scenarioName += ':' + std::to_string(static_cast(scenarioId)); + scenarioName += ':' + std::to_string(scenarioId.getNum()); const auto & mapContent = mapPieces.find(scenarioId)->second; return mapService.loadMap(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding()); } @@ -334,7 +333,7 @@ std::unique_ptr CampaignState::getMapHeader(CampaignScenarioID scena CMapService mapService; std::string scenarioName = getFilename().substr(0, getFilename().find('.')); boost::to_lower(scenarioName); - scenarioName += ':' + std::to_string(static_cast(scenarioId)); + scenarioName += ':' + std::to_string(scenarioId.getNum()); const auto & mapContent = mapPieces.find(scenarioId)->second; return mapService.loadMapHeader(mapContent.data(), mapContent.size(), scenarioName, getModName(), getEncoding()); } diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index 95d56a267..6ad6f37a1 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include "modding/IdentifierStorage.h" #include "modding/ModScope.h" #include "VCMI_Lib.h" +#include "CHeroHandler.h" #include "CArtHandler.h"//todo: remove #include "CCreatureHandler.h"//todo: remove #include "spells/CSpellHandler.h" //todo: remove @@ -191,12 +193,12 @@ std::string HeroTypeID::entityType() const CArtifact * ArtifactIDBase::toArtifact() const { - return dynamic_cast(toArtifact(VLC->artifacts())); + return dynamic_cast(toEntity(VLC)); } -const Artifact * ArtifactIDBase::toArtifact(const ArtifactService * service) const +const Artifact * ArtifactIDBase::toEntity(const Services * services) const { - return service->getByIndex(num); + return services->artifacts()->getByIndex(num); } si32 ArtifactID::decode(const std::string & identifier) @@ -237,7 +239,12 @@ const CCreature * CreatureIDBase::toCreature() const return VLC->creh->objects.at(num); } -const Creature * CreatureIDBase::toCreature(const CreatureService * creatures) const +const Creature * CreatureIDBase::toEntity(const Services * services) const +{ + return toEntity(services->creatures()); +} + +const Creature * CreatureIDBase::toEntity(const CreatureService * creatures) const { return creatures->getByIndex(num); } @@ -271,11 +278,26 @@ const CSpell * SpellIDBase::toSpell() const return VLC->spellh->objects[num]; } -const spells::Spell * SpellIDBase::toSpell(const spells::Service * service) const +const spells::Spell * SpellIDBase::toEntity(const Services * services) const +{ + return toEntity(services->spells()); +} + +const spells::Spell * SpellIDBase::toEntity(const spells::Service * service) const { return service->getByIndex(num); } +const CHero * HeroTypeID::toHeroType() const +{ + return dynamic_cast(toEntity(VLC)); +} + +const HeroType * HeroTypeID::toEntity(const Services * services) const +{ + return services->heroTypes()->getByIndex(num); +} + si32 SpellID::decode(const std::string & identifier) { auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier); @@ -369,7 +391,7 @@ si32 FactionID::decode(const std::string & identifier) if(rawId) return rawId.value(); else - return FactionID::DEFAULT; + return FactionID::DEFAULT.getNum(); } std::string FactionID::encode(const si32 index) @@ -458,6 +480,21 @@ std::string GameResID::entityType() return "resource"; } +const std::array & GameResID::ALL_RESOURCES() +{ + static const std::array allResources = { + GameResID(WOOD), + GameResID(MERCURY), + GameResID(ORE), + GameResID(SULFUR), + GameResID(CRYSTAL), + GameResID(GEMS), + GameResID(GOLD) + }; + + return allResources; +} + std::string SecondarySkill::entityType() { return "secondarySkill"; diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index d074b0f8a..f7aad1cdd 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -14,10 +14,14 @@ VCMI_LIB_NAMESPACE_BEGIN +class Services; class Artifact; class ArtifactService; class Creature; class CreatureService; +class HeroType; +class CHero; +class HeroTypeService; namespace spells { @@ -186,6 +190,9 @@ public: DLL_LINKAGE static std::string encode(const si32 index); static std::string entityType(); + const CHero * toHeroType() const; + const HeroType * toEntity(const Services * services) const; + DLL_LINKAGE static const HeroTypeID NONE; DLL_LINKAGE static const HeroTypeID RANDOM; @@ -401,13 +408,9 @@ class BuildingID : public IdentifierWithEnum { public: using IdentifierWithEnum::IdentifierWithEnum; - - DLL_LINKAGE static si32 decode(const std::string & identifier); - DLL_LINKAGE static std::string encode(const si32 index); - static std::string entityType(); }; -class ObjBase : public IdentifierBase +class MapObjectBaseID : public IdentifierBase { public: enum Type @@ -552,15 +555,38 @@ public: }; }; -class DLL_LINKAGE Obj : public IdentifierWithEnum +class DLL_LINKAGE MapObjectID : public IdentifierWithEnum { public: - using IdentifierWithEnum::IdentifierWithEnum; + using IdentifierWithEnum::IdentifierWithEnum; static std::string encode(int32_t index); static si32 decode(const std::string & identifier); }; +class MapObjectSubID : public Identifier +{ +public: + constexpr MapObjectSubID(const IdentifierBase & value): + Identifier(value.getNum()) + {} + constexpr MapObjectSubID(int32_t value = -1): + Identifier(value) + {} + + MapObjectSubID & operator =(int32_t value) + { + this->num = value; + return *this; + } + + MapObjectSubID & operator =(const IdentifierBase & value) + { + this->num = value.getNum(); + return *this; + } +}; + class DLL_LINKAGE RoadId : public Identifier { public: @@ -607,20 +633,23 @@ public: TRANSITION_POS = -3, FIRST_AVAILABLE = -2, PRE_FIRST = -1, //sometimes used as error, sometimes as first free in backpack + + // Hero HEAD, SHOULDERS, NECK, RIGHT_HAND, LEFT_HAND, TORSO, //5 RIGHT_RING, LEFT_RING, FEET, //8 MISC1, MISC2, MISC3, MISC4, //12 MACH1, MACH2, MACH3, MACH4, //16 SPELLBOOK, MISC5, //18 - AFTER_LAST, - //cres + BACKPACK_START = 19, + + // Creatures CREATURE_SLOT = 0, - COMMANDER1 = 0, COMMANDER2, COMMANDER3, COMMANDER4, COMMANDER5, COMMANDER6, COMMANDER_AFTER_LAST, - - BACKPACK_START = 19 + + // Commander + COMMANDER1 = 0, COMMANDER2, COMMANDER3, COMMANDER4, COMMANDER5, COMMANDER6 }; - static_assert (AFTER_LAST == BACKPACK_START, "incorrect number of artifact slots"); + static_assert(MISC5 < BACKPACK_START, "incorrect number of artifact slots"); DLL_LINKAGE static si32 decode(const std::string & identifier); DLL_LINKAGE static std::string encode(const si32 index); @@ -653,7 +682,7 @@ public: }; DLL_LINKAGE const CArtifact * toArtifact() const; - DLL_LINKAGE const Artifact * toArtifact(const ArtifactService * service) const; + DLL_LINKAGE const Artifact * toEntity(const Services * service) const; }; class ArtifactID : public IdentifierWithEnum @@ -693,7 +722,8 @@ public: }; DLL_LINKAGE const CCreature * toCreature() const; - DLL_LINKAGE const Creature * toCreature(const CreatureService * creatures) const; + DLL_LINKAGE const Creature * toEntity(const Services * services) const; + DLL_LINKAGE const Creature * toEntity(const CreatureService * creatures) const; }; class DLL_LINKAGE CreatureID : public IdentifierWithEnum @@ -811,7 +841,8 @@ public: }; const CSpell * toSpell() const; //deprecated - const spells::Spell * toSpell(const spells::Service * service) const; + const spells::Spell * toEntity(const Services * service) const; + const spells::Spell * toEntity(const spells::Service * service) const; }; class DLL_LINKAGE SpellID : public IdentifierWithEnum @@ -934,9 +965,11 @@ public: static si32 decode(const std::string & identifier); static std::string encode(const si32 index); static std::string entityType(); + + static const std::array & ALL_RESOURCES(); }; -class BuildingTypeUniqueID : public Identifier +class DLL_LINKAGE BuildingTypeUniqueID : public Identifier { public: BuildingTypeUniqueID(FactionID faction, BuildingID building ); @@ -963,6 +996,7 @@ public: // Deprecated // TODO: remove +using Obj = MapObjectID; using ETownType = FactionID; using EGameResID = GameResID; using River = RiverId; diff --git a/lib/constants/VariantIdentifier.h b/lib/constants/VariantIdentifier.h index 6b0394f65..8ea6a6096 100644 --- a/lib/constants/VariantIdentifier.h +++ b/lib/constants/VariantIdentifier.h @@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN /// This class represents field that may contain value of multiple different identifer types template -class DLL_LINKAGE VariantIdentifier +class VariantIdentifier { std::variant value; public: diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index a73a2391a..dac105832 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -77,34 +77,6 @@ public: } }; -static CGObjectInstance * createObject(const Obj & id, int subid, const int3 & pos, const PlayerColor & owner) -{ - CGObjectInstance * nobj; - switch(id) - { - case Obj::HERO: - { - auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->objects[subid]->heroClass->getIndex()); - nobj = handler->create(handler->getTemplates().front()); - break; - } - case Obj::TOWN: - nobj = new CGTownInstance(); - break; - default: //rest of objects - nobj = new CGObjectInstance(); - break; - } - nobj->ID = id; - nobj->subID = subid; - nobj->pos = pos; - nobj->tempOwner = owner; - if (id != Obj::HERO) - nobj->appearance = VLC->objtypeh->getHandlerFor(id, subid)->getTemplates().front(); - - return nobj; -} - HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner) { const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner); @@ -153,223 +125,6 @@ HeroTypeID CGameState::pickUnusedHeroTypeRandomly(const PlayerColor & owner) return HeroTypeID::NONE; // no available heroes at all } -std::pair CGameState::pickObject (CGObjectInstance *obj) -{ - switch(obj->ID) - { - case Obj::RANDOM_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC)); - case Obj::RANDOM_TREASURE_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE)); - case Obj::RANDOM_MINOR_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MINOR)); - case Obj::RANDOM_MAJOR_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MAJOR)); - case Obj::RANDOM_RELIC_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_RELIC)); - case Obj::RANDOM_HERO: - return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner)); - case Obj::RANDOM_MONSTER: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator())); - case Obj::RANDOM_MONSTER_L1: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 1)); - case Obj::RANDOM_MONSTER_L2: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 2)); - case Obj::RANDOM_MONSTER_L3: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 3)); - case Obj::RANDOM_MONSTER_L4: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 4)); - case Obj::RANDOM_RESOURCE: - return std::make_pair(Obj::RESOURCE,getRandomGenerator().nextInt(6)); //now it's OH3 style, use %8 for mithril - case Obj::RANDOM_TOWN: - { - PlayerColor align = (dynamic_cast(obj))->alignmentToPlayer; - si32 f; // can be negative (for random) - if(!align.isValidPlayer()) //same as owner / random - { - if(!obj->tempOwner.isValidPlayer()) - f = -1; //random - else - f = scenarioOps->getIthPlayersSettings(obj->tempOwner).castle; - } - else - { - f = scenarioOps->getIthPlayersSettings(align).castle; - } - if(f<0) - { - do - { - f = getRandomGenerator().nextInt((int)VLC->townh->size() - 1); - } - while ((*VLC->townh)[f]->town == nullptr); // find playable faction - } - return std::make_pair(Obj::TOWN,f); - } - case Obj::RANDOM_MONSTER_L5: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 5)); - case Obj::RANDOM_MONSTER_L6: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 6)); - case Obj::RANDOM_MONSTER_L7: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 7)); - case Obj::RANDOM_DWELLING: - case Obj::RANDOM_DWELLING_LVL: - case Obj::RANDOM_DWELLING_FACTION: - { - auto * dwl = dynamic_cast(obj); - int faction; - - //if castle alignment available - if(auto * info = dynamic_cast(dwl->info)) - { - faction = getRandomGenerator().nextInt((int)VLC->townh->size() - 1); - if(info->asCastle && !info->instanceId.empty()) - { - auto iter = map->instanceNames.find(info->instanceId); - - if(iter == map->instanceNames.end()) - logGlobal->error("Map object not found: %s", info->instanceId); - else - { - auto elem = iter->second; - if(elem->ID==Obj::RANDOM_TOWN) - { - randomizeObject(elem.get()); //we have to randomize the castle first - faction = elem->subID; - } - else if(elem->ID==Obj::TOWN) - faction = elem->subID; - else - logGlobal->error("Map object must be town: %s", info->instanceId); - } - } - else if(info->asCastle) - { - - for(auto & elem : map->objects) - { - if(!elem) - continue; - - if(elem->ID==Obj::RANDOM_TOWN - && dynamic_cast(elem.get())->identifier == info->identifier) - { - randomizeObject(elem); //we have to randomize the castle first - faction = elem->subID; - break; - } - else if(elem->ID==Obj::TOWN - && dynamic_cast(elem.get())->identifier == info->identifier) - { - faction = elem->subID; - break; - } - } - } - else - { - std::set temp; - - for(int i = 0; i < info->allowedFactions.size(); i++) - if(info->allowedFactions[i]) - temp.insert(i); - - if(temp.empty()) - logGlobal->error("Random faction selection failed. Nothing is allowed. Fall back to random."); - else - faction = *RandomGeneratorUtil::nextItem(temp, getRandomGenerator()); - } - } - else // castle alignment fixed - faction = obj->subID; - - int level; - - //if level set to range - if(auto * info = dynamic_cast(dwl->info)) - { - level = getRandomGenerator().nextInt(info->minLevel, info->maxLevel) - 1; - } - else // fixed level - { - level = obj->subID; - } - - delete dwl->info; - dwl->info = nullptr; - - std::pair result(Obj::NO_OBJ, -1); - CreatureID cid; - if((*VLC->townh)[faction]->town) - cid = (*VLC->townh)[faction]->town->creatures[level][0]; - else - { - //neutral faction - std::vector possibleCreatures; - std::copy_if(VLC->creh->objects.begin(), VLC->creh->objects.end(), std::back_inserter(possibleCreatures), [faction](const CCreature * c) - { - return c->getFaction().getNum() == faction; - }); - assert(!possibleCreatures.empty()); - cid = (*RandomGeneratorUtil::nextItem(possibleCreatures, getRandomGenerator()))->getId(); - } - - //NOTE: this will pick last dwelling with this creature (Mantis #900) - //check for block map equality is better but more complex solution - auto testID = [&](const Obj & primaryID) -> void - { - auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID); - for (si32 entry : dwellingIDs) - { - const auto * handler = dynamic_cast(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); - - if (handler->producesCreature(VLC->creh->objects[cid])) - result = std::make_pair(primaryID, entry); - } - }; - - testID(Obj::CREATURE_GENERATOR1); - if (result.first == Obj::NO_OBJ) - testID(Obj::CREATURE_GENERATOR4); - - if (result.first == Obj::NO_OBJ) - { - logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level)); - result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator())); - } - - return result; - } - } - return std::make_pair(Obj::NO_OBJ,-1); -} - -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 || cur->ID==Obj::MONSTER) - cur->setType(cur->ID, cur->subID); // update def, if necessary - } - else if(ran.first==Obj::HERO)//special code for hero - { - auto * h = dynamic_cast(cur); - cur->setType(ran.first, ran.second); - map->heroesOnMap.emplace_back(h); - } - else if(ran.first==Obj::TOWN)//special code for town - { - auto * t = dynamic_cast(cur); - cur->setType(ran.first, ran.second); - map->towns.emplace_back(t); - } - else - { - cur->setType(ran.first, ran.second); - } -} - int CGameState::getDate(Date mode) const { int temp; @@ -409,6 +164,8 @@ CGameState::CGameState() CGameState::~CGameState() { + // explicitly delete all ongoing battles first - BattleInfo destructor requires valid CGameState + currentBattles.clear(); map.dellNull(); } @@ -469,7 +226,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog // Explicitly initialize static variables for(auto & elem : players) { - CGKeys::playerKeyMap[elem.first] = std::set(); + CGKeys::playerKeyMap[elem.first] = std::set(); } for(auto & elem : teams) { @@ -762,20 +519,21 @@ void CGameState::initRandomFactionsForPlayers() void CGameState::randomizeMapObjects() { logGlobal->debug("\tRandomizing objects"); - for(CGObjectInstance *obj : map->objects) + for(CGObjectInstance *object : map->objects) { - if(!obj) continue; + if(!object) + continue; - randomizeObject(obj); + object->pickRandomObject(getRandomGenerator()); //handle Favouring Winds - mark tiles under it - if(obj->ID == Obj::FAVORABLE_WINDS) + if(object->ID == Obj::FAVORABLE_WINDS) { - for (int i = 0; i < obj->getWidth() ; i++) + for (int i = 0; i < object->getWidth() ; i++) { - for (int j = 0; j < obj->getHeight() ; j++) + for (int j = 0; j < object->getHeight() ; j++) { - int3 pos = obj->pos - int3(i,j,0); + int3 pos = object->pos - int3(i,j,0); if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128; } } @@ -808,7 +566,15 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy } } - CGObjectInstance * hero = createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor); + auto handler = VLC->objtypeh->getHandlerFor(Obj::HERO, heroTypeId.toHeroType()->heroClass->getIndex()); + CGObjectInstance * obj = handler->create(handler->getTemplates().front()); + CGHeroInstance * hero = dynamic_cast(obj); + + hero->ID = Obj::HERO; + hero->setHeroType(heroTypeId); + hero->tempOwner = playerColor; + + hero->pos = townPos; hero->pos += hero->getVisitableOffset(); map->getEditManager()->insertObject(hero); } @@ -864,7 +630,7 @@ void CGameState::initHeroes() hero->initHero(getRandomGenerator()); getPlayerState(hero->getOwner())->heroes.push_back(hero); - map->allHeroes[hero->type->getIndex()] = hero; + map->allHeroes[hero->getHeroType().getNum()] = hero; } // generate boats for all heroes on water @@ -878,8 +644,6 @@ void CGameState::initHeroes() CGBoat * boat = dynamic_cast(handler->create()); handler->configureObject(boat, gs->getRandomGenerator()); - boat->ID = Obj::BOAT; - boat->subID = hero->getBoatType().getNum(); boat->pos = hero->pos; boat->appearance = handler->getTemplates().front(); boat->id = ObjectInstanceID(static_cast(gs->map->objects.size())); @@ -894,19 +658,23 @@ void CGameState::initHeroes() for(auto obj : map->objects) //prisons { if(obj && obj->ID == Obj::PRISON) - map->allHeroes[obj->subID] = dynamic_cast(obj.get()); + { + auto * hero = dynamic_cast(obj.get()); + hero->initHero(getRandomGenerator()); + map->allHeroes[hero->getHeroType().getNum()] = hero; + } } std::set heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool for(auto ph : map->predefinedHeroes) { - if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID))) + if(!vstd::contains(heroesToCreate, ph->getHeroType())) continue; ph->initHero(getRandomGenerator()); heroesPool->addHeroToPool(ph); heroesToCreate.erase(ph->type->getId()); - map->allHeroes[ph->subID] = ph; + map->allHeroes[ph->getHeroType().getNum()] = ph; } for(const HeroTypeID & htype : heroesToCreate) //all not used allowed heroes go with default state into the pool @@ -992,7 +760,7 @@ void CGameState::initStartingBonus() logGlobal->error("Cannot give starting artifact - no heroes!"); break; } - const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toArtifact(VLC->artifacts()); + const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC); CGHeroInstance *hero = elem.second.heroes[0]; if(!giveHeroArtifact(hero, toGive->getId())) @@ -1017,10 +785,8 @@ void CGameState::initTowns() for (auto & elem : map->towns) { CGTownInstance * vti =(elem); - if(!vti->town) - { - vti->town = (*VLC->townh)[vti->subID]->town; - } + assert(vti->town); + if(vti->getNameTranslated().empty()) { size_t nameID = getRandomGenerator().nextInt(vti->getTown()->getRandomNamesCount() - 1); @@ -1165,7 +931,7 @@ void CGameState::initMapObjects() if(!obj) continue; - switch (obj->ID) + switch(obj->ID.toEnum()) { case Obj::QUEST_GUARD: case Obj::SEER_HUT: @@ -2103,7 +1869,7 @@ bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid) auto slot = ArtifactUtils::getArtAnyPosition(h, aid); if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot)) { - ai->putAt(ArtifactLocation(h, slot)); + ai->putAt(*h, slot); return true; } else @@ -2130,12 +1896,15 @@ std::set CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow if(hero->type) ret -= hero->type->getId(); else - ret -= HeroTypeID(hero->subID); + ret -= hero->getHeroType(); } for(auto obj : map->objects) //prisons - if(obj && obj->ID == Obj::PRISON) - ret -= HeroTypeID(obj->subID); + { + auto * hero = dynamic_cast(obj.get()); + if(hero && hero->ID == Obj::PRISON) + ret -= hero->getHeroType(); + } return ret; } @@ -2147,23 +1916,19 @@ bool CGameState::isUsedHero(const HeroTypeID & hid) const CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const { - for(auto hero : map->heroesOnMap) //heroes instances initialization - { - if(hero->type && hero->type->getId() == hid) - { - return hero; - } - } - for(auto obj : map->objects) //prisons { - if(obj && obj->ID == Obj::PRISON ) - { - auto * hero = dynamic_cast(obj.get()); - assert(hero); - if ( hero->type && hero->type->getId() == hid ) - return hero; - } + if (!obj) + continue; + + if ( obj->ID !=Obj::PRISON && obj->ID != Obj::HERO) + continue; + + auto * hero = dynamic_cast(obj.get()); + assert(hero); + + if (hero->getHeroType() == hid) + return hero; } return nullptr; diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index 94db3c60e..7b613c05d 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -115,6 +115,8 @@ public: void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; bool giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid); + /// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly + HeroTypeID pickNextHeroType(const PlayerColor & owner); void apply(CPack *pack); BattleField battleGetBattlefieldType(int3 tile, CRandomGenerator & rand); @@ -187,7 +189,6 @@ private: void initGrailPosition(); void initRandomFactionsForPlayers(); void randomizeMapObjects(); - void randomizeObject(CGObjectInstance *cur); void initPlayerStates(); void placeStartingHeroes(); void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos); @@ -214,9 +215,7 @@ private: CGHeroInstance * getUsedHero(const HeroTypeID & hid) const; bool isUsedHero(const HeroTypeID & hid) const; //looks in heroes and prisons std::set getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const; - std::pair pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns HeroTypeID pickUnusedHeroTypeRandomly(const PlayerColor & owner); // picks a unused hero type randomly - HeroTypeID pickNextHeroType(const PlayerColor & owner); // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly UpgradeInfo fillUpgradeInfo(const CStackInstance &stack) const; // ---- data ----- diff --git a/lib/gameState/CGameStateCampaign.cpp b/lib/gameState/CGameStateCampaign.cpp index 462204cb7..076403a31 100644 --- a/lib/gameState/CGameStateCampaign.cpp +++ b/lib/gameState/CGameStateCampaign.cpp @@ -90,7 +90,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vectorgetBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g]; + cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g.getNum()]; } } } @@ -134,9 +134,9 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(std::vectorartType->getId()); - ArtifactLocation al(hero, artifactPosition); - if(!takeable && !al.getSlot()->locked) //don't try removing locked artifacts -> it crashes #1719 - al.removeArtifact(); + ArtifactLocation al(hero->id, artifactPosition); + if(!takeable && !hero->getSlot(al.slot)->locked) //don't try removing locked artifacts -> it crashes #1719 + hero->getArt(al.slot)->removeFrom(*hero, al.slot); }; // process on copy - removal of artifact will invalidate container @@ -213,7 +213,7 @@ void CGameStateCampaign::placeCampaignHeroes() std::set heroesToRemove = campaignState->getReservedHeroes(); for(auto & campaignHeroReplacement : campaignHeroReplacements) - heroesToRemove.insert(HeroTypeID(campaignHeroReplacement.hero->subID)); + heroesToRemove.insert(campaignHeroReplacement.hero->getHeroType()); for(auto & heroID : heroesToRemove) { @@ -256,7 +256,7 @@ void CGameStateCampaign::placeCampaignHeroes() assert(0); // should not happen } - hero->subID = heroTypeId; + hero->setHeroType(heroTypeId); gameState->map->getEditManager()->insertObject(hero); } } @@ -300,7 +300,7 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero) CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2)); const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId()); if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot)) - scroll->putAt(ArtifactLocation(hero, slot)); + scroll->putAt(*hero, slot); else logGlobal->error("Cannot give starting scroll - no free slots!"); break; @@ -339,7 +339,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders(const std::vectortempOwner.isValidPlayer()) heroToPlace->tempOwner = heroPlaceholder->tempOwner; heroToPlace->pos = heroPlaceholder->pos; - heroToPlace->type = VLC->heroh->objects[heroToPlace->subID]; + heroToPlace->type = VLC->heroh->objects[heroToPlace->getHeroType().getNum()]; heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->type->heroClass->getIndex())->getTemplates().front(); gameState->map->removeBlockVisTiles(heroPlaceholder, true); @@ -375,7 +375,7 @@ std::vector CGameStateCampaign::generateCampaignHeroesT auto * heroPlaceholder = dynamic_cast(obj.get()); // only 1 field must be set - assert(heroPlaceholder->powerRank != heroPlaceholder->heroType); + assert(heroPlaceholder->powerRank.has_value() != heroPlaceholder->heroType.has_value()); if(heroPlaceholder->powerRank) placeholdersByPower.push_back(heroPlaceholder); @@ -396,7 +396,7 @@ std::vector CGameStateCampaign::generateCampaignHeroesT CGHeroInstance * hero = CampaignState::crossoverDeserialize(node, gameState->map); - logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->subID, hero->getNameTranslated()); + logGlobal->info("Hero crossover: Loading placeholder for %d (%s)", hero->getHeroType(), hero->getNameTranslated()); campaignHeroReplacements.emplace_back(hero, placeholder->id); } @@ -422,7 +422,7 @@ std::vector CGameStateCampaign::generateCampaignHeroesT CGHeroInstance * hero = CampaignState::crossoverDeserialize(*nodeListIter, gameState->map); nodeListIter++; - logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->subID, hero->getNameTranslated()); + logGlobal->info("Hero crossover: Loading placeholder as %d (%s)", hero->getHeroType(), hero->getNameTranslated()); campaignHeroReplacements.emplace_back(hero, placeholder->id); } @@ -468,7 +468,7 @@ void CGameStateCampaign::initHeroes() { for (auto & heroe : heroes) { - if (heroe->subID == chosenBonus->info1) + if (heroe->getHeroType().getNum() == chosenBonus->info1) { giveCampaignBonusToHero(heroe); break; @@ -498,7 +498,7 @@ void CGameStateCampaign::initStartingResources() std::vector people = getHumanPlayerInfo(); //players we will give resource bonus for(const PlayerSettings *ps : people) { - std::vector res; //resources we will give + std::vector res; //resources we will give switch (chosenBonus->info1) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: @@ -557,7 +557,7 @@ void CGameStateCampaign::initTowns() if(gameState->scenarioOps->campState->formatVCMI()) newBuilding = BuildingID(chosenBonus->info1); else - newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->subID, town->builtBuildings); + newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->builtBuildings); // Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2 while(true) diff --git a/lib/gameState/TavernHeroesPool.cpp b/lib/gameState/TavernHeroesPool.cpp index 4dabd8ab3..00aec3a73 100644 --- a/lib/gameState/TavernHeroesPool.cpp +++ b/lib/gameState/TavernHeroesPool.cpp @@ -25,7 +25,7 @@ std::map TavernHeroesPool::unusedHeroesFromPool() c { std::map pool = heroesPool; for(const auto & slot : currentTavern) - pool.erase(HeroTypeID(slot.hero->subID)); + pool.erase(slot.hero->getHeroType()); return pool; } @@ -34,7 +34,7 @@ TavernSlotRole TavernHeroesPool::getSlotRole(HeroTypeID hero) const { for (auto const & slot : currentTavern) { - if (HeroTypeID(slot.hero->subID) == hero) + if (slot.hero->getHeroType() == hero) return slot.role; } return TavernSlotRole::NONE; @@ -132,7 +132,7 @@ void TavernHeroesPool::onNewDay() void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero) { - heroesPool[HeroTypeID(hero->subID)] = hero; + heroesPool[hero->getHeroType()] = hero; } void TavernHeroesPool::setAvailability(HeroTypeID hero, std::set mask) diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 9d62b028a..1ef76010c 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -285,10 +285,9 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons VLC->identifiersHandler->registerObject(scope, "object", name, object->id); } -void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID) +void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID) { config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL - assert(ID < objects.size()); assert(objects[ID]); if ( subID >= objects[ID]->objects.size()) @@ -298,9 +297,8 @@ void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNo loadSubObject(config.meta, identifier, config, objects[ID], subID); } -void CObjectClassesHandler::removeSubObject(si32 ID, si32 subID) +void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID) { - assert(ID < objects.size()); assert(objects[ID]); assert(subID < objects[ID]->objects.size()); objects[ID]->objects[subID] = nullptr; @@ -311,7 +309,7 @@ std::vector CObjectClassesHandler::getDefaultAllowed() const return std::vector(); //TODO? } -TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const +TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObjectSubID subtype) const { try { @@ -352,9 +350,9 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(CompoundMapObjectID comp return getHandlerFor(compoundIdentifier.primaryID, compoundIdentifier.secondaryID); } -std::set CObjectClassesHandler::knownObjects() const +std::set CObjectClassesHandler::knownObjects() const { - std::set ret; + std::set ret; for(auto * entry : objects) if (entry) @@ -363,9 +361,9 @@ std::set CObjectClassesHandler::knownObjects() const return ret; } -std::set CObjectClassesHandler::knownSubObjects(si32 primaryID) const +std::set CObjectClassesHandler::knownSubObjects(MapObjectID primaryID) const { - std::set ret; + std::set ret; if (!objects.at(primaryID)) { @@ -461,7 +459,7 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG() } } -std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const +std::string CObjectClassesHandler::getObjectName(MapObjectID type, MapObjectSubID subtype) const { const auto handler = getHandlerFor(type, subtype); if (handler && handler->hasNameTextID()) @@ -470,7 +468,7 @@ std::string CObjectClassesHandler::getObjectName(si32 type, si32 subtype) const return objects[type]->getNameTranslated(); } -SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) const +SObjectSounds CObjectClassesHandler::getObjectSounds(MapObjectID type, MapObjectSubID subtype) const { // TODO: these objects may have subID's that does not have associated handler: // Prison: uses hero type as subID @@ -479,19 +477,18 @@ SObjectSounds CObjectClassesHandler::getObjectSounds(si32 type, si32 subtype) co if(type == Obj::PRISON || type == Obj::HERO || type == Obj::SPELL_SCROLL) subtype = 0; - assert(type < objects.size()); assert(objects[type]); assert(subtype < objects[type]->objects.size()); return getHandlerFor(type, subtype)->getSounds(); } -std::string CObjectClassesHandler::getObjectHandlerName(si32 type) const +std::string CObjectClassesHandler::getObjectHandlerName(MapObjectID type) const { return objects.at(type)->handlerName; } -std::string CObjectClassesHandler::getJsonKey(si32 type) const +std::string CObjectClassesHandler::getJsonKey(MapObjectID type) const { return objects.at(type)->getJsonKey(); } diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.h b/lib/mapObjectConstructors/CObjectClassesHandler.h index f75f9c434..000b50703 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.h +++ b/lib/mapObjectConstructors/CObjectClassesHandler.h @@ -105,8 +105,8 @@ public: void loadObject(std::string scope, std::string name, const JsonNode & data) override; void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; - void loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID); - void removeSubObject(si32 ID, si32 subID); + void loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID); + void removeSubObject(MapObjectID ID, MapObjectSubID subID); void beforeValidate(JsonNode & object) override; void afterLoadFinalization() override; @@ -114,22 +114,22 @@ public: std::vector getDefaultAllowed() const override; /// Queries to detect loaded objects - std::set knownObjects() const; - std::set knownSubObjects(si32 primaryID) const; + std::set knownObjects() const; + std::set knownSubObjects(MapObjectID primaryID) const; /// returns handler for specified object (ID-based). ObjectHandler keeps ownership - TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const; + TObjectTypeHandler getHandlerFor(MapObjectID type, MapObjectSubID subtype) const; TObjectTypeHandler getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const; TObjectTypeHandler getHandlerFor(CompoundMapObjectID compoundIdentifier) const; - std::string getObjectName(si32 type, si32 subtype) const; + std::string getObjectName(MapObjectID type, MapObjectSubID subtype) const; - SObjectSounds getObjectSounds(si32 type, si32 subtype) const; + SObjectSounds getObjectSounds(MapObjectID type, MapObjectSubID subtype) const; /// Returns handler string describing the handler (for use in client) - std::string getObjectHandlerName(si32 type) const; + std::string getObjectHandlerName(MapObjectID type) const; - std::string getJsonKey(si32 type) const; + std::string getJsonKey(MapObjectID type) const; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/CArmedInstance.cpp index 39f7ed971..baf878870 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/CArmedInstance.cpp @@ -73,7 +73,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(const auto & slot : Slots()) { const CStackInstance * inst = slot.second; - const CCreature * creature = VLC->creh->objects[inst->getCreatureID()]; + const auto * creature = inst->getCreatureID().toEntity(VLC); factions.insert(creature->getFaction()); // Check for undead flag instead of faction (undead mummies are neutral) @@ -92,7 +92,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(auto f : factions) { - if (VLC->factions()->getByIndex(f)->getAlignment() != EAlignment::EVIL) + if (VLC->factions()->getById(f)->getAlignment() != EAlignment::EVIL) mixableFactions++; } if (mixableFactions > 0) diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 53f219939..ae8fc22d0 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -46,7 +46,7 @@ void CBank::initObj(CRandomGenerator & rand) { daycounter = 0; resetDuration = 0; - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); + getObjectHandler()->configureObject(this, rand); } bool CBank::isCoastVisitable() const @@ -79,11 +79,7 @@ std::vector CBank::getPopupComponents(PlayerColor player) const for (auto const & guard : guardsAmounts) { - Component comp; - comp.id = Component::EComponentType::CREATURE; - comp.subtype = guard.first.getNum(); - comp.val = guard.second; - + Component comp(ComponentType::CREATURE, guard.first, guard.second); result.push_back(comp); } return result; @@ -141,7 +137,7 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const cb->sendAndApply(&cov); int banktext = 0; - switch (ID) + switch (ID.toEnum()) { case Obj::DERELICT_SHIP: banktext = 41; @@ -184,7 +180,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const if (bc) { - switch (ID) + switch (ID.toEnum()) { case Obj::DERELICT_SHIP: textID = 43; @@ -207,7 +203,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const } else { - switch (ID) + switch (ID.toEnum()) { case Obj::SHIPWRECK: case Obj::DERELICT_SHIP: @@ -220,7 +216,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const gbonus.bonus.sid = BonusSourceID(ID); gbonus.bonus.type = BonusType::MORALE; gbonus.bonus.val = -1; - switch (ID) + switch (ID.toEnum()) { case Obj::SHIPWRECK: textID = 123; @@ -236,7 +232,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const break; } cb->giveHeroBonus(&gbonus); - iw.components.emplace_back(Component::EComponentType::MORALE, 0, -1, 0); + iw.components.emplace_back(ComponentType::MORALE, -1); iw.soundID = soundBase::GRAVEYARD; break; } @@ -247,7 +243,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const gb.id = hero->id.getNum(); cb->giveHeroBonus(&gb); textID = 107; - iw.components.emplace_back(Component::EComponentType::LUCK, 0, -2, 0); + iw.components.emplace_back(ComponentType::LUCK, -2); break; } case Obj::CREATURE_BANK: @@ -267,24 +263,24 @@ void CBank::doVisit(const CGHeroInstance * hero) const //grant resources if (bc) { - for (int it = 0; it < bc->resources.size(); it++) + for (GameResID it : GameResID::ALL_RESOURCES()) { if (bc->resources[it] != 0) { - iw.components.emplace_back(Component::EComponentType::RESOURCE, it, bc->resources[it], 0); + iw.components.emplace_back(ComponentType::RESOURCE, it, bc->resources[it]); loot.appendRawString("%d %s"); - loot.replaceNumber(iw.components.back().val); - loot.replaceLocalString(EMetaText::RES_NAMES, iw.components.back().subtype); - cb->giveResource(hero->getOwner(), static_cast(it), bc->resources[it]); + loot.replaceNumber(bc->resources[it]); + loot.replaceLocalString(EMetaText::RES_NAMES, it); + cb->giveResource(hero->getOwner(), it, bc->resources[it]); } } //grant artifacts for (auto & elem : bc->artifacts) { - iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); + iw.components.emplace_back(ComponentType::ARTIFACT, elem); loot.appendRawString("%s"); loot.replaceLocalString(EMetaText::ART_NAMES, elem); - cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE); + cb->giveHeroNewArtifact(hero, elem.toArtifact(), ArtifactPosition::FIRST_AVAILABLE); } //display loot if (!iw.components.empty()) @@ -318,14 +314,14 @@ void CBank::doVisit(const CGHeroInstance * hero) const } for(const SpellID & spellId : bc->spells) { - const auto * spell = spellId.toSpell(VLC->spells()); + const auto * spell = spellId.toEntity(VLC); iw.text.appendLocalString(EMetaText::SPELL_NAME, spellId); if(spell->getLevel() <= hero->maxSpellLevel()) { if(hero->canLearnSpell(spell)) { spells.insert(spellId); - iw.components.emplace_back(Component::EComponentType::SPELL, spellId, 0, 0); + iw.components.emplace_back(ComponentType::SPELL, spellId); } } else @@ -356,7 +352,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const for(const auto & elem : ourArmy.Slots()) { - iw.components.emplace_back(*elem.second); + iw.components.emplace_back(ComponentType::CREATURE, elem.second->getId(), elem.second->getCount()); loot.appendRawString("%s"); loot.replaceCreatureName(*elem.second); } diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index b0c9c8384..04af6f8ad 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -28,7 +28,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const if(stacks.empty()) { //should not happen... - logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum()); + logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), getCreature(), id.getNum()); return "INVALID_STACK"; } @@ -41,7 +41,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const else ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex); ms.appendRawString(" "); - ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID); + ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature()); hoverName = ms.toString(); return hoverName; } @@ -54,7 +54,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const MetaString ms; ms.appendNumber(stacks.begin()->second->count); ms.appendRawString(" "); - ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID); + ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature()); ms.appendRawString("\n\n"); @@ -132,7 +132,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const BlockingDialog ynd(true,false); ynd.player = h->tempOwner; ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86); - ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID); + ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature()); cb->showBlockingDialog(&ynd); break; } @@ -146,13 +146,50 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const std::string tmp = VLC->generaltexth->advobtxt[90]; boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0)))); boost::algorithm::replace_first(tmp, "%d", std::to_string(action)); - boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated()); + boost::algorithm::replace_first(tmp,"%s",VLC->creatures()->getById(getCreature())->getNamePluralTranslated()); ynd.text.appendRawString(tmp); cb->showBlockingDialog(&ynd); break; } } +} +CreatureID CGCreature::getCreature() const +{ + return CreatureID(getObjTypeIndex().getNum()); +} + +void CGCreature::pickRandomObject(CRandomGenerator & rand) +{ + switch(ID.toEnum()) + { + case MapObjectID::RANDOM_MONSTER: + subID = VLC->creh->pickRandomMonster(rand); + break; + case MapObjectID::RANDOM_MONSTER_L1: + subID = VLC->creh->pickRandomMonster(rand, 1); + break; + case MapObjectID::RANDOM_MONSTER_L2: + subID = VLC->creh->pickRandomMonster(rand, 2); + break; + case MapObjectID::RANDOM_MONSTER_L3: + subID = VLC->creh->pickRandomMonster(rand, 3); + break; + case MapObjectID::RANDOM_MONSTER_L4: + subID = VLC->creh->pickRandomMonster(rand, 4); + break; + case MapObjectID::RANDOM_MONSTER_L5: + subID = VLC->creh->pickRandomMonster(rand, 5); + break; + case MapObjectID::RANDOM_MONSTER_L6: + subID = VLC->creh->pickRandomMonster(rand, 6); + break; + case MapObjectID::RANDOM_MONSTER_L7: + subID = VLC->creh->pickRandomMonster(rand, 7); + break; + } + ID = MapObjectID::MONSTER; + setType(ID, subID); } void CGCreature::initObj(CRandomGenerator & rand) @@ -177,16 +214,16 @@ void CGCreature::initObj(CRandomGenerator & rand) break; } - stacks[SlotID(0)]->setType(CreatureID(subID)); + stacks[SlotID(0)]->setType(getCreature()); TQuantity &amount = stacks[SlotID(0)]->count; - CCreature &c = *VLC->creh->objects[subID]; + const Creature * c = VLC->creatures()->getById(getCreature()); if(amount == 0) { - amount = rand.nextInt(c.ammMin, c.ammMax); + amount = rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax()); if(amount == 0) //armies with 0 creatures are illegal { - logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName()); + logGlobal->warn("Stack cannot have 0 creatures. Check properties of %s", c->getJsonKey()); amount = 1; } } @@ -252,7 +289,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const powerFactor = -3; std::set myKindCres; //what creatures are the same kind as we - const CCreature * myCreature = VLC->creh->objects[subID]; + const CCreature * myCreature = VLC->creh->objects[getCreature().getNum()]; myKindCres.insert(myCreature->getId()); //we myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades @@ -290,7 +327,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const return JOIN_FOR_FREE; else if(diplomacy * 2 + sympathy + 1 >= character) - return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold + return VLC->creatures()->getById(getCreature())->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold } //we are still here - creatures have not joined hero, flee or fight @@ -390,7 +427,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const if(!upgrades.empty()) { auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault()); - cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]); + cb->changeStackType(StackLocation(this, slotID), it->toCreature()); } } } @@ -404,7 +441,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const BlockingDialog ynd(true,false); ynd.player = h->tempOwner; ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91); - ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID); + ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature()); cb->showBlockingDialog(&ynd); } @@ -526,17 +563,17 @@ void CGCreature::giveReward(const CGHeroInstance * h) const if(!resources.empty()) { cb->giveResources(h->tempOwner, resources); - for(int i = 0; i < resources.size(); i++) + for(auto const & res : GameResID::ALL_RESOURCES()) { - if(resources[i] > 0) - iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0); + if(resources[res] > 0) + iw.components.emplace_back(ComponentType::RESOURCE, res, resources[res]); } } if(gainedArtifact != ArtifactID::NONE) { - cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE); - iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0); + cb->giveHeroNewArtifact(h, gainedArtifact.toArtifact(), ArtifactPosition::FIRST_AVAILABLE); + iw.components.emplace_back(ComponentType::ARTIFACT, gainedArtifact); } if(!iw.components.empty()) diff --git a/lib/mapObjects/CGCreature.h b/lib/mapObjects/CGCreature.h index 385b96cb5..13ff02f30 100644 --- a/lib/mapObjects/CGCreature.h +++ b/lib/mapObjects/CGCreature.h @@ -41,9 +41,11 @@ public: std::string getHoverText(PlayerColor player) const override; std::string getHoverText(const CGHeroInstance * hero) const override; void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void newTurn(CRandomGenerator & rand) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; + CreatureID getCreature() const; //stack formation depends on position, bool containsUpgradedStack() const; diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 65da8db66..e379901d7 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -11,8 +11,10 @@ #include "StdInc.h" #include "CGDwelling.h" #include "../serializer/JsonSerializeFormat.h" +#include "../mapping/CMap.h" #include "../mapObjectConstructors/AObjectTypeHandler.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" +#include "../mapObjectConstructors/DwellingInstanceConstructor.h" #include "../mapObjects/CGHeroInstance.h" #include "../networkPacks/StackLocation.h" #include "../networkPacks/PacksForClient.h" @@ -26,41 +28,10 @@ VCMI_LIB_NAMESPACE_BEGIN -CSpecObjInfo::CSpecObjInfo(): - owner(nullptr) -{ - -} - -void CCreGenAsCastleInfo::serializeJson(JsonSerializeFormat & handler) +void CGDwellingRandomizationInfo::serializeJson(JsonSerializeFormat & handler) { handler.serializeString("sameAsTown", instanceId); - - if(!handler.saving) - { - asCastle = !instanceId.empty(); - allowedFactions.clear(); - } - - if(!asCastle) - { - std::vector standard; - standard.resize(VLC->townh->size(), true); - - JsonSerializeFormat::LIC allowedLIC(standard, &FactionID::decode, &FactionID::encode); - allowedLIC.any = allowedFactions; - - handler.serializeLIC("allowedFactions", allowedLIC); - - if(!handler.saving) - { - allowedFactions = allowedLIC.any; - } - } -} - -void CCreGenLeveledInfo::serializeJson(JsonSerializeFormat & handler) -{ + handler.serializeIdArray("allowedFactions", allowedFactions); handler.serializeInt("minLevel", minLevel, static_cast(1)); handler.serializeInt("maxLevel", maxLevel, static_cast(7)); @@ -72,30 +43,139 @@ void CCreGenLeveledInfo::serializeJson(JsonSerializeFormat & handler) } } -void CCreGenLeveledCastleInfo::serializeJson(JsonSerializeFormat & handler) +CGDwelling::CGDwelling() = default; +CGDwelling::~CGDwelling() = default; + +FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand) { - CCreGenAsCastleInfo::serializeJson(handler); - CCreGenLeveledInfo::serializeJson(handler); + if (ID == Obj::RANDOM_DWELLING_FACTION) + return FactionID(subID); + + assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL); + assert(randomizationInfo.has_value()); + if (!randomizationInfo) + return FactionID::CASTLE; + + CGTownInstance * linkedTown = nullptr; + + if (!randomizationInfo->instanceId.empty()) + { + auto iter = cb->gameState()->map->instanceNames.find(randomizationInfo->instanceId); + + if(iter == cb->gameState()->map->instanceNames.end()) + logGlobal->error("Map object not found: %s", randomizationInfo->instanceId); + linkedTown = dynamic_cast(iter->second.get()); + } + + if (randomizationInfo->identifier != 0) + { + for(auto & elem : cb->gameState()->map->objects) + { + auto town = dynamic_cast(elem.get()); + if(town && town->identifier == randomizationInfo->identifier) + { + linkedTown = town; + break; + } + } + } + + if (linkedTown) + { + if(linkedTown->ID==Obj::RANDOM_TOWN) + linkedTown->pickRandomObject(rand); //we have to randomize the castle first + + assert(linkedTown->ID == Obj::TOWN); + if(linkedTown->ID==Obj::TOWN) + return linkedTown->getFaction(); + } + + if(!randomizationInfo->allowedFactions.empty()) + return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, rand); + + + std::vector potentialPicks; + + for (FactionID faction(0); faction < FactionID(VLC->townh->size()); ++faction) + if (VLC->factions()->getById(faction)->hasTown()) + potentialPicks.push_back(faction); + + assert(!potentialPicks.empty()); + return *RandomGeneratorUtil::nextItem(potentialPicks, rand); } -CGDwelling::CGDwelling() - : info(nullptr) +int CGDwelling::randomizeLevel(CRandomGenerator & rand) { + if (ID == Obj::RANDOM_DWELLING_LVL) + return subID.getNum(); + + assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_FACTION); + assert(randomizationInfo.has_value()); + + if (!randomizationInfo) + return rand.nextInt(1, 7) - 1; + + if(randomizationInfo->minLevel == randomizationInfo->maxLevel) + return randomizationInfo->minLevel - 1; + + return rand.nextInt(randomizationInfo->minLevel, randomizationInfo->maxLevel) - 1; } -CGDwelling::~CGDwelling() +void CGDwelling::pickRandomObject(CRandomGenerator & rand) { - vstd::clear_pointer(info); + if (ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL || ID == Obj::RANDOM_DWELLING_FACTION) + { + FactionID faction = randomizeFaction(rand); + int level = randomizeLevel(rand); + assert(faction != FactionID::NONE && faction != FactionID::NEUTRAL); + assert(level >= 0 && level <= 6); + randomizationInfo.reset(); + + CreatureID cid = (*VLC->townh)[faction]->town->creatures[level][0]; + + //NOTE: this will pick last dwelling with this creature (Mantis #900) + //check for block map equality is better but more complex solution + auto testID = [&](const Obj & primaryID) -> MapObjectSubID + { + auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID); + for (si32 entry : dwellingIDs) + { + const auto * handler = dynamic_cast(VLC->objtypeh->getHandlerFor(primaryID, entry).get()); + + if (handler->producesCreature(cid.toCreature())) + return MapObjectSubID(entry); + } + return MapObjectSubID(); + }; + + ID = Obj::CREATURE_GENERATOR1; + subID = testID(Obj::CREATURE_GENERATOR1); + + if (subID == MapObjectSubID()) + { + ID = Obj::CREATURE_GENERATOR4; + subID = testID(Obj::CREATURE_GENERATOR4); + } + + if (subID == MapObjectSubID()) + { + logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level)); + ID = Obj::CREATURE_GENERATOR4; + subID = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand); + } + + setType(ID, subID); + } } void CGDwelling::initObj(CRandomGenerator & rand) { - switch(ID) + switch(ID.toEnum()) { case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR4: { - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); + getObjectHandler()->configureObject(this, rand); if (getOwner() != PlayerColor::NEUTRAL) cb->gameState()->players[getOwner()].dwellings.emplace_back(this); @@ -121,23 +201,6 @@ void CGDwelling::initObj(CRandomGenerator & rand) } } -void CGDwelling::initRandomObjectInfo() -{ - vstd::clear_pointer(info); - switch(ID) - { - case Obj::RANDOM_DWELLING: info = new CCreGenLeveledCastleInfo(); - break; - case Obj::RANDOM_DWELLING_LVL: info = new CCreGenAsCastleInfo(); - break; - case Obj::RANDOM_DWELLING_FACTION: info = new CCreGenLeveledInfo(); - break; - } - - if(info) - info->owner = this; -} - void CGDwelling::setPropertyDer(ui8 what, ui32 val) { switch (what) @@ -256,7 +319,7 @@ void CGDwelling::newTurn(CRandomGenerator & rand) const else creaturesAccumulate = VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL); - CCreature *cre = VLC->creh->objects[creatures[i].second[0]]; + const CCreature * cre =creatures[i].second[0].toCreature(); TQuantity amount = cre->getGrowth() * (1 + cre->valOfBonuses(BonusType::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(BonusType::CREATURE_GROWTH); if (creaturesAccumulate && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures sac.creatures[i].first += amount; @@ -281,7 +344,7 @@ void CGDwelling::updateGuards() const //default condition - creatures are of level 5 or higher for (auto creatureEntry : creatures) { - if (VLC->creatures()->getByIndex(creatureEntry.second.at(0))->getLevel() >= 5 && ID != Obj::REFUGEE_CAMP) + if (VLC->creatures()->getById(creatureEntry.second.at(0))->getLevel() >= 5 && ID != Obj::REFUGEE_CAMP) { guarded = true; break; @@ -292,7 +355,7 @@ void CGDwelling::updateGuards() const { for (auto creatureEntry : creatures) { - const CCreature * crea = VLC->creh->objects[creatureEntry.second.at(0)]; + const CCreature * crea = creatureEntry.second.at(0).toCreature(); SlotID slot = getSlotFor(crea->getId()); if (hasStackAtSlot(slot)) //stack already exists, overwrite it @@ -425,10 +488,7 @@ void CGDwelling::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler) { - if(!handler.saving) - initRandomObjectInfo(); - - switch (ID) + switch (ID.toEnum()) { case Obj::WAR_MACHINE_FACTORY: case Obj::REFUGEE_CAMP: @@ -437,8 +497,10 @@ void CGDwelling::serializeJsonOptions(JsonSerializeFormat & handler) case Obj::RANDOM_DWELLING: case Obj::RANDOM_DWELLING_LVL: case Obj::RANDOM_DWELLING_FACTION: - info->serializeJson(handler); - //fall through + if (!handler.saving) + randomizationInfo = CGDwellingRandomizationInfo(); + randomizationInfo->serializeJson(handler); + [[fallthrough]]; default: serializeJsonOwner(handler); break; diff --git a/lib/mapObjects/CGDwelling.h b/lib/mapObjects/CGDwelling.h index f27cf44e2..bc63dcd85 100644 --- a/lib/mapObjects/CGDwelling.h +++ b/lib/mapObjects/CGDwelling.h @@ -16,62 +16,39 @@ VCMI_LIB_NAMESPACE_BEGIN class CGDwelling; -class DLL_LINKAGE CSpecObjInfo +class DLL_LINKAGE CGDwellingRandomizationInfo { public: - CSpecObjInfo(); - virtual ~CSpecObjInfo() = default; - - virtual void serializeJson(JsonSerializeFormat & handler) = 0; - - const CGDwelling * owner; -}; - -class DLL_LINKAGE CCreGenAsCastleInfo : public virtual CSpecObjInfo -{ -public: - bool asCastle = false; - ui32 identifier = 0;//h3m internal identifier - - std::vector allowedFactions; + std::set allowedFactions; std::string instanceId;//vcmi map instance identifier - void serializeJson(JsonSerializeFormat & handler) override; + int32_t identifier = 0;//h3m internal identifier + + uint8_t minLevel = 1; + uint8_t maxLevel = 7; //minimal and maximal level of creature in dwelling: <1, 7> + + void serializeJson(JsonSerializeFormat & handler); }; -class DLL_LINKAGE CCreGenLeveledInfo : public virtual CSpecObjInfo -{ -public: - ui8 minLevel = 1; - ui8 maxLevel = 7; //minimal and maximal level of creature in dwelling: <1, 7> - - void serializeJson(JsonSerializeFormat & handler) override; -}; - -class DLL_LINKAGE CCreGenLeveledCastleInfo : public CCreGenAsCastleInfo, public CCreGenLeveledInfo -{ -public: - CCreGenLeveledCastleInfo() = default; - void serializeJson(JsonSerializeFormat & handler) override; -}; - - class DLL_LINKAGE CGDwelling : public CArmedInstance { public: typedef std::vector > > TCreaturesSet; - CSpecObjInfo * info; //random dwelling options; not serialized + std::optional randomizationInfo; //random dwelling options; not serialized TCreaturesSet creatures; //creatures[level] -> CGDwelling(); ~CGDwelling() override; - void initRandomObjectInfo(); protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; private: + FactionID randomizeFaction(CRandomGenerator & rand); + int randomizeLevel(CRandomGenerator & rand); + + void pickRandomObject(CRandomGenerator & rand) override; void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void newTurn(CRandomGenerator & rand) const override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 116d16017..8d385fc22 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -218,7 +218,10 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const if (getSecSkillLevel(which) > 0) return false; - if (type->heroClass->secSkillProbability[which] == 0) + if (type->heroClass->secSkillProbability.count(which) == 0) + return false; + + if (type->heroClass->secSkillProbability.at(which) == 0) return false; return true; @@ -285,27 +288,28 @@ PlayerColor CGHeroInstance::getOwner() const return tempOwner; } +HeroTypeID CGHeroInstance::getHeroType() const +{ + return HeroTypeID(getObjTypeIndex().getNum()); +} + +void CGHeroInstance::setHeroType(HeroTypeID heroType) +{ + assert(type == nullptr); + subID = heroType; +} + void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID) { subID = SUBID.getNum(); initHero(rand); } -void CGHeroInstance::setType(si32 ID, si32 subID) -{ - assert(ID == Obj::HERO); // just in case - type = VLC->heroh->objects[subID]; - - CGObjectInstance::setType(ID, type->heroClass->getIndex()); // to find object handler we must use heroClass->id - this->subID = subID; // after setType subID used to store unique hero identify id. Check issue 2277 for details - randomizeArmy(type->heroClass->faction); -} - void CGHeroInstance::initHero(CRandomGenerator & rand) { assert(validTypes(true)); if(!type) - type = VLC->heroh->objects[subID]; + type = VLC->heroh->objects[getHeroType().getNum()]; if (ID == Obj::HERO) appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front(); @@ -566,18 +570,35 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter() wisdomCounter = 1; } +void CGHeroInstance::pickRandomObject(CRandomGenerator & rand) +{ + assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO); + + if (ID == Obj::RANDOM_HERO) + { + ID = Obj::HERO; + subID = cb->gameState()->pickNextHeroType(getOwner()); + type = VLC->heroh->objects[subID]; + randomizeArmy(type->heroClass->faction); + } + else + type = VLC->heroh->objects[subID]; + + auto oldSubID = subID; + + // to find object handler we must use heroClass->id + // after setType subID used to store unique hero identify id. Check issue 2277 for details + if (ID != Obj::PRISON) + setType(ID, type->heroClass->getIndex()); + else + setType(ID, 0); + + this->subID = oldSubID; +} + void CGHeroInstance::initObj(CRandomGenerator & rand) { - if(!type) - initHero(rand); //TODO: set up everything for prison before specialties are configured - if (ID != Obj::PRISON) - { - auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); - auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(terrain, this); - if (customApp) - appearance = customApp; - } } void CGHeroInstance::recreateSecondarySkillsBonuses() @@ -926,7 +947,7 @@ void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedSta iw.type = EInfoWindowMode::AUTO; iw.soundID = soundBase::pickup01 + rand.nextInt(6); iw.player = tempOwner; - iw.components.emplace_back(raisedStack); + iw.components.emplace_back(ComponentType::CREATURE, raisedStack.getId(), raisedStack.count); if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural) { @@ -1050,7 +1071,7 @@ HeroTypeID CGHeroInstance::getPortraitSource() const if (customPortraitSource.isValid()) return customPortraitSource; else - return HeroTypeID(subID); + return getHeroType(); } int32_t CGHeroInstance::getIconIndex() const @@ -1092,7 +1113,7 @@ std::string CGHeroInstance::getBiographyTextID() const CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art) { - assert(art->artType->canBePutAt(this, pos)); + assert(art->canBePutAt(this, pos)); if(ArtifactUtils::isSlotEquipment(pos)) attachTo(*art); @@ -1135,7 +1156,7 @@ void CGHeroInstance::removeSpellbook() if(hasSpellbook()) { - ArtifactLocation(this, ArtifactPosition(ArtifactPosition::SPELLBOOK)).removeArtifact(); + getArt(ArtifactPosition::SPELLBOOK)->removeFrom(*this, ArtifactPosition::SPELLBOOK); } } @@ -1502,7 +1523,7 @@ std::string CGHeroInstance::getHeroTypeName() const } else { - return VLC->heroh->objects[subID]->getJsonKey(); + return VLC->heroh->objects[getHeroType()]->getJsonKey(); } } return ""; @@ -1512,7 +1533,7 @@ void CGHeroInstance::afterAddToMap(CMap * map) { auto existingHero = std::find_if(map->objects.begin(), map->objects.end(), [&](const CGObjectInstance * o) ->bool { - return (o->ID == Obj::HERO || o->ID == Obj::PRISON) && o->subID == subID && o != this; + return o && (o->ID == Obj::HERO || o->ID == Obj::PRISON) && o->subID == subID && o != this; }); if(existingHero != map->objects.end()) @@ -1529,14 +1550,14 @@ void CGHeroInstance::afterAddToMap(CMap * map) } } - if(ID == Obj::HERO) + if(ID != Obj::PRISON) { map->heroesOnMap.emplace_back(this); } } void CGHeroInstance::afterRemoveFromMap(CMap* map) { - if (ID == Obj::HERO) + if (ID == Obj::PRISON) vstd::erase_if_present(map->heroesOnMap, this); } @@ -1661,7 +1682,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler) { auto addSkill = [this](const std::string & skillId, const std::string & levelId) { - const int rawId = CSkillHandler::decodeSkill(skillId); + const int rawId = SecondarySkill::decode(skillId); if(rawId < 0) { logGlobal->error("Invalid secondary skill %s", skillId); @@ -1736,7 +1757,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler) if(!appearance) { // crossoverDeserialize - type = VLC->heroh->objects[subID]; + type = VLC->heroh->objects[getHeroType()]; appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front(); } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 2035a488b..a07ccff9c 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -231,7 +231,8 @@ public: ////////////////////////////////////////////////////////////////////////// - void setType(si32 ID, si32 subID) override; + HeroTypeID getHeroType() const; + void setHeroType(HeroTypeID type); void initHero(CRandomGenerator & rand); void initHero(CRandomGenerator & rand, const HeroTypeID & SUBID); @@ -294,6 +295,7 @@ public: void deserializationFix(); void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; std::string getObjectName() const override; diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index ef20b1024..d6a1e433d 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -25,7 +25,7 @@ VCMI_LIB_NAMESPACE_BEGIN void CGMarket::initObj(CRandomGenerator & rand) { - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); + getObjectHandler()->configureObject(this, rand); } void CGMarket::onHeroVisit(const CGHeroInstance * h) const diff --git a/lib/mapObjects/CGObjectInstance.cpp b/lib/mapObjects/CGObjectInstance.cpp index 7d5cacbe2..74546c07c 100644 --- a/lib/mapObjects/CGObjectInstance.cpp +++ b/lib/mapObjects/CGObjectInstance.cpp @@ -40,12 +40,12 @@ CGObjectInstance::CGObjectInstance(): //must be instantiated in .cpp file for access to complete types of all member fields CGObjectInstance::~CGObjectInstance() = default; -int32_t CGObjectInstance::getObjGroupIndex() const +MapObjectID CGObjectInstance::getObjGroupIndex() const { - return ID.num; + return ID; } -int32_t CGObjectInstance::getObjTypeIndex() const +MapObjectSubID CGObjectInstance::getObjTypeIndex() const { return subID; } @@ -122,7 +122,7 @@ std::set CGObjectInstance::getBlockedOffsets() const return appearance->getBlockedOffsets(); } -void CGObjectInstance::setType(si32 newID, si32 newSubID) +void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID) { auto position = visitablePos(); auto oldOffset = getVisitableOffset(); @@ -166,9 +166,14 @@ void CGObjectInstance::setType(si32 newID, si32 newSubID) cb->gameState()->map->addBlockVisTiles(this); } +void CGObjectInstance::pickRandomObject(CRandomGenerator & rand) +{ + // no-op +} + void CGObjectInstance::initObj(CRandomGenerator & rand) { - switch(ID) + switch(ID.toEnum()) { case Obj::TAVERN: blockVisit = true; @@ -197,6 +202,11 @@ void CGObjectInstance::setProperty( ui8 what, ui32 val ) } } +TObjectTypeHandler CGObjectInstance::getObjectHandler() const +{ + return VLC->objtypeh->getHandlerFor(ID, subID); +} + void CGObjectInstance::setPropertyDer( ui8 what, ui32 val ) {} @@ -292,7 +302,7 @@ std::vector CGObjectInstance::getPopupComponents(const CGHeroInstance void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const { - switch(ID) + switch(ID.toEnum()) { case Obj::SANCTUARY: { diff --git a/lib/mapObjects/CGObjectInstance.h b/lib/mapObjects/CGObjectInstance.h index 1e57c2344..01eeffd2d 100644 --- a/lib/mapObjects/CGObjectInstance.h +++ b/lib/mapObjects/CGObjectInstance.h @@ -21,6 +21,8 @@ struct Component; class JsonSerializeFormat; class ObjectTemplate; class CMap; +class AObjectTypeHandler; +using TObjectTypeHandler = std::shared_ptr; class DLL_LINKAGE CGObjectInstance : public IObjectInterface { @@ -28,9 +30,9 @@ public: /// Position of bottom-right corner of object on map int3 pos; /// Type of object, e.g. town, hero, creature. - Obj ID; + MapObjectID ID; /// Subtype of object, depends on type - si32 subID; + MapObjectSubID subID; /// Current owner of an object (when below PLAYER_LIMIT) PlayerColor tempOwner; /// Index of object in map's list of objects @@ -45,8 +47,8 @@ public: CGObjectInstance(); //TODO: remove constructor ~CGObjectInstance() override; - int32_t getObjGroupIndex() const override; - int32_t getObjTypeIndex() const override; + MapObjectID getObjGroupIndex() const override; + MapObjectSubID getObjTypeIndex() const override; /// "center" tile from which the sight distance is calculated int3 getSightCenter() const; @@ -94,6 +96,8 @@ public: std::optional getVisitSound() const; std::optional getRemovalSound() const; + TObjectTypeHandler getObjectHandler() const; + /** VIRTUAL METHODS **/ /// Returns true if player can pass through visitable tiles of this object @@ -102,10 +106,6 @@ public: virtual int getSightRadius() const; /// returns (x,y,0) offset to a visitable tile of object virtual int3 getVisitableOffset() const; - /// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman") - virtual void setType(si32 ID, si32 subID); - - /// returns text visible in status bar with specific hero/player active. /// Returns generic name of object, without any player-specific info virtual std::string getObjectName() const; @@ -124,6 +124,7 @@ public: /** OVERRIDES OF IObjectInterface **/ void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; /// method for synchronous update. Note: For new properties classes should override setPropertyDer instead void setProperty(ui8 what, ui32 val) final; @@ -155,6 +156,9 @@ protected: /// virtual method that allows synchronously update object state on server and all clients virtual void setPropertyDer(ui8 what, ui32 val); + /// Called mostly during map randomization to turn random object into a regular one (e.g. "Random Monster" into "Pikeman") + void setType(MapObjectID ID, MapObjectSubID subID); + /// Gives dummy bonus from this object to hero. Can be used to track visited state void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration = BonusDuration::ONE_DAY) const; diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 22d5aa6e6..17e05e47f 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -257,7 +257,7 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) const std::string skillName = p.first; const std::string levelId = p.second.String(); - const int rawId = CSkillHandler::decodeSkill(skillName); + const int rawId = SecondarySkill::decode(skillName); if(rawId < 0) { logGlobal->error("Invalid secondary skill %s", skillName); diff --git a/lib/mapObjects/CGTownBuilding.cpp b/lib/mapObjects/CGTownBuilding.cpp index 4921b085a..1406786f9 100644 --- a/lib/mapObjects/CGTownBuilding.cpp +++ b/lib/mapObjects/CGTownBuilding.cpp @@ -24,12 +24,12 @@ PlayerColor CGTownBuilding::getOwner() const return town->getOwner(); } -int32_t CGTownBuilding::getObjGroupIndex() const +MapObjectID CGTownBuilding::getObjGroupIndex() const { return -1; } -int32_t CGTownBuilding::getObjTypeIndex() const +MapObjectSubID CGTownBuilding::getObjTypeIndex() const { return 0; } @@ -207,31 +207,31 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge what = PrimarySkill::KNOWLEDGE; val = 1; - iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 3, 1, 0); + iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::KNOWLEDGE, 1); break; case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire what = PrimarySkill::SPELL_POWER; val = 1; - iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 2, 1, 0); + iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::SPELL_POWER, 1); break; case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla what = PrimarySkill::ATTACK; val = 1; - iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 0, 1, 0); + iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::ATTACK, 1); break; case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars what = PrimarySkill::EXPERIENCE; val = static_cast(h->calculateXp(1000)); - iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, val, 0); + iw.components.emplace_back(ComponentType::EXPERIENCE, val); break; case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords what = PrimarySkill::DEFENSE; val = 1; - iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 1, 1, 0); + iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::DEFENSE, 1); break; case BuildingSubID::CUSTOM_VISITING_BONUS: diff --git a/lib/mapObjects/CGTownBuilding.h b/lib/mapObjects/CGTownBuilding.h index 1fffb3710..77085f67f 100644 --- a/lib/mapObjects/CGTownBuilding.h +++ b/lib/mapObjects/CGTownBuilding.h @@ -45,8 +45,8 @@ public: } PlayerColor getOwner() const override; - int32_t getObjGroupIndex() const override; - int32_t getObjTypeIndex() const override; + MapObjectID getObjGroupIndex() const override; + MapObjectSubID getObjTypeIndex() const override; int3 visitablePos() const override; int3 getPosition() const override; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 9e7f842b2..4916af24a 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -20,6 +20,7 @@ #include "../gameState/CGameState.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" +#include "../StartInfo.h" #include "../TerrainHandler.h" #include "../mapObjectConstructors/AObjectTypeHandler.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" @@ -324,7 +325,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const InfoWindow iw; iw.player = h->tempOwner; iw.text.appendRawString(h->commander->getName()); - iw.components.emplace_back(*h->commander); + iw.components.emplace_back(ComponentType::CREATURE, h->commander->getId(), h->commander->getCount()); cb->showInfoDialog(&iw); } } @@ -371,7 +372,7 @@ bool CGTownInstance::isBonusingBuildingAdded(BuildingID bid) const { auto present = std::find_if(bonusingBuildings.begin(), bonusingBuildings.end(), [&](CGTownBuilding* building) { - return building->getBuildingType().num == bid; + return building->getBuildingType() == bid; }); return present != bonusingBuildings.end(); @@ -459,6 +460,40 @@ void CGTownInstance::deleteTownBonus(BuildingID bid) delete freeIt; } +FactionID CGTownInstance::randomizeFaction(CRandomGenerator & rand) +{ + if(getOwner().isValidPlayer()) + return cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner()).castle; + + if(alignmentToPlayer.isValidPlayer()) + return cb->gameState()->scenarioOps->getIthPlayersSettings(alignmentToPlayer).castle; + + std::vector potentialPicks; + + for (FactionID faction(0); faction < FactionID(VLC->townh->size()); ++faction) + if (VLC->factions()->getById(faction)->hasTown()) + potentialPicks.push_back(faction); + + assert(!potentialPicks.empty()); + return *RandomGeneratorUtil::nextItem(potentialPicks, rand); +} + +void CGTownInstance::pickRandomObject(CRandomGenerator & rand) +{ + assert(ID == MapObjectID::TOWN || ID == MapObjectID::RANDOM_TOWN); + if (ID == MapObjectID::RANDOM_TOWN) + { + ID = MapObjectID::TOWN; + subID = randomizeFaction(rand); + } + + assert(ID == Obj::TOWN); // just in case + setType(ID, subID); + town = (*VLC->townh)[subID]->town; + randomizeArmy(subID); + updateAppearance(); +} + void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures { blockVisit = true; @@ -549,7 +584,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const TQuantity count = creatureGrowth(i); if (!count) // no dwelling - count = VLC->creatures()->getByIndex(c)->getGrowth(); + count = VLC->creatures()->getById(c)->getGrowth(); {//no lower tiers or above current month @@ -750,20 +785,11 @@ std::vector CGTownInstance::availableItemsIds(EMarketMode mode) const 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)[subID]->town; - randomizeArmy(subID); - updateAppearance(); -} - void CGTownInstance::updateAppearance() { auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId(); //FIXME: not the best way to do this - auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(terrain, this); + auto app = getObjectHandler()->getOverride(terrain, this); if (app) appearance = app; } @@ -891,7 +917,7 @@ const CTown * CGTownInstance::getTown() const { if(nullptr == town) { - return (*VLC->townh)[subID]->town; + return (*VLC->townh)[getFaction()]->town; } else return town; @@ -970,9 +996,9 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID) const return vstd::contains(builtBuildings, buildingID); } -bool CGTownInstance::hasBuilt(const BuildingID & buildingID, int townID) const +bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) const { - if (townID == town->faction->getIndex() || townID == ETownType::ANY) + if (townID == town->faction->getId() || townID == FactionID::ANY) return hasBuilt(buildingID); return false; } @@ -1074,14 +1100,12 @@ void CGTownInstance::onTownCaptured(const PlayerColor & winner) const void CGTownInstance::afterAddToMap(CMap * map) { - if(ID == Obj::TOWN) - map->towns.emplace_back(this); + map->towns.emplace_back(this); } void CGTownInstance::afterRemoveFromMap(CMap * map) { - if (ID == Obj::TOWN) - vstd::erase_if_present(map->towns, this); + vstd::erase_if_present(map->towns, this); } void CGTownInstance::reset() diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 07da57644..4a5e00511 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -141,7 +141,6 @@ public: bool allowsTrade(EMarketMode mode) const override; std::vector availableItemsIds(EMarketMode mode) const override; - void setType(si32 ID, si32 subID) override; void updateAppearance(); ////////////////////////////////////////////////////////////////////////// @@ -161,7 +160,7 @@ public: bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const; //checks if building is constructed and town has same subID bool hasBuilt(const BuildingID & buildingID) const; - bool hasBuilt(const BuildingID & buildingID, int townID) const; + bool hasBuilt(const BuildingID & buildingID, FactionID townID) const; TResources getBuildingCost(const BuildingID & buildingID) const; TResources dailyIncome() const; //calculates daily income of this town @@ -197,6 +196,7 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; void onHeroLeave(const CGHeroInstance * h) const override; void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void battleFinished(const CGHeroInstance * hero, const BattleResult & result) const override; std::string getObjectName() const override; @@ -216,6 +216,7 @@ protected: void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; private: + FactionID randomizeFaction(CRandomGenerator & rand); void setOwner(const PlayerColor & owner) const; void onTownCaptured(const PlayerColor & winner) const; int getDwellingBonus(const std::vector& creatureIds, const std::vector >& dwellings) const; diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 34162c4c3..bf9c2158d 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -28,14 +28,4 @@ CObjectHandler::CObjectHandler() logGlobal->trace("\t\tDone loading resource prices!"); } -CGObjectInstanceBySubIdFinder::CGObjectInstanceBySubIdFinder(CGObjectInstance * obj) : obj(obj) -{ - -} - -bool CGObjectInstanceBySubIdFinder::operator()(CGObjectInstance * obj) const -{ - return this->obj->subID == obj->subID; -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index 128d579b4..9cba71ab1 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -16,17 +16,6 @@ VCMI_LIB_NAMESPACE_BEGIN class CGObjectInstance; class int3; -/// function object which can be used to find an object with an specific sub ID -class CGObjectInstanceBySubIdFinder -{ -public: - CGObjectInstanceBySubIdFinder(CGObjectInstance * obj); - bool operator()(CGObjectInstance * obj) const; - -private: - CGObjectInstance * obj; -}; - class DLL_LINKAGE CObjectHandler { public: diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 5431a12a1..761be1090 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -34,7 +34,7 @@ VCMI_LIB_NAMESPACE_BEGIN -std::map > CGKeys::playerKeyMap; +std::map > CGKeys::playerKeyMap; //TODO: Remove constructor CQuest::CQuest(): @@ -144,7 +144,7 @@ void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const { if(h->hasArt(elem)) { - cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false))); + cb->removeArtifact(ArtifactLocation(h->id, h->getArtPos(elem, false))); } else { @@ -153,7 +153,7 @@ void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const auto parts = assembly->getPartsInfo(); // Remove the assembly - cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly))); + cb->removeArtifact(ArtifactLocation(h->id, h->getArtPos(assembly))); // Disassemble this backpack artifact for(const auto & ci : parts) @@ -204,13 +204,13 @@ void CQuest::addTextReplacements(MetaString & text, std::vector & com if(killTarget != ObjectInstanceID::NONE && !heroName.empty()) { - components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0); + components.emplace_back(ComponentType::HERO_PORTRAIT, heroPortrait); addKillTargetReplacements(text); } if(killTarget != ObjectInstanceID::NONE && stackToKill.type) { - components.emplace_back(stackToKill); + components.emplace_back(ComponentType::CREATURE, stackToKill.getId(), stackToKill.getCount()); addKillTargetReplacements(text); } @@ -392,15 +392,15 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi if(missionType == "Hero") { - ui32 temp; - handler.serializeId("hero", temp, 0); + HeroTypeID temp; + handler.serializeId("hero", temp, HeroTypeID::NONE); mission.heroes.emplace_back(temp); } if(missionType == "Player") { - ui32 temp; - handler.serializeId("player", temp, PlayerColor::NEUTRAL); + PlayerColor temp; + handler.serializeId("player", temp, PlayerColor::NEUTRAL); mission.players.emplace_back(temp); } } @@ -724,7 +724,7 @@ void CGQuestGuard::init(CRandomGenerator & rand) configuration.info.push_back({}); configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; - configuration.info.back().reward.removeObject = subID == 0 ? true : false; + configuration.info.back().reward.removeObject = subID.getNum() == 0 ? true : false; configuration.canRefuse = true; } @@ -775,7 +775,7 @@ std::string CGKeys::getHoverText(PlayerColor player) const std::string CGKeys::getObjectName() const { - return VLC->generaltexth->tentColors[subID] + " " + CGObjectInstance::getObjectName(); + return VLC->generaltexth->tentColors[subID.getNum()] + " " + CGObjectInstance::getObjectName(); } bool CGKeymasterTent::wasVisited (PlayerColor player) const @@ -810,7 +810,7 @@ void CGBorderGuard::getRolloverText(MetaString &text, bool onHover) const { if (!onHover) { - text.appendRawString(VLC->generaltexth->tentColors[subID]); + text.appendRawString(VLC->generaltexth->tentColors[subID.getNum()]); text.appendRawString(" "); text.appendRawString(VLC->objtypeh->getObjectName(Obj::KEYMASTER, subID)); } diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 3f76d3550..bb4980611 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -166,7 +166,7 @@ protected: class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards { public: - static std::map > playerKeyMap; //[players][keysowned] + static std::map > playerKeyMap; //[players][keysowned] //SubID 0 - lightblue, 1 - green, 2 - red, 3 - darkblue, 4 - brown, 5 - purple, 6 - white, 7 - black static void reset(); diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 4ec6783e0..1d441b9f3 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -373,7 +373,7 @@ void CRewardableObject::newTurn(CRandomGenerator & rand) const void CRewardableObject::initObj(CRandomGenerator & rand) { - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); + getObjectHandler()->configureObject(this, rand); } CRewardableObject::CRewardableObject() diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index 268c99c4c..b0e0b11cd 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -46,6 +46,9 @@ void IObjectInterface::newTurn(CRandomGenerator & rand) const void IObjectInterface::initObj(CRandomGenerator & rand) {} +void IObjectInterface::pickRandomObject(CRandomGenerator & rand) +{} + void IObjectInterface::setProperty( ui8 what, ui32 val ) {} diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index 1ed826903..8dab1a59d 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/lib/mapObjects/IObjectInterface.h @@ -10,6 +10,7 @@ #pragma once #include "../networkPacks/EInfoWindowMode.h" +#include "../constants/EntityIdentifiers.h" VCMI_LIB_NAMESPACE_BEGIN @@ -33,8 +34,8 @@ public: virtual ~IObjectInterface() = default; - virtual int32_t getObjGroupIndex() const = 0; - virtual int32_t getObjTypeIndex() const = 0; + virtual MapObjectID getObjGroupIndex() const = 0; + virtual MapObjectSubID getObjTypeIndex() const = 0; virtual PlayerColor getOwner() const = 0; virtual int3 visitablePos() const = 0; @@ -44,6 +45,7 @@ public: virtual void onHeroLeave(const CGHeroInstance * h) const; virtual void newTurn(CRandomGenerator & rand) const; virtual void initObj(CRandomGenerator & rand); //synchr + virtual void pickRandomObject(CRandomGenerator & rand); virtual void setProperty(ui8 what, ui32 val);//synchr //Called when queries created DURING HERO VISIT are resolved diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 0bfb191a7..ca5ac7b73 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -85,7 +85,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const { BlockingDialog ynd(true,false); ynd.player = h->tempOwner; - ynd.text.appendLocalString(EMetaText::ADVOB_TXT, subID == 7 ? 84 : 187); + ynd.text.appendLocalString(EMetaText::ADVOB_TXT, isAbandoned() ? 84 : 187); cb->showBlockingDialog(&ynd); return; } @@ -119,19 +119,27 @@ void CGMine::initObj(CRandomGenerator & rand) } else { - producedResource = GameResID(subID); + producedResource = GameResID(getObjTypeIndex().getNum()); } producedQuantity = defaultResProduction(); } bool CGMine::isAbandoned() const { - return (subID >= 7); + return subID.getNum() >= 7; +} + +ResourceSet CGMine::dailyIncome() const +{ + ResourceSet result; + result[producedResource] += defaultResProduction(); + + return result; } std::string CGMine::getObjectName() const { - return VLC->generaltexth->translate("core.minename", subID); + return VLC->generaltexth->translate("core.minename", getObjTypeIndex()); } std::string CGMine::getHoverText(PlayerColor player) const @@ -139,7 +147,7 @@ std::string CGMine::getHoverText(PlayerColor player) const std::string hoverName = CArmedInstance::getHoverText(player); if (tempOwner != PlayerColor::NEUTRAL) - hoverName += "\n(" + VLC->generaltexth->restypes[producedResource] + ")"; + hoverName += "\n(" + VLC->generaltexth->restypes[producedResource.getNum()] + ")"; if(stacksCount()) { @@ -161,7 +169,7 @@ void CGMine::flagMine(const PlayerColor & player) const iw.soundID = soundBase::FLAGMINE; iw.text.appendLocalString(EMetaText::MINE_EVNTS, producedResource); //not use subID, abandoned mines uses default mine texts iw.player = player; - iw.components.emplace_back(Component::EComponentType::RESOURCE, producedResource, producedQuantity, -1); + iw.components.emplace_back(ComponentType::RESOURCE_PER_DAY, producedResource, producedQuantity); cb->showInfoDialog(&iw); } @@ -206,7 +214,7 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) if(handler.saving) { JsonNode node(JsonNode::JsonType::DATA_VECTOR); - for(auto const & resID : abandonedMineResources) + for(const auto & resID : abandonedMineResources) { JsonNode one(JsonNode::JsonType::DATA_STRING); one.String() = GameConstants::RESOURCE_NAMES[resID]; @@ -237,9 +245,26 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler) } } +GameResID CGResource::resourceID() const +{ + return getObjTypeIndex().getNum(); +} + std::string CGResource::getHoverText(PlayerColor player) const { - return VLC->generaltexth->restypes[subID]; + return VLC->generaltexth->restypes[resourceID().getNum()]; +} + +void CGResource::pickRandomObject(CRandomGenerator & rand) +{ + assert(ID == Obj::RESOURCE || ID == Obj::RANDOM_RESOURCE); + + if (ID == Obj::RANDOM_RESOURCE) + { + ID = Obj::RESOURCE; + subID = rand.nextInt(EGameResID::WOOD, EGameResID::GOLD); + setType(ID, subID); + } } void CGResource::initObj(CRandomGenerator & rand) @@ -248,7 +273,7 @@ void CGResource::initObj(CRandomGenerator & rand) if(amount == CGResource::RANDOM_AMOUNT) { - switch(static_cast(subID)) + switch(resourceID().toEnum()) { case EGameResID::GOLD: amount = rand.nextInt(5, 10) * 100; @@ -285,7 +310,7 @@ void CGResource::onHeroVisit( const CGHeroInstance * h ) const void CGResource::collectRes(const PlayerColor & player) const { - cb->giveResource(player, static_cast(subID), amount); + cb->giveResource(player, resourceID(), amount); InfoWindow sii; sii.player = player; if(!message.empty()) @@ -297,9 +322,9 @@ void CGResource::collectRes(const PlayerColor & player) const { sii.type = EInfoWindowMode::INFO; sii.text.appendLocalString(EMetaText::ADVOB_TXT,113); - sii.text.replaceLocalString(EMetaText::RES_NAMES, subID); + sii.text.replaceLocalString(EMetaText::RES_NAMES, resourceID()); } - sii.components.emplace_back(Component::EComponentType::RESOURCE,subID,amount,0); + sii.components.emplace_back(ComponentType::RESOURCE, resourceID(), amount); sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6); cb->showInfoDialog(&sii); cb->removeObject(this, player); @@ -442,14 +467,14 @@ void CGTeleport::addToChannel(std::map & IDs, int SubID) const +TeleportChannelID CGMonolith::findMeChannel(const std::vector & IDs, MapObjectSubID SubID) const { for(auto obj : cb->gameState()->map->objects) { if(!obj) continue; - const auto * teleportObj = dynamic_cast(cb->getObj(obj->id)); + const auto * teleportObj = dynamic_cast(cb->getObj(obj->id)); if(teleportObj && vstd::contains(IDs, teleportObj->ID) && teleportObj->subID == SubID) return teleportObj->channel; } @@ -507,7 +532,7 @@ void CGMonolith::initObj(CRandomGenerator & rand) { std::vector IDs; IDs.push_back(ID); - switch(ID) + switch(ID.toEnum()) { case Obj::MONOLITH_ONE_WAY_ENTRANCE: type = ENTRANCE; @@ -641,7 +666,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const iw.type = EInfoWindowMode::AUTO; iw.player = h->tempOwner; iw.text.appendLocalString(EMetaText::ADVOB_TXT, 168); - iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), -countToTake)); + iw.components.emplace_back(ComponentType::CREATURE, h->getCreature(targetstack)->getId(), -countToTake); cb->showInfoDialog(&iw); cb->changeStackCount(StackLocation(h, targetstack), -countToTake); } @@ -688,6 +713,41 @@ bool CGWhirlpool::isProtected(const CGHeroInstance * h) || (h->stacksCount() == 1 && h->Slots().begin()->second->count == 1); } +ArtifactID CGArtifact::getArtifact() const +{ + if(ID == Obj::SPELL_SCROLL) + return ArtifactID::SPELL_SCROLL; + else + return getObjTypeIndex().getNum(); +} + +void CGArtifact::pickRandomObject(CRandomGenerator & rand) +{ + switch(ID.toEnum()) + { + case MapObjectID::RANDOM_ART: + subID = VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC); + break; + case MapObjectID::RANDOM_TREASURE_ART: + subID = VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE); + break; + case MapObjectID::RANDOM_MINOR_ART: + subID = VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR); + break; + case MapObjectID::RANDOM_MAJOR_ART: + subID = VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR); + break; + case MapObjectID::RANDOM_RELIC_ART: + subID = VLC->arth->pickRandomArtifact(rand, CArtifact::ART_RELIC); + break; + } + + if (ID != Obj::SPELL_SCROLL) + ID = MapObjectID::ARTIFACT; + + setType(ID, subID); +} + void CGArtifact::initObj(CRandomGenerator & rand) { blockVisit = true; @@ -700,7 +760,7 @@ void CGArtifact::initObj(CRandomGenerator & rand) storedArtifact = a; } if(!storedArtifact->artType) - storedArtifact->setType(VLC->arth->objects[subID]); + storedArtifact->setType(VLC->arth->objects[getArtifact().getNum()]); } if(ID == Obj::SPELL_SCROLL) subID = 1; @@ -713,7 +773,7 @@ void CGArtifact::initObj(CRandomGenerator & rand) std::string CGArtifact::getObjectName() const { - return VLC->artifacts()->getByIndex(subID)->getNameTranslated(); + return VLC->artifacts()->getById(getArtifact())->getNameTranslated(); } void CGArtifact::onHeroVisit(const CGHeroInstance * h) const @@ -726,27 +786,27 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const if(storedArtifact->artType->canBePutAt(h)) { - switch (ID) + switch (ID.toEnum()) { case Obj::ARTIFACT: { - iw.components.emplace_back(Component::EComponentType::ARTIFACT, subID, 0, 0); + iw.components.emplace_back(ComponentType::ARTIFACT, getArtifact()); if(!message.empty()) iw.text = message; else - iw.text.appendLocalString(EMetaText::ART_EVNTS, subID); + iw.text.appendLocalString(EMetaText::ART_EVNTS, getArtifact()); } break; case Obj::SPELL_SCROLL: { - int spellID = storedArtifact->getScrollSpellID(); - iw.components.emplace_back(Component::EComponentType::SPELL, spellID, 0, 0); + SpellID spell = storedArtifact->getScrollSpellID(); + iw.components.emplace_back(ComponentType::SPELL, spell); if(!message.empty()) iw.text = message; else { iw.text.appendLocalString(EMetaText::ADVOB_TXT,135); - iw.text.replaceLocalString(EMetaText::SPELL_NAME, spellID); + iw.text.replaceLocalString(EMetaText::SPELL_NAME, spell.getNum()); } } break; @@ -761,7 +821,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const } else { - switch(ID) + switch(ID.toEnum()) { case Obj::ARTIFACT: { diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 53021a4a9..f89876ffc 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -89,10 +89,13 @@ public: void pick( const CGHeroInstance * h ) const; void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void afterAddToMap(CMap * map) override; BattleField getBattlefield() const override; + ArtifactID getArtifact() const; + template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -113,11 +116,13 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; void initObj(CRandomGenerator & rand) override; + void pickRandomObject(CRandomGenerator & rand) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; std::string getHoverText(PlayerColor player) const override; void collectRes(const PlayerColor & player) const; + GameResID resourceID() const; template void serialize(Handler &h, const int version) { @@ -137,6 +142,7 @@ public: std::set abandonedMineResources; bool isAbandoned() const; + ResourceSet dailyIncome() const; private: void onHeroVisit(const CGHeroInstance * h) const override; @@ -219,7 +225,7 @@ public: class DLL_LINKAGE CGMonolith : public CGTeleport { - TeleportChannelID findMeChannel(const std::vector & IDs, int SubID) const; + TeleportChannelID findMeChannel(const std::vector & IDs, MapObjectSubID SubID) const; protected: void onHeroVisit(const CGHeroInstance * h) const override; diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 806c953c1..e116bf4e6 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -71,6 +71,8 @@ void CMapEvent::serializeJson(JsonSerializeFormat & handler) void CCastleEvent::serializeJson(JsonSerializeFormat & handler) { CMapEvent::serializeJson(handler); + + // TODO: handler.serializeIdArray("buildings", buildings); { std::vector temp(buildings.begin(), buildings.end()); auto a = handler.enterArray("buildings"); @@ -81,6 +83,7 @@ void CCastleEvent::serializeJson(JsonSerializeFormat & handler) buildings.insert(temp[i]); } } + { auto a = handler.enterArray("creatures"); a.syncSize(creatures); @@ -250,10 +253,10 @@ void CMap::calculateGuardingGreaturePositions() } } -CGHeroInstance * CMap::getHero(int heroID) +CGHeroInstance * CMap::getHero(HeroTypeID heroID) { for(auto & elem : heroesOnMap) - if(elem->subID == heroID) + if(elem->getHeroType() == heroID) return elem; return nullptr; } @@ -393,7 +396,7 @@ const CGObjectInstance * CMap::getObjectiveObjectFrom(const int3 & pos, Obj type // There is weird bug because of which sometimes heroes will not be found properly despite having correct position // Try to workaround that and find closest object that we can use - logGlobal->error("Failed to find object of type %d at %s", static_cast(type), pos.toString()); + logGlobal->error("Failed to find object of type %d at %s", type.getNum(), pos.toString()); logGlobal->error("Will try to find closest matching object"); CGObjectInstance * bestMatch = nullptr; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index de9d5e5a2..518b1fb6a 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -118,7 +118,7 @@ public: /// Gets object of specified type on requested position const CGObjectInstance * getObjectiveObjectFrom(const int3 & pos, Obj type); - CGHeroInstance * getHero(int heroId); + CGHeroInstance * getHero(HeroTypeID heroId); /// Sets the victory/loss condition objectives ?? void checkForObjectives(); diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 800dc4d55..5bd59ca3b 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -883,7 +883,7 @@ void CMapLoaderH3M::readPredefinedHeroes() } map->predefinedHeroes.emplace_back(hero); - logGlobal->debug("Map '%s': Hero predefined in map: %s", mapName, VLC->heroh->getByIndex(hero->subID)->getJsonKey()); + logGlobal->debug("Map '%s': Hero predefined in map: %s", mapName, VLC->heroh->getById(hero->getHeroType())->getJsonKey()); } } @@ -926,7 +926,7 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot) if(artifactID == ArtifactID::NONE) return false; - const Artifact * art = artifactID.toArtifact(VLC->artifacts()); + const Artifact * art = artifactID.toEntity(VLC); if(!art) { @@ -944,10 +944,9 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot) // He has Shackles of War (normally - MISC slot artifact) in LEFT_HAND slot set in editor // Artifact seems to be missing in game, so skip artifacts that don't fit target slot auto * artifact = ArtifactUtils::createArtifact(map, artifactID); - auto dstLoc = ArtifactLocation(hero, ArtifactPosition(slot)); - if(artifact->canBePutAt(dstLoc)) + if(artifact->canBePutAt(hero, ArtifactPosition(slot))) { - artifact->putAt(dstLoc); + artifact->putAt(*hero, ArtifactPosition(slot)); } else { @@ -1288,7 +1287,7 @@ CGObjectInstance * CMapLoaderH3M::readResource(const int3 & mapPosition, std::sh readMessageAndGuards(object->message, object, mapPosition); object->amount = reader->readUInt32(); - if(objectTemplate->subid == GameResID(EGameResID::GOLD)) + if(GameResID(objectTemplate->subid) == GameResID(EGameResID::GOLD)) { // Gold is multiplied by 100. object->amount *= 100; @@ -1323,60 +1322,34 @@ CGObjectInstance * CMapLoaderH3M::readDwellingRandom(const int3 & mapPosition, s { auto * object = new CGDwelling(); - CSpecObjInfo * spec = nullptr; - switch(objectTemplate->id) - { - case Obj::RANDOM_DWELLING: - spec = new CCreGenLeveledCastleInfo(); - break; - case Obj::RANDOM_DWELLING_LVL: - spec = new CCreGenAsCastleInfo(); - break; - case Obj::RANDOM_DWELLING_FACTION: - spec = new CCreGenLeveledInfo(); - break; - default: - throw std::runtime_error("Invalid random dwelling format"); - } - spec->owner = object; - setOwnerAndValidate(mapPosition, object, reader->readPlayer32()); - //216 and 217 - if(auto * castleSpec = dynamic_cast(spec)) + object->randomizationInfo = CGDwellingRandomizationInfo(); + + bool hasFactionInfo = objectTemplate->id == Obj::RANDOM_DWELLING || objectTemplate->id == Obj::RANDOM_DWELLING_LVL; + bool hasLevelInfo = objectTemplate->id == Obj::RANDOM_DWELLING || objectTemplate->id == Obj::RANDOM_DWELLING_FACTION; + + if (hasFactionInfo) { - castleSpec->instanceId = ""; - castleSpec->identifier = reader->readUInt32(); - if(!castleSpec->identifier) - { - castleSpec->asCastle = false; - const int MASK_SIZE = 8; - ui8 mask[2]; - mask[0] = reader->readUInt8(); - mask[1] = reader->readUInt8(); + object->randomizationInfo->identifier = reader->readUInt32(); - castleSpec->allowedFactions.clear(); - castleSpec->allowedFactions.resize(VLC->townh->size(), false); + if(object->randomizationInfo->identifier == 0) + reader->readBitmaskFactions(object->randomizationInfo->allowedFactions, false); + } + else + object->randomizationInfo->allowedFactions.insert(FactionID(objectTemplate->subid)); - for(int i = 0; i < MASK_SIZE; i++) - castleSpec->allowedFactions[i] = ((mask[0] & (1 << i)) > 0); - - for(int i = 0; i < (GameConstants::F_NUMBER - MASK_SIZE); i++) - castleSpec->allowedFactions[i + MASK_SIZE] = ((mask[1] & (1 << i)) > 0); - } - else - { - castleSpec->asCastle = true; - } + if(hasLevelInfo) + { + object->randomizationInfo->minLevel = std::max(reader->readUInt8(), static_cast(0)) + 1; + object->randomizationInfo->maxLevel = std::min(reader->readUInt8(), static_cast(6)) + 1; + } + else + { + object->randomizationInfo->minLevel = objectTemplate->subid; + object->randomizationInfo->maxLevel = objectTemplate->subid; } - //216 and 218 - if(auto * lvlSpec = dynamic_cast(spec)) - { - lvlSpec->minLevel = std::max(reader->readUInt8(), static_cast(0)) + 1; - lvlSpec->maxLevel = std::min(reader->readUInt8(), static_cast(6)) + 1; - } - object->info = spec; return object; } @@ -1515,7 +1488,7 @@ CGObjectInstance * CMapLoaderH3M::readBank(const int3 & mapPosition, std::shared CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr objectTemplate, const int3 & mapPosition, const ObjectInstanceID & objectInstanceID) { - switch(objectTemplate->id) + switch(objectTemplate->id.toEnum()) { case Obj::EVENT: return readEvent(mapPosition, objectInstanceID); @@ -1767,7 +1740,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec for(auto & elem : map->disposedHeroes) { - if(elem.heroId.getNum() == object->subID) + if(elem.heroId == object->getHeroType()) { object->nameCustomTextId = elem.name; object->customPortraitSource = elem.portrait; @@ -1777,7 +1750,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec bool hasName = reader->readBool(); if(hasName) - object->nameCustomTextId = readLocalizedString(TextIdentifier("heroes", object->subID, "name")); + object->nameCustomTextId = readLocalizedString(TextIdentifier("heroes", object->getHeroType().getNum(), "name")); if(features.levelSOD) { @@ -1888,8 +1861,8 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec } } - if (object->subID != -1) - logGlobal->debug("Map '%s': Hero on map: %s at %s, owned by %s", mapName, VLC->heroh->getByIndex(object->subID)->getJsonKey(), mapPosition.toString(), object->getOwner().toString()); + if (object->subID != MapObjectSubID()) + logGlobal->debug("Map '%s': Hero on map: %s at %s, owned by %s", mapName, VLC->heroh->getById(object->getHeroType())->getJsonKey(), mapPosition.toString(), object->getOwner().toString()); else logGlobal->debug("Map '%s': Hero on map: (random) at %s, owned by %s", mapName, mapPosition.toString(), object->getOwner().toString()); diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index 0208204a1..0f5691da8 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -171,10 +171,10 @@ namespace TriggeredEventsDetail case EMetaclass::OBJECT: { //TODO - std::set subtypes = VLC->objtypeh->knownSubObjects(type); + auto subtypes = VLC->objtypeh->knownSubObjects(type); if(!subtypes.empty()) { - si32 subtype = *subtypes.begin(); + auto subtype = *subtypes.begin(); auto handler = VLC->objtypeh->getHandlerFor(type, subtype); identifier = handler->getTypeName(); } @@ -831,7 +831,7 @@ void CMapFormatJson::serializeOptions(JsonSerializeFormat & handler) serializePredefinedHeroes(handler); - handler.serializeLIC("allowedAbilities", &CSkillHandler::decodeSkill, &CSkillHandler::encodeSkill, VLC->skillh->getDefaultAllowed(), map->allowedAbilities); + handler.serializeLIC("allowedAbilities", &SecondarySkill::decode, &SecondarySkill::encode, VLC->skillh->getDefaultAllowed(), map->allowedAbilities); handler.serializeLIC("allowedArtifacts", &ArtifactID::decode, &ArtifactID::encode, VLC->arth->getDefaultAllowed(), map->allowedArtifact); @@ -1229,7 +1229,7 @@ void CMapLoaderJson::MapObjectLoader::configure() else if(art->ID == Obj::ARTIFACT) { //specific artifact - artID = ArtifactID(art->subID); + artID = art->getArtifact(); } art->storedArtifact = ArtifactUtils::createArtifact(owner->map, artID, spellID.getNum()); @@ -1263,7 +1263,7 @@ void CMapLoaderJson::readObjects() std::sort(map->heroesOnMap.begin(), map->heroesOnMap.end(), [](const ConstTransitivePtr & a, const ConstTransitivePtr & b) { - return a->subID < b->subID; + return a->getObjTypeIndex() < b->getObjTypeIndex(); }); } diff --git a/lib/networkPacks/ArtifactLocation.h b/lib/networkPacks/ArtifactLocation.h index 777a1fdf5..085d295d0 100644 --- a/lib/networkPacks/ArtifactLocation.h +++ b/lib/networkPacks/ArtifactLocation.h @@ -9,67 +9,34 @@ */ #pragma once -#include "../ConstTransitivePtr.h" #include "../constants/EntityIdentifiers.h" VCMI_LIB_NAMESPACE_BEGIN -class CGHeroInstance; -class CStackInstance; -class CArmedInstance; -class CArtifactSet; -class CBonusSystemNode; -struct ArtSlotInfo; - -using TArtHolder = std::variant, ConstTransitivePtr>; - struct ArtifactLocation { - TArtHolder artHolder;//TODO: identify holder by id - ArtifactPosition slot = ArtifactPosition::PRE_FIRST; + ObjectInstanceID artHolder; + ArtifactPosition slot; + std::optional creature; ArtifactLocation() - : artHolder(ConstTransitivePtr()) + : artHolder(ObjectInstanceID::NONE) + , slot(ArtifactPosition::PRE_FIRST) + , creature(std::nullopt) { } - template - ArtifactLocation(const T * ArtHolder, ArtifactPosition Slot) - : artHolder(const_cast(ArtHolder)) //we are allowed here to const cast -> change will go through one of our packages... do not abuse! - , slot(Slot) - { - } - ArtifactLocation(TArtHolder ArtHolder, const ArtifactPosition & Slot) - : artHolder(std::move(std::move(ArtHolder))) - , slot(Slot) + ArtifactLocation(const ObjectInstanceID id, const ArtifactPosition & slot = ArtifactPosition::PRE_FIRST) + : artHolder(id) + , slot(slot) + , creature(std::nullopt) { } - template - bool isHolder(const T *t) const - { - if(auto ptrToT = std::get>(artHolder)) - { - return ptrToT == t; - } - return false; - } - - DLL_LINKAGE void removeArtifact(); // BE CAREFUL, this operation modifies holder (gs) - - DLL_LINKAGE const CArmedInstance *relatedObj() const; //hero or the stack owner - DLL_LINKAGE PlayerColor owningPlayer() const; - DLL_LINKAGE CArtifactSet *getHolderArtSet(); - DLL_LINKAGE CBonusSystemNode *getHolderNode(); - DLL_LINKAGE CArtifactSet *getHolderArtSet() const; - DLL_LINKAGE const CBonusSystemNode *getHolderNode() const; - - DLL_LINKAGE const CArtifactInstance *getArt() const; - DLL_LINKAGE CArtifactInstance *getArt(); - DLL_LINKAGE const ArtSlotInfo *getSlot() const; - template void serialize(Handler &h, const int version) + template void serialize(Handler & h, const int version) { h & artHolder; h & slot; + h & creature; } }; diff --git a/lib/networkPacks/Component.h b/lib/networkPacks/Component.h index fa764e085..43ec61277 100644 --- a/lib/networkPacks/Component.h +++ b/lib/networkPacks/Component.h @@ -9,44 +9,67 @@ */ #pragma once +#include "../constants/VariantIdentifier.h" +#include "../constants/EntityIdentifiers.h" + VCMI_LIB_NAMESPACE_BEGIN -class CStackBasicDescriptor; +enum class ComponentType : int8_t +{ + NONE = -1, + PRIM_SKILL, + SEC_SKILL, + RESOURCE, + RESOURCE_PER_DAY, + CREATURE, + ARTIFACT, + SPELL_SCROLL, + MANA, + EXPERIENCE, + LEVEL, + SPELL, + MORALE, + LUCK, + BUILDING, + HERO_PORTRAIT, + FLAG +}; + +using ComponentSubType = VariantIdentifier; struct Component { - enum class EComponentType : uint8_t - { - PRIM_SKILL, - SEC_SKILL, - RESOURCE, - CREATURE, - ARTIFACT, - EXPERIENCE, - SPELL, - MORALE, - LUCK, - BUILDING, - HERO_PORTRAIT, - FLAG, - INVALID //should be last - }; - EComponentType id = EComponentType::INVALID; - ui16 subtype = 0; //id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels - si32 val = 0; // + give; - take - si16 when = 0; // 0 - now; +x - within x days; -x - per x days + ComponentType type = ComponentType::NONE; + ComponentSubType subType; + std::optional value; // + give; - take template void serialize(Handler &h, const int version) { - h & id; - h & subtype; - h & val; - h & when; + h & type; + h & subType; + h & value; } + Component() = default; - DLL_LINKAGE explicit Component(const CStackBasicDescriptor &stack); - Component(Component::EComponentType Type, ui16 Subtype, si32 Val, si16 When) - :id(Type),subtype(Subtype),val(Val),when(When) + + template, bool> = true> + Component(ComponentType type, Numeric value) + : type(type) + , value(value) + { + } + + template, bool> = true> + Component(ComponentType type, IdentifierType subType) + : type(type) + , subType(subType) + { + } + + Component(ComponentType type, ComponentSubType subType, int32_t value) + : type(type) + , subType(subType) + , value(value) { } }; diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index cc6e6a4d4..c7041fd2e 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -944,7 +944,7 @@ void FoWChange::applyGs(CGameState *gs) const CGObjectInstance *o = elem; if (o) { - switch(o->ID) + switch(o->ID.toEnum()) { case Obj::HERO: case Obj::MINE: @@ -1487,6 +1487,7 @@ void NewObject::applyGs(CGameState *gs) CGObjectInstance * o = handler->create(); handler->configureObject(o, gs->getRandomGenerator()); + assert(o->ID == this->ID); if (ID == Obj::MONSTER) //probably more options will be needed { @@ -1514,8 +1515,6 @@ void NewObject::applyGs(CGameState *gs) o->appearance = handler->getTemplates().front(); o->id = ObjectInstanceID(static_cast(gs->map->objects.size())); - o->ID = ID; - o->subID = subID; o->pos = targetPos + o->getVisitableOffset(); gs->map->objects.emplace_back(o); @@ -1574,67 +1573,6 @@ struct GetBase } }; - -void ArtifactLocation::removeArtifact() -{ - CArtifactInstance *a = getArt(); - assert(a); - a->removeFrom(*this); -} - -const CArmedInstance * ArtifactLocation::relatedObj() const -{ - return std::visit(ObjectRetriever(), artHolder); -} - -PlayerColor ArtifactLocation::owningPlayer() const -{ - const auto * obj = relatedObj(); - return obj ? obj->tempOwner : PlayerColor::NEUTRAL; -} - -CArtifactSet *ArtifactLocation::getHolderArtSet() -{ - return std::visit(GetBase(), artHolder); -} - -CBonusSystemNode *ArtifactLocation::getHolderNode() -{ - return std::visit(GetBase(), artHolder); -} - -const CArtifactInstance *ArtifactLocation::getArt() const -{ - const auto * s = getSlot(); - if(s) - return s->getArt(); - else - return nullptr; -} - -CArtifactSet * ArtifactLocation::getHolderArtSet() const -{ - auto * t = const_cast(this); - return t->getHolderArtSet(); -} - -const CBonusSystemNode * ArtifactLocation::getHolderNode() const -{ - auto * t = const_cast(this); - return t->getHolderNode(); -} - -CArtifactInstance *ArtifactLocation::getArt() -{ - const ArtifactLocation *t = this; - return const_cast(t->getArt()); -} - -const ArtSlotInfo *ArtifactLocation::getSlot() const -{ - return getHolderArtSet()->getSlot(slot); -} - void ChangeStackCount::applyGs(CGameState * gs) { auto * srcObj = gs->getArmyInstance(army); @@ -1709,39 +1647,40 @@ void RebalanceStacks::applyGs(CGameState * gs) if(srcCount == count) //moving whole stack { - [[maybe_unused]] const CCreature *c = dst.army->getCreature(dst.slot); + const auto c = dst.army->getCreature(dst.slot); if(c) //stack at dest -> merge { assert(c == srcType); - auto alHere = ArtifactLocation (src.getStack(), ArtifactPosition::CREATURE_SLOT); - auto alDest = ArtifactLocation (dst.getStack(), ArtifactPosition::CREATURE_SLOT); - auto * artHere = alHere.getArt(); - auto * artDest = alDest.getArt(); - if (artHere) + + const auto srcHero = dynamic_cast(src.army.get()); + const auto dstHero = dynamic_cast(dst.army.get()); + auto srcStack = const_cast(src.getStack()); + auto dstStack = const_cast(dst.getStack()); + if(auto srcArt = srcStack->getArt(ArtifactPosition::CREATURE_SLOT)) { - if (alDest.getArt()) + if(auto dstArt = dstStack->getArt(ArtifactPosition::CREATURE_SLOT)) { - auto * hero = dynamic_cast(src.army.get()); - auto dstSlot = ArtifactUtils::getArtBackpackPosition(hero, alDest.getArt()->getTypeId()); - if(hero && dstSlot != ArtifactPosition::PRE_FIRST) + auto dstSlot = ArtifactUtils::getArtBackpackPosition(srcHero, dstArt->getTypeId()); + if(srcHero && dstSlot != ArtifactPosition::PRE_FIRST) { - artDest->move (alDest, ArtifactLocation (hero, dstSlot)); + dstArt->move(*dstStack, ArtifactPosition::CREATURE_SLOT, *srcHero, dstSlot); } //else - artifact cna be lost :/ else { EraseArtifact ea; - ea.al = alDest; + ea.al = ArtifactLocation(dstHero->id, ArtifactPosition::CREATURE_SLOT); + ea.al.creature = dst.slot; ea.applyGs(gs); logNetwork->warn("Cannot move artifact! No free slots"); } - artHere->move (alHere, alDest); + srcArt->move(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT); //TODO: choose from dialog } else //just move to the other slot before stack gets erased { - artHere->move (alHere, alDest); + srcArt->move(*srcStack, ArtifactPosition::CREATURE_SLOT, *dstStack, ArtifactPosition::CREATURE_SLOT); } } if (stackExp) @@ -1811,53 +1750,57 @@ void BulkSmartRebalanceStacks::applyGs(CGameState * gs) void PutArtifact::applyGs(CGameState *gs) { - assert(art->canBePutAt(al)); // Ensure that artifact has been correctly added via NewArtifact pack assert(vstd::contains(gs->map->artInstances, art)); assert(!art->getParentNodes().empty()); - art->putAt(al); + auto hero = gs->getHero(al.artHolder); + assert(hero); + assert(art && art->canBePutAt(hero, al.slot)); + art->putAt(*hero, al.slot); } void EraseArtifact::applyGs(CGameState *gs) { - const auto * slot = al.getSlot(); + const auto hero = gs->getHero(al.artHolder); + assert(hero); + const auto slot = hero->getSlot(al.slot); if(slot->locked) { logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->getNameTranslated()); DisassembledArtifact dis; dis.al.artHolder = al.artHolder; - auto * aset = al.getHolderArtSet(); - #ifndef NDEBUG - bool found = false; - #endif - for(auto& p : aset->artifactsWorn) + + for(auto & slotInfo : hero->artifactsWorn) { - auto art = p.second.artifact; + auto art = slotInfo.second.artifact; if(art->isCombined() && art->isPart(slot->artifact)) { - dis.al.slot = aset->getArtPos(art); - #ifndef NDEBUG - found = true; - #endif + dis.al.slot = hero->getArtPos(art); break; } } - assert(found && "Failed to determine the assembly this locked artifact belongs to"); - logGlobal->debug("Found the corresponding assembly: %s", dis.al.getSlot()->artifact->artType->getNameTranslated()); + assert((dis.al.slot != ArtifactPosition::PRE_FIRST) && "Failed to determine the assembly this locked artifact belongs to"); + logGlobal->debug("Found the corresponding assembly: %s", hero->getArt(dis.al.slot)->artType->getNameTranslated()); dis.applyGs(gs); } else { logGlobal->debug("Erasing artifact %s", slot->artifact->artType->getNameTranslated()); } - al.removeArtifact(); + auto art = hero->getArt(al.slot); + assert(art); + art->removeFrom(*hero, al.slot); } void MoveArtifact::applyGs(CGameState * gs) { - CArtifactInstance * art = src.getArt(); - assert(!ArtifactUtils::isSlotEquipment(dst.slot) || !dst.getArt()); - art->move(src, dst); + auto srcHero = gs->getArtSet(src); + auto dstHero = gs->getArtSet(dst); + assert(srcHero); + assert(dstHero); + auto art = srcHero->getArt(src.slot); + assert(art && art->canBePutAt(dstHero, dst.slot)); + art->move(*srcHero, src.slot, *dstHero, dst.slot); } void BulkMoveArtifacts::applyGs(CGameState * gs) @@ -1869,8 +1812,8 @@ void BulkMoveArtifacts::applyGs(CGameState * gs) BULK_PUT }; - auto bulkArtsOperation = [this](std::vector & artsPack, - CArtifactSet * artSet, EBulkArtsOp operation) -> void + auto bulkArtsOperation = [this, gs](std::vector & artsPack, + CArtifactSet & artSet, EBulkArtsOp operation) -> void { int numBackpackArtifactsMoved = 0; for(auto & slot : artsPack) @@ -1883,21 +1826,18 @@ void BulkMoveArtifacts::applyGs(CGameState * gs) { srcPos = ArtifactPosition(srcPos.num - numBackpackArtifactsMoved); } - const auto * slotInfo = artSet->getSlot(srcPos); - assert(slotInfo); - auto * art = const_cast(slotInfo->getArt()); + auto * art = artSet.getArt(srcPos); assert(art); switch(operation) { case EBulkArtsOp::BULK_MOVE: - const_cast(art)->move( - ArtifactLocation(srcArtHolder, srcPos), ArtifactLocation(dstArtHolder, slot.dstPos)); + art->move(artSet, srcPos, *gs->getHero(dstArtHolder), slot.dstPos); break; case EBulkArtsOp::BULK_REMOVE: - art->removeFrom(ArtifactLocation(dstArtHolder, srcPos)); + art->removeFrom(artSet, srcPos); break; case EBulkArtsOp::BULK_PUT: - art->putAt(ArtifactLocation(srcArtHolder, slot.dstPos)); + art->putAt(*gs->getHero(srcArtHolder), slot.dstPos); break; default: break; @@ -1910,37 +1850,38 @@ void BulkMoveArtifacts::applyGs(CGameState * gs) } }; + auto * leftSet = gs->getArtSet(ArtifactLocation(srcArtHolder)); if(swap) { // Swap - auto * leftSet = getSrcHolderArtSet(); - auto * rightSet = getDstHolderArtSet(); + auto * rightSet = gs->getArtSet(ArtifactLocation(dstArtHolder)); CArtifactFittingSet artFittingSet(leftSet->bearerType()); artFittingSet.artifactsWorn = rightSet->artifactsWorn; artFittingSet.artifactsInBackpack = rightSet->artifactsInBackpack; - bulkArtsOperation(artsPack1, rightSet, EBulkArtsOp::BULK_REMOVE); - bulkArtsOperation(artsPack0, leftSet, EBulkArtsOp::BULK_MOVE); - bulkArtsOperation(artsPack1, &artFittingSet, EBulkArtsOp::BULK_PUT); + bulkArtsOperation(artsPack1, *rightSet, EBulkArtsOp::BULK_REMOVE); + bulkArtsOperation(artsPack0, *leftSet, EBulkArtsOp::BULK_MOVE); + bulkArtsOperation(artsPack1, artFittingSet, EBulkArtsOp::BULK_PUT); } else { - bulkArtsOperation(artsPack0, getSrcHolderArtSet(), EBulkArtsOp::BULK_MOVE); + bulkArtsOperation(artsPack0, *leftSet, EBulkArtsOp::BULK_MOVE); } } void AssembledArtifact::applyGs(CGameState *gs) { - CArtifactSet * artSet = al.getHolderArtSet(); - const CArtifactInstance * transformedArt = al.getArt(); + auto hero = gs->getHero(al.artHolder); + assert(hero); + const auto transformedArt = hero->getArt(al.slot); assert(transformedArt); - assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->getTypeId()), [=](const CArtifact * art)->bool + assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(hero, transformedArt->getTypeId()), [=](const CArtifact * art)->bool { return art->getId() == builtArt->getId(); })); - const auto transformedArtSlot = artSet->getSlotByInstance(transformedArt); + const auto transformedArtSlot = hero->getSlotByInstance(transformedArt); auto * combinedArt = new CArtifactInstance(builtArt); gs->map->addNewArtifactInstance(combinedArt); @@ -1952,7 +1893,7 @@ void AssembledArtifact::applyGs(CGameState *gs) if(transformedArt->getTypeId() == constituent->getId()) slot = transformedArtSlot; else - slot = artSet->getArtPos(constituent->getId(), false, false); + slot = hero->getArtPos(constituent->getId(), false, false); assert(slot != ArtifactPosition::PRE_FIRST); slotsInvolved.emplace_back(slot); @@ -1972,8 +1913,8 @@ void AssembledArtifact::applyGs(CGameState *gs) break; } - if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), al.slot) - && vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), slot)) + if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(hero->bearerType()), al.slot) + && vstd::contains(combinedArt->artType->getPossibleSlots().at(hero->bearerType()), slot)) al.slot = slot; } else @@ -1986,8 +1927,8 @@ void AssembledArtifact::applyGs(CGameState *gs) // Delete parts from hero for(const auto slot : slotsInvolved) { - const auto constituentInstance = artSet->getArt(slot); - constituentInstance->removeFrom(ArtifactLocation(al.artHolder, slot)); + const auto constituentInstance = hero->getArt(slot); + constituentInstance->removeFrom(*hero, slot); if(ArtifactUtils::isSlotEquipment(al.slot) && slot != al.slot) combinedArt->addPart(constituentInstance, slot); @@ -1996,25 +1937,26 @@ void AssembledArtifact::applyGs(CGameState *gs) } // Put new combined artifacts - combinedArt->putAt(al); + combinedArt->putAt(*hero, al.slot); } void DisassembledArtifact::applyGs(CGameState *gs) { - auto * disassembled = al.getArt(); - assert(disassembled); + auto hero = gs->getHero(al.artHolder); + assert(hero); + auto disassembledArt = hero->getArt(al.slot); + assert(disassembledArt); - auto parts = disassembled->getPartsInfo(); - disassembled->removeFrom(al); + auto parts = disassembledArt->getPartsInfo(); + disassembledArt->removeFrom(*hero, al.slot); for(auto & part : parts) { - ArtifactLocation partLoc = al; // ArtifactPosition::PRE_FIRST is value of main part slot -> it'll replace combined artifact in its pos - partLoc.slot = (ArtifactUtils::isSlotEquipment(part.slot) ? part.slot : al.slot); - disassembled->detachFrom(*part.art); - part.art->putAt(partLoc); + auto slot = (ArtifactUtils::isSlotEquipment(part.slot) ? part.slot : al.slot); + disassembledArt->detachFrom(*part.art); + part.art->putAt(*hero, slot); } - gs->map->eraseArtifactInstance(disassembled); + gs->map->eraseArtifactInstance(disassembledArt); } void HeroVisit::applyGs(CGameState *gs) @@ -2171,7 +2113,7 @@ void BattleStart::applyGs(CGameState * gs) const info->battleID = gs->nextBattleID; info->localInit(); - gs->nextBattleID = vstd::next(gs->nextBattleID, 1); + gs->nextBattleID = BattleID(gs->nextBattleID.getNum() + 1); } void BattleNextRound::applyGs(CGameState * gs) const @@ -2580,13 +2522,6 @@ void TurnTimeUpdate::applyGs(CGameState *gs) const playerState.turnTimer = turnTimer; } -Component::Component(const CStackBasicDescriptor & stack) - : id(EComponentType::CREATURE) - , subtype(stack.type->getId()) - , val(stack.count) -{ -} - void EntitiesChanged::applyGs(CGameState * gs) { for(const auto & change : changes) @@ -2603,14 +2538,4 @@ const CArtifactInstance * ArtSlotInfo::getArt() const return artifact; } -CArtifactSet * BulkMoveArtifacts::getSrcHolderArtSet() -{ - return std::visit(GetBase(), srcArtHolder); -} - -CArtifactSet * BulkMoveArtifacts::getDstHolderArtSet() -{ - return std::visit(GetBase(), dstArtHolder); -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 31bc4752e..f768c297d 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -1060,14 +1060,16 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack } }; - TArtHolder srcArtHolder; - TArtHolder dstArtHolder; + ObjectInstanceID srcArtHolder; + ObjectInstanceID dstArtHolder; BulkMoveArtifacts() - : swap(false) + : srcArtHolder(ObjectInstanceID::NONE) + , dstArtHolder(ObjectInstanceID::NONE) + , swap(false) { } - BulkMoveArtifacts(TArtHolder srcArtHolder, TArtHolder dstArtHolder, bool swap) + BulkMoveArtifacts(const ObjectInstanceID srcArtHolder, const ObjectInstanceID dstArtHolder, bool swap) : srcArtHolder(std::move(srcArtHolder)) , dstArtHolder(std::move(dstArtHolder)) , swap(swap) @@ -1079,8 +1081,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack std::vector artsPack0; std::vector artsPack1; bool swap; - CArtifactSet * getSrcHolderArtSet(); - CArtifactSet * getDstHolderArtSet(); void visitTyped(ICPackVisitor & visitor) override; diff --git a/lib/pathfinder/CGPathNode.h b/lib/pathfinder/CGPathNode.h index ad6e90383..15363f419 100644 --- a/lib/pathfinder/CGPathNode.h +++ b/lib/pathfinder/CGPathNode.h @@ -197,7 +197,7 @@ struct DLL_LINKAGE CPathsInfo STRONG_INLINE CGPathNode * getNode(const int3 & coord, const ELayer layer) { - return &nodes[layer][coord.z][coord.x][coord.y]; + return &nodes[layer.getNum()][coord.z][coord.x][coord.y]; } }; diff --git a/lib/pathfinder/CPathfinder.cpp b/lib/pathfinder/CPathfinder.cpp index ee237bfe5..61cf7aa48 100644 --- a/lib/pathfinder/CPathfinder.cpp +++ b/lib/pathfinder/CPathfinder.cpp @@ -260,15 +260,18 @@ std::vector CPathfinderHelper::getTeleportExits(const PathNodeInfo & sourc teleportationExits.push_back(exit); } } - else if(options.useCastleGate - && (source.nodeObject->ID == Obj::TOWN && source.nodeObject->subID == ETownType::INFERNO - && source.objectRelations != PlayerRelations::ENEMIES)) + else if(options.useCastleGate && source.nodeObject->ID == Obj::TOWN && source.objectRelations != PlayerRelations::ENEMIES) { - /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo - /// This may be handy if we allow to use teleportation to friendly towns - for(const auto & exit : getCastleGates(source)) + auto * town = dynamic_cast(source.nodeObject); + assert(town); + if (town && town->getFaction() == FactionID::INFERNO) { - teleportationExits.push_back(exit); + /// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo + /// This may be handy if we allow to use teleportation to friendly towns + for(const auto & exit : getCastleGates(source)) + { + teleportationExits.push_back(exit); + } } } @@ -299,7 +302,7 @@ bool CPathfinder::isLayerTransitionPossible() const if(source.node->action == EPathNodeAction::BATTLE) return false; - switch(source.node->layer) + switch(source.node->layer.toEnum()) { case ELayer::LAND: if(destLayer == ELayer::AIR) @@ -502,7 +505,7 @@ void CPathfinderHelper::updateTurnInfo(const int Turn) bool CPathfinderHelper::isLayerAvailable(const EPathfindingLayer & layer) const { - switch(layer) + switch(layer.toEnum()) { case EPathfindingLayer::AIR: if(!options.useFlying) diff --git a/lib/pathfinder/PathfindingRules.cpp b/lib/pathfinder/PathfindingRules.cpp index 48aaf7814..d041aff85 100644 --- a/lib/pathfinder/PathfindingRules.cpp +++ b/lib/pathfinder/PathfindingRules.cpp @@ -123,7 +123,7 @@ void DestinationActionRule::process( EPathNodeAction action = EPathNodeAction::NORMAL; const auto * hero = pathfinderHelper->hero; - switch(destination.node->layer) + switch(destination.node->layer.toEnum()) { case EPathfindingLayer::LAND: if(source.node->layer == EPathfindingLayer::SAIL) @@ -290,7 +290,7 @@ PathfinderBlockingRule::BlockingReason MovementToDestinationRule::getBlockingRea if(destination.node->accessible == EPathAccessibility::BLOCKED) return BlockingReason::DESTINATION_BLOCKED; - switch(destination.node->layer) + switch(destination.node->layer.toEnum()) { case EPathfindingLayer::LAND: if(!pathfinderHelper->canMoveBetween(source.coord, destination.coord)) @@ -359,7 +359,7 @@ void LayerTransitionRule::process( if(source.node->layer == destination.node->layer) return; - switch(source.node->layer) + switch(source.node->layer.toEnum()) { case EPathfindingLayer::LAND: if(destination.node->layer == EPathfindingLayer::SAIL) diff --git a/lib/pathfinder/TurnInfo.cpp b/lib/pathfinder/TurnInfo.cpp index 01ba846ed..8b8763cdb 100644 --- a/lib/pathfinder/TurnInfo.cpp +++ b/lib/pathfinder/TurnInfo.cpp @@ -47,7 +47,7 @@ TurnInfo::TurnInfo(const CGHeroInstance * Hero, const int turn): bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const { - switch(layer) + switch(layer.toEnum()) { case EPathfindingLayer::AIR: if(hero && hero->boat && hero->boat->layer == EPathfindingLayer::AIR) diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index aadf30285..cb3a61ac4 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -201,7 +201,7 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand CreatureID from(VLC->identifiers()->getIdentifier(node.second.meta, "creature", node.first).value()); CreatureID dest(VLC->identifiers()->getIdentifier(node.second.meta, "creature", node.second.String()).value()); - reward.extraComponents.emplace_back(Component::EComponentType::CREATURE, dest.getNum(), 0, 0); + reward.extraComponents.emplace_back(ComponentType::CREATURE, dest); reward.creaturesChange[from] = dest; } @@ -234,7 +234,7 @@ void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CR value = JsonRandom::loadSpell(input, rng, object.variables.values).getNum(); if (category.first == "primarySkill") - value = static_cast(JsonRandom::loadPrimary(input, rng, object.variables.values)); + value = JsonRandom::loadPrimary(input, rng, object.variables.values).getNum(); if (category.first == "secondarySkill") value = JsonRandom::loadSecondary(input, rng, object.variables.values).getNum(); diff --git a/lib/rewardable/Interface.cpp b/lib/rewardable/Interface.cpp index da7f86520..b1f41e8ce 100644 --- a/lib/rewardable/Interface.cpp +++ b/lib/rewardable/Interface.cpp @@ -35,7 +35,7 @@ std::vector Rewardable::Interface::getAvailableRewards(const CGHeroInstanc { const Rewardable::VisitInfo & visit = configuration.info[i]; - if(event == visit.visitType && visit.limiter.heroAllowed(hero)) + if(event == visit.visitType && (!hero || visit.limiter.heroAllowed(hero))) { logGlobal->trace("Reward %d is allowed", i); ret.push_back(static_cast(i)); diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp index ae6602231..05c77909a 100644 --- a/lib/rewardable/Limiter.cpp +++ b/lib/rewardable/Limiter.cpp @@ -128,7 +128,7 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const for(const auto & spell : canLearnSpells) { - if (!hero->canLearnSpell(spell.toSpell(VLC->spells()), true)) + if (!hero->canLearnSpell(spell.toEntity(VLC), true)) return false; } @@ -187,49 +187,45 @@ void Rewardable::Limiter::loadComponents(std::vector & comps, const CGHeroInstance * h) const { if (heroExperience) - comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h->calculateXp(heroExperience)), 0); + comps.emplace_back(ComponentType::EXPERIENCE, static_cast(h->calculateXp(heroExperience))); if (heroLevel > 0) - comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0); + comps.emplace_back(ComponentType::EXPERIENCE, heroLevel); if (manaPoints || manaPercentage > 0) { int absoluteMana = h->manaLimit() ? (manaPercentage * h->mana / h->manaLimit() / 100) : 0; - comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, absoluteMana + manaPoints, 0); + comps.emplace_back(ComponentType::MANA, absoluteMana + manaPoints); } for (size_t i=0; i(i), primary[i], 0); + comps.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill(i), primary[i]); } for(const auto & entry : secondary) - comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0); + comps.emplace_back(ComponentType::SEC_SKILL, entry.first, entry.second); for(const auto & entry : artifacts) - comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0); + comps.emplace_back(ComponentType::ARTIFACT, entry); for(const auto & entry : spells) - comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0); + comps.emplace_back(ComponentType::SPELL, entry); for(const auto & entry : creatures) - comps.emplace_back(Component::EComponentType::CREATURE, entry.type->getId(), entry.count, 0); + comps.emplace_back(ComponentType::CREATURE, entry.type->getId(), entry.count); for(const auto & entry : players) - comps.emplace_back(Component::EComponentType::FLAG, entry, 0, 0); + comps.emplace_back(ComponentType::FLAG, entry); - //FIXME: portrait may not match hero, if custom portrait was set in map editor for(const auto & entry : heroes) - comps.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroTypes()->getById(entry)->getIconIndex(), 0, 0); + comps.emplace_back(ComponentType::HERO_PORTRAIT, entry); - for(const auto & entry : heroClasses) - comps.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroClasses()->getById(entry)->getIconIndex(), 0, 0); - for (size_t i=0; i(i), resources[i], 0); + comps.emplace_back(ComponentType::RESOURCE, GameResID(i), resources[i]); } } @@ -253,7 +249,7 @@ void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler) std::vector> fieldValue(secondary.begin(), secondary.end()); a.serializeStruct>(fieldValue, [](JsonSerializeFormat & h, std::pair & e) { - h.serializeId("skill", e.first, SecondarySkill{}, VLC->skillh->decodeSkill, VLC->skillh->encodeSkill); + h.serializeId("skill", e.first, SecondarySkill(SecondarySkill::NONE)); h.serializeId("level", e.second, 0, [](const std::string & i){return vstd::find_pos(NSecondarySkill::levels, i);}, [](si32 i){return NSecondarySkill::levels.at(i);}); }); a.syncSize(fieldValue); diff --git a/lib/rewardable/Reward.cpp b/lib/rewardable/Reward.cpp index cb080adca..c56837e57 100644 --- a/lib/rewardable/Reward.cpp +++ b/lib/rewardable/Reward.cpp @@ -74,42 +74,42 @@ void Rewardable::Reward::loadComponents(std::vector & comps, const CG for (auto & bonus : bonuses) { if (bonus.type == BonusType::MORALE) - comps.emplace_back(Component::EComponentType::MORALE, 0, bonus.val, 0); + comps.emplace_back(ComponentType::MORALE, bonus.val); if (bonus.type == BonusType::LUCK) - comps.emplace_back(Component::EComponentType::LUCK, 0, bonus.val, 0); + comps.emplace_back(ComponentType::LUCK, bonus.val); } if (heroExperience) - comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(h ? h->calculateXp(heroExperience) : heroExperience), 0); + comps.emplace_back(ComponentType::EXPERIENCE, static_cast(h ? h->calculateXp(heroExperience) : heroExperience)); if (heroLevel) - comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0); + comps.emplace_back(ComponentType::LEVEL, heroLevel); if (manaDiff || manaPercentage >= 0) - comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, h ? (calculateManaPoints(h) - h->mana) : manaDiff, 0); + comps.emplace_back(ComponentType::MANA, h ? (calculateManaPoints(h) - h->mana) : manaDiff); for (size_t i=0; i(i), primary[i], 0); + comps.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill(i), primary[i]); } for(const auto & entry : secondary) - comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0); + comps.emplace_back(ComponentType::SEC_SKILL, entry.first, entry.second); for(const auto & entry : artifacts) - comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0); + comps.emplace_back(ComponentType::ARTIFACT, entry); for(const auto & entry : spells) - comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0); + comps.emplace_back(ComponentType::SPELL, entry); for(const auto & entry : creatures) - comps.emplace_back(Component::EComponentType::CREATURE, entry.type->getId(), entry.count, 0); + comps.emplace_back(ComponentType::CREATURE, entry.type->getId(), entry.count); for (size_t i=0; i(i), resources[i], 0); + comps.emplace_back(ComponentType::RESOURCE, GameResID(i), resources[i]); } } @@ -133,7 +133,7 @@ void Rewardable::Reward::serializeJson(JsonSerializeFormat & handler) std::vector> fieldValue(secondary.begin(), secondary.end()); a.serializeStruct>(fieldValue, [](JsonSerializeFormat & h, std::pair & e) { - h.serializeId("skill", e.first, SecondarySkill{}, VLC->skillh->decodeSkill, VLC->skillh->encodeSkill); + h.serializeId("skill", e.first); h.serializeId("level", e.second, 0, [](const std::string & i){return vstd::find_pos(NSecondarySkill::levels, i);}, [](si32 i){return NSecondarySkill::levels.at(i);}); }); a.syncSize(fieldValue); diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index c963e5aad..d1bae95eb 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -217,6 +217,19 @@ void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value void CMapGenOptions::initPlayersMap() { + + std::map rememberTownTypes; + std::map rememberTeam; + + for(const auto & p : players) + { + auto town = p.second.getStartingTown(); + if (town != FactionID::RANDOM) + rememberTownTypes[p.first] = FactionID(town); + rememberTeam[p.first] = p.second.getTeam(); + } + + players.clear(); int realPlayersCnt = getHumanOrCpuPlayerCount(); @@ -368,7 +381,7 @@ const std::map & CMapGenOptions::g return players; } -void CMapGenOptions::setStartingTownForPlayer(const PlayerColor & color, si32 town) +void CMapGenOptions::setStartingTownForPlayer(const PlayerColor & color, FactionID town) { auto it = players.find(color); assert(it != players.end()); @@ -755,17 +768,17 @@ void CMapGenOptions::CPlayerSettings::setColor(const PlayerColor & value) color = value; } -si32 CMapGenOptions::CPlayerSettings::getStartingTown() const +FactionID CMapGenOptions::CPlayerSettings::getStartingTown() const { return startingTown; } -void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value) +void CMapGenOptions::CPlayerSettings::setStartingTown(FactionID value) { - assert(value >= -1); - if(value >= 0) + assert(value >= FactionID::RANDOM); + if(value != FactionID::RANDOM) { - assert(value < static_cast(VLC->townh->size())); + assert(value < FactionID(VLC->townh->size())); assert((*VLC->townh)[value]->town != nullptr); } startingTown = value; diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index 1fc4fd547..f069ec9a7 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -42,8 +42,8 @@ public: /// The starting town of the player ranging from 0 to town max count or RANDOM_TOWN. /// The default value is RANDOM_TOWN. - si32 getStartingTown() const; - void setStartingTown(si32 value); + FactionID getStartingTown() const; + void setStartingTown(FactionID value); /// The default value is EPlayerType::AI. EPlayerType getPlayerType() const; @@ -55,7 +55,7 @@ public: private: PlayerColor color; - si32 startingTown; + FactionID startingTown; EPlayerType playerType; TeamID team; @@ -118,8 +118,7 @@ public: /// The first player colors belong to standard players and the last player colors belong to comp only players. /// All standard players are by default of type EPlayerType::AI. const std::map & getPlayersSettings() const; - const std::map & getSavedPlayersMap() const; - void setStartingTownForPlayer(const PlayerColor & color, si32 town); + void setStartingTownForPlayer(const PlayerColor & color, FactionID town); /// Sets a player type for a standard player. A standard player is the opposite of a computer only player. The /// values which can be chosen for the player type are EPlayerType::AI or EPlayerType::HUMAN. void setPlayerTypeForStandardPlayer(const PlayerColor & color, EPlayerType playerType); diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index 79cca6f5f..63b3e76e6 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -442,7 +442,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const { auto player = PlayerColor(*owner - 1); auto playerSettings = map.getMapGenOptions().getPlayersSettings(); - si32 faction = FactionID::RANDOM; + FactionID faction = FactionID::RANDOM; if (vstd::contains(playerSettings, player)) { faction = playerSettings[player].getStartingTown(); diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 9934602a5..a54d54fa3 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -113,9 +113,9 @@ void Object::Instance::setPositionRaw(const int3 & position) void Object::Instance::setAnyTemplate(CRandomGenerator & rng) { - auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(); + auto templates = dObject.getObjectHandler()->getTemplates(); if(templates.empty()) - throw rmgException(boost::str(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.subID)); + throw rmgException(boost::str(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.getObjTypeIndex())); dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng); dAccessibleAreaCache.clear(); @@ -124,11 +124,11 @@ void Object::Instance::setAnyTemplate(CRandomGenerator & rng) void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng) { - auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain); + auto templates = dObject.getObjectHandler()->getTemplates(terrain); if (templates.empty()) { auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated(); - throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName)); + throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.getObjTypeIndex() % terrainName)); } dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng); @@ -335,10 +335,10 @@ void Object::Instance::finalize(RmgMap & map, CRandomGenerator & rng) if (!dObject.appearance) { const auto * terrainType = map.getTile(getPosition(true)).terType; - auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId()); + auto templates = dObject.getObjectHandler()->getTemplates(terrainType->getId()); if (templates.empty()) { - throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType)); + throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.getObjTypeIndex() % getPosition(true).toString() % terrainType)); } else { diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 9d34b3833..970a6f4bf 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -447,7 +447,6 @@ bool ObjectManager::createRequiredObjects() instance->object().getObjectName(), instance->getPosition(true).toString()); mapProxy->removeObject(&instance->object()); } - rmgNearObject.clear(); } } @@ -556,7 +555,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD } } - switch (instance->object().ID) + switch (instance->object().ID.toEnum()) { case Obj::RANDOM_TREASURE_ART: case Obj::RANDOM_MINOR_ART: //In OH3 quest artifacts have higher value than normal arts @@ -586,7 +585,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD case Obj::MONOLITH_ONE_WAY_EXIT: */ - switch (object.instances().front()->object().ID) + switch(object.instances().front()->object().ID.toEnum()) { case Obj::WATER_WHEEL: if (auto* m = zone.getModificator()) diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 20dc751b2..ed3f42bb9 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -111,7 +111,7 @@ void TreasurePlacer::addAllPossibleObjects() auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0); auto* obj = dynamic_cast(factory->create()); - obj->subID = hid; //will be initialized later + obj->setHeroType(hid); //will be initialized later obj->exp = generator.getConfig().prisonExperience[i]; obj->setOwner(PlayerColor::NEUTRAL); generator.banHero(hid); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index df7278f00..706a0de9b 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -53,14 +53,14 @@ struct VectorizedObjectInfo /// Base class for serializers capable of reading or writing data class DLL_LINKAGE CSerializer { - template - static si32 idToNumber(const T &t, typename std::enable_if::value>::type * dummy = nullptr) + template, bool> = true> + static int32_t idToNumber(const Numeric &t) { return t; } - template - static NT idToNumber(const IdentifierBase &t) + template, bool> = true> + static int32_t idToNumber(const IdentifierType &t) { return t.getNum(); } diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h index c5b274005..1d5efaeea 100644 --- a/lib/serializer/JsonSerializeFormat.h +++ b/lib/serializer/JsonSerializeFormat.h @@ -295,14 +295,16 @@ public: } ///si32-convertible identifier <-> Json string - template - void serializeId(const std::string & fieldName, T & value, const U & defaultValue) + template + void serializeId(const std::string & fieldName, IdentifierType & value, const IdentifierTypeBase & defaultValue = IdentifierType::NONE) { + static_assert(std::is_base_of_v, "This method can only serialize Identifier classes!"); + if (saving) { if (value != defaultValue) { - std::string fieldValue = E::encode(value); + std::string fieldValue = IdentifierType::encode(value.getNum()); serializeString(fieldName, fieldValue); } } @@ -313,13 +315,13 @@ public: if (!fieldValue.empty()) { - VLC->identifiers()->requestIdentifier(ModScope::scopeGame(), E::entityType(), fieldValue, [&value](int32_t index){ - value = T(index); + VLC->identifiers()->requestIdentifier(ModScope::scopeGame(), IdentifierType::entityType(), fieldValue, [&value](int32_t index){ + value = IdentifierType(index); }); } else { - value = T(defaultValue); + value = IdentifierType(defaultValue); } } } @@ -333,7 +335,7 @@ public: std::vector fieldValue; for(const T & vitem : value) - fieldValue.push_back(E::encode(vitem)); + fieldValue.push_back(E::encode(vitem.getNum())); serializeInternal(fieldName, fieldValue); } @@ -362,7 +364,7 @@ public: std::vector fieldValue; for(const T & vitem : value) - fieldValue.push_back(U::encode(vitem)); + fieldValue.push_back(U::encode(vitem.getNum())); serializeInternal(fieldName, fieldValue); } @@ -387,7 +389,24 @@ public: const TDecoder decoder = std::bind(&IInstanceResolver::decode, instanceResolver, _1); const TEncoder encoder = std::bind(&IInstanceResolver::encode, instanceResolver, _1); - serializeId(fieldName, value, defaultValue, decoder, encoder); + if (saving) + { + if (value != defaultValue) + { + std::string fieldValue = encoder(value.getNum()); + serializeString(fieldName, fieldValue); + } + } + else + { + std::string fieldValue; + serializeString(fieldName, fieldValue); + + if (!fieldValue.empty()) + value = T(decoder(fieldValue)); + else + value = T(defaultValue); + } } ///any serializable object <-> Json struct diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 7e1e3c19e..2f8b056b3 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -528,8 +528,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons request.player = parameters.caster->getCasterOwner(); request.title.appendLocalString(EMetaText::JK_TXT, 40); request.description.appendLocalString(EMetaText::JK_TXT, 41); - request.icon.id = Component::EComponentType::SPELL; - request.icon.subtype = owner->id.toEnum(); + request.icon = Component(ComponentType::SPELL, owner->id); env->genericQuery(&request, request.player, queryCallback); diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 0a2be59cc..cb7cc8832 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -717,7 +717,7 @@ IAdventureSpellMechanics::IAdventureSpellMechanics(const CSpell * s) std::unique_ptr IAdventureSpellMechanics::createMechanics(const CSpell * s) { - switch (s->id) + switch(s->id.toEnum()) { case SpellID::SUMMON_BOAT: return std::make_unique(s); diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index e3185acd9..2a0f8603d 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -23,6 +23,7 @@ VCMI_LIB_NAMESPACE_BEGIN struct Query; class IBattleState; class CRandomGenerator; +class CreatureService; class CMap; class CGameInfoCallback; class CBattleInfoCallback; @@ -32,6 +33,11 @@ class CStack; class CGObjectInstance; class CGHeroInstance; +namespace spells +{ +class Service; +} + namespace vstd { class RNG; diff --git a/lib/spells/ViewSpellInt.cpp b/lib/spells/ViewSpellInt.cpp index 2b8e36ec7..1507d761a 100644 --- a/lib/spells/ViewSpellInt.cpp +++ b/lib/spells/ViewSpellInt.cpp @@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN ObjectPosInfo::ObjectPosInfo(const CGObjectInstance * obj): - pos(obj->visitablePos()), id(obj->ID), subId(obj->subID), owner(obj->tempOwner) + pos(obj->visitablePos()), id(obj->ID), subId(obj->getObjTypeIndex()), owner(obj->tempOwner) { } diff --git a/lib/spells/effects/DemonSummon.cpp b/lib/spells/effects/DemonSummon.cpp index 09f8ec79e..8f5376702 100644 --- a/lib/spells/effects/DemonSummon.cpp +++ b/lib/spells/effects/DemonSummon.cpp @@ -49,7 +49,7 @@ void DemonSummon::apply(ServerCallback * server, const Mechanics * m, const Effe break; } - const auto *creatureType = creature.toCreature(m->creatures()); + const auto *creatureType = creature.toEntity(m->creatures()); int32_t deadCount = targetStack->unitBaseAmount(); int32_t deadTotalHealth = targetStack->getTotalHealth(); @@ -111,7 +111,7 @@ bool DemonSummon::isValidTarget(const Mechanics * m, const battle::Unit * unit) if (unit->isGhost()) return false; - const auto *creatureType = creature.toCreature(m->creatures()); + const auto *creatureType = creature.toEntity(m->creatures()); if (unit->getTotalHealth() < creatureType->getMaxHealth()) return false; diff --git a/lib/spells/effects/Dispel.cpp b/lib/spells/effects/Dispel.cpp index 3fb418acd..8ad649f9a 100644 --- a/lib/spells/effects/Dispel.cpp +++ b/lib/spells/effects/Dispel.cpp @@ -93,7 +93,7 @@ std::shared_ptr Dispel::getBonuses(const Mechanics * m, const b { if(bonus->source == BonusSource::SPELL_EFFECT) { - const Spell * sourceSpell = bonus->sid.as().toSpell(m->spells()); + const Spell * sourceSpell = bonus->sid.as().toEntity(m->spells()); if(!sourceSpell) return false;//error diff --git a/lib/spells/effects/Summon.cpp b/lib/spells/effects/Summon.cpp index 432f6ab7c..9f1eb6f41 100644 --- a/lib/spells/effects/Summon.cpp +++ b/lib/spells/effects/Summon.cpp @@ -107,7 +107,7 @@ void Summon::apply(ServerCallback * server, const Mechanics * m, const EffectTar if(summonByHealth) { - const auto *creatureType = creature.toCreature(m->creatures()); + const auto *creatureType = creature.toEntity(m->creatures()); auto creatureMaxHealth = creatureType->getMaxHealth(); amount = static_cast(valueWithBonus / creatureMaxHealth); } diff --git a/lib/spells/effects/Teleport.cpp b/lib/spells/effects/Teleport.cpp index bffffa6fd..4d29e9483 100644 --- a/lib/spells/effects/Teleport.cpp +++ b/lib/spells/effects/Teleport.cpp @@ -12,6 +12,7 @@ #include "Teleport.h" #include "Registry.h" #include "../ISpellMechanics.h" +#include "../../battle/IBattleState.h" #include "../../battle/CBattleInfoCallback.h" #include "../../battle/Unit.h" #include "../../networkPacks/PacksForClientBattle.h" @@ -76,6 +77,7 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT const auto destination = target[1].hexValue; BattleStackMoved pack; + pack.battleID = m->battle()->getBattle()->getBattleID(); pack.distance = 0; pack.stack = targetUnit->unitId(); std::vector tiles; diff --git a/mapeditor/CMakeLists.txt b/mapeditor/CMakeLists.txt index 14c409be9..3dd588fed 100644 --- a/mapeditor/CMakeLists.txt +++ b/mapeditor/CMakeLists.txt @@ -186,8 +186,8 @@ enable_pch(vcmieditor) # Copy to build directory for easier debugging add_custom_command(TARGET vcmieditor POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/ - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/mapeditor/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/icons - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/translation + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_SOURCE_DIR}/mapeditor/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/icons + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/translation ) install(TARGETS vcmieditor DESTINATION ${BIN_DIR}) diff --git a/mapeditor/inspector/inspector.cpp b/mapeditor/inspector/inspector.cpp index fe81dd27e..01839d01c 100644 --- a/mapeditor/inspector/inspector.cpp +++ b/mapeditor/inspector/inspector.cpp @@ -87,14 +87,6 @@ void Initializer::initialize(CGDwelling * o) if(!o) return; o->tempOwner = defaultPlayer; - - switch(o->ID) - { - case Obj::RANDOM_DWELLING: - case Obj::RANDOM_DWELLING_LVL: - case Obj::RANDOM_DWELLING_FACTION: - o->initRandomObjectInfo(); - } } void Initializer::initialize(CGGarrison * o) @@ -243,7 +235,7 @@ void Inspector::updateProperties(CGDwelling * o) addProperty("Owner", o->tempOwner, false); - if(dynamic_cast(o->info)) + if (o->ID == Obj::RANDOM_DWELLING || o->ID == Obj::RANDOM_DWELLING_LVL) { auto * delegate = new PickObjectDelegate(controller, PickObjectDelegate::typedFilter); addProperty("Same as town", PropertyEditorPlaceholder(), delegate, false); @@ -641,12 +633,12 @@ void Inspector::setProperty(CGDwelling * o, const QString & key, const QVariant if(key == "Same as town") { - if(auto * info = dynamic_cast(o->info)) - { - info->instanceId = ""; - if(CGTownInstance * town = data_cast(value.toLongLong())) - info->instanceId = town->instanceName; - } + if (!o->randomizationInfo.has_value()) + o->randomizationInfo = CGDwellingRandomizationInfo(); + + o->randomizationInfo->instanceId = ""; + if(CGTownInstance * town = data_cast(value.toLongLong())) + o->randomizationInfo->instanceId = town->instanceName; } } diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index 4912541de..0ba761307 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -217,7 +217,7 @@ void MapController::repairMap(CMap * map) const art->storedArtifact = a; } else - map->allowedArtifact.at(art->subID) = true; + map->allowedArtifact.at(art->getArtifact()) = true; } } } @@ -623,7 +623,7 @@ ModCompatibilityInfo MapController::modAssessmentMap(const CMap & map) if(obj->ID == Obj::HERO) continue; //stub! - auto handler = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID); + auto handler = obj->getObjectHandler(); auto modName = QString::fromStdString(handler->getJsonKey()).split(":").at(0).toStdString(); if(modName != "core") result[modName] = VLC->modh->getModInfo(modName).getVerificationInfo(); diff --git a/mapeditor/validator.cpp b/mapeditor/validator.cpp index 25783058d..daa69ce84 100644 --- a/mapeditor/validator.cpp +++ b/mapeditor/validator.cpp @@ -143,7 +143,7 @@ std::list Validator::validate(const CMap * map) if(ins->storedArtifact) { if(!map->allowedSpells[ins->storedArtifact->getScrollSpellID()]) - issues.emplace_back(QString(tr("Spell scroll %1 is prohibited by map settings")).arg(ins->storedArtifact->getScrollSpellID().toSpell(VLC->spells())->getNameTranslated().c_str()), false); + issues.emplace_back(QString(tr("Spell scroll %1 is prohibited by map settings")).arg(ins->storedArtifact->getScrollSpellID().toEntity(VLC->spells())->getNameTranslated().c_str()), false); } else issues.emplace_back(QString(tr("Spell scroll %1 doesn't have instance assigned and must be removed")).arg(ins->instanceName.c_str()), true); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7a0e39c98..a8134dfe4 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1571,11 +1571,12 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t if (!cs1.spells.empty() || !cs2.spells.empty())//create a message { - int ScholarSkillLevel = std::max(h1->getSecSkillLevel(SecondarySkill::SCHOLAR), - h2->getSecSkillLevel(SecondarySkill::SCHOLAR)); + SecondarySkill scholarSkill = SecondarySkill::SCHOLAR; + + int scholarSkillLevel = std::max(h1->getSecSkillLevel(scholarSkill), h2->getSecSkillLevel(scholarSkill)); InfoWindow iw; iw.player = h1->tempOwner; - iw.components.emplace_back(Component::EComponentType::SEC_SKILL, 18, ScholarSkillLevel, 0); + iw.components.emplace_back(ComponentType::SEC_SKILL, scholarSkill, scholarSkillLevel); iw.text.appendLocalString(EMetaText::GENERAL_TXT, 139);//"%s, who has studied magic extensively, iw.text.replaceRawString(h1->getNameTranslated()); @@ -1586,7 +1587,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t int size = static_cast(cs2.spells.size()); for (auto it : cs2.spells) { - iw.components.emplace_back(Component::EComponentType::SPELL, it, 1, 0); + iw.components.emplace_back(ComponentType::SPELL, it); iw.text.appendLocalString(EMetaText::SPELL_NAME, it.toEnum()); switch (size--) { @@ -1614,7 +1615,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t int size = static_cast(cs1.spells.size()); for (auto it : cs1.spells) { - iw.components.emplace_back(Component::EComponentType::SPELL, it, 1, 0); + iw.components.emplace_back(ComponentType::SPELL, it); iw.text.appendLocalString(EMetaText::SPELL_NAME, it.toEnum()); switch (size--) { @@ -2270,7 +2271,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, if(!t->visitingHero || !t->visitingHero->hasArt(ArtifactID::GRAIL)) COMPLAIN_RET("Cannot build this without grail!") else - removeArtifact(ArtifactLocation(t->visitingHero, t->visitingHero->getArtPos(ArtifactID::GRAIL, false))); + removeArtifact(ArtifactLocation(t->visitingHero->id, t->visitingHero->getArtPos(ArtifactID::GRAIL, false))); } break; } @@ -2315,7 +2316,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, auto isLibrary = isMageGuild ? false : t->town->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY; - if(isMageGuild || isLibrary || (t->subID == ETownType::CONFLUX && buildingID == BuildingID::GRAIL)) + if(isMageGuild || isLibrary || (t->getFaction() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL)) { if(t->visitingHero) giveSpells(t,t->visitingHero); @@ -2664,65 +2665,66 @@ bool CGameHandler::garrisonSwap(ObjectInstanceID tid) // With the amount of changes done to the function, it's more like transferArtifacts. // Function moves artifact from src to dst. If dst is not a backpack and is already occupied, old dst art goes to backpack and is replaced. -bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocation &al2) +bool CGameHandler::moveArtifact(const ArtifactLocation & src, const ArtifactLocation & dst) { - ArtifactLocation src = al1, dst = al2; - const PlayerColor srcPlayer = src.owningPlayer(), dstPlayer = dst.owningPlayer(); - const CArmedInstance *srcObj = src.relatedObj(), *dstObj = dst.relatedObj(); + ArtifactLocation srcLoc = src, dstLoc = dst; + const auto srcArtSet = getArtSet(srcLoc); + const auto dstArtSet = getArtSet(dstLoc); + assert(srcArtSet); + assert(dstArtSet); // Make sure exchange is even possible between the two heroes. - if(!isAllowedExchange(srcObj->id, dstObj->id)) + if(!isAllowedExchange(srcLoc.artHolder, dstLoc.artHolder)) COMPLAIN_RET("That heroes cannot make any exchange!"); - const CArtifactInstance *srcArtifact = src.getArt(); - const CArtifactInstance *destArtifact = dst.getArt(); - const bool isDstSlotBackpack = ArtifactUtils::isSlotBackpack(dst.slot); + const auto srcArtifact = srcArtSet->getArt(srcLoc.slot); + const auto dstArtifact = dstArtSet->getArt(dstLoc.slot); + const bool isDstSlotBackpack = dstArtSet->bearerType() == ArtBearer::HERO ? ArtifactUtils::isSlotBackpack(dstLoc.slot) : false; if(srcArtifact == nullptr) COMPLAIN_RET("No artifact to move!"); - if(destArtifact && srcPlayer != dstPlayer && !isDstSlotBackpack) + if(dstArtifact && getHero(src.artHolder)->getOwner() != getHero(dst.artHolder)->getOwner() && !isDstSlotBackpack) COMPLAIN_RET("Can't touch artifact on hero of another player!"); // Check if src/dest slots are appropriate for the artifacts exchanged. // Moving to the backpack is always allowed. - if((!srcArtifact || !isDstSlotBackpack) - && srcArtifact && !srcArtifact->canBePutAt(dst, true)) + if((!srcArtifact || !isDstSlotBackpack) && srcArtifact && !srcArtifact->canBePutAt(dstArtSet, dstLoc.slot, true)) COMPLAIN_RET("Cannot move artifact!"); - auto srcSlot = src.getSlot(); - auto dstSlot = dst.getSlot(); + auto srcSlotInfo = srcArtSet->getSlot(srcLoc.slot); + auto dstSlotInfo = dstArtSet->getSlot(dstLoc.slot); - if((srcSlot && srcSlot->locked) || (dstSlot && dstSlot->locked)) + if((srcSlotInfo && srcSlotInfo->locked) || (dstSlotInfo && dstSlotInfo->locked)) COMPLAIN_RET("Cannot move artifact locks."); if(isDstSlotBackpack && srcArtifact->artType->isBig()) COMPLAIN_RET("Cannot put big artifacts in backpack!"); - if(src.slot == ArtifactPosition::MACH4 || dst.slot == ArtifactPosition::MACH4) + if(srcLoc.slot == ArtifactPosition::MACH4 || dstLoc.slot == ArtifactPosition::MACH4) COMPLAIN_RET("Cannot move catapult!"); if(isDstSlotBackpack) { - if(!ArtifactUtils::isBackpackFreeSlots(dst.getHolderArtSet())) + if(!ArtifactUtils::isBackpackFreeSlots(dstArtSet)) COMPLAIN_RET("Backpack is full!"); - vstd::amin(dst.slot, ArtifactPosition::BACKPACK_START + dst.getHolderArtSet()->artifactsInBackpack.size()); + vstd::amin(dstLoc.slot, ArtifactPosition::BACKPACK_START + dstArtSet->artifactsInBackpack.size()); } - if(!(src.slot == ArtifactPosition::TRANSITION_POS && dst.slot == ArtifactPosition::TRANSITION_POS)) + if(!(srcLoc.slot == ArtifactPosition::TRANSITION_POS && dstLoc.slot == ArtifactPosition::TRANSITION_POS)) { - if(src.slot == dst.slot && src.artHolder == dst.artHolder) + if(srcLoc.slot == dstLoc.slot && srcLoc.artHolder == dstLoc.artHolder) COMPLAIN_RET("Won't move artifact: Dest same as source!"); // Check if dst slot is occupied - if(!isDstSlotBackpack && destArtifact) + if(!isDstSlotBackpack && dstArtifact) { // Previous artifact must be removed first - moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition::TRANSITION_POS)); + moveArtifact(dstLoc, ArtifactLocation(dstLoc.artHolder, ArtifactPosition::TRANSITION_POS)); } try { - auto hero = std::get>(dst.artHolder); - if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->getId(), dst.slot)) + auto hero = getHero(dst.artHolder); + if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->getId(), dstLoc.slot)) giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); } catch(const std::bad_variant_access &) @@ -2730,8 +2732,8 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat // object other than hero received an art - ignore } - MoveArtifact ma(&src, &dst); - if(src.artHolder == dst.artHolder) + MoveArtifact ma(&srcLoc, &dstLoc); + if(srcLoc.artHolder == dstLoc.artHolder) ma.askAssemble = false; sendAndApply(&ma); } @@ -2749,8 +2751,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID if((!psrcHero) || (!pdstHero)) COMPLAIN_RET("bulkMoveArtifacts: wrong hero's ID"); - BulkMoveArtifacts ma(static_cast>(psrcHero), - static_cast>(pdstHero), swap); + BulkMoveArtifacts ma(srcHero, dstHero, swap); auto & slotsSrcDst = ma.artsPack0; auto & slotsDstSrc = ma.artsPack1; @@ -2854,7 +2855,7 @@ bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition a if(!destArtifact) COMPLAIN_RET("assembleArtifacts: there is no such artifact instance!"); - const auto dstLoc = ArtifactLocation(hero, artifactSlot); + const auto dstLoc = ArtifactLocation(hero->id, artifactSlot); if(assemble) { CArtifact * combinedArt = VLC->arth->objects[assembleTo]; @@ -2864,8 +2865,8 @@ bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition a { COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!"); } - if(!destArtifact->canBePutAt(dstLoc) - && !destArtifact->canBePutAt(ArtifactLocation(hero, ArtifactPosition::BACKPACK_START))) + if(!destArtifact->canBePutAt(hero, artifactSlot) + && !destArtifact->canBePutAt(hero, ArtifactPosition::BACKPACK_START)) { COMPLAIN_RET("assembleArtifacts: It's impossible to give the artholder requested artifact!"); } @@ -2897,15 +2898,15 @@ bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition a bool CGameHandler::eraseArtifactByClient(const ArtifactLocation & al) { - const auto * hero = getHero(al.relatedObj()->id); + const auto * hero = getHero(al.artHolder); if(hero == nullptr) COMPLAIN_RET("eraseArtifactByClient: wrong hero's ID"); - const auto * art = al.getArt(); + const auto * art = hero->getArt(al.slot); if(art == nullptr) COMPLAIN_RET("Cannot remove artifact!"); - if(al.getArt()->artType->canBePutAt(hero) || al.slot != ArtifactPosition::TRANSITION_POS) + if(art->canBePutAt(hero) || al.slot != ArtifactPosition::TRANSITION_POS) COMPLAIN_RET("Illegal artifact removal request"); removeArtifact(al); @@ -3012,7 +3013,7 @@ bool CGameHandler::sellArtifact(const IMarket *m, const CGHeroInstance *h, Artif int resVal = 0, dump = 1; m->getOffer(art->artType->getId(), rid, dump, resVal, EMarketMode::ARTIFACT_RESOURCE); - removeArtifact(ArtifactLocation(h, h->getArtPos(art))); + removeArtifact(ArtifactLocation(h->id, h->getArtPos(art))); giveResource(h->tempOwner, rid, resVal); return true; } @@ -3216,10 +3217,10 @@ void CGameHandler::handleTimeEvents() iw.player = color; iw.text = ev.message; - for (int i=0; iresources[i] != n.res.at(player)[i]) //if resource had changed, we add it to the dialog - iw.components.emplace_back(Component::EComponentType::RESOURCE,i,n.res.at(player)[i]-was[i],0); - + iw.components.emplace_back(ComponentType::RESOURCE, i, n.res.at(player)[i] - was[i]); } for (auto & i : ev.buildings) @@ -3284,7 +3284,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n) if (!town->hasBuilt(i)) { buildStructure(town->id, i, true); - iw.components.emplace_back(Component::EComponentType::BUILDING, town->subID, i, 0); + iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i)); } } @@ -3300,8 +3300,7 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n) if (!town->creatures.at(i).second.empty() && ev.creatures.at(i) > 0)//there is dwelling { sac.creatures[i].first += ev.creatures.at(i); - iw.components.emplace_back(Component::EComponentType::CREATURE, - town->creatures.at(i).second.back(), ev.creatures.at(i), 0); + iw.components.emplace_back(ComponentType::CREATURE, town->creatures.at(i).second.back(), ev.creatures.at(i)); } } sendAndApply(&iw); //show dialog @@ -3430,7 +3429,7 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta { using events::ObjectVisitStarted; - logGlobal->debug("%s visits %s (%d:%d)", h->nodeName(), obj->getObjectName(), obj->ID, obj->subID); + logGlobal->debug("%s visits %s (%d)", h->nodeName(), obj->getObjectName(), obj->ID); if (getVisitingHero(obj) != nullptr) { @@ -3641,7 +3640,7 @@ void CGameHandler::getVictoryLossMessage(PlayerColor player, const EVictoryLossC out.player = player; out.text = victoryLossCheckResult.messageToSelf; out.text.replaceLocalString(EMetaText::COLOR, player.getNum()); - out.components.emplace_back(Component::EComponentType::FLAG, player.getNum(), 0, 0); + out.components.emplace_back(ComponentType::FLAG, player); } bool CGameHandler::dig(const CGHeroInstance *h) @@ -3669,7 +3668,7 @@ bool CGameHandler::dig(const CGHeroInstance *h) sendAndApply(&iw); iw.soundID = soundBase::invalid; - iw.components.emplace_back(Component::EComponentType::ARTIFACT, ArtifactID::GRAIL, 0, 0); + iw.components.emplace_back(ComponentType::ARTIFACT, ArtifactID(ArtifactID::GRAIL)); iw.text.clear(); iw.text.appendLocalString(EMetaText::ART_DESCR, ArtifactID::GRAIL); sendAndApply(&iw); @@ -3750,8 +3749,8 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h for(int i = 0; i < slot.size(); ++i) { - ArtifactLocation al(hero, slot[i]); - const CArtifactInstance * a = al.getArt(); + ArtifactLocation al(hero->id, slot[i]); + const CArtifactInstance * a = hero->getArt(al.slot); if(!a) { @@ -3963,7 +3962,7 @@ bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & s bool CGameHandler::giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) { assert(a->artType); - ArtifactLocation al(h, ArtifactPosition::PRE_FIRST); + ArtifactLocation al(h->id, ArtifactPosition::PRE_FIRST); if(pos == ArtifactPosition::FIRST_AVAILABLE) { @@ -3978,7 +3977,7 @@ bool CGameHandler::giveHeroArtifact(const CGHeroInstance * h, const CArtifactIns al.slot = pos; } - if(a->canBePutAt(al)) + if(a->canBePutAt(h, al.slot)) putArtifact(al, a); else return false; @@ -4144,7 +4143,7 @@ const CGHeroInstance * CGameHandler::getVisitingHero(const CGObjectInstance *obj { assert(obj); - for (auto const & query : queries->allQueries()) + for(const auto & query : queries->allQueries()) { auto visit = std::dynamic_pointer_cast(query); if (visit && visit->visitedObject == obj) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index b6996aa85..a71d6a1dd 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -130,7 +130,7 @@ public: bool giveHeroArtifact(const CGHeroInstance * h, const CArtifactInstance * a, ArtifactPosition pos) override; void putArtifact(const ArtifactLocation &al, const CArtifactInstance *a) override; void removeArtifact(const ArtifactLocation &al) override; - bool moveArtifact(const ArtifactLocation & al1, const ArtifactLocation & al2) override; + bool moveArtifact(const ArtifactLocation & src, const ArtifactLocation & dst) override; bool bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack); bool eraseArtifactByClient(const ArtifactLocation & al); void synchronizeArtifactHandlerLists(); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 540751603..c5e03acf5 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -134,7 +134,7 @@ void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack) void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack) { - gh.throwIfWrongPlayer(&pack, pack.src.owningPlayer()); //second hero can be ally + gh.throwIfWrongPlayer(&pack, gh.getOwner(pack.src.artHolder)); //second hero can be ally result = gh.moveArtifact(pack.src, pack.dst); } @@ -154,7 +154,7 @@ void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack) void ApplyGhNetPackVisitor::visitEraseArtifactByClient(EraseArtifactByClient & pack) { - gh.throwIfWrongPlayer(&pack, pack.al.owningPlayer()); + gh.throwIfWrongPlayer(&pack, gh.getOwner(pack.al.artHolder)); result = gh.eraseArtifactByClient(pack.al); } diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 230d2cf1b..472895572 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -79,7 +79,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, logGlobal->debug("War machine has been destroyed"); auto hero = dynamic_ptr_cast (army); if (hero) - removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true))); + removedWarMachines.push_back (ArtifactLocation(hero->id, hero->getArtPos(warMachine, true))); else logGlobal->error("War machine in army without hero"); } @@ -323,7 +323,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_CHANCE); for(auto & spellId : battle.getBattle()->getUsedSpells(battle.otherSide(battleResult->winner))) { - auto spell = spellId.toSpell(VLC->spells()); + auto spell = spellId.toEntity(VLC->spells()); if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance) cs.spells.insert(spell->getId()); } @@ -339,7 +339,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) if(slot != ArtifactPosition::PRE_FIRST) { arts.push_back(art); - ma->dst = ArtifactLocation(finishingBattle->winnerHero, slot); + ma->dst = ArtifactLocation(finishingBattle->winnerHero->id, slot); if(ArtifactUtils::isSlotBackpack(slot)) ma->askAssemble = false; gameHandler->sendAndApply(ma); @@ -353,8 +353,8 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first); - const CArtifactInstance * art = ma.src.getArt(); + ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first); + const CArtifactInstance * art = finishingBattle->loserHero->getArt(artSlot.first); if (art && !art->artType->isBig() && art->artType->getId() != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook) @@ -366,9 +366,9 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) { //we assume that no big artifacts can be found MoveArtifact ma; - ma.src = ArtifactLocation(finishingBattle->loserHero, + ma.src = ArtifactLocation(finishingBattle->loserHero->id, ArtifactPosition(ArtifactPosition::BACKPACK_START + slotNumber)); //backpack automatically shifts arts to beginning - const CArtifactInstance * art = ma.src.getArt(); + const CArtifactInstance * art = finishingBattle->loserHero->getArt(ArtifactPosition::BACKPACK_START + slotNumber); if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won { sendMoveArtifact(art, &ma); @@ -380,8 +380,9 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first); - const CArtifactInstance * art = ma.src.getArt(); + ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first); + ma.src.creature = finishingBattle->loserHero->findStack(finishingBattle->loserHero->commander); + const auto art = finishingBattle->loserHero->commander->getArt(artSlot.first); if (art && !art->artType->isBig()) { sendMoveArtifact(art, &ma); @@ -395,11 +396,12 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) for (auto armySlot : battle.battleGetArmyObject(loser)->stacks) { auto artifactsWorn = armySlot.second->artifactsWorn; - for (auto artSlot : artifactsWorn) + for(const auto & artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation(armySlot.second, artSlot.first); - const CArtifactInstance * art = ma.src.getArt(); + ma.src = ArtifactLocation(finishingBattle->loserHero->id, artSlot.first); + ma.src.creature = finishingBattle->loserHero->findStack(finishingBattle->loserHero->commander); + const auto art = finishingBattle->loserHero->commander->getArt(artSlot.first); if (art && !art->artType->isBig()) { sendMoveArtifact(art, &ma); @@ -417,9 +419,11 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) for (auto art : arts) //TODO; separate function to display loot for various ojects? { - iw.components.emplace_back( - Component::EComponentType::ARTIFACT, art->artType->getId(), - art->artType->getId() == ArtifactID::SPELL_SCROLL? art->getScrollSpellID() : SpellID(0), 0); + if (art->artType->getId() == ArtifactID::SPELL_SCROLL) + iw.components.emplace_back(ComponentType::SPELL_SCROLL, art->getScrollSpellID()); + else + iw.components.emplace_back(ComponentType::ARTIFACT, art->artType->getId()); + if (iw.components.size() >= 14) { gameHandler->sendAndApply(&iw); @@ -461,7 +465,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) iw.text.replaceLocalString(EMetaText::SPELL_NAME, it->toEnum()); if (i == cs.spells.size() - 2) //we just added pre-last name iw.text.replaceLocalString(EMetaText::GENERAL_TXT, 141); // " and " - iw.components.emplace_back(Component::EComponentType::SPELL, *it, 0, 0); + iw.components.emplace_back(ComponentType::SPELL, *it); } gameHandler->sendAndApply(&iw); gameHandler->sendAndApply(&cs); diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index f37f09f8a..eeb2ed73d 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -50,8 +50,8 @@ TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player, // try to find "better" slot to overwrite // we want to avoid overwriting retreated heroes when tavern still has slot with random hero // as well as avoid overwriting surrendered heroes if we can overwrite retreated hero - auto roleLeft = heroesPool->getSlotRole(HeroTypeID(heroes[0]->subID)); - auto roleRight = heroesPool->getSlotRole(HeroTypeID(heroes[1]->subID)); + auto roleLeft = heroesPool->getSlotRole(heroes[0]->getHeroType()); + auto roleRight = heroesPool->getSlotRole(heroes[1]->getHeroType()); if (roleLeft > roleRight) return TavernHeroSlot::RANDOM; @@ -73,7 +73,7 @@ void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHer sah.slotID = selectSlotForRole(color, sah.roleID); sah.player = color; - sah.hid.setNum(hero->subID); + sah.hid = hero->getHeroType(); gameHandler->sendAndApply(&sah); } @@ -84,7 +84,7 @@ void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroIns sah.slotID = selectSlotForRole(color, sah.roleID); sah.player = color; - sah.hid.setNum(hero->subID); + sah.hid = hero->getHeroType(); sah.army.clearSlots(); sah.army.setCreature(SlotID(0), hero->type->initialArmy.at(0).creature, 1); @@ -111,7 +111,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe if (newHero) { - sah.hid.setNum(newHero->subID); + sah.hid = newHero->getHeroType(); if (giveArmy) { @@ -193,7 +193,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy for(const auto & hero : recruitableHeroes) { - if(hero->subID == heroToRecruit) + if(hero->getHeroType() == heroToRecruit) recruitedHero = hero; } @@ -206,7 +206,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy HeroRecruited hr; hr.tid = mapObject->id; - hr.hid.setNum(recruitedHero->subID); + hr.hid = recruitedHero->getHeroType(); hr.player = player; hr.tile = recruitedHero->convertFromVisitablePos(targetPos ); if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat) diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp index e2561adcf..74319bc26 100644 --- a/server/queries/MapQueries.cpp +++ b/server/queries/MapQueries.cpp @@ -17,20 +17,6 @@ #include "../../lib/networkPacks/PacksForServer.h" #include "../../lib/serializer/Cast.h" -struct GetEngagedHeroIds -{ - std::optional operator()(const ConstTransitivePtr & h) const - { - return h->id; - } - std::optional operator()(const ConstTransitivePtr & s) const - { - if(s->armyObj && s->armyObj->ID == Obj::HERO) - return s->armyObj->id; - return std::optional(); - } -}; - TimerPauseQuery::TimerPauseQuery(CGameHandler * owner, PlayerColor player): CQuery(owner) { @@ -127,12 +113,12 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const if(auto arts = dynamic_ptr_cast(pack)) { - if(auto id1 = std::visit(GetEngagedHeroIds(), arts->src.artHolder)) - if(!vstd::contains(ourIds, *id1)) + if(auto id1 = arts->src.artHolder) + if(!vstd::contains(ourIds, id1)) return true; - if(auto id2 = std::visit(GetEngagedHeroIds(), arts->dst.artHolder)) - if(!vstd::contains(ourIds, *id2)) + if(auto id2 = arts->dst.artHolder) + if(!vstd::contains(ourIds, id2)) return true; return false; } @@ -144,8 +130,8 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const if(auto art = dynamic_ptr_cast(pack)) { - if (auto id = std::visit(GetEngagedHeroIds(), art->al.artHolder)) - return !vstd::contains(ourIds, *id); + if(auto id = art->al.artHolder) + return !vstd::contains(ourIds, id); } if(auto dismiss = dynamic_ptr_cast(pack)) diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index 02222e712..1c9334d1f 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -240,7 +240,7 @@ TEST_F(CGameStateTest, issue2765) gameCallback->sendAndApply(&na); PutArtifact pack; - pack.al = ArtifactLocation(defender, ArtifactPosition::MACH1); + pack.al = ArtifactLocation(defender->id, ArtifactPosition::MACH1); pack.art = a; gameCallback->sendAndApply(&pack); } @@ -334,7 +334,7 @@ TEST_F(CGameStateTest, battleResurrection) gameCallback->sendAndApply(&na); PutArtifact pack; - pack.al = ArtifactLocation(attacker, ArtifactPosition::SPELLBOOK); + pack.al = ArtifactLocation(attacker->id, ArtifactPosition::SPELLBOOK); pack.art = a; gameCallback->sendAndApply(&pack); }