diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index b73b6b795..a63896cb3 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -282,7 +282,7 @@ bool ArtifactUtilsClient::askToAssemble(const CGHeroInstance * hero, const Artif if(hero->tempOwner != LOCPLINT->playerID) return false; - auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), ArtifactUtils::isSlotEquipment(slot)); + auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId()); if(!assemblyPossibilities.empty()) { auto askThread = new boost::thread([hero, art, slot, assemblyPossibilities]() -> void diff --git a/client/widgets/CArtifactsOfHeroBase.cpp b/client/widgets/CArtifactsOfHeroBase.cpp index dd0723fd5..20f27e002 100644 --- a/client/widgets/CArtifactsOfHeroBase.cpp +++ b/client/widgets/CArtifactsOfHeroBase.cpp @@ -261,8 +261,10 @@ void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosit { arts.insert(std::pair(combinedArt, 0)); for(const auto part : combinedArt->getConstituents()) - if(artSet.hasArt(part->getId(), true)) + { + if(artSet.hasArt(part->getId(), false)) arts.at(combinedArt)++; + } } artPlace->addCombinedArtInfo(arts); } diff --git a/client/widgets/CWindowWithArtifacts.cpp b/client/widgets/CWindowWithArtifacts.cpp index 32aa31553..5c2c3964d 100644 --- a/client/widgets/CWindowWithArtifacts.cpp +++ b/client/widgets/CWindowWithArtifacts.cpp @@ -254,7 +254,7 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc) { - updateSlots(artLoc.slot); + updateSlots(); } void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) @@ -329,26 +329,23 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc) { - updateSlots(artLoc.slot); + updateSlots(); } void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc) { markPossibleSlots(); - updateSlots(artLoc.slot); + updateSlots(); } -void CWindowWithArtifacts::updateSlots(const ArtifactPosition & slot) +void CWindowWithArtifacts::updateSlots() { - auto updateSlotBody = [slot](auto artSetWeak) -> void + auto updateSlotBody = [](auto artSetWeak) -> void { if(const auto artSetPtr = artSetWeak.lock()) { - if(ArtifactUtils::isSlotEquipment(slot)) - artSetPtr->updateWornSlots(); - else if(ArtifactUtils::isSlotBackpack(slot)) - artSetPtr->updateBackpackSlots(); - + artSetPtr->updateWornSlots(); + artSetPtr->updateBackpackSlots(); artSetPtr->redraw(); } }; diff --git a/client/widgets/CWindowWithArtifacts.h b/client/widgets/CWindowWithArtifacts.h index f603ad7f5..777e4537d 100644 --- a/client/widgets/CWindowWithArtifacts.h +++ b/client/widgets/CWindowWithArtifacts.h @@ -44,7 +44,7 @@ protected: std::vector artSets; CloseCallback closeCallback; - void updateSlots(const ArtifactPosition & slot); + void updateSlots(); std::optional> getState(); std::optional findAOHbyRef(CArtifactsOfHeroBase & artsInst); void markPossibleSlots(); diff --git a/lib/ArtifactUtils.cpp b/lib/ArtifactUtils.cpp index e11c73c25..df3dd4655 100644 --- a/lib/ArtifactUtils.cpp +++ b/lib/ArtifactUtils.cpp @@ -115,7 +115,7 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target, } DLL_LINKAGE std::vector ArtifactUtils::assemblyPossibilities( - const CArtifactSet * artSet, const ArtifactID & aid, bool equipped) + const CArtifactSet * artSet, const ArtifactID & aid) { std::vector arts; const auto * art = aid.toArtifact(); @@ -129,23 +129,10 @@ DLL_LINKAGE std::vector ArtifactUtils::assemblyPossibilities( for(const auto constituent : artifact->getConstituents()) //check if all constituents are available { - if(equipped) + if(!artSet->hasArt(constituent->getId(), false, false, false)) { - // Search for equipped arts - if(!artSet->hasArt(constituent->getId(), true, false, false)) - { - possible = false; - break; - } - } - else - { - // Search in backpack - if(!artSet->hasArtBackpack(constituent->getId())) - { - possible = false; - break; - } + possible = false; + break; } } if(possible) diff --git a/lib/ArtifactUtils.h b/lib/ArtifactUtils.h index 2e70999b6..4b30f946d 100644 --- a/lib/ArtifactUtils.h +++ b/lib/ArtifactUtils.h @@ -36,7 +36,7 @@ namespace ArtifactUtils DLL_LINKAGE bool isSlotBackpack(const ArtifactPosition & slot); DLL_LINKAGE bool isSlotEquipment(const ArtifactPosition & slot); DLL_LINKAGE bool isBackpackFreeSlots(const CArtifactSet * target, const size_t reqSlots = 1); - DLL_LINKAGE std::vector assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid, bool equipped); + DLL_LINKAGE std::vector assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid); DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid); diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 66ccfe060..b1ee59bc1 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -819,12 +819,6 @@ ArtifactPosition CArtifactSet::getArtPos(const ArtifactID & aid, bool onlyWorn, return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0]; } -ArtifactPosition CArtifactSet::getArtBackpackPos(const ArtifactID & aid) const -{ - const auto result = getBackpackArtPositions(aid); - return result.empty() ? ArtifactPosition{ArtifactPosition::PRE_FIRST} : result[0]; -} - std::vector CArtifactSet::getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const { std::vector result; diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 37056f426..9f592a5f3 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -262,7 +262,6 @@ public: /// (if more than one such artifact lower ID is returned) ArtifactPosition getArtPos(const ArtifactID & aid, bool onlyWorn = true, bool allowLocked = true) const; ArtifactPosition getArtPos(const CArtifactInstance *art) const; - ArtifactPosition getArtBackpackPos(const ArtifactID & aid) const; std::vector getAllArtPositions(const ArtifactID & aid, bool onlyWorn, bool allowLocked, bool getAll) const; std::vector getBackpackArtPositions(const ArtifactID & aid) const; const CArtifactInstance * getArtByInstanceId(const ArtifactInstanceID & artInstId) const; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index dad9065c6..b31b44ab0 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1920,43 +1920,69 @@ void BulkMoveArtifacts::applyGs(CGameState * gs) void AssembledArtifact::applyGs(CGameState *gs) { CArtifactSet * artSet = al.getHolderArtSet(); - [[maybe_unused]] const CArtifactInstance *transformedArt = al.getArt(); + const CArtifactInstance * transformedArt = al.getArt(); assert(transformedArt); - bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot); - assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->artType->getId(), combineEquipped), [=](const CArtifact * art)->bool + assert(vstd::contains_if(ArtifactUtils::assemblyPossibilities(artSet, transformedArt->getTypeId()), [=](const CArtifact * art)->bool { return art->getId() == builtArt->getId(); })); + const auto transformedArtSlot = artSet->getSlotByInstance(transformedArt); auto * combinedArt = new CArtifactInstance(builtArt); gs->map->addNewArtifactInstance(combinedArt); - // Retrieve all constituents - for(const CArtifact * constituent : builtArt->getConstituents()) - { - ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->getId(), true, false) : - artSet->getArtBackpackPos(constituent->getId()); - assert(pos != ArtifactPosition::PRE_FIRST); - CArtifactInstance * constituentInstance = artSet->getArt(pos); - //move constituent from hero to be part of new, combined artifact - constituentInstance->removeFrom(ArtifactLocation(al.artHolder, pos)); - if(combineEquipped) + // Find slots for all involved artifacts + std::vector slotsInvolved; + for(const auto constituent : builtArt->getConstituents()) + { + ArtifactPosition slot; + if(transformedArt->getTypeId() == constituent->getId()) + slot = transformedArtSlot; + else + slot = artSet->getArtPos(constituent->getId(), false, false); + + assert(slot != ArtifactPosition::PRE_FIRST); + slotsInvolved.emplace_back(slot); + } + std::sort(slotsInvolved.begin(), slotsInvolved.end(), std::greater<>()); + + // Find a slot for combined artifact + al.slot = transformedArtSlot; + for(const auto slot : slotsInvolved) + { + if(ArtifactUtils::isSlotEquipment(transformedArtSlot)) { + + if(ArtifactUtils::isSlotBackpack(slot)) + { + al.slot = ArtifactPosition::BACKPACK_START; + break; + } + if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), al.slot) - && vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), pos)) - al.slot = pos; - if(al.slot == pos) - pos = ArtifactPosition::PRE_FIRST; + && vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), slot)) + al.slot = slot; } else { - al.slot = std::min(al.slot, pos); - pos = ArtifactPosition::PRE_FIRST; + if(ArtifactUtils::isSlotBackpack(slot)) + al.slot = std::min(al.slot, slot); } - combinedArt->addPart(constituentInstance, pos); } - //put new combined artifacts + // Delete parts from hero + for(const auto slot : slotsInvolved) + { + const auto constituentInstance = artSet->getArt(slot); + constituentInstance->removeFrom(ArtifactLocation(al.artHolder, slot)); + + if(ArtifactUtils::isSlotEquipment(al.slot) && slot != al.slot) + combinedArt->addPart(constituentInstance, slot); + else + combinedArt->addPart(constituentInstance, ArtifactPosition::PRE_FIRST); + } + + // Put new combined artifacts combinedArt->putAt(al); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 82c7543d7..679c04c6d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2738,7 +2738,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat } MoveArtifact ma(&src, &dst); - if(dst.slot == ArtifactPosition::TRANSITION_POS) + if(src.artHolder == dst.artHolder) ma.askAssemble = false; sendAndApply(&ma); } @@ -2853,7 +2853,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID * @param assembleTo If assemble is true, this represents the artifact ID of the combination * artifact to assemble to. Otherwise it's not used. */ -bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) +bool CGameHandler::assembleArtifacts(ObjectInstanceID heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) { const CGHeroInstance * hero = getHero(heroID); const CArtifactInstance * destArtifact = hero->getArt(artifactSlot); @@ -2861,23 +2861,27 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition if(!destArtifact) COMPLAIN_RET("assembleArtifacts: there is no such artifact instance!"); + const auto dstLoc = ArtifactLocation(hero, artifactSlot); if(assemble) { CArtifact * combinedArt = VLC->arth->objects[assembleTo]; if(!combinedArt->isCombined()) COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!"); - if (!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId(), - ArtifactUtils::isSlotEquipment(artifactSlot)), combinedArt)) + if(!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId()), combinedArt)) { COMPLAIN_RET("assembleArtifacts: It's impossible to assemble requested artifact!"); } - + if(!destArtifact->canBePutAt(dstLoc) + && !destArtifact->canBePutAt(ArtifactLocation(hero, ArtifactPosition::BACKPACK_START))) + { + COMPLAIN_RET("assembleArtifacts: It's impossible to give the artholder requested artifact!"); + } if(ArtifactUtils::checkSpellbookIsNeeded(hero, assembleTo, artifactSlot)) giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); AssembledArtifact aa; - aa.al = ArtifactLocation(hero, artifactSlot); + aa.al = dstLoc; aa.builtArt = combinedArt; sendAndApply(&aa); } @@ -2891,7 +2895,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!"); DisassembledArtifact da; - da.al = ArtifactLocation(hero, artifactSlot); + da.al = dstLoc; sendAndApply(&da); }