2
0
SoundSSGood 1 жил өмнө
parent
commit
6b658bf9c1

+ 2 - 0
client/CMakeLists.txt

@@ -125,6 +125,7 @@ set(client_SRCS
 	widgets/RadialMenu.cpp
 	widgets/markets/CAltarArtifacts.cpp
 	widgets/markets/CAltarCreatures.cpp
+	widgets/markets/CFreelancerGuild.cpp
 	widgets/markets/CTradeBase.cpp
 	widgets/markets/TradePanels.cpp
 
@@ -310,6 +311,7 @@ set(client_HEADERS
 	widgets/RadialMenu.h
 	widgets/markets/CAltarArtifacts.h
 	widgets/markets/CAltarCreatures.h
+	widgets/markets/CFreelancerGuild.h
 	widgets/markets/CTradeBase.h
 	widgets/markets/TradePanels.h
 

+ 2 - 2
client/widgets/markets/CAltarArtifacts.cpp

@@ -39,7 +39,7 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
 		CGI->generaltexth->zelp[585], [this]() {CAltarArtifacts::makeDeal(); });
 	labels.emplace_back(std::make_shared<CLabel>(450, 34, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]));
 	labels.emplace_back(std::make_shared<CLabel>(302, 423, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]));
-	selectedCost = std::make_shared<CLabel>(302, 500, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
+	lSubtitle = std::make_shared<CLabel>(302, 501, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
 	selectedArt = std::make_shared<CArtPlace>(Point(280, 442));
 
 	sacrificeAllButton = std::make_shared<CButton>(Point(393, 520), AnimationPath::builtin("ALTFILL.DEF"),
@@ -109,7 +109,7 @@ void CAltarArtifacts::sacrificeBackpack()
 void CAltarArtifacts::setSelectedArtifact(const CArtifactInstance * art)
 {
 	selectedArt->setArtifact(art);
-	selectedCost->setText(art == nullptr ? "" : std::to_string(calcExpCost(art)));
+	lSubtitle->setText(art == nullptr ? "" : std::to_string(calcExpCost(art)));
 }
 
 std::shared_ptr<CArtifactsOfHeroAltar> CAltarArtifacts::getAOHset() const

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

@@ -23,14 +23,13 @@ public:
 	void setSelectedArtifact(const CArtifactInstance * art);
 	std::shared_ptr<CArtifactsOfHeroAltar> getAOHset() const;
 	ObjectInstanceID getObjId() const;
-	void updateSlots();
+	void updateSlots() override;
 	void putBackArtifacts();
 
 private:
 	ObjectInstanceID altarId;
 	const CArtifactSet * altarArtifacts;
 	std::shared_ptr<CArtPlace> selectedArt;
-	std::shared_ptr<CLabel> selectedCost;
 	std::shared_ptr<CButton> sacrificeBackpackButton;
 	std::shared_ptr<CArtifactsOfHeroAltar> heroArts;
 	std::map<const CArtifactInstance*, std::shared_ptr<CTradeableItem>> tradeSlotsMap;

+ 36 - 15
client/widgets/markets/CAltarCreatures.cpp

@@ -49,8 +49,9 @@ CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance *
 
 	// Hero creatures panel
 	assert(leftTradePanel);
+	leftTradePanel->selectedImage->moveTo(pos.topLeft() + Point(104, 312));
+
 	leftTradePanel->moveBy(Point(45, 110));
-	leftTradePanel->updateSlotsCallback = std::bind(&CCreaturesSelling::updateSubtitle, this);
 	for(const auto & slot : leftTradePanel->slots)
 		slot->clickPressedCallback = [this](const std::shared_ptr<CTradeableItem> & heroSlot) {CAltarCreatures::onSlotClickPressed(heroSlot, hLeft);};
 
@@ -59,6 +60,7 @@ CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance *
 		{
 			CAltarCreatures::onSlotClickPressed(altarSlot, hRight);
 		}, leftTradePanel->slots);
+	rightTradePanel->selectedImage->moveTo(pos.topLeft() + Point(61, 312));
 	rightTradePanel->moveBy(Point(334, 110));
 
 	leftTradePanel->deleteSlotsCheck = rightTradePanel->deleteSlotsCheck = std::bind(&CCreaturesSelling::slotDeletingCheck, this, _1);
@@ -109,25 +111,41 @@ void CAltarCreatures::updateControls()
 	maxUnits->block(offerSlider->getAmount() == 0);
 }
 
-void CAltarCreatures::updateSubtitlesForSelected()
+void CAltarCreatures::updateSelected()
 {
+	std::optional<size_t> lImageIndex = std::nullopt;
+	std::optional<size_t> rImageIndex = std::nullopt;
+
 	if(hLeft)
+	{
 		lSubtitle->setText(std::to_string(offerSlider->getValue()));
+		lImageIndex = CGI->creatures()->getByIndex(hLeft->id)->getIconIndex();
+	}
 	else
+	{
 		lSubtitle->setText("");
+	}
 	if(hRight)
+	{
 		rSubtitle->setText(hRight->subtitle);
+		if(offerSlider->getValue() != 0)
+			rImageIndex = CGI->creatures()->getByIndex(hRight->id)->getIconIndex();
+	}
 	else
+	{
 		rSubtitle->setText("");
+	}
+	leftTradePanel->setSelectedFrameIndex(lImageIndex);
+	rightTradePanel->setSelectedFrameIndex(rImageIndex);
+	rightTradePanel->selectedImage->redraw();
 }
 
 void CAltarCreatures::updateSlots()
 {
 	rightTradePanel->deleteSlots();
-	leftTradePanel->deleteSlots();
+	CCreaturesSelling::updateSlots();
 	assert(leftTradePanel->slots.size() == rightTradePanel->slots.size());
 	readExpValues();
-	leftTradePanel->updateSlots();
 }
 
 void CAltarCreatures::deselect()
@@ -135,7 +153,8 @@ void CAltarCreatures::deselect()
 	CTradeBase::deselect();
 	offerSlider->block(true);
 	maxUnits->block(true);
-	updateSubtitlesForSelected();
+	offerSlider->scrollTo(0);
+	updateSelected();
 }
 
 TExpType CAltarCreatures::calcExpAltarForHero()
@@ -152,7 +171,6 @@ TExpType CAltarCreatures::calcExpAltarForHero()
 void CAltarCreatures::makeDeal()
 {
 	deselect();
-	offerSlider->scrollTo(0);
 	expForHero->setText(std::to_string(0));
 
 	std::vector<TradeItemSell> ids;
@@ -192,14 +210,17 @@ void CAltarCreatures::sacrificeAll()
 			unitsOnAltar[heroSlot->serial] = stackCount;
 		}
 	}
-	assert(lastSlot.has_value());
-	unitsOnAltar[lastSlot.value().num]--;
+	if(hero->needsLastStack())
+	{
+		assert(lastSlot.has_value());
+		unitsOnAltar[lastSlot.value().num]--;
+	}
 
 	if(hRight)
 		offerSlider->scrollTo(unitsOnAltar[hRight->serial]);
 	for(auto altarSlot : rightTradePanel->slots)
 		updateAltarSlot(altarSlot);
-	updateSubtitlesForSelected();
+	updateSelected();
 
 	deal->block(calcExpAltarForHero() == 0);
 }
@@ -220,18 +241,18 @@ void CAltarCreatures::onOfferSliderMoved(int newVal)
 		updateAltarSlot(hRight);
 	deal->block(calcExpAltarForHero() == 0);
 	updateControls();
-	updateSubtitlesForSelected();
+	updateSelected();
 }
 
-void CAltarCreatures::onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSide)
+void CAltarCreatures::onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot)
 {
-	if(hCurSide == newSlot)
+	if(hCurSlot == newSlot)
 		return;
 
 	auto * oppositeSlot = &hLeft;
 	auto oppositePanel = leftTradePanel;
-	CTradeBase::onSlotClickPressed(newSlot, hCurSide);
-	if(hCurSide == hLeft)
+	CTradeBase::onSlotClickPressed(newSlot, hCurSlot);
+	if(hCurSlot == hLeft)
 	{
 		oppositeSlot = &hRight;
 		oppositePanel = rightTradePanel;
@@ -246,6 +267,6 @@ void CAltarCreatures::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
 	assert(oppositeNewSlot);
 	CTradeBase::onSlotClickPressed(oppositeNewSlot, *oppositeSlot);
 	updateControls();
-	updateSubtitlesForSelected();
+	updateSelected();
 	redraw();
 }

+ 3 - 5
client/widgets/markets/CAltarCreatures.h

@@ -15,7 +15,7 @@ class CAltarCreatures : public CExperienceAltar, public CCreaturesSelling
 {
 public:
 	CAltarCreatures(const IMarket * market, const CGHeroInstance * hero);
-	void updateSlots();
+	void updateSlots() override;
 	void deselect() override;
 	TExpType calcExpAltarForHero() override;
 	void makeDeal() override;
@@ -26,12 +26,10 @@ private:
 	std::shared_ptr<CButton> maxUnits;
 	std::vector<int> unitsOnAltar;
 	std::vector<int> expPerUnit;
-	std::shared_ptr<CLabel> lSubtitle;
-	std::shared_ptr<CLabel> rSubtitle;
 
 	void readExpValues();
 	void updateControls();
-	void updateSubtitlesForSelected();
+	void updateSelected();
 	void onOfferSliderMoved(int newVal);
-	void onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSide) override;
+	void onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot) override;
 };

+ 148 - 0
client/widgets/markets/CFreelancerGuild.cpp

@@ -0,0 +1,148 @@
+/*
+ * CFreelancerGuild.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 "CFreelancerGuild.h"
+
+#include "../../gui/CGuiHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/Slider.h"
+#include "../../widgets/TextControls.h"
+
+#include "../../CGameInfo.h"
+#include "../../CPlayerInterface.h"
+
+#include "../../../CCallback.h"
+
+#include "../../../lib/CGeneralTextHandler.h"
+#include "../../../lib/mapObjects/CGHeroInstance.h"
+#include "../../../lib/mapObjects/CGMarket.h"
+#include "../../../lib/mapObjects/CGTownInstance.h"
+
+CFreelancerGuild::CFreelancerGuild(const IMarket * market, const CGHeroInstance * hero)
+	: CTradeBase(market, hero)
+	, CResourcesMarket(EMarketMode::CREATURE_RESOURCE)
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
+	labels.emplace_back(std::make_shared<CLabel>(254, -96, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW,
+		(*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->getNameTranslated()));
+	labels.emplace_back(std::make_shared<CLabel>(110, -20, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
+		boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated())));
+	deal = std::make_shared<CButton>(Point(262, 397), AnimationPath::builtin("ALTSACR.DEF"),
+		CGI->generaltexth->zelp[595], [this]() {CFreelancerGuild::makeDeal();});
+	deal->block(true);
+	maxUnits = std::make_shared<CButton>(Point(183, 397), AnimationPath::builtin("IRCBTNS.DEF"), CGI->generaltexth->zelp[596],
+		[this]() {offerSlider->scrollToMax();});
+	maxUnits->block(true);
+	lSubtitle = std::make_shared<CLabel>(113, 403, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
+	rSubtitle = std::make_shared<CLabel>(400, 382, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
+
+	// Hero creatures panel
+	assert(leftTradePanel);
+	leftTradePanel->deleteSlotsCheck = [this, hero](const std::shared_ptr<CTradeableItem> & slot)
+	{
+		return hero->getStackCount(SlotID(slot->serial)) == 0 ? true : false;
+	};
+	std::for_each(leftTradePanel->slots.cbegin(), leftTradePanel->slots.cend(), [this](auto & slot)
+		{
+			slot->clickPressedCallback = [this](const std::shared_ptr<CTradeableItem> & heroSlot)
+			{
+				CFreelancerGuild::onSlotClickPressed(heroSlot, hLeft);
+			};
+		});
+	leftTradePanel->selectedImage->moveTo(pos.topLeft() + Point(83, 327));
+
+	// Guild resources panel
+	assert(rightTradePanel);
+	rightTradePanel->moveBy(Point(282, 58));
+	std::for_each(rightTradePanel->slots.cbegin(), rightTradePanel->slots.cend(), [this](auto & slot)
+		{
+			slot->clickPressedCallback = [this](const std::shared_ptr<CTradeableItem> & heroSlot)
+			{
+				CFreelancerGuild::onSlotClickPressed(heroSlot, hRight);
+			};
+		});
+	rightTradePanel->selectedImage->moveTo(pos.topLeft() + Point(383, 335));
+
+	offerSlider = std::make_shared<CSlider>(Point(187, 366), 137, [this](int newVal)
+		{
+			CFreelancerGuild::onOfferSliderMoved(newVal);
+		}, 0, 0, 0, Orientation::HORIZONTAL);
+
+	CFreelancerGuild::deselect();
+}
+
+void CFreelancerGuild::updateSelected()
+{
+	std::optional<size_t> lImageIndex = std::nullopt;
+	std::optional<size_t> rImageIndex = std::nullopt;
+	
+	if(hLeft && hRight)
+	{
+		lSubtitle->setText(std::to_string(qtyPerPrice * offerSlider->getValue()));
+		rSubtitle->setText(std::to_string(price * offerSlider->getValue()));
+		lImageIndex = CGI->creatures()->getByIndex(hLeft->id)->getIconIndex();
+		rImageIndex = hRight->id;
+	}
+	else
+	{
+		lSubtitle->setText("");
+		rSubtitle->setText("");
+	}
+	leftTradePanel->setSelectedFrameIndex(lImageIndex);
+	rightTradePanel->setSelectedFrameIndex(rImageIndex);
+}
+
+void CFreelancerGuild::makeDeal()
+{
+	LOCPLINT->cb->trade(market, EMarketMode::CREATURE_RESOURCE, SlotID(hLeft->serial), GameResID(hRight->id), qtyPerPrice * offerSlider->getValue(), hero);
+	deselect();
+}
+
+void CFreelancerGuild::deselect()
+{
+	CTradeBase::deselect();
+	maxUnits->block(true);
+	updateSelected();
+	qtyPerPrice = 0;
+	price = 0;
+	offerSlider->scrollTo(0);
+}
+
+void CFreelancerGuild::onOfferSliderMoved(int newVal)
+{
+	if(hLeft && hRight)
+	{
+		offerSlider->scrollTo(newVal);
+		updateSelected();
+		redraw();
+	}
+}
+
+void CFreelancerGuild::onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot)
+{
+	CTradeBase::onSlotClickPressed(newSlot, hCurSlot);
+	
+	if(hLeft)
+	{
+		if(hRight)
+		{
+			market->getOffer(hLeft->id, hRight->id, qtyPerPrice, price, EMarketMode::CREATURE_RESOURCE);
+			offerSlider->setAmount((hero->getStackCount(SlotID(hLeft->serial)) - (hero->stacksCount() == 1 && hero->needsLastStack() ? 1 : 0)) / qtyPerPrice);
+			offerSlider->scrollTo(0);
+			maxUnits->block(false);
+			deal->block(false);
+		}
+		updateSelected();
+		rightTradePanel->updateSlots();
+	}
+	redraw();
+}

+ 29 - 0
client/widgets/markets/CFreelancerGuild.h

@@ -0,0 +1,29 @@
+/*
+ * CFreelancerGuild.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 "CTradeBase.h"
+
+class CFreelancerGuild : public CCreaturesSelling , public CResourcesMarket
+{
+public:
+	CFreelancerGuild(const IMarket * market, const CGHeroInstance * hero);
+	void makeDeal() override;
+	void deselect() override;
+
+private:
+	std::shared_ptr<CButton> maxUnits;
+	int qtyPerPrice;
+	int price;
+
+	void updateSelected();
+	void onOfferSliderMoved(int newVal);
+	void onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot) override;
+};

+ 35 - 0
client/widgets/markets/CTradeBase.cpp

@@ -20,6 +20,7 @@
 
 #include "../../../lib/CGeneralTextHandler.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
+#include "../../../lib/mapObjects/CGMarket.h"
 
 CTradeBase::CTradeBase(const IMarket * market, const CGHeroInstance * hero)
 	: market(market)
@@ -83,6 +84,8 @@ CExperienceAltar::CExperienceAltar()
 
 CCreaturesSelling::CCreaturesSelling()
 {
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
 	assert(hero);
 	CreaturesPanel::slotsData slots;
 	for(auto slotId = SlotID(0); slotId.num < GameConstants::ARMY_SIZE; slotId++)
@@ -91,6 +94,7 @@ CCreaturesSelling::CCreaturesSelling()
 			slots.emplace_back(std::make_tuple(creature->getId(), slotId, hero->getStackCount(slotId)));
 	}
 	leftTradePanel = std::make_shared<CreaturesPanel>(nullptr, slots);
+	leftTradePanel->updateSlotsCallback = std::bind(&CCreaturesSelling::updateSubtitle, this);
 }
 
 bool CCreaturesSelling::slotDeletingCheck(const std::shared_ptr<CTradeableItem> & slot)
@@ -103,3 +107,34 @@ void CCreaturesSelling::updateSubtitle()
 	for(auto & heroSlot : leftTradePanel->slots)
 		heroSlot->subtitle = std::to_string(this->hero->getStackCount(SlotID(heroSlot->serial)));
 }
+
+void CCreaturesSelling::updateSlots()
+{
+	leftTradePanel->deleteSlots();
+	leftTradePanel->updateSlots();
+}
+
+CResourcesMarket::CResourcesMarket(EMarketMode marketMode)
+{
+	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
+
+	rightTradePanel = std::make_shared<ResourcesPanel>([](const std::shared_ptr<CTradeableItem>&) {}, [this, marketMode]()
+		{
+			updateSubtitles(marketMode);
+		});
+	labels.emplace_back(std::make_shared<CLabel>(400, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]));
+}
+
+void CResourcesMarket::updateSubtitles(EMarketMode marketMode)
+{
+	if(hLeft)
+		for(const auto & slot : rightTradePanel->slots)
+		{
+			int h1, h2; //hlp variables for getting offer
+			market->getOffer(hLeft->id, slot->id, h1, h2, marketMode);
+
+			rightTradePanel->updateOffer(*slot, h1, h2);
+		}
+	else
+		rightTradePanel->clearSubtitles();
+};

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

@@ -35,6 +35,8 @@ public:
 	//highlighted items (nullptr if no highlight)
 	std::shared_ptr<CTradeableItem> hLeft;
 	std::shared_ptr<CTradeableItem> hRight;
+	std::shared_ptr<CLabel> lSubtitle;
+	std::shared_ptr<CLabel> rSubtitle;
 	std::shared_ptr<CButton> deal;
 	std::shared_ptr<CSlider> offerSlider;
 
@@ -49,6 +51,7 @@ public:
 	virtual void makeDeal() = 0;
 	virtual void deselect();
 	virtual void onSlotClickPressed(const std::shared_ptr<CTradeableItem> & newSlot, std::shared_ptr<CTradeableItem> & hCurSlot);
+	virtual void updateSlots() {};	// TODO make pure virtual
 };
 
 // Market subclasses
@@ -71,4 +74,14 @@ public:
 	CCreaturesSelling();
 	bool slotDeletingCheck(const std::shared_ptr<CTradeableItem> & slot);
 	void updateSubtitle();
+	void updateSlots() override;
+};
+
+class CResourcesMarket : virtual public CTradeBase, virtual public CIntObject
+{
+public:
+	CResourcesMarket(EMarketMode marketMode);
+
+private:
+	void updateSubtitles(EMarketMode marketMode);
 };

+ 17 - 0
client/widgets/markets/TradePanels.cpp

@@ -298,6 +298,20 @@ void TradePanelBase::deleteSlots()
 		slots.erase(std::remove_if(slots.begin(), slots.end(), deleteSlotsCheck), slots.end());
 }
 
+void TradePanelBase::setSelectedFrameIndex(std::optional<size_t> index)
+{
+	if(index.has_value())
+	{
+		selectedImage->enable();
+		selectedImage->setFrame(index.value());
+	}
+	else
+	{
+		selectedImage->disable();
+		selectedImage->setFrame(0);
+	}
+}
+
 ResourcesPanel::ResourcesPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback, UpdateSlotsFunctor updateSubtitles)
 {
 	assert(resourcesForTrade.size() == slotsPos.size());
@@ -311,6 +325,7 @@ ResourcesPanel::ResourcesPanel(CTradeableItem::ClickPressedFunctor clickPressedC
 		slot->setSelectionWidth(selectionWidth);
 	}
 	updateSlotsCallback = updateSubtitles;
+	selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), 0);
 }
 
 ArtifactsPanel::ArtifactsPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback, UpdateSlotsFunctor updateSubtitles,
@@ -373,6 +388,7 @@ CreaturesPanel::CreaturesPanel(CTradeableItem::ClickPressedFunctor clickPressedC
 			slot->subtitle = std::to_string(creaturesNum);
 		slot->setSelectionWidth(selectionWidth);
 	}
+	selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), 0);
 }
 
 CreaturesPanel::CreaturesPanel(CTradeableItem::ClickPressedFunctor clickPressedCallback,
@@ -389,4 +405,5 @@ CreaturesPanel::CreaturesPanel(CTradeableItem::ClickPressedFunctor clickPressedC
 		slot->subtitle = emptySlots ? "" : srcSlot->subtitle;
 		slot->setSelectionWidth(selectionWidth);
 	}
+	selectedImage = std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), 0);
 }

+ 2 - 0
client/widgets/markets/TradePanels.h

@@ -64,12 +64,14 @@ public:
 	DeleteSlotsCheck deleteSlotsCheck;
 	std::shared_ptr<CTradeableItem> selected;
 	const int selectionWidth = 2;
+	std::shared_ptr<CAnimImage> selectedImage;
 
 	virtual void updateSlots();
 	virtual void deselect();
 	virtual void clearSubtitles();
 	void updateOffer(CTradeableItem & slot, int, int);
 	void deleteSlots();
+	void setSelectedFrameIndex(std::optional<size_t> index);
 };
 
 class ResourcesPanel : public TradePanelBase

+ 0 - 16
client/windows/CAltarWindow.cpp

@@ -11,7 +11,6 @@
 #include "StdInc.h"
 #include "CAltarWindow.h"
 
-#include "../render/Canvas.h"
 #include "../widgets/TextControls.h"
 
 #include "../CGameInfo.h"
@@ -54,18 +53,3 @@ void CAltarWindow::artifactMoved(const ArtifactLocation & srcLoc, const Artifact
 	}
 	CWindowWithArtifacts::artifactMoved(srcLoc, destLoc, withRedraw);
 }
-
-/*void CAltarWindow::showAll(Canvas& to)
-{
-	// This func is temporary workaround for compliance with CTradeWindow
-	CWindowObject::showAll(to);
-
-	if(altar->hRight)
-	{
-		altar->hRight->showAllAt(altar->pos.topLeft() + Point(396, 423), "", to);
-	}
-	if(altar->hLeft)
-	{
-		altar->hLeft->showAllAt(altar->pos.topLeft() + Point(150, 423), "", to);
-	}
-}*/

+ 0 - 1
client/windows/CAltarWindow.h

@@ -24,7 +24,6 @@ public:
 	virtual const CGHeroInstance * getHero() const = 0;
 
 	void artifactMoved(const ArtifactLocation & srcLoc, const ArtifactLocation & destLoc, bool withRedraw) override;
-	//void showAll(Canvas & to) override;
 
 	std::shared_ptr<CExperienceAltar> altar;
 };

+ 32 - 14
client/windows/CMarketWindow.cpp

@@ -16,6 +16,7 @@
 
 #include "../widgets/Buttons.h"
 #include "../widgets/TextControls.h"
+#include "../widgets/markets/CFreelancerGuild.h"
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
@@ -58,9 +59,17 @@ void CMarketWindow::artifactsChanged()
 	market->artifactsChanged(false);
 }
 
+void CMarketWindow::updateGarrisons()
+{
+	CAltarWindow::updateGarrisons();
+
+	if(guild)
+		guild->updateSlots();
+}
+
 void CMarketWindow::resourceChanged()
 {
-	market->initSubs(true);
+	//market->initSubs(true);
 }
 
 void CMarketWindow::close()
@@ -76,26 +85,26 @@ const CGHeroInstance * CMarketWindow::getHero() const
 	return hero;
 }
 
-void CMarketWindow::createChangeModeButtons(EMarketMode mode, const IMarket * market, const CGHeroInstance * hero)
+void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero)
 {
-	auto isButton = [this](EMarketMode mode) -> bool
+	auto isButton = [this, currentMode, market, hero](EMarketMode modeButton) -> bool
 	{
-		if(this->market->mode == mode)
+		if(currentMode == modeButton)
 			return false;
 
-		if(!this->market->market->allowsTrade(mode))
+		if(!market->allowsTrade(modeButton))
 			return false;
 
-		if(mode == EMarketMode::RESOURCE_RESOURCE || mode == EMarketMode::RESOURCE_PLAYER)
+		if(modeButton == EMarketMode::RESOURCE_RESOURCE || modeButton == EMarketMode::RESOURCE_PLAYER)
 		{
-			if(const auto town = dynamic_cast<const CGTownInstance*>(this->market->market))
+			if(const auto town = dynamic_cast<const CGTownInstance*>(market))
 				return town->getOwner() == LOCPLINT->playerID;
 			else
 				return true;
 		}
 		else
 		{
-			return this->market->hero != nullptr;
+			return hero != nullptr;
 		}
 	};
 
@@ -122,14 +131,14 @@ void CMarketWindow::createChangeModeButtons(EMarketMode mode, const IMarket * ma
 	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)));
-	if(isButton(EMarketMode::ARTIFACT_EXP))
+	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)
 			changeModeButtons.back()->block(true);
 	}
-	if(isButton(EMarketMode::CREATURE_EXP))
+	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)));
@@ -181,8 +190,17 @@ void CMarketWindow::createFreelancersGuild(const IMarket * market, const CGHeroI
 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
 
 	background = createBg(ImagePath::builtin("TPMRKCRS.bmp"), PLAYER_COLORED);
-	this->market = std::make_shared<CMarketplaceWindow>(market, hero, []() {}, EMarketMode::CREATURE_RESOURCE);
-	createInternals(EMarketMode::CREATURE_RESOURCE, market, hero);
+	guild = std::make_shared<CFreelancerGuild>(market, hero);
+	
+	background->center();
+	pos = background->pos;
+	guild->setRedrawParent(true);
+	guild->moveTo(Point(257, 211));
+
+	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();
 }
 
 void CMarketWindow::createTransferResources(const IMarket * market, const CGHeroInstance * hero)
@@ -207,7 +225,7 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
 	background->center();
 	pos = background->pos;
 	altar->setRedrawParent(true);
-	//createChangeModeButtons(EMarketMode::ARTIFACT_EXP, market, hero);
+	createChangeModeButtons(EMarketMode::ARTIFACT_EXP, market, hero);
 	altar->moveTo(pos.topLeft());
 
 	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),
@@ -231,7 +249,7 @@ void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroIns
 	background->center();
 	pos = background->pos;
 	altar->setRedrawParent(true);
-	//createChangeModeButtons(EMarketMode::CREATURE_EXP, market, hero);
+	createChangeModeButtons(EMarketMode::CREATURE_EXP, market, hero);
 	altar->moveTo(pos.topLeft());
 
 	quitButton = std::make_shared<CButton>(quitButtonPos, AnimationPath::builtin("IOK6432.DEF"),

+ 7 - 3
client/windows/CMarketWindow.h

@@ -12,17 +12,20 @@
 #include "CTradeWindow.h"
 #include "CAltarWindow.h"
 
-class CMarketWindow : public CStatusbarWindow, public CAltarWindow
+class CFreelancerGuild;
+
+class CMarketWindow : public CStatusbarWindow, public CAltarWindow // TODO remove CAltarWindow
 {
 public:
 	CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode);
 	void resourceChanged();
 	void artifactsChanged();
+	void updateGarrisons() override;
 	void close() override;
 	const CGHeroInstance * getHero() const;
 
 private:
-	void createChangeModeButtons(EMarketMode mode, const IMarket * market, const CGHeroInstance * hero);
+	void createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero);
 	void createInternals(EMarketMode mode, const IMarket * market, const CGHeroInstance * hero);
 
 	void createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
@@ -41,4 +44,5 @@ private:
 	const Point quitButtonPos = Point(516, 520);
 
 	std::shared_ptr<CMarketplaceWindow> market;
-};
+	std::shared_ptr<CFreelancerGuild> guild;
+};

+ 3 - 28
client/windows/CTradeWindow.cpp

@@ -103,23 +103,7 @@ void CTradeWindow::initItems(bool Left)
 			CTradeBase::onSlotClickPressed(newSlot, left ? hLeft : hRight);
 			selectionChanged(left);
 		};
-
-		if(Left && mode == EMarketMode::CREATURE_RESOURCE)
-		{
-			CreaturesPanel::slotsData slots;
-			for(auto slotId = SlotID(0); slotId.num < GameConstants::ARMY_SIZE; slotId++)
-			{
-				if(const auto & creature = hero->getCreature(slotId))
-					slots.emplace_back(std::make_tuple(creature->getId(), slotId, hero->getStackCount(slotId)));
-			}
-			leftTradePanel = std::make_shared<CreaturesPanel>(std::bind(clickPressedTradePanel, _1, true), slots);
-			leftTradePanel->moveBy(Point(45, 123));
-			leftTradePanel->deleteSlotsCheck = [this](const std::shared_ptr<CTradeableItem> & slot)
-			{
-				return this->hero->getStackCount(SlotID(slot->serial)) == 0 ? true : false;
-			};
-		}
-		else if(Left && (mode == EMarketMode::RESOURCE_RESOURCE || mode == EMarketMode::RESOURCE_ARTIFACT || mode == EMarketMode::RESOURCE_PLAYER))
+		if(Left && (mode == EMarketMode::RESOURCE_RESOURCE || mode == EMarketMode::RESOURCE_ARTIFACT || mode == EMarketMode::RESOURCE_PLAYER))
 		{
 			leftTradePanel = std::make_shared<ResourcesPanel>(
 				[clickPressedTradePanel](const std::shared_ptr<CTradeableItem> & newSlot)
@@ -149,7 +133,7 @@ void CTradeWindow::initItems(bool Left)
 				});
 			rightTradePanel->moveBy(Point(327, 181));
 		}
-		else if(!Left && (mode == EMarketMode::ARTIFACT_RESOURCE || mode == EMarketMode::CREATURE_RESOURCE))
+		else if(!Left && (mode == EMarketMode::ARTIFACT_RESOURCE))
 		{
 			rightTradePanel = std::make_shared<ResourcesPanel>(std::bind(clickPressedTradePanel, _1, false),
 				std::bind(updRightSub, EMarketMode::ARTIFACT_RESOURCE));
@@ -175,7 +159,7 @@ void CTradeWindow::initItems(bool Left)
 
 void CTradeWindow::initSubs(bool Left)
 {
-	if(itemsType[Left] == EType::RESOURCE || itemsType[Left] == EType::ARTIFACT_TYPE)
+	if (itemsType[Left] == EType::RESOURCE || itemsType[Left] == EType::ARTIFACT_TYPE)
 	{ 
 		if(Left)
 			leftTradePanel->updateSlots();
@@ -236,8 +220,6 @@ ImagePath CMarketplaceWindow::getBackgroundForMode(EMarketMode mode)
 		return ImagePath::builtin("TPMRKRES.bmp");
 	case EMarketMode::RESOURCE_PLAYER:
 		return ImagePath::builtin("TPMRKPTS.bmp");
-	case EMarketMode::CREATURE_RESOURCE:
-		return ImagePath::builtin("TPMRKCRS.bmp");
 	case EMarketMode::RESOURCE_ARTIFACT:
 		return ImagePath::builtin("TPMRKABS.bmp");
 	case EMarketMode::ARTIFACT_RESOURCE:
@@ -318,8 +300,6 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta
 		labels.push_back(std::make_shared<CLabel>(154, 148, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[270]));
 		break;
 	case EMarketMode::CREATURE_RESOURCE:
-		//%s's Creatures
-		labels.push_back(std::make_shared<CLabel>(152, 102, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated())));
 		break;
 	case EMarketMode::ARTIFACT_RESOURCE:
 		//%s's Artifacts
@@ -487,11 +467,6 @@ void CMarketplaceWindow::selectionChanged(bool side)
 
 void CMarketplaceWindow::updateGarrison()
 {
-	if(mode != EMarketMode::CREATURE_RESOURCE)
-		return;
-
-	leftTradePanel->deleteSlots();
-	leftTradePanel->updateSlots();
 }
 
 void CMarketplaceWindow::artifactsChanged(bool Left)