ソースを参照

ArtifactsUIController class

SoundSSGood 1 年間 前
コミット
ef1fbffad4

+ 142 - 0
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<ArtifactID> * 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<CComponent>(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<CWindowWithArtifacts>())
+		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<CWindowWithArtifacts>())
+		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<CWindowWithArtifacts>())
+		artWin->update();
+}
+
+void ArtifactsUIController::artifactDisassembled()
+{
+	for(auto & artWin : GH.windows().findWindows<CWindowWithArtifacts>())
+		artWin->update();
+}

+ 38 - 0
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<ArtifactID> ignoredArtifacts;
+
+	boost::mutex askAssembleArtifactsMutex;
+
+	bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot, std::set<ArtifactID> * ignoredArtifacts = nullptr);
+	bool askToDisassemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
+
+	void artifactRemoved();
+	void artifactMoved();
+	void bulkArtMovementStart(size_t numOfArts);
+	void artifactAssembled();
+	void artifactDisassembled();
+};
+ 

+ 2 - 0
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

+ 10 - 61
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<void()> onYes)
-{
-	std::string text = artifact->getDescriptionTranslated();
-	text += "\n\n";
-	std::vector<std::shared_ptr<CComponent>> 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<CComponent>(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<MoveHero>(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<CWindowWithArtifacts>())
-		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<CWindowWithArtifacts>())
-	{
-		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<CWindowWithArtifacts>())
-		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<CWindowWithArtifacts>())
-		artWin->artifactDisassembled(al);
+	ArtifactsUIController::artifactDisassembled();
 }
 
 void CPlayerInterface::waitForAllDialogs()

+ 2 - 4
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<std::shared_ptr<CInfoWindow>> 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<void()> onYes);
 	void waitWhileDialog();
 	void waitForAllDialogs();
 	void openTownWindow(const CGTownInstance * town); //shows townscreen

+ 1 - 1
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);

+ 0 - 56
client/widgets/CArtPlace.cpp

@@ -245,59 +245,3 @@ void CArtPlace::addCombinedArtInfo(const std::map<const ArtifactID, std::vector<
 		text += info.toString();
 	}
 }
-
-bool ArtifactUtilsClient::askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot)
-{
-	assert(hero);
-	const auto art = hero->getArt(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<void()> 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;
-}

+ 0 - 6
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);
-}

+ 7 - 27
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()))

+ 0 - 4
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:

+ 2 - 2
lib/ArtifactUtils.cpp

@@ -196,7 +196,7 @@ DLL_LINKAGE bool ArtifactUtils::isBackpackFreeSlots(const CArtifactSet * target,
 }
 
 DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
-	const CArtifactSet * artSet, const ArtifactID & aid)
+	const CArtifactSet * artSet, const ArtifactID & aid, const bool onlyEquiped)
 {
 	std::vector<const CArtifact*> arts;
 	const auto * art = aid.toArtifact();
@@ -210,7 +210,7 @@ DLL_LINKAGE std::vector<const CArtifact*> 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;

+ 1 - 1
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<const CArtifact*> assemblyPossibilities(const CArtifactSet * artSet, const ArtifactID & aid);
+	DLL_LINKAGE std::vector<const CArtifact*> 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);

+ 4 - 5
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 <typename Handler> 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<LinkedSlots> artsPack0;
 	std::vector<LinkedSlots> 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;
 	}
 };
 

+ 1 - 1
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;
 }

+ 2 - 1
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<CArtifactInstance*>(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))