浏览代码

Merge pull request #2874 from SoundSSGood/exchange-window-update

Exchange window update
Ivan Savenko 2 年之前
父节点
当前提交
97097c20ad

+ 2 - 2
CCallback.cpp

@@ -172,9 +172,9 @@ void CCallback::assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition
 	sendRequest(&aa);
 }
 
-void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap)
+void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
 {
-	BulkExchangeArtifacts bma(srcHero, dstHero, swap);
+	BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
 	sendRequest(&bma);
 }
 

+ 2 - 2
CCallback.h

@@ -108,7 +108,7 @@ public:
 	
 	
 	// Moves all artifacts from one hero to another
-	virtual void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap) = 0;
+	virtual void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack) = 0;
 };
 
 class CBattleCallback : public IBattleCallback
@@ -171,7 +171,7 @@ public:
 	bool dismissHero(const CGHeroInstance * hero) override;
 	bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2) override;
 	void assembleArtifacts(const CGHeroInstance * hero, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
-	void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap) override;
+	void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
 	void eraseArtifactByClient(const ArtifactLocation & al) override;
 	bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
 	void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override;

+ 2 - 0
client/CMakeLists.txt

@@ -95,6 +95,7 @@ set(client_SRCS
 	widgets/Buttons.cpp
 	widgets/CArtifactHolder.cpp
 	widgets/CComponent.cpp
+	widgets/CExchangeController.cpp
 	widgets/CGarrisonInt.cpp
 	widgets/CreatureCostBox.cpp
 	widgets/ComboBox.cpp
@@ -259,6 +260,7 @@ set(client_HEADERS
 	widgets/Buttons.h
 	widgets/CArtifactHolder.h
 	widgets/CComponent.h
+	widgets/CExchangeController.h
 	widgets/CGarrisonInt.h
 	widgets/CreatureCostBox.h
 	widgets/ComboBox.h

+ 2 - 2
client/widgets/CArtifactHolder.cpp

@@ -200,8 +200,8 @@ void CHeroArtPlace::clickPressed(const Point & cursorPosition)
 
 void CHeroArtPlace::showPopupWindow(const Point & cursorPosition)
 {
-	if(rightClickCallback)
-		rightClickCallback(*this);
+	if(showPopupCallback)
+		showPopupCallback(*this);
 }
 
 void CHeroArtPlace::showAll(Canvas & to)

+ 3 - 3
client/widgets/CArtifactHolder.h

@@ -66,11 +66,11 @@ public:
 class CHeroArtPlace: public CArtPlace
 {
 public:
-	using ClickHandler = std::function<void(CHeroArtPlace&)>;
+	using ClickFunctor = std::function<void(CHeroArtPlace&)>;
 
 	ArtifactPosition slot;
-	ClickHandler leftClickCallback;
-	ClickHandler rightClickCallback;
+	ClickFunctor leftClickCallback;
+	ClickFunctor showPopupCallback;
 
 	CHeroArtPlace(Point position, const CArtifactInstance * Art = nullptr);
 	void lockSlot(bool on);

+ 1 - 1
client/widgets/CArtifactsOfHeroBackpack.cpp

@@ -42,7 +42,7 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
 		artPlace = std::make_shared<CHeroArtPlace>(pos);
 		artPlace->setArtifact(nullptr);
 		artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
 		artPlaceIdx++;
 	}
 

+ 9 - 9
client/widgets/CArtifactsOfHeroBase.cpp

@@ -55,10 +55,10 @@ void CArtifactsOfHeroBase::setPutBackPickedArtifactCallback(PutBackPickedArtCall
 }
 
 void CArtifactsOfHeroBase::init(
-	CHeroArtPlace::ClickHandler lClickCallback,
-	CHeroArtPlace::ClickHandler rClickCallback,
+	CHeroArtPlace::ClickFunctor lClickCallback,
+	CHeroArtPlace::ClickFunctor showPopupCallback,
 	const Point & position,
-	BpackScrollHandler scrollHandler)
+	BpackScrollFunctor scrollCallback)
 {
 	// CArtifactsOfHeroBase::init may be transform to CArtifactsOfHeroBase::CArtifactsOfHeroBase if OBJECT_CONSTRUCTION_CAPTURING is removed
 	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
@@ -78,16 +78,16 @@ void CArtifactsOfHeroBase::init(
 		artPlace.second->slot = artPlace.first;
 		artPlace.second->setArtifact(nullptr);
 		artPlace.second->leftClickCallback = lClickCallback;
-		artPlace.second->rightClickCallback = rClickCallback;
+		artPlace.second->showPopupCallback = showPopupCallback;
 	}
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);
 		artPlace->leftClickCallback = lClickCallback;
-		artPlace->rightClickCallback = rClickCallback;
+		artPlace->showPopupCallback = showPopupCallback;
 	}
-	leftBackpackRoll = std::make_shared<CButton>(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [scrollHandler]() { scrollHandler(-1); }, EShortcut::MOVE_LEFT);
-	rightBackpackRoll = std::make_shared<CButton>(Point(632, 364), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [scrollHandler]() { scrollHandler(+1); }, EShortcut::MOVE_RIGHT);
+	leftBackpackRoll = std::make_shared<CButton>(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [scrollCallback]() {scrollCallback(-1);}, EShortcut::MOVE_LEFT);
+	rightBackpackRoll = std::make_shared<CButton>(Point(632, 364), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [scrollCallback]() {scrollCallback(+1);}, EShortcut::MOVE_RIGHT);
 	leftBackpackRoll->block(true);
 	rightBackpackRoll->block(true);
 
@@ -102,8 +102,8 @@ void CArtifactsOfHeroBase::leftClickArtPlace(CHeroArtPlace & artPlace)
 
 void CArtifactsOfHeroBase::rightClickArtPlace(CHeroArtPlace & artPlace)
 {
-	if(rightClickCallback)
-		rightClickCallback(*this, artPlace);
+	if(showPopupCallback)
+		showPopupCallback(*this, artPlace);
 }
 
 void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero)

+ 6 - 6
client/widgets/CArtifactsOfHeroBase.h

@@ -15,15 +15,15 @@ class CArtifactsOfHeroBase : public CIntObject
 {
 protected:
 	using ArtPlacePtr = std::shared_ptr<CHeroArtPlace>;
-	using BpackScrollHandler = std::function<void(int)>;
+	using BpackScrollFunctor = std::function<void(int)>;
 
 public:
 	using ArtPlaceMap = std::map<ArtifactPosition, ArtPlacePtr>;
-	using ClickHandler = std::function<void(CArtifactsOfHeroBase&, CHeroArtPlace&)>;
+	using ClickFunctor = std::function<void(CArtifactsOfHeroBase&, CHeroArtPlace&)>;
 	using PutBackPickedArtCallback = std::function<void()>;
 
-	ClickHandler leftClickCallback;
-	ClickHandler rightClickCallback;
+	ClickFunctor leftClickCallback;
+	ClickFunctor showPopupCallback;
 	
 	CArtifactsOfHeroBase();
 	virtual void putBackPickedArtifact();
@@ -61,8 +61,8 @@ protected:
 		Point(381,296) //18
 	};
 
-	virtual void init(CHeroArtPlace::ClickHandler lClickCallback, CHeroArtPlace::ClickHandler rClickCallback,
-		const Point & position, BpackScrollHandler scrollHandler);
+	virtual void init(CHeroArtPlace::ClickFunctor lClickCallback, CHeroArtPlace::ClickFunctor showPopupCallback,
+		const Point & position, BpackScrollFunctor scrollCallback);
 	// Assigns an artifacts to an artifact place depending on it's new slot ID
 	virtual void setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot, const CArtifactSet & artSet);
 	virtual void scrollBackpackForArtSet(int offset, const CArtifactSet & artSet);

+ 2 - 2
client/widgets/CArtifactsOfHeroKingdom.cpp

@@ -29,13 +29,13 @@ CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vecto
 		artPlace.second->slot = artPlace.first;
 		artPlace.second->setArtifact(nullptr);
 		artPlace.second->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace.second->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace.second->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
 	}
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);
 		artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
-		artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
+		artPlace->showPopupCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
 	}
 	leftBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, -1));
 	rightBackpackRoll->addCallback(std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, +1));

+ 119 - 0
client/widgets/CExchangeController.cpp

@@ -0,0 +1,119 @@
+/*
+ * CExchangeController.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 "CExchangeController.h"
+
+#include "../CPlayerInterface.h"
+
+#include "../widgets/CGarrisonInt.h"
+
+#include "../../CCallback.h"
+
+#include "../lib/mapObjects/CGHeroInstance.h"
+
+CExchangeController::CExchangeController(ObjectInstanceID hero1, ObjectInstanceID hero2)
+	: left(LOCPLINT->cb->getHero(hero1))
+	, right(LOCPLINT->cb->getHero(hero2))
+{
+}
+
+void CExchangeController::swapArmy()
+{
+	auto getStacks = [](const CArmedInstance * source) -> std::vector<std::pair<SlotID, CStackInstance*>>
+	{
+		auto slots = source->Slots();
+		return std::vector<std::pair<SlotID, CStackInstance*>>(slots.begin(), slots.end());
+	};
+
+	auto leftSlots = getStacks(left);
+	auto rightSlots = getStacks(right);
+
+	auto i = leftSlots.begin(), j = rightSlots.begin();
+
+	for(; i != leftSlots.end() && j != rightSlots.end(); i++, j++)
+	{
+		LOCPLINT->cb->swapCreatures(left, right, i->first, j->first);
+	}
+
+	if(i != leftSlots.end())
+	{
+		auto freeSlots = right->getFreeSlots();
+		auto slot = freeSlots.begin();
+
+		for(; i != leftSlots.end() && slot != freeSlots.end(); i++, slot++)
+		{
+			LOCPLINT->cb->swapCreatures(left, right, i->first, *slot);
+		}
+	}
+	else if(j != rightSlots.end())
+	{
+		auto freeSlots = left->getFreeSlots();
+		auto slot = freeSlots.begin();
+
+		for(; j != rightSlots.end() && slot != freeSlots.end(); j++, slot++)
+		{
+			LOCPLINT->cb->swapCreatures(left, right, *slot, j->first);
+		}
+	}
+}
+
+void CExchangeController::moveArmy(bool leftToRight, std::optional<SlotID> heldSlot)
+{
+	const auto source = leftToRight ? left : right;
+	const auto target = leftToRight ? right : left;
+
+	if(!heldSlot.has_value())
+	{
+		auto weakestSlot = vstd::minElementByFun(source->Slots(),
+			[](const std::pair<SlotID, CStackInstance*> & s) -> int
+			{
+				return s.second->getCreatureID().toCreature()->getAIValue();
+			});
+		heldSlot = weakestSlot->first;
+	}
+	LOCPLINT->cb->bulkMoveArmy(source->id, target->id, heldSlot.value());
+}
+
+void CExchangeController::moveStack(bool leftToRight, SlotID sourceSlot)
+{
+	const auto source = leftToRight ? left : right;
+	const auto target = leftToRight ? right : left;
+	auto creature = source->getCreature(sourceSlot);
+
+	if(creature == nullptr)
+		return;
+
+	SlotID targetSlot = target->getSlotFor(creature);
+	if(targetSlot.validSlot())
+	{
+		if(source->stacksCount() == 1 && source->needsLastStack())
+		{
+			LOCPLINT->cb->splitStack(source, target, sourceSlot, targetSlot,
+				target->getStackCount(targetSlot) + source->getStackCount(sourceSlot) - 1);
+		}
+		else
+		{
+			LOCPLINT->cb->mergeOrSwapStacks(source, target, sourceSlot, targetSlot);
+		}
+	}
+}
+
+void CExchangeController::swapArtifacts(bool equipped, bool baclpack)
+{
+	LOCPLINT->cb->bulkMoveArtifacts(left->id, right->id, true, equipped, baclpack);
+}
+
+void CExchangeController::moveArtifacts(bool leftToRight, bool equipped, bool baclpack)
+{
+	const auto source = leftToRight ? left : right;
+	const auto target = leftToRight ? right : left;
+
+	LOCPLINT->cb->bulkMoveArtifacts(source->id, target->id, false, equipped, baclpack);
+}

+ 30 - 0
client/widgets/CExchangeController.h

@@ -0,0 +1,30 @@
+/*
+ * CExchangeController.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 "../windows/CWindowObject.h"
+ #include "CWindowWithArtifacts.h"
+ 
+class CCallback;
+
+class CExchangeController
+{
+public:
+	CExchangeController(ObjectInstanceID hero1, ObjectInstanceID hero2);
+	void swapArmy();
+	void moveArmy(bool leftToRight, std::optional<SlotID> heldSlot);
+	void moveStack(bool leftToRight, SlotID sourceSlot);
+	void swapArtifacts(bool equipped, bool baclpack);
+	void moveArtifacts(bool leftToRight, bool equipped, bool baclpack);
+
+private:
+	const CGHeroInstance * left;
+	const CGHeroInstance * right;
+};

+ 7 - 2
client/widgets/CWindowWithArtifacts.cpp

@@ -32,18 +32,23 @@
 #include "../../lib/CConfigHandler.h"
 
 void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr artSet)
+{
+	artSets.emplace_back(artSet);
+}
+
+void CWindowWithArtifacts::addSetAndCallbacks(CArtifactsOfHeroPtr artSet)
 {
 	CArtifactsOfHeroBase::PutBackPickedArtCallback artPutBackHandler = []() -> void
 	{
 		CCS->curh->dragAndDropCursor(nullptr);
 	};
 
-	artSets.emplace_back(artSet);
+	addSet(artSet);
 	std::visit([this, artPutBackHandler](auto artSetWeak)
 		{
 			auto artSet = artSetWeak.lock();
 			artSet->leftClickCallback = std::bind(&CWindowWithArtifacts::leftClickArtPlaceHero, this, _1, _2);
-			artSet->rightClickCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2);
+			artSet->showPopupCallback = std::bind(&CWindowWithArtifacts::rightClickArtPlaceHero, this, _1, _2);
 			artSet->setPutBackPickedArtifactCallback(artPutBackHandler);
 		}, artSet);
 }

+ 2 - 1
client/widgets/CWindowWithArtifacts.h

@@ -28,6 +28,7 @@ public:
 	using CloseCallback = std::function<void()>;
 
 	void addSet(CArtifactsOfHeroPtr artSet);
+	void addSetAndCallbacks(CArtifactsOfHeroPtr artSet);
 	void addCloseCallback(CloseCallback callback);
 	const CGHeroInstance * getHeroPickedArtifact();
 	const CArtifactInstance * getPickedArtifact();
@@ -39,7 +40,7 @@ public:
 	void artifactDisassembled(const ArtifactLocation & artLoc) override;
 	void artifactAssembled(const ArtifactLocation & artLoc) override;
 
-private:
+protected:
 	std::vector<CArtifactsOfHeroPtr> artSets;
 	CloseCallback closeCallback;
 

+ 1 - 1
client/windows/CHeroBackpackWindow.cpp

@@ -28,7 +28,7 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero)
 
 	arts = std::make_shared<CArtifactsOfHeroBackpack>(Point(windowMargin, windowMargin));
 	arts->setHero(hero);
-	addSet(arts);
+	addSetAndCallbacks(arts);
 
 	addCloseCallback(std::bind(&CHeroBackpackWindow::close, this));
 

+ 1 - 1
client/windows/CHeroWindow.cpp

@@ -219,7 +219,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 		{
 			arts = std::make_shared<CArtifactsOfHeroMain>(Point(-65, -8));
 			arts->setHero(curHero);
-			addSet(arts);
+			addSetAndCallbacks(arts);
 		}
 
 		int serial = LOCPLINT->cb->getHeroSerial(curHero, false);

+ 1 - 1
client/windows/CKingdomInterface.cpp

@@ -706,7 +706,7 @@ std::shared_ptr<CIntObject> CKingdHeroList::createHeroItem(size_t index)
 	if(index < heroesList.size())
 	{
 		auto hero = std::make_shared<CHeroItem>(heroesList[index]);
-		addSet(hero->heroArts);
+		addSetAndCallbacks(hero->heroArts);
 		return hero;
 	}
 	else

+ 2 - 2
client/windows/CTradeWindow.cpp

@@ -680,7 +680,7 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta
 		arts = std::make_shared<CArtifactsOfHeroMarket>(Point(-361, 46));
 		arts->selectArtCallback = std::bind(&CTradeWindow::artifactSelected, this, _1);
 		arts->setHero(hero);
-		addSet(arts);
+		addSetAndCallbacks(arts);
 	}
 	initItems(false);
 	initItems(true);
@@ -1133,7 +1133,7 @@ CAltarWindow::CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero,
 
 		arts = std::make_shared<CArtifactsOfHeroAltar>(Point(-365, -12));
 		arts->setHero(hero);
-		addSet(arts);
+		addSetAndCallbacks(arts);
 
 		initItems(true);
 		initItems(false);

+ 64 - 239
client/windows/GUIClasses.cpp

@@ -12,6 +12,7 @@
 
 #include "CCastleInterface.h"
 #include "CCreatureWindow.h"
+#include "CHeroBackpackWindow.h"
 #include "CHeroWindow.h"
 #include "InfoWindows.h"
 
@@ -19,62 +20,40 @@
 #include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
 #include "../CVideoHandler.h"
-#include "../CServerHandler.h"
-
-#include "../battle/BattleInterfaceClasses.h"
-#include "../battle/BattleInterface.h"
 
 #include "../gui/CGuiHandler.h"
 #include "../gui/CursorHandler.h"
-#include "../gui/TextAlignment.h"
 #include "../gui/Shortcut.h"
 #include "../gui/WindowHandler.h"
 
 #include "../widgets/CComponent.h"
 #include "../widgets/CGarrisonInt.h"
-#include "../widgets/MiscWidgets.h"
 #include "../widgets/CreatureCostBox.h"
 #include "../widgets/Buttons.h"
 #include "../widgets/Slider.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/ObjectLists.h"
 
-#include "../lobby/CSavingScreen.h"
 #include "../render/Canvas.h"
 #include "../render/CAnimation.h"
 #include "../render/IRenderHandler.h"
-#include "../CMT.h"
 
 #include "../../CCallback.h"
 
-#include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
 #include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../lib/mapObjectConstructors/CommonConstructors.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CGMarket.h"
-#include "../lib/ArtifactUtils.h"
 #include "../lib/mapObjects/CGTownInstance.h"
 #include "../lib/mapObjects/ObjectTemplate.h"
 #include "../lib/gameState/CGameState.h"
-#include "../lib/gameState/InfoAboutArmy.h"
 #include "../lib/gameState/SThievesGuildInfo.h"
-#include "../lib/CArtHandler.h"
-#include "../lib/CBuildingHandler.h"
-#include "../lib/CConfigHandler.h"
-#include "../lib/CCreatureHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/GameSettings.h"
 #include "../lib/CondSh.h"
 #include "../lib/CSkillHandler.h"
-#include "../lib/spells/CSpellHandler.h"
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/CStopWatch.h"
-#include "../lib/CTownHandler.h"
-#include "../lib/GameConstants.h"
-#include "../lib/bonuses/Bonus.h"
-#include "../lib/NetPacksBase.h"
-#include "../lib/StartInfo.h"
 #include "../lib/TextOperations.h"
 
 CRecruitmentWindow::CCreatureCard::CCreatureCard(CRecruitmentWindow * window, const CCreature * crea, int totalAmount)
@@ -623,215 +602,9 @@ static bool isQuickExchangeLayoutAvailable()
 	return CResourceHandler::get()->existsResource(ImagePath::builtin("SPRITES/" + QUICK_EXCHANGE_BG));
 }
 
-CExchangeController::CExchangeController(CExchangeWindow * view, ObjectInstanceID hero1, ObjectInstanceID hero2)
-	:left(LOCPLINT->cb->getHero(hero1)), right(LOCPLINT->cb->getHero(hero2)), cb(LOCPLINT->cb), view(view)
-{
-}
-
-std::function<void()> CExchangeController::onMoveArmyToLeft()
-{
-	return [&]() { moveArmy(false); };
-}
-
-std::function<void()> CExchangeController::onMoveArmyToRight()
-{
-	return [&]() { moveArmy(true); };
-}
-
-std::vector<CArtifactInstance *> getBackpackArts(const CGHeroInstance * hero)
-{
-	std::vector<CArtifactInstance *> result;
-
-	for(auto slot : hero->artifactsInBackpack)
-	{
-		result.push_back(slot.artifact);
-	}
-
-	return result;
-}
-
-std::function<void()> CExchangeController::onSwapArtifacts()
-{
-	return [&]()
-	{
-		cb->bulkMoveArtifacts(left->id, right->id, true);
-	};
-}
-
-std::function<void()> CExchangeController::onMoveArtifactsToLeft()
-{
-	return [&]() { moveArtifacts(false); };
-}
-
-std::function<void()> CExchangeController::onMoveArtifactsToRight()
-{
-	return [&]() { moveArtifacts(true); };
-}
-
-std::vector<std::pair<SlotID, CStackInstance *>> getStacks(const CArmedInstance * source)
-{
-	auto slots = source->Slots();
-
-	return std::vector<std::pair<SlotID, CStackInstance *>>(slots.begin(), slots.end());
-}
-
-std::function<void()> CExchangeController::onSwapArmy()
-{
-	return [&]()
-	{
-		if(left->tempOwner != cb->getPlayerID()
-		   || right->tempOwner != cb->getPlayerID())
-		{
-			return;
-		}
-
-		auto leftSlots = getStacks(left);
-		auto rightSlots = getStacks(right);
-
-		auto i = leftSlots.begin(), j = rightSlots.begin();
-
-		for(; i != leftSlots.end() && j != rightSlots.end(); i++, j++)
-		{
-			cb->swapCreatures(left, right, i->first, j->first);
-		}
-
-		if(i != leftSlots.end())
-		{
-			auto freeSlots = right->getFreeSlots();
-			auto slot = freeSlots.begin();
-
-			for(; i != leftSlots.end() && slot != freeSlots.end(); i++, slot++)
-			{
-				cb->swapCreatures(left, right, i->first, *slot);
-			}
-		}
-		else if(j != rightSlots.end())
-		{
-			auto freeSlots = left->getFreeSlots();
-			auto slot = freeSlots.begin();
-
-			for(; j != rightSlots.end() && slot != freeSlots.end(); j++, slot++)
-			{
-				cb->swapCreatures(left, right, *slot, j->first);
-			}
-		}
-	};
-}
-
-std::function<void()> CExchangeController::onMoveStackToLeft(SlotID slotID)
-{
-	return [=]()
-	{
-		if(right->tempOwner != cb->getPlayerID())
-		{
-			return;
-		}
-
-		moveStack(right, left, slotID);
-	};
-}
-
-std::function<void()> CExchangeController::onMoveStackToRight(SlotID slotID)
-{
-	return [=]()
-	{
-		if(left->tempOwner != cb->getPlayerID())
-		{
-			return;
-		}
-
-		moveStack(left, right, slotID);
-	};
-}
-
-void CExchangeController::moveStack(
-	const CGHeroInstance * source,
-	const CGHeroInstance * target,
-	SlotID sourceSlot)
-{
-	auto creature = source->getCreature(sourceSlot);
-	if(creature == nullptr)
-		return;
-
-	SlotID targetSlot = target->getSlotFor(creature);
-
-	if(targetSlot.validSlot())
-	{
-		if(source->stacksCount() == 1 && source->needsLastStack())
-		{
-			cb->splitStack(
-				source,
-				target,
-				sourceSlot,
-				targetSlot,
-				target->getStackCount(targetSlot) + source->getStackCount(sourceSlot) - 1);
-		}
-		else
-		{
-			cb->mergeOrSwapStacks(source, target, sourceSlot, targetSlot);
-		}
-	}
-}
-
-void CExchangeController::moveArmy(bool leftToRight)
-{
-	const CGHeroInstance * source = leftToRight ? left : right;
-	const CGHeroInstance * target = leftToRight ? right : left;
-	const CGarrisonSlot * selection =  this->view->getSelectedSlotID();
-	SlotID slot;
-
-	if(source->tempOwner != cb->getPlayerID())
-	{
-		return;
-	}
-
-	if(selection && selection->our() && selection->getObj() == source)
-	{
-		slot = selection->getSlot();
-	}
-	else
-	{
-		auto weakestSlot = vstd::minElementByFun(
-			source->Slots(), 
-			[](const std::pair<SlotID, CStackInstance *> & s) -> int
-			{
-				return s.second->getCreatureID().toCreature()->getAIValue();
-			});
-
-		slot = weakestSlot->first;
-	}
-
-	cb->bulkMoveArmy(source->id, target->id, slot);
-}
-
-void CExchangeController::moveArtifacts(bool leftToRight)
-{
-	const CGHeroInstance * source = leftToRight ? left : right;
-	const CGHeroInstance * target = leftToRight ? right : left;
-
-	if(source->tempOwner != cb->getPlayerID())
-	{
-		return;
-	}
-
-	cb->bulkMoveArtifacts(source->id, target->id, false);
-}
-
-void CExchangeController::moveArtifact(
-	const CGHeroInstance * source,
-	const CGHeroInstance * target,
-	ArtifactPosition srcPosition)
-{
-	auto srcLocation = ArtifactLocation(source, srcPosition);
-	auto dstLocation = ArtifactLocation(target,
-		ArtifactUtils::getArtAnyPosition(target, source->getArt(srcPosition)->getTypeId()));
-
-	cb->swapArtifacts(srcLocation, dstLocation);
-}
-
 CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID)
 	: CStatusbarWindow(PLAYER_COLORED | BORDERED, ImagePath::builtin(isQuickExchangeLayoutAvailable() ? QUICK_EXCHANGE_BG : "TRADE2")),
-	controller(this, hero1, hero2),
+	controller(hero1, hero2),
 	moveStackLeftButtons(),
 	moveStackRightButtons()
 {
@@ -890,8 +663,8 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 	artifs[1] = std::make_shared<CArtifactsOfHeroMain>(Point(98, 150));
 	artifs[1]->setHero(heroInst[1]);
 
-	addSet(artifs[0]);
-	addSet(artifs[1]);
+	addSetAndCallbacks(artifs[0]);
+	addSetAndCallbacks(artifs[1]);
 
 	for(int g=0; g<4; ++g)
 	{
@@ -982,12 +755,62 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 
 	if(qeLayout)
 	{
-		moveAllGarrButtonLeft    = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), controller.onMoveArmyToRight());
-		echangeGarrButton        = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]), controller.onSwapArmy());
-		moveAllGarrButtonRight   = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), controller.onMoveArmyToLeft());
-		moveArtifactsButtonLeft  = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), controller.onMoveArtifactsToRight());
-		echangeArtifactsButton   = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]), controller.onSwapArtifacts());
-		moveArtifactsButtonRight = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), controller.onMoveArtifactsToLeft());
+		auto moveArtifacts = [](const std::function<void(bool, bool)> moveRoutine) -> void
+		{
+			bool moveEquipped = true;
+			bool moveBackpack = true;
+
+			if(GH.isKeyboardCtrlDown())
+				moveBackpack = false;
+			else if(GH.isKeyboardShiftDown())
+				moveEquipped = false;
+			moveRoutine(moveEquipped, moveBackpack);
+		};
+
+		auto moveArmy = [this](const bool leftToRight) -> void
+		{
+			std::optional<SlotID> slotId = std::nullopt;
+			if(auto slot = getSelectedSlotID())
+				slotId = slot->getSlot();
+			controller.moveArmy(leftToRight, slotId);
+		};
+
+		auto openBackpack = [this](const CGHeroInstance * hero) -> void
+		{
+			GH.windows().createAndPushWindow<CHeroBackpackWindow>(hero);
+			for(auto artSet : artSets)
+				GH.windows().topWindow<CHeroBackpackWindow>()->addSet(artSet);
+		};
+
+		moveAllGarrButtonLeft    = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
+			std::bind(moveArmy, true));
+		exchangeGarrButton       = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]),
+			std::bind(&CExchangeController::swapArmy, &controller));
+		moveAllGarrButtonRight   = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
+			std::bind(moveArmy, false));
+		moveArtifactsButtonLeft  = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
+			std::bind(moveArtifacts, [this](bool equipped, bool baclpack) -> void {controller.moveArtifacts(true, equipped, baclpack);}));
+		exchangeArtifactsButton  = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]),
+			std::bind(moveArtifacts, [this](bool equipped, bool baclpack) -> void {controller.swapArtifacts(equipped, baclpack);}));
+		moveArtifactsButtonRight = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
+			std::bind(moveArtifacts, [this](bool equipped, bool baclpack) -> void {controller.moveArtifacts(false, equipped, baclpack);}));
+		backpackButtonLeft       = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
+			std::bind(openBackpack, heroInst[0]));
+		backpackButtonLeft->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
+		backpackButtonRight      = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
+			std::bind(openBackpack, heroInst[1]));
+		backpackButtonRight->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
+
+		auto leftHeroBlock = heroInst[0]->tempOwner != LOCPLINT->cb->getPlayerID();
+		auto rightHeroBlock = heroInst[1]->tempOwner != LOCPLINT->cb->getPlayerID();
+		moveAllGarrButtonLeft->block(leftHeroBlock);
+		exchangeGarrButton->block(leftHeroBlock || rightHeroBlock);
+		moveAllGarrButtonRight->block(rightHeroBlock);
+		moveArtifactsButtonLeft->block(leftHeroBlock);
+		exchangeArtifactsButton->block(leftHeroBlock || rightHeroBlock);
+		moveArtifactsButtonRight->block(rightHeroBlock);
+		backpackButtonLeft->block(leftHeroBlock);
+		backpackButtonRight->block(rightHeroBlock);
 
 		for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
 		{
@@ -996,14 +819,16 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 					Point(484 + 35 * i, 154),
 					AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/unitLeft.DEF"),
 					CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
-					controller.onMoveStackToLeft(SlotID(i))));
+					std::bind(&CExchangeController::moveStack, &controller, false, SlotID(i))));
+			moveStackLeftButtons.back()->block(leftHeroBlock);
 
 			moveStackRightButtons.push_back(
 				std::make_shared<CButton>(
 					Point(66 + 35 * i, 154),
 					AnimationPath::builtin(QUICK_EXCHANGE_MOD_PREFIX + "/unitRight.DEF"),
 					CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
-					controller.onMoveStackToRight(SlotID(i))));
+					std::bind(&CExchangeController::moveStack, &controller, true, SlotID(i))));
+			moveStackLeftButtons.back()->block(rightHeroBlock);
 		}
 	}
 

+ 5 - 37
client/windows/GUIClasses.h

@@ -9,10 +9,8 @@
  */
 #pragma once
 
-#include "CWindowObject.h"
-#include "../lib/GameConstants.h"
 #include "../lib/ResourceSet.h"
-#include "../lib/int3.h"
+#include "../widgets/CExchangeController.h"
 #include "../widgets/CWindowWithArtifacts.h"
 #include "../widgets/Images.h"
 
@@ -28,16 +26,13 @@ class CreatureCostBox;
 class CCreaturePic;
 class MoraleLuckBox;
 class CHeroArea;
-class CMinorResDataBar;
 class CSlider;
 class CComponentBox;
 class CTextInput;
 class CListBox;
 class CLabelGroup;
-class CToggleButton;
 class CGStatusBar;
 class CTextBox;
-class CResDataBar;
 class CGarrisonInt;
 class CGarrisonSlot;
 
@@ -246,35 +241,6 @@ public:
 	void show(Canvas & to) override;
 };
 
-class CCallback;
-class CExchangeWindow;
-
-class CExchangeController
-{
-private:
-	const CGHeroInstance * left;
-	const CGHeroInstance * right;
-	std::shared_ptr<CCallback> cb;
-	CExchangeWindow * view;
-
-public:
-	CExchangeController(CExchangeWindow * view, ObjectInstanceID hero1, ObjectInstanceID hero2);
-	std::function<void()> onMoveArmyToRight();
-	std::function<void()> onSwapArmy();
-	std::function<void()> onMoveArmyToLeft();
-	std::function<void()> onSwapArtifacts();
-	std::function<void()> onMoveArtifactsToLeft();
-	std::function<void()> onMoveArtifactsToRight();
-	std::function<void()> onMoveStackToLeft(SlotID slotID);
-	std::function<void()> onMoveStackToRight(SlotID slotID);
-
-private:
-	void moveArmy(bool leftToRight);
-	void moveArtifacts(bool leftToRight);
-	void moveArtifact(const CGHeroInstance * source, const CGHeroInstance * target, ArtifactPosition srcPosition);
-	void moveStack(const CGHeroInstance * source, const CGHeroInstance * target, SlotID sourceSlot);
-};
-
 class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public CWindowWithArtifacts
 {
 	std::array<std::shared_ptr<CLabel>, 2> titles;
@@ -303,13 +269,15 @@ class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public
 
 	std::shared_ptr<CGarrisonInt> garr;
 	std::shared_ptr<CButton> moveAllGarrButtonLeft;
-	std::shared_ptr<CButton> echangeGarrButton;
+	std::shared_ptr<CButton> exchangeGarrButton;
 	std::shared_ptr<CButton> moveAllGarrButtonRight;
 	std::shared_ptr<CButton> moveArtifactsButtonLeft;
-	std::shared_ptr<CButton> echangeArtifactsButton;
+	std::shared_ptr<CButton> exchangeArtifactsButton;
 	std::shared_ptr<CButton> moveArtifactsButtonRight;
 	std::vector<std::shared_ptr<CButton>> moveStackLeftButtons;
 	std::vector<std::shared_ptr<CButton>> moveStackRightButtons;
+	std::shared_ptr<CButton> backpackButtonLeft;
+	std::shared_ptr<CButton> backpackButtonRight;
 	CExchangeController controller;
 
 public:

+ 0 - 1
lib/ArtifactUtils.cpp

@@ -170,7 +170,6 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifa
 	auto * artInst = new CArtifactInstance(art);
 	if(art->isCombined())
 	{
-		assert(art->isCombined());
 		for(const auto & part : art->getConstituents())
 			artInst->addPart(ArtifactUtils::createNewArtifactInstance(part), ArtifactPosition::PRE_FIRST);
 	}

+ 17 - 9
lib/CArtHandler.cpp

@@ -9,7 +9,6 @@
  */
 
 #include "StdInc.h"
-#include "CArtHandler.h"
 
 #include "ArtifactUtils.h"
 #include "CGeneralTextHandler.h"
@@ -925,8 +924,10 @@ unsigned CArtifactSet::getArtPosCount(const ArtifactID & aid, bool onlyWorn, boo
 	return 0;
 }
 
-void CArtifactSet::putArtifact(ArtifactPosition slot, CArtifactInstance * art)
+CArtifactSet::ArtPlacementMap CArtifactSet::putArtifact(ArtifactPosition slot, CArtifactInstance * art)
 {
+	ArtPlacementMap resArtPlacement;
+
 	setNewArtSlot(slot, art, false);
 	if(art->artType->isCombined() && ArtifactUtils::isSlotEquipment(slot))
 	{
@@ -938,19 +939,26 @@ void CArtifactSet::putArtifact(ArtifactPosition slot, CArtifactInstance * art)
 				mainPart = part.art;
 				break;
 			}
-
-		for(auto & part : art->getPartsInfo())
+		
+		for(const auto & part : art->getPartsInfo())
 		{
 			if(part.art != mainPart)
 			{
-				if(!part.art->artType->canBePutAt(this, part.slot))
-					part.slot = ArtifactUtils::getArtAnyPosition(this, part.art->getTypeId());
+				auto partSlot = part.slot;
+				if(!part.art->artType->canBePutAt(this, partSlot))
+					partSlot = ArtifactUtils::getArtAnyPosition(this, part.art->getTypeId());
 
-				assert(ArtifactUtils::isSlotEquipment(part.slot));
-				setNewArtSlot(part.slot, part.art, true);
+				assert(ArtifactUtils::isSlotEquipment(partSlot));
+				setNewArtSlot(partSlot, part.art, true);
+				resArtPlacement.emplace(std::make_pair(part.art, partSlot));
+			}
+			else
+			{
+				resArtPlacement.emplace(std::make_pair(part.art, part.slot));
 			}
 		}
 	}
+	return resArtPlacement;
 }
 
 void CArtifactSet::removeArtifact(ArtifactPosition slot)
@@ -1031,7 +1039,7 @@ bool CArtifactSet::isPositionFree(const ArtifactPosition & pos, bool onlyLockChe
 	return true; //no slot means not used
 }
 
-void CArtifactSet::setNewArtSlot(const ArtifactPosition & slot, CArtifactInstance * art, bool locked)
+void CArtifactSet::setNewArtSlot(const ArtifactPosition & slot, ConstTransitivePtr<CArtifactInstance> art, bool locked)
 {
 	assert(!vstd::contains(artifactsWorn, slot));
 

+ 4 - 2
lib/CArtHandler.h

@@ -246,11 +246,13 @@ struct DLL_LINKAGE ArtSlotInfo
 class DLL_LINKAGE CArtifactSet
 {
 public:
+	using ArtPlacementMap = std::map<CArtifactInstance*, ArtifactPosition>;
+
 	std::vector<ArtSlotInfo> artifactsInBackpack; //hero's artifacts from bag
 	std::map<ArtifactPosition, ArtSlotInfo> artifactsWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
 	std::vector<ArtSlotInfo> artifactsTransitionPos; // Used as transition position for dragAndDrop artifact exchange
 
-	void setNewArtSlot(const ArtifactPosition & slot, CArtifactInstance * art, bool locked);
+	void setNewArtSlot(const ArtifactPosition & slot, ConstTransitivePtr<CArtifactInstance> art, bool locked);
 	void eraseArtSlot(const ArtifactPosition & slot);
 
 	const ArtSlotInfo * getSlot(const ArtifactPosition & pos) const;
@@ -275,7 +277,7 @@ public:
 	unsigned getArtPosCount(const ArtifactID & aid, bool onlyWorn = true, bool searchBackpackAssemblies = true, bool allowLocked = true) const;
 
 	virtual ArtBearer::ArtBearer bearerType() const = 0;
-	virtual void putArtifact(ArtifactPosition slot, CArtifactInstance * art);
+	virtual ArtPlacementMap putArtifact(ArtifactPosition slot, CArtifactInstance * art);
 	virtual void removeArtifact(ArtifactPosition slot);
 	virtual ~CArtifactSet();
 

+ 10 - 5
lib/CArtifactInstance.cpp

@@ -44,15 +44,19 @@ bool CCombinedArtifactInstance::isPart(const CArtifactInstance * supposedPart) c
 	return false;
 }
 
-std::vector<CCombinedArtifactInstance::PartInfo> & CCombinedArtifactInstance::getPartsInfo()
+const std::vector<CCombinedArtifactInstance::PartInfo> & CCombinedArtifactInstance::getPartsInfo() const
 {
-	// TODO romove this func. encapsulation violation
 	return partsInfo;
 }
 
-const std::vector<CCombinedArtifactInstance::PartInfo> & CCombinedArtifactInstance::getPartsInfo() const
+void CCombinedArtifactInstance::addPlacementMap(CArtifactSet::ArtPlacementMap & placementMap)
 {
-	return partsInfo;
+	if(!placementMap.empty())
+		for(auto & part : partsInfo)
+		{
+			assert(placementMap.find(part.art) != placementMap.end());
+			part.slot = placementMap.at(part.art);
+		}
 }
 
 SpellID CScrollArtifactInstance::getScrollSpellID() const
@@ -163,7 +167,8 @@ bool CArtifactInstance::isCombined() const
 
 void CArtifactInstance::putAt(const ArtifactLocation & al)
 {
-	al.getHolderArtSet()->putArtifact(al.slot, this);
+	auto placementMap = al.getHolderArtSet()->putArtifact(al.slot, this);
+	addPlacementMap(placementMap);
 }
 
 void CArtifactInstance::removeFrom(const ArtifactLocation & al)

+ 2 - 1
lib/CArtifactInstance.h

@@ -12,6 +12,7 @@
 #include "bonuses/CBonusSystemNode.h"
 #include "GameConstants.h"
 #include "ConstTransitivePtr.h"
+#include "CArtHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -37,8 +38,8 @@ public:
 	void addPart(CArtifactInstance * art, const ArtifactPosition & slot);
 	// Checks if supposed part inst is part of this combined art inst
 	bool isPart(const CArtifactInstance * supposedPart) const;
-	std::vector<PartInfo> & getPartsInfo();
 	const std::vector<PartInfo> & getPartsInfo() const;
+	void addPlacementMap(CArtifactSet::ArtPlacementMap & placementMap);
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{

+ 3 - 4
lib/CCreatureSet.cpp

@@ -868,14 +868,13 @@ ArtBearer::ArtBearer CStackInstance::bearerType() const
 	return ArtBearer::CREATURE;
 }
 
-void CStackInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
+CStackInstance::ArtPlacementMap CStackInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
 {
 	assert(!getArt(pos));
 	assert(art->artType->canBePutAt(this, pos));
 
-	CArtifactSet::putArtifact(pos, art);
-	if(ArtifactUtils::isSlotEquipment(pos))
-		attachTo(*art);
+	attachTo(*art);
+	return CArtifactSet::putArtifact(pos, art);
 }
 
 void CStackInstance::removeArtifact(ArtifactPosition pos)

+ 1 - 1
lib/CCreatureSet.h

@@ -121,7 +121,7 @@ public:
 	void setArmyObj(const CArmedInstance *ArmyObj);
 	virtual void giveStackExp(TExpType exp);
 	bool valid(bool allowUnrandomized) const;
-	void putArtifact(ArtifactPosition pos, CArtifactInstance * art) override;//from CArtifactSet
+	ArtPlacementMap putArtifact(ArtifactPosition pos, CArtifactInstance * art) override;//from CArtifactSet
 	void removeArtifact(ArtifactPosition pos) override;
 	ArtBearer::ArtBearer bearerType() const override; //from CArtifactSet
 	virtual std::string nodeName() const override; //from CBonusSystemnode

+ 7 - 1
lib/NetPacks.h

@@ -2439,12 +2439,16 @@ struct DLL_LINKAGE BulkExchangeArtifacts : public CPackForServer
 	ObjectInstanceID srcHero;
 	ObjectInstanceID dstHero;
 	bool swap = false;
+	bool equipped = true;
+	bool backpack = true;
 
 	BulkExchangeArtifacts() = default;
-	BulkExchangeArtifacts(const ObjectInstanceID & srcHero, const ObjectInstanceID & dstHero, bool swap)
+	BulkExchangeArtifacts(const ObjectInstanceID & srcHero, const ObjectInstanceID & dstHero, bool swap, bool equipped, bool backpack)
 		: srcHero(srcHero)
 		, dstHero(dstHero)
 		, swap(swap)
+		, equipped(equipped)
+		, backpack(backpack)
 	{
 	}
 
@@ -2456,6 +2460,8 @@ struct DLL_LINKAGE BulkExchangeArtifacts : public CPackForServer
 		h & srcHero;
 		h & dstHero;
 		h & swap;
+		h & equipped;
+		h & backpack;
 	}
 };
 

+ 2 - 2
lib/mapObjects/CGHeroInstance.cpp

@@ -1066,13 +1066,13 @@ std::string CGHeroInstance::getBiographyTextID() const
 	return "";
 }
 
-void CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance *art)
+CGHeroInstance::ArtPlacementMap CGHeroInstance::putArtifact(ArtifactPosition pos, CArtifactInstance * art)
 {
 	assert(art->artType->canBePutAt(this, pos));
 
-	CArtifactSet::putArtifact(pos, art);
 	if(ArtifactUtils::isSlotEquipment(pos))
 		attachTo(*art);
+	return CArtifactSet::putArtifact(pos, art);
 }
 
 void CGHeroInstance::removeArtifact(ArtifactPosition pos)

+ 1 - 1
lib/mapObjects/CGHeroInstance.h

@@ -229,7 +229,7 @@ public:
 	void initHero(CRandomGenerator & rand);
 	void initHero(CRandomGenerator & rand, const HeroTypeID & SUBID);
 
-	void putArtifact(ArtifactPosition pos, CArtifactInstance * art) override;
+	ArtPlacementMap putArtifact(ArtifactPosition pos, CArtifactInstance * art) override;
 	void removeArtifact(ArtifactPosition pos) override;
 	void initExp(CRandomGenerator & rand);
 	void initArmy(CRandomGenerator & rand, IArmyDescriptor *dst = nullptr);

+ 1 - 1
lib/mapping/CMap.cpp

@@ -492,7 +492,7 @@ void CMap::checkForObjectives()
 	}
 }
 
-void CMap::addNewArtifactInstance(CArtifactInstance * art)
+void CMap::addNewArtifactInstance(ConstTransitivePtr<CArtifactInstance> art)
 {
 	art->setId(static_cast<ArtifactInstanceID>(artInstances.size()));
 	artInstances.emplace_back(art);

+ 1 - 1
lib/mapping/CMap.h

@@ -94,7 +94,7 @@ public:
 	void removeBlockVisTiles(CGObjectInstance * obj, bool total = false);
 	void calculateGuardingGreaturePositions();
 
-	void addNewArtifactInstance(CArtifactInstance * art);
+	void addNewArtifactInstance(ConstTransitivePtr<CArtifactInstance> art);
 	void eraseArtifactInstance(CArtifactInstance * art);
 
 	void addNewQuestInstance(CQuest * quest);

+ 3 - 3
lib/mapping/MapFormatH3M.cpp

@@ -941,10 +941,10 @@ 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 artifactPos = ArtifactPosition(slot);
-	if(artifact->canBePutAt(ArtifactLocation(hero, artifactPos)))
+	auto dstLoc = ArtifactLocation(hero, ArtifactPosition(slot));
+	if(artifact->canBePutAt(dstLoc))
 	{
-		hero->putArtifact(artifactPos, artifact);
+		artifact->putAt(dstLoc);
 	}
 	else
 	{

+ 30 - 19
server/CGameHandler.cpp

@@ -2744,7 +2744,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
 	return true;
 }
 
-bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap)
+bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
 {
 	// Make sure exchange is even possible between the two heroes.
 	if(!isAllowedExchange(srcHero, dstHero))
@@ -2799,34 +2799,45 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
 				slots.push_back(BulkMoveArtifacts::LinkedSlots(slot, slot));
 			}
 		};
-		// Move over artifacts that are worn srcHero -> dstHero
-		moveArtsWorn(psrcHero, pdstHero, slotsSrcDst);
-		artFittingSet.artifactsWorn.clear();
-		// Move over artifacts that are worn dstHero -> srcHero
-		moveArtsWorn(pdstHero, psrcHero, slotsDstSrc);
-		// Move over artifacts that are in backpack srcHero -> dstHero
-		moveArtsInBackpack(psrcHero, slotsSrcDst);
-		// Move over artifacts that are in backpack dstHero -> srcHero
-		moveArtsInBackpack(pdstHero, slotsDstSrc);
+		if(equipped)
+		{
+			// Move over artifacts that are worn srcHero -> dstHero
+			moveArtsWorn(psrcHero, pdstHero, slotsSrcDst);
+			artFittingSet.artifactsWorn.clear();
+			// Move over artifacts that are worn dstHero -> srcHero
+			moveArtsWorn(pdstHero, psrcHero, slotsDstSrc);
+		}
+		if(backpack)
+		{
+			// Move over artifacts that are in backpack srcHero -> dstHero
+			moveArtsInBackpack(psrcHero, slotsSrcDst);
+			// Move over artifacts that are in backpack dstHero -> srcHero
+			moveArtsInBackpack(pdstHero, slotsDstSrc);
+		}
 	}
 	else
 	{
 		artFittingSet.artifactsInBackpack = pdstHero->artifactsInBackpack;
 		artFittingSet.artifactsWorn = pdstHero->artifactsWorn;
-
-		// Move over artifacts that are worn
-		for(auto & artInfo : psrcHero->artifactsWorn)
+		if(equipped)
 		{
-			if(ArtifactUtils::isArtRemovable(artInfo))
+			// Move over artifacts that are worn
+			for(auto & artInfo : psrcHero->artifactsWorn)
 			{
-				moveArtifact(psrcHero->getArt(artInfo.first), artInfo.first, pdstHero, slotsSrcDst);
+				if(ArtifactUtils::isArtRemovable(artInfo))
+				{
+					moveArtifact(psrcHero->getArt(artInfo.first), artInfo.first, pdstHero, slotsSrcDst);
+				}
 			}
 		}
-		// Move over artifacts that are in backpack
-		for(auto & slotInfo : psrcHero->artifactsInBackpack)
+		if(backpack)
 		{
-			moveArtifact(psrcHero->getArt(psrcHero->getArtPos(slotInfo.artifact)),
-				psrcHero->getArtPos(slotInfo.artifact), pdstHero, slotsSrcDst);
+			// Move over artifacts that are in backpack
+			for(auto & slotInfo : psrcHero->artifactsInBackpack)
+			{
+				moveArtifact(psrcHero->getArt(psrcHero->getArtPos(slotInfo.artifact)),
+					psrcHero->getArtPos(slotInfo.artifact), pdstHero, slotsSrcDst);
+			}
 		}
 	}
 	sendAndApply(&ma);

+ 1 - 1
server/CGameHandler.h

@@ -131,7 +131,7 @@ public:
 	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 bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap);
+	bool bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack);
 	bool eraseArtifactByClient(const ArtifactLocation & al);
 	void synchronizeArtifactHandlerLists();
 

+ 4 - 2
server/NetPacksServer.cpp

@@ -68,7 +68,7 @@ void ApplyGhNetPackVisitor::visitArrangeStacks(ArrangeStacks & pack)
 
 void ApplyGhNetPackVisitor::visitBulkMoveArmy(BulkMoveArmy & pack)
 {
-	gh.throwIfWrongPlayer(&pack);
+	gh.throwIfWrongOwner(&pack, pack.srcArmy);
 	result = gh.bulkMoveArmy(pack.srcArmy, pack.destArmy, pack.srcSlot);
 }
 
@@ -132,7 +132,9 @@ void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
 void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack)
 {
 	gh.throwIfWrongOwner(&pack, pack.srcHero);
-	result = gh.bulkMoveArtifacts(pack.srcHero, pack.dstHero, pack.swap);
+	if(pack.swap)
+		gh.throwIfWrongOwner(&pack, pack.dstHero);
+	result = gh.bulkMoveArtifacts(pack.srcHero, pack.dstHero, pack.swap, pack.equipped, pack.backpack);
 }
 
 void ApplyGhNetPackVisitor::visitAssembleArtifacts(AssembleArtifacts & pack)