2
0
Эх сурвалжийг харах

Merge pull request #3895 from SoundSSGood/CWindowWithArtifacts-refactoring2

CWindowWithArtifacts refactoring part2
Ivan Savenko 1 жил өмнө
parent
commit
9e0bae96cc

+ 8 - 7
client/CPlayerInterface.cpp

@@ -1772,18 +1772,19 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	adventureInt->onHeroChanged(cb->getHero(dst.artHolder));
 
-	bool redraw = true;
 	// If a bulk transfer has arrived, then redrawing only the last art movement.
 	if(numOfMovedArts != 0)
-	{
 		numOfMovedArts--;
-		if(numOfMovedArts != 0)
-			redraw = false;
-	}
 
 	for(auto artWin : GH.windows().findWindows<CWindowWithArtifacts>())
-		artWin->artifactMoved(src, dst, redraw);
-
+	{
+		artWin->artifactMoved(src, dst);
+		if(numOfMovedArts == 0)
+		{
+			artWin->update();
+			artWin->redraw();
+		}
+	}
 	waitWhileDialog();
 }
 

+ 2 - 6
client/widgets/CArtifactsOfHeroAltar.cpp

@@ -21,12 +21,8 @@
 
 CArtifactsOfHeroAltar::CArtifactsOfHeroAltar(const Point & position)
 {
-	init(
-		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
-		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
-		position,
-		std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
-
+	init(position, std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
+	enableGesture();
 	// The backpack is in the altar window above and to the right
 	for(auto & slot : backpack)
 		slot->moveBy(Point(2, -1));

+ 2 - 0
client/widgets/CArtifactsOfHeroAltar.h

@@ -16,6 +16,8 @@
 class CArtifactsOfHeroAltar : public CArtifactsOfHeroBase
 {
 public:
+	ObjectInstanceID altarId;
+
 	CArtifactsOfHeroAltar(const Point & position);
 	void deactivate() override;
 };

+ 1 - 1
client/widgets/CArtifactsOfHeroBackpack.cpp

@@ -126,7 +126,7 @@ size_t CArtifactsOfHeroBackpack::calcRows(size_t slots)
 CArtifactsOfHeroQuickBackpack::CArtifactsOfHeroQuickBackpack(const ArtifactPosition filterBySlot)
 	: CArtifactsOfHeroBackpack(0, 0)
 {
-	assert(filterBySlot != ArtifactPosition::FIRST_AVAILABLE);
+	assert(ArtifactUtils::checkIfSlotValid(*getHero(), filterBySlot));
 
 	if(!ArtifactUtils::isSlotEquipment(filterBySlot))
 		return;

+ 48 - 16
client/widgets/CArtifactsOfHeroBase.cpp

@@ -32,9 +32,9 @@ CArtifactsOfHeroBase::CArtifactsOfHeroBase()
 void CArtifactsOfHeroBase::putBackPickedArtifact()
 {
 	// Artifact located in artifactsTransitionPos should be returned
-	if(getPickedArtifact())
+	if(const auto art = getPickedArtifact())
 	{
-		auto slot = ArtifactUtils::getArtAnyPosition(curHero, curHero->artifactsTransitionPos.begin()->artifact->getTypeId());
+		auto slot = ArtifactUtils::getArtAnyPosition(curHero, art->getTypeId());
 		if(slot == ArtifactPosition::PRE_FIRST)
 		{
 			LOCPLINT->cb->eraseArtifactByClient(ArtifactLocation(curHero->id, ArtifactPosition::TRANSITION_POS));
@@ -47,8 +47,6 @@ void CArtifactsOfHeroBase::putBackPickedArtifact()
 }
 
 void CArtifactsOfHeroBase::init(
-	const CArtPlace::ClickFunctor & onClickPressedCallback,
-	const CArtPlace::ClickFunctor & onShowPopupCallback,
 	const Point & position,
 	const BpackScrollFunctor & scrollCallback)
 {
@@ -69,14 +67,14 @@ void CArtifactsOfHeroBase::init(
 	{
 		artPlace.second->slot = artPlace.first;
 		artPlace.second->setArtifact(nullptr);
-		artPlace.second->setClickPressedCallback(onClickPressedCallback);
-		artPlace.second->setShowPopupCallback(onShowPopupCallback);
+		artPlace.second->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
+		artPlace.second->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 	}
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);
-		artPlace->setClickPressedCallback(onClickPressedCallback);
-		artPlace->setShowPopupCallback(onShowPopupCallback);
+		artPlace->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
+		artPlace->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 	}
 	leftBackpackRoll = std::make_shared<CButton>(Point(379, 364), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(),
 		[scrollCallback](){scrollCallback(true);}, EShortcut::MOVE_LEFT);
@@ -90,20 +88,29 @@ void CArtifactsOfHeroBase::init(
 
 void CArtifactsOfHeroBase::clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
 {
+	if(artPlace.isLocked())
+		return;
+
 	if(clickPressedCallback)
-		clickPressedCallback(*this, artPlace, cursorPosition);
+		clickPressedCallback(artPlace, cursorPosition);
 }
 
 void CArtifactsOfHeroBase::showPopupArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
 {
+	if(artPlace.isLocked())
+		return;
+
 	if(showPopupCallback)
-		showPopupCallback(*this, artPlace, cursorPosition);
+		showPopupCallback(artPlace, cursorPosition);
 }
 
 void CArtifactsOfHeroBase::gestureArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
 {
+	if(artPlace.isLocked())
+		return;
+
 	if(gestureCallback)
-		gestureCallback(*this, artPlace, cursorPosition);
+		gestureCallback(artPlace, cursorPosition);
 }
 
 void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero)
@@ -166,6 +173,21 @@ CArtifactsOfHeroBase::ArtPlacePtr CArtifactsOfHeroBase::getArtPlace(const Artifa
 	}
 }
 
+CArtifactsOfHeroBase::ArtPlacePtr CArtifactsOfHeroBase::getArtPlace(const Point & cursorPosition)
+{
+	for(const auto & [slot, artPlace] : artWorn)
+	{
+		if(artPlace->pos.isInside(cursorPosition))
+			return artPlace;
+	}
+	for(const auto & artPlace : backpack)
+	{
+		if(artPlace->pos.isInside(cursorPosition))
+			return artPlace;
+	}
+	return nullptr;
+}
+
 void CArtifactsOfHeroBase::updateWornSlots()
 {
 	for(auto place : artWorn)
@@ -196,21 +218,31 @@ void CArtifactsOfHeroBase::updateSlot(const ArtifactPosition & slot)
 const CArtifactInstance * CArtifactsOfHeroBase::getPickedArtifact()
 {
 	// Returns only the picked up artifact. Not just highlighted like in the trading window.
-	if(!curHero || curHero->artifactsTransitionPos.empty())
-		return nullptr;
-	else
+	if(curHero)
 		return curHero->getArt(ArtifactPosition::TRANSITION_POS);
+	else
+		return nullptr;
 }
 
-void CArtifactsOfHeroBase::addGestureCallback(CArtPlace::ClickFunctor callback)
+void CArtifactsOfHeroBase::enableGesture()
 {
 	for(auto & artPlace : artWorn)
 	{
-		artPlace.second->setGestureCallback(callback);
+		artPlace.second->setGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
 		artPlace.second->addUsedEvents(GESTURE);
 	}
 }
 
+const CArtifactInstance * CArtifactsOfHeroBase::getArt(const ArtifactPosition & slot) const
+{
+	return curHero ? curHero->getArt(slot) : nullptr;
+}
+
+void CArtifactsOfHeroBase::enableKeyboardShortcuts()
+{
+	addUsedEvents(AEventsReceiver::KEYBOARD);
+}
+
 void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot)
 {
 	// Spurious call from artifactMoved in attempt to update hidden backpack slot

+ 10 - 6
client/widgets/CArtifactsOfHeroBase.h

@@ -11,9 +11,11 @@
 
 #include "CArtPlace.h"
 
+#include "../gui/Shortcut.h"
+
 class CButton;
 
-class CArtifactsOfHeroBase : virtual public CIntObject
+class CArtifactsOfHeroBase : virtual public CIntObject, public CKeyShortcut
 {
 protected:
 	using ArtPlacePtr = std::shared_ptr<CHeroArtPlace>;
@@ -21,7 +23,7 @@ protected:
 
 public:
 	using ArtPlaceMap = std::map<ArtifactPosition, ArtPlacePtr>;
-	using ClickFunctor = std::function<void(CArtifactsOfHeroBase&, CArtPlace&, const Point&)>;
+	using ClickFunctor = std::function<void(CArtPlace&, const Point&)>;
 
 	ClickFunctor clickPressedCallback;
 	ClickFunctor showPopupCallback;
@@ -38,13 +40,15 @@ public:
 	virtual void markPossibleSlots(const CArtifactInstance * art, bool assumeDestRemoved = true);
 	virtual void unmarkSlots();
 	virtual ArtPlacePtr getArtPlace(const ArtifactPosition & slot);
+	virtual ArtPlacePtr getArtPlace(const Point & cursorPosition);
 	virtual void updateWornSlots();
 	virtual void updateBackpackSlots();
 	virtual void updateSlot(const ArtifactPosition & slot);
 	virtual const CArtifactInstance * getPickedArtifact();
-	void addGestureCallback(CArtPlace::ClickFunctor callback);
+	void enableGesture();
+	const CArtifactInstance * getArt(const ArtifactPosition & slot) const;
+	void enableKeyboardShortcuts();
 
-protected:
 	const CGHeroInstance * curHero;
 	ArtPlaceMap artWorn;
 	std::vector<ArtPlacePtr> backpack;
@@ -62,8 +66,8 @@ protected:
 		Point(381,295) //18
 	};
 
-	virtual void init(const CHeroArtPlace::ClickFunctor & lClickCallback, const CHeroArtPlace::ClickFunctor & showPopupCallback,
-		const Point & position, const BpackScrollFunctor & scrollCallback);
+protected:
+	virtual void init(const Point & position, const BpackScrollFunctor & scrollCallback);
 	// Assigns an artifacts to an artifact place depending on it's new slot ID
 	virtual void setSlotData(ArtPlacePtr artPlace, const ArtifactPosition & slot);
 };

+ 1 - 1
client/widgets/CArtifactsOfHeroKingdom.cpp

@@ -33,7 +33,7 @@ CArtifactsOfHeroKingdom::CArtifactsOfHeroKingdom(ArtPlaceMap ArtWorn, std::vecto
 		artPlace.second->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
 		artPlace.second->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
 	}
-	addGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
+	enableGesture();
 	for(auto artPlace : backpack)
 	{
 		artPlace->setArtifact(nullptr);

+ 2 - 11
client/widgets/CArtifactsOfHeroMain.cpp

@@ -20,12 +20,8 @@
 
 CArtifactsOfHeroMain::CArtifactsOfHeroMain(const Point & position)
 {
-	init(
-		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
-		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
-		position,
-		std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
-	addGestureCallback(std::bind(&CArtifactsOfHeroBase::gestureArtPlace, this, _1, _2));
+	init(position, std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
+	enableGesture();
 }
 
 CArtifactsOfHeroMain::~CArtifactsOfHeroMain()
@@ -33,11 +29,6 @@ CArtifactsOfHeroMain::~CArtifactsOfHeroMain()
 	CArtifactsOfHeroBase::putBackPickedArtifact();
 }
 
-void CArtifactsOfHeroMain::enableArtifactsCostumeSwitcher()
-{
-	addUsedEvents(AEventsReceiver::KEYBOARD);
-}
-
 void CArtifactsOfHeroMain::keyPressed(EShortcut key)
 {
 	if(!shortcutPressed)

+ 1 - 4
client/widgets/CArtifactsOfHeroMain.h

@@ -11,14 +11,11 @@
 
 #include "CArtifactsOfHeroBase.h"
 
-#include "../gui/Shortcut.h"
-
-class CArtifactsOfHeroMain : public CArtifactsOfHeroBase, public CKeyShortcut
+class CArtifactsOfHeroMain : public CArtifactsOfHeroBase
 {
 public:
 	CArtifactsOfHeroMain(const Point & position);
 	~CArtifactsOfHeroMain() override;
-	void enableArtifactsCostumeSwitcher();
 	void keyPressed(EShortcut key) override;
 	void keyReleased(EShortcut key) override;
 

+ 22 - 5
client/widgets/CArtifactsOfHeroMarket.cpp

@@ -14,14 +14,31 @@
 
 CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position, const int selectionWidth)
 {
-	init(
-		std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2),
-		std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2),
-		position,
-		std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
+	init(position, std::bind(&CArtifactsOfHeroBase::scrollBackpack, this, _1));
 
 	for(const auto & [slot, artPlace] : artWorn)
 		artPlace->setSelectionWidth(selectionWidth);
 	for(auto artPlace : backpack)
 		artPlace->setSelectionWidth(selectionWidth);
 };
+
+void CArtifactsOfHeroMarket::clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition)
+{
+	if(artPlace.isLocked())
+		return;
+
+	if(const auto art = getArt(artPlace.slot))
+	{
+		if(onSelectArtCallback && art->artType->isTradable())
+		{
+			unmarkSlots();
+			artPlace.selectSlot(true);
+			onSelectArtCallback(&artPlace);
+		}
+		else
+		{
+			if(onClickNotTradableCallback)
+				onClickNotTradableCallback();
+		}
+	}
+}

+ 3 - 1
client/widgets/CArtifactsOfHeroMarket.h

@@ -14,7 +14,9 @@
 class CArtifactsOfHeroMarket : public CArtifactsOfHeroBase
 {
 public:
-	std::function<void(const CArtPlace*)> selectArtCallback;
+	std::function<void(const CArtPlace*)> onSelectArtCallback;
+	std::function<void()> onClickNotTradableCallback;
 
 	CArtifactsOfHeroMarket(const Point & position, const int selectionWidth);
+	void clickPrassedArtPlace(CArtPlace & artPlace, const Point & cursorPosition) override;
 };

+ 7 - 6
client/widgets/markets/CAltarArtifacts.cpp

@@ -32,7 +32,6 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
 
 	assert(dynamic_cast<const CGArtifactsAltar*>(market));
 	auto altarObj = dynamic_cast<const CGArtifactsAltar*>(market);
-	altarId = altarObj->id;
 	altarArtifacts = altarObj;
 
 	deal = std::make_shared<CButton>(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"),
@@ -51,6 +50,7 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
 	// Hero's artifacts
 	heroArts = std::make_shared<CArtifactsOfHeroAltar>(Point(-365, -11));
 	heroArts->setHero(hero);
+	heroArts->altarId = altarObj->id;
 
 	// Altar
 	offerTradePanel = std::make_shared<ArtifactsAltarPanel>([this](const std::shared_ptr<CTradeableItem> & altarSlot)
@@ -109,12 +109,12 @@ void CAltarArtifacts::makeDeal()
 
 void CAltarArtifacts::sacrificeAll()
 {
-	LOCPLINT->cb->bulkMoveArtifacts(heroArts->getHero()->id, altarId, false, true, true);
+	LOCPLINT->cb->bulkMoveArtifacts(heroArts->getHero()->id, heroArts->altarId, false, true, true);
 }
 
 void CAltarArtifacts::sacrificeBackpack()
 {
-	LOCPLINT->cb->bulkMoveArtifacts(heroArts->getHero()->id, altarId, false, false, true);
+	LOCPLINT->cb->bulkMoveArtifacts(heroArts->getHero()->id, heroArts->altarId, false, false, true);
 }
 
 std::shared_ptr<CArtifactsOfHeroAltar> CAltarArtifacts::getAOHset() const
@@ -179,7 +179,7 @@ void CAltarArtifacts::putBackArtifacts()
 	// TODO: If the backpack capacity limit is enabled, artifacts may remain on the altar.
 	// Perhaps should be erased in CGameHandler::objectVisitEnded if id of visited object will be available
 	if(!altarArtifacts->artifactsInBackpack.empty())
-		LOCPLINT->cb->bulkMoveArtifacts(altarId, heroArts->getHero()->id, false, true, true);
+		LOCPLINT->cb->bulkMoveArtifacts(heroArts->altarId, heroArts->getHero()->id, false, true, true);
 }
 
 CMarketBase::MarketShowcasesParams CAltarArtifacts::getShowcasesParams() const
@@ -208,7 +208,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
 				deal->block(!LOCPLINT->makingTurn);
 
 				LOCPLINT->cb->swapArtifacts(ArtifactLocation(heroArts->getHero()->id, ArtifactPosition::TRANSITION_POS),
-					ArtifactLocation(altarId, ArtifactPosition::ALTAR));
+					ArtifactLocation(heroArts->altarId, ArtifactPosition::ALTAR));
 			}
 			else
 			{
@@ -222,7 +222,8 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
 		assert(tradeSlotsMap.at(altarSlot));
 		const auto slot = altarArtifacts->getSlotByInstance(tradeSlotsMap.at(altarSlot));
 		assert(slot != ArtifactPosition::PRE_FIRST);
-		LOCPLINT->cb->swapArtifacts(ArtifactLocation(altarId, slot), ArtifactLocation(hero->id, ArtifactPosition::TRANSITION_POS));
+		LOCPLINT->cb->swapArtifacts(ArtifactLocation(heroArts->altarId, slot),
+			ArtifactLocation(hero->id, GH.isKeyboardCtrlDown() ? ArtifactPosition::FIRST_AVAILABLE : ArtifactPosition::TRANSITION_POS));
 		tradeSlotsMap.erase(altarSlot);
 	}
 }

+ 0 - 1
client/widgets/markets/CAltarArtifacts.h

@@ -26,7 +26,6 @@ public:
 	void putBackArtifacts();
 
 private:
-	ObjectInstanceID altarId;
 	const CArtifactSet * altarArtifacts;
 	std::shared_ptr<CButton> sacrificeBackpackButton;
 	std::shared_ptr<CArtifactsOfHeroAltar> heroArts;

+ 6 - 2
client/widgets/markets/CArtifactsSelling.cpp

@@ -55,14 +55,18 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
 	// Hero's artifacts
 	heroArts = std::make_shared<CArtifactsOfHeroMarket>(Point(-361, 46), offerTradePanel->selectionWidth);
 	heroArts->setHero(hero);
-	heroArts->selectArtCallback = [this](const CArtPlace * artPlace)
+	heroArts->onSelectArtCallback = [this](const CArtPlace * artPlace)
 	{
 		assert(artPlace);
 		selectedHeroSlot = artPlace->slot;
 		CArtifactsSelling::highlightingChanged();
 		CIntObject::redraw();
 	};
-
+	heroArts->onClickNotTradableCallback = []()
+	{
+		// This item can't be traded
+		LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]);
+	};
 	CArtifactsSelling::updateShowcases();
 	CArtifactsSelling::deselect();
 }

+ 22 - 4
client/windows/CHeroBackpackWindow.cpp

@@ -20,6 +20,9 @@
 #include "render/Canvas.h"
 #include "CPlayerInterface.h"
 
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/networkPacks/ArtifactLocation.h"
+
 CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero, const std::vector<CArtifactsOfHeroPtr> & artsSets)
 	: CWindowWithArtifacts(&artsSets)
 {
@@ -28,9 +31,16 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero, const std:
 	stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 0, 0));
 	arts = std::make_shared<CArtifactsOfHeroBackpack>();
 	arts->moveBy(Point(windowMargin, windowMargin));
-	addSetAndCallbacks(arts);
+	arts->clickPressedCallback = [this](const CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		clickPressedOnArtPlace(arts->getHero(), artPlace.slot, true, false, true);
+	};
+	arts->showPopupCallback = [this](CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		showArtifactAssembling(*arts, artPlace, cursorPosition);
+	};
+	addSet(arts);
 	arts->setHero(hero);
-	addCloseCallback(std::bind(&CHeroBackpackWindow::close, this));
 	quitButton = std::make_shared<CButton>(Point(), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""),
 		[this]() { WindowBase::close(); }, EShortcut::GLOBAL_RETURN);
 	pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
@@ -55,9 +65,17 @@ CHeroQuickBackpackWindow::CHeroQuickBackpackWindow(const CGHeroInstance * hero,
 	stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 0, 0));
 	arts = std::make_shared<CArtifactsOfHeroQuickBackpack>(targetSlot);
 	arts->moveBy(Point(windowMargin, windowMargin));
-	addSetAndCallbacks(static_cast<std::weak_ptr<CArtifactsOfHeroQuickBackpack>>(arts));
+	arts->clickPressedCallback = [this](const CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		if(const auto curHero = arts->getHero())
+			swapArtifactAndClose(*arts, artPlace.slot, ArtifactLocation(curHero->id, arts->getFilterSlot()));
+	};
+	arts->showPopupCallback = [this](CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		showArifactInfo(*arts, artPlace, cursorPosition);
+	};
+	addSet(arts);
 	arts->setHero(hero);
-	addCloseCallback(std::bind(&CHeroQuickBackpackWindow::close, this));
 	addUsedEvents(GESTURE);
 	pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
 	pos.h = stretchedBackground->pos.h = arts->pos.h + windowMargin;

+ 11 - 15
client/windows/CHeroWindow.cpp

@@ -46,7 +46,7 @@ void CHeroSwitcher::clickPressed(const Point & cursorPosition)
 	//TODO: do not recreate window
 	if (false)
 	{
-		owner->update(hero, true);
+		owner->update();
 	}
 	else
 	{
@@ -175,20 +175,14 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
 	labels.push_back(std::make_shared<CLabel>(69, 232, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->jktexts[6]));
 	labels.push_back(std::make_shared<CLabel>(213, 232, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::YELLOW, CGI->generaltexth->jktexts[7]));
 
-	update(hero);
+	CHeroWindow::update();
 }
 
-void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
+void CHeroWindow::update()
 {
+	CWindowWithArtifacts::update();
 	auto & heroscrn = CGI->generaltexth->heroscrn;
-
-	if(!hero) //something strange... no hero? it shouldn't happen
-	{
-		logGlobal->error("Set nullptr hero? no way...");
-		return;
-	}
-
-	assert(hero == curHero);
+	assert(curHero);
 
 	name->setText(curHero->getNameTranslated());
 	title->setText((boost::format(CGI->generaltexth->allTexts[342]) % curHero->level % curHero->getClassNameTranslated()).str());
@@ -219,9 +213,12 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 		if(!arts)
 		{
 			arts = std::make_shared<CArtifactsOfHeroMain>(Point(-65, -8));
+			arts->clickPressedCallback = [this](const CArtPlace & artPlace, const Point & cursorPosition){clickPressedOnArtPlace(curHero, artPlace.slot, true, false, false);};
+			arts->showPopupCallback = [this](CArtPlace & artPlace, const Point & cursorPosition){showArtifactAssembling(*arts, artPlace, cursorPosition);};
+			arts->gestureCallback = [this](const CArtPlace & artPlace, const Point & cursorPosition){showQuickBackpackWindow(curHero, artPlace.slot, cursorPosition);};
 			arts->setHero(curHero);
-			addSetAndCallbacks(arts);
-			enableArtifactsCostumeSwitcher();
+			addSet(arts);
+			enableKeyboardShortcuts();
 		}
 
 		int serial = LOCPLINT->cb->getHeroSerial(curHero, false);
@@ -313,8 +310,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
 	morale->set(curHero);
 	luck->set(curHero);
 
-	if(redrawNeeded)
-		redraw();
+	redraw();
 }
 
 void CHeroWindow::dismissCurrent()

+ 1 - 1
client/windows/CHeroWindow.h

@@ -100,7 +100,7 @@ public:
 
 	CHeroWindow(const CGHeroInstance * hero);
 
-	void update(const CGHeroInstance * hero, bool redrawNeeded = false); //sets main displayed hero
+	void update() override;
 
 	void dismissCurrent(); //dissmissed currently displayed hero (curHero)
 	void commanderWindow();

+ 13 - 1
client/windows/CKingdomInterface.cpp

@@ -550,7 +550,19 @@ std::shared_ptr<CIntObject> CKingdomInterface::createMainTab(size_t index)
 	case 0:
 		return std::make_shared<CKingdHeroList>(size, [this](const CWindowWithArtifacts::CArtifactsOfHeroPtr & newHeroSet)
 			{
-				addSetAndCallbacks(newHeroSet);
+				newHeroSet->clickPressedCallback = [this, newHeroSet](const CArtPlace & artPlace, const Point & cursorPosition)
+				{
+					clickPressedOnArtPlace(newHeroSet->getHero(), artPlace.slot, false, false, false);
+				};
+				newHeroSet->showPopupCallback = [this, newHeroSet](CArtPlace & artPlace, const Point & cursorPosition)
+				{
+					showArtifactAssembling(*newHeroSet, artPlace, cursorPosition);
+				};
+				newHeroSet->gestureCallback = [this, newHeroSet](const CArtPlace & artPlace, const Point & cursorPosition)
+				{
+					showQuickBackpackWindow(newHeroSet->getHero(), artPlace.slot, cursorPosition);
+				};
+				addSet(newHeroSet);
 			});
 	case 1:
 		return std::make_shared<CKingdTownList>(size);

+ 26 - 23
client/windows/CMarketWindow.cpp

@@ -62,24 +62,27 @@ CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero
 
 void CMarketWindow::updateArtifacts()
 {
-	assert(marketWidget);
-	marketWidget->update();
+	update();
 }
 
 void CMarketWindow::updateGarrisons()
 {
-	assert(marketWidget);
-	marketWidget->update();
+	update();
 }
 
 void CMarketWindow::updateResource()
 {
-	assert(marketWidget);
-	marketWidget->update();
+	update();
 }
 
 void CMarketWindow::updateHero()
 {
+	update();
+}
+
+void CMarketWindow::update()
+{
+	CWindowWithArtifacts::update();
 	assert(marketWidget);
 	marketWidget->update();
 }
@@ -98,21 +101,6 @@ bool CMarketWindow::holdsGarrison(const CArmedInstance * army)
 	return marketWidget->hero == army;
 }
 
-void CMarketWindow::artifactRemoved(const ArtifactLocation & artLoc)
-{
-	marketWidget->update();
-	CWindowWithArtifacts::artifactRemoved(artLoc);
-}
-
-void CMarketWindow::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
-{
-	if(!getState().has_value())
-		return;
-	CWindowWithArtifacts::artifactMoved(srcLoc, destLoc, withRedraw);
-	assert(marketWidget);
-	marketWidget->update();
-}
-
 void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero)
 {
 	auto isButtonVisible = [currentMode, market, hero](EMarketMode modeButton) -> bool
@@ -206,7 +194,9 @@ void CMarketWindow::createArtifactsSelling(const IMarket * market, const CGHeroI
 	artSlotBack->moveTo(pos.topLeft() + Point(18, 339));
 	auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero);
 	artSets.clear();
-	addSetAndCallbacks(artsSellingMarket->getAOHset());
+	const auto heroArts = artsSellingMarket->getAOHset();
+	heroArts->showPopupCallback = [this, heroArts](CArtPlace & artPlace, const Point & cursorPosition){showArifactInfo(*heroArts, artPlace, cursorPosition);};
+	addSet(heroArts);
 	marketWidget = artsSellingMarket;
 	initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);	
 }
@@ -246,7 +236,20 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
 	auto altarArtifacts = std::make_shared<CAltarArtifacts>(market, hero);
 	marketWidget = altarArtifacts;
 	artSets.clear();
-	addSetAndCallbacks(altarArtifacts->getAOHset());
+	const auto heroArts = altarArtifacts->getAOHset();
+	heroArts->clickPressedCallback = [this, heroArts](const CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		clickPressedOnArtPlace(heroArts->getHero(), artPlace.slot, true, true, false);
+	};
+	heroArts->showPopupCallback = [this, heroArts](CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		showArtifactAssembling(*heroArts, artPlace, cursorPosition);
+	};
+	heroArts->gestureCallback = [this, heroArts](const CArtPlace & artPlace, const Point & cursorPosition)
+	{
+		showQuickBackpackWindow(heroArts->getHero(), artPlace.slot, cursorPosition);
+	};
+	addSet(heroArts);
 	initWidgetInternals(EMarketMode::ARTIFACT_EXP, CGI->generaltexth->zelp[568]);
 	updateHero();
 	quitButton->addCallback([altarArtifacts](){altarArtifacts->putBackArtifacts();});

+ 1 - 2
client/windows/CMarketWindow.h

@@ -20,10 +20,9 @@ public:
 	void updateArtifacts();
 	void updateGarrisons() override;
 	void updateHero();
+	void update() override;
 	void close() override;
 	bool holdsGarrison(const CArmedInstance * army) override;
-	void artifactRemoved(const ArtifactLocation & artLoc) override;
-	void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override;
 
 private:
 	void createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero);

+ 174 - 407
client/windows/CWindowWithArtifacts.cpp

@@ -20,9 +20,7 @@
 
 #include "../widgets/CComponent.h"
 
-#include "../windows/CHeroWindow.h"
 #include "../windows/CSpellWindow.h"
-#include "../windows/GUIClasses.h"
 #include "../windows/CHeroBackpackWindow.h"
 #include "../CPlayerInterface.h"
 #include "../CGameInfo.h"
@@ -41,302 +39,115 @@ CWindowWithArtifacts::CWindowWithArtifacts(const std::vector<CArtifactsOfHeroPtr
 		this->artSets.insert(this->artSets.end(), artSets->begin(), artSets->end());
 }
 
-void CWindowWithArtifacts::addSet(CArtifactsOfHeroPtr newArtSet)
+void CWindowWithArtifacts::addSet(const std::shared_ptr<CArtifactsOfHeroBase> & newArtSet)
 {
 	artSets.emplace_back(newArtSet);
 }
 
-void CWindowWithArtifacts::addSetAndCallbacks(CArtifactsOfHeroPtr newArtSet)
+const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact() const
 {
-	addSet(newArtSet);
-	std::visit([this](auto artSetWeak)
+	const CGHeroInstance * hero = nullptr;
+
+	for(const auto & artSet : artSets)
+		if(const auto pickedArt = artSet->getHero()->getArt(ArtifactPosition::TRANSITION_POS))
 		{
-			auto artSet = artSetWeak.lock();
-			artSet->clickPressedCallback = std::bind(&CWindowWithArtifacts::clickPressedArtPlaceHero, this, _1, _2, _3);
-			artSet->showPopupCallback = std::bind(&CWindowWithArtifacts::showPopupArtPlaceHero, this, _1, _2, _3);
-			artSet->gestureCallback = std::bind(&CWindowWithArtifacts::gestureArtPlaceHero, this, _1, _2, _3);
-		}, newArtSet);
+			hero = artSet->getHero();
+			break;
+		}
+	return hero;
 }
 
-void CWindowWithArtifacts::addCloseCallback(const CloseCallback & callback)
+const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact() const
 {
-	closeCallback = callback;
-}
+	const CArtifactInstance * art = nullptr;
 
-const CGHeroInstance * CWindowWithArtifacts::getHeroPickedArtifact()
-{
-	auto res = getState();
-	if(res.has_value())
-		return std::get<const CGHeroInstance*>(res.value());
-	else
-		return nullptr;
-}
-
-const CArtifactInstance * CWindowWithArtifacts::getPickedArtifact()
-{
-	auto res = getState();
-	if(res.has_value())
-		return std::get<const CArtifactInstance*>(res.value());
-	else
-		return nullptr;
+	for(const auto & artSet : artSets)
+		if(const auto pickedArt = artSet->getHero()->getArt(ArtifactPosition::TRANSITION_POS))
+		{
+			art = pickedArt;
+			break;
+		}
+	return art;
 }
 
-void CWindowWithArtifacts::clickPressedArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
+void CWindowWithArtifacts::clickPressedOnArtPlace(const CGHeroInstance * hero, const ArtifactPosition & slot,
+	bool allowExchange, bool altarTrading, bool closeWindow)
 {
-	const auto currentArtSet = findAOHbyRef(artsInst);
-	assert(currentArtSet.has_value());
-
-	if(artPlace.isLocked())
+	if(!LOCPLINT->makingTurn)
 		return;
-
-	if (!LOCPLINT->makingTurn)
+	if(hero == nullptr)
 		return;
 
-	std::visit(
-		[this, &artPlace](auto artSetWeak) -> void
+	if(const auto heroArtOwner = getHeroPickedArtifact())
+	{
+		if(allowExchange || hero->id == heroArtOwner->id)
+			putPickedArtifact(*hero, slot);
+	}
+	else if(auto art = hero->getArt(slot))
+	{
+		if(hero->getOwner() == LOCPLINT->playerID)
 		{
-			const auto artSetPtr = artSetWeak.lock();
-
-			// Hero(Main, Exchange) window, Kingdom window, Altar window, Backpack window left click handler
-			if constexpr(
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> || 
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
-			{
-				const auto pickedArtInst = getPickedArtifact();
-				const auto heroPickedArt = getHeroPickedArtifact();
-				const auto hero = artSetPtr->getHero();
-				auto isTransferAllowed = false;
-				std::string msg;
-
-				if(pickedArtInst)
-				{
-					auto srcLoc = ArtifactLocation(heroPickedArt->id, ArtifactPosition::TRANSITION_POS);
-					auto dstLoc = ArtifactLocation(hero->id, artPlace.slot);
-
-					if(ArtifactUtils::isSlotBackpack(artPlace.slot))
-					{
-						if(pickedArtInst->artType->isBig())
-						{
-							// War machines cannot go to backpack
-							msg = boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArtInst->artType->getNameTranslated());
-						}
-						else
-						{
-							if(ArtifactUtils::isBackpackFreeSlots(heroPickedArt))
-								isTransferAllowed = true;
-							else
-								msg = CGI->generaltexth->translate("core.genrltxt.152");
-						}
-					}
-					// Check if artifact transfer is possible
-					else if(pickedArtInst->canBePutAt(hero, artPlace.slot, true) && (!artPlace.getArt() || hero->tempOwner == LOCPLINT->playerID))
-					{
-						isTransferAllowed = true;
-					}
-					if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
-					{
-						if(hero != heroPickedArt)
-							isTransferAllowed = false;
-					}
-					if(isTransferAllowed)
-						LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
-				}
-				else if(auto art = artPlace.getArt())
-				{
-					if(artSetPtr->getHero()->getOwner() == LOCPLINT->playerID)
-					{
-						if(checkSpecialArts(*art, hero, std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ? true : false))
-						{
-							assert(artSetPtr->getHero()->getSlotByInstance(art) != ArtifactPosition::PRE_FIRST);
-
-							if(GH.isKeyboardCmdDown())
-							{
-								std::shared_ptr<CArtifactsOfHeroMain> anotherHeroEquipmentPointer = nullptr;
-
-								for(auto set : artSets)
-								{
-									if(std::holds_alternative<std::weak_ptr<CArtifactsOfHeroMain>>(set))
-									{
-										std::shared_ptr<CArtifactsOfHeroMain> heroEquipmentPointer = std::get<std::weak_ptr<CArtifactsOfHeroMain>>(set).lock();
-										if(heroEquipmentPointer->getHero()->id != artSetPtr->getHero()->id)
-										{
-											anotherHeroEquipmentPointer = heroEquipmentPointer;
-											break;
-										}
-									}
-								}
-
-								if(anotherHeroEquipmentPointer != nullptr)
-								{
-									ArtifactPosition availablePosition = ArtifactUtils::getArtAnyPosition(anotherHeroEquipmentPointer->getHero(), art->getTypeId());
-									if(availablePosition != ArtifactPosition::PRE_FIRST)
-									{
-										LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artSetPtr->getHero()->getSlotByInstance(art)),
-										ArtifactLocation(anotherHeroEquipmentPointer->getHero()->id, availablePosition));
-									}
-								}
-							}
-							else if(GH.isKeyboardAltDown())
-							{
-								ArtifactPosition destinationPosition = ArtifactPosition::PRE_FIRST;
-
-								if(ArtifactUtils::isSlotEquipment(artPlace.slot))
-								{
-									ArtifactPosition availablePosition = ArtifactUtils::getArtBackpackPosition(artSetPtr->getHero(), art->getTypeId());
-									if(availablePosition != ArtifactPosition::PRE_FIRST)
-									{
-										destinationPosition = availablePosition;
-									}
-								}
-								else if(ArtifactUtils::isSlotBackpack(artPlace.slot))
-								{
-									ArtifactPosition availablePosition = ArtifactUtils::getArtAnyPosition(artSetPtr->getHero(), art->getTypeId());
-									if(availablePosition != ArtifactPosition::PRE_FIRST && availablePosition != ArtifactPosition::BACKPACK_START)
-									{
-										destinationPosition = availablePosition;
-									}
-								}
-
-								if(destinationPosition != ArtifactPosition::PRE_FIRST)
-								{
-									LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artPlace.slot),
-										ArtifactLocation(artSetPtr->getHero()->id, destinationPosition));
-								}
-							}
-							else
-							{
-								LOCPLINT->cb->swapArtifacts(ArtifactLocation(artSetPtr->getHero()->id, artPlace.slot),
-									ArtifactLocation(artSetPtr->getHero()->id, ArtifactPosition::TRANSITION_POS));
-							}
-						}
-					}
-					else
-					{
-						for(const auto artSlot : ArtifactUtils::unmovableSlots())
-							if(artPlace.slot == artSlot)
-							{
-								msg = CGI->generaltexth->allTexts[21];
-								break;
-							}
-					}
-				}
-
-				if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
-				{
-					if(!isTransferAllowed && artPlace.getArt() && closeCallback)
-						closeCallback();
-				}
-				else
-				{
-					if(!msg.empty())
-						LOCPLINT->showInfoDialog(msg);
-				}
-			}
-			// Market window left click handler
-			else if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>>)
-			{
-				if(artSetPtr->selectArtCallback && artPlace.getArt())
+			if(checkSpecialArts(*art, *hero, altarTrading))
+				onClickPressedCommonArtifact(*hero, slot, closeWindow);
+		}
+		else
+		{
+			for(const auto & artSlot : ArtifactUtils::unmovableSlots())
+				if(slot == artSlot)
 				{
-					if(artPlace.getArt()->artType->isTradable())
-					{
-						artSetPtr->unmarkSlots();
-						artPlace.selectSlot(true);
-						artSetPtr->selectArtCallback(&artPlace);
-					}
-					else
-					{
-						// This item can't be traded
-						LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]);
-					}
+					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]);
+					break;
 				}
-			}
-			else if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
-			{
-				const auto hero = artSetPtr->getHero();
-				LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero->id, artPlace.slot), ArtifactLocation(hero->id, artSetPtr->getFilterSlot()));
-				if(closeCallback)
-					closeCallback();
-			}
-		}, currentArtSet.value());
+		}
+	}
 }
 
-void CWindowWithArtifacts::showPopupArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
+void CWindowWithArtifacts::swapArtifactAndClose(const CArtifactsOfHeroBase & artsInst, const ArtifactPosition & slot,
+	const ArtifactLocation & dstLoc)
 {
-	const auto currentArtSet = findAOHbyRef(artsInst);
-	assert(currentArtSet.has_value());
+	LOCPLINT->cb->swapArtifacts(ArtifactLocation(artsInst.getHero()->id, slot), dstLoc);
+	close();
+}
 
-	if(artPlace.isLocked())
-		return;
+void CWindowWithArtifacts::showArtifactAssembling(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace,
+	const Point & cursorPosition) const
+{
+	if(artsInst.getArt(artPlace.slot))
+	{
+		if(ArtifactUtilsClient::askToDisassemble(artsInst.getHero(), artPlace.slot))
+			return;
+		if(ArtifactUtilsClient::askToAssemble(artsInst.getHero(), artPlace.slot))
+			return;
+		if(artPlace.text.size())
+			artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
+	}
+}
 
-	std::visit(
-		[&artPlace, &cursorPosition](auto artSetWeak) -> void
-		{
-			const auto artSetPtr = artSetWeak.lock();
-
-			// Hero (Main, Exchange) window, Kingdom window, Backpack window right click handler
-			if constexpr(
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroAltar>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
-			{
-				if(artPlace.getArt())
-				{
-					if(ArtifactUtilsClient::askToDisassemble(artSetPtr->getHero(), artPlace.slot))
-					{
-						return;
-					}
-					if(ArtifactUtilsClient::askToAssemble(artSetPtr->getHero(), artPlace.slot))
-					{
-						return;
-					}
-					if(artPlace.text.size())
-						artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
-				}
-			}
-			// Altar window, Market window right click handler
-			else if constexpr(
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroQuickBackpack>>)
-			{
-				if(artPlace.getArt() && artPlace.text.size())
-					artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
-			}
-		}, currentArtSet.value());
+void CWindowWithArtifacts::showArifactInfo(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition) const
+{
+	if(artsInst.getArt(artPlace.slot) && artPlace.text.size())
+		artPlace.LRClickableAreaWTextComp::showPopupWindow(cursorPosition);
 }
 
-void CWindowWithArtifacts::gestureArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition)
+void CWindowWithArtifacts::showQuickBackpackWindow(const CGHeroInstance * hero, const ArtifactPosition & slot,
+	const Point & cursorPosition) const
 {
-	const auto currentArtSet = findAOHbyRef(artsInst);
-	assert(currentArtSet.has_value());
-	if(artPlace.isLocked())
+	if(!settings["general"]["enableUiEnhancements"].Bool())
 		return;
 
-	std::visit(
-		[&artPlace, cursorPosition](auto artSetWeak) -> void
-		{
-			const auto artSetPtr = artSetWeak.lock();
-			if constexpr(
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>> ||
-				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
-			{
-				if(!settings["general"]["enableUiEnhancements"].Bool())
-					return;
-
-				GH.windows().createAndPushWindow<CHeroQuickBackpackWindow>(artSetPtr->getHero(), artPlace.slot);
-				auto backpackWindow = GH.windows().topWindow<CHeroQuickBackpackWindow>();
-				backpackWindow->moveTo(cursorPosition - Point(1, 1));
-				backpackWindow->fitToScreen(15);
-			}
-		}, currentArtSet.value());
+	GH.windows().createAndPushWindow<CHeroQuickBackpackWindow>(hero, slot);
+	auto backpackWindow = GH.windows().topWindow<CHeroQuickBackpackWindow>();
+	backpackWindow->moveTo(cursorPosition - Point(1, 1));
+	backpackWindow->fitToScreen(15);
 }
 
 void CWindowWithArtifacts::activate()
 {
 	if(const auto art = getPickedArtifact())
+	{
+		markPossibleSlots();
 		setCursorAnimation(*art);
+	}
 	CWindowObject::activate();
 }
 
@@ -346,18 +157,10 @@ void CWindowWithArtifacts::deactivate()
 	CWindowObject::deactivate();
 }
 
-void CWindowWithArtifacts::enableArtifactsCostumeSwitcher() const
+void CWindowWithArtifacts::enableKeyboardShortcuts() const
 {
-	for(auto artSet : artSets)
-		std::visit(
-			[](auto artSetWeak)
-			{
-				if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMain>>)
-				{
-					const auto artSetPtr = artSetWeak.lock();
-					artSetPtr->enableArtifactsCostumeSwitcher();
-				}
-			}, artSet);
+	for(auto & artSet : artSets)
+		artSet->enableKeyboardShortcuts();
 }
 
 void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
@@ -365,58 +168,19 @@ void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation & artLoc)
 	update();
 }
 
-void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
+void CWindowWithArtifacts::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc)
 {
-	auto curState = getState();
-	if(!curState.has_value())
-		// Transition state. Nothing to do here. Just skip. Need to wait for final state.
-		return;
-
-	auto pickedArtInst = std::get<const CArtifactInstance*>(curState.value());
-	auto artifactMovedBody = [this, withRedraw, &destLoc, &pickedArtInst](auto artSetWeak) -> void
-	{
-		auto artSetPtr = artSetWeak.lock();
-		if(artSetPtr)
+	for(const auto & artSet : artSets)
+		if(const auto pickedArtInst = getPickedArtifact())
 		{
-			const auto hero = artSetPtr->getHero();
-			if(pickedArtInst)
-			{
-				setCursorAnimation(*pickedArtInst);
-			}
-			else
-			{
-				artSetPtr->unmarkSlots();
-				CCS->curh->dragAndDropCursor(nullptr);
-			}
-			if(withRedraw)
-			{
-				artSetPtr->updateWornSlots();
-				artSetPtr->updateBackpackSlots();
-
-				// Update arts bonuses on window.
-				// TODO rework this part when CHeroWindow and CExchangeWindow are reworked
-				if(auto * chw = dynamic_cast<CHeroWindow*>(this))
-				{
-					chw->update(hero, true);
-				}
-				else if(auto * cew = dynamic_cast<CExchangeWindow*>(this))
-				{
-					cew->updateWidgets();
-				}
-				artSetPtr->redraw();
-			}
-
-			// Make sure the status bar is updated so it does not display old text
-			if(destLoc.artHolder == hero->id)
-			{
-				if(auto artPlace = artSetPtr->getArtPlace(destLoc.slot))
-					artPlace->hover(true);
-			}
+			markPossibleSlots();
+			setCursorAnimation(*pickedArtInst);
+		}
+		else
+		{
+			artSet->unmarkSlots();
+			CCS->curh->dragAndDropCursor(nullptr);
 		}
-	};
-
-	for(auto artSetWeak : artSets)
-		std::visit(artifactMovedBody, artSetWeak);
 }
 
 void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation & artLoc)
@@ -430,107 +194,42 @@ void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation & artLoc)
 	update();
 }
 
-void CWindowWithArtifacts::update() const
-{
-	auto updateSlotBody = [](auto artSetWeak) -> void
-	{
-		if(const auto artSetPtr = artSetWeak.lock())
-		{
-			artSetPtr->updateWornSlots();
-			artSetPtr->updateBackpackSlots();
-			artSetPtr->redraw();
-		}
-	};
-
-	for(auto artSetWeak : artSets)
-		std::visit(updateSlotBody, artSetWeak);
-}
-
-std::optional<std::tuple<const CGHeroInstance*, const CArtifactInstance*>> CWindowWithArtifacts::getState()
+void CWindowWithArtifacts::update()
 {
-	const CArtifactInstance * artInst = nullptr;
-	std::map<const CGHeroInstance*, size_t> pickedCnt;
-
-	auto getHeroArtBody = [&artInst, &pickedCnt](auto artSetWeak) -> void
+	for(const auto & artSet : artSets)
 	{
-		auto artSetPtr = artSetWeak.lock();
-		if(artSetPtr)
-		{
-			if(const auto art = artSetPtr->getPickedArtifact())
-			{
-				const auto hero = artSetPtr->getHero();
-				if(pickedCnt.count(hero) == 0)
-				{
-					pickedCnt.insert({ hero, hero->artifactsTransitionPos.size() });
-					artInst = art;
-				}
-			}
-		}
-	};
-	for(auto artSetWeak : artSets)
-		std::visit(getHeroArtBody, artSetWeak);
-
-	// The state is possible when the hero has placed an artifact in the ArtifactPosition::TRANSITION_POS,
-	// and the previous artifact has not yet removed from the ArtifactPosition::TRANSITION_POS.
-	// This is a transitional state. Then return nullopt.
-	if(std::accumulate(std::begin(pickedCnt), std::end(pickedCnt), 0, [](size_t accum, const auto & value)
-		{
-			return accum + value.second;
-		}) > 1)
-		return std::nullopt;
-	else
-		return std::make_tuple(pickedCnt.begin()->first, artInst);
-}
-
-std::optional<CWindowWithArtifacts::CArtifactsOfHeroPtr> CWindowWithArtifacts::findAOHbyRef(const CArtifactsOfHeroBase & artsInst)
-{
-	std::optional<CArtifactsOfHeroPtr> res;
+		artSet->updateWornSlots();
+		artSet->updateBackpackSlots();
 
-	auto findAOHBody = [&res, &artsInst](auto & artSetWeak) -> void
-	{
-		if(&artsInst == artSetWeak.lock().get())
-			res = artSetWeak;
-	};
-
-	for(auto artSetWeak : artSets)
-	{
-		std::visit(findAOHBody, artSetWeak);
-		if(res.has_value())
-			return res;
+		// Make sure the status bar is updated so it does not display old text
+		if(auto artPlace = artSet->getArtPlace(GH.getCursorPosition()))
+			artPlace->hover(true);
 	}
-	return res;
 }
 
-void CWindowWithArtifacts::markPossibleSlots()
+void CWindowWithArtifacts::markPossibleSlots() const
 {
 	if(const auto pickedArtInst = getPickedArtifact())
 	{
-		const auto heroArtOwner = getHeroPickedArtifact();
-		auto artifactAssembledBody = [&pickedArtInst, &heroArtOwner](auto artSetWeak) -> void
+		for(const auto & artSet : artSets)
 		{
-			if(auto artSetPtr = artSetWeak.lock())
-			{
-				if(artSetPtr->isActive())
-				{
-					const auto hero = artSetPtr->getHero();
-					if(heroArtOwner == hero || !std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroKingdom>>)
-						artSetPtr->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID);
-				}
-			}
-		};
+			const auto hero = artSet->getHero();
+			if(hero == nullptr || !artSet->isActive())
+				continue;
 
-		for(auto artSetWeak : artSets)
-			std::visit(artifactAssembledBody, artSetWeak);
+			if(getHeroPickedArtifact() == hero || !std::dynamic_pointer_cast<CArtifactsOfHeroKingdom>(artSet))
+				artSet->markPossibleSlots(pickedArtInst, hero->tempOwner == LOCPLINT->playerID);
+		}
 	}
 }
 
-bool CWindowWithArtifacts::checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance * hero, bool isTrade) const
+bool CWindowWithArtifacts::checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance & hero, bool isTrade) const
 {
 	const auto artId = artInst.getTypeId();
 	
 	if(artId == ArtifactID::SPELLBOOK)
 	{
-		GH.windows().createAndPushWindow<CSpellWindow>(hero, LOCPLINT, LOCPLINT->battleInt.get());
+		GH.windows().createAndPushWindow<CSpellWindow>(&hero, LOCPLINT, LOCPLINT->battleInt.get());
 		return false;
 	}
 	if(artId == ArtifactID::CATAPULT)
@@ -549,9 +248,8 @@ bool CWindowWithArtifacts::checkSpecialArts(const CArtifactInstance & artInst, c
 	return true;
 }
 
-void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
+void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst) const
 {
-	markPossibleSlots();
 	if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool())
 	{
 		assert(artInst.getScrollSpellID().num >= 0);
@@ -564,3 +262,72 @@ void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
 		CCS->curh->dragAndDropCursor(AnimationPath::builtin("artifact"), artInst.artType->getIconIndex());
 	}
 }
+
+void CWindowWithArtifacts::putPickedArtifact(const CGHeroInstance & curHero, const ArtifactPosition & targetSlot) const
+{
+	const auto heroArtOwner = getHeroPickedArtifact();
+	const auto pickedArt = getPickedArtifact();
+	auto srcLoc = ArtifactLocation(heroArtOwner->id, ArtifactPosition::TRANSITION_POS);
+	auto dstLoc = ArtifactLocation(curHero.id, targetSlot);
+
+	if(ArtifactUtils::isSlotBackpack(dstLoc.slot))
+	{
+		if(pickedArt->artType->isBig())
+		{
+			// War machines cannot go to backpack
+			LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % pickedArt->artType->getNameTranslated()));
+		}
+		else
+		{
+			if(ArtifactUtils::isBackpackFreeSlots(heroArtOwner))
+				LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
+			else
+				LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.152"));
+		}
+	}
+	// Check if artifact transfer is possible
+	else if(pickedArt->canBePutAt(&curHero, dstLoc.slot, true) && (!curHero.getArt(targetSlot) || curHero.tempOwner == LOCPLINT->playerID))
+	{
+		LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
+	}
+}
+
+void CWindowWithArtifacts::onClickPressedCommonArtifact(const CGHeroInstance & curHero, const ArtifactPosition & slot, bool closeWindow)
+{
+	assert(curHero.getArt(slot));
+	auto srcLoc = ArtifactLocation(curHero.id, slot);
+	auto dstLoc = ArtifactLocation(curHero.id, ArtifactPosition::TRANSITION_POS);
+
+	if(GH.isKeyboardCmdDown())
+	{
+		for(const auto & anotherSet : artSets)
+		{
+			if(std::dynamic_pointer_cast<CArtifactsOfHeroMain>(anotherSet) && curHero.id != anotherSet->getHero()->id)
+			{
+				dstLoc.slot = ArtifactPosition::FIRST_AVAILABLE;
+				dstLoc.artHolder = anotherSet->getHero()->id;
+				break;
+			}
+			if(const auto heroSetAltar = std::dynamic_pointer_cast<CArtifactsOfHeroAltar>(anotherSet))
+			{
+				dstLoc.slot = ArtifactPosition::FIRST_AVAILABLE;
+				dstLoc.artHolder = heroSetAltar->altarId;
+				break;
+			}
+		}
+	}
+	else if(GH.isKeyboardAltDown())
+	{
+		const auto artId = curHero.getArt(slot)->getTypeId();
+		if(ArtifactUtils::isSlotEquipment(slot))
+			dstLoc.slot = ArtifactUtils::getArtBackpackPosition(&curHero, artId);
+		else if(ArtifactUtils::isSlotBackpack(slot))
+			dstLoc.slot = ArtifactUtils::getArtEquippedPosition(&curHero, artId);
+	}
+	else if(closeWindow)
+	{
+		close();
+	}
+	if(dstLoc.slot != ArtifactPosition::PRE_FIRST)
+		LOCPLINT->cb->swapArtifacts(srcLoc, dstLoc);
+}

+ 18 - 25
client/windows/CWindowWithArtifacts.h

@@ -19,41 +19,34 @@
 class CWindowWithArtifacts : virtual public CWindowObject
 {
 public:
-	using CArtifactsOfHeroPtr = std::variant<
-		std::weak_ptr<CArtifactsOfHeroMarket>,
-		std::weak_ptr<CArtifactsOfHeroAltar>,
-		std::weak_ptr<CArtifactsOfHeroKingdom>,
-		std::weak_ptr<CArtifactsOfHeroMain>,
-		std::weak_ptr<CArtifactsOfHeroBackpack>,
-		std::weak_ptr<CArtifactsOfHeroQuickBackpack>>;
-	using CloseCallback = std::function<void()>;
+	using CArtifactsOfHeroPtr = std::shared_ptr<CArtifactsOfHeroBase>;
 
 	std::vector<CArtifactsOfHeroPtr> artSets;
-	CloseCallback closeCallback;
 
 	explicit CWindowWithArtifacts(const std::vector<CArtifactsOfHeroPtr> * artSets = nullptr);
-	void addSet(CArtifactsOfHeroPtr newArtSet);
-	void addSetAndCallbacks(CArtifactsOfHeroPtr newArtSet);
-	void addCloseCallback(const CloseCallback & callback);
-	const CGHeroInstance * getHeroPickedArtifact();
-	const CArtifactInstance * getPickedArtifact();
-	void clickPressedArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
-	void showPopupArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
-	void gestureArtPlaceHero(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition);
+	void addSet(const std::shared_ptr<CArtifactsOfHeroBase> & newArtSet);
+	const CGHeroInstance * getHeroPickedArtifact() const;
+	const CArtifactInstance * getPickedArtifact() const;
+	void clickPressedOnArtPlace(const CGHeroInstance * hero, const ArtifactPosition & slot,
+		bool allowExchange, bool altarTrading, bool closeWindow);
+	void swapArtifactAndClose(const CArtifactsOfHeroBase & artsInst, const ArtifactPosition & slot, const ArtifactLocation & dstLoc);
+	void showArtifactAssembling(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition) const;
+	void showArifactInfo(const CArtifactsOfHeroBase & artsInst, CArtPlace & artPlace, const Point & cursorPosition) const;
+	void showQuickBackpackWindow(const CGHeroInstance * hero, const ArtifactPosition & slot, const Point & cursorPosition) const;
 	void activate() override;
 	void deactivate() override;
-	void enableArtifactsCostumeSwitcher() const;
+	void enableKeyboardShortcuts() const;
 
 	virtual void artifactRemoved(const ArtifactLocation & artLoc);
-	virtual void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw);
+	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:
-	void update() const;
-	std::optional<std::tuple<const CGHeroInstance*, const CArtifactInstance*>> getState();
-	std::optional<CArtifactsOfHeroPtr> findAOHbyRef(const CArtifactsOfHeroBase & artsInst);
-	void markPossibleSlots();
-	bool checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance * hero, bool isTrade) const;
-	void setCursorAnimation(const CArtifactInstance & artInst);
+	void markPossibleSlots() const;
+	bool checkSpecialArts(const CArtifactInstance & artInst, const CGHeroInstance & hero, bool isTrade) const;
+	void setCursorAnimation(const CArtifactInstance & artInst) const;
+	void putPickedArtifact(const CGHeroInstance & curHero, const ArtifactPosition & targetSlot) const;
+	void onClickPressedCommonArtifact(const CGHeroInstance & curHero, const ArtifactPosition & slot, bool closeWindow);
 };

+ 12 - 5
client/windows/GUIClasses.cpp

@@ -759,12 +759,18 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 	}
 
 	artifs[0] = std::make_shared<CArtifactsOfHeroMain>(Point(-334, 151));
+	artifs[0]->clickPressedCallback = [this, hero = heroInst[0]](const CArtPlace & artPlace, const Point & cursorPosition){clickPressedOnArtPlace(hero, artPlace.slot, true, false, false);};
+	artifs[0]->showPopupCallback = [this, heroArts = artifs[0]](CArtPlace & artPlace, const Point & cursorPosition){showArtifactAssembling(*heroArts, artPlace, cursorPosition);};
+	artifs[0]->gestureCallback = [this, hero = heroInst[0]](const CArtPlace & artPlace, const Point & cursorPosition){showQuickBackpackWindow(hero, artPlace.slot, cursorPosition);};
 	artifs[0]->setHero(heroInst[0]);
 	artifs[1] = std::make_shared<CArtifactsOfHeroMain>(Point(98, 151));
+	artifs[1]->clickPressedCallback = [this, hero = heroInst[1]](const CArtPlace & artPlace, const Point & cursorPosition){clickPressedOnArtPlace(hero, artPlace.slot, true, false, false);};
+	artifs[1]->showPopupCallback = [this, heroArts = artifs[1]](CArtPlace & artPlace, const Point & cursorPosition){showArtifactAssembling(*heroArts, artPlace, cursorPosition);};
+	artifs[1]->gestureCallback = [this, hero = heroInst[1]](const CArtPlace & artPlace, const Point & cursorPosition){showQuickBackpackWindow(hero, artPlace.slot, cursorPosition);};
 	artifs[1]->setHero(heroInst[1]);
 
-	addSetAndCallbacks(artifs[0]);
-	addSetAndCallbacks(artifs[1]);
+	addSet(artifs[0]);
+	addSet(artifs[1]);
 
 	for(int g=0; g<4; ++g)
 	{
@@ -925,7 +931,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 		}
 	}
 
-	updateWidgets();
+	CWindowWithArtifacts::update();
 }
 
 const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const
@@ -937,7 +943,7 @@ void CExchangeWindow::updateGarrisons()
 {
 	garr->recreateSlots();
 
-	updateWidgets();
+	update();
 }
 
 bool CExchangeWindow::holdsGarrison(const CArmedInstance * army)
@@ -951,8 +957,9 @@ void CExchangeWindow::questlog(int whichHero)
 	LOCPLINT->showQuestLog();
 }
 
-void CExchangeWindow::updateWidgets()
+void CExchangeWindow::update()
 {
+	CWindowWithArtifacts::update();
 	for(size_t leftRight : {0, 1})
 	{
 		const CGHeroInstance * hero = heroInst.at(leftRight);

+ 1 - 1
client/windows/GUIClasses.h

@@ -328,7 +328,7 @@ public:
 
 	void questlog(int whichHero); //questlog button callback; whichHero: 0 - left, 1 - right
 
-	void updateWidgets();
+	void update() override;
 
 	const CGarrisonSlot * getSelectedSlotID() const;
 

+ 33 - 1
lib/ArtifactUtils.cpp

@@ -19,7 +19,39 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+DLL_LINKAGE bool ArtifactUtils::checkIfSlotValid(const CArtifactSet & artSet, const ArtifactPosition & slot)
+{
+	if(artSet.bearerType() == ArtBearer::HERO)
+	{
+		if(isSlotEquipment(slot) || isSlotBackpack(slot) || slot == ArtifactPosition::TRANSITION_POS)
+			return true;
+	}
+	else if(artSet.bearerType() == ArtBearer::ALTAR)
+	{
+		if(isSlotBackpack(slot))
+			return true;
+	}
+	else if(artSet.bearerType() == ArtBearer::COMMANDER)
+	{
+		if(vstd::contains(commanderSlots(), slot))
+			return true;
+	}
+	else if(artSet.bearerType() == ArtBearer::CREATURE)
+	{
+		if(slot == ArtifactPosition::CREATURE_SLOT)
+			return true;
+	}
+	return false;
+}
+
 DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid)
+{
+	if(auto targetSlot = getArtEquippedPosition(target, aid); targetSlot != ArtifactPosition::PRE_FIRST)
+		return targetSlot;
+	return getArtBackpackPosition(target, aid);
+}
+
+DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtEquippedPosition(const CArtifactSet * target, const ArtifactID & aid)
 {
 	const auto * art = aid.toArtifact();
 	for(const auto & slot : art->getPossibleSlots().at(target->bearerType()))
@@ -27,7 +59,7 @@ DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtAnyPosition(const CArtifactSet
 		if(art->canBePutAt(target, slot))
 			return slot;
 	}
-	return getArtBackpackPosition(target, aid);
+	return ArtifactPosition::PRE_FIRST;
 }
 
 DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtBackpackPosition(const CArtifactSet * target, const ArtifactID & aid)

+ 2 - 1
lib/ArtifactUtils.h

@@ -25,8 +25,9 @@ class CMap;
 
 namespace ArtifactUtils
 {
-	// Calculates where an artifact gets placed when it gets transferred from one hero to another.
+	DLL_LINKAGE bool checkIfSlotValid(const CArtifactSet & artSet, const ArtifactPosition & slot);
 	DLL_LINKAGE ArtifactPosition getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid);
+	DLL_LINKAGE ArtifactPosition getArtEquippedPosition(const CArtifactSet * target, const ArtifactID & aid);
 	DLL_LINKAGE ArtifactPosition getArtBackpackPosition(const CArtifactSet * target, const ArtifactID & aid);
 	// TODO: Make this constexpr when the toolset is upgraded
 	DLL_LINKAGE const std::vector<ArtifactPosition> & unmovableSlots();

+ 3 - 12
lib/CArtHandler.cpp

@@ -897,13 +897,7 @@ const CArtifactInstance * CArtifactSet::getAssemblyByConstituent(const ArtifactI
 const ArtSlotInfo * CArtifactSet::getSlot(const ArtifactPosition & pos) const
 {
 	if(pos == ArtifactPosition::TRANSITION_POS)
-	{
-		// Always add to the end. Always take from the beginning.
-		if(artifactsTransitionPos.empty())
-			return nullptr;
-		else
-			return &(*artifactsTransitionPos.begin());
-	}
+		return &artifactsTransitionPos;
 	if(vstd::contains(artifactsWorn, pos))
 		return &artifactsWorn.at(pos);
 	if(ArtifactUtils::isSlotBackpack(pos))
@@ -936,9 +930,7 @@ void CArtifactSet::setNewArtSlot(const ArtifactPosition & slot, ConstTransitiveP
 	ArtSlotInfo * slotInfo;
 	if(slot == ArtifactPosition::TRANSITION_POS)
 	{
-		// Always add to the end. Always take from the beginning.
-		artifactsTransitionPos.emplace_back();
-		slotInfo = &artifactsTransitionPos.back();
+		slotInfo = &artifactsTransitionPos;
 	}
 	else if(ArtifactUtils::isSlotEquipment(slot))
 	{
@@ -957,8 +949,7 @@ void CArtifactSet::eraseArtSlot(const ArtifactPosition & slot)
 {
 	if(slot == ArtifactPosition::TRANSITION_POS)
 	{
-		assert(!artifactsTransitionPos.empty());
-		artifactsTransitionPos.erase(artifactsTransitionPos.begin());
+		artifactsTransitionPos.artifact = nullptr;
 	}
 	else if(ArtifactUtils::isSlotBackpack(slot))
 	{

+ 1 - 1
lib/CArtHandler.h

@@ -194,7 +194,7 @@ public:
 
 	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
+	ArtSlotInfo artifactsTransitionPos; // Used as transition position for dragAndDrop artifact exchange
 
 	void setNewArtSlot(const ArtifactPosition & slot, ConstTransitivePtr<CArtifactInstance> art, bool locked);
 	void eraseArtSlot(const ArtifactPosition & slot);

+ 12 - 6
server/CGameHandler.cpp

@@ -2721,10 +2721,16 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
 	if(!isAllowedExchange(src.artHolder, dst.artHolder))
 		COMPLAIN_RET("That heroes cannot make any exchange!");
 
+	COMPLAIN_RET_FALSE_IF(!ArtifactUtils::checkIfSlotValid(*srcArtSet, src.slot), "moveArtifact: wrong artifact source slot");
 	const auto srcArtifact = srcArtSet->getArt(src.slot);
-	const auto dstArtifact = dstArtSet->getArt(dst.slot);
+	auto dstSlot = dst.slot;
+	if(dstSlot == ArtifactPosition::FIRST_AVAILABLE)
+		dstSlot = ArtifactUtils::getArtAnyPosition(dstArtSet, srcArtifact->getTypeId());
+	if(!ArtifactUtils::checkIfSlotValid(*dstArtSet, dstSlot))
+		return true;
+	const auto dstArtifact = dstArtSet->getArt(dstSlot);
 	const bool isDstSlotOccupied = dstArtSet->bearerType() == ArtBearer::ALTAR ? false : dstArtifact != nullptr;
-	const bool isDstSlotBackpack = dstArtSet->bearerType() == ArtBearer::HERO ? ArtifactUtils::isSlotBackpack(dst.slot) : false;
+	const bool isDstSlotBackpack = dstArtSet->bearerType() == ArtBearer::HERO ? ArtifactUtils::isSlotBackpack(dstSlot) : false;
 
 	if(srcArtifact == nullptr)
 		COMPLAIN_RET("No artifact to move!");
@@ -2733,23 +2739,23 @@ bool CGameHandler::moveArtifact(const PlayerColor & player, const ArtifactLocati
 
 	// Check if src/dest slots are appropriate for the artifacts exchanged.
 	// Moving to the backpack is always allowed.
-	if((!srcArtifact || !isDstSlotBackpack) && !srcArtifact->canBePutAt(dstArtSet, dst.slot, true))
+	if((!srcArtifact || !isDstSlotBackpack) && !srcArtifact->canBePutAt(dstArtSet, dstSlot, true))
 		COMPLAIN_RET("Cannot move artifact!");
 
 	auto srcSlotInfo = srcArtSet->getSlot(src.slot);
-	auto dstSlotInfo = dstArtSet->getSlot(dst.slot);
+	auto dstSlotInfo = dstArtSet->getSlot(dstSlot);
 
 	if((srcSlotInfo && srcSlotInfo->locked) || (dstSlotInfo && dstSlotInfo->locked))
 		COMPLAIN_RET("Cannot move artifact locks.");
 
 	if(isDstSlotBackpack && srcArtifact->artType->isBig())
 		COMPLAIN_RET("Cannot put big artifacts in backpack!");
-	if(src.slot == ArtifactPosition::MACH4 || dst.slot == ArtifactPosition::MACH4)
+	if(src.slot == ArtifactPosition::MACH4 || dstSlot == ArtifactPosition::MACH4)
 		COMPLAIN_RET("Cannot move catapult!");
 	if(isDstSlotBackpack && !ArtifactUtils::isBackpackFreeSlots(dstArtSet))
 		COMPLAIN_RET("Backpack is full!");
 
-	auto dstSlot = std::min(dst.slot, ArtifactPosition(ArtifactPosition::BACKPACK_START + dstArtSet->artifactsInBackpack.size()));
+	dstSlot = std::min(dstSlot, ArtifactPosition(ArtifactPosition::BACKPACK_START + dstArtSet->artifactsInBackpack.size()));
 
 	if(src.slot == dstSlot && src.artHolder == dst.artHolder)
 		COMPLAIN_RET("Won't move artifact: Dest same as source!");