浏览代码

Implemented Scuttle Boat and Dimension Door spells.
Fixed #417 and #418.
Moved / refactored some code. Minor fixes.

Michał W. Urbańczyk 15 年之前
父节点
当前提交
80f49badc9

+ 38 - 19
CCallback.cpp

@@ -7,6 +7,7 @@
 #include "lib/map.h"
 #include "hch/CBuildingHandler.h"
 #include "hch/CDefObjInfoHandler.h"
+#include "hch/CGeneralTextHandler.h"
 #include "hch/CHeroHandler.h"
 #include "hch/CObjectHandler.h"
 #include "lib/Connection.h"
@@ -33,21 +34,7 @@
  *
  */
 
-static int gcd(int x, int y)
-{
-	int temp;
-	if (y > x)
-		std::swap(x,y);
-	while (y != 0)
-	{
-		temp = y;
-		y = x-y;
-		x = temp;
-		if (y > x)
-			std::swap(x,y);
-	}
-	return x;
-}
+
 HeroMoveDetails::HeroMoveDetails(int3 Src, int3 Dst, CGHeroInstance*Ho)
 	:src(Src),dst(Dst),ho(Ho)
 {
@@ -186,12 +173,18 @@ const CGTownInstance * CCallback::getTownInfo(int val, bool mode) const //mode =
 
 bool CCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown &dest ) const
 {
-	const CGTownInstance *t = dynamic_cast<const CGTownInstance *>(town);
-	if(!t || !isVisible(t, player)) //it's not a town or it's not visible for layer
+	if(!isVisible(town, player)) //it's not a town or it's not visible for layer
 		return false;
 
+	bool detailed = hasAccess(town->tempOwner);
+
 	//TODO vision support, info about allies
-	dest.initFromTown(t, false);
+	if(town->ID == 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);
+	else
+		return false;
 	return true;
 }
 
@@ -245,7 +238,7 @@ bool CCallback::getHeroInfo( const CGObjectInstance *hero, InfoAboutHero &dest )
 		return false;
 	
 	//TODO vision support, info about allies
-	dest.initFromHero(h, false);
+	dest.initFromHero(h, hasAccess(h->tempOwner));
 	return true;
 }
 
@@ -963,6 +956,11 @@ void CCallback::castSpell(const CGHeroInstance *hero, int spellID, const int3 &p
 	sendRequest(&cas);
 }
 
+bool CCallback::hasAccess(int playerId) const
+{
+	return playerId == player  ||  player < 0;
+}
+
 InfoAboutTown::InfoAboutTown()
 {
 	tType = NULL;
@@ -1004,3 +1002,24 @@ void InfoAboutTown::initFromTown( const CGTownInstance *t, bool detailed )
 		}
 	}*/
 }
+
+void InfoAboutTown::initFromGarrison(const CGGarrison *garr, bool detailed)
+{
+	obj = garr;
+	fortLevel = 0;
+	army = garr->army;
+	name = CGI->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;
+	}
+}

+ 3 - 0
CCallback.h

@@ -42,6 +42,7 @@ struct CPackForServer;
 class CMapHeader;
 struct CGPathNode;
 struct CGPath;
+class CGGarrison;
 
 struct InfoAboutTown
 {
@@ -65,6 +66,7 @@ struct InfoAboutTown
 	InfoAboutTown();
 	~InfoAboutTown();
 	void initFromTown(const CGTownInstance *t, bool detailed);
+	void initFromGarrison(const CGGarrison *garr, bool detailed);
 };
 
 class ICallback
@@ -201,6 +203,7 @@ private:
 	template <typename T> void sendRequest(const T*request);
 
 protected:
+	bool hasAccess(int playerId) const;
 	int player;
 
 public:

+ 1 - 4
client/AdventureMapButton.cpp

@@ -152,10 +152,7 @@ void AdventureMapButton::clickLeft(tribool down, bool previousState)
 void AdventureMapButton::clickRight(tribool down, bool previousState)
 {
 	if(down && helpBox.size()) //there is no point to show window with nothing inside...
-		if(LOCPLINT)
-			adventureInt->handleRightClick(helpBox,down,this);
-		else
-			GH.pushInt(new CRClickPopupInt(CMessage::genWindow(helpBox, 0),true));
+		CRClickPopup::createAndPush(helpBox);
 }
 
 void AdventureMapButton::hover (bool on)

+ 368 - 416
client/CAdvmapInterface.cpp

@@ -27,6 +27,7 @@
 #include <sstream>
 #include "CPreGame.h"
 #include "../lib/VCMI_Lib.h"
+#include "../hch/CSpellHandler.h"
 
 #ifdef _MSC_VER
 #pragma warning (disable : 4355)
@@ -272,7 +273,7 @@ void CMinimap::updateRadar()
 
 void CMinimap::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(rcText,down,this);
+	adventureInt->handleRightClick(rcText,down);
 }
 
 void CMinimap::clickLeft(tribool down, bool previousState)
@@ -473,96 +474,12 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
 {
 	if ((down==false) || indeterminate(down))
 		return;
+
 	int3 mp = whichTileIsIt();
 	if (mp.x<0 || mp.y<0 || mp.x >= LOCPLINT->cb->getMapSize().x || mp.y >= LOCPLINT->cb->getMapSize().y)
 		return;
 
-	std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mp),  //blocking objects at tile
-		vobjs = LOCPLINT->cb->getVisitableObjs(mp); //visitable objects
-
-	if (adventureInt->selection->ID != HEROI_TYPE) //hero is not selected (presumably town)
-	{
-		if(currentPath)
-		{
-			tlog2<<"Warning: Lost path?" << std::endl;
-			//delete currentPath;
-			currentPath = NULL;
-		}
-
-		for(size_t i=0; i < bobjs.size(); ++i)
-		{
-			if(bobjs[i]->ID == TOWNI_TYPE && bobjs[i]->getOwner() == LOCPLINT->playerID) //our town clicked
-			{
-				if(adventureInt->selection == (bobjs[i])) //selected town clicked
-					LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(bobjs[i]));
-				else
-					adventureInt->select(static_cast<const CArmedInstance*>(bobjs[i]));
-
-				return;
-			}
-			else if(bobjs[i]->ID == HEROI_TYPE && bobjs[i]->tempOwner == LOCPLINT->playerID) //hero clicked - select him
-			{
-				adventureInt->select(static_cast<const CArmedInstance*>(bobjs[i]));
-				return;
-			}
-		}
-	}
-
-	else //hero is selected
-	{
-		bool townEntrance = false; //town entrance tile has been clicked?
-		const CGHeroInstance * currentHero = static_cast<const CGHeroInstance*>(adventureInt->selection);
-
-		for(size_t i=0; i < vobjs.size(); ++i)
-		{
-			if(vobjs[i]->ID == TOWNI_TYPE)
-				townEntrance = true;
-		}
-
-		if(!townEntrance) //not entrance - select town or open hero window
-		{
-			for(size_t i=0; i < bobjs.size(); ++i)
-			{
-				const CGObjectInstance *o = bobjs[i];
-				const CGPathNode *pn = LOCPLINT->cb->getPathInfo(mp);
-				if(  ((o->ID == HEROI_TYPE && pn->turns == 255)  //inaccessible hero
-							|| o->ID == TOWNI_TYPE)										   //or town
-					&& o->tempOwner == LOCPLINT->playerID) //but must belong to us
-				{
-					adventureInt->select(static_cast<const CArmedInstance*>(o));
-					return;
-				}
-				else if(o->ID == HEROI_TYPE //it's a hero
-					&& o->tempOwner == LOCPLINT->playerID  //our hero (is this condition needed?)
-					&& currentHero == (o) ) //and selected one 
-				{
-					LOCPLINT->openHeroWindow(currentHero);
-					return;
-				}
-			}
-		}
-		else if(currentHero == vobjs.back()) //selected hero is standing at the town entrance
-		{
-			LOCPLINT->openHeroWindow(currentHero);
-			return;
-		}
-
-		//still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
-		if (currentPath  &&  currentPath->endPos() == mp)//we'll be moving
-		{
-			LOCPLINT->pim->unlock();
-			LOCPLINT->moveHero(currentHero,*currentPath);
-			LOCPLINT->pim->lock();
-		}
-		else if(mp.z == currentHero->pos.z) //remove old path and find a new one if we clicked on the map level on which hero is present
-		{
-			int3 bufpos = currentHero->getPosition(false);
-			CGPath &path = LOCPLINT->paths[currentHero];
-			currentPath = &path;
-			if(!LOCPLINT->cb->getPath2(mp, path))
-				LOCPLINT->eraseCurrentPathOf(currentHero);
-		}
-	} //end of hero is selected "case"
+	adventureInt->tileLClicked(mp);
 }
 void CTerrainRect::clickRight(tribool down, bool previousState)
 {
@@ -571,118 +488,7 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
 	if (!CGI->mh->map->isInTheMap(mp) || down != true)
 		return;
 
-	std::vector < const CGObjectInstance * > objs = LOCPLINT->cb->getBlockingObjs(mp);
-	if(!objs.size()) 
-	{
-		// Bare or undiscovered terrain
-		const TerrainTile * tile = LOCPLINT->cb->getTileInfo(mp);
-		if (tile) 
-		{
-			std::string hlp;
-			CGI->mh->getTerrainDescr(mp, hlp, true);
-			CSimpleWindow * temp = CMessage::genWindow(hlp, LOCPLINT->playerID, true);
-			CRClickPopupInt *rcpi = new CRClickPopupInt(temp,true);
-			GH.pushInt(rcpi);
-		}
-		return;
-	}
-
-	const CGObjectInstance * obj = objs.back();
-	switch(obj->ID)
-	{
-	case HEROI_TYPE:
-		{
-			if(!vstd::contains(graphics->heroWins,obj->subID) || obj->tempOwner != LOCPLINT->playerID)
-			{
-				InfoAboutHero iah;
-				if(LOCPLINT->cb->getHeroInfo(obj, iah))
-				{
-					SDL_Surface *iwin = graphics->drawHeroInfoWin(iah);
-					CInfoPopup * ip = new CInfoPopup(iwin, GH.current->motion.x-iwin->w,
-													  GH.current->motion.y-iwin->h, true);
-					GH.pushInt(ip);
-				}
-				else
-				{
-					tlog3 << "Warning - no infowin for hero " << obj->id << std::endl;
-				}
-			}
-			else
-			{
-				CInfoPopup * ip = new CInfoPopup(graphics->heroWins[obj->subID],
-					GH.current->motion.x-graphics->heroWins[obj->subID]->w,
-					GH.current->motion.y-graphics->heroWins[obj->subID]->h,false
-					);
-				GH.pushInt(ip);
-			}
-			break;
-		}
-	case TOWNI_TYPE:
-		{
-			if(!vstd::contains(graphics->townWins,obj->id) || obj->tempOwner != LOCPLINT->playerID)
-			{
-				InfoAboutTown iah;
-				if(LOCPLINT->cb->getTownInfo(obj, iah))
-				{
-					SDL_Surface *iwin = graphics->drawTownInfoWin(iah);
-					CInfoPopup * ip = new CInfoPopup(iwin, GH.current->motion.x - iwin->w/2,
-						GH.current->motion.y - iwin->h/2, true);
-					GH.pushInt(ip);
-				}
-				else
-				{
-					tlog3 << "Warning - no infowin for town " << obj->id << std::endl;
-				}
-			}
-			else
-			{
-				CInfoPopup * ip = new CInfoPopup(graphics->townWins[obj->id],
-					GH.current->motion.x - graphics->townWins[obj->id]->w/2,
-					GH.current->motion.y - graphics->townWins[obj->id]->h/2,false
-					);
-				GH.pushInt(ip);
-			}
-			break;
-		}
-	case 33: // Garrison
-	case 219:
-		{
-			const CGGarrison *garr = dynamic_cast<const CGGarrison *>(obj);
-
-			if (garr != NULL) {
-				InfoAboutTown iah;
-
-				iah.obj = garr;
-				iah.fortLevel = 0;
-				iah.army = garr->army;
-				iah.name = VLC->generaltexth->names[33]; // "Garrison"
-				iah.owner = garr->tempOwner;
-				iah.built = false;
-				iah.tType = NULL;
-
-				// Show detailed info only to owning player.
-				if (garr->tempOwner == LOCPLINT->playerID) {
-					iah.details = new InfoAboutTown::Details;
-					iah.details->customRes = false;
-					iah.details->garrisonedHero = false;
-					iah.details->goldIncome = -1;
-					iah.details->hallLevel = -1;
-				}
-
-				SDL_Surface *iwin = graphics->drawTownInfoWin(iah);
-				CInfoPopup * ip = new CInfoPopup(iwin,
-					GH.current->motion.x - iwin->w/2,
-					GH.current->motion.y - iwin->h/2, true);
-				GH.pushInt(ip);
-			}
-			break;
-		}
-	default:
-		{
-			adventureInt->handleRightClick(obj->getHoverText(),down,this);
-			break;
-		}
-	}
+	adventureInt->tileRClicked(mp);
 }
 void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 {
@@ -700,163 +506,7 @@ void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 	else
 		return;
 
-	std::vector<std::string> temp = LOCPLINT->cb->getObjDescriptions(pom);
-	if (temp.size())
-	{
-		boost::replace_all(temp.back(),"\n"," ");
-		adventureInt->statusbar.print(temp.back());
-	}
-	else
-	{
-		std::string hlp;
-		CGI->mh->getTerrainDescr(pom, hlp, false);
-		adventureInt->statusbar.print(hlp);
-		//adventureInt->statusbar.clear();
-	}
-
-	const CGPathNode *pnode = LOCPLINT->cb->getPathInfo(pom);
-	std::vector<const CGObjectInstance *> objs = LOCPLINT->cb->getBlockingObjs(pom); 
-	const CGObjectInstance *obj = objs.size() ? objs.back() : NULL;
-	bool accessible  =  pnode->turns < 255;
-
-	int turns = pnode->turns;
-	amin(turns, 3);
-
-	if(adventureInt->selection)
-	{
-		if(adventureInt->selection->ID == TOWNI_TYPE)
-		{
-			if(obj && obj->tempOwner == LOCPLINT->playerID)
-			{
-				if(obj->ID == TOWNI_TYPE)
-				{
-					CGI->curh->changeGraphic(0, 3);
-				}
-				else if(obj->ID == HEROI_TYPE)
-				{
-					CGI->curh->changeGraphic(0, 2);
-				}
-			}
-			else
-			{
-				CGI->curh->changeGraphic(0, 0);
-			}
-		}
-		else if(adventureInt->selection->ID == HEROI_TYPE)
-		{
-			const CGHeroInstance *h = static_cast<const CGHeroInstance *>(adventureInt->selection);
-			if(obj)
-			{
-				if(obj->ID == HEROI_TYPE)
-				{
-					if(obj->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies
-					{
-						if(accessible)
-							CGI->curh->changeGraphic(0, 5 + turns*6);
-						else
-							CGI->curh->changeGraphic(0, 0);
-					}
-					else //our hero
-					{
-						if(adventureInt->selection == obj)
-							CGI->curh->changeGraphic(0, 2);
-						else if(accessible)
-							CGI->curh->changeGraphic(0, 8 + turns*6);
-						else
-							CGI->curh->changeGraphic(0, 2);
-					}
-				}
-				else if(obj->ID == TOWNI_TYPE)
-				{
-					if(obj->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies
-					{
-						if(accessible) {
-							const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(obj);
-
-							// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
-							if (townObj && townObj->army.slots.empty())
-								CGI->curh->changeGraphic(0, 9 + turns*6);
-							else
-								CGI->curh->changeGraphic(0, 5 + turns*6);
-								
-						} else {
-							CGI->curh->changeGraphic(0, 0);
-						}
-					}
-					else //our town
-					{
-						if(accessible)
-							CGI->curh->changeGraphic(0, 9 + turns*6);
-						else
-							CGI->curh->changeGraphic(0, 3);
-					}
-				}
-				else if(obj->ID == 54) //monster
-				{
-					if(accessible)
-						CGI->curh->changeGraphic(0, 5 + turns*6);
-					else
-						CGI->curh->changeGraphic(0, 0);
-				}
-				else if(obj->ID == 8) //boat
-				{
-					if(accessible)
-						CGI->curh->changeGraphic(0, 6 + turns*6);
-					else
-						CGI->curh->changeGraphic(0, 0);
-				}
-				else if (obj->ID == 33 || obj->ID == 219) // Garrison
-				{
-					if (accessible) {
-						const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(obj);
-
-						// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
-						if (garrObj && garrObj->tempOwner != LOCPLINT->playerID
-							&& !garrObj->army.slots.empty())
-						{
-							CGI->curh->changeGraphic(0, 5 + turns*6);
-						}
-						else
-						{
-							CGI->curh->changeGraphic(0, 9 + turns*6);
-						}
-					} else {
-						CGI->curh->changeGraphic(0, 0);
-					}
-				}
-				else
-				{
-					if(accessible)
-					{
-						if(pnode->land)
-							CGI->curh->changeGraphic(0, 9 + turns*6);
-						else
-							CGI->curh->changeGraphic(0, 28 + turns);
-					}
-					else
-						CGI->curh->changeGraphic(0, 0);
-				}
-			} 
-			else //no objs 
-			{
-				if(accessible)
-				{
-					if(pnode->land)
-					{
-						if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
-							CGI->curh->changeGraphic(0, 4 + turns*6);
-						else
-							CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
-					}
-					else
-						CGI->curh->changeGraphic(0, 6 + turns*6);
-				}
-				else
-					CGI->curh->changeGraphic(0, 0);
-			}
-		}
-	}
-	//tlog1 << "Tile " << pom << ": Turns=" << (int)pnode->turns <<"  Move:=" << pnode->moveRemains <</* " (from  "  << ")" << */std::endl;
+	adventureInt->tileHovered(curHoveredTile);
 }
 void CTerrainRect::hover(bool on)
 {
@@ -1321,6 +971,7 @@ CInfoBar::CInfoBar()
 	week2 = CDefHandler::giveDef("NEWWEEK2.DEF");
 	week3 = CDefHandler::giveDef("NEWWEEK3.DEF");
 	week4 = CDefHandler::giveDef("NEWWEEK4.DEF");
+	selInfoWin = NULL;
 }
 CInfoBar::~CInfoBar()
 {
@@ -1329,8 +980,12 @@ CInfoBar::~CInfoBar()
 	delete week2;
 	delete week3;
 	delete week4;
+
+	if(selInfoWin)
+		SDL_FreeSurface(selInfoWin);
 }
-void CInfoBar::draw(SDL_Surface * to, const CGObjectInstance * specific)
+
+void CInfoBar::showAll(SDL_Surface * to)
 {
 	if ((mode>=0) && mode<5)
 	{
@@ -1340,24 +995,11 @@ void CInfoBar::draw(SDL_Surface * to, const CGObjectInstance * specific)
 	else if (mode==5)
 	{
 		mode = -1;
-		draw(to,adventureInt->selection);
 	}
-	if (!specific)
-		specific = adventureInt->selection;
-
-	if(!specific)
-		return;
 
-	if(specific->ID == HEROI_TYPE) //hero
-	{
-		if(graphics->heroWins.find(specific->subID)!=graphics->heroWins.end())
-			blitAt(graphics->heroWins[specific->subID],pos.x,pos.y,to);
-	}
-	else if (specific->ID == TOWNI_TYPE)
+	if(selInfoWin)
 	{
-		const CGTownInstance * t = static_cast<const CGTownInstance*>(specific);
-		if(graphics->townWins.find(t->id)!=graphics->townWins.end())
-			blitAt(graphics->townWins[t->id],pos.x,pos.y,to);
+		blitAt(selInfoWin, pos.x, pos.y, to);
 	}
 }
 
@@ -1450,7 +1092,7 @@ void CInfoBar::showComp(SComponent * comp, int time)
 
 void CInfoBar::tick()
 {
-	if((mode >= 0) && (mode < 5))
+	if(mode >= 0  &&  mode < 5)
 	{
 		pom++;
 		if (pom >= getAnim(mode)->ourImages.size())
@@ -1458,20 +1100,19 @@ void CInfoBar::tick()
 			deactivateTimer();
 			toNextTick = -1;
 			mode = 5;
-			draw(screen2);
+			showAll(screen2);
 			return;
 		}
 		toNextTick = 150;
 		blitAnim(mode);
 	}
-	else if (mode == 6)
+	else if(mode == 6)
 	{
 		deactivateTimer();
 		toNextTick = -1;
 		mode = 5;
-		draw(screen2);
+		showAll(screen2);
 	}
-
 }
 
 void CInfoBar::show( SDL_Surface * to )
@@ -1491,6 +1132,13 @@ void CInfoBar::deactivate()
 		deactivateTimer();
 }
 
+void CInfoBar::updateSelection(const CGObjectInstance *obj)
+{
+	if(selInfoWin)
+		SDL_FreeSurface(selInfoWin);
+	selInfoWin = LOCPLINT->infoWin(obj);
+}
+
 CAdvMapInt::CAdvMapInt()
 :statusbar(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG),
 kingOverview(CGI->generaltexth->zelp[293].first,CGI->generaltexth->zelp[293].second,
@@ -1526,6 +1174,7 @@ endTurn(CGI->generaltexth->zelp[302].first,CGI->generaltexth->zelp[302].second,
 heroList(ADVOPT.hlistSize),
 townList(ADVOPT.tlistSize,ADVOPT.tlistX,ADVOPT.tlistY,ADVOPT.tlistAU,ADVOPT.tlistAD)//(5,&genRect(192,48,747,196),747,196,747,372),
 {
+	spellBeingCasted = NULL;
 	player = 0;
 	pos.x = pos.y = 0;
 	pos.w = screen->w;
@@ -1593,24 +1242,21 @@ void CAdvMapInt::fsleepWake()
 {
 }
 void CAdvMapInt::fmoveHero()
-{
-	if (selection->ID!=HEROI_TYPE)
-		return;
-	if (!terrain.currentPath)
+{	
+	const CGHeroInstance *h = curHero();
+	if (!h || !terrain.currentPath)
 		return;
 
-	LOCPLINT->pim->unlock();
-	LOCPLINT->moveHero(static_cast<const CGHeroInstance*>(adventureInt->selection),*terrain.currentPath);
-	LOCPLINT->pim->lock();
+	LOCPLINT->moveHero(h, *terrain.currentPath);
 }
 
 void CAdvMapInt::fshowSpellbok()
 {
-	if (selection->ID!=HEROI_TYPE) //checking necessary values
+	if (!curHero()) //checking necessary values
 		return;
 
-
-	CSpellWindow * spellWindow = new CSpellWindow(genRect(595, 620, (conf.cc.resx - 620)/2, (conf.cc.resy - 595)/2), (static_cast<const CGHeroInstance*>(adventureInt->selection)), LOCPLINT, false);
+	centerOn(selection);
+	CSpellWindow * spellWindow = new CSpellWindow(genRect(595, 620, (conf.cc.resx - 620)/2, (conf.cc.resy - 595)/2), curHero(), LOCPLINT, false);
 	GH.pushInt(spellWindow);
 }
 
@@ -1736,7 +1382,7 @@ void CAdvMapInt::showAll(SDL_Surface *to)
 
 	statusbar.show(to);
 
-	infoBar.draw(to);
+	infoBar.showAll(to);
 	LOCPLINT->cingconsole->show(to);
 }
 void CAdvMapInt::show(SDL_Surface *to)
@@ -1802,10 +1448,6 @@ void CAdvMapInt::selectionChanged()
 }
 void CAdvMapInt::centerOn(int3 on)
 {
-	// TODO:convertPosition should not belong to CGHeroInstance, and it
-	// should be split in 2 methods.
-	on = CGHeroInstance::convertPosition(on, false);
-
 	on.x -= CGI->mh->frameW;
 	on.y -= CGI->mh->frameH;
 	
@@ -1818,10 +1460,20 @@ void CAdvMapInt::centerOn(int3 on)
 	if(GH.topInt() == this)
 		underground.redraw();
 }
+
+void CAdvMapInt::centerOn(const CGObjectInstance *obj)
+{
+	centerOn(obj->getSightCenter());
+}
+
 void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 {
 	ui8 Dir = 0;
 	int k = key.keysym.sym;
+	const CGHeroInstance *h = curHero(); //selected hero
+	const CGTownInstance *t = curTown(); //selected town
+
+
 	switch(k)
 	{
 	case SDLK_i: 
@@ -1834,7 +1486,6 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 		return;
 	case SDLK_d: 
 		{
-			const CGHeroInstance *h = dynamic_cast<const CGHeroInstance*>(selection);
 			if(h && isActive() && key.state == SDL_PRESSED)
 				LOCPLINT->tryDiggging(h);
 			return;
@@ -1847,7 +1498,6 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 		{
 			if(!isActive()) 
 				return;
-			const CGHeroInstance *h = dynamic_cast<const CGHeroInstance*>(selection);
 			if(h && key.state == SDL_PRESSED)
 			{
 				LOCPLINT->pim->unlock();
@@ -1860,10 +1510,10 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 		{
 			if(!isActive() || !selection || key.state != SDL_PRESSED) 
 				return;
-			if(selection->ID == HEROI_TYPE)
-				LOCPLINT->openHeroWindow(static_cast<const CGHeroInstance*>(selection));
-			else if(selection->ID == TOWNI_TYPE)
-				LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(selection));
+			if(h)
+				LOCPLINT->openHeroWindow(h);
+			else if(t)
+				LOCPLINT->openTownWindow(t);
 			return;
 		}
 	case SDLK_t:
@@ -1919,12 +1569,12 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 			if(k < 0 || k > 8 || key.state != SDL_PRESSED)
 				return;
 
+			if(!h) 
+				break;
 
-			const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(selection);
-			if(!h) break;
 			if(k == 4)
 			{
-				centerOn(h->getPosition(false));
+				centerOn(h);
 				return;
 			}
 
@@ -1940,9 +1590,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 
 			if(!path.nodes[0].turns)
 			{
-				LOCPLINT->pim->unlock();
 				LOCPLINT->moveHero(h, path);
-				LOCPLINT->pim->lock();
 			}
 		}
 
@@ -1955,13 +1603,11 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 	else
 		scrollingDir &= ~Dir;
 }
-void CAdvMapInt::handleRightClick(std::string text, tribool down, CIntObject * client)
+void CAdvMapInt::handleRightClick(std::string text, tribool down)
 {
-	if (down)
+	if(down)
 	{
-		CSimpleWindow * temp = CMessage::genWindow(text,LOCPLINT->playerID,true);
-		CRClickPopupInt *rcpi = new CRClickPopupInt(temp,true);
-		GH.pushInt(rcpi);
+		CRClickPopup::createAndPush(text);
 	}
 }
 int3 CAdvMapInt::verifyPos(int3 ver)
@@ -1981,12 +1627,12 @@ int3 CAdvMapInt::verifyPos(int3 ver)
 	return ver;
 }
 
-void CAdvMapInt::select(const CArmedInstance *sel )
+void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/)
 {
 	LOCPLINT->cb->setSelection(sel);
-
-	centerOn(sel->pos);
 	selection = sel;
+	if(centerView)
+		centerOn(sel);
 
 	terrain.currentPath = NULL;
 	if(sel->ID==TOWNI_TYPE)
@@ -2009,7 +1655,8 @@ void CAdvMapInt::select(const CArmedInstance *sel )
 	}
 	townList.draw(screen);
 	heroList.draw(screen);
-	infoBar.draw(screen);
+	infoBar.updateSelection(sel);
+	infoBar.showAll(screen);
 }
 
 void CAdvMapInt::mouseMoved( const SDL_MouseMotionEvent & sEvent )
@@ -2090,10 +1737,317 @@ void CAdvMapInt::startTurn()
 	state = INGAME;
 }
 
+void CAdvMapInt::tileLClicked(const int3 &mp)
+{
+	std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mp),  //blocking objects at tile
+		vobjs = LOCPLINT->cb->getVisitableObjs(mp); //visitable objects
+	const TerrainTile *tile = LOCPLINT->cb->getTileInfo(mp);
+	const CGObjectInstance *topBlocking = bobjs.size() ? bobjs.back() : NULL;
+
+
+	int3 selPos = selection->getSightCenter();
+	if(spellBeingCasted && isInScreenRange(selPos, mp))
+	{
+		const TerrainTile *heroTile = LOCPLINT->cb->getTileInfo(selPos);
+
+		switch(spellBeingCasted->id)
+		{
+		case Spells::SCUTTLE_BOAT: //Scuttle Boat 
+			if(topBlocking && topBlocking->ID == 8)
+				leaveCastingMode(true, mp);
+			break;
+		case Spells::DIMENSION_DOOR:
+			if(!tile || tile->isClear(heroTile))
+				leaveCastingMode(true, mp);
+			break;
+		}
+		return;
+	}
+
+	if (selection->ID != HEROI_TYPE) //hero is not selected (presumably town)
+	{
+		assert(!terrain.currentPath); //path can be active only when hero is selected
+		if(selection == topBlocking) //selected town clicked
+			LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
+		else if(topBlocking && (topBlocking->ID == TOWNI_TYPE || topBlocking->ID == HEROI_TYPE) && topBlocking->tempOwner == LOCPLINT->playerID) //our town/hero clicked
+			select(static_cast<const CArmedInstance*>(topBlocking), false);
+	}
+	else if(const CGHeroInstance * currentHero = curHero()) //hero is selected
+	{
+		const CGPathNode *pn = LOCPLINT->cb->getPathInfo(mp);
+		if(currentHero == topBlocking) //clicked selected hero
+		{
+			LOCPLINT->openHeroWindow(currentHero);
+		}
+		else if(topBlocking && (topBlocking->ID == HEROI_TYPE || topBlocking->ID == TOWNI_TYPE) //clicked our town or hero
+			&& pn->turns == 255 && topBlocking->tempOwner == LOCPLINT->playerID) //at inaccessible tile
+		{
+			select(static_cast<const CArmedInstance*>(topBlocking), false);
+		}
+		else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
+		{
+			if (terrain.currentPath  &&  terrain.currentPath->endPos() == mp)//we'll be moving
+			{
+				LOCPLINT->moveHero(currentHero,*terrain.currentPath);
+			}
+			else if(mp.z == currentHero->pos.z) //remove old path and find a new one if we clicked on the map level on which hero is present
+			{
+				CGPath &path = LOCPLINT->paths[currentHero];
+				terrain.currentPath = &path;
+				if(!LOCPLINT->cb->getPath2(mp, path)) //try getting path, erase if failed
+					LOCPLINT->eraseCurrentPathOf(currentHero);
+			}
+		}
+	} //end of hero is selected "case"
+	else
+	{
+		throw std::string("Nothing is selected...");
+	}
+}
+
+void CAdvMapInt::tileHovered(const int3 &tile)
+{
+	std::vector<std::string> temp = LOCPLINT->cb->getObjDescriptions(tile);
+	if (temp.size())
+	{
+		boost::replace_all(temp.back(),"\n"," ");
+		statusbar.print(temp.back());
+	}
+	else
+	{
+		std::string hlp;
+		CGI->mh->getTerrainDescr(tile, hlp, false);
+		statusbar.print(hlp);
+	}
+
+	const CGPathNode *pnode = LOCPLINT->cb->getPathInfo(tile);
+	std::vector<const CGObjectInstance *> objs = LOCPLINT->cb->getBlockingObjs(tile); 
+	const CGObjectInstance *objAtTile = objs.size() ? objs.back() : NULL;
+	bool accessible  =  pnode->turns < 255;
+
+	int turns = pnode->turns;
+	amin(turns, 3);
+
+	if(!selection) //may occur just at the start of game (fake move before full intiialization)
+		return;
+
+	if(spellBeingCasted)
+	{
+		switch(spellBeingCasted->id)
+		{
+		case Spells::SCUTTLE_BOAT:
+			if(objAtTile && objAtTile->ID == 8)
+				CGI->curh->changeGraphic(0, 42);
+			else
+				CGI->curh->changeGraphic(0, 0);
+			return;
+		case Spells::DIMENSION_DOOR:
+			{
+				const TerrainTile *t = LOCPLINT->cb->getTileInfo(tile);
+				int3 hpos = selection->getSightCenter();
+				if((!t  ||  t->isClear(LOCPLINT->cb->getTileInfo(hpos)))   &&   isInScreenRange(hpos, tile))
+					CGI->curh->changeGraphic(0, 41);
+				else
+					CGI->curh->changeGraphic(0, 0);
+				return;
+			}
+		}
+	}
+
+	if(selection->ID == TOWNI_TYPE)
+	{
+		if(objAtTile && objAtTile->tempOwner == LOCPLINT->playerID)
+		{
+			if(objAtTile->ID == TOWNI_TYPE)
+				CGI->curh->changeGraphic(0, 3);
+			else if(objAtTile->ID == HEROI_TYPE)
+				CGI->curh->changeGraphic(0, 2);
+		}
+		else
+			CGI->curh->changeGraphic(0, 0);
+	}
+	else if(const CGHeroInstance *h = curHero())
+	{
+		if(objAtTile)
+		{
+			if(objAtTile->ID == HEROI_TYPE)
+			{
+				if(objAtTile->tempOwner != LOCPLINT->playerID) //enemy hero TODO: allies
+				{
+					if(accessible)
+						CGI->curh->changeGraphic(0, 5 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 0);
+				}
+				else //our hero
+				{
+					if(selection == objAtTile)
+						CGI->curh->changeGraphic(0, 2);
+					else if(accessible)
+						CGI->curh->changeGraphic(0, 8 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 2);
+				}
+			}
+			else if(objAtTile->ID == TOWNI_TYPE)
+			{
+				if(objAtTile->tempOwner != LOCPLINT->playerID) //enemy town TODO: allies
+				{
+					if(accessible) 
+					{
+						const CGTownInstance* townObj = dynamic_cast<const CGTownInstance*>(objAtTile);
+
+						// Show movement cursor for unguarded enemy towns, otherwise attack cursor.
+						if (townObj && townObj->army.slots.empty())
+							CGI->curh->changeGraphic(0, 9 + turns*6);
+						else
+							CGI->curh->changeGraphic(0, 5 + turns*6);
+
+					} 
+					else 
+					{
+						CGI->curh->changeGraphic(0, 0);
+					}
+				}
+				else //our town
+				{
+					if(accessible)
+						CGI->curh->changeGraphic(0, 9 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 3);
+				}
+			}
+			else if(objAtTile->ID == 54) //monster
+			{
+				if(accessible)
+					CGI->curh->changeGraphic(0, 5 + turns*6);
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+			else if(objAtTile->ID == 8) //boat
+			{
+				if(accessible)
+					CGI->curh->changeGraphic(0, 6 + turns*6);
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+			else if (objAtTile->ID == 33 || objAtTile->ID == 219) // Garrison
+			{
+				if (accessible) 
+				{
+					const CGGarrison* garrObj = dynamic_cast<const CGGarrison*>(objAtTile); //TODO evil evil cast!
+
+					// Show battle cursor for guarded enemy garrisons, otherwise movement cursor.
+					if (garrObj  &&  garrObj->tempOwner != LOCPLINT->playerID  &&  !garrObj->army.slots.empty())
+						CGI->curh->changeGraphic(0, 5 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 9 + turns*6);
+				} 
+				else 
+					CGI->curh->changeGraphic(0, 0);
+			}
+			else
+			{
+				if(accessible)
+				{
+					if(pnode->land)
+						CGI->curh->changeGraphic(0, 9 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 28 + turns);
+				}
+				else
+					CGI->curh->changeGraphic(0, 0);
+			}
+		} 
+		else //no objs 
+		{
+			if(accessible)
+			{
+				if(pnode->land)
+				{
+					if(LOCPLINT->cb->getTileInfo(h->getPosition(false))->tertype != TerrainTile::water)
+						CGI->curh->changeGraphic(0, 4 + turns*6);
+					else
+						CGI->curh->changeGraphic(0, 7 + turns*6); //anchor
+				}
+				else
+					CGI->curh->changeGraphic(0, 6 + turns*6);
+			}
+			else
+				CGI->curh->changeGraphic(0, 0);
+		}
+	}
+}
+
+void CAdvMapInt::tileRClicked(const int3 &mp)
+{
+	if(spellBeingCasted)
+	{
+		leaveCastingMode();
+		LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
+		return;
+	}
+
+	std::vector < const CGObjectInstance * > objs = LOCPLINT->cb->getBlockingObjs(mp);
+	if(!objs.size()) 
+	{
+		// Bare or undiscovered terrain
+		const TerrainTile * tile = LOCPLINT->cb->getTileInfo(mp);
+		if (tile) 
+		{
+			std::string hlp;
+			CGI->mh->getTerrainDescr(mp, hlp, true);
+			CRClickPopup::createAndPush(hlp);
+		}
+		return;
+	}
+
+	const CGObjectInstance * obj = objs.back();
+	CRClickPopup::createAndPush(obj, GH.current->motion, CENTER);
+}
+
+void CAdvMapInt::enterCastingMode(const CSpell * sp)
+{
+	using namespace Spells;
+	assert(sp->id == SCUTTLE_BOAT  ||  sp->id == DIMENSION_DOOR);
+	spellBeingCasted = sp;
+
+	deactivate();
+	terrain.activate();
+	GH.fakeMouseMove();
+}
+
+void CAdvMapInt::leaveCastingMode(bool cast /*= false*/, int3 dest /*= int3(-1, -1, -1)*/)
+{
+	assert(spellBeingCasted);
+	int id = spellBeingCasted->id;
+	spellBeingCasted = NULL;
+	terrain.deactivate();
+	activate();
+
+	LOCPLINT->cb->castSpell(curHero(), id, dest);
+}
+
+const CGHeroInstance * CAdvMapInt::curHero() const
+{
+	if(selection && selection->ID == HEROI_TYPE)
+		return static_cast<const CGHeroInstance *>(selection);
+	else
+		return NULL;
+}
+
+const CGTownInstance * CAdvMapInt::curTown() const
+{
+	if(selection && selection->ID == TOWNI_TYPE)
+		return static_cast<const CGTownInstance *>(selection);
+	else
+		return NULL;
+}
+
 CAdventureOptions::CAdventureOptions()
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	bg = new CPicture(BitmapHandler::loadBitmap("ADVOPTS.bmp"), 0, 0);
+	bg = new CPicture("ADVOPTS.bmp");
 	graphics->blueToPlayersAdv(bg->bg, LOCPLINT->playerID);
 	pos = bg->center();
 	exit = new AdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
@@ -2106,13 +2060,11 @@ CAdventureOptions::CAdventureOptions()
 	puzzle = new AdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 81, "ADVPUZ.DEF");
 	puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
 
-	const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection);
 	dig = new AdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 139, "ADVDIG.DEF");
-	if(h)
+	if(const CGHeroInstance *h = adventureInt->curHero())
 		dig->callback += boost::bind(&CPlayerInterface::tryDiggging, LOCPLINT, h);
 	else
 		dig->block(true);
-
 }
 
 CAdventureOptions::~CAdventureOptions()

+ 16 - 5
client/CAdvmapInterface.h

@@ -13,6 +13,7 @@ class CAdvMapInt;
 class CGHeroInstance;
 class CGTownInstance;
 class CHeroWindow;
+class CSpell;
 /*****************************/
 
 /*
@@ -116,18 +117,20 @@ public:
 	SComponent * current;
 	int mode;
 	int pom;
+	SDL_Surface *selInfoWin; //info box for selection
 
 	CInfoBar();
 	~CInfoBar();
 	void newDay(int Day); //start showing new day/week animation
 	void showComp(SComponent * comp, int time=5000);
 	void tick();
-	void draw(SDL_Surface * to, const CGObjectInstance * specific=NULL); // if specific==0 function draws info about selected hero/town
+	void showAll(SDL_Surface * to); // if specific==0 function draws info about selected hero/town
 	void blitAnim(int mode);//0 - day, 1 - week
 	CDefHandler * getAnim(int mode);
 	void show(SDL_Surface * to);
 	void activate();
 	void deactivate();
+	void updateSelection(const CGObjectInstance *obj);
 };
 /*****************************/
 class CAdvMapInt : public CIntObject //adventure map interface
@@ -166,13 +169,13 @@ public:
 		endTurn;//- end turn
 
 	CTerrainRect terrain; //visible terrain
-
 	CResDataBar resdatabar;
-
 	CHeroList heroList;
 	CTownList townList;
 	CInfoBar infoBar;
 
+	const CSpell *spellBeingCasted; //NULL if none
+
 	CHeroWindow * heroWindow;
 
 	const CArmedInstance *selection; //currently selected town/hero
@@ -195,11 +198,12 @@ public:
 	void show(SDL_Surface * to); //redraws terrain
 	void showAll(SDL_Surface * to); //shows and activates adv. map interface
 
-	void select(const CArmedInstance *sel);
+	void select(const CArmedInstance *sel, bool centerView = true);
 	void selectionChanged();
 	void centerOn(int3 on);
+	void centerOn(const CGObjectInstance *obj);
 	int3 verifyPos(int3 ver);
-	void handleRightClick(std::string text, tribool down, CIntObject * client);
+	void handleRightClick(std::string text, tribool down);
 	void keyPressed(const SDL_KeyboardEvent & key);
 	void mouseMoved (const SDL_MouseMotionEvent & sEvent);
 	bool isActive();
@@ -207,6 +211,13 @@ public:
 	void setPlayer(int Player);
 	void startHotSeatWait(int Player);
 	void startTurn();
+	void tileLClicked(const int3 &mp);
+	void tileHovered(const int3 &tile);
+	void tileRClicked(const int3 &mp);
+	void enterCastingMode(const CSpell * sp);
+	void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1));
+	const CGHeroInstance * curHero() const;
+	const CGTownInstance * curTown() const;
 };
 
 extern CAdvMapInt *adventureInt;

+ 6 - 1
client/CCursorHandler.cpp

@@ -164,7 +164,12 @@ void CCursorHandler::shiftPos( int &x, int &y )
 				y -= 16;
 			}
 		}
-		else if(number < 31)
+		else if(number == 41)
+		{
+			x -= 14;
+			y -= 16;
+		}
+		else if(number < 31 || number == 42)
 		{
 			x -= 20;
 			y -= 20;

+ 8 - 0
client/CMT.cpp

@@ -77,6 +77,7 @@ std::queue<SDL_Event*> events;
 boost::mutex eventsM;
 
 static bool gOnlyAI = false;
+static bool setResolution = false; //set by event handling thread after resolution is adjusted
 
 void processCommand(const std::string &message);
 static void setScreenRes(int w, int h, int bpp, bool fullscreen);
@@ -483,6 +484,7 @@ static void listenForEvents()
 		else if(ev->type == SDL_USEREVENT && ev->user.code == 1)
 		{
 			setScreenRes(conf.cc.resx,conf.cc.resy,conf.cc.bpp,conf.cc.fullscreen);
+			setResolution = true;
 			delete ev;
 			continue;
 		}
@@ -540,5 +542,11 @@ void startGame(StartInfo * options)
 	}
 
 	CGI->musich->stopMusic();
+
+	//allow event handling thread change resolution
+	eventsM.unlock();
+	while(!setResolution) boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+	eventsM.lock();
+
 	client->connectionHandler = new boost::thread(&CClient::run, client);
 }

+ 75 - 139
client/CPlayerInterface.cpp

@@ -133,14 +133,6 @@ CPlayerInterface::~CPlayerInterface()
 		cingconsole->deactivate();
 	delete cingconsole;
 
-	for(std::map<int,SDL_Surface*>::iterator i=graphics->heroWins.begin(); i!= graphics->heroWins.end(); i++)
-		SDL_FreeSurface(i->second);
-	graphics->heroWins.clear();
-
-	for(std::map<int,SDL_Surface*>::iterator i=graphics->townWins.begin(); i!= graphics->townWins.end(); i++)
-		SDL_FreeSurface(i->second);
-	graphics->townWins.clear();
-
 	LOCPLINT = NULL;
 }
 void CPlayerInterface::init(ICallback * CB)
@@ -149,13 +141,6 @@ void CPlayerInterface::init(ICallback * CB)
 	if(!adventureInt)
 		adventureInt = new CAdvMapInt();
 
-	std::vector<const CGTownInstance*> tt = cb->getTownsInfo(false);
-	for(int i=0;i<tt.size();i++)
-	{
-		SDL_Surface * pom = infoWin(tt[i]);
-		graphics->townWins.insert(std::pair<int,SDL_Surface*>(tt[i]->id,pom));
-	}
-
 	if(!towns.size() && !wanderingHeroes.size())
 	{
 		recreateHeroTownList();
@@ -248,7 +233,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	const CGHeroInstance * ho = cb->getHeroInfo(details.id); //object representing this hero
 	int3 hp = details.start;
 
-	adventureInt->centerOn(ho->pos); //actualizing screen pos
+	adventureInt->centerOn(ho); //actualizing screen pos
 	adventureInt->minimap.draw(screen2);
 	adventureInt->heroList.draw(screen2);
 
@@ -336,15 +321,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	graphics->heroWins.erase(hero->ID);
 	wanderingHeroes -= hero;
 	adventureInt->heroList.updateHList(hero);
 }
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	if(graphics->heroWins.find(hero->subID)==graphics->heroWins.end())
-		graphics->heroWins.insert(std::pair<int,SDL_Surface*>(hero->subID,infoWin(hero)));
 	wanderingHeroes.push_back(hero);
 	adventureInt->heroList.updateHList();
 }
@@ -356,38 +338,31 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town)
 
 SDL_Surface * CPlayerInterface::infoWin(const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
 {
-	if (specific)
+	if(!specific)
+		specific = adventureInt->selection;
+
+	assert(specific);
+
+	switch(specific->ID)
 	{
-		switch (specific->ID)
+	case HEROI_TYPE: 				
 		{
-		case HEROI_TYPE:
-			return graphics->drawHeroInfoWin(dynamic_cast<const CGHeroInstance*>(specific));
-			break;
-		case TOWNI_TYPE:
-			return graphics->drawTownInfoWin(dynamic_cast<const CGTownInstance*>(specific));
-			break;
-		default:
-			return NULL;
-			break;
+			InfoAboutHero iah;
+			bool gotInfo = LOCPLINT->cb->getHeroInfo(specific, iah);
+			assert(gotInfo);
+			return graphics->drawHeroInfoWin(iah);
 		}
-	}
-	else
-	{
-		switch (adventureInt->selection->ID)
+	case TOWNI_TYPE:
+	case 33: // Garrison
+	case 219:
 		{
-		case HEROI_TYPE:
-			{
-				const CGHeroInstance * curh = (const CGHeroInstance *)adventureInt->selection;
-				return graphics->drawHeroInfoWin(curh);
-			}
-		case TOWNI_TYPE:
-			{
-				return graphics->drawTownInfoWin((const CGTownInstance *)adventureInt->selection);
-			}
-		default:
-			tlog1 << "Strange... selection is neither hero nor town\n";
-			return NULL;
+			InfoAboutTown iah;
+			bool gotInfo = LOCPLINT->cb->getTownInfo(specific, iah);
+			assert(gotInfo);
+			return graphics->drawTownInfoWin(iah);
 		}
+	default:
+		return NULL;
 	}
 }
 
@@ -408,12 +383,12 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
 	if(which >= PRIMARY_SKILLS) //no need to redraw infowin if this is experience (exp is treated as prim skill with id==4)
 		return;
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	redrawHeroWin(hero);
+	updateInfo(hero);
 }
 void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	redrawHeroWin(hero);
+	updateInfo(hero);
 }
 void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
 {
@@ -439,10 +414,7 @@ void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, int pskill, std:
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	//redraw infowindow
-	SDL_FreeSurface(graphics->townWins[town->id]);
-	graphics->townWins[town->id] = infoWin(town);
-
+	updateInfo(town);
 
 	if(town->garrisonHero && vstd::contains(wanderingHeroes,town->garrisonHero)) //wandering hero moved to the garrison
 	{
@@ -479,30 +451,7 @@ void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownIn
 void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	if(obj->ID == HEROI_TYPE) //hero
-	{
-		const CGHeroInstance * hh;
-		if(hh = dynamic_cast<const CGHeroInstance*>(obj))
-		{
-			SDL_FreeSurface(graphics->heroWins[hh->subID]);
-			graphics->heroWins[hh->subID] = infoWin(hh);
-		}
-	}
-	else if (obj->ID == TOWNI_TYPE) //town
-	{
-		const CGTownInstance * tt;
-		if(tt = static_cast<const CGTownInstance*>(obj))
-		{
-			SDL_FreeSurface(graphics->townWins[tt->id]);
-			graphics->townWins[tt->id] = infoWin(tt);
-		}
-		if(tt->visitingHero)
-		{
-			SDL_FreeSurface(graphics->heroWins[tt->visitingHero->subID]);
-			graphics->heroWins[tt->visitingHero->subID] = infoWin(tt->visitingHero);
-		}
-
-	}
+	updateInfo(obj);
 
 	bool wasGarrison = false;
 	for(std::list<IShowActivable*>::iterator i = GH.listInt.begin(); i != GH.listInt.end(); i++)
@@ -530,12 +479,10 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, int buildingID,
 	switch (buildingID)
 	{
 	case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 15:
-		{
-			SDL_FreeSurface(graphics->townWins[town->id]);
-			graphics->townWins[town->id] = infoWin(town);
-			break;
-		}
+		updateInfo(town);
+		break;
 	}
+
 	if(!castleInt)
 		return;
 	if(castleInt->town!=town)
@@ -983,7 +930,7 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const HeroB
 {
 	if(bonus.type == HeroBonus::NONE)	return;
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	redrawHeroWin(hero);
+	updateInfo(hero);
 }
 
 template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, const int version )
@@ -1004,72 +951,63 @@ void CPlayerInterface::serialize( CISer<CLoadFile> &h, const int version )
 	firstCall = -1;
 }
 
-void CPlayerInterface::redrawHeroWin(const CGHeroInstance * hero)
-{
-	if(!vstd::contains(graphics->heroWins,hero->subID))
-	{
-		tlog1 << "Cannot redraw infowindow for hero with subID=" << hero->subID << " - not present in our map\n";
-		return;
-	}
-	SDL_FreeSurface(graphics->heroWins[hero->subID]);
-	graphics->heroWins[hero->subID] = infoWin(hero);
-	if (adventureInt->selection == hero)
-		adventureInt->infoBar.draw(screen);
-}
-
 bool CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
 {
 	if (!h)
 		return false; //can't find hero
 
+	pim->unlock();
 	bool result = false;
-	path.convert(0);
-	boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
-	stillMoveHero.data = CONTINUE_MOVE;
-
-	enum TerrainTile::EterrainType currentTerrain = TerrainTile::border; // not init yet
-	enum TerrainTile::EterrainType newTerrain;
-	int sh = -1;
 
-	for(int i=path.nodes.size()-1; i>0 && stillMoveHero.data == CONTINUE_MOVE; i--)
 	{
-		//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
-		if(path.nodes[i-1].turns)
+		path.convert(0);
+		boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
+		stillMoveHero.data = CONTINUE_MOVE;
+
+		enum TerrainTile::EterrainType currentTerrain = TerrainTile::border; // not init yet
+		enum TerrainTile::EterrainType newTerrain;
+		int sh = -1;
+
+		for(int i=path.nodes.size()-1; i>0 && stillMoveHero.data == CONTINUE_MOVE; i--)
 		{
-			stillMoveHero.data = STOP_MOVE;
-			break;
-		}
-		// Start a new sound for the hero movement or let the existing one carry on.
+			//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
+			if(path.nodes[i-1].turns)
+			{
+				stillMoveHero.data = STOP_MOVE;
+				break;
+			}
+			// Start a new sound for the hero movement or let the existing one carry on.
 #if 0
-		// TODO
-		if (hero is flying && sh == -1)
-			sh = CGI->soundh->playSound(soundBase::horseFlying, -1);
+			// TODO
+			if (hero is flying && sh == -1)
+				sh = CGI->soundh->playSound(soundBase::horseFlying, -1);
 #endif
-		{
-			newTerrain = cb->getTileInfo(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->tertype;
+			{
+				newTerrain = cb->getTileInfo(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->tertype;
 
-			if (newTerrain != currentTerrain) {
-				CGI->soundh->stopSound(sh);
-				sh = CGI->soundh->playSound(CGI->soundh->horseSounds[newTerrain], -1);
-				currentTerrain = newTerrain;
+				if (newTerrain != currentTerrain) {
+					CGI->soundh->stopSound(sh);
+					sh = CGI->soundh->playSound(CGI->soundh->horseSounds[newTerrain], -1);
+					currentTerrain = newTerrain;
+				}
 			}
-		}
 
-		stillMoveHero.data = WAITING_MOVE;
+			stillMoveHero.data = WAITING_MOVE;
 
-		int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z);
-		cb->moveHero(h,endpos);
+			int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z);
+			cb->moveHero(h,endpos);
 
-		eventsM.unlock();
-		while(stillMoveHero.data != STOP_MOVE  &&  stillMoveHero.data != CONTINUE_MOVE)
-			stillMoveHero.cond.wait(un);
-		eventsM.lock();
-	}
+			eventsM.unlock();
+			while(stillMoveHero.data != STOP_MOVE  &&  stillMoveHero.data != CONTINUE_MOVE)
+				stillMoveHero.cond.wait(un);
+			eventsM.lock();
+		}
 
-	CGI->soundh->stopSound(sh);
+		CGI->soundh->stopSound(sh);
+		cb->recalculatePaths();
+	}
 
-	//stillMoveHero = false;
-	cb->recalculatePaths();
+	pim->lock();
 	return result;
 }
 
@@ -1905,15 +1843,6 @@ void CPlayerInterface::acceptTurn()
 		adventureInt->startTurn();
 
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	for(std::map<int,SDL_Surface*>::iterator i=graphics->heroWins.begin(); i!=graphics->heroWins.end();i++) //redraw hero infoboxes
-		SDL_FreeSurface(i->second);
-	graphics->heroWins.clear();
-	std::vector <const CGHeroInstance *> hh = cb->getHeroesInfo(false);
-	for(int i=0;i<hh.size();i++)
-	{
-		SDL_Surface * pom = infoWin(hh[i]);
-		graphics->heroWins.insert(std::pair<int,SDL_Surface*>(hh[i]->subID,pom));
-	}
 
 	/* TODO: This isn't quite right. First day in game should play
 	 * NEWDAY. And we don't play NEWMONTH. */
@@ -1952,3 +1881,10 @@ void CPlayerInterface::tryDiggging(const CGHeroInstance *h)
 			cb->dig(h);
 	}
 }
+
+void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
+{
+	adventureInt->infoBar.updateSelection(specific);
+// 	if (adventureInt->selection == specific)
+// 		adventureInt->infoBar.showAll(screen);
+}

+ 1 - 0
client/CPlayerInterface.h

@@ -207,6 +207,7 @@ public:
 	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(ICallback * CB);
 	int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
 	void showInfoDialog(const std::string &text, const std::vector<SComponent*> & components = std::vector<SComponent*>(), int soundID = 0);

+ 1 - 3
client/CPreGame.cpp

@@ -2467,9 +2467,7 @@ void CBonusSelection::CRegion::clickRight( tribool down, bool previousState )
 	if( down && !CSDL_Ext::isTransparent(graphics[0], GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) &&
 		rclickText.size() )
 	{
-		CSimpleWindow * temp = CMessage::genWindow(rclickText, 0, true);
-		CRClickPopupInt *rcpi = new CRClickPopupInt(temp, true);
-		GH.pushInt(rcpi);
+		CRClickPopup::createAndPush(rclickText);
 	}
 }
 

+ 18 - 2
client/CSpellWindow.cpp

@@ -51,7 +51,7 @@ void SpellbookInteractiveArea::clickLeft(tribool down, bool previousState)
 
 void SpellbookInteractiveArea::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(textOnRclick, down, this);
+	adventureInt->handleRightClick(textOnRclick, down);
 }
 
 void SpellbookInteractiveArea::hover(bool on)
@@ -688,13 +688,14 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 			}
 			else
 			{
+				using namespace Spells;
 				int spell = mySpell;
 				const CGHeroInstance *h = owner->myHero;
 				owner->fexitb();
 
 				switch(spell)
 				{
-				case 0: //summon boat
+				case SUMMON_BOAT:
 					{
 						int3 pos = h->bestLocation();
 						if(pos.x < 0)
@@ -703,6 +704,21 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 							return;
 						}
 					}
+					break;
+				case SCUTTLE_BOAT:
+				case DIMENSION_DOOR:
+					adventureInt->enterCastingMode(sp);
+					return;
+				case VISIONS:
+				case VIEW_EARTH:
+				case DISGUISE:
+				case VIEW_AIR:
+				case FLY:
+				case WATER_WALK:
+				case TOWN_PORTAL:
+					break;
+				default:
+					assert(0);
 				}
 
 				LOCPLINT->cb->castSpell(h, spell);

+ 4 - 1
client/Client.cpp

@@ -313,7 +313,10 @@ int CClient::getCurrentPlayer()
 
 int CClient::getSelectedHero()
 {
-	return IGameCallback::getSelectedHero(getCurrentPlayer())->id;
+	if(const CGHeroInstance *selHero = IGameCallback::getSelectedHero(getCurrentPlayer()))
+		return selHero->id;
+	else
+		return -1;
 }
 
 void CClient::newGame( CConnection *con, StartInfo *si )

+ 5 - 0
client/GUIBase.h

@@ -71,6 +71,9 @@ struct Point
 	Point(const int3 &a)
 		:x(a.x),y(a.y)
 	{}
+	Point(const SDL_MouseMotionEvent &a)
+		:x(a.x),y(a.y)
+	{}
 	
 	template<typename T>
 	Point operator+(const T &b) const
@@ -365,6 +368,8 @@ public:
 	ui8 defActions; //which calls will be tried to be redirected to children
 	ui8 recActions; //which calls we allow te receive from parent
 
+	enum EAlignment {TOPLEFT, CENTER, BOTTOMRIGHT};
+
 	void disable(); //deactivates if needed, blocks all automatic activity, allows only disposal
 	void enable(bool activation = true); //activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
 	void defActivate();

+ 71 - 41
client/GUIClasses.cpp

@@ -770,21 +770,55 @@ void CRClickPopup::close()
 	GH.popIntTotally(this);
 }
 
+void CRClickPopup::createAndPush(const std::string &txt)
+{
+	int player = LOCPLINT ? LOCPLINT->playerID : 1; //if no player, then use blue
+	CSimpleWindow * temp = CMessage::genWindow(txt,player,true);
+	CRClickPopupInt *rcpi = new CRClickPopupInt(temp,true);
+	GH.pushInt(rcpi);
+}
+
+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
+	if(iWin)
+		GH.pushInt(new CInfoPopup(iWin, p, alignment, true));
+	else
+		CRClickPopup::createAndPush(obj->getHoverText());
+}
+
+CRClickPopup::CRClickPopup()
+{
+}
+
+CRClickPopup::~CRClickPopup()
+{
+}
+
 CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free)
  :free(Free),bitmap(Bitmap)
 {
-	CGI->curh->hide();
+	init(x, y);
+}
 
-	pos.x = x;
-	pos.y = y;
-	pos.h = bitmap->h;
-	pos.w = bitmap->w;
 
-	// Put the window back on screen if necessary
-	amax(pos.x, 0);
-	amax(pos.y, 0);
-	amin(pos.x, conf.cc.resx - bitmap->w);
-	amin(pos.y, conf.cc.resy - bitmap->h);
+CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free/*=false*/)
+ : free(Free),bitmap(Bitmap)
+{
+	switch(alignment)
+	{
+	case BOTTOMRIGHT:
+		init(p.x - Bitmap->w, p.y - Bitmap->h);
+		break;
+	case CENTER:
+		init(p.x - Bitmap->w/2, p.y - Bitmap->h/2);
+		break;
+	case TOPLEFT:
+		init(p.x, p.y);
+		break;
+	default:
+		assert(0); //not implemented
+	}
 }
 
 CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
@@ -818,6 +852,22 @@ CInfoPopup::~CInfoPopup()
 	CGI->curh->show();
 }
 
+void CInfoPopup::init(int x, int y)
+{
+	CGI->curh->hide();
+
+	pos.x = x;
+	pos.y = y;
+	pos.h = bitmap->h;
+	pos.w = bitmap->w;
+
+	// Put the window back on screen if necessary
+	amax(pos.x, 0);
+	amax(pos.y, 0);
+	amin(pos.x, conf.cc.resx - bitmap->w);
+	amin(pos.y, conf.cc.resy - bitmap->h);
+}
+
 void SComponent::init(Etype Type, int Subtype, int Val)
 {
 	std::ostringstream oss;
@@ -961,7 +1011,7 @@ SDL_Surface * SComponent::getImg()
 void SComponent::clickRight(tribool down, bool previousState)
 {
 	if(description.size())
-		adventureInt->handleRightClick(description,down,this);
+		adventureInt->handleRightClick(description,down);
 }
 void SComponent::activate()
 {
@@ -1277,7 +1327,7 @@ void CHeroList::select(int which)
 		adventureInt->selection = NULL;
 		adventureInt->terrain.currentPath = NULL;
 		draw(screen);
-		adventureInt->infoBar.draw(screen);
+		adventureInt->infoBar.showAll(screen);
 	}
 	if (which>=LOCPLINT->wanderingHeroes.size())
 		return;
@@ -1397,11 +1447,11 @@ void CHeroList::clickRight(tribool down, bool previousState)
 		/***************************ARROWS*****************************************/
 		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y) && from>0)
 		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[303].second,down,this);
+			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,this);
+			adventureInt->handleRightClick(CGI->generaltexth->zelp[304].second,down);
 		}
 		else
 		{
@@ -1413,21 +1463,11 @@ void CHeroList::clickRight(tribool down, bool previousState)
 			if ((ny>SIZE || ny<0) || (from+ny>=LOCPLINT->wanderingHeroes.size()))
 			{
 				return;
-			}
+			}			//show popup
 
-			//show popup
-			CInfoPopup * ip = new CInfoPopup(graphics->heroWins[LOCPLINT->wanderingHeroes[from+ny]->subID],
-				GH.current->motion.x-graphics->heroWins[LOCPLINT->wanderingHeroes[from+ny]->subID]->w,
-				GH.current->motion.y-graphics->heroWins[LOCPLINT->wanderingHeroes[from+ny]->subID]->h,
-				false);
-			GH.pushInt(ip);
+			CRClickPopup::createAndPush(LOCPLINT->wanderingHeroes[from+ny], GH.current->motion);
 		}
 	}
-	else
-	{
-		adventureInt->handleRightClick(CGI->generaltexth->zelp[303].second,down,this);
-		adventureInt->handleRightClick(CGI->generaltexth->zelp[304].second,down,this);
-	}
 }
 
 void CHeroList::hover (bool on)
@@ -1692,11 +1732,11 @@ void CTownList::clickRight(tribool down, bool previousState)
 		/***************************ARROWS*****************************************/
 		if(isItIn(&arrupp,GH.current->motion.x,GH.current->motion.y) && from>0)
 		{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[306].second,down,this);
+			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,this);
+			adventureInt->handleRightClick(CGI->generaltexth->zelp[307].second,down);
 		}
 		//if not buttons then towns
 		int hx = GH.current->motion.x, hy = GH.current->motion.y;
@@ -1709,17 +1749,7 @@ void CTownList::clickRight(tribool down, bool previousState)
 		}
 
 		//show popup
-		CInfoPopup * ip = new CInfoPopup(
-			graphics->townWins[LOCPLINT->towns[from+ny]->id],
-			GH.current->motion.x-graphics->townWins[LOCPLINT->towns[from+ny]->id]->w,
-			GH.current->motion.y-graphics->townWins[LOCPLINT->towns[from+ny]->id]->h,
-			false);
-		GH.pushInt(ip);
-	}
-	else
-	{
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[306].second,down,this);
-			adventureInt->handleRightClick(CGI->generaltexth->zelp[307].second,down,this);
+		CRClickPopup::createAndPush(LOCPLINT->towns[from+ny], GH.current->motion);
 	}
 }
 
@@ -3912,7 +3942,7 @@ void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
 }
 void LRClickableAreaWText::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(text, down, this);
+	adventureInt->handleRightClick(text, down);
 }
 void LRClickableAreaWText::activate()
 {
@@ -3950,7 +3980,7 @@ void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
 }
 void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(text, down, this);
+	adventureInt->handleRightClick(text, down);
 }
 void LRClickableAreaWTextComp::activate()
 {

+ 11 - 2
client/GUIClasses.h

@@ -63,6 +63,7 @@ class CArtifact;
 class CArtifactsOfHero;
 class CResDataBar;
 struct SPuzzleInfo;
+class CGGarrison;
 
 extern SDL_Color tytulowy, tlo, zwykly ;
 
@@ -103,7 +104,12 @@ public:
 	virtual void deactivate();
 	virtual void close();
 	void clickRight(tribool down, bool previousState);
-	virtual ~CRClickPopup(){}; //d-tor
+
+	CRClickPopup();
+	virtual ~CRClickPopup(); //d-tor
+
+	static void createAndPush(const std::string &txt);
+	static void createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment = BOTTOMRIGHT);
 };
 
 class CRClickPopupInt : public CRClickPopup //popup displayed on R-click
@@ -125,7 +131,10 @@ public:
 	void close();
 	void show(SDL_Surface * to);
 	CInfoPopup(SDL_Surface * Bitmap, int x, int y, bool Free=false); //c-tor
+	CInfoPopup(SDL_Surface * Bitmap, const Point &p, EAlignment alignment, bool Free=false); //c-tor
 	CInfoPopup(SDL_Surface *Bitmap = NULL, bool Free = false); //default c-tor
+
+	void init(int x, int y);
 	~CInfoPopup(); //d-tor
 };
 
@@ -265,7 +274,7 @@ class CLabel
 	: public virtual CIntObject
 {
 public:
-	enum EAlignment {TOPLEFT, CENTER, BOTTOMRIGHT} alignment;
+	EAlignment alignment;
 	EFonts font;
 	SDL_Color color;
 	std::string text;

+ 0 - 2
client/Graphics.h

@@ -64,8 +64,6 @@ public:
 	CDefEssential *luck22, *luck30, *luck42, *luck82,
 		*morale22, *morale30, *morale42, *morale82,
 		*halls, *forts, *bigTownPic;
-	std::map<int,SDL_Surface*> heroWins; //hero_ID => infobox
-	std::map<int,SDL_Surface*> townWins; //town_ID => infobox
 	CDefEssential * artDefs; //artifacts
 	std::vector<SDL_Surface *> portraitSmall; //48x32 px portraits of heroes
 	std::vector<SDL_Surface *> portraitLarge; //58x64 px portraits of heroes

+ 6 - 4
client/SDL_Extensions.cpp

@@ -1013,11 +1013,13 @@ void CSDL_Ext::setPlayerColor(SDL_Surface * sur, unsigned char player)
 		return;
 	if(sur->format->BitsPerPixel==8)
 	{
-		if(player != 255)
-			*(sur->format->palette->colors+5) = graphics->playerColors[player];
-		else
-			*(sur->format->palette->colors+5) = *graphics->neutralColor;
+		SDL_Color *color = (player == 255 
+							? graphics->neutralColor
+							: &graphics->playerColors[player]);
+		SDL_SetColors(sur, color, 5, 1);
 	}
+	else
+		tlog3 << "Warning, setPlayerColor called on not 8bpp surface!\n";
 }
 int readNormalNr (std::istream &in, int bytCon)
 {

+ 13 - 1
hch/CObjectHandler.cpp

@@ -23,6 +23,7 @@
 #include "../lib/map.h"
 #include <sstream>
 #include <SDL_stdinc.h>
+#include <boost/foreach.hpp>
 using namespace boost::assign;
 
 /*
@@ -1238,7 +1239,7 @@ int CGHeroInstance::getBoatType() const
 	case 2:
 		return 2;
 	default:
-		assert(0);
+		throw std::string("Wrong alignment!");
 	}
 }
 
@@ -1254,6 +1255,17 @@ int CGHeroInstance::getSpellCost(const CSpell *sp) const
 	return sp->costs[getSpellSchoolLevel(sp)];
 }
 
+int CGHeroInstance::getBonusesCount(int from, int id) const
+{
+	int ret = 0;
+
+	BOOST_FOREACH(const HeroBonus &hb, bonuses)
+		if(hb.source == from  &&  hb.id == id)
+			ret++;
+
+	return ret;
+}
+
 void CGDwelling::initObj()
 {
 	switch(ID)

+ 1 - 0
hch/CObjectHandler.h

@@ -313,6 +313,7 @@ public:
 
 	const BonusList *ownerBonuses() const;
 	const HeroBonus *getBonus(int from, int id) const;
+	int getBonusesCount(int from, int id) const;
 	int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
 	bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const; //determines if hero has a bonus of given type (and optionally subtype)
 	void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1) const; //out: pairs<modifier value, modifier description>

+ 9 - 0
hch/CSpellHandler.cpp

@@ -194,6 +194,15 @@ static bool startsWithX(const std::string &s)
 	return s.size() && s[0] == 'x';
 }
 
+bool DLL_EXPORT isInScreenRange(const int3 &center, const int3 &pos)
+{
+	int3 diff = pos - center;
+	if(diff.x >= -9  &&  diff.x <= 9  &&  diff.y >= -8  &&  diff.y <= 8)
+		return true;
+	else
+		return false;
+}
+
 void CSpellHandler::loadSpells()
 {
 	std::string buf = bitmaph->getTextFile("SPTRAITS.TXT"), pom;

+ 7 - 0
hch/CSpellHandler.h

@@ -51,6 +51,13 @@ public:
 	}
 };
 
+namespace Spells
+{
+	enum {SUMMON_BOAT=0, SCUTTLE_BOAT, VISIONS, VIEW_EARTH, DISGUISE, VIEW_AIR, FLY, WATER_WALK, DIMENSION_DOOR, TOWN_PORTAL};
+}
+
+bool DLL_EXPORT isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
+
 class DLL_EXPORT CSpellHandler
 {
 public:

+ 12 - 10
lib/CGameState.cpp

@@ -1988,12 +1988,16 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 		return false;
 
 	int3 hpos = hero->getPosition(false);
-	tribool blockLandSea; //true - blocks sea, false - blocks land, indeterminate - allows all
 
-	if (!hero->canWalkOnSea())
-		blockLandSea = (map->getTile(hpos).tertype != TerrainTile::water); //block land if hero is on water and vice versa
-	else
-		blockLandSea = boost::logic::indeterminate;
+	bool flying = false; //hero is under flying effect	TODO
+	bool waterWalking = false; //hero is on land and can walk on water TODO
+	bool onLand = map->getTile(hpos).tertype != TerrainTile::water;
+// 	tribool blockLandSea; //true - blocks sea, false - blocks land, indeterminate - allows all
+// 
+// 	if (!hero->canWalkOnSea())
+// 		blockLandSea = (map->getTile(hpos).tertype != TerrainTile::water); //block land if hero is on water and vice versa
+// 	else
+// 		blockLandSea = boost::logic::indeterminate;
 
 	const std::vector<std::vector<std::vector<ui8> > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap;
 
@@ -2016,9 +2020,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 			node.coord.y = j;
 			node.coord.z = dest.z;
 
-			if ((tinfo->tertype == TerrainTile::rock) //it's rock
-				|| ((blockLandSea) && (tinfo->tertype == TerrainTile::water)) //it's sea and we cannot walk on sea
-				|| ((!blockLandSea) && (tinfo->tertype != TerrainTile::water)) //it's land and we cannot walk on land
+			if(!tinfo->entrableTerrain(onLand, flying || waterWalking)
 				|| !FoW[i][j][src.z] //tile is covered by the FoW
 			)
 			{
@@ -2039,7 +2041,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 			d.accessible = true; //for allowing visiting objects
 		}
 
-		if(blockLandSea && t->tertype == TerrainTile::water) //hero can walk only on land and dst lays on the water
+		if(onLand && t->tertype == TerrainTile::water) //hero can walk only on land and dst lays on the water
 		{
 			size_t i = 0;
 			for(; i < t->visitableObjects.size(); i++)
@@ -2048,7 +2050,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 
 			d.accessible = (i < t->visitableObjects.size()); //dest is accessible only if there is boat/hero
 		}
-		else if(!blockLandSea && t->tertype != TerrainTile::water) //hero is moving by water
+		else if(!onLand && t->tertype != TerrainTile::water) //hero is moving by water
 		{
 			d.accessible = (t->siodmyTajemniczyBajt & 64) && !t->blocked; //tile is accessible if it's coastal and not blocked
 		}

+ 1 - 1
lib/HeroBonus.h

@@ -56,7 +56,7 @@ struct DLL_EXPORT HeroBonus
 		FREE_SHIP_BOARDING //movement points preserved with ship boarding and landing
 	};
 	enum BonusDuration{PERMANENT, ONE_BATTLE, ONE_DAY, ONE_WEEK};
-	enum BonusSource{ARTIFACT, OBJECT};
+	enum BonusSource{ARTIFACT, OBJECT, CASTED_SPELL};
 
 	ui8 duration; //uses BonusDuration values
 	ui8 type; //uses BonusType values - says to what is this bonus

+ 1 - 1
lib/NetPacks.h

@@ -476,7 +476,7 @@ struct TryMoveHero : public CPackForClient //501
 
 	ui32 id, movePoints;
 	ui8 result; //uses EResult
-	int3 start, end;
+	int3 start, end; //h3m format
 	std::set<int3> fowRevealed; //revealed tiles
 
 	bool humanKnows; //used locally during applying to client

+ 16 - 0
lib/map.cpp

@@ -2129,3 +2129,19 @@ CVictoryCondition::CVictoryCondition()
 	obj = NULL;
 	ID = allowNormalVictory = appliesToAI = count = 0;
 }
+
+bool TerrainTile::entrableTerrain(const TerrainTile *from /*= NULL*/) const
+{
+	return entrableTerrain(from ? from->tertype != water : true, from ? from->tertype == water : true);
+}
+
+bool TerrainTile::entrableTerrain(bool allowLand, bool allowSea) const
+{
+	return tertype != rock 
+		&& (allowSea && tertype == water   ||   allowLand && tertype != water);
+}
+
+bool TerrainTile::isClear(const TerrainTile *from /*= NULL*/) const
+{
+	return entrableTerrain(from) && !blocked;
+}

+ 4 - 0
lib/map.h

@@ -95,6 +95,10 @@ struct DLL_EXPORT TerrainTile
 			//these flags (and obj vectors) will be restored in map serialization
 		}
 	}
+
+	bool entrableTerrain(const TerrainTile *from = NULL) const; //checks if terrain is not a rock. If from is water/land, same type is also required. 
+	bool entrableTerrain(bool allowLand, bool allowSea) const; //checks if terrain is not a rock. If from is water/land, same type is also required. 
+	bool isClear(const TerrainTile *from = NULL) const; //checks for blocking objs and terraint type (water / land)
 };
 struct DLL_EXPORT SheroName //name of starting hero
 {

+ 75 - 10
server/CGameHandler.cpp

@@ -4213,9 +4213,10 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
 	if(s->combatSpell)
 		COMPLAIN_RET("This function can be used only for adventure map spells!");
 
+	using namespace Spells;
 	switch(spellID)
 	{
-	case 0: //Summon Boat 
+	case SUMMON_BOAT: //Summon Boat 
 		{
 			//check if spell works at all
 			if(rand() % 100 >= s->powers[schoolLevel]) //power is % chance of success
@@ -4277,15 +4278,79 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
 			break;
 		}
 
-	case 1: //Scuttle Boat 
-	case 2: //Visions 
-	case 3: //View Earth 
-	case 4: //Disguise 
-	case 5: //View Air 
-	case 6: //Fly 
-	case 7: //Water Walk 
-	case 8: //Dimension Door
-	case 9: //Town Portal 
+	case SCUTTLE_BOAT: //Scuttle Boat 
+		{
+			//check if spell works at all
+			if(rand() % 100 >= s->powers[schoolLevel]) //power is % chance of success
+			{
+				InfoWindow iw;
+				iw.player = h->tempOwner;
+				iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
+				iw.text.addReplacement(h->name);
+				sendAndApply(&iw);
+				return true; //TODO? or should it be false? request was correct and realized, but spell failed...
+			}
+
+			//TODO: test range, visibility
+			const TerrainTile *t = &gs->map->getTile(pos);
+			if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != 8)
+				COMPLAIN_RET("There is no boat to scuttle!");
+
+			RemoveObject ro;
+			ro.id = t->visitableObjects.back()->id;
+			sendAndApply(&ro);
+			break;
+		}
+	case DIMENSION_DOOR: //Dimension Door
+		{
+			const TerrainTile *dest = getTile(pos);
+			const TerrainTile *curr = getTile(h->getSightCenter());
+
+			if(!dest)
+				COMPLAIN_RET("Destination tile doesn't exist!");
+			if(!h->movement)
+				COMPLAIN_RET("Hero needs movement points to cast Dimension Door!");
+			if(h->getBonusesCount(HeroBonus::CASTED_SPELL, Spells::DIMENSION_DOOR) >= s->powers[schoolLevel]) //limit casts per turn
+			{
+				InfoWindow iw;
+				iw.player = h->tempOwner;
+				iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
+				iw.text.addReplacement(h->name);
+				sendAndApply(&iw);
+				break;
+			}
+
+			GiveBonus gb;
+			gb.id = h->id;
+			gb.bonus = HeroBonus(HeroBonus::ONE_DAY, HeroBonus::NONE, HeroBonus::CASTED_SPELL, 0, Spells::DIMENSION_DOOR);
+			sendAndApply(&gb);
+			
+			if(!dest->isClear(curr)) //wrong dest tile
+			{
+				InfoWindow iw;
+				iw.player = h->tempOwner;
+				iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
+				sendAndApply(&iw);
+				break;
+			}
+
+			TryMoveHero tmh;
+			tmh.id = h->id;
+			tmh.movePoints = std::max<int>(0, h->movement - 300);
+			tmh.result = TryMoveHero::TELEPORTATION;
+			tmh.start = h->pos;
+			tmh.end = pos;
+			getTilesInRange(tmh.fowRevealed, pos, h->getSightRadious(), h->tempOwner,1);
+			sendAndApply(&tmh);
+		}
+		break;
+	case VISIONS: //Visions 
+	case VIEW_EARTH: //View Earth 
+	case DISGUISE: //Disguise 
+	case VIEW_AIR: //View Air 
+	case FLY: //Fly 
+	case WATER_WALK: //Water Walk 
+	case TOWN_PORTAL: //Town Portal 
 	default:
 		COMPLAIN_RET("This spell is not implemented yet!");
 	}