Browse Source

Market window unified

SoundSSGood 1 year ago
parent
commit
433d55cac3

+ 0 - 2
client/CMakeLists.txt

@@ -133,7 +133,6 @@ set(client_SRCS
 	widgets/markets/CTradeBase.cpp
 	widgets/markets/TradePanels.cpp
 
-	windows/CAltarWindow.cpp
 	windows/CCastleInterface.cpp
 	windows/CCreatureWindow.cpp
 	windows/CHeroOverview.cpp
@@ -322,7 +321,6 @@ set(client_HEADERS
 	widgets/markets/CTradeBase.h
 	widgets/markets/TradePanels.h
 
-	windows/CAltarWindow.h
 	windows/CCastleInterface.h
 	windows/CCreatureWindow.h
 	windows/CHeroOverview.h

+ 4 - 4
client/CPlayerInterface.cpp

@@ -419,8 +419,8 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, Prim
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	if (which == PrimarySkill::EXPERIENCE)
 	{
-		for (auto ctw : GH.windows().findWindows<CAltarWindow>())
-			ctw->updateExpToLevel();
+		for(auto ctw : GH.windows().findWindows<CMarketWindow>())
+			ctw->updateHero();
 	}
 	else
 		adventureInt->onHeroChanged(hero);
@@ -450,7 +450,7 @@ void CPlayerInterface::receivedResource()
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	for (auto mw : GH.windows().findWindows<CMarketWindow>())
-		mw->resourceChanged();
+		mw->updateResource();
 
 	GH.windows().totalRedraw();
 }
@@ -1696,7 +1696,7 @@ void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket * bm)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	for (auto cmw : GH.windows().findWindows<CMarketWindow>())
-		cmw->artifactsChanged();
+		cmw->updateArtifacts();
 }
 
 void CPlayerInterface::showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID)

+ 20 - 24
client/widgets/markets/CAltarArtifacts.cpp

@@ -86,6 +86,23 @@ void CAltarArtifacts::deselect()
 	}
 }
 
+void CAltarArtifacts::updateSlots()
+{
+	CExperienceAltar::updateSlots();
+	if(const auto art = hero->getArt(ArtifactPosition::TRANSITION_POS))
+	{
+		hRight = offerTradePanel->selectedSlot;
+		hRight->setID(art->getTypeId().num);
+		offerQty = calcExpCost(art->getTypeId());
+	}
+	else
+	{
+		hRight.reset();
+		offerQty = 0;
+	}
+	updateSelected();
+}
+
 void CAltarArtifacts::makeDeal()
 {
 	std::vector<TradeItemSell> positions;
@@ -107,32 +124,11 @@ void CAltarArtifacts::sacrificeBackpack()
 	LOCPLINT->cb->bulkMoveArtifacts(heroArts->getHero()->id, altarId, false, false, true);
 }
 
-void CAltarArtifacts::setSelectedArtifact(std::optional<ArtifactID> id)
-{
-	if(id.has_value())
-	{
-		hRight = offerTradePanel->selectedSlot;
-		hRight->setID(id.value().num);
-		offerQty = calcExpCost(id.value());
-	}
-	else
-	{
-		hRight.reset();
-		offerQty = 0;
-	}
-	updateSelected();
-}
-
 std::shared_ptr<CArtifactsOfHeroAltar> CAltarArtifacts::getAOHset() const
 {
 	return heroArts;
 }
 
-ObjectInstanceID CAltarArtifacts::getObjId() const
-{
-	return altarId;
-}
-
 void CAltarArtifacts::updateAltarSlots()
 {
 	assert(altarArtifacts->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
@@ -157,7 +153,7 @@ void CAltarArtifacts::updateAltarSlots()
 	{
 		assert(tradeSlot.first->id == -1);
 		assert(altarArtifacts->getSlotByInstance(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
-		tradeSlot.first->setID(tradeSlot.second->getTypeId());
+		tradeSlot.first->setID(tradeSlot.second->getTypeId().num);
 		tradeSlot.first->subtitle->setText(std::to_string(calcExpCost(tradeSlot.second->getTypeId())));
 	}
 
@@ -174,7 +170,7 @@ void CAltarArtifacts::updateAltarSlots()
 		for(auto & altarSlot : offerTradePanel->slots)
 			if(altarSlot->id == -1)
 			{
-				altarSlot->setID(slotInfo.artifact->getTypeId());
+				altarSlot->setID(slotInfo.artifact->getTypeId().num);
 				altarSlot->subtitle->setText(std::to_string(calcExpCost(slotInfo.artifact->getTypeId())));
 				tradeSlotsMap.try_emplace(altarSlot, slotInfo.artifact);
 				break;
@@ -198,7 +194,7 @@ CTradeBase::SelectionParams CAltarArtifacts::getSelectionParams() const
 	if(hRight)
 		return std::make_tuple(
 			std::nullopt,
-			SelectionParamOneSide {std::to_string(offerQty), GameResID(hRight->id)}
+			SelectionParamOneSide {std::to_string(offerQty), CGI->artifacts()->getByIndex(hRight->id)->getIconIndex()}
 	);
 	return std::make_tuple(std::nullopt, std::nullopt);
 }

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

@@ -19,11 +19,10 @@ public:
 	TExpType calcExpAltarForHero() override;
 	void deselect() override;
 	void makeDeal() override;
+	void updateSlots() override;
 	void sacrificeAll() override;
 	void sacrificeBackpack();
-	void setSelectedArtifact(std::optional<ArtifactID> id);
 	std::shared_ptr<CArtifactsOfHeroAltar> getAOHset() const;
-	ObjectInstanceID getObjId() const;
 	void putBackArtifacts();
 
 private:

+ 1 - 1
client/widgets/markets/CAltarCreatures.cpp

@@ -115,7 +115,7 @@ void CAltarCreatures::updateControls()
 
 void CAltarCreatures::updateSlots()
 {
-	CTradeBase::updateSlots();
+	CExperienceAltar::updateSlots();
 	assert(bidTradePanel->slots.size() == offerTradePanel->slots.size());
 }
 

+ 1 - 1
client/widgets/markets/CArtifactsBuying.cpp

@@ -55,7 +55,7 @@ CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance
 	{
 		return vstd::contains(this->market->availableItemsIds(EMarketMode::RESOURCE_ARTIFACT), ArtifactID(slot->id)) ? false : true;
 	};
-	offerTradePanel->moveTo(pos.topLeft() + Point(328, 182));
+	offerTradePanel->moveTo(pos.topLeft() + Point(328, 181));
 
 	CTradeBase::updateSlots();
 	CTradeBase::deselect();

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

@@ -17,9 +17,13 @@
 #include "../../widgets/TextControls.h"
 
 #include "../../CGameInfo.h"
+#include "../../CPlayerInterface.h"
+
+#include "../../../CCallback.h"
 
 #include "../../../lib/CArtifactInstance.h"
 #include "../../../lib/CGeneralTextHandler.h"
+#include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../../../lib/mapObjects/CGMarket.h"
 
 CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero)
@@ -44,6 +48,7 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
 		assert(artForTrade);
 		bidSelectedSlot->setID(artForTrade->getTypeId().num);
 		hLeft = bidSelectedSlot;
+		selectedHeroSlot = artPlace->slot;
 		if(hRight)
 		{	// dublicate
 			this->market->getOffer(hLeft->id, hRight->id, bidQty, offerQty, EMarketMode::ARTIFACT_RESOURCE);
@@ -58,14 +63,24 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
 	assert(offerTradePanel);
 	offerTradePanel->moveTo(pos.topLeft() + Point(326, 184));
 	offerTradePanel->selectedSlot->moveTo(pos.topLeft() + Point(409, 473));
+	offerTradePanel->selectedSlot->subtitle->moveBy(Point(0, 1));
 	
 	CArtifactsSelling::updateSelected();
+	CArtifactsSelling::deselect();
+}
+
+void CArtifactsSelling::deselect()
+{
 	CTradeBase::deselect();
+	selectedHeroSlot = ArtifactPosition::PRE_FIRST;
+	heroArts->unmarkSlots();
 }
 
 void CArtifactsSelling::makeDeal()
 {
-
+	const auto art = hero->getArt(selectedHeroSlot);
+	assert(art);
+	LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_RESOURCE, art->getId(), GameResID(hRight->id), offerQty, hero);
 }
 
 void CArtifactsSelling::updateSelected()
@@ -86,6 +101,16 @@ void CArtifactsSelling::updateSelected()
 	}
 }
 
+void CArtifactsSelling::updateSlots()
+{
+	CTradeBase::updateSlots();
+	if(selectedHeroSlot != ArtifactPosition::PRE_FIRST)
+	{
+		if(hero->getArt(selectedHeroSlot) == nullptr)
+			deselect();
+	}
+}
+
 std::shared_ptr<CArtifactsOfHeroMarket> CArtifactsSelling::getAOHset() const
 {
 	return heroArts;
@@ -96,7 +121,7 @@ CTradeBase::SelectionParams CArtifactsSelling::getSelectionParams() const
 	if(hLeft && hRight)
 		return std::make_tuple(
 			std::nullopt,
-			SelectionParamOneSide {std::to_string(offerQty), GameResID(hRight->id)}
+			SelectionParamOneSide {std::to_string(offerQty), hRight->id}
 		);
 	else
 		return std::make_tuple(std::nullopt, std::nullopt);

+ 3 - 0
client/widgets/markets/CArtifactsSelling.h

@@ -16,14 +16,17 @@ class CArtifactsSelling : public CResourcesBuying
 {
 public:
 	CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);
+	void deselect() override;
 	void makeDeal() override;
 	void updateSelected() override;
+	void updateSlots() override;
 	std::shared_ptr<CArtifactsOfHeroMarket> getAOHset() const;
 
 private:
 	std::shared_ptr<CArtifactsOfHeroMarket> heroArts;
 	std::shared_ptr<CLabel> bidSelectedSubtitle;
 	std::shared_ptr<CTradeableItem> bidSelectedSlot;
+	ArtifactPosition selectedHeroSlot;
 
 	CTradeBase::SelectionParams getSelectionParams() const override;
 	void onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot) override;

+ 9 - 2
client/widgets/markets/CTradeBase.cpp

@@ -25,6 +25,7 @@
 
 #include "../../../lib/CGeneralTextHandler.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
+#include "../../../lib/CHeroHandler.h"
 #include "../../../lib/mapObjects/CGMarket.h"
 
 CTradeBase::CTradeBase(const IMarket * market, const CGHeroInstance * hero, const SelectionParamsFunctor & getParamsCallback)
@@ -120,8 +121,14 @@ CExperienceAltar::CExperienceAltar()
 	texts.emplace_back(std::make_shared<CTextBox>(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW));
 	// Total experience on the Altar
 	texts.emplace_back(std::make_shared<CTextBox>(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW));
-	expToLevel = std::make_shared<CLabel>(75, 477, FONT_SMALL, ETextAlignment::CENTER);
-	expForHero = std::make_shared<CLabel>(75, 545, FONT_SMALL, ETextAlignment::CENTER);
+	expToLevel = std::make_shared<CLabel>(76, 477, FONT_SMALL, ETextAlignment::CENTER);
+	expForHero = std::make_shared<CLabel>(76, 545, FONT_SMALL, ETextAlignment::CENTER);
+}
+
+void CExperienceAltar::updateSlots()
+{
+	CTradeBase::updateSlots();
+	expToLevel->setText(std::to_string(CGI->heroh->reqExp(CGI->heroh->level(hero->exp) + 1) - hero->exp));
 }
 
 CCreaturesSelling::CCreaturesSelling()

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

@@ -72,6 +72,7 @@ public:
 	const Point dealButtonPos = Point(269, 520);
 
 	CExperienceAltar();
+	void updateSlots() override;
 	virtual void sacrificeAll() = 0;
 	virtual TExpType calcExpAltarForHero() = 0;
 };

+ 5 - 2
client/widgets/markets/TradePanels.cpp

@@ -24,7 +24,7 @@
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 
 CTradeableItem::CTradeableItem(const Rect & area, EType Type, int ID, int Serial)
-	: SelectableSlot(area, Point(1, 2))
+	: SelectableSlot(area, Point(1, 1))
 	, type(EType(-1)) // set to invalid, will be corrected in setType
 	, id(ID)
 	, serial(Serial)
@@ -74,7 +74,8 @@ void CTradeableItem::setType(EType newType)
 			break;
 		case EType::ARTIFACT_PLACEHOLDER:
 		case EType::ARTIFACT_INSTANCE:
-			subtitle->moveTo(pos.topLeft() + Point(22, 55));
+			image->moveTo(pos.topLeft() + Point(0, 1));
+			subtitle->moveTo(pos.topLeft() + Point(22, 56));
 			break;
 		case EType::ARTIFACT_TYPE:
 			subtitle->moveTo(pos.topLeft() + Point(35, 57));
@@ -287,6 +288,7 @@ ArtifactsPanel::ArtifactsPanel(const CTradeableItem::ClickPressedFunctor & click
 	}
 	updateSlotsCallback = updateSubtitles;
 	selectedSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::ARTIFACT_TYPE, 0, 0);
+	selectedSlot->subtitle->moveBy(Point(0, 1));
 }
 
 PlayersPanel::PlayersPanel(const CTradeableItem::ClickPressedFunctor & clickPressedCallback)
@@ -359,6 +361,7 @@ ArtifactsAltarPanel::ArtifactsAltarPanel(const CTradeableItem::ClickPressedFunct
 		auto slot = slots.emplace_back(std::make_shared<CTradeableItem>(Rect(altarSlotPos, Point(44, 44)), EType::ARTIFACT_PLACEHOLDER, -1, slotNum));
 		slot->clickPressedCallback = clickPressedCallback;
 		slot->subtitle->clear();
+		slot->subtitle->moveBy(Point(0, -1));
 		slotNum++;
 	}
 	selectedSlot = std::make_shared<CTradeableItem>(Rect(selectedPos, slotDimension), EType::ARTIFACT_TYPE, 0, 0);

+ 1 - 1
client/widgets/markets/TradePanels.h

@@ -94,7 +94,7 @@ class ArtifactsPanel : public TradePanelBase
 		Point(83, 158)
 	};
 	const size_t slotsForTrade = 7;
-	const Point slotDimension = Point(69, 66);
+	const Point slotDimension = Point(69, 68);
 	const Point selectedPos = Point(83, 266);
 
 public:

+ 0 - 55
client/windows/CAltarWindow.cpp

@@ -1,55 +0,0 @@
-/*
- * CAltarWindow.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 "CAltarWindow.h"
-
-#include "../widgets/TextControls.h"
-
-#include "../CGameInfo.h"
-
-#include "../lib/networkPacks/ArtifactLocation.h"
-#include "../../lib/CHeroHandler.h"
-#include "../../lib/mapObjects/CGHeroInstance.h"
-
-void CAltarWindow::updateExpToLevel()
-{
-	assert(altar);
-	altar->expToLevel->setText(std::to_string(CGI->heroh->reqExp(CGI->heroh->level(altar->hero->exp) + 1) - altar->hero->exp));
-}
-
-void CAltarWindow::updateGarrisons()
-{
-	if(auto altarCreatures = std::static_pointer_cast<CAltarCreatures>(altar))
-		altarCreatures->updateSlots();
-}
-
-bool CAltarWindow::holdsGarrison(const CArmedInstance * army)
-{
-	return getHero() == army;
-}
-
-void CAltarWindow::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
-{
-	if(!getState().has_value())
-		return;
-
-	if(auto altarArtifacts = std::static_pointer_cast<CAltarArtifacts>(altar))
-	{
-		if(srcLoc.artHolder == altarArtifacts->getObjId() || destLoc.artHolder == altarArtifacts->getObjId())
-			altarArtifacts->updateSlots();
-
-		if(const auto pickedArt = getPickedArtifact())
-			altarArtifacts->setSelectedArtifact(pickedArt->getTypeId());
-		else
-			altarArtifacts->setSelectedArtifact(std::nullopt);
-	}
-	CWindowWithArtifacts::artifactMoved(srcLoc, destLoc, withRedraw);
-}

+ 0 - 29
client/windows/CAltarWindow.h

@@ -1,29 +0,0 @@
-/*
- * CAltarWindow.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 "../widgets/CWindowWithArtifacts.h"
-#include "../widgets/markets/CAltarArtifacts.h"
-#include "../widgets/markets/CAltarCreatures.h"
-
-class CExperienceAltar;
-
-class CAltarWindow : public CWindowWithArtifacts, public IGarrisonHolder
-{
-public:
-	void updateExpToLevel();
-	void updateGarrisons() override;
-	bool holdsGarrison(const CArmedInstance * army) override;
-	virtual const CGHeroInstance * getHero() const = 0;
-
-	void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override;
-
-	std::shared_ptr<CExperienceAltar> altar;
-};

+ 2 - 2
client/windows/CCastleInterface.cpp

@@ -685,7 +685,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 	logGlobal->trace("You've clicked on %d", (int)building.toEnum());
 	const CBuilding *b = town->town->buildings.find(building)->second;
 
-	if(building >= BuildingID::DWELL_FIRST)
+	if (building >= BuildingID::DWELL_FIRST)
 	{
 		enterDwelling((building-BuildingID::DWELL_FIRST)%GameConstants::CREATURES_PER_TOWN);
 	}
@@ -744,7 +744,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 		case BuildingID::SPECIAL_1:
 		case BuildingID::SPECIAL_2:
 		case BuildingID::SPECIAL_3:
-				switch(subID)
+				switch (subID)
 				{
 				case BuildingSubID::NONE:
 						enterBuilding(building);

+ 90 - 127
client/windows/CMarketWindow.cpp

@@ -16,6 +16,8 @@
 
 #include "../widgets/Buttons.h"
 #include "../widgets/TextControls.h"
+#include "../widgets/markets/CAltarArtifacts.h"
+#include "../widgets/markets/CAltarCreatures.h"
 #include "../widgets/markets/CArtifactsBuying.h"
 #include "../widgets/markets/CArtifactsSelling.h"
 #include "../widgets/markets/CFreelancerGuild.h"
@@ -32,13 +34,14 @@
 
 CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode)
 	: CStatusbarWindow(PLAYER_COLORED)
-	, hero(hero)
 	, windowClosedCallback(onWindowClosed)
 {
 	assert(mode == EMarketMode::RESOURCE_RESOURCE || mode == EMarketMode::RESOURCE_PLAYER || mode == EMarketMode::CREATURE_RESOURCE ||
 		mode == EMarketMode::RESOURCE_ARTIFACT || mode == EMarketMode::ARTIFACT_RESOURCE || mode == EMarketMode::ARTIFACT_EXP ||
 		mode == EMarketMode::CREATURE_EXP);
 	
+	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
+
 	if(mode == EMarketMode::RESOURCE_RESOURCE)
 		createMarketResources(market, hero);
 	else if(mode == EMarketMode::RESOURCE_PLAYER)
@@ -54,31 +57,31 @@ CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero
 	else if(mode == EMarketMode::CREATURE_EXP)
 		createAltarCreatures(market, hero);
 
-	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 	statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
 }
 
-void CMarketWindow::artifactsChanged()
+void CMarketWindow::updateArtifacts()
 {
-	//market->artifactsChanged(false);
-	if(artsBuy)
-		artsBuy->updateSlots();
+	assert(marketWidget);
+	marketWidget->updateSlots();
 }
 
 void CMarketWindow::updateGarrisons()
 {
-	if(guild)
-		guild->updateSlots();
-	if(altar)
-		altar->updateSlots();
+	assert(marketWidget);
+	marketWidget->updateSlots();
+}
+
+void CMarketWindow::updateResource()
+{
+	assert(marketWidget);
+	marketWidget->updateSlots();
 }
 
-void CMarketWindow::resourceChanged()
+void CMarketWindow::updateHero()
 {
-	if(resRes)
-		resRes->updateSlots();
-	if(trRes)
-		trRes->updateSlots();
+	assert(marketWidget);
+	marketWidget->updateSlots();
 }
 
 void CMarketWindow::close()
@@ -89,9 +92,25 @@ void CMarketWindow::close()
 	CWindowObject::close();
 }
 
-const CGHeroInstance * CMarketWindow::getHero() const
+bool CMarketWindow::holdsGarrison(const CArmedInstance * army)
 {
-	return hero;
+	assert(marketWidget);
+	return marketWidget->hero == army;
+}
+
+void CMarketWindow::artifactRemoved(const ArtifactLocation & artLoc)
+{
+	marketWidget->updateSlots();
+	CWindowWithArtifacts::artifactRemoved(artLoc);
+}
+
+void CMarketWindow::artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw)
+{
+	if(!getState().has_value())
+		return;
+	assert(marketWidget);
+	marketWidget->updateSlots();
+	CWindowWithArtifacts::artifactMoved(srcLoc, destLoc, withRedraw);
 }
 
 void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero)
@@ -117,82 +136,76 @@ void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMark
 		}
 	};
 
-	auto buttonPosY = 520;
 	changeModeButtons.clear();
+	auto buttonPos = Point(18, 520);
 
-	if(isButton(EMarketMode::RESOURCE_PLAYER))
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(18, buttonPosY), AnimationPath::builtin("TPMRKBU1.DEF"),
-			CGI->generaltexth->zelp[612], std::bind(&CMarketWindow::createTransferResources, this, market, hero)));
+	auto addButton = [this, &buttonPos](const AnimationPath & picPath, const std::pair<std::string, std::string> & buttonHelpContainer,
+		const std::function<void()> & pressButtonFunctor)
+	{
+		changeModeButtons.emplace_back(std::make_shared<CButton>(buttonPos, picPath, buttonHelpContainer, pressButtonFunctor));
+		buttonPos -= Point(0, buttonHeightWithMargin);
+	};
 
-	buttonPosY -= buttonHeightWithMargin;
+	if(isButton(EMarketMode::RESOURCE_PLAYER))
+		addButton(AnimationPath::builtin("TPMRKBU1.DEF"), CGI->generaltexth->zelp[612], std::bind(&CMarketWindow::createTransferResources, this, market, hero));
 	if(isButton(EMarketMode::ARTIFACT_RESOURCE))
-	{
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(18, buttonPosY), AnimationPath::builtin("TPMRKBU3.DEF"),
-			CGI->generaltexth->zelp[613], std::bind(&CMarketWindow::createArtifactsSelling, this, market, hero)));
-		buttonPosY -= buttonHeightWithMargin;
-	}
+		addButton(AnimationPath::builtin("TPMRKBU3.DEF"), CGI->generaltexth->zelp[613], std::bind(&CMarketWindow::createArtifactsSelling, this, market, hero));
+	if(isButton(EMarketMode::RESOURCE_ARTIFACT))
+		addButton(AnimationPath::builtin("TPMRKBU2.DEF"), CGI->generaltexth->zelp[598], std::bind(&CMarketWindow::createArtifactsBuying, this, market, hero));
+
+	buttonPos = Point(516, 520 - buttonHeightWithMargin);
 	if(isButton(EMarketMode::CREATURE_RESOURCE))
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(516, buttonPosY), AnimationPath::builtin("TPMRKBU4.DEF"),
-			CGI->generaltexth->zelp[599], std::bind(&CMarketWindow::createFreelancersGuild, this, market, hero)));
+		addButton(AnimationPath::builtin("TPMRKBU4.DEF"), CGI->generaltexth->zelp[599], std::bind(&CMarketWindow::createFreelancersGuild, this, market, hero));
 	if(isButton(EMarketMode::RESOURCE_RESOURCE))
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(516, buttonPosY), AnimationPath::builtin("TPMRKBU5.DEF"),
-			CGI->generaltexth->zelp[605], std::bind(&CMarketWindow::createMarketResources, this, market, hero)));
-	if(isButton(EMarketMode::RESOURCE_ARTIFACT))
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(18, buttonPosY), AnimationPath::builtin("TPMRKBU2.DEF"),
-			CGI->generaltexth->zelp[598], std::bind(&CMarketWindow::createArtifactsBuying, this, market, hero)));
+		addButton(AnimationPath::builtin("TPMRKBU5.DEF"), CGI->generaltexth->zelp[605], std::bind(&CMarketWindow::createMarketResources, this, market, hero));
+	
+	buttonPos = Point(516, 421);
 	if(isButton(EMarketMode::CREATURE_EXP))
 	{
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(516, 421), AnimationPath::builtin("ALTSACC.DEF"),
-			CGI->generaltexth->zelp[572], std::bind(&CMarketWindow::createAltarCreatures, this, market, hero)));
-		if(altar->hero->getAlignment() == EAlignment::GOOD)
+		addButton(AnimationPath::builtin("ALTSACC.DEF"), CGI->generaltexth->zelp[572], std::bind(&CMarketWindow::createAltarCreatures, this, market, hero));
+		if(marketWidget->hero->getAlignment() == EAlignment::GOOD)
 			changeModeButtons.back()->block(true);
 	}
 	if(isButton(EMarketMode::ARTIFACT_EXP))
 	{
-		changeModeButtons.emplace_back(std::make_shared<CButton>(Point(516, 421), AnimationPath::builtin("ALTART.DEF"),
-			CGI->generaltexth->zelp[580], std::bind(&CMarketWindow::createAltarArtifacts, this, market, hero)));
-		if(altar->hero->getAlignment() == EAlignment::EVIL)
+		addButton(AnimationPath::builtin("ALTART.DEF"), CGI->generaltexth->zelp[580], std::bind(&CMarketWindow::createAltarArtifacts, this, market, hero));
+		if(marketWidget->hero->getAlignment() == EAlignment::EVIL)
 			changeModeButtons.back()->block(true);
 	}
 }
 
-void CMarketWindow::createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
+void CMarketWindow::initWidgetInternals(const EMarketMode mode, const std::pair<std::string, std::string> & quitButtonHelpContainer)
 {
-	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
-
-	background = createBg(ImagePath::builtin("TPMRKABS.bmp"), PLAYER_COLORED);
-	artsBuy = std::make_shared<CArtifactsBuying>(market, hero);
-
 	background->center();
 	pos = background->pos;
-	artsBuy->setRedrawParent(true);
-	artsBuy->moveTo(pos.topLeft());
+	marketWidget->setRedrawParent(true);
+	marketWidget->moveTo(pos.topLeft());
 
-	createChangeModeButtons(EMarketMode::RESOURCE_ARTIFACT, market, hero);
+	createChangeModeButtons(mode, marketWidget->market, marketWidget->hero);
 	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[600], [this]() {close(); }, EShortcut::GLOBAL_RETURN);
+		quitButtonHelpContainer, [this](){close();}, EShortcut::GLOBAL_RETURN);
 	redraw();
 }
 
-void CMarketWindow::createArtifactsSelling(const IMarket * market, const CGHeroInstance * hero)
+void CMarketWindow::createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
 {
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
-	artsBuy.reset();
-	background = createBg(ImagePath::builtin("TPMRKASS.bmp"), PLAYER_COLORED);
 
-	artsSel = std::make_shared<CArtifactsSelling>(market, hero);
-	artSets.clear();
-	addSetAndCallbacks(artsSel->getAOHset());
+	background = createBg(ImagePath::builtin("TPMRKABS.bmp"), PLAYER_COLORED);
+	marketWidget = std::make_shared<CArtifactsBuying>(market, hero);
+	initWidgetInternals(EMarketMode::RESOURCE_ARTIFACT, CGI->generaltexth->zelp[600]);
+}
 
-	background->center();
-	pos = background->pos;
-	artsSel->setRedrawParent(true);
-	artsSel->moveTo(pos.topLeft());
+void CMarketWindow::createArtifactsSelling(const IMarket * market, const CGHeroInstance * hero)
+{
+	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
-	createChangeModeButtons(EMarketMode::ARTIFACT_RESOURCE, market, hero);
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[600], [this]() {close(); }, EShortcut::GLOBAL_RETURN);
-	redraw();
+	background = createBg(ImagePath::builtin("TPMRKASS.bmp"), PLAYER_COLORED);
+	auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero);
+	artSets.clear();
+	addSetAndCallbacks(artsSellingMarket->getAOHset());
+	marketWidget = artsSellingMarket;
+	initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);
 }
 
 void CMarketWindow::createMarketResources(const IMarket * market, const CGHeroInstance * hero)
@@ -200,17 +213,8 @@ void CMarketWindow::createMarketResources(const IMarket * market, const CGHeroIn
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
 	background = createBg(ImagePath::builtin("TPMRKRES.bmp"), PLAYER_COLORED);
-	resRes = std::make_shared<CMarketResources>(market, hero);
-
-	background->center();
-	pos = background->pos;
-	resRes->setRedrawParent(true);
-	resRes->moveTo(pos.topLeft());
-
-	createChangeModeButtons(EMarketMode::RESOURCE_RESOURCE, market, hero);
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[600], [this]() {close(); }, EShortcut::GLOBAL_RETURN);
-	redraw();
+	marketWidget = std::make_shared<CMarketResources>(market, hero);
+	initWidgetInternals(EMarketMode::RESOURCE_RESOURCE, CGI->generaltexth->zelp[600]);
 }
 
 void CMarketWindow::createFreelancersGuild(const IMarket * market, const CGHeroInstance * hero)
@@ -218,17 +222,8 @@ void CMarketWindow::createFreelancersGuild(const IMarket * market, const CGHeroI
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
 	background = createBg(ImagePath::builtin("TPMRKCRS.bmp"), PLAYER_COLORED);
-	guild = std::make_shared<CFreelancerGuild>(market, hero);
-	
-	background->center();
-	pos = background->pos;
-	guild->setRedrawParent(true);
-	guild->moveTo(pos.topLeft());
-
-	createChangeModeButtons(EMarketMode::CREATURE_RESOURCE, market, hero);
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[600], [this]() {close(); }, EShortcut::GLOBAL_RETURN);
-	redraw();
+	marketWidget = std::make_shared<CFreelancerGuild>(market, hero);
+	initWidgetInternals(EMarketMode::CREATURE_RESOURCE, CGI->generaltexth->zelp[600]);
 }
 
 void CMarketWindow::createTransferResources(const IMarket * market, const CGHeroInstance * hero)
@@ -236,17 +231,8 @@ void CMarketWindow::createTransferResources(const IMarket * market, const CGHero
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
 	background = createBg(ImagePath::builtin("TPMRKPTS.bmp"), PLAYER_COLORED);
-	trRes = std::make_shared<CTransferResources>(market, hero);
-
-	background->center();
-	pos = background->pos;
-	trRes->setRedrawParent(true);
-	trRes->moveTo(pos.topLeft());
-
-	createChangeModeButtons(EMarketMode::RESOURCE_PLAYER, market, hero);
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[600], [this]() {close(); }, EShortcut::GLOBAL_RETURN);
-	redraw();
+	marketWidget = std::make_shared<CTransferResources>(market, hero);
+	initWidgetInternals(EMarketMode::RESOURCE_PLAYER, CGI->generaltexth->zelp[600]);
 }
 
 void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroInstance * hero)
@@ -255,25 +241,12 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
 
 	background = createBg(ImagePath::builtin("ALTRART2.bmp"), PLAYER_COLORED);
 	auto altarArtifacts = std::make_shared<CAltarArtifacts>(market, hero);
-	altar = altarArtifacts;
+	marketWidget = altarArtifacts;
 	artSets.clear();
 	addSetAndCallbacks(altarArtifacts->getAOHset());
-
-	background->center();
-	pos = background->pos;
-	altar->setRedrawParent(true);
-	createChangeModeButtons(EMarketMode::ARTIFACT_EXP, market, hero);
-	altar->moveTo(pos.topLeft());
-
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[568], [this, altarArtifacts]()
-		{
-			altarArtifacts->putBackArtifacts();
-			CMarketWindow::close();
-		}, EShortcut::GLOBAL_RETURN);
-
-	updateExpToLevel();
-	redraw();
+	initWidgetInternals(EMarketMode::ARTIFACT_EXP, CGI->generaltexth->zelp[568]);
+	updateHero();
+	quitButton->addCallback([altarArtifacts](){altarArtifacts->putBackArtifacts();});
 }
 
 void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroInstance * hero)
@@ -281,17 +254,7 @@ void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroIns
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
 	background = createBg(ImagePath::builtin("ALTARMON.bmp"), PLAYER_COLORED);
-	altar = std::make_shared<CAltarCreatures>(market, hero);
-
-	background->center();
-	pos = background->pos;
-	altar->setRedrawParent(true);
-	createChangeModeButtons(EMarketMode::CREATURE_EXP, market, hero);
-	altar->moveTo(pos.topLeft());
-
-	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
-		CGI->generaltexth->zelp[568], std::bind(&CMarketWindow::close, this), EShortcut::GLOBAL_RETURN);
-
-	updateExpToLevel();
-	redraw();
+	marketWidget = std::make_shared<CAltarCreatures>(market, hero);
+	initWidgetInternals(EMarketMode::CREATURE_EXP, CGI->generaltexth->zelp[568]);
+	updateHero();
 }

+ 10 - 18
client/windows/CMarketWindow.h

@@ -10,27 +10,25 @@
 #pragma once
 
 #include "../widgets/markets/CTradeBase.h"
+#include "../widgets/CWindowWithArtifacts.h"
 #include "CWindowObject.h"
-#include "CAltarWindow.h"
 
-class CArtifactsBuying;
-class CArtifactsSelling;
-class CFreelancerGuild;
-class CMarketResources;
-class CTransferResources;
-
-class CMarketWindow : public CStatusbarWindow, public CAltarWindow // TODO remove CAltarWindow
+class CMarketWindow : public CStatusbarWindow, public CWindowWithArtifacts, public IGarrisonHolder
 {
 public:
 	CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode);
-	void resourceChanged();
-	void artifactsChanged();
+	void updateResource();
+	void updateArtifacts();
 	void updateGarrisons() override;
+	void updateHero();
 	void close() override;
-	const CGHeroInstance * getHero() const;
+	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);
+	void initWidgetInternals(const EMarketMode mode, const std::pair<std::string, std::string> & quitButtonHelpContainer);
 
 	void createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
 	void createArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);
@@ -41,15 +39,9 @@ private:
 	void createAltarCreatures(const IMarket * market, const CGHeroInstance * hero);
 
 	const int buttonHeightWithMargin = 32 + 3;
-	const CGHeroInstance * hero;
 	std::vector<std::shared_ptr<CButton>> changeModeButtons;
 	std::shared_ptr<CButton> quitButton;
 	std::function<void()> windowClosedCallback;
 	const Point quitButtonPos = Point(516, 520);
-
-	std::shared_ptr<CFreelancerGuild> guild;
-	std::shared_ptr<CMarketResources> resRes;
-	std::shared_ptr<CTransferResources> trRes;
-	std::shared_ptr<CArtifactsBuying> artsBuy;
-	std::shared_ptr<CArtifactsSelling> artsSel;
+	std::shared_ptr<CTradeBase> marketWidget;
 };