Browse Source

* attacking and capturing towns
* r-click popups on selected town/hero.bonus and teams in pregame
* disabled music due to bugs in SDL_mixer and smpeg (probably it was the source of reported random crashes / hangups)
* minor fixes

Michał W. Urbańczyk 16 years ago
parent
commit
66dd31fd8a

+ 16 - 0
AI/GeniusAI/genius.vcproj

@@ -295,6 +295,14 @@
 				>
 			</File>
 		</Filter>
+		<File
+			RelativePath=".\AIPriorities.cpp"
+			>
+		</File>
+		<File
+			RelativePath=".\AIPriorities.h"
+			>
+		</File>
 		<File
 			RelativePath=".\CGeniusAI.cpp"
 			>
@@ -311,6 +319,14 @@
 			RelativePath=".\DLLMain.cpp"
 			>
 		</File>
+		<File
+			RelativePath=".\neuralNetwork.cpp"
+			>
+		</File>
+		<File
+			RelativePath=".\neuralNetwork.h"
+			>
+		</File>
 	</Files>
 	<Globals>
 	</Globals>

+ 31 - 6
client/CMT.cpp

@@ -129,8 +129,8 @@ void init()
 	CGI->soundh->init();
 	CGI->soundh->setVolume(GDefaultOptions.soundVolume);
 	CGI->musich = new CMusicHandler;
-	CGI->musich->init();
-	CGI->musich->setVolume(GDefaultOptions.musicVolume);
+	//CGI->musich->init();
+	//CGI->musich->setVolume(GDefaultOptions.musicVolume);
 	tlog0<<"\tInitializing sound: "<<pomtime.getDif()<<std::endl;
 	tlog0<<"Initializing screen, fonts and sound handling: "<<tmh.getDif()<<std::endl;
 
@@ -242,6 +242,10 @@ void processCommand(const std::string &message)
 			break;
 		}
 	}
+	else if(cn=="redraw")
+	{
+		GH.totalRedraw();
+	}
 	else if(cn=="screen")
 	{
 		tlog0 << "Screenbuf points to ";
@@ -386,6 +390,8 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen)
 		tlog1 << "Error: SDL says that " << w << "x" << h << " resolution is not available!\n";
 		return;
 	}
+
+	bool bufOnScreen = (screenBuf == screen);
 	
 	if(suggestedBpp != bpp)
 	{
@@ -406,12 +412,16 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen)
 		throw "Requested screen resolution is not available\n";
 	}
 
+	tlog0 << "New screen flags: " << screen->flags << std::endl;
+
 	if(screen2)
 		SDL_FreeSurface(screen2);
 	screen2 = CSDL_Ext::copySurface(screen);
 	SDL_EnableUNICODE(1);
 	SDL_WM_SetCaption(NAME.c_str(),""); //set window title
 	SDL_ShowCursor(SDL_DISABLE);
+
+	screenBuf = bufOnScreen ? screen : screen2;
 }
 
 void listenForEvents() 
@@ -421,26 +431,37 @@ void listenForEvents()
 	{
 		ev = new SDL_Event();
 
+		//tlog0 << "Waiting... ";
 		int ret = SDL_WaitEvent(ev);
-		if(ret == 0 || (ev->type==SDL_QUIT)  ||  (ev->type == SDL_KEYDOWN && ev->key.keysym.sym==SDLK_F4 && (ev->key.keysym.mod & KMOD_ALT)))
+		//tlog0 << "got " << (int)ev->type;
+		if(/*ret == 0 || */(ev->type==SDL_QUIT)  ||  (ev->type == SDL_KEYDOWN && ev->key.keysym.sym==SDLK_F4 && (ev->key.keysym.mod & KMOD_ALT)))
 		{
-			LOCPLINT->pim->lock();
+			if(LOCPLINT)
+				LOCPLINT->pim->lock();
 			client->close();
 			console->end();
 			SDL_Delay(750);
 			tlog0 << "Ending...\n";
 			exit(EXIT_SUCCESS);
 		}
-		else if(ev->type == SDL_KEYDOWN && ev->key.keysym.sym==SDLK_F4)
+		else if(LOCPLINT && ev->type == SDL_KEYDOWN && ev->key.keysym.sym==SDLK_F4)
 		{
 			boost::unique_lock<boost::recursive_mutex> lock(*LOCPLINT->pim);
 			bool full = !(screen->flags&SDL_FULLSCREEN);
 			setScreenRes(conf.cc.resx,conf.cc.resy,conf.cc.bpp,full);
 			GH.totalRedraw();
 		}
+		else if(ev->type == SDL_USEREVENT && ev->user.code == 1)
+		{
+			setScreenRes(conf.cc.resx,conf.cc.resy,conf.cc.bpp,conf.cc.fullscreen);
+			delete ev;
+			continue;
+		}
+		//tlog0 << " pushing ";
 		eventsM.lock();
 		events.push(ev);
 		eventsM.unlock();
+		//tlog0 << " done\n";
 	}
 }
 
@@ -456,7 +477,11 @@ void startGame(StartInfo * options)
 
 	if(screen->w != conf.cc.resx   ||   screen->h != conf.cc.resy)
 	{
-		setScreenRes(conf.cc.resx,conf.cc.resy,conf.cc.bpp,conf.cc.fullscreen);
+		//push special event to order event reading thread to change resolution
+		SDL_Event ev;
+		ev.type = SDL_USEREVENT;
+		ev.user.code = 1;
+		SDL_PushEvent(&ev);
 	}
 
 	CClient cl;

+ 6 - 6
client/CMessage.cpp

@@ -102,12 +102,12 @@ void CMessage::dispose()
 SDL_Surface * CMessage::drawBox1(int w, int h, int playerColor) //draws box for window
 {
 	//prepare surface
-	SDL_Surface * ret = SDL_CreateRGBSurface(screen->flags, w, h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
+	SDL_Surface * ret = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
 	for (int i=0; i<h; i+=background->h)//background
 	{
-		for (int j=0; j<w; j+=background->w-1)
+		for (int j=0; j<w; j+=background->w)
 		{
-			SDL_BlitSurface(background,&genRect(background->h,background->w-1,1,0),ret,&genRect(h,w,j,i)); //FIXME taking address of temporary
+			SDL_BlitSurface(background,&genRect(background->h,background->w,0,0),ret,&genRect(h,w,j,i)); //FIXME taking address of temporary
 		}
 	}
 	drawBorder(playerColor, ret, w, h);
@@ -515,7 +515,7 @@ void CMessage::drawBorder(int playerColor, SDL_Surface * ret, int w, int h, int
 		SDL_BlitSurface
 			(piecesOfBox[playerColor][6],NULL,ret,&genRect(piecesOfBox[playerColor][6]->h,piecesOfBox[playerColor][6]->w,x+i,y+0));
 		SDL_BlitSurface
-			(piecesOfBox[playerColor][7],NULL,ret,&genRect(piecesOfBox[playerColor][7]->h,piecesOfBox[playerColor][7]->w,x+i,y+h-piecesOfBox[playerColor][7]->h));
+			(piecesOfBox[playerColor][7],NULL,ret,&genRect(piecesOfBox[playerColor][7]->h,piecesOfBox[playerColor][7]->w,x+i,y+h-piecesOfBox[playerColor][7]->h+1));
 	}
 	//obwodka I-szego rzedu pionowa  //border of 1st series, vertical
 	for (int i=0; i<h-piecesOfBox[playerColor][4]->h; i+=piecesOfBox[playerColor][4]->h)
@@ -531,9 +531,9 @@ void CMessage::drawBorder(int playerColor, SDL_Surface * ret, int w, int h, int
 	SDL_BlitSurface
 		(piecesOfBox[playerColor][1],NULL,ret,&genRect(piecesOfBox[playerColor][1]->h,piecesOfBox[playerColor][1]->w,x+w-piecesOfBox[playerColor][1]->w,y+0));
 	SDL_BlitSurface
-		(piecesOfBox[playerColor][2],NULL,ret,&genRect(piecesOfBox[playerColor][2]->h,piecesOfBox[playerColor][2]->w,x+0,y+h-piecesOfBox[playerColor][2]->h));
+		(piecesOfBox[playerColor][2],NULL,ret,&genRect(piecesOfBox[playerColor][2]->h,piecesOfBox[playerColor][2]->w,x+0,y+h-piecesOfBox[playerColor][2]->h+1));
 	SDL_BlitSurface
-		(piecesOfBox[playerColor][3],NULL,ret,&genRect(piecesOfBox[playerColor][3]->h,piecesOfBox[playerColor][3]->w,x+w-piecesOfBox[playerColor][3]->w,y+h-piecesOfBox[playerColor][3]->h));
+		(piecesOfBox[playerColor][3],NULL,ret,&genRect(piecesOfBox[playerColor][3]->h,piecesOfBox[playerColor][3]->w,x+w-piecesOfBox[playerColor][3]->w,y+h-piecesOfBox[playerColor][3]->h+1));
 }
 
 ComponentResolved::ComponentResolved()

+ 3 - 2
client/CPlayerInterface.cpp

@@ -175,10 +175,8 @@ void CPlayerInterface::yourTurn()
 
 		while(makingTurn) // main loop
 		{
-			updateWater();
 			pim->lock();
 
-
 			//if there are any waiting dialogs, show them
 			if(dialogs.size() && !showingDialog->get())
 			{
@@ -1504,6 +1502,9 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 			if(cb->isVisible(*it))
 				adventureInt->minimap.showTile(*it);
 		}
+
+		if(obj->ID == TOWNI_TYPE)
+			adventureInt->townList.genList();
 	}
 
 }

+ 284 - 63
client/CPreGame.cpp

@@ -23,6 +23,8 @@
 #include "../hch/CMusicHandler.h"
 #include "../hch/CVideoHandler.h"
 #include "AdventureMapButton.h"
+#include "GUIClasses.h"
+#include "../hch/CCreatureHandler.h"
 /*
  * CPreGame.cpp, part of VCMI engine
  *
@@ -212,7 +214,6 @@ CSelectionScreen::CSelectionScreen( EState Type )
 		break;
 
 	case loadGame:
-		card->difficulty->select(current->seldiff, 0);
 		sel->recActions = 255;
 		start  = new AdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startGame, this), 414, 535, "SCNRLOD.DEF", SDLK_b);
 		break;
@@ -669,6 +670,7 @@ void SelectionTab::clickLeft( tribool down, bool previousState )
 InfoCard::InfoCard( EState Type )
 {
 	OBJ_CONSTRUCTION;
+	used = RCLICK;
 	sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
 	sFlags = CDefHandler::giveDef("ITGFLAGS.DEF");
 	type = Type;
@@ -779,10 +781,10 @@ void InfoCard::showAll( SDL_Surface * to )
 
 		//print flags
 		int fx=64, ex=244, myT;
-		if (curMap->howManyTeams)
+		//if (curMap->howManyTeams)
 			myT = curMap->players[playerColor].team;
-		else 
-			myT = -1;
+		//else 
+		//	myT = -1;
 		for (std::vector<PlayerSettings>::const_iterator i = curOpts->playerInfos.begin(); i != curOpts->playerInfos.end(); i++)
 		{
 			int *myx = ((i->color == playerColor  ||  curMap->players[i->color].team == myT) ? &fx : &ex);
@@ -815,12 +817,47 @@ void InfoCard::showAll( SDL_Surface * to )
 
 void InfoCard::changeSelection( const CMapInfo *to )
 {
-	//current = to;
+	if(type == loadGame)
+		difficulty->select(curMap->seldiff, 0);
 	GH.totalRedraw();
 }
 
-OptionsTab::OptionsTab( EState Type/*, StartInfo &Opts */)
-	//:opts(Opts)
+void InfoCard::clickRight( tribool down, bool previousState )
+{
+	static const Rect flagArea(19, 397, 335, 23);
+	if(down && isItInLoc(flagArea, GH.current->motion.x, GH.current->motion.y))
+		showTeamsPopup();
+}
+
+void InfoCard::showTeamsPopup()
+{
+	SDL_Surface *bmp = CMessage::drawBox1(256, 90 + 50 * curMap->howManyTeams);
+	CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[657], 128, 30, FONT_MEDIUM, tytulowy, bmp); //{Team Alignments}
+
+	for(int i = 0; i < curMap->howManyTeams; i++)
+	{
+		std::vector<ui8> flags;
+		std::string hlp = CGI->generaltexth->allTexts[656]; //Team %d
+		hlp.replace(hlp.find("%d"), 2, boost::lexical_cast<std::string>(i+1));
+		CSDL_Ext::printAtMiddle(hlp, 128, 65 + 50*i, FONT_SMALL, zwykly, bmp);
+
+		for(int j = 0; j < PLAYER_LIMIT; j++)
+			if((curMap->players[j].canHumanPlay || curMap->players[j].canComputerPlay)
+				&& curMap->players[j].team == i)
+				flags.push_back(j);
+
+		int curx = 128 - 9*flags.size();
+		for(int j = 0; j < flags.size(); j++)
+		{
+			blitAt(sFlags->ourImages[flags[j]].bitmap, curx, 75 + 50*i, bmp);
+			curx += 18;
+		}	
+	}
+
+	GH.pushInt(new CInfoPopup(bmp, true));
+}
+
+OptionsTab::OptionsTab( EState Type)
 {
 	OBJ_CONSTRUCTION;
 	bg = new CPicture(BitmapHandler::loadBitmap("ADVOPTBK.bmp"), 3, 6, true);
@@ -1068,12 +1105,13 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 	else
 		flag = NULL;
 
+	defActions &= ~SHARE_POS;
 	town = new SelectedBox(TOWN, s.serial);
-	town->pos = pos + Point(119, 2);
+	town->pos += pos + Point(119, 2);
 	hero = new SelectedBox(HERO, s.serial);
-	hero->pos = pos + Point(195, 2);
+	hero->pos += pos + Point(195, 2);
 	bonus = new SelectedBox(BONUS, s.serial);
-	bonus->pos = pos + Point(271, 2);
+	bonus->pos += pos + Point(271, 2);
 }
 
 void OptionsTab::PlayerOptionsEntry::showAll( SDL_Surface * to )
@@ -1104,70 +1142,57 @@ void OptionsTab::PlayerOptionsEntry::selectButtons(bool onlyHero)
 void OptionsTab::SelectedBox::showAll( SDL_Surface * to )
 {
 	PlayerSettings &s = curOpts->playerInfos[player];
-	SDL_Surface *toBlit = NULL;
-	const std::string *toPrint = NULL;
+	SDL_Surface *toBlit = getImg();
+	const std::string *toPrint = getText();
+	blitAt(toBlit, pos, to);
+	printAtMiddleLoc(*toPrint, 23, 39, FONT_TINY, zwykly, to);
+}
 
+OptionsTab::SelectedBox::SelectedBox( SelType Which, ui8 Player )
+:which(Which), player(Player)
+{
+	SDL_Surface *img = getImg();
+	pos.w = img->w;
+	pos.h = img->h;
+	used = RCLICK;
+}
+
+SDL_Surface * OptionsTab::SelectedBox::getImg() const
+{
+	PlayerSettings &s = curOpts->playerInfos[player];
 	switch(which)
 	{
 	case TOWN:
+		if (s.castle < F_NUMBER  &&  s.castle >= 0)
+			return graphics->getPic(s.castle, true, false);
+		else if (s.castle == -1)
+			return CGP->rTown;
+		else if (s.castle == -2)
+			return CGP->nTown;
+	case HERO:
+		if (s.hero == -1)
 		{
-			if (s.castle < F_NUMBER  &&  s.castle >= 0)
-			{
-				toBlit = graphics->getPic(s.castle, true, false);
-				toPrint = &CGI->townh->towns[s.castle].Name();
-			}
-			else if (s.castle == -1)
-			{
-				toBlit  = CGP->rTown;
-				toPrint = &CGI->generaltexth->allTexts[522];
-			}
-			else if (s.castle == -2)
-			{
-				toBlit  = CGP->nTown;
-				toPrint = &CGI->generaltexth->allTexts[523];
-			}
+			return CGP->rHero;
 		}
-		break;
-	case HERO:
+		else if (s.hero == -2)
 		{
-			if (s.hero == -1)
-			{
-				toBlit  = CGP->rHero;
-				toPrint = &CGI->generaltexth->allTexts[522];
-			}
-			else if (s.hero == -2)
-			{
-				if(s.heroPortrait >= 0)
-				{
-					toBlit = graphics->portraitSmall[s.heroPortrait];
-					if(s.heroName.length())
-						toPrint = &s.heroName;
-					else
-						toPrint = &CGI->heroh->heroes[s.heroPortrait]->name;
-				}
-				else
-				{
-					toBlit  = CGP->nHero;
-					toPrint = &CGI->generaltexth->allTexts[523];
-				}
-			}
+			if(s.heroPortrait >= 0)
+				return graphics->portraitSmall[s.heroPortrait];
 			else
-			{
-				toBlit = graphics->portraitSmall[s.hero];
-				toPrint = &s.heroName;
-			}
+				return CGP->nHero;
+		}
+		else
+		{
+			return graphics->portraitSmall[s.hero];
 		}
 		break;
 	case BONUS:
 		{
 			int pom;
-			toPrint = &CGI->generaltexth->arraytxt[214 + s.bonus];
-
 			switch (s.bonus)
 			{
 			case -1:
 				pom=10;
-				toPrint = &CGI->generaltexth->allTexts[522];
 				break;
 			case 0:
 				pom=9;
@@ -1178,17 +1203,213 @@ void OptionsTab::SelectedBox::showAll( SDL_Surface * to )
 			case 2:
 				pom=CGI->townh->towns[s.castle].bonus;
 				break;
+			default: 
+				assert(0);
 			}
-			toBlit = CGP->bonuses->ourImages[pom].bitmap;
+			return CGP->bonuses->ourImages[pom].bitmap;
 		}
-		break;
+	default:
+		return NULL;
 	}
+}
 
-	blitAt(toBlit, pos, to);
-	printAtMiddleLoc(*toPrint, 23, 39, FONT_TINY, zwykly, to);
+const std::string * OptionsTab::SelectedBox::getText() const
+{
+	PlayerSettings &s = curOpts->playerInfos[player];	
+	switch(which)
+	{
+	case TOWN:
+		if (s.castle < F_NUMBER  &&  s.castle >= 0)
+			return &CGI->townh->towns[s.castle].Name();
+		else if (s.castle == -1)
+			return &CGI->generaltexth->allTexts[522];
+		else if (s.castle == -2)
+			return &CGI->generaltexth->allTexts[523];
+	case HERO:
+		if (s.hero == -1)
+			return &CGI->generaltexth->allTexts[522];
+		else if (s.hero == -2)
+		{
+			if(s.heroPortrait >= 0)
+			{
+				if(s.heroName.length())
+					return &s.heroName;
+				else
+					return &CGI->heroh->heroes[s.heroPortrait]->name;
+			}
+			else
+				return &CGI->generaltexth->allTexts[523];
+		}
+		else
+		{
+			//if(s.heroName.length())
+			//	return &s.heroName;
+			//else
+				return &CGI->heroh->heroes[s.hero]->name;
+		}
+	case BONUS:
+		switch (s.bonus)
+		{
+		case -1:
+			return &CGI->generaltexth->allTexts[522];
+		default:
+			return &CGI->generaltexth->arraytxt[214 + s.bonus];
+		}
+	default:
+		return NULL;
+	}
 }
 
-OptionsTab::SelectedBox::SelectedBox( SelType Which, ui8 Player )
-:which(Which), player(Player)
+void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
 {
+	if(indeterminate(down) || !down) return;
+	PlayerSettings &s = curOpts->playerInfos[player];
+	SDL_Surface *bmp = NULL;
+	const std::string *title = NULL, *subTitle = NULL;
+
+	subTitle = getText();
+
+	int val;
+	switch(which)
+	{
+	case TOWN: val = s.castle; break;
+	case HERO: 
+		val = s.hero; 
+		if(val < 0)
+		{
+			int p9 = curMap->players[s.color].p9;
+			if(p9 != 255)
+				val = p9;
+		}
+		break;
+	case BONUS: val = s.bonus; break;
+	}
+
+	if(val == -1  ||  which == BONUS) //random or bonus box
+	{
+		bmp = CMessage::drawBox1(256, 190);
+		std::string *description = NULL;
+
+		switch(which)
+		{
+		case TOWN:
+			title = &CGI->generaltexth->allTexts[103];
+			description = &CGI->generaltexth->allTexts[104];
+			break;
+		case HERO:
+			title = &CGI->generaltexth->allTexts[101];
+			description = &CGI->generaltexth->allTexts[102];
+			break;
+		case BONUS:
+			{
+				switch(val)
+				{
+				case brandom:
+					title = &CGI->generaltexth->allTexts[86]; //{Random Bonus}
+					description = &CGI->generaltexth->allTexts[94]; //Gold, wood and ore, or an artifact is randomly chosen as your starting bonus
+					break;
+				case bartifact:
+					title = &CGI->generaltexth->allTexts[83]; //{Artifact Bonus}
+					description = &CGI->generaltexth->allTexts[90]; //An artifact is randomly chosen and equipped to your starting hero
+					break;
+				case bgold:
+					title = &CGI->generaltexth->allTexts[84]; //{Gold Bonus}
+					subTitle = &CGI->generaltexth->allTexts[87]; //500-1000
+					description = &CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool
+					break;
+				case bresource:
+					{
+						title = &CGI->generaltexth->allTexts[85]; //{Resource Bonus}
+						switch(CGI->townh->towns[s.castle].primaryRes)
+						{
+						case 1:
+							subTitle = &CGI->generaltexth->allTexts[694];
+							description = &CGI->generaltexth->allTexts[690];
+							break;
+						case 3:
+							subTitle = &CGI->generaltexth->allTexts[695];
+							description = &CGI->generaltexth->allTexts[691];
+							break;
+						case 4:
+							subTitle = &CGI->generaltexth->allTexts[692];
+							description = &CGI->generaltexth->allTexts[688];
+							break;
+						case 5:
+							subTitle = &CGI->generaltexth->allTexts[693];
+							description = &CGI->generaltexth->allTexts[689];
+							break;
+						case 127:
+							subTitle = &CGI->generaltexth->allTexts[89]; //5-10 wood / 5-10 ore
+							description = &CGI->generaltexth->allTexts[93]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
+							break;
+						}
+					}
+					break;
+				}
+			}
+			break;
+		}
+
+		if(description)
+			CSDL_Ext::printAtMiddleWB(*description, 125, 145, FONT_SMALL, 37, zwykly, bmp);
+	}
+	else if(val == -2)
+	{
+		return;
+	}
+	else if(which == TOWN)
+	{
+		bmp = CMessage::drawBox1(256, 319);
+		title = &CGI->generaltexth->allTexts[80];
+
+		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[79], 135, 137, FONT_MEDIUM, tytulowy, bmp);
+		
+		const CTown &t = CGI->townh->towns[val];
+		//print creatures
+		int x = 60, y = 159;
+		for(int i = 0; i < 7; i++)
+		{
+			int c = t.basicCreatures[i];
+			blitAt(graphics->smallImgs[c], x, y, bmp);
+			CSDL_Ext::printAtMiddleWB(CGI->creh->creatures[c].nameSing, x + 16, y + 45, FONT_TINY, 10, zwykly, bmp);
+
+			if(i == 2)
+			{
+				x = 40;
+				y += 76;
+			}
+			else
+			{
+				x += 52;
+			}
+		}
+
+	}
+	else if(val >= 0)
+	{
+		const CHero *h = CGI->heroh->heroes[val];
+		bmp = CMessage::drawBox1(320, 255);
+		title = &CGI->generaltexth->allTexts[77];
+
+		CSDL_Ext::printAtMiddle(*title, 167, 36, FONT_MEDIUM, tytulowy, bmp);
+		CSDL_Ext::printAtMiddle(*subTitle + " - " + h->heroClass->name, 160, 99, FONT_SMALL, zwykly, bmp);
+
+		blitAt(getImg(), 136, 56, bmp);
+
+		//print specialty
+		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[78], 166, 132, FONT_MEDIUM, tytulowy, bmp);
+		blitAt(graphics->un44->ourImages[val].bitmap, 140, 150, bmp);
+
+		GH.pushInt(new CInfoPopup(bmp, true));
+		return;
+	}
+
+	if(title)
+		CSDL_Ext::printAtMiddle(*title, 135, 36, FONT_MEDIUM, tytulowy, bmp);
+	if(subTitle)
+		CSDL_Ext::printAtMiddle(*subTitle, 127, 103, FONT_SMALL, zwykly, bmp);
+
+	blitAt(getImg(), 104, 60, bmp);
+
+	GH.pushInt(new CInfoPopup(bmp, true));
 }

+ 8 - 1
client/CPreGame.h

@@ -61,6 +61,8 @@ public:
 
 	void changeSelection(const CMapInfo *to);
 	void showAll(SDL_Surface * to);
+	void clickRight(tribool down, bool previousState);
+	void showTeamsPopup();
 	InfoCard(EState Type);
 	~InfoCard();
 };
@@ -106,8 +108,13 @@ public:
 	{
 		SelType which;
 		ui8 player; //serial nr
-		void showAll(SDL_Surface * to);
+
+		SDL_Surface *getImg() const;
+		const std::string *getText() const;
+
 		SelectedBox(SelType Which, ui8 Player);
+		void showAll(SDL_Surface * to);
+		void clickRight(tribool down, bool previousState);
 	};
 
 	struct PlayerOptionsEntry : public CIntObject

+ 1 - 1
client/Client.h

@@ -101,7 +101,7 @@ public:
 	void heroVisitCastle(int obj, int heroID){};
 	void stopHeroVisitCastle(int obj, int heroID){};
 	void giveHeroArtifact(int artid, int hid, int position){}; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0){}; //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL){}; //use hero=NULL for no hero
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb = 0){}; //if any of armies is hero, hero will be used
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0){}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	void setAmount(int objid, ui32 val){};

+ 10 - 0
client/GUIBase.cpp

@@ -536,6 +536,16 @@ void CIntObject::enable(bool activation)
 	recActions = 255;
 }
 
+bool CIntObject::isItInLoc( const SDL_Rect &rect, int x, int y )
+{
+	return isItIn(&rect, x - pos.x, y - pos.y);
+}
+
+bool CIntObject::isItInLoc( const SDL_Rect &rect, const Point &p )
+{
+	return isItIn(&rect, p.x - pos.x, p.y - pos.y);
+}
+
 CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
 {
 	bg = BG; 

+ 2 - 0
client/GUIBase.h

@@ -331,6 +331,8 @@ public:
 	void printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst, bool refresh = false);
 	void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst, bool refrsh = false);
 	void blitAtLoc(SDL_Surface * src, int x, int y, SDL_Surface * dst);
+	bool isItInLoc(const SDL_Rect &rect, int x, int y);
+	bool isItInLoc(const SDL_Rect &rect, const Point &p);
 };
 
 //class for binding keys to left mouse button clicks

+ 6 - 0
client/GUIClasses.cpp

@@ -683,6 +683,11 @@ CInfoPopup::CInfoPopup(SDL_Surface *Bitmap, bool Free)
 {
 	free=Free;
 	bitmap=Bitmap;
+
+	pos.x = screen->w/2 - bitmap->w/2;
+	pos.y = screen->h/2 - bitmap->h/2;
+	pos.h = bitmap->h;
+	pos.w = bitmap->w;
 }
 
 void CInfoPopup::close()
@@ -1402,6 +1407,7 @@ CTownList::CTownList(int Size, int x, int y, std::string arrupg, std::string arr
 
 void CTownList::genList()
 {
+	items.clear();
 	int howMany = LOCPLINT->cb->howManyTowns();
 	for (int i=0;i<howMany;i++)
 	{

+ 7 - 1
client/SDL_Extensions.cpp

@@ -216,6 +216,9 @@ void CSDL_Ext::printAt(const std::string & text, int x, int y, TTF_Font * font,
 
 void CSDL_Ext::printAt( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=zwykly*/, SDL_Surface * dst/*=screen*/, bool refresh /*= false*/ )
 {
+	if(!text.size())
+		return;
+
 	assert(dst);
 	assert(font < Graphics::FONTS_NUMBER);
 	assert(dst->format->BytesPerPixel == 3   ||   dst->format->BytesPerPixel == 4); //  24/32 bpp dst only
@@ -225,8 +228,11 @@ void CSDL_Ext::printAt( const std::string & text, int x, int y, EFonts font, SDL
 	Uint8 *px = NULL;
 	Uint8 *src = NULL;
 
+	//if text is in {} braces, we'll ommit them
+	const int first = (text[0] == '{' ? 1 : 0);
+	const int beyondEnd = (text[text.size()-1] == '}' ? text.size()-1 : text.size());
 
-	for(int txti = 0; txti < text.size(); txti++)
+	for(int txti = first; txti < beyondEnd; txti++)
 	{
 		const unsigned char c = text[txti];
 		src = f->chars[c].pixels;

+ 4 - 0
client/VCMI_client.vcproj

@@ -512,6 +512,10 @@
 				RelativePath="..\hch\CVideoHandler.h"
 				>
 			</File>
+			<File
+				RelativePath=".\FontBase.h"
+				>
+			</File>
 			<File
 				RelativePath=".\FunctionList.h"
 				>

+ 28 - 1
hch/CObjectHandler.cpp

@@ -1406,7 +1406,26 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
 {
 	if(getOwner() != h->getOwner())
 	{
-		return;
+		//TODO ally check
+		if(army)
+		{
+			const CGHeroInstance *defendingHero = NULL;
+			if(visitingHero)
+				defendingHero = visitingHero;
+			else if(garrisonHero)
+				defendingHero = garrisonHero;
+
+			const CArmedInstance *defendingArmy = this;
+			if(defendingHero)
+				defendingArmy = defendingHero;
+
+			bool outsideTown = (defendingHero == visitingHero && garrisonHero);
+			cb->startBattleI(h, defendingArmy, getSightCenter(), h, defendingHero, false, boost::bind(&CGTownInstance::fightOver, this, h, _1), (outsideTown ? NULL : this));
+		}
+		else
+		{
+			cb->setOwner(id, h->tempOwner);
+		}
 	}
 	cb->heroVisitCastle(id,h->id);
 }
@@ -1440,6 +1459,14 @@ void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
 	offsets += int3(-1,3,0), int3(-3,3,0);
 }
 
+void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result ) const
+{
+	if(result->winner == 0)
+	{
+		cb->setOwner(id, h->tempOwner);
+	}
+}
+
 void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const
 {
 	if(visitors.find(h->id)==visitors.end())

+ 1 - 0
hch/CObjectHandler.h

@@ -397,6 +397,7 @@ public:
 	//////////////////////////////////////////////////////////////////////////
 
 
+	void fightOver(const CGHeroInstance *h, BattleResult *result) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void onHeroLeave(const CGHeroInstance * h) const;
 	void initObj();

+ 8 - 6
lib/CGameState.cpp

@@ -1403,8 +1403,8 @@ bool CGameState::battleCanFlee(int player)
 	const CGHeroInstance *h1 = getHero(curB->hero1);
 	const CGHeroInstance *h2 = getHero(curB->hero2);
 
-	if(h1->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war
-		|| h2->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE))
+	if(h1 && h1->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE) //eg. one of heroes is wearing shakles of war
+		|| h2 && h2->hasBonusOfType(HeroBonus::ENEMY_CANT_ESCAPE))
 		return false;
 
 	return true;
@@ -1557,7 +1557,7 @@ void CGameState::loadTownDInfos()
 
 void CGameState::getNeighbours(int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand)
 {
-	int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
+	static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
 					int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
 
 	vec.clear();
@@ -1704,6 +1704,8 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 	else
 		blockLandSea = boost::logic::indeterminate;
 
+	const std::vector<std::vector<std::vector<ui8> > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap;
+
 	//graph initialization
 	std::vector< std::vector<CPathNode> > graph;
 	graph.resize(map->width);
@@ -1726,7 +1728,7 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 			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
-				|| !getPlayer(hero->tempOwner)->fogOfWarMap[i][j][src.z] //tile is covered by the FoW
+				|| !FoW[i][j][src.z] //tile is covered by the FoW
 			)
 			{
 				node.accesible = false;
@@ -1750,10 +1752,10 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
 		{
 			size_t i = 0;
 			for(; i < t->visitableObjects.size(); i++)
-				if(t->visitableObjects[i]->ID == 8) //it's a Boat
+				if(t->visitableObjects[i]->ID == 8  ||  t->visitableObjects[i]->ID == HEROI_TYPE) //it's a Boat
 					break;
 
-			d.accesible = (i < t->visitableObjects.size()); //dest is accessible only if there is boat
+			d.accesible = (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
 		{

+ 1 - 0
lib/CGameState.h

@@ -110,6 +110,7 @@ struct DLL_EXPORT BattleInfo
 	ui8 side1, side2; //side1 - attacker, side2 - defender
 	si32 round, activeStack;
 	ui8 siege; //    = 0 ordinary battle    = 1 a siege with a Fort    = 2 a siege with a Citadel    = 3 a siege with a Castle
+	si32 tid; //used during town siege - id of attacked town; -1 if not town defence
 	int3 tile; //for background and bonuses
 	si32 hero1, hero2;
 	CCreatureSet army1, army2;

+ 1 - 1
lib/IGameCallback.h

@@ -79,7 +79,7 @@ public:
 	virtual void heroVisitCastle(int obj, int heroID)=0;
 	virtual void stopHeroVisitCastle(int obj, int heroID)=0;
 	virtual void giveHeroArtifact(int artid, int hid, int position)=0; //pos==-1 - first free slot in backpack=0; pos==-2 - default if available or backpack
-	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0)=0; //use hero=NULL for no hero
+	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0, const CGTownInstance *town = NULL)=0; //use hero=NULL for no hero
 	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb = 0)=0; //if any of armies is hero, hero will be used
 	virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	//virtual void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb)=0; //for hero<=>neutral army

+ 19 - 2
lib/NetPacksLib.cpp

@@ -552,9 +552,26 @@ DLL_EXPORT void SetObjectProperty::applyGs( CGameState *gs )
 {
 	CGObjectInstance *obj = gs->map->objects[id];
 	if(!obj)
+	{
 		tlog1 << "Wrong object ID - property cannot be set!\n";
-	else
-		obj->setProperty(what,val);
+		return;
+	}
+
+	if(what == 1)
+	{
+		if(obj->ID == TOWNI_TYPE)
+		{
+			CGTownInstance *t = static_cast<CGTownInstance*>(obj);
+			if(t->tempOwner < PLAYER_LIMIT)
+				gs->getPlayer(t->tempOwner)->towns -= t;
+
+			if(val < PLAYER_LIMIT)
+				gs->getPlayer(val)->towns.push_back(t);
+		}
+	}
+
+	
+	obj->setProperty(what,val);
 }
 
 DLL_EXPORT void SetHoverName::applyGs( CGameState *gs )

+ 22 - 7
lib/map.cpp

@@ -253,7 +253,7 @@ void CMapHeader::initFromMemory( unsigned char *bufor, int &i )
 	{
 		for(int rr=0; rr<8; ++rr)
 		{
-			players[rr].team=bufor[i++];
+			players[rr].team = bufor[i++];
 		}
 	}
 
@@ -1991,7 +1991,7 @@ void Mapa::readEvents( unsigned char * bufor, int &i )
 	}
 }
 
-bool Mapa::isInTheMap( int3 pos ) const
+bool Mapa::isInTheMap(const int3 &pos) const
 {
 	if(pos.x<0 || pos.y<0 || pos.z<0 || pos.x >= width || pos.y >= height || pos.z > twoLevel)
 		return false;
@@ -2086,18 +2086,33 @@ const TerrainTile & Mapa::getTile( int3 tile ) const
 	return terrain[tile.x][tile.y][tile.z];
 }
 
-bool Mapa::isWaterTile( int3 pos ) const
+bool Mapa::isWaterTile(const int3 &pos) const
 {
 	return isInTheMap(pos) && getTile(pos).tertype == TerrainTile::water;
 }
 
 void CMapInfo::countPlayers()
 {
-	playerAmnt=humenPlayers=0;
-	for (int i=0;i<PLAYER_LIMIT;i++)
+	playerAmnt = humenPlayers = 0;
+	for(int i=0;i<PLAYER_LIMIT;i++)
 	{
-		if (players[i].canHumanPlay) {playerAmnt++;humenPlayers++;}
-		else if (players[i].canComputerPlay) {playerAmnt++;}
+		if(players[i].canHumanPlay)
+		{
+			playerAmnt++;
+			humenPlayers++;
+		}
+		else if(players[i].canComputerPlay)
+		{
+			playerAmnt++;
+		}
+	}
+
+	if(!howManyTeams) //no alliances
+	{
+		//howManyTeams = playerAmnt;
+		for(int i=0;i<PLAYER_LIMIT;i++)
+			if(players[i].canComputerPlay || players[i].canHumanPlay)
+				players[i].team = howManyTeams++;
 	}
 }
 

+ 4 - 5
lib/map.h

@@ -125,7 +125,7 @@ struct DLL_EXPORT PlayerInfo
 	PlayerInfo(): p7(0), p8(0), p9(0), canHumanPlay(0), canComputerPlay(0),
 		AITactic(0), allowedFactions(0), isFactionRandom(0),
 		mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0),
-		team(0), generateHero(0) {};
+		team(255), generateHero(0) {};
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -219,7 +219,6 @@ public:
 	LossCondition lossCondition;
 	CVictoryCondition victoryCondition; //victory conditions
 	std::vector<PlayerInfo> players; // info about players - size 8
-	std::vector<ui8> teams;  // teams[i] = team of player no i
 	ui8 howManyTeams;
 	std::vector<ui8> allowedHeroes; //allowedHeroes[hero_ID] - if the hero is allowed
 	void initFromMemory(unsigned char *bufor, int &i);
@@ -232,7 +231,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int Version)
 	{
 		h & version & name & description & width & height & twoLevel & difficulty & levelLimit & areAnyPLayers;
-		h & players & teams & lossCondition & victoryCondition & howManyTeams;
+		h & players & lossCondition & victoryCondition & howManyTeams;
 	}
 };
 
@@ -338,8 +337,8 @@ struct DLL_EXPORT Mapa : public CMapHeader
 	TerrainTile &getTile(int3 tile);
 	const TerrainTile &getTile(int3 tile) const;
 	CGHeroInstance * getHero(int ID, int mode=0);
-	bool isInTheMap(int3 pos) const;
-	bool isWaterTile(int3 pos) const; //out-of-pos safe
+	bool isInTheMap(const int3 &pos) const;
+	bool isWaterTile(const int3 &pos) const; //out-of-pos safe
 	template <typename Handler> void serialize(Handler &h, const int formatVersion)
 	{
 		h & static_cast<CMapHeader&>(*this);

+ 16 - 6
server/CGameHandler.cpp

@@ -322,10 +322,10 @@ static CCreatureSet takeCasualties(int color, const CCreatureSet &set, BattleInf
 	return ret;
 }
 
-void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb)
+void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town)
 {
 	BattleInfo *curB = new BattleInfo;
-	setupBattle(curB, tile, army1->army, army2->army, hero1, hero2, creatureBank); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
+	setupBattle(curB, tile, army1->army, army2->army, hero1, hero2, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
 	NEW_ROUND;
 	//TODO: pre-tactic stuff, call scripts etc.
 
@@ -857,13 +857,12 @@ namespace CGH
 	}
 }
 
-void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank )
+void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town)
 {
 	battleResult.set(NULL);
 	std::vector<CStack*> & stacks = (curB->stacks);
 
 	curB->tile = tile;
-	curB->siege = 0; //TODO: add sieges
 	curB->army1=army1;
 	curB->army2=army2;
 	curB->hero1=(hero1)?(hero1->id):(-1);
@@ -873,6 +872,17 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
 	curB->round = -2;
 	curB->activeStack = -1;
 
+	if(town)
+	{
+		curB->tid = town->id;
+		curB->siege = town->fortLevel();
+	}
+	else
+	{
+		curB->tid = -1;
+		curB->siege = 0;
+	}
+
 	//reading battleStartpos
 	std::ifstream positions;
 	positions.open("config" PATHSEPARATOR "battleStartpos.txt", std::ios_base::in|std::ios_base::binary);
@@ -1540,7 +1550,7 @@ void CGameHandler::giveHeroArtifact(int artid, int hid, int position) //pos==-1
 	sendAndApply(&sha);
 }
 
-void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb) //use hero=NULL for no hero
+void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town) //use hero=NULL for no hero
 {
 	engageIntoBattle(army1->tempOwner);
 	engageIntoBattle(army2->tempOwner);
@@ -1548,7 +1558,7 @@ void CGameHandler::startBattleI(const CArmedInstance *army1, const CArmedInstanc
 	if(army2->tempOwner < PLAYER_LIMIT)
 		states.setFlag(army2->tempOwner,&PlayerStatus::engagedIntoBattle,true);
 
-	boost::thread(boost::bind(&CGameHandler::startBattle, this, army1, army2, tile, hero1, hero2, creatureBank, cb));
+	boost::thread(boost::bind(&CGameHandler::startBattle, this, army1, army2, tile, hero1, hero2, creatureBank, cb, town));
 }
 
 void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb )

+ 3 - 3
server/CGameHandler.h

@@ -84,11 +84,11 @@ public:
 	bool isAllowedExchange(int id1, int id2);
 	void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
 	void moveStack(int stack, int dest);
-	void startBattle(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
+	void startBattle(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero
 	void prepareAttack(BattleAttack &bat, CStack *att, CStack *def); //if last parameter is true, attack is by shooting, if false it's a melee attack
 	void prepareAttacked(BattleStackAttacked &bsa, CStack *def);
 	void checkForBattleEnd( std::vector<CStack*> &stacks );
-	void setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank );
+	void setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet &army1, const CCreatureSet &army2, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool creatureBank, const CGTownInstance *town);
 
 
 	CGameHandler(void);
@@ -122,7 +122,7 @@ public:
 	void giveHeroArtifact(int artid, int hid, int position); //pos==-1 - first free slot in backpack; pos==-2 - default if available or backpack
 	void moveArtifact(int hid, int oldPosition, int destPos);
 	void removeArtifact(int hid, int pos);
-	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
+	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank, boost::function<void(BattleResult*)> cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank, boost::function<void(BattleResult*)> cb);
 	void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank, boost::function<void(BattleResult*)> cb = 0); //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
 	//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army