فهرست منبع

Version for development release 0.73c
* version set to 0.73c, bumped save format version, updated changelog
* new stack queue for higher resolutions (needs new graphics!)
* improved stack ordering during battle
* many minor fixes
* temporarily disabled AI

Michał W. Urbańczyk 16 سال پیش
والد
کامیت
ee3c318ed9

+ 1 - 1
AI/GeniusAI/CGeniusAI.cpp

@@ -816,6 +816,7 @@ void CGeniusAI::yourTurn()
 	static boost::mutex mutex;
 	boost::mutex::scoped_lock lock(mutex);
 	m_cb->waitTillRealize = true;
+	m_cb->endTurn();
 	static int seed = rand();
 	srand(seed);
 	if(m_cb->getDate()==1)
@@ -843,7 +844,6 @@ void CGeniusAI::yourTurn()
 		objective->fulfill(*this,trueGameState);
 
 	seed = rand();
-	m_cb->endTurn();
 	
 	m_cb->waitTillRealize = false;
 }

+ 77 - 1
ChangeLog

@@ -1,4 +1,80 @@
-0.72 -> 0.73 (1 Aug 2009) as for r1044
+0.73 -> 0.74
+GENERAL:
+* Scenario Information window
+* Save Game window
+* VCMI window should start centered
+* support for Necromancy and Ballistics secondary skills
+* new artifacts supported, including those improving Necromancy, Legion Statue parts, Shackles of War and most of combination artifacts (but not combining)
+* Ellipsis won't be split when breaking text on several lines
+* split button will be grayed out when no creature is selected
+* a few fixes for shipyard window
+
+ADVENTURE INTERFACE:
+* Cursor shows if tile is accesible and how many turns away
+* moving hero with arrow keys / numpad
+* fixed Next Hero button behaviour
+* fixed Surface/Underground switch button in higher resolutions
+
+BATTLES:
+* partial siege support
+* new stack queue for higher resolutions (graphics made by Dru, thx!)
+* 'Q' pressing toggles the queue displaying (so it can be enabled/disabled it with single key press)
+* more creatures special abilities supported
+* stack 
+* fixed crashes occuring on attacking two hex creatures from back
+* even large stack numbers will fit the boxes
+* shooters attacking twice (like Grand Elves) won't attack twice in melee 
+* ballista can shoot even if there's an enemy creature next to it 
+* improved obstacles placement, so they'll better fit hexes (thx to Ivan!)
+* selecting attack directions works as in H3
+* estimating damage that will be dealt while choosing stack to be attacked
+* modified the positioning of battle effects, they should look about right now.
+* after selecting a spell during combat, l-click is locked for any action other than casting. 
+* new spells supported:
+- Anti-Magic
+- Cure
+- Resurrection 
+- Animate Dead 
+- Counterstrike 
+- Berserk 
+- Hypnotize 
+- Blind 
+- Fire Elemental 
+- Earth Elemental 
+- Water Elemental 
+- Air Elemental 
+- Remove obstacle
+
+TOWNS:
+* enemy castle can be taken over
+* garrisoned hero can buy a spellbook
+* Lookout Tower supported
+* heroes available in tavern sould be always different
+* ship bought in town will be correctly placed
+
+HERO WINDOW:
+* war machines cannot be unequiped
+
+PREGAME:
+* sorting: a second click on the column header sorts in descending order.
+* advanced options tab: r-click popups for selected town, hero and bonus
+* starting scenario / game by double click
+* arrows in options tab are hidden when not available 
+* subtitles for choosen hero/town/bonus in pregame
+
+OBJECTS:
+* fixed pairing Subterranean Gates
+New objects supported:
+- Borderguard & Keymaster Tent
+- Cartographer
+- Creature banks
+- Eye of the Magi & Hut of the Magi
+- Garrison
+- Stables
+- Pandora Box
+
+
+0.72 -> 0.73 (1 Aug 2009)
 GENERAL:
 * infowindow popup will be completely on screen
 * fixed possible crash with in game console

+ 2 - 1
client/CAdvmapInterface.cpp

@@ -1729,7 +1729,8 @@ void CAdvMapInt::centerOn(int3 on)
 	LOCPLINT->adventureInt->updateScreen=true;
 	updateMinimap=true;
 	underground.curimg = on.z; //change underground switch button image 
-	underground.redraw();
+	if(GH.topInt() == this)
+		underground.redraw();
 }
 void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
 {

+ 222 - 62
client/CBattleInterface.cpp

@@ -25,6 +25,7 @@
 #include "../hch/CVideoHandler.h"
 #include "../hch/CTownHandler.h"
 #include <boost/assign/list_of.hpp>
+#include <boost/lexical_cast.hpp>
 #ifndef __GNUC__
 const double M_PI = 3.14159265358979323846;
 #else
@@ -100,7 +101,7 @@ bool CBattleAnimation::isEarliest(bool perStackConcurrency)
 
 		CReverseAnim * revAnim = dynamic_cast<CReverseAnim *>(stAnim);
 
-		if(revAnim && stAnim->stackID == thAnim->stackID && revAnim->priority)
+		if(revAnim && thAnim && stAnim && stAnim->stackID == thAnim->stackID && revAnim->priority)
 			return false;
 
 		if(it->first)
@@ -545,7 +546,8 @@ bool CBattleStackMoved::init()
 	}
 	//unit reversed
 
-	owner->moveSh = CGI->soundh->playSound(movedStack->creature->sounds.move, -1);
+	if(owner->moveSh <= 0)
+		owner->moveSh = CGI->soundh->playSound(movedStack->creature->sounds.move, -1);
 
 	//step shift calculation
 	posX = owner->creAnims[stackID]->pos.x, posY = owner->creAnims[stackID]->pos.y; // for precise calculations ;]
@@ -630,6 +632,12 @@ void CBattleStackMoved::endAnim()
 		owner->creAnims[stackID]->pos.y = coords.second;
 	}
 
+	if(owner->moveSh >= 0)
+	{
+		CGI->soundh->stopSound(owner->moveSh);
+		owner->moveSh = -1;
+	}
+
 	delete this;
 }
 
@@ -727,8 +735,6 @@ void CBattleMoveEnd::endAnim()
 		owner->creAnims[stackID]->setType(2); //resetting to default
 
 	CGI->curh->show();
-	CGI->soundh->stopSound(owner->moveSh);
-
 	delete this;
 }
 
@@ -1011,13 +1017,29 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	: attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), activeStack(-1), stackToActivate(-1),
 	  mouseHoveredStack(-1), previouslyHoveredHex(-1), currentlyHoveredHex(-1), spellDestSelectMode(false),
 	  spellToCast(NULL), givenCommand(NULL), myTurn(false), resWindow(NULL), animIDhelper(0),
-	  showStackQueue(false), moveStarted(false), moveSh(-1), siegeH(NULL), bresult(NULL)
+	  moveStarted(false), moveSh(-1), siegeH(NULL), bresult(NULL), queue(NULL)
 {
+	ObjectConstruction h__l__p(this);
+
 	animsAreDisplayed.setn(false);
 	pos = myRect;
 	strongInterest = true;
 	givenCommand = new CondSh<BattleAction *>(NULL);
 	
+	//create stack queue
+	bool embedQueue = screen->h < 700;
+	queue = new CStackQueue(embedQueue);
+ 	if(!embedQueue && settings.showQueue)
+	{
+		pos.y += queue->pos.h / 2; //center whole window
+		queue->moveTo(Point(pos.x, pos.y - queue->pos.h));
+// 		queue->pos.x = pos.x;
+// 		queue->pos.y = pos.y - queue->pos.h;
+//  		pos.h += queue->pos.h;
+//  		center();
+ 	}
+	queue->update();
+
 	//preparing siege info
 	const CGTownInstance * town = LOCPLINT->cb->battleGetDefendedTown();
 	if(town)
@@ -1239,6 +1261,7 @@ CBattleInterface::~CBattleInterface()
 
 	delete attackingHero;
 	delete defendingHero;
+	delete queue;
 
 	SDL_FreeSurface(cellBorder);
 	SDL_FreeSurface(cellShade);
@@ -1297,6 +1320,8 @@ void CBattleInterface::activate()
 		attackingHero->activate();
 	if(defendingHero)
 		defendingHero->activate();
+	if(settings.showQueue)
+		queue->activate();
 
 	LOCPLINT->cingconsole->activate();
 }
@@ -1323,6 +1348,8 @@ void CBattleInterface::deactivate()
 		attackingHero->deactivate();
 	if(defendingHero)
 		defendingHero->deactivate();
+	if(settings.showQueue)
+		queue->deactivate();
 
 	LOCPLINT->cingconsole->deactivate();
 }
@@ -1337,6 +1364,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	SDL_Rect buf;
 	SDL_GetClipRect(to, &buf);
 	SDL_SetClipRect(to, &pos);
+
 	//printing background and hexes
 	if(activeStack != -1 && creAnims[activeStack]->getType() != 0) //show everything with range
 	{
@@ -1492,11 +1520,6 @@ void CBattleInterface::show(SDL_Surface * to)
 		{
 			activateStack();
 		}
-		if(pendingAnims.size() == 0 && bresult != NULL)
-		{
-			displayBattleFinished();
-		}
-
 		//anims ended
 		animsAreDisplayed.setn(false);
 	}
@@ -1531,53 +1554,6 @@ void CBattleInterface::show(SDL_Surface * to)
 			SDL_BlitSurface(bitmapToBlit, NULL, to, &genRect(bitmapToBlit->h, bitmapToBlit->w, pos.x + it->x, pos.y + it->y));
 		}
 	}
-	
-
-	//showing queue of stacks
-	if(showStackQueue)
-	{
-		const int QUEUE_SIZE = 10;
-
-		int xPos = screen->w/2 - ( stacks.size() * 37 )/2;
-		int yPos = (screen->h - 600)/2 + 10;
-
-		std::vector<const CStack *> stacksSorted;
-// 		const CStack *curStack = LOCPLINT->cb->battleGetStackByID(activeStack);
-// 		if(curStack)
-// 			stacksSorted.push_back(curStack);
-		LOCPLINT->cb->getStackQueue(stacksSorted, QUEUE_SIZE);
-
-
-		for(size_t b=0; b < stacksSorted.size(); ++b)
-		{
-			const CStack * s = stacksSorted[b];
-			SDL_BlitSurface(graphics->smallImgs[-2], NULL, to, &genRect(32, 32, xPos, yPos));
-
-			//printing colored border
-			for(int xFrom = xPos-1; xFrom<xPos+33; ++xFrom)
-			{
-				for(int yFrom = yPos-1; yFrom<yPos+33; ++yFrom)
-				{
-					if(xFrom == xPos-1 || xFrom == xPos+32 || yFrom == yPos-1 || yFrom == yPos+32)
-					{
-						SDL_Color pc;
-						if(s->owner != 255)
-						{
-							pc = graphics->playerColors[s->owner];
-						}
-						else
-						{
-							pc = *graphics->neutralColor;
-						}
-						CSDL_Ext::SDL_PutPixelWithoutRefresh(to, xFrom, yFrom, pc.r, pc.g, pc.b);
-					}
-				}
-			}
-			//colored border printed
-			SDL_BlitSurface(graphics->smallImgs[s->creature->idNumber], NULL, to, &genRect(32, 32, xPos, yPos));
-			xPos += 37;
-		}
-	}
 
 	SDL_SetClipRect(to, &buf); //restoring previous clip_rect
 
@@ -1594,15 +1570,35 @@ void CBattleInterface::show(SDL_Surface * to)
 	//showing in-game console
 	LOCPLINT->cingconsole->show(to);
 
+	Rect posWithQueue = Rect(pos.x, pos.y, 800, 600);
+
+	if(settings.showQueue)
+	{
+		if(!queue->embedded)
+		{
+			posWithQueue.y -= queue->pos.h;
+			posWithQueue.h += queue->pos.h;
+		}
+
+		//showing queue
+		if(!bresult)
+			queue->showAll(to);
+	}
+
 	//printing border around interface
 	if(screen->w != 800 || screen->h !=600)
-		CMessage::drawBorder(LOCPLINT->playerID,to,828,628,pos.x-14,pos.y-15);
+	{
+		CMessage::drawBorder(LOCPLINT->playerID,to,posWithQueue.w + 28, posWithQueue.h + 28, posWithQueue.x-14, posWithQueue.y-15);
+	}
 }
 void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key)
 {
-	if(key.keysym.sym == SDLK_q)
+	if(key.keysym.sym == SDLK_q && key.state == SDL_PRESSED)
 	{
-		showStackQueue = key.state==SDL_PRESSED;
+		if(settings.showQueue) //hide queue
+			hideQueue();
+		else
+			showQueue();
 	}
 	else if(key.keysym.sym == SDLK_ESCAPE && spellDestSelectMode)
 	{
@@ -2121,7 +2117,7 @@ void CBattleInterface::newRound(int number)
 
 void CBattleInterface::giveCommand(ui8 action, ui16 tile, ui32 stack, si32 additional)
 {
-	if(!LOCPLINT->cb->battleGetStackByID(stack))
+	if(!LOCPLINT->cb->battleGetStackByID(stack) && action != 1 && action != 4 && action != 5)
 	{
 		return;
 	}
@@ -2406,7 +2402,9 @@ void CBattleInterface::stackIsShooting(int ID, int dest)
 void CBattleInterface::battleFinished(const BattleResult& br)
 {
 	bresult = &br;
-	//animsAreDisplayed.waitUntil(false);
+	LOCPLINT->pim->unlock();
+	animsAreDisplayed.waitUntil(false);
+	LOCPLINT->pim->lock();
 	displayBattleFinished();
 }
 
@@ -2602,6 +2600,7 @@ int CBattleInterface::getAnimSpeed() const
 void CBattleInterface::activateStack()
 {
 	activeStack = stackToActivate;
+	queue->update();
 	stackToActivate = -1;
 	myTurn = true;
 	redrawBackgroundWithHexes(activeStack);
@@ -2899,6 +2898,51 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 	}
 }
 
+void CBattleInterface::endAction(const BattleAction* action)
+{	
+	//if((action->actionType==2 || (action->actionType==6 && action->destinationTile!=cb->battleGetPos(action->stackNumber)))) //activating interface when move is finished
+	{
+		activate();
+	}
+	if(action->actionType == 1)
+	{
+		if(action->side)
+			defendingHero->setPhase(0);
+		else
+			attackingHero->setPhase(0);
+	}
+	if(action->actionType == 2 && creAnims[action->stackNumber]->getType() != 2) //walk or walk & attack
+	{
+		pendingAnims.push_back(std::make_pair(new CBattleMoveEnd(this, action->stackNumber, action->destinationTile), false));
+	}
+}
+
+void CBattleInterface::hideQueue()
+{
+	settings.showQueue = false;
+	//if(queue->active)
+		queue->deactivate();
+
+	if(!queue->embedded)
+	{
+		moveBy(Point(0, -queue->pos.h / 2));
+		GH.totalRedraw();
+	}
+}
+
+void CBattleInterface::showQueue()
+{
+	settings.showQueue = true;
+	//if(!queue->active)
+		queue->activate();
+
+	if(!queue->embedded)
+	{
+		moveBy(Point(0, +queue->pos.h / 2));
+		GH.totalRedraw();
+	}
+}
+
 void CBattleHero::show(SDL_Surface *to)
 {
 	//animation of flag
@@ -3706,3 +3750,119 @@ void CBattleInterface::SiegeHelper::printPartOfWall(SDL_Surface * to, int what)
 		blitAt(walls[what], pos.x, pos.y, to);
 	}
 }
+
+void CStackQueue::update()
+{
+	stacksSorted.clear();
+	LOCPLINT->cb->getStackQueue(stacksSorted, QUEUE_SIZE);
+	for (int i = 0; i < QUEUE_SIZE ; i++)
+	{
+		stackBoxes[i]->setStack(stacksSorted[i]);
+	}
+}
+
+CStackQueue::CStackQueue(bool Embedded)
+:embedded(Embedded)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+	if(embedded)
+	{
+		box = NULL;
+		bg = NULL;
+		pos.w = QUEUE_SIZE * 37;
+		pos.h = 32; //height of small creature img
+		pos.x = screen->w/2 - pos.w/2;
+		pos.y = (screen->h - 600)/2 + 10;
+	}
+	else
+	{
+		box = BitmapHandler::loadBitmap("CHRROP.pcx");
+		bg = BitmapHandler::loadBitmap("DIBOXPI.pcx");
+		pos.w = 600;
+		pos.h = bg->h;
+	}
+
+	stackBoxes.resize(QUEUE_SIZE);
+	for (int i = 0; i < QUEUE_SIZE; i++)
+	{
+		stackBoxes[i] = new StackBox(box);
+		stackBoxes[i]->pos.x += 6 + (embedded ? 37 : 79)*i;
+	}
+}
+
+CStackQueue::~CStackQueue()
+{
+	SDL_FreeSurface(box);
+}
+
+void CStackQueue::showAll( SDL_Surface *to )
+{
+	if(bg)
+	{
+		for (int w = 0; w < pos.w; w += bg->w)
+		{
+			blitAtLoc(bg, w, 0, to);		
+		}
+	}
+	CIntObject::showAll(to);
+}
+
+void CStackQueue::StackBox::showAll( SDL_Surface *to )
+{
+	assert(my);
+	if(bg)
+	{
+		graphics->blueToPlayersAdv(bg, my->owner);
+		//SDL_UpdateRect(bg, 0, 0, 0, 0);
+		blitAt(bg, pos, to);
+		blitAt(graphics->bigImgs[my->creature->idNumber], pos.x +9, pos.y + 1, to);
+		printAtMiddleLoc(makeNumberShort(my->amount), pos.w/2, pos.h - 12, FONT_MEDIUM, zwykly, to);
+	}
+	else
+	{
+		blitAt(graphics->smallImgs[-2], pos, to);
+		blitAt(graphics->smallImgs[my->creature->idNumber], pos, to);
+		const SDL_Color &ownerColor = (my->owner == 255 ? *graphics->neutralColor : graphics->playerColors[my->owner]);
+		CSDL_Ext::drawBorder(to, pos, int3(ownerColor.r, ownerColor.g, ownerColor.b));
+		printAtMiddleLoc(makeNumberShort(my->amount), pos.w/2, pos.h - 8, FONT_TINY, zwykly, to);
+	}
+}
+
+void CStackQueue::StackBox::setStack( const CStack *nStack )
+{
+	my = nStack;
+}
+
+CStackQueue::StackBox::StackBox(SDL_Surface *BG)
+:bg(BG), my(NULL)
+{
+	if(bg)
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+	else
+	{
+		pos.w = pos.h = 32;
+	}
+
+	pos.y += 2;
+}
+
+CStackQueue::StackBox::~StackBox()
+{
+}
+
+void CStackQueue::StackBox::hover( bool on )
+{
+
+}
+
+BattleSettings::BattleSettings()
+{
+	printCellBorders = true;
+	printStackRange = true;
+	animSpeed = 2;
+	printMouseShadow = true;
+	showQueue = true;
+}

+ 38 - 10
client/CBattleInterface.h

@@ -316,22 +316,16 @@ public:
 
 struct BattleSettings
 {
-	BattleSettings()
-	{
-		printCellBorders = true;
-		printStackRange = true;
-		animSpeed = 2;
-		printMouseShadow = true;
-	}
+	BattleSettings();
 	bool printCellBorders; //if true, cell borders will be printed
 	bool printStackRange; //if true,range of active stack will be printed
 	int animSpeed; //speed of animation; 1 - slowest, 2 - medium, 4 - fastest
 	bool printMouseShadow; //if true, hex under mouse will be shaded
-
+	bool showQueue;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & printCellBorders & printStackRange & animSpeed & printMouseShadow;
+		h & printCellBorders & printStackRange & animSpeed & printMouseShadow & showQueue;
 	}
 };
 
@@ -343,6 +337,37 @@ struct SBattleEffect
 	int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
 };
 
+class CStackQueue : public CIntObject
+{
+	class StackBox : public CIntObject
+	{
+	public:
+		const CStack *my;
+		SDL_Surface *bg;
+
+		void hover (bool on);
+		void showAll(SDL_Surface *to);
+		void setStack(const CStack *nStack);
+		StackBox(SDL_Surface *BG);
+		~StackBox();
+	};
+
+public:
+	static const int QUEUE_SIZE = 10;
+	const bool embedded;
+	std::vector<const CStack *> stacksSorted;
+	std::vector<StackBox *> stackBoxes;
+
+	SDL_Surface *box;
+	SDL_Surface *bg;
+
+	void showAll(SDL_Surface *to);
+	CStackQueue(bool Embedded);
+	~CStackQueue();
+	void update();
+	//void showAll(SDL_Surface *to);
+};
+
 class CBattleInterface : public CIntObject
 {
 private:
@@ -351,6 +376,7 @@ private:
 		* bWait, * bDefence, * bConsoleUp, * bConsoleDown;
 	CBattleConsole * console;
 	CBattleHero * attackingHero, * defendingHero; //fighting heroes
+	CStackQueue *queue;
 	CCreatureSet * army1, * army2; //fighting armies
 	CGHeroInstance * attackingHeroInstance, * defendingHeroInstance;
 	std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
@@ -428,7 +454,6 @@ public:
 	CondSh<BattleAction *> *givenCommand; //data != NULL if we have i.e. moved current unit
 	bool myTurn; //if true, interface is active (commands can be ordered
 	CBattleResultWindow * resWindow; //window of end of battle
-	bool showStackQueue; //if true, queue of stacks will be shown
 
 	bool moveStarted; //if true, the creature that is already moving is going to make its first step
 	int moveSh;		  // sound handler used when moving a unit
@@ -471,6 +496,9 @@ public:
 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
 	void castThisSpell(int spellID); //called when player has chosen a spell from spellbook
 	void displayEffect(ui32 effect, int destTile); //displays effect of a spell on the battlefield; affected: true - attacker. false - defender
+	void endAction(const BattleAction* action);
+	void hideQueue();
+	void showQueue();
 
 	friend class CBattleHex;
 	friend class CBattleResultWindow;

+ 29 - 16
client/CCastleInterface.cpp

@@ -524,6 +524,8 @@ void CCastleInterface::close()
 {
 	if(town->visitingHero)
 		LOCPLINT->adventureInt->select(town->visitingHero);
+	else
+		LOCPLINT->adventureInt->select(town);
 	LOCPLINT->castleInt = NULL;
 	GH.popIntTotally(this);
 	CGI->musich->stopMusic(5000);
@@ -906,6 +908,7 @@ void CCastleInterface::recreateBuildings()
 	}
 
 	//ship in shipyard
+	bool isThereShip = false;
 	if(vstd::contains(town->builtBuildings,6))
 	{
 		std::vector <const CGObjectInstance *> vobjs = LOCPLINT->cb->getVisitableObjs(town->bestLocation());
@@ -914,6 +917,7 @@ void CCastleInterface::recreateBuildings()
 			Structure * st = CGI->townh->structures[town->subID][20];
 			buildings.push_back(new CBuildingRect(st));
 			s.insert(std::pair<int,int>(st->group,st->ID));
+			isThereShip = true;
 		}
 	}
 
@@ -943,26 +947,35 @@ void CCastleInterface::recreateBuildings()
 		}
 	}
 	//code for the shipyard in the Castle
-	else if((town->subID == 0) && (town->builtBuildings.find(6)!=town->builtBuildings.end()))
+	else if(town->subID == 0)
 	{
-		CBuildingRect *shipyard = NULL;
-		for(size_t i=0;i<buildings.size();i++)
+		int shipID = 0;
+		if(isThereShip)
+			shipID = 20;
+		else if(vstd::contains(town->builtBuildings, 6))
+			shipID = 6;
+
+		if(shipID)
 		{
-			if(buildings[i]->str->ID==6)
+			CBuildingRect *shipyard = NULL;
+			for(size_t i=0;i<buildings.size();i++)
 			{
-				shipyard=buildings[i];
-				break;
+				if(buildings[i]->str->ID==shipID)
+				{
+					shipyard=buildings[i];
+					break;
+				}
+			}
+			if(town->builtBuildings.find(8)!=town->builtBuildings.end()) //there is citadel
+			{
+				shipyard->offset = 1;
+				shipyard->max = shipyard->def->ourImages.size();
+			}
+			else
+			{
+				shipyard->offset = 0;
+				shipyard->max = 1;
 			}
-		}
-		if(town->builtBuildings.find(8)!=town->builtBuildings.end()) //there is citadel
-		{
-			shipyard->offset = 1;
-			shipyard->max = shipyard->def->ourImages.size();
-		}
-		else
-		{
-			shipyard->offset = 0;
-			shipyard->max = 1;
 		}
 	}
 

+ 1 - 16
client/CPlayerInterface.cpp

@@ -1085,22 +1085,7 @@ void CPlayerInterface::actionFinished(const BattleAction* action)
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	delete curAction;
 	curAction = NULL;
-	//if((action->actionType==2 || (action->actionType==6 && action->destinationTile!=cb->battleGetPos(action->stackNumber)))) //activating interface when move is finished
-	{
-		battleInt->activate();
-	}
-	if(action->actionType == 1)
-	{
-		if(action->side)
-			battleInt->defendingHero->setPhase(0);
-		else
-			battleInt->attackingHero->setPhase(0);
-	}
-	if(action->actionType == 2 && battleInt->creAnims[action->stackNumber]->getType() != 2) //walk or walk & attack
-	{
-		battleInt->pendingAnims.push_back(std::make_pair(new CBattleMoveEnd(battleInt, action->stackNumber, action->destinationTile), false));
-	}
-
+	battleInt->endAction(action);
 }
 
 BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn of that stack

+ 25 - 3
client/GUIBase.cpp

@@ -631,6 +631,20 @@ const Rect & CIntObject::center()
 	return center(pos);
 }
 
+void CIntObject::moveBy( const Point &p, bool propagate /*= true*/ )
+{
+	pos.x += p.x;
+	pos.y += p.y;
+	if(propagate)
+		for(size_t i = 0; i < children.size(); i++)
+			children[i]->moveBy(p, propagate);
+}
+
+void CIntObject::moveTo( const Point &p, bool propagate /*= true*/ )
+{
+	moveBy(Point(p.x - pos.x, p.y - pos.y), propagate);
+}
+
 CPicture::CPicture( SDL_Surface *BG, int x, int y, bool Free )
 {
 	bg = BG; 
@@ -647,8 +661,15 @@ CPicture::CPicture( const std::string &bmpname, int x, int y )
 	freeSurf = true;;
 	pos.x += x;
 	pos.y += y;
-	pos.w = bg->w;
-	pos.h = bg->h;
+	if(bg)
+	{
+		pos.w = bg->w;
+		pos.h = bg->h;
+	}
+	else
+	{
+		pos.w = pos.h = 0;
+	}
 }
 
 CPicture::~CPicture()
@@ -659,7 +680,8 @@ CPicture::~CPicture()
 
 void CPicture::showAll( SDL_Surface * to )
 {
-	blitAt(bg, pos, to);
+	if(bg)
+		blitAt(bg, pos, to);
 }
 
 ObjectConstruction::ObjectConstruction( CIntObject *obj )

+ 6 - 0
client/GUIBase.h

@@ -191,6 +191,10 @@ struct Rect : public SDL_Rect
 		y -= p.y;
 		return *this;
 	}
+	template<typename T> Rect operator-(const T &t)
+	{
+		return Rect(x + t.x, y + t.y, w, h);
+	}
 	Rect operator&(const Rect &p) const //rect intersection
 	{
 		bool intersect = true;
@@ -355,6 +359,8 @@ public:
 	bool isItInLoc(const SDL_Rect &rect, const Point &p);
 	const Rect & center(const Rect &r); //sets pos so that r will be in the center of screen, returns new position
 	const Rect & center(); //centers when pos.w and pos.h are set, returns new position
+	void moveBy(const Point &p, bool propagate = true);
+	void moveTo(const Point &p, bool propagate = true);
 };
 
 //class for binding keys to left mouse button clicks

+ 3 - 3
client/GUIClasses.cpp

@@ -1885,7 +1885,7 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, co
 	max = new AdventureMapButton(CGI->generaltexth->zelp[553],boost::bind(&CRecruitmentWindow::Max,this),pos.x+134,pos.y+313,"IRCBTNS.DEF",SDLK_m);
 	buy = new AdventureMapButton(CGI->generaltexth->zelp[554],boost::bind(&CRecruitmentWindow::Buy,this),pos.x+212,pos.y+313,"IBY6432.DEF",SDLK_RETURN);
 	cancel = new AdventureMapButton(CGI->generaltexth->zelp[555],boost::bind(&CRecruitmentWindow::Cancel,this),pos.x+290,pos.y+313,"ICN6432.DEF",SDLK_ESCAPE);
-	slider = new CSlider(pos.x+176,pos.y+279,135,boost::bind(&CRecruitmentWindow::sliderMoved,this, _1),1,0,0,true);
+	slider = new CSlider(pos.x+176,pos.y+279,135,boost::bind(&CRecruitmentWindow::sliderMoved,this, _1),0,0,0,true);
 
 	initCres();
 
@@ -1990,7 +1990,7 @@ CSplitWindow::CSplitWindow(int cid, int max, CGarrisonInt *Owner, int Last, int
 	ok = new AdventureMapButton("","",boost::bind(&CSplitWindow::split,this),pos.x+20,pos.y+263,"IOK6432.DEF",SDLK_RETURN);
 	cancel = new AdventureMapButton("","",boost::bind(&CSplitWindow::close,this),pos.x+214,pos.y+263,"ICN6432.DEF",SDLK_ESCAPE);
 	int sliderPositions = max - (last>=0) - (last==2);
-	slider = new CSlider(pos.x+21,pos.y+194,257,boost::bind(&CSplitWindow::sliderMoved,this,_1),1,sliderPositions,val,true);
+	slider = new CSlider(pos.x+21,pos.y+194,257,boost::bind(&CSplitWindow::sliderMoved,this,_1),0,sliderPositions,val,true);
 	a1 = max-val;
 	a2 = val;
 	anim = new CCreaturePic(&CGI->creh->creatures[cid]);
@@ -2875,7 +2875,7 @@ CSystemOptionsWindow::~CSystemOptionsWindow()
 
 void CSystemOptionsWindow::bquitf()
 {
-	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], std::vector<SComponent*>(), boost::bind(exit, 0), boost::bind(&CSystemOptionsWindow::activate, this), false);
+	LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[578], std::vector<SComponent*>(), boost::bind(exit, 0), 0, false);
 }
 
 void CSystemOptionsWindow::breturnf()

+ 5 - 4
client/Graphics.cpp

@@ -527,10 +527,11 @@ void Graphics::blueToPlayersAdv(SDL_Surface * sur, int player)
 			return;
 		}
 
-		for(int i=0; i<32; ++i)
-		{
-			sur->format->palette->colors[224+i] = palette[i];
-		}
+		SDL_SetColors(sur, palette, 224, 32);
+		//for(int i=0; i<32; ++i)
+		//{
+		//	sur->format->palette->colors[224+i] = palette[i];
+		//}
 	}
 	else if(sur->format->BitsPerPixel == 24) //should never happen in general
 	{

+ 7 - 1
client/SDL_Extensions.cpp

@@ -829,7 +829,7 @@ void CSDL_Ext::update(SDL_Surface * what)
 	if(what)
 		SDL_UpdateRect(what, 0, 0, what->w, what->h);
 }
-void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, int3 color)
+void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color)
 {
 	for(int i=0;i<w;i++)
 	{
@@ -842,6 +842,12 @@ void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, int3 co
 		SDL_PutPixelWithoutRefresh(sur,x+w-1,y+i,color.x,color.y,color.z);
 	}
 }
+
+void CSDL_Ext::drawBorder( SDL_Surface * sur, const SDL_Rect &r, const int3 &color )
+{
+	drawBorder(sur, r.x, r.y, r.w, r.h, color);
+}
+
 void CSDL_Ext::setPlayerColor(SDL_Surface * sur, unsigned char player)
 {
 	if(player==254)

+ 2 - 1
client/SDL_Extensions.h

@@ -124,7 +124,8 @@ namespace CSDL_Ext
 	void printAtMiddleWB(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor=tytulowy, SDL_Surface * dst=screen, bool refrsh = false);
 
 	void update(SDL_Surface * what = screen); //updates whole surface (default - main screen)
-	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, int3 color);
+	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color);
+	void drawBorder(SDL_Surface * sur, const SDL_Rect &r, const int3 &color);
 	void setPlayerColor(SDL_Surface * sur, unsigned char player); //sets correct color of flags; -1 for neutral
 	std::string processStr(std::string str, std::vector<std::string> & tor); //replaces %s in string
 	SDL_Surface * newSurface(int w, int h, SDL_Surface * mod=screen); //creates new surface, with flags/format same as in surface given

+ 1 - 1
config/CREDEFS.TXT

@@ -167,7 +167,7 @@ Names_of_Units_DEFS_in_order_of_ID
 165 ZM165GD.DEF
 166 ZM166GD.DEF
 167 ZM167GD.DEF
-168 ZM168GD.DEF
+168 ZM168DG.DEF
 169 ZM169ZL.DEF
 170 ZM170SW.DEF
 171 ZM171SR.DEF

+ 1 - 0
config/buildings2.txt

@@ -14,6 +14,7 @@ CASTLE 0
 37
 16
 6
+20
 18
 19
 34

+ 3 - 0
config/buildings4.txt

@@ -7,6 +7,9 @@ GROUP
 3
 4
 GROUP
+6
+20
+GROUP
 7
 8
 9

+ 1 - 0
config/cr_abils.txt

@@ -160,6 +160,7 @@
 + 117 SPELL_DAMAGE_REDUCTION 95 -1 0		//diamond golems reduce dmg from spells
 + 121 LEVEL_SPELL_IMMUNITY 5 0 0	//magic elementals are immune to all spells
 + 123 DOUBLE_WIDE 0 0 0 			//ice elemental should be treated as double-wide
+- 133 FLYING						//Crystal Dragons do not fly
 + 140 DOUBLE_WIDE 0 0 0 			//boar should be treated as double-wide
 + 142 DOUBLE_WIDE 0 0 0 			//nomads should be treated as double-wide
 + 144 FULL_HP_REGENERATION 0 0 0 			//troll

+ 1 - 1
global.h

@@ -20,7 +20,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.73b")
+#define NAME_VER ("VCMI 0.73c")
 extern std::string NAME; //full name
 extern std::string NAME_AFFIX; //client / server
 #define CONSOLE_LOGGING_LEVEL 5

+ 1 - 1
hch/CObjectHandler.cpp

@@ -1582,7 +1582,7 @@ int3 CGTownInstance::getSightCenter() const
 
 void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
 {
-	offsets += int3(-1,3,0), int3(-3,3,0);
+	offsets += int3(-1,2,0), int3(-3,2,0);
 }
 
 void CGTownInstance::fightOver( const CGHeroInstance *h, BattleResult *result ) const

+ 85 - 59
lib/CGameState.cpp

@@ -661,36 +661,36 @@ std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool
 	return std::make_pair(path, dist[dest]);
 }
 
-int CStack::valOfFeatures(StackFeature::ECombatFeatures type, int subtype) const
+int CStack::valOfFeatures(StackFeature::ECombatFeatures type, int subtype, int turn) const
 {
 	int ret = 0;
 	if(subtype == -1024) //any subtype
 	{
 		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type)
+			if(i->type == type	&&  (!turn || i->turnsRemain > turn))
 				ret += i->value;
 	}
 	else //given subtype
 	{
 		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type && i->subtype == subtype)
+			if(i->type == type && i->subtype == subtype && (!turn || i->turnsRemain > turn))
 				ret += i->value;
 	}
 	return ret;
 }
 
-bool CStack::hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype) const
+bool CStack::hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype, int turn) const
 {
 	if(subtype == -1024) //any subtype
 	{
 		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type)
+			if(i->type == type && (!turn || i->turnsRemain > turn))
 				return true;
 	}
 	else //given subtype
 	{
 		for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
-			if(i->type == type && i->subtype == subtype)
+			if(i->type == type && i->subtype == subtype && (!turn || i->turnsRemain > turn))
 				return true;
 	}
 	return false;
@@ -712,19 +712,19 @@ CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
 	state.insert(ALIVE);
 }
 
-ui32 CStack::Speed() const
+ui32 CStack::Speed( int turn /*= 0*/ ) const
 {
-	if(hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //war machnes cannot move
+	if(hasFeatureOfType(StackFeature::SIEGE_WEAPON, -1024, turn)) //war machnes cannot move
 		return 0;
 
 	int speed = creature->speed;
 
-	speed += valOfFeatures(StackFeature::SPEED_BONUS);
+	speed += valOfFeatures(StackFeature::SPEED_BONUS, -1024, turn);
 
 	int percentBonus = 0;
 	for(int g=0; g<features.size(); ++g)
 	{
-		if(features[g].type == StackFeature::SPEED_BONUS)
+		if(features[g].type == StackFeature::SPEED_BONUS, -1024, turn)
 		{
 			percentBonus += features[g].additionalInfo;
 		}
@@ -748,11 +748,12 @@ ui32 CStack::Speed() const
 	return speed;
 }
 
-const CStack::StackEffect * CStack::getEffect(ui16 id) const
+const CStack::StackEffect * CStack::getEffect( ui16 id, int turn /*= 0*/ ) const
 {
 	for (unsigned int i=0; i< effects.size(); i++)
 		if(effects[i].id == id)
-			return &effects[i];
+			if(!turn || effects[i].turnsRemain > turn)
+				return &effects[i];
 	return NULL;
 }
 
@@ -838,22 +839,25 @@ ui16 CStack::MaxHealth() const
 	return creature->hitPoints + valOfFeatures(StackFeature::HP_BONUS);
 }
 
-bool CStack::willMove() const
+bool CStack::willMove(int turn /*= 0*/) const
 {
-	return !vstd::contains(state, DEFENDING)
-		&& !moved()
-		&& canMove();
+	return ( turn ? true : !vstd::contains(state, DEFENDING) )
+		&& !moved(turn)
+		&& canMove(turn);
 }
 
-bool CStack::canMove() const
+bool CStack::canMove( int turn /*= 0*/ ) const
 {
 	return alive()
-		&& ! hasFeatureOfType(StackFeature::NOT_ACTIVE); //eg. Ammo Cart
+		&& !hasFeatureOfType(StackFeature::NOT_ACTIVE, -1024, turn); //eg. Ammo Cart
 }
 
-bool CStack::moved() const
+bool CStack::moved( int turn /*= 0*/ ) const
 {
-	return vstd::contains(state, MOVED);
+	if(!turn)
+		return vstd::contains(state, MOVED);
+	else
+		return false;
 }
 
 CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const
@@ -2150,6 +2154,15 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 			int cost = getMovementCost(hero, cp->coord, dp.coord, movement);
 			int remains = movement - cost;
 
+			if(remains < 0)
+			{
+				//occurs rarely, when hero with low movepoints tries to go leave the road
+				turn++;
+				movement = hero->maxMovePoints(ct.tertype != TerrainTile::water);
+				cost = getMovementCost(hero, cp->coord, dp.coord, movement); //cost must be updated, movement points changed :(
+				remains = movement - cost;
+			}
+
 			if(dp.turns==0xff		//we haven't been here before
 				|| dp.turns > turn
 				|| (dp.turns >= turn  &&  dp.moveRemains < remains)) //this route is faster
@@ -2760,7 +2773,7 @@ bool CGameState::battleCanShoot(int ID, int dest)
 const CStack * BattleInfo::getNextStack() const
 {
 	std::vector<const CStack *> hlp;
-	getStackQueue(hlp, 1, 2);
+	getStackQueue(hlp, 1, -1);
 
 	if(hlp.size())
 		return hlp[0];
@@ -2768,7 +2781,7 @@ const CStack * BattleInfo::getNextStack() const
 		return NULL;
 }
 
-static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
+static const CStack *takeStack(std::vector<const CStack *> &st, int &curside, int turn)
 {
 	const CStack *ret = NULL;
 	unsigned i, //fastest stack
@@ -2782,7 +2795,7 @@ static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
 		return NULL;
 
 	const CStack *fastest = st[i], *other = NULL;
-	int bestSpeed = fastest->Speed();
+	int bestSpeed = fastest->Speed(turn);
 
 	if(fastest->attackerOwned != curside)
 	{
@@ -2793,7 +2806,7 @@ static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
 		for(j = i + 1; j < st.size(); j++)
 		{
 			if(!st[j]) continue;
-			if(st[j]->attackerOwned != curside || st[j]->Speed() != bestSpeed)
+			if(st[j]->attackerOwned != curside || st[j]->Speed(turn) != bestSpeed)
 				break;
 		}
 
@@ -2804,7 +2817,7 @@ static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
 		else
 		{
 			other = st[j];
-			if(other->Speed() != bestSpeed)
+			if(other->Speed(turn) != bestSpeed)
 				ret = fastest;
 			else
 				ret = other;
@@ -2821,43 +2834,55 @@ static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
 	return ret;
 }
 
-void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, int mode, int lastMoved ) const 
+void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, int turn /*= 0*/, int lastMoved /*= -1*/ ) const
 {
 	//we'll split creatures with remaining movement to 4 parts
 	std::vector<const CStack *> phase[4]; //0 - turrets/catapult, 1 - normal (unmoved) creatures, other war machines, 2 - waited cres that had morale, 3 - rest of waited cres
 	int toMove = 0; //how many stacks still has move
+	const CStack *active = getStack(activeStack);
+
+	//active stack hasn't taken any action yet - must be placed at the beginning of queue, no matter what
+	if(!turn && active && active->willMove() && !vstd::contains(active->state, WAITING))
+	{
+		out.push_back(active);
+		if(out.size() == howMany)
+			return;
+	}
+
 
 	for(unsigned int i=0; i<stacks.size(); ++i)
 	{
 		const CStack * const s = stacks[i];
-		if(mode != 1  &&  s->willMove()
-			|| mode == 1  &&  s->canMove())
+		if(turn <= 0 && !s->willMove() //we are considering current round and stack won't move
+			|| turn > 0 && !s->canMove(turn) //stack won't be able to move in later rounds
+			|| turn <= 0 && s == active) //it's active stack already added at the beginning of queue
 		{
-			int p = -1; //in which phase this tack will move?
-			if(!mode && vstd::contains(s->state, WAITING))
-			{
-				if(vstd::contains(s->state, HAD_MORALE))
-					p = 2;
-				else
-					p = 3;
-			}
-			else if(vstd::contains(s->state, StackFeature::SIEGE_WEAPON)	 //catapult and turrets are first
-				&& (s->creature->idNumber == 145  ||  s->creature->idNumber == 149))
-			{
-				p = 0;
-			}
-			else
-			{
-				p = 1;
-			}
+			continue;
+		}
 
-			phase[p].push_back(s);
-			toMove++;
+		int p = -1; //in which phase this tack will move?
+		if(turn <= 0 && vstd::contains(s->state, WAITING)) //consider waiting state only for ongoing round
+		{
+			if(vstd::contains(s->state, HAD_MORALE))
+				p = 2;
+			else
+				p = 3;
 		}
+		else if(s->creature->idNumber == 145  ||  s->creature->idNumber == 149) //catapult and turrets are first
+		{
+			p = 0;
+		}
+		else
+		{
+			p = 1;
+		}
+
+		phase[p].push_back(s);
+		toMove++;
 	}
 
 	for(int i = 0; i < 4; i++)
-		std::sort(phase[i].begin(), phase[i].end(), CMP_stack(i));
+		std::sort(phase[i].begin(), phase[i].end(), CMP_stack(i, turn > 0 ? turn : 0));
 
 	for(size_t i = 0; i < phase[0].size() && i < howMany; i++)
 		out.push_back(phase[0][i]);
@@ -2867,12 +2892,12 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
 
 	if(lastMoved == -1)
 	{
-		const CStack *current = getStack(activeStack);
-		if(current)
+		if(active)
 		{
-			lastMoved = !current->attackerOwned;
-			if(!current->willMove() || mode == 2)
-				lastMoved = !lastMoved;
+			if(out.size() && out.front() == active)
+				lastMoved = active->attackerOwned;
+			else
+				lastMoved = active->attackerOwned;
 		}
 		else
 		{
@@ -2883,14 +2908,14 @@ void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, i
 	int pi = 1;
 	while(out.size() < howMany)
 	{
-		const CStack *hlp = takeStack(phase[pi], lastMoved);
+		const CStack *hlp = takeStack(phase[pi], lastMoved, turn);
 		if(!hlp)
 		{
 			pi++;
 			if(pi > 3)
 			{
-				if(mode != 2)
-					getStackQueue(out, howMany, 1, lastMoved);
+				//if(turn != 2)
+					getStackQueue(out, howMany, turn + 1, lastMoved);
 				return;
 			}
 		}
@@ -3003,7 +3028,7 @@ bool CMP_stack::operator()( const CStack* a, const CStack* b )
 		//TODO? turrets order
 	case 1: //fastest first, upper slot first
 		{
-			int as = a->Speed(), bs = b->Speed();
+			int as = a->Speed(turn), bs = b->Speed(turn);
 			if(as != bs)
 				return as > bs;
 			else
@@ -3013,7 +3038,7 @@ bool CMP_stack::operator()( const CStack* a, const CStack* b )
 		//TODO: should be replaced with order of receiving morale!
 	case 3: //fastest last, upper slot first
 		{
-			int as = a->Speed(), bs = b->Speed();
+			int as = a->Speed(turn), bs = b->Speed(turn);
 			if(as != bs)
 				return as < bs;
 			else
@@ -3026,9 +3051,10 @@ bool CMP_stack::operator()( const CStack* a, const CStack* b )
 
 }
 
-CMP_stack::CMP_stack( int Phase /*= 1*/ )
+CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn )
 {
 	phase = Phase;
+	turn = Turn;
 }
 
 PlayerState::PlayerState() 

+ 10 - 9
lib/CGameState.h

@@ -138,7 +138,7 @@ struct DLL_EXPORT BattleInfo
 			& castSpells & si;
 	}
 	const CStack * getNextStack() const; //which stack will have turn after current one
-	void getStackQueue(std::vector<const CStack *> &out, int howMany, int mode = 0, int lastMoved = -1) const; //returns stack in order of their movement action
+	void getStackQueue(std::vector<const CStack *> &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action
 	CStack * getStack(int stackID, bool onlyAlive = true);
 	const CStack * getStack(int stackID, bool onlyAlive = true) const;
 	CStack * getStackT(int tileID, bool onlyAlive = true);
@@ -191,17 +191,17 @@ public:
 	};
 	std::vector<StackEffect> effects;
 
-	int valOfFeatures(StackFeature::ECombatFeatures type, int subtype = -1024) const;//subtype -> subtype of bonus, if -1024 then any
-	bool hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype = -1024) const; //determines if stack has a bonus of given type (and optionally subtype)
+	int valOfFeatures(StackFeature::ECombatFeatures type, int subtype = -1024, int turn = 0) const;//subtype -> subtype of bonus, if -1024 then any
+	bool hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype = -1024, int turn = 0) const; //determines if stack has a bonus of given type (and optionally subtype)
 
 	CStack(CCreature * C, int A, int O, int I, bool AO, int S); //c-tor
 	CStack() : ID(-1), creature(NULL), amount(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1) {} //c-tor
-	const StackEffect * getEffect(ui16 id) const; //effect id (SP)
+	const StackEffect * getEffect(ui16 id, int turn = 0) const; //effect id (SP)
 	ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack
-	bool willMove() const; //if stack has remaining move this turn
-	bool moved() const; //if stack was already moved this turn
-	bool canMove() const; //if stack can move
-	ui32 Speed() const; //get speed of creature with all modificators
+	bool willMove(int turn = 0) const; //if stack has remaining move this turn
+	bool moved(int turn = 0) const; //if stack was already moved this turn
+	bool canMove(int turn = 0) const; //if stack can move
+	ui32 Speed(int turn = 0) const; //get speed of creature with all modificators
 	si8 Morale() const; //get morale of stack with all modificators
 	si8 Luck() const; //get luck of stack with all modificators
 	si32 Attack() const; //get attack of stack with all modificators
@@ -236,10 +236,11 @@ public:
 class DLL_EXPORT CMP_stack
 {
 	int phase; //rules of which phase will be used
+	int turn;
 public:
 
 	bool operator ()(const CStack* a, const CStack* b);
-	CMP_stack(int Phase = 1);
+	CMP_stack(int Phase = 1, int Turn = 0);
 };
 
 struct UpgradeInfo

+ 1 - 1
lib/Connection.h

@@ -19,7 +19,7 @@
 #include <boost/mpl/identity.hpp>
 
 #include <boost/type_traits/is_array.hpp>
-const ui32 version = 708;
+const ui32 version = 709;
 class CConnection;
 namespace mpl = boost::mpl;
 

+ 10 - 1
server/CGameHandler.cpp

@@ -19,6 +19,7 @@
 #include <boost/thread/shared_mutex.hpp>
 #include <boost/assign/list_of.hpp>
 #include <fstream>
+#include <boost/system/system_error.hpp>
 
 /*
  * CGameHandler.cpp, part of VCMI engine
@@ -623,6 +624,11 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 			pack = NULL;
 		}
 	}
+	catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
+	{
+		tlog1 << e.what() << std::endl;
+		end2 = true;
+	}
 	HANDLE_EXCEPTION(end2 = true);
 handleConEnd:
 	tlog1 << "Ended handling connection\n";
@@ -637,6 +643,9 @@ int CGameHandler::moveStack(int stack, int dest)
 	CStack *curStack = gs->curB->getStack(stack),
 		*stackAtEnd = gs->curB->getStackT(dest);
 
+	assert(curStack);
+	assert(dest < BFIELD_SIZE);
+
 	//initing necessary tables
 	bool accessibility[BFIELD_SIZE];
 	std::vector<int> accessible = gs->curB->getAccessibility(curStack->ID, false);
@@ -1393,7 +1402,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 	//OR hero is on land and dest is water and (there is not present only one object - boat)
 	if((t.tertype == TerrainTile::rock  ||  (t.blocked && !t.visitable)) 
 			&& complain("Cannot move hero, destination tile is blocked!") 
-		|| (!h->boat && !h->canWalkOnSea() && t.tertype == TerrainTile::water && (t.visitableObjects.size() != 1 ||  t.visitableObjects.front()->ID != 8)) 
+		|| (!h->boat && !h->canWalkOnSea() && t.tertype == TerrainTile::water && (t.visitableObjects.size() != 1 ||  (t.visitableObjects.front()->ID != 8 && t.visitableObjects.front()->ID != HEROI_TYPE)))  //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land)
 			&& complain("Cannot move hero, destination tile is on water!")
 		|| (h->boat && t.tertype != TerrainTile::water && t.blocked)
 			&& complain("Cannot disembark hero, tile is blocked!")

+ 6 - 1
server/CVCMIServer.cpp

@@ -258,6 +258,11 @@ int main(int argc, char** argv)
 			server.start();
 		}
 		io_service.run();
-	} HANDLE_EXCEPTION
+	} 
+	catch(boost::system::system_error &e) //for boost errors just log, not crash - probably client shut down connection
+	{
+		tlog1 << e.what() << std::endl;
+		end2 = true;
+	}HANDLE_EXCEPTION
   return 0;
 }