Browse Source

- rewrote most of classes from adventure map window
-- new files: client/AdventureMapClasses.*
-- implemented all missing details from infobox
- textinput can handle numbers as input
- fixed several bugs caused by CIntObject changes
- fixed #988

Ivan Savenko 13 years ago
parent
commit
9d2711fb51

+ 5 - 4
Global.h

@@ -185,11 +185,12 @@ namespace vstd
 	}
 
 	//returns position of first element in vector c equal to s, if there is no such element, -1 is returned
-	template <typename T1, typename T2>
-	int find_pos(const std::vector<T1> & c, const T2 &s)
+	template <typename Container, typename T2>
+	int find_pos(const Container & c, const T2 &s)
 	{
-		for(size_t i=0; i < c.size(); ++i)
-			if(c[i] == s)
+		size_t i=0;
+		for (auto iter = c.begin(); iter != c.end(); iter++, i++)
+			if(*iter == s)
 				return i;
 		return -1;
 	}

+ 935 - 0
client/AdventureMapClasses.cpp

@@ -0,0 +1,935 @@
+#include "StdInc.h"
+#include "AdventureMapClasses.h"
+
+#include "../CCallback.h"
+#include "../lib/JsonNode.h"
+#include "../lib/map.h"
+#include "../lib/CObjectHandler.h"
+#include "../lib/CGameState.h"
+#include "../lib/CGeneralTextHandler.h"
+#include "../lib/NetPacks.h"
+#include "CAdvmapInterface.h"
+#include "CAnimation.h"
+#include "CGameInfo.h"
+#include "CPlayerInterface.h"
+#include "CMusicHandler.h"
+#include "Graphics.h"
+#include "GUIClasses.h"
+#include "UIFramework/CGuiHandler.h"
+#include "UIFramework/SDL_Pixels.h"
+
+/*
+ * CAdventureMapClasses.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
+ *
+ */
+
+CList::CListItem::CListItem(CList * Parent):
+    CIntObject(LCLICK | RCLICK | HOVER),
+    parent(Parent),
+    selection(nullptr)
+{
+}
+
+CList::CListItem::~CListItem()
+{
+	// select() method in this was already destroyed so we can't safely call method in parent
+	if (parent->selected == this)
+		parent->selected = nullptr;
+}
+
+void CList::CListItem::clickRight(tribool down, bool previousState)
+{
+	if (down == true)
+		showTooltip();
+}
+
+void CList::CListItem::clickLeft(tribool down, bool previousState)
+{
+	if (down == true)
+	{
+		//second click on already selected item
+		if (parent->selected == this)
+			open();
+		else
+		{
+			//first click - switch selection
+			parent->select(this);
+		}
+	}
+}
+
+void CList::CListItem::hover(bool on)
+{
+	if (on)
+		GH.statusbar->print(getHoverText());
+	else
+		GH.statusbar->clear();
+}
+
+void CList::CListItem::onSelect(bool on)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	vstd::clear_pointer(selection);
+	if (on)
+		selection = genSelection();
+	select(on);
+	GH.totalRedraw();
+}
+
+CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount,
+             int helpUp, int helpDown, CListBox::CreateFunc create, CListBox::DestroyFunc destroy):
+    CIntObject(0, position),
+    size(Size),
+    selected(nullptr)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	scrollUp = new CAdventureMapButton(CGI->generaltexth->zelp[helpUp], 0, 0, 0, btnUp);
+	list = new CListBox(create, destroy, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
+
+	//assign callback only after list was created
+	scrollUp->callback = boost::bind(&CListBox::moveToPrev, list);
+	scrollDown = new CAdventureMapButton(CGI->generaltexth->zelp[helpDown], boost::bind(&CListBox::moveToNext, list), 0, scrollUp->pos.h + 32*size, btnDown);
+
+	scrollDown->callback += boost::bind(&CList::update, this);
+	scrollUp->callback += boost::bind(&CList::update, this);
+
+	update();
+}
+
+void CList::update()
+{
+	bool onTop = list->getPos() == 0;
+	bool onBottom = list->getPos() + size >= list->size();
+
+	scrollUp->block(onTop);
+	scrollDown->block(onBottom);
+}
+
+void CList::select(CListItem *which)
+{
+	if (selected == which)
+		return;
+
+	if (selected)
+		selected->onSelect(false);
+
+	selected = which;
+	if (which)
+	{
+		which->onSelect(true);
+		onSelect();
+	}
+}
+
+int CList::getSelectedIndex()
+{
+	return list->getIndexOf(selected);
+}
+
+void CList::selectIndex(int which)
+{
+	if (which < 0)
+	{
+		if (selected)
+			select(nullptr);
+	}
+	else
+	{
+		list->scrollTo(which);
+		update();
+		select(dynamic_cast<CListItem*>(list->getItem(which)));
+	}
+}
+
+void CList::selectNext()
+{
+	int index = getSelectedIndex();
+	if (index < 0)
+		selectIndex(0);
+	else if (index + 1 < list->size())
+		selectIndex(index+1);
+}
+
+void CList::selectPrev()
+{
+	int index = getSelectedIndex();
+	if (index <= 0)
+		selectIndex(0);
+	else
+		selectIndex(index-1);
+}
+
+CHeroList::CEmptyHeroItem::CEmptyHeroItem()
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	auto move = new CAnimImage("IMOBIL", 0, 0, 0, 1);
+	auto img  = new CPicture("HPSXXX", move->pos.w + 1);
+	auto mana = new CAnimImage("IMANA", 0, 0, move->pos.w + img->pos.w + 2, 1 );
+
+	pos.w = mana->pos.w + mana->pos.x - pos.x;
+	pos.h = std::max(std::max<ui16>(move->pos.h + 1, mana->pos.h + 1), img->pos.h);
+}
+
+CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero):
+    CListItem(parent),
+    hero(Hero)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	movement = new CAnimImage("IMOBIL", 0, 0, 0, 1);
+	portrait = new CAnimImage("PortraitsSmall", hero->portrait, 0, movement->pos.w + 1);
+	mana     = new CAnimImage("IMANA", 0, 0, movement->pos.w + portrait->pos.w + 2, 1 );
+
+	pos.w = mana->pos.w + mana->pos.x - pos.x;
+	pos.h = std::max(std::max<ui16>(movement->pos.h + 1, mana->pos.h + 1), portrait->pos.h);
+
+	update();
+}
+
+void CHeroList::CHeroItem::update()
+{
+	movement->setFrame(std::min<size_t>(movement->size(), hero->movement / 100));
+	mana->setFrame(std::min<size_t>(mana->size(), hero->mana / 5));
+	redraw();
+}
+
+CIntObject * CHeroList::CHeroItem::genSelection()
+{
+	return new CPicture("HPSYYY", movement->pos.w + 1);
+}
+
+void CHeroList::CHeroItem::select(bool on)
+{
+	if (on && adventureInt->selection != hero)
+			adventureInt->select(hero);
+}
+
+void CHeroList::CHeroItem::open()
+{
+	LOCPLINT->openHeroWindow(hero);
+}
+
+void CHeroList::CHeroItem::showTooltip()
+{
+	CRClickPopup::createAndPush(hero, GH.current->motion);
+}
+
+std::string CHeroList::CHeroItem::getHoverText()
+{
+	return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->name % hero->type->heroClass->name);
+}
+
+CIntObject * CHeroList::createHeroItem(size_t index)
+{
+	if (LOCPLINT->wanderingHeroes.size() > index)
+		return new CHeroItem(this, LOCPLINT->wanderingHeroes[index]);
+	return new CEmptyHeroItem();
+}
+
+CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
+    CList(size, position, btnUp, btnDown, LOCPLINT->wanderingHeroes.size(), 303, 304, boost::bind(&CHeroList::createHeroItem, this, _1))
+{
+}
+
+void CHeroList::select(const CGHeroInstance * hero)
+{
+	selectIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
+}
+
+void CHeroList::update(const CGHeroInstance * hero)
+{
+	if (vstd::contains(LOCPLINT->wanderingHeroes, hero))
+	{
+		//this hero is already present, update its status
+		for (auto iter = list->getItems().begin(); iter != list->getItems().end(); iter++)
+		{
+			auto item = dynamic_cast<CHeroItem*>(*iter);
+			if (item && item->hero == hero)
+			{
+				item->update();
+				return;
+			}
+		}
+		return;
+	}
+	//simplest solution for now: reset list and restore selection
+
+	list->resize(LOCPLINT->wanderingHeroes.size());
+	if (adventureInt->selection)
+	{
+		auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
+		if (hero)
+			select(hero);
+	}
+	CList::update();
+}
+
+CIntObject * CTownList::createTownItem(size_t index)
+{
+	if (LOCPLINT->towns.size() > index)
+		return new CTownItem(this, LOCPLINT->towns[index]);
+	return new CAnimImage("ITPA", 0);
+}
+
+CTownList::CTownItem::CTownItem(CTownList *parent, const CGTownInstance *Town):
+    CListItem(parent),
+    town(Town)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	picture = new CAnimImage("ITPA", 0);
+	pos = picture->pos;
+	update();
+}
+
+CIntObject * CTownList::CTownItem::genSelection()
+{
+	return new CAnimImage("ITPA", 1);
+}
+
+void CTownList::CTownItem::update()
+{
+	size_t iconIndex = town->subID*2;
+	if (!town->hasFort())
+		iconIndex += GameConstants::F_NUMBER*2;
+
+	if(town->builded >= GameConstants::MAX_BUILDING_PER_TURN)
+		iconIndex++;
+
+	picture->setFrame(iconIndex + 2);
+	redraw();
+}
+
+void CTownList::CTownItem::select(bool on)
+{
+	if (on && adventureInt->selection != town)
+			adventureInt->select(town);
+}
+
+void CTownList::CTownItem::open()
+{
+	LOCPLINT->openTownWindow(town);
+}
+
+void CTownList::CTownItem::showTooltip()
+{
+	CRClickPopup::createAndPush(town, GH.current->motion);
+}
+
+std::string CTownList::CTownItem::getHoverText()
+{
+	return town->hoverName;
+}
+
+CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
+    CList(size, position, btnUp, btnDown, LOCPLINT->towns.size(),  306, 307, boost::bind(&CTownList::createTownItem, this, _1))
+{
+}
+
+void CTownList::select(const CGTownInstance * town)
+{
+	selectIndex(vstd::find_pos(LOCPLINT->towns, town));
+}
+
+void CTownList::update(const CGTownInstance *)
+{
+	//simplest solution for now: reset list and restore selection
+
+	list->resize(LOCPLINT->towns.size());
+	if (adventureInt->selection)
+	{
+		auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
+		if (town)
+			select(town);
+	}
+	CList::update();
+}
+
+const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
+{
+	static const SDL_Color fogOfWar = {0, 0, 0, 255};
+
+	const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
+
+	// if tile is not visible it will be black on minimap
+	if(!tile)
+		return fogOfWar;
+
+	// if object at tile is owned - it will be colored as its owner
+	BOOST_FOREACH(const CGObjectInstance *obj, tile->blockingObjects)
+	{
+		//heroes will be blitted later
+		if (obj->ID == GameConstants::HEROI_TYPE)
+			continue;
+
+		int player = obj->getOwner();
+		if(player == GameConstants::NEUTRAL_PLAYER)
+			return *graphics->neutralColor;
+		else
+		if (player < GameConstants::PLAYER_LIMIT)
+			return graphics->playerColors[player];
+	}
+
+	// else - use terrain color (blocked version or normal)
+	if (tile->blocked && (!tile->visitable))
+		return parent->colors.find(tile->tertype)->second.second;
+	else
+		return parent->colors.find(tile->tertype)->second.first;
+}
+
+void CMinimapInstance::blitTileWithColor(const SDL_Color &color, const int3 &tile, SDL_Surface *to, int toX, int toY)
+{
+	//method is mostly copy-pasted from drawScaled()
+	int3 mapSizes = LOCPLINT->cb->getMapSize();
+
+	double stepX = double(pos.w) / mapSizes.x;
+	double stepY = double(pos.h) / mapSizes.y;
+
+	//coordinates of rectangle on minimap representing this tile
+	// begin - first to blit, end - first NOT to blit
+	int xBegin = toX + stepX * tile.x;
+	int yBegin = toY + stepY * tile.y;
+	int xEnd = toX + stepX * (tile.x + 1);
+	int yEnd = toY + stepY * (tile.y + 1);
+
+	for (int y=yBegin; y<yEnd; y++)
+	{
+		Uint8 *ptr = (Uint8*)to->pixels + y * to->pitch + xBegin * minimap->format->BytesPerPixel;
+
+		for (int x=xBegin; x<xEnd; x++)
+			ColorPutter<4, 1>::PutColor(ptr, color);
+	}
+}
+
+void CMinimapInstance::refreshTile(const int3 &tile)
+{
+	blitTileWithColor(getTileColor(int3(tile.x, tile.y, level)), tile, minimap, 0, 0);
+}
+
+void CMinimapInstance::drawScaled(int level)
+{
+	int3 mapSizes = LOCPLINT->cb->getMapSize();
+
+	//size of one map tile on our minimap
+	double stepX = double(pos.w) / mapSizes.x;
+	double stepY = double(pos.h) / mapSizes.y;
+
+	double currY = 0;
+	for (int y=0; y<mapSizes.y; y++, currY += stepY)
+	{
+		double currX = 0;
+		for (int x=0; x<mapSizes.x; x++, currX += stepX)
+		{
+			const SDL_Color &color = getTileColor(int3(x,y,level));
+
+			//coordinates of rectangle on minimap representing this tile
+			// begin - first to blit, end - first NOT to blit
+			int xBegin = currX;
+			int yBegin = currY;
+			int xEnd = currX + stepX;
+			int yEnd = currY + stepY;
+
+			for (int y=yBegin; y<yEnd; y++)
+			{
+				Uint8 *ptr = (Uint8*)minimap->pixels + y * minimap->pitch + xBegin * minimap->format->BytesPerPixel;
+
+				for (int x=xBegin; x<xEnd; x++)
+					ColorPutter<4, 1>::PutColor(ptr, color);
+			}
+		}
+	}
+}
+
+CMinimapInstance::CMinimapInstance(CMinimap *Parent, int Level):
+    parent(Parent),
+    minimap(CSDL_Ext::createSurfaceWithBpp<4>(parent->pos.w, parent->pos.h)),
+    level(Level)
+{
+	pos.w = parent->pos.w;
+	pos.h = parent->pos.h;
+	drawScaled(level);
+}
+
+CMinimapInstance::~CMinimapInstance()
+{
+	SDL_FreeSurface(minimap);
+}
+
+void CMinimapInstance::showAll(SDL_Surface *to)
+{
+	blitAtLoc(minimap, 0, 0, to);
+
+	//draw heroes
+	std::vector <const CGHeroInstance *> heroes = LOCPLINT->cb->getHeroesInfo(false);
+	BOOST_FOREACH(auto & hero, heroes)
+	{
+		int3 position = hero->getPosition(false);
+		if (position.z == level)
+		{
+			const SDL_Color & color = graphics->playerColors[hero->getOwner()];
+			blitTileWithColor(color, position, to, pos.x, pos.y);
+		}
+	}
+}
+
+std::map<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string from)
+{
+	std::map<int, std::pair<SDL_Color, SDL_Color> > ret;
+
+	const JsonNode config(GameConstants::DATA_DIR + from);
+
+	BOOST_FOREACH(const JsonNode &m, config["MinimapColors"].Vector())
+	{
+		int id = m["terrain_id"].Float();
+
+		const JsonVector &unblockedVec = m["unblocked"].Vector();
+		SDL_Color normal =
+		{
+			ui8(unblockedVec[0].Float()),
+			ui8(unblockedVec[1].Float()),
+			ui8(unblockedVec[2].Float()),
+			ui8(255)
+		};
+
+		const JsonVector &blockedVec = m["blocked"].Vector();
+		SDL_Color blocked =
+		{
+			ui8(blockedVec[0].Float()),
+			ui8(blockedVec[1].Float()),
+			ui8(blockedVec[2].Float()),
+			ui8(255)
+		};
+
+		ret.insert(std::make_pair(id, std::make_pair(normal, blocked)));
+	}
+	return ret;
+}
+
+CMinimap::CMinimap(const Rect &position):
+    CIntObject(LCLICK | RCLICK | HOVER | MOVE, position.topLeft()),
+    aiShield(nullptr),
+    minimap(nullptr),
+    level(0),
+    colors(loadColors("/config/minimap.json"))
+{
+	pos.w = position.w;
+	pos.h = position.h;
+}
+
+void CMinimap::moveAdvMapSelection()
+{
+	// 0 = top-left corner, 1 = bottom-right corner
+	double dx = double(GH.current->motion.x - pos.x) / pos.w;
+	double dy = double(GH.current->motion.y - pos.y) / pos.h;
+
+	int3 mapSizes = LOCPLINT->cb->getMapSize();
+
+	int3 newLocation =
+	{
+	    si32(mapSizes.x * dx),
+	    si32(mapSizes.y * dy),
+	    si32(level)
+	};
+
+	adventureInt->centerOn(newLocation);
+
+	redraw();
+}
+
+void CMinimap::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+		moveAdvMapSelection();
+}
+
+void CMinimap::clickRight(tribool down, bool previousState)
+{
+	adventureInt->handleRightClick(CGI->generaltexth->zelp[291].second, down);
+}
+
+void CMinimap::hover(bool on)
+{
+	if (on)
+		GH.statusbar->print(CGI->generaltexth->zelp[291].first);
+	else
+		GH.statusbar->clear();
+}
+
+void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent)
+{
+	if (pressedL)
+		moveAdvMapSelection();
+}
+
+void CMinimap::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+	if (minimap)
+	{
+		int3 mapSizes = LOCPLINT->cb->getMapSize();
+
+		//draw radar
+		SDL_Rect oldClip;
+		SDL_Rect radar =
+		{
+			si16(adventureInt->position.x * pos.w / mapSizes.x + pos.x),
+			si16(adventureInt->position.y * pos.h / mapSizes.y + pos.y),
+			ui16(adventureInt->terrain.tilesw * pos.w / mapSizes.x),
+			ui16(adventureInt->terrain.tilesh * pos.h / mapSizes.y)
+		};
+
+		SDL_GetClipRect(to, &oldClip);
+		SDL_SetClipRect(to, &pos);
+		CSDL_Ext::drawDashedBorder(to, radar, int3(255,75,125));
+		SDL_SetClipRect(to, &oldClip);
+	}
+}
+
+void CMinimap::update()
+{
+	if (aiShield) //AI turn is going on. There is no need to update minimap
+		return;
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	vstd::clear_pointer(minimap);
+	minimap = new CMinimapInstance(this, level);
+}
+
+void CMinimap::setLevel(int newLevel)
+{
+	level = newLevel;
+	update();
+}
+
+void CMinimap::setAIRadar(bool on)
+{
+	if (on)
+	{
+		OBJ_CONSTRUCTION_CAPTURING_ALL;
+		vstd::clear_pointer(minimap);
+		if (!aiShield)
+			aiShield = new CPicture("AIShield");
+	}
+	else
+	{
+		vstd::clear_pointer(aiShield);
+		update();
+	}
+}
+
+void CMinimap::hideTile(const int3 &pos)
+{
+	if (minimap)
+		minimap->refreshTile(pos);
+}
+
+void CMinimap::showTile(const int3 &pos)
+{
+	if (minimap)
+		minimap->refreshTile(pos);
+}
+
+CInfoBar::CVisibleInfo::CVisibleInfo(Point position):
+    CIntObject(0, position),
+    aiProgress(nullptr)
+{
+
+}
+
+void CInfoBar::CVisibleInfo::show(SDL_Surface *to)
+{
+	CIntObject::show(to);
+	BOOST_FOREACH(auto object, forceRefresh)
+		object->showAll(to);
+}
+
+void CInfoBar::CVisibleInfo::loadHero(const CGHeroInstance * hero)
+{
+	assert(children.empty()); // visible info should be re-created to change type
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CPicture("ADSTATHR");
+	new CHeroTooltip(Point(0,0), hero);
+}
+
+void CInfoBar::CVisibleInfo::loadTown(const CGTownInstance *town)
+{
+	assert(children.empty()); // visible info should be re-created to change type
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CPicture("ADSTATCS");
+	new CTownTooltip(Point(0,0), town);
+}
+
+void CInfoBar::CVisibleInfo::playNewDaySound()
+{
+	if (LOCPLINT->cb->getDate(1) != 1) // not first day of the week
+		CCS->soundh->playSound(soundBase::newDay);
+	else
+	if (LOCPLINT->cb->getDate(2) != 1) // not first week in month
+		CCS->soundh->playSound(soundBase::newWeek);
+	else
+	if (LOCPLINT->cb->getDate(3) != 1) // not first month
+		CCS->soundh->playSound(soundBase::newMonth);
+	else
+		CCS->soundh->playSound(soundBase::newDay);
+}
+
+std::string CInfoBar::CVisibleInfo::getNewDayName()
+{
+	if (LOCPLINT->cb->getDate(0) == 1)
+		return "NEWDAY";
+
+	if (LOCPLINT->cb->getDate(1) != 1)
+		return "NEWDAY";
+
+	switch(LOCPLINT->cb->getDate(2))
+	{
+	case 1:  return "NEWWEEK1";
+	case 2:  return "NEWWEEK2";
+	case 3:  return "NEWWEEK3";
+	case 4:  return "NEWWEEK4";
+	default: assert(0); return "";
+	}
+}
+
+void CInfoBar::CVisibleInfo::loadDay()
+{
+	assert(children.empty()); // visible info should be re-created first to change type
+
+	playNewDaySound();
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CShowableAnim(1, 0, getNewDayName(), CShowableAnim::PLAY_ONCE);
+
+	std::string labelText;
+	if (LOCPLINT->cb->getDate(1) == 1 && LOCPLINT->cb->getDate(0) != 1) // monday of any week but first - show new week info
+		labelText = CGI->generaltexth->allTexts[63] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(2));
+	else
+		labelText = CGI->generaltexth->allTexts[64] + " " + boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(1));
+
+	forceRefresh.push_back(new CLabel(95, 31, FONT_MEDIUM, CENTER, Colors::Cornsilk, labelText));
+}
+
+void CInfoBar::CVisibleInfo::loadEnemyTurn(int player)
+{
+	assert(children.empty()); // visible info should be re-created to change type
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CPicture("ADSTATNX");
+	new CAnimImage("CREST58", player, 0, 20, 51);
+	new CShowableAnim(99, 51, "HOURSAND");
+
+	// FIXME: currently there is no way to get progress from VCAI
+	// if this will change at some point switch this ifdef to enable correct code
+#if 0
+	//prepare hourglass for updating AI turn
+	aiProgress = new CAnimImage("HOURGLAS", 0, 0, 99, 51);
+	forceRefresh.push_back(aiProgress);
+#else
+	//create hourglass that will be always animated ignoring AI status
+	new CShowableAnim(99, 51, "HOURGLAS", CShowableAnim::PLAY_ONCE, 40);
+#endif
+}
+
+void CInfoBar::CVisibleInfo::loadGameStatus()
+{
+	assert(children.empty()); // visible info should be re-created to change type
+
+	//get amount of halls of each level
+	std::vector<int> halls(4, 0);
+	BOOST_FOREACH(auto town, LOCPLINT->towns)
+		halls[town->hallLevel()]++;
+
+	std::vector<int> allies, enemies;
+
+	//generate list of allies and enemies
+	for(int i = 0; i < GameConstants::PLAYER_LIMIT; i++)
+	{
+		if(LOCPLINT->cb->getPlayerStatus(i) == PlayerState::INGAME)
+		{
+			if (LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, i) > 0)
+				allies.push_back(i);
+			else
+				enemies.push_back(i);
+		}
+	}
+
+	//generate component
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CPicture("ADSTATIN");
+	auto allyLabel  = new CLabel(10, 106, FONT_SMALL, TOPLEFT, Colors::Cornsilk, CGI->generaltexth->allTexts[390] + ":");
+	auto enemyLabel = new CLabel(10, 136, FONT_SMALL, TOPLEFT, Colors::Cornsilk, CGI->generaltexth->allTexts[391] + ":");
+
+	int posx = allyLabel->pos.w + allyLabel->pos.x - pos.x + 4;
+	BOOST_FOREACH(int & player, allies)
+	{
+		auto image = new CAnimImage("ITGFLAGS", player, 0, posx, 102);
+		posx += image->pos.w;
+	}
+
+	posx = enemyLabel->pos.w + enemyLabel->pos.x - pos.x + 4;
+	BOOST_FOREACH(int & player, enemies)
+	{
+		auto image = new CAnimImage("ITGFLAGS", player, 0, posx, 132);
+		posx += image->pos.w;
+	}
+
+	for (size_t i=0; i<halls.size(); i++)
+	{
+		new CAnimImage("itmtl", i, 0, 6 + 42 * i , 11);
+		if (halls[i])
+			new CLabel( 26 + 42 * i, 64, FONT_SMALL, CENTER, Colors::Cornsilk, boost::lexical_cast<std::string>(halls[i]));
+	}
+}
+
+void CInfoBar::CVisibleInfo::loadComponent(const Component compToDisplay, std::string message)
+{
+	assert(children.empty()); // visible info should be re-created to change type
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	new CPicture("ADSTATOT");
+
+	auto comp = new CComponent(compToDisplay);
+	comp->moveTo(Point(pos.x+52, pos.y+54));
+
+	new CTextBox(message, Rect(8, 8, 164, 50), 0, FONT_SMALL, CENTER, Colors::Cornsilk);
+	new CLabel(91, 158, FONT_SMALL, CENTER, Colors::Cornsilk, comp->subtitle);
+}
+
+void CInfoBar::CVisibleInfo::updateEnemyTurn(double progress)
+{
+	if (aiProgress)
+	aiProgress->setFrame((aiProgress->size() - 1) * progress);
+}
+
+void CInfoBar::reset(EState newState = EMPTY)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	vstd::clear_pointer(visibleInfo);
+	currentObject = nullptr;
+	state = newState;
+	visibleInfo = new CVisibleInfo(Point(8, 12));
+}
+
+void CInfoBar::showSelection()
+{
+	if (adventureInt->selection)
+	{
+		auto hero = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
+		if (hero)
+		{
+			showHeroSelection(hero, false);
+			return;
+		}
+		auto town = dynamic_cast<const CGTownInstance *>(adventureInt->selection);
+		if (town)
+		{
+			showTownSelection(town, false);
+			return;
+		}
+	}
+	showGameStatus();//FIXME: may be incorrect but shouldn't happen in general
+}
+
+void CInfoBar::tick()
+{
+	removeUsedEvents(TIME);
+	showSelection();
+}
+
+void CInfoBar::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+	{
+		if (state == HERO || state == TOWN)
+			showGameStatus();
+		else if (state == GAME)
+			showDate();
+		else
+			showSelection();
+	}
+}
+
+void CInfoBar::clickRight(tribool down, bool previousState)
+{
+	adventureInt->handleRightClick(CGI->generaltexth->allTexts[109], down);
+}
+
+void CInfoBar::hover(bool on)
+{
+	if (on)
+		GH.statusbar->print(CGI->generaltexth->zelp[292].first);
+	else
+		GH.statusbar->clear();
+}
+
+CInfoBar::CInfoBar(const Rect &position):
+    CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()),
+    visibleInfo(nullptr),
+    state(EMPTY),
+    currentObject(nullptr)
+{
+	pos.w = position.w;
+	pos.h = position.h;
+	//FIXME: enable some mode? Should be done by advMap::select() when game starts but just in case?
+}
+
+void CInfoBar::showDate()
+{
+	reset(DATE);
+	visibleInfo->loadDay();
+	setTimer(3000);
+	redraw();
+}
+
+void CInfoBar::showComponent(const Component comp, std::string message)
+{
+	reset(COMPONENT);
+	visibleInfo->loadComponent(comp, message);
+	setTimer(3000);
+	redraw();
+}
+
+void CInfoBar::startEnemyTurn(ui8 color)
+{
+	reset(AITURN);
+	visibleInfo->loadEnemyTurn(color);
+	redraw();
+}
+
+void CInfoBar::updateEnemyTurn(double progress)
+{
+	assert(state == AITURN);
+	visibleInfo->updateEnemyTurn(progress);
+	redraw();
+}
+
+void CInfoBar::showHeroSelection(const CGHeroInstance * hero, bool onlyUpdate)
+{
+	reset(HERO);
+	currentObject = hero;
+	visibleInfo->loadHero(hero);
+	redraw();
+}
+
+void CInfoBar::showTownSelection(const CGTownInstance * town, bool onlyUpdate)
+{
+	reset(TOWN);
+	currentObject = town;
+	visibleInfo->loadTown(town);
+	redraw();
+}
+
+void CInfoBar::showGameStatus()
+{
+	reset(GAME);
+	visibleInfo->loadGameStatus();
+	setTimer(3000);
+	redraw();
+}

+ 311 - 0
client/AdventureMapClasses.h

@@ -0,0 +1,311 @@
+#pragma once
+
+#include "UIFramework/CIntObject.h"
+#include "UIFramework/CIntObjectClasses.h"
+
+class CArmedInstance;
+class CShowableAnim;
+class CGGarrison;
+class CGObjectInstance;
+class CGHeroInstance;
+class CGTownInstance;
+struct Component;
+struct InfoAboutArmy;
+struct InfoAboutHero;
+struct InfoAboutTown;
+
+/*
+ * CAdventureMapClasses.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
+ *
+ */
+
+/// Base UI Element for hero\town lists
+class CList : public CIntObject
+{
+protected:
+	class CListItem : public CIntObject
+	{
+		CList * parent;
+		CIntObject * selection;
+	public:
+		CListItem(CList * parent);
+		~CListItem();
+
+		void clickRight(tribool down, bool previousState);
+		void clickLeft(tribool down, bool previousState);
+		void hover(bool on);
+		void onSelect(bool on);
+
+		/// create object with selection rectangle
+		virtual CIntObject * genSelection()=0;
+		/// reaction on item selection (e.g. enable selection border)
+		/// NOTE: item may be deleted in selected state
+		virtual void select(bool on)=0;
+		/// open item (town or hero screen)
+		virtual void open()=0;
+		/// show right-click tooltip
+		virtual void showTooltip()=0;
+		/// get hover text for status bar
+		virtual std::string getHoverText()=0;
+	};
+
+	CListBox * list;
+	const size_t size;
+
+	/**
+	 * @brief CList - protected constructor
+	 * @param Size - maximal amount of visible at once items
+	 * @param position - cordinates
+	 * @param btnUp - path to image to use as top button
+	 * @param btnDown - path to image to use as bottom button
+	 * @param listAmount - amount of items in the list
+	 * @param helpUp - index in zelp.txt for button help tooltip
+	 * @param helpDown - index in zelp.txt for button help tooltip
+	 * @param create - function for creating items in listbox
+	 * @param destroy - function for deleting items in listbox
+	 */
+	CList(int size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown,
+	      CListBox::CreateFunc create, CListBox::DestroyFunc destroy = CListBox::DestroyFunc());
+
+	//for selection\deselection
+	CListItem *selected;
+	void select(CListItem * which);
+	friend class CListItem;
+
+	/// should be called when list is invalidated
+	void update();
+
+public:
+
+	CAdventureMapButton * scrollUp;
+	CAdventureMapButton * scrollDown;
+
+	/// functions that will be called when selection changes
+	CFunctionList<void()> onSelect;
+
+	/// return index of currently selected element
+	int getSelectedIndex();
+
+	/// set of methods to switch selection
+	void selectIndex(int which);
+	void selectNext();
+	void selectPrev();
+};
+
+/// List of heroes which is shown at the right of the adventure map screen
+class CHeroList	: public CList
+{
+	/// Empty hero item used as placeholder for unused entries in list
+	class CEmptyHeroItem : public CIntObject
+	{
+	public:
+		CEmptyHeroItem();
+	};
+
+	class CHeroItem : public CListItem
+	{
+		CAnimImage * movement;
+		CAnimImage * mana;
+		CAnimImage * portrait;
+	public:
+		const CGHeroInstance * const hero;
+
+		CHeroItem(CHeroList *parent, const CGHeroInstance * hero);
+
+		CIntObject * genSelection();
+		void update();
+		void select(bool on);
+		void open();
+		void showTooltip();
+		std::string getHoverText();
+	};
+
+	CIntObject * createHeroItem(size_t index);
+public:
+	/**
+	 * @brief CHeroList
+	 * @param Size, position, btnUp, btnDown @see CList::CList
+	 */
+	CHeroList(int size, Point position, std::string btnUp, std::string btnDown);
+
+	/// Select specific hero and scroll if needed
+	void select(const CGHeroInstance * hero = nullptr);
+
+	/// Update hero. Will add or remove it from the list if needed
+	void update(const CGHeroInstance * hero = nullptr);
+};
+
+/// List of towns which is shown at the right of the adventure map screen or in the town screen
+class CTownList	: public CList
+{
+	class CTownItem : public CListItem
+	{
+		CAnimImage * picture;
+	public:
+		const CGTownInstance * const town;
+
+		CTownItem(CTownList *parent, const CGTownInstance * town);
+
+		CIntObject * genSelection();
+		void update();
+		void select(bool on);
+		void open();
+		void showTooltip();
+		std::string getHoverText();
+	};
+
+	CIntObject * createTownItem(size_t index);
+public:
+	/**
+	 * @brief CTownList
+	 * @param Size, position, btnUp, btnDown @see CList::CList
+	 */
+	CTownList(int size, Point position, std::string btnUp, std::string btnDown);
+
+	/// Select specific town and scroll if needed
+	void select(const CGTownInstance * town = nullptr);
+
+	/// Update town. Will add or remove it from the list if needed
+	void update(const CGTownInstance * town = nullptr);
+};
+
+class CMinimap;
+
+class CMinimapInstance : public CIntObject
+{
+	CMinimap *parent;
+	SDL_Surface * minimap;
+	int level;
+
+	//get color of selected tile on minimap
+	const SDL_Color & getTileColor(const int3 & pos);
+
+	void blitTileWithColor(const SDL_Color & color, const int3 & pos, SDL_Surface *to, int x, int y);
+
+	//draw minimap already scaled.
+	//result is not antialiased. Will result in "missing" pixels on huge maps (>144)
+	void drawScaled(int level);
+public:
+	CMinimapInstance(CMinimap * parent, int level);
+	~CMinimapInstance();
+
+	void showAll(SDL_Surface *to);
+
+	void refreshTile(const int3 &pos);
+};
+
+/// Minimap which is displayed at the right upper corner of adventure map
+class CMinimap : public CIntObject
+{
+	CPicture *aiShield; //the graphic displayed during AI turn
+	CMinimapInstance * minimap;
+	int level;
+
+	//to initialize colors
+	std::map<int, std::pair<SDL_Color, SDL_Color> > loadColors(std::string from);
+
+	void moveAdvMapSelection();
+
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+	void hover (bool on);
+	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
+
+public:
+	// terrainID -> (normal color, blocked color)
+	const std::map<int, std::pair<SDL_Color, SDL_Color> > colors;
+
+	CMinimap(const Rect & position);
+
+	//should be called to invalidate whole map - different player or level
+	void update();
+	void setLevel(int level);
+	void setAIRadar(bool on);
+
+	void showAll(SDL_Surface * to);
+
+	void hideTile(const int3 &pos); //puts FoW
+	void showTile(const int3 &pos); //removes FoW
+};
+
+/// Info box which shows next week/day information, hold the current date
+class CInfoBar : public CIntObject
+{
+	//all visible information located in one object - for ease of replacing
+	class CVisibleInfo : public CIntObject
+	{
+		//list of objects that must be redrawed on each frame on a top of animation
+		std::list<CIntObject *> forceRefresh;
+
+		//the only part of gui we need to know about for updating - AI progress
+		CAnimImage *aiProgress;
+
+		std::string getNewDayName();
+		void playNewDaySound();
+
+	public:
+		CVisibleInfo(Point position);
+
+		void show(SDL_Surface *to);
+
+		//functions that must be called only once
+		void loadHero(const CGHeroInstance * hero);
+		void loadTown(const CGTownInstance * town);
+		void loadDay();
+		void loadEnemyTurn(int player);
+		void loadGameStatus();
+		void loadComponent(const Component comp, std::string message);
+
+		//can be called multiple times
+		void updateEnemyTurn(double progress);
+	};
+
+	enum EState
+	{
+		EMPTY, HERO, TOWN, DATE, GAME, AITURN, COMPONENT
+	};
+
+	CVisibleInfo * visibleInfo;
+	EState state;
+	//currently displayed object. May be null if state is not hero or town
+	const CGObjectInstance * currentObject;
+
+	//removes all information about current state, deactivates timer (if any)
+	void reset(EState newState);
+	//reset to default view - selected object
+	void showSelection();
+
+	void tick();
+
+	void clickLeft(tribool down, bool previousState);
+	void clickRight(tribool down, bool previousState);
+	void hover(bool on);
+
+public:
+	CInfoBar(const Rect & pos);
+
+	/// show new day/week animation
+	void showDate();
+
+	/// show component for 3 seconds. Used to display picked up resources
+	void showComponent(const Component comp, std::string message);
+
+	/// print enemy turn progress
+	void startEnemyTurn(ui8 color);
+	/// updates enemy turn.
+	/// NOTE: currently DISABLED. Check comments in CInfoBar::CVisibleInfo::loadEnemyTurn()
+	void updateEnemyTurn(double progress);
+
+	/// show hero\town information
+	/// if onlyUpdate set to true this call won't switch to town\hero but only update current view
+	void showHeroSelection(const CGHeroInstance * hero, bool onlyUpdate);
+	void showTownSelection(const CGTownInstance * town, bool onlyUpdate);
+
+	/// for 3 seconds shows amount of town halls and players status
+	void showGameStatus();
+};

+ 2 - 5
client/BattleInterface/CBattleAnimations.cpp

@@ -428,12 +428,10 @@ if( !isEarliest(false) )
 	}
 	//unit reversed
 
-	if(owner->moveSh >= 0)
+	if (owner->moveSh == -1)
 	{
-		CCS->soundh->stopSound(owner->moveSh);
-		owner->moveSh = -1;
+		owner->moveSh = CCS->soundh->playSound(battle_sound(movedStack->getCreature(), move), -1);
 	}
-	owner->moveSh = CCS->soundh->playSound(battle_sound(movedStack->getCreature(), move), -1);
 
 	//step shift calculation
 	posX = myAnim()->pos.x, posY = myAnim()->pos.y; // for precise calculations ;]
@@ -540,7 +538,6 @@ void CMovementAnimation::endAnim()
 		CCS->soundh->stopSound(owner->moveSh);
 		owner->moveSh = -1;
 	}
-
 	delete this;
 }
 

+ 53 - 668
client/CAdvmapInterface.cpp

@@ -53,414 +53,6 @@ using namespace CSDL_Ext;
 
 CAdvMapInt *adventureInt;
 
-CMinimap::CMinimap()
-{
-	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	addUsedEvents(LCLICK | RCLICK | HOVER);
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	statusbarTxt = CGI->generaltexth->zelp[291].first;
-	rcText = CGI->generaltexth->zelp[291].second;
-	pos.x=ADVOPT.minimapX;
-	pos.y=ADVOPT.minimapY;
-	pos.h=ADVOPT.minimapW;
-	pos.w=ADVOPT.minimapH;
-
-	temps = CSDL_Ext::createSurfaceWithBpp<4>(pos.w,pos.h);
-	aiShield = new CPicture("AISHIELD.bmp");
-
-	const JsonNode config(GameConstants::DATA_DIR + "/config/minimap.json");
-	const JsonVector &minimap_vec = config["MinimapColors"].Vector();
-
-	BOOST_FOREACH(const JsonNode &m, minimap_vec) {
-		std::pair<int,SDL_Color> vinya;
-
-		vinya.first = m["terrain_id"].Float();
-
-		const JsonVector &unblocked_vec = m["unblocked"].Vector();
-		vinya.second.r = unblocked_vec[0].Float();
-		vinya.second.g = unblocked_vec[1].Float();
-		vinya.second.b = unblocked_vec[2].Float();
-		vinya.second.unused = 255;
-		colors.insert(vinya);
-
-		const JsonVector &blocked_vec = m["blocked"].Vector();
-		vinya.second.r = blocked_vec[0].Float();
-		vinya.second.g = blocked_vec[1].Float();
-		vinya.second.b = blocked_vec[2].Float();
-		vinya.second.unused = 255;
-		colorsBlocked.insert(vinya);
-	}
-}
-
-CMinimap::~CMinimap()
-{
-	SDL_FreeSurface(temps);
-	for (std::map<int, CMinimapSurfacesRef>::iterator it = surfs.begin(); it != surfs.end(); ++it)
-	{
-		it->second.free();
-	}
-}
-
-void CMinimap::draw(SDL_Surface * to)
-{
-	int player = adventureInt->player;
-	if(LOCPLINT->makingTurn)
-	{
-		int3 mapSizes = LOCPLINT->cb->getMapSize();
-		//draw terrain
-		blitAt(surfs[player].map()[adventureInt->position.z],0,0,temps);
-
-		//draw heroes
-		std::vector <const CGHeroInstance *> hh = LOCPLINT->cb->getHeroesInfo(false);
-		int mw = surfs[player].map()[0]->w, mh = surfs[player].map()[0]->h,
-			wo = mw/mapSizes.x, ho = mh/mapSizes.y;
-
-		for (size_t i=0; i < hh.size(); ++i)
-		{
-			int3 hpos = hh[i]->getPosition(false);
-			if(hpos.z!=adventureInt->position.z)
-				continue;
-
-			int3 maplgp ( (hpos.x*mw)/mapSizes.x, (hpos.y*mh)/mapSizes.y, hpos.z );
-			for (int ii=0; ii<wo; ii++)
-			{
-				for (int jj=0; jj<ho; jj++)
-				{
-					SDL_PutPixelWithoutRefresh(temps,maplgp.x+ii,maplgp.y+jj,graphics->playerColors[hh[i]->getOwner()].r,
-						graphics->playerColors[hh[i]->getOwner()].g,graphics->playerColors[hh[i]->getOwner()].b);
-				}
-			}
-		}
-
-		blitAt(surfs[player].flObjs()[adventureInt->position.z],0,0,temps);
-
-		blitAt(surfs[player].FoW()[adventureInt->position.z],0,0,temps);
-
-		//draw radar
-		const int tilesw=(ADVOPT.advmapW+31)/32;
-		const int tilesh=(ADVOPT.advmapH+31)/32;
-		int bx = static_cast<int>((adventureInt->position.x / static_cast<double>(mapSizes.x)) * pos.w),
-			by = static_cast<int>((adventureInt->position.y / static_cast<double>(mapSizes.y)) * pos.h),
-			rx = static_cast<int>((tilesw / static_cast<double>(mapSizes.x)) * pos.w), //width
-			ry = static_cast<int>((tilesh / static_cast<double>(mapSizes.y)) * pos.h); //height
-
-		CSDL_Ext::drawDashedBorder(temps, Rect(bx, by, rx, ry), int3(255,75,125));
-
-		//blitAt(radar,bx,by,temps);
-		blitAt(temps,pos.x,pos.y,to);
-	}
-	else
-	{
-		aiShield->showAll(to);
-	}
-}
-
-CMinimapSurfacesRef::CMinimapSurfacesRef() : ready(false)
-{
-}
-
-
-void CMinimapSurfacesRef::redraw(int level)
-{
-	ready = true;
-	initMap(level);
-
-	//FoW
-	initFoW(level);
-
-	//flaggable objects
-	initFlaggableObjs(level);
-
-	//showing tiles
-	showVisibleTiles();
-}
-
-void CMinimapSurfacesRef::initMap(int level)
-{
-	const Rect &minimap_pos = adventureInt->minimap.pos;
-	std::map<int,SDL_Color> &colors = adventureInt->minimap.colors;
-	std::map<int,SDL_Color> &colorsBlocked = adventureInt->minimap.colorsBlocked;
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	for (size_t i=0; i<CGI->mh->sizes.z; i++)
-	{
-		SDL_Surface *pom;
-		if ((level>=0) && (i!=level))
-			continue;
-		if (map_.size()<i+1)
-			pom = CSDL_Ext::newSurface(minimap_pos.w,minimap_pos.h,screen);
-		else pom = map_[i];
-		for (int x=0;x<minimap_pos.w;x++)
-		{
-			for (int y=0;y<minimap_pos.h;y++)
-			{
-				int mx=(mapSizes.x*x)/minimap_pos.w;
-				int my=(mapSizes.y*y)/minimap_pos.h;
-				const TerrainTile * tile = LOCPLINT->cb->getTile(int3(mx, my, i), false);
-				if(tile)
-				{
-					if (tile->blocked && (!tile->visitable))
-						SDL_PutPixelWithoutRefresh(pom, x, y, colorsBlocked[tile->tertype].r, colorsBlocked[tile->tertype].g, colorsBlocked[tile->tertype].b);
-					else
-						SDL_PutPixelWithoutRefresh(pom, x, y, colors[tile->tertype].r, colors[tile->tertype].g, colors[tile->tertype].b);
-				}
-			}
-		}
-		map_.push_back(pom);
-
-	}
-}
-
-void CMinimapSurfacesRef::initFoW(int level)
-{
-	const Rect &minimap_pos = adventureInt->minimap.pos;
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	int mw = map_[0]->w, mh = map_[0]->h;//,
-		//wo = mw/mapSizes.x, ho = mh/mapSizes.y; //TODO use me
-	for(int d=0; d<CGI->mh->map->twoLevel+1; ++d)
-	{
-		if(level>=0 && d!=level)
-			continue;
-		SDL_Surface * pt = CSDL_Ext::createSurfaceWithBpp<4>(minimap_pos.w, minimap_pos.h);
-		for (int i=0; i<mw; i++)
-		{
-			for (int j=0; j<mh; j++)
-			{
-				int3 pp( ((i*mapSizes.x)/mw), ((j*mapSizes.y)/mh), d );
-				if ( !LOCPLINT->cb->isVisible(pp) )
-				{
-					CSDL_Ext::SDL_PutPixelWithoutRefresh(pt,i,j,0,0,0);
-				}
-			}
-		}
-		FoW_.push_back(pt);
-	}
-}
-
-void CMinimapSurfacesRef::initFlaggableObjs(int level)
-{
-	const Rect &minimap_pos = adventureInt->minimap.pos;
-	int mw = map_[0]->w, mh = map_[0]->h;
-	for(int d=0; d<CGI->mh->map->twoLevel+1; ++d)
-	{
-		if(level>=0 && d!=level)
-			continue;
-		SDL_Surface * pt = CSDL_Ext::createSurfaceWithBpp<4>(minimap_pos.w, minimap_pos.h);
-		for (int i=0; i<mw; i++)
-		{
-			for (int j=0; j<mh; j++)
-			{
-				CSDL_Ext::SDL_PutPixelWithoutRefresh(pt,i,j,0,0,0,0);
-			}
-		}
-		flObjs_.push_back(pt);
-	}
-}
-
-void CMinimap::updateRadar()
-{}
-
-void CMinimap::clickRight(tribool down, bool previousState)
-{
-	adventureInt->handleRightClick(rcText,down);
-}
-
-void CMinimap::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-		addUsedEvents(MOVE);
-	else
-		removeUsedEvents(MOVE);
-
-	//ClickableL::clickLeft(down);
-	if (!((bool)down))
-		return;
-
-	double dx = (GH.current->motion.x - pos.x) / static_cast<double>(pos.w),
-		dy = (GH.current->motion.y - pos.y) / static_cast<double>(pos.h);
-
-	int3 newCPos;
-	newCPos.x = (CGI->mh->sizes.x*dx);
-	newCPos.y = (CGI->mh->sizes.y*dy);
-	newCPos.z = adventureInt->position.z;
-	adventureInt->centerOn(newCPos);
-}
-
-void CMinimap::hover (bool on)
-{
-	//Hoverable::hover(on);
-	if (on)
-		adventureInt->statusbar.print(statusbarTxt);
-	else if (adventureInt->statusbar.current==statusbarTxt)
-		adventureInt->statusbar.clear();
-}
-
-void CMinimap::mouseMoved (const SDL_MouseMotionEvent & sEvent)
-{
-	if (pressedL)
-	{
-		clickLeft(true, true);
-	}
-}
-void CMinimap::activate()
-{
-	CIntObject::activate();
-}
-
-void CMinimap::deactivate()
-{
-	CIntObject::deactivate();
-}
-
-std::vector<SDL_Surface*> & CMinimapSurfacesRef::map()
-{
-	if (!ready) redraw();
-	return map_;
-}
-std::vector<SDL_Surface*> & CMinimapSurfacesRef::FoW()
-{
-	if (!ready) redraw();
-	return FoW_;
-}
-std::vector<SDL_Surface*> & CMinimapSurfacesRef::flObjs()
-{
-	if (!ready) redraw();
-	return flObjs_;
-}
-
-void CMinimapSurfacesRef::free()
-{
-	if (ready)
-	{
-		for (int g = 0; g < map_.size(); ++g)
-			SDL_FreeSurface(map_[g]);
-		map_.clear();
-
-		for (int g = 0; g < FoW_.size(); ++g)
-			SDL_FreeSurface(FoW_[g]);
-		FoW_.clear();
-
-		for (int g = 0; g < flObjs_.size(); ++g)
-			SDL_FreeSurface(flObjs_[g]);
-		flObjs_.clear();
-	}
-}
-
-void CMinimap::showTile(const int3 &pos)
-{
-	const int player = adventureInt->player;
-	std::vector<SDL_Surface*> &map = surfs[player].map();
-	std::vector<SDL_Surface*> &FoW = surfs[player].FoW();
-	std::vector<SDL_Surface*> &flObjs = surfs[player].flObjs();
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	//drawing terrain
-	int mw = map[0]->w, mh = map[0]->h;
-	double wo = ((double)mw)/mapSizes.x, ho = ((double)mh)/mapSizes.y;
-	for (int ii=0; ii<wo; ii++)
-	{
-		for (int jj=0; jj<ho; jj++)
-		{
-			if ((pos.x*wo+ii<this->pos.w) && (pos.y*ho+jj<this->pos.h))
-				CSDL_Ext::SDL_PutPixelWithoutRefresh(FoW[pos.z],pos.x*wo+ii,pos.y*ho+jj,0,0,0,0);
-
-			const TerrainTile * tile = LOCPLINT->cb->getTile(pos, false);
-			if(tile)
-			{
-				if (tile->blocked && (!tile->visitable))
-					SDL_PutPixelWithoutRefresh(surfs[player].map()[pos.z], pos.x*wo+ii, pos.y*ho+jj, colorsBlocked[tile->tertype].r, colorsBlocked[tile->tertype].g, colorsBlocked[tile->tertype].b);
-				else
-					SDL_PutPixelWithoutRefresh(surfs[player].map()[pos.z], pos.x*wo+ii, pos.y*ho+jj, colors[tile->tertype].r, colors[tile->tertype].g, colors[tile->tertype].b);
-			}
-		}
-	}
-	//drawing flaggable objects
-	int woShifted = wo, hoShifted = ho; //for better minimap rendering on L-sized maps
-	std::vector < const CGObjectInstance * > oo = LOCPLINT->cb->getFlaggableObjects(pos);
-	for(size_t v=0; v<oo.size(); ++v)
-	{
-		if(!dynamic_cast< const CGHeroInstance * >(oo[v])) //heroes have been printed
-		{
-			int3 maplgp ( (pos.x*mw)/mapSizes.x, (pos.y*mh)/mapSizes.y, pos.z );
-			if(((int)wo) * mapSizes.x != mw   &&   pos.x+1 < mapSizes.x)//minimap size in X is not multiple of map size in X
-
-			{
-				std::vector < const CGObjectInstance * > op1x = LOCPLINT->cb->getFlaggableObjects(int3(pos.x+1, pos.y, pos.z));
-				if(op1x.size()!=0)
-				{
-					woShifted = wo + 1;
-				}
-				else
-				{
-					woShifted = wo;
-				}
-			}
-			if(((int)ho) * mapSizes.y != mh   &&   pos.y+1 < mapSizes.y) //minimap size in Y is not multiple of map size in Y
-			{
-				std::vector < const CGObjectInstance * > op1y = LOCPLINT->cb->getFlaggableObjects(int3(pos.x, pos.y+1, pos.z));
-				if(op1y.size()!=0)
-				{
-					hoShifted = ho + 1;
-				}
-				else
-				{
-					hoShifted = ho;
-				}
-			}
-
-			for (int ii=0; ii<woShifted; ii++) //rendering flaggable objects
-			{
-				for (int jj=0; jj<hoShifted; jj++)
-				{
-					if(oo[v]->tempOwner == 255)
-						SDL_PutPixelWithoutRefresh(flObjs[pos.z],maplgp.x+ii,maplgp.y+jj,graphics->neutralColor->r,
-							graphics->neutralColor->g,graphics->neutralColor->b);
-					else
-						SDL_PutPixelWithoutRefresh(flObjs[pos.z],maplgp.x+ii,maplgp.y+jj,graphics->playerColors[oo[v]->getOwner()].r,
-							graphics->playerColors[oo[v]->getOwner()].g,graphics->playerColors[oo[v]->getOwner()].b);
-				}
-			}
-		}
-	}
-	//flaggable objects drawn
-}
-
-void CMinimapSurfacesRef::showVisibleTiles(int level)
-{
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	for(int d=0; d<CGI->mh->map->twoLevel+1; ++d)
-	{
-		if(level>=0 && d!=level)
-			continue;
-		for(int x=0; x<mapSizes.x; ++x)
-		{
-			for(int y=0; y<mapSizes.y; ++y)
-			{
-				if(LOCPLINT->cb->isVisible(int3(x, y, d)))
-				{
-					adventureInt->minimap.showTile(int3(x, y, d));
-				}
-			}
-		}
-	}
-}
-
-void CMinimap::hideTile(const int3 &pos)
-{
-	const int player = adventureInt->player;
-	std::vector<SDL_Surface*> &map = surfs[player].map();
-	std::vector<SDL_Surface*> &FoW = surfs[player].FoW();
-	int3 mapSizes = LOCPLINT->cb->getMapSize();
-	//drawing terrain
-	int mw = map[0]->w, mh = map[0]->h;
-	double wo = ((double)mw)/mapSizes.x, ho = ((double)mh)/mapSizes.y;
-	for (int ii=0; ii<wo; ii++)
-	{
-		for (int jj=0; jj<ho; jj++)
-		{
-			if ((pos.x*wo+ii<this->pos.w) && (pos.y*ho+jj<this->pos.h))
-				CSDL_Ext::SDL_PutPixelWithoutRefresh(FoW[pos.z],pos.x*wo+ii,pos.y*ho+jj,0,0,0);
-		}
-	}
-}
 
 CTerrainRect::CTerrainRect()
 	:curHoveredTile(-1,-1,-1), currentPath(NULL)
@@ -769,207 +361,9 @@ void CResDataBar::showAll(SDL_Surface * to)
 	draw(to);
 }
 
-
-CInfoBar::CInfoBar()
-{
-	pom = -1;
-	mode = NOTHING;
-	pos.x=ADVOPT.infoboxX;
-	pos.y=ADVOPT.infoboxY;
-	pos.w=194;
-	pos.h=186;
-	day = CDefHandler::giveDef("NEWDAY.DEF");
-	week1 = CDefHandler::giveDef("NEWWEEK1.DEF");
-	week2 = CDefHandler::giveDef("NEWWEEK2.DEF");
-	week3 = CDefHandler::giveDef("NEWWEEK3.DEF");
-	week4 = CDefHandler::giveDef("NEWWEEK4.DEF");
-	hourglass = CDefHandler::giveDef("HOURGLAS.DEF");
-	hourglassSand = CDefHandler::giveDef("HOURSAND.DEF");
-	selInfoWin = NULL;
-}
-CInfoBar::~CInfoBar()
-{
-	delete day;
-	delete week1;
-	delete week2;
-	delete week3;
-	delete week4;
-
-	if(selInfoWin)
-		SDL_FreeSurface(selInfoWin);
-}
-
-void CInfoBar::showAll(SDL_Surface * to)
-{
-	if (mode >= NEW_DAY  &&  mode <= NEW_WEEK4)
-	{
-		blitAnim(mode);
-	}
-	else if(mode == ENEMY_TURN)
-	{
-		CPicture bg("ADSTATOT.bmp");
-		bg.convertToScreenBPP();
-		CAnimImage ai("CREST58", enemyTurnInfo.color, 0, 20, 51);
-		ai.showAll(&*bg);
-
-		int hourglassFrame = enemyTurnInfo.progress * hourglass->ourImages.size();
-		static int sandFrame = 0;
-		vstd::amin(hourglassFrame, hourglass->ourImages.size()-1);
-		blitAt(hourglassSand->ourImages[sandFrame++ % hourglassSand->ourImages.size()].bitmap, 99, 51, bg);
-		blitAt(hourglass->ourImages[hourglassFrame].bitmap, 99, 51, bg);
-		blitAtLoc(bg, 8, 11, to);
-	}
-	else if(selInfoWin)
-	{
-		blitAt(selInfoWin, pos.x, pos.y, to);
-	}
-}
-
-CDefHandler * CInfoBar::getAnim(EMode mode)
-{
-	switch(mode)
-	{
-	case NEW_DAY:
-		return day;
-	case NEW_WEEK1:
-		return week1;
-	case NEW_WEEK2:
-		return week2;
-	case NEW_WEEK3:
-		return week3;
-	case NEW_WEEK4:
-		return week4;
-	default:
-		return NULL;
-	}
-}
-
-void CInfoBar::blitAnim(EMode mode)//0 - day, 1 - week
-{
-	CDefHandler * anim = NULL;
-	std::ostringstream txt;
-	anim = getAnim(mode);
-	if(mode > NEW_DAY) //new week animation
-	{
-		txt << CGI->generaltexth->allTexts[63] << " " << LOCPLINT->cb->getDate(2);
-	}
-	else if(mode == NEW_DAY) //new day
-	{
-		txt << CGI->generaltexth->allTexts[64] << " " << LOCPLINT->cb->getDate(1);
-	}
-	blitAt(anim->ourImages[pom].bitmap,pos.x+9,pos.y+10);
-	printAtMiddle(txt.str(),pos.x+95,pos.y+31,FONT_MEDIUM,Colors::Cornsilk);
-	if (pom == anim->ourImages.size()-1)
-		setTimer(750);
-}
-
-void CInfoBar::newDay(int Day)
-{
-	if(LOCPLINT->cb->getDate(1) != 1)
-	{
-		mode = NEW_DAY; //showing day
-	}
-	else
-	{
-		switch(LOCPLINT->cb->getDate(2))
-		{
-		case 1:
-			mode = NEW_WEEK1;
-			break;
-		case 2:
-			mode = NEW_WEEK2;
-			break;
-		case 3:
-			mode = NEW_WEEK3;
-			break;
-		case 4:
-			mode = NEW_WEEK4;
-			break;
-		default:
-			mode = NOTHING;
-			break;
-		}
-	}
-	pom = 0;
-	setTimer(500);
-	blitAnim(mode);
-}
-
-void CInfoBar::showComp(const CComponent * comp, int time/*=5000*/)
-{
-	if(comp->type != CComponent::hero)
-	{
-		curSel = NULL;
-	}
-
-	SDL_Surface * b = BitmapHandler::loadBitmap("ADSTATOT.bmp");
-	blitAt(b,pos.x+8,pos.y+11);
-
-	CComponent* tempComp = (CComponent*)comp; //evil. TODO: remove need to move component
-	tempComp->moveTo(Point(pos.x+52, pos.y+54));
-	tempComp->showAll(screen);
-	printAtMiddle(comp->subtitle,pos.x+91,pos.y+158,FONT_SMALL,Colors::Cornsilk);
-	printAtMiddleWB(comp->description,pos.x+94,pos.y+31,FONT_SMALL,26,Colors::Cornsilk);
-	SDL_FreeSurface(b);
-	setTimer(time);
-	mode = SHOW_COMPONENT;
-}
-
-void CInfoBar::tick()
-{
-	if(mode >= NEW_DAY  &&  mode <= NEW_WEEK4) //animation
-	{
-		pom++;
-		if (pom >= getAnim(mode)->ourImages.size())
-		{
-			removeUsedEvents(TIME);
-			mode = NOTHING;
-		}
-	}
-	else if(mode == SHOW_COMPONENT)
-	{
-		removeUsedEvents(TIME);
-		mode = NOTHING;
-	}
-
-	if(adventureInt == GH.topInt())
-		redraw();
-}
-
-void CInfoBar::show(SDL_Surface * to)
-{
-
-}
-
-void CInfoBar::deactivate()
-{
-	CIntObject::deactivate();
-
-	mode = NOTHING;
-}
-
-void CInfoBar::updateSelection(const CGObjectInstance *obj)
-{
-	if(obj->ID == GameConstants::HEROI_TYPE)
-		curSel = static_cast<const CGHeroInstance*>(obj);
-	else
-		curSel = NULL;
-	if(selInfoWin)
-		SDL_FreeSurface(selInfoWin);
-	selInfoWin = LOCPLINT->infoWin(obj);
-}
-
-void CInfoBar::enemyTurn(ui8 color, double progress)
-{
-	mode = ENEMY_TURN;
-	enemyTurnInfo.color = color;
-	enemyTurnInfo.progress = progress;
-	redraw();
-	setTimer(250);
-}
-
-CAdvMapInt::CAdvMapInt()
-:statusbar(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG),
+CAdvMapInt::CAdvMapInt():
+    minimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH)),
+statusbar(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG),
 kingOverview(CGI->generaltexth->zelp[293].first,CGI->generaltexth->zelp[293].second,
 			 boost::bind(&CAdvMapInt::fshowOverview,this),&ADVOPT.kingOverview, SDLK_k),
 
@@ -1000,8 +394,9 @@ nextHero(CGI->generaltexth->zelp[301].first,CGI->generaltexth->zelp[301].second,
 endTurn(CGI->generaltexth->zelp[302].first,CGI->generaltexth->zelp[302].second,
 		  boost::bind(&CAdvMapInt::fendTurn,this), &ADVOPT.endTurn, SDLK_e),
 
-heroList(ADVOPT.hlistSize),
-townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlistAD)//(5,&genRect(192,48,747,196),747,196,747,372),
+heroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD),
+townList(ADVOPT.tlistSize, Point(ADVOPT.tlistX, ADVOPT.tlistY), ADVOPT.tlistAU, ADVOPT.tlistAD),
+infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192) )
 {
 	duringAITurn = false;
 	state = NA;
@@ -1010,7 +405,7 @@ townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlis
 	pos.w = screen->w;
 	pos.h = screen->h;
 	selection = NULL;
-	townList.fun = boost::bind(&CAdvMapInt::selectionChanged,this);
+	townList.onSelect = boost::bind(&CAdvMapInt::selectionChanged,this);
 	adventureInt=this;
 	bg = BitmapHandler::loadBitmap(ADVOPT.mainGraphic);
 	scrollingDir = 0;
@@ -1020,12 +415,6 @@ townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlis
 	heroAnim=0;
 	heroAnimValHitCount=0; // hero animation frame
 
-	heroList.init();
-	heroList.genList();
-	//townList.init();
-	//townList.genList();
-
-
 	for (int g=0; g<ADVOPT.gemG.size(); ++g)
 	{
 		gems.push_back(CDefHandler::giveDef(ADVOPT.gemG[g]));
@@ -1066,7 +455,7 @@ void CAdvMapInt::fswitchLevel()
 		underground.showAll(screenBuf);
 	}
 	updateScreen = true;
-	minimap.draw(screenBuf);
+	minimap.setLevel(position.z);
 }
 void CAdvMapInt::fshowQuestlog()
 {
@@ -1122,10 +511,11 @@ void CAdvMapInt::fsystemOptions()
 
 void CAdvMapInt::fnextHero()
 {
-	int next = getNextHeroIndex(heroList.selected);
+	auto hero = dynamic_cast<const CGHeroInstance*>(selection);
+	int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero));
 	if (next < 0)
 		return;
-	heroList.select(next);
+	select(LOCPLINT->wanderingHeroes[next], true);
 }
 
 void CAdvMapInt::fendTurn()
@@ -1193,7 +583,7 @@ int CAdvMapInt::getNextHeroIndex(int startIndex)
 
 void CAdvMapInt::updateNextHero(const CGHeroInstance *h)
 {
-	int start = heroList.getPosOfHero(h);
+	int start = vstd::find_pos(LOCPLINT->wanderingHeroes, h);
 	int next = getNextHeroIndex(start);
 	if (next < 0)
 	{
@@ -1279,9 +669,9 @@ void CAdvMapInt::showAll(SDL_Surface * to)
 	nextHero.showAll(to);
 	endTurn.showAll(to);
 
-	minimap.draw(to);
-	heroList.draw(to);
-	townList.draw(to);
+	minimap.showAll(to);
+	heroList.showAll(to);
+	townList.showAll(to);
 	updateScreen = true;
 	show(to);
 
@@ -1350,7 +740,7 @@ void CAdvMapInt::show(SDL_Surface * to)
 		if(scrollingDir)
 		{
 			updateScreen = true;
-			updateMinimap=true;
+			minimap.redraw();
 		}
 	}
 	if(updateScreen)
@@ -1361,31 +751,30 @@ void CAdvMapInt::show(SDL_Surface * to)
 		updateScreen=false;
 		LOCPLINT->cingconsole->showAll(to);
 	}
-	if (updateMinimap)
-	{
-		minimap.draw(to);
-		updateMinimap=false;
-	}
+	infoBar.show(to);
+	statusbar.showAll(to);
 }
 
 void CAdvMapInt::selectionChanged()
 {
-	const CGTownInstance *to = LOCPLINT->towns[townList.selected];
+	const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];
 	select(to);
 }
 void CAdvMapInt::centerOn(int3 on)
 {
+	bool switchedLevels = on.z != position.z;
+
 	on.x -= CGI->mh->frameW;
 	on.y -= CGI->mh->frameH;
 
 	on = LOCPLINT->repairScreenPos(on);
 
-	adventureInt->position = on;
-	adventureInt->updateScreen=true;
-	updateMinimap=true;
+	position = on;
+	updateScreen=true;
 	underground.setIndex(on.z,true); //change underground switch button image
-	if(GH.topInt() == this)
-		underground.redraw();
+	underground.redraw();
+	if (switchedLevels)
+		minimap.setLevel(position.z);
 }
 
 void CAdvMapInt::centerOn(const CGObjectInstance *obj)
@@ -1599,30 +988,30 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/)
 	terrain.currentPath = NULL;
 	if(sel->ID==GameConstants::TOWNI_TYPE)
 	{
-		int pos = vstd::find_pos(LOCPLINT->towns,sel);
-		townList.selected = pos;
-		townList.fixPos();
+		auto town = dynamic_cast<const CGTownInstance*>(sel);
+
+		infoBar.showTownSelection(town, false);
+		townList.select(town);
+		heroList.select(nullptr);
+
 		updateSleepWake(NULL);
 		updateMoveHero(NULL);
 	}
 	else //hero selected
 	{
-		const CGHeroInstance *h = static_cast<const CGHeroInstance*>(sel);
-		if(LOCPLINT->getWHero(heroList.selected) != h)
-		{
-			heroList.selected = heroList.getPosOfHero(h);
-			heroList.fixPos();
-		}
+		auto hero = dynamic_cast<const CGHeroInstance*>(sel);
+
+		infoBar.showHeroSelection(hero, false);
+		heroList.select(hero);
+		townList.select(nullptr);
 
-		terrain.currentPath = LOCPLINT->getAndVerifyPath(h);
+		terrain.currentPath = LOCPLINT->getAndVerifyPath(hero);
 
-		updateSleepWake(h);
-		updateMoveHero(h);
+		updateSleepWake(hero);
+		updateMoveHero(hero);
 	}
-	townList.draw(screen);
-	heroList.draw(screen);
-	infoBar.updateSelection(sel);
-	infoBar.showAll(screen);
+	townList.redraw();
+	heroList.redraw();
 }
 
 void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
@@ -1702,6 +1091,7 @@ void CAdvMapInt::startTurn()
 	if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID)
 	{
 		adjustActiveness(false);
+		minimap.setAIRadar(false);
 	}
 }
 
@@ -2091,8 +1481,9 @@ void CAdvMapInt::aiTurnStarted()
 {
 	adjustActiveness(true);
 	CCS->musich->playMusicFromSet(CCS->musich->aiMusics);
-	adventureInt->minimap.redraw();
-	adventureInt->infoBar.enemyTurn(LOCPLINT->cb->getCurrentPlayer(), 0.5);
+	adventureInt->minimap.setAIRadar(true);
+	adventureInt->infoBar.startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
+	adventureInt->infoBar.showAll(screen);//force refresh on inactive object
 }
 
 void CAdvMapInt::adjustActiveness(bool aiTurnStart)
@@ -2106,34 +1497,28 @@ void CAdvMapInt::adjustActiveness(bool aiTurnStart)
 		activate();
 }
 
-CAdventureOptions::CAdventureOptions()
+CAdventureOptions::CAdventureOptions():
+    CWindowObject("ADVOPTS", PLAYER_COLORED)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	bg = new CPicture("ADVOPTS.bmp");
-	graphics->blueToPlayersAdv(bg->bg, LOCPLINT->playerID);
-	pos = bg->center();
-	exit = new CAdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
+
+	exit = new CAdventureMapButton("","",boost::bind(&CAdventureOptions::close, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
-	//scenInfo = new CAdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 24, "ADVINFO.DEF",SDLK_i);
-	scenInfo = new CAdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 198, "ADVINFO.DEF",SDLK_i);
+	scenInfo = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 198, "ADVINFO.DEF",SDLK_i);
 	scenInfo->callback += CAdventureOptions::showScenarioInfo;
 	//viewWorld = new CAdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
 
-	puzzle = new CAdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 81, "ADVPUZ.DEF");
+	puzzle = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 81, "ADVPUZ.DEF");
 	puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
 
-	dig = new CAdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 139, "ADVDIG.DEF");
+	dig = new CAdventureMapButton("","", boost::bind(&CAdventureOptions::close, this), 24, 139, "ADVDIG.DEF");
 	if(const CGHeroInstance *h = adventureInt->curHero())
 		dig->callback += boost::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h);
 	else
 		dig->block(true);
 }
 
-CAdventureOptions::~CAdventureOptions()
-{
-}
-
 void CAdventureOptions::showScenarioInfo()
 {
 	GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));

+ 5 - 94
client/CAdvmapInterface.h

@@ -5,6 +5,7 @@
 #include "SDL.h"
 #include "UIFramework/CIntObjectClasses.h"
 #include "GUIClasses.h"
+#include "AdventureMapClasses.h"
 
 class CDefHandler;
 class CCallback;
@@ -19,7 +20,7 @@ class IShipyard;
 /*****************************/
 
 /*
- * CAdcmapInterface.h, part of VCMI engine
+ * CAdvmapInterface.h, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
  *
@@ -29,65 +30,15 @@ class IShipyard;
  */
 
 /// Adventure options dialogue where you can view the world, dig, play the replay of the last turn,...
-class CAdventureOptions : public CIntObject
+class CAdventureOptions : public CWindowObject
 {
 public:
-	CPicture *bg;
 	CAdventureMapButton *exit, *viewWorld, *puzzle, *dig, *scenInfo, *replay;
 
 	CAdventureOptions();
-	~CAdventureOptions();
 	static void showScenarioInfo();
 };
 
-//Player-specific minimaps
-class CMinimapSurfacesRef
-{
-public:
-	CMinimapSurfacesRef();
-	// see private members descriptions
-	std::vector< SDL_Surface* > &map();
-	std::vector< SDL_Surface* > &FoW();
-	std::vector< SDL_Surface* > &flObjs();
-	void free();
-private:
-	void redraw(int level=-1);// (level==-1) => redraw all levels
-	void initMap(int level=-1);// (level==-1) => redraw all levels
-	void initFoW(int level=-1);// (level==-1) => redraw all levels
-	void initFlaggableObjs(int level=-1);// (level==-1) => redraw all levels
-	void showVisibleTiles(int level=-1);// (level==-1) => redraw all levels
-private:    
-	std::vector< SDL_Surface* > map_, FoW_, flObjs_; //one bitmap for each level (terrain, Fog of War, flaggable objects) (one for underworld, one for surface)
-	bool ready;
-};
-
-/// Minimap which is displayed at the right upper corner of adventure map
-class CMinimap : public CIntObject
-{
-public:
-	CPicture *aiShield; //the graphic displayed during AI turn
-	SDL_Surface * temps;
-	std::map<int,SDL_Color> colors;
-	std::map<int,SDL_Color> colorsBlocked;
-
-    std::map<int, CMinimapSurfacesRef> surfs;
-	std::string statusbarTxt, rcText;
-
-	CMinimap();
-	~CMinimap();
-	void draw(SDL_Surface * to);
-	void updateRadar();
-
-	void clickRight(tribool down, bool previousState);
-	void clickLeft(tribool down, bool previousState);
-	void hover (bool on);
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
-	void activate(); // makes button active
-	void deactivate(); // makes button inactive (but don't deletes)
-	void hideTile(const int3 &pos); //puts FoW
-	void showTile(const int3 &pos); //removes FoW
-};
-
 /// Holds information about which tiles of the terrain are shown/not shown at the screen
 class CTerrainRect
 	:  public CIntObject
@@ -112,8 +63,7 @@ public:
 
 /// Resources bar which shows information about how many gold, crystals,... you have
 /// Current date is displayed too
-class CResDataBar
-	: public CIntObject
+class CResDataBar : public CIntObject
 {
 public:
 	SDL_Surface * bg;
@@ -130,45 +80,6 @@ public:
 	void showAll(SDL_Surface * to);
 };
 
-/// Info box which shows next week/day information, hold the current date
-class CInfoBar : public CIntObject
-{
-	enum EMode {NOTHING = -1, NEW_DAY, NEW_WEEK1, NEW_WEEK2, NEW_WEEK3, NEW_WEEK4, ____, SHOW_COMPONENT, ENEMY_TURN};
-	CDefHandler *day, *week1, *week2, *week3, *week4, *hourglass, *hourglassSand;
-	CComponent * current;
-	int pom;
-	SDL_Surface *selInfoWin; //info box for selection
-	CDefHandler * getAnim(EMode mode);
-
-	struct EnemyTurn
-	{
-		ui8 color;
-		double progress; //0-1
-
-		EnemyTurn()
-		{
-			color = 255;
-			progress = 0.;
-		}
-	} enemyTurnInfo;
-public:
-	EMode mode;
-	const CGHeroInstance * curSel;
-
-	CInfoBar();
-	~CInfoBar();
-	void newDay(int Day); //start showing new day/week animation
-	void showComp(const CComponent * comp, int time=5000);
-	void enemyTurn(ui8 color, double progress);
-	void tick();
-	void showAll(SDL_Surface * to); // if specific==0 function draws info about selected hero/town
-	void blitAnim(EMode mode);//0 - day, 1 - week
-
-	void show(SDL_Surface * to);
-	void deactivate();
-	void updateSelection(const CGObjectInstance *obj);
-};
-
 /// That's a huge class which handles general adventure map actions and 
 /// shows the right menu(questlog, spellbook, end turn,..) from where you 
 /// can get to the towns and heroes. 
@@ -191,7 +102,7 @@ public:
 
 	enum{NA, INGAME, WAITING} state;
 
-	bool updateScreen, updateMinimap ;
+	bool updateScreen;
 	ui8 anim, animValHitCount; //animation frame
 	ui8 heroAnim, heroAnimValHitCount; //animation frame
 

+ 4 - 0
client/CAnimation.cpp

@@ -1279,6 +1279,7 @@ void CShowableAnim::reset()
 {
 	value = 0;
 	frame = first;
+
 	if (callback)
 		callback();
 }
@@ -1297,6 +1298,9 @@ void CShowableAnim::show(SDL_Surface * to)
 		blitImage(first, group, to);
 	blitImage(frame, group, to);
 
+	if ((flags & PLAY_ONCE) && frame + 1 == last)
+		return;
+
 	if ( ++value == frameDelay )
 	{
 		value = 0;

+ 1 - 0
client/CAnimation.h

@@ -260,6 +260,7 @@ public:
 		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;

+ 1 - 1
client/CBitmapHandler.cpp

@@ -187,7 +187,7 @@ SDL_Surface * BitmapHandler::loadBitmap(std::string fname, bool setKey)
 	if (!(bitmap = loadBitmapFromLod(bitmaph, fname, setKey)) &&
 		!(bitmap = loadBitmapFromLod(bitmaph_ab, fname, setKey)) &&
 		!(bitmap = loadBitmapFromLod(spriteh, fname, setKey)))
-		tlog1<<"Failed to find file "<<fname<<"\n";
+		tlog0<<"Error: Failed to find file "<<fname<<"\n";
 
 	return bitmap;
 }

+ 18 - 39
client/CCastleInterface.cpp

@@ -918,7 +918,7 @@ void CCastleBuildings::openTownHall()
 	GH.pushInt(new CHallInterface(town));
 }
 
-CCastleInterface::CCastleInterface(const CGTownInstance * Town, int listPos):
+CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInstance * from):
     CWindowObject("", PLAYER_COLORED | BORDERED),
 	hall(NULL),
 	fort(NULL),
@@ -953,13 +953,12 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, int listPos):
 	statusbar = new CGStatusBar(new CPicture(*panel, barRect, 9, 555, false));
 	resdatabar = new CResDataBar("ZRESBAR", 3, 575, 32, 2, 85, 85);
 
-	townlist = new CTownList(3, 744, 414, "IAM014", "IAM015");
-	townlist->fun = boost::bind(&CCastleInterface::townChange, this);
-	townlist->selected = vstd::find_pos(LOCPLINT->towns, Town);
+	townlist = new CTownList(3, Point(744, 414), "IAM014", "IAM015");
+	if (from)
+		townlist->select(from);
 
-	townlist->from = townlist->selected - listPos;
-	vstd::amax(townlist->from, 0);
-	vstd::amin(townlist->from, LOCPLINT->towns.size() - townlist->SIZE);
+	townlist->select(town); //this will scroll list to select current town
+	townlist->onSelect = boost::bind(&CCastleInterface::townChange, this);
 
 	recreateIcons();
 	CCS->musich->playMusic(CCS->musich->townMusics[town->subID], -1);
@@ -993,12 +992,12 @@ void CCastleInterface::castleTeleport(int where)
 
 void CCastleInterface::townChange()
 {
-	const CGTownInstance * nt = LOCPLINT->towns[townlist->selected];
-	int tpos = townlist->selected - townlist->from;
-	if ( nt == town )
+	const CGTownInstance * dest = LOCPLINT->towns[townlist->getSelectedIndex()];
+	const CGTownInstance * town = this->town;// "this" is going to be deleted
+	if ( dest == town )
 		return;
 	GH.popIntTotally(this);
-	GH.pushInt(new CCastleInterface(nt, tpos));
+	GH.pushInt(new CCastleInterface(dest, town));
 }
 
 void CCastleInterface::addBuilding(int bid)
@@ -1233,20 +1232,10 @@ void CCastleInterface::keyPressed( const SDL_KeyboardEvent & key )
 	switch(key.keysym.sym)
 	{
 	case SDLK_UP:
-		if(townlist->selected)
-		{
-			townlist->selected--;
-			townlist->from--;
-			townChange();
-		}
+		townlist->selectPrev();
 		break;
 	case SDLK_DOWN:
-		if(townlist->selected < LOCPLINT->towns.size() - 1)
-		{
-			townlist->selected++;
-			townlist->from++;
-			townChange();
-		}
+		townlist->selectNext();
 		break;
 	case SDLK_SPACE:
 		if(!!town->visitingHero && town->garrisonHero)
@@ -1397,12 +1386,6 @@ void CBuildWindow::buyFunc()
 	GH.popInts(2); //we - build window and hall screen
 }
 
-void CBuildWindow::clickRight(tribool down, bool previousState)
-{
-	if((!down || indeterminate(down)))
-		close();
-}
-
 std::string CBuildWindow::getTextForState(int state)
 {
 	std::string ret;
@@ -1432,9 +1415,11 @@ std::string CBuildWindow::getTextForState(int state)
 	return ret;
 }
 
-CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Building, int State, bool Mode):
-	CWindowObject("TPUBUILD", PLAYER_COLORED),
-	town(Town), building(Building), state(State), mode(Mode)
+CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Building, int State, bool rightClick):
+    CWindowObject("TPUBUILD", PLAYER_COLORED | (rightClick ? RCLICK_POPUP : 0)),
+	town(Town),
+    building(Building),
+    state(State)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
@@ -1486,11 +1471,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 		posY +=75;
 	}
 
-	if(mode)
-	{	//popup
-		addUsedEvents(RCLICK);
-	}
-	else
+	if(!rightClick)
 	{	//normal window
 		buy = new CAdventureMapButton(boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->Name()),
 		          "", boost::bind(&CBuildWindow::buyFunc,this), 45, 446,"IBUY30", SDLK_RETURN);
@@ -1505,8 +1486,6 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 	}
 }
 
-CBuildWindow::~CBuildWindow()
-{}
 
 std::string CFortScreen::getBgName(const CGTownInstance *town)
 {

+ 2 - 5
client/CCastleInterface.h

@@ -217,7 +217,7 @@ public:
 	HeroSlots *heroes;
 	CCastleBuildings *builds;
 
-	CCastleInterface(const CGTownInstance * Town, int listPos = 1); //c-tor
+	CCastleInterface(const CGTownInstance * Town, const CGTownInstance * from = nullptr); //c-tor
 	~CCastleInterface();
 
 	void castleTeleport(int where);
@@ -269,7 +269,6 @@ class CBuildWindow: public CWindowObject
 	const CGTownInstance *town;
 	const CBuilding *building;
 	int state; //state - same as CHallInterface::CBuildingBox::state
-	bool mode; // 0 - normal (with buttons), 1 - r-click popup
 
 	CAnimImage *buildingPic;
 	CAdventureMapButton *buy;
@@ -287,9 +286,7 @@ class CBuildWindow: public CWindowObject
 	void buyFunc();
 
 public:
-	void clickRight(tribool down, bool previousState);
-	CBuildWindow(const CGTownInstance *Town, const CBuilding * building, int State, bool Mode); //c-tor
-	~CBuildWindow(); //d-tor
+	CBuildWindow(const CGTownInstance *Town, const CBuilding * building, int State, bool rightClick); //c-tor
 };
 
 //Small class to display 

+ 3 - 3
client/CKingdomInterface.h

@@ -248,7 +248,7 @@ public:
 };
 
 /// List item with town
-class CTownItem : public CGarrisonHolder
+class CTownItem : public CIntObject, public CGarrisonHolder
 {
 	CAnimImage *background;
 	CAnimImage *picture;
@@ -272,7 +272,7 @@ public:
 };
 
 /// List item with hero
-class CHeroItem : public CWindowWithGarrison
+class CHeroItem : public CIntObject, public CWindowWithGarrison
 {
 	const CGHeroInstance * hero;
 
@@ -322,7 +322,7 @@ public:
 };
 
 /// Tab with all town-specific data
-class CKingdTownList : public CGarrisonHolder
+class CKingdTownList : public CIntObject, public CGarrisonHolder
 {
 private:
 	std::vector<CTownItem*> townItems;

+ 6 - 2
client/CMusicHandler.cpp

@@ -500,13 +500,17 @@ MusicEntry::~MusicEntry()
 
 void MusicEntry::load(musicBase::musicID ID)
 {
+	if (music)
+	{
+		tlog5<<"Del-ing music file "<<filename<<"\n";
+		Mix_FreeMusic(music);
+	}
+
 	currentID = ID;
 	filename = GameConstants::DATA_DIR + "/Mp3/";
 	filename += owner->musics[ID];
 
 	tlog5<<"Loading music file "<<filename<<"\n";
-	if (music)
-		Mix_FreeMusic(music);
 
 	music = Mix_LoadMUS(filename.c_str());
 

+ 43 - 93
client/CPlayerInterface.cpp

@@ -136,18 +136,13 @@ void CPlayerInterface::init(CCallback * CB)
 {
 	cb = dynamic_cast<CCallback*>(CB);
 	if(observerInDuelMode)
-	{
-
 		return;
-	}
+
+	if(!towns.size() && !wanderingHeroes.size())
+		initializeHeroTownList();
 
 	if(!adventureInt)
 		adventureInt = new CAdvMapInt();
-
-	if(!towns.size() && !wanderingHeroes.size())
-	{
-		recreateHeroTownList();
-	}
 }
 void CPlayerInterface::yourTurn()
 {
@@ -250,8 +245,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	}
 
 	adventureInt->centerOn(ho); //actualizing screen pos
-	adventureInt->minimap.draw(screen2);
-	adventureInt->heroList.draw(screen2);
+	adventureInt->minimap.redraw();
+	adventureInt->heroList.redraw();
 
 	bool directlyAttackingCreature =
 		CGI->mh->map->isInTheMap(details.attackedFrom)
@@ -329,8 +324,8 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	ho->isStanding = true;
 
 	//move finished
-	adventureInt->minimap.draw(screen2);
-	adventureInt->heroList.updateMove(ho);
+	adventureInt->minimap.redraw();
+	adventureInt->heroList.update(ho);
 
 	//check if user cancelled movement
 	{
@@ -376,13 +371,13 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 	if(vstd::contains(paths, hero))
 		paths.erase(hero);
 
-	adventureInt->heroList.updateHList(hero);
+	adventureInt->heroList.update(hero);
 }
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	wanderingHeroes.push_back(hero);
-	adventureInt->heroList.updateHList();
+	adventureInt->heroList.update(hero);
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 {
@@ -392,36 +387,6 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 	GH.pushInt(castleInt);
 }
 
-SDL_Surface * CPlayerInterface::infoWin(const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
-{
-	if(!specific)
-		specific = adventureInt->selection;
-
-	assert(specific);
-
-	switch(specific->ID)
-	{
-	case GameConstants::HEROI_TYPE:
-		{
-			InfoAboutHero iah;
-			bool gotInfo = LOCPLINT->cb->getHeroInfo(specific, iah);
-			assert(gotInfo);
-			return graphics->drawHeroInfoWin(iah);
-		}
-	case GameConstants::TOWNI_TYPE:
-	case 33: // Garrison
-	case 219:
-		{
-			InfoAboutTown iah;
-			bool gotInfo = LOCPLINT->cb->getTownInfo(specific, iah);
-			assert(gotInfo);
-			return graphics->drawTownInfoWin(iah);
-		}
-	default:
-		return NULL;
-	}
-}
-
 int3 CPlayerInterface::repairScreenPos(int3 pos)
 {
 	if(pos.x<-CGI->mh->frameW)
@@ -516,6 +481,7 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 		CGI->mh->printObject(town->visitingHero);
 		wanderingHeroes.push_back(town->visitingHero);
 	}
+	adventureInt->heroList.update();
 	adventureInt->updateNextHero(NULL);
 
 	if(CCastleInterface *c = castleInt)
@@ -546,20 +512,18 @@ void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownIn
 	waitWhileDialog();
 	openTownWindow(town);
 }
-void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj, bool updateInfobox /*= true*/ )
+void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	if(updateInfobox)
-		updateInfo(obj);
+	updateInfo(obj);
 
 	for(std::list<IShowActivatable*>::iterator i = GH.listInt.begin(); i != GH.listInt.end(); i++)
 	{
-		if((*i)->type & IShowActivatable::WITH_GARRISON)
-		{
-			CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(*i);
+		CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(*i);
+		if (cgh)
 			cgh->updateGarrisons();
-		}
-		else if(CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(*i))
+
+		if(CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(*i))
 		{
 			if(obj == cmw->hero)
 				cmw->garrisonChanged();
@@ -593,6 +557,7 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 		castleInt->removeBuilding(buildingID);
 		break;
 	}
+	adventureInt->townList.update(town);
 }
 
 void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
@@ -941,13 +906,13 @@ void CPlayerInterface::yourTacticPhase(int distance)
 		boost::this_thread::sleep(boost::posix_time::millisec(1));
 }
 
-void CPlayerInterface::showComp(const CComponent &comp)
+void CPlayerInterface::showComp(const Component &comp, std::string message)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	waitWhileDialog(); //Fix for mantis #98
 
 	CCS->soundh->playSoundFromSet(CCS->soundh->pickupSounds);
-	adventureInt->infoBar.showComp(&comp,4000);
+	adventureInt->infoBar.showComponent(comp, message);
 }
 
 void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector<Component*> &components, int soundID)
@@ -1377,6 +1342,7 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 				towns.push_back(static_cast<const CGTownInstance *>(obj));
 			else
 				towns -= obj;
+			adventureInt->townList.update();
 		}
 
 		assert(cb->getTownsInfo().size() == towns.size());
@@ -1384,10 +1350,11 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 
 }
 
-void CPlayerInterface::recreateHeroTownList()
+void CPlayerInterface::initializeHeroTownList()
 {
-	std::vector <const CGHeroInstance *> newWanderingHeroes;
 	std::vector<const CGHeroInstance*> allHeroes = cb->getHeroesInfo();
+	/*
+	std::vector <const CGHeroInstance *> newWanderingHeroes;
 
 	//applying current heroes order to new heroes info
 	int j;
@@ -1401,13 +1368,15 @@ void CPlayerInterface::recreateHeroTownList()
 	//all the rest of new heroes go the end of the list
 	wanderingHeroes.clear();
 	wanderingHeroes = newWanderingHeroes;
-	newWanderingHeroes.clear();
+	newWanderingHeroes.clear();*/
+
 	for (int i = 0; i < allHeroes.size(); i++)
 		if (!allHeroes[i]->inTownGarrison)
 			wanderingHeroes += allHeroes[i];
 
-	std::vector<const CGTownInstance*> newTowns;
 	std::vector<const CGTownInstance*> allTowns = cb->getTownsInfo();
+	/*
+	std::vector<const CGTownInstance*> newTowns;
 	for (int i = 0; i < towns.size(); i++)
 		if ((j = vstd::find_pos(allTowns, towns[i])) >= 0)
 		{
@@ -1417,18 +1386,12 @@ void CPlayerInterface::recreateHeroTownList()
 
 	towns.clear();
 	towns = newTowns;
-	newTowns.clear();
+	newTowns.clear();*/
 	for(int i = 0; i < allTowns.size(); i++)
 		towns.push_back(allTowns[i]);
 
-	adventureInt->updateNextHero(NULL);
-}
-
-const CGHeroInstance * CPlayerInterface::getWHero( int pos )
-{
-	if(pos < 0 || pos >= wanderingHeroes.size())
-		return NULL;
-	return wanderingHeroes[pos];
+	if (adventureInt)
+		adventureInt->updateNextHero(NULL);
 }
 
 void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level)
@@ -2137,24 +2100,6 @@ void CPlayerInterface::acceptTurn()
 	if(howManyPeople > 1)
 		adventureInt->startTurn();
 
-	//boost::unique_lock<boost::recursive_mutex> un(*pim);
-
-	//Select sound for day start
-	int totalDays = cb->getDate();
-	int day = cb->getDate(1);
-	int week = cb->getDate(2);
-
-	if (totalDays == 1)
-		CCS->soundh->playSound(soundBase::newDay);
-	else if (day != 1)
-		CCS->soundh->playSound(soundBase::newDay);
-	else if (week != 1)
-		CCS->soundh->playSound(soundBase::newWeek);
-	else
-		CCS->soundh->playSound(soundBase::newMonth);
-
-	adventureInt->infoBar.newDay(day);
-
 	//select first hero if available.
 	//TODO: check if hero is slept
 	if(wanderingHeroes.size())
@@ -2162,6 +2107,11 @@ void CPlayerInterface::acceptTurn()
 	else
 		adventureInt->select(towns.front());
 
+	//show new day animation and sound on infobar
+	adventureInt->infoBar.showDate();
+
+	adventureInt->heroList.update();
+	adventureInt->townList.update();
 	adventureInt->updateNextHero(NULL);
 	adventureInt->showAll(screen);
 
@@ -2209,9 +2159,10 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance *h)
 
 void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
 {
-	adventureInt->infoBar.updateSelection(specific);
-// 	if (adventureInt->selection == specific)
-// 		adventureInt->infoBar.showAll(screen);
+	if (specific->ID == GameConstants::TOWNI_TYPE)
+		adventureInt->infoBar.showTownSelection(dynamic_cast<const CGTownInstance *>(specific), true);
+	else
+		adventureInt->infoBar.showHeroSelection(dynamic_cast<const CGHeroInstance *>(specific), true);
 }
 
 void CPlayerInterface::battleNewRoundFirst( int round )
@@ -2329,13 +2280,12 @@ void CPlayerInterface::stacksErased(const StackLocation &location)
 	garrisonChanged(location.army);
 }
 
-#define UPDATE_IF(LOC) static_cast<const CArmedInstance*>(adventureInt->infoBar.curSel) == LOC.army.get()
 void CPlayerInterface::stacksSwapped(const StackLocation &loc1, const StackLocation &loc2)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	garrisonChanged(loc1.army, UPDATE_IF(loc1));
+	garrisonChanged(loc1.army);
 	if(loc2.army != loc1.army)
-		garrisonChanged(loc2.army, UPDATE_IF(loc2));
+		garrisonChanged(loc2.army);
 }
 
 void CPlayerInterface::newStackInserted(const StackLocation &location, const CStackInstance &stack)
@@ -2348,9 +2298,9 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 
-	garrisonChanged(src.army, UPDATE_IF(src));
+	garrisonChanged(src.army);
 	if(dst.army != src.army)
-		garrisonChanged(dst.army, UPDATE_IF(dst));
+		garrisonChanged(dst.army);
 }
 #undef UPDATE_IF
 

+ 4 - 7
client/CPlayerInterface.h

@@ -103,9 +103,8 @@ public:
 
 	std::list<CInfoWindow *> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
 
-
 	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
-	std::vector<const CGTownInstance *> towns; //our heroes on the adventure map (not the garrisoned ones)
+	std::vector<const CGTownInstance *> towns; //our towns on the adventure map
 	std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
 	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
 
@@ -122,8 +121,7 @@ public:
 	} spellbookSettings;
 
 	void update();
-	void recreateHeroTownList();
-	const CGHeroInstance *getWHero(int pos); //returns NULL if position is not valid
+	void initializeHeroTownList();
 	int getLastIndex(std::string namePrefix);
 
 	//overridden funcs from CGameInterface
@@ -179,7 +177,7 @@ public:
 	void objectRemoved(const CGObjectInstance *obj) OVERRIDE;
 	void gameOver(ui8 player, bool victory) OVERRIDE;
 	void playerStartsTurn(ui8 player) OVERRIDE; //called before yourTurn on active itnerface
-	void showComp(const CComponent &comp) OVERRIDE; //display component in the advmapint infobox
+	void showComp(const Component &comp, std::string message) OVERRIDE; //display component in the advmapint infobox
 	void serialize(COSer<CSaveFile> &h, const int version) OVERRIDE; //saving
 	void serialize(CISer<CLoadFile> &h, const int version) OVERRIDE; //loading
 
@@ -207,7 +205,7 @@ public:
 
 	//-------------//
 	void showArtifactAssemblyDialog(ui32 artifactID, ui32 assembleTo, bool assemble, CFunctionList<void()> onYes, CFunctionList<void()> onNo);
-	void garrisonChanged(const CGObjectInstance * obj, bool updateInfobox = true);
+	void garrisonChanged(const CGObjectInstance * obj);
 	void heroKilled(const CGHeroInstance* hero);
 	void waitWhileDialog(bool unlockPim = true);
 	void waitForAllDialogs(bool unlockPim = true);
@@ -217,7 +215,6 @@ public:
 	void redrawHeroWin(const CGHeroInstance * hero);
 	void openTownWindow(const CGTownInstance * town); //shows townscreen
 	void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
-	SDL_Surface * infoWin(const CGObjectInstance * specific); //specific=0 => draws info about selected town/hero
 	void updateInfo(const CGObjectInstance * specific);
 	void init(CCallback * CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on

+ 8 - 5
client/CPreGame.cpp

@@ -1159,7 +1159,10 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 				positions = 16;
 			}
 			if(tabType == CMenuScreen::saveGame)
+			{
 				txt = new CTextInput(Rect(32, 539, 350, 20), Point(-32, -25), "GSSTRIP.bmp", 0);
+				txt->filters.add(CTextInput::filenameFilter);
+			}
 			break;
 		case CMenuScreen::campaignList:
 			getFiles(toParse, GameConstants::DATA_DIR + "/Maps", "h3c"); //get all campaigns
@@ -1245,7 +1248,7 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 		if(selectedName.size())
 		{
 			if(selectedName[2] == 'M') //name starts with ./Maps instead of ./Games => there was nothing to select
-				txt->setText("NEWGAME");
+				txt->setTxt("NEWGAME");
 			else
 				selectFName(selectedName);
 		}
@@ -1302,7 +1305,7 @@ void SelectionTab::select( int position )
 		slider->moveTo(slider->value + position - positions + 1);
 
 	if(txt)
-		txt->setText(fs::basename(curItems[py]->filename));
+		txt->setTxt(fs::basename(curItems[py]->filename));
 
 	onSelect(curItems[py]);
 }
@@ -1572,7 +1575,7 @@ void CChatBox::keyPressed(const SDL_KeyboardEvent & key)
 	if(key.keysym.sym == SDLK_RETURN  &&  key.state == SDL_PRESSED  &&  inputBox->text.size())
 	{
 		SEL->postChatMessage(inputBox->text);
-		inputBox->setText("");
+		inputBox->setTxt("");
 	}
 	else
 		inputBox->keyPressed(key);
@@ -2690,7 +2693,7 @@ CMultiMode::CMultiMode()
 
 	bar = new CGStatusBar(new CPicture(Rect(7, 465, 440, 18), 0));//226, 472
 	txt = new CTextInput(Rect(19, 436, 334, 16), *bg);
-	txt->setText(settings["general"]["playerName"].String()); //Player
+	txt->setTxt(settings["general"]["playerName"].String()); //Player
 
 	btns[0] = new CAdventureMapButton(CGI->generaltexth->zelp[266], bind(&CMultiMode::openHotseat, this), 373, 78, "MUBHOT.DEF");
 	btns[1] = new CAdventureMapButton("Host TCP/IP game", "", bind(&CMultiMode::hostTCP, this), 373, 78 + 57*1, "MUBHOST.DEF");
@@ -2740,7 +2743,7 @@ CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
 	cancel = new CAdventureMapButton(CGI->generaltexth->zelp[561], bind(&CGuiHandler::popIntTotally, ref(GH), this), 205, 338, "MUBCANC.DEF", SDLK_ESCAPE);
 	bar = new CGStatusBar(new CPicture(Rect(7, 381, 348, 18), 0));//226, 472
 
-	txt[0]->setText(firstPlayer, true);
+	txt[0]->setTxt(firstPlayer, true);
 	txt[0]->giveFocus();
 }
 

+ 11 - 0
client/FunctionList.h

@@ -72,6 +72,17 @@ public:
 				funcs2[i](a);
 		}
 	}
+	// Me wants variadic templates :(
+	template <typename Arg1, typename Arg2>
+	void operator()(Arg1 & a, Arg2 & b) const
+	{
+		std::vector<boost::function<Signature> > funcs2 = funcs; //backup
+		for(int i=0;i<funcs2.size(); i++)
+		{
+			if (funcs2[i])
+				funcs2[i](a, b);
+		}
+	}
 };
 
 template<typename Signature>

+ 259 - 689
client/GUIClasses.cpp

@@ -66,6 +66,138 @@ 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::Cornsilk, 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));
+
+	BOOST_FOREACH(auto & slot, army.army)
+	{
+		new CAnimImage("CPRSMALL", slot.second.type->idNumber + 2, 0, slotsPos[slot.first].x, slotsPos[slot.first].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 = VLC->generaltexth->arraytxt[171 + 3*(slot.second.count)];
+		}
+
+		new CLabel(slotsPos[slot.first].x + 17, slotsPos[slot.first].y + 41, FONT_TINY, CENTER, Colors::Cornsilk, 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::Cornsilk,
+			           boost::lexical_cast<std::string>(hero.details->primskills[i]));
+
+		new CLabel(158, 98, FONT_TINY, CENTER, Colors::Cornsilk,
+		           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 imageIndex = town.tType->typeID * 2;
+	if (town.fortLevel == 0)
+		imageIndex += GameConstants::F_NUMBER * 2;
+	if (town.built >= GameConstants::MAX_BUILDING_PER_TURN)
+		imageIndex++;
+
+	new CAnimImage("itpt", imageIndex, 0, 3, 2);
+
+	if(town.details)
+	{
+		if (town.details->hallLevel)
+			new CAnimImage("ITMTLS", town.details->hallLevel, 0, 67, 31);
+
+		if (town.details->goldIncome)
+			new CLabel(157, 58, FONT_TINY, CENTER, Colors::Cornsilk,
+		               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 == 127 )// 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::hover (bool on)
 {
@@ -213,29 +345,25 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 						owner->pb = upg;//store the second stack owner (up or down army)
 						owner->splitting = false;
 
-						int totalAmount = owner->highlighted->count;
-						if(creature)
-							totalAmount += count;
+						int minLeft=0, minRight=0;
 
-						int last = -1;
 						if(upg != owner->highlighted->upg) //not splitting within same army
 						{
 							if(owner->highlighted->getObj()->stacksCount() == 1 //we're splitting away the last stack
 								&& owner->highlighted->getObj()->needsLastStack() )
 							{
-								last = 0;
+								minLeft = 1;
 							}
 							if(getObj()->stacksCount() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
 								&& owner->highlighted->creature == creature
 								&& getObj()->needsLastStack() )
 							{
-								last += 2;
+								minRight = 1;
 							}
 						}
 
-
-						CSplitWindow * spw = new CSplitWindow(owner->highlighted->creature->idNumber, totalAmount, owner, last, count);
-						GH.pushInt(spw);
+						GH.pushInt(new CSplitWindow(owner->highlighted->creature, boost::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
+						                            minLeft, minRight, count, owner->highlighted->count));
 						refr = true;
 					}
 					else if(creature != owner->highlighted->creature) //swap
@@ -425,9 +553,9 @@ void CGarrisonInt::splitClick()
 	splitting = !splitting;
 	redraw();
 }
-void CGarrisonInt::splitStacks(int am2)
+void CGarrisonInt::splitStacks(int, int amountRight)
 {
-	LOCPLINT->cb->splitStack(armedObjs[highlighted->upg], armedObjs[pb], highlighted->ID, p2, am2);
+	LOCPLINT->cb->splitStack(armedObjs[highlighted->upg], armedObjs[pb], highlighted->ID, p2, amountRight);
 }
 
 CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point &garsOffset,
@@ -683,21 +811,6 @@ void CComponent::init(Etype Type, int Subtype, int Val)
 	pos.h = image->pos.h;
 }
 
-//NOTE: whole method can be removed after 0.89+1 release
-void CComponent::show(SDL_Surface * to)
-{
-	//In some places components position set manually instead of moveBy - it should be fixed
-	if (pos.x != image->pos.x
-	 || pos.y != image->pos.y)
-	{
-		tlog0 << "Error: Component position may be broken. Please report\n";
-		image->moveTo(pos.topLeft());
-		CIntObject::showAll(to);
-	}
-	else
-		CIntObject::show(to);
-}
-
 std::string CComponent::getFileName()
 {
 	switch(type)
@@ -916,553 +1029,7 @@ void CSelWindow::madeChoice()
 	LOCPLINT->cb->selectionMade(ret+1,ID);
 }
 
-CHeroList::CHeroList(int Size)
-:CList(Size)
-{
-	arrup = CDefHandler::giveDef(conf.go()->ac.hlistAU);
-	arrdo = CDefHandler::giveDef(conf.go()->ac.hlistAD);
-	mobile = CDefHandler::giveDef(conf.go()->ac.hlistMB);
-	mana = CDefHandler::giveDef(conf.go()->ac.hlistMN);
-	empty = BitmapHandler::loadBitmap("HPSXXX.bmp");
-	selection = BitmapHandler::loadBitmap("HPSYYY.bmp");
-	SDL_SetColorKey(selection,SDL_SRCCOLORKEY,SDL_MapRGB(selection->format,0,255,255));
-
-	pos = genRect(32*SIZE+arrup->height+arrdo->height, std::max(arrup->width,arrdo->width), conf.go()->ac.hlistX, conf.go()->ac.hlistY);
-
-	arrupp = genRect(arrup->height, arrup->width, pos.x, pos.y);
-	arrdop = genRect(arrdo->height, arrdo->width, pos.x, pos.y+32*SIZE+arrup->height);
- //32px per hero
-	posmobx = pos.x+1;
-	posmoby = pos.y+arrup->height+1;
-	posporx = pos.x+mobile->width+2;
-	pospory = pos.y+arrup->height;
-	posmanx = pos.x+1+50+mobile->width;
-	posmany = pos.y+arrup->height+1;
-
-	from = 0;
-	selected = -1;
-	pressed = indeterminate;
-}
-
-void CHeroList::init()
-{
-	int w = pos.w+1, h = pos.h+4;
-	bg = CSDL_Ext::newSurface(w,h,screen);
-	Rect srcRect = genRect(w, h, pos.x, pos.y);
-	Rect dstRect = genRect(w, h, 0, 0);
-	CSDL_Ext::blitSurface(adventureInt->bg, &srcRect, bg, &dstRect);
-}
-
-void CHeroList::genList()
-{
-	//int howMany = LOCPLINT->cb->howManyHeroes();
-	//for (int i=0;i<howMany;i++)
-	//{
-	//	const CGHeroInstance * h = LOCPLINT->cb->getHeroInfo(i,0);
-	//	if(!h->inTownGarrison)
-	//		items.push_back(std::pair<const CGHeroInstance *,CPath *>(h,NULL));
-	//}
-}
-
-void CHeroList::select(int which)
-{
-	if (which<0)
-	{
-		selected = which;
-		adventureInt->selection = NULL;
-		adventureInt->terrain.currentPath = NULL;
-		draw(screen);
-		adventureInt->infoBar.showAll(screen);
-	}
-	if (which>=LOCPLINT->wanderingHeroes.size())
-		return;
-
-	selected = which;
-	adventureInt->select(LOCPLINT->wanderingHeroes[which]);
-	fixPos();
-	draw(screen);
-}
-
-void CHeroList::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-	{
-		/***************************ARROWS*****************************************/
-		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y))
-		{
-			if(from>0)
-			{
-				blitAt(arrup->ourImages[1].bitmap,arrupp.x,arrupp.y);
-				pressed = true;
-			}
-			return;
-		}
-		else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y))
-		{
-			if(LOCPLINT->wanderingHeroes.size()-from>SIZE)
-			{
-				blitAt(arrdo->ourImages[1].bitmap,arrdop.x,arrdop.y);
-				pressed = false;
-			}
-			return;
-		}
-		/***************************HEROES*****************************************/
-		int hx = GH.current->motion.x, hy = GH.current->motion.y;
-		hx-=pos.x;
-		hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-		int ny = hy/32;
-		if (ny>=SIZE || ny<0)
-			return;
-		if ( (ny+from)==selected && (adventureInt->selection->ID == GameConstants::HEROI_TYPE))
-			LOCPLINT->openHeroWindow(LOCPLINT->wanderingHeroes[selected]);//print hero screen
-		select(ny+from);
-	}
-	else
-	{
-		if (indeterminate(pressed))
-			return;
-		if (pressed) //up
-		{
-			blitAt(arrup->ourImages[0].bitmap,arrupp.x,arrupp.y);
-			pressed = indeterminate;
-			if (!down)
-			{
-				from--;
-				if (from<0)
-					from=0;
-				draw(screen);
-			}
-		}
-		else if (!pressed) //down
-		{
-			blitAt(arrdo->ourImages[0].bitmap,arrdop.x,arrdop.y);
-			pressed = indeterminate;
-			if (!down)
-			{
-				from++;
-				//if (from<items.size()-5)
-				//	from=items.size()-5;
-
-				draw(screen);
-			}
-		}
-		else
-			throw 0;
-
-	}
-}
-
-void CHeroList::mouseMoved (const SDL_MouseMotionEvent & sEvent)
-{
-	if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y))
-	{
-		if (from>0)
-			adventureInt->statusbar.print(CGI->generaltexth->zelp[303].first);
-		else
-			adventureInt->statusbar.clear();
-		return;
-	}
-	else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y))
-	{
-		if ((LOCPLINT->wanderingHeroes.size()-from)  >  SIZE)
-			adventureInt->statusbar.print(CGI->generaltexth->zelp[304].first);
-		else
-			adventureInt->statusbar.clear();
-		return;
-	}
-	//if not buttons then heroes
-	int hx = GH.current->motion.x, hy = GH.current->motion.y;
-	hx-=pos.x;
-	hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-	int ny = hy/32;
-	if ((ny>SIZE || ny<0) || (from+ny>=LOCPLINT->wanderingHeroes.size()))
-	{
-		adventureInt->statusbar.clear();
-		return;
-	}
-	std::vector<std::string> temp;
-	temp.push_back(LOCPLINT->wanderingHeroes[from+ny]->name);
-	temp.push_back(LOCPLINT->wanderingHeroes[from+ny]->type->heroClass->name);
-	adventureInt->statusbar.print( processStr(CGI->generaltexth->allTexts[15],temp) );
-	//select(ny+from);
-}
-
-void CHeroList::clickRight(tribool down, bool previousState)
-{
-	if (down)
-	{
-		/***************************ARROWS*****************************************/
-		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y) && from>0)
-		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[303].second,down);
-		}
-		else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y) && (LOCPLINT->wanderingHeroes.size()-from>5))
-		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[304].second,down);
-		}
-		else
-		{
-			//if not buttons then heroes
-			int hx = GH.current->motion.x, hy = GH.current->motion.y;
-			hx-=pos.x;
-			hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-			int ny = hy/32;
-			if ((ny>SIZE || ny<0) || (from+ny>=LOCPLINT->wanderingHeroes.size()))
-			{
-				return;
-			}			//show popup
-
-			CRClickPopup::createAndPush(LOCPLINT->wanderingHeroes[from+ny], GH.current->motion);
-		}
-	}
-}
-
-void CHeroList::hover (bool on)
-{
-}
-
-void CHeroList::keyPressed (const SDL_KeyboardEvent & key)
-{
-}
-
-void CHeroList::updateHList(const CGHeroInstance *toRemove)
-{
-	if(toRemove) //remove specific hero
-		LOCPLINT->wanderingHeroes -= toRemove;
-	else
-		LOCPLINT->recreateHeroTownList();
-
-
-	if(selected >= LOCPLINT->wanderingHeroes.size())
-		select(LOCPLINT->wanderingHeroes.size()-1);
-
-	if(toRemove)
-	{
-		if(LOCPLINT->wanderingHeroes.size() == 0)
-			adventureInt->townList.select(0);
-		else
-			select(selected);
-	}
-
-	adventureInt->updateNextHero(NULL);
-}
-
-void CHeroList::updateMove(const CGHeroInstance* which) //draws move points bar
-{
-	int ser = -1;
-	for(int i=0; i<LOCPLINT->wanderingHeroes.size() && ser<0; i++)
-		if(LOCPLINT->wanderingHeroes[i]->subID == which->subID)
-			ser = i;
-	ser -= from;
-	if(ser<0 || ser >= SIZE) return;
-	int pom = std::min((which->movement)/100,(ui32)mobile->ourImages.size()-1);
-	blitAt(mobile->ourImages[pom].bitmap,posmobx,posmoby+ser*32); //move point
-}
-
-void CHeroList::draw(SDL_Surface * to)
-{
-	for (int iT=0+from;iT<SIZE+from;iT++)
-	{
-		int i = iT-from;
-		if (iT>=LOCPLINT->wanderingHeroes.size())
-		{
-			blitAt(mobile->ourImages[0].bitmap,posmobx,posmoby+i*32,to);
-			blitAt(mana->ourImages[0].bitmap,posmanx,posmany+i*32,to);
-			blitAt(empty,posporx,pospory+i*32,to);
-			continue;
-		}
-		const CGHeroInstance *cur = LOCPLINT->wanderingHeroes[iT];
-		int pom = cur->movement / 100;
-		if (pom>25) pom=25;
-		if (pom<0) pom=0;
-		blitAt(mobile->ourImages[pom].bitmap,posmobx,posmoby+i*32,to); //move point
-		pom = cur->mana / 5;
-		if (pom>25) pom=25;
-		if (pom<0) pom=0;
-		blitAt(mana->ourImages[pom].bitmap,posmanx,posmany+i*32,to); //mana
-		SDL_Surface * temp = graphics->portraitSmall[cur->portrait];
-		blitAt(temp,posporx,pospory+i*32,to);
-		if (adventureInt->selection && (selected == iT) && (adventureInt->selection->ID == GameConstants::HEROI_TYPE))
-		{
-			blitAt(selection,posporx,pospory+i*32,to);
-		}
-		//TODO: support for custom portraits
-	}
-	if (from>0)
-		blitAt(arrup->ourImages[0].bitmap,arrupp.x,arrupp.y,to);
-	else
-		blitAt(arrup->ourImages[2].bitmap,arrupp.x,arrupp.y,to);
-
-	if (LOCPLINT->wanderingHeroes.size()-from > SIZE)
-		blitAt(arrdo->ourImages[0].bitmap,arrdop.x,arrdop.y,to);
-	else
-		blitAt(arrdo->ourImages[2].bitmap,arrdop.x,arrdop.y,to);
-}
-
-int CHeroList::getPosOfHero(const CGHeroInstance* h)
-{
-	return vstd::find_pos(LOCPLINT->wanderingHeroes, h, std::equal_to<const CGHeroInstance*>());
-}
-
-void CHeroList::show(SDL_Surface * to)
-{
-
-}
-
-void CHeroList::showAll(SDL_Surface * to)
-{
-	draw(to);
-}
-
-int CHeroList::size()
-{
-	return LOCPLINT->wanderingHeroes.size();
-}
-
-CTownList::~CTownList()
-{
-	delete arrup;
-	delete arrdo;
-}
-
-CTownList::CTownList(int Size, int x, int y, std::string arrupg, std::string arrdog)
-:CList(Size)
-{
-	arrup = CDefHandler::giveDef(arrupg);
-	arrdo = CDefHandler::giveDef(arrdog);
-	pos.x += x;
-	pos.y += y;
-	pos.w = std::max(arrdo->width, arrup->width);
-	pos.h = arrdo->height + arrup->height + Size*32;
-
-	arrupp.x=pos.x;
-	arrupp.y=pos.y;
-	arrupp.w=arrup->width;
-	arrupp.h=arrup->height;
-	arrdop.x=pos.x;
-	arrdop.y=pos.y+arrup->height+32*SIZE;
-	arrdop.w=arrdo->width;
-	arrdop.h=arrdo->height;
-	posporx = arrdop.x;
-	pospory = arrupp.y + arrupp.h;
-
-	pressed = indeterminate;
-
-	from = 0;
-	selected = -1;
-}
-
-void CTownList::genList()
-{
-// 	LOCPLINT->towns.clear();
-// 	int howMany = LOCPLINT->cb->howManyTowns();
-// 	for (int i=0;i<howMany;i++)
-// 	{
-// 		LOCPLINT->towns.push_back(LOCPLINT->cb->getTownInfo(i,0));
-// 	}
-}
-
-void CTownList::select(int which)
-{
-	which %= LOCPLINT->towns.size();
-	selected = which;
-	fixPos();
-	if(!fun.empty())
-		fun();
-}
-
-void CTownList::mouseMoved (const SDL_MouseMotionEvent & sEvent)
-{
-	if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y))
-	{
-		if (from>0)
-			GH.statusbar->print(CGI->generaltexth->zelp[306].first);
-		else
-			GH.statusbar->clear();
-		return;
-	}
-	else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y))
-	{
-		if ((LOCPLINT->towns.size()-from)  >  SIZE)
-			GH.statusbar->print(CGI->generaltexth->zelp[307].first);
-		else
-			GH.statusbar->clear();
-		return;
-	}
-	//if not buttons then towns
-	int hx = GH.current->motion.x, hy = GH.current->motion.y;
-	hx-=pos.x;
-	hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-	int ny = hy/32;
-	if ((ny>=SIZE || ny<0) || (from+ny>=LOCPLINT->towns.size()))
-	{
-		GH.statusbar->clear();
-		return;
-	};
-	std::string temp = CGI->generaltexth->tcommands[4];
-	boost::algorithm::replace_first(temp,"%s",LOCPLINT->towns[from+ny]->name);
-	temp += ", "+LOCPLINT->towns[from+ny]->town->Name();
-	GH.statusbar->print(temp);
-}
-
-void CTownList::clickLeft(tribool down, bool previousState)
-{
-	if (down)
-	{
-		/***************************ARROWS*****************************************/
-		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y))
-		{
-			if(from>0)
-			{
-				blitAt(arrup->ourImages[1].bitmap,arrupp.x,arrupp.y,screenBuf);
-				pressed = true;
-			}
-			return;
-		}
-		else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y))
-		{
-			if(LOCPLINT->towns.size()-from > SIZE)
-			{
-				blitAt(arrdo->ourImages[1].bitmap,arrdop.x,arrdop.y,screenBuf);
-				pressed = false;
-			}
-			return;
-		}
-		/***************************TOWNS*****************************************/
-		int hx = GH.current->motion.x, hy = GH.current->motion.y;
-		hx-=pos.x;
-		hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-		int ny = hy/32;
-		if (ny>=SIZE || ny<0)
-			return;
-		if(GH.topInt() == adventureInt
-		  && (ny+from)==selected
-		  && adventureInt->selection->ID == GameConstants::TOWNI_TYPE
-		  )
-			LOCPLINT->openTownWindow(LOCPLINT->towns[selected]);//print town screen
-		else
-			select(ny+from);
-	}
-	else
-	{
-		if (indeterminate(pressed))
-			return;
-		if (pressed) //up
-		{
-			blitAt(arrup->ourImages[0].bitmap,arrupp.x,arrupp.y,screenBuf);
-			pressed = indeterminate;
-			if (!down)
-			{
-				from--;
-				if (from<0)
-					from=0;
-
-				draw(screenBuf);
-			}
-		}
-		else if (!pressed) //down
-		{
-			blitAt(arrdo->ourImages[0].bitmap,arrdop.x,arrdop.y,screenBuf);
-			pressed = indeterminate;
-			if (!down)
-			{
-				from++;
-				//if (from<LOCPLINT->towns.size()-5)
-				//	from=LOCPLINT->towns.size()-5;
-
-				draw(screenBuf);
-			}
-		}
-		else
-			throw 0;
-
-	}
-}
-
-void CTownList::clickRight(tribool down, bool previousState)
-{
-	if (down)
-	{
-		/***************************ARROWS*****************************************/
-		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y) && from>0)
-		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[306].second,down);
-		}
-		else if(isItIn(&arrdop,GH.current->motion.x,GH.current->motion.y) && (LOCPLINT->towns.size()-from>5))
-		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[307].second,down);
-		}
-		//if not buttons then towns
-		int hx = GH.current->motion.x, hy = GH.current->motion.y;
-		hx-=pos.x;
-		hy-=pos.y; hy-=arrup->ourImages[0].bitmap->h;
-		int ny = hy/32;
-		if ((ny>=SIZE || ny<0) || (from+ny>=LOCPLINT->towns.size()))
-		{
-			return;
-		}
-
-		//show popup
-		CRClickPopup::createAndPush(LOCPLINT->towns[from+ny], GH.current->motion);
-	}
-}
-
-void CTownList::hover (bool on)
-{
-}
-
-void CTownList::keyPressed (const SDL_KeyboardEvent & key)
-{
-}
-
-void CTownList::draw(SDL_Surface * to)
-{
-	for (int iT=0+from;iT<SIZE+from;iT++)
-	{
-		int i = iT-from;
-		if (iT>=LOCPLINT->towns.size())
-		{
-			blitAt(graphics->getPic(-1),posporx,pospory+i*32,to);
-			continue;
-		}
-
-		blitAt(graphics->getPic(LOCPLINT->towns[iT]->subID,LOCPLINT->towns[iT]->hasFort(),LOCPLINT->towns[iT]->builded),posporx,pospory+i*32,to);
-
-		if (adventureInt->selection && (selected == iT) && (adventureInt->selection->ID == GameConstants::TOWNI_TYPE))
-		{
-			blitAt(graphics->getPic(-2),posporx,pospory+i*32,to);
-		}
-	}
-	if (from>0)
-		blitAt(arrup->ourImages[0].bitmap,arrupp.x,arrupp.y,to);
-	else
-		blitAt(arrup->ourImages[2].bitmap,arrupp.x,arrupp.y,to);
-
-	if (LOCPLINT->towns.size()-from>SIZE)
-		blitAt(arrdo->ourImages[0].bitmap,arrdop.x,arrdop.y,to);
-	else
-		blitAt(arrdo->ourImages[2].bitmap,arrdop.x,arrdop.y,to);
-}
-
-void CTownList::show(SDL_Surface * to)
-{
 
-}
-
-void CTownList::showAll(SDL_Surface * to)
-{
-	draw(to);
-}
-
-int CTownList::size()
-{
-	return LOCPLINT->towns.size();
-}
-
-void CTownList::selectNext()
-{
-	select(selected+1);
-}
 
 CCreaturePic::CCreaturePic(int x, int y, const CCreature *cre, bool Big, bool Animated)
 {
@@ -1695,136 +1262,78 @@ void CRecruitmentWindow::initCres()
 	slider->setAmount(std::min(amounts[which],creatures[which].amount));
 }
 
-CSplitWindow::CSplitWindow(int cid, int max, CGarrisonInt *Owner, int Last, int val)
+CSplitWindow::CSplitWindow(const CCreature * creature, boost::function<void(int, int)> callback_,
+                           int leftMin_, int rightMin_, int leftAmount_, int rightAmount_):
+    CWindowObject("GPUCRDIV", PLAYER_COLORED),
+    callback(callback_),
+    leftAmount(leftAmount_),
+    rightAmount(rightAmount_),
+    leftMin(leftMin_),
+    rightMin(rightMin_),
+    slider(nullptr)
 {
-	last = Last;
-	which = 1;
-	c=cid;
-	slider = NULL;
-	gar = Owner;
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
-	SDL_Surface *hhlp = BitmapHandler::loadBitmap("GPUCRDIV.bmp");
-	graphics->blueToPlayersAdv(hhlp,LOCPLINT->playerID);
-	bitmap = SDL_ConvertSurface(hhlp,screen->format,0);
-	SDL_SetColorKey(bitmap,SDL_SRCCOLORKEY,SDL_MapRGB(bitmap->format,0,255,255));
-	SDL_FreeSurface(hhlp);
-	pos.x = screen->w/2 - bitmap->w/2;
-	pos.y = screen->h/2 - bitmap->h/2;
-	pos.w = bitmap->w;
-	pos.h = bitmap->h;
-	ok = new CAdventureMapButton("","",boost::bind(&CSplitWindow::split,this),pos.x+20,pos.y+263,"IOK6432.DEF",SDLK_RETURN);
-	cancel = new CAdventureMapButton("","",boost::bind(&CSplitWindow::close,this),pos.x+214,pos.y+263,"ICN6432.DEF",SDLK_ESCAPE);
-	int sliderPositions = max - (last>=0) - (last==2);
-	slider = new CSlider(pos.x+21,pos.y+194,257,boost::bind(&CSplitWindow::sliderMoved,this,_1),0,sliderPositions,val,true);
-	a1 = max-val;
-	a2 = val;
-	animLeft = new CCreaturePic(pos.x+20,  pos.y+54, CGI->creh->creatures[cid], true, false);
-	animRight = new CCreaturePic(pos.x+177, pos.y+54, CGI->creh->creatures[cid], true, false);
+	int total = leftAmount + rightAmount;
+	int leftMax = total - rightMin;
+	int rightMax = total - leftMin;
 
-	std::string title = CGI->generaltexth->allTexts[256];
-	boost::algorithm::replace_first(title,"%s",CGI->creh->creatures[cid]->namePl);
-	printAtMiddle(title,150,34,FONT_BIG,Colors::Jasmine,bitmap);
-	addUsedEvents(LCLICK | KEYBOARD);
-}
+	ok = new CAdventureMapButton("", "", boost::bind(&CSplitWindow::apply, this), 20, 263, "IOK6432", SDLK_RETURN);
+	cancel = new CAdventureMapButton("", "", boost::bind(&CSplitWindow::close, this), 214, 263, "ICN6432", SDLK_ESCAPE);
 
-CSplitWindow::~CSplitWindow() //d-tor
-{
-	SDL_FreeSurface(bitmap);
-	delete ok;
-	delete cancel;
-	delete slider;
-	delete animLeft;
-	delete animRight;
-}
+	int sliderPositions = total - leftMin - rightMin;
 
-void CSplitWindow::split()
-{
-	gar->splitStacks(a2);
-	close();
-}
+	leftInput = new CTextInput(Rect(20, 218, 100, 36), FONT_BIG, boost::bind(&CSplitWindow::setAmountText, this, _1, true));
+	rightInput = new CTextInput(Rect(176, 218, 100, 36), FONT_BIG, boost::bind(&CSplitWindow::setAmountText, this, _1, false));
 
-void CSplitWindow::close()
-{
-	GH.popIntTotally(this);
-}
+	//add filters to allow only number input
+	leftInput->filters.add(boost::bind(&CTextInput::numberFilter, _1, _2, leftMin, leftMax));
+	rightInput->filters.add(boost::bind(&CTextInput::numberFilter, _1, _2, rightMin, rightMax));
 
-void CSplitWindow::sliderMoved(int to)
-{
-	int all = a1+a2;
-	a2 = to + (last==1 || last==2);
-	if(slider)
-		a1 = all - a2;
-}
+	leftInput->setTxt(boost::lexical_cast<std::string>(leftAmount), false);
+	rightInput->setTxt(boost::lexical_cast<std::string>(rightAmount), false);
 
-void CSplitWindow::show(SDL_Surface * to)
-{
-	blitAt(bitmap,pos.x,pos.y,to);
-	ok->showAll(to);
-	cancel->showAll(to);
-	slider->showAll(to);
-	printAtMiddle(boost::lexical_cast<std::string>(a1) + (!which ? "_" : ""),pos.x+70,pos.y+237,FONT_BIG,Colors::Cornsilk,to);
-	printAtMiddle(boost::lexical_cast<std::string>(a2) + (which ? "_" : ""),pos.x+233,pos.y+237,FONT_BIG,Colors::Cornsilk,to);
-	animLeft->show(to);
-	animRight->show(to);
-}
+	animLeft = new CCreaturePic(20, 54, creature, true, false);
+	animRight = new CCreaturePic(177, 54,creature, true, false);
 
-void CSplitWindow::keyPressed (const SDL_KeyboardEvent & key)
-{
-	SDLKey k = key.keysym.sym;
-	if (CGuiHandler::isNumKey(k)) //convert numpad number to normal digit
-		k = CGuiHandler::numToDigit(k);
+	slider = new CSlider(21, 194, 257, boost::bind(&CSplitWindow::sliderMoved, this, _1), 0, sliderPositions, rightAmount - rightMin, true);
 
-	if(key.state != SDL_PRESSED)
-		return;
+	std::string title = CGI->generaltexth->allTexts[256];
+	boost::algorithm::replace_first(title,"%s", creature->namePl);
+	new CLabel(150, 34, FONT_BIG, CENTER, Colors::Jasmine, title);
+}
 
-	int &cur = (which ? a2 : a1),
-		&sec = (which ? a1 : a2),
-		ncur = cur;
-	if (k == SDLK_BACKSPACE)
-	{
-		ncur /= 10;
-	}
-	else if(k == SDLK_TAB)
-	{
-		which = !which;
-	}
-	else if (k == SDLK_LEFT)
-	{
-		ncur--;
-	}
-	else if (k == SDLK_RIGHT)
+void CSplitWindow::setAmountText(std::string text, bool left)
+{
+	try
 	{
-		ncur++;
+		setAmount(boost::lexical_cast<int>(text), left);
+		slider->moveTo(rightAmount - rightMin);
 	}
-	else
+	catch(boost::bad_lexical_cast &)
 	{
-		int number = k - SDLK_0;
-		if (number < 0   ||   number > 9) //not a number pressed
-		{
-			return;
-		}
-		ncur = cur*10 + number;
 	}
-	int delta = ncur - cur;
-	if(delta > sec)
-	{
-		cur += sec;
-		sec = 0;
-	}
-	slider->moveTo(which ? ncur : a1+a2-ncur);
 }
 
-void CSplitWindow::clickLeft(tribool down, bool previousState)
+void CSplitWindow::setAmount(int value, bool left)
 {
-	if(down)
-	{
-		Point click(GH.current->motion.x,GH.current->motion.y);
-		click = click - pos.topLeft();
-		if(Rect(19,216,105,40).isIn(click)) //left picture
-			which = 0;
-		else if(Rect(175,216,105,40).isIn(click)) //right picture
-			which = 1;
-	}
+	int total = leftAmount + rightAmount;
+	leftAmount  = left ? value : total - value;
+	rightAmount = left ? total - value : value;
+
+	leftInput->setTxt(boost::lexical_cast<std::string>(leftAmount));
+	rightInput->setTxt(boost::lexical_cast<std::string>(rightAmount));
+}
+
+void CSplitWindow::apply()
+{
+	callback(leftAmount, rightAmount);
+	close();
+}
+
+void CSplitWindow::sliderMoved(int to)
+{
+	setAmount(rightMin + to, false);
 }
 
 void CLevelWindow::close()
@@ -4163,7 +3672,6 @@ IShowActivatable::IShowActivatable()
 
 CGarrisonHolder::CGarrisonHolder()
 {
-	type |= WITH_GARRISON;
 }
 
 void CWindowWithGarrison::updateGarrisons()
@@ -5593,11 +5101,11 @@ void CUniversityWindow::CItem::showAll(SDL_Surface * to)
 	printAtMiddleLoc  (CGI->generaltexth->skillName[ID], 22, -13, FONT_SMALL, Colors::Cornsilk,to);//Name
 	printAtMiddleLoc  (CGI->generaltexth->levels[0], 22, 57, FONT_SMALL, Colors::Cornsilk,to);//Level(always basic)
 
-	CIntObject::showAll(to);
+	CAnimImage::showAll(to);
 }
 
 CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int Y):
-	CAnimImage ("SECSKILL", ID*3+3, 0, X, Y),
+	CAnimImage ("SECSKILL", _ID*3+3, 0, X, Y),
 	ID(_ID),
 	parent(_parent)
 {
@@ -6232,11 +5740,73 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
 	GH.pushInt(rcpi);
 }
 
+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("TOWNQVBK", RCLICK_POPUP | PLAYER_COLORED, toScreen(position))
+{
+	InfoAboutTown iah;
+	bool gotInfo = LOCPLINT->cb->getTownInfo(town, iah);
+	assert(gotInfo);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CTownTooltip(Point(9, 10), iah);
+}
+
+CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero):
+    CWindowObject("HEROQVBK", RCLICK_POPUP | PLAYER_COLORED, toScreen(position))
+{
+	InfoAboutHero iah;
+	bool gotInfo = LOCPLINT->cb->getHeroInfo(hero, iah);
+	assert(gotInfo);
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	new CHeroTooltip(Point(9, 10), iah);
+}
+
+CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr):
+    CWindowObject("TOWNQVBK", RCLICK_POPUP | PLAYER_COLORED, toScreen(position))
+{
+	InfoAboutTown iah;
+	bool gotInfo = LOCPLINT->cb->getTownInfo(garr, iah);
+	assert(gotInfo);
+
+	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 GameConstants::HEROI_TYPE:
+		return new CInfoBoxPopup(position, dynamic_cast<const CGHeroInstance *>(specific));
+	case GameConstants::TOWNI_TYPE:
+		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 NULL;
+	}
+}
+
 void CRClickPopup::createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment /*= BOTTOMRIGHT*/)
 {
-	SDL_Surface *iWin = LOCPLINT->infoWin(obj); //try get custom infowindow for this obj
+	CIntObject *iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
 	if(iWin)
-		GH.pushInt(new CInfoPopup(iWin, p, alignment, true));
+		GH.pushInt(iWin);
 	else
 		CRClickPopup::createAndPush(obj->getHoverText());
 }

+ 74 - 72
client/GUIClasses.h

@@ -73,6 +73,9 @@ class CArtifactInstance;
 class IBonusBearer;
 class CArtPlace;
 class CAnimImage;
+struct InfoAboutArmy;
+struct InfoAboutHero;
+struct InfoAboutTown;
 
 /// text + comp. + ok button
 class CInfoWindow : public CSimpleWindow
@@ -124,6 +127,7 @@ public:
 	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 CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
 };
@@ -156,6 +160,16 @@ public:
 	~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
 {
@@ -185,8 +199,6 @@ public:
 	CComponent(); //c-tor
 
 	void clickRight(tribool down, bool previousState); //call-in
-
-	void show(SDL_Surface * to);
 };
 
 class CSelectableComponent : public CComponent, public CKeyShortcut
@@ -206,6 +218,38 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////////
 
+/// 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;
 
@@ -264,68 +308,18 @@ public:
 	void recreateSlots();
 
 	void splitClick(); //handles click on split button
-	void splitStacks(int am2); //TODO: comment me
+	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, 2nd = 3)
+	//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=NULL, bool _removableUnits = true, bool smallImgs = false, bool _twoRows=false); //c-tor
 	~CGarrisonInt(); //d-tor
 };
 
-/// List of heroes which is shown at the right of the adventure map screen
-class CHeroList
-	: public CList
-{
-public:
-	CDefHandler *mobile, *mana; //mana and movement indicators
-	int posmobx, posporx, posmanx, posmoby, pospory, posmany;
-
-	CHeroList(int Size); //c-tor
-	int getPosOfHero(const CGHeroInstance* h); //hero's position on list
-	void genList();
-	void select(int which); //call-in
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent); //call-in
-	void clickLeft(tribool down, bool previousState); //call-in
-	void clickRight(tribool down, bool previousState); //call-in
-	void hover (bool on); //call-in
-	void keyPressed (const SDL_KeyboardEvent & key); //call-in
-	void updateHList(const CGHeroInstance *toRemove=NULL); //removes specific hero from the list or recreates it
-	void updateMove(const CGHeroInstance* which); //draws move points bar
-	void draw(SDL_Surface * to);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	void init();
-	int size(); //how many elements do we have
-};
-
-/// List of towns which is shown at the right of the adventure map screen
-class CTownList
-	: public CList
-{
-public:
-	boost::function<void()> fun; //function called on selection change
-	int posporx,pospory;
-
-	CTownList(int Size, int x, int y, std::string arrupg, std::string arrdog); //c-tor
-	~CTownList(); //d-tor
-	void genList();
-	void select(int which); //call-in
-	void selectNext(); //switches to the next town or the first one if none is selected
-	void mouseMoved (const SDL_MouseMotionEvent & sEvent);  //call-in
-	void clickLeft(tribool down, bool previousState);  //call-in
-	void clickRight(tribool down, bool previousState); //call-in
-	void hover (bool on);  //call-in
-	void keyPressed (const SDL_KeyboardEvent & key);  //call-in
-	void draw(SDL_Surface * to);
-	void show(SDL_Surface * to);
-	void showAll(SDL_Surface * to);
-	int size(); //how many elements do we have
-};
-
 /// draws picture with creature on background, use Animated=true to get animation
 class CCreaturePic : public CIntObject
 {
@@ -380,26 +374,34 @@ public:
 };
 
 /// Split window where creatures can be splitted up into two single unit stacks
-class CSplitWindow : public CIntObject
+class CSplitWindow : public CWindowObject
 {
-public:
-	CGarrisonInt *gar;
+	boost::function<void(int, int)> callback;
+	int leftAmount;
+	int rightAmount;
+
+	int leftMin;
+	int rightMin;
+
 	CSlider *slider;
 	CCreaturePic *animLeft, *animRight; //creature's animation
 	CAdventureMapButton *ok, *cancel;
-	SDL_Surface *bitmap; //background
-	int a1, a2, c; //TODO: comment me
-	bool which; //which creature is selected
-	int last; //0/1/2 - at least one creature must be in the src/dst/both stacks; -1 - no restrictions
 
-	CSplitWindow(int cid, int max, CGarrisonInt *Owner, int Last = -1, int val=0); //c-tor; val - initial amount of second stack
-	~CSplitWindow(); //d-tor
-	void split();
-	void close();
-	void show(SDL_Surface * to);
-	void clickLeft(tribool down, bool previousState); //call-in
-	void keyPressed (const SDL_KeyboardEvent & key); //call-in
-	void sliderMoved(int to);
+	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, boost::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
@@ -896,11 +898,11 @@ public:
 	friend class CArtPlace;
 };
 
-class CGarrisonHolder : public virtual CIntObject
+class CGarrisonHolder
 {
 public:
 	CGarrisonHolder();
-	virtual void updateGarrisons(){};
+	virtual void updateGarrisons(){}
 };
 
 class CWindowWithGarrison : public virtual CGarrisonHolder
@@ -1076,7 +1078,7 @@ public:
 };
 
 /// Hill fort is the building where you can upgrade units
-class CHillFortWindow : public CWindowWithGarrison
+class CHillFortWindow : public CIntObject, public CWindowWithGarrison
 {
 public:
 

+ 0 - 123
client/Graphics.cpp

@@ -43,121 +43,6 @@ using namespace CSDL_Ext;
 
 Graphics * graphics = NULL;
 
-SDL_Surface * Graphics::drawHeroInfoWin(const InfoAboutHero &curh)
-{
-	char buf[15];
-	blueToPlayersAdv(hInfo,curh.owner);
-	SDL_Surface * ret = SDL_DisplayFormat(hInfo);
-	SDL_SetColorKey(ret,SDL_SRCCOLORKEY,SDL_MapRGB(ret->format,0,255,255));
-
-	printAt(curh.name,75,13,FONT_SMALL,Colors::Cornsilk,ret); //name
-	blitAt(graphics->portraitLarge[curh.portrait],11,12,ret); //portrait
-
-	//army
-	for (ArmyDescriptor::const_iterator i = curh.army.begin(); i!=curh.army.end();i++)
-	{
-		blitAt(graphics->smallImgs[i->second.type->idNumber],slotsPos[(*i).first].first+1,slotsPos[(*i).first].second+1,ret);
-		if(curh.details)
-		{
-			SDL_itoa((*i).second.count,buf,10);
-			printAtMiddle(buf,slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,Colors::Cornsilk,ret);
-		}
-		else
-		{
-			printAtMiddle(VLC->generaltexth->arraytxt[174 + 3*(i->second.count)],slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,Colors::Cornsilk,ret);
-		}
-	}
-
-	if(curh.details)
-	{
-		for (int i = 0; i < GameConstants::PRIMARY_SKILLS; i++)
-		{
-			SDL_itoa(curh.details->primskills[i], buf, 10);
-			printAtMiddle(buf,84+28*i,70,FONT_SMALL,Colors::Cornsilk,ret);
-		}
-
-		//mana points
-		SDL_itoa(curh.details->mana,buf,10);
-		printAtMiddle(buf,167,108,FONT_TINY,Colors::Cornsilk,ret); 
-
-		blitAt(morale22->ourImages[curh.details->morale+3].bitmap,14,84,ret);	//luck
-		blitAt(luck22->ourImages[curh.details->morale+3].bitmap,14,101,ret);	//morale
-	}
-	return ret;
-}
-
-SDL_Surface * Graphics::drawHeroInfoWin(const CGHeroInstance * curh)
-{
-	InfoAboutHero iah;
-	iah.initFromHero(curh, true);
-	return drawHeroInfoWin(iah);
-}
-
-SDL_Surface * Graphics::drawTownInfoWin(const CGTownInstance * curh)
-{
-	InfoAboutTown iah;
-	iah.initFromTown(curh, true);
-	return drawTownInfoWin(iah);
-}
-
-SDL_Surface * Graphics::drawTownInfoWin( const InfoAboutTown & curh )
-{
-	char buf[10];
-	blueToPlayersAdv(tInfo,curh.owner);
-	SDL_Surface * ret = SDL_DisplayFormat(tInfo);
-	SDL_SetColorKey(ret,SDL_SRCCOLORKEY,SDL_MapRGB(ret->format,0,255,255));
-
-	printAt(curh.name,75,12,FONT_SMALL,Colors::Cornsilk,ret); //name
-	int pom = curh.fortLevel - 1; if(pom<0) pom = 3; //fort pic id
-	blitAt(forts->ourImages[pom].bitmap,115,42,ret); //fort
-
-	for (ArmyDescriptor::const_iterator i=curh.army.begin(); i!=curh.army.end();i++)
-	{
-		//if(!i->second.second)
-		//	continue;
-		blitAt(graphics->smallImgs[(*i).second.type->idNumber],slotsPos[(*i).first].first+1,slotsPos[(*i).first].second+1,ret);
-		if(curh.details)
-		{
-			// Show exact creature amount.
-			SDL_itoa((*i).second.count,buf,10);
-			printAtMiddle(buf,slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,Colors::Cornsilk,ret);
-		}
-		else
-		{
-			// Show only a rough amount for creature stacks.
-			// TODO: Deal with case when no information at all about size shold be presented.
-			std::string roughAmount = curh.obj->getRoughAmount(i->first);
-			printAtMiddle(roughAmount,slotsPos[(*i).first].first+17,slotsPos[(*i).first].second+41,FONT_TINY,Colors::Cornsilk,ret);
-		}
-	}
-
-	//blit town icon
-	if (curh.tType) {
-		pom = curh.tType->typeID*2;
-		if (!curh.fortLevel)
-			pom += GameConstants::F_NUMBER*2;
-		if(curh.built)
-			pom++;
-		blitAt(bigTownPic->ourImages[pom].bitmap,13,13,ret);
-	}
-
-	if(curh.details)
-	{
-		//hall level icon
-		if((pom=curh.details->hallLevel) >= 0)
-			blitAt(halls->ourImages[pom].bitmap, 77, 42, ret);
-
-		if (curh.details->goldIncome >= 0) {
-			SDL_itoa(curh.details->goldIncome, buf, 10); //gold income
-			printAtMiddle(buf, 167, 70, FONT_TINY, Colors::Cornsilk, ret);
-		}
-		if(curh.details->garrisonedHero) //garrisoned hero icon
-			blitAt(graphics->heroInGarrison,158,87,ret);
-	}
-
-	return ret;
-}
-
 void Graphics::loadPaletteAndColors()
 {
 	std::string pals = bitmaph->getTextFile("PLAYERS.PAL", FILE_OTHER);
@@ -249,14 +134,6 @@ void Graphics::initializeBattleGraphics()
 }
 Graphics::Graphics()
 {
-	slotsPos.push_back(std::pair<int,int>(44,82));
-	slotsPos.push_back(std::pair<int,int>(80,82));
-	slotsPos.push_back(std::pair<int,int>(116,82));
-	slotsPos.push_back(std::pair<int,int>(26,131));
-	slotsPos.push_back(std::pair<int,int>(62,131));
-	slotsPos.push_back(std::pair<int,int>(98,131));
-	slotsPos.push_back(std::pair<int,int>(134,131));
-
 	CDefHandler *smi, *smi2;
 
 	std::vector<Task> tasks; //preparing list of graphics to load

+ 0 - 5
client/Graphics.h

@@ -46,7 +46,6 @@ public:
 
 	SDL_Surface * hInfo, *tInfo; //hero and town infobox bgs
 	SDL_Surface *heroInGarrison; //icon for town infobox
-	std::vector<std::pair<int, int> > slotsPos; //creature slot positions in infoboxes
 	CDefEssential *luck22, *luck30, *luck42, *luck82,
 		*morale22, *morale30, *morale42, *morale82,
 		*halls, *forts, *bigTownPic;
@@ -102,10 +101,6 @@ public:
 	void loadHeroPortraits();
 	void loadWallPositions();
 	void loadErmuToPicture();
-	SDL_Surface * drawHeroInfoWin(const InfoAboutHero &curh);
-	SDL_Surface * drawHeroInfoWin(const CGHeroInstance * curh);
-	SDL_Surface * drawTownInfoWin(const InfoAboutTown & curh);
-	SDL_Surface * drawTownInfoWin(const CGTownInstance * curh);
 	SDL_Surface * getPic(int ID, bool fort=true, bool builded=false); //returns small picture of town: ID=-1 - blank; -2 - border; -3 - random
 	void blueToPlayersAdv(SDL_Surface * sur, int player); //replaces blue interface colour with a color of player
 	void loadTrueType();

+ 2 - 0
client/Makefile.am

@@ -41,6 +41,8 @@ vcmiclient_SOURCES =  \
 	./UIFramework/SDL_Extensions.cpp \
 	./UIFramework/SDL_Extensions.h \
 	./UIFramework/SDL_Pixels.h \
+	AdventureMapClasses.cpp \
+	AdventureMapClasses.h \
 	CAdvmapInterface.cpp \
 	CAdvmapInterface.h \
 	CAnimation.cpp \

+ 18 - 0
client/Makefile.in

@@ -72,6 +72,7 @@ am_vcmiclient_OBJECTS = vcmiclient-CCallback.$(OBJEXT) \
 	vcmiclient-Geometries.$(OBJEXT) \
 	vcmiclient-CCursorHandler.$(OBJEXT) \
 	vcmiclient-SDL_Extensions.$(OBJEXT) \
+	vcmiclient-AdventureMapClasses.$(OBJEXT) \
 	vcmiclient-CAdvmapInterface.$(OBJEXT) \
 	vcmiclient-CAnimation.$(OBJEXT) \
 	vcmiclient-CBitmapHandler.$(OBJEXT) \
@@ -344,6 +345,8 @@ vcmiclient_SOURCES = \
 	./UIFramework/SDL_Extensions.cpp \
 	./UIFramework/SDL_Extensions.h \
 	./UIFramework/SDL_Pixels.h \
+	AdventureMapClasses.cpp \
+	AdventureMapClasses.h \
 	CAdvmapInterface.cpp \
 	CAdvmapInterface.h \
 	CAnimation.cpp \
@@ -481,6 +484,7 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcmiclient-AdventureMapClasses.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcmiclient-CAdvmapInterface.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcmiclient-CAnimation.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vcmiclient-CBattleAnimations.Po@am__quote@
@@ -691,6 +695,20 @@ vcmiclient-SDL_Extensions.obj: ./UIFramework/SDL_Extensions.cpp
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -c -o vcmiclient-SDL_Extensions.obj `if test -f './UIFramework/SDL_Extensions.cpp'; then $(CYGPATH_W) './UIFramework/SDL_Extensions.cpp'; else $(CYGPATH_W) '$(srcdir)/./UIFramework/SDL_Extensions.cpp'; fi`
 
+vcmiclient-AdventureMapClasses.o: AdventureMapClasses.cpp
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -MT vcmiclient-AdventureMapClasses.o -MD -MP -MF $(DEPDIR)/vcmiclient-AdventureMapClasses.Tpo -c -o vcmiclient-AdventureMapClasses.o `test -f 'AdventureMapClasses.cpp' || echo '$(srcdir)/'`AdventureMapClasses.cpp
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/vcmiclient-AdventureMapClasses.Tpo $(DEPDIR)/vcmiclient-AdventureMapClasses.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='AdventureMapClasses.cpp' object='vcmiclient-AdventureMapClasses.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -c -o vcmiclient-AdventureMapClasses.o `test -f 'AdventureMapClasses.cpp' || echo '$(srcdir)/'`AdventureMapClasses.cpp
+
+vcmiclient-AdventureMapClasses.obj: AdventureMapClasses.cpp
+@am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -MT vcmiclient-AdventureMapClasses.obj -MD -MP -MF $(DEPDIR)/vcmiclient-AdventureMapClasses.Tpo -c -o vcmiclient-AdventureMapClasses.obj `if test -f 'AdventureMapClasses.cpp'; then $(CYGPATH_W) 'AdventureMapClasses.cpp'; else $(CYGPATH_W) '$(srcdir)/AdventureMapClasses.cpp'; fi`
+@am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/vcmiclient-AdventureMapClasses.Tpo $(DEPDIR)/vcmiclient-AdventureMapClasses.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	$(AM_V_CXX)source='AdventureMapClasses.cpp' object='vcmiclient-AdventureMapClasses.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -c -o vcmiclient-AdventureMapClasses.obj `if test -f 'AdventureMapClasses.cpp'; then $(CYGPATH_W) 'AdventureMapClasses.cpp'; else $(CYGPATH_W) '$(srcdir)/AdventureMapClasses.cpp'; fi`
+
 vcmiclient-CAdvmapInterface.o: CAdvmapInterface.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(vcmiclient_CXXFLAGS) $(CXXFLAGS) -MT vcmiclient-CAdvmapInterface.o -MD -MP -MF $(DEPDIR)/vcmiclient-CAdvmapInterface.Tpo -c -o vcmiclient-CAdvmapInterface.o `test -f 'CAdvmapInterface.cpp' || echo '$(srcdir)/'`CAdvmapInterface.cpp
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/vcmiclient-CAdvmapInterface.Tpo $(DEPDIR)/vcmiclient-CAdvmapInterface.Po

+ 1 - 4
client/NetPacksClient.cpp

@@ -793,12 +793,9 @@ void SetSelection::applyCl(CClient *cl)
 
 void ShowInInfobox::applyCl(CClient *cl)
 {
-	CComponent sc(c);
-	text.toString(sc.description);
-	INTERFACE_CALL_IF_PRESENT(player,showComp, sc);
+	INTERFACE_CALL_IF_PRESENT(player,showComp, c, text.toString());
 }
 
-
 void AdvmapSpellCast::applyCl(CClient *cl)
 {
 	cl->invalidatePaths();

+ 11 - 7
client/UIFramework/CGuiHandler.cpp

@@ -120,12 +120,12 @@ void CGuiHandler::totalRedraw()
 
 void CGuiHandler::updateTime()
 {
-	int tv = th.getDiff();
+	int ms = mainFPSmng->getElapsedMilliseconds();
 	std::list<CIntObject*> hlp = timeinterested;
 	for (std::list<CIntObject*>::iterator i=hlp.begin(); i != hlp.end();i++)
 	{
 		if(!vstd::contains(timeinterested,*i)) continue;
-		(*i)->onTimer(tv);
+		(*i)->onTimer(ms);
 	}
 }
 
@@ -470,14 +470,18 @@ void CFramerateManager::init()
 void CFramerateManager::framerateDelay()
 {
 	ui32 currentTicks = SDL_GetTicks();
-	this->timeElapsed = currentTicks - this->lastticks;
+	timeElapsed = currentTicks - lastticks;
 
 	// FPS is higher than it should be, then wait some time
-	if (this->timeElapsed < this->rateticks)
+	if (timeElapsed < rateticks)
 	{
-		SDL_Delay(ceil(this->rateticks) - this->timeElapsed);
+		SDL_Delay(ceil(this->rateticks) - timeElapsed);
 	}
+	currentTicks = SDL_GetTicks();
 
-	this->fps = ceil(1000.0 / this->timeElapsed);
-	this->lastticks = SDL_GetTicks();
+	fps = ceil(1000.0 / timeElapsed);
+
+	//recalculate timeElapsed for external calls via getElapsed()
+	timeElapsed = currentTicks - lastticks;
+	lastticks = SDL_GetTicks();
 }

+ 1 - 2
client/UIFramework/CGuiHandler.h

@@ -34,7 +34,7 @@ public:
 	CFramerateManager(int rate); // initializes the manager with a given fps rate
 	void init(); // needs to be called directly before the main game loop to reset the internal timer
 	void framerateDelay(); // needs to be called every game update cycle
-	double getElapsedSeconds() const { return this->timeElapsed / 1000; }
+	ui32 getElapsedMilliseconds() const {return this->timeElapsed;}
 };
 
 // Handles GUI logic and drawing
@@ -42,7 +42,6 @@ class CGuiHandler
 {
 public:
 	CFramerateManager * mainFPSmng; //to keep const framerate
-	CStopWatch th;
 	std::list<IShowActivatable *> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
 	IStatusBar * statusbar;
 

+ 23 - 18
client/UIFramework/CIntObject.cpp

@@ -1,4 +1,4 @@
-#include "StdInc.h"
+#include "../StdInc.h"
 #include "CIntObject.h"
 #include "CGuiHandler.h"
 #include "SDL_Extensions.h"
@@ -138,7 +138,7 @@ CIntObject::CIntObject(int used_, Point pos_):
 
 void CIntObject::setTimer(int msToTrigger)
 {
-	if (!active & TIME )
+	if (!(active & TIME))
 		activateTimer();
 	toNextTick = timerDelay = msToTrigger;
 	used |= TIME;
@@ -170,10 +170,7 @@ void CIntObject::showAll(SDL_Surface * to)
 			if(children[i]->recActions & SHOWALL)
 				children[i]->showAll(to);
 
-	}//FIXME: needed?
-	/*
-	else
-		show(to);*/
+	}
 }
 
 void CIntObject::activate()
@@ -183,7 +180,10 @@ void CIntObject::activate()
 		if ((used | GENERAL) == active_m)
 			return;
 		else
+		{
+			tlog1 << "Warning: IntObject re-activated with mismatching used and active\n";
 			deactivate(); //FIXME: better to avoid such possibility at all
+		}
 	}
 
 	active_m |= GENERAL;
@@ -307,14 +307,14 @@ void CIntObject::printToLoc( const std::string & text, int x, int y, EFonts font
 void CIntObject::addUsedEvents(ui16 newActions)
 {
 	if (active_m)
-		activate(newActions & ~used);
+		activate(~used & newActions);
 	used |= newActions;
 }
 
 void CIntObject::removeUsedEvents(ui16 newActions)
 {
 	if (active_m)
-		deactivate(newActions & used);
+		deactivate(used & newActions);
 	used &= ~newActions;
 }
 
@@ -373,12 +373,12 @@ void CIntObject::addChild(CIntObject *child, bool adjustPosition /*= false*/)
 {
 	if (vstd::contains(children, child))
 	{
-		tlog4<< "Warning: object already assigned to this parent!\n";
+//		tlog4<< "Warning: object already assigned to this parent!\n";
 		return;
 	}
 	if (child->parent_m)
 	{
-		tlog4<< "Warning: object already has parent!\n";
+//		tlog4<< "Warning: object already has parent!\n";
 		child->parent_m->removeChild(child, adjustPosition);
 	}
 	children.push_back(child);
@@ -412,15 +412,20 @@ void CIntObject::drawBorderLoc(SDL_Surface * sur, const Rect &r, const int3 &col
 
 void CIntObject::redraw()
 {
-	if (parent_m && (type & REDRAW_PARENT))
-	{
-		parent_m->redraw();
-	}
-	else
+	//currently most of calls come from active objects so this check won't affect them
+	//it should fix glitches when called by inactive elements located below active window
+	if (active)
 	{
-		showAll(screenBuf);
-		if(screenBuf != screen)
-			showAll(screen);
+		if (parent_m && (type & REDRAW_PARENT))
+		{
+			parent_m->redraw();
+		}
+		else
+		{
+			showAll(screenBuf);
+			if(screenBuf != screen)
+				showAll(screen);
+		}
 	}
 }
 

+ 155 - 62
client/UIFramework/CIntObjectClasses.cpp

@@ -5,6 +5,7 @@
 #include "SDL_Extensions.h"
 #include "../Graphics.h"
 #include "../CAnimation.h"
+#include "CCursorHandler.h"
 #include "../CGameInfo.h"
 #include "../../CCallback.h"
 #include "../CConfigHandler.h"
@@ -702,7 +703,7 @@ CSlider::CSlider(int x, int y, int totalw, boost::function<void(int)> Moved, int
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	setAmount(amount);
 
-	addUsedEvents(LCLICK);
+	addUsedEvents(LCLICK | KEYBOARD | WHEEL);
 	strongInterest = true;
 
 
@@ -804,9 +805,11 @@ void CSlider::keyPressed(const SDL_KeyboardEvent & key)
 	switch(key.keysym.sym)
 	{
 	case SDLK_UP:
+	case SDLK_LEFT:
 		moveDest = value - 1;
 		break;
 	case SDLK_DOWN:
+	case SDLK_RIGHT:
 		moveDest = value + 1;
 		break;
 	case SDLK_PAGEUP:
@@ -902,13 +905,14 @@ CIntObject * CTabbedInt::getItem()
 
 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)
+	CObjectList(create, destroy),
+	first(InitialPos),
+	totalSize(TotalSize),
+	itemOffset(ItemOffset),
+    slider(nullptr)
 {
 	pos += Pos;
-	items.resize(VisibleSize, NULL);
+	items.resize(VisibleSize, nullptr);
 
 	if (Slider & 1)
 	{
@@ -947,14 +951,48 @@ void CListBox::reset()
 	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)
-		moveToPos(which - items.size());
+	else if (first + items.size() <= which && which < totalSize)
+		moveToPos(which - items.size() + 1);
 }
 
 void CListBox::moveToPos(size_t which)
@@ -1006,7 +1044,12 @@ void CListBox::moveToPrev()
 	}
 }
 
-std::list<CIntObject*> CListBox::getItems()
+size_t CListBox::getPos()
+{
+	return first;
+}
+
+const std::list<CIntObject *> &CListBox::getItems()
 {
 	return items;
 }
@@ -1081,30 +1124,6 @@ std::string CStatusBar::getCurrent()
 	return current;
 }
 
-void CList::clickLeft(tribool down, bool previousState)
-{
-};
-
-CList::CList(int Size)
-:SIZE(Size)
-{
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD | MOVE);
-}
-
-void CList::fixPos()
-{
-	if(selected < 0) //no selection, do nothing
-		return;
-	if(selected < from) //scroll up
-		from = selected;
-	else if(from + SIZE <= selected)
-		from = selected - SIZE + 1; 
-
-	vstd::amin(from, size() - SIZE);
-	vstd::amax(from, 0);
-	draw(screen);
-}
-
 void CHoverableArea::hover (bool on)
 {
 	if (on)
@@ -1159,14 +1178,8 @@ void LRClickableAreaWText::init()
 void CLabel::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
-	std::string *hlpText = NULL;  //if NULL, text field will be used
-	if(ignoreLeadingWhitespace)
-	{
-		hlpText = new std::string(text);
-		boost::trim_left(*hlpText);
-	}
 
-	std::string &toPrint = hlpText ? *hlpText : text;
+	std::string toPrint = visibleText();
 	if(!toPrint.length())
 		return;
 
@@ -1188,6 +1201,14 @@ CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, con
 	pos.h = graphics->fonts[font]->height;
 }
 
+std::string CLabel::visibleText()
+{
+	std::string ret = text;
+	if(ignoreLeadingWhitespace)
+		boost::trim_left(ret);
+	return ret;
+}
+
 void CLabel::setTxt(const std::string &Txt)
 {
 	text = Txt;
@@ -1375,6 +1396,21 @@ void CGStatusBar::calcOffset()
 	}
 }
 
+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;
+	textOffset = Point(pos.w/2, pos.h/2);
+	captureAllKeys = true;
+	bg = nullptr;
+	addUsedEvents(LCLICK | KEYBOARD);
+	giveFocus();
+}
+
 CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB )
 :cb(CB)
 {
@@ -1406,11 +1442,9 @@ CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
 	giveFocus();
 }
 
-void CTextInput::showAll(SDL_Surface * to)
+std::string CTextInput::visibleText()
 {
-	CIntObject::showAll(to);
-	const std::string toPrint = focus ? text + "_" : text;
-	CSDL_Ext::printAt(toPrint, pos.x, pos.y, FONT_SMALL, Colors::Cornsilk, to);
+	return focus ? text + "_" : text;
 }
 
 void CTextInput::clickLeft( tribool down, bool previousState )
@@ -1431,33 +1465,31 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key )
 		return;
 	}
 
+	std::string oldText = text;
 	switch(key.keysym.sym)
 	{
 	case SDLK_BACKSPACE:
-		if(text.size())
+		if(!text.empty())
 			text.resize(text.size()-1);
 		break;
 	default:
-		char c = key.keysym.unicode; //TODO 16-/>8
-		static const std::string forbiddenChars = "<>:\"/\\|?*"; //if we are entering a filename, some special characters won't be allowed
-		if(!vstd::contains(forbiddenChars,c) && std::isprint(c))
-			text += c;
+		text += key.keysym.unicode; //TODO 16-/>8
 		break;
 	}
-	redraw();
-	cb(text);
-}
 
-void CTextInput::setText( const std::string &nText, bool callCb )
-{
-	text = nText;
-	redraw();
-	if(callCb)
+	filters(text, oldText);
+	if (text != oldText)
+	{
+		redraw();
 		cb(text);
+	}
 }
 
-CTextInput::~CTextInput()
+void CTextInput::setTxt( const std::string &nText, bool callCb )
 {
+	CLabel::setTxt(nText);
+	if(callCb)
+		cb(text);
 }
 
 bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
@@ -1465,9 +1497,57 @@ bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
 	if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
 		return false;
 
+	//this should allow all non-printable keys to go through (for example arrows)
+	if (key.keysym.unicode < ' ')
+		return false;
+
 	return true;
 }
 
+void CTextInput::filenameFilter(std::string & text, const std::string &)
+{
+	static const std::string forbiddenChars = "<>:\"/\\|?*"; //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
+		tlog0 << "Warning: failed to convert "<< text << " to number!\n";
+		text = oldText;
+	}
+}
+
 CFocusable::CFocusable()
 {
 	focusables.push_back(this);
@@ -1511,23 +1591,29 @@ void CFocusable::moveFocus()
 }
 
 CWindowObject::CWindowObject(std::string imageName, int options_, Point centerAt):
-    CIntObject(options_ & RCLICK_POPUP ? RCLICK : 0, Point()),
+    CIntObject(getUsedEvents(options_), Point()),
     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);
 }
 
 CWindowObject::CWindowObject(std::string imageName, int options_):
-    CIntObject(options & RCLICK_POPUP ? RCLICK : 0, Point()),
+    CIntObject(getUsedEvents(options_), Point()),
     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();
 }
@@ -1545,6 +1631,13 @@ CPicture * CWindowObject::createBg(std::string imageName, bool playerColored)
 	return image;
 }
 
+int CWindowObject::getUsedEvents(int options)
+{
+	if (options & RCLICK_POPUP)
+		return RCLICK;
+	return 0;
+}
+
 void CWindowObject::showAll(SDL_Surface *to)
 {
 	CIntObject::showAll(to);
@@ -1559,6 +1652,6 @@ void CWindowObject::close()
 
 void CWindowObject::clickRight(tribool down, bool previousState)
 {
-	if((!down || indeterminate(down)))
-		close();
+	close();
+	CCS->curh->show();
 }

+ 35 - 38
client/UIFramework/CIntObjectClasses.h

@@ -273,8 +273,18 @@ public:
 	//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
-	std::list< CIntObject * > getItems();
+	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);
@@ -283,11 +293,12 @@ public:
 	void moveToPos(size_t which);
 	void moveToNext();
 	void moveToPrev();
+
+	size_t getPos();
 };
 
 /// Status bar which is shown at the bottom of the in-game screens
-class CStatusBar
-	: public CIntObject, public IStatusBar
+class CStatusBar : public CIntObject, public IStatusBar
 {
 public:
 	SDL_Surface * bg; //background
@@ -304,9 +315,11 @@ public:
 };
 
 /// Label which shows text
-class CLabel
-	: public virtual CIntObject
+class CLabel : public virtual CIntObject
 {
+protected:
+	virtual std::string visibleText();
+
 public:
 	EAlignment alignment;
 	EFonts font;
@@ -397,46 +410,29 @@ public:
 };
 
 /// Text input box where players can enter text
-class CTextInput
-	: public CLabel, public CFocusable
+class CTextInput : public CLabel, public CFocusable
 {
+protected:
+	std::string visibleText();
+
 public:
 	CFunctionList<void(const std::string &)> cb;
+	CFunctionList<void(std::string &, const std::string &)> filters;
+	void setTxt(const std::string &nText, bool callCb = false);
 
-	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 = NULL);
-	~CTextInput();
 
-	void showAll(SDL_Surface * to) OVERRIDE;
-	void clickLeft(tribool down, bool previousState) OVERRIDE;
-	void keyPressed(const SDL_KeyboardEvent & key) OVERRIDE;
-	bool captureThisEvent(const SDL_KeyboardEvent & key) OVERRIDE;
-};
+	void clickLeft(tribool down, bool previousState) override;
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	bool captureThisEvent(const SDL_KeyboardEvent & key) override;
 
-/// Listbox UI Element
-class CList : public CIntObject
-{
-public:
-	SDL_Surface * bg; //background bitmap
-	CDefHandler *arrup, *arrdo; //button arrows for scrolling list
-	SDL_Surface *empty, *selection;
-	SDL_Rect arrupp, arrdop; //positions of arrows
-	int posw, posh; //position width/height
-	int selected, //id of selected position, <0 if none
-		from;
-	const int SIZE; //size of list
-	tribool pressed; //true=up; false=down; indeterminate=none
-
-	CList(int Size = 5); //c-tor
-	void clickLeft(tribool down, bool previousState);
-	virtual void mouseMoved (const SDL_MouseMotionEvent & sEvent)=0; //call-in
-	virtual void genList()=0;
-	virtual void select(int which)=0;
-	virtual void draw(SDL_Surface * to)=0;
-	virtual int size() = 0; //how many elements do we have
-	void fixPos(); //scrolls list, so the selection will be visible
+	//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);
 };
 
 /// Shows a text by moving the mouse cursor over the object
@@ -468,9 +464,10 @@ public:
 
 /// Basic class for windows
 // TODO: status bar?
-class CWindowObject : public virtual CIntObject
+class CWindowObject : public CIntObject
 {
 	CPicture * createBg(std::string imageName, bool playerColored);
+	int getUsedEvents(int options);
 
 	int options;
 

+ 4 - 1
client/UIFramework/SDL_Extensions.cpp

@@ -995,7 +995,10 @@ void CSDL_Ext::SDL_PutPixelWithoutRefresh(SDL_Surface *ekran, const int & x, con
 
 void CSDL_Ext::SDL_PutPixelWithoutRefreshIfInSurf(SDL_Surface *ekran, const int & x, const int & y, const Uint8 & R, const Uint8 & G, const Uint8 & B, Uint8 A /*= 255*/)
 {
-	if(x >= 0  &&  x < ekran->w  &&  y >= 0  &&  y < ekran->h)
+	const SDL_Rect & rect = ekran->clip_rect;
+
+	if(x >= rect.x  &&  x < rect.w + rect.x
+	&& y >= rect.y  &&  y < rect.h + rect.y)
 		SDL_PutPixelWithoutRefresh(ekran, x, y, R, G, B, A);
 }
 

+ 13 - 12
lib/CCreatureHandler.cpp

@@ -25,7 +25,8 @@ CCreatureHandler::CCreatureHandler()
 	VLC->creh = this;
 
 	// Set the faction alignments to the defaults:
-	// Good: Castle, Rampart, Tower	// Evil: Inferno, Necropolis, Dungeon
+	// Good: Castle, Rampart, Tower
+	// Evil: Inferno, Necropolis, Dungeon
 	// Neutral: Stronghold, Fortess, Conflux
 	factionAlignments += 1, 1, 1, -1, -1, -1, 0, 0, 0;
 	doubledCreatures +=  4, 14, 20, 28, 44, 60, 70, 72, 85, 86, 100, 104; //according to Strategija
@@ -39,29 +40,29 @@ CCreatureHandler::CCreatureHandler()
 int CCreature::getQuantityID(const int & quantity)
 {
 	if (quantity<5)
-		return 0;
-	if (quantity<10)
 		return 1;
-	if (quantity<20)
+	if (quantity<10)
 		return 2;
-	if (quantity<50)
+	if (quantity<20)
 		return 3;
-	if (quantity<100)
+	if (quantity<50)
 		return 4;
-	if (quantity<250)
+	if (quantity<100)
 		return 5;
-	if (quantity<500)
+	if (quantity<250)
 		return 6;
-	if (quantity<1000)
+	if (quantity<500)
 		return 7;
-	return 8;
+	if (quantity<1000)
+		return 8;
+	return 9;
 }
 
 int CCreature::estimateCreatureCount(ui32 countID)
 {
-	static const int creature_count[] = { 3, 8, 15, 35, 75, 175, 375, 750, 2500 };
+	static const int creature_count[] = { 0, 3, 8, 15, 35, 75, 175, 375, 750, 2500 };
 
-	if (countID > 8)
+	if (countID > 9)
 		assert("Wrong countID!");
 
 	return creature_count[countID];

+ 10 - 2
lib/CCreatureSet.cpp

@@ -221,7 +221,10 @@ ui64 CCreatureSet::getPower (TSlot slot) const
 }
 std::string CCreatureSet::getRoughAmount (TSlot slot) const
 {
-	return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getStackCount(slot))];
+	int quantity = CCreature::getQuantityID(getStackCount(slot));
+	if (quantity)
+		return VLC->generaltexth->arraytxt[174 + 3*CCreature::getQuantityID(getStackCount(slot))];
+	return "";
 }
 
 int CCreatureSet::stacksCount() const
@@ -897,7 +900,12 @@ void CStackInstance::setArmyObj(const CArmedInstance *ArmyObj)
 
 std::string CStackInstance::getQuantityTXT(bool capitalized /*= true*/) const
 {
-	return VLC->generaltexth->arraytxt[174 + getQuantityID()*3 + 2 - capitalized];
+	int quantity = getQuantityID();
+
+	if (quantity)
+		return VLC->generaltexth->arraytxt[174 + quantity*3 - 1 - capitalized];
+	else
+		return "";
 }
 
 bool CStackInstance::valid(bool allowUnrandomized) const

+ 81 - 77
lib/CGameState.cpp

@@ -137,62 +137,6 @@ public:
 	}
 } *objCaller = NULL;
 
-InfoAboutTown::InfoAboutTown()
-{
-	tType = NULL;
-	details = NULL;
-	fortLevel = 0;
-	owner = -1;
-}
-
-InfoAboutTown::~InfoAboutTown()
-{
-	delete details;
-}
-
-void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
-{
-	obj = t;
-	army = ArmyDescriptor(t->getUpperArmy(), detailed);
-	built = t->builded;
-	fortLevel = t->fortLevel();
-	name = t->name;
-	tType = t->town;
-	owner = t->tempOwner;
-
-	if(detailed)
-	{
-		//include details about hero
-		details = new Details;
-		details->goldIncome = t->dailyIncome();
-		details->customRes = vstd::contains(t->builtBuildings, 15);
-		details->hallLevel = t->hallLevel();
-		details->garrisonedHero = t->garrisonHero;
-	}
-	//TODO: adjust undetailed info about army to our count of thieves guilds
-}
-
-void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed)
-{
-	obj = garr;
-	fortLevel = 0;
-	army = ArmyDescriptor(garr, detailed);
-	name = VLC->generaltexth->names[33]; // "Garrison"
-	owner = garr->tempOwner;
-	built = false;
-	tType = NULL;
-
-	// Show detailed info only to owning player.
-	if(detailed)
-	{
-		details = new InfoAboutTown::Details;
-		details->customRes = false;
-		details->garrisonedHero = false;
-		details->goldIncome = -1;
-		details->hallLevel = -1;
-	}
-}
-
 void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
 {
 	int type = txt.first, ser = txt.second;
@@ -2590,32 +2534,69 @@ std::string PlayerState::nodeName() const
 // 	}
 // }
 
-InfoAboutHero::InfoAboutHero()
+InfoAboutArmy::InfoAboutArmy():
+    owner(GameConstants::NEUTRAL_PLAYER)
+{}
+
+InfoAboutArmy::InfoAboutArmy(const CArmedInstance *Army, bool detailed)
+{
+	initFromArmy(Army, detailed);
+}
+
+void InfoAboutArmy::initFromArmy(const CArmedInstance *Army, bool detailed)
 {
-	details = NULL;
-	hclass = NULL;
-	portrait = -1;
+	army = ArmyDescriptor(Army, detailed);
+	owner = Army->tempOwner;
+	name = Army->getHoverText();
+}
+
+void InfoAboutHero::assign(const InfoAboutHero & iah)
+{
+	InfoAboutArmy::operator = (iah);
+
+	details = (iah.details ? new Details(*iah.details) : NULL);
+	hclass = iah.hclass;
+	portrait = iah.portrait;
 }
 
-InfoAboutHero::InfoAboutHero( const InfoAboutHero & iah )
+InfoAboutHero::InfoAboutHero():
+    details(nullptr),
+    hclass(nullptr),
+    portrait(-1)
+{}
+
+InfoAboutHero::InfoAboutHero(const InfoAboutHero & iah):
+    InfoAboutArmy()
 {
 	assign(iah);
 }
 
+InfoAboutHero::InfoAboutHero(const CGHeroInstance *h, bool detailed)
+{
+	initFromHero(h, detailed);
+}
+
 InfoAboutHero::~InfoAboutHero()
 {
 	delete details;
 }
 
-void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
+InfoAboutHero & InfoAboutHero::operator=(const InfoAboutHero & iah)
 {
-	if(!h) return;
+	assign(iah);
+	return *this;
+}
+
+void InfoAboutHero::initFromHero(const CGHeroInstance *h, bool detailed)
+{
+	if(!h)
+		return;
+
+	initFromArmy(h, detailed);
 
-	owner = h->tempOwner;
 	hclass = h->type->heroClass;
 	name = h->name;
 	portrait = h->portrait;
-	army = ArmyDescriptor(h, detailed);
 
 	if(detailed)
 	{
@@ -2633,25 +2614,47 @@ void InfoAboutHero::initFromHero( const CGHeroInstance *h, bool detailed )
 	}
 }
 
-void InfoAboutHero::assign( const InfoAboutHero & iah )
+InfoAboutTown::InfoAboutTown():
+    details(nullptr),
+    tType(nullptr),
+    built(0),
+    fortLevel(0)
 {
-	army = iah.army;
-	details = (iah.details ? new Details(*iah.details) : NULL);
-	hclass = iah.hclass;
-	name = iah.name;
-	owner = iah.owner;
-	portrait = iah.portrait;
+
 }
 
-InfoAboutHero & InfoAboutHero::operator=( const InfoAboutHero & iah )
+InfoAboutTown::InfoAboutTown(const CGTownInstance *t, bool detailed)
 {
-	assign(iah);
-	return *this;
+	initFromTown(t, detailed);
 }
 
+InfoAboutTown::~InfoAboutTown()
+{
+	delete details;
+}
+
+void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
+{
+	initFromArmy(t, detailed);
+	army = ArmyDescriptor(t->getUpperArmy(), detailed);
+	built = t->builded;
+	fortLevel = t->fortLevel();
+	name = t->name;
+	tType = t->town;
 
+	if(detailed)
+	{
+		//include details about hero
+		details = new Details;
+		details->goldIncome = t->dailyIncome();
+		details->customRes = vstd::contains(t->builtBuildings, 15);
+		details->hallLevel = t->hallLevel();
+		details->garrisonedHero = t->garrisonHero;
+	}
+}
 
-ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed) : isDetailed(detailed)
+ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed)
+    : isDetailed(detailed)
 {
 	for(TSlots::const_iterator i = army->Slots().begin(); i != army->Slots().end(); i++)
 	{
@@ -2662,7 +2665,8 @@ ArmyDescriptor::ArmyDescriptor(const CArmedInstance *army, bool detailed) : isDe
 	}
 }
 
-ArmyDescriptor::ArmyDescriptor() : isDetailed(true)
+ArmyDescriptor::ArmyDescriptor()
+    : isDetailed(false)
 {
 
 }

+ 26 - 18
lib/CGameState.h

@@ -76,55 +76,63 @@ struct ArmyDescriptor : public std::map<TSlot, CStackBasicDescriptor>
 	DLL_LINKAGE int getStrength() const;
 };
 
-struct DLL_LINKAGE InfoAboutHero
+struct DLL_LINKAGE InfoAboutArmy
+{
+	ui8 owner;
+	std::string name;
+
+	ArmyDescriptor army;
+
+	InfoAboutArmy();
+	InfoAboutArmy(const CArmedInstance *Army, bool detailed);
+
+	void initFromArmy(const CArmedInstance *Army, bool detailed);
+};
+
+struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy
 {
 private:
 	void assign(const InfoAboutHero & iah);
 public:
 	struct DLL_LINKAGE Details
 	{
-		std::vector<int> primskills;
-		int mana, luck, morale;
+		std::vector<si32> primskills;
+		si32 mana, luck, morale;
 	} *details;
 
-	char owner;
 	const CHeroClass *hclass;
-	std::string name;
 	int portrait;
 
-	ArmyDescriptor army; 
-
 	InfoAboutHero();
 	InfoAboutHero(const InfoAboutHero & iah);
-	InfoAboutHero & operator=(const InfoAboutHero & iah);
+	InfoAboutHero(const CGHeroInstance *h, bool detailed);
 	~InfoAboutHero();
+
+	InfoAboutHero & operator=(const InfoAboutHero & iah);
+
 	void initFromHero(const CGHeroInstance *h, bool detailed);
 };
 
 /// Struct which holds a int information about a town
-struct DLL_LINKAGE InfoAboutTown
+struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy
 {
-	struct Details
+	struct DLL_LINKAGE Details
 	{
-		int hallLevel, goldIncome;
+		si32 hallLevel, goldIncome;
 		bool customRes;
 		bool garrisonedHero;
 
 	} *details;
 
-	const CArmedInstance * obj;
-	char fortLevel; //0 - none
-	char owner;
-	std::string name;
 	CTown *tType;
-	bool built;
 
-	ArmyDescriptor army; //numbers of creatures are valid only if details
+	si32 built;
+	si32 fortLevel; //0 - none
 
 	InfoAboutTown();
+	InfoAboutTown(const CGTownInstance *t, bool detailed);
 	~InfoAboutTown();
 	void initFromTown(const CGTownInstance *t, bool detailed);
-	void initFromGarrison(const CGGarrison *garr, bool detailed);
 };
 
 // typedef si32 TResourceUnit;

+ 1 - 1
lib/CLodHandler.cpp

@@ -78,7 +78,7 @@ ui8 * CLodHandler::giveFile(std::string fname, LodFileType type, int * length)
 			result = -1;
 		if(result<0)
 		{
-			tlog1<<"Error in file reading: " << myDir << "/" << ourEntry.name << std::endl;
+			tlog1<<"Error in file reading: " << myDir << "/" << ourEntry.realName << std::endl;
 			delete[] outp;
 			return NULL;
 		}

+ 1 - 1
lib/IGameCallback.cpp

@@ -783,7 +783,7 @@ bool CGameInfoCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown
 	if(town->ID == GameConstants::TOWNI_TYPE)
 		dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
 	else if(town->ID == 33 || town->ID == 219)
-		dest.initFromGarrison(static_cast<const CGGarrison *>(town), detailed);
+		dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
 	else
 		return false;
 	return true;

+ 1 - 1
lib/IGameEventsReceiver.h

@@ -118,5 +118,5 @@ public:
 	virtual void playerBlocked(int reason){}; //reason: 0 - upcoming battle
 	virtual void gameOver(ui8 player, bool victory){}; //player lost or won the game
 	virtual void playerStartsTurn(ui8 player){};
-	virtual void showComp(const CComponent &comp) {}; //display component in the advmapint infobox
+	virtual void showComp(const Component &comp, std::string message) {}; //display component in the advmapint infobox
 };