Browse Source

Merged GUI refactoring into develop, fixed conflicts

Ivan Savenko 11 năm trước cách đây
mục cha
commit
b1285bc506
100 tập tin đã thay đổi với 7955 bổ sung11820 xóa
  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));
 
 }
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác