Sfoglia il codice sorgente

Merged GUI refactoring into develop, fixed conflicts

Ivan Savenko 11 anni fa
parent
commit
b1285bc506
100 ha cambiato i file con 7955 aggiunte e 11820 eliminazioni
  1. BIN
      Mods/vcmi/Data/StackQueueLarge.png
  2. BIN
      Mods/vcmi/Data/StackQueueSmall.png
  3. BIN
      Mods/vcmi/Data/questDialog.png
  4. BIN
      Mods/vcmi/Data/stackWindow/bonus-effects.png
  5. BIN
      Mods/vcmi/Data/stackWindow/button-panel.png
  6. BIN
      Mods/vcmi/Data/stackWindow/commander-abilities.png
  7. BIN
      Mods/vcmi/Data/stackWindow/commander-bg.png
  8. BIN
      Mods/vcmi/Data/stackWindow/icons.png
  9. BIN
      Mods/vcmi/Data/stackWindow/info-panel-0.png
  10. BIN
      Mods/vcmi/Data/stackWindow/info-panel-1.png
  11. BIN
      Mods/vcmi/Data/stackWindow/info-panel-2.png
  12. BIN
      Mods/vcmi/Data/stackWindow/spell-effects.png
  13. 8 0
      Mods/vcmi/Sprites/buttons/commander.json
  14. BIN
      Mods/vcmi/Sprites/buttons/commanderNormal.png
  15. BIN
      Mods/vcmi/Sprites/buttons/commanderPressed.png
  16. 8 0
      Mods/vcmi/Sprites/buttons/resolution.json
  17. BIN
      Mods/vcmi/Sprites/buttons/resolutionNormal.png
  18. BIN
      Mods/vcmi/Sprites/buttons/resolutionPressed.png
  19. BIN
      Mods/vcmi/Sprites/stackWindow/cancel-normal.png
  20. BIN
      Mods/vcmi/Sprites/stackWindow/cancel-pressed.png
  21. 8 0
      Mods/vcmi/Sprites/stackWindow/cancelButton.json
  22. BIN
      Mods/vcmi/Sprites/stackWindow/level-0.png
  23. BIN
      Mods/vcmi/Sprites/stackWindow/level-1.png
  24. BIN
      Mods/vcmi/Sprites/stackWindow/level-10.png
  25. BIN
      Mods/vcmi/Sprites/stackWindow/level-2.png
  26. BIN
      Mods/vcmi/Sprites/stackWindow/level-3.png
  27. BIN
      Mods/vcmi/Sprites/stackWindow/level-4.png
  28. BIN
      Mods/vcmi/Sprites/stackWindow/level-5.png
  29. BIN
      Mods/vcmi/Sprites/stackWindow/level-6.png
  30. BIN
      Mods/vcmi/Sprites/stackWindow/level-7.png
  31. BIN
      Mods/vcmi/Sprites/stackWindow/level-8.png
  32. BIN
      Mods/vcmi/Sprites/stackWindow/level-9.png
  33. 17 0
      Mods/vcmi/Sprites/stackWindow/levels.json
  34. 7 0
      Mods/vcmi/Sprites/stackWindow/switchModeIcons.json
  35. BIN
      Mods/vcmi/Sprites/stackWindow/upgrade-normal.png
  36. BIN
      Mods/vcmi/Sprites/stackWindow/upgrade-pressed.png
  37. 8 0
      Mods/vcmi/Sprites/stackWindow/upgradeButton.json
  38. 2 1
      README.md
  39. 0 935
      client/CCreatureWindow.cpp
  40. 0 163
      client/CCreatureWindow.h
  41. 3 2
      client/CMT.cpp
  42. 33 16
      client/CMakeLists.txt
  43. 6 4
      client/CMessage.cpp
  44. 0 1
      client/CMusicHandler.h
  45. 20 13
      client/CPlayerInterface.cpp
  46. 5 4
      client/CPlayerInterface.h
  47. 138 138
      client/CPreGame.cpp
  48. 30 24
      client/CPreGame.h
  49. 3 0
      client/Client.cpp
  50. 5 1
      client/Client.h
  51. 0 6263
      client/GUIClasses.cpp
  52. 0 1202
      client/GUIClasses.h
  53. 0 1
      client/Graphics.cpp
  54. 3 1
      client/NetPacksClient.cpp
  55. 1 0
      client/battle/CBattleAnimations.cpp
  56. 1 1
      client/battle/CBattleAnimations.h
  57. 37 39
      client/battle/CBattleInterface.cpp
  58. 7 7
      client/battle/CBattleInterface.h
  59. 41 39
      client/battle/CBattleInterfaceClasses.cpp
  60. 8 8
      client/battle/CBattleInterfaceClasses.h
  61. 4 4
      client/battle/CCreatureAnimation.cpp
  62. 2 1
      client/battle/CCreatureAnimation.h
  63. 10 6
      client/gui/CAnimation.cpp
  64. 3 161
      client/gui/CAnimation.h
  65. 3 1
      client/gui/CCursorHandler.cpp
  66. 3 1
      client/gui/CGuiHandler.cpp
  67. 1 1
      client/gui/CGuiHandler.h
  68. 19 0
      client/gui/CIntObject.cpp
  69. 6 4
      client/gui/CIntObject.h
  70. 0 2011
      client/gui/CIntObjectClasses.cpp
  71. 0 557
      client/gui/CIntObjectClasses.h
  72. 6 1
      client/gui/Geometries.cpp
  73. 3 5
      client/gui/Geometries.h
  74. 0 1
      client/gui/SDL_Extensions.cpp
  75. 8 6
      client/gui/SDL_Extensions.h
  76. 289 23
      client/widgets/AdventureMapClasses.cpp
  77. 32 4
      client/widgets/AdventureMapClasses.h
  78. 740 0
      client/widgets/Buttons.cpp
  79. 261 0
      client/widgets/Buttons.h
  80. 1017 0
      client/widgets/CArtifactHolder.cpp
  81. 145 0
      client/widgets/CArtifactHolder.h
  82. 453 0
      client/widgets/CComponent.cpp
  83. 112 0
      client/widgets/CComponent.h
  84. 512 0
      client/widgets/CGarrisonInt.cpp
  85. 122 0
      client/widgets/CGarrisonInt.h
  86. 533 0
      client/widgets/Images.cpp
  87. 226 0
      client/widgets/Images.h
  88. 442 0
      client/widgets/MiscWidgets.cpp
  89. 150 0
      client/widgets/MiscWidgets.h
  90. 234 0
      client/widgets/ObjectLists.cpp
  91. 111 0
      client/widgets/ObjectLists.h
  92. 642 0
      client/widgets/TextControls.cpp
  93. 189 0
      client/widgets/TextControls.h
  94. 128 83
      client/windows/CAdvmapInterface.cpp
  95. 15 16
      client/windows/CAdvmapInterface.h
  96. 73 26
      client/windows/CCastleInterface.cpp
  97. 11 12
      client/windows/CCastleInterface.h
  98. 871 0
      client/windows/CCreatureWindow.cpp
  99. 120 0
      client/windows/CCreatureWindow.h
  100. 60 33
      client/windows/CHeroWindow.cpp

BIN
Mods/vcmi/Data/StackQueueLarge.png


BIN
Mods/vcmi/Data/StackQueueSmall.png


BIN
Mods/vcmi/Data/questDialog.png


BIN
Mods/vcmi/Data/stackWindow/bonus-effects.png


BIN
Mods/vcmi/Data/stackWindow/button-panel.png


BIN
Mods/vcmi/Data/stackWindow/commander-abilities.png


BIN
Mods/vcmi/Data/stackWindow/commander-bg.png


BIN
Mods/vcmi/Data/stackWindow/icons.png


BIN
Mods/vcmi/Data/stackWindow/info-panel-0.png


BIN
Mods/vcmi/Data/stackWindow/info-panel-1.png


BIN
Mods/vcmi/Data/stackWindow/info-panel-2.png


BIN
Mods/vcmi/Data/stackWindow/spell-effects.png


+ 8 - 0
Mods/vcmi/Sprites/buttons/commander.json

@@ -0,0 +1,8 @@
+{
+	"basepath" : "buttons/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "commanderNormal.png"},
+		{ "frame" : 1, "file" : "commanderPressed.png"}
+	]
+}

BIN
Mods/vcmi/Sprites/buttons/commanderNormal.png


BIN
Mods/vcmi/Sprites/buttons/commanderPressed.png


+ 8 - 0
Mods/vcmi/Sprites/buttons/resolution.json

@@ -0,0 +1,8 @@
+{
+	"basepath" : "buttons/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "resolutionNormal.png"},
+		{ "frame" : 1, "file" : "resolutionPressed.png"}
+	]
+}

BIN
Mods/vcmi/Sprites/buttons/resolutionNormal.png


BIN
Mods/vcmi/Sprites/buttons/resolutionPressed.png


BIN
Mods/vcmi/Sprites/stackWindow/cancel-normal.png


BIN
Mods/vcmi/Sprites/stackWindow/cancel-pressed.png


+ 8 - 0
Mods/vcmi/Sprites/stackWindow/cancelButton.json

@@ -0,0 +1,8 @@
+{
+	"basepath" : "stackWindow/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "cancel-normal.png"},
+		{ "frame" : 1, "file" : "cancel-pressed.png"}
+	]
+}

BIN
Mods/vcmi/Sprites/stackWindow/level-0.png


BIN
Mods/vcmi/Sprites/stackWindow/level-1.png


BIN
Mods/vcmi/Sprites/stackWindow/level-10.png


BIN
Mods/vcmi/Sprites/stackWindow/level-2.png


BIN
Mods/vcmi/Sprites/stackWindow/level-3.png


BIN
Mods/vcmi/Sprites/stackWindow/level-4.png


BIN
Mods/vcmi/Sprites/stackWindow/level-5.png


BIN
Mods/vcmi/Sprites/stackWindow/level-6.png


BIN
Mods/vcmi/Sprites/stackWindow/level-7.png


BIN
Mods/vcmi/Sprites/stackWindow/level-8.png


BIN
Mods/vcmi/Sprites/stackWindow/level-9.png


+ 17 - 0
Mods/vcmi/Sprites/stackWindow/levels.json

@@ -0,0 +1,17 @@
+{
+	"basepath" : "stackWindow/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "level-0.png"},
+		{ "frame" : 1, "file" : "level-1.png"},
+		{ "frame" : 2, "file" : "level-2.png"},
+		{ "frame" : 3, "file" : "level-3.png"},
+		{ "frame" : 4, "file" : "level-4.png"},
+		{ "frame" : 5, "file" : "level-5.png"},
+		{ "frame" : 6, "file" : "level-6.png"},
+		{ "frame" : 7, "file" : "level-7.png"},
+		{ "frame" : 8, "file" : "level-8.png"},
+		{ "frame" : 9, "file" : "level-9.png"},
+		{ "frame" : 10,"file" : "level-10.png"}
+	]
+}

+ 7 - 0
Mods/vcmi/Sprites/stackWindow/switchModeIcons.json

@@ -0,0 +1,7 @@
+{
+	"images" :
+	[
+		{ "frame" : 0, "file" : "SECSK32:69"},
+		{ "frame" : 1, "file" : "SECSK32:28"}
+	]
+}

BIN
Mods/vcmi/Sprites/stackWindow/upgrade-normal.png


BIN
Mods/vcmi/Sprites/stackWindow/upgrade-pressed.png


+ 8 - 0
Mods/vcmi/Sprites/stackWindow/upgradeButton.json

@@ -0,0 +1,8 @@
+{
+	"basepath" : "stackWindow/",
+	"images" :
+	[
+		{ "frame" : 0, "file" : "upgrade-normal.png"},
+		{ "frame" : 1, "file" : "upgrade-pressed.png"}
+	]
+}

+ 2 - 1
README.md

@@ -15,6 +15,7 @@ For building from source see project wiki at http://wiki.vcmi.eu/
 
 ## Copyright and license
 
-VCMI Project is released under GPL version 2 or later
+VCMI Project source code is licensed under GPL version 2 or later.
+VCMI Project assets are licensed under CC-BY-SA 4.0. Assets sources and information about contributors are available under following link: [https://github.com/vcmi/vcmi-assets]
 
 Copyright (C) 2007-2014  VCMI Team (check AUTHORS file for the contributors list)

+ 0 - 935
client/CCreatureWindow.cpp

@@ -1,935 +0,0 @@
-#include "StdInc.h"
-#include "CCreatureWindow.h"
-
-#include "../lib/CCreatureSet.h"
-#include "CGameInfo.h"
-#include "../lib/CGeneralTextHandler.h"
-#include "../lib/BattleState.h"
-#include "../CCallback.h"
-
-#include <SDL.h>
-#include "gui/SDL_Extensions.h"
-#include "CBitmapHandler.h"
-#include "CDefHandler.h"
-#include "Graphics.h"
-#include "CPlayerInterface.h"
-#include "../lib/CConfigHandler.h"
-#include "CAnimation.h"
-
-#include "../lib/CGameState.h"
-#include "../lib/BattleState.h"
-#include "../lib/CSpellHandler.h"
-#include "../lib/CArtHandler.h"
-#include "../lib/NetPacksBase.h" //ArtifactLocation
-#include "../lib/CModHandler.h"
-#include "../lib/IBonusTypeHandler.h"
-
-#include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
-
-using namespace CSDL_Ext;
-
-class CCreatureArtifactInstance;
-class CSelectableSkill;
-
-/*
- * CCreatureWindow.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
- *
- */
-
-CCreatureWindow::CCreatureWindow (const CStack &stack, CreWinType Type):
-    CWindowObject(PLAYER_COLORED | (Type == OTHER ? RCLICK_POPUP : 0 ) ),
-    type(Type)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if (stack.base)
-		init(stack.base, &stack, dynamic_cast<const CGHeroInstance*>(stack.base->armyObj));
-	else
-	{
-		auto   s = new CStackInstance(stack.type, 1); //TODO: war machines and summons should be regular stacks
-		init(s, &stack, nullptr);
-		delete s;
-	}
-}
-
-CCreatureWindow::CCreatureWindow (const CStackInstance &stack, CreWinType Type):
-    CWindowObject(PLAYER_COLORED | (Type == OTHER ? RCLICK_POPUP : 0 ) ),
-    type(Type)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	init(&stack, &stack, dynamic_cast<const CGHeroInstance*>(stack.armyObj));
-}
-
-CCreatureWindow::CCreatureWindow(CreatureID Cid, CreWinType Type, int creatureCount):
-   CWindowObject(PLAYER_COLORED | (Type == OTHER ? RCLICK_POPUP : 0 ) ),
-    type(Type)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	auto   stack = new CStackInstance(Cid, creatureCount); //TODO: simplify?
-	init(stack, CGI->creh->creatures[Cid], nullptr);
-	delete stack;
-}
-
-CCreatureWindow::CCreatureWindow(const CStackInstance &st, CreWinType Type, std::function<void()> Upg, std::function<void()> Dsm, UpgradeInfo *ui):
-    CWindowObject(PLAYER_COLORED | (Type == OTHER ? RCLICK_POPUP : 0 ) ),
-    type(Type),
-    dismiss(nullptr),
-    upgrade(nullptr),
-    ok(nullptr),
-    dsm(Dsm)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	init(&st, &st,dynamic_cast<const CGHeroInstance*>(st.armyObj));
-
-	//print abilities text - if r-click popup
-	if(type)
-	{
-		if(Upg && ui)
-		{
-			TResources upgradeCost = ui->cost[0] * st.count;
-			for(TResources::nziterator i(upgradeCost); i.valid(); i++)
-			{
-				BLOCK_CAPTURING;
-				upgResCost.push_back(new CComponent(CComponent::resource, i->resType, i->resVal));
-			}
-
-			if(LOCPLINT->cb->getResourceAmount().canAfford(upgradeCost))
-			{
-				CFunctionList<void()> fs;
-				fs += Upg;
-				fs += std::bind(&CCreatureWindow::close,this);
-				CFunctionList<void()> cfl;
-				cfl += std::bind(&CPlayerInterface::showYesNoDialog,
-				                   LOCPLINT, CGI->generaltexth->allTexts[207], fs, nullptr, false, upgResCost);
-				upgrade = new CAdventureMapButton("",CGI->generaltexth->zelp[446].second,cfl,385, 148,"IVIEWCR.DEF",SDLK_u);
-			}
-			else
-			{
-				upgrade = new CAdventureMapButton("",CGI->generaltexth->zelp[446].second,std::function<void()>(),385, 148,"IVIEWCR.DEF");
-				upgrade->callback.funcs.clear();
-				upgrade->setOffset(2);
-			}
-
-		}
-		if(Dsm)
-		{
-			CFunctionList<void()> fs[2];
-			//on dismiss confirmed
-			fs[0] += Dsm; //dismiss
-			fs[0] += std::bind(&CCreatureWindow::close,this);//close this window
-			CFunctionList<void()> cfl;
-			cfl = std::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],fs[0],fs[1],false,std::vector<CComponent*>());
-			dismiss = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second,cfl,333, 148,"IVIEWCR2.DEF",SDLK_d);
-		}
-	}
-}
-
-CCreatureWindow::CCreatureWindow (const CCommanderInstance * Commander, const CStack * stack):
-    CWindowObject(PLAYER_COLORED),
-	commander (Commander)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if (stack)
-	{
-		type = COMMANDER_BATTLE;
-		init(commander, stack, dynamic_cast<const CGHeroInstance*>(commander->armyObj));
-	}
-	else
-	{
-		type = COMMANDER;
-		init(commander, commander, dynamic_cast<const CGHeroInstance*>(commander->armyObj));
-	}
-
-	std::function<void()> Dsm;
-	CFunctionList<void()> fs[2];
-	//on dismiss confirmed
-	fs[0] += Dsm; //dismiss
-	fs[0] += std::bind(&CCreatureWindow::close,this);//close this window
-	CFunctionList<void()> cfl;
-	cfl = std::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],fs[0],fs[1],false,std::vector<CComponent*>());
-	if (type < COMMANDER_LEVEL_UP) //can dismiss only in regular window
-		dismiss = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second, cfl, 333, 148,"IVIEWCR2.DEF", SDLK_d);
-}
-
-CCreatureWindow::CCreatureWindow (std::vector<ui32> &skills, const CCommanderInstance * Commander, std::function<void(ui32)> callback):
-    CWindowObject(PLAYER_COLORED),
-    type(COMMANDER_LEVEL_UP),
-	commander (Commander),
-	selectedOption (0), //choose something before drawing
-	upgradeOptions(skills), //copy skills to choose from
-	levelUp (callback)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	init(commander, commander, dynamic_cast<const CGHeroInstance*>(commander->armyObj));
-
-	std::function<void()> Dsm;
-	CFunctionList<void()> fs[2];
-	//on dismiss confirmed
-	fs[0] += Dsm; //dismiss
-	fs[0] += std::bind(&CCreatureWindow::close,this);//close this window
-	CFunctionList<void()> cfl;
-	cfl = std::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],fs[0],fs[1],false,std::vector<CComponent*>());
-	if (type < COMMANDER_LEVEL_UP) //can dismiss only in regular window
-		dismiss = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second, cfl, 333, 148,"IVIEWCR2.DEF", SDLK_d);
-}
-
-void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *StackNode, const CGHeroInstance *HeroOwner)
-{
-	creatureArtifact = nullptr; //may be set later
-	artifactImage = nullptr;
-	spellEffectsPics = nullptr;
-	stack = Stack;
-	c = stack->type;
-	if(!StackNode)
-		stackNode = c;
-	else
-		stackNode = StackNode;
-	const CStack *battleStack = dynamic_cast<const CStack*>(stackNode); //only during battle
-	heroOwner = HeroOwner;
-
-	if (battleStack)
-		count = boost::lexical_cast<std::string>(battleStack->count);
-	else if (Stack->count)
-		count = boost::lexical_cast<std::string>(Stack->count);
-
-	if (type < COMMANDER)
-		commander = nullptr;
-
-	bool creArt = false;
-	displayedArtifact = ArtifactPosition::CREATURE_SLOT; // 0
-
-	//Basic graphics - need to calculate size
-
-	int commanderOffset = 0;
-	if (type >= COMMANDER)
-		commanderOffset = 74;
-
-	if (commander) //secondary skills
-	{
-		creArt = true;
-		for (int i = ECommander::ATTACK; i <= ECommander::SPELL_POWER; ++i)
-		{
-			if (commander->secondarySkills[i] || vstd::contains(upgradeOptions, i))
-			{
-				std::string file = skillToFile(i);
-
-				skillPictures.push_back(new CPicture(file, 0,0));
-			}
-		}
-
-		if (type == COMMANDER_LEVEL_UP)
-		{
-			for (auto option : upgradeOptions)
-			{
-				ui32 index = selectableSkills.size();
-				auto   selectableSkill = new CSelectableSkill();
-				selectableSkill->callback = std::bind(&CCreatureWindow::selectSkill, this, index);
-
-				if (option < 100)
-				{
-					selectableSkill->pos = skillPictures[index]->pos; //resize
-					selectableSkills.push_back (selectableSkill);
-				}
-				else
-				{
-					selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
-					const Bonus *b = CGI->creh->skillRequirements[option-100].first;
-					bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(b, false), stack->bonusToString(b, true), stack->bonusToGraphics(b)));
-					selectableBonuses.push_back (selectableSkill); //insert these before other bonuses
-				}
-			}
-		}
-	}
-
-	BonusList bl, blTemp;
-	blTemp = (*(stackNode->getBonuses(Selector::durationType(Bonus::PERMANENT).And(Selector::anyRange()))));
-
-	while (blTemp.size())
-	{
-		Bonus * b = blTemp.front();
-
-		bl.push_back (new Bonus(*b));
-		bl.back()->val = blTemp.valOfBonuses(Selector::typeSubtype(b->type, b->subtype)); //merge multiple bonuses into one
-		blTemp.remove_if (Selector::typeSubtype(b->type, b->subtype)); //remove used bonuses
-	}
-
-	std::string text, img;
-	for(Bonus* b : bl)
-	{
-		text = stack->bonusToString(b, false);
-		img = stack->bonusToGraphics(b);
-		if (text.size() || img.size()) //if it's possible to give any description or image for this kind of bonus
-		{
-			bonusItems.push_back (new CBonusItem(genRect(0, 0, 251, 57), text, stack->bonusToString(b, true), img));
-		}
-	}
-
-
-	//handle Magic resistance separately :/
-	const IBonusBearer *temp = stack;
-
-	if (battleStack)
-	{
-		temp = battleStack;
-	}
-
-	int magicResistance = temp->magicResistance();
-
-	if (magicResistance)
-	{
-		Bonus b;
-		b.type = Bonus::MAGIC_RESISTANCE;
-
-		text = VLC->getBth()->bonusToString(&b,temp,false);
-		const std::string description = VLC->getBth()->bonusToString(&b,temp,true);
-		bonusItems.push_back (new CBonusItem(genRect(0, 0, 251, 57), text, description, stack->bonusToGraphics(&b)));
-	}
-
-	bonusRows = std::min ((int)((bonusItems.size() + 1) / 2), (screen->h - 230) / 60);
-	if (type >= COMMANDER)
-		vstd::amin(bonusRows, 3);
-	else
-		vstd::amin(bonusRows, 4);
-	vstd::amax(bonusRows, 1);
-
-	if (type >= COMMANDER)
-	{
-		setBackground("CommWin" + boost::lexical_cast<std::string>(bonusRows) + ".pcx");
-		for (int i = 0; i < skillPictures.size(); ++i)
-		{
-			skillPictures[i]->moveTo (Point (pos.x + 37 + i * 84, pos.y + 224));
-		}
-		for (int i = 0; i < selectableSkills.size(); ++i)
-		{
-			if (upgradeOptions[i] < skillPictures.size()) // it's secondary skill
-			{
-				selectableSkills[i]->pos = skillPictures[upgradeOptions[i]]->pos; //dirty workaround
-			}
-			else
-				break;
-		}
-		//print commander level
-		new CLabel(488, 62, FONT_MEDIUM, CENTER, Colors::YELLOW,
-		           boost::lexical_cast<std::string>((ui16)(commander->level)));
-
-		new CLabel(488, 82, FONT_SMALL, CENTER, Colors::WHITE,
-		           boost::lexical_cast<std::string>(stack->experience));
-	}
-	else
-		setBackground("CreWin" + boost::lexical_cast<std::string>(bonusRows) + ".pcx"); //1 to 4 rows for now
-
-	//Buttons
-	ok = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second, std::bind(&CCreatureWindow::close,this), 489, 148, "hsbtns.def", SDLK_RETURN);
-	ok->assignedKeys.insert(SDLK_ESCAPE);
-
-	if (type <= BATTLE) //in battle or info window
-	{
-		upgrade = nullptr;
-		dismiss = nullptr;
-	}
-	anim = new CCreaturePic(22, 48, c);
-
-	//Stats
-	morale = new MoraleLuckBox(true, genRect(42, 42, 335, 100));
-	morale->set(stackNode);
-	luck = new MoraleLuckBox(false, genRect(42, 42, 387, 100));
-	luck->set(stackNode);
-
-	new CAnimImage("PSKIL42", 4, 0, 387, 51); //exp icon - Print it always?
-	if (type) //not in fort window
-	{
-		if (CGI->modh->modules.STACK_EXP && type < COMMANDER)
-		{
-			int rank = std::min(stack->getExpRank(), 10); //hopefully nobody adds more
-			new CLabel(488, 82, FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast<std::string>(stack->experience));
-			new CLabel(488, 62, FONT_MEDIUM, CENTER, Colors::YELLOW,
-			           CGI->generaltexth->zcrexp[rank] + " [" + boost::lexical_cast<std::string>(rank) + "]");
-
-			if (type > BATTLE) //we need it only on adv. map
-			{
-				int tier = stack->type->level;
-				if (!vstd::iswithin(tier, 1, 7))
-					tier = 0;
-				int number;
-				std::string expText = CGI->generaltexth->zcrexp[324];
-				boost::replace_first (expText, "%s", c->namePl);
-				boost::replace_first (expText, "%s", CGI->generaltexth->zcrexp[rank]);
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(rank));
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(stack->experience));
-				number = CGI->creh->expRanks[tier][rank] - stack->experience;
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number));
-
-				number = CGI->creh->maxExpPerBattle[tier]; //percent
-				boost::replace_first (expText, "%i%", boost::lexical_cast<std::string>(number));
-				number *= CGI->creh->expRanks[tier].back() / 100; //actual amount
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number));
-
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(stack->count)); //Number of Creatures in stack
-
-				int expmin = std::max(CGI->creh->expRanks[tier][std::max(rank-1, 0)], (ui32)1);
-				number = (stack->count * (stack->experience - expmin)) / expmin; //Maximum New Recruits without losing current Rank
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //TODO
-
-				boost::replace_first (expText, "%.2f", boost::lexical_cast<std::string>(1)); //TODO Experience Multiplier
-				number = CGI->creh->expAfterUpgrade;
-				boost::replace_first (expText, "%.2f", boost::lexical_cast<std::string>(number) + "%"); //Upgrade Multiplier
-
-				expmin = CGI->creh->expRanks[tier][9];
-				int expmax = CGI->creh->expRanks[tier][10];
-				number = expmax - expmin;
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //Experience after Rank 10
-				number = (stack->count * (expmax - expmin)) / expmin;
-				boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //Maximum New Recruits to remain at Rank 10 if at Maximum Experience
-
-				expArea = new LRClickableAreaWTextComp(Rect(334, 49, 160, 44),CComponent::experience);
-				expArea->text = expText;
-				expArea->bonusValue = 0; //TDO: some specific value or no number at all
-			}
-		}
-
-		if (CGI->modh->modules.STACK_ARTIFACT)
-		{
-			creArt = true;
-		}
-	}
-	if (creArt) //stack or commander artifacts
-	{
-		setArt (stack->getArt(ArtifactPosition::CREATURE_SLOT));
-		if (type > BATTLE && type < COMMANDER_BATTLE) //artifact buttons inactive in battle
-		{
-			//TODO: disable buttons if no artifact is equipped
-			leftArtRoll = new CAdventureMapButton(std::string(), std::string(), std::bind (&CCreatureWindow::scrollArt, this, -1), 437, 98, "hsbtns3.def", SDLK_LEFT);
-			rightArtRoll = new CAdventureMapButton(std::string(), std::string(), std::bind (&CCreatureWindow::scrollArt, this, +1), 516, 98, "hsbtns5.def", SDLK_RIGHT);
-			if (heroOwner)
-				passArtToHero = new CAdventureMapButton(std::string(), std::string(), std::bind (&CCreatureWindow::passArtifactToHero, this), 437, 148, "OVBUTN1.DEF", SDLK_HOME);
-		}
-	}
-	
-	if (battleStack) //only during battle
-	{
-		spellEffectsPics = new CAnimation("SpellInt.def");
-
-		//spell effects
-		int printed=0; //how many effect pics have been printed
-		std::vector<si32> spells = battleStack->activeSpells();
-		for(si32 effect : spells)
-		{
-			const si32 imageIndex = effect+1; //there is "null" frame with index 0 in SpellInt.def
-			std::string spellText;
-			spellEffectsPics->load(imageIndex);
-			IImage * effectIcon = spellEffectsPics->getImage(imageIndex,0,false); //todo: better way to determine presence of icon
-			spellEffectsPics->unload(imageIndex);
-			if (effectIcon != nullptr) //not all effects have graphics (for eg. Acid Breath)
-			{
-				spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
-				boost::replace_first (spellText, "%s", CGI->spellh->objects[effect]->name);
-				int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
-				boost::replace_first (spellText, "%d", boost::lexical_cast<std::string>(duration));
-
-				new CAnimImage("SpellInt.def", imageIndex, 0, 20 + 52 * printed, 184);
-				spellEffects.push_back(new LRClickableAreaWText(Rect(20 + 52 * printed, 184, 50, 38), spellText, spellText));
-				if (++printed >= 10) //we can fit only 10 effects
-					break;
-			}
-		}
-		
-		//print current health
-		printLine (5, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
-	}
-
-	if (bonusItems.size() > (bonusRows << 1)) //only after graphics are created
-	{
-		slider = new CSlider(528, 231 + commanderOffset, bonusRows*60, std::bind (&CCreatureWindow::sliderMoved, this, _1),
-		bonusRows, (bonusItems.size() + 1) >> 1, 0, false, 0);
-	}
-	else //slider automatically places bonus Items
-		recreateSkillList (0);
-
-	showAll(screen2);
-
-	//AUIDAT.DEF
-}
-
-void CCreatureWindow::printLine(int nr, const std::string &text, int baseVal, int val/*=-1*/, bool range/*=false*/)
-{
-	new CLabel(162, 48 + nr*19, FONT_SMALL, TOPLEFT, Colors::WHITE, text);
-
-	std::string hlp;
-	if(range && baseVal != val)
-		hlp = boost::str(boost::format("%d - %d") % baseVal % val);
-	else if(baseVal != val && val>=0)
-		hlp = boost::str(boost::format("%d (%d)") % baseVal % val);
-	else
-		hlp = boost::lexical_cast<std::string>(baseVal);
-
-	new CLabel(325, 64 + nr*19, FONT_SMALL, BOTTOMRIGHT, Colors::WHITE, hlp);
-}
-
-void CCreatureWindow::recreateSkillList(int Pos)
-{
-	int commanderOffset = 0;
-	if (type >= COMMANDER)
-		commanderOffset = 74;
-
-	int n = 0, i = 0, j = 0;
-	int numSkills = std::min ((bonusRows + Pos) << 1, (int)bonusItems.size());
-	for (n = 0; n < Pos << 1; ++n)
-	{
-		bonusItems[n]->visible = false;
-		if (n < selectableBonuses.size())
-			selectableBonuses[n]->deactivate(); //we assume that bonuses are at front of the list
-	}
-	for (n = Pos << 1; n < numSkills; ++n)
-	{
-		int offsetx = 257*j - (bonusRows == 4 ? 1 : 0);
-		int offsety = 60*i + (bonusRows > 1 ? 1 : 0) + commanderOffset; //lack of precision :/
-
-		bonusItems[n]->moveTo (Point(pos.x + offsetx + 10, pos.y + offsety + 230), true);
-		bonusItems[n]->visible = true;
-		if (n < selectableBonuses.size())
-		{
-			selectableBonuses[n]->moveTo (Point(bonusItems[n]->pos.x + 12, bonusItems[n]->pos.y + 2)); //for some reason bonusItems have dimensions 0?
-			//selectableBonuses[n]->pos = bonusItems[n]->bonusGraphics->pos;
-			selectableBonuses[n]->activate();
-		}
-
-		if (++j > 1) //next line
-		{
-			++i;
-			j = 0;
-		}
-	}
-	for (n = numSkills; n < bonusItems.size(); ++n)
-	{
-		bonusItems[n]->visible = false;
-		if (n < selectableBonuses.size())
-			selectableBonuses[n]->deactivate();
-	}
-}
-
-void CCreatureWindow::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-
-	printAtMiddleLoc((type >= COMMANDER ? c->nameSing : c->namePl), 180, 30, FONT_SMALL, Colors::YELLOW, to); //creature name
-
-	printLine(0, CGI->generaltexth->primarySkillNames[0], c->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), stackNode->Attack());
-	printLine(1, CGI->generaltexth->primarySkillNames[1], c->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), stackNode->Defense());
-
-	if (stackNode->valOfBonuses(Bonus::SHOTS) && stackNode->hasBonusOfType(Bonus::SHOOTER))
-	{//only for shooting units - important with wog exp shooters
-		if (type == BATTLE)
-			printLine(2, CGI->generaltexth->allTexts[198], stackNode->valOfBonuses(Bonus::SHOTS), dynamic_cast<const CStack*>(stackNode)->shots);
-		else
-			printLine(2, CGI->generaltexth->allTexts[198], stackNode->valOfBonuses(Bonus::SHOTS));
-	}
-	if (stackNode->valOfBonuses(Bonus::CASTS))
-	{
-		printAtMiddleLoc(CGI->generaltexth->allTexts[399], 356, 62, FONT_SMALL, Colors::WHITE, to);
-		std::string casts;
-		if (type == BATTLE)
-			casts = boost::lexical_cast<std::string>((ui16)dynamic_cast<const CStack*>(stackNode)->casts); //ui8 is converted to char :(
-		else
-			casts = boost::lexical_cast<std::string>(stackNode->valOfBonuses(Bonus::CASTS));
-		printAtMiddleLoc(casts, 356, 82, FONT_SMALL, Colors::WHITE, to);
-	}
-
-	//TODO
-	int dmgMultiply = 1;
-	if(heroOwner && stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
-		dmgMultiply += heroOwner->Attack();
-
-	printLine(3, CGI->generaltexth->allTexts[199], stackNode->getMinDamage() * dmgMultiply, stackNode->getMaxDamage() * dmgMultiply, true);
-	printLine(4, CGI->generaltexth->allTexts[388], c->valOfBonuses(Bonus::STACK_HEALTH), stackNode->valOfBonuses(Bonus::STACK_HEALTH));
-	printLine(6, CGI->generaltexth->zelp[441].first, c->Speed(), stackNode->Speed());
-
-	for(CBonusItem* b : bonusItems)
-		b->showAll (to);
-
-	for(auto s : selectableSkills)
-		s->showAll (to);
-
-	for (int i = 0; i < skillPictures.size(); i++)
-	{
-		skillPictures[i]->bg = BitmapHandler::loadBitmap (skillToFile(i));
-		skillPictures[i]->showAll (to);
-	}
-
-	if (upgradeOptions.size() && (type == COMMANDER_LEVEL_UP && upgradeOptions[selectedOption] >= 100)) //add frame to selected skill
-	{
-		int index = selectedOption - selectableSkills.size(); //this is screwed
-		CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
-	}
-}
-
-void CCreatureWindow::show(SDL_Surface * to)
-{
-	CIntObject::show(to);
-	if (!count.empty()) //army stack
-		graphics->fonts[FONT_TIMES]->renderTextRight(to, count, Colors::WHITE, Point(pos.x + 114, pos.y + 174));
-}
-
-
-void CCreatureWindow::close()
-{
-	if (upgradeOptions.size()) //a skill for commander was chosen
-		levelUp (selectedOption); //callback value is vector index
-
-	GH.popIntTotally(this);
-}
-
-void CCreatureWindow::sliderMoved(int newpos)
-{
-	recreateSkillList(newpos); //move components
-	redraw();
-}
-
-std::string CCreatureWindow::skillToFile (int skill)
-{
-		std::string file = "zvs/Lib1.res/_";
-		switch (skill)
-		{
-			case ECommander::ATTACK:
-				file += "AT";
-				break;
-			case ECommander::DEFENSE:
-				file += "DF";
-				break;
-			case ECommander::HEALTH:
-				file += "HP";
-				break;
-			case ECommander::DAMAGE:
-				file += "DM";
-				break;
-			case ECommander::SPEED:
-				file += "SP";
-				break;
-			case ECommander::SPELL_POWER:
-				file += "MP";
-				break;
-		}
-		std::string sufix = boost::lexical_cast<std::string>((int)(commander->secondarySkills[skill])); //casting ui8 causes ascii char conversion
-		if (type == COMMANDER_LEVEL_UP)
-		{
-			if (upgradeOptions.size() && upgradeOptions[selectedOption] == skill)//that one specific skill is selected
-				sufix += "="; //level-up highlight
-			else if (!vstd::contains(upgradeOptions, skill))
-				sufix = "no"; //not available - no number
-		}
-		file += sufix += ".bmp";
-
-		return file;
-}
-
-void CCreatureWindow::setArt(const CArtifactInstance *art)
-{
-	creatureArtifact = art;
-	if (creatureArtifact)
-	{
-		if (artifactImage == nullptr)
-			artifactImage = new CAnimImage("ARTIFACT", creatureArtifact->artType->iconIndex, 0, 466, 100);
-		else
-			artifactImage->setFrame(creatureArtifact->artType->iconIndex);
-	}
-	else
-		artifactImage = nullptr;
-
-	redraw();
-}
-
-void CCreatureWindow::scrollArt(int dir)
-{
-	//TODO: get next artifact
-	int size = stack->artifactsWorn.size();
-	displayedArtifact  =  size ? static_cast<ArtifactPosition>((displayedArtifact + dir) % size)
-	                           : static_cast<ArtifactPosition>(ArtifactPosition::CREATURE_SLOT);
-	setArt (stack->getArt(displayedArtifact));
-}
-
-void CCreatureWindow::passArtifactToHero()
-{
-	const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(stack->armyObj);
-	if (h && creatureArtifact)
-	{
-		LOCPLINT->cb->swapArtifacts (ArtifactLocation (stack, displayedArtifact), ArtifactLocation(h, creatureArtifact->firstBackpackSlot(h)));
-	}
-	else
-        logGlobal->warnStream() << "Pass artifact to hero should be disabled, no hero or no artifact!";
-
-	//redraw is handled via CArtifactHolder interface
-}
-
-void CCreatureWindow::artifactRemoved (const ArtifactLocation &artLoc)
-{
-	//align artifacts to remove holes
-	for (auto al : stack->artifactsWorn)
-	{
-		ArtifactPosition freeSlot = al.second.artifact->firstAvailableSlot(stack);
-		if (freeSlot < al.first)
-			LOCPLINT->cb->swapArtifacts (ArtifactLocation(stack, al.first), ArtifactLocation(stack, freeSlot));
-	}
-	int size = stack->artifactsWorn.size();
-	displayedArtifact  =  size ? static_cast<ArtifactPosition>(displayedArtifact % size)
-	                           : static_cast<ArtifactPosition>(ArtifactPosition::CREATURE_SLOT); //0
-	setArt (stack->getArt(displayedArtifact));
-}
-void CCreatureWindow::artifactMoved (const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
-{
-	artifactRemoved (artLoc); //same code
-}
-
-void CCreatureWindow::selectSkill (ui32 which)
-{
-	selectedOption = which;
-	redraw();
-}
-
-CCreatureWindow::~CCreatureWindow()
-{
- 	for (auto & elem : upgResCost)
- 		delete elem;
-	bonusItems.clear();
-	
-	if(spellEffectsPics!=nullptr)
-		delete spellEffectsPics;
-}
-
-CBonusItem::CBonusItem()
-{
-
-}
-
-CBonusItem::CBonusItem(const Rect &Pos, const std::string &Name, const std::string &Description, const std::string &graphicsName)
-{
-	OBJ_CONSTRUCTION;
-	visible = false;
-
-	name = Name;
-	description = Description;
-	if (graphicsName.size())
-		bonusGraphics = new CPicture(graphicsName, 26, 232);
-	else
-		bonusGraphics = nullptr;
-
-	removeUsedEvents(ALL); //no actions atm
-}
-
-void CBonusItem::showAll (SDL_Surface * to)
-{
-	if (visible)
-	{
-		graphics->fonts[FONT_SMALL]->renderTextLeft(to, name, Colors::YELLOW, Point(pos.x + 72, pos.y + 6));
-		graphics->fonts[FONT_SMALL]->renderTextLeft(to, description, Colors::WHITE,  Point(pos.x + 72, pos.y + 30));
-		if (bonusGraphics && bonusGraphics->bg)
-			blitAtLoc(bonusGraphics->bg, 12, 2, to);
-	}
-}
-
-CBonusItem::~CBonusItem()
-{
-	//delete bonusGraphics; //automatic destruction
-}
-
-void CSelectableSkill::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-		callback();
-}
-
-void CCreInfoWindow::show(SDL_Surface * to)
-{
-	CIntObject::show(to);
-	creatureCount->showAll(to);
-}
-
-CCreInfoWindow::CCreInfoWindow(const CStackInstance &stack, bool LClicked, std::function<void()> upgradeFunc, std::function<void()> dismissFunc, UpgradeInfo *upgradeInfo):
-    CWindowObject(PLAYER_COLORED | (LClicked ? 0 : RCLICK_POPUP), "CRSTKPU")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	init(stack.type, &stack, dynamic_cast<const CGHeroInstance*>(stack.armyObj), stack.count, LClicked);
-
-	//additional buttons if opened with left click
-	if(LClicked)
-	{
-		std::function<void()> closeFunc = std::bind(&CCreInfoWindow::close,this);
-
-		if(upgradeFunc && upgradeInfo)
-		{
-			TResources upgradeCost = upgradeInfo->cost[0] * stack.count;
-			for(TResources::nziterator i(upgradeCost); i.valid(); i++)
-			{
-				BLOCK_CAPTURING;
-				upgResCost.push_back(new CComponent(CComponent::resource, i->resType, i->resVal));
-			}
-
-			CFunctionList<void()> onUpgrade;
-			onUpgrade += upgradeFunc;
-			onUpgrade += closeFunc;
-
-			std::function<void()> dialog = std::bind(&CPlayerInterface::showYesNoDialog,
-				LOCPLINT,
-				CGI->generaltexth->allTexts[207],
-				onUpgrade, 0, false,
-				std::ref(upgResCost));
-
-			upgrade = new CAdventureMapButton("", CGI->generaltexth->zelp[446].second, dialog, 76, 237, "IVIEWCR", SDLK_u);
-			upgrade->block(!LOCPLINT->cb->getResourceAmount().canAfford(upgradeCost));
-		}
-
-		if(dismissFunc)
-		{
-			CFunctionList<void()> onDismiss;
-			onDismiss += dismissFunc;
-			onDismiss += closeFunc;
-
-			std::function<void()> dialog = std::bind(&CPlayerInterface::showYesNoDialog,
-				LOCPLINT,
-				CGI->generaltexth->allTexts[12],
-				onDismiss, 0, true, std::vector<CComponent*>());
-
-			dismiss = new CAdventureMapButton("", CGI->generaltexth->zelp[445].second, dialog, 21, 237, "IVIEWCR2",SDLK_d);
-		}
-	}
-}
-
-CCreInfoWindow::CCreInfoWindow(int creatureID, bool LClicked, int creatureCount):
-    CWindowObject(PLAYER_COLORED | (LClicked ? 0 : RCLICK_POPUP), "CRSTKPU")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	const CCreature *creature = CGI->creh->creatures[creatureID];
-	init(creature, nullptr, nullptr, creatureCount, LClicked);
-}
-
-CCreInfoWindow::CCreInfoWindow(const CStack &stack, bool LClicked):
-    CWindowObject(PLAYER_COLORED | (LClicked ? 0 : RCLICK_POPUP), "CRSTKPU")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	init(stack.getCreature(), &stack, stack.getMyHero(), stack.count, LClicked);
-}
-
-CCreInfoWindow::~CCreInfoWindow()
-{
-	for(CComponent* object : upgResCost)
-		delete object;
-}
-
-void CCreInfoWindow::printLine(int position, const std::string &text, int baseVal, int val/*=-1*/, bool range/*=false*/)
-{
-	infoTexts[position].first = new CLabel(155, 48 + position*19, FONT_SMALL, TOPLEFT, Colors::WHITE, text);
-	std::string valueStr;
-
-	if(range && baseVal != val)
-		valueStr = boost::str(boost::format("%d - %d") % baseVal % val);
-
-	else if(baseVal != val && val>=0)
-		valueStr = boost::str(boost::format("%d (%d)") % baseVal % val);
-
-	else
-		valueStr = boost::lexical_cast<std::string>(baseVal);
-
-	infoTexts[position].second = new CLabel(276, 63 + position*19, FONT_SMALL, BOTTOMRIGHT, Colors::WHITE, valueStr);
-}
-
-void CCreInfoWindow::init(const CCreature *creature, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner, int count, bool LClicked)
-{
-	removeUsedEvents(ALL);
-	if (!LClicked)
-		addUsedEvents(RCLICK);
-
-	if(!stackNode)
-		stackNode = creature;
-
-	animation = new CCreaturePic(21, 48, creature);
-
-	std::string countStr = boost::lexical_cast<std::string>(count);
-	creatureCount = new CLabel(114, 174, FONT_TIMES, BOTTOMRIGHT, Colors::WHITE, countStr);
-
-	creatureName = new CLabel(149, 30, FONT_SMALL, CENTER, Colors::YELLOW, creature->namePl);
-
-	printLine(0, CGI->generaltexth->primarySkillNames[0], creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK));
-	printLine(1, CGI->generaltexth->primarySkillNames[1], creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), stackNode->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE));
-
-	if(stackNode->valOfBonuses(Bonus::SHOTS))
-		printLine(2, CGI->generaltexth->allTexts[198], stackNode->valOfBonuses(Bonus::SHOTS));
-
-	//TODO
-	int dmgMultiply = 1;
-	if(heroOwner && stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
-		dmgMultiply += heroOwner->Attack();
-
-	printLine(3, CGI->generaltexth->allTexts[199],   stackNode->getMinDamage() * dmgMultiply, stackNode->getMaxDamage() * dmgMultiply, true);
-	printLine(4, CGI->generaltexth->allTexts[388],   creature->valOfBonuses(Bonus::STACK_HEALTH), stackNode->valOfBonuses(Bonus::STACK_HEALTH));
-	printLine(6, CGI->generaltexth->zelp[441].first, creature->valOfBonuses(Bonus::STACKS_SPEED), stackNode->valOfBonuses(Bonus::STACKS_SPEED));
-
-	//setting morale
-	morale = new MoraleLuckBox(true, genRect(42, 42, 22, 186));
-	morale->set(stackNode);
-	//setting luck
-	luck = new MoraleLuckBox(false, genRect(42, 42, 75, 186));
-	luck->set(stackNode);
-
-	if(!LClicked)
-	{
-		abilityText = new CLabel(17, 231, FONT_SMALL, TOPLEFT, Colors::WHITE, creature->abilityText);
-	}
-	else
-	{
-		abilityText = nullptr;
-		ok = new CAdventureMapButton("", CGI->generaltexth->zelp[445].second,
-			std::bind(&CCreInfoWindow::close,this), 216, 237, "IOKAY.DEF", SDLK_RETURN);
-		ok->assignedKeys.insert(SDLK_ESCAPE);
-	}
-
-	//if we are displying window fo r stack in battle, there are several more things that we need to display
-	if(const CStack *battleStack = dynamic_cast<const CStack*>(stackNode))
-	{
-		//print at most 3 spell effects
-		std::vector<si32> spells = battleStack->activeSpells();
-		for (size_t i=0; i< std::min(spells.size(), size_t(3)); i++)
-			effects.push_back(new CAnimImage("SpellInt", spells[i]+1, 0, 127 + 52*i, 186));
-
-		//print current health
-		printLine(5, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
-	}
-}
-
-CIntObject * createCreWindow(
-	const CStack *s, bool lclick/* = false*/)
-{
-	auto c = dynamic_cast<const CCommanderInstance *>(s->base);
-	if (c)
-	{
-		return new CCreatureWindow (c, s);
-	}
-	else
-	{
-		if(settings["general"]["classicCreatureWindow"].Bool())
-			return new CCreInfoWindow(*s, lclick);
-		else
-			return new CCreatureWindow(*s, LOCPLINT->battleInt ? CCreatureWindow::BATTLE : CCreatureWindow::OTHER);
-	}
-}
-
-CIntObject * createCreWindow(CreatureID Cid, CCreatureWindow::CreWinType Type, int creatureCount)
-{
-	if(settings["general"]["classicCreatureWindow"].Bool())
-		return new CCreInfoWindow(Cid, Type, creatureCount);
-	else
-		return new CCreatureWindow(Cid, Type, creatureCount);
-}
-
-CIntObject * createCreWindow(const CStackInstance *s, CCreatureWindow::CreWinType type, std::function<void()> Upg, std::function<void()> Dsm, UpgradeInfo *ui)
-{
-	if(settings["general"]["classicCreatureWindow"].Bool())
-		return new CCreInfoWindow(*s, type==CCreatureWindow::HERO, Upg, Dsm, ui);
-	else
-		return  new CCreatureWindow(*s, type, Upg, Dsm, ui);
-}

+ 0 - 163
client/CCreatureWindow.h

@@ -1,163 +0,0 @@
-#pragma once
-
-#include "gui/CIntObject.h"
-#include "../lib/HeroBonus.h"
-#include "GUIClasses.h"
-
-/*
- * CCreatureWindow.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
- *
- */
-
-struct Bonus;
-class CCreature;
-class CStackInstance;
-class CCommanderInstance;
-class CStack;
-struct ArtifactLocation;
-class CCreatureArtifactInstance;
-class CAdventureMapButton;
-class CBonusItem;
-class CGHeroInstance;
-class CComponent;
-class LRClickableAreaWText;
-class MoraleLuckBox;
-class CAdventureMapButton;
-struct UpgradeInfo;
-class CPicture;
-class CCreaturePic;
-class LRClickableAreaWTextComp;
-class CSlider;
-class CLabel;
-class CAnimImage;
-class CSelectableSkill;
-
-// New creature window
-class CCreatureWindow : public CWindowObject, public CArtifactHolder
-{
-public:
-	enum CreWinType {OTHER = 0, BATTLE = 1, ARMY = 2, HERO = 3, COMMANDER = 4, COMMANDER_LEVEL_UP = 5, COMMANDER_BATTLE = 6}; // > 3 are opened permanently
-	//bool active; //TODO: comment me
-	CreWinType type;
-	int bonusRows; //height of skill window
-	ArtifactPosition displayedArtifact;
-
-	std::string count; //creature count in text format
-	const CCreature *c; //related creature
-	const CStackInstance *stack;
-	const CBonusSystemNode *stackNode;
-	const CCommanderInstance * commander;
-	const CGHeroInstance *heroOwner;
-	const CArtifactInstance *creatureArtifact; //currently worn artifact
-	std::vector<CComponent*> upgResCost; //cost of upgrade (if not possible then empty)
-	std::vector<CBonusItem*> bonusItems;
-	std::vector<LRClickableAreaWText*> spellEffects;
-
-	CCreaturePic *anim; //related creature's animation
-	MoraleLuckBox *luck, *morale;
-	LRClickableAreaWTextComp * expArea; //displays exp details
-	CSlider * slider; //Abilities
-	CAdventureMapButton *dismiss, *upgrade, *ok;
-	CAdventureMapButton * leftArtRoll, * rightArtRoll; //artifact selection
-	CAdventureMapButton * passArtToHero;
-	CAnimImage * artifactImage;
-	CAnimation * spellEffectsPics; //bitmaps representing spells affecting a stack in battle
-
-	//commander level-up
-	int selectedOption; //index for upgradeOptions
-	std::vector<ui32> upgradeOptions; //value 0-5 - secondary skills, 100+ - special skills
-	std::vector<CSelectableSkill *> selectableSkills, selectableBonuses;
-	std::vector<CPicture *> skillPictures; //secondary skills
-
-	std::string skillToFile(int skill); //return bitmap for secondary skill depending on selection / avaliability
-	void selectSkill (ui32 which);
-	void setArt(const CArtifactInstance *creatureArtifact);
-
-	void artifactRemoved (const ArtifactLocation &artLoc);
-	void artifactMoved (const ArtifactLocation &artLoc, const ArtifactLocation &destLoc);
-	void artifactDisassembled (const ArtifactLocation &artLoc) {return;};
-	void artifactAssembled (const ArtifactLocation &artLoc) {return;};
-
-	std::function<void()> dsm; //dismiss button callback
-	std::function<void()> Upg; //upgrade button callback
-	std::function<void(ui32)> levelUp; //choose commander skill to level up
-
-	CCreatureWindow(const CStack & stack, CreWinType type); //battle c-tor
-	CCreatureWindow (const CStackInstance &stack, CreWinType Type); //pop-up c-tor
-	CCreatureWindow(const CStackInstance &st, CreWinType Type, std::function<void()> Upg, std::function<void()> Dsm, UpgradeInfo *ui); //full garrison window
-	CCreatureWindow(const CCommanderInstance * commander, const CStack * stack = nullptr); //commander window
-	CCreatureWindow(std::vector<ui32> &skills, const CCommanderInstance * commander, std::function<void(ui32)> callback); 
-	CCreatureWindow(CreatureID Cid, CreWinType Type, int creatureCount); //c-tor
-
-	void init(const CStackInstance *stack, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner);
-	void showAll(SDL_Surface * to);
-	void show(SDL_Surface * to);
-	void printLine(int nr, const std::string &text, int baseVal, int val=-1, bool range=false);
-	void sliderMoved(int newpos);
-	void close();
-	~CCreatureWindow(); //d-tor
-
-	void recreateSkillList(int pos);
-	void scrollArt(int dir);
-	void passArtifactToHero();
-};
-
-class CBonusItem : public LRClickableAreaWTextComp //responsible for displaying creature skill, active or not
-{
-public:
-	std::string name, description;
-	CPicture * bonusGraphics;
-	bool visible;
-
-	CBonusItem();
-	CBonusItem(const Rect &Pos, const std::string &Name, const std::string &Description, const std::string &graphicsName);
-	~CBonusItem();
-
-	void showAll (SDL_Surface * to);
-};
-
-class CSelectableSkill : public LRClickableAreaWText
-{
-public:
-	std::function<void()> callback; //TODO: create more generic clickable class than AdvMapButton?
-
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState){};
-};
-
-/// original creature info window
-class CCreInfoWindow : public CWindowObject
-{
-public:
-	CLabel * creatureCount;
-	CLabel * creatureName;
-	CLabel * abilityText;
-
-	CCreaturePic * animation;
-	std::vector<CComponent *> upgResCost; //cost of upgrade (if not possible then empty)
-	std::vector<CAnimImage *> effects;
-	std::map<size_t, std::pair<CLabel *, CLabel * > > infoTexts;
-
-	MoraleLuckBox * luck, * morale;
-
-	CAdventureMapButton * dismiss, * upgrade, * ok;
-
-	CCreInfoWindow(const CStackInstance & st, bool LClicked, std::function<void()> Upg = nullptr, std::function<void()> Dsm = nullptr, UpgradeInfo * ui = nullptr);
-	CCreInfoWindow(const CStack & st, bool LClicked = 0);
-	CCreInfoWindow(int Cid, bool LClicked, int creatureCount);
-	~CCreInfoWindow();
-
-	void init(const CCreature * cre, const CBonusSystemNode * stackNode, const CGHeroInstance * heroOwner, int creatureCount, bool LClicked);
-	void printLine(int nr, const std::string & text, int baseVal, int val = -1, bool range = false);
-
-	void show(SDL_Surface * to);
-};
-
-CIntObject *createCreWindow(const CStack *s, bool lclick = false);
-CIntObject *createCreWindow(CreatureID Cid, CCreatureWindow::CreWinType Type, int creatureCount);
-CIntObject *createCreWindow(const CStackInstance *s, CCreatureWindow::CreWinType type, std::function<void()> Upg = nullptr, std::function<void()> Dsm = nullptr, UpgradeInfo *ui = nullptr);

+ 3 - 2
client/CMT.cpp

@@ -8,13 +8,13 @@
 
 #include "../lib/filesystem/Filesystem.h"
 #include "CPreGame.h"
-#include "CCastleInterface.h"
+#include "windows/CCastleInterface.h"
 #include "../lib/CConsoleHandler.h"
 #include "gui/CCursorHandler.h"
 #include "../lib/CGameState.h"
 #include "../CCallback.h"
 #include "CPlayerInterface.h"
-#include "CAdvmapInterface.h"
+#include "windows/CAdvmapInterface.h"
 #include "../lib/CBuildingHandler.h"
 #include "CVideoHandler.h"
 #include "../lib/CHeroHandler.h"
@@ -39,6 +39,7 @@
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
+#include "../lib/CondSh.h"
 
 #ifdef _WIN32
 #include "SDL_syswm.h"

+ 33 - 16
client/CMakeLists.txt

@@ -14,39 +14,56 @@ set(client_SRCS
 		battle/CBattleInterfaceClasses.cpp
 		battle/CCreatureAnimation.cpp
 
+		gui/CAnimation.cpp
+		gui/CCursorHandler.cpp
 		gui/CGuiHandler.cpp
 		gui/CIntObject.cpp
-		gui/CIntObjectClasses.cpp
 		gui/Fonts.cpp
 		gui/Geometries.cpp
-		gui/CCursorHandler.cpp
 		gui/SDL_Extensions.cpp
 
-		CPreGame.cpp
-		Client.cpp
-		CPlayerInterface.cpp
-		CMT.cpp
-		GUIClasses.cpp
-		AdventureMapClasses.cpp
-		CAdvmapInterface.cpp
-		CAnimation.cpp
+		widgets/AdventureMapClasses.cpp
+		widgets/Buttons.cpp
+		widgets/CArtifactHolder.cpp
+		widgets/CComponent.cpp
+		widgets/CGarrisonInt.cpp
+		widgets/Images.cpp
+		widgets/MiscWidgets.cpp
+		widgets/ObjectLists.cpp
+		widgets/TextControls.cpp
+
+		windows/CAdvmapInterface.cpp
+		windows/CCastleInterface.cpp
+		windows/CCreatureWindow.cpp
+		windows/CHeroWindow.cpp
+		windows/CKingdomInterface.cpp
+		windows/CQuestLog.cpp
+		windows/CSpellWindow.cpp
+		windows/CTradeWindow.cpp
+		windows/CWindowObject
+		windows/InfoWindows.cpp
+		windows/GUIClasses.cpp
+
 		CBitmapHandler.cpp
-		CCastleInterface.cpp
-		CCreatureWindow.cpp
 		CDefHandler.cpp
 		CGameInfo.cpp
-		CHeroWindow.cpp
-		CKingdomInterface.cpp
+		Client.cpp
 		CMessage.cpp
+		CMT.cpp
 		CMusicHandler.cpp
-		CSpellWindow.cpp
+		CPlayerInterface.cpp
+		CPreGame.cpp
 		CVideoHandler.cpp
-		CQuestLog.cpp
 		Graphics.cpp
 		mapHandler.cpp
 		NetPacksClient.cpp
 )
 
+set(client_HEADERS
+		gui/SDL_Pixels.h
+		gui/SDL_Compat.h
+)
+
 if(APPLE)
 	# OS X specific includes
 	include_directories(${SPARKLE_INCLUDE_DIR})

+ 6 - 4
client/CMessage.cpp

@@ -11,17 +11,19 @@
 #include "StdInc.h"
 #include "CMessage.h"
 
-#include "SDL_ttf.h"
 #include "CDefHandler.h"
-#include "CAnimation.h"
 #include "CGameInfo.h"
 #include "gui/SDL_Extensions.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "Graphics.h"
-#include "GUIClasses.h"
+#include "windows/GUIClasses.h"
 #include "../lib/CConfigHandler.h"
 #include "CBitmapHandler.h"
-#include "gui/CIntObjectClasses.h"
+
+#include "widgets/CComponent.h"
+#include "windows/InfoWindows.h"
+#include "widgets/Buttons.h"
+#include "widgets/TextControls.h"
 
 const int BETWEEN_COMPS_ROWS = 10;
 const int BEFORE_COMPONENTS = 30;

+ 0 - 1
client/CMusicHandler.h

@@ -2,7 +2,6 @@
 
 #include "../lib/CConfigHandler.h"
 #include "../lib/CSoundBase.h"
-#include "../lib/CCreatureHandler.h"
 
 /*
  * CMusicHandler.h, part of VCMI engine

+ 20 - 13
client/CPlayerInterface.cpp

@@ -1,21 +1,24 @@
 #include "StdInc.h"
-#include "CAdvmapInterface.h"
+#include "windows/CAdvmapInterface.h"
 #include "battle/CBattleInterface.h"
 #include "battle/CBattleInterfaceClasses.h"
 #include "../CCallback.h"
-#include "CCastleInterface.h"
+#include "windows/CCastleInterface.h"
 #include "gui/CCursorHandler.h"
-#include "CKingdomInterface.h"
+#include "windows/CKingdomInterface.h"
 #include "CGameInfo.h"
-#include "CHeroWindow.h"
-#include "CCreatureWindow.h"
-#include "CQuestLog.h"
+#include "windows/CHeroWindow.h"
+#include "windows/CCreatureWindow.h"
+#include "windows/CQuestLog.h"
 #include "CMessage.h"
 #include "CPlayerInterface.h"
 #include "gui/SDL_Extensions.h"
+#include "widgets/CComponent.h"
+#include "windows/CTradeWindow.h"
 #include "../lib/CConfigHandler.h"
 #include "battle/CCreatureAnimation.h"
 #include "Graphics.h"
+#include "windows/GUIClasses.h"
 #include "../lib/CArtHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
@@ -35,7 +38,9 @@
 #include "../lib/CGameState.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
+#include "windows/InfoWindows.h"
 #include "../lib/UnlockGuard.h"
+#include <SDL.h>
 
 #ifdef min
 #undef min
@@ -494,10 +499,12 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
 	waitWhileDialog();
 	CCS->soundh->playSound(soundBase::heroNewLevel);
 
-	CCreatureWindow * cw = new CCreatureWindow(skills, commander,
-												[=](ui32 selection){ cb->selectionMade(selection, queryID); });
-	GH.pushInt(cw);
+	GH.pushInt(new CStackWindow(commander, skills, [=](ui32 selection)
+	{
+		cb->selectionMade(selection, queryID);
+	}));
 }
+
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
@@ -1285,8 +1292,8 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
 	
 	if (adventureInt && adventureInt->isHeroSleeping(h))
 	{
-		adventureInt->sleepWake.clickLeft(true, false);
-		adventureInt->sleepWake.clickLeft(false, true);
+		adventureInt->sleepWake->clickLeft(true, false);
+		adventureInt->sleepWake->clickLeft(false, true);
 		//could've just called
 		//adventureInt->fsleepWake();
 		//but no authentic button click/sound ;-)
@@ -1321,7 +1328,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
 	waitForAllDialogs();
 
 	auto  cgw = new CGarrisonWindow(up,down,removableUnits);
-	cgw->quit->callback += onEnd;
+	cgw->quit->addCallback(onEnd);
 	GH.pushInt(cgw);
 }
 
@@ -2239,7 +2246,7 @@ void CPlayerInterface::acceptTurn()
 		if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt()))
 			iw->close();
 
-		adventureInt->endTurn.callback();
+		adventureInt->fendTurn();
 	}
 
 	// warn player if he has no town

+ 5 - 4
client/CPlayerInterface.h

@@ -1,11 +1,12 @@
 #pragma once
 
 
-#include "../lib/CondSh.h"
+//#include "../lib/CondSh.h"
 #include "../lib/FunctionList.h"
 #include "../lib/CGameInterface.h"
+#include "../lib/NetPacksBase.h"
 #include "gui/CIntObject.h"
-#include "../lib/CGameState.h"
+//#include "../lib/CGameState.h"
 
 #ifdef __GNUC__
 #define sprintf_s snprintf
@@ -29,8 +30,8 @@
  */
 
 class CDefEssential;
-class CAdventureMapButton;
-class CHighlightableButtonsGroup;
+class CButton;
+class CToggleGroup;
 class CDefHandler;
 struct TryMoveHero;
 class CDefEssential;

+ 138 - 138
client/CPreGame.cpp

@@ -9,7 +9,6 @@
 #include "gui/SDL_Extensions.h"
 #include "CGameInfo.h"
 #include "gui/CCursorHandler.h"
-#include "CAnimation.h"
 #include "CDefHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CTownHandler.h"
@@ -23,7 +22,7 @@
 #include "../lib/Connection.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/mapping/CMap.h"
-#include "GUIClasses.h"
+#include "windows/GUIClasses.h"
 #include "CPlayerInterface.h"
 #include "../CCallback.h"
 #include "CMessage.h"
@@ -38,7 +37,13 @@
 #include "../lib/CConfigHandler.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
+#include "gui/CAnimation.h"
+#include "widgets/CComponent.h"
+#include "widgets/Buttons.h"
+#include "widgets/MiscWidgets.h"
+#include "widgets/ObjectLists.h"
+#include "widgets/TextControls.h"
+#include "windows/InfoWindows.h"
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/CRandomGenerator.h"
@@ -367,7 +372,7 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri
 	return std::function<void()>();
 }
 
-CAdventureMapButton* CMenuEntry::createButton(CMenuScreen* parent, const JsonNode& button)
+CButton* CMenuEntry::createButton(CMenuScreen* parent, const JsonNode& button)
 {
 	std::function<void()> command = genCommand(parent, parent->menuNameToEntry, button["command"].String());
 
@@ -383,7 +388,7 @@ CAdventureMapButton* CMenuEntry::createButton(CMenuScreen* parent, const JsonNod
 	if (posy < 0)
 		posy = pos.h + posy;
 
-	return new CAdventureMapButton(help, command, posx, posy, button["name"].String(), button["hotkey"].Float());
+	return new CButton(Point(posx, posy), button["name"].String(), help, command, button["hotkey"].Float());
 }
 
 CMenuEntry::CMenuEntry(CMenuScreen* parent, const JsonNode &config)
@@ -639,38 +644,39 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 	{
 	case CMenuScreen::newGame:
 		{
-            card->difficulty->onChange = std::bind(&CSelectionScreen::difficultyChange, this, _1);
-			card->difficulty->select(1, 0);
-			CAdventureMapButton * select = new CAdventureMapButton(CGI->generaltexth->zelp[45], 0, 411, 80, "GSPBUTT.DEF", SDLK_s);
-			select->callback = [&]()
+			SDL_Color orange = {232, 184, 32, 0};
+			SDL_Color overlayColor = multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST ? orange : Colors::WHITE;
+
+			card->difficulty->addCallback(std::bind(&CSelectionScreen::difficultyChange, this, _1));
+			card->difficulty->setSelected(1);
+			CButton * select = new CButton(Point(411, 80), "GSPBUTT.DEF", CGI->generaltexth->zelp[45], 0, SDLK_s);
+			select->addCallback([&]()
 			{
 				toggleTab(sel);
 				changeSelection(sel->getSelectedMapInfo());
-			};
-			select->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL);
+			});
+			select->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL, overlayColor);
 
-            CAdventureMapButton *opts = new CAdventureMapButton(CGI->generaltexth->zelp[46], std::bind(&CSelectionScreen::toggleTab, this, opt), 411, 510, "GSPBUTT.DEF", SDLK_a);
-			opts->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL);
+			CButton *opts = new CButton(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], boost::bind(&CSelectionScreen::toggleTab, this, opt), SDLK_a);
+			opts->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL, overlayColor);
 
-			CAdventureMapButton * randomBtn = new CAdventureMapButton(CGI->generaltexth->zelp[47], 0, 411, 105, "GSPBUTT.DEF", SDLK_r);
-			randomBtn->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL);
-			randomBtn->callback = [&]()
+			CButton * randomBtn = new CButton(Point(411, 105), "GSPBUTT.DEF", CGI->generaltexth->zelp[47], 0, SDLK_r);
+			randomBtn->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, overlayColor);
+			randomBtn->addCallback([&]()
 			{
 				toggleTab(randMapTab);
 				changeSelection(randMapTab->getMapInfo());
-			};
+			});
 
-            start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], std::bind(&CSelectionScreen::startScenario, this), 411, 535, "SCNRBEG.DEF", SDLK_b);
+			start  = new CButton(Point(411, 535), "SCNRBEG.DEF", CGI->generaltexth->zelp[103], boost::bind(&CSelectionScreen::startScenario, this), SDLK_b);
 
 			if(network)
 			{
-                CAdventureMapButton *hideChat = new CAdventureMapButton(CGI->generaltexth->zelp[48], std::bind(&InfoCard::toggleChat, card), 619, 83, "GSPBUT2.DEF", SDLK_h);
+				CButton *hideChat = new CButton(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], boost::bind(&InfoCard::toggleChat, card), SDLK_h);
 				hideChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
 
 				if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)
 				{
-					SDL_Color orange = {232, 184, 32, 0};
-					select->text->color = opts->text->color = randomBtn->text->color = orange;
 					select->block(true);
 					opts->block(true);
 					randomBtn->block(true);
@@ -681,21 +687,21 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		break;
 	case CMenuScreen::loadGame:
 		sel->recActions = 255;
-        start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], std::bind(&CSelectionScreen::startScenario, this), 411, 535, "SCNRLOD.DEF", SDLK_l);
+		start  = new CButton(Point(411, 535), "SCNRLOD.DEF", CGI->generaltexth->zelp[103], boost::bind(&CSelectionScreen::startScenario, this), SDLK_l);
 		break;
 	case CMenuScreen::saveGame:
 		sel->recActions = 255;
-        start  = new CAdventureMapButton("", CGI->generaltexth->zelp[103].second, std::bind(&CSelectionScreen::startScenario, this), 411, 535, "SCNRSAV.DEF");
+		start  = new CButton(Point(411, 535), "SCNRSAV.DEF", CGI->generaltexth->zelp[103], boost::bind(&CSelectionScreen::startScenario, this), SDLK_s);
 		break;
 	case CMenuScreen::campaignList:
 		sel->recActions = 255;
-        start  = new CAdventureMapButton(std::pair<std::string, std::string>(), std::bind(&CSelectionScreen::startCampaign, this), 411, 535, "SCNRLOD.DEF", SDLK_b);
+		start  = new CButton(Point(411, 535), "SCNRLOD.DEF", CButton::tooltip(), boost::bind(&CSelectionScreen::startCampaign, this), SDLK_b);
 		break;
 	}
 
 	start->assignedKeys.insert(SDLK_RETURN);
 
-    back = new CAdventureMapButton("", CGI->generaltexth->zelp[105].second, std::bind(&CGuiHandler::popIntTotally, &GH, this), 581, 535, "SCNRBACK.DEF", SDLK_ESCAPE);
+	back = new CButton(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], boost::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
 
 	if(network)
 	{
@@ -995,7 +1001,7 @@ void CSelectionScreen::setSInfo(const StartInfo &si)
 	if(current)
 		opt->recreate(); //will force to recreate using current sInfo
 
-	card->difficulty->select(si.difficulty, 0);
+	card->difficulty->setSelected(si.difficulty);
 
 	GH.totalRedraw();
 }
@@ -1258,7 +1264,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
 			int sizes[] = {36, 72, 108, 144, 0};
 			const char * names[] = {"SCSMBUT.DEF", "SCMDBUT.DEF", "SCLGBUT.DEF", "SCXLBUT.DEF", "SCALBUT.DEF"};
 			for(int i = 0; i < 5; i++)
-                new CAdventureMapButton("", CGI->generaltexth->zelp[54+i].second, std::bind(&SelectionTab::filter, this, sizes[i], true), 158 + 47*i, 46, names[i]);
+				new CButton(Point(158 + 47*i, 46), names[i], CGI->generaltexth->zelp[54+i], boost::bind(&SelectionTab::filter, this, sizes[i], true));
 		}
 
 		//sort buttons buttons
@@ -1272,20 +1278,19 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const std::function<void(CM
 				if(criteria == _name)
 					criteria = generalSortingBy;
 
-                new CAdventureMapButton("", CGI->generaltexth->zelp[107+i].second, std::bind(&SelectionTab::sortBy, this, criteria), xpos[i], 86, names[i]);
+				new CButton(Point(xpos[i], 86), names[i], CGI->generaltexth->zelp[107+i], boost::bind(&SelectionTab::sortBy, this, criteria));
 			}
 		}
 	}
 	else
 	{
 		//sort by buttons
-        new CAdventureMapButton("", "", std::bind(&SelectionTab::sortBy, this, _numOfMaps), 23, 86, "CamCusM.DEF"); //by num of maps
-        new CAdventureMapButton("", "", std::bind(&SelectionTab::sortBy, this, _name), 55, 86, "CamCusL.DEF"); //by name
+		new CButton(Point(23, 86), "CamCusM.DEF", CButton::tooltip(), boost::bind(&SelectionTab::sortBy, this, _numOfMaps)); //by num of maps
+		new CButton(Point(55, 86), "CamCusL.DEF", CButton::tooltip(), boost::bind(&SelectionTab::sortBy, this, _name)); //by name
 	}
 
-    slider = new CSlider(372, 86, tabType != CMenuScreen::saveGame ? 480 : 430, std::bind(&SelectionTab::sliderMove, this, _1), positions, curItems.size(), 0, false, 1);
+	slider = new CSlider(Point(372, 86), tabType != CMenuScreen::saveGame ? 480 : 430, boost::bind(&SelectionTab::sliderMove, this, _1), positions, curItems.size(), 0, false, CSlider::BLUE);
 	slider->addUsedEvents(WHEEL);
-	slider->slider->keepFrame = true;
 	format =  CDefHandler::giveDef("SCSELC.DEF");
 
 	sortingBy = _format;
@@ -1351,16 +1356,16 @@ void SelectionTab::select( int position )
 	if(!curItems.size()) return;
 
 	// New selection. py is the index in curItems.
-	int py = position + slider->value;
+	int py = position + slider->getValue();
 	vstd::amax(py, 0);
 	vstd::amin(py, curItems.size()-1);
 
 	selectionPos = py;
 
 	if(position < 0)
-		slider->moveTo(slider->value + position);
+		slider->moveBy(position);
 	else if(position >= positions)
-		slider->moveTo(slider->value + position - positions + 1);
+		slider->moveBy(position - positions + 1);
 
 	if(txt)
 	{
@@ -1374,7 +1379,7 @@ void SelectionTab::select( int position )
 
 void SelectionTab::selectAbs( int position )
 {
-	select(position - slider->value);
+	select(position - slider->getValue());
 }
 
 int SelectionTab::getPosition( int x, int y )
@@ -1397,7 +1402,7 @@ void SelectionTab::sliderMove( int slidPos )
 void SelectionTab::printMaps(SDL_Surface *to)
 {
 
-	int elemIdx = slider->value;
+	int elemIdx = slider->getValue();
 
 	// Display all elements if there's enough space
 	//if(slider->amount < slider->capacity)
@@ -1555,15 +1560,15 @@ void SelectionTab::keyPressed( const SDL_KeyboardEvent & key )
 		moveBy = +positions-1;
 		break;
 	case SDLK_HOME:
-		select(-slider->value);
+		select(-slider->getValue());
 		return;
 	case SDLK_END:
-		select(curItems.size() - slider->value);
+		select(curItems.size() - slider->getValue());
 		return;
 	default:
 		return;
 	}
-	select(selectionPos - slider->value + moveBy);
+	select(selectionPos - slider->getValue() + moveBy);
 }
 
 void SelectionTab::onDoubleClick()
@@ -1571,7 +1576,7 @@ void SelectionTab::onDoubleClick()
 	if(getLine() != -1) //double clicked scenarios list
 	{
 		//act as if start button was pressed
-		(static_cast<CSelectionScreen*>(parent))->start->callback();
+		(static_cast<CSelectionScreen*>(parent))->start->clickLeft(false, true);
 	}
 }
 
@@ -1616,27 +1621,25 @@ CRandomMapTab::CRandomMapTab()
 	bg = new CPicture("RANMAPBK", 0, 6);
 
 	// Map Size
-	mapSizeBtnGroup = new CHighlightableButtonsGroup(0);
+	mapSizeBtnGroup = new CToggleGroup(0);
 	mapSizeBtnGroup->pos.y += 81;
 	mapSizeBtnGroup->pos.x += 158;
 	const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX");
 	addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198);
-	mapSizeBtnGroup->select(1, false);
-	mapSizeBtnGroup->onChange = [&](int btnId)
+	mapSizeBtnGroup->setSelected(1);
+	mapSizeBtnGroup->addCallback([&](int btnId)
 	{
 		const std::vector<int> mapSizeVal = boost::assign::list_of(CMapHeader::MAP_SIZE_SMALL)(CMapHeader::MAP_SIZE_MIDDLE)
 				(CMapHeader::MAP_SIZE_LARGE)(CMapHeader::MAP_SIZE_XLARGE);
 		mapGenOptions.setWidth(mapSizeVal[btnId]);
 		mapGenOptions.setHeight(mapSizeVal[btnId]);
 		updateMapInfo();
-	};
+	});
 
 	// Two levels
-	twoLevelsBtn = new CHighlightableButton(0, 0, std::map<int,std::string>(),
-		CGI->generaltexth->zelp[202].second, false, "RANUNDR", nullptr, 346, 81);
+	twoLevelsBtn = new CToggleButton(Point(346, 81), "RANUNDR", CGI->generaltexth->zelp[202]);
 	//twoLevelsBtn->select(true); for now, deactivated
-	twoLevelsBtn->callback = [&]() { mapGenOptions.setHasTwoLevels(true); updateMapInfo(); };
-	twoLevelsBtn->callback2 = [&]() { mapGenOptions.setHasTwoLevels(false); updateMapInfo(); };
+	twoLevelsBtn->addCallback([&](bool on) { mapGenOptions.setHasTwoLevels(on); updateMapInfo(); });
 
 	// Create number defs list
 	std::vector<std::string> numberDefs;
@@ -1648,128 +1651,130 @@ CRandomMapTab::CRandomMapTab()
 	const int NUMBERS_WIDTH = 32;
 	const int BTNS_GROUP_LEFT_MARGIN = 67;
 	// Amount of players
-	playersCntGroup = new CHighlightableButtonsGroup(0);
+	playersCntGroup = new CToggleGroup(0);
 	playersCntGroup->pos.y += 153;
 	playersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
-	playersCntGroup->onChange = [&](int btnId)
+	playersCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setPlayerCount(btnId);
 		deactivateButtonsFrom(teamsCntGroup, btnId);
 		deactivateButtonsFrom(compOnlyPlayersCntGroup, 8 - btnId + 1);
 		validatePlayersCnt(btnId);
 		updateMapInfo();
-	};
+	});
 
 	// Amount of teams
-	teamsCntGroup = new CHighlightableButtonsGroup(0);
+	teamsCntGroup = new CToggleGroup(0);
 	teamsCntGroup->pos.y += 219;
 	teamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
-	teamsCntGroup->onChange = [&](int btnId)
+	teamsCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setTeamCount(btnId);
 		updateMapInfo();
-	};
+	});
 
 	// Computer only players
-	compOnlyPlayersCntGroup = new CHighlightableButtonsGroup(0);
+	compOnlyPlayersCntGroup = new CToggleGroup(0);
 	compOnlyPlayersCntGroup->pos.y += 285;
 	compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
-	compOnlyPlayersCntGroup->select(0, true);
-	compOnlyPlayersCntGroup->onChange = [&](int btnId)
+	compOnlyPlayersCntGroup->setSelected(0);
+	compOnlyPlayersCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setCompOnlyPlayerCount(btnId);
 		deactivateButtonsFrom(compOnlyTeamsCntGroup, btnId);
 		validateCompOnlyPlayersCnt(btnId);
 		updateMapInfo();
-	};
+	});
 
 	// Computer only teams
-	compOnlyTeamsCntGroup = new CHighlightableButtonsGroup(0);
+	compOnlyTeamsCntGroup = new CToggleGroup(0);
 	compOnlyTeamsCntGroup->pos.y += 351;
 	compOnlyTeamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
 	deactivateButtonsFrom(compOnlyTeamsCntGroup, 0);
-	compOnlyTeamsCntGroup->onChange = [&](int btnId)
+	compOnlyTeamsCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setCompOnlyTeamCount(btnId);
 		updateMapInfo();
-	};
+	});
 
 	const int WIDE_BTN_WIDTH = 85;
 	// Water content
-	waterContentGroup = new CHighlightableButtonsGroup(0);
+	waterContentGroup = new CToggleGroup(0);
 	waterContentGroup->pos.y += 419;
 	waterContentGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD");
 	addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
-	waterContentGroup->onChange = [&](int btnId)
+	waterContentGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId));
-	};
+	});
 
 	// Monster strength
-	monsterStrengthGroup = new CHighlightableButtonsGroup(0);
+	monsterStrengthGroup = new CToggleGroup(0);
 	monsterStrengthGroup->pos.y += 485;
 	monsterStrengthGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG");
 	addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
-	monsterStrengthGroup->onChange = [&](int btnId)
+	monsterStrengthGroup->addCallback([&](int btnId)
 	{
 		if (btnId < 0)
 			mapGenOptions.setMonsterStrength(EMonsterStrength::RANDOM);
 		else
 			mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId + EMonsterStrength::GLOBAL_WEAK)); //value 2 to 4
-	};
+	});
 
 	// Show random maps btn
-	showRandMaps = new CAdventureMapButton("", CGI->generaltexth->zelp[252].second, 0, 54, 535, "RANSHOW");
+	showRandMaps = new CButton(Point(54, 535), "RANSHOW", CGI->generaltexth->zelp[252]);
 
 	// Initialize map info object
 	updateMapInfo();
 }
 
-void CRandomMapTab::addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const
+void CRandomMapTab::addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex, int helpRandIndex) const
 {
 	addButtonsToGroup(group, defs, nStart, nEnd, btnWidth, helpStartIndex);
 
 	// Buttons are relative to button group, TODO better solution?
 	SObjectConstruction obj__i(group);
 	const std::string RANDOM_DEF = "RANRAND";
-	group->addButton(new CHighlightableButton("", CGI->generaltexth->zelp[helpRandIndex].second, 0, 256, 0, RANDOM_DEF, CMapGenOptions::RANDOM_SIZE));
-	group->select(CMapGenOptions::RANDOM_SIZE, true);
+	group->addToggle(CMapGenOptions::RANDOM_SIZE, new CToggleButton(Point(256, 0), RANDOM_DEF, CGI->generaltexth->zelp[helpRandIndex]));
+	group->setSelected(CMapGenOptions::RANDOM_SIZE);
 }
 
-void CRandomMapTab::addButtonsToGroup(CHighlightableButtonsGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const
+void CRandomMapTab::addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int nStart, int nEnd, int btnWidth, int helpStartIndex) const
 {
 	// Buttons are relative to button group, TODO better solution?
 	SObjectConstruction obj__i(group);
 	int cnt = nEnd - nStart + 1;
 	for(int i = 0; i < cnt; ++i)
 	{
-		group->addButton(new CHighlightableButton("", CGI->generaltexth->zelp[helpStartIndex + i].second, 0, i * btnWidth, 0, defs[i + nStart], i + nStart));
+		auto button = new CToggleButton(Point(i * btnWidth, 0), defs[i + nStart], CGI->generaltexth->zelp[helpStartIndex + i]);
+		// For blocked state we should use pressed image actually
+		button->setImageOrder(0, 1, 1, 3);
+
+		group->addToggle(i + nStart, button);
 	}
 }
 
-void CRandomMapTab::deactivateButtonsFrom(CHighlightableButtonsGroup * group, int startId)
+void CRandomMapTab::deactivateButtonsFrom(CToggleGroup * group, int startId)
 {
-	for(CHighlightableButton * btn : group->buttons)
+	logGlobal->infoStream() << "Blocking buttons from " << startId;
+	for(auto toggle : group->buttons)
 	{
-		if(startId == CMapGenOptions::RANDOM_SIZE || btn->ID < startId)
+		if (auto button = dynamic_cast<CToggleButton*>(toggle.second))
 		{
-			if(btn->isBlocked())
+			if(startId == CMapGenOptions::RANDOM_SIZE || toggle.first < startId)
 			{
-				btn->setOffset(0);
-				btn->setState(CButtonBase::NORMAL);
+				button->block(false);
+			}
+			else
+			{
+				button->block(true);
 			}
-		}
-		else
-		{
-			// Blocked state looks like frame 'selected'=1
-			btn->setOffset(-1);
-			btn->setState(CButtonBase::BLOCKED);
 		}
 	}
 }
@@ -1784,12 +1789,12 @@ void CRandomMapTab::validatePlayersCnt(int playersCnt)
 	if(mapGenOptions.getTeamCount() >= playersCnt)
 	{
 		mapGenOptions.setTeamCount(playersCnt - 1);
-		teamsCntGroup->select(mapGenOptions.getTeamCount(), true);
+		teamsCntGroup->setSelected(mapGenOptions.getTeamCount());
 	}
 	if(mapGenOptions.getCompOnlyPlayerCount() > 8 - playersCnt)
 	{
 		mapGenOptions.setCompOnlyPlayerCount(8 - playersCnt);
-		compOnlyPlayersCntGroup->select(mapGenOptions.getCompOnlyPlayerCount(), true);
+		compOnlyPlayersCntGroup->setSelected(mapGenOptions.getCompOnlyPlayerCount());
 	}
 
 	validateCompOnlyPlayersCnt(mapGenOptions.getCompOnlyPlayerCount());
@@ -1805,7 +1810,7 @@ void CRandomMapTab::validateCompOnlyPlayersCnt(int compOnlyPlayersCnt)
 	if(mapGenOptions.getCompOnlyTeamCount() >= compOnlyPlayersCnt)
 	{
 		mapGenOptions.setCompOnlyTeamCount(compOnlyPlayersCnt - 1);
-		compOnlyTeamsCntGroup->select(mapGenOptions.getCompOnlyTeamCount(), true);
+		compOnlyTeamsCntGroup->setSelected(mapGenOptions.getCompOnlyTeamCount());
 	}
 }
 
@@ -1960,20 +1965,20 @@ InfoCard::InfoCard( bool Network )
 		pos.h = bg->pos.h;
 		sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
 		sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");
-		difficulty = new CHighlightableButtonsGroup(0);
+		difficulty = new CToggleGroup(0);
 		{
 			static const char *difButns[] = {"GSPBUT3.DEF", "GSPBUT4.DEF", "GSPBUT5.DEF", "GSPBUT6.DEF", "GSPBUT7.DEF"};
 
 			for(int i = 0; i < 5; i++)
 			{
-				difficulty->addButton(new CHighlightableButton("", CGI->generaltexth->zelp[24+i].second, 0, 110 + i*32, 450, difButns[i], i));
+				auto button = new CToggleButton(Point(110 + i*32, 450), difButns[i], CGI->generaltexth->zelp[24+i]);
+
+				difficulty->addToggle(i, button);
+				if(SEL->screenType != CMenuScreen::newGame)
+					button->block(true);
 			}
 		}
 
-		if(SEL->screenType != CMenuScreen::newGame)
-			difficulty->block(true);
-
-
 		if(network)
 		{
 			playerListBg = new CPicture("CHATPLUG.bmp", 16, 276);
@@ -2161,8 +2166,8 @@ void InfoCard::changeSelection( const CMapInfo *to )
 			mapDescription->setText(to->mapHeader->description);
 
 		if(SEL->screenType != CMenuScreen::newGame && SEL->screenType != CMenuScreen::campaignList) {
-			difficulty->block(true);
-			difficulty->select(SEL->sInfo.difficulty, 0);
+			//difficulty->block(true);
+			difficulty->setSelected(SEL->sInfo.difficulty);
 		}
 	}
 	redraw();
@@ -2242,7 +2247,7 @@ OptionsTab::OptionsTab():
 	pos = bg->pos;
 
 	if(SEL->screenType == CMenuScreen::newGame)
-        turnDuration = new CSlider(55, 551, 194, std::bind(&OptionsTab::setTurnLength, this, _1), 1, 11, 11, true, 1);
+		turnDuration = new CSlider(Point(55, 551), 194, boost::bind(&OptionsTab::setTurnLength, this, _1), 1, 11, 11, true, CSlider::BLUE);
 }
 
 OptionsTab::~OptionsTab()
@@ -2261,7 +2266,7 @@ void OptionsTab::showAll(SDL_Surface * to)
 	printAtMiddleWBLoc(CGI->generaltexth->allTexts[520], 349, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Bonus
 	printAtMiddleLoc(CGI->generaltexth->allTexts[521], 222, 538, FONT_SMALL, Colors::YELLOW, to); // Player Turn Duration
 	if (turnDuration)
-		printAtMiddleLoc(CGI->generaltexth->turnDurations[turnDuration->value], 319,559, FONT_SMALL, Colors::WHITE, to);//Turn duration value
+		printAtMiddleLoc(CGI->generaltexth->turnDurations[turnDuration->getValue()], 319,559, FONT_SMALL, Colors::WHITE, to);//Turn duration value
 }
 
 void OptionsTab::nextCastle( PlayerColor player, int dir )
@@ -2546,12 +2551,12 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 	bg = new CPicture(BitmapHandler::loadBitmap(bgs[s.color.getNum()]), 0, 0, true);
 	if(SEL->screenType == CMenuScreen::newGame)
 	{
-        btns[0] = new CAdventureMapButton(CGI->generaltexth->zelp[132], std::bind(&OptionsTab::nextCastle, owner, s.color, -1), 107, 5, "ADOPLFA.DEF");
-        btns[1] = new CAdventureMapButton(CGI->generaltexth->zelp[133], std::bind(&OptionsTab::nextCastle, owner, s.color, +1), 168, 5, "ADOPRTA.DEF");
-        btns[2] = new CAdventureMapButton(CGI->generaltexth->zelp[148], std::bind(&OptionsTab::nextHero, owner, s.color, -1), 183, 5, "ADOPLFA.DEF");
-        btns[3] = new CAdventureMapButton(CGI->generaltexth->zelp[149], std::bind(&OptionsTab::nextHero, owner, s.color, +1), 244, 5, "ADOPRTA.DEF");
-        btns[4] = new CAdventureMapButton(CGI->generaltexth->zelp[164], std::bind(&OptionsTab::nextBonus, owner, s.color, -1), 259, 5, "ADOPLFA.DEF");
-        btns[5] = new CAdventureMapButton(CGI->generaltexth->zelp[165], std::bind(&OptionsTab::nextBonus, owner, s.color, +1), 320, 5, "ADOPRTA.DEF");
+		btns[0] = new CButton(Point(107, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[132], boost::bind(&OptionsTab::nextCastle, owner, s.color, -1));
+		btns[1] = new CButton(Point(168, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[133], boost::bind(&OptionsTab::nextCastle, owner, s.color, +1));
+		btns[2] = new CButton(Point(183, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[148], boost::bind(&OptionsTab::nextHero, owner, s.color, -1));
+		btns[3] = new CButton(Point(244, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[149], boost::bind(&OptionsTab::nextHero, owner, s.color, +1));
+		btns[4] = new CButton(Point(259, 5), "ADOPLFA.DEF", CGI->generaltexth->zelp[164], boost::bind(&OptionsTab::nextBonus, owner, s.color, -1));
+		btns[5] = new CButton(Point(320, 5), "ADOPRTA.DEF", CGI->generaltexth->zelp[165], boost::bind(&OptionsTab::nextBonus, owner, s.color, +1));
 	}
 	else
 		for(auto & elem : btns)
@@ -2573,7 +2578,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 		&&  SEL->current->mapHeader->players[s.color.getNum()].canHumanPlay
 		&&  SEL->multiPlayer != CMenuScreen::MULTI_NETWORK_GUEST)
 	{
-        flag = new CAdventureMapButton(CGI->generaltexth->zelp[180], std::bind(&OptionsTab::flagPressed, owner, s.color), -43, 2, flags[s.color.getNum()]);
+		flag = new CButton(Point(-43, 2), flags[s.color.getNum()], CGI->generaltexth->zelp[180], boost::bind(&OptionsTab::flagPressed, owner, s.color));
 		flag->hoverable = true;
 	}
 	else
@@ -2979,8 +2984,8 @@ CScenarioInfo::CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *start
 	opt->recreate();
 	card->changeSelection(current);
 
-	card->difficulty->select(startInfo->difficulty, 0);
-    back = new CAdventureMapButton("", CGI->generaltexth->zelp[105].second, std::bind(&CGuiHandler::popIntTotally, &GH, this), 584, 535, "SCNRBACK.DEF", SDLK_ESCAPE);
+	card->difficulty->setSelected(startInfo->difficulty);
+	back = new CButton(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], boost::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
 }
 
 CScenarioInfo::~CScenarioInfo()
@@ -3059,10 +3064,10 @@ CMultiMode::CMultiMode()
 	txt = new CTextInput(Rect(19, 436, 334, 16), *bg);
 	txt->setText(settings["general"]["playerName"].String()); //Player
 
-    btns[0] = new CAdventureMapButton(CGI->generaltexth->zelp[266], std::bind(&CMultiMode::openHotseat, this), 373, 78, "MUBHOT.DEF");
-    btns[1] = new CAdventureMapButton("Host TCP/IP game", "", std::bind(&CMultiMode::hostTCP, this), 373, 78 + 57*1, "MUBHOST.DEF");
-    btns[2] = new CAdventureMapButton("Join TCP/IP game", "", std::bind(&CMultiMode::joinTCP, this), 373, 78 + 57*2, "MUBJOIN.DEF");
-    btns[6] = new CAdventureMapButton(CGI->generaltexth->zelp[288], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), 373, 424, "MUBCANC.DEF", SDLK_ESCAPE);
+	btns[0] = new CButton(Point(373, 78),        "MUBHOT.DEF",  CGI->generaltexth->zelp[266], boost::bind(&CMultiMode::openHotseat, this));
+	btns[1] = new CButton(Point(373, 78 + 57*1), "MUBHOST.DEF", CButton::tooltip("Host TCP/IP game", ""), boost::bind(&CMultiMode::hostTCP, this));
+	btns[2] = new CButton(Point(373, 78 + 57*2), "MUBJOIN.DEF", CButton::tooltip("Join TCP/IP game", ""), boost::bind(&CMultiMode::joinTCP, this));
+	btns[6] = new CButton(Point(373, 424),       "MUBCANC.DEF", CGI->generaltexth->zelp[288], [&] { GH.popIntTotally(this);}, SDLK_ESCAPE);
 }
 
 void CMultiMode::openHotseat()
@@ -3102,8 +3107,8 @@ CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
         txt[i]->cb += std::bind(&CHotSeatPlayers::onChange, this, _1);
 	}
 
-    ok = new CAdventureMapButton(CGI->generaltexth->zelp[560], std::bind(&CHotSeatPlayers::enterSelectionScreen, this), 95, 338, "MUBCHCK.DEF", SDLK_RETURN);
-    cancel = new CAdventureMapButton(CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), 205, 338, "MUBCANC.DEF", SDLK_ESCAPE);
+	ok = new CButton(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], boost::bind(&CHotSeatPlayers::enterSelectionScreen, this), SDLK_RETURN);
+	cancel = new CButton(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], boost::bind(&CGuiHandler::popIntTotally, boost::ref(GH), this), SDLK_ESCAPE);
 	bar = new CGStatusBar(new CPicture(Rect(7, 381, 348, 18), 0));//226, 472
 
 	txt[0]->setText(firstPlayer, true);
@@ -3166,9 +3171,9 @@ void CBonusSelection::init()
 
 	blitAt(panel, 456, 6, background);
 
-    startB = new CAdventureMapButton("", "", std::bind(&CBonusSelection::startMap, this), 475, 536, "CBBEGIB.DEF", SDLK_RETURN);
-    restartB = new CAdventureMapButton("", "", std::bind(&CBonusSelection::restartMap, this), 475, 536, "CBRESTB.DEF", SDLK_RETURN);
-    backB = new CAdventureMapButton("", "", std::bind(&CBonusSelection::goBack, this), 624, 536, "CBCANCB.DEF", SDLK_ESCAPE);
+	startB   = new CButton(Point(475, 536), "CBBEGIB.DEF", CButton::tooltip(), boost::bind(&CBonusSelection::startMap, this), SDLK_RETURN);
+	restartB = new CButton(Point(475, 536), "CBRESTB.DEF", CButton::tooltip(), boost::bind(&CBonusSelection::restartMap, this), SDLK_RETURN);
+	backB    = new CButton(Point(624, 536), "CBCANCB.DEF", CButton::tooltip(), boost::bind(&CBonusSelection::goBack, this), SDLK_ESCAPE);
 
 	//campaign name
 	if (ourCampaign->camp->header.name.length())
@@ -3190,7 +3195,7 @@ void CBonusSelection::init()
 
 	//bonus choosing
 	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, CGI->generaltexth->allTexts[71], Colors::WHITE, Point(511, 432));
-	bonuses = new CHighlightableButtonsGroup(bind(&CBonusSelection::selectBonus, this, _1));
+	bonuses = new CToggleGroup(bind(&CBonusSelection::selectBonus, this, _1));
 
 	//set left part of window
 	bool isCurrentMapConquerable = ourCampaign->currentMap && ourCampaign->camp->conquerable(*ourCampaign->currentMap);
@@ -3241,8 +3246,8 @@ void CBonusSelection::init()
 	//difficulty selection buttons
 	if (ourCampaign->camp->header.difficultyChoosenByPlayer)
 	{
-        diffLb = new CAdventureMapButton("", "", std::bind(&CBonusSelection::decreaseDifficulty, this), 694, 508, "SCNRBLF.DEF");
-        diffRb = new CAdventureMapButton("", "", std::bind(&CBonusSelection::increaseDifficulty, this), 738, 508, "SCNRBRT.DEF");
+		diffLb = new CButton(Point(694, 508), "SCNRBLF.DEF", CButton::tooltip(), boost::bind(&CBonusSelection::decreaseDifficulty, this));
+		diffRb = new CButton(Point(738, 508), "SCNRBRT.DEF", CButton::tooltip(), boost::bind(&CBonusSelection::increaseDifficulty, this));
 	}
 
 	//load miniflags
@@ -3442,13 +3447,8 @@ void CBonusSelection::updateBonusSelection()
 
 	updateStartButtonState(-1);
 
-	for (auto & elem : bonuses->buttons)
-	{
-		if (elem->active)
-			elem->deactivate();
-		delete elem;
-	}
-	bonuses->buttons.clear();
+	delete bonuses;
+	bonuses = new CToggleGroup(bind(&CBonusSelection::selectBonus, this, _1));
 
 	static const char *bonusPics[] = {"SPELLBON.DEF", "TWCRPORT.DEF", "", "ARTIFBON.DEF", "SPELLBON.DEF",
 		"PSKILBON.DEF", "SSKILBON.DEF", "BORES.DEF", "PORTRAITSLARGE", "PORTRAITSLARGE"};
@@ -3602,7 +3602,7 @@ void CBonusSelection::updateBonusSelection()
 			break;
 		}
 
-		CHighlightableButton *bonusButton = new CHighlightableButton(desc, desc, 0, 475 + i*68, 455, "", i);
+		CToggleButton *bonusButton = new CToggleButton(Point(475 + i*68, 455), "", CButton::tooltip(desc, desc));
 
 		if (picNumber != -1)
 			picName += ":" + boost::lexical_cast<std::string>(picNumber);
@@ -3612,13 +3612,13 @@ void CBonusSelection::updateBonusSelection()
 		bonusButton->setImage(anim);
 		const SDL_Color brightYellow = { 242, 226, 110, 0 };
 		bonusButton->borderColor = brightYellow;
-		bonuses->addButton(bonusButton);
+		bonuses->addToggle(i, bonusButton);
 	}
 
 	// set bonus if already chosen
 	if(vstd::contains(ourCampaign->chosenCampaignBonuses, selectedMap))
 	{
-		bonuses->select(ourCampaign->chosenCampaignBonuses[selectedMap], false);
+		bonuses->setSelected(ourCampaign->chosenCampaignBonuses[selectedMap]);
 	}
 }
 
@@ -3727,11 +3727,11 @@ void CBonusSelection::updateStartButtonState(int selected /*= -1*/)
 {
 	if(selected == -1)
 	{
-		startB->setState(ourCampaign->camp->scenarios[selectedMap].travelOptions.bonusesToChoose.size() ? CButtonBase::BLOCKED : CButtonBase::NORMAL);
+		startB->block(ourCampaign->camp->scenarios[selectedMap].travelOptions.bonusesToChoose.size());
 	}
-	else if(startB->getState() == CButtonBase::BLOCKED)
+	else if(startB->isBlocked())
 	{
-		startB->setState(CButtonBase::NORMAL);
+		startB->block(false);
 	}
 }
 
@@ -4091,14 +4091,14 @@ void CCampaignScreen::CCampaignButton::show(SDL_Surface * to)
 	}
 }
 
-CAdventureMapButton* CCampaignScreen::createExitButton(const JsonNode& button)
+CButton* CCampaignScreen::createExitButton(const JsonNode& button)
 {
 	std::pair<std::string, std::string> help;
 	if (!button["help"].isNull() && button["help"].Float() > 0)
 		help = CGI->generaltexth->zelp[button["help"].Float()];
 
-    std::function<void()> close = std::bind(&CGuiHandler::popIntTotally, &GH, this);
-	return new CAdventureMapButton(help, close, button["x"].Float(), button["y"].Float(), button["name"].String(), button["hotkey"].Float());
+	std::function<void()> close = boost::bind(&CGuiHandler::popIntTotally, &GH, this);
+	return new CButton(Point(button["x"].Float(), button["y"].Float()), button["name"].String(), help, close, button["hotkey"].Float());
 }
 
 
@@ -4239,8 +4239,8 @@ CSimpleJoinScreen::CSimpleJoinScreen()
     port->cb += std::bind(&CSimpleJoinScreen::onChange, this, _1);
     port->filters.add(std::bind(&CTextInput::numberFilter, _1, _2, 0, 65535));
 
-    ok = new CAdventureMapButton(CGI->generaltexth->zelp[560], std::bind(&CSimpleJoinScreen::enterSelectionScreen, this), 26, 142, "MUBCHCK.DEF", SDLK_RETURN);
-    cancel = new CAdventureMapButton(CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), 142, 142, "MUBCANC.DEF", SDLK_ESCAPE);
+	ok     = new CButton(Point( 26, 142), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], boost::bind(&CSimpleJoinScreen::enterSelectionScreen, this), SDLK_RETURN);
+	cancel = new CButton(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], boost::bind(&CGuiHandler::popIntTotally, boost::ref(GH), this), SDLK_ESCAPE);
 	bar = new CGStatusBar(new CPicture(Rect(7, 186, 218, 18), 0));
 
 	port->setText(boost::lexical_cast<std::string>(settings["server"]["port"].Float()), true);

+ 30 - 24
client/CPreGame.h

@@ -1,12 +1,11 @@
 #pragma once
 
-#include "../lib/filesystem/Filesystem.h"
-#include <SDL.h>
+//#include "../lib/filesystem/Filesystem.h"
 #include "../lib/StartInfo.h"
-#include "GUIClasses.h"
 #include "../lib/FunctionList.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/rmg/CMapGenerator.h"
+#include "windows/CWindowObject.h"
 
 /*
  * CPreGame.h, part of VCMI engine
@@ -18,6 +17,7 @@
  *
  */
 
+class CMapInfo;
 class CMusicHandler;
 class CMapHeader;
 class CCampaignHeader;
@@ -32,6 +32,12 @@ class CMapGenOptions;
 class CRandomMapTab;
 struct CPackForSelectionScreen;
 struct PlayerInfo;
+class CMultiLineLabel;
+class CToggleButton;
+class CToggleGroup;
+class CTabbedInt;
+class CButton;
+class CSlider;
 
 namespace boost{ class thread; class recursive_mutex;}
 
@@ -80,9 +86,9 @@ public:
 class CMenuEntry : public CIntObject
 {
 	std::vector<CPicture*> images;
-	std::vector<CAdventureMapButton*> buttons;
+	std::vector<CButton*> buttons;
 
-	CAdventureMapButton* createButton(CMenuScreen* parent, const JsonNode& button);
+	CButton* createButton(CMenuScreen* parent, const JsonNode& button);
 public:
 	CMenuEntry(CMenuScreen* parent, const JsonNode &config);
 };
@@ -126,7 +132,7 @@ public:
 	CChatBox *chat;
 	CPicture *playerListBg;
 
-	CHighlightableButtonsGroup *difficulty;
+	CToggleGroup *difficulty;
 	CDefHandler *sizes, *sFlags;
 
 	void changeSelection(const CMapInfo *to);
@@ -239,8 +245,8 @@ public:
 		PlayerInfo &pi;
 		PlayerSettings &s;
 		CPicture *bg;
-		CAdventureMapButton *btns[6]; //left and right for town, hero, bonus
-		CAdventureMapButton *flag;
+		CButton *btns[6]; //left and right for town, hero, bonus
+		CButton *flag;
 		SelectedBox *town;
 		SelectedBox *hero;
 		SelectedBox *bonus;
@@ -296,17 +302,17 @@ public:
 	const CMapGenOptions & getMapGenOptions() const;
 
 private:
-    void addButtonsToGroup(CHighlightableButtonsGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const;
-    void addButtonsWithRandToGroup(CHighlightableButtonsGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const;
-    void deactivateButtonsFrom(CHighlightableButtonsGroup * group, int startId);
+    void addButtonsToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex) const;
+    void addButtonsWithRandToGroup(CToggleGroup * group, const std::vector<std::string> & defs, int startIndex, int endIndex, int btnWidth, int helpStartIndex, int helpRandIndex) const;
+    void deactivateButtonsFrom(CToggleGroup * group, int startId);
     void validatePlayersCnt(int playersCnt);
     void validateCompOnlyPlayersCnt(int compOnlyPlayersCnt);
 
     CPicture * bg;
-	CHighlightableButton * twoLevelsBtn;
-	CHighlightableButtonsGroup * mapSizeBtnGroup, * playersCntGroup, * teamsCntGroup, * compOnlyPlayersCntGroup,
+	CToggleButton * twoLevelsBtn;
+	CToggleGroup * mapSizeBtnGroup, * playersCntGroup, * teamsCntGroup, * compOnlyPlayersCntGroup,
 		* compOnlyTeamsCntGroup, * waterContentGroup, * monsterStrengthGroup;
-    CAdventureMapButton * showRandMaps;
+    CButton * showRandMaps;
 	CMapGenOptions mapGenOptions;
 	unique_ptr<CMapInfo> mapInfo;
 	CFunctionList<void(const CMapInfo *)> mapInfoChanged;
@@ -347,7 +353,7 @@ public:
 	InfoCard *card;
 	OptionsTab *opt;
 	CRandomMapTab * randMapTab;
-	CAdventureMapButton *start, *back;
+	CButton *start, *back;
 
 	SelectionTab *sel;
 	CIntObject *curTab;
@@ -395,7 +401,7 @@ public:
 class CScenarioInfo : public CIntObject, public ISelectionScreenInfo
 {
 public:
-	CAdventureMapButton *back;
+	CButton *back;
 	InfoCard *card;
 	OptionsTab *opt;
 
@@ -409,7 +415,7 @@ class CMultiMode : public CIntObject
 public:
 	CPicture *bg;
 	CTextInput *txt;
-	CAdventureMapButton *btns[7]; //0 - hotseat, 6 - cancel
+	CButton *btns[7]; //0 - hotseat, 6 - cancel
 	CGStatusBar *bar;
 
 	CMultiMode();
@@ -424,7 +430,7 @@ class CHotSeatPlayers : public CIntObject
 	CPicture *bg;
 	CTextBox *title;
 	CTextInput* txt[8];
-	CAdventureMapButton *ok, *cancel;
+	CButton *ok, *cancel;
 	CGStatusBar *bar;
 
 	void onChange(std::string newText);
@@ -512,14 +518,14 @@ private:
 
 	// GUI components
 	SDL_Surface * background;
-	CAdventureMapButton * startB, * restartB, * backB;
+	CButton * startB, * restartB, * backB;
 	CTextBox * campaignDescription, * mapDescription;
 	std::vector<SCampPositions> campDescriptions;
 	std::vector<CRegion *> regions;
 	CRegion * highlightedRegion;
-	CHighlightableButtonsGroup * bonuses;
+	CToggleGroup * bonuses;
 	SDL_Surface * diffPics[5]; //pictures of difficulties, user-selectable (or not if campaign locks this)
-	CAdventureMapButton * diffLb, * diffRb; //buttons for changing difficulty
+	CButton * diffLb, * diffRb; //buttons for changing difficulty
 	CDefHandler * sizes; //icons of map sizes
 	CDefHandler * sFlags;
 
@@ -560,11 +566,11 @@ private:
 		void show(SDL_Surface * to);
 	};
 
-	CAdventureMapButton *back;
+	CButton *back;
 	std::vector<CCampaignButton*> campButtons;
 	std::vector<CPicture*> images;
 
-	CAdventureMapButton* createExitButton(const JsonNode& button);
+	CButton* createExitButton(const JsonNode& button);
 
 public:
 	enum CampaignSet {ROE, AB, SOD, WOG};
@@ -630,7 +636,7 @@ class CSimpleJoinScreen : public CIntObject
 {
 	CPicture * bg;
 	CTextBox * title;
-	CAdventureMapButton * ok, * cancel;
+	CButton * ok, * cancel;
 	CGStatusBar * bar;
 	CTextInput * address;
 	CTextInput * port;

+ 3 - 0
client/Client.cpp

@@ -1,4 +1,7 @@
 #include "StdInc.h"
+#include "Client.h"
+
+#include <SDL.h>
 
 #include "CMusicHandler.h"
 #include "../lib/mapping/CCampaignHandler.h"

+ 5 - 1
client/Client.h

@@ -2,8 +2,9 @@
 
 
 #include "../lib/IGameCallback.h"
-#include "../lib/CondSh.h"
+#include "../lib/BattleAction.h"
 #include "../lib/CStopWatch.h"
+#include "../lib/int3.h"
 
 /*
  * Client.h, part of VCMI engine
@@ -15,6 +16,9 @@
  *
  */
 
+class CPack;
+class CCampaignState;
+class CBattleCallback;
 class IGameEventsReceiver;
 class IBattleEventsReceiver;
 class CBattleGameInterface;

+ 0 - 6263
client/GUIClasses.cpp

@@ -1,6263 +0,0 @@
-#include "StdInc.h"
-#include "GUIClasses.h"
-#include "gui/SDL_Extensions.h"
-
-#include "CAdvmapInterface.h"
-#include "CBitmapHandler.h"
-#include "CDefHandler.h"
-#include "battle/CBattleInterface.h"
-#include "battle/CBattleInterfaceClasses.h"
-#include "../CCallback.h"
-#include "CCastleInterface.h"
-#include "CCreatureWindow.h"
-#include "gui/CCursorHandler.h"
-#include "CGameInfo.h"
-#include "CHeroWindow.h"
-#include "CMessage.h"
-#include "../lib/CConfigHandler.h"
-#include "battle/CCreatureAnimation.h"
-#include "CPlayerInterface.h"
-#include "Graphics.h"
-#include "CAnimation.h"
-#include "../lib/CArtHandler.h"
-#include "../lib/CBuildingHandler.h"
-#include "../lib/CGeneralTextHandler.h"
-#include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
-#include "../lib/CSpellHandler.h"
-#include "../lib/CTownHandler.h"
-#include "../lib/CondSh.h"
-#include "../lib/mapping/CMap.h"
-#include "mapHandler.h"
-#include "../lib/CStopWatch.h"
-#include "../lib/NetPacksBase.h"
-#include "CSpellWindow.h"
-#include "CHeroWindow.h"
-#include "CVideoHandler.h"
-#include "../lib/StartInfo.h"
-#include "CPreGame.h"
-#include "../lib/HeroBonus.h"
-#include "../lib/CCreatureHandler.h"
-#include "CMusicHandler.h"
-#include "../lib/BattleState.h"
-#include "../lib/CGameState.h"
-#include "../lib/GameConstants.h"
-#include "gui/CGuiHandler.h"
-
-/*
- * GUIClasses.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
- *
- */
-
-using namespace boost::assign;
-using namespace CSDL_Ext;
-
-extern std::queue<SDL_Event> events;
-extern boost::mutex eventsM;
-
-std::list<CFocusable*> CFocusable::focusables;
-CFocusable * CFocusable::inputWithFocus;
-
-#undef min
-#undef max
-
-void CArmyTooltip::init(const InfoAboutArmy &army)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	new CLabel(66, 2, FONT_SMALL, TOPLEFT, Colors::WHITE, army.name);
-
-	std::vector<Point> slotsPos;
-	slotsPos.push_back(Point(36,73));
-	slotsPos.push_back(Point(72,73));
-	slotsPos.push_back(Point(108,73));
-	slotsPos.push_back(Point(18,122));
-	slotsPos.push_back(Point(54,122));
-	slotsPos.push_back(Point(90,122));
-	slotsPos.push_back(Point(126,122));
-
-	for(auto & slot : army.army)
-	{
-		if(slot.first.getNum() >= GameConstants::ARMY_SIZE)
-		{
-            logGlobal->warnStream() << "Warning: " << army.name << " has stack in slot " << slot.first;
-			continue;
-		}
-
-		new CAnimImage("CPRSMALL", slot.second.type->iconIndex, 0, slotsPos[slot.first.getNum()].x, slotsPos[slot.first.getNum()].y);
-
-		std::string subtitle;
-		if(army.army.isDetailed)
-			subtitle = boost::lexical_cast<std::string>(slot.second.count);
-		else
-		{
-			//if =0 - we have no information about stack size at all
-			if (slot.second.count)
-				subtitle = CGI->generaltexth->arraytxt[171 + 3*(slot.second.count)];
-		}
-
-		new CLabel(slotsPos[slot.first.getNum()].x + 17, slotsPos[slot.first.getNum()].y + 41, FONT_TINY, CENTER, Colors::WHITE, subtitle);
-	}
-
-}
-
-CArmyTooltip::CArmyTooltip(Point pos, const InfoAboutArmy &army):
-    CIntObject(0, pos)
-{
-	init(army);
-}
-
-CArmyTooltip::CArmyTooltip(Point pos, const CArmedInstance * army):
-    CIntObject(0, pos)
-{
-	init(InfoAboutArmy(army, true));
-}
-
-void CHeroTooltip::init(const InfoAboutHero &hero)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CAnimImage("PortraitsLarge", hero.portrait, 0, 3, 2);
-
-	if(hero.details)
-	{
-		for (size_t i = 0; i < hero.details->primskills.size(); i++)
-			new CLabel(75 + 28 * i, 58, FONT_SMALL, CENTER, Colors::WHITE,
-			           boost::lexical_cast<std::string>(hero.details->primskills[i]));
-
-		new CLabel(158, 98, FONT_TINY, CENTER, Colors::WHITE,
-		           boost::lexical_cast<std::string>(hero.details->mana));
-
-		new CAnimImage("IMRL22", hero.details->morale + 3, 0, 5, 74);
-		new CAnimImage("ILCK22", hero.details->luck + 3, 0, 5, 91);
-	}
-}
-
-CHeroTooltip::CHeroTooltip(Point pos, const InfoAboutHero &hero):
-    CArmyTooltip(pos, hero)
-{
-	init(hero);
-}
-
-CHeroTooltip::CHeroTooltip(Point pos, const CGHeroInstance * hero):
-    CArmyTooltip(pos, InfoAboutHero(hero, true))
-{
-	init(InfoAboutHero(hero, true));
-}
-
-void CTownTooltip::init(const InfoAboutTown &town)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	//order of icons in def: fort, citadel, castle, no fort
-	size_t fortIndex = town.fortLevel ? town.fortLevel - 1 : 3;
-
-	new CAnimImage("ITMCLS", fortIndex, 0, 105, 31);
-
-	assert(town.tType);
-
-	size_t iconIndex = town.tType->clientInfo.icons[town.fortLevel > 0][town.built >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
-
-	new CAnimImage("itpt", iconIndex, 0, 3, 2);
-
-	if(town.details)
-	{
-		new CAnimImage("ITMTLS", town.details->hallLevel, 0, 67, 31);
-
-		if (town.details->goldIncome)
-			new CLabel(157, 58, FONT_TINY, CENTER, Colors::WHITE,
-		               boost::lexical_cast<std::string>(town.details->goldIncome));
-
-		if(town.details->garrisonedHero) //garrisoned hero icon
-			new CPicture("TOWNQKGH", 149, 76);
-
-		if(town.details->customRes)//silo is built
-		{
-			if (town.tType->primaryRes == Res::WOOD_AND_ORE )// wood & ore
-			{
-				new CAnimImage("SMALRES", Res::WOOD, 0, 7, 75);
-				new CAnimImage("SMALRES", Res::ORE , 0, 7, 88);
-			}
-			else
-				new CAnimImage("SMALRES", town.tType->primaryRes, 0, 7, 81);
-		}
-	}
-}
-
-CTownTooltip::CTownTooltip(Point pos, const InfoAboutTown &town):
-    CArmyTooltip(pos, town)
-{
-	init(town);
-}
-
-CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town):
-    CArmyTooltip(pos, InfoAboutTown(town, true))
-{
-	init(InfoAboutTown(town, true));
-}
-
-void CGarrisonSlot::setHighlight(bool on)
-{
-	if (on)
-		selectionImage->enable(); //show
-	else
-		selectionImage->disable(); //hide
-}
-
-void CGarrisonSlot::hover (bool on)
-{
-	////Hoverable::hover(on);
-	if(on)
-	{
-		std::string temp;
-		if(creature)
-		{
-			if(owner->getSelection())
-			{
-				if(owner->getSelection() == this)
-				{
-					temp = CGI->generaltexth->tcommands[4]; //View %s
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
-				}
-				else if (owner->getSelection()->creature == creature)
-				{
-					temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
-				}
-				else if (owner->getSelection()->creature)
-				{
-					temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
-					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
-				}
-				else
-				{
-                    logGlobal->warnStream() << "Warning - shouldn't be - highlighted void slot "<<owner->getSelection();
-                    logGlobal->warnStream() << "Highlighted set to nullptr";
-					owner->selectSlot(nullptr);
-				}
-			}
-			else
-			{
-				if(upg)
-				{
-					temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
-				}
-				else if(owner->armedObjs[0] && owner->armedObjs[0]->ID == Obj::TOWN)
-				{
-					temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
-				}
-				else
-				{
-					temp = CGI->generaltexth->allTexts[481]; //Select %s
-				}
-				boost::algorithm::replace_first(temp,"%s",creature->nameSing);
-			};
-		}
-		else
-		{
-			if(owner->getSelection())
-			{
-				const CArmedInstance *highl = owner->getSelection()->getObj();
-				if(  highl->needsLastStack()		//we are moving stack from hero's
-				  && highl->stacksCount() == 1	//it's only stack
-				  && owner->getSelection()->upg != upg	//we're moving it to the other garrison
-				  )
-				{
-					temp = CGI->generaltexth->tcommands[5]; //Cannot move last army to garrison
-				}
-				else
-				{
-					temp = CGI->generaltexth->tcommands[6]; //Move %s
-					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
-				}
-			}
-			else
-			{
-				temp = CGI->generaltexth->tcommands[11]; //Empty
-			}
-		}
-		GH.statusbar->setText(temp);
-	}
-	else
-	{
-		GH.statusbar->clear();
-	}
-}
-
-const CArmedInstance * CGarrisonSlot::getObj() const
-{
-	return 	(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]);
-}
-
-bool CGarrisonSlot::our() const
-{
-	return 	upg?(owner->owned[1]):(owner->owned[0]);
-}
-
-void CGarrisonSlot::clickRight(tribool down, bool previousState)
-{
-	if(down && creature)
-	{
-		GH.pushInt(createCreWindow(myStack, CCreatureWindow::ARMY));
-	}
-}
-void CGarrisonSlot::clickLeft(tribool down, bool previousState)
-{
-	if(down)
-	{
-		bool refr = false;
-		if(owner->getSelection())
-		{
-			if(owner->getSelection() == this) //view info
-			{
-				UpgradeInfo pom;
-				LOCPLINT->cb->getUpgradeInfo(getObj(), ID, pom);
-
-				bool canUpgrade = getObj()->tempOwner == LOCPLINT->playerID && pom.oldID>=0; //upgrade is possible
-				bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID && (getObj()->stacksCount()>1  || !getObj()->needsLastStack());
-				std::function<void()> upgr = nullptr;
-				std::function<void()> dism = nullptr;
-				if(canUpgrade) upgr = [=] { LOCPLINT->cb->upgradeCreature(getObj(), ID, pom.newID[0]); };
-				if(canDismiss) dism = [=] { LOCPLINT->cb->dismissCreature(getObj(), ID); };
-
-				owner->selectSlot(nullptr);
-				owner->setSplittingMode(false);
-
-				for(auto & elem : owner->splitButtons)
-					elem->block(true);
-
-				redraw();
-				refr = true;
-				CIntObject *creWindow = createCreWindow(myStack, CCreatureWindow::HERO, upgr, dism, &pom);
-				GH.pushInt(creWindow);
-			}
-			else
-			{
-				// Only allow certain moves if troops aren't removable or not ours.
-				if (  ( owner->getSelection()->our()//our creature is selected
-				     || owner->getSelection()->creature == creature )//or we are rebalancing army
-				   && ( owner->removableUnits
-				     || (upg == 0 &&  ( owner->getSelection()->upg == 1 && !creature ) )
-					 || (upg == 1 &&    owner->getSelection()->upg == 1 ) ) )
-				{
-					//we want to split
-					if((owner->getSplittingMode() || LOCPLINT->shiftPressed())
-						&& (!creature
-							|| (creature == owner->getSelection()->creature)))
-					{
-						owner->p2 = ID; //store the second stack pos
-						owner->pb = upg;//store the second stack owner (up or down army)
-						owner->setSplittingMode(false);
-
-						int minLeft=0, minRight=0;
-
-						if(upg != owner->getSelection()->upg) //not splitting within same army
-						{
-							if(owner->getSelection()->getObj()->stacksCount() == 1 //we're splitting away the last stack
-								&& owner->getSelection()->getObj()->needsLastStack() )
-							{
-								minLeft = 1;
-							}
-							if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
-								&& owner->getSelection()->creature == creature
-								&& getObj()->needsLastStack() )
-							{
-								minRight = 1;
-							}
-						}
-
-						int countLeft = owner->getSelection()->myStack ? owner->getSelection()->myStack->count : 0;
-						int countRight = myStack ? myStack->count : 0;
-
-						GH.pushInt(new CSplitWindow(owner->getSelection()->creature, std::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
-						                            minLeft, minRight, countLeft, countRight));
-						refr = true;
-					}
-					else if(creature != owner->getSelection()->creature) //swap
-					{
-						LOCPLINT->cb->swapCreatures(
-							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							ID,owner->getSelection()->ID);
-					}
-					else //merge
-					{
-						LOCPLINT->cb->mergeStacks(
-							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
-							owner->getSelection()->ID,ID);
-					}
-				}
-				else // Highlight
-				{
-					if(creature)
-						owner->selectSlot(this);
-					redraw();
-					refr = true;
-				}
-			}
-		}
-		else //highlight or drop artifact
-		{
-			bool artSelected = false;
-			if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt())) //dirty solution
-			{
-				const CArtifactsOfHero::SCommonPart *commonInfo = chw->artSets.front()->commonInfo;
-				if (const CArtifactInstance *art = commonInfo->src.art)
-				{
-					const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero();
-					artSelected = true;
-					ArtifactLocation src(srcHero, commonInfo->src.slotID);
-					ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
-					if (art->canBePutAt(dst, true))
-					{	//equip clicked stack
-						if(dst.getArt())
-						{
-							//creature can wear only one active artifact
-							//if we are placing a new one, the old one will be returned to the hero's backpack
-							LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, dst.getArt()->firstBackpackSlot(srcHero)));
-						}
-						LOCPLINT->cb->swapArtifacts(src, dst);
-					}
-				}
-			}
-			if (!artSelected && creature)
-			{
-				owner->selectSlot(this);
-				if(creature)
-				{
-					for(auto & elem : owner->splitButtons)
-						elem->block(false);
-				}
-			}
-			redraw();
-			refr = true;
-		}
-		if(refr) {hover(false);	hover(true); } //to refresh statusbar
-	}
-}
-
-void CGarrisonSlot::update()
-{
-	if (getObj() != nullptr)
-	{
-		addUsedEvents(LCLICK | RCLICK | HOVER);
-		myStack = getObj()->getStackPtr(ID);
-		creature = myStack ? myStack->type : nullptr;
-	}
-	else
-	{
-		removeUsedEvents(LCLICK | RCLICK | HOVER);
-		myStack = nullptr;
-		creature = nullptr;
-	}
-
-	if (creature)
-	{
-		creatureImage->enable();
-		creatureImage->setFrame(creature->iconIndex);
-
-		stackCount->enable();
-		stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
-	}
-	else
-	{
-		creatureImage->disable();
-		stackCount->disable();
-	}
-}
-
-CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg, const CStackInstance * Creature):
-    ID(IID),
-    owner(Owner),
-    myStack(Creature),
-    creature(Creature ? Creature->type : nullptr),
-    upg(Upg)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if (getObj())
-		addUsedEvents(LCLICK | RCLICK | HOVER);
-	pos.x += x;
-	pos.y += y;
-
-	std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
-
-	creatureImage = new CAnimImage(imgName, creature ? creature->iconIndex : 0);
-	if (!creature)
-		creatureImage->disable();
-
-	selectionImage = new CAnimImage(imgName, 1);
-	selectionImage->disable();
-
-	if(Owner->smallIcons)
-	{
-		pos.w = 32;
-		pos.h = 32;
-	}
-	else
-	{
-		pos.w = 58;
-		pos.h = 64;
-	}
-
-	stackCount = new CLabel(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, BOTTOMRIGHT, Colors::WHITE);
-	if (!creature)
-		stackCount->disable();
-	else
-		stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
-}
-
-void CGarrisonInt::addSplitBtn(CAdventureMapButton * button)
-{
-	addChild(button);
-	button->recActions = defActions;
-	splitButtons.push_back(button);
-	button->block(getSelection() == nullptr);
-}
-
-void CGarrisonInt::createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int posY, int distance, int Upg )
-{
-	ret.resize(7);
-
-	if (set)
-	{
-		for(auto & elem : set->Slots())
-		{
-			ret[elem.first.getNum()] = new CGarrisonSlot(this, posX + (elem.first.getNum()*distance), posY, elem.first, Upg, elem.second);
-		}
-	}
-
-	for(int i=0; i<ret.size(); i++)
-		if(!ret[i])
-			ret[i] = new CGarrisonSlot(this, posX + (i*distance), posY, SlotID(i), Upg, nullptr);
-
-	if (twoRows)
-		for (int i=4; i<ret.size(); i++)
-		{
-			ret[i]->pos.x -= 126;
-			ret[i]->pos.y += 37;
-		};
-}
-
-void CGarrisonInt::createSlots()
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	int width = smallIcons? 32 : 58;
-
-	createSet(slotsUp, armedObjs[0], 0, 0, width+interx, 0);
-	createSet(slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
-}
-
-void CGarrisonInt::recreateSlots()
-{
-	selectSlot(nullptr);
-	setSplittingMode(false);
-
-	for(auto & elem : splitButtons)
-		elem->block(true);
-
-
-	for(CGarrisonSlot * slot : slotsUp)
-		slot->update();
-
-	for(CGarrisonSlot * slot : slotsDown)
-		slot->update();
-}
-
-void CGarrisonInt::splitClick()
-{
-	if(!getSelection())
-		return;
-	setSplittingMode(!getSplittingMode());
-	redraw();
-}
-void CGarrisonInt::splitStacks(int, int amountRight)
-{
-	LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
-}
-
-CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
-                           SDL_Surface *pomsur, const Point& SurOffset,
-                           const CArmedInstance *s1, const CArmedInstance *s2,
-                           bool _removableUnits, bool smallImgs, bool _twoRows ) :
-    highlighted(nullptr),
-    inSplittingMode(false),
-    interx(inx),
-    garOffset(garsOffset),
-    smallIcons(smallImgs),
-    removableUnits(_removableUnits),
-    twoRows(_twoRows)
-{
-	setArmy(s1, false);
-	setArmy(s2, true);
-	pos.x += x;
-	pos.y += y;
-	createSlots();
-}
-
-const CGarrisonSlot * CGarrisonInt::getSelection()
-{
-	return highlighted;
-}
-
-void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
-{
-	if (slot != highlighted)
-	{
-		if (highlighted)
-			highlighted->setHighlight(false);
-
-		highlighted = slot;
-		for (auto button : splitButtons)
-			button->block(highlighted == nullptr);
-
-		if (highlighted)
-			highlighted->setHighlight(true);
-	}
-}
-
-void CGarrisonInt::setSplittingMode(bool on)
-{
-	assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
-
-	if (inSplittingMode || on)
-	{
-		for(CGarrisonSlot * slot : slotsUp)
-			slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
-
-		for(CGarrisonSlot * slot : slotsDown)
-			slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
-		inSplittingMode = on;
-	}
-}
-
-bool CGarrisonInt::getSplittingMode()
-{
-	return inSplittingMode;
-}
-
-void CGarrisonInt::setArmy(const CArmedInstance *army, bool bottomGarrison)
-{
-	owned[bottomGarrison] =  army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == PlayerColor::UNFLAGGABLE) : false;
-	armedObjs[bottomGarrison] = army;
-}
-
-CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps, const TButtonsInfo &Buttons, bool delComps)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	type |= BLOCK_ADV_HOTKEYS;
-	ID = QueryID(-1);
-	for(auto & Button : Buttons)
-	{
-		CAdventureMapButton *button = new CAdventureMapButton("","",std::bind(&CInfoWindow::close,this),0,0,Button.first);
-		button->borderColor = Colors::METALLIC_GOLD;
-		button->borderEnabled = true;
-		button->callback.add(Button.second); //each button will close the window apart from call-defined actions
-		buttons.push_back(button);
-	}
-
-	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
-	if(!text->slider)
-	{
-		text->resize(text->label->textSize);
-	}
-
-	if(buttons.size())
-	{
-		buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
-		buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
-	}
-
-	for(auto & comp : comps)
-	{
-		comp->recActions = 0xff;
-		addChild(comp);
-		comp->recActions &= ~(SHOWALL | UPDATE);
-		components.push_back(comp);
-	}
-	setDelComps(delComps);
-	CMessage::drawIWindow(this,Text,player);
-}
-
-CInfoWindow::CInfoWindow()
-{
-	ID = QueryID(-1);
-	setDelComps(false);
-	text = nullptr;
-}
-
-void CInfoWindow::close()
-{
-	GH.popIntTotally(this);
-	if(LOCPLINT)
-		LOCPLINT->showingDialog->setn(false);
-}
-
-void CInfoWindow::show(SDL_Surface * to)
-{
-	CIntObject::show(to);
-}
-
-CInfoWindow::~CInfoWindow()
-{
-	if(!delComps)
-	{
-		for (auto & elem : components)
-			removeChild(elem);
-	}
-}
-
-void CInfoWindow::showAll(SDL_Surface * to)
-{
-	CSimpleWindow::show(to);
-	CIntObject::showAll(to);
-}
-
-void CInfoWindow::showInfoDialog(const std::string &text, const std::vector<CComponent *> *components, bool DelComps, PlayerColor player)
-{
-	CInfoWindow * window = CInfoWindow::create(text, player, components, DelComps);
-	GH.pushInt(window);
-}
-
-void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps, PlayerColor player)
-{
-	assert(!LOCPLINT || LOCPLINT->showingDialog->get());
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("ICANCEL.DEF",0));
-	CInfoWindow * temp = new CInfoWindow(text, player, components ? *components : std::vector<CComponent*>(), pom, DelComps);
-	for(auto & elem : onYes.funcs)
-		temp->buttons[0]->callback += elem;
-	for(auto & elem : onNo.funcs)
-		temp->buttons[1]->callback += elem;
-
-	GH.pushInt(temp);
-}
-
-void CInfoWindow::showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const std::function<void()> & onOk, bool delComps, PlayerColor player)
-{
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	CInfoWindow * temp = new CInfoWindow(text, player, *components, pom, delComps);
-	temp->buttons[0]->callback += onOk;
-
-	GH.pushInt(temp);
-}
-
-CInfoWindow * CInfoWindow::create(const std::string &text, PlayerColor playerID /*= 1*/, const std::vector<CComponent*> *components /*= nullptr*/, bool DelComps)
-{
-	std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
-	pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
-	CInfoWindow * ret = new CInfoWindow(text, playerID, components ? *components : std::vector<CComponent*>(), pom, DelComps);
-	return ret;
-}
-
-std::string CInfoWindow::genText(std::string title, std::string description)
-{
-	return std::string("{") + title + "}" + "\n\n" + description;
-}
-
-void CInfoWindow::setDelComps(bool DelComps)
-{
-	delComps = DelComps;
-	for(CComponent *comp : components)
-	{
-		if(delComps)
-			comp->recActions |= DISPOSE;
-		else
-			comp->recActions &= ~DISPOSE;
-	}
-}
-
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free)
- :free(Free),bitmap(Bitmap)
-{
-	init(x, y);
-}
-
-
-CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free/*=false*/)
- : free(Free),bitmap(Bitmap)
-{
-	switch(alignment)
-	{
-	case BOTTOMRIGHT:
-		init(p.x - Bitmap->w, p.y - Bitmap->h);
-		break;
-	case CENTER:
-		init(p.x - Bitmap->w/2, p.y - Bitmap->h/2);
-		break;
-	case TOPLEFT:
-		init(p.x, p.y);
-		break;
-	default:
-		assert(0); //not implemented
-	}
-}
-
-CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
-{
-	CCS->curh->hide();
-
-	free=Free;
-	bitmap=Bitmap;
-
-	if(bitmap)
-	{
-		pos.x = screen->w/2 - bitmap->w/2;
-		pos.y = screen->h/2 - bitmap->h/2;
-		pos.h = bitmap->h;
-		pos.w = bitmap->w;
-	}
-}
-
-void CInfoPopup::close()
-{
-	if(free)
-		SDL_FreeSurface(bitmap);
-	GH.popIntTotally(this);
-}
-void CInfoPopup::show(SDL_Surface * to)
-{
-	blitAt(bitmap,pos.x,pos.y,to);
-}
-CInfoPopup::~CInfoPopup()
-{
-	CCS->curh->show();
-}
-
-void CInfoPopup::init(int x, int y)
-{
-	CCS->curh->hide();
-
-	pos.x = x;
-	pos.y = y;
-	pos.h = bitmap->h;
-	pos.w = bitmap->w;
-
-	// Put the window back on screen if necessary
-	vstd::amax(pos.x, 0);
-	vstd::amax(pos.y, 0);
-	vstd::amin(pos.x, screen->w - bitmap->w);
-	vstd::amin(pos.y, screen->h - bitmap->h);
-}
-
-CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize):
-	image(nullptr),
-    perDay(false)
-{
-	addUsedEvents(RCLICK);
-	init(Type, Subtype, Val, imageSize);
-}
-
-CComponent::CComponent(const Component &c):
-	image(nullptr),
-    perDay(false)
-{
-	addUsedEvents(RCLICK);
-
-	if(c.id == Component::RESOURCE && c.when==-1)
-		perDay = true;
-
-	init((Etype)c.id,c.subtype,c.val, large);
-}
-
-void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	compType = Type;
-	subtype = Subtype;
-	val = Val;
-	size = imageSize;
-
-	assert(compType < typeInvalid);
-	assert(size < sizeInvalid);
-
-	setSurface(getFileName()[size], getIndex());
-
-	pos.w = image->pos.w;
-	pos.h = image->pos.h;
-
-	EFonts font = FONT_SMALL;
-	if (imageSize < small)
-		font = FONT_TINY; //other sizes?
-
-	pos.h += 4; //distance between text and image
-
-	std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), font);
-	for(auto & line : textLines)
-	{
-		int height = graphics->fonts[font]->getLineHeight();
-		auto   label = new CLabel(pos.w/2, pos.h + height/2, font, CENTER, Colors::WHITE, line);
-
-		pos.h += height;
-		if (label->pos.w > pos.w)
-		{
-			pos.x -= (label->pos.w - pos.w)/2;
-			pos.w = label->pos.w;
-		}
-	}
-}
-
-const std::vector<std::string> CComponent::getFileName()
-{
-	static const std::string  primSkillsArr [] = {"PSKIL32",        "PSKIL32",        "PSKIL42",        "PSKILL"};
-	static const std::string  secSkillsArr [] =  {"SECSK32",        "SECSK32",        "SECSKILL",       "SECSK82"};
-	static const std::string  resourceArr [] =   {"SMALRES",        "RESOURCE",       "RESOUR82",       "RESOUR82"};
-	static const std::string  creatureArr [] =   {"CPRSMALL",       "CPRSMALL",       "TWCRPORT",       "TWCRPORT"};
-	static const std::string  artifactArr[]  =   {"Artifact",       "Artifact",       "Artifact",       "Artifact"};
-	static const std::string  spellsArr [] =     {"SpellInt",       "SpellInt",       "SPELLSCR",       "SPELLSCR"};
-	static const std::string  moraleArr [] =     {"IMRL22",         "IMRL30",         "IMRL42",         "imrl82"};
-	static const std::string  luckArr [] =       {"ILCK22",         "ILCK30",         "ILCK42",         "ilck82"};
-	static const std::string  heroArr [] =       {"PortraitsSmall", "PortraitsSmall", "PortraitsLarge", "PortraitsLarge"};
-	static const std::string  flagArr [] =       {"CREST58",        "CREST58",        "CREST58",        "CREST58"};
-
-	auto gen = [](const std::string * arr)
-	{
-		return std::vector<std::string>(arr, arr + 4);
-	};
-
-	switch(compType)
-	{
-	case primskill:  return gen(primSkillsArr);
-	case secskill:   return gen(secSkillsArr);
-	case resource:   return gen(resourceArr);
-	case creature:   return gen(creatureArr);
-	case artifact:   return gen(artifactArr);
-	case experience: return gen(primSkillsArr);
-	case spell:      return gen(spellsArr);
-	case morale:     return gen(moraleArr);
-	case luck:       return gen(luckArr);
-	case building:   return std::vector<std::string>(4, CGI->townh->factions[subtype]->town->clientInfo.buildingsIcons);
-	case hero:       return gen(heroArr);
-	case flag:       return gen(flagArr);
-	}
-	assert(0);
-	return std::vector<std::string>();
-}
-
-size_t CComponent::getIndex()
-{
-	switch(compType)
-	{
-	case primskill:  return subtype;
-	case secskill:   return subtype*3 + 3 + val - 1;
-	case resource:   return subtype;
-	case creature:   return CGI->creh->creatures[subtype]->iconIndex;
-	case artifact:   return CGI->arth->artifacts[subtype]->iconIndex;
-	case experience: return 4;
-	case spell:      return subtype;
-	case morale:     return val+3;
-	case luck:       return val+3;
-	case building:   return val;
-	case hero:       return subtype;
-	case flag:       return subtype;
-	}
-	assert(0);
-	return 0;
-}
-
-std::string CComponent::getDescription()
-{
-	switch (compType)
-	{
-	case primskill:  return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
-	                                     : CGI->generaltexth->allTexts[149]; //mana
-	case secskill:   return CGI->generaltexth->skillInfoTexts[subtype][val-1];
-	case resource:   return CGI->generaltexth->allTexts[242];
-	case creature:   return "";
-	case artifact:   return CGI->arth->artifacts[subtype]->Description();
-	case experience: return CGI->generaltexth->allTexts[241];
-	case spell:      return CGI->spellh->objects[subtype]->getLevelInfo(val).description;
-	case morale:     return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
-	case luck:       return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
-	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
-	case hero:       return "";
-	case flag:       return "";
-	}
-	assert(0);
-	return "";
-}
-
-std::string CComponent::getSubtitle()
-{
-	if (!perDay)
-		return getSubtitleInternal();
-
-	std::string ret = CGI->generaltexth->allTexts[3];
-	boost::replace_first(ret, "%d", getSubtitleInternal());
-	return ret;
-}
-
-std::string CComponent::getSubtitleInternal()
-{
-	//FIXME: some of these are horrible (e.g creature)
-	switch(compType)
-	{
-	case primskill:  return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387]));
-	case secskill:   return CGI->generaltexth->levels[val-1] + "\n" + CGI->generaltexth->skillName[subtype];
-	case resource:   return boost::lexical_cast<std::string>(val);
-	case creature:   return (val? boost::lexical_cast<std::string>(val) + " " : "") + CGI->creh->creatures[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing);
-	case artifact:   return CGI->arth->artifacts[subtype]->Name();
-	case experience:
-		{
-			if (subtype == 1) //+1 level - tree of knowledge
-			{
-				std::string level = CGI->generaltexth->allTexts[442];
-				boost::replace_first(level, "1", boost::lexical_cast<std::string>(val));
-				return level;
-			}
-			else
-				return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
-		}
-	case spell:      return CGI->spellh->objects[subtype]->name;
-	case morale:     return "";
-	case luck:       return "";
-	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
-	case hero:       return "";
-	case flag:       return CGI->generaltexth->capColors[subtype];
-	}
-	assert(0);
-	return "";
-}
-
-void CComponent::setSurface(std::string defName, int imgPos)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	vstd::clear_pointer(image);
-	image = new CAnimImage(defName, imgPos);
-}
-
-void CComponent::clickRight(tribool down, bool previousState)
-{
-	if(!getDescription().empty())
-		adventureInt->handleRightClick(getDescription(), down);
-}
-
-void CSelectableComponent::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-	{
-		if(onSelect)
-			onSelect();
-	}
-}
-
-void CSelectableComponent::init()
-{
-	selected = false;
-}
-
-CSelectableComponent::CSelectableComponent(const Component &c, std::function<void()> OnSelect):
-	CComponent(c),onSelect(OnSelect)
-{
-	type |= REDRAW_PARENT;
-	addUsedEvents(LCLICK | KEYBOARD);
-	init();
-}
-
-CSelectableComponent::CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize, std::function<void()> OnSelect):
-	CComponent(Type,Sub,Val, imageSize),onSelect(OnSelect)
-{
-	type |= REDRAW_PARENT;
-	addUsedEvents(LCLICK | KEYBOARD);
-	init();
-}
-
-void CSelectableComponent::select(bool on)
-{
-	if(on != selected)
-	{
-		selected = on;
-		redraw();
-	}
-}
-
-void CSelectableComponent::showAll(SDL_Surface * to)
-{
-	CComponent::showAll(to);
-	if(selected)
-	{
-		CSDL_Ext::drawBorder(to, Rect::around(image->pos), int3(239,215,123));
-	}
-}
-
-void CComponentBox::selectionChanged(CSelectableComponent * newSelection)
-{
-	if (newSelection == selected)
-		return;
-
-	if (selected)
-		selected->select(false);
-
-	selected = newSelection;
-	if (onSelect)
-		onSelect(selectedIndex());
-
-	if (selected)
-		selected->select(true);
-}
-
-int CComponentBox::selectedIndex()
-{
-	if (selected)
-		return std::find(components.begin(), components.end(), selected) - components.begin();
-	return -1;
-}
-
-Point CComponentBox::getOrTextPos(CComponent *left, CComponent *right)
-{
-	int leftSubtitle  = ( left->pos.w -  left->image->pos.w) / 2;
-	int rightSubtitle = (right->pos.w - right->image->pos.w) / 2;
-	int fullDistance = getDistance(left, right) + leftSubtitle + rightSubtitle;
-
-	return Point(fullDistance/2 - leftSubtitle, (left->image->pos.h + right->image->pos.h) / 4);
-}
-
-int CComponentBox::getDistance(CComponent *left, CComponent *right)
-{
-	static const int betweenImagesMin = 20;
-	static const int betweenSubtitlesMin = 10;
-
-	int leftSubtitle  = ( left->pos.w -  left->image->pos.w) / 2;
-	int rightSubtitle = (right->pos.w - right->image->pos.w) / 2;
-	int subtitlesOffset = leftSubtitle + rightSubtitle;
-
-	return std::max(betweenSubtitlesMin, betweenImagesMin - subtitlesOffset);
-}
-
-void CComponentBox::placeComponents(bool selectable)
-{
-	static const int betweenRows = 22;
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if (components.empty())
-		return;
-
-	//prepare components
-	for(auto & comp : components)
-	{
-		addChild(comp);
-		comp->moveTo(Point(pos.x, pos.y));
-	}
-
-	struct RowData
-	{
-		size_t comps;
-		int width;
-		int height;
-		RowData (size_t Comps, int Width, int Height):
-		comps(Comps), width (Width), height (Height){};
-	};
-	std::vector<RowData> rows;
-	rows.push_back (RowData (0,0,0));
-
-	//split components in rows
-	CComponent * prevComp = nullptr;
-
-	for(CComponent * comp : components)
-	{
-		//make sure that components are smaller than our width
-		//assert(pos.w == 0 || pos.w < comp->pos.w);
-
-		const int distance = prevComp ? getDistance(prevComp, comp) : 0;
-
-		//start next row
-		if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full
-			|| rows.back().comps >= 4) // no more than 4 comps per row
-		{
-			prevComp = nullptr;
-			rows.push_back (RowData (0,0,0));
-		}
-
-		if (prevComp)
-			rows.back().width += distance;
-
-		rows.back().comps++;
-		rows.back().width += comp->pos.w;
-
-		vstd::amax(rows.back().height, comp->pos.h);
-		prevComp = comp;
-	}
-
-	if (pos.w == 0)
-	{
-		for(auto & row : rows)
-			vstd::amax(pos.w, row.width);
-	}
-
-	int height = (rows.size() - 1) * betweenRows;
-	for(auto & row : rows)
-		height += row.height;
-
-	//assert(pos.h == 0 || pos.h < height);
-	if (pos.h == 0)
-		pos.h = height;
-
-	auto iter = components.begin();
-	int currentY = (pos.h - height) / 2;
-
-	//move components to their positions
-	for (auto & rows_row : rows)
-	{
-		// amount of free space we may add on each side of every component
-		int freeSpace = (pos.w - rows_row.width) / (rows_row.comps * 2);
-		prevComp = nullptr;
-
-		int currentX = 0;
-		for (size_t col = 0; col < rows_row.comps; col++)
-		{
-			currentX += freeSpace;
-			if (prevComp)
-			{
-				if (selectable)
-				{
-					Point orPos = Point(currentX - freeSpace, currentY) + getOrTextPos(prevComp, *iter);
-
-					new CLabel(orPos.x, orPos.y, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[4]);
-				}
-				currentX += getDistance(prevComp, *iter);
-			}
-
-			(*iter)->moveBy(Point(currentX, currentY));
-			currentX += (*iter)->pos.w;
-			currentX += freeSpace;
-
-			prevComp = *(iter++);
-		}
-		currentY += rows_row.height + betweenRows;
-	}
-}
-
-CComponentBox::CComponentBox(CComponent * _components, Rect position):
-    components(1, _components),
-    selected(nullptr)
-{
-	type |= REDRAW_PARENT;
-	pos = position + pos;
-	placeComponents(false);
-}
-
-CComponentBox::CComponentBox(std::vector<CComponent *> _components, Rect position):
-    components(_components),
-    selected(nullptr)
-{
-	type |= REDRAW_PARENT;
-	pos = position + pos;
-	placeComponents(false);
-}
-
-CComponentBox::CComponentBox(std::vector<CSelectableComponent *> _components, Rect position, std::function<void(int newID)> _onSelect):
-    components(_components.begin(), _components.end()),
-    selected(nullptr),
-    onSelect(_onSelect)
-{
-	type |= REDRAW_PARENT;
-	pos = position + pos;
-	placeComponents(true);
-
-	assert(!components.empty());
-
-	int key = SDLK_1;
-	for(auto & comp : _components)
-	{
-		comp->onSelect = std::bind(&CComponentBox::selectionChanged, this, comp);
-		comp->assignedKeys.insert(key++);
-	}
-	selectionChanged(_components.front());
-}
-
-void CSelWindow::selectionChange(unsigned to)
-{
-	for (unsigned i=0;i<components.size();i++)
-	{
-		CSelectableComponent * pom = dynamic_cast<CSelectableComponent*>(components[i]);
-		if (!pom)
-			continue;
-		pom->select(i==to);
-	}
-	redraw();
-}
-
-CSelWindow::CSelWindow(const std::string &Text, PlayerColor player, int charperline, const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	ID = askID;
-	for(int i=0;i<Buttons.size();i++)
-	{
-		buttons.push_back(new CAdventureMapButton("","",Buttons[i].second,0,0,Buttons[i].first));
-		if(!i  &&  askID.getNum() >= 0)
-			buttons.back()->callback += std::bind(&CSelWindow::madeChoice,this);
-		buttons[i]->callback += std::bind(&CInfoWindow::close,this); //each button will close the window apart from call-defined actions
-	}
-
-	text = new CTextBox(Text, Rect(0, 0, 250, 100), 0, FONT_MEDIUM, CENTER, Colors::WHITE);
-
-	buttons.front()->assignedKeys.insert(SDLK_RETURN); //first button - reacts on enter
-	buttons.back()->assignedKeys.insert(SDLK_ESCAPE); //last button - reacts on escape
-
-	if(buttons.size() > 1  &&  askID.getNum() >= 0) //cancel button functionality
-		buttons.back()->callback += std::bind(&CCallback::selectionMade,LOCPLINT->cb.get(),0,askID);
-
-	for(int i=0;i<comps.size();i++)
-	{
-		comps[i]->recActions = 255;
-		addChild(comps[i]);
-		components.push_back(comps[i]);
-		comps[i]->onSelect = std::bind(&CSelWindow::selectionChange,this,i);
-		if(i<9)
-			comps[i]->assignedKeys.insert(SDLK_1+i);
-	}
-	CMessage::drawIWindow(this, Text, player);
-}
-
-void CSelWindow::madeChoice()
-{
-	if(ID.getNum() < 0)
-		return;
-	int ret = -1;
-	for (int i=0;i<components.size();i++)
-	{
-		if(dynamic_cast<CSelectableComponent*>(components[i])->selected)
-		{
-			ret = i;
-		}
-	}
-	LOCPLINT->cb->selectionMade(ret+1,ID);
-}
-
-CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool Animated)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	pos.x+=x;
-	pos.y+=y;
-
-	TFaction faction = cre->faction;
-
-	assert(CGI->townh->factions.size() > faction);
-
-	if(Big)
-		bg = new CPicture(CGI->townh->factions[faction]->creatureBg130);
-	else
-		bg = new CPicture(CGI->townh->factions[faction]->creatureBg120);
-	bg->needRefresh = true;
-	anim = new CCreatureAnim(0, 0, cre->animDefName, Rect());
-	anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h);
-	anim->startPreview(cre->hasBonusOfType(Bonus::SIEGE_WEAPON));
-
-	pos.w = bg->pos.w;
-	pos.h = bg->pos.h;
-}
-
-CRecruitmentWindow::CCreatureCard::CCreatureCard(CRecruitmentWindow *window, const CCreature *crea, int totalAmount):
-    CIntObject(LCLICK | RCLICK),
-    parent(window),
-    selected(false),
-    creature(crea),
-    amount(totalAmount)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	pic = new CCreaturePic(1,1, creature, true, true);
-	// 1 + 1 px for borders
-	pos.w = pic->pos.w + 2;
-	pos.h = pic->pos.h + 2;
-}
-
-void CRecruitmentWindow::CCreatureCard::select(bool on)
-{
-	selected = on;
-	redraw();
-}
-
-void CRecruitmentWindow::CCreatureCard::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-		parent->select(this);
-}
-
-void CRecruitmentWindow::CCreatureCard::clickRight(tribool down, bool previousState)
-{
-	if (down)
-		GH.pushInt(createCreWindow(creature->idNumber, CCreatureWindow::OTHER, 0));
-}
-
-void CRecruitmentWindow::CCreatureCard::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-	if (selected)
-		drawBorder(to, pos, int3(248, 0, 0));
-	else
-		drawBorder(to, pos, int3(232, 212, 120));
-}
-
-CRecruitmentWindow::CCostBox::CCostBox(Rect position, std::string title)
-{
-	type |= REDRAW_PARENT;
-	pos = position + pos;
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CLabel(pos.w/2, 10, FONT_SMALL, CENTER, Colors::WHITE, title);
-}
-
-void CRecruitmentWindow::CCostBox::set(TResources res)
-{
-	//just update values
-	for(auto & item : resources)
-	{
-		item.second.first->setText(boost::lexical_cast<std::string>(res[item.first]));
-	}
-}
-
-void CRecruitmentWindow::CCostBox::createItems(TResources res)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	for(auto & curr : resources)
-	{
-		delete curr.second.first;
-		delete curr.second.second;
-	}
-	resources.clear();
-
-	TResources::nziterator iter(res);
-	while (iter.valid())
-	{
-		CAnimImage * image = new CAnimImage("RESOURCE", iter->resType);
-		CLabel * text = new CLabel(15, 43, FONT_SMALL, CENTER, Colors::WHITE, "0");
-
-		resources.insert(std::make_pair(iter->resType, std::make_pair(text, image)));
-		iter++;
-	}
-
-	if (!resources.empty())
-	{
-		int curx = pos.w / 2 - (16 * resources.size()) - (8 * (resources.size() - 1));
-		//reverse to display gold as first resource
-		for (auto & res : boost::adaptors::reverse(resources))
-		{
-			res.second.first->moveBy(Point(curx, 22));
-			res.second.second->moveBy(Point(curx, 22));
-			curx += 48;
-		}
-	}
-	redraw();
-}
-
-void CRecruitmentWindow::select(CCreatureCard *card)
-{
-	if (card == selected)
-		return;
-
-	if (selected)
-		selected->select(false);
-
-	selected = card;
-
-	if (selected)
-		selected->select(true);
-
-	if (card)
-	{
-		si32 maxAmount = card->creature->maxAmount(LOCPLINT->cb->getResourceAmount());
-
-		vstd::amin(maxAmount, card->amount);
-
-		slider->setAmount(maxAmount);
-
-		if(slider->value != maxAmount)
-			slider->moveTo(maxAmount);
-		else // if slider already at 0 - emulate call to sliderMoved()
-			sliderMoved(maxAmount);
-
-		costPerTroopValue->createItems(card->creature->cost);
-		totalCostValue->createItems(card->creature->cost);
-
-		costPerTroopValue->set(card->creature->cost);
-		totalCostValue->set(card->creature->cost * maxAmount);
-
-		//Recruit %s
-		title->setText(boost::str(boost::format(CGI->generaltexth->tcommands[21]) % card->creature->namePl));
-
-		maxButton->block(maxAmount == 0);
-		slider->block(maxAmount == 0);
-	}
-}
-
-void CRecruitmentWindow::buy()
-{
-	CreatureID crid =  selected->creature->idNumber;
-	SlotID dstslot = dst-> getSlotFor(crid);
-
-	if(!dstslot.validSlot() && !vstd::contains(CGI->arth->bigArtifacts,CGI->arth->creatureToMachineID(crid))) //no available slot
-	{
-		std::string txt;
-		if(dst->ID == Obj::HERO)
-		{
-			txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them.
-			boost::algorithm::replace_first(txt, "%s", slider->value > 1 ? CGI->creh->creatures[crid]->namePl : CGI->creh->creatures[crid]->nameSing);
-		}
-		else
-		{
-			txt = CGI->generaltexth->allTexts[17]; //There is no room in the garrison for this army.
-		}
-
-		LOCPLINT->showInfoDialog(txt);
-		return;
-	}
-
-	onRecruit(crid, slider->value);
-	if(level >= 0)
-		close();
-}
-
-void CRecruitmentWindow::showAll(SDL_Surface * to)
-{
-	CWindowObject::showAll(to);
-
-	// recruit\total values
-	drawBorder(to, pos.x + 172, pos.y + 222, 67, 42, int3(239,215,123));
-	drawBorder(to, pos.x + 246, pos.y + 222, 67, 42, int3(239,215,123));
-
-	//cost boxes
-	drawBorder(to, pos.x + 64,  pos.y + 222, 99, 76, int3(239,215,123));
-	drawBorder(to, pos.x + 322, pos.y + 222, 99, 76, int3(239,215,123));
-
-	//buttons borders
-	drawBorder(to, pos.x + 133, pos.y + 312, 66, 34, int3(173,142,66));
-	drawBorder(to, pos.x + 211, pos.y + 312, 66, 34, int3(173,142,66));
-	drawBorder(to, pos.x + 289, pos.y + 312, 66, 34, int3(173,142,66));
-}
-
-CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const std::function<void(CreatureID,int)> &Recruit, int y_offset):
-    CWindowObject(PLAYER_COLORED, "TPRCRT"),
-	onRecruit(Recruit),
-    level(Level),
-    dst(Dst),
-    selected(nullptr),
-    dwelling(Dwelling)
-{
-	moveBy(Point(0, y_offset));
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	slider = new CSlider(176,279,135,nullptr,0,0,0,true);
-	slider->moved = std::bind(&CRecruitmentWindow::sliderMoved,this, _1);
-
-	maxButton = new CAdventureMapButton(CGI->generaltexth->zelp[553],std::bind(&CSlider::moveToMax,slider),134,313,"IRCBTNS.DEF",SDLK_m);
-	buyButton = new CAdventureMapButton(CGI->generaltexth->zelp[554],std::bind(&CRecruitmentWindow::buy,this),212,313,"IBY6432.DEF",SDLK_RETURN);
-	cancelButton = new CAdventureMapButton(CGI->generaltexth->zelp[555],std::bind(&CRecruitmentWindow::close,this),290,313,"ICN6432.DEF",SDLK_ESCAPE);
-
-	title = new CLabel(243, 32, FONT_BIG, CENTER, Colors::YELLOW);
-	availableValue = new CLabel(205, 253, FONT_SMALL, CENTER, Colors::WHITE);
-	toRecruitValue = new CLabel(279, 253, FONT_SMALL, CENTER, Colors::WHITE);
-
-	costPerTroopValue =  new CCostBox(Rect(65, 222, 97, 74), CGI->generaltexth->allTexts[346]);
-	totalCostValue = new CCostBox(Rect(323, 222, 97, 74), CGI->generaltexth->allTexts[466]);
-
-	new CLabel(205, 233, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[465]); //available t
-	new CLabel(279, 233, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[16]); //recruit t
-
-	availableCreaturesChanged();
-}
-
-void CRecruitmentWindow::availableCreaturesChanged()
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	size_t selectedIndex = 0;
-
-	if (!cards.empty() && selected) // find position of selected item
-		selectedIndex = std::find(cards.begin(), cards.end(), selected) - cards.begin();
-
-	//deselect card
-	select(nullptr);
-
-	//delete old cards
-	for(auto & card : cards)
-		delete card;
-	cards.clear();
-
-	for(int i=0; i<dwelling->creatures.size(); i++)
-	{
-		//find appropriate level
-		if(level >= 0 && i != level)
-			continue;
-
-		int amount = dwelling->creatures[i].first;
-
-		//create new cards
-		for(auto & creature : boost::adaptors::reverse(dwelling->creatures[i].second))
-			cards.push_back(new CCreatureCard(this, CGI->creh->creatures[creature], amount));
-	}
-
-	assert(!cards.empty());
-
-	const int creatureWidth = 102;
-
-	//normal distance between cards - 18px
-	int requiredSpace = 18;
-	//maximum distance we can use without reaching window borders
-	int availableSpace = pos.w - 50 - creatureWidth * cards.size();
-
-	if (cards.size() > 1) // avoid division by zero
-		availableSpace /= cards.size() - 1;
-	else
-		availableSpace = 0;
-
-	assert(availableSpace >= 0);
-
-	const int spaceBetween = std::min(requiredSpace, availableSpace);
-	const int totalCreatureWidth = spaceBetween + creatureWidth;
-
-	//now we know total amount of cards and can move them to correct position
-	int curx = pos.w / 2 - (creatureWidth*cards.size()/2) - (spaceBetween*(cards.size()-1)/2);
-	for(auto & card : cards)
-	{
-		card->moveBy(Point(curx, 64));
-		curx += totalCreatureWidth;
-	}
-
-	//restore selection
-	select(cards[selectedIndex]);
-
-	if(slider->value == slider->amount)
-		slider->moveTo(slider->amount);
-	else // if slider already at 0 - emulate call to sliderMoved()
-		sliderMoved(slider->amount);
-}
-
-void CRecruitmentWindow::sliderMoved(int to)
-{
-	if (!selected)
-		return;
-
-	buyButton->block(!to);
-	availableValue->setText(boost::lexical_cast<std::string>(selected->amount - to));
-	toRecruitValue->setText(boost::lexical_cast<std::string>(to));
-
-	totalCostValue->set(selected->creature->cost * to);
-}
-
-CSplitWindow::CSplitWindow(const CCreature * creature, std::function<void(int, int)> callback_,
-                           int leftMin_, int rightMin_, int leftAmount_, int rightAmount_):
-    CWindowObject(PLAYER_COLORED, "GPUCRDIV"),
-    callback(callback_),
-    leftAmount(leftAmount_),
-    rightAmount(rightAmount_),
-    leftMin(leftMin_),
-    rightMin(rightMin_),
-    slider(nullptr)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	int total = leftAmount + rightAmount;
-	int leftMax = total - rightMin;
-	int rightMax = total - leftMin;
-
-	ok = new CAdventureMapButton("", "", std::bind(&CSplitWindow::apply, this), 20, 263, "IOK6432", SDLK_RETURN);
-	cancel = new CAdventureMapButton("", "", std::bind(&CSplitWindow::close, this), 214, 263, "ICN6432", SDLK_ESCAPE);
-
-	int sliderPositions = total - leftMin - rightMin;
-
-	leftInput = new CTextInput(Rect(20, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, true));
-	rightInput = new CTextInput(Rect(176, 218, 100, 36), FONT_BIG, std::bind(&CSplitWindow::setAmountText, this, _1, false));
-
-	//add filters to allow only number input
-	leftInput->filters.add(std::bind(&CTextInput::numberFilter, _1, _2, leftMin, leftMax));
-	rightInput->filters.add(std::bind(&CTextInput::numberFilter, _1, _2, rightMin, rightMax));
-
-	leftInput->setText(boost::lexical_cast<std::string>(leftAmount), false);
-	rightInput->setText(boost::lexical_cast<std::string>(rightAmount), false);
-
-	animLeft = new CCreaturePic(20, 54, creature, true, false);
-	animRight = new CCreaturePic(177, 54,creature, true, false);
-
-	slider = new CSlider(21, 194, 257, std::bind(&CSplitWindow::sliderMoved, this, _1), 0, sliderPositions, rightAmount - rightMin, true);
-
-	std::string title = CGI->generaltexth->allTexts[256];
-	boost::algorithm::replace_first(title,"%s", creature->namePl);
-	new CLabel(150, 34, FONT_BIG, CENTER, Colors::YELLOW, title);
-}
-
-void CSplitWindow::setAmountText(std::string text, bool left)
-{
-	try
-	{
-		setAmount(boost::lexical_cast<int>(text), left);
-		slider->moveTo(rightAmount - rightMin);
-	}
-	catch(boost::bad_lexical_cast &)
-	{
-	}
-}
-
-void CSplitWindow::setAmount(int value, bool left)
-{
-	int total = leftAmount + rightAmount;
-	leftAmount  = left ? value : total - value;
-	rightAmount = left ? total - value : value;
-
-	leftInput->setText(boost::lexical_cast<std::string>(leftAmount));
-	rightInput->setText(boost::lexical_cast<std::string>(rightAmount));
-}
-
-void CSplitWindow::apply()
-{
-	callback(leftAmount, rightAmount);
-	close();
-}
-
-void CSplitWindow::sliderMoved(int to)
-{
-	setAmount(rightMin + to, false);
-}
-
-CLevelWindow::CLevelWindow(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, std::function<void(ui32)> callback):
-    CWindowObject(PLAYER_COLORED, "LVLUPBKG"),
-    cb(callback)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	LOCPLINT->showingDialog->setn(true);
-
-	new CAnimImage("PortraitsLarge", hero->portrait, 0, 170, 66);
-	new CAdventureMapButton("", "", std::bind(&CLevelWindow::close, this), 297, 413, "IOKAY", SDLK_RETURN);
-
-	//%s has gained a level.
-	new CLabel(192, 33, FONT_MEDIUM, CENTER, Colors::WHITE,
-	           boost::str(boost::format(CGI->generaltexth->allTexts[444]) % hero->name));
-
-	//%s is now a level %d %s.
-	new CLabel(192, 162, FONT_MEDIUM, CENTER, Colors::WHITE,
-	           boost::str(boost::format(CGI->generaltexth->allTexts[445]) % hero->name % hero->level % hero->type->heroClass->name));
-
-	new CAnimImage("PSKIL42", pskill, 0, 174, 190);
-
-	new CLabel(192, 253, FONT_MEDIUM, CENTER, Colors::WHITE,
-	           CGI->generaltexth->primarySkillNames[pskill] + " +1");
-
-	if (!skills.empty())
-	{
-		std::vector<CSelectableComponent *> comps;
-
-		for(auto & skill : skills)
-		{
-			comps.push_back(new CSelectableComponent(
-								CComponent::secskill,
-								skill,
-								hero->getSecSkillLevel( SecondarySkill(skill) )+1,
-								CComponent::medium));
-		}
-		box = new CComponentBox(comps, Rect(75, 300, pos.w - 150, 100));
-	}
-	else
-		box = nullptr;
-}
-
-CLevelWindow::~CLevelWindow()
-{
-	//FIXME: call callback if there was nothing to select?
-	if (box && box->selectedIndex() != -1)
-		cb(box->selectedIndex());
-
-	LOCPLINT->showingDialog->setn(false);
-}
-
-void CMinorResDataBar::show(SDL_Surface * to)
-{
-}
-
-void CMinorResDataBar::showAll(SDL_Surface * to)
-{
-	blitAt(bg,pos.x,pos.y,to);
-	for (Res::ERes i=Res::WOOD; i<=Res::GOLD; vstd::advance(i, 1))
-	{
-		std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
-
-		graphics->fonts[FONT_SMALL]->renderTextCenter(to, text, Colors::WHITE, Point(pos.x + 50 + 76 * i, pos.y + pos.h/2));
-	}
-	std::vector<std::string> temp;
-
-	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::MONTH)));
-	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK)));
-	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)));
-
-	std::string datetext =  CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
-	                        + ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
-
-	graphics->fonts[FONT_SMALL]->renderTextCenter(to, processStr(datetext,temp), Colors::WHITE, Point(pos.x+545+(pos.w-545)/2,pos.y+pos.h/2));
-}
-
-CMinorResDataBar::CMinorResDataBar()
-{
-	bg = BitmapHandler::loadBitmap("KRESBAR.bmp");
-	CSDL_Ext::setDefaultColorKey(bg);	
-	graphics->blueToPlayersAdv(bg,LOCPLINT->playerID);
-	pos.x = 7;
-	pos.y = 575;
-	pos.w = bg->w;
-	pos.h = bg->h;
-}
-CMinorResDataBar::~CMinorResDataBar()
-{
-	SDL_FreeSurface(bg);
-}
-
-CObjectListWindow::CItem::CItem(CObjectListWindow *_parent, size_t _id, std::string _text):
-	parent(_parent),
-	index(_id)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	border = new CPicture("TPGATES");
-	pos = border->pos;
-	addUsedEvents(LCLICK);
-	type |= REDRAW_PARENT;
-
-	text = new CLabel(pos.w/2, pos.h/2, FONT_SMALL, CENTER, Colors::WHITE, _text);
-	select(index == parent->selected);
-}
-
-void CObjectListWindow::CItem::select(bool on)
-{
-	if (on)
-		border->recActions = 255;
-	else
-		border->recActions = ~(UPDATE | SHOWALL);
-	redraw();
-}
-
-void CObjectListWindow::CItem::clickLeft(tribool down, bool previousState)
-{
-	if( previousState && !down)
-		parent->changeSelection(index);
-}
-
-CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
-				std::function<void(int)> Callback):
-    CWindowObject(PLAYER_COLORED, "TPGATE"),
-	onSelect(Callback)
-{
-	items.reserve(_items.size());
-	for(int id : _items)
-	{
-		items.push_back(std::make_pair(id, CGI->mh->map->objects[id]->getObjectName()));
-	}
-
-	init(titlePic, _title, _descr);
-}
-
-CObjectListWindow::CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
-				std::function<void(int)> Callback):
-    CWindowObject(PLAYER_COLORED, "TPGATE"),
-	onSelect(Callback)
-{
-	items.reserve(_items.size());
-
-	for (size_t i=0; i<_items.size(); i++)
-		items.push_back(std::make_pair(int(i), _items[i]));
-
-	init(titlePic, _title, _descr);
-}
-
-void CObjectListWindow::init(CIntObject * titlePic, std::string _title, std::string _descr)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	title = new CLabel(152, 27, FONT_BIG, CENTER, Colors::YELLOW, _title);
-	descr = new CLabel(145, 133, FONT_SMALL, CENTER, Colors::WHITE, _descr);
-
-	ok = new CAdventureMapButton("","",std::bind(&CObjectListWindow::elementSelected, this),15,402,"IOKAY.DEF", SDLK_RETURN);
-	ok->block(true);
-	exit = new CAdventureMapButton("","",std::bind(&CGuiHandler::popIntTotally,&GH, this),228,402,"ICANCEL.DEF",SDLK_ESCAPE);
-
-	if (titlePic)
-	{
-		titleImage = titlePic;
-		addChild(titleImage);
-		titleImage->recActions = defActions;
-		titleImage->pos.x = pos.w/2 + pos.x - titleImage->pos.w/2;
-		titleImage->pos.y =75 + pos.y - titleImage->pos.h/2;
-	}
-	list = new CListBox(std::bind(&CObjectListWindow::genItem, this, _1), CListBox::DestroyFunc(),
-		Point(14, 151), Point(0, 25), 9, items.size(), 0, 1, Rect(262, -32, 256, 256) );
-	list->type |= REDRAW_PARENT;
-}
-
-CIntObject * CObjectListWindow::genItem(size_t index)
-{
-	if (index < items.size())
-		return new CItem(this, index, items[index].second);
-	return nullptr;
-}
-
-void CObjectListWindow::elementSelected()
-{
-	std::function<void(int)> toCall = onSelect;//save
-	int where = items[selected].first;      //required variables
-	GH.popIntTotally(this);//then destroy window
-	toCall(where);//and send selected object
-}
-
-void CObjectListWindow::changeSelection(size_t which)
-{
-	ok->block(false);
-	if (selected == which)
-		return;
-
-	std::list< CIntObject * > elements = list->getItems();
-	for(CIntObject * element : elements)
-	{
-		CItem *item;
-		if ( (item = dynamic_cast<CItem*>(element)) )
-		{
-			if (item->index == selected)
-				item->select(false);
-			if (item->index == which)
-				item->select(true);
-		}
-	}
-	selected = which;
-}
-
-void CObjectListWindow::keyPressed (const SDL_KeyboardEvent & key)
-{
-	if(key.state != SDL_PRESSED)
-		return;
-
-	int sel = selected;
-
-	switch(key.keysym.sym)
-	{
-	break; case SDLK_UP:
-		sel -=1;
-
-	break; case SDLK_DOWN:
-		sel +=1;
-
-	break; case SDLK_PAGEUP:
-		sel -=9;
-
-	break; case SDLK_PAGEDOWN:
-		sel +=9;
-
-	break; case SDLK_HOME:
-		sel = 0;
-
-	break; case SDLK_END:
-		sel = items.size();
-
-	break; default:
-		return;
-	}
-
-	vstd::abetween(sel, 0, items.size()-1);
-	list->scrollTo(sel);
-	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 = std::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],std::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
-	ok->assignedKeys.insert(SDLK_ESCAPE);
-	deal = new CAdventureMapButton(CGI->generaltexth->zelp[595],std::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF");
-	deal->block(true);
-
-	if(sliderNeeded)
-	{
-		slider = new CSlider(231,490,137,nullptr,0,0);
-		slider->moved = std::bind(&CMarketplaceWindow::sliderMoved,this,_1);
-		max = new CAdventureMapButton(CGI->generaltexth->zelp[596],std::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],std::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF");
-	if(printButtonFor(EMarketMode::RESOURCE_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[605],std::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF");
-	if(printButtonFor(EMarketMode::CREATURE_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[599],std::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],std::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF");
-	if(printButtonFor(EMarketMode::ARTIFACT_RESOURCE))
-		new CAdventureMapButton(CGI->generaltexth->zelp[613],std::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 = std::bind(&CAltarWindow::sliderMoved,this,_1);
-		max = new CAdventureMapButton(CGI->generaltexth->zelp[578],std::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF");
-
-		sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0);
-		sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[579],std::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], std::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTFILL.DEF");
-		sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty());
-		sacrificeBackpack = new CAdventureMapButton(CGI->generaltexth->zelp[570],std::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],std::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN);
-	ok->assignedKeys.insert(SDLK_ESCAPE);
-
-	deal = new CAdventureMapButton(CGI->generaltexth->zelp[585],std::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF");
-
-	if(Hero->getAlignment() != ::EAlignment::EVIL && Mode == EMarketMode::CREATURE_EXP)
-		new CAdventureMapButton(CGI->generaltexth->zelp[580], std::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], std::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"];
-	volume->Float() = newVolume;
-}
-
-void CSystemOptionsWindow::setSoundVolume( int newVolume )
-{
-	Settings volume = settings.write["general"]["sound"];
-	volume->Float() = newVolume;
-}
-
-void CSystemOptionsWindow::setHeroMoveSpeed( int newSpeed )
-{
-	Settings speed = settings.write["adventure"]["heroSpeed"];
-	speed->Float() = newSpeed;
-}
-
-void CSystemOptionsWindow::setMapScrollingSpeed( int newSpeed )
-{
-	Settings speed = settings.write["adventure"]["scrollSpeed"];
-	speed->Float() = newSpeed;
-}
-
-CSystemOptionsWindow::CSystemOptionsWindow():
-    CWindowObject(PLAYER_COLORED, "SysOpBck"),
-    onFullscreenChanged(settings.listen["video"]["fullscreen"])
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	title = new CLabel(242, 32, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[568]);
-
-	const JsonNode & texts = CGI->generaltexth->localizedTexts["systemOptions"];
-
-	//left window section
-	leftGroup =  new CLabelGroup(FONT_MEDIUM, CENTER, Colors::YELLOW);
-	leftGroup->add(122,  64, CGI->generaltexth->allTexts[569]);
-	leftGroup->add(122, 130, CGI->generaltexth->allTexts[570]);
-	leftGroup->add(122, 196, CGI->generaltexth->allTexts[571]);
-	leftGroup->add(122, 262, texts["resolutionButton"]["label"].String());
-	leftGroup->add(122, 347, CGI->generaltexth->allTexts[394]);
-	leftGroup->add(122, 412, CGI->generaltexth->allTexts[395]);
-
-	//right section
-	rightGroup = new CLabelGroup(FONT_MEDIUM, TOPLEFT, Colors::WHITE);
-	rightGroup->add(282, 57,  CGI->generaltexth->allTexts[572]);
-	rightGroup->add(282, 89,  CGI->generaltexth->allTexts[573]);
-	rightGroup->add(282, 121, CGI->generaltexth->allTexts[574]);
-	rightGroup->add(282, 153, CGI->generaltexth->allTexts[577]);
-	rightGroup->add(282, 185, texts["creatureWindowButton"]["label"].String());
-	rightGroup->add(282, 217, texts["fullscreenButton"]["label"].String());
-
-	//setting up buttons
-	load = new CAdventureMapButton (CGI->generaltexth->zelp[321].first, CGI->generaltexth->zelp[321].second,
-									std::bind(&CSystemOptionsWindow::bloadf, this), 246,  298, "SOLOAD.DEF", SDLK_l);
-	load->swappedImages = true;
-	load->update();
-
-	save = new CAdventureMapButton (CGI->generaltexth->zelp[322].first, CGI->generaltexth->zelp[322].second,
-	                                std::bind(&CSystemOptionsWindow::bsavef, this), 357, 298, "SOSAVE.DEF", SDLK_s);
-	save->swappedImages = true;
-	save->update();
-
-	restart = new CAdventureMapButton (CGI->generaltexth->zelp[323].first, CGI->generaltexth->zelp[323].second,
-									   std::bind(&CSystemOptionsWindow::brestartf, this), 246, 357, "SORSTRT", SDLK_r);
-	restart->swappedImages = true;
-	restart->update();
-
-	mainMenu = new CAdventureMapButton (CGI->generaltexth->zelp[320].first, CGI->generaltexth->zelp[320].second,
-	                                    std::bind(&CSystemOptionsWindow::bmainmenuf, this), 357, 357, "SOMAIN.DEF", SDLK_m);
-	mainMenu->swappedImages = true;
-	mainMenu->update();
-
-	quitGame = new CAdventureMapButton (CGI->generaltexth->zelp[324].first, CGI->generaltexth->zelp[324].second,
-	                                    std::bind(&CSystemOptionsWindow::bquitf, this), 246, 415, "soquit.def", SDLK_q);
-	quitGame->swappedImages = true;
-	quitGame->update();
-	backToMap = new CAdventureMapButton (CGI->generaltexth->zelp[325].first, CGI->generaltexth->zelp[325].second,
-	                                     std::bind(&CSystemOptionsWindow::breturnf, this), 357, 415, "soretrn.def", SDLK_RETURN);
-	backToMap->swappedImages = true;
-	backToMap->update();
-	backToMap->assignedKeys.insert(SDLK_ESCAPE);
-
-	heroMoveSpeed = new CHighlightableButtonsGroup(0);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[349].second),CGI->generaltexth->zelp[349].second, "sysopb1.def", 28, 77, 1);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].second),CGI->generaltexth->zelp[350].second, "sysopb2.def", 76, 77, 2);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].second),CGI->generaltexth->zelp[351].second, "sysopb3.def", 124, 77, 4);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].second),CGI->generaltexth->zelp[352].second, "sysopb4.def", 172, 77, 8);
-	heroMoveSpeed->select(settings["adventure"]["heroSpeed"].Float(), 1);
-	heroMoveSpeed->onChange = std::bind(&CSystemOptionsWindow::setHeroMoveSpeed, this, _1);
-
-	mapScrollSpeed = new CHighlightableButtonsGroup(0);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].second),CGI->generaltexth->zelp[357].second, "sysopb9.def", 28, 210, 1);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].second),CGI->generaltexth->zelp[358].second, "sysob10.def", 92, 210, 2);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].second),CGI->generaltexth->zelp[359].second, "sysob11.def", 156, 210, 4);
-	mapScrollSpeed->select(settings["adventure"]["scrollSpeed"].Float(), 1);
-	mapScrollSpeed->onChange = std::bind(&CSystemOptionsWindow::setMapScrollingSpeed, this, _1);
-
-	musicVolume = new CHighlightableButtonsGroup(0, true);
-	for(int i=0; i<10; ++i)
-		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].second),CGI->generaltexth->zelp[326+i].second, "syslb.def", 29 + 19*i, 359, i*11);
-
-	musicVolume->select(CCS->musich->getVolume(), 1);
-	musicVolume->onChange = std::bind(&CSystemOptionsWindow::setMusicVolume, this, _1);
-
-	effectsVolume = new CHighlightableButtonsGroup(0, true);
-	for(int i=0; i<10; ++i)
-		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].second),CGI->generaltexth->zelp[336+i].second, "syslb.def", 29 + 19*i, 425, i*11);
-
-	effectsVolume->select(CCS->soundh->getVolume(), 1);
-	effectsVolume->onChange = std::bind(&CSystemOptionsWindow::setSoundVolume, this, _1);
-
-	showReminder = new CHighlightableButton(
-		std::bind(&CSystemOptionsWindow::toggleReminder, this, true), std::bind(&CSystemOptionsWindow::toggleReminder, this, false),
-		std::map<int,std::string>(), CGI->generaltexth->zelp[361].second, false, "sysopchk.def", nullptr, 246, 87, false);
-
-	quickCombat = new CHighlightableButton(
-		std::bind(&CSystemOptionsWindow::toggleQuickCombat, this, true), std::bind(&CSystemOptionsWindow::toggleQuickCombat, this, false),
-		std::map<int,std::string>(), CGI->generaltexth->zelp[362].second, false, "sysopchk.def", nullptr, 246, 87+32, false);
-
-	spellbookAnim = new CHighlightableButton(
-		std::bind(&CSystemOptionsWindow::toggleSpellbookAnim, this, true), std::bind(&CSystemOptionsWindow::toggleSpellbookAnim, this, false),
-		std::map<int,std::string>(), CGI->generaltexth->zelp[364].second, false, "sysopchk.def", nullptr, 246, 87+64, false);
-
-	newCreatureWin = new CHighlightableButton(
-		std::bind(&CSystemOptionsWindow::toggleCreatureWin, this, true), std::bind(&CSystemOptionsWindow::toggleCreatureWin, this, false),
-		std::map<int,std::string>(), texts["creatureWindowButton"]["help"].String(), false, "sysopchk.def", nullptr, 246, 183, false);
-
-	fullscreen = new CHighlightableButton(
-		std::bind(&CSystemOptionsWindow::toggleFullscreen, this, true), std::bind(&CSystemOptionsWindow::toggleFullscreen, this, false),
-		std::map<int,std::string>(), texts["fullscreenButton"]["help"].String(), false, "sysopchk.def", nullptr, 246, 215, false);
-
-	showReminder->select(settings["adventure"]["heroReminder"].Bool());
-	quickCombat->select(settings["adventure"]["quickCombat"].Bool());
-	spellbookAnim->select(settings["video"]["spellbookAnimation"].Bool());
-	newCreatureWin->select(settings["general"]["classicCreatureWindow"].Bool());
-	fullscreen->select(settings["video"]["fullscreen"].Bool());
-
-	onFullscreenChanged([&](const JsonNode &newState){ fullscreen->select(newState.Bool());});
-
-	gameResButton = new CAdventureMapButton("", texts["resolutionButton"]["help"].String(),
-	                                        std::bind(&CSystemOptionsWindow::selectGameRes, this),
-	                                        28, 275,"SYSOB12", SDLK_g);
-
-	std::string resText;
-	resText += boost::lexical_cast<std::string>(settings["video"]["screenRes"]["width"].Float());
-	resText += "x";
-	resText += boost::lexical_cast<std::string>(settings["video"]["screenRes"]["height"].Float());
-	gameResLabel = new CLabel(170, 292, FONT_MEDIUM, CENTER, Colors::YELLOW, resText);
-
-}
-
-void CSystemOptionsWindow::selectGameRes()
-{
-	std::vector<std::string> items;
-	const JsonNode & texts = CGI->generaltexth->localizedTexts["systemOptions"]["resolutionMenu"];
-
-	for( config::CConfigHandler::GuiOptionsMap::value_type& value : conf.guiOptions)
-	{
-		std::string resX = boost::lexical_cast<std::string>(value.first.first);
-		std::string resY = boost::lexical_cast<std::string>(value.first.second);
-		items.push_back(resX + 'x' + resY);
-	}
-
-	GH.pushInt(new CObjectListWindow(items, nullptr, texts["label"].String(), texts["help"].String(),
-			   std::bind(&CSystemOptionsWindow::setGameRes, this, _1)));
-}
-
-void CSystemOptionsWindow::setGameRes(int index)
-{
-	auto iter = conf.guiOptions.begin();
-	std::advance(iter, index);
-
-	//do not set resolution to illegal one (0x0)
-	assert(iter!=conf.guiOptions.end() && iter->first.first > 0 && iter->first.second > 0);
-
-	Settings gameRes = settings.write["video"]["screenRes"];
-	gameRes["width"].Float() = iter->first.first;
-	gameRes["height"].Float() = iter->first.second;
-
-	std::string resText;
-	resText += boost::lexical_cast<std::string>(iter->first.first);
-	resText += "x";
-	resText += boost::lexical_cast<std::string>(iter->first.second);
-	gameResLabel->setText(resText);
-}
-
-void CSystemOptionsWindow::toggleReminder(bool on)
-{
-	Settings heroReminder = settings.write["adventure"]["heroReminder"];
-	heroReminder->Bool() = on;
-}
-
-void CSystemOptionsWindow::toggleQuickCombat(bool on)
-{
-	Settings quickCombat = settings.write["adventure"]["quickCombat"];
-	quickCombat->Bool() = on;
-}
-
-void CSystemOptionsWindow::toggleSpellbookAnim(bool on)
-{
-	Settings quickCombat = settings.write["video"]["spellbookAnimation"];
-	quickCombat->Bool() = on;
-}
-
-void CSystemOptionsWindow::toggleCreatureWin(bool on)
-{
-	Settings classicCreatureWindow = settings.write["general"]["classicCreatureWindow"];
-	classicCreatureWindow->Bool() = on;
-}
-
-void CSystemOptionsWindow::toggleFullscreen(bool on)
-{
-	Settings fullscreen = settings.write["video"]["fullscreen"];
-	fullscreen->Bool() = on;
-}
-
-void CSystemOptionsWindow::bquitf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this]{ closeAndPushEvent(SDL_QUIT); }, 0);
-}
-
-void CSystemOptionsWindow::breturnf()
-{
-	GH.popIntTotally(this);
-}
-
-void CSystemOptionsWindow::bmainmenuf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], [this]{ closeAndPushEvent(SDL_USEREVENT, RETURN_TO_MAIN_MENU); }, 0);
-}
-
-void CSystemOptionsWindow::bloadf()
-{
-	GH.popIntTotally(this);
-	LOCPLINT->proposeLoadingGame();
-}
-
-void CSystemOptionsWindow::bsavef()
-{
-	GH.popIntTotally(this);
-	GH.pushInt(new CSavingScreen(CPlayerInterface::howManyPeople > 1));
-}
-
-void CSystemOptionsWindow::brestartf()
-{
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [this]{ closeAndPushEvent(SDL_USEREVENT, RESTART_GAME); }, 0);
-}
-
-void CSystemOptionsWindow::closeAndPushEvent(int eventType, int code /*= 0*/)
-{
-	GH.popIntTotally(this);
-	GH.pushSDLEvent(eventType, code);
-}
-
-CTavernWindow::CTavernWindow(const CGObjectInstance *TavernObj):
-    CWindowObject(PLAYER_COLORED, "TPTAVERN"),
-	tavernObj(TavernObj)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	std::vector<const CGHeroInstance*> h = LOCPLINT->cb->getAvailableHeroes(TavernObj);
-	if(h.size() < 2)
-		h.resize(2, nullptr);
-
-	h1 = new HeroPortrait(selected,0,72,299,h[0]);
-	h2 = new HeroPortrait(selected,1,162,299,h[1]);
-
-	selected = 0;
-	if (!h[0])
-		selected = 1;
-	if (!h[0] && !h[1])
-		selected = -1;
-	oldSelected = -1;
-
-	new CLabel(200, 35, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[37]);
-	new CLabel(320, 328, FONT_SMALL, CENTER, Colors::WHITE, "2500");
-	new CTextBox(LOCPLINT->cb->getTavernGossip(tavernObj), Rect(32, 190, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE);
-
-	new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-	cancel = new CAdventureMapButton(CGI->generaltexth->tavernInfo[7],"", std::bind(&CTavernWindow::close, this), 310, 428, "ICANCEL.DEF", SDLK_ESCAPE);
-	recruit = new CAdventureMapButton("", "", std::bind(&CTavernWindow::recruitb, this), 272, 355, "TPTAV01.DEF", SDLK_RETURN);
-	thiefGuild = new CAdventureMapButton(CGI->generaltexth->tavernInfo[5],"", std::bind(&CTavernWindow::thievesguildb, this), 22, 428, "TPTAV02.DEF", SDLK_t);
-
-	if(LOCPLINT->cb->getResourceAmount(Res::GOLD) < 2500) //not enough gold
-	{
-		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[0]; //Cannot afford a Hero
-		recruit->block(true);
-	}
-	else if(LOCPLINT->castleInt && LOCPLINT->cb->howManyHeroes(true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER)
-	{
-		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[1]; //Cannot recruit. You already have %d Heroes.
-		boost::algorithm::replace_first(recruit->hoverTexts[0],"%d",boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes(true)));
-		recruit->block(true);
-	}
-	else if((!LOCPLINT->castleInt) && LOCPLINT->cb->howManyHeroes(false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
-	{
-		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[1]; //Cannot recruit. You already have %d Heroes.
-		boost::algorithm::replace_first(recruit->hoverTexts[0], "%d", boost::lexical_cast<std::string>(LOCPLINT->cb->howManyHeroes(false)));
-		recruit->block(true);
-	}
-	else if(LOCPLINT->castleInt && LOCPLINT->castleInt->town->visitingHero)
-	{
-		recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[2]; //Cannot recruit. You already have a Hero in this town.
-		recruit->block(true);
-	}
-	else
-	{
-		if(selected == -1)
-			recruit->block(true);
-	}
-	if (LOCPLINT->castleInt)
-		CCS->videoh->open(LOCPLINT->castleInt->town->town->clientInfo.tavernVideo);
-	else
-		CCS->videoh->open("TAVERN.BIK");
-}
-
-void CTavernWindow::recruitb()
-{
-	const CGHeroInstance *toBuy = (selected ? h2 : h1)->h;
-	const CGObjectInstance *obj = tavernObj;
-	close();
-	LOCPLINT->cb->recruitHero(obj, toBuy);
-}
-
-void CTavernWindow::thievesguildb()
-{
-	GH.pushInt( new CThievesGuildWindow(tavernObj) );
-}
-
-CTavernWindow::~CTavernWindow()
-{
-	CCS->videoh->close();
-}
-
-void CTavernWindow::show(SDL_Surface * to)
-{
-	CWindowObject::show(to);
-
-	CCS->videoh->update(pos.x+70, pos.y+56, to, true, false);
-	if(selected >= 0)
-	{
-		HeroPortrait *sel = selected ? h2 : h1;
-
-		if (selected != oldSelected  &&  !recruit->isBlocked())
-		{
-			// Selected hero just changed. Update RECRUIT button hover text if recruitment is allowed.
-			oldSelected = selected;
-
-			recruit->hoverTexts[0] = CGI->generaltexth->tavernInfo[3]; //Recruit %s the %s
-			boost::algorithm::replace_first(recruit->hoverTexts[0],"%s",sel->h->name);
-			boost::algorithm::replace_first(recruit->hoverTexts[0],"%s",sel->h->type->heroClass->name);
-		}
-
-		printAtMiddleWBLoc(sel->description, 146, 395, FONT_SMALL, 200, Colors::WHITE, to);
-		CSDL_Ext::drawBorder(to,sel->pos.x-2,sel->pos.y-2,sel->pos.w+4,sel->pos.h+4,int3(247,223,123));
-	}
-}
-
-void CTavernWindow::HeroPortrait::clickLeft(tribool down, bool previousState)
-{
-	if(previousState && !down && h)
-		*_sel = _id;
-}
-
-void CTavernWindow::HeroPortrait::clickRight(tribool down, bool previousState)
-{
-	if(down && h)
-	{
-		GH.pushInt(new CRClickPopupInt(new CHeroWindow(h), true));
-	}
-}
-
-CTavernWindow::HeroPortrait::HeroPortrait(int &sel, int id, int x, int y, const CGHeroInstance *H)
-: h(H), _sel(&sel), _id(id)
-{
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	h = H;
-	pos.x += x;
-	pos.y += y;
-	pos.w = 58;
-	pos.h = 64;
-
-	if(H)
-	{
-		hoverName = CGI->generaltexth->tavernInfo[4];
-		boost::algorithm::replace_first(hoverName,"%s",H->name);
-
-		int artifs = h->artifactsWorn.size() + h->artifactsInBackpack.size();
-		for(int i=13; i<=17; i++) //war machines and spellbook don't count
-			if(vstd::contains(h->artifactsWorn, ArtifactPosition(i)))
-				artifs--;
-
-		description = CGI->generaltexth->allTexts[215];
-		boost::algorithm::replace_first(description, "%s", h->name);
-		boost::algorithm::replace_first(description, "%d", boost::lexical_cast<std::string>(h->level));
-		boost::algorithm::replace_first(description, "%s", h->type->heroClass->name);
-		boost::algorithm::replace_first(description, "%d", boost::lexical_cast<std::string>(artifs));
-
-		new CAnimImage("portraitsLarge", h->portrait);
-	}
-}
-
-void CTavernWindow::HeroPortrait::hover( bool on )
-{
-	//Hoverable::hover(on);
-	if(on)
-		GH.statusbar->setText(hoverName);
-	else
-		GH.statusbar->clear();
-}
-
-void CInGameConsole::show(SDL_Surface * to)
-{
-	int number = 0;
-
-	std::vector<std::list< std::pair< std::string, int > >::iterator> toDel;
-
-	boost::unique_lock<boost::mutex> lock(texts_mx);
-	for(auto it = texts.begin(); it != texts.end(); ++it, ++number)
-	{
-		Point leftBottomCorner(0, screen->h);
-		if(LOCPLINT->battleInt)
-		{
-			leftBottomCorner = LOCPLINT->battleInt->pos.bottomLeft();
-		}
-		graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN,
-		    Point(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number*20));
-
-		if(SDL_GetTicks() - it->second > defaultTimeout)
-		{
-			toDel.push_back(it);
-		}
-	}
-
-	for(auto & elem : toDel)
-	{
-		texts.erase(elem);
-	}
-}
-
-void CInGameConsole::print(const std::string &txt)
-{
-	boost::unique_lock<boost::mutex> lock(texts_mx);
-	int lineLen = conf.go()->ac.outputLineLength;
-
-	if(txt.size() < lineLen)
-	{
-		texts.push_back(std::make_pair(txt, SDL_GetTicks()));
-		if(texts.size() > maxDisplayedTexts)
-		{
-			texts.pop_front();
-		}
-	}
-	else
-	{
-		assert(lineLen);
-		for(int g=0; g<txt.size() / lineLen + 1; ++g)
-		{
-			std::string part = txt.substr(g * lineLen, lineLen);
-			if(part.size() == 0)
-				break;
-
-			texts.push_back(std::make_pair(part, SDL_GetTicks()));
-			if(texts.size() > maxDisplayedTexts)
-			{
-				texts.pop_front();
-			}
-		}
-	}
-}
-
-void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
-{
-	if(key.type != SDL_KEYDOWN) return;
-
-	if(!captureAllKeys && key.keysym.sym != SDLK_TAB) return; //because user is not entering any text
-
-	switch(key.keysym.sym)
-	{
-	case SDLK_TAB:
-	case SDLK_ESCAPE:
-		{
-			if(captureAllKeys)
-			{
-				captureAllKeys = false;
-				endEnteringText(false);
-			}
-			else if(SDLK_TAB)
-			{
-				captureAllKeys = true;
-				startEnteringText();
-			}
-			break;
-		}
-	case SDLK_RETURN: //enter key
-		{
-			if(enteredText.size() > 0  &&  captureAllKeys)
-			{
-				captureAllKeys = false;
-				endEnteringText(true);
-                CCS->soundh->playSound("CHAT");
-			}
-			break;
-		}
-	case SDLK_BACKSPACE:
-		{
-			if(enteredText.size() > 1)
-			{
-				Unicode::trimRight(enteredText,2);				
-				enteredText += '_';
-				refreshEnteredText();
-			}
-			break;
-		}
-	case SDLK_UP: //up arrow
-		{
-			if(previouslyEntered.size() == 0)
-				break;
-
-			if(prevEntDisp == -1)
-			{
-				prevEntDisp = previouslyEntered.size() - 1;
-				enteredText = previouslyEntered[prevEntDisp] + "_";
-				refreshEnteredText();
-			}
-			else if( prevEntDisp > 0)
-			{
-				--prevEntDisp;
-				enteredText = previouslyEntered[prevEntDisp] + "_";
-				refreshEnteredText();
-			}
-			break;
-		}
-	case SDLK_DOWN: //down arrow
-		{
-			if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
-			{
-				++prevEntDisp;
-				enteredText = previouslyEntered[prevEntDisp] + "_";
-				refreshEnteredText();
-			}
-			else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature
-			{
-				prevEntDisp = -1;
-				enteredText = "_";
-				refreshEnteredText();
-			}
-			break;
-		}
-	default:
-		{
-			#ifdef VCMI_SDL1
-			if(enteredText.size() > 0 && enteredText.size() < conf.go()->ac.inputLineLength)
-			{
-				if( key.keysym.unicode < 0x80 && key.keysym.unicode > 0 )
-				{
-					enteredText[enteredText.size()-1] = (char)key.keysym.unicode;
-					enteredText += "_";
-					refreshEnteredText();
-				}
-			}
-			#endif // VCMI_SDL1
-			break;
-		}
-	}
-}
-
-#ifndef VCMI_SDL1
-
-void CInGameConsole::textInputed(const SDL_TextInputEvent & event)
-{
-	if(!captureAllKeys || enteredText.size() == 0)
-		return;
-	enteredText.resize(enteredText.size()-1);
-	
-	enteredText += event.text;
-	enteredText += "_";	
-	
-	refreshEnteredText();			
-}
-
-void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
-{
- //do nothing here
-}
-
-#endif // VCMI_SDL1
-
-void CInGameConsole::startEnteringText()
-{
-	CSDL_Ext::startTextInput(&pos);
-
-	enteredText = "_";
-	if(GH.topInt() == adventureInt)
-	{
-		GH.statusbar->alignment = TOPLEFT;
-		GH.statusbar->setText(enteredText);
-
-		//Prevent changes to the text from mouse interaction with the adventure map
-		GH.statusbar->lock(true);
-	}
-	else if(LOCPLINT->battleInt)
-	{
-		LOCPLINT->battleInt->console->ingcAlter = enteredText;
-	}
-}
-
-void CInGameConsole::endEnteringText(bool printEnteredText)
-{
-	CSDL_Ext::stopTextInput();
-	
-	prevEntDisp = -1;
-	if(printEnteredText)
-	{
-		std::string txt = enteredText.substr(0, enteredText.size()-1);
-		LOCPLINT->cb->sendMessage(txt);
-		previouslyEntered.push_back(txt);
-		//print(txt);
-	}
-	enteredText = "";
-	if(GH.topInt() == adventureInt)
-	{
-		GH.statusbar->alignment = CENTER;
-		GH.statusbar->lock(false);
-		GH.statusbar->clear();
-	}
-	else if(LOCPLINT->battleInt)
-	{
-		LOCPLINT->battleInt->console->ingcAlter = "";
-	}
-}
-
-void CInGameConsole::refreshEnteredText()
-{
-	if(GH.topInt() == adventureInt)
-	{
-		GH.statusbar->lock(false);
-		GH.statusbar->clear();
-		GH.statusbar->setText(enteredText);
-		GH.statusbar->lock(true);
-	}
-	else if(LOCPLINT->battleInt)
-	{
-		LOCPLINT->battleInt->console->ingcAlter = enteredText;
-	}
-}
-
-CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDisplayedTexts(10)
-{
-	#ifdef VCMI_SDL1
-	addUsedEvents(KEYBOARD);
-	#else
-	addUsedEvents(KEYBOARD | TEXTINPUT);
-	#endif
-}
-
-CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits ):
-    CWindowObject(PLAYER_COLORED, "GARRISON")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	garr = new CGarrisonInt(92, 127, 4, Point(0,96), background->bg, Point(93,127), up, down, removableUnits);
-	{
-		CAdventureMapButton *split = new CAdventureMapButton(CGI->generaltexth->tcommands[3],"",std::bind(&CGarrisonInt::splitClick,garr),88,314,"IDV6432.DEF");
-		removeChild(split);
-		garr->addSplitBtn(split);
-	}
-	quit = new CAdventureMapButton(CGI->generaltexth->tcommands[8],"",std::bind(&CGarrisonWindow::close,this),399,314,"IOK6432.DEF",SDLK_RETURN);
-
-	std::string titleText;
-	if (garr->armedObjs[1]->tempOwner == garr->armedObjs[0]->tempOwner)
-		titleText = CGI->generaltexth->allTexts[709];
-	else
-	{
-		titleText = CGI->generaltexth->allTexts[35];
-		boost::algorithm::replace_first(titleText, "%s", garr->armedObjs[0]->Slots().begin()->second->type->namePl);
-	}
-	new CLabel(275, 30, FONT_BIG, CENTER, Colors::YELLOW, titleText);
-
-	new CAnimImage("CREST58", garr->armedObjs[0]->getOwner().getNum(), 0, 28, 124);
-	new CAnimImage("PortraitsLarge", dynamic_cast<const CGHeroInstance*>(garr->armedObjs[1])->portrait, 0, 29, 222);
-}
-
-
-IShowActivatable::IShowActivatable()
-{
-	type = 0;
-}
-
-CGarrisonHolder::CGarrisonHolder()
-{
-}
-
-void CWindowWithGarrison::updateGarrisons()
-{
-	garr->recreateSlots();
-}
-
-CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art):
-    locked(false), picked(false), marked(false), ourArt(Art)
-{
-	pos += position;
-	pos.w = pos.h = 44;
-	createImage();
-}
-
-void CArtPlace::createImage()
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	int graphic = 0;
-	if (ourArt)
-		graphic = ourArt->artType->iconIndex;
-	if (locked)
-		graphic = ArtifactID::ART_LOCK;
-
-	image = new CAnimImage("artifact", graphic);
-	if (!ourArt)
-		image->disable();
-
-	selection = new CAnimImage("artifact", ArtifactID::ART_SELECTION);
-	selection->disable();
-}
-
-void CArtPlace::lockSlot(bool on)
-{
-	if (locked == on)
-		return;
-
-	locked = on;
-
-	if (on)
-		image->setFrame(ArtifactID::ART_LOCK);
-	else
-		image->setFrame(ourArt->artType->iconIndex);
-}
-
-void CArtPlace::pickSlot(bool on)
-{
-	if (picked == on)
-		return;
-
-	picked = on;
-	if (on)
-		image->disable();
-	else
-		image->enable();
-}
-
-void CArtPlace::selectSlot(bool on)
-{
-	if (marked == on)
-		return;
-
-	marked = on;
-	if (on)
-		selection->enable();
-	else
-		selection->disable();
-}
-
-void CArtPlace::clickLeft(tribool down, bool previousState)
-{
-	//LRClickableAreaWTextComp::clickLeft(down);
-	bool inBackpack = slotID >= GameConstants::BACKPACK_START,
-		srcInBackpack = ourOwner->commonInfo->src.slotID >= GameConstants::BACKPACK_START,
-		srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
-
-	if(ourOwner->highlightModeCallback && ourArt)
-	{
-		if(down)
-		{
-			if(ourArt->artType->id < 7) //War Machine or Spellbook
-			{
-				LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); //This item can't be traded.
-			}
-			else
-			{
-				ourOwner->unmarkSlots(false);
-				selectSlot(true);
-				ourOwner->highlightModeCallback(this);
-			}
-		}
-		return;
-	}
-
-	// If clicked on spellbook, open it only if no artifact is held at the moment.
-	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
-	{
-		if(ourArt->artType->id == 0)
-		{
-			auto   spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt);
-			GH.pushInt(spellWindow);
-		}
-	}
-
-	if (!down && previousState)
-	{
-		if(ourArt && ourArt->artType->id == 0) //spellbook
-			return; //this is handled separately
-
-		if(!ourOwner->commonInfo->src.AOH) //nothing has been clicked
-		{
-			if(ourArt  //to prevent selecting empty slots (bugfix to what GrayFace reported)
-				&&  ourOwner->curHero->tempOwner == LOCPLINT->playerID)//can't take art from another player
-			{
-				if(ourArt->artType->id == 3) //catapult cannot be highlighted
-				{
-					std::vector<CComponent *> catapult(1, new CComponent(CComponent::artifact, 3, 0));
-					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped.
-					return;
-				}
-				select();
-			}
-		}
-		else if(ourArt == ourOwner->commonInfo->src.art) //restore previously picked artifact
-		{
-			deselect();
-		}
-		else //perform artifact transition
-		{
-			if(inBackpack) // Backpack destination.
-			{
-				if(srcInBackpack && slotID == ourOwner->commonInfo->src.slotID + 1) //next slot (our is not visible, so visually same as "old" place) to the art -> make nothing, return artifact to slot
-				{
-					deselect();
-				}
-				else
-				{
-					const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
-
-					switch(cur->id)
-					{
-					case ArtifactID::CATAPULT:
-						//should not happen, catapult cannot be selected
-						assert(cur->id != ArtifactID::CATAPULT);
-						break;
-					case ArtifactID::BALLISTA: case ArtifactID::AMMO_CART: case ArtifactID::FIRST_AID_TENT: //war machines cannot go to backpack
-						LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->Name()));
-						break;
-					default:
-						setMeAsDest();
-						vstd::amin(ourOwner->commonInfo->dst.slotID, ArtifactPosition(
-							ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START));
-						if(srcInBackpack && srcInSameHero)
-						{
-							if(!ourArt								//cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
-							  || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
-								vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
-						}
-						if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
-							deselect();
-						else
-							ourOwner->realizeCurrentTransaction();
-						break;
-					}
-				}
-			}
-			//check if swap is possible
-			else if (fitsHere(ourOwner->commonInfo->src.art) &&
-				(!ourArt || ourOwner->curHero->tempOwner == LOCPLINT->playerID))
-			{
-				setMeAsDest();
-//
-// 				// Special case when the dest artifact can't be fit into the src slot.
-// 				//CGI->arth->unequipArtifact(ourOwner->curHero->artifWorn, slotID);
-// 				const CArtifactsOfHero* srcAOH = ourOwner->commonInfo->src.AOH;
-// 				ui16 srcSlotID = ourOwner->commonInfo->src.slotID;
-// 				if (ourArt && srcSlotID < 19 && !ourArt->canBePutAt(ArtifactLocation(srcAOH->curHero, srcSlotID)))
-// 				{
-// 					// Put dest artifact into owner's backpack.
-// 					ourOwner->commonInfo->src.AOH = ourOwner;
-// 					ourOwner->commonInfo->src.slotID = ourOwner->curHero->artifacts.size() + 19;
-// 				}
-
-				ourOwner->realizeCurrentTransaction();
-			}
-		}
-	}
-}
-
-void CArtPlace::clickRight(tribool down, bool previousState)
-{
-	if(down && ourArt && !locked && text.size() && !picked)  //if there is no description or it's a lock, do nothing ;]
-	{
-		if (slotID < GameConstants::BACKPACK_START)
-		{
-			if(ourOwner->allowedAssembling)
-			{
-				std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
-
-				// If the artifact can be assembled, display dialog.
-				for(const CArtifact *combination : assemblyPossibilities)
-				{
-					LOCPLINT->showArtifactAssemblyDialog(
-						ourArt->artType->id,
-						combination->id,
-						true,
-						std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id),
-						0);
-
-					if(assemblyPossibilities.size() > 2)
-					{
-                        logGlobal->warnStream() << "More than one possibility of assembling... taking only first";
-						break;
-					}
-					return;
-				}
-
-				// Otherwise if the artifact can be diasassembled, display dialog.
-				if(ourArt->canBeDisassembled())
-				{
-					LOCPLINT->showArtifactAssemblyDialog(
-						ourArt->artType->id,
-						0,
-						false,
-						std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID()),
-						0);
-					return;
-				}
-			}
-		}
-
-		// Lastly just show the artifact description.
-		LRClickableAreaWTextComp::clickRight(down, previousState);
-	}
-}
-
-/**
- * Selects artifact slot so that the containing artifact looks like it's picked up.
- */
-void CArtPlace::select ()
-{
-	if (locked)
-		return;
-
-	selectSlot(true);
-	pickSlot(true);
-	if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear
-	{
-		for(int i = 0; i < GameConstants::BACKPACK_START; i++)
-		{
-			CArtPlace *ap = ourOwner->getArtPlace(i);
-			ap->pickSlot(ourArt->isPart(ap->ourArt));
-		}
-	}
-
-	//int backpackCorrection = -(slotID - Arts::BACKPACK_START < ourOwner->backpackPos);
-
-	CCS->curh->dragAndDropCursor(new CAnimImage("artifact", ourArt->artType->iconIndex));
-	ourOwner->commonInfo->src.setTo(this, false);
-	ourOwner->markPossibleSlots(ourArt);
-
-	if(slotID >= GameConstants::BACKPACK_START)
-		ourOwner->scrollBackpack(0); //will update slots
-
-	ourOwner->updateParentWindow();
-	ourOwner->safeRedraw();
-}
-
-/**
- * Deselects the artifact slot. FIXME: Not used. Maybe it should?
- */
-void CArtPlace::deselect ()
-{
-	pickSlot(false);
-	if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks
-	{
-		for(int i = 0; i < GameConstants::BACKPACK_START; i++)
-			ourOwner->getArtPlace(i)->pickSlot(false);
-	}
-
-	CCS->curh->dragAndDropCursor(nullptr);
-	ourOwner->unmarkSlots();
-	ourOwner->commonInfo->src.clear();
-	if(slotID >= GameConstants::BACKPACK_START)
-		ourOwner->scrollBackpack(0); //will update slots
-
-
-	ourOwner->updateParentWindow();
-	ourOwner->safeRedraw();
-}
-
-void CArtPlace::showAll(SDL_Surface * to)
-{
-	if (ourArt && !picked && ourArt == ourOwner->curHero->getArt(slotID, false)) //last condition is needed for disassembling -> artifact may be gone, but we don't know yet TODO: real, nice solution
-	{
-		CIntObject::showAll(to);
-	}
-
-	if(marked && active)
-	{
-		// Draw vertical bars.
-		for (int i = 0; i < pos.h; ++i)
-		{
-			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x,             pos.y + i, 240, 220, 120);
-			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + pos.w - 1, pos.y + i, 240, 220, 120);
-		}
-
-		// Draw horizontal bars.
-		for (int i = 0; i < pos.w; ++i)
-		{
-			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y,             240, 220, 120);
-			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y + pos.h - 1, 240, 220, 120);
-		}
-	}
-}
-
-bool CArtPlace::fitsHere(const CArtifactInstance * art) const
-{
-	// You can place 'no artifact' anywhere.
-	if(!art)
-		return true;
-
-	// Anything can but War Machines can be placed in backpack.
-	if (slotID >= GameConstants::BACKPACK_START)
-		return !CGI->arth->isBigArtifact(art->artType->id);
-
-	return art->canBePutAt(ArtifactLocation(ourOwner->curHero, slotID), true);
-}
-
-void CArtPlace::setMeAsDest(bool backpackAsVoid /*= true*/)
-{
-	ourOwner->commonInfo->dst.setTo(this, backpackAsVoid);
-}
-
-void CArtPlace::setArtifact(const CArtifactInstance *art)
-{
-	baseType = -1; //by default we don't store any component
-	ourArt = art;
-	if(!art)
-	{
-		image->disable();
-		text = std::string();
-		hoverText = CGI->generaltexth->allTexts[507];
-	}
-	else
-	{
-		image->enable();
-		image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
-
-		std::string artDesc = ourArt->artType->Description();
-		if (vstd::contains (artDesc, '{'))
-			text = artDesc;
-		else
-			text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style
-
-		if(art->artType->id == 1) //spell scroll
-		{
-			// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
-			// so we want to replace text in [...] with a spell name
-			// however other language versions don't have name placeholder at all, so we have to be careful
-			int spellID = art->getGivenSpellID();
-			size_t nameStart = text.find_first_of('[');
-			size_t nameEnd = text.find_first_of(']', nameStart);
-			if(spellID >= 0)
-			{
-				if(nameStart != std::string::npos  &&  nameEnd != std::string::npos)
-					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
-
-				//add spell component info (used to provide a pic in r-click popup)
-				baseType = CComponent::spell;
-				type = spellID;
-				bonusValue = 0;
-			}
-		}
-		else
-		{
-			baseType = CComponent::artifact;
-			type = art->artType->id;
-			bonusValue = 0;
-		}
-		if (art->artType->constituents) //display info about components of combined artifact
-		{
-			//TODO
-		}
-		else if (art->artType->constituentOf.size()) //display info about set
-		{
-			std::string artList;
-			auto combinedArt = art->artType->constituentOf[0];
-			text += "\n\n";
-			text += "{" + combinedArt->Name() + "}";
-			int wornArtifacts = 0;
-			for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
-			{
-				artList += "\n" + a->Name();
-				if (ourOwner->curHero->hasArt(a->id, true))
-					wornArtifacts++;
-			}
-			text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " +
-				boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
-			//TODO: fancy colors and fonts for this text
-		}
-
-		if (locked) // Locks should appear as empty.
-			hoverText = CGI->generaltexth->allTexts[507];
-		else
-			hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
-	}
-}
-
-void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
-{
-	if((!down) && previousState)
-	{
-		std::vector<CComponent*> comp(1, createComponent());
-		LOCPLINT->showInfoDialog(text, comp);
-	}
-}
-
-LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, int BaseType)
-	: LRClickableAreaWText(Pos), baseType(BaseType), bonusValue(-1)
-{
-}
-
-CComponent * LRClickableAreaWTextComp::createComponent() const
-{
-	if(baseType >= 0)
-		return new CComponent(CComponent::Etype(baseType), type, bonusValue);
-	else
-		return nullptr;
-}
-
-void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
-{
-	if(down)
-	{
-		if(CComponent *comp = createComponent())
-		{
-			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
-			return;
-		}
-	}
-
-	LRClickableAreaWText::clickRight(down, previousState); //only if with-component variant not occurred
-}
-
-CHeroArea::CHeroArea(int x, int y, const CGHeroInstance * _hero):hero(_hero)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-	pos.x += x;	pos.w = 58;
-	pos.y += y;	pos.h = 64;
-
-	if (hero)
-		new CAnimImage("PortraitsLarge", hero->portrait);
-}
-
-void CHeroArea::clickLeft(tribool down, bool previousState)
-{
-	if((!down) && previousState && hero)
-		LOCPLINT->openHeroWindow(hero);
-}
-
-void CHeroArea::clickRight(tribool down, bool previousState)
-{
-	if((!down) && previousState && hero)
-		LOCPLINT->openHeroWindow(hero);
-}
-
-void CHeroArea::hover(bool on)
-{
-	if (on && hero)
-		GH.statusbar->setText(hero->getObjectName());
-	else
-		GH.statusbar->clear();
-}
-
-void LRClickableAreaOpenTown::clickLeft(tribool down, bool previousState)
-{
-	if((!down) && previousState && town)
-		{
-		LOCPLINT->openTownWindow(town);
-		if ( type == 2 )
-			LOCPLINT->castleInt->builds->buildingClicked(BuildingID::VILLAGE_HALL);
-		else if ( type == 3 && town->fortLevel() )
-			LOCPLINT->castleInt->builds->buildingClicked(BuildingID::FORT);
-		}
-}
-
-void LRClickableAreaOpenTown::clickRight(tribool down, bool previousState)
-{
-	if((!down) && previousState && town)
-		LOCPLINT->openTownWindow(town);//TODO: popup?
-}
-
-LRClickableAreaOpenTown::LRClickableAreaOpenTown()
-	: LRClickableAreaWTextComp(Rect(0,0,0,0), -1)
-{
-}
-
-void CArtifactsOfHero::SCommonPart::reset()
-{
-	src.clear();
-	dst.clear();
-	CCS->curh->dragAndDropCursor(nullptr);
-}
-
-void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
-{
-// 	// An update is made, rather than initialization.
-// 	if (curHero && curHero->id == hero->id)
-// 	{
-// 		if(curHero != hero)
-// 		{
-// 			//delete	curHero;
-// 			curHero = hero; //was: creating a copy
-// 		}
-//
-// 		// Compensate backpack pos if an artifact was insertad before it.
-// 		if (commonInfo->dst.slotID >= 19 && commonInfo->destAOH == this
-// 			&& commonInfo->dst.slotID - 19 < backpackPos)
-// 		{
-// 			backpackPos++;
-// 		}
-//
-// 		if (updateState && commonInfo->srcAOH == this)
-// 		{
-// 			// A swap was made, make the replaced artifact the current selected.
-// 			if (commonInfo->dst.slotID < 19 && commonInfo->destArtifact)
-// 			{
-// // 				// Temporarily remove artifact from hero.
-// // 				if (commonInfo->srcSlotID < 19)
-// // 					CGI->arth->unequipArtifact(curHero->artifWorn, commonInfo->srcSlotID);
-// // 				else
-// // 					curHero->artifacts.erase(curHero->artifacts.begin() + (commonInfo->srcSlotID - 19));
-//
-// 				updateParentWindow(); //TODO: evil! but does the thing
-//
-// 				// Source <- Dest
-// 				commonInfo->srcArtifact = commonInfo->destArtifact;
-//
-// 				// Reset destination parameters.
-// 				commonInfo->dst.clear();
-//
-// 				CCS->curh->dragAndDropCursor(graphics->artDefs->ourImages[commonInfo->srcArtifact->id].bitmap);
-// 				markPossibleSlots(commonInfo->srcArtifact);
-// 			}
-// 			else if (commonInfo->destAOH != nullptr)
-// 			{
-// 				// Reset all parameters.
-// 				commonInfo->reset();
-// 				unmarkSlots();
-// 			}
-// 		}
-// 	}
-// 	else
-// 	{
-// 		commonInfo->reset();
-// 	}
-//
-// 	if(hero != curHero)
-// 	{
-// // 		delete curHero;
-// 		// 		curHero = new CGHeroInstance(*hero);
-// 		curHero = hero; //was: creating a copy
-// 	}
-
-	curHero = hero;
-	if (curHero->artifactsInBackpack.size() > 0)
-		backpackPos %= curHero->artifactsInBackpack.size();
-	else
-		backpackPos = 0;
-
-	// Fill the slots for worn artifacts and backpack.
-	for (int g = 0; g < artWorn.size() ; g++)
-		setSlotData(artWorn[g], ArtifactPosition(g));
-	scrollBackpack(0);
-}
-
-void CArtifactsOfHero::dispose()
-{
-	//vstd::clear_pointer(curHero);
-	//unmarkSlots(false);
-	CCS->curh->dragAndDropCursor(nullptr);
-}
-
-void CArtifactsOfHero::scrollBackpack(int dir)
-{
-	int artsInBackpack = curHero->artifactsInBackpack.size();
-	backpackPos += dir;
-	if(backpackPos < 0)// No guarantee of modulus behavior with negative operands -> we keep it positive
-		backpackPos += artsInBackpack;
-
-	if(artsInBackpack)
-		backpackPos %= artsInBackpack;
-
-	std::multiset<const CArtifactInstance *> toOmit = artifactsOnAltar;
-	if(commonInfo->src.art) //if we picked an art from backapck, its slot has to be omitted
-		toOmit.insert(commonInfo->src.art);
-
-	int omitedSoFar = 0;
-
-	//set new data
-	size_t s = 0;
-	for( ; s < artsInBackpack; ++s)
-	{
-
-		if (s < artsInBackpack)
-		{
-			auto slotID = ArtifactPosition(GameConstants::BACKPACK_START + (s + backpackPos)%artsInBackpack);
-			const CArtifactInstance *art = curHero->getArt(slotID);
-			assert(art);
-			if(!vstd::contains(toOmit, art))
-			{
-				if(s - omitedSoFar < backpack.size())
-					setSlotData(backpack[s-omitedSoFar], slotID);
-			}
-			else
-			{
-				toOmit -= art;
-				omitedSoFar++;
-				continue;
-			}
-		}
-	}
-	for( ; s - omitedSoFar < backpack.size(); s++)
-		eraseSlotData(backpack[s-omitedSoFar], ArtifactPosition(GameConstants::BACKPACK_START + s));
-
-	//in artifact merchant selling artifacts we may have highlight on one of backpack artifacts -> market needs update, cause artifact under highlight changed
-	if(highlightModeCallback)
-	{
-		for(auto & elem : backpack)
-		{
-			if(elem->marked)
-			{
-				highlightModeCallback(elem);
-				break;
-			}
-		}
-	}
-
-	//blocking scrolling if there is not enough artifacts to scroll
-	bool scrollingPossible = artsInBackpack - omitedSoFar > backpack.size();
-	leftArtRoll->block(!scrollingPossible);
-	rightArtRoll->block(!scrollingPossible);
-
-	safeRedraw();
-
-}
-
-/**
- * Marks possible slots where a given artifact can be placed, except backpack.
- *
- * @param art Artifact checked against.
- */
-void CArtifactsOfHero::markPossibleSlots(const CArtifactInstance* art)
-{
-	for(CArtifactsOfHero *aoh : commonInfo->participants)
-		for(CArtPlace *place : aoh->artWorn)
-			place->selectSlot(art->canBePutAt(ArtifactLocation(aoh->curHero, place->slotID), true));
-
-	safeRedraw();
-}
-
-/**
- * Unamarks all slots.
- */
-void CArtifactsOfHero::unmarkSlots(bool withRedraw /*= true*/)
-{
-	if(commonInfo)
-		for(CArtifactsOfHero *aoh : commonInfo->participants)
-			aoh->unmarkLocalSlots(false);
-	else
-		unmarkLocalSlots(false);\
-
-	if(withRedraw)
-		safeRedraw();
-}
-
-void CArtifactsOfHero::unmarkLocalSlots(bool withRedraw /*= true*/)
-{
-	for(CArtPlace *place : artWorn)
-		place->selectSlot(false);
-	for(CArtPlace *place : backpack)
-		place->selectSlot(false);
-
-	if(withRedraw)
-		safeRedraw();
-}
-
-/**
- * Assigns an artifacts to an artifact place depending on it's new slot ID.
- */
-void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID)
-{
-	if(!artPlace && slotID >= GameConstants::BACKPACK_START) //spurious call from artifactMoved in attempt to update hidden backpack slot
-	{
-		return;
-	}
-
-	artPlace->pickSlot(false);
-	artPlace->slotID = slotID;
-
-	if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
-	{
-		artPlace->setArtifact(asi->artifact);
-		artPlace->lockSlot(asi->locked);
-	}
-	else
-		artPlace->setArtifact(nullptr);
-}
-
-/**
- * Makes given artifact slot appear as empty with a certain slot ID.
- */
-void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, ArtifactPosition slotID)
-{
-	artPlace->pickSlot(false);
-	artPlace->slotID = slotID;
-	artPlace->setArtifact(nullptr);
-}
-
-CArtifactsOfHero::CArtifactsOfHero(std::vector<CArtPlace *> ArtWorn, std::vector<CArtPlace *> Backpack,
-	CAdventureMapButton *leftScroll, CAdventureMapButton *rightScroll, bool createCommonPart):
-
-	curHero(nullptr),
-	artWorn(ArtWorn), backpack(Backpack),
-	backpackPos(0), commonInfo(nullptr), updateState(false),
-	leftArtRoll(leftScroll), rightArtRoll(rightScroll),
-	allowedAssembling(true), highlightModeCallback(nullptr)
-{
-	if(createCommonPart)
-	{
-		commonInfo = new CArtifactsOfHero::SCommonPart;
-		commonInfo->participants.insert(this);
-	}
-
-	// Init slots for worn artifacts.
-	for (size_t g = 0; g < artWorn.size() ; g++)
-	{
-		artWorn[g]->ourOwner = this;
-		eraseSlotData(artWorn[g], ArtifactPosition(g));
-	}
-
-	// Init slots for the backpack.
-	for(size_t s=0; s<backpack.size(); ++s)
-	{
-		backpack[s]->ourOwner = this;
-		eraseSlotData(backpack[s], ArtifactPosition(GameConstants::BACKPACK_START + s));
-	}
-
-	leftArtRoll->callback  += std::bind(&CArtifactsOfHero::scrollBackpack,this,-1);
-	rightArtRoll->callback += std::bind(&CArtifactsOfHero::scrollBackpack,this,+1);
-}
-
-CArtifactsOfHero::CArtifactsOfHero(const Point& position, bool createCommonPart /*= false*/)
- : curHero(nullptr), backpackPos(0), commonInfo(nullptr), updateState(false), allowedAssembling(true), highlightModeCallback(nullptr)
-{
-	if(createCommonPart)
-	{
-		commonInfo = new CArtifactsOfHero::SCommonPart;
-		commonInfo->participants.insert(this);
-	}
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	pos += position;
-	artWorn.resize(19);
-
-	std::vector<Point> slotPos;
-	slotPos += Point(509,30),  Point(567,240), Point(509,80),
-	           Point(383,68),  Point(564,183), Point(509,130),
-	           Point(431,68),  Point(610,183), Point(515,295),
-	           Point(383,143), Point(399,194), Point(415,245),
-	           Point(431,296), Point(564,30),  Point(610,30),
-	           Point(610,76),  Point(610,122), Point(610,310),
-	           Point(381,296);
-
-	// Create slots for worn artifacts.
-	for (size_t g = 0; g < GameConstants::BACKPACK_START ; g++)
-	{
-		artWorn[g] = new CArtPlace(slotPos[g]);
-		artWorn[g]->ourOwner = this;
-		eraseSlotData(artWorn[g], ArtifactPosition(g));
-	}
-
-	// Create slots for the backpack.
-	for(size_t s=0; s<5; ++s)
-	{
-		auto   add = new CArtPlace(Point(403 + 46 * s, 365));
-
-		add->ourOwner = this;
-		eraseSlotData(add, ArtifactPosition(GameConstants::BACKPACK_START + s));
-
-		backpack.push_back(add);
-	}
-
-	leftArtRoll = new CAdventureMapButton(std::string(), std::string(), std::bind(&CArtifactsOfHero::scrollBackpack,this,-1), 379, 364, "hsbtns3.def", SDLK_LEFT);
-	rightArtRoll = new CAdventureMapButton(std::string(), std::string(), std::bind(&CArtifactsOfHero::scrollBackpack,this,+1), 632, 364, "hsbtns5.def", SDLK_RIGHT);
-}
-
-CArtifactsOfHero::~CArtifactsOfHero()
-{
-	dispose();
-}
-
-void CArtifactsOfHero::updateParentWindow()
-{
-	if (CHeroWindow* chw = dynamic_cast<CHeroWindow*>(GH.topInt()))
-	{
-		if(updateState)
-			chw->curHero = curHero;
-		else
-			chw->update(curHero, true);
-	}
-	else if(CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(GH.topInt()))
-	{
-
-		//use our copy of hero to draw window
-		if(cew->heroInst[0]->id == curHero->id)
-			cew->heroInst[0] = curHero;
-		else
-			cew->heroInst[1] = curHero;
-
-		if(!updateState)
-		{
-			cew->deactivate();
-// 			for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
-// 			{
-// 				if(cew->heroInst[g] == curHero)
-// 				{
-// 					cew->artifs[g]->setHero(curHero);
-// 				}
-// 			}
-
-
-			cew->prepareBackground();
-			cew->redraw();
-			cew->activate();
-		}
-	}
-}
-
-void CArtifactsOfHero::safeRedraw()
-{
-	if (active)
-	{
-		if(parent)
-			parent->redraw();
-		else
-			redraw();
-	}
-}
-
-void CArtifactsOfHero::realizeCurrentTransaction()
-{
-	assert(commonInfo->src.AOH);
-	assert(commonInfo->dst.AOH);
-	LOCPLINT->cb->swapArtifacts(ArtifactLocation(commonInfo->src.AOH->curHero, commonInfo->src.slotID),
-								ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID));
-}
-
-void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
-{
-	bool isCurHeroSrc = src.isHolder(curHero),
-		isCurHeroDst = dst.isHolder(curHero);
-	if(isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
-		updateSlot(src.slot);
-	if(isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START)
-		updateSlot(dst.slot);
-	if(isCurHeroSrc  ||  isCurHeroDst) //we need to update all slots, artifact might be combined and affect more slots
-		updateWornSlots(false);
-
-	if (!src.isHolder(curHero) && !isCurHeroDst)
-		return;
-
-	if(commonInfo->src == src) //artifact was taken from us
-	{
-		assert(commonInfo->dst == dst  //expected movement from slot ot slot
-			||  dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START //artifact moved back to backpack (eg. to make place for art we are moving)
-			|| dst.getHolderArtSet()->bearerType() != ArtBearer::HERO);
-		commonInfo->reset();
-		unmarkSlots();
-	}
-	else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it
-	{
-		assert(dst.slot >= GameConstants::BACKPACK_START);
-		commonInfo->reset();
-
-		CArtPlace *ap = nullptr;
-		for(CArtifactsOfHero *aoh : commonInfo->participants)
-		{
-			if(dst.isHolder(aoh->curHero))
-			{
-				commonInfo->src.AOH = aoh;
-				if((ap = aoh->getArtPlace(dst.slot)))
-					break;
-			}
-		}
-
-		if(ap)
-		{
-			ap->select();
-		}
-		else
-		{
-			commonInfo->src.art = dst.getArt();
-			commonInfo->src.slotID = dst.slot;
-			assert(commonInfo->src.AOH);
-			CCS->curh->dragAndDropCursor(new CAnimImage("artifact", dst.getArt()->artType->iconIndex));
-			markPossibleSlots(dst.getArt());
-		}
-	}
-	else if(src.slot >= GameConstants::BACKPACK_START &&
-	        src.slot <  commonInfo->src.slotID &&
-			src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
-	{
-		//int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
-		vstd::advance(commonInfo->src.slotID, -1);
-		assert(commonInfo->src.valid());
-	}
-	else
-	{
-		//when moving one artifact onto another it leads to two art movements: dst->backapck; src->dst
-		// however after first movement we pick the art from backpack and the second movement coming when
-		// we have a different artifact may look surprising... but it's valid.
-	}
-
-	updateParentWindow();
- 	int shift = 0;
-// 	if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
-// 		shift++;
-//
- 	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos)
-		shift++;
-	if(dst.slot < GameConstants::BACKPACK_START  &&  src.slot - GameConstants::BACKPACK_START < backpackPos)
- 		shift--;
-
-	if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
-	 || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
-		scrollBackpack(shift); //update backpack slots
-}
-
-void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
-{
-	if(al.isHolder(curHero))
-	{
-		if(al.slot < GameConstants::BACKPACK_START)
-			updateWornSlots(0);
-		else
-			scrollBackpack(0); //update backpack slots
-	}
-}
-
-CArtPlace * CArtifactsOfHero::getArtPlace(int slot)
-{
-	if(slot < GameConstants::BACKPACK_START)
-	{
-		return artWorn[slot];
-	}
-	else
-	{
-		for(CArtPlace *ap : backpack)
-			if(ap->slotID == slot)
-				return ap;
-	}
-
-	return nullptr;
-}
-
-void CArtifactsOfHero::artifactAssembled(const ArtifactLocation &al)
-{
-	if(al.isHolder(curHero))
-		updateWornSlots();
-}
-
-void CArtifactsOfHero::artifactDisassembled(const ArtifactLocation &al)
-{
-	if(al.isHolder(curHero))
-		updateWornSlots();
-}
-
-void CArtifactsOfHero::updateWornSlots(bool redrawParent /*= true*/)
-{
-	for(int i = 0; i < artWorn.size(); i++)
-		updateSlot(ArtifactPosition(i));
-
-
-	if(redrawParent)
-		updateParentWindow();
-}
-
-const CGHeroInstance * CArtifactsOfHero::getHero() const
-{
-	return curHero;
-}
-
-void CArtifactsOfHero::updateSlot(ArtifactPosition slotID)
-{
-	setSlotData(getArtPlace(slotID), slotID);
-}
-
-void CExchangeWindow::questlog(int whichHero)
-{
-	CCS->curh->dragAndDropCursor(nullptr);
-}
-
-void CExchangeWindow::prepareBackground()
-{
-	//printing heroes' names and levels
-	auto genTitle = [](const CGHeroInstance *h)
-	{
-		return boost::str(boost::format(CGI->generaltexth->allTexts[138])
-	                      % h->name % h->level % h->type->heroClass->name);
-	};
-
-	new CLabel(147, 25, FONT_SMALL, CENTER, Colors::WHITE, genTitle(heroInst[0]));
-	new CLabel(653, 25, FONT_SMALL, CENTER, Colors::WHITE, genTitle(heroInst[1]));
-
-	//printing primary skills
-	for(int g=0; g<4; ++g)
-		new CAnimImage("PSKIL32", g, 0, 385, 19 + 36*g);
-
-	//heroes related thing
-	for(int b=0; b<ARRAY_COUNT(heroInst); b++)
-	{
-		CHeroWithMaybePickedArtifact heroWArt = CHeroWithMaybePickedArtifact(this, heroInst[b]);
-		//printing primary skills' amounts
-		for(int m=0; m<GameConstants::PRIMARY_SKILLS; ++m)
-			new CLabel(352 + 93 * b, 35 + 36 * m, FONT_SMALL, CENTER, Colors::WHITE,
-		               boost::lexical_cast<std::string>(heroWArt.getPrimSkillLevel(static_cast<PrimarySkill::PrimarySkill>(m))));
-
-		//printing secondary skills
-		for(int m=0; m<heroInst[b]->secSkills.size(); ++m)
-		{
-			int id = heroInst[b]->secSkills[m].first;
-			int level = heroInst[b]->secSkills[m].second;
-			new CAnimImage("SECSK32", id*3 + level + 2 , 0, 32 + 36 * m + 454 * b, 88);
-		}
-
-		//hero's specialty
-		new CAnimImage("UN32", heroInst[b]->type->imageIndex, 0, 67 + 490*b, 45);
-
-		//experience
-		new CAnimImage("PSKIL32", 4, 0, 103 + 490*b, 45);
-		new CLabel(119 + 490*b, 71, FONT_SMALL, CENTER, Colors::WHITE, makeNumberShort(heroInst[b]->exp));
-
-		//mana points
-		new CAnimImage("PSKIL32", 5, 0, 139 + 490*b, 45);
-		new CLabel(155 + 490*b, 71, FONT_SMALL, CENTER, Colors::WHITE, makeNumberShort(heroInst[b]->mana));
-	}
-
-	//printing portraits
-	new CAnimImage("PortraitsLarge", heroInst[0]->portrait, 0, 257, 13);
-	new CAnimImage("PortraitsLarge", heroInst[1]->portrait, 0, 485, 13);
-}
-
-CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID):
-    CWindowObject(PLAYER_COLORED | BORDERED, "TRADE2")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	heroInst[0] = LOCPLINT->cb->getHero(hero1);
-	heroInst[1] = LOCPLINT->cb->getHero(hero2);
-
-	prepareBackground();
-
-	artifs[0] = new CArtifactsOfHero(Point(-334, 150));
-	artifs[0]->commonInfo = new CArtifactsOfHero::SCommonPart;
-	artifs[0]->commonInfo->participants.insert(artifs[0]);
-	artifs[0]->setHero(heroInst[0]);
-	artifs[1] = new CArtifactsOfHero(Point(96, 150));
-	artifs[1]->commonInfo = artifs[0]->commonInfo;
-	artifs[1]->commonInfo->participants.insert(artifs[1]);
-	artifs[1]->setHero(heroInst[1]);
-
-	artSets.push_back(artifs[0]);
-	artSets.push_back(artifs[1]);
-
-	//primary skills
-	for(int g=0; g<4; ++g)
-	{
-		//primary skill's clickable areas
-		primSkillAreas.push_back(new LRClickableAreaWTextComp());
-		primSkillAreas[g]->pos = genRect(32, 140, pos.x + 329, pos.y + 19 + 36 * g);
-		primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
-		primSkillAreas[g]->type = g;
-		primSkillAreas[g]->bonusValue = -1;
-		primSkillAreas[g]->baseType = 0;
-		primSkillAreas[g]->hoverText = CGI->generaltexth->heroscrn[1];
-		boost::replace_first(primSkillAreas[g]->hoverText, "%s", CGI->generaltexth->primarySkillNames[g]);
-	}
-
-	//heroes related thing
-	for(int b=0; b<ARRAY_COUNT(heroInst); b++)
-	{
-		//secondary skill's clickable areas
-		for(int g=0; g<heroInst[b]->secSkills.size(); ++g)
-		{
-			int skill = heroInst[b]->secSkills[g].first,
-				level = heroInst[b]->secSkills[g].second; // <1, 3>
-			secSkillAreas[b].push_back(new LRClickableAreaWTextComp());
-			secSkillAreas[b][g]->pos = genRect(32, 32, pos.x + 32 + g*36 + b*454 , pos.y + 88);
-			secSkillAreas[b][g]->baseType = 1;
-
-			secSkillAreas[b][g]->type = skill;
-			secSkillAreas[b][g]->bonusValue = level;
-			secSkillAreas[b][g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
-
-			secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21];
-			boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->levels[level - 1]);
-			boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->skillName[skill]);
-		}
-
-		portrait[b] = new CHeroArea(257 + 228*b, 13, heroInst[b]);
-
-		specialty[b] = new LRClickableAreaWText();
-		specialty[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + 45);
-		specialty[b]->hoverText = CGI->generaltexth->heroscrn[27];
-		specialty[b]->text = heroInst[b]->type->specDescr;
-
-		experience[b] = new LRClickableAreaWText();
-		experience[b]->pos = genRect(32, 32, pos.x + 105 + 490*b, pos.y + 45);
-		experience[b]->hoverText = CGI->generaltexth->heroscrn[9];
-		experience[b]->text = CGI->generaltexth->allTexts[2].c_str();
-		boost::algorithm::replace_first(experience[b]->text, "%d", boost::lexical_cast<std::string>(heroInst[b]->level));
-		boost::algorithm::replace_first(experience[b]->text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(heroInst[b]->level+1)));
-		boost::algorithm::replace_first(experience[b]->text, "%d", boost::lexical_cast<std::string>(heroInst[b]->exp));
-
-		spellPoints[b] = new LRClickableAreaWText();
-		spellPoints[b]->pos = genRect(32, 32, pos.x + 141 + 490*b, pos.y + 45);
-		spellPoints[b]->hoverText = CGI->generaltexth->heroscrn[22];
-		spellPoints[b]->text = CGI->generaltexth->allTexts[205];
-		boost::algorithm::replace_first(spellPoints[b]->text, "%s", heroInst[b]->name);
-		boost::algorithm::replace_first(spellPoints[b]->text, "%d", boost::lexical_cast<std::string>(heroInst[b]->mana));
-		boost::algorithm::replace_first(spellPoints[b]->text, "%d", boost::lexical_cast<std::string>(heroInst[b]->manaLimit()));
-
-		//setting morale
-		morale[b] = new MoraleLuckBox(true, genRect(32, 32, 176 + 490*b, 39), true);
-		morale[b]->set(heroInst[b]);
-		//setting luck
-		luck[b] = new MoraleLuckBox(false, genRect(32, 32, 212 + 490*b, 39), true);
-		luck[b]->set(heroInst[b]);
-	}
-
-	//buttons
-	quit = new CAdventureMapButton(CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), 732, 567, "IOKAY.DEF", SDLK_RETURN);
-	if(queryID.getNum() > 0)
-		quit->callback += [=]{ LOCPLINT->cb->selectionMade(0, queryID); };
-
-	questlogButton[0] = new CAdventureMapButton(CGI->generaltexth->heroscrn[0], "", std::bind(&CExchangeWindow::questlog,this, 0), 10,  44, "hsbtns4.def");
-	questlogButton[1] = new CAdventureMapButton(CGI->generaltexth->heroscrn[0], "", std::bind(&CExchangeWindow::questlog,this, 1), 740, 44, "hsbtns4.def");
-
-	Rect barRect(5, 578, 725, 18);
-	ourBar = new CGStatusBar(new CPicture(*background, barRect, 5, 578, false));
-
-	//garrison interface
-	garr = new CGarrisonInt(69, 131, 4, Point(418,0), *background, Point(69,131), heroInst[0],heroInst[1], true, true);
-	garr->addSplitBtn(new CAdventureMapButton(CGI->generaltexth->tcommands[3], "", std::bind(&CGarrisonInt::splitClick, garr),  10, 132, "TSBTNS.DEF"));
-	garr->addSplitBtn(new CAdventureMapButton(CGI->generaltexth->tcommands[3], "", std::bind(&CGarrisonInt::splitClick, garr), 740, 132, "TSBTNS.DEF"));
-}
-
-CExchangeWindow::~CExchangeWindow() //d-tor
-{
-	delete artifs[0]->commonInfo;
-	artifs[0]->commonInfo = nullptr;
-	artifs[1]->commonInfo = nullptr;
-}
-
-CShipyardWindow::CShipyardWindow(const std::vector<si32> &cost, int state, int boatType, const std::function<void()> &onBuy):
-    CWindowObject(PLAYER_COLORED, "TPSHIP")
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	bgWater = new CPicture("TPSHIPBK", 100, 69);
-
-	std::string boatFilenames[3] = {"AB01_", "AB02_", "AB03_"};
-
-	Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2);
-	bgShip = new CAnimImage(boatFilenames[boatType], 0, 7, 120, 96, CShowableAnim::USE_RLE);
-	bgShip->center(waterCenter);
-
-	// Create resource icons and costs.
-	std::string goldValue = boost::lexical_cast<std::string>(cost[Res::GOLD]);
-	std::string woodValue = boost::lexical_cast<std::string>(cost[Res::WOOD]);
-
-	goldCost = new CLabel(118, 294, FONT_SMALL, CENTER, Colors::WHITE, goldValue);
-	woodCost = new CLabel(212, 294, FONT_SMALL, CENTER, Colors::WHITE, woodValue);
-
-	goldPic = new CAnimImage("RESOURCE", Res::GOLD, 0, 100, 244);
-	woodPic = new CAnimImage("RESOURCE", Res::WOOD, 0, 196, 244);
-
-	quit = new CAdventureMapButton(CGI->generaltexth->allTexts[599], "", std::bind(&CShipyardWindow::close, this), 224, 312, "ICANCEL", SDLK_RETURN);
-	build = new CAdventureMapButton(CGI->generaltexth->allTexts[598], "", std::bind(&CShipyardWindow::close, this), 42, 312, "IBUY30", SDLK_RETURN);
-	build->callback += onBuy;
-
-	for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
-	{
-		if(cost[i] > LOCPLINT->cb->getResourceAmount(i))
-		{
-			build->block(true);
-			break;
-		}
-	}
-
-	statusBar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	title =     new CLabel(164, 27,  FONT_BIG,    CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[13]);
-	costLabel = new CLabel(164, 220, FONT_MEDIUM, CENTER, Colors::WHITE,   CGI->generaltexth->jktexts[14]);
-}
-
-CPuzzleWindow::CPuzzleWindow(const int3 &GrailPos, double discoveredRatio):
-    CWindowObject(PLAYER_COLORED | BORDERED, "PUZZLE"),
-    grailPos(GrailPos),
-    currentAlpha(SDL_ALPHA_OPAQUE)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CCS->soundh->playSound(soundBase::OBELISK);
-
-	quitb = new CAdventureMapButton(CGI->generaltexth->allTexts[599], "", std::bind(&CPuzzleWindow::close, this), 670, 538, "IOK6432.DEF", SDLK_RETURN);
-	quitb->assignedKeys.insert(SDLK_ESCAPE);
-	quitb->borderColor = Colors::METALLIC_GOLD;
-	quitb->borderEnabled = true;
-
-	new CPicture("PUZZLOGO", 607, 3);
-	new CLabel(700, 95, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[463]);
-	new CResDataBar("ARESBAR.bmp", 3, 575, 32, 2, 85, 85);
-
-	int faction = LOCPLINT->cb->getStartInfo()->playerInfos.find(LOCPLINT->playerID)->second.castle;
-
-	auto & puzzleMap = CGI->townh->factions[faction]->puzzleMap;
-
-	for(auto & elem : puzzleMap)
-	{
-		const SPuzzleInfo & info = elem;
-
-		auto   piece = new CPicture(info.filename, info.x, info.y);
-
-		//piece that will slowly disappear
-		if(info.whenUncovered <= GameConstants::PUZZLE_MAP_PIECES * discoveredRatio)
-		{
-			piecesToRemove.push_back(piece);
-			piece->needRefresh = true;
-			piece->recActions = piece->recActions & ~SHOWALL;
-			#ifndef VCMI_SDL1
-			SDL_SetSurfaceBlendMode(piece->bg,SDL_BLENDMODE_BLEND);
-			#endif // VCMI_SDL1
-		}
-	}
-}
-
-void CPuzzleWindow::showAll(SDL_Surface * to)
-{
-	int3 moveInt = int3(8, 9, 0);
-	Rect mapRect = genRect(544, 591, pos.x + 8, pos.y + 7);
-
-	CGI->mh->terrainRect
-		(grailPos - moveInt, adventureInt->anim,
-		 &LOCPLINT->cb->getVisibilityMap(), true, adventureInt->heroAnim,
-		 to, &mapRect, 0, 0, true, moveInt);
-
-	CWindowObject::showAll(to);
-}
-
-void CPuzzleWindow::show(SDL_Surface * to)
-{
-	static int animSpeed = 2;
-
-	if (currentAlpha < animSpeed)
-	{
-		//animation done
-		for(auto & piece : piecesToRemove)
-			delete piece;
-		piecesToRemove.clear();
-	}
-	else
-	{
-		//update disappearing puzzles
-		for(auto & piece : piecesToRemove)
-			piece->setAlpha(currentAlpha);
-		currentAlpha -= animSpeed;
-	}
-	CWindowObject::show(to);
-}
-
-void CTransformerWindow::CItem::move()
-{
-	if (left)
-		moveBy(Point(289, 0));
-	else
-		moveBy(Point(-289, 0));
-	left = !left;
-}
-
-void CTransformerWindow::CItem::clickLeft(tribool down, bool previousState)
-{
-	if(previousState && (!down))
-	{
-		move();
-		parent->showAll(screen2);
-	}
-}
-
-void CTransformerWindow::CItem::update()
-{
-	icon->setFrame(parent->army->getCreature(SlotID(id))->idNumber + 2);
-}
-
-CTransformerWindow::CItem::CItem(CTransformerWindow * parent, int size, int id):
-	id(id), size(size), parent(parent)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	addUsedEvents(LCLICK);
-	left = true;
-	pos.w = 58;
-	pos.h = 64;
-
-	pos.x += 45  + (id%3)*83 + id/6*83;
-	pos.y += 109 + (id/3)*98;
-	icon = new CAnimImage("TWCRPORT", parent->army->getCreature(SlotID(id))->idNumber + 2);
-	new CLabel(28, 76,FONT_SMALL, CENTER, Colors::WHITE, boost::lexical_cast<std::string>(size));//stack size
-}
-
-void CTransformerWindow::makeDeal()
-{
-	for (auto & elem : items)
-		if (!elem->left)
-			LOCPLINT->cb->trade(town, EMarketMode::CREATURE_UNDEAD, elem->id, 0, 0, hero);
-}
-
-void CTransformerWindow::addAll()
-{
-	for (auto & elem : items)
-		if (elem->left)
-			elem->move();
-	showAll(screen2);
-}
-
-void CTransformerWindow::updateGarrisons()
-{
-	for(auto & item : items)
-	{
-		item->update();
-	}
-}
-
-CTransformerWindow::CTransformerWindow(const CGHeroInstance * _hero, const CGTownInstance * _town):
-    CWindowObject(PLAYER_COLORED, "SKTRNBK"),
-    hero(_hero),
-    town(_town)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	if (hero)
-		army = hero;
-	else
-		army = town;
-
-	for (int i=0; i<GameConstants::ARMY_SIZE; i++ )
-		if ( army->getCreature(SlotID(i)) )
-			items.push_back(new CItem(this, army->getStackCount(SlotID(i)), i));
-
-	all    = new CAdventureMapButton(CGI->generaltexth->zelp[590],std::bind(&CTransformerWindow::addAll,this),     146,416,"ALTARMY.DEF",SDLK_a);
-	convert= new CAdventureMapButton(CGI->generaltexth->zelp[591],std::bind(&CTransformerWindow::makeDeal,this),   269,416,"ALTSACR.DEF",SDLK_RETURN);
-	cancel = new CAdventureMapButton(CGI->generaltexth->zelp[592],std::bind(&CTransformerWindow::close, this),392,416,"ICANCEL.DEF",SDLK_ESCAPE);
-	bar    = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	new CLabel(153, 29,FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[485]);//holding area
-	new CLabel(153+295, 29, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[486]);//transformer
-	new CTextBox(CGI->generaltexth->allTexts[487], Rect(26,  56, 255, 40), 0, FONT_MEDIUM, CENTER, Colors::YELLOW);//move creatures to create skeletons
-	new CTextBox(CGI->generaltexth->allTexts[488], Rect(320, 56, 255, 40), 0, FONT_MEDIUM, CENTER, Colors::YELLOW);//creatures here will become skeletons
-
-}
-
-void CUniversityWindow::CItem::clickLeft(tribool down, bool previousState)
-{
-	if(previousState && (!down))
-	{
-		if ( state() != 2 )
-			return;
-		auto  win = new CUnivConfirmWindow(parent, ID, LOCPLINT->cb->getResourceAmount(Res::GOLD) >= 2000);
-		GH.pushInt(win);
-	}
-}
-
-void CUniversityWindow::CItem::clickRight(tribool down, bool previousState)
-{
-	if(down)
-	{
-		CRClickPopup::createAndPush(CGI->generaltexth->skillInfoTexts[ID][0],
-		        new CComponent(CComponent::secskill, ID, 1));
-	}
-}
-
-void CUniversityWindow::CItem::hover(bool on)
-{
-	if (on)
-		GH.statusbar->setText(CGI->generaltexth->skillName[ID]);
-	else
-		GH.statusbar->clear();
-}
-
-int CUniversityWindow::CItem::state()
-{
-	if (parent->hero->getSecSkillLevel(SecondarySkill(ID)))//hero know this skill
-		return 1;
-	if (!parent->hero->canLearnSkill())//can't learn more skills
-		return 0;
-	if (parent->hero->type->heroClass->secSkillProbability[ID]==0)//can't learn this skill (like necromancy for most of non-necros)
-		return 0;
-	return 2;
-}
-
-void CUniversityWindow::CItem::showAll(SDL_Surface * to)
-{
-	CPicture * bar;
-	switch (state())
-	{
-		case 0: bar = parent->red;
-		        break;
-		case 1: bar = parent->yellow;
-		        break;
-		case 2: bar = parent->green;
-		        break;
-		default:bar = nullptr;
-		        break;
-	}
-	assert(bar);
-
-	blitAtLoc(bar->bg, -28, -22, to);
-	blitAtLoc(bar->bg, -28,  48, to);
-	printAtMiddleLoc  (CGI->generaltexth->skillName[ID], 22, -13, FONT_SMALL, Colors::WHITE,to);//Name
-	printAtMiddleLoc  (CGI->generaltexth->levels[0], 22, 57, FONT_SMALL, Colors::WHITE,to);//Level(always basic)
-
-	CAnimImage::showAll(to);
-}
-
-CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int Y):
-	CAnimImage ("SECSKILL", _ID*3+3, 0, X, Y),
-	ID(_ID),
-	parent(_parent)
-{
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-}
-
-CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market):
-    CWindowObject(PLAYER_COLORED, "UNIVERS1"),
-    hero(_hero),
-    market(_market)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	green  = new CPicture("UNIVGREN.PCX");
-	yellow = new CPicture("UNIVGOLD.PCX");//bars
-	red    = new CPicture("UNIVRED.PCX");
-
-	green->recActions  =
-	yellow->recActions =
-	red->recActions    = DISPOSE;
-
-	CIntObject * titlePic = nullptr;
-
-	if (market->o->ID == Obj::TOWN)
-		titlePic = new CAnimImage(CGI->townh->factions[ETownType::CONFLUX]->town->clientInfo.buildingsIcons, BuildingID::MAGIC_UNIVERSITY);
-	else
-		titlePic = new CPicture("UNIVBLDG");
-
-	titlePic->center(Point(232 + pos.x, 76 + pos.y));
-
-	//Clerk speech
-	new CTextBox(CGI->generaltexth->allTexts[603], Rect(24, 129, 413, 70), 0, FONT_SMALL, CENTER, Colors::WHITE);
-
-	//University
-	new CLabel(231, 26, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[602]);
-
-	std::vector<int> list = market->availableItemsIds(EMarketMode::RESOURCE_SKILL);
-
-	assert(list.size() == 4);
-
-	for (int i=0; i<list.size(); i++)//prepare clickable items
-		items.push_back(new CItem(this, list[i], 54+i*104, 234));
-
-	cancel = new CAdventureMapButton(CGI->generaltexth->zelp[632],
-		std::bind(&CUniversityWindow::close, this),200,313,"IOKAY.DEF",SDLK_RETURN);
-
-	bar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-}
-
-CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * PARENT, int SKILL, bool available ):
-    CWindowObject(PLAYER_COLORED, "UNIVERS2.PCX"),
-    parent(PARENT)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	std::string text = CGI->generaltexth->allTexts[608];
-	boost::replace_first(text, "%s", CGI->generaltexth->levels[0]);
-	boost::replace_first(text, "%s", CGI->generaltexth->skillName[SKILL]);
-	boost::replace_first(text, "%d", "2000");
-
-	new CTextBox(text, Rect(24, 129, 413, 70), 0, FONT_SMALL, CENTER, Colors::WHITE);//Clerk speech
-
-	new CLabel(230, 37,  FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth-> skillName[SKILL]);//Skill name
-	new CAnimImage("SECSKILL", SKILL*3+3, 0, 211, 51);//skill
-	new CLabel(230, 107, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->levels[1]);//Skill level
-
-	new CAnimImage("RESOURCE", Res::GOLD, 0, 210, 210);//gold
-	new CLabel(230, 267, FONT_SMALL, CENTER, Colors::WHITE, "2000");//Cost
-
-	std::string hoverText = CGI->generaltexth->allTexts[609];
-	boost::replace_first(hoverText, "%s", CGI->generaltexth->levels[0]+ " " + CGI->generaltexth->skillName[SKILL]);
-
-	text = CGI->generaltexth->zelp[633].second;
-	boost::replace_first(text, "%s", CGI->generaltexth->levels[0]);
-	boost::replace_first(text, "%s", CGI->generaltexth->skillName[SKILL]);
-	boost::replace_first(text, "%d", "2000");
-
-	confirm= new CAdventureMapButton(hoverText, text, std::bind(&CUnivConfirmWindow::makeDeal, this, SKILL),
-	         148,299,"IBY6432.DEF",SDLK_RETURN);
-	confirm->block(!available);
-
-	cancel = new CAdventureMapButton(CGI->generaltexth->zelp[631],std::bind(&CUnivConfirmWindow::close, this),
-	         252,299,"ICANCEL.DEF",SDLK_ESCAPE);
-	bar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-}
-
-void CUnivConfirmWindow::makeDeal(int skill)
-{
-	LOCPLINT->cb->trade(parent->market->o, EMarketMode::RESOURCE_SKILL, 6, skill, 1, parent->hero);
-	close();
-}
-
-CHillFortWindow::CHillFortWindow(const CGHeroInstance *visitor, const CGObjectInstance *object):
-    CWindowObject(PLAYER_COLORED, "APHLFTBK"),
-	fort(object),
-    hero(visitor)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	slotsCount=7;
-	resources =  CDefHandler::giveDefEss("SMALRES.DEF");
-
-	new CLabel(325, 32, FONT_BIG, CENTER, Colors::YELLOW, fort->getObjectName());//Hill Fort
-
-	heroPic = new CHeroArea(30, 60, hero);
-
-	currState.resize(slotsCount+1);
-	costs.resize(slotsCount);
-	totalSumm.resize(GameConstants::RESOURCE_QUANTITY);
-	std::vector<std::string> files;
-	files += "APHLF1R.DEF", "APHLF1Y.DEF", "APHLF1G.DEF";
-	for (int i=0; i<slotsCount; i++)
-	{
-		currState[i] = getState(SlotID(i));
-		upgrade[i] = new CAdventureMapButton(getTextForSlot(SlotID(i)),"",std::bind(&CHillFortWindow::makeDeal, this, SlotID(i)),
-		                                    107+i*76, 171, "", SDLK_1+i, &files);
-		upgrade[i]->block(currState[i] == -1);
-	}
-	files.clear();
-	files += "APHLF4R.DEF", "APHLF4Y.DEF", "APHLF4G.DEF";
-	currState[slotsCount] = getState(SlotID(slotsCount));
-	upgradeAll = new CAdventureMapButton(CGI->generaltexth->allTexts[432],"",std::bind(&CHillFortWindow::makeDeal, this, SlotID(slotsCount)),
-	                                    30, 231, "", SDLK_0, &files);
-	quit = new CAdventureMapButton("","",std::bind(&CHillFortWindow::close, this), 294, 275, "IOKAY.DEF", SDLK_RETURN);
-	bar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
-
-	garr = new CGarrisonInt(108, 60, 18, Point(),background->bg,Point(108,60),hero,nullptr);
-	updateGarrisons();
-}
-
-void CHillFortWindow::updateGarrisons()
-{
-	for (int i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
-		totalSumm[i]=0;
-
-	for (int i=0; i<slotsCount; i++)
-	{
-		costs[i].clear();
-		int newState = getState(SlotID(i));
-		if (newState != -1)
-		{
-			UpgradeInfo info;
-			LOCPLINT->cb->getUpgradeInfo(hero, SlotID(i), info);
-			if (info.newID.size())//we have upgrades here - update costs
-			{
-				costs[i] = info.cost[0] * hero->getStackCount(SlotID(i));
-				totalSumm += costs[i];
-			}
-		}
-
-		currState[i] = newState;
-		upgrade[i]->setIndex(newState);
-		upgrade[i]->block(currState[i] == -1);
-		upgrade[i]->hoverTexts[0] = getTextForSlot(SlotID(i));
-	}
-
-	int newState = getState(SlotID(slotsCount));
-	currState[slotsCount] = newState;
-	upgradeAll->setIndex(newState);
-	garr->recreateSlots();
-}
-
-void CHillFortWindow::makeDeal(SlotID slot)
-{
-	assert(slot.getNum()>=0);
-	int offset = (slot.getNum() == slotsCount)?2:0;
-	switch (currState[slot.getNum()])
-	{
-		case 0:
-			LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[314 + offset],
-			          std::vector<CComponent*>(), soundBase::sound_todo);
-			break;
-		case 1:
-			LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[313 + offset],
-			          std::vector<CComponent*>(), soundBase::sound_todo);
-			break;
-		case 2:
-			for (int i=0; i<slotsCount; i++)
-				if ( slot.getNum() ==i || ( slot.getNum() == slotsCount && currState[i] == 2 ) )//this is activated slot or "upgrade all"
-				{
-					UpgradeInfo info;
-					LOCPLINT->cb->getUpgradeInfo(hero, SlotID(i), info);
-					LOCPLINT->cb->upgradeCreature(hero, SlotID(i), info.newID[0]);
-				}
-			break;
-
-	}
-}
-
-void CHillFortWindow::showAll (SDL_Surface *to)
-{
-	CWindowObject::showAll(to);
-
-	for ( int i=0; i<slotsCount; i++)
-	{
-		if ( currState[i] == 0 || currState[i] == 2 )
-		{
-			if ( costs[i].size() )//we have several elements
-			{
-				int curY = 128;//reverse iterator is used to display gold as first element
-				for(int j = costs[i].size()-1; j >= 0; j--)
-				{
-					int val = costs[i][j];
-					if(!val) continue;
-
-					blitAtLoc(resources->ourImages[j].bitmap, 104+76*i, curY, to);
-					printToLoc(boost::lexical_cast<std::string>(val), 168+76*i, curY+16, FONT_SMALL, Colors::WHITE, to);
-					curY += 20;
-				}
-			}
-			else//free upgrade - print gold image and "Free" text
-			{
-				blitAtLoc(resources->ourImages[6].bitmap, 104+76*i, 128, to);
-				printToLoc(CGI->generaltexth->allTexts[344], 168+76*i, 144, FONT_SMALL, Colors::WHITE, to);
-			}
-		}
-	}
-	for (int i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
-	{
-		if (totalSumm[i])//this resource is used - display it
-		{
-			blitAtLoc(resources->ourImages[i].bitmap, 104+76*i, 237, to);
-			printToLoc(boost::lexical_cast<std::string>(totalSumm[i]), 166+76*i, 253, FONT_SMALL, Colors::WHITE, to);
-		}
-	}
-}
-
-std::string CHillFortWindow::getTextForSlot(SlotID slot)
-{
-	if ( !hero->getCreature(slot) )//we don`t have creature here
-		return "";
-
-	std::string str = CGI->generaltexth->allTexts[318];
-	int amount = hero->getStackCount(slot);
-	if ( amount == 1 )
-		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->nameSing);
-	else
-		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->namePl);
-
-	return str;
-}
-
-int CHillFortWindow::getState(SlotID slot)
-{
-	TResources myRes = LOCPLINT->cb->getResourceAmount();
-	if ( slot.getNum() == slotsCount )//"Upgrade all" slot
-	{
-		bool allUpgraded = true;//All creatures are upgraded?
-		for (int i=0; i<slotsCount; i++)
-			allUpgraded &=  currState[i] == 1 || currState[i] == -1;
-
-		if (allUpgraded)
-			return 1;
-
-		if(!totalSumm.canBeAfforded(myRes))
-			return 0;
-
-		return 2;
-	}
-
-	if (hero->slotEmpty(slot))//no creature here
-		return -1;
-
-	UpgradeInfo info;
-	LOCPLINT->cb->getUpgradeInfo(hero, slot, info);
-	if (!info.newID.size())//already upgraded
-		return 1;
-
-	if(!(info.cost[0] * hero->getStackCount(slot)).canBeAfforded(myRes))
-			return 0;
-
-	return 2;//can upgrade
-}
-
-CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner):
-    CWindowObject(PLAYER_COLORED | BORDERED, "TpRank"),
-	owner(_owner)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	type |= BLOCK_ADV_HOTKEYS;
-
-	SThievesGuildInfo tgi; //info to be displayed
-	LOCPLINT->cb->getThievesGuildInfo(tgi, owner);
-
-	exitb = new CAdventureMapButton (CGI->generaltexth->allTexts[600], "", std::bind(&CThievesGuildWindow::close,this), 748, 556, "TPMAGE1", SDLK_RETURN);
-	exitb->assignedKeys.insert(SDLK_ESCAPE);
-	statusBar = new CGStatusBar(3, 555, "TStatBar.bmp", 742);
-
-	resdatabar = new CMinorResDataBar();
-	resdatabar->pos.x += pos.x;
-	resdatabar->pos.y += pos.y;
-
-	//data for information table:
-	// fields[row][column] = list of id's of players for this box
-	static std::vector< std::vector< PlayerColor > > SThievesGuildInfo::* fields[] =
-		{ &SThievesGuildInfo::numOfTowns, &SThievesGuildInfo::numOfHeroes,       &SThievesGuildInfo::gold,
-		  &SThievesGuildInfo::woodOre,    &SThievesGuildInfo::mercSulfCrystGems, &SThievesGuildInfo::obelisks,
-		  &SThievesGuildInfo::artifacts,  &SThievesGuildInfo::army,              &SThievesGuildInfo::income };
-
-	//printing texts & descriptions to background
-
-	for(int g=0; g<12; ++g)
-	{
-		int posY[] = {400, 460, 510};
-		int y;
-		if(g < 9)
-			y = 52 + 32*g;
-		else
-			y = posY[g-9];
-
-		std::string text = CGI->generaltexth->jktexts[24+g];
-		boost::algorithm::trim_if(text,boost::algorithm::is_any_of("\""));
-		new CLabel(135, y, FONT_MEDIUM, CENTER, Colors::YELLOW, text);
-	}
-
-	for(int g=1; g<tgi.playerColors.size(); ++g)
-		new CAnimImage("PRSTRIPS", g-1, 0, 250 + 66*g, 7);
-
-	for(int g=0; g<tgi.playerColors.size(); ++g)
-		new CLabel(283 + 66*g, 24, FONT_BIG, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[16+g]);
-
-	//printing flags
-	for(int g = 0; g < ARRAY_COUNT(fields); ++g) //by lines
-	{
-		for(int b=0; b<(tgi .* fields[g]).size(); ++b) //by places (1st, 2nd, ...)
-		{
-			std::vector<PlayerColor> &players = (tgi .* fields[g])[b]; //get players with this place in this line
-
-			//position of box
-			int xpos = 259 + 66 * b;
-			int ypos = 41 +  32 * g;
-
-			size_t rowLength[2]; //size of each row
-			rowLength[0] = std::min<size_t>(players.size(), 4);
-			rowLength[1] = players.size() - rowLength[0];
-
-			for (size_t j=0; j< 2; j++)
-			{
-				// origin of this row | offset for 2nd row| shift right for short rows
-				//if we have 2 rows, start either from mid or beginning (depending on count), otherwise center the flags
-				int rowStartX = xpos   +   (j ? 6 + (rowLength[j] < 3 ? 12 : 0) : 24 - 6 * rowLength[j]);
-				int rowStartY = ypos   +   (j ? 4 : 0);
-
-				for (size_t i=0; i< rowLength[j]; i++)
-				{
-					new CAnimImage("itgflags", players[i + j*4].getNum(), 0, rowStartX + i*12, rowStartY);
-				}
-			}
-		}
-	}
-
-	static const std::string colorToBox[] = {"PRRED.BMP", "PRBLUE.BMP", "PRTAN.BMP", "PRGREEN.BMP", "PRORANGE.BMP", "PRPURPLE.BMP", "PRTEAL.BMP", "PRROSE.bmp"};
-
-	//printing best hero
-	int counter = 0;
-	for(auto & iter : tgi.colorToBestHero)
-	{
-		if(iter.second.portrait >= 0)
-		{
-			new CPicture(colorToBox[iter.first.getNum()], 253 + 66 * counter, 334);
-			new CAnimImage("PortraitsSmall", iter.second.portrait, 0, 260 + 66 * counter, 360);
-			//TODO: r-click info:
-			// - r-click on hero
-			// - r-click on primary skill label
-			if(iter.second.details)
-			{
-				new CTextBox(CGI->generaltexth->allTexts[184], Rect(260 + 66*counter, 396, 52, 64),
-				             0, FONT_TINY, TOPLEFT, Colors::WHITE);
-				for (int i=0; i<iter.second.details->primskills.size(); ++i)
-				{
-					new CLabel(310 + 66 * counter, 407 + 11*i, FONT_TINY, BOTTOMRIGHT, Colors::WHITE,
-					           boost::lexical_cast<std::string>(iter.second.details->primskills[i]));
-				}
-			}
-		}
-		counter++;
-	}
-
-	//printing best creature
-	counter = 0;
-	for(auto & it : tgi.bestCreature)
-	{
-		if(it.second >= 0)
-			new CAnimImage("TWCRPORT", it.second+2, 0, 255 + 66 * counter, 479);
-		counter++;
-	}
-
-	//printing personality
-	counter = 0;
-	for(auto & it : tgi.personality)
-	{
-		std::string text;
-        if(it.second == EAiTactic::NONE)
-        {
-			text = CGI->generaltexth->arraytxt[172];
-        }
-        else if(it.second != EAiTactic::RANDOM)
-        {
-            text = CGI->generaltexth->arraytxt[168 + it.second];
-        }
-
-		new CLabel(283 + 66*counter, 459, FONT_SMALL, CENTER, Colors::WHITE, text);
-
-		counter++;
-	}
-}
-
-void MoraleLuckBox::set(const IBonusBearer *node)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
-	const int noneTxtId = 108; //Russian version uses same text for neutral morale\luck
-	const int neutralDescr[] = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
-	const int componentType[] = {CComponent::luck, CComponent::morale};
-	const int hoverTextBase[] = {7, 4};
-	const Bonus::BonusType bonusType[] = {Bonus::LUCK, Bonus::MORALE};
-	int (IBonusBearer::*getValue[])() const = {&IBonusBearer::LuckVal, &IBonusBearer::MoraleVal};
-
-	int mrlt = -9;
-	TModDescr mrl;
-
-	if (node)
-	{
-		node->getModifiersWDescr(mrl, bonusType[morale]);
-		bonusValue = (node->*getValue[morale])();
-	}
-	else
-		bonusValue = 0;
-
-	mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
-	hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
-	baseType = componentType[morale];
-	text = CGI->generaltexth->arraytxt[textId[morale]];
-	boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
-	if (!mrl.size())
-		text += CGI->generaltexth->arraytxt[noneTxtId];
-	else
-	{
-		//it's a creature window
-		if ((morale && node->hasBonusOfType(Bonus::UNDEAD)) ||
-			node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING))
-		{
-			text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
-		}
-		else
-		{
-			for(auto & elem : mrl)
-			{
-				if (elem.first) //no bonuses with value 0
-					text += "\n" + elem.second;
-			}
-		}
-	}
-
-	std::string imageName;
-	if (small)
-		imageName = morale ? "IMRL30": "ILCK30";
-	else
-		imageName = morale ? "IMRL42" : "ILCK42";
-
-	delete image;
-	image = new CAnimImage(imageName, bonusValue + 3);
-	image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2));//center icon
-}
-
-MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small):
-	image(nullptr),
-	morale(Morale),
-	small(Small)
-{
-	bonusValue = 0;
-	pos = r + pos;
-}
-
-CArtifactHolder::CArtifactHolder()
-{
-}
-
-void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation &artLoc)
-{
-	for(CArtifactsOfHero *aoh : artSets)
-		aoh->artifactRemoved(artLoc);
-}
-
-void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
-{
-	CArtifactsOfHero *destaoh = nullptr;
-	for(CArtifactsOfHero *aoh : artSets)
-	{
-		aoh->artifactMoved(artLoc, destLoc);
-		aoh->redraw();
-		if(destLoc.isHolder(aoh->getHero()))
-			destaoh = aoh;
-	}
-
-	//Make sure the status bar is updated so it does not display old text
-	if(destaoh != nullptr && destaoh->getArtPlace(destLoc.slot) != nullptr)
-	{
-		destaoh->getArtPlace(destLoc.slot)->hover(true);
-	}
-}
-
-void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc)
-{
-	for(CArtifactsOfHero *aoh : artSets)
-		aoh->artifactDisassembled(artLoc);
-}
-
-void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation &artLoc)
-{
-	for(CArtifactsOfHero *aoh : artSets)
-		aoh->artifactAssembled(artLoc);
-}
-
-void CArtifactsOfHero::SCommonPart::Artpos::clear()
-{
-	slotID = ArtifactPosition::PRE_FIRST;
-	AOH = nullptr;
-	art = nullptr;
-}
-
-CArtifactsOfHero::SCommonPart::Artpos::Artpos()
-{
-	clear();
-}
-
-void CArtifactsOfHero::SCommonPart::Artpos::setTo(const CArtPlace *place, bool dontTakeBackpack)
-{
-	slotID = place->slotID;
-	AOH = place->ourOwner;
-
-	if(slotID >= 19 && dontTakeBackpack)
-		art = nullptr;
-	else
-		art = place->ourArt;
-}
-
-bool CArtifactsOfHero::SCommonPart::Artpos::operator==(const ArtifactLocation &al) const
-{
-	if(!AOH)
-		return false;
-	bool ret = al.isHolder(AOH->curHero)  &&  al.slot == slotID;
-
-	//assert(al.getArt() == art);
-	return ret;
-}
-
-bool CArtifactsOfHero::SCommonPart::Artpos::valid()
-{
-	assert(AOH && art);
-	return art == AOH->curHero->getArt(slotID);
-}
-
-void CRClickPopup::clickRight(tribool down, bool previousState)
-{
-	if(down)
-		return;
-	close();
-}
-
-void CRClickPopup::close()
-{
-	GH.popIntTotally(this);
-}
-
-void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
-{
-	PlayerColor player = LOCPLINT ? LOCPLINT->playerID : PlayerColor(1); //if no player, then use blue
-
-	CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
-	temp->center(Point(GH.current->motion)); //center on mouse
-	temp->fitToScreen(10);
-	auto  rcpi = new CRClickPopupInt(temp,true);
-	GH.pushInt(rcpi);
-}
-
-void CRClickPopup::createAndPush(const std::string &txt, CComponent * component)
-{
-	CInfoWindow::TCompsInfo intComps;
-	intComps.push_back(component);
-
-	createAndPush(txt, intComps);
-}
-
-Point CInfoBoxPopup::toScreen(Point p)
-{
-	vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);
-	vstd::abetween(p.y, adventureInt->terrain.pos.y + 100, adventureInt->terrain.pos.y + adventureInt->terrain.pos.h - 100);
-
-	return p;
-}
-
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town):
-    CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
-{
-	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(town, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CTownTooltip(Point(9, 10), iah);
-}
-
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero):
-    CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
-{
-	InfoAboutHero iah;
-	LOCPLINT->cb->getHeroInfo(hero, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CHeroTooltip(Point(9, 10), iah);
-}
-
-CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr):
-    CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
-{
-	InfoAboutTown iah;
-	LOCPLINT->cb->getTownInfo(garr, iah);
-
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CArmyTooltip(Point(9, 10), iah);
-}
-
-CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
-{
-	if(!specific)
-		specific = adventureInt->selection;
-
-	assert(specific);
-
-	switch(specific->ID)
-	{
-	case Obj::HERO:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGHeroInstance *>(specific));
-	case Obj::TOWN:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGTownInstance *>(specific));
-	case Obj::GARRISON:
-	case Obj::GARRISON2:
-		return new CInfoBoxPopup(position, dynamic_cast<const CGGarrison *>(specific));
-	default:
-		return nullptr;
-	}
-}
-
-void CRClickPopup::createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment /*= BOTTOMRIGHT*/)
-{
-	CIntObject *iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
-	if(iWin)
-		GH.pushInt(iWin);
-	else
-	{
-		if (adventureInt->curHero())
-			CRClickPopup::createAndPush(obj->getHoverText(adventureInt->curHero()));
-		else
-			CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
-	}
-}
-
-CRClickPopup::CRClickPopup()
-{
-	addUsedEvents(RCLICK);
-}
-
-CRClickPopup::~CRClickPopup()
-{
-}
-
-void CRClickPopupInt::show(SDL_Surface * to)
-{
-	inner->show(to);
-}
-
-CRClickPopupInt::CRClickPopupInt( IShowActivatable *our, bool deleteInt )
-{
-	CCS->curh->hide();
-	inner = our;
-	delInner = deleteInt;
-}
-
-CRClickPopupInt::~CRClickPopupInt()
-{
-	// 	//workaround for hero window issue - if it's our interface, call dispose to properly reset it's state
-	// 	//TODO? it might be better to rewrite hero window so it will bee newed/deleted on opening / closing (not effort-worthy now, but on some day...?)
-	// 	if(LOCPLINT && inner == adventureInt->heroWindow)
-	// 		adventureInt->heroWindow->dispose();
-
-	if(delInner)
-		delete inner;
-
-	CCS->curh->show();
-}
-
-void CRClickPopupInt::showAll(SDL_Surface * to)
-{
-	inner->showAll(to);
-}

+ 0 - 1202
client/GUIClasses.h

@@ -1,1202 +0,0 @@
-#pragma once
-
-#include "CAnimation.h"
-#include "../lib/FunctionList.h"
-#include "../lib/ResourceSet.h"
-#include "../lib/CConfigHandler.h"
-#include "../lib/GameConstants.h"
-#include "gui/CIntObject.h"
-#include "gui/CIntObjectClasses.h"
-#include "../lib/GameConstants.h"
-
-#ifdef max
-#undef max
-#endif
-#ifdef min
-#undef min
-#endif
-
-/*
- * GUIClasses.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
- *
- */
-
-struct ArtifactLocation;
-class CStackBasicDescriptor;
-class CBonusSystemNode;
-class CArtifact;
-class CDefEssential;
-class CAdventureMapButton;
-class CHighlightableButtonsGroup;
-class CDefHandler;
-struct HeroMoveDetails;
-class CDefEssential;
-class CGHeroInstance;
-class CAdvMapInt;
-class CCastleInterface;
-class CBattleInterface;
-class CStack;
-class CComponent;
-class CCreature;
-struct SDL_Surface;
-struct CPath;
-class CCreatureAnim;
-class CSelectableComponent;
-class CCreatureSet;
-class CGObjectInstance;
-class CGDwelling;
-class CSlider;
-struct UpgradeInfo;
-template <typename T> struct CondSh;
-class CInGameConsole;
-class CGarrisonInt;
-class CInGameConsole;
-struct Component;
-class CArmedInstance;
-class CGTownInstance;
-class StackState;
-class CPlayerInterface;
-class CHeroWindow;
-class CArtifact;
-class CArtifactsOfHero;
-class CCreatureArtifactSet;
-class CResDataBar;
-struct SPuzzleInfo;
-class CGGarrison;
-class CStackInstance;
-class IMarket;
-class CTextBox;
-class CArtifactInstance;
-class IBonusBearer;
-class CArtPlace;
-class CAnimImage;
-struct InfoAboutArmy;
-struct InfoAboutHero;
-struct InfoAboutTown;
-
-/// text + comp. + ok button
-class CInfoWindow : public CSimpleWindow
-{ //window able to delete its components when closed
-	bool delComps; //whether comps will be deleted
-
-public:
-	typedef std::vector<std::pair<std::string,CFunctionList<void()> > > TButtonsInfo;
-	typedef std::vector<CComponent*> TCompsInfo;
-	QueryID ID; //for identification
-	CTextBox *text;
-	std::vector<CAdventureMapButton *> buttons;
-	std::vector<CComponent*> components;
-	CSlider *slider;
-
-	void setDelComps(bool DelComps);
-	virtual void close();
-
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void sliderMoved(int to);
-
-	CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo &comps = TCompsInfo(), const TButtonsInfo &Buttons = TButtonsInfo(), bool delComps = true); //c-tor
-	CInfoWindow(); //c-tor
-	~CInfoWindow(); //d-tor
-
-	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
-	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1));
-	static void showOkDialog(const std::string & text, const std::vector<CComponent*> *components, const std::function<void()> & onOk, bool delComps = true, PlayerColor player = PlayerColor(1));
-	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1));
-	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false);
-
-	/// create text from title and description: {title}\n\n description
-	static std::string genText(std::string title, std::string description);
-};
-
-/// component selection window
-class CSelWindow : public CInfoWindow
-{ //warning - this window deletes its components by closing!
-public:
-	void selectionChange(unsigned to);
-	void madeChoice(); //looks for selected component and calls callback
-	CSelWindow(const std::string& text, PlayerColor player, int charperline ,const std::vector<CSelectableComponent*> &comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID); //c-tor
-	CSelWindow(){}; //c-tor
-	//notification - this class inherits important destructor from CInfoWindow
-};
-
-/// popup displayed on R-click
-class CRClickPopup : public CIntObject
-{
-public:
-	virtual void close();
-	void clickRight(tribool down, bool previousState);
-
-	CRClickPopup();
-	virtual ~CRClickPopup(); //d-tor
-
-	static CIntObject* createInfoWin(Point position, const CGObjectInstance * specific);
-	static void createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
-	static void createAndPush(const std::string &txt, CComponent * component);
-	static void createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
-};
-
-/// popup displayed on R-click
-class CRClickPopupInt : public CRClickPopup
-{
-public:
-	IShowActivatable *inner;
-	bool delInner;
-
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	CRClickPopupInt(IShowActivatable *our, bool deleteInt); //c-tor
-	virtual ~CRClickPopupInt(); //d-tor
-};
-
-class CInfoPopup : public CRClickPopup
-{
-public:
-	bool free; //TODO: comment me
-	SDL_Surface * bitmap; //popup background
-	void close();
-	void show(SDL_Surface * to);
-	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false); //c-tor
-	CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free=false); //c-tor
-	CInfoPopup(SDL_Surface * Bitmap = nullptr, bool Free = false); //default c-tor
-
-	void init(int x, int y);
-	~CInfoPopup(); //d-tor
-};
-
-/// popup on adventure map for town\hero objects
-class CInfoBoxPopup : public CWindowObject
-{
-	Point toScreen(Point pos);
-public:
-	CInfoBoxPopup(Point position, const CGTownInstance * town);
-	CInfoBoxPopup(Point position, const CGHeroInstance * hero);
-	CInfoBoxPopup(Point position, const CGGarrison * garr);
-};
-
-/// common popup window component
-class CComponent : public virtual CIntObject
-{
-public:
-	enum Etype
-	{
-		primskill, secskill, resource, creature, artifact, experience, spell, morale, luck, building, hero, flag, typeInvalid
-	};
-
-	//NOTE: not all types have exact these sizes or have less than 4 of them. In such cases closest one will be used
-	enum ESize
-	{
-		tiny,  // ~22-24px
-		small, // ~30px
-		medium,// ~42px
-		large,  // ~82px
-		sizeInvalid
-	};
-
-private:
-	size_t getIndex();
-	const std::vector<std::string> getFileName();
-	void setSurface(std::string defName, int imgPos);
-	std::string getSubtitleInternal();
-
-	void init(Etype Type, int Subtype, int Val, ESize imageSize);
-
-public:
-	CAnimImage *image; //our image
-
-	Etype compType; //component type
-	ESize size; //component size.
-	int subtype; //type-dependant subtype. See getSomething methods for details
-	int val; // value \ strength \ amount of component. See getSomething methods for details
-	bool perDay; // add "per day" text to subtitle
-
-	std::string getDescription();
-	std::string getSubtitle();
-
-	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large);//c-tor
-	CComponent(const Component &c); //c-tor
-
-	void clickRight(tribool down, bool previousState); //call-in
-};
-
-/// component that can be selected or deselected
-class CSelectableComponent : public CComponent, public CKeyShortcut
-{
-	void init();
-public:
-	bool selected; //if true, this component is selected
-	std::function<void()> onSelect; //function called on selection change
-
-	void showAll(SDL_Surface * to);
-	void select(bool on);
-
-	void clickLeft(tribool down, bool previousState); //call-in
-	CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize=large, std::function<void()> OnSelect = nullptr); //c-tor
-	CSelectableComponent(const Component &c, std::function<void()> OnSelect = nullptr); //c-tor
-};
-
-/// box with multiple components (up to 8?)
-/// will take ownership on components and delete them afterwards
-class CComponentBox : public CIntObject
-{
-	std::vector<CComponent *> components;
-
-	CSelectableComponent * selected;
-	std::function<void(int newID)> onSelect;
-
-	void selectionChanged(CSelectableComponent * newSelection);
-
-	//get position of "or" text between these comps
-	//it will place "or" equidistant to both images
-	Point getOrTextPos(CComponent *left, CComponent * right);
-
-	//get distance between these copmonents
-	int getDistance(CComponent *left, CComponent * right);
-	void placeComponents(bool selectable);
-
-public:
-	/// return index of selected item
-	int selectedIndex();
-
-	/// constructor for quite common 1-components popups
-	/// if position width or height are 0 then it will be determined automatically
-	CComponentBox(CComponent * components, Rect position);
-	/// constructor for non-selectable components
-	CComponentBox(std::vector<CComponent *> components, Rect position);
-
-	/// constructor for selectable components
-	/// will also create "or" labels between components
-	/// onSelect - optional function that will be called every time on selection change
-	CComponentBox(std::vector<CSelectableComponent *> components, Rect position, std::function<void(int newID)> onSelect = nullptr);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-/// base class for hero/town/garrison tooltips
-class CArmyTooltip : public CIntObject
-{
-	void init(const InfoAboutArmy &army);
-public:
-	CArmyTooltip(Point pos, const InfoAboutArmy &army);
-	CArmyTooltip(Point pos, const CArmedInstance * army);
-};
-
-/// Class for hero tooltip. Does not have any background!
-/// background for infoBox: ADSTATHR
-/// background for tooltip: HEROQVBK
-class CHeroTooltip : public CArmyTooltip
-{
-	void init(const InfoAboutHero &hero);
-public:
-	CHeroTooltip(Point pos, const InfoAboutHero &hero);
-	CHeroTooltip(Point pos, const CGHeroInstance * hero);
-};
-
-/// Class for town tooltip. Does not have any background!
-/// background for infoBox: ADSTATCS
-/// background for tooltip: TOWNQVBK
-class CTownTooltip : public CArmyTooltip
-{
-	void init(const InfoAboutTown &town);
-public:
-	CTownTooltip(Point pos, const InfoAboutTown &town);
-	CTownTooltip(Point pos, const CGTownInstance * town);
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class CGarrisonInt;
-
-/// A single garrison slot which holds one creature of a specific amount
-class CGarrisonSlot : public CIntObject
-{
-	SlotID ID; //for identification
-	CGarrisonInt *owner;
-	const CStackInstance *myStack; //nullptr if slot is empty
-	const CCreature *creature;
-	int upg; //0 - up garrison, 1 - down garrison
-
-	CAnimImage * creatureImage;
-	CAnimImage * selectionImage; // image for selection, not always visible
-	CLabel * stackCount;
-
-	void setHighlight(bool on);
-public:
-	virtual void hover (bool on); //call-in
-	const CArmedInstance * getObj() const;
-	bool our() const;
-	void clickRight(tribool down, bool previousState);
-	void clickLeft(tribool down, bool previousState);
-	void update();
-	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg=0, const CStackInstance * Creature=nullptr);
-
-	friend class CGarrisonInt;
-};
-
-/// Class which manages slots of upper and lower garrison, splitting of units
-class CGarrisonInt :public CIntObject
-{
-	CGarrisonSlot *highlighted; //chosen slot. Should be changed only via selectSlot
-	bool inSplittingMode;
-
-public:
-	void selectSlot(CGarrisonSlot * slot); //null = deselect
-	const CGarrisonSlot * getSelection();
-
-	void setSplittingMode(bool on);
-	bool getSplittingMode();
-
-	int interx; //space between slots
-	Point garOffset; //offset between garrisons (not used if only one hero)
-	std::vector<CAdventureMapButton *> splitButtons; //may be empty if no buttons
-
-	SlotID p2; //TODO: comment me
-	int	shiftPos;//1st slot of the second row, set shiftPoint for effect
-	bool pb,
-	     smallIcons, //true - 32x32 imgs, false - 58x64
-	     removableUnits,//player can remove units from up
-	     twoRows,//slots will be placed in 2 rows
-		 owned[2];//player owns up or down army [0] upper, [1] lower
-
-// 	const CCreatureSet *set1; //top set of creatures
-// 	const CCreatureSet *set2; //bottom set of creatures
-
-	std::vector<CGarrisonSlot*> slotsUp, slotsDown; //slots of upper and lower garrison
-	const CArmedInstance *armedObjs[2]; //[0] is upper, [1] is down
-	//const CArmedInstance *oup, *odown; //upper and lower garrisons (heroes or towns)
-
-	void setArmy(const CArmedInstance *army, bool bottomGarrison);
-	void addSplitBtn(CAdventureMapButton * button);
-	void createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int distance, int posY, int Upg );
-
-	void createSlots();
-	void recreateSlots();
-
-	void splitClick(); //handles click on split button
-	void splitStacks(int amountLeft, int amountRight); //TODO: comment me
-	//x, y - position;
-	//inx - distance between slots;
-	//pomsur, SurOffset - UNUSED
-	//s1, s2 - top and bottom armies;
-	//removableUnits - you can take units from top;
-	//smallImgs - units images size 64x58 or 32x32;
-	//twoRows - display slots in 2 row (1st row = 4 slots, 2nd = 3 slots)
-	CGarrisonInt(int x, int y, int inx, const Point &garsOffset, SDL_Surface *pomsur, const Point &SurOffset, const CArmedInstance *s1, const CArmedInstance *s2=nullptr, bool _removableUnits = true, bool smallImgs = false, bool _twoRows=false); //c-tor
-};
-
-/// draws picture with creature on background, use Animated=true to get animation
-class CCreaturePic : public CIntObject
-{
-private:
-	CPicture *bg;
-	CCreatureAnim *anim; //displayed animation
-
-public:
-	CCreaturePic(int x, int y, const CCreature *cre, bool Big=true, bool Animated=true); //c-tor
-};
-
-/// Recruitment window where you can recruit creatures
-class CRecruitmentWindow : public CWindowObject
-{
-	class CCreatureCard : public CIntObject
-	{
-		CRecruitmentWindow * parent;
-		CCreaturePic *pic; //creature's animation
-		bool selected;
-
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void showAll(SDL_Surface *to);
-	public:
-		const CCreature * creature;
-		si32 amount;
-
-		void select(bool on);
-
-		CCreatureCard(CRecruitmentWindow * window, const CCreature *crea, int totalAmount);
-	};
-
-	/// small class to display creature costs
-	class CCostBox : public CIntObject
-	{
-		std::map<int, std::pair<CLabel *, CAnimImage * > > resources;
-	public:
-		//res - resources to show
-		void set(TResources res);
-		//res - visible resources
-		CCostBox(Rect position, std::string title);
-		void createItems(TResources res);
-	};
-
-	std::function<void(CreatureID,int)> onRecruit; //void (int ID, int amount) <-- call to recruit creatures
-
-	int level;
-	const CArmedInstance *dst;
-
-	CCreatureCard * selected;
-	std::vector<CCreatureCard *> cards;
-
-	CSlider *slider; //for selecting amount
-	CAdventureMapButton *maxButton, *buyButton, *cancelButton;
-	//labels for visible values
-	CLabel * title;
-	CLabel * availableValue;
-	CLabel * toRecruitValue;
-	CCostBox * costPerTroopValue;
-	CCostBox * totalCostValue;
-
-	void select(CCreatureCard * card);
-	void buy();
-	void sliderMoved(int to);
-
-	void showAll(SDL_Surface *to);
-public:
-	const CGDwelling * const dwelling;
-	CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const std::function<void(CreatureID,int)> & Recruit, int y_offset = 0); //creatures - pairs<creature_ID,amount> //c-tor
-	void availableCreaturesChanged();
-};
-
-/// Split window where creatures can be split up into two single unit stacks
-class CSplitWindow : public CWindowObject
-{
-	std::function<void(int, int)> callback;
-	int leftAmount;
-	int rightAmount;
-
-	int leftMin;
-	int rightMin;
-
-	CSlider *slider;
-	CCreaturePic *animLeft, *animRight; //creature's animation
-	CAdventureMapButton *ok, *cancel;
-
-	CTextInput *leftInput, *rightInput;
-	void setAmountText(std::string text, bool left);
-	void setAmount(int value, bool left);
-	void sliderMoved(int value);
-	void apply();
-
-public:
-	/**
-	 * creature - displayed creature
-	 * callback(leftAmount, rightAmount) - function to call on close
-	 * leftMin, rightMin - minimal amount of creatures in each stack
-	 * leftAmount, rightAmount - amount of creatures in each stack
-	 */
-	CSplitWindow(const CCreature * creature, std::function<void(int, int)> callback,
-	             int leftMin, int rightMin, int leftAmount, int rightAmount);
-};
-
-/// Raised up level windowe where you can select one out of two skills
-class CLevelWindow : public CWindowObject
-{
-	CComponentBox * box; //skills to select
-	std::function<void(ui32)> cb;
-
-	void selectionChanged(unsigned to);
-public:
-
-	CLevelWindow(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, std::function<void(ui32)> callback); //c-tor
-	~CLevelWindow(); //d-tor
-
-};
-
-/// Resource bar like that at the bottom of the adventure map screen
-class CMinorResDataBar : public CIntObject
-{
-public:
-	SDL_Surface *bg; //background bitmap
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	CMinorResDataBar(); //c-tor
-	~CMinorResDataBar(); //d-tor
-};
-
-/// Town portal, castle gate window
-class CObjectListWindow : public CWindowObject
-{
-	class CItem : public CIntObject
-	{
-		CObjectListWindow *parent;
-		CLabel *text;
-		CPicture *border;
-	public:
-		const size_t index;
-		CItem(CObjectListWindow *parent, size_t id, std::string text);
-
-		void select(bool on);
-		void clickLeft(tribool down, bool previousState);
-	};
-
-	std::function<void(int)> onSelect;//called when OK button is pressed, returns id of selected item.
-	CLabel * title;
-	CLabel * descr;
-
-	CListBox * list;
-	CIntObject * titleImage;//title image (castle gate\town portal picture)
-	CAdventureMapButton *ok, *exit;
-
-	std::vector< std::pair<int, std::string> > items;//all items present in list
-
-	void init(CIntObject * titlePic, std::string _title, std::string _descr);
-public:
-	size_t selected;//index of currently selected item
-	/// Callback will be called when OK button is pressed, returns id of selected item. initState = initially selected item
-	/// Image can be nullptr
-	///item names will be taken from map objects
-	CObjectListWindow(const std::vector<int> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
-                      std::function<void(int)> Callback);
-	CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
-                      std::function<void(int)> Callback);
-
-	CIntObject *genItem(size_t index);
-	void elementSelected();//call callback and close this window
-	void changeSelection(size_t which);
-	void keyPressed (const SDL_KeyboardEvent & key);
-};
-
-class CArtifactHolder
-{
-public:
-	CArtifactHolder();
-
-	virtual void artifactRemoved(const ArtifactLocation &artLoc)=0;
-	virtual void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)=0;
-	virtual void artifactDisassembled(const ArtifactLocation &artLoc)=0;
-	virtual void artifactAssembled(const ArtifactLocation &artLoc)=0;
-};
-
-class CWindowWithArtifacts : public CArtifactHolder
-{
-public:
-	std::vector<CArtifactsOfHero *> artSets;
-
-	void artifactRemoved(const ArtifactLocation &artLoc);
-	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc);
-	void artifactDisassembled(const ArtifactLocation &artLoc);
-	void artifactAssembled(const ArtifactLocation &artLoc);
-};
-
-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:
-	CLabel *title;
-	CLabelGroup *leftGroup;
-	CLabelGroup *rightGroup;
-	CAdventureMapButton *load, *save, *restart, *mainMenu, *quitGame, *backToMap; //load and restart are not used yet
-	CHighlightableButtonsGroup * heroMoveSpeed;
-	CHighlightableButtonsGroup * mapScrollSpeed;
-	CHighlightableButtonsGroup * musicVolume, * effectsVolume;
-
-	//CHighlightableButton * showPath;
-	CHighlightableButton * showReminder;
-	CHighlightableButton * quickCombat;
-	CHighlightableButton * spellbookAnim;
-	CHighlightableButton * newCreatureWin;
-	CHighlightableButton * fullscreen;
-
-	CAdventureMapButton *gameResButton;
-	CLabel *gameResLabel;
-
-	SettingsListener onFullscreenChanged;
-
-	void setMusicVolume( int newVolume );
-	void setSoundVolume( int newVolume );
-	void setHeroMoveSpeed( int newSpeed );
-	void setMapScrollingSpeed( int newSpeed );
-
-	//functions bound to buttons
-	void bloadf(); //load game
-	void bsavef(); //save game
-	void bquitf(); //quit game
-	void breturnf(); //return to game
-	void brestartf(); //restart game
-	void bmainmenuf(); //return to main menu
-
-	//functions for checkboxes
-	void toggleReminder(bool on);
-	void toggleQuickCombat(bool on);
-	void toggleSpellbookAnim(bool on);
-	void toggleCreatureWin(bool on);
-	void toggleFullscreen(bool on);
-
-	void selectGameRes();
-	void setGameRes(int index);
-	void closeAndPushEvent(int eventType, int code = 0);
-
-public:
-	CSystemOptionsWindow(); //c-tor
-};
-
-class CTavernWindow : public CWindowObject
-{
-public:
-	class HeroPortrait : public CIntObject
-	{
-	public:
-		std::string hoverName;
-		std::string description; // "XXX is a level Y ZZZ with N artifacts"
-		const CGHeroInstance *h;
-
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void hover (bool on);
-		HeroPortrait(int &sel, int id, int x, int y, const CGHeroInstance *H);
-
-	private:
-		int *_sel;
-		const int _id;
-
-	} *h1, *h2; //recruitable heroes
-
-	CGStatusBar *bar; //tavern's internal status bar
-	int selected;//0 (left) or 1 (right)
-	int oldSelected;//0 (left) or 1 (right)
-
-	CAdventureMapButton *thiefGuild, *cancel, *recruit;
-	const CGObjectInstance *tavernObj;
-
-	CTavernWindow(const CGObjectInstance *TavernObj); //c-tor
-	~CTavernWindow(); //d-tor
-
-	void recruitb();
-	void thievesguildb();
-	void show(SDL_Surface * to);
-};
-
-class CInGameConsole : public CIntObject
-{
-private:
-	std::list< std::pair< std::string, int > > texts; //list<text to show, time of add>
-	boost::mutex texts_mx;		// protects texts
-	std::vector< std::string > previouslyEntered; //previously entered texts, for up/down arrows to work
-	int prevEntDisp; //displayed entry from previouslyEntered - if none it's -1
-	int defaultTimeout; //timeout for new texts (in ms)
-	int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
-public:
-	std::string enteredText;
-	void show(SDL_Surface * to);
-	void print(const std::string &txt);
-	void keyPressed (const SDL_KeyboardEvent & key); //call-in
-
-#ifndef VCMI_SDL1
-	void textInputed(const SDL_TextInputEvent & event) override;
-	void textEdited(const SDL_TextEditingEvent & event) override;
-#endif // VCMI_SDL1
-
-	void startEnteringText();
-	void endEnteringText(bool printEnteredText);
-	void refreshEnteredText();
-
-	CInGameConsole(); //c-tor
-};
-
-/// Can interact on left and right mouse clicks
-class LRClickableAreaWTextComp: public LRClickableAreaWText
-{
-public:
-	int baseType;
-	int bonusValue, type;
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
-
-	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
-	CComponent * createComponent() const;
-};
-
-class MoraleLuckBox : public LRClickableAreaWTextComp
-{
-	CAnimImage *image;
-public:
-	bool morale; //true if morale, false if luck
-	bool small;
-
-	void set(const IBonusBearer *node);
-
-	MoraleLuckBox(bool Morale, const Rect &r, bool Small=false);
-};
-
-/// Opens hero window by left-clicking on it
-class CHeroArea: public CIntObject
-{
-	const CGHeroInstance * hero;
-public:
-
-	CHeroArea(int x, int y, const CGHeroInstance * _hero);
-
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void hover(bool on);
-};
-
-/// Opens town screen by left-clicking on it
-class LRClickableAreaOpenTown: public LRClickableAreaWTextComp
-{
-public:
-	const CGTownInstance * town;
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	LRClickableAreaOpenTown();
-};
-
-/// Artifacts can be placed there. Gets shown at the hero window
-class CArtPlace: public LRClickableAreaWTextComp
-{
-	CAnimImage *image;
-	CAnimImage *selection;
-
-	void createImage();
-
-public:
-	// consider these members as const - change them only with appropriate methods e.g. lockSlot()
-	bool locked;
-	bool picked;
-	bool marked;
-
-	ArtifactPosition slotID; //Arts::EPOS enum + backpack starting from Arts::BACKPACK_START
-
-	void lockSlot(bool on);
-	void pickSlot(bool on);
-	void selectSlot(bool on);
-
-	CArtifactsOfHero * ourOwner;
-	const CArtifactInstance * ourArt; // should be changed only with setArtifact()
-
-	CArtPlace(Point position, const CArtifactInstance * Art = nullptr); //c-tor
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void select ();
-	void deselect ();
-	void showAll(SDL_Surface * to);
-	bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here
-
-	void setMeAsDest(bool backpackAsVoid = true);
-	void setArtifact(const CArtifactInstance *art);
-};
-
-/// Contains artifacts of hero. Distincts which artifacts are worn or backpacked
-class CArtifactsOfHero : public CIntObject
-{
-	const CGHeroInstance * curHero;
-
-	std::vector<CArtPlace *> artWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
-	std::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
-	int backpackPos; //number of first art visible in backpack (in hero's vector)
-
-public:
-	struct SCommonPart
-	{
-		struct Artpos
-		{
-			ArtifactPosition slotID;
-			const CArtifactsOfHero *AOH;
-			const CArtifactInstance *art;
-
-			Artpos();
-			void clear();
-			void setTo(const CArtPlace *place, bool dontTakeBackpack);
-			bool valid();
-			bool operator==(const ArtifactLocation &al) const;
-		} src, dst;
-
-		std::set<CArtifactsOfHero *> participants; // Needed to mark slots.
-
-		void reset();
-	} * commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally
-
-	bool updateState; // Whether the commonInfo should be updated on setHero or not.
-
-	CAdventureMapButton * leftArtRoll, * rightArtRoll;
-	bool allowedAssembling;
-	std::multiset<const CArtifactInstance*> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be omitted in backpack slots
-	std::function<void(CArtPlace*)> highlightModeCallback; //if set, clicking on art place doesn't pick artifact but highlights the slot and calls this function
-
-	void realizeCurrentTransaction(); //calls callback with parameters stored in commonInfo
-	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
-	void artifactRemoved(const ArtifactLocation &al);
-	void artifactAssembled(const ArtifactLocation &al);
-	void artifactDisassembled(const ArtifactLocation &al);
-	CArtPlace *getArtPlace(int slot);
-
-	void setHero(const CGHeroInstance * hero);
-	const CGHeroInstance *getHero() const;
-	void dispose(); //free resources not needed after closing windows and reset state
-	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
-
-	void safeRedraw();
-	void markPossibleSlots(const CArtifactInstance* art);
-	void unmarkSlots(bool withRedraw = true); //unmarks slots in all visible AOHs
-	void unmarkLocalSlots(bool withRedraw = true); //unmarks slots in that particular AOH
-	void setSlotData (CArtPlace* artPlace, ArtifactPosition slotID);
-	void updateWornSlots (bool redrawParent = true);
-
-	void updateSlot(ArtifactPosition i);
-	void eraseSlotData (CArtPlace* artPlace, ArtifactPosition slotID);
-
-	CArtifactsOfHero(const Point& position, bool createCommonPart = false);
-	//Alternative constructor, used if custom artifacts positioning required (Kingdom interface)
-	CArtifactsOfHero(std::vector<CArtPlace *> ArtWorn, std::vector<CArtPlace *> Backpack,
-		CAdventureMapButton *leftScroll, CAdventureMapButton *rightScroll, bool createCommonPart = false);
-	~CArtifactsOfHero(); //d-tor
-	void updateParentWindow();
-	friend class CArtPlace;
-};
-
-class CGarrisonHolder
-{
-public:
-	CGarrisonHolder();
-	virtual void updateGarrisons()=0;
-};
-
-class CWindowWithGarrison : public virtual CGarrisonHolder
-{
-public:
-	CGarrisonInt *garr;
-	virtual void updateGarrisons();
-};
-
-/// Garrison window where you can take creatures out of the hero to place it on the garrison
-class CGarrisonWindow : public CWindowObject, public CWindowWithGarrison
-{
-public:
-	CAdventureMapButton * quit;
-
-	CGarrisonWindow(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits); //c-tor
-};
-
-class CExchangeWindow : public CWindowObject, public CWindowWithGarrison, public CWindowWithArtifacts
-{
-	CGStatusBar * ourBar; //internal statusbar
-
-	CAdventureMapButton * quit, * questlogButton[2];
-
-	std::vector<LRClickableAreaWTextComp *> secSkillAreas[2], primSkillAreas;
-
-	MoraleLuckBox *morale[2], *luck[2];
-
-	LRClickableAreaWText *specialty[2];
-	LRClickableAreaWText *experience[2];
-	LRClickableAreaWText *spellPoints[2];
-	CHeroArea *portrait[2];
-
-public:
-
-	const CGHeroInstance* heroInst[2];
-	CArtifactsOfHero * artifs[2];
-
-	void questlog(int whichHero); //questlog button callback; whichHero: 0 - left, 1 - right
-
-	void prepareBackground(); //prepares or redraws bg
-
-	CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID); //c-tor
-	~CExchangeWindow(); //d-tor
-};
-
-/// Here you can buy ships
-class CShipyardWindow : public CWindowObject
-{
-public:
-	CGStatusBar *bar;
-	CPicture *bgWater;
-
-	CLabel *title;
-	CLabel *costLabel;
-
-	CAnimImage *woodPic, *goldPic;
-	CLabel *woodCost, *goldCost;
-
-	CAnimImage *bgShip;
-	CAdventureMapButton *build, *quit;
-
-	CGStatusBar * statusBar;
-
-	CShipyardWindow(const std::vector<si32> &cost, int state, int boatType, const std::function<void()> &onBuy);
-};
-
-/// Puzzle screen which gets uncovered when you visit obilisks
-class CPuzzleWindow : public CWindowObject
-{
-private:
-	int3 grailPos;
-
-	CAdventureMapButton * quitb;
-
-	std::vector<CPicture * > piecesToRemove;
-	ui8 currentAlpha;
-
-public:
-	void showAll(SDL_Surface * to);
-	void show(SDL_Surface * to);
-
-	CPuzzleWindow(const int3 &grailPos, double discoveredRatio);
-};
-
-/// Creature transformer window
-class CTransformerWindow : public CWindowObject, public CGarrisonHolder
-{
-public:
-	class CItem : public CIntObject
-	{
-	public:
-		int id;//position of creature in hero army
-		bool left;//position of the item
-		int size; //size of creature stack
-		CTransformerWindow * parent;
-		CAnimImage *icon;
-
-		void move();
-		void clickLeft(tribool down, bool previousState);
-		void update();
-		CItem(CTransformerWindow * parent, int size, int id);
-	};
-
-	const CArmedInstance *army;//object with army for transforming (hero or town)
-	const CGHeroInstance *hero;//only if we have hero in town
-	const CGTownInstance *town;//market, town garrison is used if hero == nullptr
-	std::vector<CItem*> items;
-
-	CAdventureMapButton *all, *convert, *cancel;
-	CGStatusBar *bar;
-	void makeDeal();
-	void addAll();
-	void updateGarrisons();
-	CTransformerWindow(const CGHeroInstance * _hero, const CGTownInstance * _town); //c-tor
-};
-
-class CUniversityWindow : public CWindowObject
-{
-	class CItem : public CAnimImage
-	{
-	public:
-		int ID;//id of selected skill
-		CUniversityWindow * parent;
-
-		void showAll(SDL_Surface * to);
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void hover(bool on);
-		int state();//0=can't learn, 1=learned, 2=can learn
-		CItem(CUniversityWindow * _parent, int _ID, int X, int Y);
-	};
-
-public:
-	const CGHeroInstance *hero;
-	const IMarket * market;
-
-	CPicture * green, * yellow, * red;//colored bars near skills
-	std::vector<CItem*> items;
-
-	CAdventureMapButton *cancel;
-	CGStatusBar *bar;
-
-	CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market); //c-tor
-};
-
-/// Confirmation window for University
-class CUnivConfirmWindow : public CWindowObject
-{
-public:
-	CUniversityWindow * parent;
-	CGStatusBar *bar;
-	CAdventureMapButton *confirm, *cancel;
-
-	CUnivConfirmWindow(CUniversityWindow * PARENT, int SKILL, bool available); //c-tor
-	void makeDeal(int skill);
-};
-
-/// Hill fort is the building where you can upgrade units
-class CHillFortWindow : public CWindowObject, public CWindowWithGarrison
-{
-public:
-
-	int slotsCount;//=7;
-	CGStatusBar * bar;
-	CDefEssential *resources;
-	CHeroArea *heroPic;//clickable hero image
-	CAdventureMapButton *quit,//closes window
-	                   *upgradeAll,//upgrade all creatures
-	                   *upgrade[7];//upgrade single creature
-
-	const CGObjectInstance * fort;
-	const CGHeroInstance * hero;
-	std::vector<int> currState;//current state of slot - to avoid calls to getState or updating buttons
-	std::vector<TResources> costs;// costs [slot ID] [resource ID] = resource count for upgrade
-	TResources totalSumm; // totalSum[resource ID] = value
-
-	CHillFortWindow(const CGHeroInstance *visitor, const CGObjectInstance *object); //c-tor
-
-	void showAll (SDL_Surface *to);
-	std::string getDefForSlot(SlotID slot);//return def name for this slot
-	std::string getTextForSlot(SlotID slot);//return hover text for this slot
-	void makeDeal(SlotID slot);//-1 for upgrading all creatures
-	int getState(SlotID slot); //-1 = no creature 0=can't upgrade, 1=upgraded, 2=can upgrade
-	void updateGarrisons();//update buttons after garrison changes
-};
-
-class CThievesGuildWindow : public CWindowObject
-{
-	const CGObjectInstance * owner;
-
-	CGStatusBar * statusBar;
-	CAdventureMapButton * exitb;
-	CMinorResDataBar * resdatabar;
-
-public:
-	CThievesGuildWindow(const CGObjectInstance * _owner);
-};

+ 0 - 1
client/Graphics.cpp

@@ -21,7 +21,6 @@
 #include "../lib/vcmi_endian.h"
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
-#include "CAnimation.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 
 using namespace boost::assign;

+ 3 - 1
client/NetPacksClient.cpp

@@ -17,7 +17,7 @@
 #include "../lib/CSoundBase.h"
 #include "../lib/StartInfo.h"
 #include "mapHandler.h"
-#include "GUIClasses.h"
+#include "windows/GUIClasses.h"
 #include "../lib/CConfigHandler.h"
 #include "gui/SDL_Extensions.h"
 #include "battle/CBattleInterface.h"
@@ -26,6 +26,8 @@
 #include "../lib/BattleState.h"
 #include "../lib/GameConstants.h"
 #include "gui/CGuiHandler.h"
+#include "widgets/MiscWidgets.h"
+#include "widgets/AdventureMapClasses.h"
 #include "CMT.h"
 
 //macros to avoid code duplication - calls given method with given arguments if interface for specific player is present

+ 1 - 0
client/battle/CBattleAnimations.cpp

@@ -19,6 +19,7 @@
 #include "../../CCallback.h"
 #include "../../lib/BattleState.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 /*
  * CBattleAnimations.cpp, part of VCMI engine

+ 1 - 1
client/battle/CBattleAnimations.h

@@ -1,7 +1,7 @@
 #pragma once
 
-#include "../CAnimation.h"
 #include "../../lib/BattleHex.h"
+#include "../widgets/Images.h"
 
 class CBattleInterface;
 class CStack;

+ 37 - 39
client/battle/CBattleInterface.cpp

@@ -1,40 +1,38 @@
 #include "StdInc.h"
 #include "CBattleInterface.h"
 
-#include "../CGameInfo.h"
-#include "../gui/SDL_Extensions.h"
-#include "../CAdvmapInterface.h"
-#include "../CAnimation.h"
+#include "CBattleAnimations.h"
+#include "CBattleInterfaceClasses.h"
+#include "CCreatureAnimation.h"
+
 #include "../CBitmapHandler.h"
-#include "../../lib/CHeroHandler.h"
-# include "../CDefHandler.h"
-#include "../../lib/CSpellHandler.h"
-#include "../CMusicHandler.h"
+#include "../CDefHandler.h"
+#include "../CGameInfo.h"
 #include "../CMessage.h"
+#include "../CMT.h"
+#include "../CMusicHandler.h"
+#include "../CPlayerInterface.h"
+#include "../CVideoHandler.h"
+#include "../Graphics.h"
+#include "../gui/CCursorHandler.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/SDL_Extensions.h"
+#include "../windows/CAdvmapInterface.h"
+#include "../windows/CCreatureWindow.h"
+#include "../windows/CSpellWindow.h"
+
 #include "../../CCallback.h"
 #include "../../lib/BattleState.h"
-#include "../../lib/CGeneralTextHandler.h"
-#include "CCreatureAnimation.h"
-#include "../Graphics.h"
-#include "../CSpellWindow.h"
 #include "../../lib/CConfigHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CHeroHandler.h"
 #include "../../lib/CondSh.h"
-#include "../../lib/NetPacks.h"
-#include "../CPlayerInterface.h"
-#include "../CCreatureWindow.h"
-#include "../CVideoHandler.h"
+#include "../../lib/CRandomGenerator.h"
+#include "../../lib/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"
+#include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"
-#include "../../lib/CRandomGenerator.h"
-
-#include "CBattleAnimations.h"
-#include "CBattleInterfaceClasses.h"
-
-#include "../gui/CCursorHandler.h"
-#include "../gui/CGuiHandler.h"
-#include "../CMT.h"
-
-
+#include "../../lib/NetPacks.h"
 #include "../../lib/UnlockGuard.h"
 
 using namespace boost::assign;
@@ -224,17 +222,17 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 // 	blitAt(menu, pos.x, 556 + pos.y);
 
 	//preparing buttons and console
-	bOptions = new CAdventureMapButton (CGI->generaltexth->zelp[381].first, CGI->generaltexth->zelp[381].second, std::bind(&CBattleInterface::bOptionsf,this), 3, 561, "icm003.def", SDLK_o);
-	bSurrender = new CAdventureMapButton (CGI->generaltexth->zelp[379].first, CGI->generaltexth->zelp[379].second, std::bind(&CBattleInterface::bSurrenderf,this), 54, 561, "icm001.def", SDLK_s);
-	bFlee = new CAdventureMapButton (CGI->generaltexth->zelp[380].first, CGI->generaltexth->zelp[380].second, std::bind(&CBattleInterface::bFleef,this), 105, 561, "icm002.def", SDLK_r);
-	bAutofight  = new CAdventureMapButton (CGI->generaltexth->zelp[382].first, CGI->generaltexth->zelp[382].second, std::bind(&CBattleInterface::bAutofightf,this), 157, 561, "icm004.def", SDLK_a);
-	bSpell = new CAdventureMapButton (CGI->generaltexth->zelp[385].first, CGI->generaltexth->zelp[385].second, std::bind(&CBattleInterface::bSpellf,this), 645, 561, "icm005.def", SDLK_c);
-	bWait = new CAdventureMapButton (CGI->generaltexth->zelp[386].first, CGI->generaltexth->zelp[386].second, std::bind(&CBattleInterface::bWaitf,this), 696, 561, "icm006.def", SDLK_w);
-	bDefence = new CAdventureMapButton (CGI->generaltexth->zelp[387].first, CGI->generaltexth->zelp[387].second, std::bind(&CBattleInterface::bDefencef,this), 747, 561, "icm007.def", SDLK_d);
+	bOptions =     new CButton (Point(  3, 561), "icm003.def", CGI->generaltexth->zelp[381], boost::bind(&CBattleInterface::bOptionsf,this), SDLK_o);
+	bSurrender =   new CButton (Point( 54, 561), "icm001.def", CGI->generaltexth->zelp[379], boost::bind(&CBattleInterface::bSurrenderf,this), SDLK_s);
+	bFlee =        new CButton (Point(105, 561), "icm002.def", CGI->generaltexth->zelp[380], boost::bind(&CBattleInterface::bFleef,this), SDLK_r);
+	bAutofight  =  new CButton (Point(157, 561), "icm004.def", CGI->generaltexth->zelp[382], boost::bind(&CBattleInterface::bAutofightf,this), SDLK_a);
+	bSpell =       new CButton (Point(645, 561), "icm005.def", CGI->generaltexth->zelp[385], boost::bind(&CBattleInterface::bSpellf,this), SDLK_c);
+	bWait =        new CButton (Point(696, 561), "icm006.def", CGI->generaltexth->zelp[386], boost::bind(&CBattleInterface::bWaitf,this), SDLK_w);
+	bDefence =     new CButton (Point(747, 561), "icm007.def", CGI->generaltexth->zelp[387], boost::bind(&CBattleInterface::bDefencef,this), SDLK_d);
 	bDefence->assignedKeys.insert(SDLK_SPACE);
-	bConsoleUp = new CAdventureMapButton (std::string(), std::string(), std::bind(&CBattleInterface::bConsoleUpf,this), 624, 561, "ComSlide.def", SDLK_UP);
-	bConsoleDown = new CAdventureMapButton (std::string(), std::string(), std::bind(&CBattleInterface::bConsoleDownf,this), 624, 580, "ComSlide.def", SDLK_DOWN);
-	bConsoleDown->setOffset(2);
+	bConsoleUp =   new CButton (Point(624, 561), "ComSlide.def", std::make_pair("", ""), boost::bind(&CBattleInterface::bConsoleUpf,this), SDLK_UP);
+	bConsoleDown = new CButton (Point(624, 580), "ComSlide.def", std::make_pair("", ""), boost::bind(&CBattleInterface::bConsoleDownf,this), SDLK_DOWN);
+	bConsoleDown->setImageOrder(2, 3, 4, 5);
 	console = new CBattleConsole();
 	console->pos.x += 211;
 	console->pos.y += 560;
@@ -242,8 +240,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	console->pos.h = 38;
 	if(tacticsMode)
 	{
-		btactNext = new CAdventureMapButton(std::string(), std::string(), std::bind(&CBattleInterface::bTacticNextStack,this, (CStack*)nullptr), 213, 560, "icm011.def", SDLK_SPACE);
-		btactEnd = new CAdventureMapButton(std::string(), std::string(), std::bind(&CBattleInterface::bEndTacticPhase,this), 419, 560, "icm012.def", SDLK_RETURN);
+		btactNext = new CButton(Point(213, 560), "icm011.def", std::make_pair("", ""), [&]{ bTacticNextStack(nullptr);}, SDLK_SPACE);
+		btactEnd =  new CButton(Point(419, 560), "icm012.def", std::make_pair("", ""), [&]{ bEndTacticPhase();}, SDLK_RETURN);
 		menu = BitmapHandler::loadBitmap("COPLACBR.BMP");
 	}
 	else
@@ -2432,7 +2430,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 			{
 				cursorFrame = ECursor::COMBAT_QUERY;
 				consoleMsg = (boost::format(CGI->generaltexth->allTexts[297]) % shere->getName()).str();
-				realizeAction = [=]{ GH.pushInt(createCreWindow(shere, true)); };
+				realizeAction = [=]{ GH.pushInt(new CStackWindow(shere, false)); };
 				break;
 			}
 		}

+ 7 - 7
client/battle/CBattleInterface.h

@@ -1,10 +1,10 @@
 #pragma once
 
 
-#include "../../lib/CCreatureSet.h"
+//#include "../../lib/CCreatureSet.h"
 #include "../../lib/ConstTransitivePtr.h" //may be reundant
-#include "../CAnimation.h"
 #include "../../lib/GameConstants.h"
+
 #include "CBattleAnimations.h"
 
 /*
@@ -23,9 +23,9 @@ class CGHeroInstance;
 class CDefHandler;
 class CStack;
 class CCallback;
-class CAdventureMapButton;
-class CHighlightableButton;
-class CHighlightableButtonsGroup;
+class CButton;
+class CToggleButton;
+class CToggleGroup;
 struct BattleResult;
 struct BattleSpellCast;
 struct CObstacleInstance;
@@ -120,7 +120,7 @@ class CBattleInterface : public CIntObject
 	};
 private:
 	SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes;
-	CAdventureMapButton * bOptions, * bSurrender, * bFlee, * bAutofight, * bSpell,
+	CButton * bOptions, * bSurrender, * bFlee, * bAutofight, * bSpell,
 		* bWait, * bDefence, * bConsoleUp, * bConsoleDown, *btactNext, *btactEnd;
 	CBattleConsole * console;
 	CBattleHero * attackingHero, * defendingHero; //fighting heroes
@@ -338,7 +338,7 @@ public:
 	InfoAboutHero enemyHero() const;
 
 	friend class CPlayerInterface;
-	friend class CAdventureMapButton;
+	friend class CButton;
 	friend class CInGameConsole;
 	
 	friend class CBattleResultWindow;

+ 41 - 39
client/battle/CBattleInterfaceClasses.cpp

@@ -1,29 +1,34 @@
 #include "StdInc.h"
 #include "CBattleInterfaceClasses.h"
 
-#include "../gui/SDL_Extensions.h"
 #include "CBattleInterface.h"
-#include "../CGameInfo.h"
+
+#include "../CBitmapHandler.h"
 #include "../CDefHandler.h"
-#include "../gui/CCursorHandler.h"
+#include "../CGameInfo.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
 #include "../CPlayerInterface.h"
-#include "../../CCallback.h"
-#include "../CSpellWindow.h"
+#include "../CVideoHandler.h"
 #include "../Graphics.h"
-#include "../../lib/CConfigHandler.h"
+#include "../gui/CCursorHandler.h"
 #include "../gui/CGuiHandler.h"
-#include "../gui/CIntObjectClasses.h"
+#include "../gui/SDL_Extensions.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/TextControls.h"
+#include "../windows/CCreatureWindow.h"
+#include "../windows/CSpellWindow.h"
+
+#include "../../CCallback.h"
+#include "../../lib/BattleState.h"
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/CGameState.h"
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CTownHandler.h"
 #include "../../lib/NetPacks.h"
-#include "../../lib/CCreatureHandler.h"
-#include "../../lib/BattleState.h"
 #include "../../lib/StartInfo.h"
-#include "../CMusicHandler.h"
-#include "../CVideoHandler.h"
-#include "../../lib/CTownHandler.h"
-#include "../CBitmapHandler.h"
-#include "../CCreatureWindow.h"
-#include "../CMessage.h"
+#include "../../lib/CondSh.h"
 
 /*
  * CBattleInterfaceClasses.cpp, part of VCMI engine
@@ -258,26 +263,23 @@ CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInt
 	background = new CPicture("comopbck.bmp");
 	background->colorize(owner->getCurrentPlayerInterface()->playerID);
 
-	viewGrid = new CHighlightableButton(std::bind(&CBattleInterface::setPrintCellBorders, owner, true), std::bind(&CBattleInterface::setPrintCellBorders, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[427].first)(3,CGI->generaltexth->zelp[427].first), CGI->generaltexth->zelp[427].second, false, "sysopchk.def", nullptr, 25, 56, false);
-	viewGrid->select(settings["battle"]["cellBorders"].Bool());
-	movementShadow = new CHighlightableButton(std::bind(&CBattleInterface::setPrintStackRange, owner, true), std::bind(&CBattleInterface::setPrintStackRange, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[428].first)(3,CGI->generaltexth->zelp[428].first), CGI->generaltexth->zelp[428].second, false, "sysopchk.def", nullptr, 25, 89, false);
-	movementShadow->select(settings["battle"]["stackRange"].Bool());
-	mouseShadow = new CHighlightableButton(std::bind(&CBattleInterface::setPrintMouseShadow, owner, true), std::bind(&CBattleInterface::setPrintMouseShadow, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[429].first)(3,CGI->generaltexth->zelp[429].first), CGI->generaltexth->zelp[429].second, false, "sysopchk.def", nullptr, 25, 122, false);
-	mouseShadow->select(settings["battle"]["mouseShadow"].Bool());
-
-	animSpeeds = new CHighlightableButtonsGroup(0);
-	animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[422].first),CGI->generaltexth->zelp[422].second, "sysopb9.def", 28, 225, 40);
-	animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[423].first),CGI->generaltexth->zelp[423].second, "sysob10.def", 92, 225, 63);
-	animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[424].first),CGI->generaltexth->zelp[424].second, "sysob11.def",156, 225, 100);
-	animSpeeds->select(owner->getAnimSpeed(), 1);
-	animSpeeds->onChange = std::bind(&CBattleInterface::setAnimSpeed, owner, _1);
-
-	setToDefault = new CAdventureMapButton (CGI->generaltexth->zelp[393], std::bind(&CBattleOptionsWindow::bDefaultf,this), 246, 359, "codefaul.def");
-	setToDefault->swappedImages = true;
-	setToDefault->update();
-	exit = new CAdventureMapButton (CGI->generaltexth->zelp[392], std::bind(&CBattleOptionsWindow::bExitf,this), 357, 359, "soretrn.def",SDLK_RETURN);
-	exit->swappedImages = true;
-	exit->update();
+	viewGrid = new CToggleButton(Point(25, 56), "sysopchk.def", CGI->generaltexth->zelp[427], [&](bool on){owner->setPrintCellBorders(on);} );
+	viewGrid->setSelected(settings["battle"]["cellBorders"].Bool());
+	movementShadow = new CToggleButton(Point(25, 89), "sysopchk.def", CGI->generaltexth->zelp[428], [&](bool on){owner->setPrintStackRange(on);});
+	movementShadow->setSelected(settings["battle"]["stackRange"].Bool());
+	mouseShadow = new CToggleButton(Point(25, 122), "sysopchk.def", CGI->generaltexth->zelp[429], [&](bool on){owner->setPrintMouseShadow(on);});
+	mouseShadow->setSelected(settings["battle"]["mouseShadow"].Bool());
+
+	animSpeeds = new CToggleGroup([&](int value){ owner->setAnimSpeed(value);});
+	animSpeeds->addToggle(40,  new CToggleButton(Point( 28, 225), "sysopb9.def", CGI->generaltexth->zelp[422]));
+	animSpeeds->addToggle(63,  new CToggleButton(Point( 92, 225), "sysob10.def", CGI->generaltexth->zelp[423]));
+	animSpeeds->addToggle(100, new CToggleButton(Point(156, 225), "sysob11.def", CGI->generaltexth->zelp[424]));
+	animSpeeds->setSelected(owner->getAnimSpeed());
+
+	setToDefault = new CButton (Point(246, 359), "codefaul.def", CGI->generaltexth->zelp[393], [&]{ bDefaultf(); });
+	setToDefault->setImageOrder(1, 0, 2, 3);
+	exit = new CButton (Point(357, 359), "soretrn.def", CGI->generaltexth->zelp[392], [&]{ bExitf();}, SDLK_RETURN);
+	exit->setImageOrder(1, 0, 2, 3);
 
 	//creating labels
 	labels.push_back(new CLabel(242,  32, FONT_BIG,    CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[392]));//window title
@@ -307,6 +309,7 @@ CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInt
 
 void CBattleOptionsWindow::bDefaultf()
 {
+	//TODO: implement
 }
 
 void CBattleOptionsWindow::bExitf()
@@ -322,9 +325,8 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 	CPicture * bg = new CPicture("CPRESULT");
 	bg->colorize(owner.playerID);
 
-	exit = new CAdventureMapButton ("", "", std::bind(&CBattleResultWindow::bExitf,this), 384, 505, "iok6432.def", SDLK_RETURN);
+	exit = new CButton (Point(384, 505), "iok6432.def", std::make_pair("", ""), [&]{ bExitf();}, SDLK_RETURN);
 	exit->borderColor = Colors::METALLIC_GOLD;
-	exit->borderEnabled = true;
 
 	if(br.winner==0) //attacker won
 	{
@@ -606,7 +608,7 @@ void CClickableHex::clickRight(tribool down, bool previousState)
 		if(!myst->alive()) return;
 		if(down)
 		{
-			GH.pushInt(createCreWindow(myst));
+			GH.pushInt(new CStackWindow(myst, true));
 		}
 	}
 }
@@ -701,7 +703,7 @@ CStackQueue::StackBox::StackBox(bool small):
     small(small)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	bg = new CPicture(small ? "StackQueueBgSmall" : "StackQueueBgBig" );
+	bg = new CPicture(small ? "StackQueueSmall" : "StackQueueLarge" );
 
 	if (small)
 	{

+ 8 - 8
client/battle/CBattleInterfaceClasses.h

@@ -8,9 +8,9 @@ class CDefHandler;
 class CGHeroInstance;
 class CBattleInterface;
 class CPicture;
-class CAdventureMapButton;
-class CHighlightableButton;
-class CHighlightableButtonsGroup;
+class CButton;
+class CToggleButton;
+class CToggleGroup;
 class CLabel;
 struct BattleResult;
 class CStack;
@@ -72,9 +72,9 @@ class CBattleOptionsWindow : public CIntObject
 {
 private:
 	CPicture * background;
-	CAdventureMapButton * setToDefault, * exit;
-	CHighlightableButton * viewGrid, * movementShadow, * mouseShadow;
-	CHighlightableButtonsGroup * animSpeeds;
+	CButton * setToDefault, * exit;
+	CToggleButton * viewGrid, * movementShadow, * mouseShadow;
+	CToggleGroup * animSpeeds;
 
 	std::vector<CLabel*> labels;
 public:
@@ -88,7 +88,7 @@ public:
 class CBattleResultWindow : public CIntObject
 {
 private:
-	CAdventureMapButton *exit;
+	CButton *exit;
 	CPlayerInterface &owner;
 public:
 	CBattleResultWindow(const BattleResult & br, const SDL_Rect & pos, CPlayerInterface &_owner); //c-tor
@@ -152,4 +152,4 @@ public:
 	void update();
 	void showAll(SDL_Surface *to);
 	void blitBg(SDL_Surface * to);
-};
+};

+ 4 - 4
client/battle/CCreatureAnimation.cpp

@@ -1,16 +1,16 @@
 #include "StdInc.h"
 #include "CCreatureAnimation.h"
 
+#include "../../lib/vcmi_endian.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CCreatureHandler.h"
-#include "../../lib/vcmi_endian.h"
-#include "../gui/SDL_Extensions.h"
-#include "../gui/SDL_Pixels.h"
-
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/CBinaryReader.h"
 #include "../../lib/filesystem/CMemoryStream.h"
 
+#include "../gui/SDL_Extensions.h"
+#include "../gui/SDL_Pixels.h"
+
 /*
  * CCreatureAnimation.cpp, part of VCMI engine
  *

+ 2 - 1
client/battle/CCreatureAnimation.h

@@ -1,7 +1,8 @@
 #pragma once
 
 #include "../../lib/FunctionList.h"
-#include "../CAnimation.h"
+#include "../gui/SDL_Extensions.h"
+#include "../widgets/Images.h"
 
 /*
  * CCreatureAnimation.h, part of VCMI engine

+ 10 - 6
client/CAnimation.cpp → client/gui/CAnimation.cpp

@@ -1,17 +1,18 @@
 #include "StdInc.h"
+#include "CAnimation.h"
+
 #include <SDL_image.h>
 
+#include "../CBitmapHandler.h"
+#include "../Graphics.h"
+#include "../gui/SDL_Extensions.h"
+#include "../gui/SDL_Pixels.h"
+
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/ISimpleResourceLoader.h"
 #include "../lib/JsonNode.h"
 #include "../lib/CRandomGenerator.h"
 
-#include "CBitmapHandler.h"
-#include "Graphics.h"
-#include "CAnimation.h"
-#include "gui/SDL_Extensions.h"
-#include "gui/SDL_Pixels.h"
-
 /*
  * CAnimation.cpp, part of VCMI engine
  *
@@ -1221,6 +1222,7 @@ void CAnimation::getAnimInfo()
             logGlobal->errorStream()<<", "<<anim->images.begin()->second.size()<<" image loaded in group "<< anim->images.begin()->first;
 	}
 }
+<<<<<<< HEAD:client/CAnimation.cpp
 
 CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
 	frame(Frame),
@@ -1537,3 +1539,5 @@ void CCreatureAnim::clearAndSet(EAnimType type)
 		queue.pop();
 	set(type);
 }
+=======
+>>>>>>> refactoring/guiClasses:client/gui/CAnimation.cpp

+ 3 - 161
client/CAnimation.h → client/gui/CAnimation.h

@@ -1,7 +1,8 @@
 #pragma once
 
-#include "../lib/vcmi_endian.h"
-#include "gui/CIntObject.h"
+#include "../../lib/vcmi_endian.h"
+#include "gui/Geometries.h"
+#include "../../lib/GameConstants.h"
 
 /*
  * CAnimation.h, part of VCMI engine
@@ -219,162 +220,3 @@ public:
 	//total count of frames in group (including not loaded)
 	size_t size(size_t group=0) const;
 };
-
-
-/// Class for displaying one image from animation
-class CAnimImage: public CIntObject
-{
-private:
-	CAnimation* anim;
-	//displayed frame/group
-	size_t frame;
-	size_t group;
-	PlayerColor player;
-	ui8 flags;
-
-	void init();
-
-public:
-	CAnimImage(std::string name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
-	CAnimImage(CAnimation* anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
-	~CAnimImage();//d-tor
-
-	//size of animation
-	size_t size();
-
-	//change displayed frame on this one
-	void setFrame(size_t Frame, size_t Group=0);
-
-	//makes image player-colored
-	void playerColored(PlayerColor player);
-
-	void showAll(SDL_Surface * to);
-};
-
-/// Base class for displaying animation, used as superclass for different animations
-class CShowableAnim: public CIntObject
-{
-public:
-	enum EFlags
-	{
-		BASE=1,            //base frame will be blitted before current one
-		HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
-		VERTICAL_FLIP=4,   //TODO: will be displayed rotated
-		USE_RLE=8,         //RLE-d version, support full alpha-channel for 8-bit images
-		PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
-		PLAY_ONCE=32       //play animation only once and stop at last frame
-	};
-protected:
-	CAnimation anim;
-
-	size_t group, frame;//current frame
-
-	size_t first, last; //animation range
-
-	//TODO: replace with time delay(needed for battles)
-	ui32 frameDelay;//delay in frames of each image
-	ui32 value;//how many times current frame was showed
-
-	ui8 flags;//Flags from EFlags enum
-
-	//blit image with optional rotation, fitting into rect, etc
-	void blitImage(size_t frame, size_t group, SDL_Surface *to);
-
-	//For clipping in rect, offsets of picture coordinates
-	int xOffset, yOffset;
-
-	ui8 alpha;
-
-public:
-	//called when next animation sequence is required
-	std::function<void()> callback;
-
-	//Set per-surface alpha, 0 = transparent, 255 = opaque
-	void setAlpha(ui32 alphaValue);
-
-	CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0);
-	~CShowableAnim();
-
-	//set animation to group or part of group
-	bool set(size_t Group);
-	bool set(size_t Group, size_t from, size_t to=-1);
-
-	//set rotation flags
-	void rotate(bool on, bool vertical=false);
-
-	//move displayed part of picture (if picture is clipped to rect)
-	void clipRect(int posX, int posY, int width, int height);
-
-	//set frame to first, call callback
-	virtual void reset();
-
-	//show current frame and increase counter
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-};
-
-/// Creature-dependend animations like attacking, moving,...
-class CCreatureAnim: public CShowableAnim
-{
-public:
-
-	enum EHeroAnimType
-	{
-		HERO_HOLDING = 0,
-		HERO_IDLE = 1, // idling movement that happens from time to time
-		HERO_DEFEAT = 2, // played when army loses stack or on friendly fire
-		HERO_VICTORY = 3, // when enemy stack killed or huge damage is dealt
-		HERO_CAST_SPELL = 4 // spellcasting
-	};
-
-	enum EAnimType // list of creature animations, numbers were taken from def files
-	{
-		MOVING=0,
-		MOUSEON=1,
-		HOLDING=2,
-		HITTED=3,
-		DEFENCE=4,
-		DEATH=5,
-		//DEATH2=6, //unused?
-		TURN_L=7,
-		TURN_R=8, //same
-		//TURN_L2=9, //identical to previous?
-		//TURN_R2=10,
-		ATTACK_UP=11,
-		ATTACK_FRONT=12,
-		ATTACK_DOWN=13,
-		SHOOT_UP=14,
-		SHOOT_FRONT=15,
-		SHOOT_DOWN=16,
-		CAST_UP=17,
-		CAST_FRONT=18,
-		CAST_DOWN=19,
-		MOVE_START=20,
-		MOVE_END=21,
-		DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
-
-	};
-
-private:
-	//queue of animations waiting to be displayed
-	std::queue<EAnimType> queue;
-
-	//this function is used as callback if preview flag was set during construction
-	void loopPreview(bool warMachine);
-
-public:
-	//change anim to next if queue is not empty, call callback othervice
-	void reset();
-
-	//add sequence to the end of queue
-	void addLast(EAnimType newType);
-
-	void startPreview(bool warMachine);
-
-	//clear queue and set animation to this sequence
-	void clearAndSet(EAnimType type);
-
-	CCreatureAnim(int x, int y, std::string name, Rect picPos,
-	              ui8 flags= USE_RLE, EAnimType = HOLDING );
-
-};

+ 3 - 1
client/gui/CCursorHandler.cpp

@@ -2,9 +2,11 @@
 #include "CCursorHandler.h"
 
 #include <SDL.h>
+
 #include "SDL_Extensions.h"
-#include "../CAnimation.h"
 #include "CGuiHandler.h"
+#include "widgets/Images.h"
+
 #include "../CMT.h"
 
 /*

+ 3 - 1
client/gui/CGuiHandler.cpp

@@ -1,10 +1,12 @@
 #include "StdInc.h"
 #include "CGuiHandler.h"
 
+#include <SDL.h>
 
 #include "CIntObject.h"
-#include "../CGameInfo.h"
 #include "CCursorHandler.h"
+
+#include "../CGameInfo.h"
 #include "../../lib/CThreadHelper.h"
 #include "../../lib/CConfigHandler.h"
 #include "../CMT.h"

+ 1 - 1
client/gui/CGuiHandler.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../../lib/CStopWatch.h"
+//#include "../../lib/CStopWatch.h"
 #include "Geometries.h"
 #include "SDL_Extensions.h"
 

+ 19 - 0
client/gui/CIntObject.cpp

@@ -1,9 +1,15 @@
 #include "StdInc.h"
 #include "CIntObject.h"
+
 #include "CGuiHandler.h"
 #include "SDL_Extensions.h"
 #include "../CMessage.h"
 
+IShowActivatable::IShowActivatable()
+{
+	type = 0;
+}
+
 void ILockedUpdatable::runLocked(std::function<void(IUpdateable*)> cb)
 {
 	boost::unique_lock<boost::recursive_mutex> lock(updateGuard);	
@@ -317,6 +323,19 @@ bool CIntObject::captureThisEvent(const SDL_KeyboardEvent & key)
 	return captureAllKeys;
 }
 
+CKeyShortcut::CKeyShortcut()
+{}
+
+CKeyShortcut::CKeyShortcut(int key)
+{
+	if (key != SDLK_UNKNOWN)
+		assignedKeys.insert(key);
+}
+
+CKeyShortcut::CKeyShortcut(std::set<int> Keys)
+    :assignedKeys(Keys)
+{}
+
 void CKeyShortcut::keyPressed(const SDL_KeyboardEvent & key)
 {
 	if(vstd::contains(assignedKeys,key.keysym.sym)

+ 6 - 4
client/gui/CIntObject.h

@@ -10,7 +10,7 @@
  
 #pragma once
 
-#include <SDL_events.h>
+//#include <SDL_events.h>
 #include "Geometries.h"
 #include "../Graphics.h"
 
@@ -18,6 +18,8 @@ struct SDL_Surface;
 class CPicture;
 class CGuiHandler;
 
+struct SDL_KeyboardEvent;
+
 using boost::logic::tribool;
 
 // Defines a activate/deactive method
@@ -216,8 +218,8 @@ class CKeyShortcut : public virtual CIntObject
 {
 public:
 	std::set<int> assignedKeys;
-	CKeyShortcut(){}; //c-tor
-	CKeyShortcut(int key){assignedKeys.insert(key);}; //c-tor
-	CKeyShortcut(std::set<int> Keys):assignedKeys(Keys){}; //c-tor
+	CKeyShortcut();
+	CKeyShortcut(int key);
+	CKeyShortcut(std::set<int> Keys);
 	virtual void keyPressed(const SDL_KeyboardEvent & key); //call-in
 };

+ 0 - 2011
client/gui/CIntObjectClasses.cpp

@@ -1,2011 +0,0 @@
-#include "StdInc.h"
-#include "CIntObjectClasses.h"
-
-#include "../CBitmapHandler.h"
-#include "SDL_Pixels.h"
-#include "SDL_Extensions.h"
-#include "../Graphics.h"
-#include "../CAnimation.h"
-#include "CCursorHandler.h"
-#include "../CGameInfo.h"
-#include "../../CCallback.h"
-#include "../../lib/CConfigHandler.h"
-#include "../battle/CBattleInterface.h"
-#include "../battle/CBattleInterfaceClasses.h"
-#include "../CPlayerInterface.h"
-#include "../CMessage.h"
-#include "../CMusicHandler.h"
-#include "../GUIClasses.h"
-#include "CGuiHandler.h"
-#include "../CAdvmapInterface.h"
-#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
-
-CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
-{
-	init();
-	bg = BG;
-	freeSurf = Free;
-	pos.x += x;
-	pos.y += y;
-	pos.w = BG->w;
-	pos.h = BG->h;
-}
-
-CPicture::CPicture( const std::string &bmpname, int x, int y )
-{
-	init();
-	bg = BitmapHandler::loadBitmap(bmpname);
-	freeSurf = true;;
-	pos.x += x;
-	pos.y += y;
-	if(bg)
-	{
-		pos.w = bg->w;
-		pos.h = bg->h;
-	}
-	else
-	{
-		pos.w = pos.h = 0;
-	}
-}
-
-CPicture::CPicture(const Rect &r, const SDL_Color &color, bool screenFormat /*= false*/)
-{
-	init();
-	createSimpleRect(r, screenFormat, SDL_MapRGB(bg->format, color.r, color.g,color.b));
-}
-
-CPicture::CPicture(const Rect &r, ui32 color, bool screenFormat /*= false*/)
-{
-	init();
-	createSimpleRect(r, screenFormat, color);
-}
-
-CPicture::CPicture(SDL_Surface *BG, const Rect &SrcRect, int x /*= 0*/, int y /*= 0*/, bool free /*= false*/)
-{
-	needRefresh = false;
-	srcRect = new Rect(SrcRect);
-	pos.x += x;
-	pos.y += y;
-	pos.w = srcRect->w;
-	pos.h = srcRect->h;
-	bg = BG;
-	freeSurf = free;
-}
-
-void CPicture::setSurface(SDL_Surface *to)
-{
-	bg = to;
-	if (srcRect)
-	{
-		pos.w = srcRect->w;
-		pos.h = srcRect->h;
-	}
-	else
-	{
-		pos.w = bg->w;
-		pos.h = bg->h;
-	}
-}
-
-CPicture::~CPicture()
-{
-	if(freeSurf)
-		SDL_FreeSurface(bg);
-	delete srcRect;
-}
-
-void CPicture::init()
-{
-	needRefresh = false;
-	srcRect = nullptr;
-}
-
-void CPicture::show(SDL_Surface * to)
-{
-	if (needRefresh)
-		showAll(to);
-}
-
-void CPicture::showAll(SDL_Surface * to)
-{
-	if(bg)
-	{
-		if(srcRect)
-		{
-			SDL_Rect srcRectCpy = *srcRect;
-			SDL_Rect dstRect = srcRectCpy;
-			dstRect.x = pos.x;
-			dstRect.y = pos.y;
-
-			CSDL_Ext::blitSurface(bg, &srcRectCpy, to, &dstRect);
-		}
-		else
-			blitAt(bg, pos, to);
-	}
-}
-
-void CPicture::convertToScreenBPP()
-{
-	SDL_Surface *hlp = bg;
-	bg = SDL_ConvertSurface(hlp,screen->format,0);
-	CSDL_Ext::setDefaultColorKey(bg);	
-	SDL_FreeSurface(hlp);
-}
-
-void CPicture::setAlpha(int value)
-{	
-	#ifdef VCMI_SDL1
-	SDL_SetAlpha(bg, SDL_SRCALPHA, value);	
-	#else
-	SDL_SetSurfaceAlphaMod(bg,value);
-	#endif // 0
-}
-
-void CPicture::scaleTo(Point size)
-{
-	SDL_Surface * scaled = CSDL_Ext::scaleSurface(bg, size.x, size.y);
-
-	if(freeSurf)
-		SDL_FreeSurface(bg);
-
-	setSurface(scaled);
-	freeSurf = false;
-}
-
-void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color)
-{
-	pos += r;
-	pos.w = r.w;
-	pos.h = r.h;
-	if(screenFormat)
-		bg = CSDL_Ext::newSurface(r.w, r.h);
-	else
-		bg = SDL_CreateRGBSurface(SDL_SWSURFACE, r.w, r.h, 8, 0, 0, 0, 0);
-
-	SDL_FillRect(bg, nullptr, color);
-	freeSurf = true;
-}
-
-void CPicture::colorizeAndConvert(PlayerColor player)
-{
-	assert(bg);
-	colorize(player);
-	convertToScreenBPP();
-}
-
-void CPicture::colorize(PlayerColor player)
-{
-	assert(bg);
-	graphics->blueToPlayersAdv(bg, player);
-}
-
-CFilledTexture::CFilledTexture(std::string imageName, Rect position):
-    CIntObject(0, position.topLeft()),
-    texture(BitmapHandler::loadBitmap(imageName))
-{
-	pos.w = position.w;
-	pos.h = position.h;
-}
-
-CFilledTexture::~CFilledTexture()
-{
-	SDL_FreeSurface(texture);
-}
-
-void CFilledTexture::showAll(SDL_Surface *to)
-{
-	CSDL_Ext::CClipRectGuard guard(to, pos);
-	CSDL_Ext::fillTexture(to, texture);
-}
-
-CButtonBase::CButtonBase()
-{
-	swappedImages = keepFrame = false;
-	bitmapOffset = 0;
-	state=NORMAL;
-	image = nullptr;
-	text = nullptr;
-}
-
-CButtonBase::~CButtonBase()
-{
-
-}
-
-void CButtonBase::update()
-{
-	if (text)
-	{
-		if (state == PRESSED)
-			text->moveTo(Point(pos.x+pos.w/2+1, pos.y+pos.h/2+1));
-		else
-			text->moveTo(Point(pos.x+pos.w/2, pos.y+pos.h/2));
-	}
-
-	int newPos = (int)state + bitmapOffset;
-	if (newPos < 0)
-		newPos = 0;
-
-	if (state == HIGHLIGHTED && image->size() < 4)
-		newPos = image->size()-1;
-
-	if (swappedImages)
-	{
-		if (newPos == 0) newPos = 1;
-		else if (newPos == 1) newPos = 0;
-	}
-
-	if (!keepFrame)
-		image->setFrame(newPos);
-
-	if (active)
-		redraw();
-}
-
-void CButtonBase::addTextOverlay( const std::string &Text, EFonts font, SDL_Color color)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	delete text;
-	text = new CLabel(pos.w/2, pos.h/2, font, CENTER, color, Text);
-	update();
-}
-
-void CButtonBase::setOffset(int newOffset)
-{
-	if (bitmapOffset == newOffset)
-		return;
-	bitmapOffset = newOffset;
-	update();
-}
-
-void CButtonBase::setState(ButtonState newState)
-{
-	if (state == newState)
-		return;
-	state = newState;
-	update();
-}
-
-CButtonBase::ButtonState CButtonBase::getState()
-{
-	return state;
-}
-
-bool CButtonBase::isBlocked()
-{
-	return state == BLOCKED;
-}
-
-bool CButtonBase::isHighlighted()
-{
-	return state == HIGHLIGHTED;
-}
-
-void CButtonBase::block(bool on)
-{
-	setState(on?BLOCKED:NORMAL);
-}
-
-CAdventureMapButton::CAdventureMapButton ()
-{
-	hoverable = actOnDown = borderEnabled = soundDisabled = false;
-	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y,  const std::string &defName,int key, std::vector<std::string> * add, bool playerColoredButton )
-{
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(Callback, pom, HelpBox, playerColoredButton, defName, add, x, y, key);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key/*=0*/ )
-{
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(Callback, pom, HelpBox, info->playerColoured, info->defName, &info->additionalDefs, info->x, info->y, key);
-}
-
-CAdventureMapButton::CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-{
-	std::map<int,std::string> pom;
-	pom[0] = help.first;
-	init(Callback, pom, help.second, playerColoredButton, defName, add, x, y, key);
-}
-
-void CAdventureMapButton::onButtonClicked()
-{
-	// debug logging to figure out pressed button (and as result - player actions) in case of crash
-	logAnim->traceStream() << "Button clicked at " << pos.x << "x" << pos.y;
-	CIntObject * parent = this->parent;
-	std::string prefix = "Parent is";
-	while (parent)
-	{
-		logAnim->traceStream() << prefix << typeid(*parent).name() << " at " << parent->pos.x << "x" << parent->pos.y;
-		parent = parent->parent;
-		prefix = '\t' + prefix;
-	}
-	callback();
-}
-
-void CAdventureMapButton::clickLeft(tribool down, bool previousState)
-{
-	if(isBlocked())
-		return;
-
-	if (down)
-	{
-		if (!soundDisabled)
-			CCS->soundh->playSound(soundBase::button);
-		setState(PRESSED);
-	}
-	else if(hoverable && hovered)
-		setState(HIGHLIGHTED);
-	else
-		setState(NORMAL);
-
-	if (actOnDown && down)
-	{
-		onButtonClicked();
-	}
-	else if (!actOnDown && previousState && (down==false))
-	{
-		onButtonClicked();
-	}
-}
-
-void CAdventureMapButton::clickRight(tribool down, bool previousState)
-{
-	if(down && helpBox.size()) //there is no point to show window with nothing inside...
-		CRClickPopup::createAndPush(helpBox);
-}
-
-void CAdventureMapButton::hover (bool on)
-{
-	if(hoverable)
-	{
-		if(on)
-			setState(HIGHLIGHTED);
-		else
-			setState(NORMAL);
-	}
-
-	if(pressedL && on)
-		setState(PRESSED);
-
-	std::string *name = (vstd::contains(hoverTexts,getState()))
-		? (&hoverTexts[getState()])
-		: (vstd::contains(hoverTexts,0) ? (&hoverTexts[0]) : nullptr);
-	if(name && name->size() && !isBlocked()) //if there is no name, there is nohing to display also
-	{
-		if (LOCPLINT && LOCPLINT->battleInt) //for battle buttons
-		{
-			if(on && LOCPLINT->battleInt->console->alterTxt == "")
-			{
-				LOCPLINT->battleInt->console->alterTxt = *name;
-				LOCPLINT->battleInt->console->whoSetAlter = 1;
-			}
-			else if (LOCPLINT->battleInt->console->alterTxt == *name)
-			{
-				LOCPLINT->battleInt->console->alterTxt = "";
-				LOCPLINT->battleInt->console->whoSetAlter = 0;
-			}
-		}
-		else if(GH.statusbar) //for other buttons
-		{
-			if (on)
-				GH.statusbar->setText(*name);
-			else if ( GH.statusbar->getText()==(*name) )
-				GH.statusbar->clear();
-		}
-	}
-}
-
-void CAdventureMapButton::init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
-{
-	currentImage = -1;
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
-	callback = Callback;
-	hoverable = actOnDown = borderEnabled = soundDisabled = false;
-	CSDL_Ext::colorSetAlpha(borderColor,1);// represents a transparent color, used for HighlightableButton
-	hoverTexts = Name;
-	helpBox=HelpBox;
-
-	if (key != SDLK_UNKNOWN)
-		assignedKeys.insert(key);
-
-	pos.x += x;
-	pos.y += y;
-
-	if (!defName.empty())
-		imageNames.push_back(defName);
-	if (add)
-		for (auto & elem : *add)
-			imageNames.push_back(elem);
-	setIndex(0, playerColoredButton);
-}
-
-void CAdventureMapButton::setIndex(size_t index, bool playerColoredButton)
-{
-	if (index == currentImage || index>=imageNames.size())
-		return;
-	currentImage = index;
-	setImage(new CAnimation(imageNames[index]), playerColoredButton);
-}
-
-void CAdventureMapButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	delete image;
-	image = new CAnimImage(anim, getState(), 0, 0, 0, animFlags);
-	if (playerColoredButton)
-		image->playerColored(LOCPLINT->playerID);
-
-	pos.w = image->pos.w;
-	pos.h = image->pos.h;
-}
-
-void CAdventureMapButton::setPlayerColor(PlayerColor player)
-{
-	if (image)
-		image->playerColored(player);
-}
-
-void CAdventureMapButton::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-	
-	#ifdef VCMI_SDL1
-	if (borderEnabled && borderColor.unused == 0)
-		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
-	#else
-	if (borderEnabled && borderColor.a == 0)
-		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor.r, borderColor.g, borderColor.b));	
-	#endif // 0
-}
-
-void CHighlightableButton::select(bool on)
-{
-	selected = on;
-	if (on)
-	{
-		borderEnabled = true;
-		setState(HIGHLIGHTED);
-		callback();
-	}
-	else
-	{
-		borderEnabled = false;
-		setState(NORMAL);
-		callback2();
-	}
-
-	if(hoverTexts.size()>1)
-	{
-		hover(false);
-		hover(true);
-	}
-}
-
-void CHighlightableButton::clickLeft(tribool down, bool previousState)
-{
-	if(isBlocked())
-		return;
-
-	if (down && !(onlyOn && isHighlighted()))
-	{
-		CCS->soundh->playSound(soundBase::button);
-		setState(PRESSED);
-	}
-
-	if(previousState)//mouse up
-	{
-		if(down == false && getState() == PRESSED)
-			select(!selected);
-		else
-			setState(selected?HIGHLIGHTED:NORMAL);
-	}
-}
-
-CHighlightableButton::CHighlightableButton( const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key)
-: onlyOn(false), selected(false), callback2(onDeselect)
-{
-	init(onSelect,Name,HelpBox,playerColoredButton,defName,add,x,y,key);
-}
-
-CHighlightableButton::CHighlightableButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-: onlyOn(false), selected(false) // TODO: callback2(???)
-{
-	ID = myid;
-	std::map<int,std::string> pom;
-	pom[0] = help.first;
-	init(onSelect, pom, help.second, playerColoredButton, defName, add, x, y, key);
-}
-
-CHighlightableButton::CHighlightableButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key/*=0*/, std::vector<std::string> * add /*= nullptr*/, bool playerColoredButton /*= false */ )
-: onlyOn(false), selected(false) // TODO: callback2(???)
-{
-	ID = myid;
-	std::map<int,std::string> pom;
-	pom[0] = Name;
-	init(onSelect, pom,HelpBox, playerColoredButton, defName, add, x, y, key);
-}
-
-void CHighlightableButtonsGroup::addButton(CHighlightableButton* bt)
-{
-	if (bt->parent)
-		bt->parent->removeChild(bt);
-	addChild(bt);
-	bt->recActions = defActions;//FIXME: not needed?
-
-    bt->callback += std::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
-	bt->onlyOn = true;
-	buttons.push_back(bt);
-}
-
-void CHighlightableButtonsGroup::addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect, int key)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CHighlightableButton *bt = new CHighlightableButton(OnSelect, 0, tooltip, HelpBox, false, defName, nullptr, x, y, key);
-	if(musicLike)
-	{
-		bt->setOffset(buttons.size()-3);
-	}
-	bt->ID = uid;
-    bt->callback += std::bind(&CHighlightableButtonsGroup::selectionChanged,this,bt->ID);
-	bt->onlyOn = true;
-	buttons.push_back(bt);
-}
-
-CHighlightableButtonsGroup::CHighlightableButtonsGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
-: onChange(OnChange), musicLike(musicLikeButtons)
-{}
-
-CHighlightableButtonsGroup::~CHighlightableButtonsGroup()
-{
-
-}
-
-void CHighlightableButtonsGroup::select(int id, bool mode)
-{
-	assert(!buttons.empty());
-
-	CHighlightableButton *bt = buttons.front();
-	if(mode)
-	{
-		for(auto btn : buttons)
-			if (btn->ID == id)
-				bt = btn;
-	}
-	else
-	{
-		bt = buttons[id];
-	}
-	bt->select(true);
-	selectionChanged(bt->ID);
-}
-
-void CHighlightableButtonsGroup::selectionChanged(int to)
-{
-	for(auto & elem : buttons)
-		if(elem->ID!=to && elem->isHighlighted())
-			elem->select(false);
-	onChange(to);
-	if (parent)
-		parent->redraw();
-}
-
-void CHighlightableButtonsGroup::show(SDL_Surface * to)
-{
-	if (musicLike)
-	{
-		for(auto & elem : buttons)
-			if(elem->isHighlighted())
-				elem->show(to);
-	}
-	else
-		CIntObject::show(to);
-}
-
-void CHighlightableButtonsGroup::showAll(SDL_Surface * to)
-{
-	if (musicLike)
-	{
-		for(auto & elem : buttons)
-			if(elem->isHighlighted())
-				elem->showAll(to);
-	}
-	else
-		CIntObject::showAll(to);
-}
-
-void CHighlightableButtonsGroup::block( ui8 on )
-{
-	for(auto & elem : buttons)
-	{
-		elem->block(on);
-	}
-}
-
-void CSlider::sliderClicked()
-{
-	if(!(active & MOVE))
-		addUsedEvents(MOVE);
-}
-
-void CSlider::mouseMoved (const SDL_MouseMotionEvent & sEvent)
-{
-	double v = 0;
-	if(horizontal)
-	{
-		if(	std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2  )
-			return;
-		v = sEvent.x - pos.x - 24;
-		v *= positions;
-		v /= (pos.w - 48);
-	}
-	else
-	{
-		if(std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2  )
-			return;
-		v = sEvent.y - pos.y - 24;
-		v *= positions;
-		v /= (pos.h - 48);
-	}
-	v += 0.5;
-	if(v!=value)
-	{
-		moveTo(v);
-		redrawSlider();
-	}
-}
-
-void CSlider::redrawSlider()
-{
-	//slider->show(screenBuf);
-}
-
-void CSlider::moveLeft()
-{
-	moveTo(value-1);
-}
-
-void CSlider::moveRight()
-{
-	moveTo(value+1);
-}
-
-void CSlider::moveTo(int to)
-{
-	vstd::amax(to, 0);
-	vstd::amin(to, positions);
-
-	//same, old position?
-	if(value == to)
-		return;
-
-	value = to;
-	if(horizontal)
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(to) / positions;
-			part*=(pos.w-48);
-			int newPos = part + pos.x + 16 - slider->pos.x;
-			slider->moveBy(Point(newPos, 0));
-		}
-		else
-			slider->moveTo(Point(pos.x+16, pos.y));
-	}
-	else
-	{
-		if(positions)
-		{
-			double part = static_cast<double>(to) / positions;
-			part*=(pos.h-48);
-			int newPos = part + pos.y + 16 - slider->pos.y;
-			slider->moveBy(Point(0, newPos));
-		}
-		else
-			slider->moveTo(Point(pos.x, pos.y+16));
-	}
-
-	if(moved)
-		moved(to);
-}
-
-void CSlider::clickLeft(tribool down, bool previousState)
-{
-	if(down && !slider->isBlocked())
-	{
-		double pw = 0;
-		double rw = 0;
-		if(horizontal)
-		{
-			pw = GH.current->motion.x-pos.x-25;
-			rw = pw / static_cast<double>(pos.w - 48);
-		}
-		else
-		{
-			pw = GH.current->motion.y-pos.y-24;
-			rw = pw / (pos.h-48);
-		}
-		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
-			return;
-		// 		if (rw>1) return;
-		// 		if (rw<0) return;
-		slider->clickLeft(true, slider->pressedL);
-		moveTo(rw * positions  +  0.5);
-		return;
-	}
-	if(active & MOVE)
-		removeUsedEvents(MOVE);
-}
-
-CSlider::~CSlider()
-{
-
-}
-
-CSlider::CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, int style):
-    capacity(Capacity),
-    amount(Amount),
-    scrollStep(1),
-    horizontal(Horizontal),
-    moved(Moved)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	setAmount(amount);
-
-	addUsedEvents(LCLICK | KEYBOARD | WHEEL);
-	strongInterest = true;
-
-
-	left = new CAdventureMapButton();
-	right = new CAdventureMapButton();
-	slider = new CAdventureMapButton();
-
-	pos.x += x;
-	pos.y += y;
-
-	if(horizontal)
-	{
-		left->pos.y = slider->pos.y = right->pos.y = pos.y;
-		left->pos.x = pos.x;
-		right->pos.x = pos.x + totalw - 16;
-	}
-	else
-	{
-		left->pos.x = slider->pos.x = right->pos.x = pos.x;
-		left->pos.y = pos.y;
-		right->pos.y = pos.y + totalw - 16;
-	}
-
-    left->callback = std::bind(&CSlider::moveLeft,this);
-    right->callback = std::bind(&CSlider::moveRight,this);
-    slider->callback = std::bind(&CSlider::sliderClicked,this);
-	left->pos.w = left->pos.h = right->pos.w = right->pos.h = slider->pos.w = slider->pos.h = 16;
-	if(horizontal)
-	{
-		pos.h = 16;
-		pos.w = totalw;
-	}
-	else
-	{
-		pos.w = 16;
-		pos.h = totalw;
-	}
-
-	if(style == 0)
-	{
-		std::string name = horizontal?"IGPCRDIV.DEF":"OVBUTN2.DEF";
-		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
-
-		//use source def to create custom animations. Format "name.def:123" will load this frame from def file
-		auto animLeft = new CAnimation();
-		animLeft->setCustom(name + ":0", 0);
-		animLeft->setCustom(name + ":1", 1);
-		left->setImage(animLeft);
-
-		auto animRight = new CAnimation();
-		animRight->setCustom(name + ":2", 0);
-		animRight->setCustom(name + ":3", 1);
-		right->setImage(animRight);
-
-		auto animSlider = new CAnimation();
-		animSlider->setCustom(name + ":4", 0);
-		slider->setImage(animSlider);
-	}
-	else
-	{
-		left->setImage(new CAnimation(horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF"));
-		right->setImage(new CAnimation(horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF"));
-		slider->setImage(new CAnimation("SCNRBSL.DEF"));
-	}
-	slider->actOnDown = true;
-	slider->soundDisabled = true;
-	left->soundDisabled = true;
-	right->soundDisabled = true;
-
-	value = -1;
-	moveTo(Value);
-}
-
-void CSlider::block( bool on )
-{
-	left->block(on);
-	right->block(on);
-	slider->block(on);
-}
-
-void CSlider::setAmount( int to )
-{
-	amount = to;
-	positions = to - capacity;
-	vstd::amax(positions, 0);
-}
-
-void CSlider::showAll(SDL_Surface * to)
-{
-	CSDL_Ext::fillRectBlack(to, &pos);
-	CIntObject::showAll(to);
-}
-
-void CSlider::wheelScrolled(bool down, bool in)
-{
-	moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
-}
-
-void CSlider::keyPressed(const SDL_KeyboardEvent & key)
-{
-	if(key.state != SDL_PRESSED) return;
-
-	int moveDest = 0;
-	switch(key.keysym.sym)
-	{
-	case SDLK_UP:
-	case SDLK_LEFT:
-		moveDest = value - scrollStep;
-		break;
-	case SDLK_DOWN:
-	case SDLK_RIGHT:
-		moveDest = value + scrollStep;
-		break;
-	case SDLK_PAGEUP:
-		moveDest = value - capacity + scrollStep;
-		break;
-	case SDLK_PAGEDOWN:
-		moveDest = value + capacity - scrollStep;
-		break;
-	case SDLK_HOME:
-		moveDest = 0;
-		break;
-	case SDLK_END:
-		moveDest = amount - capacity;
-		break;
-	default:
-		return;
-	}
-
-	moveTo(moveDest);
-}
-
-void CSlider::moveToMax()
-{
-	moveTo(amount);
-}
-
-static void intDeleter(CIntObject* object)
-{
-	delete object;
-}
-
-CObjectList::CObjectList(CreateFunc create, DestroyFunc destroy):
-createObject(create),
-destroyObject(destroy)
-{
-	if (!destroyObject)
-		destroyObject = intDeleter;
-}
-
-void CObjectList::deleteItem(CIntObject* item)
-{
-	if (!item)
-		return;
-	removeChild(item);
-	destroyObject(item);
-}
-
-CIntObject* CObjectList::createItem(size_t index)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	CIntObject * item = createObject(index);
-	if (item == nullptr)
-		item = new CIntObject();
-
-	item->recActions = defActions;
-
-	addChild(item);
-	return item;
-}
-
-CTabbedInt::CTabbedInt(CreateFunc create, DestroyFunc destroy, Point position, size_t ActiveID):
-CObjectList(create, destroy),
-activeTab(nullptr),
-activeID(ActiveID)
-{
-	pos += position;
-	reset();
-}
-
-void CTabbedInt::setActive(size_t which)
-{
-	if (which != activeID)
-	{
-		activeID = which;
-		reset();
-	}
-}
-
-void CTabbedInt::reset()
-{
-	deleteItem(activeTab);
-	activeTab = createItem(activeID);
-	activeTab->moveTo(pos.topLeft());
-
-	if (active)
-		redraw();
-}
-
-CIntObject * CTabbedInt::getItem()
-{
-	return activeTab;
-}
-
-CListBox::CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
-				   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
-	CObjectList(create, destroy),
-	first(InitialPos),
-	totalSize(TotalSize),
-	itemOffset(ItemOffset),
-    slider(nullptr)
-{
-	pos += Pos;
-	items.resize(VisibleSize, nullptr);
-
-	if (Slider & 1)
-	{
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-        slider = new CSlider(SliderPos.x, SliderPos.y, SliderPos.w, std::bind(&CListBox::moveToPos, this, _1),
-			VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4);
-	}
-	reset();
-}
-
-// Used to move active items after changing list position
-void CListBox::updatePositions()
-{
-	Point itemPos = pos.topLeft();
-	for (auto & elem : items)
-	{
-		(elem)->moveTo(itemPos);
-		itemPos += itemOffset;
-	}
-	if (active)
-	{
-		redraw();
-		if (slider)
-			slider->moveTo(first);
-	}
-}
-
-void CListBox::reset()
-{
-	size_t current = first;
-	for (auto & elem : items)
-	{
-		deleteItem(elem);
-		elem = createItem(current++);
-	}
-	updatePositions();
-}
-
-void CListBox::resize(size_t newSize)
-{
-	totalSize = newSize;
-	if (slider)
-		slider->setAmount(totalSize);
-	reset();
-}
-
-size_t CListBox::size()
-{
-	return totalSize;
-}
-
-CIntObject * CListBox::getItem(size_t which)
-{
-	if (which < first || which > first + items.size() || which > totalSize)
-		return nullptr;
-
-	size_t i=first;
-	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
-		if( i == which)
-			return *iter;
-	return nullptr;
-}
-
-size_t CListBox::getIndexOf(CIntObject *item)
-{
-	size_t i=first;
-	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
-		if(*iter == item)
-			return i;
-	return size_t(-1);
-}
-
-void CListBox::scrollTo(size_t which)
-{
-	//scroll up
-	if (first > which)
-		moveToPos(which);
-	//scroll down
-	else if (first + items.size() <= which && which < totalSize)
-		moveToPos(which - items.size() + 1);
-}
-
-void CListBox::moveToPos(size_t which)
-{
-	//Calculate new position
-	size_t maxPossible;
-	if (totalSize > items.size())
-		maxPossible = totalSize - items.size();
-	else
-		maxPossible = 0;
-
-	size_t newPos = std::min(which, maxPossible);
-
-	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
-	if (first - newPos == 1)
-		moveToPrev();
-	else if (newPos - first == 1)
-		moveToNext();
-	else if (newPos != first)
-	{
-		first = newPos;
-		reset();
-	}
-}
-
-void CListBox::moveToNext()
-{
-	//Remove front item and insert new one to end
-	if (first + items.size() < totalSize)
-	{
-		first++;
-		deleteItem(items.front());
-		items.pop_front();
-		items.push_back(createItem(first+items.size()));
-		updatePositions();
-	}
-}
-
-void CListBox::moveToPrev()
-{
-	//Remove last item and insert new one at start
-	if (first)
-	{
-		first--;
-		deleteItem(items.back());
-		items.pop_back();
-		items.push_front(createItem(first));
-		updatePositions();
-	}
-}
-
-size_t CListBox::getPos()
-{
-	return first;
-}
-
-const std::list<CIntObject *> &CListBox::getItems()
-{
-	return items;
-}
-
-void CSimpleWindow::show(SDL_Surface * to)
-{
-	if(bitmap)
-		blitAt(bitmap,pos.x,pos.y,to);
-}
-CSimpleWindow::~CSimpleWindow()
-{
-	if (bitmap)
-	{
-		SDL_FreeSurface(bitmap);
-		bitmap=nullptr;
-	}
-}
-
-void CHoverableArea::hover (bool on)
-{
-	if (on)
-		GH.statusbar->setText(hoverText);
-	else if (GH.statusbar->getText()==hoverText)
-		GH.statusbar->clear();
-}
-
-CHoverableArea::CHoverableArea()
-{
-	addUsedEvents(HOVER);
-}
-
-CHoverableArea::~CHoverableArea()
-{
-}
-
-void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
-{
-	if(!down && previousState)
-	{
-		LOCPLINT->showInfoDialog(text);
-	}
-}
-void LRClickableAreaWText::clickRight(tribool down, bool previousState)
-{
-	adventureInt->handleRightClick(text, down);
-}
-
-LRClickableAreaWText::LRClickableAreaWText()
-{
-	init();
-}
-
-LRClickableAreaWText::LRClickableAreaWText(const Rect &Pos, const std::string &HoverText /*= ""*/, const std::string &ClickText /*= ""*/)
-{
-	init();
-	pos = Pos + pos;
-	hoverText = HoverText;
-	text = ClickText;
-}
-
-LRClickableAreaWText::~LRClickableAreaWText()
-{
-}
-
-void LRClickableAreaWText::init()
-{
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-}
-
-std::string CLabel::visibleText()
-{
-	return text;
-}
-
-void CLabel::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-
-	if(!visibleText().empty())
-		blitLine(to, pos, visibleText());
-
-}
-
-CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
-:CTextContainer(Align, Font, Color), text(Text)
-{
-	type |= REDRAW_PARENT;
-	autoRedraw = true;
-	pos.x += x;
-	pos.y += y;
-	pos.w = pos.h = 0;
-	bg = nullptr;
-
-	if (alignment == TOPLEFT) // causes issues for MIDDLE
-	{
-		pos.w = graphics->fonts[font]->getStringWidth(visibleText().c_str());
-		pos.h = graphics->fonts[font]->getLineHeight();
-	}
-}
-
-Point CLabel::getBorderSize()
-{
-	return Point(0, 0);
-}
-
-std::string CLabel::getText()
-{
-	return text;
-}
-
-void CLabel::setText(const std::string &Txt)
-{
-	text = Txt;
-	if(autoRedraw)
-	{
-		if(bg || !parent)
-			redraw();
-		else
-			parent->redraw();
-	}
-}
-
-CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
-    CLabel(position.x, position.y, Font, Align, Color, Text),
-    visibleSize(0, 0, position.w, position.h)
-{
-	pos.w = position.w;
-	pos.h = position.h;
-	splitText(Text);
-}
-
-void CMultiLineLabel::setVisibleSize(Rect visibleSize)
-{
-	this->visibleSize = visibleSize;
-	redraw();
-}
-
-void CMultiLineLabel::scrollTextBy(int distance)
-{
-	scrollTextTo(visibleSize.y + distance);
-}
-
-void CMultiLineLabel::scrollTextTo(int distance)
-{
-	Rect size = visibleSize;
-	size.y = distance;
-	setVisibleSize(size);
-}
-
-void CMultiLineLabel::setText(const std::string &Txt)
-{
-	splitText(Txt);
-	CLabel::setText(Txt);
-}
-
-void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what)
-{
-	const IFont * f = graphics->fonts[font];
-	Point where = destRect.topLeft();
-
-	// input is rect in which given text should be placed
-	// calculate proper position for top-left corner of the text
-	if (alignment == TOPLEFT)
-	{
-		where.x += getBorderSize().x;
-		where.y += getBorderSize().y;
-	}
-
-	if (alignment == CENTER)
-	{
-		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2;
-		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2;
-	}
-
-	if (alignment == BOTTOMRIGHT)
-	{
-		where.x += getBorderSize().x + destRect.w - f->getStringWidth(what);
-		where.y += getBorderSize().y + destRect.h - f->getLineHeight();
-	}
-
-	size_t begin = 0;
-	std::string delimeters = "{}";
-	size_t currDelimeter = 0;
-
-	do
-	{
-		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin);
-		if (begin != end)
-		{
-			std::string toPrint = what.substr(begin, end - begin);
-
-			if (currDelimeter % 2) // Enclosed in {} text - set to yellow
-				f->renderTextLeft(to, toPrint, Colors::YELLOW, where);
-			else // Non-enclosed text, use default color
-				f->renderTextLeft(to, toPrint, color, where);
-			begin = end;
-
-			where.x += f->getStringWidth(toPrint);
-		}
-		currDelimeter++;
-	}
-	while (begin++ != std::string::npos);
-}
-
-CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color):
-	alignment(alignment),
-	font(font),
-	color(color)
-{}
-
-void CMultiLineLabel::showAll(SDL_Surface * to)
-{
-	CIntObject::showAll(to);
-
-	const IFont * f = graphics->fonts[font];
-
-	// calculate which lines should be visible
-	int totalLines = lines.size();
-	int beginLine  = visibleSize.y;
-	int endLine    = getTextLocation().h + visibleSize.y;
-
-	if (beginLine < 0)
-		beginLine = 0;
-	else
-		beginLine /= f->getLineHeight();
-
-	if (endLine < 0)
-		endLine = 0;
-	else
-		endLine /= f->getLineHeight();
-	endLine++;
-
-	// and where they should be displayed
-	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * f->getLineHeight());
-	Point lineSize  = Point(getTextLocation().w, f->getLineHeight());
-
-	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
-
-	for (int i = beginLine; i < std::min(totalLines, endLine); i++)
-	{
-		if (!lines[i].empty()) //non-empty line
-			blitLine(to, Rect(lineStart, lineSize), lines[i]);
-
-		lineStart.y += f->getLineHeight();
-	}
-}
-
-void CMultiLineLabel::splitText(const std::string &Txt)
-{
-	lines.clear();
-
-	const IFont * f = graphics->fonts[font];
-	int lineHeight =  f->getLineHeight();
-
-	lines = CMessage::breakText(Txt, pos.w, font);
-
-	 textSize.y = lineHeight * lines.size();
-	 textSize.x = 0;
-	for(const std::string &line : lines)
-		vstd::amax( textSize.x, f->getStringWidth(line.c_str()));
-	redraw();
-}
-
-Rect CMultiLineLabel::getTextLocation()
-{
-	// this method is needed for vertical alignment alignment of text
-	// when height of available text is smaller than height of widget
-	// in this case - we should add proper offset to display text at required position
-	if (pos.h <= textSize.y)
-		return pos;
-
-	Point textSize(pos.w, graphics->fonts[font]->getLineHeight() * lines.size());
-	Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
-
-	switch(alignment)
-	{
-	case TOPLEFT:     return Rect(pos.topLeft(), textSize);
-	case CENTER:      return Rect(pos.topLeft() + textOffset / 2, textSize);
-	case BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
-	}
-	assert(0);
-	return Rect();
-}
-
-CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
-	font(Font), align(Align), color(Color)
-{}
-
-void CLabelGroup::add(int x, int y, const std::string &text)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	new CLabel(x, y, font, align, color, text);
-}
-
-CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/):
-    sliderStyle(SliderStyle),
-    slider(nullptr)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	label = new CMultiLineLabel(rect, Font, Align, Color);
-
-	type |= REDRAW_PARENT;
-	pos.x += rect.x;
-	pos.y += rect.y;
-	pos.h = rect.h;
-	pos.w = rect.w;
-
-	assert(pos.w >= 40); //we need some space
-	setText(Text);
-}
-
-void CTextBox::sliderMoved(int to)
-{
-	label->scrollTextTo(to);
-}
-
-void CTextBox::resize(Point newSize)
-{
-	pos.w = newSize.x;
-	pos.h = newSize.y;
-	label->pos.w = pos.w;
-	label->pos.h = pos.h;
-	if (slider)
-		vstd::clear_pointer(slider); // will be recreated if needed later
-
-	setText(label->getText()); // force refresh
-}
-
-void CTextBox::setText(const std::string &text)
-{
-	label->setText(text);
-	if (label->textSize.y <= label->pos.h && slider)
-	{
-		// slider is no longer needed
-		vstd::clear_pointer(slider);
-		label->pos.w = pos.w;
-		label->setText(text);
-	}
-	else if (label->textSize.y > label->pos.h && !slider)
-	{
-		// create slider and update widget
-		label->pos.w = pos.w - 32;
-		label->setText(text);
-
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-        slider = new CSlider(pos.w - 32, 0, pos.h, std::bind(&CTextBox::sliderMoved, this, _1),
-		                     label->pos.h, label->textSize.y, 0, false, sliderStyle);
-		slider->scrollStep = graphics->fonts[label->font]->getLineHeight();
-	}
-}
-
-void CGStatusBar::setText(const std::string & Text)
-{
-	if(!textLock)
-		CLabel::setText(Text);
-}
-
-void CGStatusBar::clear()
-{
-	setText("");
-}
-
-CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= CENTER*/, const SDL_Color &Color /*= Colors::WHITE*/)
-: CLabel(BG->pos.x, BG->pos.y, Font, Align, Color, "")
-{
-	init();
-	bg = BG;
-	addChild(bg);
-	pos = bg->pos;
-	getBorderSize();
-    textLock = false;
-}
-
-CGStatusBar::CGStatusBar(int x, int y, std::string name/*="ADROLLVR.bmp"*/, int maxw/*=-1*/)
-: CLabel(x, y, FONT_SMALL, CENTER)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	init();
-	bg = new CPicture(name);
-	pos = bg->pos;
-	if((unsigned int)maxw < pos.w)
-	{
-		vstd::amin(pos.w, maxw);
-		bg->srcRect = new Rect(0, 0, maxw, pos.h);
-	}
-    textLock = false;
-}
-
-CGStatusBar::~CGStatusBar()
-{
-	GH.statusbar = oldStatusBar;
-}
-
-void CGStatusBar::show(SDL_Surface * to)
-{
-    showAll(to);
-}
-
-void CGStatusBar::init()
-{
-	oldStatusBar = GH.statusbar;
-	GH.statusbar = this;
-}
-
-Point CGStatusBar::getBorderSize()
-{
-	//Width of borders where text should not be printed
-	static const Point borderSize(5,1);
-
-	switch(alignment)
-	{
-	case TOPLEFT:     return Point(borderSize.x, borderSize.y);
-	case CENTER:      return Point(pos.w/2, pos.h/2);
-	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
-	}
-	assert(0);
-	return Point();
-}
-
-void CGStatusBar::lock(bool shouldLock)
-{
-    textLock = shouldLock;
-}
-
-CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB):
-    CLabel(Pos.x, Pos.y, font, CENTER),
-    cb(CB)
-{
-	type |= REDRAW_PARENT;
-	focus = false;
-	pos.h = Pos.h;
-	pos.w = Pos.w;
-	captureAllKeys = true;
-	bg = nullptr;
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB )
-:cb(CB)
-{
-	focus = false;
-	pos += Pos;
-	captureAllKeys = true;
-	OBJ_CONSTRUCTION;
-	bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
-{
-	focus = false;
-	pos += Pos;
-	captureAllKeys = true;
-	OBJ_CONSTRUCTION;
-	bg = new CPicture(Pos, 0, true);
-	Rect hlp = Pos;
-	if(srf)
-		CSDL_Ext::blitSurface(srf, &hlp, *bg, nullptr);
-	else
-		SDL_FillRect(*bg, nullptr, 0);
-	pos.w = bg->pos.w;
-	pos.h = bg->pos.h;
-	bg->pos = pos;
-	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
-	giveFocus();
-}
-
-void CTextInput::focusGot()
-{
-	CSDL_Ext::startTextInput(&pos);	
-}
-
-void CTextInput::focusLost()
-{
-	CSDL_Ext::stopTextInput();
-}
-
-
-std::string CTextInput::visibleText()
-{
-	return focus ? text + newText + "_" : text;
-}
-
-void CTextInput::clickLeft( tribool down, bool previousState )
-{
-	if(down && !focus)
-		giveFocus();
-}
-
-void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
-{
-
-	if(!focus || key.state != SDL_PRESSED)
-		return;
-
-	if(key.keysym.sym == SDLK_TAB)
-	{
-		moveFocus();
-		GH.breakEventHandling();
-		return;
-	}
-
-	bool redrawNeeded = false;
-	#ifdef VCMI_SDL1
-	std::string oldText = text;
-	#endif // 0	
-	switch(key.keysym.sym)
-	{
-	case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
-		return;
-	case SDLK_BACKSPACE:
-		if(!newText.empty())
-		{
-			Unicode::trimRight(newText);
-			redrawNeeded = true;
-		}
-		else if(!text.empty())
-		{
-			Unicode::trimRight(text);
-			redrawNeeded = true;
-		}			
-		break;
-	default:
-		#ifdef VCMI_SDL1
-		if (key.keysym.unicode < ' ')
-			return;
-		else
-		{
-			text += key.keysym.unicode; //TODO 16-/>8
-			redrawNeeded = true;
-		}			
-		#endif // 0
-		break;
-	}
-	#ifdef VCMI_SDL1
-	filters(text, oldText);
-	#endif // 0
-	if (redrawNeeded)
-	{
-		redraw();
-		cb(text);
-	}	
-}
-
-void CTextInput::setText( const std::string &nText, bool callCb )
-{
-	CLabel::setText(nText);
-	if(callCb)
-		cb(text);
-}
-
-bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
-{
-	if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
-		return false;
-	
-	#ifdef VCMI_SDL1
-	//this should allow all non-printable keys to go through (for example arrows)
-	if (key.keysym.unicode < ' ')
-		return false;
-
-	return true;
-	#else
-	return false;
-	#endif
-}
-
-#ifndef VCMI_SDL1
-void CTextInput::textInputed(const SDL_TextInputEvent & event)
-{
-	if(!focus)
-		return;
-	std::string oldText = text;
-	
-	text += event.text;	
-	
-	filters(text,oldText);
-	if (text != oldText)
-	{
-		redraw();
-		cb(text);
-	}	
-	newText = "";
-}
-
-void CTextInput::textEdited(const SDL_TextEditingEvent & event)
-{
-	if(!focus)
-		return;
-		
-	newText = event.text;
-	redraw();
-	cb(text+newText);	
-}
-
-#endif
-
-
-void CTextInput::filenameFilter(std::string & text, const std::string &)
-{
-	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
-	size_t pos;
-	while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
-		text.erase(pos, 1);
-}
-
-void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
-{
-	assert(minValue < maxValue);
-
-	if (text.empty())
-		text = "0";
-
-	size_t pos = 0;
-	if (text[0] == '-') //allow '-' sign as first symbol only
-		pos++;
-
-	while (pos < text.size())
-	{
-		if (text[pos] < '0' || text[pos] > '9')
-		{
-			text = oldText;
-			return; //new text is not number.
-		}
-		pos++;
-	}
-	try
-	{
-		int value = boost::lexical_cast<int>(text);
-		if (value < minValue)
-			text = boost::lexical_cast<std::string>(minValue);
-		else if (value > maxValue)
-			text = boost::lexical_cast<std::string>(maxValue);
-	}
-	catch(boost::bad_lexical_cast &)
-	{
-		//Should never happen. Unless I missed some cases
-        logGlobal->warnStream() << "Warning: failed to convert "<< text << " to number!";
-		text = oldText;
-	}
-}
-
-CFocusable::CFocusable()
-{
-	focusables.push_back(this);
-}
-
-CFocusable::~CFocusable()
-{
-	if(inputWithFocus == this)
-	{
-		focusLost();
-		inputWithFocus = nullptr;
-	}	
-
-	focusables -= this;
-}
-void CFocusable::giveFocus()
-{
-	if(inputWithFocus)
-	{
-		inputWithFocus->focus = false;
-		inputWithFocus->focusLost();
-		inputWithFocus->redraw();
-	}
-
-	focus = true;
-	inputWithFocus = this;
-	focusGot();
-	redraw();	
-}
-
-void CFocusable::moveFocus()
-{
-	auto i = vstd::find(focusables, this),
-		ourIt = i;
-	for(i++; i != ourIt; i++)
-	{
-		if(i == focusables.end())
-			i = focusables.begin();
-
-		if((*i)->active)
-		{
-			(*i)->giveFocus();
-			break;;
-		}
-	}
-}
-
-CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt):
-    CIntObject(getUsedEvents(options_), Point()),
-    shadow(nullptr),
-    options(options_),
-    background(createBg(imageName, options & PLAYER_COLORED))
-{
-	assert(parent == nullptr); //Safe to remove, but windows should not have parent
-
-	if (options & RCLICK_POPUP)
-		CCS->curh->hide();
-
-	if (background)
-		pos = background->center(centerAt);
-	else
-		center(centerAt);
-
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-CWindowObject::CWindowObject(int options_, std::string imageName):
-    CIntObject(getUsedEvents(options_), Point()),
-    shadow(nullptr),
-    options(options_),
-    background(createBg(imageName, options & PLAYER_COLORED))
-{
-	assert(parent == nullptr); //Safe to remove, but windows should not have parent
-
-	if (options & RCLICK_POPUP)
-		CCS->curh->hide();
-
-	if (background)
-		pos = background->center();
-	else
-		center(Point(screen->w/2, screen->h/2));
-
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-CWindowObject::~CWindowObject()
-{
-	setShadow(false);
-}
-
-CPicture * CWindowObject::createBg(std::string imageName, bool playerColored)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	if (imageName.empty())
-		return nullptr;
-
-	auto  image = new CPicture(imageName);
-	if (playerColored)
-		image->colorize(LOCPLINT->playerID);
-	return image;
-}
-
-void CWindowObject::setBackground(std::string filename)
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-	delete background;
-	background = createBg(filename, options & PLAYER_COLORED);
-
-	if (background)
-		pos = background->center(Point(pos.w/2 + pos.x, pos.h/2 + pos.y));
-
-	updateShadow();
-}
-
-int CWindowObject::getUsedEvents(int options)
-{
-	if (options & RCLICK_POPUP)
-		return RCLICK;
-	return 0;
-}
-
-void CWindowObject::updateShadow()
-{
-	setShadow(false);
-	if (!(options & SHADOW_DISABLED))
-		setShadow(true);
-}
-
-void CWindowObject::setShadow(bool on)
-{
-	//size of shadow
-	static const int size = 8;
-
-	if (on == bool(shadow))
-		return;
-
-	vstd::clear_pointer(shadow);
-
-	//object too small to cast shadow
-	if (pos.h <= size || pos.w <= size)
-		return;
-
-	if (on)
-	{
-
-		//helper to set last row
-		auto blitAlphaRow = [](SDL_Surface *surf, size_t row)
-		{
-			Uint8 * ptr = (Uint8*)surf->pixels + surf->pitch * (row);
-
-			for (size_t i=0; i< surf->w; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+=4;
-			}
-		};
-
-		// helper to set last column
-		auto blitAlphaCol = [](SDL_Surface *surf, size_t col)
-		{
-			Uint8 * ptr = (Uint8*)surf->pixels + 4 * (col);
-
-			for (size_t i=0; i< surf->h; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+= surf->pitch;
-			}
-		};
-
-		static SDL_Surface * shadowCornerTempl = nullptr;
-		static SDL_Surface * shadowBottomTempl = nullptr;
-		static SDL_Surface * shadowRightTempl = nullptr;
-
-		//one-time initialization
-		if (!shadowCornerTempl)
-		{
-			//create "template" surfaces
-			shadowCornerTempl = CSDL_Ext::createSurfaceWithBpp<4>(size, size);
-			shadowBottomTempl = CSDL_Ext::createSurfaceWithBpp<4>(1, size);
-			shadowRightTempl  = CSDL_Ext::createSurfaceWithBpp<4>(size, 1);
-
-			Uint32 shadowColor = SDL_MapRGBA(shadowCornerTempl->format, 0, 0, 0, 192);
-
-			//fill with shadow body color
-			SDL_FillRect(shadowCornerTempl, nullptr, shadowColor);
-			SDL_FillRect(shadowBottomTempl, nullptr, shadowColor);
-			SDL_FillRect(shadowRightTempl,  nullptr, shadowColor);
-
-			//fill last row and column with more transparent color
-			blitAlphaCol(shadowRightTempl , size-1);
-			blitAlphaCol(shadowCornerTempl, size-1);
-			blitAlphaRow(shadowBottomTempl, size-1);
-			blitAlphaRow(shadowCornerTempl, size-1);
-		}
-
-		OBJ_CONSTRUCTION_CAPTURING_ALL;
-
-		//FIXME: do something with this points
-		Point shadowStart;
-		if (options & BORDERED)
-			shadowStart = Point(size - 14, size - 14);
-		else
-			shadowStart = Point(size, size);
-
-		Point shadowPos;
-		if (options & BORDERED)
-			shadowPos = Point(pos.w + 14, pos.h + 14);
-		else
-			shadowPos = Point(pos.w, pos.h);
-
-		Point fullsize;
-		if (options & BORDERED)
-			fullsize = Point(pos.w + 28, pos.h + 29);
-		else
-			fullsize = Point(pos.w, pos.h);
-
-		//create base 8x8 piece of shadow
-		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl);
-		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurfaceFast(shadowBottomTempl, fullsize.x - size, size);
-		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurfaceFast(shadowRightTempl,  size, fullsize.y - size);
-
-		blitAlphaCol(shadowBottom, 0);
-		blitAlphaRow(shadowRight, 0);
-
-		//generate "shadow" object with these 3 pieces in it
-		shadow = new CIntObject;
-		shadow->addChild(new CPicture(shadowCorner, shadowPos.x, shadowPos.y));
-		shadow->addChild(new CPicture(shadowRight,  shadowPos.x, shadowStart.y));
-		shadow->addChild(new CPicture(shadowBottom, shadowStart.x, shadowPos.y));
-	}
-}
-
-void CWindowObject::showAll(SDL_Surface *to)
-{
-	CIntObject::showAll(to);
-	if ((options & BORDERED) && (pos.h != to->h || pos.w != to->w))
-		CMessage::drawBorder(LOCPLINT ? LOCPLINT->playerID : PlayerColor(1), to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
-}
-
-void CWindowObject::close()
-{
-	GH.popIntTotally(this);
-}
-
-void CWindowObject::clickRight(tribool down, bool previousState)
-{
-	close();
-	CCS->curh->show();
-}

+ 0 - 557
client/gui/CIntObjectClasses.h

@@ -1,557 +0,0 @@
-#pragma once
-
-#include "CIntObject.h"
-#include "SDL_Extensions.h"
-#include "../../lib/FunctionList.h"
-
-struct SDL_Surface;
-struct Rect;
-class CAnimImage;
-class CLabel;
-class CAnimation;
-class CDefHandler;
-
-/*
- * CPicture.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
- *
- */
-
-// Window GUI class
-class CSimpleWindow : public CIntObject
-{
-public:
-	SDL_Surface * bitmap; //background
-	virtual void show(SDL_Surface * to);
-	CSimpleWindow():bitmap(nullptr){}; //c-tor
-	virtual ~CSimpleWindow(); //d-tor
-};
-
-// Image class
-class CPicture : public CIntObject
-{
-	void setSurface(SDL_Surface *to);
-public: 
-	SDL_Surface * bg;
-	Rect * srcRect; //if nullptr then whole surface will be used
-	bool freeSurf; //whether surface will be freed upon CPicture destruction
-	bool needRefresh;//Surface needs to be displayed each frame
-
-	operator SDL_Surface*()
-	{
-		return bg;
-	}
-
-	CPicture(const Rect & r, const SDL_Color & color, bool screenFormat = false); //rect filled with given color
-	CPicture(const Rect & r, ui32 color, bool screenFormat = false); //rect filled with given color
-	CPicture(SDL_Surface * BG, int x = 0, int y=0, bool Free = true); //wrap existing SDL_Surface
-	CPicture(const std::string &bmpname, int x=0, int y=0);
-	CPicture(SDL_Surface *BG, const Rect &SrcRext, int x = 0, int y = 0, bool free = false); //wrap subrect of given surface
-	~CPicture();
-	void init();
-
-	//set alpha value for whole surface. Note: may be messed up if surface is shared
-	// 0=transparent, 255=opaque
-	void setAlpha(int value);
-
-	void scaleTo(Point size);
-	void createSimpleRect(const Rect &r, bool screenFormat, ui32 color);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void convertToScreenBPP();
-	void colorizeAndConvert(PlayerColor player);
-	void colorize(PlayerColor player);
-};
-
-/// area filled with specific texture
-class CFilledTexture : CIntObject
-{
-	SDL_Surface * texture;
-
-public:
-	CFilledTexture(std::string imageName, Rect position);
-	~CFilledTexture();
-	void showAll(SDL_Surface *to);
-};
-
-namespace config{struct ButtonInfo;}
-
-/// Base class for buttons.
-class CButtonBase : public CKeyShortcut
-{
-public:
-	enum ButtonState
-	{
-		NORMAL=0,
-		PRESSED=1,
-		BLOCKED=2,
-		HIGHLIGHTED=3
-	};
-private:
-	int bitmapOffset; // base offset of visible bitmap from animation
-	ButtonState state;//current state of button from enum
-
-public:
-	bool swappedImages,//fix for some buttons: normal and pressed image are swapped
-		keepFrame; // don't change visual representation
-
-    void addTextOverlay(const std::string &Text, EFonts font, SDL_Color color = Colors::WHITE);
-	void update();//to refresh button after image or text change
-
-	void setOffset(int newOffset);
-	void setState(ButtonState newState);
-	ButtonState getState();
-
-	//just to make code clearer
-	void block(bool on);
-	bool isBlocked();
-	bool isHighlighted();
-
-	CAnimImage * image; //image for this button
-	CLabel * text;//text overlay
-
-	CButtonBase(); //c-tor
-	virtual ~CButtonBase(); //d-tor
-};
-
-/// Typical Heroes 3 button which can be inactive or active and can 
-/// hold further information if you right-click it
-class CAdventureMapButton : public CButtonBase
-{
-	std::vector<std::string> imageNames;//store list of images that can be used by this button
-	size_t currentImage;
-
-	void onButtonClicked(); // calls callback
-public:
-	std::map<int, std::string> hoverTexts; //text for statusbar
-	std::string helpBox; //for right-click help
-	CFunctionList<void()> callback;
-	bool actOnDown,//runs when mouse is pressed down over it, not when up
-		hoverable,//if true, button will be highlighted when hovered
-		borderEnabled,
-		soundDisabled;
-	SDL_Color borderColor;
-
-	void clickRight(tribool down, bool previousState);
-	virtual void clickLeft(tribool down, bool previousState);
-	void hover (bool on);
-
-	CAdventureMapButton(); //c-tor
-	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CAdventureMapButton( const std::pair<std::string, std::string> &help, const CFunctionList<void()> &Callback, int x, int y, const std::string &defName, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CAdventureMapButton( const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &Callback, config::ButtonInfo *info, int key=0);//c-tor
-
-	void init(const CFunctionList<void()> &Callback, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key );
-
-	void setIndex(size_t index, bool playerColoredButton=false);
-	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
-	void setPlayerColor(PlayerColor player);
-	void showAll(SDL_Surface * to);
-};
-
-/// A button which can be selected/deselected
-class CHighlightableButton 
-	: public CAdventureMapButton
-{
-public:
-	CHighlightableButton(const CFunctionList<void()> &onSelect, const CFunctionList<void()> &onDeselect, const std::map<int,std::string> &Name, const std::string &HelpBox, bool playerColoredButton, const std::string &defName, std::vector<std::string> * add, int x, int y, int key=0);
-	CHighlightableButton(const std::pair<std::string, std::string> &help, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	CHighlightableButton(const std::string &Name, const std::string &HelpBox, const CFunctionList<void()> &onSelect, int x, int y, const std::string &defName, int myid, int key=0, std::vector<std::string> * add = nullptr, bool playerColoredButton = false );//c-tor
-	bool onlyOn;//button can not be de-selected
-	bool selected;//state of highlightable button
-	int ID; //for identification
-	CFunctionList<void()> callback2; //when de-selecting
-	void select(bool on);
-	void clickLeft(tribool down, bool previousState);
-};
-
-/// A group of buttons where one button can be selected
-class CHighlightableButtonsGroup : public CIntObject
-{
-public:
-	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
-	std::vector<CHighlightableButton*> buttons;
-	bool musicLike; //determines the behaviour of this group
-
-	//void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid);
-	void addButton(CHighlightableButton* bt);//add existing button, it'll be deleted by CHighlightableButtonsGroup destructor
-	void addButton(const std::map<int,std::string> &tooltip, const std::string &HelpBox, const std::string &defName, int x, int y, int uid, const CFunctionList<void()> &OnSelect=0, int key=0); //creates new button
-	CHighlightableButtonsGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
-	~CHighlightableButtonsGroup();
-	void select(int id, bool mode); //mode==0: id is serial; mode==1: id is unique button id
-	void selectionChanged(int to);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void block(ui8 on);
-};
-
-/// A typical slider which can be orientated horizontally/vertically.
-class CSlider : public CIntObject
-{
-public:
-	CAdventureMapButton *left, *right, *slider; //if vertical then left=up
-	int capacity;//how many elements can be active at same time (e.g. hero list = 5)
-	int amount; //total amount of elements (e.g. hero list = 0-8)
-	int positions; //number of highest position (0 if there is only one)
-	int value; //first active element
-	int scrollStep; // how many elements will be scrolled via one click, default = 1
-	bool horizontal;
-	bool wheelScrolling;
-	bool keyScrolling;
-
-	std::function<void(int)> moved;
-
-	void redrawSlider(); 
-	void sliderClicked();
-	void moveLeft();
-	void moveRight();
-	void moveTo(int to);
-	void block(bool on);
-	void setAmount(int to);
-
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void wheelScrolled(bool down, bool in);
-	void clickLeft(tribool down, bool previousState);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void showAll(SDL_Surface * to);	
-
-	CSlider(int x, int y, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, 
-		int Value=0, bool Horizontal=true, int style = 0); //style 0 - brown, 1 - blue
-	~CSlider();
-	void moveToMax();
-};
-
-/// Used as base for Tabs and List classes
-class CObjectList : public CIntObject
-{
-public:
-	typedef std::function<CIntObject* (size_t)> CreateFunc;
-	typedef std::function<void(CIntObject *)> DestroyFunc;
-
-private:
-	CreateFunc createObject;
-	DestroyFunc destroyObject;
-
-protected:
-	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
-	void deleteItem(CIntObject* item);
-	CIntObject* createItem(size_t index);
-
-	CObjectList(CreateFunc create, DestroyFunc destroy = DestroyFunc());//Protected constructor
-};
-
-/// Window element with multiple tabs
-class CTabbedInt : public CObjectList
-{
-private:
-	CIntObject * activeTab;
-	size_t activeID;
-
-public:
-	//CreateFunc, DestroyFunc - see CObjectList
-	//Pos - position of object, all tabs will be moved to this position
-	//ActiveID - ID of initially active tab
-	CTabbedInt(CreateFunc create, DestroyFunc destroy = DestroyFunc(), Point position=Point(), size_t ActiveID=0);
-
-	void setActive(size_t which);
-	//recreate active tab
-	void reset();
-
-	//return currently active item
-	CIntObject * getItem();
-};
-
-/// List of IntObjects with optional slider
-class CListBox : public CObjectList
-{
-private:
-	std::list< CIntObject* > items;
-	size_t first;
-	size_t totalSize;
-
-	Point itemOffset;
-	CSlider * slider;
-
-	void updatePositions();
-public:
-	//CreateFunc, DestroyFunc - see CObjectList
-	//Pos - position of first item
-	//ItemOffset - distance between items in the list
-	//VisibleSize - maximal number of displayable at once items
-	//TotalSize
-	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
-	//SliderPos - position of slider, if present
-	CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
-		size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
-
-	//recreate all visible items
-	void reset();
-
-	//change or get total amount of items in the list
-	void resize(size_t newSize);
-	size_t size();
-
-	//return item with index which or null if not present
-	CIntObject * getItem(size_t which);
-
-	//return currently active items
-	const std::list< CIntObject * > & getItems();
-
-	//get index of this item. -1 if not found
-	size_t getIndexOf(CIntObject * item);
-
-	//scroll list to make item which visible
-	void scrollTo(size_t which);
-
-	//scroll list to specified position
-	void moveToPos(size_t which);
-	void moveToNext();
-	void moveToPrev();
-
-	size_t getPos();
-};
-
-/// Small helper class to manage group of similar labels
-class CLabelGroup : public CIntObject
-{
-	std::list<CLabel*> labels;
-	EFonts font;
-	EAlignment align;
-	SDL_Color color;
-public:
-	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
-	void add(int x=0, int y=0, const std::string &text =  "");
-};
-
-/// Base class for all text-related widgets.
-/// Controls text blitting-related options
-class CTextContainer : public virtual CIntObject
-{
-protected:
-	/// returns size of border, for left- or right-aligned text
-	virtual Point getBorderSize() = 0;
-	/// do actual blitting of line. Text "what" will be placed at "where" and aligned according to alignment
-	void blitLine(SDL_Surface * to, Rect where, std::string what);
-
-	CTextContainer(EAlignment alignment, EFonts font, SDL_Color color);
-
-public:
-	EAlignment alignment;
-	EFonts font;
-	SDL_Color color; // default font color. Can be overridden by placing "{}" into the string
-};
-
-/// Label which shows text
-class CLabel : public CTextContainer
-{
-protected:
-	Point getBorderSize() override;
-	virtual std::string visibleText();
-
-	CPicture *bg;
-public:
-
-	std::string text;
-	bool autoRedraw;  //whether control will redraw itself on setTxt
-
-	std::string getText();
-	virtual void setText(const std::string &Txt);
-
-	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
-	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
-	void showAll(SDL_Surface * to); //shows statusbar (with current text)
-};
-
-/// Multi-line label that can display multiple lines of text
-/// If text is too big to fit into requested area remaining part will not be visible
-class CMultiLineLabel : public CLabel
-{
-	// text to blit, split into lines that are no longer than widget width
-	std::vector<std::string> lines;
-
-	// area of text that actually will be printed, default is widget size
-	Rect visibleSize;
-
-	void splitText(const std::string &Txt);
-	Rect getTextLocation();
-public:
-	// total size of text, x = longest line of text, y = total height of lines
-	Point textSize;
-
-	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
-
-	void setText(const std::string &Txt);
-	void showAll(SDL_Surface * to);
-
-	void setVisibleSize(Rect visibleSize);
-	// scrolls text visible in widget. Positive value will move text up
-	void scrollTextTo(int distance);
-	void scrollTextBy(int distance);
-};
-
-/// a multi-line label that tries to fit text with given available width and height;
-/// if not possible, it creates a slider for scrolling text
-class CTextBox : public CIntObject
-{
-	int sliderStyle;
-public:
-	CMultiLineLabel * label;
-	CSlider *slider;
-
-	CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
-
-	void resize(Point newSize);
-	void setText(const std::string &Txt);
-	void sliderMoved(int to);
-};
-
-/// Status bar which is shown at the bottom of the in-game screens
-class CGStatusBar : public CLabel
-{
-	bool textLock; //Used for blocking changes to the text
-	void init();
-
-	CGStatusBar *oldStatusBar;
-protected:
-	Point getBorderSize() override;
-
-public:
-
-	void clear();//clears statusbar and refreshes
-	void setText(const std::string & Text) override; //prints text and refreshes statusbar
-
-	void show(SDL_Surface * to); //shows statusbar (with current text)
-
-	CGStatusBar(CPicture *BG, EFonts Font = FONT_SMALL, EAlignment Align = CENTER, const SDL_Color &Color = Colors::WHITE); //given CPicture will be captured by created sbar and it's pos will be used as pos for sbar
-	CGStatusBar(int x, int y, std::string name, int maxw=-1);
-	~CGStatusBar();
-
-	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
-};
-
-/// UIElement which can get input focus
-class CFocusable : public virtual CIntObject
-{
-protected:
-	virtual void focusGot(){};
-	virtual void focusLost(){};
-public:
-	bool focus; //only one focusable control can have focus at one moment
-
-	void giveFocus(); //captures focus
-	void moveFocus(); //moves focus to next active control (may be used for tab switching)
-
-	static std::list<CFocusable*> focusables; //all existing objs
-	static CFocusable *inputWithFocus; //who has focus now
-	CFocusable();
-	~CFocusable();
-};
-
-/// Text input box where players can enter text
-class CTextInput : public CLabel, public CFocusable
-{
-	std::string newText;
-protected:
-	std::string visibleText() override;
-
-	void focusGot() override;
-	void focusLost() override;
-public:
-	CFunctionList<void(const std::string &)> cb;
-	CFunctionList<void(std::string &, const std::string &)> filters;
-	void setText(const std::string &nText, bool callCb = false);
-
-	CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB);
-	CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB);
-	CTextInput(const Rect &Pos, SDL_Surface *srf = nullptr);
-
-	void clickLeft(tribool down, bool previousState) override;
-	void keyPressed(const SDL_KeyboardEvent & key) override;
-	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
-	
-#ifndef VCMI_SDL1
-	void textInputed(const SDL_TextInputEvent & event) override;
-	void textEdited(const SDL_TextEditingEvent & event) override;
-	
-	
-#endif // VCMI_SDL1	
-
-	//Filter that will block all characters not allowed in filenames
-	static void filenameFilter(std::string &text, const std::string & oldText);
-	//Filter that will allow only input of numbers in range min-max (min-max are allowed)
-    //min-max should be set via something like std::bind
-	static void numberFilter(std::string &text, const std::string & oldText, int minValue, int maxValue);
-};
-
-/// Shows a text by moving the mouse cursor over the object
-class CHoverableArea: public virtual CIntObject
-{
-public:
-	std::string hoverText;
-
-	virtual void hover (bool on);
-
-	CHoverableArea();
-	virtual ~CHoverableArea();
-};
-
-/// Can interact on left and right mouse clicks, plus it shows a text when by hovering over it
-class LRClickableAreaWText: public CHoverableArea
-{
-public:
-	std::string text;
-
-	LRClickableAreaWText();
-	LRClickableAreaWText(const Rect &Pos, const std::string &HoverText = "", const std::string &ClickText = "");
-	virtual ~LRClickableAreaWText();
-	void init();
-
-	virtual void clickLeft(tribool down, bool previousState);
-	virtual void clickRight(tribool down, bool previousState);
-};
-
-/// Basic class for windows
-class CWindowObject : public CIntObject
-{
-	CPicture * createBg(std::string imageName, bool playerColored);
-	int getUsedEvents(int options);
-
-	CIntObject *shadow;
-	void setShadow(bool on);
-
-	int options;
-
-protected:
-	CPicture * background;
-
-	//Simple function with call to GH.popInt
-	void close();
-	//Used only if RCLICK_POPUP was set
-	void clickRight(tribool down, bool previousState);
-	//To display border
-	void showAll(SDL_Surface *to);
-	//change or set background image
-	void setBackground(std::string filename);
-	void updateShadow();
-public:
-	enum EOptions
-	{
-		PLAYER_COLORED=1, //background will be player-colored
-		RCLICK_POPUP=2, // window will behave as right-click popup
-		BORDERED=4, // window will have border if current resolution is bigger than size of window
-		SHADOW_DISABLED=8 //this window won't display any shadow
-	};
-
-	/*
-	 * options - EOpions enum
-	 * imageName - name for background image, can be empty
-	 * centerAt - position of window center. Default - center of the screen
-	*/
-	CWindowObject(int options, std::string imageName, Point centerAt);
-	CWindowObject(int options, std::string imageName = "");
-	~CWindowObject();
-};

+ 6 - 1
client/gui/Geometries.cpp

@@ -1,6 +1,11 @@
 #include "StdInc.h"
 #include "Geometries.h"
 #include "../CMT.h"
+#include <SDL_events.h>
+
+Point::Point(const SDL_MouseMotionEvent &a)
+	:x(a.x),y(a.y)
+{}
 
 Rect Rect::createCentered( int w, int h )
 {
@@ -15,4 +20,4 @@ Rect Rect::around(const Rect &r, int width /*= 1*/) /*creates rect around anothe
 Rect Rect::centerIn(const Rect &r)
 {
 	return Rect(r.x + (r.w - w) / 2, r.y + (r.h - h) / 2, w, h);
-}
+}

+ 3 - 5
client/gui/Geometries.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include <SDL_video.h>
-#include <SDL_events.h>
 #include "../../lib/int3.h"
 
 /*
@@ -21,6 +20,7 @@
 #undef min
 #endif
 
+struct SDL_MouseMotionEvent;
 
 // A point with x/y coordinate, used mostly for graphic rendering
 struct Point
@@ -38,9 +38,7 @@ struct Point
 	Point(const int3 &a)
 		:x(a.x),y(a.y)
 	{}
-	Point(const SDL_MouseMotionEvent &a)
-		:x(a.x),y(a.y)
-	{}
+	Point(const SDL_MouseMotionEvent &a);
 
 	template<typename T>
 	Point operator+(const T &b) const
@@ -265,4 +263,4 @@ struct Rect : public SDL_Rect
 		ret.h = y2 -ret.y;
 		return ret;
 	}
-};
+};

+ 0 - 1
client/gui/SDL_Extensions.cpp

@@ -2,7 +2,6 @@
 #include "SDL_Extensions.h"
 #include "SDL_Pixels.h"
 
-#include <SDL_ttf.h>
 #include "../CGameInfo.h"
 #include "../CMessage.h"
 #include "../CDefHandler.h"

+ 8 - 6
client/gui/SDL_Extensions.h

@@ -16,10 +16,11 @@
 #endif
 
 #include <SDL_video.h>
-#include <SDL_ttf.h>
+#include <SDL_events.h>
 #include "../../lib/int3.h"
-#include "../Graphics.h"
+//#include "../Graphics.h"
 #include "Geometries.h"
+#include "../../lib/GameConstants.h"
 
 
 //A macro to force inlining some of our functions. Compiler (at least MSVC) is not so smart here-> without that displaying is MUCH slower
@@ -141,15 +142,16 @@ typename boost::enable_if_c<boost::is_unsigned<T>::type, T>::type abs(T arg)
 }
 
 template<typename IntType>
-std::string makeNumberShort(IntType number) //the output is a string containing at most 5 characters [4 if positive] (eg. intead 10000 it gives 10k)
+std::string makeNumberShort(IntType number, IntType maxLength = 3) //the output is a string containing at most 5 characters [4 if positive] (eg. intead 10000 it gives 10k)
 {
-	if (abs(number) < 1000)
+	IntType max = pow(10, maxLength);
+	if (abs(number) < max)
 		return boost::lexical_cast<std::string>(number);
 
-	std::string symbols = "kMGTPE";
+	std::string symbols = " kMGTPE";
 	auto iter = symbols.begin();
 
-	while (number >= 1000)
+	while (number >= max)
 	{
 		number /= 1000;
 		iter++;

+ 289 - 23
client/AdventureMapClasses.cpp → client/widgets/AdventureMapClasses.cpp

@@ -1,30 +1,43 @@
 #include "StdInc.h"
 #include "AdventureMapClasses.h"
 
-#include "../CCallback.h"
-#include "../lib/JsonNode.h"
-#include "../lib/filesystem/Filesystem.h"
-#include "../lib/mapping/CMap.h"
-#include "../lib/CModHandler.h"
-#include "../lib/mapObjects/CGHeroInstance.h"
-#include "../lib/CGameState.h"
-#include "../lib/CGeneralTextHandler.h"
-#include "../lib/CTownHandler.h"
-#include "../lib/NetPacksBase.h"
-#include "../lib/CHeroHandler.h"
-#include "../lib/StringConstants.h"
-#include "CAdvmapInterface.h"
-#include "CAnimation.h"
-#include "CGameInfo.h"
-#include "CPlayerInterface.h"
-#include "CMusicHandler.h"
-#include "Graphics.h"
-#include "GUIClasses.h"
-#include "gui/CGuiHandler.h"
-#include "gui/SDL_Pixels.h"
+#include <SDL.h>
+
+#include "MiscWidgets.h"
+#include "CComponent.h"
+
+#include "../CGameInfo.h"
+#include "../CMusicHandler.h"
+#include "../CPlayerInterface.h"
+#include "../CPreGame.h"
+#include "../Graphics.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../gui/SDL_Pixels.h"
+
+#include "../windows/InfoWindows.h"
+#include "../windows/CAdvmapInterface.h"
+#include "../windows/GUIClasses.h"
+
+#include "../battle/CBattleInterfaceClasses.h"
+#include "../battle/CBattleInterface.h"
+
+#include "../../CCallback.h"
+#include "../../lib/StartInfo.h"
+#include "../../lib/CGameState.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CHeroHandler.h"
+#include "../../lib/CModHandler.h"
+#include "../../lib/CTownHandler.h"
+#include "../../lib/filesystem/Filesystem.h"
+#include "../../lib/JsonNode.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapping/CMap.h"
+#include "../../lib/NetPacksBase.h"
+#include "../../lib/StringConstants.h"
 
 /*
- * CAdventureMapClasses.h, part of VCMI engine
+ * CAdventureMapClasses.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
  *
@@ -93,15 +106,23 @@ CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, s
     selected(nullptr)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	scrollUp = new CAdventureMapButton(CGI->generaltexth->zelp[helpUp], 0, 0, 0, btnUp);
+	scrollUp = new CButton(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]);
 	list = new CListBox(create, destroy, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
 
 	//assign callback only after list was created
+<<<<<<< HEAD:client/AdventureMapClasses.cpp
     scrollUp->callback = std::bind(&CListBox::moveToPrev, list);
     scrollDown = new CAdventureMapButton(CGI->generaltexth->zelp[helpDown], std::bind(&CListBox::moveToNext, list), 0, scrollUp->pos.h + 32*size, btnDown);
 
     scrollDown->callback += std::bind(&CList::update, this);
     scrollUp->callback += std::bind(&CList::update, this);
+=======
+	scrollUp->addCallback(boost::bind(&CListBox::moveToPrev, list));
+	scrollDown = new CButton(Point(0, scrollUp->pos.h + 32*size), btnDown, CGI->generaltexth->zelp[helpDown], boost::bind(&CListBox::moveToNext, list));
+
+	scrollDown->addCallback(boost::bind(&CList::update, this));
+	scrollUp->addCallback(boost::bind(&CList::update, this));
+>>>>>>> refactoring/guiClasses:client/widgets/AdventureMapClasses.cpp
 
 	update();
 }
@@ -950,3 +971,248 @@ void CInfoBar::showGameStatus()
 	setTimer(3000);
 	redraw();
 }
+
+void CInGameConsole::show(SDL_Surface * to)
+{
+	int number = 0;
+
+	std::vector<std::list< std::pair< std::string, int > >::iterator> toDel;
+
+	boost::unique_lock<boost::mutex> lock(texts_mx);
+	for(auto it = texts.begin(); it != texts.end(); ++it, ++number)
+	{
+		Point leftBottomCorner(0, screen->h);
+		if(LOCPLINT->battleInt)
+		{
+			leftBottomCorner = LOCPLINT->battleInt->pos.bottomLeft();
+		}
+		graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN,
+			Point(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number*20));
+
+		if(SDL_GetTicks() - it->second > defaultTimeout)
+		{
+			toDel.push_back(it);
+		}
+	}
+
+	for(auto & elem : toDel)
+	{
+		texts.erase(elem);
+	}
+}
+
+void CInGameConsole::print(const std::string &txt)
+{
+	boost::unique_lock<boost::mutex> lock(texts_mx);
+	int lineLen = conf.go()->ac.outputLineLength;
+
+	if(txt.size() < lineLen)
+	{
+		texts.push_back(std::make_pair(txt, SDL_GetTicks()));
+		if(texts.size() > maxDisplayedTexts)
+		{
+			texts.pop_front();
+		}
+	}
+	else
+	{
+		assert(lineLen);
+		for(int g=0; g<txt.size() / lineLen + 1; ++g)
+		{
+			std::string part = txt.substr(g * lineLen, lineLen);
+			if(part.size() == 0)
+				break;
+
+			texts.push_back(std::make_pair(part, SDL_GetTicks()));
+			if(texts.size() > maxDisplayedTexts)
+			{
+				texts.pop_front();
+			}
+		}
+	}
+}
+
+void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
+{
+	if(key.type != SDL_KEYDOWN) return;
+
+	if(!captureAllKeys && key.keysym.sym != SDLK_TAB) return; //because user is not entering any text
+
+	switch(key.keysym.sym)
+	{
+	case SDLK_TAB:
+	case SDLK_ESCAPE:
+		{
+			if(captureAllKeys)
+			{
+				captureAllKeys = false;
+				endEnteringText(false);
+			}
+			else if(SDLK_TAB)
+			{
+				captureAllKeys = true;
+				startEnteringText();
+			}
+			break;
+		}
+	case SDLK_RETURN: //enter key
+		{
+			if(enteredText.size() > 0  &&  captureAllKeys)
+			{
+				captureAllKeys = false;
+				endEnteringText(true);
+				CCS->soundh->playSound("CHAT");
+			}
+			break;
+		}
+	case SDLK_BACKSPACE:
+		{
+			if(enteredText.size() > 1)
+			{
+				Unicode::trimRight(enteredText,2);
+				enteredText += '_';
+				refreshEnteredText();
+			}
+			break;
+		}
+	case SDLK_UP: //up arrow
+		{
+			if(previouslyEntered.size() == 0)
+				break;
+
+			if(prevEntDisp == -1)
+			{
+				prevEntDisp = previouslyEntered.size() - 1;
+				enteredText = previouslyEntered[prevEntDisp] + "_";
+				refreshEnteredText();
+			}
+			else if( prevEntDisp > 0)
+			{
+				--prevEntDisp;
+				enteredText = previouslyEntered[prevEntDisp] + "_";
+				refreshEnteredText();
+			}
+			break;
+		}
+	case SDLK_DOWN: //down arrow
+		{
+			if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
+			{
+				++prevEntDisp;
+				enteredText = previouslyEntered[prevEntDisp] + "_";
+				refreshEnteredText();
+			}
+			else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature
+			{
+				prevEntDisp = -1;
+				enteredText = "_";
+				refreshEnteredText();
+			}
+			break;
+		}
+	default:
+		{
+			#ifdef VCMI_SDL1
+			if(enteredText.size() > 0 && enteredText.size() < conf.go()->ac.inputLineLength)
+			{
+				if( key.keysym.unicode < 0x80 && key.keysym.unicode > 0 )
+				{
+					enteredText[enteredText.size()-1] = (char)key.keysym.unicode;
+					enteredText += "_";
+					refreshEnteredText();
+				}
+			}
+			#endif // VCMI_SDL1
+			break;
+		}
+	}
+}
+
+#ifndef VCMI_SDL1
+
+void CInGameConsole::textInputed(const SDL_TextInputEvent & event)
+{
+	if(!captureAllKeys || enteredText.size() == 0)
+		return;
+	enteredText.resize(enteredText.size()-1);
+
+	enteredText += event.text;
+	enteredText += "_";
+
+	refreshEnteredText();
+}
+
+void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
+{
+ //do nothing here
+}
+
+#endif // VCMI_SDL1
+
+void CInGameConsole::startEnteringText()
+{
+	CSDL_Ext::startTextInput(&pos);
+
+	enteredText = "_";
+	if(GH.topInt() == adventureInt)
+	{
+		GH.statusbar->alignment = TOPLEFT;
+		GH.statusbar->setText(enteredText);
+
+		//Prevent changes to the text from mouse interaction with the adventure map
+		GH.statusbar->lock(true);
+	}
+	else if(LOCPLINT->battleInt)
+	{
+		LOCPLINT->battleInt->console->ingcAlter = enteredText;
+	}
+}
+
+void CInGameConsole::endEnteringText(bool printEnteredText)
+{
+	CSDL_Ext::stopTextInput();
+
+	prevEntDisp = -1;
+	if(printEnteredText)
+	{
+		std::string txt = enteredText.substr(0, enteredText.size()-1);
+		LOCPLINT->cb->sendMessage(txt);
+		previouslyEntered.push_back(txt);
+		//print(txt);
+	}
+	enteredText = "";
+	if(GH.topInt() == adventureInt)
+	{
+		GH.statusbar->alignment = CENTER;
+		GH.statusbar->lock(false);
+		GH.statusbar->clear();
+	}
+	else if(LOCPLINT->battleInt)
+	{
+		LOCPLINT->battleInt->console->ingcAlter = "";
+	}
+}
+
+void CInGameConsole::refreshEnteredText()
+{
+	if(GH.topInt() == adventureInt)
+	{
+		GH.statusbar->lock(false);
+		GH.statusbar->clear();
+		GH.statusbar->setText(enteredText);
+		GH.statusbar->lock(true);
+	}
+	else if(LOCPLINT->battleInt)
+	{
+		LOCPLINT->battleInt->console->ingcAlter = enteredText;
+	}
+}
+
+CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDisplayedTexts(10)
+{
+	#ifdef VCMI_SDL1
+	addUsedEvents(KEYBOARD);
+	#else
+	addUsedEvents(KEYBOARD | TEXTINPUT);
+	#endif
+}

+ 32 - 4
client/AdventureMapClasses.h → client/widgets/AdventureMapClasses.h

@@ -1,7 +1,7 @@
 #pragma once
 
-#include "gui/CIntObject.h"
-#include "gui/CIntObjectClasses.h"
+#include "ObjectLists.h"
+#include "../../lib/FunctionList.h"
 
 class CArmedInstance;
 class CShowableAnim;
@@ -9,6 +9,7 @@ class CGGarrison;
 class CGObjectInstance;
 class CGHeroInstance;
 class CGTownInstance;
+class CButton;
 struct Component;
 struct InfoAboutArmy;
 struct InfoAboutHero;
@@ -82,8 +83,8 @@ protected:
 
 public:
 
-	CAdventureMapButton * scrollUp;
-	CAdventureMapButton * scrollDown;
+	CButton * scrollUp;
+	CButton * scrollDown;
 
 	/// functions that will be called when selection changes
 	CFunctionList<void()> onSelect;
@@ -313,3 +314,30 @@ public:
 	/// for 3 seconds shows amount of town halls and players status
 	void showGameStatus();
 };
+
+class CInGameConsole : public CIntObject
+{
+private:
+	std::list< std::pair< std::string, int > > texts; //list<text to show, time of add>
+	boost::mutex texts_mx;		// protects texts
+	std::vector< std::string > previouslyEntered; //previously entered texts, for up/down arrows to work
+	int prevEntDisp; //displayed entry from previouslyEntered - if none it's -1
+	int defaultTimeout; //timeout for new texts (in ms)
+	int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
+public:
+	std::string enteredText;
+	void show(SDL_Surface * to);
+	void print(const std::string &txt);
+	void keyPressed (const SDL_KeyboardEvent & key); //call-in
+
+#ifndef VCMI_SDL1
+	void textInputed(const SDL_TextInputEvent & event) override;
+	void textEdited(const SDL_TextEditingEvent & event) override;
+#endif // VCMI_SDL1
+
+	void startEnteringText();
+	void endEnteringText(bool printEnteredText);
+	void refreshEnteredText();
+
+	CInGameConsole(); //c-tor
+};

+ 740 - 0
client/widgets/Buttons.cpp

@@ -0,0 +1,740 @@
+#include "StdInc.h"
+#include "Buttons.h"
+
+#include "Images.h"
+#include "TextControls.h"
+
+#include "../CMusicHandler.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+#include "../gui/CAnimation.h"
+#include "../gui/CGuiHandler.h"
+#include "../windows/InfoWindows.h"
+#include "../../lib/CConfigHandler.h"
+
+/*
+ * Buttons.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
+ *
+ */
+
+ClickableArea::ClickableArea(CIntObject * object, CFunctionList<void()> callback):
+	callback(callback),
+	area(nullptr)
+{
+	if (object)
+		pos = object->pos;
+	setArea(object);
+}
+
+void ClickableArea::addCallback(std::function<void()> callback)
+{
+	this->callback += callback;
+}
+
+void ClickableArea::setArea(CIntObject * object)
+{
+	delete area;
+	addChild(area);
+	pos.w = object->pos.w;
+	pos.h = object->pos.h;
+}
+
+void ClickableArea::onClick()
+{
+	callback();
+}
+
+void ClickableArea::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+		onClick();
+}
+
+void CButton::update()
+{
+	if (overlay)
+	{
+		if (state == PRESSED)
+			overlay->moveTo(overlay->pos.centerIn(pos).topLeft() + Point(1,1));
+		else
+			overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
+	}
+
+	int newPos = stateToIndex[int(state)];
+	if (newPos < 0)
+		newPos = 0;
+
+	if (state == HIGHLIGHTED && image->size() < 4)
+		newPos = image->size()-1;
+	image->setFrame(newPos);
+
+	if (active)
+		redraw();
+}
+
+void CButton::addCallback(std::function<void()> callback)
+{
+	this->callback += callback;
+}
+
+void CButton::addTextOverlay( const std::string &Text, EFonts font, SDL_Color color)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	addOverlay(new CLabel(pos.w/2, pos.h/2, font, CENTER, color, Text));
+	update();
+}
+
+void CButton::addOverlay(CIntObject *newOverlay)
+{
+	delete overlay;
+	overlay = newOverlay;
+	addChild(newOverlay);
+	overlay->moveTo(overlay->pos.centerIn(pos).topLeft());
+	update();
+}
+
+void CButton::addImage(std::string filename)
+{
+	imageNames.push_back(filename);
+}
+
+void CButton::addHoverText(ButtonState state, std::string text)
+{
+	hoverTexts[state] = text;
+}
+
+void CButton::setImageOrder(int state1, int state2, int state3, int state4)
+{
+	stateToIndex[0] = state1;
+	stateToIndex[1] = state2;
+	stateToIndex[2] = state3;
+	stateToIndex[3] = state4;
+	update();
+}
+
+void CButton::setState(ButtonState newState)
+{
+	if (state == newState)
+		return;
+	state = newState;
+	update();
+}
+
+CButton::ButtonState CButton::getState()
+{
+	return state;
+}
+
+bool CButton::isBlocked()
+{
+	return state == BLOCKED;
+}
+
+bool CButton::isHighlighted()
+{
+	return state == HIGHLIGHTED;
+}
+
+void CButton::block(bool on)
+{
+	setState(on?BLOCKED:NORMAL);
+}
+
+void CButton::onButtonClicked()
+{
+	// debug logging to figure out pressed button (and as result - player actions) in case of crash
+	logAnim->traceStream() << "Button clicked at " << pos.x << "x" << pos.y << ", " << callback.funcs.size() << " functions";
+	CIntObject * parent = this->parent;
+	std::string prefix = "Parent is";
+	while (parent)
+	{
+		logAnim->traceStream() << prefix << typeid(*parent).name() << " at " << parent->pos.x << "x" << parent->pos.y;
+		parent = parent->parent;
+		prefix = '\t' + prefix;
+	}
+	callback();
+}
+
+void CButton::clickLeft(tribool down, bool previousState)
+{
+	if(isBlocked())
+		return;
+
+	if (down)
+	{
+		if (!soundDisabled)
+			CCS->soundh->playSound(soundBase::button);
+		setState(PRESSED);
+	}
+	else if(hoverable && hovered)
+		setState(HIGHLIGHTED);
+	else
+		setState(NORMAL);
+
+	if (actOnDown && down)
+	{
+		onButtonClicked();
+	}
+	else if (!actOnDown && previousState && (down==false))
+	{
+		onButtonClicked();
+	}
+}
+
+void CButton::clickRight(tribool down, bool previousState)
+{
+	if(down && helpBox.size()) //there is no point to show window with nothing inside...
+		CRClickPopup::createAndPush(helpBox);
+}
+
+void CButton::hover (bool on)
+{
+	if(hoverable && !isBlocked())
+	{
+		if(on)
+			setState(HIGHLIGHTED);
+		else
+			setState(NORMAL);
+	}
+
+	/*if(pressedL && on) // WTF is this? When this is used?
+		setState(PRESSED);*/
+
+	std::string name = hoverTexts[getState()].empty()
+		? hoverTexts[getState()]
+		: hoverTexts[0];
+
+	if(!name.empty() && !isBlocked()) //if there is no name, there is nohing to display also
+	{
+		if (LOCPLINT && LOCPLINT->battleInt) //for battle buttons
+		{
+			if(on && LOCPLINT->battleInt->console->alterTxt == "")
+			{
+				LOCPLINT->battleInt->console->alterTxt = name;
+				LOCPLINT->battleInt->console->whoSetAlter = 1;
+			}
+			else if (LOCPLINT->battleInt->console->alterTxt == name)
+			{
+				LOCPLINT->battleInt->console->alterTxt = "";
+				LOCPLINT->battleInt->console->whoSetAlter = 0;
+			}
+		}
+		else if(GH.statusbar) //for other buttons
+		{
+			if (on)
+				GH.statusbar->setText(name);
+			else if ( GH.statusbar->getText()==(name) )
+				GH.statusbar->clear();
+		}
+	}
+}
+
+CButton::CButton(Point position, const std::string &defName, const std::pair<std::string, std::string> &help, CFunctionList<void()> Callback, int key, bool playerColoredButton):
+    CKeyShortcut(key),
+    callback(Callback)
+{
+	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
+
+	stateToIndex[0] = 0;
+	stateToIndex[1] = 1;
+	stateToIndex[2] = 2;
+	stateToIndex[3] = 3;
+
+	state=NORMAL;
+	image = nullptr;
+	overlay = nullptr;
+
+	currentImage = -1;
+	hoverable = actOnDown = soundDisabled = false;
+	hoverTexts[0] = help.first;
+	helpBox=help.second;
+
+	pos.x += position.x;
+	pos.y += position.y;
+
+	if (!defName.empty())
+	{
+		imageNames.push_back(defName);
+		setIndex(0, playerColoredButton);
+	}
+}
+
+void CButton::setIndex(size_t index, bool playerColoredButton)
+{
+	if (index == currentImage || index>=imageNames.size())
+		return;
+	currentImage = index;
+	setImage(new CAnimation(imageNames[index]), playerColoredButton);
+}
+
+void CButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	image = new CAnimImage(anim, getState(), 0, 0, 0, animFlags);
+	if (playerColoredButton)
+		image->playerColored(LOCPLINT->playerID);
+	pos = image->pos;
+}
+
+void CButton::setPlayerColor(PlayerColor player)
+{
+	if (image)
+		image->playerColored(player);
+}
+
+void CButton::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+	
+	#ifdef VCMI_SDL1
+	if (borderColor && borderColor->unused == 0)
+		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor->r, borderColor->g, borderColor->b));
+	#else
+	if (borderColor && borderColor->a == 0)
+		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor->r, borderColor->g, borderColor->b));
+	#endif // 0
+}
+
+std::pair<std::string, std::string> CButton::tooltip()
+{
+	return std::pair<std::string, std::string>();
+}
+
+std::pair<std::string, std::string> CButton::tooltip(const JsonNode & localizedTexts)
+{
+	return std::make_pair(localizedTexts["label"].String(), localizedTexts["help"].String());
+}
+
+std::pair<std::string, std::string> CButton::tooltip(const std::string & hover, const std::string & help)
+{
+	return std::make_pair(hover, help);
+}
+
+CToggleBase::CToggleBase(CFunctionList<void (bool)> callback):
+    callback(callback),
+    selected(false),
+    allowDeselection(true)
+{
+}
+
+CToggleBase::~CToggleBase()
+{
+}
+
+void CToggleBase::doSelect(bool on)
+{
+	// for overrides
+}
+
+void CToggleBase::setSelected(bool on)
+{
+	bool changed = (on != selected);
+	selected = on;
+	doSelect(on);
+	if (changed)
+		callback(on);
+}
+
+bool CToggleBase::canActivate()
+{
+	if (selected && !allowDeselection)
+		return false;
+	return true;
+}
+
+void CToggleBase::addCallback(std::function<void(bool)> function)
+{
+	callback.add(function);
+}
+
+CToggleButton::CToggleButton(Point position, const std::string &defName, const std::pair<std::string, std::string> &help,
+                             CFunctionList<void(bool)> callback, int key, bool playerColoredButton):
+  CButton(position, defName, help, 0, key, playerColoredButton),
+  CToggleBase(callback)
+{
+	allowDeselection = true;
+}
+
+void CToggleButton::doSelect(bool on)
+{
+	if (on)
+	{
+		setState(HIGHLIGHTED);
+	}
+	else
+	{
+		setState(NORMAL);
+	}
+}
+
+void CToggleButton::clickLeft(tribool down, bool previousState)
+{
+	// force refresh
+	hover(false);
+	hover(true);
+
+	if(isBlocked())
+		return;
+
+	if (down && canActivate())
+	{
+		CCS->soundh->playSound(soundBase::button);
+		setState(PRESSED);
+	}
+
+	if(previousState)//mouse up
+	{
+		if(down == false && getState() == PRESSED && canActivate())
+		{
+			onButtonClicked();
+			setSelected(!selected);
+		}
+		else
+			doSelect(selected); // restore
+	}
+}
+
+void CToggleGroup::addCallback(std::function<void(int)> callback)
+{
+	onChange += callback;
+}
+
+void CToggleGroup::addToggle(int identifier, CToggleBase* bt)
+{
+	if (auto intObj = dynamic_cast<CIntObject*>(bt)) // hack-ish workagound to avoid diamond problem with inheritance
+	{
+		if (intObj->parent)
+			intObj->parent->removeChild(intObj);
+		addChild(intObj);
+	}
+
+	bt->addCallback([=] (bool on) { if (on) selectionChanged(identifier);});
+	bt->allowDeselection = false;
+
+	assert(buttons[identifier] == nullptr);
+	buttons[identifier] = bt;
+}
+
+CToggleGroup::CToggleGroup(const CFunctionList<void(int)> &OnChange, bool musicLikeButtons)
+: onChange(OnChange), musicLike(musicLikeButtons)
+{}
+
+void CToggleGroup::setSelected(int id)
+{
+	selectionChanged(id);
+}
+
+void CToggleGroup::selectionChanged(int to)
+{
+	if (to == selectedID)
+		return;
+
+	int oldSelection = selectedID;
+	selectedID = to;
+
+	if (buttons.count(oldSelection))
+		buttons[oldSelection]->setSelected(false);
+
+	if (buttons.count(to))
+		buttons[to]->setSelected(true);
+
+	onChange(to);
+	if (parent)
+		parent->redraw();
+}
+
+void CToggleGroup::show(SDL_Surface * to)
+{
+	if (musicLike)
+	{
+		if (auto intObj = dynamic_cast<CIntObject*>(buttons[selectedID])) // hack-ish workagound to avoid diamond problem with inheritance
+			intObj->show(to);
+	}
+	else
+		CIntObject::show(to);
+}
+
+void CToggleGroup::showAll(SDL_Surface * to)
+{
+	if (musicLike)
+	{
+		if (auto intObj = dynamic_cast<CIntObject*>(buttons[selectedID])) // hack-ish workagound to avoid diamond problem with inheritance
+			intObj->showAll(to);
+	}
+	else
+		CIntObject::showAll(to);
+}
+
+void CSlider::sliderClicked()
+{
+	if(!(active & MOVE))
+		addUsedEvents(MOVE);
+}
+
+void CSlider::mouseMoved (const SDL_MouseMotionEvent & sEvent)
+{
+	double v = 0;
+	if(horizontal)
+	{
+		if(	std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2+40  ||  std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2  )
+			return;
+		v = sEvent.x - pos.x - 24;
+		v *= positions;
+		v /= (pos.w - 48);
+	}
+	else
+	{
+		if(std::abs(sEvent.x-(pos.x+pos.w/2)) > pos.w/2+40  ||  std::abs(sEvent.y-(pos.y+pos.h/2)) > pos.h/2  )
+			return;
+		v = sEvent.y - pos.y - 24;
+		v *= positions;
+		v /= (pos.h - 48);
+	}
+	v += 0.5;
+	if(v!=value)
+	{
+		moveTo(v);
+	}
+}
+
+void CSlider::setScrollStep(int to)
+{
+	scrollStep = to;
+}
+
+int CSlider::getAmount()
+{
+	return amount;
+}
+
+int CSlider::getValue()
+{
+	return value;
+}
+
+void CSlider::moveLeft()
+{
+	moveTo(value-1);
+}
+
+void CSlider::moveRight()
+{
+	moveTo(value+1);
+}
+
+void CSlider::moveBy(int amount)
+{
+	moveTo(value + amount);
+}
+
+void CSlider::updateSliderPos()
+{
+	if(horizontal)
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(value) / positions;
+			part*=(pos.w-48);
+			int newPos = part + pos.x + 16 - slider->pos.x;
+			slider->moveBy(Point(newPos, 0));
+		}
+		else
+			slider->moveTo(Point(pos.x+16, pos.y));
+	}
+	else
+	{
+		if(positions)
+		{
+			double part = static_cast<double>(value) / positions;
+			part*=(pos.h-48);
+			int newPos = part + pos.y + 16 - slider->pos.y;
+			slider->moveBy(Point(0, newPos));
+		}
+		else
+			slider->moveTo(Point(pos.x, pos.y+16));
+	}
+}
+
+void CSlider::moveTo(int to)
+{
+	vstd::amax(to, 0);
+	vstd::amin(to, positions);
+
+	//same, old position?
+	if(value == to)
+		return;
+	value = to;
+
+	updateSliderPos();
+
+	moved(to);
+}
+
+void CSlider::clickLeft(tribool down, bool previousState)
+{
+	if(down && !slider->isBlocked())
+	{
+		double pw = 0;
+		double rw = 0;
+		if(horizontal)
+		{
+			pw = GH.current->motion.x-pos.x-25;
+			rw = pw / static_cast<double>(pos.w - 48);
+		}
+		else
+		{
+			pw = GH.current->motion.y-pos.y-24;
+			rw = pw / (pos.h-48);
+		}
+		if(pw < -8  ||  pw > (horizontal ? pos.w : pos.h) - 40)
+			return;
+		// 		if (rw>1) return;
+		// 		if (rw<0) return;
+		slider->clickLeft(true, slider->pressedL);
+		moveTo(rw * positions  +  0.5);
+		return;
+	}
+	if(active & MOVE)
+		removeUsedEvents(MOVE);
+}
+
+CSlider::~CSlider()
+{
+
+}
+
+CSlider::CSlider(Point position, int totalw, std::function<void(int)> Moved, int Capacity, int Amount, int Value, bool Horizontal, CSlider::EStyle style):
+    capacity(Capacity),
+    horizontal(Horizontal),
+    amount(Amount),
+    value(Value),
+    scrollStep(1),
+    moved(Moved)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	setAmount(amount);
+
+	addUsedEvents(LCLICK | KEYBOARD | WHEEL);
+	strongInterest = true;
+
+	pos.x += position.x;
+	pos.y += position.y;
+
+	if(style == BROWN)
+	{
+		std::string name = horizontal?"IGPCRDIV.DEF":"OVBUTN2.DEF";
+		//NOTE: this images do not have "blocked" frames. They should be implemented somehow (e.g. palette transform or something...)
+
+		left =   new CButton(Point(), name, CButton::tooltip());
+		right =  new CButton(Point(), name, CButton::tooltip());
+		slider = new CButton(Point(), name, CButton::tooltip());
+
+		left->setImageOrder(0, 1, 1, 1);
+		right->setImageOrder(2, 3, 3, 3);
+		slider->setImageOrder(4, 4, 4, 4);
+	}
+	else
+	{
+		left = new CButton(Point(), horizontal ? "SCNRBLF.DEF" : "SCNRBUP.DEF", CButton::tooltip());
+		right = new CButton(Point(), horizontal ? "SCNRBRT.DEF" : "SCNRBDN.DEF", CButton::tooltip());
+		slider = new CButton(Point(), "SCNRBSL.DEF", CButton::tooltip());
+	}
+	slider->actOnDown = true;
+	slider->soundDisabled = true;
+	left->soundDisabled = true;
+	right->soundDisabled = true;
+
+	if (horizontal)
+		right->moveBy(Point(totalw - right->pos.w, 0));
+	else
+		right->moveBy(Point(0, totalw - right->pos.h));
+
+	left->addCallback(boost::bind(&CSlider::moveLeft,this));
+	right->addCallback(boost::bind(&CSlider::moveRight,this));
+	slider->addCallback(boost::bind(&CSlider::sliderClicked,this));
+
+	if(horizontal)
+	{
+		pos.h = slider->pos.h;
+		pos.w = totalw;
+	}
+	else
+	{
+		pos.w = slider->pos.w;
+		pos.h = totalw;
+	}
+
+	updateSliderPos();
+}
+
+void CSlider::block( bool on )
+{
+	left->block(on);
+	right->block(on);
+	slider->block(on);
+}
+
+void CSlider::setAmount( int to )
+{
+	amount = to;
+	positions = to - capacity;
+	vstd::amax(positions, 0);
+}
+
+void CSlider::showAll(SDL_Surface * to)
+{
+	CSDL_Ext::fillRectBlack(to, &pos);
+	CIntObject::showAll(to);
+}
+
+void CSlider::wheelScrolled(bool down, bool in)
+{
+	moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
+}
+
+void CSlider::keyPressed(const SDL_KeyboardEvent & key)
+{
+	if(key.state != SDL_PRESSED) return;
+
+	int moveDest = 0;
+	switch(key.keysym.sym)
+	{
+	case SDLK_UP:
+	case SDLK_LEFT:
+		moveDest = value - scrollStep;
+		break;
+	case SDLK_DOWN:
+	case SDLK_RIGHT:
+		moveDest = value + scrollStep;
+		break;
+	case SDLK_PAGEUP:
+		moveDest = value - capacity + scrollStep;
+		break;
+	case SDLK_PAGEDOWN:
+		moveDest = value + capacity - scrollStep;
+		break;
+	case SDLK_HOME:
+		moveDest = 0;
+		break;
+	case SDLK_END:
+		moveDest = amount - capacity;
+		break;
+	default:
+		return;
+	}
+
+	moveTo(moveDest);
+}
+
+void CSlider::moveToMax()
+{
+	moveTo(amount);
+}

+ 261 - 0
client/widgets/Buttons.h

@@ -0,0 +1,261 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+
+#include "../../lib/FunctionList.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+namespace config
+{
+	struct ButtonInfo;
+}
+
+/*
+ * Buttons.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 ClickableArea : public CIntObject //TODO: derive from LRCLickableArea? Or somehow use its right-click/hover data?
+{
+	CFunctionList<void()> callback;
+
+	CIntObject * area;
+
+protected:
+	void onClick();
+
+public:
+	ClickableArea(CIntObject * object, CFunctionList<void()> callback);
+
+	void addCallback(std::function<void()> callback);
+	void setArea(CIntObject * object);
+
+	void clickLeft(tribool down, bool previousState) override;
+};
+
+/// Typical Heroes 3 button which can be inactive or active and can
+/// hold further information if you right-click it
+class CButton : public CKeyShortcut
+{
+	CFunctionList<void()> callback;
+
+public:
+	enum ButtonState
+	{
+		NORMAL=0,
+		PRESSED=1,
+		BLOCKED=2,
+		HIGHLIGHTED=3
+	};
+private:
+	std::vector<std::string> imageNames;//store list of images that can be used by this button
+	size_t currentImage;
+
+	ButtonState state;//current state of button from enum
+
+	std::array<int, 4> stateToIndex; // mapping of button state to index of frame in animation
+	std::array<std::string, 4> hoverTexts; //texts for statusbar, if empty - first entry will be used
+	std::string helpBox; //for right-click help
+
+	CAnimImage * image; //image for this button
+	CIntObject * overlay;//object-overlay, can be null
+
+protected:
+	void onButtonClicked(); // calls callback
+	void update();//to refresh button after image or text change
+
+	// internal method to change state. Public change can be done only via block()
+	void setState(ButtonState newState);
+	ButtonState getState();
+
+public:
+	bool actOnDown,//runs when mouse is pressed down over it, not when up
+		hoverable,//if true, button will be highlighted when hovered (e.g. main menu)
+		soundDisabled;
+
+	// if set, button will have 1-px border around it with this color
+	boost::optional<SDL_Color> borderColor;
+
+	/// adds one more callback to on-click actions
+	void addCallback(std::function<void()> callback);
+
+	/// adds overlay on top of button image. Only one overlay can be active at once
+	void addOverlay(CIntObject * newOverlay);
+	void addTextOverlay(const std::string &Text, EFonts font, SDL_Color color = Colors::WHITE);
+
+	void addImage(std::string filename);
+	void addHoverText(ButtonState state, std::string text);
+
+	void setImageOrder(int state1, int state2, int state3, int state4);
+	void block(bool on);
+
+	/// State modifiers
+	bool isBlocked();
+	bool isHighlighted();
+
+	/// Constructor
+	CButton(Point position, const std::string &defName, const std::pair<std::string, std::string> &help,
+	        CFunctionList<void()> Callback = 0, int key=0, bool playerColoredButton = false );
+
+	/// Appearance modifiers
+	void setIndex(size_t index, bool playerColoredButton=false);
+	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
+	void setPlayerColor(PlayerColor player);
+
+	/// CIntObject overrides
+	void clickRight(tribool down, bool previousState) override;
+	void clickLeft(tribool down, bool previousState) override;
+	void hover (bool on) override;
+	void showAll(SDL_Surface * to) override;
+
+	/// generates tooltip that can be passed into constructor
+	static std::pair<std::string, std::string> tooltip();
+	static std::pair<std::string, std::string> tooltip(const JsonNode & localizedTexts);
+	static std::pair<std::string, std::string> tooltip(const std::string & hover, const std::string & help = "");
+};
+
+class CToggleBase
+{
+	CFunctionList<void(bool)> callback;
+protected:
+
+	bool selected;
+
+	// internal method for overrides
+	virtual void doSelect(bool on);
+
+	// returns true if toggle can change its state
+	bool canActivate();
+
+public:
+	/// if set to false - button can not be deselected normally
+	bool allowDeselection;
+
+	CToggleBase(CFunctionList<void(bool)> callback);
+	virtual ~CToggleBase();
+
+	/// Changes selection to "on", and calls callback
+	void setSelected(bool on);
+
+	void addCallback(std::function<void(bool)> callback);
+};
+
+class ClickableToggle : public ClickableArea, public CToggleBase
+{
+public:
+	ClickableToggle(CIntObject * object, CFunctionList<void()> selectFun, CFunctionList<void()> deselectFun);
+	void clickLeft(tribool down, bool previousState) override;
+};
+
+/// A button which can be selected/deselected, checkbox
+class CToggleButton : public CButton, public CToggleBase
+{
+	void doSelect(bool on) override;
+public:
+	CToggleButton(Point position, const std::string &defName, const std::pair<std::string, std::string> &help,
+	              CFunctionList<void(bool)> Callback = 0, int key=0, bool playerColoredButton = false );
+	void clickLeft(tribool down, bool previousState) override;
+
+	// bring overrides into scope
+	using CButton::addCallback;
+	using CToggleBase::addCallback;
+};
+
+class CToggleGroup : public CIntObject
+{
+	CFunctionList<void(int)> onChange; //called when changing selected button with new button's id
+
+	int selectedID;
+	bool musicLike; //determines the behaviour of this group
+	void selectionChanged(int to);
+public:
+	std::map<int, CToggleBase*> buttons;
+
+	CToggleGroup(const CFunctionList<void(int)> & OnChange, bool musicLikeButtons = false);
+
+	void addCallback(std::function<void(int)> callback);
+
+	/// add one toggle/button into group
+	void addToggle(int index, CToggleBase * button);
+	/// Changes selection to specific value. Will select toggle with this ID, if present
+	void setSelected(int id);
+
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+};
+
+/// A typical slider which can be orientated horizontally/vertically.
+class CSlider : public CIntObject
+{
+	CButton *left, *right, *slider; //if vertical then left=up
+	int capacity;//how many elements can be active at same time (e.g. hero list = 5)
+	int positions; //number of highest position (0 if there is only one)
+	bool horizontal;
+	bool wheelScrolling;
+	bool keyScrolling;
+
+	int amount; //total amount of elements (e.g. hero list = 0-8)
+	int value; //first active element
+	int scrollStep; // how many elements will be scrolled via one click, default = 1
+	CFunctionList<void(int)> moved;
+
+	void updateSliderPos();
+	void sliderClicked();
+
+public:
+	enum EStyle {
+		BROWN,
+		BLUE
+	};
+
+	void block(bool on);
+
+	/// Controls how many items wil be scrolled via one click
+	void setScrollStep(int to);
+
+	/// Value modifiers
+	void moveLeft();
+	void moveRight();
+	void moveTo(int value);
+	void moveBy(int amount);
+	void moveToMax();
+
+	/// Amount modifier
+	void setAmount(int to);
+
+	/// Accessors
+	int getAmount();
+	int getValue();
+
+	void addCallback(std::function<void(int)> callback);
+
+	void keyPressed(const SDL_KeyboardEvent & key);
+	void wheelScrolled(bool down, bool in);
+	void clickLeft(tribool down, bool previousState);
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+	void showAll(SDL_Surface * to);	
+
+	/**
+	 * @param position, coordinates of slider
+	 * @param length, length of slider ribbon, including left/right buttons
+	 * @param Moved, function that will be called whenever slider moves
+	 * @param Capacity, maximal number of visible at once elements
+	 * @param Amount, total amount of elements, including not visible
+	 * @param Value, starting position
+	 */
+	CSlider(Point position, int length, std::function<void(int)> Moved, int Capacity, int Amount,
+		int Value=0, bool Horizontal=true, EStyle style = BROWN);
+	~CSlider();
+};

+ 1017 - 0
client/widgets/CArtifactHolder.cpp

@@ -0,0 +1,1017 @@
+#include "StdInc.h"
+#include "CArtifactHolder.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "Buttons.h"
+#include "CComponent.h"
+
+#include "../windows/CHeroWindow.h"
+#include "../windows/CSpellWindow.h"
+#include "../windows/GUIClasses.h"
+#include "../CPlayerInterface.h"
+#include "../CGameInfo.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CArtHandler.h"
+#include "../../lib/CSpellHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+/*
+ * CArtifactHolder.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
+ *
+ */
+
+CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art):
+    locked(false), picked(false), marked(false), ourArt(Art)
+{
+	pos += position;
+	pos.w = pos.h = 44;
+	createImage();
+}
+
+void CArtPlace::createImage()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	int graphic = 0;
+	if (ourArt)
+		graphic = ourArt->artType->iconIndex;
+	if (locked)
+		graphic = ArtifactID::ART_LOCK;
+
+	image = new CAnimImage("artifact", graphic);
+	if (!ourArt)
+		image->disable();
+
+	selection = new CAnimImage("artifact", ArtifactID::ART_SELECTION);
+	selection->disable();
+}
+
+void CArtPlace::lockSlot(bool on)
+{
+	if (locked == on)
+		return;
+
+	locked = on;
+
+	if (on)
+		image->setFrame(ArtifactID::ART_LOCK);
+	else
+		image->setFrame(ourArt->artType->iconIndex);
+}
+
+void CArtPlace::pickSlot(bool on)
+{
+	if (picked == on)
+		return;
+
+	picked = on;
+	if (on)
+		image->disable();
+	else
+		image->enable();
+}
+
+void CArtPlace::selectSlot(bool on)
+{
+	if (marked == on)
+		return;
+
+	marked = on;
+	if (on)
+		selection->enable();
+	else
+		selection->disable();
+}
+
+void CArtPlace::clickLeft(tribool down, bool previousState)
+{
+	//LRClickableAreaWTextComp::clickLeft(down);
+	bool inBackpack = slotID >= GameConstants::BACKPACK_START,
+		srcInBackpack = ourOwner->commonInfo->src.slotID >= GameConstants::BACKPACK_START,
+		srcInSameHero = ourOwner->commonInfo->src.AOH == ourOwner;
+
+	if(ourOwner->highlightModeCallback && ourArt)
+	{
+		if(down)
+		{
+			if(ourArt->artType->id < 7) //War Machine or Spellbook
+			{
+				LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[21]); //This item can't be traded.
+			}
+			else
+			{
+				ourOwner->unmarkSlots(false);
+				selectSlot(true);
+				ourOwner->highlightModeCallback(this);
+			}
+		}
+		return;
+	}
+
+	// If clicked on spellbook, open it only if no artifact is held at the moment.
+	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
+	{
+		if(ourArt->artType->id == 0)
+		{
+			auto   spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt);
+			GH.pushInt(spellWindow);
+		}
+	}
+
+	if (!down && previousState)
+	{
+		if(ourArt && ourArt->artType->id == 0) //spellbook
+			return; //this is handled separately
+
+		if(!ourOwner->commonInfo->src.AOH) //nothing has been clicked
+		{
+			if(ourArt  //to prevent selecting empty slots (bugfix to what GrayFace reported)
+				&&  ourOwner->curHero->tempOwner == LOCPLINT->playerID)//can't take art from another player
+			{
+				if(ourArt->artType->id == 3) //catapult cannot be highlighted
+				{
+					std::vector<CComponent *> catapult(1, new CComponent(CComponent::artifact, 3, 0));
+					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped.
+					return;
+				}
+				select();
+			}
+		}
+		else if(ourArt == ourOwner->commonInfo->src.art) //restore previously picked artifact
+		{
+			deselect();
+		}
+		else //perform artifact transition
+		{
+			if(inBackpack) // Backpack destination.
+			{
+				if(srcInBackpack && slotID == ourOwner->commonInfo->src.slotID + 1) //next slot (our is not visible, so visually same as "old" place) to the art -> make nothing, return artifact to slot
+				{
+					deselect();
+				}
+				else
+				{
+					const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
+
+					switch(cur->id)
+					{
+					case ArtifactID::CATAPULT:
+						//should not happen, catapult cannot be selected
+						assert(cur->id != ArtifactID::CATAPULT);
+						break;
+					case ArtifactID::BALLISTA: case ArtifactID::AMMO_CART: case ArtifactID::FIRST_AID_TENT: //war machines cannot go to backpack
+						LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->Name()));
+						break;
+					default:
+						setMeAsDest();
+						vstd::amin(ourOwner->commonInfo->dst.slotID, ArtifactPosition(
+							ourOwner->curHero->artifactsInBackpack.size() + GameConstants::BACKPACK_START));
+						if(srcInBackpack && srcInSameHero)
+						{
+							if(!ourArt								//cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact
+							  || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted
+								vstd::advance(ourOwner->commonInfo->dst.slotID, -1);
+						}
+						if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst
+							deselect();
+						else
+							ourOwner->realizeCurrentTransaction();
+						break;
+					}
+				}
+			}
+			//check if swap is possible
+			else if (fitsHere(ourOwner->commonInfo->src.art) &&
+				(!ourArt || ourOwner->curHero->tempOwner == LOCPLINT->playerID))
+			{
+				setMeAsDest();
+//
+// 				// Special case when the dest artifact can't be fit into the src slot.
+// 				//CGI->arth->unequipArtifact(ourOwner->curHero->artifWorn, slotID);
+// 				const CArtifactsOfHero* srcAOH = ourOwner->commonInfo->src.AOH;
+// 				ui16 srcSlotID = ourOwner->commonInfo->src.slotID;
+// 				if (ourArt && srcSlotID < 19 && !ourArt->canBePutAt(ArtifactLocation(srcAOH->curHero, srcSlotID)))
+// 				{
+// 					// Put dest artifact into owner's backpack.
+// 					ourOwner->commonInfo->src.AOH = ourOwner;
+// 					ourOwner->commonInfo->src.slotID = ourOwner->curHero->artifacts.size() + 19;
+// 				}
+
+				ourOwner->realizeCurrentTransaction();
+			}
+		}
+	}
+}
+
+void CArtPlace::clickRight(tribool down, bool previousState)
+{
+	if(down && ourArt && !locked && text.size() && !picked)  //if there is no description or it's a lock, do nothing ;]
+	{
+		if (slotID < GameConstants::BACKPACK_START)
+		{
+			if(ourOwner->allowedAssembling)
+			{
+				std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
+
+				// If the artifact can be assembled, display dialog.
+				for(const CArtifact *combination : assemblyPossibilities)
+				{
+					LOCPLINT->showArtifactAssemblyDialog(
+						ourArt->artType->id,
+						combination->id,
+						true,
+						boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id),
+						0);
+
+					if(assemblyPossibilities.size() > 2)
+					{
+                        logGlobal->warnStream() << "More than one possibility of assembling... taking only first";
+						break;
+					}
+					return;
+				}
+
+				// Otherwise if the artifact can be diasassembled, display dialog.
+				if(ourArt->canBeDisassembled())
+				{
+					LOCPLINT->showArtifactAssemblyDialog(
+						ourArt->artType->id,
+						0,
+						false,
+						boost::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, false, ArtifactID()),
+						0);
+					return;
+				}
+			}
+		}
+
+		// Lastly just show the artifact description.
+		LRClickableAreaWTextComp::clickRight(down, previousState);
+	}
+}
+
+/**
+ * Selects artifact slot so that the containing artifact looks like it's picked up.
+ */
+void CArtPlace::select ()
+{
+	if (locked)
+		return;
+
+	selectSlot(true);
+	pickSlot(true);
+	if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear
+	{
+		for(int i = 0; i < GameConstants::BACKPACK_START; i++)
+		{
+			CArtPlace *ap = ourOwner->getArtPlace(i);
+			ap->pickSlot(ourArt->isPart(ap->ourArt));
+		}
+	}
+
+	//int backpackCorrection = -(slotID - Arts::BACKPACK_START < ourOwner->backpackPos);
+
+	CCS->curh->dragAndDropCursor(new CAnimImage("artifact", ourArt->artType->iconIndex));
+	ourOwner->commonInfo->src.setTo(this, false);
+	ourOwner->markPossibleSlots(ourArt);
+
+	if(slotID >= GameConstants::BACKPACK_START)
+		ourOwner->scrollBackpack(0); //will update slots
+
+	ourOwner->updateParentWindow();
+	ourOwner->safeRedraw();
+}
+
+/**
+ * Deselects the artifact slot. FIXME: Not used. Maybe it should?
+ */
+void CArtPlace::deselect ()
+{
+	pickSlot(false);
+	if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks
+	{
+		for(int i = 0; i < GameConstants::BACKPACK_START; i++)
+			ourOwner->getArtPlace(i)->pickSlot(false);
+	}
+
+	CCS->curh->dragAndDropCursor(nullptr);
+	ourOwner->unmarkSlots();
+	ourOwner->commonInfo->src.clear();
+	if(slotID >= GameConstants::BACKPACK_START)
+		ourOwner->scrollBackpack(0); //will update slots
+
+
+	ourOwner->updateParentWindow();
+	ourOwner->safeRedraw();
+}
+
+void CArtPlace::showAll(SDL_Surface * to)
+{
+	if (ourArt && !picked && ourArt == ourOwner->curHero->getArt(slotID, false)) //last condition is needed for disassembling -> artifact may be gone, but we don't know yet TODO: real, nice solution
+	{
+		CIntObject::showAll(to);
+	}
+
+	if(marked && active)
+	{
+		// Draw vertical bars.
+		for (int i = 0; i < pos.h; ++i)
+		{
+			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x,             pos.y + i, 240, 220, 120);
+			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + pos.w - 1, pos.y + i, 240, 220, 120);
+		}
+
+		// Draw horizontal bars.
+		for (int i = 0; i < pos.w; ++i)
+		{
+			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y,             240, 220, 120);
+			CSDL_Ext::SDL_PutPixelWithoutRefresh(to, pos.x + i, pos.y + pos.h - 1, 240, 220, 120);
+		}
+	}
+}
+
+bool CArtPlace::fitsHere(const CArtifactInstance * art) const
+{
+	// You can place 'no artifact' anywhere.
+	if(!art)
+		return true;
+
+	// Anything can but War Machines can be placed in backpack.
+	if (slotID >= GameConstants::BACKPACK_START)
+		return !CGI->arth->isBigArtifact(art->artType->id);
+
+	return art->canBePutAt(ArtifactLocation(ourOwner->curHero, slotID), true);
+}
+
+void CArtPlace::setMeAsDest(bool backpackAsVoid /*= true*/)
+{
+	ourOwner->commonInfo->dst.setTo(this, backpackAsVoid);
+}
+
+void CArtPlace::setArtifact(const CArtifactInstance *art)
+{
+	baseType = -1; //by default we don't store any component
+	ourArt = art;
+	if(!art)
+	{
+		image->disable();
+		text = std::string();
+		hoverText = CGI->generaltexth->allTexts[507];
+	}
+	else
+	{
+		image->enable();
+		image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
+
+		std::string artDesc = ourArt->artType->Description();
+		if (vstd::contains (artDesc, '{'))
+			text = artDesc;
+		else
+			text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style
+
+		if(art->artType->id == 1) //spell scroll
+		{
+			// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
+			// so we want to replace text in [...] with a spell name
+			// however other language versions don't have name placeholder at all, so we have to be careful
+			int spellID = art->getGivenSpellID();
+			size_t nameStart = text.find_first_of('[');
+			size_t nameEnd = text.find_first_of(']', nameStart);
+			if(spellID >= 0)
+			{
+				if(nameStart != std::string::npos  &&  nameEnd != std::string::npos)
+					text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
+
+				//add spell component info (used to provide a pic in r-click popup)
+				baseType = CComponent::spell;
+				type = spellID;
+				bonusValue = 0;
+			}
+		}
+		else
+		{
+			baseType = CComponent::artifact;
+			type = art->artType->id;
+			bonusValue = 0;
+		}
+		if (art->artType->constituents) //display info about components of combined artifact
+		{
+			//TODO
+		}
+		else if (art->artType->constituentOf.size()) //display info about set
+		{
+			std::string artList;
+			auto combinedArt = art->artType->constituentOf[0];
+			text += "\n\n";
+			text += "{" + combinedArt->Name() + "}";
+			int wornArtifacts = 0;
+			for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
+			{
+				artList += "\n" + a->Name();
+				if (ourOwner->curHero->hasArt(a->id, true))
+					wornArtifacts++;
+			}
+			text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " +
+				boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
+			//TODO: fancy colors and fonts for this text
+		}
+
+		if (locked) // Locks should appear as empty.
+			hoverText = CGI->generaltexth->allTexts[507];
+		else
+			hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
+	}
+}
+
+void CArtifactsOfHero::SCommonPart::reset()
+{
+	src.clear();
+	dst.clear();
+	CCS->curh->dragAndDropCursor(nullptr);
+}
+
+void CArtifactsOfHero::setHero(const CGHeroInstance * hero)
+{
+// 	// An update is made, rather than initialization.
+// 	if (curHero && curHero->id == hero->id)
+// 	{
+// 		if(curHero != hero)
+// 		{
+// 			//delete	curHero;
+// 			curHero = hero; //was: creating a copy
+// 		}
+//
+// 		// Compensate backpack pos if an artifact was insertad before it.
+// 		if (commonInfo->dst.slotID >= 19 && commonInfo->destAOH == this
+// 			&& commonInfo->dst.slotID - 19 < backpackPos)
+// 		{
+// 			backpackPos++;
+// 		}
+//
+// 		if (updateState && commonInfo->srcAOH == this)
+// 		{
+// 			// A swap was made, make the replaced artifact the current selected.
+// 			if (commonInfo->dst.slotID < 19 && commonInfo->destArtifact)
+// 			{
+// // 				// Temporarily remove artifact from hero.
+// // 				if (commonInfo->srcSlotID < 19)
+// // 					CGI->arth->unequipArtifact(curHero->artifWorn, commonInfo->srcSlotID);
+// // 				else
+// // 					curHero->artifacts.erase(curHero->artifacts.begin() + (commonInfo->srcSlotID - 19));
+//
+// 				updateParentWindow(); //TODO: evil! but does the thing
+//
+// 				// Source <- Dest
+// 				commonInfo->srcArtifact = commonInfo->destArtifact;
+//
+// 				// Reset destination parameters.
+// 				commonInfo->dst.clear();
+//
+// 				CCS->curh->dragAndDropCursor(graphics->artDefs->ourImages[commonInfo->srcArtifact->id].bitmap);
+// 				markPossibleSlots(commonInfo->srcArtifact);
+// 			}
+// 			else if (commonInfo->destAOH != nullptr)
+// 			{
+// 				// Reset all parameters.
+// 				commonInfo->reset();
+// 				unmarkSlots();
+// 			}
+// 		}
+// 	}
+// 	else
+// 	{
+// 		commonInfo->reset();
+// 	}
+//
+// 	if(hero != curHero)
+// 	{
+// // 		delete curHero;
+// 		// 		curHero = new CGHeroInstance(*hero);
+// 		curHero = hero; //was: creating a copy
+// 	}
+
+	curHero = hero;
+	if (curHero->artifactsInBackpack.size() > 0)
+		backpackPos %= curHero->artifactsInBackpack.size();
+	else
+		backpackPos = 0;
+
+	// Fill the slots for worn artifacts and backpack.
+	for (int g = 0; g < artWorn.size() ; g++)
+		setSlotData(artWorn[g], ArtifactPosition(g));
+	scrollBackpack(0);
+}
+
+void CArtifactsOfHero::dispose()
+{
+	//vstd::clear_pointer(curHero);
+	//unmarkSlots(false);
+	CCS->curh->dragAndDropCursor(nullptr);
+}
+
+void CArtifactsOfHero::scrollBackpack(int dir)
+{
+	int artsInBackpack = curHero->artifactsInBackpack.size();
+	backpackPos += dir;
+	if(backpackPos < 0)// No guarantee of modulus behavior with negative operands -> we keep it positive
+		backpackPos += artsInBackpack;
+
+	if(artsInBackpack)
+		backpackPos %= artsInBackpack;
+
+	std::multiset<const CArtifactInstance *> toOmit = artifactsOnAltar;
+	if(commonInfo->src.art) //if we picked an art from backapck, its slot has to be omitted
+		toOmit.insert(commonInfo->src.art);
+
+	int omitedSoFar = 0;
+
+	//set new data
+	size_t s = 0;
+	for( ; s < artsInBackpack; ++s)
+	{
+
+		if (s < artsInBackpack)
+		{
+			auto slotID = ArtifactPosition(GameConstants::BACKPACK_START + (s + backpackPos)%artsInBackpack);
+			const CArtifactInstance *art = curHero->getArt(slotID);
+			assert(art);
+			if(!vstd::contains(toOmit, art))
+			{
+				if(s - omitedSoFar < backpack.size())
+					setSlotData(backpack[s-omitedSoFar], slotID);
+			}
+			else
+			{
+				toOmit -= art;
+				omitedSoFar++;
+				continue;
+			}
+		}
+	}
+	for( ; s - omitedSoFar < backpack.size(); s++)
+		eraseSlotData(backpack[s-omitedSoFar], ArtifactPosition(GameConstants::BACKPACK_START + s));
+
+	//in artifact merchant selling artifacts we may have highlight on one of backpack artifacts -> market needs update, cause artifact under highlight changed
+	if(highlightModeCallback)
+	{
+		for(auto & elem : backpack)
+		{
+			if(elem->marked)
+			{
+				highlightModeCallback(elem);
+				break;
+			}
+		}
+	}
+
+	//blocking scrolling if there is not enough artifacts to scroll
+	bool scrollingPossible = artsInBackpack - omitedSoFar > backpack.size();
+	leftArtRoll->block(!scrollingPossible);
+	rightArtRoll->block(!scrollingPossible);
+
+	safeRedraw();
+
+}
+
+/**
+ * Marks possible slots where a given artifact can be placed, except backpack.
+ *
+ * @param art Artifact checked against.
+ */
+void CArtifactsOfHero::markPossibleSlots(const CArtifactInstance* art)
+{
+	for(CArtifactsOfHero *aoh : commonInfo->participants)
+		for(CArtPlace *place : aoh->artWorn)
+			place->selectSlot(art->canBePutAt(ArtifactLocation(aoh->curHero, place->slotID), true));
+
+	safeRedraw();
+}
+
+/**
+ * Unamarks all slots.
+ */
+void CArtifactsOfHero::unmarkSlots(bool withRedraw /*= true*/)
+{
+	if(commonInfo)
+		for(CArtifactsOfHero *aoh : commonInfo->participants)
+			aoh->unmarkLocalSlots(false);
+	else
+		unmarkLocalSlots(false);\
+
+	if(withRedraw)
+		safeRedraw();
+}
+
+void CArtifactsOfHero::unmarkLocalSlots(bool withRedraw /*= true*/)
+{
+	for(CArtPlace *place : artWorn)
+		place->selectSlot(false);
+	for(CArtPlace *place : backpack)
+		place->selectSlot(false);
+
+	if(withRedraw)
+		safeRedraw();
+}
+
+/**
+ * Assigns an artifacts to an artifact place depending on it's new slot ID.
+ */
+void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID)
+{
+	if(!artPlace && slotID >= GameConstants::BACKPACK_START) //spurious call from artifactMoved in attempt to update hidden backpack slot
+	{
+		return;
+	}
+
+	artPlace->pickSlot(false);
+	artPlace->slotID = slotID;
+
+	if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
+	{
+		artPlace->setArtifact(asi->artifact);
+		artPlace->lockSlot(asi->locked);
+	}
+	else
+		artPlace->setArtifact(nullptr);
+}
+
+/**
+ * Makes given artifact slot appear as empty with a certain slot ID.
+ */
+void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, ArtifactPosition slotID)
+{
+	artPlace->pickSlot(false);
+	artPlace->slotID = slotID;
+	artPlace->setArtifact(nullptr);
+}
+
+CArtifactsOfHero::CArtifactsOfHero(std::vector<CArtPlace *> ArtWorn, std::vector<CArtPlace *> Backpack,
+	CButton *leftScroll, CButton *rightScroll, bool createCommonPart):
+
+	curHero(nullptr),
+	artWorn(ArtWorn), backpack(Backpack),
+	backpackPos(0), commonInfo(nullptr), updateState(false),
+	leftArtRoll(leftScroll), rightArtRoll(rightScroll),
+	allowedAssembling(true), highlightModeCallback(nullptr)
+{
+	if(createCommonPart)
+	{
+		commonInfo = new CArtifactsOfHero::SCommonPart;
+		commonInfo->participants.insert(this);
+	}
+
+	// Init slots for worn artifacts.
+	for (size_t g = 0; g < artWorn.size() ; g++)
+	{
+		artWorn[g]->ourOwner = this;
+		eraseSlotData(artWorn[g], ArtifactPosition(g));
+	}
+
+	// Init slots for the backpack.
+	for(size_t s=0; s<backpack.size(); ++s)
+	{
+		backpack[s]->ourOwner = this;
+		eraseSlotData(backpack[s], ArtifactPosition(GameConstants::BACKPACK_START + s));
+	}
+
+	leftArtRoll->addCallback(boost::bind(&CArtifactsOfHero::scrollBackpack,this,-1));
+	rightArtRoll->addCallback(boost::bind(&CArtifactsOfHero::scrollBackpack,this,+1));
+}
+
+CArtifactsOfHero::CArtifactsOfHero(const Point& position, bool createCommonPart /*= false*/)
+ : curHero(nullptr), backpackPos(0), commonInfo(nullptr), updateState(false), allowedAssembling(true), highlightModeCallback(nullptr)
+{
+	using namespace boost::assign;
+
+	if(createCommonPart)
+	{
+		commonInfo = new CArtifactsOfHero::SCommonPart;
+		commonInfo->participants.insert(this);
+	}
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	pos += position;
+	artWorn.resize(19);
+
+	std::vector<Point> slotPos;
+	slotPos += Point(509,30),  Point(567,240), Point(509,80),
+	           Point(383,68),  Point(564,183), Point(509,130),
+	           Point(431,68),  Point(610,183), Point(515,295),
+	           Point(383,143), Point(399,194), Point(415,245),
+	           Point(431,296), Point(564,30),  Point(610,30),
+	           Point(610,76),  Point(610,122), Point(610,310),
+	           Point(381,296);
+
+	// Create slots for worn artifacts.
+	for (size_t g = 0; g < GameConstants::BACKPACK_START ; g++)
+	{
+		artWorn[g] = new CArtPlace(slotPos[g]);
+		artWorn[g]->ourOwner = this;
+		eraseSlotData(artWorn[g], ArtifactPosition(g));
+	}
+
+	// Create slots for the backpack.
+	for(size_t s=0; s<5; ++s)
+	{
+		auto   add = new CArtPlace(Point(403 + 46 * s, 365));
+
+		add->ourOwner = this;
+		eraseSlotData(add, ArtifactPosition(GameConstants::BACKPACK_START + s));
+
+		backpack.push_back(add);
+	}
+
+	leftArtRoll =  new CButton(Point(379, 364), "hsbtns3.def", CButton::tooltip(), [&]{ scrollBackpack(-1);}, SDLK_LEFT);
+	rightArtRoll = new CButton(Point(632, 364), "hsbtns5.def", CButton::tooltip(), [&]{ scrollBackpack(+1);}, SDLK_RIGHT);
+}
+
+CArtifactsOfHero::~CArtifactsOfHero()
+{
+	dispose();
+}
+
+void CArtifactsOfHero::updateParentWindow()
+{
+	if (CHeroWindow* chw = dynamic_cast<CHeroWindow*>(GH.topInt()))
+	{
+		if(updateState)
+			chw->curHero = curHero;
+		else
+			chw->update(curHero, true);
+	}
+	else if(CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(GH.topInt()))
+	{
+
+		//use our copy of hero to draw window
+		if(cew->heroInst[0]->id == curHero->id)
+			cew->heroInst[0] = curHero;
+		else
+			cew->heroInst[1] = curHero;
+
+		if(!updateState)
+		{
+			cew->deactivate();
+// 			for(int g=0; g<ARRAY_COUNT(cew->heroInst); ++g)
+// 			{
+// 				if(cew->heroInst[g] == curHero)
+// 				{
+// 					cew->artifs[g]->setHero(curHero);
+// 				}
+// 			}
+
+
+			cew->prepareBackground();
+			cew->redraw();
+			cew->activate();
+		}
+	}
+}
+
+void CArtifactsOfHero::safeRedraw()
+{
+	if (active)
+	{
+		if(parent)
+			parent->redraw();
+		else
+			redraw();
+	}
+}
+
+void CArtifactsOfHero::realizeCurrentTransaction()
+{
+	assert(commonInfo->src.AOH);
+	assert(commonInfo->dst.AOH);
+	LOCPLINT->cb->swapArtifacts(ArtifactLocation(commonInfo->src.AOH->curHero, commonInfo->src.slotID),
+								ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID));
+}
+
+void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
+{
+	bool isCurHeroSrc = src.isHolder(curHero),
+		isCurHeroDst = dst.isHolder(curHero);
+	if(isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
+		updateSlot(src.slot);
+	if(isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START)
+		updateSlot(dst.slot);
+	if(isCurHeroSrc  ||  isCurHeroDst) //we need to update all slots, artifact might be combined and affect more slots
+		updateWornSlots(false);
+
+	if (!src.isHolder(curHero) && !isCurHeroDst)
+		return;
+
+	if(commonInfo->src == src) //artifact was taken from us
+	{
+		assert(commonInfo->dst == dst  //expected movement from slot ot slot
+			||  dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START //artifact moved back to backpack (eg. to make place for art we are moving)
+			|| dst.getHolderArtSet()->bearerType() != ArtBearer::HERO);
+		commonInfo->reset();
+		unmarkSlots();
+	}
+	else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it
+	{
+		assert(dst.slot >= GameConstants::BACKPACK_START);
+		commonInfo->reset();
+
+		CArtPlace *ap = nullptr;
+		for(CArtifactsOfHero *aoh : commonInfo->participants)
+		{
+			if(dst.isHolder(aoh->curHero))
+			{
+				commonInfo->src.AOH = aoh;
+				if((ap = aoh->getArtPlace(dst.slot)))
+					break;
+			}
+		}
+
+		if(ap)
+		{
+			ap->select();
+		}
+		else
+		{
+			commonInfo->src.art = dst.getArt();
+			commonInfo->src.slotID = dst.slot;
+			assert(commonInfo->src.AOH);
+			CCS->curh->dragAndDropCursor(new CAnimImage("artifact", dst.getArt()->artType->iconIndex));
+			markPossibleSlots(dst.getArt());
+		}
+	}
+	else if(src.slot >= GameConstants::BACKPACK_START &&
+	        src.slot <  commonInfo->src.slotID &&
+			src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
+	{
+		//int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
+		vstd::advance(commonInfo->src.slotID, -1);
+		assert(commonInfo->src.valid());
+	}
+	else
+	{
+		//when moving one artifact onto another it leads to two art movements: dst->backapck; src->dst
+		// however after first movement we pick the art from backpack and the second movement coming when
+		// we have a different artifact may look surprising... but it's valid.
+	}
+
+	updateParentWindow();
+ 	int shift = 0;
+// 	if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
+// 		shift++;
+//
+ 	if(src.slot < GameConstants::BACKPACK_START  &&  dst.slot - GameConstants::BACKPACK_START < backpackPos)
+		shift++;
+	if(dst.slot < GameConstants::BACKPACK_START  &&  src.slot - GameConstants::BACKPACK_START < backpackPos)
+ 		shift--;
+
+	if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
+	 || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
+		scrollBackpack(shift); //update backpack slots
+}
+
+void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
+{
+	if(al.isHolder(curHero))
+	{
+		if(al.slot < GameConstants::BACKPACK_START)
+			updateWornSlots(0);
+		else
+			scrollBackpack(0); //update backpack slots
+	}
+}
+
+CArtPlace * CArtifactsOfHero::getArtPlace(int slot)
+{
+	if(slot < GameConstants::BACKPACK_START)
+	{
+		return artWorn[slot];
+	}
+	else
+	{
+		for(CArtPlace *ap : backpack)
+			if(ap->slotID == slot)
+				return ap;
+	}
+
+	return nullptr;
+}
+
+void CArtifactsOfHero::artifactAssembled(const ArtifactLocation &al)
+{
+	if(al.isHolder(curHero))
+		updateWornSlots();
+}
+
+void CArtifactsOfHero::artifactDisassembled(const ArtifactLocation &al)
+{
+	if(al.isHolder(curHero))
+		updateWornSlots();
+}
+
+void CArtifactsOfHero::updateWornSlots(bool redrawParent /*= true*/)
+{
+	for(int i = 0; i < artWorn.size(); i++)
+		updateSlot(ArtifactPosition(i));
+
+
+	if(redrawParent)
+		updateParentWindow();
+}
+
+const CGHeroInstance * CArtifactsOfHero::getHero() const
+{
+	return curHero;
+}
+
+void CArtifactsOfHero::updateSlot(ArtifactPosition slotID)
+{
+	setSlotData(getArtPlace(slotID), slotID);
+}
+
+CArtifactHolder::CArtifactHolder()
+{
+}
+
+void CWindowWithArtifacts::artifactRemoved(const ArtifactLocation &artLoc)
+{
+	for(CArtifactsOfHero *aoh : artSets)
+		aoh->artifactRemoved(artLoc);
+}
+
+void CWindowWithArtifacts::artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)
+{
+	CArtifactsOfHero *destaoh = nullptr;
+	for(CArtifactsOfHero *aoh : artSets)
+	{
+		aoh->artifactMoved(artLoc, destLoc);
+		aoh->redraw();
+		if(destLoc.isHolder(aoh->getHero()))
+			destaoh = aoh;
+	}
+
+	//Make sure the status bar is updated so it does not display old text
+	if(destaoh != nullptr && destaoh->getArtPlace(destLoc.slot) != nullptr)
+	{
+		destaoh->getArtPlace(destLoc.slot)->hover(true);
+	}
+}
+
+void CWindowWithArtifacts::artifactDisassembled(const ArtifactLocation &artLoc)
+{
+	for(CArtifactsOfHero *aoh : artSets)
+		aoh->artifactDisassembled(artLoc);
+}
+
+void CWindowWithArtifacts::artifactAssembled(const ArtifactLocation &artLoc)
+{
+	for(CArtifactsOfHero *aoh : artSets)
+		aoh->artifactAssembled(artLoc);
+}
+
+void CArtifactsOfHero::SCommonPart::Artpos::clear()
+{
+	slotID = ArtifactPosition::PRE_FIRST;
+	AOH = nullptr;
+	art = nullptr;
+}
+
+CArtifactsOfHero::SCommonPart::Artpos::Artpos()
+{
+	clear();
+}
+
+void CArtifactsOfHero::SCommonPart::Artpos::setTo(const CArtPlace *place, bool dontTakeBackpack)
+{
+	slotID = place->slotID;
+	AOH = place->ourOwner;
+
+	if(slotID >= 19 && dontTakeBackpack)
+		art = nullptr;
+	else
+		art = place->ourArt;
+}
+
+bool CArtifactsOfHero::SCommonPart::Artpos::operator==(const ArtifactLocation &al) const
+{
+	if(!AOH)
+		return false;
+	bool ret = al.isHolder(AOH->curHero)  &&  al.slot == slotID;
+
+	//assert(al.getArt() == art);
+	return ret;
+}
+
+bool CArtifactsOfHero::SCommonPart::Artpos::valid()
+{
+	assert(AOH && art);
+	return art == AOH->curHero->getArt(slotID);
+}

+ 145 - 0
client/widgets/CArtifactHolder.h

@@ -0,0 +1,145 @@
+#pragma once
+
+//#include "CComponent.h"
+#include "MiscWidgets.h"
+
+/*
+ * CArtifactHolder.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 CArtifactsOfHero;
+class CAnimImage;
+class CButton;
+
+struct ArtifactLocation;
+
+class CArtifactHolder
+{
+public:
+	CArtifactHolder();
+
+	virtual void artifactRemoved(const ArtifactLocation &artLoc)=0;
+	virtual void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc)=0;
+	virtual void artifactDisassembled(const ArtifactLocation &artLoc)=0;
+	virtual void artifactAssembled(const ArtifactLocation &artLoc)=0;
+};
+
+class CWindowWithArtifacts : public CArtifactHolder
+{
+public:
+	std::vector<CArtifactsOfHero *> artSets;
+
+	void artifactRemoved(const ArtifactLocation &artLoc);
+	void artifactMoved(const ArtifactLocation &artLoc, const ArtifactLocation &destLoc);
+	void artifactDisassembled(const ArtifactLocation &artLoc);
+	void artifactAssembled(const ArtifactLocation &artLoc);
+};
+
+/// Artifacts can be placed there. Gets shown at the hero window
+class CArtPlace: public LRClickableAreaWTextComp
+{
+	CAnimImage *image;
+	CAnimImage *selection;
+
+	void createImage();
+
+public:
+	// consider these members as const - change them only with appropriate methods e.g. lockSlot()
+	bool locked;
+	bool picked;
+	bool marked;
+
+	ArtifactPosition slotID; //Arts::EPOS enum + backpack starting from Arts::BACKPACK_START
+
+	void lockSlot(bool on);
+	void pickSlot(bool on);
+	void selectSlot(bool on);
+
+	CArtifactsOfHero * ourOwner;
+	const CArtifactInstance * ourArt; // should be changed only with setArtifact()
+
+	CArtPlace(Point position, const CArtifactInstance * Art = nullptr); //c-tor
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+	void select ();
+	void deselect ();
+	void showAll(SDL_Surface * to);
+	bool fitsHere (const CArtifactInstance * art) const; //returns true if given artifact can be placed here
+
+	void setMeAsDest(bool backpackAsVoid = true);
+	void setArtifact(const CArtifactInstance *art);
+};
+
+/// Contains artifacts of hero. Distincts which artifacts are worn or backpacked
+class CArtifactsOfHero : public CIntObject
+{
+	const CGHeroInstance * curHero;
+
+	std::vector<CArtPlace *> artWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
+	std::vector<CArtPlace *> backpack; //hero's visible backpack (only 5 elements!)
+	int backpackPos; //number of first art visible in backpack (in hero's vector)
+
+public:
+	struct SCommonPart
+	{
+		struct Artpos
+		{
+			ArtifactPosition slotID;
+			const CArtifactsOfHero *AOH;
+			const CArtifactInstance *art;
+
+			Artpos();
+			void clear();
+			void setTo(const CArtPlace *place, bool dontTakeBackpack);
+			bool valid();
+			bool operator==(const ArtifactLocation &al) const;
+		} src, dst;
+
+		std::set<CArtifactsOfHero *> participants; // Needed to mark slots.
+
+		void reset();
+	} * commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally
+
+	bool updateState; // Whether the commonInfo should be updated on setHero or not.
+
+	CButton * leftArtRoll, * rightArtRoll;
+	bool allowedAssembling;
+	std::multiset<const CArtifactInstance*> artifactsOnAltar; //artifacts id that are technically present in backpack but in GUI are moved to the altar - they'll be omitted in backpack slots
+	std::function<void(CArtPlace*)> highlightModeCallback; //if set, clicking on art place doesn't pick artifact but highlights the slot and calls this function
+
+	void realizeCurrentTransaction(); //calls callback with parameters stored in commonInfo
+	void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
+	void artifactRemoved(const ArtifactLocation &al);
+	void artifactAssembled(const ArtifactLocation &al);
+	void artifactDisassembled(const ArtifactLocation &al);
+	CArtPlace *getArtPlace(int slot);
+
+	void setHero(const CGHeroInstance * hero);
+	const CGHeroInstance *getHero() const;
+	void dispose(); //free resources not needed after closing windows and reset state
+	void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
+
+	void safeRedraw();
+	void markPossibleSlots(const CArtifactInstance* art);
+	void unmarkSlots(bool withRedraw = true); //unmarks slots in all visible AOHs
+	void unmarkLocalSlots(bool withRedraw = true); //unmarks slots in that particular AOH
+	void setSlotData (CArtPlace* artPlace, ArtifactPosition slotID);
+	void updateWornSlots (bool redrawParent = true);
+
+	void updateSlot(ArtifactPosition i);
+	void eraseSlotData (CArtPlace* artPlace, ArtifactPosition slotID);
+
+	CArtifactsOfHero(const Point& position, bool createCommonPart = false);
+	//Alternative constructor, used if custom artifacts positioning required (Kingdom interface)
+	CArtifactsOfHero(std::vector<CArtPlace *> ArtWorn, std::vector<CArtPlace *> Backpack,
+		CButton *leftScroll, CButton *rightScroll, bool createCommonPart = false);
+	~CArtifactsOfHero(); //d-tor
+	void updateParentWindow();
+	friend class CArtPlace;
+};

+ 453 - 0
client/widgets/CComponent.cpp

@@ -0,0 +1,453 @@
+#include "StdInc.h"
+#include "CComponent.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../CMessage.h"
+#include "../CGameInfo.h"
+#include "../widgets/Images.h"
+#include "../windows/CAdvmapInterface.h"
+
+#include "../../lib/CArtHandler.h"
+#include "../../lib/CTownHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/CSpellHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/NetPacksBase.h"
+
+/*
+ * CComponent.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
+ *
+ */
+
+CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize):
+	image(nullptr),
+	perDay(false)
+{
+	addUsedEvents(RCLICK);
+	init(Type, Subtype, Val, imageSize);
+}
+
+CComponent::CComponent(const Component &c):
+	image(nullptr),
+	perDay(false)
+{
+	addUsedEvents(RCLICK);
+
+	if(c.id == Component::RESOURCE && c.when==-1)
+		perDay = true;
+
+	init((Etype)c.id,c.subtype,c.val, large);
+}
+
+void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	compType = Type;
+	subtype = Subtype;
+	val = Val;
+	size = imageSize;
+
+	assert(compType < typeInvalid);
+	assert(size < sizeInvalid);
+
+	setSurface(getFileName()[size], getIndex());
+
+	pos.w = image->pos.w;
+	pos.h = image->pos.h;
+
+	EFonts font = FONT_SMALL;
+	if (imageSize < small)
+		font = FONT_TINY; //other sizes?
+
+	pos.h += 4; //distance between text and image
+
+	std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), font);
+	for(auto & line : textLines)
+	{
+		int height = graphics->fonts[font]->getLineHeight();
+		auto   label = new CLabel(pos.w/2, pos.h + height/2, font, CENTER, Colors::WHITE, line);
+
+		pos.h += height;
+		if (label->pos.w > pos.w)
+		{
+			pos.x -= (label->pos.w - pos.w)/2;
+			pos.w = label->pos.w;
+		}
+	}
+}
+
+const std::vector<std::string> CComponent::getFileName()
+{
+	static const std::string  primSkillsArr [] = {"PSKIL32",        "PSKIL32",        "PSKIL42",        "PSKILL"};
+	static const std::string  secSkillsArr [] =  {"SECSK32",        "SECSK32",        "SECSKILL",       "SECSK82"};
+	static const std::string  resourceArr [] =   {"SMALRES",        "RESOURCE",       "RESOUR82",       "RESOUR82"};
+	static const std::string  creatureArr [] =   {"CPRSMALL",       "CPRSMALL",       "TWCRPORT",       "TWCRPORT"};
+	static const std::string  artifactArr[]  =   {"Artifact",       "Artifact",       "Artifact",       "Artifact"};
+	static const std::string  spellsArr [] =     {"SpellInt",       "SpellInt",       "SPELLSCR",       "SPELLSCR"};
+	static const std::string  moraleArr [] =     {"IMRL22",         "IMRL30",         "IMRL42",         "imrl82"};
+	static const std::string  luckArr [] =       {"ILCK22",         "ILCK30",         "ILCK42",         "ilck82"};
+	static const std::string  heroArr [] =       {"PortraitsSmall", "PortraitsSmall", "PortraitsLarge", "PortraitsLarge"};
+	static const std::string  flagArr [] =       {"CREST58",        "CREST58",        "CREST58",        "CREST58"};
+
+	auto gen = [](const std::string * arr)
+	{
+		return std::vector<std::string>(arr, arr + 4);
+	};
+
+	switch(compType)
+	{
+	case primskill:  return gen(primSkillsArr);
+	case secskill:   return gen(secSkillsArr);
+	case resource:   return gen(resourceArr);
+	case creature:   return gen(creatureArr);
+	case artifact:   return gen(artifactArr);
+	case experience: return gen(primSkillsArr);
+	case spell:      return gen(spellsArr);
+	case morale:     return gen(moraleArr);
+	case luck:       return gen(luckArr);
+	case building:   return std::vector<std::string>(4, CGI->townh->factions[subtype]->town->clientInfo.buildingsIcons);
+	case hero:       return gen(heroArr);
+	case flag:       return gen(flagArr);
+	}
+	assert(0);
+	return std::vector<std::string>();
+}
+
+size_t CComponent::getIndex()
+{
+	switch(compType)
+	{
+	case primskill:  return subtype;
+	case secskill:   return subtype*3 + 3 + val - 1;
+	case resource:   return subtype;
+	case creature:   return CGI->creh->creatures[subtype]->iconIndex;
+	case artifact:   return CGI->arth->artifacts[subtype]->iconIndex;
+	case experience: return 4;
+	case spell:      return subtype;
+	case morale:     return val+3;
+	case luck:       return val+3;
+	case building:   return val;
+	case hero:       return subtype;
+	case flag:       return subtype;
+	}
+	assert(0);
+	return 0;
+}
+
+std::string CComponent::getDescription()
+{
+	switch (compType)
+	{
+	case primskill:  return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
+										 : CGI->generaltexth->allTexts[149]; //mana
+	case secskill:   return CGI->generaltexth->skillInfoTexts[subtype][val-1];
+	case resource:   return CGI->generaltexth->allTexts[242];
+	case creature:   return "";
+	case artifact:   return CGI->arth->artifacts[subtype]->Description();
+	case experience: return CGI->generaltexth->allTexts[241];
+	case spell:      return CGI->spellh->objects[subtype]->getLevelInfo(val).description;
+	case morale:     return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
+	case luck:       return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
+	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
+	case hero:       return "";
+	case flag:       return "";
+	}
+	assert(0);
+	return "";
+}
+
+std::string CComponent::getSubtitle()
+{
+	if (!perDay)
+		return getSubtitleInternal();
+
+	std::string ret = CGI->generaltexth->allTexts[3];
+	boost::replace_first(ret, "%d", getSubtitleInternal());
+	return ret;
+}
+
+std::string CComponent::getSubtitleInternal()
+{
+	//FIXME: some of these are horrible (e.g creature)
+	switch(compType)
+	{
+	case primskill:  return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387]));
+	case secskill:   return CGI->generaltexth->levels[val-1] + "\n" + CGI->generaltexth->skillName[subtype];
+	case resource:   return boost::lexical_cast<std::string>(val);
+	case creature:   return (val? boost::lexical_cast<std::string>(val) + " " : "") + CGI->creh->creatures[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing);
+	case artifact:   return CGI->arth->artifacts[subtype]->Name();
+	case experience:
+		{
+			if (subtype == 1) //+1 level - tree of knowledge
+			{
+				std::string level = CGI->generaltexth->allTexts[442];
+				boost::replace_first(level, "1", boost::lexical_cast<std::string>(val));
+				return level;
+			}
+			else
+				return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
+		}
+	case spell:      return CGI->spellh->objects[subtype]->name;
+	case morale:     return "";
+	case luck:       return "";
+	case building:   return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
+	case hero:       return "";
+	case flag:       return CGI->generaltexth->capColors[subtype];
+	}
+	assert(0);
+	return "";
+}
+
+void CComponent::setSurface(std::string defName, int imgPos)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	vstd::clear_pointer(image);
+	image = new CAnimImage(defName, imgPos);
+}
+
+void CComponent::clickRight(tribool down, bool previousState)
+{
+	if(!getDescription().empty())
+		adventureInt->handleRightClick(getDescription(), down);
+}
+
+void CSelectableComponent::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+	{
+		if(onSelect)
+			onSelect();
+	}
+}
+
+void CSelectableComponent::init()
+{
+	selected = false;
+}
+
+CSelectableComponent::CSelectableComponent(const Component &c, std::function<void()> OnSelect):
+	CComponent(c),onSelect(OnSelect)
+{
+	type |= REDRAW_PARENT;
+	addUsedEvents(LCLICK | KEYBOARD);
+	init();
+}
+
+CSelectableComponent::CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize, std::function<void()> OnSelect):
+	CComponent(Type,Sub,Val, imageSize),onSelect(OnSelect)
+{
+	type |= REDRAW_PARENT;
+	addUsedEvents(LCLICK | KEYBOARD);
+	init();
+}
+
+void CSelectableComponent::select(bool on)
+{
+	if(on != selected)
+	{
+		selected = on;
+		redraw();
+	}
+}
+
+void CSelectableComponent::showAll(SDL_Surface * to)
+{
+	CComponent::showAll(to);
+	if(selected)
+	{
+		CSDL_Ext::drawBorder(to, Rect::around(image->pos), int3(239,215,123));
+	}
+}
+
+void CComponentBox::selectionChanged(CSelectableComponent * newSelection)
+{
+	if (newSelection == selected)
+		return;
+
+	if (selected)
+		selected->select(false);
+
+	selected = newSelection;
+	if (onSelect)
+		onSelect(selectedIndex());
+
+	if (selected)
+		selected->select(true);
+}
+
+int CComponentBox::selectedIndex()
+{
+	if (selected)
+		return std::find(components.begin(), components.end(), selected) - components.begin();
+	return -1;
+}
+
+Point CComponentBox::getOrTextPos(CComponent *left, CComponent *right)
+{
+	int leftSubtitle  = ( left->pos.w -  left->image->pos.w) / 2;
+	int rightSubtitle = (right->pos.w - right->image->pos.w) / 2;
+	int fullDistance = getDistance(left, right) + leftSubtitle + rightSubtitle;
+
+	return Point(fullDistance/2 - leftSubtitle, (left->image->pos.h + right->image->pos.h) / 4);
+}
+
+int CComponentBox::getDistance(CComponent *left, CComponent *right)
+{
+	static const int betweenImagesMin = 20;
+	static const int betweenSubtitlesMin = 10;
+
+	int leftSubtitle  = ( left->pos.w -  left->image->pos.w) / 2;
+	int rightSubtitle = (right->pos.w - right->image->pos.w) / 2;
+	int subtitlesOffset = leftSubtitle + rightSubtitle;
+
+	return std::max(betweenSubtitlesMin, betweenImagesMin - subtitlesOffset);
+}
+
+void CComponentBox::placeComponents(bool selectable)
+{
+	static const int betweenRows = 22;
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if (components.empty())
+		return;
+
+	//prepare components
+	for(auto & comp : components)
+	{
+		addChild(comp);
+		comp->moveTo(Point(pos.x, pos.y));
+	}
+
+	struct RowData
+	{
+		size_t comps;
+		int width;
+		int height;
+		RowData (size_t Comps, int Width, int Height):
+		comps(Comps), width (Width), height (Height){};
+	};
+	std::vector<RowData> rows;
+	rows.push_back (RowData (0,0,0));
+
+	//split components in rows
+	CComponent * prevComp = nullptr;
+
+	for(CComponent * comp : components)
+	{
+		//make sure that components are smaller than our width
+		//assert(pos.w == 0 || pos.w < comp->pos.w);
+
+		const int distance = prevComp ? getDistance(prevComp, comp) : 0;
+
+		//start next row
+		if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full
+			|| rows.back().comps >= 4) // no more than 4 comps per row
+		{
+			prevComp = nullptr;
+			rows.push_back (RowData (0,0,0));
+		}
+
+		if (prevComp)
+			rows.back().width += distance;
+
+		rows.back().comps++;
+		rows.back().width += comp->pos.w;
+
+		vstd::amax(rows.back().height, comp->pos.h);
+		prevComp = comp;
+	}
+
+	if (pos.w == 0)
+	{
+		for(auto & row : rows)
+			vstd::amax(pos.w, row.width);
+	}
+
+	int height = (rows.size() - 1) * betweenRows;
+	for(auto & row : rows)
+		height += row.height;
+
+	//assert(pos.h == 0 || pos.h < height);
+	if (pos.h == 0)
+		pos.h = height;
+
+	auto iter = components.begin();
+	int currentY = (pos.h - height) / 2;
+
+	//move components to their positions
+	for (auto & rows_row : rows)
+	{
+		// amount of free space we may add on each side of every component
+		int freeSpace = (pos.w - rows_row.width) / (rows_row.comps * 2);
+		prevComp = nullptr;
+
+		int currentX = 0;
+		for (size_t col = 0; col < rows_row.comps; col++)
+		{
+			currentX += freeSpace;
+			if (prevComp)
+			{
+				if (selectable)
+				{
+					Point orPos = Point(currentX - freeSpace, currentY) + getOrTextPos(prevComp, *iter);
+
+					new CLabel(orPos.x, orPos.y, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[4]);
+				}
+				currentX += getDistance(prevComp, *iter);
+			}
+
+			(*iter)->moveBy(Point(currentX, currentY));
+			currentX += (*iter)->pos.w;
+			currentX += freeSpace;
+
+			prevComp = *(iter++);
+		}
+		currentY += rows_row.height + betweenRows;
+	}
+}
+
+CComponentBox::CComponentBox(CComponent * _components, Rect position):
+	components(1, _components),
+	selected(nullptr)
+{
+	type |= REDRAW_PARENT;
+	pos = position + pos;
+	placeComponents(false);
+}
+
+CComponentBox::CComponentBox(std::vector<CComponent *> _components, Rect position):
+	components(_components),
+	selected(nullptr)
+{
+	type |= REDRAW_PARENT;
+	pos = position + pos;
+	placeComponents(false);
+}
+
+CComponentBox::CComponentBox(std::vector<CSelectableComponent *> _components, Rect position, std::function<void(int newID)> _onSelect):
+	components(_components.begin(), _components.end()),
+	selected(nullptr),
+	onSelect(_onSelect)
+{
+	type |= REDRAW_PARENT;
+	pos = position + pos;
+	placeComponents(true);
+
+	assert(!components.empty());
+
+	int key = SDLK_1;
+	for(auto & comp : _components)
+	{
+		comp->onSelect = boost::bind(&CComponentBox::selectionChanged, this, comp);
+		comp->assignedKeys.insert(key++);
+	}
+	selectionChanged(_components.front());
+}

+ 112 - 0
client/widgets/CComponent.h

@@ -0,0 +1,112 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+
+/*
+ * CComponent.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
+ *
+ */
+
+struct Component;
+class CAnimImage;
+
+/// common popup window component
+class CComponent : public virtual CIntObject
+{
+public:
+	enum Etype
+	{
+		primskill, secskill, resource, creature, artifact, experience, spell, morale, luck, building, hero, flag, typeInvalid
+	};
+
+	//NOTE: not all types have exact these sizes or have less than 4 of them. In such cases closest one will be used
+	enum ESize
+	{
+		tiny,  // ~22-24px
+		small, // ~30px
+		medium,// ~42px
+		large,  // ~82px
+		sizeInvalid
+	};
+
+private:
+	size_t getIndex();
+	const std::vector<std::string> getFileName();
+	void setSurface(std::string defName, int imgPos);
+	std::string getSubtitleInternal();
+
+	void init(Etype Type, int Subtype, int Val, ESize imageSize);
+
+public:
+	CAnimImage *image; //our image
+
+	Etype compType; //component type
+	ESize size; //component size.
+	int subtype; //type-dependant subtype. See getSomething methods for details
+	int val; // value \ strength \ amount of component. See getSomething methods for details
+	bool perDay; // add "per day" text to subtitle
+
+	std::string getDescription();
+	std::string getSubtitle();
+
+	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large);//c-tor
+	CComponent(const Component &c); //c-tor
+
+	void clickRight(tribool down, bool previousState); //call-in
+};
+
+/// component that can be selected or deselected
+class CSelectableComponent : public CComponent, public CKeyShortcut
+{
+	void init();
+public:
+	bool selected; //if true, this component is selected
+	std::function<void()> onSelect; //function called on selection change
+
+	void showAll(SDL_Surface * to);
+	void select(bool on);
+
+	void clickLeft(tribool down, bool previousState); //call-in
+	CSelectableComponent(Etype Type, int Sub, int Val, ESize imageSize=large, std::function<void()> OnSelect = nullptr); //c-tor
+	CSelectableComponent(const Component &c, std::function<void()> OnSelect = nullptr); //c-tor
+};
+
+/// box with multiple components (up to 8?)
+/// will take ownership on components and delete them afterwards
+class CComponentBox : public CIntObject
+{
+	std::vector<CComponent *> components;
+
+	CSelectableComponent * selected;
+	std::function<void(int newID)> onSelect;
+
+	void selectionChanged(CSelectableComponent * newSelection);
+
+	//get position of "or" text between these comps
+	//it will place "or" equidistant to both images
+	Point getOrTextPos(CComponent *left, CComponent * right);
+
+	//get distance between these copmonents
+	int getDistance(CComponent *left, CComponent * right);
+	void placeComponents(bool selectable);
+
+public:
+	/// return index of selected item
+	int selectedIndex();
+
+	/// constructor for quite common 1-components popups
+	/// if position width or height are 0 then it will be determined automatically
+	CComponentBox(CComponent * components, Rect position);
+	/// constructor for non-selectable components
+	CComponentBox(std::vector<CComponent *> components, Rect position);
+
+	/// constructor for selectable components
+	/// will also create "or" labels between components
+	/// onSelect - optional function that will be called every time on selection change
+	CComponentBox(std::vector<CSelectableComponent *> components, Rect position, std::function<void(int newID)> onSelect = nullptr);
+};

+ 512 - 0
client/widgets/CGarrisonInt.cpp

@@ -0,0 +1,512 @@
+#include "StdInc.h"
+#include "CGarrisonInt.h"
+
+#include "../gui/CGuiHandler.h"
+
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/TextControls.h"
+#include "../windows/CCreatureWindow.h"
+#include "../windows/GUIClasses.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+#include "../../lib/CGameState.h"
+
+/*
+ * CGarrisonInt.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
+ *
+ */
+
+void CGarrisonSlot::setHighlight(bool on)
+{
+	if (on)
+		selectionImage->enable(); //show
+	else
+		selectionImage->disable(); //hide
+}
+
+void CGarrisonSlot::hover (bool on)
+{
+	////Hoverable::hover(on);
+	if(on)
+	{
+		std::string temp;
+		if(creature)
+		{
+			if(owner->getSelection())
+			{
+				if(owner->getSelection() == this)
+				{
+					temp = CGI->generaltexth->tcommands[4]; //View %s
+					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+				}
+				else if (owner->getSelection()->creature == creature)
+				{
+					temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
+					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+				}
+				else if (owner->getSelection()->creature)
+				{
+					temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+				}
+				else
+				{
+                    logGlobal->warnStream() << "Warning - shouldn't be - highlighted void slot "<<owner->getSelection();
+                    logGlobal->warnStream() << "Highlighted set to nullptr";
+					owner->selectSlot(nullptr);
+				}
+			}
+			else
+			{
+				if(upg)
+				{
+					temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
+				}
+				else if(owner->armedObjs[0] && owner->armedObjs[0]->ID == Obj::TOWN)
+				{
+					temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
+				}
+				else
+				{
+					temp = CGI->generaltexth->allTexts[481]; //Select %s
+				}
+				boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+			};
+		}
+		else
+		{
+			if(owner->getSelection())
+			{
+				const CArmedInstance *highl = owner->getSelection()->getObj();
+				if(  highl->needsLastStack()		//we are moving stack from hero's
+				  && highl->stacksCount() == 1	//it's only stack
+				  && owner->getSelection()->upg != upg	//we're moving it to the other garrison
+				  )
+				{
+					temp = CGI->generaltexth->tcommands[5]; //Cannot move last army to garrison
+				}
+				else
+				{
+					temp = CGI->generaltexth->tcommands[6]; //Move %s
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
+				}
+			}
+			else
+			{
+				temp = CGI->generaltexth->tcommands[11]; //Empty
+			}
+		}
+		GH.statusbar->setText(temp);
+	}
+	else
+	{
+		GH.statusbar->clear();
+	}
+}
+
+const CArmedInstance * CGarrisonSlot::getObj() const
+{
+	return 	(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]);
+}
+
+bool CGarrisonSlot::our() const
+{
+	return 	upg?(owner->owned[1]):(owner->owned[0]);
+}
+
+void CGarrisonSlot::clickRight(tribool down, bool previousState)
+{
+	if(down && creature)
+	{
+		GH.pushInt(new CStackWindow(myStack, true));
+	}
+}
+void CGarrisonSlot::clickLeft(tribool down, bool previousState)
+{
+	if(down)
+	{
+		bool refr = false;
+		if(owner->getSelection())
+		{
+			if(owner->getSelection() == this) //view info
+			{
+				UpgradeInfo pom;
+				LOCPLINT->cb->getUpgradeInfo(getObj(), ID, pom);
+
+				bool canUpgrade = getObj()->tempOwner == LOCPLINT->playerID && pom.oldID>=0; //upgrade is possible
+				bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID && (getObj()->stacksCount()>1  || !getObj()->needsLastStack());
+				std::function<void(CreatureID)> upgr = nullptr;
+				std::function<void()> dism = nullptr;
+				if(canUpgrade) upgr = [=] (CreatureID newID) { LOCPLINT->cb->upgradeCreature(getObj(), ID, newID); };
+				if(canDismiss) dism = [=] { LOCPLINT->cb->dismissCreature(getObj(), ID); };
+
+				owner->selectSlot(nullptr);
+				owner->setSplittingMode(false);
+
+				for(auto & elem : owner->splitButtons)
+					elem->block(true);
+
+				redraw();
+				refr = true;
+				GH.pushInt(new CStackWindow(myStack, dism, pom, upgr));
+			}
+			else
+			{
+				// Only allow certain moves if troops aren't removable or not ours.
+				if (  ( owner->getSelection()->our()//our creature is selected
+				     || owner->getSelection()->creature == creature )//or we are rebalancing army
+				   && ( owner->removableUnits
+				     || (upg == 0 &&  ( owner->getSelection()->upg == 1 && !creature ) )
+					 || (upg == 1 &&    owner->getSelection()->upg == 1 ) ) )
+				{
+					//we want to split
+					if((owner->getSplittingMode() || LOCPLINT->shiftPressed())
+						&& (!creature
+							|| (creature == owner->getSelection()->creature)))
+					{
+						owner->p2 = ID; //store the second stack pos
+						owner->pb = upg;//store the second stack owner (up or down army)
+						owner->setSplittingMode(false);
+
+						int minLeft=0, minRight=0;
+
+						if(upg != owner->getSelection()->upg) //not splitting within same army
+						{
+							if(owner->getSelection()->getObj()->stacksCount() == 1 //we're splitting away the last stack
+								&& owner->getSelection()->getObj()->needsLastStack() )
+							{
+								minLeft = 1;
+							}
+							if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
+								&& owner->getSelection()->creature == creature
+								&& getObj()->needsLastStack() )
+							{
+								minRight = 1;
+							}
+						}
+
+						int countLeft = owner->getSelection()->myStack ? owner->getSelection()->myStack->count : 0;
+						int countRight = myStack ? myStack->count : 0;
+
+						GH.pushInt(new CSplitWindow(owner->getSelection()->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
+						                            minLeft, minRight, countLeft, countRight));
+						refr = true;
+					}
+					else if(creature != owner->getSelection()->creature) //swap
+					{
+						LOCPLINT->cb->swapCreatures(
+							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							ID,owner->getSelection()->ID);
+					}
+					else //merge
+					{
+						LOCPLINT->cb->mergeStacks(
+							(!owner->getSelection()->upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							(!upg)?(owner->armedObjs[0]):(owner->armedObjs[1]),
+							owner->getSelection()->ID,ID);
+					}
+				}
+				else // Highlight
+				{
+					if(creature)
+						owner->selectSlot(this);
+					redraw();
+					refr = true;
+				}
+			}
+		}
+		else //highlight or drop artifact
+		{
+			bool artSelected = false;
+			if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt())) //dirty solution
+			{
+				const CArtifactsOfHero::SCommonPart *commonInfo = chw->artSets.front()->commonInfo;
+				if (const CArtifactInstance *art = commonInfo->src.art)
+				{
+					const CGHeroInstance *srcHero = commonInfo->src.AOH->getHero();
+					artSelected = true;
+					ArtifactLocation src(srcHero, commonInfo->src.slotID);
+					ArtifactLocation dst(myStack, ArtifactPosition::CREATURE_SLOT);
+					if (art->canBePutAt(dst, true))
+					{	//equip clicked stack
+						if(dst.getArt())
+						{
+							//creature can wear only one active artifact
+							//if we are placing a new one, the old one will be returned to the hero's backpack
+							LOCPLINT->cb->swapArtifacts(dst, ArtifactLocation(srcHero, dst.getArt()->firstBackpackSlot(srcHero)));
+						}
+						LOCPLINT->cb->swapArtifacts(src, dst);
+					}
+				}
+			}
+			if (!artSelected && creature)
+			{
+				owner->selectSlot(this);
+				if(creature)
+				{
+					for(auto & elem : owner->splitButtons)
+						elem->block(false);
+				}
+			}
+			redraw();
+			refr = true;
+		}
+		if(refr) {hover(false);	hover(true); } //to refresh statusbar
+	}
+}
+
+void CGarrisonSlot::update()
+{
+	if (getObj() != nullptr)
+	{
+		addUsedEvents(LCLICK | RCLICK | HOVER);
+		myStack = getObj()->getStackPtr(ID);
+		creature = myStack ? myStack->type : nullptr;
+	}
+	else
+	{
+		removeUsedEvents(LCLICK | RCLICK | HOVER);
+		myStack = nullptr;
+		creature = nullptr;
+	}
+
+	if (creature)
+	{
+		creatureImage->enable();
+		creatureImage->setFrame(creature->iconIndex);
+
+		stackCount->enable();
+		stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
+	}
+	else
+	{
+		creatureImage->disable();
+		stackCount->disable();
+	}
+}
+
+CGarrisonSlot::CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg, const CStackInstance * Creature):
+    ID(IID),
+    owner(Owner),
+    myStack(Creature),
+    creature(Creature ? Creature->type : nullptr),
+    upg(Upg)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if (getObj())
+		addUsedEvents(LCLICK | RCLICK | HOVER);
+	pos.x += x;
+	pos.y += y;
+
+	std::string imgName = owner->smallIcons ? "cprsmall" : "TWCRPORT";
+
+	creatureImage = new CAnimImage(imgName, creature ? creature->iconIndex : 0);
+	if (!creature)
+		creatureImage->disable();
+
+	selectionImage = new CAnimImage(imgName, 1);
+	selectionImage->disable();
+
+	if(Owner->smallIcons)
+	{
+		pos.w = 32;
+		pos.h = 32;
+	}
+	else
+	{
+		pos.w = 58;
+		pos.h = 64;
+	}
+
+	stackCount = new CLabel(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, BOTTOMRIGHT, Colors::WHITE);
+	if (!creature)
+		stackCount->disable();
+	else
+		stackCount->setText(boost::lexical_cast<std::string>(myStack->count));
+}
+
+void CGarrisonInt::addSplitBtn(CButton * button)
+{
+	addChild(button);
+	button->recActions = defActions;
+	splitButtons.push_back(button);
+	button->block(getSelection() == nullptr);
+}
+
+void CGarrisonInt::createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int posY, int distance, int Upg )
+{
+	ret.resize(7);
+
+	if (set)
+	{
+		for(auto & elem : set->Slots())
+		{
+			ret[elem.first.getNum()] = new CGarrisonSlot(this, posX + (elem.first.getNum()*distance), posY, elem.first, Upg, elem.second);
+		}
+	}
+
+	for(int i=0; i<ret.size(); i++)
+		if(!ret[i])
+			ret[i] = new CGarrisonSlot(this, posX + (i*distance), posY, SlotID(i), Upg, nullptr);
+
+	if (twoRows)
+		for (int i=4; i<ret.size(); i++)
+		{
+			ret[i]->pos.x -= 126;
+			ret[i]->pos.y += 37;
+		};
+}
+
+void CGarrisonInt::createSlots()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	int width = smallIcons? 32 : 58;
+
+	createSet(slotsUp, armedObjs[0], 0, 0, width+interx, 0);
+	createSet(slotsDown, armedObjs[1], garOffset.x, garOffset.y, width+interx, 1);
+}
+
+void CGarrisonInt::recreateSlots()
+{
+	selectSlot(nullptr);
+	setSplittingMode(false);
+
+	for(auto & elem : splitButtons)
+		elem->block(true);
+
+
+	for(CGarrisonSlot * slot : slotsUp)
+		slot->update();
+
+	for(CGarrisonSlot * slot : slotsDown)
+		slot->update();
+}
+
+void CGarrisonInt::splitClick()
+{
+	if(!getSelection())
+		return;
+	setSplittingMode(!getSplittingMode());
+	redraw();
+}
+void CGarrisonInt::splitStacks(int, int amountRight)
+{
+	LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
+}
+
+CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
+                           SDL_Surface *pomsur, const Point& SurOffset,
+                           const CArmedInstance *s1, const CArmedInstance *s2,
+                           bool _removableUnits, bool smallImgs, bool _twoRows ) :
+    highlighted(nullptr),
+    inSplittingMode(false),
+    interx(inx),
+    garOffset(garsOffset),
+    smallIcons(smallImgs),
+    removableUnits(_removableUnits),
+    twoRows(_twoRows)
+{
+	setArmy(s1, false);
+	setArmy(s2, true);
+	pos.x += x;
+	pos.y += y;
+	createSlots();
+}
+
+const CGarrisonSlot * CGarrisonInt::getSelection()
+{
+	return highlighted;
+}
+
+void CGarrisonInt::selectSlot(CGarrisonSlot *slot)
+{
+	if (slot != highlighted)
+	{
+		if (highlighted)
+			highlighted->setHighlight(false);
+
+		highlighted = slot;
+		for (auto button : splitButtons)
+			button->block(highlighted == nullptr);
+
+		if (highlighted)
+			highlighted->setHighlight(true);
+	}
+}
+
+void CGarrisonInt::setSplittingMode(bool on)
+{
+	assert(on == false || highlighted != nullptr); //can't be in splitting mode without selection
+
+	if (inSplittingMode || on)
+	{
+		for(CGarrisonSlot * slot : slotsUp)
+			slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
+
+		for(CGarrisonSlot * slot : slotsDown)
+			slot->setHighlight( ( on && (slot->creature == nullptr || slot->creature == getSelection()->creature)));
+		inSplittingMode = on;
+	}
+}
+
+bool CGarrisonInt::getSplittingMode()
+{
+	return inSplittingMode;
+}
+
+void CGarrisonInt::setArmy(const CArmedInstance *army, bool bottomGarrison)
+{
+	owned[bottomGarrison] =  army ? (army->tempOwner == LOCPLINT->playerID || army->tempOwner == PlayerColor::UNFLAGGABLE) : false;
+	armedObjs[bottomGarrison] = army;
+}
+
+CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits ):
+	CWindowObject(PLAYER_COLORED, "GARRISON")
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	garr = new CGarrisonInt(92, 127, 4, Point(0,96), background->bg, Point(93,127), up, down, removableUnits);
+	{
+		CButton *split = new CButton(Point(88, 314), "IDV6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[3], ""), [&]{ garr->splitClick(); } );
+		removeChild(split);
+		garr->addSplitBtn(split);
+	}
+	quit = new CButton(Point(399, 314), "IOK6432.DEF", CButton::tooltip(CGI->generaltexth->tcommands[8], ""), [&]{ close(); }, SDLK_RETURN);
+
+	std::string titleText;
+	if (garr->armedObjs[1]->tempOwner == garr->armedObjs[0]->tempOwner)
+		titleText = CGI->generaltexth->allTexts[709];
+	else
+	{
+		titleText = CGI->generaltexth->allTexts[35];
+		boost::algorithm::replace_first(titleText, "%s", garr->armedObjs[0]->Slots().begin()->second->type->namePl);
+	}
+	new CLabel(275, 30, FONT_BIG, CENTER, Colors::YELLOW, titleText);
+
+	new CAnimImage("CREST58", garr->armedObjs[0]->getOwner().getNum(), 0, 28, 124);
+	new CAnimImage("PortraitsLarge", dynamic_cast<const CGHeroInstance*>(garr->armedObjs[1])->portrait, 0, 29, 222);
+}
+
+CGarrisonHolder::CGarrisonHolder()
+{
+}
+
+void CWindowWithGarrison::updateGarrisons()
+{
+	garr->recreateSlots();
+}

+ 122 - 0
client/widgets/CGarrisonInt.h

@@ -0,0 +1,122 @@
+#pragma once
+
+#include "../windows/CWindowObject.h"
+
+/*
+ * CGarrisonInt.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 CGarrisonInt;
+class CButton;
+class CArmedInstance;
+class CAnimImage;
+class CCreatureSet;
+class CGarrisonSlot;
+class CStackInstance;
+class CLabel;
+
+/// A single garrison slot which holds one creature of a specific amount
+class CGarrisonSlot : public CIntObject
+{
+	SlotID ID; //for identification
+	CGarrisonInt *owner;
+	const CStackInstance *myStack; //nullptr if slot is empty
+	const CCreature *creature;
+	int upg; //0 - up garrison, 1 - down garrison
+
+	CAnimImage * creatureImage;
+	CAnimImage * selectionImage; // image for selection, not always visible
+	CLabel * stackCount;
+
+	void setHighlight(bool on);
+public:
+	virtual void hover (bool on); //call-in
+	const CArmedInstance * getObj() const;
+	bool our() const;
+	void clickRight(tribool down, bool previousState);
+	void clickLeft(tribool down, bool previousState);
+	void update();
+	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, int Upg=0, const CStackInstance * Creature=nullptr);
+
+	friend class CGarrisonInt;
+};
+
+/// Class which manages slots of upper and lower garrison, splitting of units
+class CGarrisonInt :public CIntObject
+{
+	CGarrisonSlot *highlighted; //chosen slot. Should be changed only via selectSlot
+	bool inSplittingMode;
+
+public:
+	void selectSlot(CGarrisonSlot * slot); //null = deselect
+	const CGarrisonSlot * getSelection();
+
+	void setSplittingMode(bool on);
+	bool getSplittingMode();
+
+	int interx; //space between slots
+	Point garOffset; //offset between garrisons (not used if only one hero)
+	std::vector<CButton *> splitButtons; //may be empty if no buttons
+
+	SlotID p2; //TODO: comment me
+	int	shiftPos;//1st slot of the second row, set shiftPoint for effect
+	bool pb,
+		 smallIcons, //true - 32x32 imgs, false - 58x64
+		 removableUnits,//player can remove units from up
+		 twoRows,//slots will be placed in 2 rows
+		 owned[2];//player owns up or down army [0] upper, [1] lower
+
+// 	const CCreatureSet *set1; //top set of creatures
+// 	const CCreatureSet *set2; //bottom set of creatures
+
+	std::vector<CGarrisonSlot*> slotsUp, slotsDown; //slots of upper and lower garrison
+	const CArmedInstance *armedObjs[2]; //[0] is upper, [1] is down
+	//const CArmedInstance *oup, *odown; //upper and lower garrisons (heroes or towns)
+
+	void setArmy(const CArmedInstance *army, bool bottomGarrison);
+	void addSplitBtn(CButton * button);
+	void createSet(std::vector<CGarrisonSlot*> &ret, const CCreatureSet * set, int posX, int distance, int posY, int Upg );
+
+	void createSlots();
+	void recreateSlots();
+
+	void splitClick(); //handles click on split button
+	void splitStacks(int amountLeft, int amountRight); //TODO: comment me
+	//x, y - position;
+	//inx - distance between slots;
+	//pomsur, SurOffset - UNUSED
+	//s1, s2 - top and bottom armies;
+	//removableUnits - you can take units from top;
+	//smallImgs - units images size 64x58 or 32x32;
+	//twoRows - display slots in 2 row (1st row = 4 slots, 2nd = 3 slots)
+	CGarrisonInt(int x, int y, int inx, const Point &garsOffset, SDL_Surface *pomsur, const Point &SurOffset, const CArmedInstance *s1, const CArmedInstance *s2=nullptr, bool _removableUnits = true, bool smallImgs = false, bool _twoRows=false); //c-tor
+};
+
+class CGarrisonHolder
+{
+public:
+	CGarrisonHolder();
+	virtual void updateGarrisons()=0;
+};
+
+class CWindowWithGarrison : public virtual CGarrisonHolder
+{
+public:
+	CGarrisonInt *garr;
+	virtual void updateGarrisons();
+};
+
+/// Garrison window where you can take creatures out of the hero to place it on the garrison
+class CGarrisonWindow : public CWindowObject, public CWindowWithGarrison
+{
+public:
+	CButton * quit;
+
+	CGarrisonWindow(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits); //c-tor
+};

+ 533 - 0
client/widgets/Images.cpp

@@ -0,0 +1,533 @@
+#include "StdInc.h"
+#include "Images.h"
+
+#include "MiscWidgets.h"
+
+#include "../gui/CAnimation.h"
+#include "../gui/SDL_Pixels.h"
+#include "../gui/SDL_Extensions.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../battle/CBattleInterface.h"
+#include "../battle/CBattleInterfaceClasses.h"
+
+#include "../CBitmapHandler.h"
+#include "../Graphics.h"
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+#include "../windows/CAdvmapInterface.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+#include "../../lib/CRandomGenerator.h"
+
+/*
+ * Images.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
+ *
+ */
+
+CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
+{
+	init();
+	bg = BG;
+	freeSurf = Free;
+	pos.x += x;
+	pos.y += y;
+	pos.w = BG->w;
+	pos.h = BG->h;
+}
+
+CPicture::CPicture( const std::string &bmpname, int x, int y )
+{
+	init();
+	bg = BitmapHandler::loadBitmap(bmpname);
+	freeSurf = true;;
+	pos.x += x;
+	pos.y += y;
+	if(bg)
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+	else
+	{
+		pos.w = pos.h = 0;
+	}
+}
+
+CPicture::CPicture(const Rect &r, const SDL_Color &color, bool screenFormat /*= false*/)
+{
+	init();
+	createSimpleRect(r, screenFormat, SDL_MapRGB(bg->format, color.r, color.g,color.b));
+}
+
+CPicture::CPicture(const Rect &r, ui32 color, bool screenFormat /*= false*/)
+{
+	init();
+	createSimpleRect(r, screenFormat, color);
+}
+
+CPicture::CPicture(SDL_Surface *BG, const Rect &SrcRect, int x /*= 0*/, int y /*= 0*/, bool free /*= false*/)
+{
+	needRefresh = false;
+	srcRect = new Rect(SrcRect);
+	pos.x += x;
+	pos.y += y;
+	pos.w = srcRect->w;
+	pos.h = srcRect->h;
+	bg = BG;
+	freeSurf = free;
+}
+
+void CPicture::setSurface(SDL_Surface *to)
+{
+	bg = to;
+	if (srcRect)
+	{
+		pos.w = srcRect->w;
+		pos.h = srcRect->h;
+	}
+	else
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+}
+
+CPicture::~CPicture()
+{
+	if(freeSurf)
+		SDL_FreeSurface(bg);
+	delete srcRect;
+}
+
+void CPicture::init()
+{
+	needRefresh = false;
+	srcRect = nullptr;
+}
+
+void CPicture::show(SDL_Surface * to)
+{
+	if (needRefresh)
+		showAll(to);
+}
+
+void CPicture::showAll(SDL_Surface * to)
+{
+	if(bg)
+	{
+		if(srcRect)
+		{
+			SDL_Rect srcRectCpy = *srcRect;
+			SDL_Rect dstRect = srcRectCpy;
+			dstRect.x = pos.x;
+			dstRect.y = pos.y;
+
+			CSDL_Ext::blitSurface(bg, &srcRectCpy, to, &dstRect);
+		}
+		else
+			blitAt(bg, pos, to);
+	}
+}
+
+void CPicture::convertToScreenBPP()
+{
+	SDL_Surface *hlp = bg;
+	bg = SDL_ConvertSurface(hlp,screen->format,0);
+	CSDL_Ext::setDefaultColorKey(bg);	
+	SDL_FreeSurface(hlp);
+}
+
+void CPicture::setAlpha(int value)
+{	
+	#ifdef VCMI_SDL1
+	SDL_SetAlpha(bg, SDL_SRCALPHA, value);	
+	#else
+	SDL_SetSurfaceAlphaMod(bg,value);
+	#endif // 0
+}
+
+void CPicture::scaleTo(Point size)
+{
+	SDL_Surface * scaled = CSDL_Ext::scaleSurface(bg, size.x, size.y);
+
+	if(freeSurf)
+		SDL_FreeSurface(bg);
+
+	setSurface(scaled);
+	freeSurf = false;
+}
+
+void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color)
+{
+	pos += r;
+	pos.w = r.w;
+	pos.h = r.h;
+	if(screenFormat)
+		bg = CSDL_Ext::newSurface(r.w, r.h);
+	else
+		bg = SDL_CreateRGBSurface(SDL_SWSURFACE, r.w, r.h, 8, 0, 0, 0, 0);
+
+	SDL_FillRect(bg, nullptr, color);
+	freeSurf = true;
+}
+
+void CPicture::colorizeAndConvert(PlayerColor player)
+{
+	assert(bg);
+	colorize(player);
+	convertToScreenBPP();
+}
+
+void CPicture::colorize(PlayerColor player)
+{
+	assert(bg);
+	graphics->blueToPlayersAdv(bg, player);
+}
+
+CFilledTexture::CFilledTexture(std::string imageName, Rect position):
+    CIntObject(0, position.topLeft()),
+    texture(BitmapHandler::loadBitmap(imageName))
+{
+	pos.w = position.w;
+	pos.h = position.h;
+}
+
+CFilledTexture::~CFilledTexture()
+{
+	SDL_FreeSurface(texture);
+}
+
+void CFilledTexture::showAll(SDL_Surface *to)
+{
+	CSDL_Ext::CClipRectGuard guard(to, pos);
+	CSDL_Ext::fillTexture(to, texture);
+}
+
+CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+	frame(Frame),
+	group(Group),
+	player(-1),
+	flags(Flags)
+{
+	pos.x += x;
+	pos.y += y;
+	anim = new CAnimation(name);
+	init();
+}
+
+CAnimImage::CAnimImage(CAnimation *Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+	anim(Anim),
+	frame(Frame),
+	group(Group),
+	player(-1),
+	flags(Flags)
+{
+	pos.x += x;
+	pos.y += y;
+	init();
+}
+
+size_t CAnimImage::size()
+{
+	return anim->size(group);
+}
+
+void CAnimImage::init()
+{
+	anim->load(frame, group);
+	if (flags & CShowableAnim::BASE)
+		anim->load(0,group);
+
+	IImage *img = anim->getImage(frame, group);
+	if (img)
+	{
+		pos.w = img->width();
+		pos.h = img->height();
+	}
+}
+
+CAnimImage::~CAnimImage()
+{
+	anim->unload(frame, group);
+	if (flags & CShowableAnim::BASE)
+		anim->unload(0,group);
+	delete anim;
+}
+
+void CAnimImage::showAll(SDL_Surface * to)
+{
+	IImage *img;
+
+	if ( flags & CShowableAnim::BASE && frame != 0)
+		if ((img = anim->getImage(0, group)))
+			img->draw(to, pos.x, pos.y);
+
+	if ((img = anim->getImage(frame, group)))
+		img->draw(to, pos.x, pos.y);
+}
+
+void CAnimImage::setFrame(size_t Frame, size_t Group)
+{
+	if (frame == Frame && group==Group)
+		return;
+	if (anim->size(Group) > Frame)
+	{
+		anim->load(Frame, Group);
+		anim->unload(frame, group);
+		frame = Frame;
+		group = Group;
+		IImage *img = anim->getImage(frame, group);
+		if (img)
+		{
+			if (flags & CShowableAnim::PLAYER_COLORED)
+				img->playerColored(player);
+			pos.w = img->width();
+			pos.h = img->height();
+		}
+	}
+	else
+		logGlobal->errorStream() << "Error: accessing unavailable frame " << Group << ":" << Frame << " in CAnimation!";
+}
+
+void CAnimImage::playerColored(PlayerColor currPlayer)
+{
+	player = currPlayer;
+	flags |= CShowableAnim::PLAYER_COLORED;
+	anim->getImage(frame, group)->playerColored(player);
+	if (flags & CShowableAnim::BASE)
+			anim->getImage(0, group)->playerColored(player);
+}
+
+CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group):
+	anim(new CAnimation(name, Flags & USE_RLE)),
+	group(Group),
+	frame(0),
+	first(0),
+	frameDelay(Delay),
+	value(0),
+	flags(Flags),
+	xOffset(0),
+	yOffset(0),
+	alpha(255)
+{
+	anim->loadGroup(group);
+	last = anim->size(group);
+
+	pos.w = anim->getImage(0, group)->width();
+	pos.h = anim->getImage(0, group)->height();
+	pos.x+= x;
+	pos.y+= y;
+}
+
+CShowableAnim::~CShowableAnim()
+{
+	anim->unloadGroup(group);
+	delete anim;
+}
+
+void CShowableAnim::setAlpha(ui32 alphaValue)
+{
+	alpha = std::min<ui32>(alphaValue, 255);
+}
+
+bool CShowableAnim::set(size_t Group, size_t from, size_t to)
+{
+	size_t max = anim->size(Group);
+
+	if (to < max)
+		max = to;
+
+	if (max < from || max == 0)
+		return false;
+
+	anim->load(Group);
+	anim->unload(group);
+	group = Group;
+	frame = first = from;
+	last = max;
+	value = 0;
+	return true;
+}
+
+bool CShowableAnim::set(size_t Group)
+{
+	if (anim->size(Group)== 0)
+		return false;
+	if (group != Group)
+	{
+		anim->loadGroup(Group);
+		anim->unloadGroup(group);
+		first = 0;
+		group = Group;
+		last = anim->size(Group);
+	}
+	frame = value = 0;
+	return true;
+}
+
+void CShowableAnim::reset()
+{
+	value = 0;
+	frame = first;
+
+	if (callback)
+		callback();
+}
+
+void CShowableAnim::clipRect(int posX, int posY, int width, int height)
+{
+	xOffset = posX;
+	yOffset = posY;
+	pos.w = width;
+	pos.h = height;
+}
+
+void CShowableAnim::show(SDL_Surface * to)
+{
+	if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling
+		blitImage(first, group, to);
+	blitImage(frame, group, to);
+
+	if ((flags & PLAY_ONCE) && frame + 1 == last)
+		return;
+
+	if ( ++value == frameDelay )
+	{
+		value = 0;
+		if ( ++frame >= last)
+			reset();
+	}
+}
+
+void CShowableAnim::showAll(SDL_Surface * to)
+{
+	if ( flags & BASE )// && frame != first)
+		blitImage(first, group, to);
+	blitImage(frame, group, to);
+}
+
+void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
+{
+	assert(to);
+	Rect src( xOffset, yOffset, pos.w, pos.h);
+	IImage * img = anim->getImage(frame, group);
+	if (img)
+		img->draw(to, pos.x-xOffset, pos.y-yOffset, &src, alpha);
+}
+
+void CShowableAnim::rotate(bool on, bool vertical)
+{
+	ui8 flag = vertical? VERTICAL_FLIP:HORIZONTAL_FLIP;
+	if (on)
+		flags |= flag;
+	else
+		flags &= ~flag;
+}
+
+CCreatureAnim::CCreatureAnim(int x, int y, std::string name, Rect picPos, ui8 flags, EAnimType type):
+	CShowableAnim(x,y,name,flags,4,type)
+{
+	xOffset = picPos.x;
+	yOffset = picPos.y;
+	if (picPos.w)
+		pos.w = picPos.w;
+	if (picPos.h)
+		pos.h = picPos.h;
+};
+
+void CCreatureAnim::loopPreview(bool warMachine)
+{
+	std::vector<EAnimType> available;
+
+	static const EAnimType creaPreviewList[] = {HOLDING, HITTED, DEFENCE, ATTACK_FRONT, CAST_FRONT};
+	static const EAnimType machPreviewList[] = {HOLDING, MOVING, SHOOT_UP, SHOOT_FRONT, SHOOT_DOWN};
+	auto & previewList = warMachine ? machPreviewList : creaPreviewList;
+
+	for (auto & elem : previewList)
+		if (anim->size(elem))
+			available.push_back(elem);
+
+	size_t rnd = CRandomGenerator::getDefault().nextInt(available.size() * 2 - 1);
+
+	if (rnd >= available.size())
+	{
+		EAnimType type;
+		if ( anim->size(MOVING) == 0 )//no moving animation present
+			type = HOLDING;
+		else
+			type = MOVING;
+
+		//display this anim for ~1 second (time is random, but it looks good)
+		for (size_t i=0; i< 12/anim->size(type) + 1; i++)
+			addLast(type);
+	}
+	else
+		addLast(available[rnd]);
+}
+
+void CCreatureAnim::addLast(EAnimType newType)
+{
+	if (type != MOVING && newType == MOVING)//starting moving - play init sequence
+	{
+		queue.push( MOVE_START );
+	}
+	else if (type == MOVING && newType != MOVING )//previous anim was moving - finish it
+	{
+		queue.push( MOVE_END );
+	}
+	if (newType == TURN_L || newType == TURN_R)
+		queue.push(newType);
+
+	queue.push(newType);
+}
+
+void CCreatureAnim::reset()
+{
+	//if we are in the middle of rotation - set flag
+	if (type == TURN_L && !queue.empty() && queue.front() == TURN_L)
+		rotate(true);
+	if (type == TURN_R && !queue.empty() && queue.front() == TURN_R)
+		rotate(false);
+
+	while (!queue.empty())
+	{
+		EAnimType at = queue.front();
+		queue.pop();
+		if (set(at))
+			return;
+	}
+	if  (callback)
+		callback();
+	while (!queue.empty())
+	{
+		EAnimType at = queue.front();
+		queue.pop();
+		if (set(at))
+			return;
+	}
+	set(HOLDING);
+}
+
+void CCreatureAnim::startPreview(bool warMachine)
+{
+	callback = boost::bind(&CCreatureAnim::loopPreview, this, warMachine);
+}
+
+void CCreatureAnim::clearAndSet(EAnimType type)
+{
+	while (!queue.empty())
+		queue.pop();
+	set(type);
+}

+ 226 - 0
client/widgets/Images.h

@@ -0,0 +1,226 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+/*
+ * Images.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
+ *
+ */
+
+// Image class
+class CPicture : public CIntObject
+{
+	void setSurface(SDL_Surface *to);
+public: 
+	SDL_Surface * bg;
+	Rect * srcRect; //if nullptr then whole surface will be used
+	bool freeSurf; //whether surface will be freed upon CPicture destruction
+	bool needRefresh;//Surface needs to be displayed each frame
+
+	operator SDL_Surface*()
+	{
+		return bg;
+	}
+
+	CPicture(const Rect & r, const SDL_Color & color, bool screenFormat = false); //rect filled with given color
+	CPicture(const Rect & r, ui32 color, bool screenFormat = false); //rect filled with given color
+	CPicture(SDL_Surface * BG, int x = 0, int y=0, bool Free = true); //wrap existing SDL_Surface
+	CPicture(const std::string &bmpname, int x=0, int y=0);
+	CPicture(SDL_Surface *BG, const Rect &SrcRext, int x = 0, int y = 0, bool free = false); //wrap subrect of given surface
+	~CPicture();
+	void init();
+
+	//set alpha value for whole surface. Note: may be messed up if surface is shared
+	// 0=transparent, 255=opaque
+	void setAlpha(int value);
+
+	void scaleTo(Point size);
+	void createSimpleRect(const Rect &r, bool screenFormat, ui32 color);
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	void convertToScreenBPP();
+	void colorizeAndConvert(PlayerColor player);
+	void colorize(PlayerColor player);
+};
+
+/// area filled with specific texture
+class CFilledTexture : CIntObject
+{
+	SDL_Surface * texture;
+
+public:
+	CFilledTexture(std::string imageName, Rect position);
+	~CFilledTexture();
+	void showAll(SDL_Surface *to);
+};
+
+/// Class for displaying one image from animation
+class CAnimImage: public CIntObject
+{
+private:
+	CAnimation* anim;
+	//displayed frame/group
+	size_t frame;
+	size_t group;
+	PlayerColor player;
+	ui8 flags;
+
+	void init();
+
+public:
+	CAnimImage(std::string name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	CAnimImage(CAnimation* anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	~CAnimImage();//d-tor
+
+	//size of animation
+	size_t size();
+
+	//change displayed frame on this one
+	void setFrame(size_t Frame, size_t Group=0);
+
+	//makes image player-colored
+	void playerColored(PlayerColor player);
+
+	void showAll(SDL_Surface * to);
+};
+
+/// Base class for displaying animation, used as superclass for different animations
+class CShowableAnim: public CIntObject
+{
+public:
+	enum EFlags
+	{
+		BASE=1,            //base frame will be blitted before current one
+		HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
+		VERTICAL_FLIP=4,   //TODO: will be displayed rotated
+		USE_RLE=8,         //RLE-d version, support full alpha-channel for 8-bit images
+		PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
+		PLAY_ONCE=32       //play animation only once and stop at last frame
+	};
+protected:
+	CAnimation * anim;
+
+	size_t group, frame;//current frame
+
+	size_t first, last; //animation range
+
+	//TODO: replace with time delay(needed for battles)
+	ui32 frameDelay;//delay in frames of each image
+	ui32 value;//how many times current frame was showed
+
+	ui8 flags;//Flags from EFlags enum
+
+	//blit image with optional rotation, fitting into rect, etc
+	void blitImage(size_t frame, size_t group, SDL_Surface *to);
+
+	//For clipping in rect, offsets of picture coordinates
+	int xOffset, yOffset;
+
+	ui8 alpha;
+
+public:
+	//called when next animation sequence is required
+	std::function<void()> callback;
+
+	//Set per-surface alpha, 0 = transparent, 255 = opaque
+	void setAlpha(ui32 alphaValue);
+
+	CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0);
+	~CShowableAnim();
+
+	//set animation to group or part of group
+	bool set(size_t Group);
+	bool set(size_t Group, size_t from, size_t to=-1);
+
+	//set rotation flags
+	void rotate(bool on, bool vertical=false);
+
+	//move displayed part of picture (if picture is clipped to rect)
+	void clipRect(int posX, int posY, int width, int height);
+
+	//set frame to first, call callback
+	virtual void reset();
+
+	//show current frame and increase counter
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+};
+
+/// Creature-dependend animations like attacking, moving,...
+class CCreatureAnim: public CShowableAnim
+{
+public:
+
+	enum EHeroAnimType
+	{
+		HERO_HOLDING = 0,
+		HERO_IDLE = 1, // idling movement that happens from time to time
+		HERO_DEFEAT = 2, // played when army loses stack or on friendly fire
+		HERO_VICTORY = 3, // when enemy stack killed or huge damage is dealt
+		HERO_CAST_SPELL = 4 // spellcasting
+	};
+
+	enum EAnimType // list of creature animations, numbers were taken from def files
+	{
+		MOVING=0,
+		MOUSEON=1,
+		HOLDING=2,
+		HITTED=3,
+		DEFENCE=4,
+		DEATH=5,
+		//DEATH2=6, //unused?
+		TURN_L=7,
+		TURN_R=8, //same
+		//TURN_L2=9, //identical to previous?
+		//TURN_R2=10,
+		ATTACK_UP=11,
+		ATTACK_FRONT=12,
+		ATTACK_DOWN=13,
+		SHOOT_UP=14,
+		SHOOT_FRONT=15,
+		SHOOT_DOWN=16,
+		CAST_UP=17,
+		CAST_FRONT=18,
+		CAST_DOWN=19,
+		MOVE_START=20,
+		MOVE_END=21,
+		DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here
+
+	};
+
+private:
+	//queue of animations waiting to be displayed
+	std::queue<EAnimType> queue;
+
+	//this function is used as callback if preview flag was set during construction
+	void loopPreview(bool warMachine);
+
+public:
+	//change anim to next if queue is not empty, call callback othervice
+	void reset();
+
+	//add sequence to the end of queue
+	void addLast(EAnimType newType);
+
+	void startPreview(bool warMachine);
+
+	//clear queue and set animation to this sequence
+	void clearAndSet(EAnimType type);
+
+	CCreatureAnim(int x, int y, std::string name, Rect picPos,
+				  ui8 flags= USE_RLE, EAnimType = HOLDING );
+
+};

+ 442 - 0
client/widgets/MiscWidgets.cpp

@@ -0,0 +1,442 @@
+#include "StdInc.h"
+#include "MiscWidgets.h"
+
+#include "CComponent.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../gui/CCursorHandler.h"
+
+#include "../CBitmapHandler.h"
+#include "../CPlayerInterface.h"
+#include "../CMessage.h"
+#include "../CGameInfo.h"
+#include "../windows/CAdvmapInterface.h"
+#include "../windows/CCastleInterface.h"
+#include "../windows/InfoWindows.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CModHandler.h"
+#include "../../lib/CGameState.h"
+
+/*
+ * MiscWidgets.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
+ *
+ */
+
+void CHoverableArea::hover (bool on)
+{
+	if (on)
+		GH.statusbar->setText(hoverText);
+	else if (GH.statusbar->getText()==hoverText)
+		GH.statusbar->clear();
+}
+
+CHoverableArea::CHoverableArea()
+{
+	addUsedEvents(HOVER);
+}
+
+CHoverableArea::~CHoverableArea()
+{
+}
+
+void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
+{
+	if(!down && previousState && !text.empty())
+	{
+		LOCPLINT->showInfoDialog(text);
+	}
+}
+void LRClickableAreaWText::clickRight(tribool down, bool previousState)
+{
+	if (!text.empty())
+		adventureInt->handleRightClick(text, down);
+}
+
+LRClickableAreaWText::LRClickableAreaWText()
+{
+	init();
+}
+
+LRClickableAreaWText::LRClickableAreaWText(const Rect &Pos, const std::string &HoverText /*= ""*/, const std::string &ClickText /*= ""*/)
+{
+	init();
+	pos = Pos + pos;
+	hoverText = HoverText;
+	text = ClickText;
+}
+
+LRClickableAreaWText::~LRClickableAreaWText()
+{
+}
+
+void LRClickableAreaWText::init()
+{
+	addUsedEvents(LCLICK | RCLICK | HOVER);
+}
+
+void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
+{
+	if((!down) && previousState)
+	{
+		std::vector<CComponent*> comp(1, createComponent());
+		LOCPLINT->showInfoDialog(text, comp);
+	}
+}
+
+LRClickableAreaWTextComp::LRClickableAreaWTextComp(const Rect &Pos, int BaseType)
+	: LRClickableAreaWText(Pos), baseType(BaseType), bonusValue(-1)
+{
+}
+
+CComponent * LRClickableAreaWTextComp::createComponent() const
+{
+	if(baseType >= 0)
+		return new CComponent(CComponent::Etype(baseType), type, bonusValue);
+	else
+		return nullptr;
+}
+
+void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
+{
+	if(down)
+	{
+		if(CComponent *comp = createComponent())
+		{
+			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
+			return;
+		}
+	}
+
+	LRClickableAreaWText::clickRight(down, previousState); //only if with-component variant not occurred
+}
+
+CHeroArea::CHeroArea(int x, int y, const CGHeroInstance * _hero):hero(_hero)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	addUsedEvents(LCLICK | RCLICK | HOVER);
+	pos.x += x;	pos.w = 58;
+	pos.y += y;	pos.h = 64;
+
+	if (hero)
+		new CAnimImage("PortraitsLarge", hero->portrait);
+}
+
+void CHeroArea::clickLeft(tribool down, bool previousState)
+{
+	if((!down) && previousState && hero)
+		LOCPLINT->openHeroWindow(hero);
+}
+
+void CHeroArea::clickRight(tribool down, bool previousState)
+{
+	if((!down) && previousState && hero)
+		LOCPLINT->openHeroWindow(hero);
+}
+
+void CHeroArea::hover(bool on)
+{
+	if (on && hero)
+		GH.statusbar->setText(hero->getObjectName());
+	else
+		GH.statusbar->clear();
+}
+
+void LRClickableAreaOpenTown::clickLeft(tribool down, bool previousState)
+{
+	if((!down) && previousState && town)
+		{
+		LOCPLINT->openTownWindow(town);
+		if ( type == 2 )
+			LOCPLINT->castleInt->builds->buildingClicked(BuildingID::VILLAGE_HALL);
+		else if ( type == 3 && town->fortLevel() )
+			LOCPLINT->castleInt->builds->buildingClicked(BuildingID::FORT);
+		}
+}
+
+void LRClickableAreaOpenTown::clickRight(tribool down, bool previousState)
+{
+	if((!down) && previousState && town)
+		LOCPLINT->openTownWindow(town);//TODO: popup?
+}
+
+LRClickableAreaOpenTown::LRClickableAreaOpenTown()
+	: LRClickableAreaWTextComp(Rect(0,0,0,0), -1)
+{
+}
+
+void CMinorResDataBar::show(SDL_Surface * to)
+{
+}
+
+void CMinorResDataBar::showAll(SDL_Surface * to)
+{
+	blitAt(bg,pos.x,pos.y,to);
+	for (Res::ERes i=Res::WOOD; i<=Res::GOLD; vstd::advance(i, 1))
+	{
+		std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
+
+		graphics->fonts[FONT_SMALL]->renderTextCenter(to, text, Colors::WHITE, Point(pos.x + 50 + 76 * i, pos.y + pos.h/2));
+	}
+	std::vector<std::string> temp;
+
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::MONTH)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::WEEK)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(Date::DAY_OF_WEEK)));
+
+	std::string datetext =  CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+							+ ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
+
+	graphics->fonts[FONT_SMALL]->renderTextCenter(to, CSDL_Ext::processStr(datetext,temp), Colors::WHITE, Point(pos.x+545+(pos.w-545)/2,pos.y+pos.h/2));
+}
+
+CMinorResDataBar::CMinorResDataBar()
+{
+	bg = BitmapHandler::loadBitmap("KRESBAR.bmp");
+	CSDL_Ext::setDefaultColorKey(bg);
+	graphics->blueToPlayersAdv(bg,LOCPLINT->playerID);
+	pos.x = 7;
+	pos.y = 575;
+	pos.w = bg->w;
+	pos.h = bg->h;
+}
+
+CMinorResDataBar::~CMinorResDataBar()
+{
+	SDL_FreeSurface(bg);
+}
+
+void CArmyTooltip::init(const InfoAboutArmy &army)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	new CLabel(66, 2, FONT_SMALL, TOPLEFT, Colors::WHITE, army.name);
+
+	std::vector<Point> slotsPos;
+	slotsPos.push_back(Point(36,73));
+	slotsPos.push_back(Point(72,73));
+	slotsPos.push_back(Point(108,73));
+	slotsPos.push_back(Point(18,122));
+	slotsPos.push_back(Point(54,122));
+	slotsPos.push_back(Point(90,122));
+	slotsPos.push_back(Point(126,122));
+
+	for(auto & slot : army.army)
+	{
+		if(slot.first.getNum() >= GameConstants::ARMY_SIZE)
+		{
+			logGlobal->warnStream() << "Warning: " << army.name << " has stack in slot " << slot.first;
+			continue;
+		}
+
+		new CAnimImage("CPRSMALL", slot.second.type->iconIndex, 0, slotsPos[slot.first.getNum()].x, slotsPos[slot.first.getNum()].y);
+
+		std::string subtitle;
+		if(army.army.isDetailed)
+			subtitle = boost::lexical_cast<std::string>(slot.second.count);
+		else
+		{
+			//if =0 - we have no information about stack size at all
+			if (slot.second.count)
+				subtitle = CGI->generaltexth->arraytxt[171 + 3*(slot.second.count)];
+		}
+
+		new CLabel(slotsPos[slot.first.getNum()].x + 17, slotsPos[slot.first.getNum()].y + 41, FONT_TINY, CENTER, Colors::WHITE, subtitle);
+	}
+
+}
+
+CArmyTooltip::CArmyTooltip(Point pos, const InfoAboutArmy &army):
+	CIntObject(0, pos)
+{
+	init(army);
+}
+
+CArmyTooltip::CArmyTooltip(Point pos, const CArmedInstance * army):
+	CIntObject(0, pos)
+{
+	init(InfoAboutArmy(army, true));
+}
+
+void CHeroTooltip::init(const InfoAboutHero &hero)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CAnimImage("PortraitsLarge", hero.portrait, 0, 3, 2);
+
+	if(hero.details)
+	{
+		for (size_t i = 0; i < hero.details->primskills.size(); i++)
+			new CLabel(75 + 28 * i, 58, FONT_SMALL, CENTER, Colors::WHITE,
+					   boost::lexical_cast<std::string>(hero.details->primskills[i]));
+
+		new CLabel(158, 98, FONT_TINY, CENTER, Colors::WHITE,
+				   boost::lexical_cast<std::string>(hero.details->mana));
+
+		new CAnimImage("IMRL22", hero.details->morale + 3, 0, 5, 74);
+		new CAnimImage("ILCK22", hero.details->luck + 3, 0, 5, 91);
+	}
+}
+
+CHeroTooltip::CHeroTooltip(Point pos, const InfoAboutHero &hero):
+	CArmyTooltip(pos, hero)
+{
+	init(hero);
+}
+
+CHeroTooltip::CHeroTooltip(Point pos, const CGHeroInstance * hero):
+	CArmyTooltip(pos, InfoAboutHero(hero, true))
+{
+	init(InfoAboutHero(hero, true));
+}
+
+void CTownTooltip::init(const InfoAboutTown &town)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	//order of icons in def: fort, citadel, castle, no fort
+	size_t fortIndex = town.fortLevel ? town.fortLevel - 1 : 3;
+
+	new CAnimImage("ITMCLS", fortIndex, 0, 105, 31);
+
+	assert(town.tType);
+
+	size_t iconIndex = town.tType->clientInfo.icons[town.fortLevel > 0][town.built >= CGI->modh->settings.MAX_BUILDING_PER_TURN];
+
+	new CAnimImage("itpt", iconIndex, 0, 3, 2);
+
+	if(town.details)
+	{
+		new CAnimImage("ITMTLS", town.details->hallLevel, 0, 67, 31);
+
+		if (town.details->goldIncome)
+			new CLabel(157, 58, FONT_TINY, CENTER, Colors::WHITE,
+					   boost::lexical_cast<std::string>(town.details->goldIncome));
+
+		if(town.details->garrisonedHero) //garrisoned hero icon
+			new CPicture("TOWNQKGH", 149, 76);
+
+		if(town.details->customRes)//silo is built
+		{
+			if (town.tType->primaryRes == Res::WOOD_AND_ORE )// wood & ore
+			{
+				new CAnimImage("SMALRES", Res::WOOD, 0, 7, 75);
+				new CAnimImage("SMALRES", Res::ORE , 0, 7, 88);
+			}
+			else
+				new CAnimImage("SMALRES", town.tType->primaryRes, 0, 7, 81);
+		}
+	}
+}
+
+CTownTooltip::CTownTooltip(Point pos, const InfoAboutTown &town):
+	CArmyTooltip(pos, town)
+{
+	init(town);
+}
+
+CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town):
+	CArmyTooltip(pos, InfoAboutTown(town, true))
+{
+	init(InfoAboutTown(town, true));
+}
+
+
+void MoraleLuckBox::set(const IBonusBearer *node)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	const int textId[] = {62, 88}; //eg %s \n\n\n {Current Luck Modifiers:}
+	const int noneTxtId = 108; //Russian version uses same text for neutral morale\luck
+	const int neutralDescr[] = {60, 86}; //eg {Neutral Morale} \n\n Neutral morale means your armies will neither be blessed with extra attacks or freeze in combat.
+	const int componentType[] = {CComponent::luck, CComponent::morale};
+	const int hoverTextBase[] = {7, 4};
+	const Bonus::BonusType bonusType[] = {Bonus::LUCK, Bonus::MORALE};
+	int (IBonusBearer::*getValue[])() const = {&IBonusBearer::LuckVal, &IBonusBearer::MoraleVal};
+
+	int mrlt = -9;
+	TModDescr mrl;
+
+	if (node)
+	{
+		node->getModifiersWDescr(mrl, bonusType[morale]);
+		bonusValue = (node->*getValue[morale])();
+	}
+	else
+		bonusValue = 0;
+
+	mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
+	hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
+	baseType = componentType[morale];
+	text = CGI->generaltexth->arraytxt[textId[morale]];
+	boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
+	if (!mrl.size())
+		text += CGI->generaltexth->arraytxt[noneTxtId];
+	else
+	{
+		//it's a creature window
+		if ((morale && node->hasBonusOfType(Bonus::UNDEAD)) ||
+			node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING))
+		{
+			text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
+		}
+		else
+		{
+			for(auto & elem : mrl)
+			{
+				if (elem.first) //no bonuses with value 0
+					text += "\n" + elem.second;
+			}
+		}
+	}
+
+	std::string imageName;
+	if (small)
+		imageName = morale ? "IMRL30": "ILCK30";
+	else
+		imageName = morale ? "IMRL42" : "ILCK42";
+
+	delete image;
+	image = new CAnimImage(imageName, bonusValue + 3);
+	image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2));//center icon
+}
+
+MoraleLuckBox::MoraleLuckBox(bool Morale, const Rect &r, bool Small):
+	image(nullptr),
+	morale(Morale),
+	small(Small)
+{
+	bonusValue = 0;
+	pos = r + pos;
+}
+
+CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool Animated)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	pos.x+=x;
+	pos.y+=y;
+
+	TFaction faction = cre->faction;
+
+	assert(CGI->townh->factions.size() > faction);
+
+	if(Big)
+		bg = new CPicture(CGI->townh->factions[faction]->creatureBg130);
+	else
+		bg = new CPicture(CGI->townh->factions[faction]->creatureBg120);
+	bg->needRefresh = true;
+	anim = new CCreatureAnim(0, 0, cre->animDefName, Rect());
+	anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h);
+	anim->startPreview(cre->hasBonusOfType(Bonus::SIEGE_WEAPON));
+
+	pos.w = bg->pos.w;
+	pos.h = bg->pos.h;
+}

+ 150 - 0
client/widgets/MiscWidgets.h

@@ -0,0 +1,150 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+
+/*
+ * MiscWidgets.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 CCreatureAnim;
+class CComponent;
+class CGGarrison;
+class CSelectableComponent;
+class InfoAboutArmy;
+class CArmedInstance;
+class IBonusBearer;
+class CAnimImage;
+
+/// Shows a text by moving the mouse cursor over the object
+class CHoverableArea: public virtual CIntObject
+{
+public:
+	std::string hoverText;
+
+	virtual void hover (bool on);
+
+	CHoverableArea();
+	virtual ~CHoverableArea();
+};
+
+/// Can interact on left and right mouse clicks, plus it shows a text when by hovering over it
+class LRClickableAreaWText: public CHoverableArea
+{
+public:
+	std::string text;
+
+	LRClickableAreaWText();
+	LRClickableAreaWText(const Rect &Pos, const std::string &HoverText = "", const std::string &ClickText = "");
+	virtual ~LRClickableAreaWText();
+	void init();
+
+	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickRight(tribool down, bool previousState);
+};
+
+/// base class for hero/town/garrison tooltips
+class CArmyTooltip : public CIntObject
+{
+	void init(const InfoAboutArmy &army);
+public:
+	CArmyTooltip(Point pos, const InfoAboutArmy &army);
+	CArmyTooltip(Point pos, const CArmedInstance * army);
+};
+
+/// Class for hero tooltip. Does not have any background!
+/// background for infoBox: ADSTATHR
+/// background for tooltip: HEROQVBK
+class CHeroTooltip : public CArmyTooltip
+{
+	void init(const InfoAboutHero &hero);
+public:
+	CHeroTooltip(Point pos, const InfoAboutHero &hero);
+	CHeroTooltip(Point pos, const CGHeroInstance * hero);
+};
+
+/// Class for town tooltip. Does not have any background!
+/// background for infoBox: ADSTATCS
+/// background for tooltip: TOWNQVBK
+class CTownTooltip : public CArmyTooltip
+{
+	void init(const InfoAboutTown &town);
+public:
+	CTownTooltip(Point pos, const InfoAboutTown &town);
+	CTownTooltip(Point pos, const CGTownInstance * town);
+};
+
+/// draws picture with creature on background, use Animated=true to get animation
+class CCreaturePic : public CIntObject
+{
+private:
+	CPicture *bg;
+	CCreatureAnim *anim; //displayed animation
+
+public:
+	CCreaturePic(int x, int y, const CCreature *cre, bool Big=true, bool Animated=true); //c-tor
+};
+
+/// Resource bar like that at the bottom of the adventure map screen
+class CMinorResDataBar : public CIntObject
+{
+public:
+	SDL_Surface *bg; //background bitmap
+	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
+	CMinorResDataBar(); //c-tor
+	~CMinorResDataBar(); //d-tor
+};
+
+/// Opens hero window by left-clicking on it
+class CHeroArea: public CIntObject
+{
+	const CGHeroInstance * hero;
+public:
+
+	CHeroArea(int x, int y, const CGHeroInstance * _hero);
+
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+	void hover(bool on);
+};
+
+/// Can interact on left and right mouse clicks
+class LRClickableAreaWTextComp: public LRClickableAreaWText
+{
+public:
+	int baseType;
+	int bonusValue, type;
+	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickRight(tribool down, bool previousState);
+
+	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
+	CComponent * createComponent() const;
+};
+
+/// Opens town screen by left-clicking on it
+class LRClickableAreaOpenTown: public LRClickableAreaWTextComp
+{
+public:
+	const CGTownInstance * town;
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+	LRClickableAreaOpenTown();
+};
+
+class MoraleLuckBox : public LRClickableAreaWTextComp
+{
+	CAnimImage *image;
+public:
+	bool morale; //true if morale, false if luck
+	bool small;
+
+	void set(const IBonusBearer *node);
+
+	MoraleLuckBox(bool Morale, const Rect &r, bool Small=false);
+};

+ 234 - 0
client/widgets/ObjectLists.cpp

@@ -0,0 +1,234 @@
+#include "StdInc.h"
+#include "ObjectLists.h"
+
+#include "../gui/CGuiHandler.h"
+#include "Buttons.h"
+
+/*
+ * ObjectLists.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
+ *
+ */
+
+
+static void intDeleter(CIntObject* object)
+{
+	delete object;
+}
+
+CObjectList::CObjectList(CreateFunc create, DestroyFunc destroy):
+createObject(create),
+destroyObject(destroy)
+{
+	if (!destroyObject)
+		destroyObject = intDeleter;
+}
+
+void CObjectList::deleteItem(CIntObject* item)
+{
+	if (!item)
+		return;
+	removeChild(item);
+	destroyObject(item);
+}
+
+CIntObject* CObjectList::createItem(size_t index)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	CIntObject * item = createObject(index);
+	if (item == nullptr)
+		item = new CIntObject();
+
+	item->recActions = defActions;
+
+	addChild(item);
+	return item;
+}
+
+CTabbedInt::CTabbedInt(CreateFunc create, DestroyFunc destroy, Point position, size_t ActiveID):
+CObjectList(create, destroy),
+activeTab(nullptr),
+activeID(ActiveID)
+{
+	pos += position;
+	reset();
+}
+
+void CTabbedInt::setActive(size_t which)
+{
+	if (which != activeID)
+	{
+		activeID = which;
+		reset();
+	}
+}
+
+void CTabbedInt::reset()
+{
+	deleteItem(activeTab);
+	activeTab = createItem(activeID);
+	activeTab->moveTo(pos.topLeft());
+
+	if (active)
+		redraw();
+}
+
+CIntObject * CTabbedInt::getItem()
+{
+	return activeTab;
+}
+
+CListBox::CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+				   size_t TotalSize, size_t InitialPos, int Slider, Rect SliderPos):
+	CObjectList(create, destroy),
+	first(InitialPos),
+	totalSize(TotalSize),
+	itemOffset(ItemOffset),
+    slider(nullptr)
+{
+	pos += Pos;
+	items.resize(VisibleSize, nullptr);
+
+	if (Slider & 1)
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		slider = new CSlider(SliderPos.topLeft(), SliderPos.w, boost::bind(&CListBox::moveToPos, this, _1),
+			VisibleSize, TotalSize, InitialPos, Slider & 2, Slider & 4 ? CSlider::BLUE : CSlider::BROWN);
+	}
+	reset();
+}
+
+// Used to move active items after changing list position
+void CListBox::updatePositions()
+{
+	Point itemPos = pos.topLeft();
+	for (auto & elem : items)
+	{
+		(elem)->moveTo(itemPos);
+		itemPos += itemOffset;
+	}
+	if (active)
+	{
+		redraw();
+		if (slider)
+			slider->moveTo(first);
+	}
+}
+
+void CListBox::reset()
+{
+	size_t current = first;
+	for (auto & elem : items)
+	{
+		deleteItem(elem);
+		elem = createItem(current++);
+	}
+	updatePositions();
+}
+
+void CListBox::resize(size_t newSize)
+{
+	totalSize = newSize;
+	if (slider)
+		slider->setAmount(totalSize);
+	reset();
+}
+
+size_t CListBox::size()
+{
+	return totalSize;
+}
+
+CIntObject * CListBox::getItem(size_t which)
+{
+	if (which < first || which > first + items.size() || which > totalSize)
+		return nullptr;
+
+	size_t i=first;
+	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
+		if( i == which)
+			return *iter;
+	return nullptr;
+}
+
+size_t CListBox::getIndexOf(CIntObject *item)
+{
+	size_t i=first;
+	for (auto iter = items.begin(); iter != items.end(); iter++, i++)
+		if(*iter == item)
+			return i;
+	return size_t(-1);
+}
+
+void CListBox::scrollTo(size_t which)
+{
+	//scroll up
+	if (first > which)
+		moveToPos(which);
+	//scroll down
+	else if (first + items.size() <= which && which < totalSize)
+		moveToPos(which - items.size() + 1);
+}
+
+void CListBox::moveToPos(size_t which)
+{
+	//Calculate new position
+	size_t maxPossible;
+	if (totalSize > items.size())
+		maxPossible = totalSize - items.size();
+	else
+		maxPossible = 0;
+
+	size_t newPos = std::min(which, maxPossible);
+
+	//If move distance is 1 (most of calls from Slider) - use faster shifts instead of resetting all items
+	if (first - newPos == 1)
+		moveToPrev();
+	else if (newPos - first == 1)
+		moveToNext();
+	else if (newPos != first)
+	{
+		first = newPos;
+		reset();
+	}
+}
+
+void CListBox::moveToNext()
+{
+	//Remove front item and insert new one to end
+	if (first + items.size() < totalSize)
+	{
+		first++;
+		deleteItem(items.front());
+		items.pop_front();
+		items.push_back(createItem(first+items.size()));
+		updatePositions();
+	}
+}
+
+void CListBox::moveToPrev()
+{
+	//Remove last item and insert new one at start
+	if (first)
+	{
+		first--;
+		deleteItem(items.back());
+		items.pop_back();
+		items.push_front(createItem(first));
+		updatePositions();
+	}
+}
+
+size_t CListBox::getPos()
+{
+	return first;
+}
+
+const std::list<CIntObject *> &CListBox::getItems()
+{
+	return items;
+}

+ 111 - 0
client/widgets/ObjectLists.h

@@ -0,0 +1,111 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+
+struct SDL_Surface;
+struct Rect;
+class CAnimImage;
+class CSlider;
+class CLabel;
+class CAnimation;
+class CDefHandler;
+
+/*
+ * ObjectLists.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
+ *
+ */
+
+/// Used as base for Tabs and List classes
+class CObjectList : public CIntObject
+{
+public:
+	typedef std::function<CIntObject* (size_t)> CreateFunc;
+	typedef std::function<void(CIntObject *)> DestroyFunc;
+
+private:
+	CreateFunc createObject;
+	DestroyFunc destroyObject;
+
+protected:
+	//Internal methods for safe creation of items (Children capturing and activation/deactivation if needed)
+	void deleteItem(CIntObject* item);
+	CIntObject* createItem(size_t index);
+
+	CObjectList(CreateFunc create, DestroyFunc destroy = DestroyFunc());//Protected constructor
+};
+
+/// Window element with multiple tabs
+class CTabbedInt : public CObjectList
+{
+private:
+	CIntObject * activeTab;
+	size_t activeID;
+
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of object, all tabs will be moved to this position
+	//ActiveID - ID of initially active tab
+	CTabbedInt(CreateFunc create, DestroyFunc destroy = DestroyFunc(), Point position=Point(), size_t ActiveID=0);
+
+	void setActive(size_t which);
+	//recreate active tab
+	void reset();
+
+	//return currently active item
+	CIntObject * getItem();
+};
+
+/// List of IntObjects with optional slider
+class CListBox : public CObjectList
+{
+private:
+	std::list< CIntObject* > items;
+	size_t first;
+	size_t totalSize;
+
+	Point itemOffset;
+	CSlider * slider;
+
+	void updatePositions();
+public:
+	//CreateFunc, DestroyFunc - see CObjectList
+	//Pos - position of first item
+	//ItemOffset - distance between items in the list
+	//VisibleSize - maximal number of displayable at once items
+	//TotalSize
+	//Slider - slider style, bit field: 1 = present(disabled), 2=horisontal(vertical), 4=blue(brown)
+	//SliderPos - position of slider, if present
+	CListBox(CreateFunc create, DestroyFunc destroy, Point Pos, Point ItemOffset, size_t VisibleSize,
+		size_t TotalSize, size_t InitialPos=0, int Slider=0, Rect SliderPos=Rect() );
+
+	//recreate all visible items
+	void reset();
+
+	//change or get total amount of items in the list
+	void resize(size_t newSize);
+	size_t size();
+
+	//return item with index which or null if not present
+	CIntObject * getItem(size_t which);
+
+	//return currently active items
+	const std::list< CIntObject * > & getItems();
+
+	//get index of this item. -1 if not found
+	size_t getIndexOf(CIntObject * item);
+
+	//scroll list to make item which visible
+	void scrollTo(size_t which);
+
+	//scroll list to specified position
+	void moveToPos(size_t which);
+	void moveToNext();
+	void moveToPrev();
+
+	size_t getPos();
+};

+ 642 - 0
client/widgets/TextControls.cpp

@@ -0,0 +1,642 @@
+#include "StdInc.h"
+#include "TextControls.h"
+
+#include "Buttons.h"
+#include "Images.h"
+
+#include "../CMessage.h"
+#include "../gui/CGuiHandler.h"
+
+#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
+
+/*
+ * TextControls.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
+ *
+ */
+
+std::string CLabel::visibleText()
+{
+	return text;
+}
+
+void CLabel::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+
+	if(!visibleText().empty())
+		blitLine(to, pos, visibleText());
+
+}
+
+CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
+:CTextContainer(Align, Font, Color), text(Text)
+{
+	type |= REDRAW_PARENT;
+	autoRedraw = true;
+	pos.x += x;
+	pos.y += y;
+	pos.w = pos.h = 0;
+	bg = nullptr;
+
+	if (alignment == TOPLEFT) // causes issues for MIDDLE
+	{
+		pos.w = graphics->fonts[font]->getStringWidth(visibleText().c_str());
+		pos.h = graphics->fonts[font]->getLineHeight();
+	}
+}
+
+Point CLabel::getBorderSize()
+{
+	return Point(0, 0);
+}
+
+std::string CLabel::getText()
+{
+	return text;
+}
+
+void CLabel::setText(const std::string &Txt)
+{
+	text = Txt;
+	if(autoRedraw)
+	{
+		if(bg || !parent)
+			redraw();
+		else
+			parent->redraw();
+	}
+}
+
+CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text):
+    CLabel(position.x, position.y, Font, Align, Color, Text),
+    visibleSize(0, 0, position.w, position.h)
+{
+	pos.w = position.w;
+	pos.h = position.h;
+	splitText(Text);
+}
+
+void CMultiLineLabel::setVisibleSize(Rect visibleSize)
+{
+	this->visibleSize = visibleSize;
+	redraw();
+}
+
+void CMultiLineLabel::scrollTextBy(int distance)
+{
+	scrollTextTo(visibleSize.y + distance);
+}
+
+void CMultiLineLabel::scrollTextTo(int distance)
+{
+	Rect size = visibleSize;
+	size.y = distance;
+	setVisibleSize(size);
+}
+
+void CMultiLineLabel::setText(const std::string &Txt)
+{
+	splitText(Txt);
+	CLabel::setText(Txt);
+}
+
+void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what)
+{
+	const IFont * f = graphics->fonts[font];
+	Point where = destRect.topLeft();
+
+	// input is rect in which given text should be placed
+	// calculate proper position for top-left corner of the text
+	if (alignment == TOPLEFT)
+	{
+		where.x += getBorderSize().x;
+		where.y += getBorderSize().y;
+	}
+
+	if (alignment == CENTER)
+	{
+		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2;
+		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2;
+	}
+
+	if (alignment == BOTTOMRIGHT)
+	{
+		where.x += getBorderSize().x + destRect.w - f->getStringWidth(what);
+		where.y += getBorderSize().y + destRect.h - f->getLineHeight();
+	}
+
+	size_t begin = 0;
+	std::string delimeters = "{}";
+	size_t currDelimeter = 0;
+
+	do
+	{
+		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin);
+		if (begin != end)
+		{
+			std::string toPrint = what.substr(begin, end - begin);
+
+			if (currDelimeter % 2) // Enclosed in {} text - set to yellow
+				f->renderTextLeft(to, toPrint, Colors::YELLOW, where);
+			else // Non-enclosed text, use default color
+				f->renderTextLeft(to, toPrint, color, where);
+			begin = end;
+
+			where.x += f->getStringWidth(toPrint);
+		}
+		currDelimeter++;
+	}
+	while (begin++ != std::string::npos);
+}
+
+CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color):
+	alignment(alignment),
+	font(font),
+	color(color)
+{}
+
+void CMultiLineLabel::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+
+	const IFont * f = graphics->fonts[font];
+
+	// calculate which lines should be visible
+	int totalLines = lines.size();
+	int beginLine  = visibleSize.y;
+	int endLine    = getTextLocation().h + visibleSize.y;
+
+	if (beginLine < 0)
+		beginLine = 0;
+	else
+		beginLine /= f->getLineHeight();
+
+	if (endLine < 0)
+		endLine = 0;
+	else
+		endLine /= f->getLineHeight();
+	endLine++;
+
+	// and where they should be displayed
+	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * f->getLineHeight());
+	Point lineSize  = Point(getTextLocation().w, f->getLineHeight());
+
+	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
+
+	for (int i = beginLine; i < std::min(totalLines, endLine); i++)
+	{
+		if (!lines[i].empty()) //non-empty line
+			blitLine(to, Rect(lineStart, lineSize), lines[i]);
+
+		lineStart.y += f->getLineHeight();
+	}
+}
+
+void CMultiLineLabel::splitText(const std::string &Txt)
+{
+	lines.clear();
+
+	const IFont * f = graphics->fonts[font];
+	int lineHeight =  f->getLineHeight();
+
+	lines = CMessage::breakText(Txt, pos.w, font);
+
+	 textSize.y = lineHeight * lines.size();
+	 textSize.x = 0;
+	for(const std::string &line : lines)
+		vstd::amax( textSize.x, f->getStringWidth(line.c_str()));
+	redraw();
+}
+
+Rect CMultiLineLabel::getTextLocation()
+{
+	// this method is needed for vertical alignment alignment of text
+	// when height of available text is smaller than height of widget
+	// in this case - we should add proper offset to display text at required position
+	if (pos.h <= textSize.y)
+		return pos;
+
+	Point textSize(pos.w, graphics->fonts[font]->getLineHeight() * lines.size());
+	Point textOffset(pos.w - textSize.x, pos.h - textSize.y);
+
+	switch(alignment)
+	{
+	case TOPLEFT:     return Rect(pos.topLeft(), textSize);
+	case CENTER:      return Rect(pos.topLeft() + textOffset / 2, textSize);
+	case BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
+	}
+	assert(0);
+	return Rect();
+}
+
+CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
+	font(Font), align(Align), color(Color)
+{}
+
+void CLabelGroup::add(int x, int y, const std::string &text)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CLabel(x, y, font, align, color, text);
+}
+
+CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= TOPLEFT*/, const SDL_Color &Color /*= Colors::WHITE*/):
+    sliderStyle(SliderStyle),
+    slider(nullptr)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	label = new CMultiLineLabel(rect, Font, Align, Color);
+
+	type |= REDRAW_PARENT;
+	pos.x += rect.x;
+	pos.y += rect.y;
+	pos.h = rect.h;
+	pos.w = rect.w;
+
+	assert(pos.w >= 40); //we need some space
+	setText(Text);
+}
+
+void CTextBox::sliderMoved(int to)
+{
+	label->scrollTextTo(to);
+}
+
+void CTextBox::resize(Point newSize)
+{
+	pos.w = newSize.x;
+	pos.h = newSize.y;
+	label->pos.w = pos.w;
+	label->pos.h = pos.h;
+	if (slider)
+		vstd::clear_pointer(slider); // will be recreated if needed later
+
+	setText(label->getText()); // force refresh
+}
+
+void CTextBox::setText(const std::string &text)
+{
+	label->setText(text);
+	if (label->textSize.y <= label->pos.h && slider)
+	{
+		// slider is no longer needed
+		vstd::clear_pointer(slider);
+		label->pos.w = pos.w;
+		label->setText(text);
+	}
+	else if (label->textSize.y > label->pos.h && !slider)
+	{
+		// create slider and update widget
+		label->pos.w = pos.w - 32;
+		label->setText(text);
+
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		slider = new CSlider(Point(pos.w - 32, 0), pos.h, boost::bind(&CTextBox::sliderMoved, this, _1),
+		                     label->pos.h, label->textSize.y, 0, false, CSlider::EStyle(sliderStyle));
+		slider->setScrollStep(graphics->fonts[label->font]->getLineHeight());
+	}
+}
+
+void CGStatusBar::setText(const std::string & Text)
+{
+	if(!textLock)
+		CLabel::setText(Text);
+}
+
+void CGStatusBar::clear()
+{
+	setText("");
+}
+
+CGStatusBar::CGStatusBar(CPicture *BG, EFonts Font /*= FONT_SMALL*/, EAlignment Align /*= CENTER*/, const SDL_Color &Color /*= Colors::WHITE*/)
+: CLabel(BG->pos.x, BG->pos.y, Font, Align, Color, "")
+{
+	init();
+	bg = BG;
+	addChild(bg);
+	pos = bg->pos;
+	getBorderSize();
+    textLock = false;
+}
+
+CGStatusBar::CGStatusBar(int x, int y, std::string name/*="ADROLLVR.bmp"*/, int maxw/*=-1*/)
+: CLabel(x, y, FONT_SMALL, CENTER)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	init();
+	bg = new CPicture(name);
+	pos = bg->pos;
+	if((unsigned int)maxw < pos.w)
+	{
+		vstd::amin(pos.w, maxw);
+		bg->srcRect = new Rect(0, 0, maxw, pos.h);
+	}
+    textLock = false;
+}
+
+CGStatusBar::~CGStatusBar()
+{
+	GH.statusbar = oldStatusBar;
+}
+
+void CGStatusBar::show(SDL_Surface * to)
+{
+    showAll(to);
+}
+
+void CGStatusBar::init()
+{
+	oldStatusBar = GH.statusbar;
+	GH.statusbar = this;
+}
+
+Point CGStatusBar::getBorderSize()
+{
+	//Width of borders where text should not be printed
+	static const Point borderSize(5,1);
+
+	switch(alignment)
+	{
+	case TOPLEFT:     return Point(borderSize.x, borderSize.y);
+	case CENTER:      return Point(pos.w/2, pos.h/2);
+	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
+	}
+	assert(0);
+	return Point();
+}
+
+void CGStatusBar::lock(bool shouldLock)
+{
+    textLock = shouldLock;
+}
+
+CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB):
+    CLabel(Pos.x, Pos.y, font, CENTER),
+    cb(CB)
+{
+	type |= REDRAW_PARENT;
+	focus = false;
+	pos.h = Pos.h;
+	pos.w = Pos.w;
+	captureAllKeys = true;
+	bg = nullptr;
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB )
+:cb(CB)
+{
+	focus = false;
+	pos += Pos;
+	captureAllKeys = true;
+	OBJ_CONSTRUCTION;
+	bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
+{
+	focus = false;
+	pos += Pos;
+	captureAllKeys = true;
+	OBJ_CONSTRUCTION;
+	bg = new CPicture(Pos, 0, true);
+	Rect hlp = Pos;
+	if(srf)
+		CSDL_Ext::blitSurface(srf, &hlp, *bg, nullptr);
+	else
+		SDL_FillRect(*bg, nullptr, 0);
+	pos.w = bg->pos.w;
+	pos.h = bg->pos.h;
+	bg->pos = pos;
+	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
+	giveFocus();
+}
+
+void CTextInput::focusGot()
+{
+	CSDL_Ext::startTextInput(&pos);	
+}
+
+void CTextInput::focusLost()
+{
+	CSDL_Ext::stopTextInput();
+}
+
+
+std::string CTextInput::visibleText()
+{
+	return focus ? text + newText + "_" : text;
+}
+
+void CTextInput::clickLeft( tribool down, bool previousState )
+{
+	if(down && !focus)
+		giveFocus();
+}
+
+void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
+{
+
+	if(!focus || key.state != SDL_PRESSED)
+		return;
+
+	if(key.keysym.sym == SDLK_TAB)
+	{
+		moveFocus();
+		GH.breakEventHandling();
+		return;
+	}
+
+	bool redrawNeeded = false;
+	#ifdef VCMI_SDL1
+	std::string oldText = text;
+	#endif // 0	
+	switch(key.keysym.sym)
+	{
+	case SDLK_DELETE: // have index > ' ' so it won't be filtered out by default section
+		return;
+	case SDLK_BACKSPACE:
+		if(!newText.empty())
+		{
+			Unicode::trimRight(newText);
+			redrawNeeded = true;
+		}
+		else if(!text.empty())
+		{
+			Unicode::trimRight(text);
+			redrawNeeded = true;
+		}			
+		break;
+	default:
+		#ifdef VCMI_SDL1
+		if (key.keysym.unicode < ' ')
+			return;
+		else
+		{
+			text += key.keysym.unicode; //TODO 16-/>8
+			redrawNeeded = true;
+		}			
+		#endif // 0
+		break;
+	}
+	#ifdef VCMI_SDL1
+	filters(text, oldText);
+	#endif // 0
+	if (redrawNeeded)
+	{
+		redraw();
+		cb(text);
+	}	
+}
+
+void CTextInput::setText( const std::string &nText, bool callCb )
+{
+	CLabel::setText(nText);
+	if(callCb)
+		cb(text);
+}
+
+bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
+{
+	if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
+		return false;
+	
+	#ifdef VCMI_SDL1
+	//this should allow all non-printable keys to go through (for example arrows)
+	if (key.keysym.unicode < ' ')
+		return false;
+
+	return true;
+	#else
+	return false;
+	#endif
+}
+
+#ifndef VCMI_SDL1
+void CTextInput::textInputed(const SDL_TextInputEvent & event)
+{
+	if(!focus)
+		return;
+	std::string oldText = text;
+	
+	text += event.text;	
+	
+	filters(text,oldText);
+	if (text != oldText)
+	{
+		redraw();
+		cb(text);
+	}	
+	newText = "";
+}
+
+void CTextInput::textEdited(const SDL_TextEditingEvent & event)
+{
+	if(!focus)
+		return;
+		
+	newText = event.text;
+	redraw();
+	cb(text+newText);	
+}
+
+#endif
+
+
+void CTextInput::filenameFilter(std::string & text, const std::string &)
+{
+	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
+	size_t pos;
+	while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
+		text.erase(pos, 1);
+}
+
+void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
+{
+	assert(minValue < maxValue);
+
+	if (text.empty())
+		text = "0";
+
+	size_t pos = 0;
+	if (text[0] == '-') //allow '-' sign as first symbol only
+		pos++;
+
+	while (pos < text.size())
+	{
+		if (text[pos] < '0' || text[pos] > '9')
+		{
+			text = oldText;
+			return; //new text is not number.
+		}
+		pos++;
+	}
+	try
+	{
+		int value = boost::lexical_cast<int>(text);
+		if (value < minValue)
+			text = boost::lexical_cast<std::string>(minValue);
+		else if (value > maxValue)
+			text = boost::lexical_cast<std::string>(maxValue);
+	}
+	catch(boost::bad_lexical_cast &)
+	{
+		//Should never happen. Unless I missed some cases
+        logGlobal->warnStream() << "Warning: failed to convert "<< text << " to number!";
+		text = oldText;
+	}
+}
+
+CFocusable::CFocusable()
+{
+	focusables.push_back(this);
+}
+
+CFocusable::~CFocusable()
+{
+	if(inputWithFocus == this)
+	{
+		focusLost();
+		inputWithFocus = nullptr;
+	}	
+
+	focusables -= this;
+}
+void CFocusable::giveFocus()
+{
+	if(inputWithFocus)
+	{
+		inputWithFocus->focus = false;
+		inputWithFocus->focusLost();
+		inputWithFocus->redraw();
+	}
+
+	focus = true;
+	inputWithFocus = this;
+	focusGot();
+	redraw();	
+}
+
+void CFocusable::moveFocus()
+{
+	auto i = vstd::find(focusables, this),
+		ourIt = i;
+	for(i++; i != ourIt; i++)
+	{
+		if(i == focusables.end())
+			i = focusables.begin();
+
+		if((*i)->active)
+		{
+			(*i)->giveFocus();
+			break;;
+		}
+	}
+}

+ 189 - 0
client/widgets/TextControls.h

@@ -0,0 +1,189 @@
+#pragma once
+
+#include "../gui/CIntObject.h"
+#include "../gui/SDL_Extensions.h"
+#include "../../lib/FunctionList.h"
+
+/*
+ * TextControls.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 CSlider;
+
+/// Base class for all text-related widgets.
+/// Controls text blitting-related options
+class CTextContainer : public virtual CIntObject
+{
+protected:
+	/// returns size of border, for left- or right-aligned text
+	virtual Point getBorderSize() = 0;
+	/// do actual blitting of line. Text "what" will be placed at "where" and aligned according to alignment
+	void blitLine(SDL_Surface * to, Rect where, std::string what);
+
+	CTextContainer(EAlignment alignment, EFonts font, SDL_Color color);
+
+public:
+	EAlignment alignment;
+	EFonts font;
+	SDL_Color color; // default font color. Can be overridden by placing "{}" into the string
+};
+
+/// Label which shows text
+class CLabel : public CTextContainer
+{
+protected:
+	Point getBorderSize() override;
+	virtual std::string visibleText();
+
+	CPicture *bg;
+public:
+
+	std::string text;
+	bool autoRedraw;  //whether control will redraw itself on setTxt
+
+	std::string getText();
+	virtual void setText(const std::string &Txt);
+
+	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT,
+	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
+	void showAll(SDL_Surface * to); //shows statusbar (with current text)
+};
+
+/// Small helper class to manage group of similar labels
+class CLabelGroup : public CIntObject
+{
+	std::list<CLabel*> labels;
+	EFonts font;
+	EAlignment align;
+	SDL_Color color;
+public:
+	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
+	void add(int x=0, int y=0, const std::string &text =  "");
+};
+
+/// Multi-line label that can display multiple lines of text
+/// If text is too big to fit into requested area remaining part will not be visible
+class CMultiLineLabel : public CLabel
+{
+	// text to blit, split into lines that are no longer than widget width
+	std::vector<std::string> lines;
+
+	// area of text that actually will be printed, default is widget size
+	Rect visibleSize;
+
+	void splitText(const std::string &Txt);
+	Rect getTextLocation();
+public:
+	// total size of text, x = longest line of text, y = total height of lines
+	Point textSize;
+
+	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  "");
+
+	void setText(const std::string &Txt);
+	void showAll(SDL_Surface * to);
+
+	void setVisibleSize(Rect visibleSize);
+	// scrolls text visible in widget. Positive value will move text up
+	void scrollTextTo(int distance);
+	void scrollTextBy(int distance);
+};
+
+/// a multi-line label that tries to fit text with given available width and height;
+/// if not possible, it creates a slider for scrolling text
+class CTextBox : public CIntObject
+{
+	int sliderStyle;
+public:
+	CMultiLineLabel * label;
+	CSlider *slider;
+
+	CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE);
+
+	void resize(Point newSize);
+	void setText(const std::string &Txt);
+	void sliderMoved(int to);
+};
+
+/// Status bar which is shown at the bottom of the in-game screens
+class CGStatusBar : public CLabel
+{
+	bool textLock; //Used for blocking changes to the text
+	void init();
+
+	CGStatusBar *oldStatusBar;
+protected:
+	Point getBorderSize() override;
+
+public:
+
+	void clear();//clears statusbar and refreshes
+	void setText(const std::string & Text) override; //prints text and refreshes statusbar
+
+	void show(SDL_Surface * to); //shows statusbar (with current text)
+
+	CGStatusBar(CPicture *BG, EFonts Font = FONT_SMALL, EAlignment Align = CENTER, const SDL_Color &Color = Colors::WHITE); //given CPicture will be captured by created sbar and it's pos will be used as pos for sbar
+	CGStatusBar(int x, int y, std::string name, int maxw=-1);
+	~CGStatusBar();
+
+	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
+};
+
+/// UIElement which can get input focus
+class CFocusable : public virtual CIntObject
+{
+protected:
+	virtual void focusGot(){};
+	virtual void focusLost(){};
+public:
+	bool focus; //only one focusable control can have focus at one moment
+
+	void giveFocus(); //captures focus
+	void moveFocus(); //moves focus to next active control (may be used for tab switching)
+
+	static std::list<CFocusable*> focusables; //all existing objs
+	static CFocusable *inputWithFocus; //who has focus now
+	CFocusable();
+	~CFocusable();
+};
+
+/// Text input box where players can enter text
+class CTextInput : public CLabel, public CFocusable
+{
+	std::string newText;
+protected:
+	std::string visibleText() override;
+
+	void focusGot() override;
+	void focusLost() override;
+public:
+	CFunctionList<void(const std::string &)> cb;
+	CFunctionList<void(std::string &, const std::string &)> filters;
+	void setText(const std::string &nText, bool callCb = false);
+
+	CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB);
+	CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB);
+	CTextInput(const Rect &Pos, SDL_Surface *srf = nullptr);
+
+	void clickLeft(tribool down, bool previousState) override;
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
+	
+#ifndef VCMI_SDL1
+	void textInputed(const SDL_TextInputEvent & event) override;
+	void textEdited(const SDL_TextEditingEvent & event) override;
+	
+	
+#endif // VCMI_SDL1	
+
+	//Filter that will block all characters not allowed in filenames
+	static void filenameFilter(std::string &text, const std::string & oldText);
+	//Filter that will allow only input of numbers in range min-max (min-max are allowed)
+	//min-max should be set via something like boost::bind
+	static void numberFilter(std::string &text, const std::string & oldText, int minValue, int maxValue);
+};

+ 128 - 83
client/CAdvmapInterface.cpp → client/windows/CAdvmapInterface.cpp

@@ -1,36 +1,43 @@
 #include "StdInc.h"
 #include "CAdvmapInterface.h"
 
-#include "../CCallback.h"
 #include "CCastleInterface.h"
-#include "gui/CCursorHandler.h"
-#include "CGameInfo.h"
 #include "CHeroWindow.h"
 #include "CKingdomInterface.h"
-#include "CMessage.h"
-#include "CPlayerInterface.h"
-#include "gui/SDL_Extensions.h"
-#include "CBitmapHandler.h"
-#include "../lib/CConfigHandler.h"
 #include "CSpellWindow.h"
-#include "Graphics.h"
-#include "CDefHandler.h"
-#include "../lib/CGeneralTextHandler.h"
-#include "../lib/CHeroHandler.h"
-#include "../lib/mapObjects/CGHeroInstance.h"
-#include "../lib/CTownHandler.h"
-#include "../lib/mapping/CMap.h"
-#include "../lib/JsonNode.h"
-#include "mapHandler.h"
-#include "CPreGame.h"
-#include "../lib/VCMI_Lib.h"
-#include "../lib/CSpellHandler.h"
-#include "../lib/CSoundBase.h"
-#include "../lib/CGameState.h"
-#include "CMusicHandler.h"
-#include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
-#include "../lib/UnlockGuard.h"
+#include "GUIClasses.h"
+#include "CTradeWindow.h"
+
+#include "../CBitmapHandler.h"
+#include "../CDefHandler.h"
+#include "../CGameInfo.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+#include "../CPlayerInterface.h"
+#include "../CPreGame.h"
+#include "../Graphics.h"
+#include "../mapHandler.h"
+
+#include "../gui/CCursorHandler.h"
+#include "../gui/CGuiHandler.h"
+#include "../gui/SDL_Extensions.h"
+#include "../widgets/MiscWidgets.h"
+#include "../windows/InfoWindows.h"
+
+#include "../../CCallback.h"
+
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/CGameState.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CHeroHandler.h"
+#include "../../lib/CSoundBase.h"
+#include "../../lib/CSpellHandler.h"
+#include "../../lib/CTownHandler.h"
+#include "../../lib/JsonNode.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapping/CMap.h"
+#include "../../lib/UnlockGuard.h"
+#include "../../lib/VCMI_Lib.h"
 
 #ifdef _MSC_VER
 #pragma warning (disable : 4355)
@@ -363,6 +370,7 @@ void CResDataBar::showAll(SDL_Surface * to)
 CAdvMapInt::CAdvMapInt():
     minimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH)),
 statusbar(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG),
+<<<<<<< HEAD:client/CAdvmapInterface.cpp
 kingOverview(CGI->generaltexth->zelp[293].first,CGI->generaltexth->zelp[293].second,
 			 std::bind(&CAdvMapInt::fshowOverview,this),&ADVOPT.kingOverview, SDLK_k),
 
@@ -392,6 +400,8 @@ nextHero(CGI->generaltexth->zelp[301].first,CGI->generaltexth->zelp[301].second,
 
 endTurn(CGI->generaltexth->zelp[302].first,CGI->generaltexth->zelp[302].second,
 		  std::bind(&CAdvMapInt::fendTurn,this), &ADVOPT.endTurn, SDLK_e),
+=======
+>>>>>>> refactoring/guiClasses:client/windows/CAdvmapInterface.cpp
 
 heroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD),
 townList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD),
@@ -420,9 +430,28 @@ infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192) )
 		gems.push_back(CDefHandler::giveDef(ADVOPT.gemG[g]));
 	}
 
+	auto makeButton = [&] (int textID, std::function<void()> callback, config::ButtonInfo info, int key) -> CButton *
+	{
+		auto button = new CButton(Point(info.x, info.y), info.defName, CGI->generaltexth->zelp[textID], callback, key, info.playerColoured);
+
+		for (auto image : info.additionalDefs)
+			button->addImage(image);
+		return button;
+	};
+
+	kingOverview = makeButton(293, boost::bind(&CAdvMapInt::fshowOverview,this),     ADVOPT.kingOverview, SDLK_k);
+	underground  = makeButton(294, boost::bind(&CAdvMapInt::fswitchLevel,this),      ADVOPT.underground,  SDLK_u);
+	questlog     = makeButton(295, boost::bind(&CAdvMapInt::fshowQuestlog,this),     ADVOPT.questlog,     SDLK_q);
+	sleepWake    = makeButton(296, boost::bind(&CAdvMapInt::fsleepWake,this),        ADVOPT.sleepWake,    SDLK_w);
+	moveHero     = makeButton(297, boost::bind(&CAdvMapInt::fmoveHero,this),         ADVOPT.moveHero,     SDLK_m);
+	spellbook    = makeButton(298, boost::bind(&CAdvMapInt::fshowSpellbok,this),     ADVOPT.spellbook,    SDLK_c);
+	advOptions   = makeButton(299, boost::bind(&CAdvMapInt::fadventureOPtions,this), ADVOPT.advOptions,   SDLK_a);
+	sysOptions   = makeButton(300, boost::bind(&CAdvMapInt::fsystemOptions,this),    ADVOPT.sysOptions,   SDLK_o);
+	nextHero     = makeButton(301, boost::bind(&CAdvMapInt::fnextHero,this),         ADVOPT.nextHero,     SDLK_h);
+	endTurn      = makeButton(302, boost::bind(&CAdvMapInt::fendTurn,this),          ADVOPT.endTurn,      SDLK_e);
 
 	setPlayer(LOCPLINT->playerID);
-	underground.block(!CGI->mh->map->twoLevel);
+	underground->block(!CGI->mh->map->twoLevel);
 	addUsedEvents(MOVE);
 }
 
@@ -445,14 +474,14 @@ void CAdvMapInt::fswitchLevel()
 	if (position.z)
 	{
 		position.z--;
-		underground.setIndex(0,true);
-		underground.showAll(screenBuf);
+		underground->setIndex(0,true);
+		underground->showAll(screenBuf);
 	}
 	else
 	{
-		underground.setIndex(1,true);
+		underground->setIndex(1,true);
 		position.z++;
-		underground.showAll(screenBuf);
+		underground->showAll(screenBuf);
 	}
 	updateScreen = true;
 	minimap.setLevel(position.z);
@@ -537,14 +566,13 @@ void CAdvMapInt::fendTurn()
 
 void CAdvMapInt::updateSleepWake(const CGHeroInstance *h)
 {
-	sleepWake.block(!h);
+	sleepWake->block(!h);
 	if (!h)
 		return;
 	bool state = isHeroSleeping(h);
-	sleepWake.setIndex(state ? 1 : 0, true);
-	sleepWake.assignedKeys.clear();
-	sleepWake.assignedKeys.insert(state ? SDLK_w : SDLK_z);
-	sleepWake.update();
+	sleepWake->setIndex(state ? 1 : 0, true);
+	sleepWake->assignedKeys.clear();
+	sleepWake->assignedKeys.insert(state ? SDLK_w : SDLK_z);
 }
 
 void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath)
@@ -554,10 +582,10 @@ void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath)
 		 hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
 	if (!h)
 	{
-		moveHero.block(true);
+		moveHero->block(true);
 		return;
 	}
-	moveHero.block(!hasPath || (h->movement == 0));
+	moveHero->block(!hasPath || (h->movement == 0));
 }
 
 int CAdvMapInt::getNextHeroIndex(int startIndex)
@@ -587,12 +615,12 @@ void CAdvMapInt::updateNextHero(const CGHeroInstance *h)
 	int next = getNextHeroIndex(start);
 	if (next < 0)
 	{
-		nextHero.block(true);
+		nextHero->block(true);
 		return;
 	}
 	const CGHeroInstance *nextH = LOCPLINT->wanderingHeroes[next];
 	bool noActiveHeroes = (next == start) && ((nextH->movement == 0) || isHeroSleeping(nextH));
-	nextHero.block(noActiveHeroes);
+	nextHero->block(noActiveHeroes);
 }
 
 void CAdvMapInt::activate()
@@ -605,16 +633,16 @@ void CAdvMapInt::activate()
 	GH.statusbar = &statusbar;
 	if(!duringAITurn)
 	{
-		kingOverview.activate();
-		underground.activate();
-		questlog.activate();
-		sleepWake.activate();
-		moveHero.activate();
-		spellbook.activate();
-		sysOptions.activate();
-		advOptions.activate();
-		nextHero.activate();
-		endTurn.activate();
+		kingOverview->activate();
+		underground->activate();
+		questlog->activate();
+		sleepWake->activate();
+		moveHero->activate();
+		spellbook->activate();
+		sysOptions->activate();
+		advOptions->activate();
+		nextHero->activate();
+		endTurn->activate();
 
 		minimap.activate();
 		heroList.activate();
@@ -635,16 +663,16 @@ void CAdvMapInt::deactivate()
 		scrollingDir = 0;
 
 		CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
-		kingOverview.deactivate();
-		underground.deactivate();
-		questlog.deactivate();
-		sleepWake.deactivate();
-		moveHero.deactivate();
-		spellbook.deactivate();
-		advOptions.deactivate();
-		sysOptions.deactivate();
-		nextHero.deactivate();
-		endTurn.deactivate();
+		kingOverview->deactivate();
+		underground->deactivate();
+		questlog->deactivate();
+		sleepWake->deactivate();
+		moveHero->deactivate();
+		spellbook->deactivate();
+		advOptions->deactivate();
+		sysOptions->deactivate();
+		nextHero->deactivate();
+		endTurn->deactivate();
 		minimap.deactivate();
 		heroList.deactivate();
 		townList.deactivate();
@@ -661,16 +689,16 @@ void CAdvMapInt::showAll(SDL_Surface * to)
 	if(state != INGAME)
 		return;
 
-	kingOverview.showAll(to);
-	underground.showAll(to);
-	questlog.showAll(to);
-	sleepWake.showAll(to);
-	moveHero.showAll(to);
-	spellbook.showAll(to);
-	advOptions.showAll(to);
-	sysOptions.showAll(to);
-	nextHero.showAll(to);
-	endTurn.showAll(to);
+	kingOverview->showAll(to);
+	underground->showAll(to);
+	questlog->showAll(to);
+	sleepWake->showAll(to);
+	moveHero->showAll(to);
+	spellbook->showAll(to);
+	advOptions->showAll(to);
+	sysOptions->showAll(to);
+	nextHero->showAll(to);
+	endTurn->showAll(to);
 
 	minimap.showAll(to);
 	heroList.showAll(to);
@@ -781,8 +809,8 @@ void CAdvMapInt::centerOn(int3 on)
 
 	position = on;
 	updateScreen=true;
-	underground.setIndex(on.z,true); //change underground switch button image
-	underground.redraw();
+	underground->setIndex(on.z,true); //change underground switch button image
+	underground->redraw();
 	if (switchedLevels)
 		minimap.setLevel(position.z);
 }
@@ -1096,16 +1124,16 @@ void CAdvMapInt::setPlayer(PlayerColor Player)
 	player = Player;
 	graphics->blueToPlayersAdv(bg,player);
 
-	kingOverview.setPlayerColor(player);
-	underground.setPlayerColor(player);
-	questlog.setPlayerColor(player);
-	sleepWake.setPlayerColor(player);
-	moveHero.setPlayerColor(player);
-	spellbook.setPlayerColor(player);
-	sysOptions.setPlayerColor(player);
-	advOptions.setPlayerColor(player);
-	nextHero.setPlayerColor(player);
-	endTurn.setPlayerColor(player);
+	kingOverview->setPlayerColor(player);
+	underground->setPlayerColor(player);
+	questlog->setPlayerColor(player);
+	sleepWake->setPlayerColor(player);
+	moveHero->setPlayerColor(player);
+	spellbook->setPlayerColor(player);
+	sysOptions->setPlayerColor(player);
+	advOptions->setPlayerColor(player);
+	nextHero->setPlayerColor(player);
+	endTurn->setPlayerColor(player);
 	graphics->blueToPlayersAdv(resdatabar.bg,player);
 
 	//heroList.updateHList();
@@ -1525,10 +1553,11 @@ void CAdvMapInt::adjustActiveness(bool aiTurnStart)
 }
 
 CAdventureOptions::CAdventureOptions():
-    CWindowObject(PLAYER_COLORED, "ADVOPTS")
+	CWindowObject(PLAYER_COLORED, "ADVOPTS")
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
+<<<<<<< HEAD:client/CAdvmapInterface.cpp
 	exit = new CAdventureMapButton("","",std::bind(&CAdventureOptions::close, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
@@ -1542,6 +1571,22 @@ CAdventureOptions::CAdventureOptions():
 	dig = new CAdventureMapButton("","", std::bind(&CAdventureOptions::close, this), 24, 139, "ADVDIG.DEF");
 	if(const CGHeroInstance *h = adventureInt->curHero())
 		dig->callback += std::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h);
+=======
+	exit = new CButton(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), boost::bind(&CAdventureOptions::close, this), SDLK_RETURN);
+	exit->assignedKeys.insert(SDLK_ESCAPE);
+
+	scenInfo = new CButton(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&]{ close(); }, SDLK_i);
+	scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
+
+	//viewWorld = new CButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
+
+	puzzle = new CButton(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&]{ close(); }, SDLK_p);
+	puzzle->addCallback(boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
+
+	dig = new CButton(Point(24, 139), "ADVDIG.DEF", CButton::tooltip(), [&]{ close(); }, SDLK_d);
+	if(const CGHeroInstance *h = adventureInt->curHero())
+		dig->addCallback(boost::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h));
+>>>>>>> refactoring/guiClasses:client/windows/CAdvmapInterface.cpp
 	else
 		dig->block(true);
 }

+ 15 - 16
client/CAdvmapInterface.h → client/windows/CAdvmapInterface.h

@@ -1,11 +1,10 @@
 #pragma once
 
-#include <typeinfo>
+#include "../widgets/AdventureMapClasses.h"
+#include "CWindowObject.h"
 
-#include "SDL.h"
-#include "gui/CIntObjectClasses.h"
-#include "GUIClasses.h"
-#include "AdventureMapClasses.h"
+#include "../widgets/TextControls.h"
+#include "../widgets/Buttons.h"
 
 class CDefHandler;
 class CCallback;
@@ -33,7 +32,7 @@ class IShipyard;
 class CAdventureOptions : public CWindowObject
 {
 public:
-	CAdventureMapButton *exit, *viewWorld, *puzzle, *dig, *scenInfo, *replay;
+	CButton *exit, *viewWorld, *puzzle, *dig, *scenInfo, *replay;
 
 	CAdventureOptions();
 	static void showScenarioInfo();
@@ -111,16 +110,16 @@ public:
 	CMinimap minimap;
 	CGStatusBar statusbar;
 
-	CAdventureMapButton kingOverview,//- kingdom overview
-		underground,//- underground switch
-		questlog,//- questlog
-		sleepWake, //- sleep/wake hero
-		moveHero, //- move hero
-		spellbook,//- spellbook
-		advOptions, //- adventure options
-		sysOptions,//- system options
-		nextHero, //- next hero
-		endTurn;//- end turn
+	CButton * kingOverview;
+	CButton * underground;
+	CButton * questlog;
+	CButton * sleepWake;
+	CButton * moveHero;
+	CButton * spellbook;
+	CButton * advOptions;
+	CButton * sysOptions;
+	CButton * nextHero;
+	CButton * endTurn;
 
 	CTerrainRect terrain; //visible terrain
 	CResDataBar resdatabar;

+ 73 - 26
client/CCastleInterface.cpp → client/windows/CCastleInterface.cpp

@@ -1,29 +1,36 @@
 #include "StdInc.h"
 #include "CCastleInterface.h"
 
-#include "../CCallback.h"
-#include "../lib/CArtHandler.h"
-#include "../lib/CBuildingHandler.h"
-#include "../lib/CCreatureHandler.h"
-#include "../lib/CGeneralTextHandler.h"
-#include "../lib/CModHandler.h"
-#include "../lib/mapObjects/CGHeroInstance.h"
-#include "../lib/CSpellHandler.h"
-#include "../lib/CTownHandler.h"
 #include "CAdvmapInterface.h"
-#include "CAnimation.h"
-#include "CBitmapHandler.h"
-#include "CDefHandler.h"
-#include "CGameInfo.h"
 #include "CHeroWindow.h"
-#include "CMessage.h"
-#include "CMusicHandler.h"
-#include "CPlayerInterface.h"
-#include "Graphics.h"
-#include "gui/SDL_Extensions.h"
-#include "../lib/GameConstants.h"
-#include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
+#include "CTradeWindow.h"
+#include "GUIClasses.h"
+
+#include "../CBitmapHandler.h"
+#include "../CDefHandler.h"
+#include "../CGameInfo.h"
+#include "../CMessage.h"
+#include "../CMusicHandler.h"
+#include "../CPlayerInterface.h"
+#include "../Graphics.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../gui/SDL_Extensions.h"
+#include "../windows/InfoWindows.h"
+#include "../widgets/MiscWidgets.h"
+#include "../widgets/CComponent.h"
+
+#include "../../CCallback.h"
+#include "../../lib/CArtHandler.h"
+#include "../../lib/CBuildingHandler.h"
+#include "../../lib/CCreatureHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CModHandler.h"
+#include "../../lib/CSpellHandler.h"
+#include "../../lib/CTownHandler.h"
+#include "../../lib/GameConstants.h"
+#include "../../lib/mapObjects/CGHeroInstance.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
 
 using namespace boost::assign;
 
@@ -836,7 +843,11 @@ void CCastleBuildings::enterTownHall()
 		else
 		{
 			LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[673]);
+<<<<<<< HEAD:client/CCastleInterface.cpp
 			(dynamic_cast<CInfoWindow*>(GH.topInt()))->buttons[0]->callback += std::bind(&CCastleBuildings::openTownHall, this);
+=======
+			dynamic_cast<CInfoWindow*>(GH.topInt())->buttons[0]->addCallback(boost::bind(&CCastleBuildings::openTownHall, this));
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 		}
 	}
 	else
@@ -881,12 +892,21 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
 	income = new CLabel(195, 443, FONT_SMALL, CENTER);
 	icon = new CAnimImage("ITPT", 0, 0, 15, 387);
 
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	exit = new CAdventureMapButton(CGI->generaltexth->tcommands[8], "", std::bind(&CCastleInterface::close,this), 744, 544, "TSBTNS", SDLK_RETURN);
+=======
+	exit = new CButton(Point(744, 544), "TSBTNS", CButton::tooltip(CGI->generaltexth->tcommands[8]), [&]{close();}, SDLK_RETURN);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 	exit->assignedKeys.insert(SDLK_ESCAPE);
-	exit->setOffset(4);
+	exit->setImageOrder(4, 5, 6, 7);
 
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	split = new CAdventureMapButton(CGI->generaltexth->tcommands[3], "", std::bind(&CGarrisonInt::splitClick,garr), 744, 382, "TSBTNS.DEF");
 	split->callback += std::bind(&HeroSlots::splitClicked, heroes);
+=======
+	split = new CButton(Point(744, 382), "TSBTNS.DEF", CButton::tooltip(CGI->generaltexth->tcommands[3]), [&]{garr->splitClick();});
+	split->addCallback(boost::bind(&HeroSlots::splitClicked, heroes));
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 	garr->addSplitBtn(split);
 
 	Rect barRect(9, 182, 732, 18);
@@ -1304,8 +1324,12 @@ CHallInterface::CHallInterface(const CGTownInstance *Town):
 	statusBar = new CGStatusBar(new CPicture(*background, barRect, 5, 556, false));
 
 	title = new CLabel(399, 12, FONT_MEDIUM, CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->Name());
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	exit = new CAdventureMapButton(CGI->generaltexth->hcommands[8], "",
 	           std::bind(&CHallInterface::close,this), 748, 556, "TPMAGE1.DEF", SDLK_RETURN);
+=======
+	exit = new CButton(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->hcommands[8]), [&]{close();}, SDLK_RETURN);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
 	auto & boxList = town->town->clientInfo.hallSlots;
@@ -1403,15 +1427,24 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 
 	if(!rightClick)
 	{	//normal window
+<<<<<<< HEAD:client/CCastleInterface.cpp
 		buy = new CAdventureMapButton(boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->Name()),
 		          "", std::bind(&CBuildWindow::buyFunc,this), 45, 446,"IBUY30", SDLK_RETURN);
+=======
+		std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->Name());
+		std::string tooltipNo  = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->Name());
+
+		buy = new CButton(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&]{ buyFunc(); }, SDLK_RETURN);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 		buy->borderColor = Colors::METALLIC_GOLD;
-		buy->borderEnabled = true;
 
+<<<<<<< HEAD:client/CCastleInterface.cpp
 		cancel = new CAdventureMapButton(boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->Name()),
 		             "", std::bind(&CBuildWindow::close,this), 290, 445, "ICANCEL", SDLK_ESCAPE);
+=======
+		cancel = new CButton(Point(290, 445), "ICANCEL", CButton::tooltip(tooltipNo), [&] { close();}, SDLK_ESCAPE);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 		cancel->borderColor = Colors::METALLIC_GOLD;
-		cancel->borderEnabled = true;
 		buy->block(state!=7 || LOCPLINT->playerID != town->tempOwner);
 	}
 }
@@ -1441,7 +1474,11 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 	title = new CLabel(400, 12, FONT_BIG, CENTER, Colors::WHITE, fortBuilding->Name());
 
 	std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->Name());
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	exit = new CAdventureMapButton(text, "", std::bind(&CFortScreen::close,this) ,748, 556, "TPMAGE1", SDLK_RETURN);
+=======
+	exit = new CButton(Point(748, 556), "TPMAGE1", CButton::tooltip(text), [&]{ close(); }, SDLK_RETURN);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
 	std::vector<Point> positions;
@@ -1640,7 +1677,6 @@ void CFortScreen::RecruitArea::clickRight(tribool down, bool previousState)
 
 CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner,std::string imagem) :CWindowObject(BORDERED,imagem)
 {
-
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
 	window = new CPicture(owner->town->town->clientInfo.guildWindow , 332, 76);
@@ -1651,7 +1687,11 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner,std::string imagem)
 	Rect barRect(7, 556, 737, 18);
 	statusBar = new CGStatusBar(new CPicture(*background, barRect, 7, 556, false));
 
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	exit = new CAdventureMapButton(CGI->generaltexth->allTexts[593],"",std::bind(&CMageGuildScreen::close,this), 748, 556,"TPMAGE1.DEF",SDLK_RETURN);
+=======
+	exit = new CButton(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->allTexts[593]), [&]{ close(); }, SDLK_RETURN);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
 	std::vector<std::vector<Point> > positions;
@@ -1728,13 +1768,20 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 	                boost::lexical_cast<std::string>(CGI->arth->artifacts[aid]->price));
 
 	std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->nameSing);
+<<<<<<< HEAD:client/CCastleInterface.cpp
 	buy = new CAdventureMapButton(text,"",std::bind(&CBlacksmithDialog::close, this), 42, 312,"IBUY30.DEF",SDLK_RETURN);
 
 	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->nameSing);
 	cancel = new CAdventureMapButton(text,"",std::bind(&CBlacksmithDialog::close, this), 224, 312,"ICANCEL.DEF",SDLK_ESCAPE);
+=======
+	buy = new CButton(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&]{ close(); }, SDLK_RETURN);
+
+	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->nameSing);
+	cancel = new CButton(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&]{ close(); }, SDLK_ESCAPE);
+>>>>>>> refactoring/guiClasses:client/windows/CCastleInterface.cpp
 
 	if(possible)
-		buy->callback += [=]{ LOCPLINT->cb->buyArtifact(LOCPLINT->cb->getHero(hid),aid); };
+		buy->addCallback([=]{ LOCPLINT->cb->buyArtifact(LOCPLINT->cb->getHero(hid),aid); });
 	else
 		buy->block(true);
 

+ 11 - 12
client/CCastleInterface.h → client/windows/CCastleInterface.h

@@ -1,10 +1,9 @@
 #pragma once
 
+#include "../widgets/CGarrisonInt.h"
+#include "../widgets/Images.h"
 
-#include "CAnimation.h"
-#include "GUIClasses.h"
-
-class CAdventureMapButton;
+class CButton;
 class CBuilding;
 class CCastleBuildings;
 class CCreaturePic;
@@ -204,8 +203,8 @@ class CCastleInterface : public CWindowObject, public CWindowWithGarrison
 
 	CTownInfo *hall, *fort;
 
-	CAdventureMapButton *exit;
-	CAdventureMapButton *split;
+	CButton *exit;
+	CButton *split;
 
 	std::vector<CCreaInfo*> creainfo;//small icons of creatures (bottom-left corner);
 
@@ -261,7 +260,7 @@ class CHallInterface : public CWindowObject
 	CLabel *title;
 	CGStatusBar *statusBar;
 	CMinorResDataBar * resdatabar;
-	CAdventureMapButton *exit;
+	CButton *exit;
 
 public:
 	CHallInterface(const CGTownInstance * Town); //c-tor
@@ -273,8 +272,8 @@ class CBuildWindow: public CWindowObject
 	const CGTownInstance *town;
 	const CBuilding *building;
 
-	CAdventureMapButton *buy;
-	CAdventureMapButton *cancel;
+	CButton *buy;
+	CButton *cancel;
 
 	std::string getTextForState(int state);
 	void buyFunc();
@@ -329,7 +328,7 @@ class CFortScreen : public CWindowObject
 	std::vector<RecruitArea*> recAreas;
 	CMinorResDataBar * resdatabar;
 	CGStatusBar *statusBar;
-	CAdventureMapButton *exit;
+	CButton *exit;
 
 	std::string getBgName(const CGTownInstance *town);
 
@@ -354,7 +353,7 @@ class CMageGuildScreen : public CWindowObject
 		void hover(bool on);
 	};
 	CPicture *window;
-	CAdventureMapButton *exit;
+	CButton *exit;
 	std::vector<Scroll *> spells;
 	CMinorResDataBar * resdatabar;
 	CGStatusBar *statusBar;
@@ -366,7 +365,7 @@ public:
 /// The blacksmith window where you can buy available in town war machine
 class CBlacksmithDialog : public CWindowObject
 {
-	CAdventureMapButton *buy, *cancel;
+	CButton *buy, *cancel;
 	CPicture *animBG;
 	CCreatureAnim * anim;
 	CLabel * title;

+ 871 - 0
client/windows/CCreatureWindow.cpp

@@ -0,0 +1,871 @@
+#include "StdInc.h"
+#include "CCreatureWindow.h"
+
+#include "../CGameInfo.h"
+#include "../CPlayerInterface.h"
+#include "../widgets/Buttons.h"
+#include "../widgets/CComponent.h"
+#include "../widgets/Images.h"
+#include "../widgets/TextControls.h"
+#include "../widgets/ObjectLists.h"
+#include "../gui/CGuiHandler.h"
+
+#include "../../CCallback.h"
+#include "../../lib/BattleState.h"
+#include "../../lib/CBonusTypeHandler.h"
+#include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/CModHandler.h"
+#include "../../lib/CHeroHandler.h"
+#include "../../lib/CSpellHandler.h"
+#include "../../lib/CGameState.h"
+
+using namespace CSDL_Ext;
+
+class CCreatureArtifactInstance;
+class CSelectableSkill;
+
+/*
+ * CCreatureWindow.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
+ *
+ */
+
+struct StackWindowInfo
+{
+	// helper structs
+	struct CommanderLevelInfo
+	{
+		std::vector<ui32> skills;
+		std::function<void(ui32)> callback;
+	};
+	struct StackDismissInfo
+	{
+		std::function<void()> callback;
+	};
+	struct StackUpgradeInfo
+	{
+		UpgradeInfo info;
+		std::function<void(CreatureID)> callback;
+	};
+
+	// pointers to permament objects in game state
+	const CCreature * creature;
+	const CCommanderInstance * commander;
+	const CStackInstance * stackNode;
+	const CGHeroInstance * owner;
+
+	// temporary objects which should be kept as copy if needed
+	boost::optional<CommanderLevelInfo> levelupInfo;
+	boost::optional<StackDismissInfo> dismissInfo;
+	boost::optional<StackUpgradeInfo> upgradeInfo;
+
+	// misc fields
+	unsigned int creatureCount;
+	bool popupWindow;
+
+	StackWindowInfo();
+};
+
+namespace
+{
+	namespace EStat
+	{
+		enum EStat
+		{
+			ATTACK,
+			DEFENCE,
+			SHOTS,
+			DAMAGE,
+			HEALTH,
+			HEALTH_LEFT,
+			SPEED,
+			MANA
+		};
+	}
+}
+
+StackWindowInfo::StackWindowInfo():
+	creature(nullptr),
+	commander(nullptr),
+	stackNode(nullptr),
+	owner(nullptr),
+	creatureCount(0),
+	popupWindow(false)
+{
+}
+
+void CStackWindow::CWindowSection::createBackground(std::string path)
+{
+	background = new CPicture("stackWindow/" + path);
+	pos = background->pos;
+}
+
+void CStackWindow::CWindowSection::printStatString(int index, std::string name, std::string value)
+{
+	new CLabel(145, 32 + index*19, FONT_SMALL, TOPLEFT, Colors::WHITE, name);
+	new CLabel(307, 48 + index*19, FONT_SMALL, BOTTOMRIGHT, Colors::WHITE, value);
+}
+
+void CStackWindow::CWindowSection::printStatRange(int index, std::string name, int min, int max)
+{
+	if(min != max)
+		printStatString(index, name, boost::str(boost::format("%d - %d") % min % max));
+	else
+		printStatString(index, name, boost::str(boost::format("%d") % min));
+}
+
+void CStackWindow::CWindowSection::printStatBase(int index, std::string name, int base, int current)
+{
+	if(base != current)
+		printStatString(index, name, boost::str(boost::format("%d (%d)") % base % current));
+	else
+		printStatString(index, name, boost::str(boost::format("%d") % base));
+}
+
+void CStackWindow::CWindowSection::printStat(int index, std::string name, int value)
+{
+	printStatBase(index, name, value, value);
+}
+
+std::string CStackWindow::generateStackExpDescription()
+{
+	const CStackInstance * stack = info->stackNode;
+	const CCreature * creature = info->creature;
+
+	int tier = stack->type->level;
+	int rank = stack->getExpRank();
+	if (!vstd::iswithin(tier, 1, 7))
+		tier = 0;
+	int number;
+	std::string expText = CGI->generaltexth->zcrexp[325];
+	boost::replace_first (expText, "%s", creature->namePl);
+	boost::replace_first (expText, "%s", CGI->generaltexth->zcrexp[rank]);
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(rank));
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(stack->experience));
+	number = CGI->creh->expRanks[tier][rank] - stack->experience;
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number));
+
+	number = CGI->creh->maxExpPerBattle[tier]; //percent
+	boost::replace_first (expText, "%i%", boost::lexical_cast<std::string>(number));
+	number *= CGI->creh->expRanks[tier].back() / 100; //actual amount
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number));
+
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(stack->count)); //Number of Creatures in stack
+
+	int expmin = std::max(CGI->creh->expRanks[tier][std::max(rank-1, 0)], (ui32)1);
+	number = (stack->count * (stack->experience - expmin)) / expmin; //Maximum New Recruits without losing current Rank
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //TODO
+
+	boost::replace_first (expText, "%.2f", boost::lexical_cast<std::string>(1)); //TODO Experience Multiplier
+	number = CGI->creh->expAfterUpgrade;
+	boost::replace_first (expText, "%.2f", boost::lexical_cast<std::string>(number) + "%"); //Upgrade Multiplier
+
+	expmin = CGI->creh->expRanks[tier][9];
+	int expmax = CGI->creh->expRanks[tier][10];
+	number = expmax - expmin;
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //Experience after Rank 10
+	number = (stack->count * (expmax - expmin)) / expmin;
+	boost::replace_first (expText, "%i", boost::lexical_cast<std::string>(number)); //Maximum New Recruits to remain at Rank 10 if at Maximum Experience
+
+	return expText;
+}
+
+void CStackWindow::removeStackArtifact(ArtifactPosition pos)
+{
+	auto art = info->stackNode->getArt(ArtifactPosition::CREATURE_SLOT);
+	LOCPLINT->cb->swapArtifacts(ArtifactLocation(info->stackNode, pos),
+								ArtifactLocation(info->owner, art->firstBackpackSlot(info->owner)));
+	delete stackArtifactButton;
+	delete stackArtifactHelp;
+	delete stackArtifactIcon;
+}
+
+void CStackWindow::setStackArtifact(const CArtifactInstance * art, Point artPos)
+{
+	if (art)
+	{
+		stackArtifactIcon = new CAnimImage("ARTIFACT", art->artType->iconIndex, 0, pos.x, pos.y);
+		stackArtifactHelp = new LRClickableAreaWTextComp(Rect(artPos, Point(44, 44)), CComponent::artifact);
+		stackArtifactHelp->type = art->artType->id;
+
+		const JsonNode & text = VLC->generaltexth->localizedTexts["creatureWindow"]["returnArtifact"];
+
+		if (info->owner)
+		{
+			stackArtifactButton = new CButton(Point(artPos.x - 2 , artPos.y + 46), "stackWindow/cancelButton",
+			                                  CButton::tooltip(text),
+			                                  [=]{ removeStackArtifact(ArtifactPosition::CREATURE_SLOT); });
+		}
+	}
+
+}
+
+void CStackWindow::CWindowSection::createStackInfo(bool showExp, bool showArt)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if (showExp && showArt)
+		createBackground("info-panel-2");
+	else if (showExp || showArt)
+		createBackground("info-panel-1");
+	else
+		createBackground("info-panel-0");
+
+	new CCreaturePic(5, 41, parent->info->creature);
+
+	std::string visibleName;
+	if (parent->info->commander != nullptr)
+		visibleName = parent->info->commander->type->nameSing;
+	else
+		visibleName = parent->info->creature->namePl;
+	new CLabel(215, 12, FONT_SMALL, CENTER, Colors::YELLOW, visibleName);
+
+	int dmgMultiply = 1;
+	if(parent->info->owner && parent->info->stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
+		dmgMultiply += parent->info->owner->Attack();
+
+	new CPicture("stackWindow/icons", 117, 32);
+	printStatBase(EStat::ATTACK, CGI->generaltexth->primarySkillNames[0], parent->info->creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), parent->info->stackNode->Attack());
+	printStatBase(EStat::DEFENCE, CGI->generaltexth->primarySkillNames[1], parent->info->creature->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE), parent->info->stackNode->Defense());
+	printStatRange(EStat::DAMAGE, CGI->generaltexth->allTexts[199], parent->info->stackNode->getMinDamage() * dmgMultiply, parent->info->stackNode->getMaxDamage() * dmgMultiply);
+	printStatBase(EStat::HEALTH, CGI->generaltexth->allTexts[388], parent->info->creature->valOfBonuses(Bonus::STACK_HEALTH), parent->info->stackNode->valOfBonuses(Bonus::STACK_HEALTH));
+	printStatBase(EStat::SPEED, CGI->generaltexth->zelp[441].first, parent->info->creature->Speed(), parent->info->stackNode->Speed());
+
+	const CStack * battleStack = dynamic_cast<const CStack*>(parent->info->stackNode);
+	bool shooter = parent->info->stackNode->hasBonusOfType(Bonus::SHOOTER) && parent->info->stackNode->valOfBonuses(Bonus::SHOTS);
+	bool caster  = parent->info->stackNode->valOfBonuses(Bonus::CASTS);
+
+	if (battleStack != nullptr) // in battle
+	{
+		if (shooter)
+			printStatBase(EStat::SHOTS, CGI->generaltexth->allTexts[198], battleStack->valOfBonuses(Bonus::SHOTS), battleStack->shots);
+		if (caster)
+			printStatBase(EStat::MANA, CGI->generaltexth->allTexts[399], battleStack->valOfBonuses(Bonus::CASTS), battleStack->casts);
+		printStat(EStat::HEALTH_LEFT, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
+	}
+	else
+	{
+		if (shooter)
+			printStat(EStat::SHOTS, CGI->generaltexth->allTexts[198], parent->info->stackNode->valOfBonuses(Bonus::SHOTS));
+		if (caster)
+			printStat(EStat::MANA, CGI->generaltexth->allTexts[399], parent->info->stackNode->valOfBonuses(Bonus::CASTS));
+	}
+
+	auto morale = new MoraleLuckBox(true, genRect(42, 42, 321, 110));
+	morale->set(parent->info->stackNode);
+	auto luck = new MoraleLuckBox(false, genRect(42, 42, 375, 110));
+	luck->set(parent->info->stackNode);
+
+	if (showArt)
+	{
+		Point pos = showExp ? Point(375, 32) : Point(347, 32);
+		parent->setStackArtifact(parent->info->stackNode->getArt(ArtifactPosition::CREATURE_SLOT), pos);
+	}
+
+	if (showExp)
+	{
+		const CStackInstance * stack = parent->info->stackNode;
+		Point pos = showArt ? Point(321, 32) : Point(347, 32);
+		if (parent->info->commander)
+		{
+			const CCommanderInstance * commander = parent->info->commander;
+			new CAnimImage("PSKIL42", 4, 0, pos.x, pos.y); // experience icon
+
+			auto expArea = new LRClickableAreaWTextComp(Rect(pos.x, pos.y, 44, 44), CComponent::experience);
+			expArea->text = CGI->generaltexth->allTexts[2];
+			expArea->bonusValue = commander->getExpRank();
+			boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(commander->getExpRank()));
+			boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(CGI->heroh->reqExp(commander->getExpRank()+1)));
+			boost::replace_first(expArea->text, "%d", boost::lexical_cast<std::string>(commander->experience));
+		}
+		else
+		{
+			new CAnimImage("stackWindow/levels", stack->getExpRank(), 0, pos.x, pos.y);
+			auto expArea = new LRClickableAreaWText(Rect(pos.x, pos.y, 44, 44));
+			expArea->text = parent->generateStackExpDescription();
+		}
+		new CLabel(pos.x + 21, pos.y + 52, FONT_SMALL, CENTER, Colors::WHITE, makeNumberShort<TExpType>(stack->experience, 6));
+	}
+}
+
+void CStackWindow::CWindowSection::createActiveSpells()
+{
+	static const Point firstPos(7 ,4); // position of 1st spell box
+	static const Point offset(54, 0);  // offset of each spell box from previous
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	createBackground("spell-effects");
+
+	const CStack * battleStack = dynamic_cast<const CStack*>(parent->info->stackNode);
+
+	assert(battleStack); // Section should be created only for battles
+
+	//spell effects
+	int printed=0; //how many effect pics have been printed
+	std::vector<si32> spells = battleStack->activeSpells();
+	for(si32 effect : spells)
+	{
+		std::string spellText;
+		if (effect < 77) //not all effects have graphics (for eg. Acid Breath)
+		{
+			spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
+			boost::replace_first (spellText, "%s", CGI->spellh->objects[effect]->name);
+			int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
+			boost::replace_first (spellText, "%d", boost::lexical_cast<std::string>(duration));
+
+			new CAnimImage("SpellInt", effect + 1, 0, firstPos.x + offset.x * printed, firstPos.x + offset.y * printed);
+			new LRClickableAreaWText(Rect(firstPos + offset * printed, Point(50, 38)), spellText, spellText);
+			if (++printed >= 8) // interface limit reached
+				break;
+		}
+	}
+}
+
+void CStackWindow::CWindowSection::createCommanderSection()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	auto onCreate = [=](size_t index) -> CIntObject *
+	{
+		return parent->switchTab(index);
+	};
+	auto onDestroy = [=](CIntObject * obj)
+	{
+		delete obj;
+	};
+	parent->commanderTab = new CTabbedInt(onCreate, onDestroy, Point(0,0), 0);
+	pos.w = parent->pos.w;
+	pos.h = 177; // height of commander info
+	if (parent->info->levelupInfo)
+		pos.h += 59; // height of abilities selection
+}
+
+static std::string skillToFile (int skill, int level, bool selected)
+{
+	// FIXME: is this a correct hadling?
+	// level 0 = skill not present, use image with "no" suffix
+	// level 1-5 = skill available, mapped to images indexed as 0-4
+	// selecting skill means that it will appear one level higher (as if alredy upgraded)
+	std::string file = "zvs/Lib1.res/_";
+	switch (skill)
+	{
+		case ECommander::ATTACK:
+			file += "AT";
+			break;
+		case ECommander::DEFENSE:
+			file += "DF";
+			break;
+		case ECommander::HEALTH:
+			file += "HP";
+			break;
+		case ECommander::DAMAGE:
+			file += "DM";
+			break;
+		case ECommander::SPEED:
+			file += "SP";
+			break;
+		case ECommander::SPELL_POWER:
+			file += "MP";
+			break;
+	}
+	std::string sufix;
+	if (selected)
+		level++; // UI will display resulting level
+	if (level == 0)
+		sufix = "no"; //not avaliable - no number
+	else
+		sufix = boost::lexical_cast<std::string>(level-1);
+	if (selected)
+		sufix += "="; //level-up highlight
+
+	return file + sufix + ".bmp";
+}
+
+void CStackWindow::CWindowSection::createCommander()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	createBackground("commander-bg");
+
+	auto getSkillPos = [&](int index)
+	{
+		return Point(10 + 80 * (index%3), 20 + 80 * (index/3));
+	};
+
+	auto getSkillImage = [this](int skillIndex) -> std::string
+	{
+		bool selected = ((parent->selectedSkill == skillIndex) && parent->info->levelupInfo );
+		return skillToFile(skillIndex, parent->info->commander->secondarySkills[skillIndex], selected);
+	};
+
+	for (int index = ECommander::ATTACK; index <= ECommander::SPELL_POWER; ++index)
+	{
+		Point skillPos = getSkillPos(index);
+
+		auto icon = new CClickableObject(new CPicture(getSkillImage(index), skillPos.x, skillPos.y), [=]{});
+
+		if (parent->selectedSkill == index)
+			parent->selectedIcon = icon;
+
+		if (parent->info->levelupInfo && vstd::contains(parent->info->levelupInfo->skills, index)) // can be upgraded - enable selection switch
+		{
+			icon->callback = [=]
+			{
+				parent->setSelection(index, icon);
+			};
+		}
+	}
+	//TODO: commander artifacts
+}
+
+CIntObject * CStackWindow::createSkillEntry(int index)
+{
+	for (auto skillID : info->levelupInfo->skills)
+	{
+		if (index == 0 && skillID >= 100)
+		{
+			const Bonus *bonus = CGI->creh->skillRequirements[skillID-100].first;
+			const CStackInstance *stack = info->commander;
+			CClickableObject * icon = new CClickableObject(new CPicture(stack->bonusToGraphics(bonus)), []{});
+			icon->callback = [=]
+			{
+				setSelection(skillID, icon);
+			};
+			icon->text = stack->bonusToString(bonus, true);
+			icon->hoverText = stack->bonusToString(bonus, false);
+			return icon;
+		}
+		if (skillID >= 100)
+			index--;
+	}
+	return nullptr;
+}
+
+void CStackWindow::CWindowSection::createCommanderAbilities()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	auto bg2 = new CPicture("stackWindow/commander-abilities.png");
+	bg2->moveBy(Point(0, pos.h));
+	size_t abilitiesCount = boost::range::count_if(parent->info->levelupInfo->skills, [](ui32 skillID)
+	{
+		return skillID >= 100;
+	});
+
+	auto list = new CListBox([=] (int index)
+	{
+		return parent->createSkillEntry(index);
+	},
+	[=] (CIntObject * elem)
+	{
+		delete elem;
+	},
+	Point(38, 3+pos.h), Point(63, 0), 6, abilitiesCount);
+
+	auto leftBtn   = new CButton(Point(10,  pos.h + 6), "hsbtns3.def", CButton::tooltip(), [=]{ list->moveToPrev(); }, SDLK_LEFT);
+	auto rightBtn =  new CButton(Point(411, pos.h + 6), "hsbtns5.def", CButton::tooltip(), [=]{ list->moveToNext(); }, SDLK_RIGHT);
+
+	if (abilitiesCount <= 6)
+	{
+		leftBtn->block(true);
+		rightBtn->block(true);
+	}
+}
+
+void CStackWindow::setSelection(si32 newSkill, CClickableObject * newIcon)
+{
+	auto getSkillImage = [this](int skillIndex) -> std::string
+	{
+		bool selected = ((selectedSkill == skillIndex) && info->levelupInfo );
+		return skillToFile(skillIndex, info->commander->secondarySkills[skillIndex], selected);
+	};
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	int oldSelection = selectedSkill; // update selection
+	selectedSkill = newSkill;
+
+	if (selectedIcon && oldSelection < 100) // recreate image on old selection, only for skills
+		selectedIcon->setObject(new CPicture(getSkillImage(oldSelection)));
+
+	selectedIcon = newIcon; // update new selection
+	if (newSkill < 100)
+		newIcon->setObject(new CPicture(getSkillImage(newSkill)));
+}
+
+void CStackWindow::CWindowSection::createBonuses(boost::optional<size_t> preferredSize)
+{
+	// size of single image for an item
+	static const int itemHeight = 59;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	size_t totalSize = (parent->activeBonuses.size() + 1) / 2;
+	size_t visibleSize = preferredSize ? preferredSize.get() : std::min<size_t>(3, totalSize);
+
+	pos.w = parent->pos.w;
+	pos.h = itemHeight * visibleSize;
+
+	auto onCreate = [=](size_t index) -> CIntObject *
+	{
+		return parent->createBonusEntry(index);
+	};
+	auto onDestroy = [=](CIntObject * obj)
+	{
+		delete obj;
+	};
+	new CListBox(onCreate, onDestroy, Point(0, 0), Point(0, itemHeight), visibleSize, totalSize, 0, 1, Rect(pos.w - 15, 0, pos.h, pos.h));
+}
+
+void CStackWindow::CWindowSection::createButtonPanel()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	createBackground("button-panel");
+
+	if (parent->info->dismissInfo)
+	{
+		auto onDismiss = [=]()
+		{
+			parent->info->dismissInfo->callback();
+			parent->close();
+		};
+		auto onClick = [=] ()
+		{
+			LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[12], onDismiss, 0, false, std::vector<CComponent*>());
+		};
+		new CButton(Point(5, 5),"IVIEWCR2.DEF", CGI->generaltexth->zelp[445], onClick, SDLK_d);
+	}
+	if (parent->info->upgradeInfo)
+	{
+		// used space overlaps with commander switch button
+		// besides - should commander really be upgradeable?
+		assert(!parent->info->commander);
+
+		StackWindowInfo::StackUpgradeInfo & upgradeInfo = parent->info->upgradeInfo.get();
+		size_t buttonsToCreate = std::min<size_t>(upgradeInfo.info.newID.size(), 3); // no more than 3 windows on UI - space limit
+
+		for (size_t i=0; i<buttonsToCreate; i++)
+		{
+			TResources totalCost = upgradeInfo.info.cost[i] * parent->info->creatureCount;
+
+			auto onUpgrade = [=]()
+			{
+				upgradeInfo.callback(upgradeInfo.info.newID[i]);
+				parent->close();
+			};
+			auto onClick = [=]()
+			{
+				std::vector<CComponent*> resComps;
+				for(TResources::nziterator i(totalCost); i.valid(); i++)
+				{
+					resComps.push_back(new CComponent(CComponent::resource, i->resType, i->resVal));
+				}
+				LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[207], onUpgrade, nullptr, true, resComps);
+			};
+			auto upgradeBtn = new CButton(Point(221 + i * 40, 5), "stackWindow/upgradeButton", CGI->generaltexth->zelp[446], onClick, SDLK_1);
+
+			upgradeBtn->addOverlay(new CAnimImage("CPRSMALL", VLC->creh->creatures[upgradeInfo.info.newID[i]]->iconIndex));
+
+			if (!LOCPLINT->cb->getResourceAmount().canAfford(totalCost))
+				upgradeBtn->block(true);
+		}
+	}
+
+	if (parent->info->commander)
+	{
+		for (size_t i=0; i<2; i++)
+		{
+			std::string btnIDs[2] = { "showSkills", "showBonuses" };
+			auto onSwitch = [&, i]()
+			{
+				parent->switchButtons[parent->activeTab]->enable();
+				parent->commanderTab->setActive(i);
+				parent->switchButtons[i]->disable();
+				parent->redraw(); // FIXME: enable/disable don't redraw screen themselves
+			};
+
+			const JsonNode & text = VLC->generaltexth->localizedTexts["creatureWindow"][btnIDs[i]];
+			parent->switchButtons[i] = new CButton(Point(302 + i*40, 5), "stackWindow/upgradeButton", CButton::tooltip(text), onSwitch);
+			parent->switchButtons[i]->addOverlay(new CAnimImage("stackWindow/switchModeIcons", i));
+		}
+		parent->switchButtons[parent->activeTab]->disable();
+	}
+
+	auto exitBtn = new CButton(Point(382, 5), "hsbtns.def", CGI->generaltexth->zelp[445], [=]{ parent->close(); }, SDLK_RETURN);
+	exitBtn->assignedKeys.insert(SDLK_ESCAPE);
+}
+
+CStackWindow::CWindowSection::CWindowSection(CStackWindow * parent):
+	parent(parent)
+{
+}
+
+CClickableObject::CClickableObject(CIntObject *object, std::function<void()> callback):
+	object(nullptr),
+	callback(callback)
+{
+	pos = object->pos;
+	setObject(object);
+}
+
+void CClickableObject::setObject(CIntObject *newObject)
+{
+	delete object;
+	object = newObject;
+	addChild(object);
+	object->moveTo(pos.topLeft());
+	redraw();
+}
+
+void CClickableObject::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+		callback();
+}
+
+CIntObject * CStackWindow::createBonusEntry(size_t index)
+{
+	auto section = new CWindowSection(this);
+	section->createBonusEntry(index);
+	return section;
+}
+
+void CStackWindow::CWindowSection::createBonusEntry(size_t index)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	createBackground("bonus-effects");
+	createBonusItem(index * 2, Point(6, 4));
+	createBonusItem(index * 2 + 1, Point(214, 4));
+}
+
+void CStackWindow::CWindowSection::createBonusItem(size_t index, Point position)
+{
+	if (parent->activeBonuses.size() > index)
+	{
+		BonusInfo & bi = parent->activeBonuses[index];
+		new CPicture(bi.imagePath, position.x, position.y);
+		new CLabel(position.x + 60, position.y + 2,  FONT_SMALL, TOPLEFT, Colors::WHITE, bi.name);
+		new CLabel(position.x + 60, position.y + 25, FONT_SMALL, TOPLEFT, Colors::WHITE, bi.description);
+	}
+}
+
+CIntObject * CStackWindow::switchTab(size_t index)
+{
+	switch (index)
+	{
+		case 0:
+		{
+			activeTab = 0;
+			auto ret = new CWindowSection(this);
+			ret->createCommander();
+			if (info->levelupInfo)
+				ret->createCommanderAbilities();
+			return ret;
+		}
+		case 1:
+		{
+			activeTab = 1;
+			auto ret = new CWindowSection(this);
+			if (info->levelupInfo)
+				ret->createBonuses(4);
+			else
+				ret->createBonuses(3);
+			return ret;
+		}
+		default:
+		{
+			return nullptr;
+		}
+	}
+}
+
+void CStackWindow::initSections()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	CWindowSection * currentSection;
+
+	bool showArt = CGI->modh->modules.STACK_ARTIFACT && info->commander == nullptr && info->stackNode;
+	bool showExp = (CGI->modh->modules.STACK_EXP || info->commander != nullptr) && info->stackNode;
+	currentSection = new CWindowSection(this);
+	currentSection->createStackInfo(showExp, showArt);
+	pos.w = currentSection->pos.w;
+	pos.h += currentSection->pos.h;
+
+	if (dynamic_cast<const CStack*>(info->stackNode)) // in battle
+	{
+		currentSection = new CWindowSection(this);
+		currentSection->pos.y += pos.h;
+		currentSection->createActiveSpells();
+		pos.h += currentSection->pos.h;
+	}
+	if (info->commander)
+	{
+		currentSection = new CWindowSection(this);
+		currentSection->pos.y += pos.h;
+		currentSection->createCommanderSection();
+		pos.h += currentSection->pos.h;
+	}
+	if (!info->commander && !activeBonuses.empty())
+	{
+		currentSection = new CWindowSection(this);
+		currentSection->pos.y += pos.h;
+		currentSection->createBonuses();
+		pos.h += currentSection->pos.h;
+	}
+
+	if (!info->popupWindow)
+	{
+		currentSection = new CWindowSection(this);
+		currentSection->pos.y += pos.h;
+		currentSection->createButtonPanel();
+		pos.h += currentSection->pos.h;
+		//FIXME: add status bar to image?
+	}
+	updateShadow();
+	pos = center(pos);
+}
+
+void CStackWindow::initBonusesList()
+{
+	BonusList output, input;
+	input = *(info->stackNode->getBonuses(Selector::durationType(Bonus::PERMANENT).And(Selector::anyRange())));
+
+	while (!input.empty())
+	{
+		Bonus * b = input.front();
+
+		output.push_back(new Bonus(*b));
+		output.back()->val = input.valOfBonuses(Selector::typeSubtype(b->type, b->subtype)); //merge multiple bonuses into one
+		input.remove_if (Selector::typeSubtype(b->type, b->subtype)); //remove used bonuses
+	}
+
+	BonusInfo bonusInfo;
+	for(Bonus* b : output)
+	{
+		bonusInfo.name = info->stackNode->bonusToString(b, false);
+		bonusInfo.imagePath = info->stackNode->bonusToGraphics(b);
+
+		//if it's possible to give any description or image for this kind of bonus
+		//TODO: figure out why half of bonuses don't have proper description
+		if (!bonusInfo.name.empty() || !bonusInfo.imagePath.empty())
+			activeBonuses.push_back(bonusInfo);
+	}
+
+	//handle Magic resistance separately :/
+	int magicResistance = info->stackNode->magicResistance();
+
+	if (magicResistance)
+	{
+		BonusInfo bonusInfo;
+		Bonus b;
+		b.type = Bonus::MAGIC_RESISTANCE;
+
+		bonusInfo.name = VLC->getBth()->bonusToString(&b, info->stackNode, false);
+		bonusInfo.description = VLC->getBth()->bonusToString(&b, info->stackNode, true);
+		bonusInfo.imagePath = info->stackNode->bonusToGraphics(&b);
+		activeBonuses.push_back(bonusInfo);
+	}
+}
+
+void CStackWindow::init()
+{
+	stackArtifactHelp = nullptr;
+	stackArtifactIcon = nullptr;
+	stackArtifactButton = nullptr;
+
+	selectedIcon = nullptr;
+	selectedSkill = 0;
+	if (info->levelupInfo)
+	{
+		selectedSkill = info->levelupInfo->skills.front();
+		logGlobal->debugStream() << "Received commander level-up. Possible options are: ";
+		for (auto skill : info->levelupInfo->skills)
+			logGlobal->debugStream() << "Skill #" << skill;
+	}
+
+	commanderTab = nullptr;
+	activeTab = 0;
+
+	initBonusesList();
+	initSections();
+}
+
+CStackWindow::CStackWindow(const CStack * stack, bool popup):
+	CWindowObject(BORDERED | (popup ? RCLICK_POPUP : 0))
+{
+	info->stackNode = stack->base;
+	info->creature = stack->type;
+	info->creatureCount = stack->count;
+	info->popupWindow = popup;
+	init();
+}
+
+CStackWindow::CStackWindow(const CCreature * creature, bool popup):
+	CWindowObject(BORDERED | (popup ? RCLICK_POPUP : 0)),
+	info(new StackWindowInfo())
+{
+	info->stackNode = new CStackInstance(creature, 1); // FIXME: free data
+	info->creature = creature;
+	info->popupWindow = popup;
+	init();
+}
+
+CStackWindow::CStackWindow(const CStackInstance * stack, bool popup):
+	CWindowObject(BORDERED | (popup ? RCLICK_POPUP : 0)),
+	info(new StackWindowInfo())
+{
+	info->stackNode = stack;
+	info->creature = stack->type;
+	info->creatureCount = stack->count;
+	info->popupWindow = popup;
+	init();
+}
+
+CStackWindow::CStackWindow(const CStackInstance * stack, std::function<void()> dismiss, const UpgradeInfo & upgradeInfo, std::function<void(CreatureID)> callback):
+	CWindowObject(BORDERED),
+	info(new StackWindowInfo())
+{
+	info->stackNode = stack;
+	info->creature = stack->type;
+	info->creatureCount = stack->count;
+
+	info->upgradeInfo = StackWindowInfo::StackUpgradeInfo();
+	info->dismissInfo = StackWindowInfo::StackDismissInfo();
+	info->upgradeInfo->info = upgradeInfo;
+	info->upgradeInfo->callback = callback;
+	info->dismissInfo->callback = dismiss;
+	init();
+}
+
+CStackWindow::CStackWindow(const CCommanderInstance * commander, bool popup):
+	CWindowObject(BORDERED | (popup ? RCLICK_POPUP : 0)),
+	info(new StackWindowInfo())
+{
+	info->stackNode = commander;
+	info->creature = commander->type;
+	info->commander = commander;
+	info->creatureCount = 1;
+	info->popupWindow = popup;
+	init();
+}
+
+CStackWindow::CStackWindow(const CCommanderInstance * commander, std::vector<ui32> &skills, std::function<void(ui32)> callback):
+	CWindowObject(BORDERED),
+	info(new StackWindowInfo())
+{
+	info->stackNode = commander;
+	info->creature = commander->type;
+	info->commander = commander;
+	info->creatureCount = 1;
+	info->levelupInfo = StackWindowInfo::CommanderLevelInfo();
+	info->levelupInfo->skills = skills;
+	info->levelupInfo->callback = callback;
+	init();
+}
+
+CStackWindow::~CStackWindow()
+{
+	if (info->levelupInfo)
+	{
+		logGlobal->debugStream() << "Selected skill was " << selectedSkill;
+		info->levelupInfo->callback(vstd::find_pos(info->levelupInfo->skills, selectedSkill));
+	}
+}

+ 120 - 0
client/windows/CCreatureWindow.h

@@ -0,0 +1,120 @@
+#pragma once
+
+#include "../../lib/HeroBonus.h"
+#include "../widgets/MiscWidgets.h"
+#include "CWindowObject.h"
+
+/*
+ * CCreatureWindow.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 StackWindowInfo;
+class CCommanderInstance;
+class CStackInstance;
+class CStack;
+struct UpgradeInfo;
+class CTabbedInt;
+class CButton;
+
+class CClickableObject : public LRClickableAreaWText
+{
+	CIntObject * object; // passive object that will be used to determine clickable area
+public:
+	CClickableObject(CIntObject * object, std::function<void()> callback);
+
+	std::function<void()> callback; //TODO: create more generic clickable class than AdvMapButton?
+
+	void clickLeft(tribool down, bool previousState) override;
+	//void clickRight(tribool down, bool previousState){};
+
+	void setObject(CIntObject * object);
+};
+
+class CStackWindow : public CWindowObject
+{
+	struct BonusInfo
+	{
+		std::string name;
+		std::string description;
+		std::string imagePath;
+	};
+
+	class CWindowSection : public CIntObject
+	{
+		CStackWindow * parent;
+
+		CPicture * background;
+
+		void createBackground(std::string path);
+		void createBonusItem(size_t index, Point position);
+
+		void printStatString(int index, std::string name, std::string value);
+		void printStatRange(int index, std::string name, int min, int max);
+		void printStatBase(int index, std::string name, int base, int current);
+		void printStat(int index, std::string name, int value);
+	public:
+		void createStackInfo(bool showExp, bool showArt);
+		void createActiveSpells();
+		void createCommanderSection();
+		void createCommander();
+		void createCommanderAbilities();
+		void createBonuses(boost::optional<size_t> size = boost::optional<size_t>());
+		void createBonusEntry(size_t index);
+		void createButtonPanel();
+
+		CWindowSection(CStackWindow * parent);
+	};
+
+	CAnimImage * stackArtifactIcon;
+	LRClickableAreaWTextComp * stackArtifactHelp;
+	CButton * stackArtifactButton;
+
+	std::unique_ptr<StackWindowInfo> info;
+	std::vector<BonusInfo> activeBonuses;
+	size_t activeTab;
+	CTabbedInt * commanderTab;
+
+	std::map<int, CButton *> switchButtons;
+
+	void setSelection(si32 newSkill, CClickableObject * newIcon);
+	CClickableObject * selectedIcon;
+	si32 selectedSkill;
+
+	CIntObject * createBonusEntry(size_t index);
+	CIntObject * switchTab(size_t index);
+
+	void removeStackArtifact(ArtifactPosition pos);
+	void setStackArtifact(const CArtifactInstance * art, Point artPos);
+
+	void initSections();
+	void initBonusesList();
+
+	void init();
+
+	std::string generateStackExpDescription();
+
+	CIntObject * createSkillEntry(int index);
+
+public:
+	// for battles
+	CStackWindow(const CStack * stack, bool popup);
+
+	// for non-existing stacks, e.g. recruit screen
+	CStackWindow(const CCreature * creature, bool popup);
+
+	// for normal stacks in armies
+	CStackWindow(const CStackInstance * stack, bool popup);
+	CStackWindow(const CStackInstance * stack, std::function<void()> dismiss, const UpgradeInfo & info, std::function<void(CreatureID)> callback);
+
+	// for commanders & commander level-up dialog
+	CStackWindow(const CCommanderInstance * commander, bool popup);
+	CStackWindow(const CCommanderInstance * commander, std::vector<ui32> &skills, std::function<void(ui32)> callback);
+
+	~CStackWindow();
+};

+ 60 - 33
client/CHeroWindow.cpp → client/windows/CHeroWindow.cpp

@@ -1,34 +1,34 @@
 #include "StdInc.h"
+#include "CHeroWindow.h"
 
-#include "CAnimation.h"
 #include "CAdvmapInterface.h"
-#include "../CCallback.h"
-#include "CGameInfo.h"
-#include "CHeroWindow.h"
-#include "CMessage.h"
-#include "CKingdomInterface.h"
 #include "CCreatureWindow.h"
-#include "SDL.h"
-#include "gui/SDL_Extensions.h"
-#include "CBitmapHandler.h"
-#include "Graphics.h"
+#include "CKingdomInterface.h"
 #include "CSpellWindow.h"
-#include "../lib/CConfigHandler.h"
-#include "CPlayerInterface.h"
+#include "GUIClasses.h"
+
+#include "../CBitmapHandler.h"
+#include "../CDefHandler.h"
+#include "../CGameInfo.h"
+#include "../CMessage.h"
+#include "../CMT.h"
+#include "../CPlayerInterface.h"
+#include "../Graphics.h"
+
+#include "../gui/SDL_Extensions.h"
+#include "../gui/CGuiHandler.h"
+#include "../widgets/MiscWidgets.h"
+#include "../widgets/CComponent.h"
+
+#include "../../CCallback.h"
 
 #include "../lib/CArtHandler.h"
-#include "CDefHandler.h"
+#include "../lib/CConfigHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/NetPacksBase.h"
 
-#include "gui/CGuiHandler.h"
-#include "gui/CIntObjectClasses.h"
-#include "CMT.h"
-
-#undef min
-
 /*
  * CHeroWindow.cpp, part of VCMI engine
  *
@@ -93,8 +93,11 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
     CWindowObject(PLAYER_COLORED, "HeroScr4"),
 	heroWArt(this, hero)
 {
+	auto & heroscrn = CGI->generaltexth->heroscrn;
+
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	garr = nullptr;
+	tacticsButton = nullptr;
 	curHero = hero;
 	listSelection = nullptr;
 
@@ -103,23 +106,32 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
 	//artifs = new CArtifactsOfHero(pos.topLeft(), true);
 	ourBar = new CGStatusBar(7, 559, "ADROLLVR.bmp", 660); // new CStatusBar(pos.x+72, pos.y+567, "ADROLLVR.bmp", 660);
 
+<<<<<<< HEAD:client/CHeroWindow.cpp
 	quitButton = new CAdventureMapButton(CGI->generaltexth->heroscrn[17], std::string(),std::bind(&CHeroWindow::close,this), 609, 516, "hsbtns.def", SDLK_RETURN);
 	quitButton->assignedKeys.insert(SDLK_ESCAPE);
 	dismissButton = new CAdventureMapButton(std::string(), CGI->generaltexth->heroscrn[28], std::bind(&CHeroWindow::dismissCurrent,this), 454, 429, "hsbtns2.def", SDLK_d);
 	questlogButton = new CAdventureMapButton(CGI->generaltexth->heroscrn[0], std::string(), std::bind(&CHeroWindow::questlog,this), 314, 429, "hsbtns4.def", SDLK_q);
+=======
+	quitButton = new CButton(Point(609, 516), "hsbtns.def", CButton::tooltip(heroscrn[17]), [&]{ close(); }, SDLK_RETURN);
+	quitButton->assignedKeys.insert(SDLK_ESCAPE);
+	dismissButton = new CButton(Point(454, 429), "hsbtns2.def", CButton::tooltip(heroscrn[28]), [&]{ dismissCurrent(); }, SDLK_d);
+	questlogButton = new CButton(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [&]{ questlog(); }, SDLK_q);
+>>>>>>> refactoring/guiClasses:client/windows/CHeroWindow.cpp
 
-	formations = new CHighlightableButtonsGroup(0);
-	formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[23]),CGI->generaltexth->heroscrn[29], "hsbtns6.def", 481, 483, 0, 0, SDLK_t);
-	formations->addButton(map_list_of(0,CGI->generaltexth->heroscrn[24]),CGI->generaltexth->heroscrn[30], "hsbtns7.def", 481, 519, 1, 0, SDLK_l);
-
-	tacticsButton = new CHighlightableButton(0, 0, map_list_of(0,CGI->generaltexth->heroscrn[26])(3,CGI->generaltexth->heroscrn[25]), CGI->generaltexth->heroscrn[31], false, "hsbtns8.def", nullptr, 539, 483, SDLK_b);
+	formations = new CToggleGroup(0);
+	formations->addToggle(0, new CToggleButton(Point(481, 483), "hsbtns6.def", std::make_pair(heroscrn[23], heroscrn[29]), 0, SDLK_t));
+	formations->addToggle(1, new CToggleButton(Point(481, 519), "hsbtns7.def", std::make_pair(heroscrn[24], heroscrn[30]), 0, SDLK_l));
 
 	if (hero->commander)
 	{
+<<<<<<< HEAD:client/CHeroWindow.cpp
 		commanderButton = new CAdventureMapButton ("Commander", "Commander info", std::bind(&CHeroWindow::commanderWindow, this), 317, 18, "chftke.def", SDLK_c, nullptr, false);
+=======
+		auto texts = CGI->generaltexth->localizedTexts["heroWindow"]["openCommander"];
+		commanderButton = new CButton (Point(317, 18), "buttons/commander", CButton::tooltip(texts), [&]{ commanderWindow(); }, SDLK_c);
+>>>>>>> refactoring/guiClasses:client/windows/CHeroWindow.cpp
 	}
 
-
 	//right list of heroes
 	for(int i=0; i < std::min(LOCPLINT->cb->howManyHeroes(false), 8); i++)
 		heroList.push_back(new CHeroSwitcher(Point(612, 87 + i * 54), LOCPLINT->cb->getHeroBySerial(i, false)));
@@ -180,7 +192,9 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
 }
 
 void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= false*/)
-{	
+{
+	auto & heroscrn = CGI->generaltexth->heroscrn;
+
 	if(!hero) //something strange... no hero? it shouldn't happen
 	{
         logGlobal->errorStream() << "Set nullptr hero? no way...";
@@ -192,10 +206,11 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 	specArea->text = curHero->type->specDescr;
 	specImage->setFrame(curHero->type->imageIndex);
 
-	tacticsButton->callback.clear();
-	tacticsButton->callback2.clear();
+	delete tacticsButton;
+	tacticsButton = new CToggleButton(Point(539, 483), "hsbtns8.def", std::make_pair(heroscrn[26], heroscrn[31]), 0, SDLK_b);
+	tacticsButton->addHoverText(CButton::HIGHLIGHTED, CGI->generaltexth->heroscrn[25]);
 
-	dismissButton->hoverTexts[0] = boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->name % curHero->type->heroClass->name);
+	dismissButton->addHoverText(CButton::NORMAL, boost::str(boost::format(CGI->generaltexth->heroscrn[16]) % curHero->name % curHero->type->heroClass->name));
 	portraitArea->hoverText = boost::str(boost::format(CGI->generaltexth->allTexts[15]) % curHero->name % curHero->type->heroClass->name);
 	portraitArea->text = curHero->getBiography();
 	portraitImage->setFrame(curHero->portrait);
@@ -204,10 +219,17 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 		OBJ_CONSTRUCTION_CAPTURING_ALL;
 		if(!garr)
 		{
+			std::string helpBox = heroscrn[32];
+			boost::algorithm::replace_first(helpBox, "%s", CGI->generaltexth->allTexts[43]);
+
 			garr = new CGarrisonInt(15, 485, 8, Point(), background->bg, Point(15,485), curHero);
+<<<<<<< HEAD:client/CHeroWindow.cpp
 			auto split = new CAdventureMapButton(CGI->generaltexth->allTexts[256], CGI->generaltexth->heroscrn[32],
 					std::bind(&CGarrisonInt::splitClick,garr), 539, 519, "hsbtns9.def", false, nullptr, false); //deleted by garrison destructor
 			boost::algorithm::replace_first(split->hoverTexts[0],"%s",CGI->generaltexth->allTexts[43]);
+=======
+			auto split = new CButton(Point(539, 519), "hsbtns9.def", CButton::tooltip(CGI->generaltexth->allTexts[256], helpBox), [&]{ garr->splitClick(); });
+>>>>>>> refactoring/guiClasses:client/windows/CHeroWindow.cpp
 
 			garr->addSplitBtn(split);
 		}
@@ -239,7 +261,7 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 		secSkillAreas[g]->type = skill;
 		secSkillAreas[g]->bonusValue = level;
 		secSkillAreas[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
-		secSkillAreas[g]->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[21]) % CGI->generaltexth->levels[level-1] % CGI->generaltexth->skillName[skill]);
+		secSkillAreas[g]->hoverText = boost::str(boost::format(heroscrn[21]) % CGI->generaltexth->levels[level-1] % CGI->generaltexth->skillName[skill]);
 		secSkillImages[g]->setFrame(skill*3 + level + 2);
 	}
 
@@ -274,14 +296,19 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
 	else
 	{
 		tacticsButton->block(false);
-		tacticsButton->callback = vstd::assigno(curHero->tacticFormationEnabled,true);
-		tacticsButton->callback2 = vstd::assigno(curHero->tacticFormationEnabled,false);
+		tacticsButton->addCallback( [&](bool on) {curHero->tacticFormationEnabled = on;});
+
 	}
 
 	//setting formations
+<<<<<<< HEAD:client/CHeroWindow.cpp
 	formations->onChange = 0;
 	formations->select(curHero->formation,true);
 	formations->onChange = std::bind(&CCallback::setFormation, LOCPLINT->cb.get(), curHero, _1);
+=======
+	formations->setSelected(curHero->formation);
+	formations->addCallback([&] (int value) { LOCPLINT->cb->setFormation(curHero, value); });
+>>>>>>> refactoring/guiClasses:client/windows/CHeroWindow.cpp
 
 	morale->set(&heroWArt);
 	luck->set(&heroWArt);
@@ -328,7 +355,7 @@ void CHeroWindow::commanderWindow()
 		}
 	}
 	else
-		GH.pushInt(new CCreatureWindow (curHero->commander));
+		GH.pushInt(new CStackWindow(curHero->commander, false));
 
 }
 

Some files were not shown because too many files changed in this diff