diff --git a/client/ArtifactsUIController.cpp b/client/ArtifactsUIController.cpp new file mode 100644 index 000000000..859f350fe --- /dev/null +++ b/client/ArtifactsUIController.cpp @@ -0,0 +1,142 @@ +/* + * ArtifactsUIController.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "ArtifactsUIController.h" +#include "CGameInfo.h" +#include "CPlayerInterface.h" + +#include "../CCallback.h" +#include "../lib/ArtifactUtils.h" +#include "../lib/CGeneralTextHandler.h" +#include "../lib/mapObjects/CGHeroInstance.h" + +#include "gui/CGuiHandler.h" +#include "gui/WindowHandler.h" +#include "widgets/CComponent.h" +#include "windows/CWindowWithArtifacts.h" + +bool ArtifactsUIController::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, std::set * ignoredArtifacts) +{ + assert(hero); + const auto art = hero->getArt(slot); + assert(art); + + if(hero->tempOwner != LOCPLINT->playerID) + return false; + + auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId(), true); + if(!assemblyPossibilities.empty()) + { + auto askThread = new boost::thread([this, hero, art, slot, assemblyPossibilities, ignoredArtifacts]() -> void + { + boost::mutex::scoped_lock askLock(askAssembleArtifactsMutex); + for(const auto combinedArt : assemblyPossibilities) + { + boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); + if(ignoredArtifacts && vstd::contains(*ignoredArtifacts, combinedArt->getId())) + continue; + + bool assembleConfirmed = false; + MetaString message = MetaString::createFromTextID(art->artType->getDescriptionTextID()); + message.appendEOL(); + message.appendEOL(); + message.appendRawString(CGI->generaltexth->allTexts[732]); // You possess all of the components needed to assemble the + message.replaceName(ArtifactID(combinedArt->getId())); + LOCPLINT->showYesNoDialog(message.toString(), [&assembleConfirmed, hero, slot, combinedArt]() + { + assembleConfirmed = true; + LOCPLINT->cb.get()->assembleArtifacts(hero, slot, true, combinedArt->getId()); + }, nullptr, {std::make_shared(ComponentType::ARTIFACT, combinedArt->getId())}); + + LOCPLINT->waitWhileDialog(); + if(ignoredArtifacts) + ignoredArtifacts->emplace(combinedArt->getId()); + if(assembleConfirmed) + break; + } + }); + askThread->detach(); + return true; + } + return false; +} + +bool ArtifactsUIController::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot) +{ + assert(hero); + const auto art = hero->getArt(slot); + assert(art); + + if(hero->tempOwner != LOCPLINT->playerID) + return false; + + if(art->isCombined()) + { + if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->getConstituents().size() - 1)) + return false; + + MetaString message = MetaString::createFromTextID(art->artType->getDescriptionTextID()); + message.appendEOL(); + message.appendEOL(); + message.appendRawString(CGI->generaltexth->allTexts[733]); // Do you wish to disassemble this artifact? + LOCPLINT->showYesNoDialog(message.toString(), [hero, slot]() + { + LOCPLINT->cb->assembleArtifacts(hero, slot, false, ArtifactID()); + }, nullptr); + return true; + } + return false; +} + +void ArtifactsUIController::artifactRemoved() +{ + for(auto & artWin : GH.windows().findWindows()) + artWin->update(); + LOCPLINT->waitWhileDialog(); +} + +void ArtifactsUIController::artifactMoved() +{ + // If a bulk transfer has arrived, then redrawing only the last art movement. + if(numOfMovedArts != 0) + numOfMovedArts--; + + for(auto & artWin : GH.windows().findWindows()) + if(numOfMovedArts == 0) + { + artWin->update(); + artWin->redraw(); + } + LOCPLINT->waitWhileDialog(); +} + +void ArtifactsUIController::bulkArtMovementStart(size_t numOfArts) +{ + numOfMovedArts = numOfArts; + if(numOfArtsAskAssembleSession == 0) + { + // Do not start the next session until the previous one is finished + numOfArtsAskAssembleSession = numOfArts; // TODO this is wrong + ignoredArtifacts.clear(); + } +} + +void ArtifactsUIController::artifactAssembled() +{ + for(auto & artWin : GH.windows().findWindows()) + artWin->update(); +} + +void ArtifactsUIController::artifactDisassembled() +{ + for(auto & artWin : GH.windows().findWindows()) + artWin->update(); +} diff --git a/client/ArtifactsUIController.h b/client/ArtifactsUIController.h new file mode 100644 index 000000000..0378dc9ee --- /dev/null +++ b/client/ArtifactsUIController.h @@ -0,0 +1,38 @@ +/* + * ArtifactsUIController.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + #pragma once + +#include "../lib/constants/EntityIdentifiers.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGHeroInstance; + +VCMI_LIB_NAMESPACE_END + +class ArtifactsUIController +{ +public: + size_t numOfMovedArts; + size_t numOfArtsAskAssembleSession; + std::set ignoredArtifacts; + + boost::mutex askAssembleArtifactsMutex; + + bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, std::set * ignoredArtifacts = nullptr); + bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot); + + void artifactRemoved(); + void artifactMoved(); + void bulkArtMovementStart(size_t numOfArts); + void artifactAssembled(); + void artifactDisassembled(); +}; + \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 36fc2c8a5..65f156973 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -168,6 +168,7 @@ set(client_SRCS windows/settings/BattleOptionsTab.cpp windows/settings/AdventureOptionsTab.cpp + ArtifactsUIController.cpp CGameInfo.cpp CMT.cpp CPlayerInterface.cpp @@ -371,6 +372,7 @@ set(client_HEADERS windows/settings/BattleOptionsTab.h windows/settings/AdventureOptionsTab.h + ArtifactsUIController.h CGameInfo.h CMT.h CPlayerInterface.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3bad090da..ae0f33e9f 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1251,35 +1251,6 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer GH.windows().pushWindow(cgw); } -/** - * Shows the dialog that appears when right-clicking an artifact that can be assembled - * into a combinational one on an artifact screen. Does not require the combination of - * artifacts to be legal. - */ -void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes) -{ - std::string text = artifact->getDescriptionTranslated(); - text += "\n\n"; - std::vector> scs; - - if(assembledArtifact) - { - // You possess all of the components to... - text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated()); - - // Picture of assembled artifact at bottom. - auto sc = std::make_shared(ComponentType::ARTIFACT, assembledArtifact->getId()); - scs.push_back(sc); - } - else - { - // Do you wish to disassemble this artifact? - text += CGI->generaltexth->allTexts[733]; - } - - showYesNoDialog(text, onYes, nullptr, scs); -} - void CPlayerInterface::requestRealized( PackageApplied *pa ) { if(pa->packType == CTypeList::getInstance().getTypeID(nullptr)) @@ -1739,14 +1710,14 @@ void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al) { if(auto hero = cb->getHero(al.artHolder)) { - auto art = hero->getArt(al.slot); - if(art == nullptr) + if(hero->getArt(al.slot) == nullptr) { - logGlobal->error("artifact location %d points to nothing", - al.slot.num); + logGlobal->error("artifact location %d points to nothing", al.slot.num); return; } - ArtifactUtilsClient::askToAssemble(hero, al.slot); + askToAssemble(hero, al.slot, &ignoredArtifacts); + if(numOfArtsAskAssembleSession != 0) + numOfArtsAskAssembleSession--; } } @@ -1760,55 +1731,33 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; adventureInt->onHeroChanged(cb->getHero(al.artHolder)); - - for(auto artWin : GH.windows().findWindows()) - artWin->artifactRemoved(al); - - waitWhileDialog(); + ArtifactsUIController::artifactRemoved(); } void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) { EVENT_HANDLER_CALLED_BY_CLIENT; adventureInt->onHeroChanged(cb->getHero(dst.artHolder)); - - // If a bulk transfer has arrived, then redrawing only the last art movement. - if(numOfMovedArts != 0) - numOfMovedArts--; - - for(auto artWin : GH.windows().findWindows()) - { - artWin->artifactMoved(src, dst); - if(numOfMovedArts == 0) - { - artWin->update(); - artWin->redraw(); - } - } - waitWhileDialog(); + ArtifactsUIController::artifactMoved(); } void CPlayerInterface::bulkArtMovementStart(size_t numOfArts) { - numOfMovedArts = numOfArts; + ArtifactsUIController::bulkArtMovementStart(numOfArts); } void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; adventureInt->onHeroChanged(cb->getHero(al.artHolder)); - - for(auto artWin : GH.windows().findWindows()) - artWin->artifactAssembled(al); + ArtifactsUIController::artifactAssembled(); } void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) { EVENT_HANDLER_CALLED_BY_CLIENT; adventureInt->onHeroChanged(cb->getHero(al.artHolder)); - - for(auto artWin : GH.windows().findWindows()) - artWin->artifactDisassembled(al); + ArtifactsUIController::artifactDisassembled(); } void CPlayerInterface::waitForAllDialogs() diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 25f671864..9ac6e32eb 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -12,6 +12,7 @@ #include "../lib/FunctionList.h" #include "../lib/CGameInterface.h" #include "gui/CIntObject.h" +#include "ArtifactsUIController.h" VCMI_LIB_NAMESPACE_BEGIN @@ -56,11 +57,9 @@ namespace boost } /// Central class for managing user interface logic -class CPlayerInterface : public CGameInterface, public IUpdateable +class CPlayerInterface : public CGameInterface, public IUpdateable, public ArtifactsUIController { bool ignoreEvents; - size_t numOfMovedArts; - int autosaveCount; std::list> dialogs; //queue of dialogs awaiting to be shown (not currently shown!) @@ -180,7 +179,6 @@ public: // public interface for use by client via LOCPLINT access void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard; void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2); - void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes); void waitWhileDialog(); void waitForAllDialogs(); void openTownWindow(const CGTownInstance * town); //shows townscreen diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 8678d987c..3a7d4d1a0 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -307,7 +307,7 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack) const auto dstLoc = ArtifactLocation(pack.dstArtHolder, slotToMove.dstPos); callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc); - if(pack.askAssemble) + if(slotToMove.askAssemble) callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::askToAssembleArtifact, dstLoc); if(pack.interfaceOwner != dstOwner) callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc); diff --git a/client/widgets/CArtPlace.cpp b/client/widgets/CArtPlace.cpp index 0e2b31139..520efd1be 100644 --- a/client/widgets/CArtPlace.cpp +++ b/client/widgets/CArtPlace.cpp @@ -245,59 +245,3 @@ void CArtPlace::addCombinedArtInfo(const std::mapgetArt(slot); - assert(art); - - if(hero->tempOwner != LOCPLINT->playerID) - return false; - - auto assemblyPossibilities = ArtifactUtils::assemblyPossibilities(hero, art->getTypeId()); - if(!assemblyPossibilities.empty()) - { - auto askThread = new boost::thread([hero, art, slot, assemblyPossibilities]() -> void - { - boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); - for(const auto combinedArt : assemblyPossibilities) - { - LOCPLINT->waitWhileDialog(); - bool assembleConfirmed = false; - CFunctionList onYesHandlers([&assembleConfirmed]() -> void {assembleConfirmed = true; }); - onYesHandlers += std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combinedArt->getId()); - - LOCPLINT->showArtifactAssemblyDialog(art->artType, combinedArt, onYesHandlers); - if(assembleConfirmed) - break; - } - }); - askThread->detach(); - return true; - } - return false; -} - -bool ArtifactUtilsClient::askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot) -{ - assert(hero); - const auto art = hero->getArt(slot); - assert(art); - - if(hero->tempOwner != LOCPLINT->playerID) - return false; - - if(art->isCombined()) - { - if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->getConstituents().size() - 1)) - return false; - - LOCPLINT->showArtifactAssemblyDialog( - art->artType, - nullptr, - std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, false, ArtifactID())); - return true; - } - return false; -} diff --git a/client/widgets/CArtPlace.h b/client/widgets/CArtPlace.h index 4d62eca97..95ff02b7e 100644 --- a/client/widgets/CArtPlace.h +++ b/client/widgets/CArtPlace.h @@ -59,9 +59,3 @@ public: void clickPressed(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; }; - -namespace ArtifactUtilsClient -{ - bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot); - bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot); -} diff --git a/client/windows/CWindowWithArtifacts.cpp b/client/windows/CWindowWithArtifacts.cpp index 1f8006a71..2326e481b 100644 --- a/client/windows/CWindowWithArtifacts.cpp +++ b/client/windows/CWindowWithArtifacts.cpp @@ -117,9 +117,9 @@ void CWindowWithArtifacts::showArtifactAssembling(const CArtifactsOfHeroBase & a { if(artsInst.getArt(artPlace.slot)) { - if(ArtifactUtilsClient::askToDisassemble(artsInst.getHero(), artPlace.slot)) + if(LOCPLINT->askToDisassemble(artsInst.getHero(), artPlace.slot)) return; - if(ArtifactUtilsClient::askToAssemble(artsInst.getHero(), artPlace.slot)) + if(LOCPLINT->askToAssemble(artsInst.getHero(), artPlace.slot)) return; if(artPlace.text.size()) artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition); @@ -166,14 +166,13 @@ void CWindowWithArtifacts::enableKeyboardShortcuts() const artSet->enableKeyboardShortcuts(); } -void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc) -{ - update(); -} - -void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc) +void CWindowWithArtifacts::update() { for(const auto & artSet : artSets) + { + artSet->updateWornSlots(); + artSet->updateBackpackSlots(); + if(const auto pickedArtInst = getPickedArtifact()) { markPossibleSlots(); @@ -184,25 +183,6 @@ void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const artSet->unmarkSlots(); CCS->curh->dragAndDropCursor(nullptr); } -} - -void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc) -{ - update(); -} - -void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc) -{ - markPossibleSlots(); - update(); -} - -void CWindowWithArtifacts::update() -{ - for(const auto & artSet : artSets) - { - artSet->updateWornSlots(); - artSet->updateBackpackSlots(); // Make sure the status bar is updated so it does not display old text if(auto artPlace = artSet->getArtPlace(GH.getCursorPosition())) diff --git a/client/windows/CWindowWithArtifacts.h b/client/windows/CWindowWithArtifacts.h index 2eb3810f7..c6af89aba 100644 --- a/client/windows/CWindowWithArtifacts.h +++ b/client/windows/CWindowWithArtifacts.h @@ -37,10 +37,6 @@ public: void deactivate() override; void enableKeyboardShortcuts() const; - virtual void artifactRemoved(const ArtifactLocation & artLoc); - virtual void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc); - virtual void artifactDisassembled(const ArtifactLocation & artLoc); - virtual void artifactAssembled(const ArtifactLocation & artLoc); virtual void update(); protected: diff --git a/lib/ArtifactUtils.cpp b/lib/ArtifactUtils.cpp index 0ca32387b..dd0bd67ba 100644 --- a/lib/ArtifactUtils.cpp +++ b/lib/ArtifactUtils.cpp @@ -196,7 +196,7 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target, } DLL_LINKAGE std::vector ArtifactUtils::assemblyPossibilities( - const CArtifactSet * artSet, const ArtifactID & aid) + const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped) { std::vector arts; const auto * art = aid.toArtifact(); @@ -210,7 +210,7 @@ DLL_LINKAGE std::vector ArtifactUtils::assemblyPossibilities( for(const auto constituent : artifact->getConstituents()) //check if all constituents are available { - if(!artSet->hasArt(constituent->getId(), false, false, false)) + if(!artSet->hasArt(constituent->getId(), onlyEquiped, false, false)) { possible = false; break; diff --git a/lib/ArtifactUtils.h b/lib/ArtifactUtils.h index ef6d35de1..46eb878e6 100644 --- a/lib/ArtifactUtils.h +++ b/lib/ArtifactUtils.h @@ -39,7 +39,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); + DLL_LINKAGE std::vector assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped = false); DLL_LINKAGE CArtifactInstance * createScroll(const SpellID & sid); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const CArtifact * art); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid); diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 70c3847f1..be9f94715 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -1031,17 +1031,20 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack { ArtifactPosition srcPos; ArtifactPosition dstPos; + bool askAssemble; LinkedSlots() = default; - LinkedSlots(const ArtifactPosition & srcPos, const ArtifactPosition & dstPos) + LinkedSlots(const ArtifactPosition & srcPos, const ArtifactPosition & dstPos, bool askAssemble = false) : srcPos(srcPos) , dstPos(dstPos) + , askAssemble(askAssemble) { } template void serialize(Handler & h) { h & srcPos; h & dstPos; + h & askAssemble; } }; @@ -1056,7 +1059,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack , srcArtHolder(ObjectInstanceID::NONE) , dstArtHolder(ObjectInstanceID::NONE) , swap(false) - , askAssemble(false) , srcCreature(std::nullopt) , dstCreature(std::nullopt) { @@ -1066,7 +1068,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack , srcArtHolder(srcArtHolder) , dstArtHolder(dstArtHolder) , swap(swap) - , askAssemble(false) , srcCreature(std::nullopt) , dstCreature(std::nullopt) { @@ -1077,7 +1078,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack std::vector artsPack0; std::vector artsPack1; bool swap; - bool askAssemble; void visitTyped(ICPackVisitor & visitor) override; @@ -1091,7 +1091,6 @@ struct DLL_LINKAGE BulkMoveArtifacts : CArtifactOperationPack h & srcCreature; h & dstCreature; h & swap; - h & askAssemble; } }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 27984bbc4..2834deaaf 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2783,7 +2783,7 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati ma.artsPack0.push_back(BulkMoveArtifacts::LinkedSlots(src.slot, dstSlot)); if(src.artHolder != dst.artHolder) - ma.askAssemble = true; + ma.artsPack0.back().askAssemble = true; sendAndApply(&ma); return true; } diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index b278bfbab..591e2885f 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -407,6 +407,8 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) if(dstSlot != ArtifactPosition::PRE_FIRST) { pack.artsPack0.emplace_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot)); + if(ArtifactUtils::isSlotEquipment(dstSlot)) + pack.artsPack0.back().askAssemble = true; arts.emplace_back(art); artFittingSet.putArtifact(dstSlot, const_cast(art)); } @@ -421,7 +423,6 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) if(finishingBattle->loserHero) { packHero.srcArtHolder = finishingBattle->loserHero->id; - packHero.askAssemble = true; for(const auto & artSlot : finishingBattle->loserHero->artifactsWorn) { if(ArtifactUtils::isArtRemovable(artSlot))