Prechádzať zdrojové kódy

Reducing size of GUI classes. Split trade windows into a separate file

Ivan Savenko 11 rokov pred
rodič
commit
55ff933b7f

+ 1 - 0
client/CAdvmapInterface.cpp

@@ -30,6 +30,7 @@
 #include "CMusicHandler.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CIntObjectClasses.h"
+#include "gui/CTradeWindow.h"
 #include "../lib/UnlockGuard.h"
 
 #ifdef _MSC_VER

+ 1 - 0
client/CCastleInterface.cpp

@@ -24,6 +24,7 @@
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
 #include "gui/CIntObjectClasses.h"
+#include "gui/CTradeWindow.h"
 
 using namespace boost::assign;
 

+ 1 - 0
client/CMakeLists.txt

@@ -20,6 +20,7 @@ set(client_SRCS
 		gui/CGuiHandler.cpp
 		gui/CIntObject.cpp
 		gui/CIntObjectClasses.cpp
+		gui/CTradeWindow.cpp
 		gui/Fonts.cpp
 		gui/Geometries.cpp
 		gui/CCursorHandler.cpp

+ 1 - 0
client/CPlayerInterface.cpp

@@ -13,6 +13,7 @@
 #include "CMessage.h"
 #include "CPlayerInterface.h"
 #include "gui/SDL_Extensions.h"
+#include "gui/CTradeWindow.h"
 #include "../lib/CConfigHandler.h"
 #include "battle/CCreatureAnimation.h"
 #include "Graphics.h"

+ 0 - 1475
client/GUIClasses.cpp

@@ -1089,1481 +1089,6 @@ void CObjectListWindow::keyPressed (const SDL_KeyboardEvent & key)
 	changeSelection(sel);
 }
 
-CTradeWindow::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial):
-    CIntObject(LCLICK | HOVER | RCLICK, pos),
-    type(EType(-1)),// set to invalid, will be corrected in setType
-    id(ID),
-    serial(Serial),
-    left(Left)
-{
-	downSelection = false;
-	hlp = nullptr;
-	image = nullptr;
-	setType(Type);
-}
-
-void CTradeWindow::CTradeableItem::setType(EType newType)
-{
-	if (type != newType)
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-		type = newType;
-		delete image;
-
-		if (getIndex() < 0)
-		{
-			image = new CAnimImage(getFilename(), 0);
-			image->disable();
-		}
-		else
-			image = new CAnimImage(getFilename(), getIndex());
-	}
-}
-
-void CTradeWindow::CTradeableItem::setID(int newID)
-{
-	if (id != newID)
-	{
-		id = newID;
-		if (image)
-		{
-			int index = getIndex();
-			if (index < 0)
-				image->disable();
-			else
-			{
-				image->enable();
-				image->setFrame(index);
-			}
-		}
-	}
-}
-
-std::string CTradeWindow::CTradeableItem::getFilename()
-{
-	switch(type)
-	{
-	case RESOURCE:
-		return "RESOURCE";
-	case PLAYER:
-		return "CREST58";
-	case ARTIFACT_TYPE:
-	case ARTIFACT_PLACEHOLDER:
-	case ARTIFACT_INSTANCE:
-		return "artifact";
-	case CREATURE:
-		return "TWCRPORT";
-	default:
-		return "";
-	}
-}
-
-int CTradeWindow::CTradeableItem::getIndex()
-{
-	if (id < 0)
-		return -1;
-
-	switch(type)
-	{
-	case RESOURCE:
-	case PLAYER:
-		return id;
-	case ARTIFACT_TYPE:
-	case ARTIFACT_INSTANCE:
-	case ARTIFACT_PLACEHOLDER:
-		return VLC->arth->artifacts[id]->iconIndex;
-	case CREATURE:
-		return VLC->creh->creatures[id]->iconIndex;
-	default:
-		return -1;
-	}
-}
-
-void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to)
-{
-	Point posToBitmap;
-	Point posToSubCenter;
-
-	switch(type)
-	{
-	case RESOURCE:
-		posToBitmap = Point(19,9);
-		posToSubCenter = Point(36, 59);
-		break;
-	case CREATURE_PLACEHOLDER:
-	case CREATURE:
-		posToSubCenter = Point(29, 76);
-		if(downSelection)
-			posToSubCenter.y += 5;
-		break;
-	case PLAYER:
-		posToSubCenter = Point(31, 76);
-		break;
-	case ARTIFACT_PLACEHOLDER:
-	case ARTIFACT_INSTANCE:
-		posToSubCenter = Point(19, 55);
-		if(downSelection)
-			posToSubCenter.y += 8;
-		break;
-	case ARTIFACT_TYPE:
-		posToSubCenter = Point(19, 58);
-		break;
-	}
-
-	if (image)
-	{
-		image->moveTo(pos.topLeft() + posToBitmap);
-		CIntObject::showAll(to);
-	}
-
-	printAtMiddleLoc(subtitle, posToSubCenter, FONT_SMALL, Colors::WHITE, to);
-}
-
-void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
-{
-	CTradeWindow *mw = dynamic_cast<CTradeWindow *>(parent);
-	assert(mw);
-	if(down)
-	{
-
-		if(type == ARTIFACT_PLACEHOLDER)
-		{
-			CAltarWindow *aw = static_cast<CAltarWindow *>(mw);
-			if(const CArtifactInstance *movedArt = aw->arts->commonInfo->src.art)
-			{
-				aw->moveFromSlotToAltar(aw->arts->commonInfo->src.slotID, this, movedArt);
-			}
-			else if(const CArtifactInstance *art = getArtInstance())
-			{
-				aw->arts->commonInfo->src.AOH = aw->arts;
-				aw->arts->commonInfo->src.art = art;
-				aw->arts->commonInfo->src.slotID = aw->hero->getArtPos(art);
-				aw->arts->markPossibleSlots(art);
-
-				//aw->arts->commonInfo->dst.AOH = aw->arts;
-				CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex));
-
-				aw->arts->artifactsOnAltar.erase(art);
-				setID(-1);
-				subtitle = "";
-				aw->deal->block(!aw->arts->artifactsOnAltar.size());
-			}
-
-			aw->calcTotalExp();
-			return;
-		}
-		if(left)
-		{
-			if(mw->hLeft != this)
-				mw->hLeft = this;
-			else
-				return;
-		}
-		else
-		{
-			if(mw->hRight != this)
-				mw->hRight = this;
-			else
-				return;
-		}
-		mw->selectionChanged(left);
-	}
-}
-
-void CTradeWindow::CTradeableItem::showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to)
-{
-	Rect oldPos = pos;
-	std::string oldSub = subtitle;
-	downSelection = true;
-
-	moveTo(dstPos);
-	subtitle = customSub;
-	showAll(to);
-
-	downSelection = false;
-	moveTo(oldPos.topLeft());
-	subtitle = oldSub;
-}
-
-void CTradeWindow::CTradeableItem::hover(bool on)
-{
-	if(!on)
-	{
-		GH.statusbar->clear();
-		return;
-	}
-
-	switch(type)
-	{
-	case CREATURE:
-	case CREATURE_PLACEHOLDER:
-		GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
-		break;
-	case ARTIFACT_PLACEHOLDER:
-		if(id < 0)
-			GH.statusbar->setText(CGI->generaltexth->zelp[582].first);
-		else
-			GH.statusbar->setText(CGI->arth->artifacts[id]->Name());
-		break;
-	}
-}
-
-void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
-{
-	if(down)
-	{
-		switch(type)
-		{
-		case CREATURE:
-		case CREATURE_PLACEHOLDER:
-			//GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
-			break;
-		case ARTIFACT_TYPE:
-		case ARTIFACT_PLACEHOLDER:
-			if(id >= 0)
-				adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down);
-			break;
-		}
-	}
-}
-
-std::string CTradeWindow::CTradeableItem::getName(int number /*= -1*/) const
-{
-	switch(type)
-	{
-	case PLAYER:
-		return CGI->generaltexth->capColors[id];
-	case RESOURCE:
-		return CGI->generaltexth->restypes[id];
-	case CREATURE:
-		if(number == 1)
-			return CGI->creh->creatures[id]->nameSing;
-		else
-			return CGI->creh->creatures[id]->namePl;
-	case ARTIFACT_TYPE:
-	case ARTIFACT_INSTANCE:
-		return CGI->arth->artifacts[id]->Name();
-	}
-	assert(0);
-	return "";
-}
-
-const CArtifactInstance * CTradeWindow::CTradeableItem::getArtInstance() const
-{
-	switch(type)
-	{
-	case ARTIFACT_PLACEHOLDER:
-	case ARTIFACT_INSTANCE:
-		return (const CArtifactInstance *)hlp;
-	default:
-		return nullptr;
-	}
-}
-
-void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art)
-{
-	assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE);
-	hlp = art;
-	if(art)
-		setID(art->artType->id);
-	else
-		setID(-1);
-}
-
-CTradeWindow::CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode):
-    CWindowObject(PLAYER_COLORED, bgName),
-	market(Market),
-    hero(Hero),
-    arts(nullptr),
-    hLeft(nullptr),
-    hRight(nullptr),
-    readyToTrade(false)
-{
-	type |= BLOCK_ADV_HOTKEYS;
-	mode = Mode;
-	initTypes();
-}
-
-void CTradeWindow::initTypes()
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-		itemsType[1] = RESOURCE;
-		itemsType[0] = RESOURCE;
-		break;
-	case EMarketMode::RESOURCE_PLAYER:
-		itemsType[1] = RESOURCE;
-		itemsType[0] = PLAYER;
-		break;
-	case EMarketMode::CREATURE_RESOURCE:
-		itemsType[1] = CREATURE;
-		itemsType[0] = RESOURCE;
-		break;
-	case EMarketMode::RESOURCE_ARTIFACT:
-		itemsType[1] = RESOURCE;
-		itemsType[0] = ARTIFACT_TYPE;
-		break;
-	case EMarketMode::ARTIFACT_RESOURCE:
-		itemsType[1] = ARTIFACT_INSTANCE;
-		itemsType[0] = RESOURCE;
-		break;
-	case EMarketMode::CREATURE_EXP:
-		itemsType[1] = CREATURE;
-		itemsType[0] = CREATURE_PLACEHOLDER;
-		break;
-	case EMarketMode::ARTIFACT_EXP:
-		itemsType[1] = ARTIFACT_TYPE;
-		itemsType[0] = ARTIFACT_PLACEHOLDER;
-		break;
-	}
-}
-
-void CTradeWindow::initItems(bool Left)
-{
-	if(Left && (itemsType[1] == ARTIFACT_TYPE || itemsType[1] == ARTIFACT_INSTANCE))
-	{
-		int xOffset = 0, yOffset = 0;
-		if(mode == EMarketMode::ARTIFACT_RESOURCE)
-		{
-			xOffset = -361;
-			yOffset = +46;
-
-			auto  hlp = new CTradeableItem(Point(137, 469), itemsType[Left], -1, 1, 0);
-			hlp->recActions &= ~(UPDATE | SHOWALL);
-			items[Left].push_back(hlp);
-		}
-		else //ARTIFACT_EXP
-		{
-			xOffset = -363;
-			yOffset = -12;
-		}
-
-		BLOCK_CAPTURING;
-		arts = new CArtifactsOfHero(Point(pos.x+xOffset, pos.y+yOffset));
-		arts->commonInfo = new CArtifactsOfHero::SCommonPart;
-		arts->commonInfo->participants.insert(arts);
-		arts->recActions = 255;
-		arts->setHero(hero);
-		arts->allowedAssembling = false;
-		addChild(arts);
-		artSets.push_back(arts);
-
-		if(mode == EMarketMode::ARTIFACT_RESOURCE)
-			arts->highlightModeCallback = boost::bind(&CTradeWindow::artifactSelected, this, _1);
-		return;
-	}
-
-	std::vector<int> *ids = getItemsIds(Left);
-	std::vector<Rect> pos;
-	int amount = -1;
-
-	getPositionsFor(pos, Left, itemsType[Left]);
-
-	if(Left || !ids)
-		amount = 7;
-	else
-		amount = ids->size();
-
-	if(ids)
-		vstd::amin(amount, ids->size());
-
-	for(int j=0; j<amount; j++)
-	{
-		int id = (ids && ids->size()>j) ? (*ids)[j] : j;
-		if(id < 0 && mode != EMarketMode::ARTIFACT_EXP)  //when sacrificing artifacts we need to prepare empty slots
-			continue;
-
-		auto  hlp = new CTradeableItem(pos[j].topLeft(), itemsType[Left], id, Left, j);
-		hlp->pos = pos[j] + this->pos.topLeft();
-		items[Left].push_back(hlp);
-	}
-
-	initSubs(Left);
-}
-
-std::vector<int> *CTradeWindow::getItemsIds(bool Left)
-{
-	std::vector<int> *ids = nullptr;
-
-	if(mode == EMarketMode::ARTIFACT_EXP)
-		return new std::vector<int>(22, -1);
-
-	if(Left)
-	{
-		switch(itemsType[1])
-		{
-		case CREATURE:
-			ids = new std::vector<int>;
-			for(int i = 0; i < 7; i++)
-			{
-				if(const CCreature *c = hero->getCreature(SlotID(i)))
-					ids->push_back(c->idNumber);
-				else
-					ids->push_back(-1);
-			}
-			break;
-		}
-	}
-	else
-	{
-		switch(itemsType[0])
-		{
-		case PLAYER:
-			ids = new std::vector<int>;
-			for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
-				if(PlayerColor(i) != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(PlayerColor(i)) == EPlayerStatus::INGAME)
-					ids->push_back(i);
-			break;
-
-		case ARTIFACT_TYPE:
-			ids = new std::vector<int>(market->availableItemsIds(mode));
-			break;
-		}
-	}
-
-	return ids;
-}
-
-void CTradeWindow::getPositionsFor(std::vector<Rect> &poss, bool Left, EType type) const
-{
-	if(mode == EMarketMode::ARTIFACT_EXP && !Left)
-	{
-		//22 boxes, 5 in row, last row: two boxes centered
-		int h, w, x, y, dx, dy;
-		h = w = 44;
-		x = 317;
-		y = 53;
-		dx = 54;
-		dy = 70;
-		for (int i = 0; i < 4 ; i++)
-			for (int j = 0; j < 5 ; j++)
-				poss += Rect(x + dx*j, y + dy*i, w, h);
-
-		poss += Rect(x + dx*1.5, y + dy*4, w, h);
-		poss += Rect(x + dx*2.5, y + dy*4, w, h);
-	}
-	else
-	{
-		//seven boxes:
-		//  X  X  X
-		//  X  X  X
-		//     X
-		int h, w, x, y, dx, dy;
-		int leftToRightOffset;
-		getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset);
-
-		poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y),
-			genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy),
-			genRect(h, w, x + dx, y + 2*dy);
-
-		if(!Left)
-		{
-			for(Rect &r : poss)
-				r.x += leftToRightOffset;
-		}
-	}
-}
-
-void CTradeWindow::initSubs(bool Left)
-{
-	for(CTradeableItem *t : items[Left])
-	{
-		if(Left)
-		{
-			switch(itemsType[1])
-			{
-			case CREATURE:
-				t->subtitle = boost::lexical_cast<std::string>(hero->getStackCount(SlotID(t->serial)));
-				break;
-			case RESOURCE:
-				t->subtitle = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(t->serial)));
-				break;
-			}
-		}
-		else //right side
-		{
-			if(itemsType[0] == PLAYER)
-			{
-				t->subtitle = CGI->generaltexth->capColors[t->id];
-			}
-			else if(hLeft)//artifact, creature
-			{
-				int h1, h2; //hlp variables for getting offer
-				market->getOffer(hLeft->id, t->id, h1, h2, mode);
-				if(t->id != hLeft->id || mode != EMarketMode::RESOURCE_RESOURCE) //don't allow exchanging same resources
-				{
-					std::ostringstream oss;
-					oss << h2;
-					if(h1!=1)
-						oss << "/" << h1;
-					t->subtitle = oss.str();
-				}
-				else
-					t->subtitle = CGI->generaltexth->allTexts[164]; // n/a
-			}
-			else
-				t->subtitle = "";
-		}
-	}
-}
-
-void CTradeWindow::showAll(SDL_Surface * to)
-{
-	CWindowObject::showAll(to);
-
-	if(hRight)
-		CSDL_Ext::drawBorder(to,hRight->pos.x-1,hRight->pos.y-1,hRight->pos.w+2,hRight->pos.h+2,int3(255,231,148));
-	if(hLeft && hLeft->type != ARTIFACT_INSTANCE)
-		CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148));
-
-	if(readyToTrade)
-	{
-		hLeft->showAllAt(pos.topLeft() + selectionOffset(true), selectionSubtitle(true), to);
-		hRight->showAllAt(pos.topLeft() + selectionOffset(false), selectionSubtitle(false), to);
-	}
-}
-
-void CTradeWindow::removeItems(const std::set<CTradeableItem *> &toRemove)
-{
-	for(CTradeableItem *t : toRemove)
-		removeItem(t);
-}
-
-void CTradeWindow::removeItem(CTradeableItem * t)
-{
-	items[t->left] -= t;
-	delete t;
-
-	if(hRight == t)
-	{
-		hRight = nullptr;
-		selectionChanged(false);
-	}
-}
-
-void CTradeWindow::getEmptySlots(std::set<CTradeableItem *> &toRemove)
-{
-	for(CTradeableItem *t : items[1])
-		if(!hero->getStackCount(SlotID(t->serial)))
-			toRemove.insert(t);
-}
-
-void CTradeWindow::setMode(EMarketMode::EMarketMode Mode)
-{
-	const IMarket *m = market;
-	const CGHeroInstance *h = hero;
-	CTradeWindow *nwindow = nullptr;
-
-	GH.popIntTotally(this);
-
-	switch(Mode)
-	{
-	case EMarketMode::CREATURE_EXP:
-	case EMarketMode::ARTIFACT_EXP:
-		nwindow = new CAltarWindow(m, h, Mode);
-		break;
-	default:
-		nwindow = new CMarketplaceWindow(m, h, Mode);
-		break;
-	}
-
-	GH.pushInt(nwindow);
-}
-
-void CTradeWindow::artifactSelected(CArtPlace *slot)
-{
-	assert(mode == EMarketMode::ARTIFACT_RESOURCE);
-	items[1][0]->setArtInstance(slot->ourArt);
-	if(slot->ourArt)
-		hLeft = items[1][0];
-	else
-		hLeft = nullptr;
-
-	selectionChanged(true);
-}
-
-std::string CMarketplaceWindow::getBackgroundForMode(EMarketMode::EMarketMode mode)
-{
-	switch(mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-		return "TPMRKRES.bmp";
-	case EMarketMode::RESOURCE_PLAYER:
-		return "TPMRKPTS.bmp";
-	case EMarketMode::CREATURE_RESOURCE:
-		return "TPMRKCRS.bmp";
-	case EMarketMode::RESOURCE_ARTIFACT:
-		return "TPMRKABS.bmp";
-	case EMarketMode::ARTIFACT_RESOURCE:
-		return "TPMRKASS.bmp";
-	}
-	assert(0);
-	return "";
-}
-
-CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode)
-    : CTradeWindow(getBackgroundForMode(Mode), Market, Hero, Mode)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	madeTransaction = false;
-	bool sliderNeeded = true;
-
-	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	std::string title;
-
-	if (market->o->ID == Obj::TOWN)
-	{
-		switch (mode)
-		{
-		break; case EMarketMode::CREATURE_RESOURCE:
-			title = CGI->townh->factions[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name();
-
-		break; case EMarketMode::RESOURCE_ARTIFACT:
-			title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
-			sliderNeeded = false;
-
-		break; case EMarketMode::ARTIFACT_RESOURCE:
-			title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
-			sliderNeeded = false;
-
-		break; default:
-			title = CGI->generaltexth->allTexts[158];
-		}
-	}
-	else
-	{
-		switch (market->o->ID)
-		{
-		break; case Obj::BLACK_MARKET:   title = CGI->generaltexth->allTexts[349];
-		break; case Obj::TRADING_POST:  title = CGI->generaltexth->allTexts[159];
-		break; case Obj::TRADING_POST_SNOW: title = CGI->generaltexth->allTexts[159];
-		break; default:  title = market->o->getObjectName();
-		}
-	}
-
-	new CLabel(300, 27, FONT_BIG, CENTER, Colors::YELLOW, title);
-
-	initItems(false);
-	initItems(true);
-
-	ok = new CAdventureMapButton(CGI->generaltexth->zelp[600],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
-	ok->assignedKeys.insert(SDLK_ESCAPE);
-	deal = new CAdventureMapButton(CGI->generaltexth->zelp[595],boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF");
-	deal->block(true);
-
-	if(sliderNeeded)
-	{
-		slider = new CSlider(231,490,137,nullptr,0,0);
-		slider->moved = boost::bind(&CMarketplaceWindow::sliderMoved,this,_1);
-		max = new CAdventureMapButton(CGI->generaltexth->zelp[596],boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF");
-		max->block(true);
-	}
-	else
-	{
-		slider = nullptr;
-		max = nullptr;
-		deal->moveBy(Point(-30, 0));
-	}
-
-	Rect traderTextRect;
-
-	//left side
-	switch(Mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::RESOURCE_PLAYER:
-	case EMarketMode::RESOURCE_ARTIFACT:
-		new CLabel(154, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[270]);
-		break;
-
-	case EMarketMode::CREATURE_RESOURCE:
-		//%s's Creatures
-		new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE,
-		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
-		break;
-	case EMarketMode::ARTIFACT_RESOURCE:
-		//%s's Artifacts
-		new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE,
-		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
-		break;
-	}
-
-	//right side
-	switch(Mode)
-	{
-	case EMarketMode::RESOURCE_RESOURCE:
-	case EMarketMode::CREATURE_RESOURCE:
-	case EMarketMode::RESOURCE_ARTIFACT:
-	case EMarketMode::ARTIFACT_RESOURCE:
-		new CLabel(445, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]);
-		traderTextRect = Rect(316, 48, 260, 75);
-		break;
-	case EMarketMode::RESOURCE_PLAYER:
-		new CLabel(445, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[169]);
-		traderTextRect = Rect(28, 48, 260, 75);
-		break;
-	}
-
-	traderText = new CTextBox("", traderTextRect, 0, FONT_SMALL, CENTER);
-	int specialOffset = mode == EMarketMode::ARTIFACT_RESOURCE ? 35 : 0; //in selling artifacts mode we need to move res-res and art-res buttons down
-
-	if(printButtonFor(EMarketMode::RESOURCE_PLAYER))
-		new CAdventureMapButton(CGI->generaltexth->zelp[612],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
-	if(printButtonFor(EMarketMode::RESOURCE_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF");
-	if(printButtonFor(EMarketMode::CREATURE_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[599],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::CREATURE_RESOURCE), 516, 485,"TPMRKBU4.DEF"); //was y=450, changed to not overlap res-res in some conditions
-	if(printButtonFor(EMarketMode::RESOURCE_ARTIFACT))
-		new CAdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF");
-	if(printButtonFor(EMarketMode::ARTIFACT_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"); //was y=450, changed to not overlap res-art in some conditions
-
-	updateTraderText();
-}
-
-CMarketplaceWindow::~CMarketplaceWindow()
-{
-	hLeft = hRight = nullptr;
-	for(auto & elem : items[1])
-		delete elem;
-	for(auto & elem : items[0])
-		delete elem;
-
-	items[1].clear();
-	items[0].clear();
-}
-
-
-
-void CMarketplaceWindow::setMax()
-{
-	slider->moveToMax();
-}
-
-void CMarketplaceWindow::makeDeal()
-{
-	int sliderValue = 0;
-	if(slider)
-		sliderValue = slider->value;
-	else
-		sliderValue = !deal->isBlocked(); //should always be 1
-
-	if(!sliderValue)
-		return;
-
-	int leftIdToSend = -1;
-	switch (mode)
-	{
-		case EMarketMode::CREATURE_RESOURCE:
-			leftIdToSend = hLeft->serial;
-			break;
-		case EMarketMode::ARTIFACT_RESOURCE:
-			leftIdToSend = hLeft->getArtInstance()->id.getNum();
-			break;
-		default:
-			leftIdToSend = hLeft->id;
-			break;
-	}
-
-	if(slider)
-	{
-		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
-		slider->moveTo(0);
-	}
-	else
-	{
-		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero);
-	}
-	madeTransaction = true;
-
-	hLeft = nullptr;
-	hRight = nullptr;
-	selectionChanged(true);
-}
-
-void CMarketplaceWindow::sliderMoved( int to )
-{
-	redraw();
-}
-
-void CMarketplaceWindow::selectionChanged(bool side)
-{
-	readyToTrade = hLeft && hRight;
-	if(mode == EMarketMode::RESOURCE_RESOURCE)
-		readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected
-
- 	if(mode == EMarketMode::ARTIFACT_RESOURCE && !hLeft)
-		arts->unmarkSlots(false);
-
-	if(readyToTrade)
-	{
-		int soldItemId = hLeft->id;
-		market->getOffer(soldItemId, hRight->id, r1, r2, mode);
-
-		if(slider)
-		{
-			int newAmount = -1;
-			if(itemsType[1] == RESOURCE)
-				newAmount = LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(soldItemId));
-			else if(itemsType[1] ==  CREATURE)
-				newAmount = hero->getStackCount(SlotID(hLeft->serial)) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
-			else
-				assert(0);
-
-			slider->setAmount(newAmount / r1);
-			slider->moveTo(0);
-			max->block(false);
-			deal->block(false);
-		}
-		else if(itemsType[1] == RESOURCE) //buying -> check if we can afford transaction
-		{
-			deal->block(LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(soldItemId)) < r1);
-		}
-		else
-			deal->block(false);
-	}
-	else
-	{
-		if(slider)
-		{
-			max->block(true);
-			slider->setAmount(0);
-			slider->moveTo(0);
-		}
-		deal->block(true);
-	}
-
-	if(side && itemsType[0] != PLAYER) //items[1] selection changed, recalculate offers
-		initSubs(false);
-
-	updateTraderText();
-	redraw();
-}
-
-bool CMarketplaceWindow::printButtonFor(EMarketMode::EMarketMode M) const
-{
-	return market->allowsTrade(M) && M != mode && (hero || ( M != EMarketMode::CREATURE_RESOURCE && M != EMarketMode::RESOURCE_ARTIFACT && M != EMarketMode::ARTIFACT_RESOURCE ));
-}
-
-void CMarketplaceWindow::garrisonChanged()
-{
-	if(mode != EMarketMode::CREATURE_RESOURCE)
-		return;
-
-	std::set<CTradeableItem *> toRemove;
-	getEmptySlots(toRemove);
-
-
-	removeItems(toRemove);
-	initSubs(true);
-}
-
-void CMarketplaceWindow::artifactsChanged(bool Left)
-{
-	assert(!Left);
-	if(mode != EMarketMode::RESOURCE_ARTIFACT)
-		return;
-
-	std::vector<int> available = market->availableItemsIds(mode);
-	std::set<CTradeableItem *> toRemove;
-	for(CTradeableItem *t : items[0])
-		if(!vstd::contains(available, t->id))
-			toRemove.insert(t);
-
-	removeItems(toRemove);
-	redraw();
-}
-
-std::string CMarketplaceWindow::selectionSubtitle(bool Left) const
-{
-	if(Left)
-	{
-		switch(itemsType[1])
-		{
-		case RESOURCE:
-		case CREATURE:
-			{
-				int val = slider
-					? slider->value * r1
-					: (((deal->isBlocked())) ? 0 : r1);
-
-				return boost::lexical_cast<std::string>(val);
-			}
-		case ARTIFACT_INSTANCE:
-			return ((deal->isBlocked()) ? "0" : "1");
-		}
-	}
-	else
-	{
-		switch(itemsType[0])
-		{
-		case RESOURCE:
-			if(slider)
-				return boost::lexical_cast<std::string>( slider->value * r2 );
-			else
-				return boost::lexical_cast<std::string>(r2);
-		case ARTIFACT_TYPE:
-			return ((deal->isBlocked()) ? "0" : "1");
-		case PLAYER:
-			return (hRight ? CGI->generaltexth->capColors[hRight->id] : "");
-		}
-	}
-
-	return "???";
-}
-
-Point CMarketplaceWindow::selectionOffset(bool Left) const
-{
-	if(Left)
-	{
-		switch(itemsType[1])
-		{
-		case RESOURCE:
-			return Point(122, 446);
-		case CREATURE:
-			return Point(128, 450);
-		case ARTIFACT_INSTANCE:
-			return Point(134, 466);
-		}
-	}
-	else
-	{
-		switch(itemsType[0])
-		{
-		case RESOURCE:
-			if(mode == EMarketMode::ARTIFACT_RESOURCE)
-				return Point(410, 469);
-			else
-				return Point(410, 446);
-		case ARTIFACT_TYPE:
-			return Point(425, 447);
-		case PLAYER:
-			return Point(417, 451);
-		}
-	}
-
-	assert(0);
-	return Point(0,0);
-}
-
-void CMarketplaceWindow::resourceChanged(int type, int val)
-{
-	initSubs(true);
-}
-
-void CMarketplaceWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const
-{
-	switch(type)
-	{
-	case RESOURCE:
-		dx = 82;
-		dy = 79;
-		x = 39;
-		y = 180;
-		h = 66;
-		w = 74;
-		break;
-	case PLAYER:
-		dx = 83;
-		dy = 118;
-		h = 64;
-		w = 58;
-		x = 44;
-		y = 83;
-		assert(Right);
-		break;
-	case CREATURE://45,123
-		x = 45;
-		y = 123;
-		w = 58;
-		h = 64;
-		dx = 83;
-		dy = 98;
-		assert(!Right);
-		break;
-	case ARTIFACT_TYPE://45,123
-		x = 340-289;
-		y = 180;
-		w = 44;
-		h = 44;
-		dx = 83;
-		dy = 79;
-		break;
-	}
-
-	leftToRightOffset = 289;
-}
-
-void CMarketplaceWindow::updateTraderText()
-{
-	if(readyToTrade)
-	{
-		if(mode == EMarketMode::RESOURCE_PLAYER)
-		{
-			//I can give %s to the %s player.
-			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[165]) % hLeft->getName() % hRight->getName()));
-		}
-		else if(mode == EMarketMode::RESOURCE_ARTIFACT)
-		{
-			//I can offer you the %s for %d %s of %s.
-			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[267]) % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName()));
-		}
-		else if(mode == EMarketMode::RESOURCE_RESOURCE)
-		{
-			//I can offer you %d %s of %s for %d %s of %s.
-			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[157]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName()));
-		}
-		else if(mode == EMarketMode::CREATURE_RESOURCE)
-		{
-			//I can offer you %d %s of %s for %d %s.
-			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[269]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % hLeft->getName(r1)));
-		}
-		else if(mode == EMarketMode::ARTIFACT_RESOURCE)
-		{
-			//I can offer you %d %s of %s for your %s.
-			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[268]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % hLeft->getName(r1)));
-		}
-		return;
-	}
-
-	int gnrtxtnr = -1;
-	if(madeTransaction)
-	{
-		if(mode == EMarketMode::RESOURCE_PLAYER)
-			gnrtxtnr = 166; //Are there any other resources you'd like to give away?
-		else
-			gnrtxtnr = 162; //You have received quite a bargain.  I expect to make no profit on the deal.  Can I interest you in any of my other wares?
-	}
-	else
-	{
-		if(mode == EMarketMode::RESOURCE_PLAYER)
-			gnrtxtnr = 167; //If you'd like to give any of your resources to another player, click on the item you wish to give and to whom.
-		else
-			gnrtxtnr = 163; //Please inspect our fine wares.  If you feel like offering a trade, click on the items you wish to trade with and for.
-	}
-	traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]);
-}
-
-CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*= nullptr*/, EMarketMode::EMarketMode Mode)
-	:CTradeWindow((Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, Mode)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if(Mode == EMarketMode::CREATURE_EXP)
-	{
-		//%s's Creatures
-		new CLabel(155, 30, FONT_SMALL, CENTER, Colors::YELLOW,
-		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
-
-		//Altar of Sacrifice
-		new CLabel(450, 30, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479]);
-
-		 //To sacrifice creatures, move them from your army on to the Altar and click Sacrifice
-		new CTextBox(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW);
-
-		slider = new CSlider(231,481,137,nullptr,0,0);
-		slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1);
-		max = new CAdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF");
-
-		sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0);
-		sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF");
-		sacrificeBackpack = nullptr;
-
-		initItems(true);
-		mimicCres();
-		artIcon = nullptr;
-	}
-	else
-	{
-		//Sacrifice artifacts for experience
-		new CLabel(450, 34, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]);
-		//%s's Creatures
-		new CLabel(302, 423, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]);
-
-		sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[571], boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTFILL.DEF");
-		sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty());
-		sacrificeBackpack = new CAdventureMapButton(CGI->generaltexth->zelp[570],boost::bind(&CAltarWindow::SacrificeBackpack,this),147,520,"ALTEMBK.DEF");
-		sacrificeBackpack->block(hero->artifactsInBackpack.empty());
-
-		slider = nullptr;
-		max = nullptr;
-
-		initItems(true);
-		initItems(false);
-		artIcon = new CAnimImage("ARTIFACT", 0, 0, 281, 442);
-		artIcon->disable();
-	}
-
-	//Experience needed to reach next level
-	new CTextBox(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, CENTER, Colors::YELLOW);
-	//Total experience on the Altar
-	new CTextBox(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW);
-
-	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	ok = new CAdventureMapButton(CGI->generaltexth->zelp[568],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
-	ok->assignedKeys.insert(SDLK_ESCAPE);
-
-	deal = new CAdventureMapButton(CGI->generaltexth->zelp[585],boost::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF");
-
-	if(Hero->getAlignment() != ::EAlignment::EVIL && Mode == EMarketMode::CREATURE_EXP)
-		new CAdventureMapButton(CGI->generaltexth->zelp[580], boost::bind(&CTradeWindow::setMode,this, EMarketMode::ARTIFACT_EXP), 516, 421, "ALTART.DEF");
-	if(Hero->getAlignment() != ::EAlignment::GOOD && Mode == EMarketMode::ARTIFACT_EXP)
-		new CAdventureMapButton(CGI->generaltexth->zelp[572], boost::bind(&CTradeWindow::setMode,this, EMarketMode::CREATURE_EXP), 516, 421, "ALTSACC.DEF");
-
-	expPerUnit.resize(GameConstants::ARMY_SIZE, 0);
-	getExpValues();
-
-	expToLevel = new CLabel(73, 475, FONT_SMALL, CENTER);
-	expOnAltar = new CLabel(73, 543, FONT_SMALL, CENTER);
-
-	setExpToLevel();
-	calcTotalExp();
-	blockTrade();
-}
-
-CAltarWindow::~CAltarWindow()
-{
-
-}
-
-void CAltarWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const
-{
-	leftToRightOffset = 289;
-	x = 45;
-	y = 110;
-	w = 58;
-	h = 64;
-	dx = 83;
-	dy = 98;
-}
-
-void CAltarWindow::sliderMoved(int to)
-{
-	sacrificedUnits[hLeft->serial] = to;
-	updateRight(hRight);
-	deal->block(!to);
-	calcTotalExp();
-	redraw();
-}
-
-void CAltarWindow::makeDeal()
-{
-	if(mode == EMarketMode::CREATURE_EXP)
-	{
-		blockTrade();
-		slider->value = 0;
-
-		std::vector<int> toSacrifice = sacrificedUnits;
-		for (int i = 0; i < toSacrifice.size(); i++)
-		{
-			if(toSacrifice[i])
-				LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero);
-		}
-
-		for(int& val : sacrificedUnits)
-			val = 0;
-
-		for(CTradeableItem *t : items[0])
-		{
-			t->setType(CREATURE_PLACEHOLDER);
-			t->subtitle = "";
-		}
-	}
-	else
-	{
-		for(const CArtifactInstance *art : arts->artifactsOnAltar) //sacrifice each artifact on the list
-		{
-			LOCPLINT->cb->trade(market->o, mode, hero->getArtPos(art), -1, 1, hero);
-		}
-		arts->artifactsOnAltar.clear();
-
-		for(CTradeableItem *t : items[0])
-		{
-			t->setID(-1);
-			t->subtitle = "";
-		}
-
-		arts->commonInfo->reset();
-		//arts->scrollBackpack(0);
-		deal->block(true);
-	}
-
-	calcTotalExp();
-}
-
-void CAltarWindow::SacrificeAll()
-{
-	if(mode == EMarketMode::CREATURE_EXP)
-	{
-		bool movedAnything = false;
-		for(CTradeableItem *t : items[1])
-			sacrificedUnits[t->serial] = hero->getStackCount(SlotID(t->serial));
-
-		sacrificedUnits[items[1].front()->serial]--;
-
-		for(CTradeableItem *t : items[0])
-		{
-			updateRight(t);
-			if(t->type == CREATURE)
-				movedAnything = true;
-		}
-
-		deal->block(!movedAnything);
-		calcTotalExp();
-	}
-	else
-	{
-		for(auto i = hero->artifactsWorn.cbegin(); i != hero->artifactsWorn.cend(); i++)
-		{
-			if(i->second.artifact->artType->id != ArtifactID::ART_LOCK) //ignore locks from assembled artifacts
-				moveFromSlotToAltar(i->first, nullptr, i->second.artifact);
-		}
-
-		SacrificeBackpack();
-	}
-	redraw();
-}
-
-void CAltarWindow::selectionChanged(bool side)
-{
-	if(mode != EMarketMode::CREATURE_EXP)
-		return;
-
-	CTradeableItem *&selected = side ? hLeft : hRight;
-	CTradeableItem *&theOther = side ? hRight : hLeft;
-
-	theOther = *std::find_if(items[!side].begin(), items[!side].end(), [&](const CTradeableItem * item)
-	{
-		return item->serial == selected->serial;
-	});
-
-	int stackCount = 0;
-	for (int i = 0; i < GameConstants::ARMY_SIZE; i++)
-		if(hero->getStackCount(SlotID(i)) > sacrificedUnits[i])
-			stackCount++;
-
-	slider->setAmount(hero->getStackCount(SlotID(hLeft->serial)) - (stackCount == 1));
-	slider->block(!slider->amount);
-	slider->value = sacrificedUnits[hLeft->serial];
-	max->block(!slider->amount);
-	readyToTrade = true;
-	redraw();
-}
-
-void CAltarWindow::mimicCres()
-{
-	std::vector<Rect> positions;
-	getPositionsFor(positions, false, CREATURE);
-
-	for(CTradeableItem *t : items[1])
-	{
-		auto  hlp = new CTradeableItem(positions[t->serial].topLeft(), CREATURE_PLACEHOLDER, t->id, false, t->serial);
-		hlp->pos = positions[t->serial] + this->pos.topLeft();
-		items[0].push_back(hlp);
-	}
-}
-
-Point CAltarWindow::selectionOffset(bool Left) const
-{
-	if(Left)
-		return Point(150, 421);
-	else
-		return Point(396, 421);
-}
-
-std::string CAltarWindow::selectionSubtitle(bool Left) const
-{
-	if(Left && slider && hLeft)
-		return boost::lexical_cast<std::string>(slider->value);
-	else if(!Left && hRight)
-		return hRight->subtitle;
-	else
-		return "";
-}
-
-void CAltarWindow::artifactsChanged(bool left)
-{
-
-}
-
-void CAltarWindow::garrisonChanged()
-{
-	if(mode != EMarketMode::CREATURE_EXP)
-		return;
-
-	std::set<CTradeableItem *> empty;
-	getEmptySlots(empty);
-
-	for(CTradeableItem *t : empty)
-	{
-		removeItem(*std::find_if(items[0].begin(), items[0].end(), [&](const CTradeableItem * item)
-		{
-			return item->serial == t->serial;
-		}));
-	}
-
-	initSubs(true);
-	getExpValues();
-}
-
-void CAltarWindow::getExpValues()
-{
-	int dump;
-	for(CTradeableItem *t : items[1])
-		if(t->id >= 0)
-			market->getOffer(t->id, 0, dump, expPerUnit[t->serial], EMarketMode::CREATURE_EXP);
-}
-
-void CAltarWindow::calcTotalExp()
-{
-	int val = 0;
-	if(mode == EMarketMode::CREATURE_EXP)
-	{
-		for (int i = 0; i < sacrificedUnits.size(); i++)
-		{
-			val += expPerUnit[i] * sacrificedUnits[i];
-		}
-	}
-	else
-	{
-		for(const CArtifactInstance *art : arts->artifactsOnAltar)
-		{
-			int dmp, valOfArt;
-			market->getOffer(art->artType->id, 0, dmp, valOfArt, mode);
-			val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i);
-		}
-	}
-	val = hero->calculateXp(val);
-	expOnAltar->setText(boost::lexical_cast<std::string>(val));
-}
-
-void CAltarWindow::setExpToLevel()
-{
-	expToLevel->setText(boost::lexical_cast<std::string>(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp));
-}
-
-void CAltarWindow::blockTrade()
-{
-	hLeft = hRight = nullptr;
-	readyToTrade = false;
-	if(slider)
-	{
-		slider->block(true);
-		max->block(true);
-	}
-	deal->block(true);
-}
-
-void CAltarWindow::updateRight(CTradeableItem *toUpdate)
-{
-	int val = sacrificedUnits[toUpdate->serial];
-	toUpdate->setType(val ? CREATURE : CREATURE_PLACEHOLDER);
-	toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % boost::lexical_cast<std::string>(val * expPerUnit[toUpdate->serial])) : ""; //%s exp
-}
-
-int CAltarWindow::firstFreeSlot()
-{
-	int ret = -1;
-	while(items[0][++ret]->id >= 0  &&  ret + 1 < items[0].size());
-	return ret < items[0].size() ? ret : -1;
-}
-
-void CAltarWindow::SacrificeBackpack()
-{
-	std::multiset<const CArtifactInstance *> toOmmit = arts->artifactsOnAltar;
-
-	for (auto & elem : hero->artifactsInBackpack)
-	{
-
-		if(vstd::contains(toOmmit, elem.artifact))
-		{
-			toOmmit -= elem.artifact;
-			continue;
-		}
-
-		putOnAltar(nullptr, elem.artifact);
-	}
-
-	arts->scrollBackpack(0);
-	calcTotalExp();
-}
-
-void CAltarWindow::artifactPicked()
-{
-	redraw();
-}
-
-void CAltarWindow::showAll(SDL_Surface * to)
-{
-	CTradeWindow::showAll(to);
-	if(mode == EMarketMode::ARTIFACT_EXP && arts && arts->commonInfo->src.art)
-	{
-		artIcon->setFrame(arts->commonInfo->src.art->artType->iconIndex);
-		artIcon->showAll(to);
-
-		int dmp, val;
-		market->getOffer(arts->commonInfo->src.art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
-		printAtMiddleLoc(boost::lexical_cast<std::string>(val), 304, 498, FONT_SMALL, Colors::WHITE, to);
-	}
-}
-
-bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art)
-{
-	int artID = art->artType->id;
-	if(artID != 1 && artID < 7) //special art
-	{
-        logGlobal->warnStream() << "Cannot put special artifact on altar!";
-		return false;
-	}
-
-	if(!altarSlot)
-	{
-		int slotIndex = firstFreeSlot();
-		if(slotIndex < 0)
-		{
-            logGlobal->warnStream() << "No free slots on altar!";
-			return false;
-		}
-		altarSlot = items[0][slotIndex];
-	}
-
-	int dmp, val;
-	market->getOffer(artID, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
-
-	arts->artifactsOnAltar.insert(art);
-	altarSlot->setArtInstance(art);
-	altarSlot->subtitle = boost::lexical_cast<std::string>(val);
-
-	deal->block(false);
-	return true;
-}
-
-void CAltarWindow::moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art)
-{
-	auto freeBackpackSlot = ArtifactPosition(hero->artifactsInBackpack.size() + GameConstants::BACKPACK_START);
-	if(arts->commonInfo->src.art)
-	{
-		arts->commonInfo->dst.slotID = freeBackpackSlot;
-		arts->commonInfo->dst.AOH = arts;
-	}
-
-	if(putOnAltar(altarSlot, art))
-	{
-		if(slotID < GameConstants::BACKPACK_START)
-			LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slotID), ArtifactLocation(hero, freeBackpackSlot));
-		else
-		{
-			arts->commonInfo->src.clear();
-			arts->commonInfo->dst.clear();
-			CCS->curh->dragAndDropCursor(nullptr);
-			arts->unmarkSlots(false);
-		}
-	}
-}
-
 void CSystemOptionsWindow::setMusicVolume( int newVolume )
 {
 	Settings volume = settings.write["general"]["music"];

+ 0 - 150
client/GUIClasses.h

@@ -387,156 +387,6 @@ public:
 	void keyPressed (const SDL_KeyboardEvent & key);
 };
 
-class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice
-{
-public:
-	enum EType
-	{
-		RESOURCE, PLAYER, ARTIFACT_TYPE, CREATURE, CREATURE_PLACEHOLDER, ARTIFACT_PLACEHOLDER, ARTIFACT_INSTANCE
-	};
-	class CTradeableItem : public CIntObject
-	{
-		CAnimImage * image;
-
-		std::string getFilename();
-		int getIndex();
-	public:
-		const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact
-		EType type;
-		int id;
-		const int serial;
-		const bool left;
-		std::string subtitle; //empty if default
-
-		void setType(EType newType);
-		void setID(int newID);
-
-		const CArtifactInstance *getArtInstance() const;
-		void setArtInstance(const CArtifactInstance *art);
-
-		CFunctionList<void()> callback;
-		bool downSelection;
-
-		void showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to);
-
-		void clickRight(tribool down, bool previousState);
-		void hover (bool on);
-		void showAll(SDL_Surface * to);
-		void clickLeft(tribool down, bool previousState);
-		std::string getName(int number = -1) const;
-		CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial);
-	};
-
-	const IMarket *market;
-	const CGHeroInstance *hero;
-
-	CArtifactsOfHero *arts;
-	//all indexes: 1 = left, 0 = right
-	std::vector<CTradeableItem*> items[2];
-	CTradeableItem *hLeft, *hRight; //highlighted items (nullptr if no highlight)
-	EType itemsType[2];
-
-	EMarketMode::EMarketMode mode;//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact
-	CAdventureMapButton *ok, *max, *deal;
-	CSlider *slider; //for choosing amount to be exchanged
-	bool readyToTrade;
-
-	CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c
-
-	void showAll(SDL_Surface * to);
-
-	void initSubs(bool Left);
-	void initTypes();
-	void initItems(bool Left);
-	std::vector<int> *getItemsIds(bool Left); //nullptr if default
-	void getPositionsFor(std::vector<Rect> &poss, bool Left, EType type) const;
-	void removeItems(const std::set<CTradeableItem *> &toRemove);
-	void removeItem(CTradeableItem * t);
-	void getEmptySlots(std::set<CTradeableItem *> &toRemove);
-	void setMode(EMarketMode::EMarketMode Mode); //mode setter
-
-	void artifactSelected(CArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot
-
-	virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0;
-	virtual void selectionChanged(bool side) = 0; //true == left
-	virtual Point selectionOffset(bool Left) const = 0;
-	virtual std::string selectionSubtitle(bool Left) const = 0;
-	virtual void garrisonChanged() = 0;
-	virtual void artifactsChanged(bool left) = 0;
-};
-
-class CMarketplaceWindow : public CTradeWindow
-{
-	bool printButtonFor(EMarketMode::EMarketMode M) const;
-
-	std::string getBackgroundForMode(EMarketMode::EMarketMode mode);
-public:
-	int r1, r2; //suggested amounts of traded resources
-	bool madeTransaction; //if player made at least one transaction
-	CTextBox *traderText;
-
-	void setMax();
-	void sliderMoved(int to);
-	void makeDeal();
-	void selectionChanged(bool side); //true == left
-	CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = nullptr, EMarketMode::EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE); //c-tor
-	~CMarketplaceWindow(); //d-tor
-
-	Point selectionOffset(bool Left) const;
-	std::string selectionSubtitle(bool Left) const;
-
-
-	void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold)
-	void artifactsChanged(bool left);
-	void resourceChanged(int type, int val);
-
-	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
-	void updateTraderText();
-};
-
-class CAltarWindow : public CTradeWindow
-{
-	CAnimImage * artIcon;
-public:
-	CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c-tor
-
-	void getExpValues();
-	~CAltarWindow(); //d-tor
-
-	std::vector<int> sacrificedUnits, //[slot_nr] -> how many creatures from that slot will be sacrificed
-		expPerUnit;
-
-	CAdventureMapButton *sacrificeAll, *sacrificeBackpack;
-	CLabel *expToLevel, *expOnAltar;
-
-
-	void selectionChanged(bool side); //true == left
-	void SacrificeAll();
-	void SacrificeBackpack();
-
-	void putOnAltar(int backpackIndex);
-	bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art);
-	void makeDeal();
-	void showAll(SDL_Surface * to);
-
-	void blockTrade();
-	void sliderMoved(int to);
-	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
-	void mimicCres();
-
-	Point selectionOffset(bool Left) const;
-	std::string selectionSubtitle(bool Left) const;
-	void garrisonChanged();
-	void artifactsChanged(bool left);
-	void calcTotalExp();
-	void setExpToLevel();
-	void updateRight(CTradeableItem *toUpdate);
-
-	void artifactPicked();
-	int firstFreeSlot();
-	void moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art);
-};
-
 class CSystemOptionsWindow : public CWindowObject
 {
 private:

+ 1507 - 0
client/gui/CTradeWindow.cpp

@@ -0,0 +1,1507 @@
+#include "StdInc.h"
+#include "CTradeWindow.h"
+
+#include "CGuiHandler.h"
+#include "CAnimation.h"
+#include "CCursorHandler.h"
+
+#include "../CAdvmapInterface.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/VCMI_Lib.h"
+#include "../../lib/CArtHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CHeroHandler.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+
+/*
+ * CTradeWindow.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
+ *
+ */
+
+CTradeWindow::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial):
+    CIntObject(LCLICK | HOVER | RCLICK, pos),
+    type(EType(-1)),// set to invalid, will be corrected in setType
+    id(ID),
+    serial(Serial),
+    left(Left)
+{
+	downSelection = false;
+	hlp = nullptr;
+	image = nullptr;
+	setType(Type);
+}
+
+void CTradeWindow::CTradeableItem::setType(EType newType)
+{
+	if (type != newType)
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		type = newType;
+		delete image;
+
+		if (getIndex() < 0)
+		{
+			image = new CAnimImage(getFilename(), 0);
+			image->disable();
+		}
+		else
+			image = new CAnimImage(getFilename(), getIndex());
+	}
+}
+
+void CTradeWindow::CTradeableItem::setID(int newID)
+{
+	if (id != newID)
+	{
+		id = newID;
+		if (image)
+		{
+			int index = getIndex();
+			if (index < 0)
+				image->disable();
+			else
+			{
+				image->enable();
+				image->setFrame(index);
+			}
+		}
+	}
+}
+
+std::string CTradeWindow::CTradeableItem::getFilename()
+{
+	switch(type)
+	{
+	case RESOURCE:
+		return "RESOURCE";
+	case PLAYER:
+		return "CREST58";
+	case ARTIFACT_TYPE:
+	case ARTIFACT_PLACEHOLDER:
+	case ARTIFACT_INSTANCE:
+		return "artifact";
+	case CREATURE:
+		return "TWCRPORT";
+	default:
+		return "";
+	}
+}
+
+int CTradeWindow::CTradeableItem::getIndex()
+{
+	if (id < 0)
+		return -1;
+
+	switch(type)
+	{
+	case RESOURCE:
+	case PLAYER:
+		return id;
+	case ARTIFACT_TYPE:
+	case ARTIFACT_INSTANCE:
+	case ARTIFACT_PLACEHOLDER:
+		return VLC->arth->artifacts[id]->iconIndex;
+	case CREATURE:
+		return VLC->creh->creatures[id]->iconIndex;
+	default:
+		return -1;
+	}
+}
+
+void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to)
+{
+	Point posToBitmap;
+	Point posToSubCenter;
+
+	switch(type)
+	{
+	case RESOURCE:
+		posToBitmap = Point(19,9);
+		posToSubCenter = Point(36, 59);
+		break;
+	case CREATURE_PLACEHOLDER:
+	case CREATURE:
+		posToSubCenter = Point(29, 76);
+		if(downSelection)
+			posToSubCenter.y += 5;
+		break;
+	case PLAYER:
+		posToSubCenter = Point(31, 76);
+		break;
+	case ARTIFACT_PLACEHOLDER:
+	case ARTIFACT_INSTANCE:
+		posToSubCenter = Point(19, 55);
+		if(downSelection)
+			posToSubCenter.y += 8;
+		break;
+	case ARTIFACT_TYPE:
+		posToSubCenter = Point(19, 58);
+		break;
+	}
+
+	if (image)
+	{
+		image->moveTo(pos.topLeft() + posToBitmap);
+		CIntObject::showAll(to);
+	}
+
+	printAtMiddleLoc(subtitle, posToSubCenter, FONT_SMALL, Colors::WHITE, to);
+}
+
+void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
+{
+	CTradeWindow *mw = dynamic_cast<CTradeWindow *>(parent);
+	assert(mw);
+	if(down)
+	{
+
+		if(type == ARTIFACT_PLACEHOLDER)
+		{
+			CAltarWindow *aw = static_cast<CAltarWindow *>(mw);
+			if(const CArtifactInstance *movedArt = aw->arts->commonInfo->src.art)
+			{
+				aw->moveFromSlotToAltar(aw->arts->commonInfo->src.slotID, this, movedArt);
+			}
+			else if(const CArtifactInstance *art = getArtInstance())
+			{
+				aw->arts->commonInfo->src.AOH = aw->arts;
+				aw->arts->commonInfo->src.art = art;
+				aw->arts->commonInfo->src.slotID = aw->hero->getArtPos(art);
+				aw->arts->markPossibleSlots(art);
+
+				//aw->arts->commonInfo->dst.AOH = aw->arts;
+				CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex));
+
+				aw->arts->artifactsOnAltar.erase(art);
+				setID(-1);
+				subtitle = "";
+				aw->deal->block(!aw->arts->artifactsOnAltar.size());
+			}
+
+			aw->calcTotalExp();
+			return;
+		}
+		if(left)
+		{
+			if(mw->hLeft != this)
+				mw->hLeft = this;
+			else
+				return;
+		}
+		else
+		{
+			if(mw->hRight != this)
+				mw->hRight = this;
+			else
+				return;
+		}
+		mw->selectionChanged(left);
+	}
+}
+
+void CTradeWindow::CTradeableItem::showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to)
+{
+	Rect oldPos = pos;
+	std::string oldSub = subtitle;
+	downSelection = true;
+
+	moveTo(dstPos);
+	subtitle = customSub;
+	showAll(to);
+
+	downSelection = false;
+	moveTo(oldPos.topLeft());
+	subtitle = oldSub;
+}
+
+void CTradeWindow::CTradeableItem::hover(bool on)
+{
+	if(!on)
+	{
+		GH.statusbar->clear();
+		return;
+	}
+
+	switch(type)
+	{
+	case CREATURE:
+	case CREATURE_PLACEHOLDER:
+		GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
+		break;
+	case ARTIFACT_PLACEHOLDER:
+		if(id < 0)
+			GH.statusbar->setText(CGI->generaltexth->zelp[582].first);
+		else
+			GH.statusbar->setText(CGI->arth->artifacts[id]->Name());
+		break;
+	}
+}
+
+void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
+{
+	if(down)
+	{
+		switch(type)
+		{
+		case CREATURE:
+		case CREATURE_PLACEHOLDER:
+			//GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl));
+			break;
+		case ARTIFACT_TYPE:
+		case ARTIFACT_PLACEHOLDER:
+			if(id >= 0)
+				adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down);
+			break;
+		}
+	}
+}
+
+std::string CTradeWindow::CTradeableItem::getName(int number /*= -1*/) const
+{
+	switch(type)
+	{
+	case PLAYER:
+		return CGI->generaltexth->capColors[id];
+	case RESOURCE:
+		return CGI->generaltexth->restypes[id];
+	case CREATURE:
+		if(number == 1)
+			return CGI->creh->creatures[id]->nameSing;
+		else
+			return CGI->creh->creatures[id]->namePl;
+	case ARTIFACT_TYPE:
+	case ARTIFACT_INSTANCE:
+		return CGI->arth->artifacts[id]->Name();
+	}
+	assert(0);
+	return "";
+}
+
+const CArtifactInstance * CTradeWindow::CTradeableItem::getArtInstance() const
+{
+	switch(type)
+	{
+	case ARTIFACT_PLACEHOLDER:
+	case ARTIFACT_INSTANCE:
+		return (const CArtifactInstance *)hlp;
+	default:
+		return nullptr;
+	}
+}
+
+void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art)
+{
+	assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE);
+	hlp = art;
+	if(art)
+		setID(art->artType->id);
+	else
+		setID(-1);
+}
+
+CTradeWindow::CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode):
+    CWindowObject(PLAYER_COLORED, bgName),
+	market(Market),
+    hero(Hero),
+    arts(nullptr),
+    hLeft(nullptr),
+    hRight(nullptr),
+    readyToTrade(false)
+{
+	type |= BLOCK_ADV_HOTKEYS;
+	mode = Mode;
+	initTypes();
+}
+
+void CTradeWindow::initTypes()
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+		itemsType[1] = RESOURCE;
+		itemsType[0] = RESOURCE;
+		break;
+	case EMarketMode::RESOURCE_PLAYER:
+		itemsType[1] = RESOURCE;
+		itemsType[0] = PLAYER;
+		break;
+	case EMarketMode::CREATURE_RESOURCE:
+		itemsType[1] = CREATURE;
+		itemsType[0] = RESOURCE;
+		break;
+	case EMarketMode::RESOURCE_ARTIFACT:
+		itemsType[1] = RESOURCE;
+		itemsType[0] = ARTIFACT_TYPE;
+		break;
+	case EMarketMode::ARTIFACT_RESOURCE:
+		itemsType[1] = ARTIFACT_INSTANCE;
+		itemsType[0] = RESOURCE;
+		break;
+	case EMarketMode::CREATURE_EXP:
+		itemsType[1] = CREATURE;
+		itemsType[0] = CREATURE_PLACEHOLDER;
+		break;
+	case EMarketMode::ARTIFACT_EXP:
+		itemsType[1] = ARTIFACT_TYPE;
+		itemsType[0] = ARTIFACT_PLACEHOLDER;
+		break;
+	}
+}
+
+void CTradeWindow::initItems(bool Left)
+{
+	if(Left && (itemsType[1] == ARTIFACT_TYPE || itemsType[1] == ARTIFACT_INSTANCE))
+	{
+		int xOffset = 0, yOffset = 0;
+		if(mode == EMarketMode::ARTIFACT_RESOURCE)
+		{
+			xOffset = -361;
+			yOffset = +46;
+
+			auto  hlp = new CTradeableItem(Point(137, 469), itemsType[Left], -1, 1, 0);
+			hlp->recActions &= ~(UPDATE | SHOWALL);
+			items[Left].push_back(hlp);
+		}
+		else //ARTIFACT_EXP
+		{
+			xOffset = -363;
+			yOffset = -12;
+		}
+
+		BLOCK_CAPTURING;
+		arts = new CArtifactsOfHero(Point(pos.x+xOffset, pos.y+yOffset));
+		arts->commonInfo = new CArtifactsOfHero::SCommonPart;
+		arts->commonInfo->participants.insert(arts);
+		arts->recActions = 255;
+		arts->setHero(hero);
+		arts->allowedAssembling = false;
+		addChild(arts);
+		artSets.push_back(arts);
+
+		if(mode == EMarketMode::ARTIFACT_RESOURCE)
+			arts->highlightModeCallback = boost::bind(&CTradeWindow::artifactSelected, this, _1);
+		return;
+	}
+
+	std::vector<int> *ids = getItemsIds(Left);
+	std::vector<Rect> pos;
+	int amount = -1;
+
+	getPositionsFor(pos, Left, itemsType[Left]);
+
+	if(Left || !ids)
+		amount = 7;
+	else
+		amount = ids->size();
+
+	if(ids)
+		vstd::amin(amount, ids->size());
+
+	for(int j=0; j<amount; j++)
+	{
+		int id = (ids && ids->size()>j) ? (*ids)[j] : j;
+		if(id < 0 && mode != EMarketMode::ARTIFACT_EXP)  //when sacrificing artifacts we need to prepare empty slots
+			continue;
+
+		auto  hlp = new CTradeableItem(pos[j].topLeft(), itemsType[Left], id, Left, j);
+		hlp->pos = pos[j] + this->pos.topLeft();
+		items[Left].push_back(hlp);
+	}
+
+	initSubs(Left);
+}
+
+std::vector<int> *CTradeWindow::getItemsIds(bool Left)
+{
+	std::vector<int> *ids = nullptr;
+
+	if(mode == EMarketMode::ARTIFACT_EXP)
+		return new std::vector<int>(22, -1);
+
+	if(Left)
+	{
+		switch(itemsType[1])
+		{
+		case CREATURE:
+			ids = new std::vector<int>;
+			for(int i = 0; i < 7; i++)
+			{
+				if(const CCreature *c = hero->getCreature(SlotID(i)))
+					ids->push_back(c->idNumber);
+				else
+					ids->push_back(-1);
+			}
+			break;
+		}
+	}
+	else
+	{
+		switch(itemsType[0])
+		{
+		case PLAYER:
+			ids = new std::vector<int>;
+			for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++)
+				if(PlayerColor(i) != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(PlayerColor(i)) == EPlayerStatus::INGAME)
+					ids->push_back(i);
+			break;
+
+		case ARTIFACT_TYPE:
+			ids = new std::vector<int>(market->availableItemsIds(mode));
+			break;
+		}
+	}
+
+	return ids;
+}
+
+void CTradeWindow::getPositionsFor(std::vector<Rect> &poss, bool Left, EType type) const
+{
+	using namespace boost::assign;
+
+	if(mode == EMarketMode::ARTIFACT_EXP && !Left)
+	{
+		//22 boxes, 5 in row, last row: two boxes centered
+		int h, w, x, y, dx, dy;
+		h = w = 44;
+		x = 317;
+		y = 53;
+		dx = 54;
+		dy = 70;
+		for (int i = 0; i < 4 ; i++)
+			for (int j = 0; j < 5 ; j++)
+				poss += Rect(x + dx*j, y + dy*i, w, h);
+
+		poss += Rect(x + dx*1.5, y + dy*4, w, h);
+		poss += Rect(x + dx*2.5, y + dy*4, w, h);
+	}
+	else
+	{
+		//seven boxes:
+		//  X  X  X
+		//  X  X  X
+		//     X
+		int h, w, x, y, dx, dy;
+		int leftToRightOffset;
+		getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset);
+
+		poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y),
+			genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy),
+			genRect(h, w, x + dx, y + 2*dy);
+
+		if(!Left)
+		{
+			for(Rect &r : poss)
+				r.x += leftToRightOffset;
+		}
+	}
+}
+
+void CTradeWindow::initSubs(bool Left)
+{
+	for(CTradeableItem *t : items[Left])
+	{
+		if(Left)
+		{
+			switch(itemsType[1])
+			{
+			case CREATURE:
+				t->subtitle = boost::lexical_cast<std::string>(hero->getStackCount(SlotID(t->serial)));
+				break;
+			case RESOURCE:
+				t->subtitle = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(t->serial)));
+				break;
+			}
+		}
+		else //right side
+		{
+			if(itemsType[0] == PLAYER)
+			{
+				t->subtitle = CGI->generaltexth->capColors[t->id];
+			}
+			else if(hLeft)//artifact, creature
+			{
+				int h1, h2; //hlp variables for getting offer
+				market->getOffer(hLeft->id, t->id, h1, h2, mode);
+				if(t->id != hLeft->id || mode != EMarketMode::RESOURCE_RESOURCE) //don't allow exchanging same resources
+				{
+					std::ostringstream oss;
+					oss << h2;
+					if(h1!=1)
+						oss << "/" << h1;
+					t->subtitle = oss.str();
+				}
+				else
+					t->subtitle = CGI->generaltexth->allTexts[164]; // n/a
+			}
+			else
+				t->subtitle = "";
+		}
+	}
+}
+
+void CTradeWindow::showAll(SDL_Surface * to)
+{
+	CWindowObject::showAll(to);
+
+	if(hRight)
+		CSDL_Ext::drawBorder(to,hRight->pos.x-1,hRight->pos.y-1,hRight->pos.w+2,hRight->pos.h+2,int3(255,231,148));
+	if(hLeft && hLeft->type != ARTIFACT_INSTANCE)
+		CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148));
+
+	if(readyToTrade)
+	{
+		hLeft->showAllAt(pos.topLeft() + selectionOffset(true), selectionSubtitle(true), to);
+		hRight->showAllAt(pos.topLeft() + selectionOffset(false), selectionSubtitle(false), to);
+	}
+}
+
+void CTradeWindow::removeItems(const std::set<CTradeableItem *> &toRemove)
+{
+	for(CTradeableItem *t : toRemove)
+		removeItem(t);
+}
+
+void CTradeWindow::removeItem(CTradeableItem * t)
+{
+	items[t->left] -= t;
+	delete t;
+
+	if(hRight == t)
+	{
+		hRight = nullptr;
+		selectionChanged(false);
+	}
+}
+
+void CTradeWindow::getEmptySlots(std::set<CTradeableItem *> &toRemove)
+{
+	for(CTradeableItem *t : items[1])
+		if(!hero->getStackCount(SlotID(t->serial)))
+			toRemove.insert(t);
+}
+
+void CTradeWindow::setMode(EMarketMode::EMarketMode Mode)
+{
+	const IMarket *m = market;
+	const CGHeroInstance *h = hero;
+	CTradeWindow *nwindow = nullptr;
+
+	GH.popIntTotally(this);
+
+	switch(Mode)
+	{
+	case EMarketMode::CREATURE_EXP:
+	case EMarketMode::ARTIFACT_EXP:
+		nwindow = new CAltarWindow(m, h, Mode);
+		break;
+	default:
+		nwindow = new CMarketplaceWindow(m, h, Mode);
+		break;
+	}
+
+	GH.pushInt(nwindow);
+}
+
+void CTradeWindow::artifactSelected(CArtPlace *slot)
+{
+	assert(mode == EMarketMode::ARTIFACT_RESOURCE);
+	items[1][0]->setArtInstance(slot->ourArt);
+	if(slot->ourArt)
+		hLeft = items[1][0];
+	else
+		hLeft = nullptr;
+
+	selectionChanged(true);
+}
+
+std::string CMarketplaceWindow::getBackgroundForMode(EMarketMode::EMarketMode mode)
+{
+	switch(mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+		return "TPMRKRES.bmp";
+	case EMarketMode::RESOURCE_PLAYER:
+		return "TPMRKPTS.bmp";
+	case EMarketMode::CREATURE_RESOURCE:
+		return "TPMRKCRS.bmp";
+	case EMarketMode::RESOURCE_ARTIFACT:
+		return "TPMRKABS.bmp";
+	case EMarketMode::ARTIFACT_RESOURCE:
+		return "TPMRKASS.bmp";
+	}
+	assert(0);
+	return "";
+}
+
+CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode)
+    : CTradeWindow(getBackgroundForMode(Mode), Market, Hero, Mode)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	madeTransaction = false;
+	bool sliderNeeded = true;
+
+	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
+
+	std::string title;
+
+	if (market->o->ID == Obj::TOWN)
+	{
+		switch (mode)
+		{
+		break; case EMarketMode::CREATURE_RESOURCE:
+			title = CGI->townh->factions[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name();
+
+		break; case EMarketMode::RESOURCE_ARTIFACT:
+			title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
+			sliderNeeded = false;
+
+		break; case EMarketMode::ARTIFACT_RESOURCE:
+			title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
+			sliderNeeded = false;
+
+		break; default:
+			title = CGI->generaltexth->allTexts[158];
+		}
+	}
+	else
+	{
+		switch (market->o->ID)
+		{
+		break; case Obj::BLACK_MARKET:   title = CGI->generaltexth->allTexts[349];
+		break; case Obj::TRADING_POST:  title = CGI->generaltexth->allTexts[159];
+		break; case Obj::TRADING_POST_SNOW: title = CGI->generaltexth->allTexts[159];
+		break; default:  title = market->o->getObjectName();
+		}
+	}
+
+	new CLabel(300, 27, FONT_BIG, CENTER, Colors::YELLOW, title);
+
+	initItems(false);
+	initItems(true);
+
+	ok = new CAdventureMapButton(CGI->generaltexth->zelp[600],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
+	ok->assignedKeys.insert(SDLK_ESCAPE);
+	deal = new CAdventureMapButton(CGI->generaltexth->zelp[595],boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF");
+	deal->block(true);
+
+	if(sliderNeeded)
+	{
+		slider = new CSlider(231,490,137,nullptr,0,0);
+		slider->moved = boost::bind(&CMarketplaceWindow::sliderMoved,this,_1);
+		max = new CAdventureMapButton(CGI->generaltexth->zelp[596],boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF");
+		max->block(true);
+	}
+	else
+	{
+		slider = nullptr;
+		max = nullptr;
+		deal->moveBy(Point(-30, 0));
+	}
+
+	Rect traderTextRect;
+
+	//left side
+	switch(Mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::RESOURCE_PLAYER:
+	case EMarketMode::RESOURCE_ARTIFACT:
+		new CLabel(154, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[270]);
+		break;
+
+	case EMarketMode::CREATURE_RESOURCE:
+		//%s's Creatures
+		new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE,
+		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
+		break;
+	case EMarketMode::ARTIFACT_RESOURCE:
+		//%s's Artifacts
+		new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE,
+		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
+		break;
+	}
+
+	//right side
+	switch(Mode)
+	{
+	case EMarketMode::RESOURCE_RESOURCE:
+	case EMarketMode::CREATURE_RESOURCE:
+	case EMarketMode::RESOURCE_ARTIFACT:
+	case EMarketMode::ARTIFACT_RESOURCE:
+		new CLabel(445, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]);
+		traderTextRect = Rect(316, 48, 260, 75);
+		break;
+	case EMarketMode::RESOURCE_PLAYER:
+		new CLabel(445, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[169]);
+		traderTextRect = Rect(28, 48, 260, 75);
+		break;
+	}
+
+	traderText = new CTextBox("", traderTextRect, 0, FONT_SMALL, CENTER);
+	int specialOffset = mode == EMarketMode::ARTIFACT_RESOURCE ? 35 : 0; //in selling artifacts mode we need to move res-res and art-res buttons down
+
+	if(printButtonFor(EMarketMode::RESOURCE_PLAYER))
+		new CAdventureMapButton(CGI->generaltexth->zelp[612],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
+	if(printButtonFor(EMarketMode::RESOURCE_RESOURCE))
+		new CAdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF");
+	if(printButtonFor(EMarketMode::CREATURE_RESOURCE))
+		new CAdventureMapButton(CGI->generaltexth->zelp[599],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::CREATURE_RESOURCE), 516, 485,"TPMRKBU4.DEF"); //was y=450, changed to not overlap res-res in some conditions
+	if(printButtonFor(EMarketMode::RESOURCE_ARTIFACT))
+		new CAdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF");
+	if(printButtonFor(EMarketMode::ARTIFACT_RESOURCE))
+		new CAdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"); //was y=450, changed to not overlap res-art in some conditions
+
+	updateTraderText();
+}
+
+CMarketplaceWindow::~CMarketplaceWindow()
+{
+	hLeft = hRight = nullptr;
+	for(auto & elem : items[1])
+		delete elem;
+	for(auto & elem : items[0])
+		delete elem;
+
+	items[1].clear();
+	items[0].clear();
+}
+
+
+
+void CMarketplaceWindow::setMax()
+{
+	slider->moveToMax();
+}
+
+void CMarketplaceWindow::makeDeal()
+{
+	int sliderValue = 0;
+	if(slider)
+		sliderValue = slider->value;
+	else
+		sliderValue = !deal->isBlocked(); //should always be 1
+
+	if(!sliderValue)
+		return;
+
+	int leftIdToSend = -1;
+	switch (mode)
+	{
+		case EMarketMode::CREATURE_RESOURCE:
+			leftIdToSend = hLeft->serial;
+			break;
+		case EMarketMode::ARTIFACT_RESOURCE:
+			leftIdToSend = hLeft->getArtInstance()->id.getNum();
+			break;
+		default:
+			leftIdToSend = hLeft->id;
+			break;
+	}
+
+	if(slider)
+	{
+		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero);
+		slider->moveTo(0);
+	}
+	else
+	{
+		LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero);
+	}
+	madeTransaction = true;
+
+	hLeft = nullptr;
+	hRight = nullptr;
+	selectionChanged(true);
+}
+
+void CMarketplaceWindow::sliderMoved( int to )
+{
+	redraw();
+}
+
+void CMarketplaceWindow::selectionChanged(bool side)
+{
+	readyToTrade = hLeft && hRight;
+	if(mode == EMarketMode::RESOURCE_RESOURCE)
+		readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected
+
+ 	if(mode == EMarketMode::ARTIFACT_RESOURCE && !hLeft)
+		arts->unmarkSlots(false);
+
+	if(readyToTrade)
+	{
+		int soldItemId = hLeft->id;
+		market->getOffer(soldItemId, hRight->id, r1, r2, mode);
+
+		if(slider)
+		{
+			int newAmount = -1;
+			if(itemsType[1] == RESOURCE)
+				newAmount = LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(soldItemId));
+			else if(itemsType[1] ==  CREATURE)
+				newAmount = hero->getStackCount(SlotID(hLeft->serial)) - (hero->Slots().size() == 1  &&  hero->needsLastStack());
+			else
+				assert(0);
+
+			slider->setAmount(newAmount / r1);
+			slider->moveTo(0);
+			max->block(false);
+			deal->block(false);
+		}
+		else if(itemsType[1] == RESOURCE) //buying -> check if we can afford transaction
+		{
+			deal->block(LOCPLINT->cb->getResourceAmount(static_cast<Res::ERes>(soldItemId)) < r1);
+		}
+		else
+			deal->block(false);
+	}
+	else
+	{
+		if(slider)
+		{
+			max->block(true);
+			slider->setAmount(0);
+			slider->moveTo(0);
+		}
+		deal->block(true);
+	}
+
+	if(side && itemsType[0] != PLAYER) //items[1] selection changed, recalculate offers
+		initSubs(false);
+
+	updateTraderText();
+	redraw();
+}
+
+bool CMarketplaceWindow::printButtonFor(EMarketMode::EMarketMode M) const
+{
+	return market->allowsTrade(M) && M != mode && (hero || ( M != EMarketMode::CREATURE_RESOURCE && M != EMarketMode::RESOURCE_ARTIFACT && M != EMarketMode::ARTIFACT_RESOURCE ));
+}
+
+void CMarketplaceWindow::garrisonChanged()
+{
+	if(mode != EMarketMode::CREATURE_RESOURCE)
+		return;
+
+	std::set<CTradeableItem *> toRemove;
+	getEmptySlots(toRemove);
+
+
+	removeItems(toRemove);
+	initSubs(true);
+}
+
+void CMarketplaceWindow::artifactsChanged(bool Left)
+{
+	assert(!Left);
+	if(mode != EMarketMode::RESOURCE_ARTIFACT)
+		return;
+
+	std::vector<int> available = market->availableItemsIds(mode);
+	std::set<CTradeableItem *> toRemove;
+	for(CTradeableItem *t : items[0])
+		if(!vstd::contains(available, t->id))
+			toRemove.insert(t);
+
+	removeItems(toRemove);
+	redraw();
+}
+
+std::string CMarketplaceWindow::selectionSubtitle(bool Left) const
+{
+	if(Left)
+	{
+		switch(itemsType[1])
+		{
+		case RESOURCE:
+		case CREATURE:
+			{
+				int val = slider
+					? slider->value * r1
+					: (((deal->isBlocked())) ? 0 : r1);
+
+				return boost::lexical_cast<std::string>(val);
+			}
+		case ARTIFACT_INSTANCE:
+			return ((deal->isBlocked()) ? "0" : "1");
+		}
+	}
+	else
+	{
+		switch(itemsType[0])
+		{
+		case RESOURCE:
+			if(slider)
+				return boost::lexical_cast<std::string>( slider->value * r2 );
+			else
+				return boost::lexical_cast<std::string>(r2);
+		case ARTIFACT_TYPE:
+			return ((deal->isBlocked()) ? "0" : "1");
+		case PLAYER:
+			return (hRight ? CGI->generaltexth->capColors[hRight->id] : "");
+		}
+	}
+
+	return "???";
+}
+
+Point CMarketplaceWindow::selectionOffset(bool Left) const
+{
+	if(Left)
+	{
+		switch(itemsType[1])
+		{
+		case RESOURCE:
+			return Point(122, 446);
+		case CREATURE:
+			return Point(128, 450);
+		case ARTIFACT_INSTANCE:
+			return Point(134, 466);
+		}
+	}
+	else
+	{
+		switch(itemsType[0])
+		{
+		case RESOURCE:
+			if(mode == EMarketMode::ARTIFACT_RESOURCE)
+				return Point(410, 469);
+			else
+				return Point(410, 446);
+		case ARTIFACT_TYPE:
+			return Point(425, 447);
+		case PLAYER:
+			return Point(417, 451);
+		}
+	}
+
+	assert(0);
+	return Point(0,0);
+}
+
+void CMarketplaceWindow::resourceChanged(int type, int val)
+{
+	initSubs(true);
+}
+
+void CMarketplaceWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const
+{
+	switch(type)
+	{
+	case RESOURCE:
+		dx = 82;
+		dy = 79;
+		x = 39;
+		y = 180;
+		h = 66;
+		w = 74;
+		break;
+	case PLAYER:
+		dx = 83;
+		dy = 118;
+		h = 64;
+		w = 58;
+		x = 44;
+		y = 83;
+		assert(Right);
+		break;
+	case CREATURE://45,123
+		x = 45;
+		y = 123;
+		w = 58;
+		h = 64;
+		dx = 83;
+		dy = 98;
+		assert(!Right);
+		break;
+	case ARTIFACT_TYPE://45,123
+		x = 340-289;
+		y = 180;
+		w = 44;
+		h = 44;
+		dx = 83;
+		dy = 79;
+		break;
+	}
+
+	leftToRightOffset = 289;
+}
+
+void CMarketplaceWindow::updateTraderText()
+{
+	if(readyToTrade)
+	{
+		if(mode == EMarketMode::RESOURCE_PLAYER)
+		{
+			//I can give %s to the %s player.
+			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[165]) % hLeft->getName() % hRight->getName()));
+		}
+		else if(mode == EMarketMode::RESOURCE_ARTIFACT)
+		{
+			//I can offer you the %s for %d %s of %s.
+			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[267]) % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName()));
+		}
+		else if(mode == EMarketMode::RESOURCE_RESOURCE)
+		{
+			//I can offer you %d %s of %s for %d %s of %s.
+			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[157]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName()));
+		}
+		else if(mode == EMarketMode::CREATURE_RESOURCE)
+		{
+			//I can offer you %d %s of %s for %d %s.
+			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[269]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % hLeft->getName(r1)));
+		}
+		else if(mode == EMarketMode::ARTIFACT_RESOURCE)
+		{
+			//I can offer you %d %s of %s for your %s.
+			traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[268]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % hLeft->getName(r1)));
+		}
+		return;
+	}
+
+	int gnrtxtnr = -1;
+	if(madeTransaction)
+	{
+		if(mode == EMarketMode::RESOURCE_PLAYER)
+			gnrtxtnr = 166; //Are there any other resources you'd like to give away?
+		else
+			gnrtxtnr = 162; //You have received quite a bargain.  I expect to make no profit on the deal.  Can I interest you in any of my other wares?
+	}
+	else
+	{
+		if(mode == EMarketMode::RESOURCE_PLAYER)
+			gnrtxtnr = 167; //If you'd like to give any of your resources to another player, click on the item you wish to give and to whom.
+		else
+			gnrtxtnr = 163; //Please inspect our fine wares.  If you feel like offering a trade, click on the items you wish to trade with and for.
+	}
+	traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]);
+}
+
+CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*= nullptr*/, EMarketMode::EMarketMode Mode)
+	:CTradeWindow((Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, Mode)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if(Mode == EMarketMode::CREATURE_EXP)
+	{
+		//%s's Creatures
+		new CLabel(155, 30, FONT_SMALL, CENTER, Colors::YELLOW,
+		           boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name));
+
+		//Altar of Sacrifice
+		new CLabel(450, 30, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479]);
+
+		 //To sacrifice creatures, move them from your army on to the Altar and click Sacrifice
+		new CTextBox(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW);
+
+		slider = new CSlider(231,481,137,nullptr,0,0);
+		slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1);
+		max = new CAdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF");
+
+		sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0);
+		sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF");
+		sacrificeBackpack = nullptr;
+
+		initItems(true);
+		mimicCres();
+		artIcon = nullptr;
+	}
+	else
+	{
+		//Sacrifice artifacts for experience
+		new CLabel(450, 34, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]);
+		//%s's Creatures
+		new CLabel(302, 423, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]);
+
+		sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[571], boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTFILL.DEF");
+		sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty());
+		sacrificeBackpack = new CAdventureMapButton(CGI->generaltexth->zelp[570],boost::bind(&CAltarWindow::SacrificeBackpack,this),147,520,"ALTEMBK.DEF");
+		sacrificeBackpack->block(hero->artifactsInBackpack.empty());
+
+		slider = nullptr;
+		max = nullptr;
+
+		initItems(true);
+		initItems(false);
+		artIcon = new CAnimImage("ARTIFACT", 0, 0, 281, 442);
+		artIcon->disable();
+	}
+
+	//Experience needed to reach next level
+	new CTextBox(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, CENTER, Colors::YELLOW);
+	//Total experience on the Altar
+	new CTextBox(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW);
+
+	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
+
+	ok = new CAdventureMapButton(CGI->generaltexth->zelp[568],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
+	ok->assignedKeys.insert(SDLK_ESCAPE);
+
+	deal = new CAdventureMapButton(CGI->generaltexth->zelp[585],boost::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF");
+
+	if(Hero->getAlignment() != ::EAlignment::EVIL && Mode == EMarketMode::CREATURE_EXP)
+		new CAdventureMapButton(CGI->generaltexth->zelp[580], boost::bind(&CTradeWindow::setMode,this, EMarketMode::ARTIFACT_EXP), 516, 421, "ALTART.DEF");
+	if(Hero->getAlignment() != ::EAlignment::GOOD && Mode == EMarketMode::ARTIFACT_EXP)
+		new CAdventureMapButton(CGI->generaltexth->zelp[572], boost::bind(&CTradeWindow::setMode,this, EMarketMode::CREATURE_EXP), 516, 421, "ALTSACC.DEF");
+
+	expPerUnit.resize(GameConstants::ARMY_SIZE, 0);
+	getExpValues();
+
+	expToLevel = new CLabel(73, 475, FONT_SMALL, CENTER);
+	expOnAltar = new CLabel(73, 543, FONT_SMALL, CENTER);
+
+	setExpToLevel();
+	calcTotalExp();
+	blockTrade();
+}
+
+CAltarWindow::~CAltarWindow()
+{
+
+}
+
+void CAltarWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const
+{
+	leftToRightOffset = 289;
+	x = 45;
+	y = 110;
+	w = 58;
+	h = 64;
+	dx = 83;
+	dy = 98;
+}
+
+void CAltarWindow::sliderMoved(int to)
+{
+	sacrificedUnits[hLeft->serial] = to;
+	updateRight(hRight);
+	deal->block(!to);
+	calcTotalExp();
+	redraw();
+}
+
+void CAltarWindow::makeDeal()
+{
+	if(mode == EMarketMode::CREATURE_EXP)
+	{
+		blockTrade();
+		slider->value = 0;
+
+		std::vector<int> toSacrifice = sacrificedUnits;
+		for (int i = 0; i < toSacrifice.size(); i++)
+		{
+			if(toSacrifice[i])
+				LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero);
+		}
+
+		for(int& val : sacrificedUnits)
+			val = 0;
+
+		for(CTradeableItem *t : items[0])
+		{
+			t->setType(CREATURE_PLACEHOLDER);
+			t->subtitle = "";
+		}
+	}
+	else
+	{
+		for(const CArtifactInstance *art : arts->artifactsOnAltar) //sacrifice each artifact on the list
+		{
+			LOCPLINT->cb->trade(market->o, mode, hero->getArtPos(art), -1, 1, hero);
+		}
+		arts->artifactsOnAltar.clear();
+
+		for(CTradeableItem *t : items[0])
+		{
+			t->setID(-1);
+			t->subtitle = "";
+		}
+
+		arts->commonInfo->reset();
+		//arts->scrollBackpack(0);
+		deal->block(true);
+	}
+
+	calcTotalExp();
+}
+
+void CAltarWindow::SacrificeAll()
+{
+	if(mode == EMarketMode::CREATURE_EXP)
+	{
+		bool movedAnything = false;
+		for(CTradeableItem *t : items[1])
+			sacrificedUnits[t->serial] = hero->getStackCount(SlotID(t->serial));
+
+		sacrificedUnits[items[1].front()->serial]--;
+
+		for(CTradeableItem *t : items[0])
+		{
+			updateRight(t);
+			if(t->type == CREATURE)
+				movedAnything = true;
+		}
+
+		deal->block(!movedAnything);
+		calcTotalExp();
+	}
+	else
+	{
+		for(auto i = hero->artifactsWorn.cbegin(); i != hero->artifactsWorn.cend(); i++)
+		{
+			if(i->second.artifact->artType->id != ArtifactID::ART_LOCK) //ignore locks from assembled artifacts
+				moveFromSlotToAltar(i->first, nullptr, i->second.artifact);
+		}
+
+		SacrificeBackpack();
+	}
+	redraw();
+}
+
+void CAltarWindow::selectionChanged(bool side)
+{
+	if(mode != EMarketMode::CREATURE_EXP)
+		return;
+
+	CTradeableItem *&selected = side ? hLeft : hRight;
+	CTradeableItem *&theOther = side ? hRight : hLeft;
+
+	theOther = *std::find_if(items[!side].begin(), items[!side].end(), [&](const CTradeableItem * item)
+	{
+		return item->serial == selected->serial;
+	});
+
+	int stackCount = 0;
+	for (int i = 0; i < GameConstants::ARMY_SIZE; i++)
+		if(hero->getStackCount(SlotID(i)) > sacrificedUnits[i])
+			stackCount++;
+
+	slider->setAmount(hero->getStackCount(SlotID(hLeft->serial)) - (stackCount == 1));
+	slider->block(!slider->amount);
+	slider->value = sacrificedUnits[hLeft->serial];
+	max->block(!slider->amount);
+	readyToTrade = true;
+	redraw();
+}
+
+void CAltarWindow::mimicCres()
+{
+	std::vector<Rect> positions;
+	getPositionsFor(positions, false, CREATURE);
+
+	for(CTradeableItem *t : items[1])
+	{
+		auto  hlp = new CTradeableItem(positions[t->serial].topLeft(), CREATURE_PLACEHOLDER, t->id, false, t->serial);
+		hlp->pos = positions[t->serial] + this->pos.topLeft();
+		items[0].push_back(hlp);
+	}
+}
+
+Point CAltarWindow::selectionOffset(bool Left) const
+{
+	if(Left)
+		return Point(150, 421);
+	else
+		return Point(396, 421);
+}
+
+std::string CAltarWindow::selectionSubtitle(bool Left) const
+{
+	if(Left && slider && hLeft)
+		return boost::lexical_cast<std::string>(slider->value);
+	else if(!Left && hRight)
+		return hRight->subtitle;
+	else
+		return "";
+}
+
+void CAltarWindow::artifactsChanged(bool left)
+{
+
+}
+
+void CAltarWindow::garrisonChanged()
+{
+	if(mode != EMarketMode::CREATURE_EXP)
+		return;
+
+	std::set<CTradeableItem *> empty;
+	getEmptySlots(empty);
+
+	for(CTradeableItem *t : empty)
+	{
+		removeItem(*std::find_if(items[0].begin(), items[0].end(), [&](const CTradeableItem * item)
+		{
+			return item->serial == t->serial;
+		}));
+	}
+
+	initSubs(true);
+	getExpValues();
+}
+
+void CAltarWindow::getExpValues()
+{
+	int dump;
+	for(CTradeableItem *t : items[1])
+		if(t->id >= 0)
+			market->getOffer(t->id, 0, dump, expPerUnit[t->serial], EMarketMode::CREATURE_EXP);
+}
+
+void CAltarWindow::calcTotalExp()
+{
+	int val = 0;
+	if(mode == EMarketMode::CREATURE_EXP)
+	{
+		for (int i = 0; i < sacrificedUnits.size(); i++)
+		{
+			val += expPerUnit[i] * sacrificedUnits[i];
+		}
+	}
+	else
+	{
+		for(const CArtifactInstance *art : arts->artifactsOnAltar)
+		{
+			int dmp, valOfArt;
+			market->getOffer(art->artType->id, 0, dmp, valOfArt, mode);
+			val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i);
+		}
+	}
+	val = hero->calculateXp(val);
+	expOnAltar->setText(boost::lexical_cast<std::string>(val));
+}
+
+void CAltarWindow::setExpToLevel()
+{
+	expToLevel->setText(boost::lexical_cast<std::string>(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp));
+}
+
+void CAltarWindow::blockTrade()
+{
+	hLeft = hRight = nullptr;
+	readyToTrade = false;
+	if(slider)
+	{
+		slider->block(true);
+		max->block(true);
+	}
+	deal->block(true);
+}
+
+void CAltarWindow::updateRight(CTradeableItem *toUpdate)
+{
+	int val = sacrificedUnits[toUpdate->serial];
+	toUpdate->setType(val ? CREATURE : CREATURE_PLACEHOLDER);
+	toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % boost::lexical_cast<std::string>(val * expPerUnit[toUpdate->serial])) : ""; //%s exp
+}
+
+int CAltarWindow::firstFreeSlot()
+{
+	int ret = -1;
+	while(items[0][++ret]->id >= 0  &&  ret + 1 < items[0].size());
+	return ret < items[0].size() ? ret : -1;
+}
+
+void CAltarWindow::SacrificeBackpack()
+{
+	std::multiset<const CArtifactInstance *> toOmmit = arts->artifactsOnAltar;
+
+	for (auto & elem : hero->artifactsInBackpack)
+	{
+
+		if(vstd::contains(toOmmit, elem.artifact))
+		{
+			toOmmit -= elem.artifact;
+			continue;
+		}
+
+		putOnAltar(nullptr, elem.artifact);
+	}
+
+	arts->scrollBackpack(0);
+	calcTotalExp();
+}
+
+void CAltarWindow::artifactPicked()
+{
+	redraw();
+}
+
+void CAltarWindow::showAll(SDL_Surface * to)
+{
+	CTradeWindow::showAll(to);
+	if(mode == EMarketMode::ARTIFACT_EXP && arts && arts->commonInfo->src.art)
+	{
+		artIcon->setFrame(arts->commonInfo->src.art->artType->iconIndex);
+		artIcon->showAll(to);
+
+		int dmp, val;
+		market->getOffer(arts->commonInfo->src.art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
+		printAtMiddleLoc(boost::lexical_cast<std::string>(val), 304, 498, FONT_SMALL, Colors::WHITE, to);
+	}
+}
+
+bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art)
+{
+	int artID = art->artType->id;
+	if(artID != 1 && artID < 7) //special art
+	{
+        logGlobal->warnStream() << "Cannot put special artifact on altar!";
+		return false;
+	}
+
+	if(!altarSlot)
+	{
+		int slotIndex = firstFreeSlot();
+		if(slotIndex < 0)
+		{
+            logGlobal->warnStream() << "No free slots on altar!";
+			return false;
+		}
+		altarSlot = items[0][slotIndex];
+	}
+
+	int dmp, val;
+	market->getOffer(artID, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
+
+	arts->artifactsOnAltar.insert(art);
+	altarSlot->setArtInstance(art);
+	altarSlot->subtitle = boost::lexical_cast<std::string>(val);
+
+	deal->block(false);
+	return true;
+}
+
+void CAltarWindow::moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art)
+{
+	auto freeBackpackSlot = ArtifactPosition(hero->artifactsInBackpack.size() + GameConstants::BACKPACK_START);
+	if(arts->commonInfo->src.art)
+	{
+		arts->commonInfo->dst.slotID = freeBackpackSlot;
+		arts->commonInfo->dst.AOH = arts;
+	}
+
+	if(putOnAltar(altarSlot, art))
+	{
+		if(slotID < GameConstants::BACKPACK_START)
+			LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slotID), ArtifactLocation(hero, freeBackpackSlot));
+		else
+		{
+			arts->commonInfo->src.clear();
+			arts->commonInfo->dst.clear();
+			CCS->curh->dragAndDropCursor(nullptr);
+			arts->unmarkSlots(false);
+		}
+	}
+}

+ 166 - 0
client/gui/CTradeWindow.h

@@ -0,0 +1,166 @@
+#pragma once
+
+#include "CIntObjectClasses.h"
+#include "CArtifactHolder.h"
+
+/*
+ * CTradeWindow.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
+ *
+ */
+
+class IMarket;
+
+class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice
+{
+public:
+	enum EType
+	{
+		RESOURCE, PLAYER, ARTIFACT_TYPE, CREATURE, CREATURE_PLACEHOLDER, ARTIFACT_PLACEHOLDER, ARTIFACT_INSTANCE
+	};
+	class CTradeableItem : public CIntObject
+	{
+		CAnimImage * image;
+
+		std::string getFilename();
+		int getIndex();
+	public:
+		const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact
+		EType type;
+		int id;
+		const int serial;
+		const bool left;
+		std::string subtitle; //empty if default
+
+		void setType(EType newType);
+		void setID(int newID);
+
+		const CArtifactInstance *getArtInstance() const;
+		void setArtInstance(const CArtifactInstance *art);
+
+		CFunctionList<void()> callback;
+		bool downSelection;
+
+		void showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to);
+
+		void clickRight(tribool down, bool previousState);
+		void hover (bool on);
+		void showAll(SDL_Surface * to);
+		void clickLeft(tribool down, bool previousState);
+		std::string getName(int number = -1) const;
+		CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial);
+	};
+
+	const IMarket *market;
+	const CGHeroInstance *hero;
+
+	CArtifactsOfHero *arts;
+	//all indexes: 1 = left, 0 = right
+	std::vector<CTradeableItem*> items[2];
+	CTradeableItem *hLeft, *hRight; //highlighted items (nullptr if no highlight)
+	EType itemsType[2];
+
+	EMarketMode::EMarketMode mode;//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact
+	CAdventureMapButton *ok, *max, *deal;
+	CSlider *slider; //for choosing amount to be exchanged
+	bool readyToTrade;
+
+	CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c
+
+	void showAll(SDL_Surface * to);
+
+	void initSubs(bool Left);
+	void initTypes();
+	void initItems(bool Left);
+	std::vector<int> *getItemsIds(bool Left); //nullptr if default
+	void getPositionsFor(std::vector<Rect> &poss, bool Left, EType type) const;
+	void removeItems(const std::set<CTradeableItem *> &toRemove);
+	void removeItem(CTradeableItem * t);
+	void getEmptySlots(std::set<CTradeableItem *> &toRemove);
+	void setMode(EMarketMode::EMarketMode Mode); //mode setter
+
+	void artifactSelected(CArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot
+
+	virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0;
+	virtual void selectionChanged(bool side) = 0; //true == left
+	virtual Point selectionOffset(bool Left) const = 0;
+	virtual std::string selectionSubtitle(bool Left) const = 0;
+	virtual void garrisonChanged() = 0;
+	virtual void artifactsChanged(bool left) = 0;
+};
+
+class CMarketplaceWindow : public CTradeWindow
+{
+	bool printButtonFor(EMarketMode::EMarketMode M) const;
+
+	std::string getBackgroundForMode(EMarketMode::EMarketMode mode);
+public:
+	int r1, r2; //suggested amounts of traded resources
+	bool madeTransaction; //if player made at least one transaction
+	CTextBox *traderText;
+
+	void setMax();
+	void sliderMoved(int to);
+	void makeDeal();
+	void selectionChanged(bool side); //true == left
+	CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = nullptr, EMarketMode::EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE); //c-tor
+	~CMarketplaceWindow(); //d-tor
+
+	Point selectionOffset(bool Left) const;
+	std::string selectionSubtitle(bool Left) const;
+
+
+	void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold)
+	void artifactsChanged(bool left);
+	void resourceChanged(int type, int val);
+
+	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
+	void updateTraderText();
+};
+
+class CAltarWindow : public CTradeWindow
+{
+	CAnimImage * artIcon;
+public:
+	CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c-tor
+
+	void getExpValues();
+	~CAltarWindow(); //d-tor
+
+	std::vector<int> sacrificedUnits, //[slot_nr] -> how many creatures from that slot will be sacrificed
+		expPerUnit;
+
+	CAdventureMapButton *sacrificeAll, *sacrificeBackpack;
+	CLabel *expToLevel, *expOnAltar;
+
+
+	void selectionChanged(bool side); //true == left
+	void SacrificeAll();
+	void SacrificeBackpack();
+
+	void putOnAltar(int backpackIndex);
+	bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art);
+	void makeDeal();
+	void showAll(SDL_Surface * to);
+
+	void blockTrade();
+	void sliderMoved(int to);
+	void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const;
+	void mimicCres();
+
+	Point selectionOffset(bool Left) const;
+	std::string selectionSubtitle(bool Left) const;
+	void garrisonChanged();
+	void artifactsChanged(bool left);
+	void calcTotalExp();
+	void setExpToLevel();
+	void updateRight(CTradeableItem *toUpdate);
+
+	void artifactPicked();
+	int firstFreeSlot();
+	void moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art);
+};