瀏覽代碼

* fixed crash on clicking on the map frame
* battle settings will be stored
* fixed calculating battle casualties
* fixed crash when clicking on enemy stack without moving mouse when we receive action
* fixed issue when splitting stack to the hero with only one creatures

Michał W. Urbańczyk 16 年之前
父節點
當前提交
307c20e7dc

+ 1 - 1
AI/GeniusAI/CGeniusAI.cpp

@@ -1029,7 +1029,7 @@ void CGeniusAI::battleEnd(BattleResult *br)
 		case 2:	std::cout << "It's a draw." << std::endl;break;
 	};
 	cout << "lost ";
-	for(std::set<std::pair<ui32,si32> >::iterator i = br->casualties[0].begin(); i !=br->casualties[0].end();i++)
+	for(std::map<ui32,si32>::iterator i = br->casualties[0].begin(); i !=br->casualties[0].end();i++)
 		cout << i->second << " " << VLC->creh->creatures[i->first].namePl << endl;
 				
 	delete m_battleLogic;

+ 12 - 3
client/CAdvmapInterface.cpp

@@ -471,7 +471,7 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
 	if ((down==false) || indeterminate(down))
 		return;
 	int3 mp = whichTileIsIt();
-	if ((mp.x<0) || (mp.y<0))
+	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
@@ -660,11 +660,20 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
 }
 void CTerrainRect::mouseMoved (const SDL_MouseMotionEvent & sEvent)
 {
-	int3 pom=LOCPLINT->adventureInt->verifyPos(whichTileIsIt(sEvent.x,sEvent.y));
-	if (pom!=curHoveredTile)
+	int3 tHovered = whichTileIsIt(sEvent.x,sEvent.y);
+	int3 pom = LOCPLINT->adventureInt->verifyPos(tHovered);
+
+	if(tHovered != pom) //tile outside the map
+	{
+		CGI->curh->changeGraphic(0, 0);
+		return;
+	}
+
+	if (pom != curHoveredTile)
 		curHoveredTile=pom;
 	else
 		return;
+
 	std::vector<std::string> temp = LOCPLINT->cb->getObjDescriptions(pom);
 	if (temp.size())
 	{

+ 120 - 42
client/CBattleInterface.cpp

@@ -47,7 +47,6 @@ extern SDL_Surface * screen;
 extern TTF_Font * TNRB16, *TNR, *GEOR13, *GEORXX, *GEORM, *GEOR16;
 extern SDL_Color zwykly;
 
-BattleSettings CBattleInterface::settings;
 CondSh<bool> CBattleInterface::animsAreDisplayed;
 
 struct CMP_stack2
@@ -481,7 +480,7 @@ void CDefenceAnim::nextFrame()
 
 	if(!owner->creAnims[stackID]->onLastFrameInGroup())
 	{
-		if( owner->creAnims[stackID]->getType() == 5 && (owner->animCount+1)%(4/CBattleInterface::settings.animSpeed)==0
+		if( owner->creAnims[stackID]->getType() == 5 && (owner->animCount+1)%(4/LOCPLINT->sysOpts.animSpeed)==0
 			&& !owner->creAnims[stackID]->onLastFrameInGroup() )
 		{
 			owner->creAnims[stackID]->incrementFrame();
@@ -694,7 +693,7 @@ void CBattleMoveStart::nextFrame()
 	}
 	else
 	{
-		if((owner->animCount+1)%(4/CBattleInterface::settings.animSpeed)==0)
+		if((owner->animCount+1)%(4/LOCPLINT->sysOpts.animSpeed)==0)
 			owner->creAnims[stackID]->incrementFrame();
 	}
 }
@@ -1047,7 +1046,7 @@ CBattleInterface::CBattleInterface(CCreatureSet * army1, CCreatureSet * army2, C
 	//create stack queue
 	bool embedQueue = screen->h < 700;
 	queue = new CStackQueue(embedQueue);
- 	if(!embedQueue && settings.showQueue)
+ 	if(!embedQueue && LOCPLINT->sysOpts.showQueue)
 	{
 		pos.y += queue->pos.h / 2; //center whole window
 		queue->moveTo(Point(pos.x, pos.y - queue->pos.h));
@@ -1306,21 +1305,24 @@ CBattleInterface::~CBattleInterface()
 
 void CBattleInterface::setPrintCellBorders(bool set)
 {
-	settings.printCellBorders = set;
+	LOCPLINT->sysOpts.printCellBorders = set;
+	LOCPLINT->sysOpts.settingsChanged();
 	redrawBackgroundWithHexes(activeStack);
 	GH.totalRedraw();
 }
 
 void CBattleInterface::setPrintStackRange(bool set)
 {
-	settings.printStackRange = set;
+	LOCPLINT->sysOpts.printStackRange = set;
+	LOCPLINT->sysOpts.settingsChanged();
 	redrawBackgroundWithHexes(activeStack);
 	GH.totalRedraw();
 }
 
 void CBattleInterface::setPrintMouseShadow(bool set)
 {
-	settings.printMouseShadow = set;
+	LOCPLINT->sysOpts.printMouseShadow = set;
+	LOCPLINT->sysOpts.settingsChanged();
 }
 
 void CBattleInterface::activate()
@@ -1345,7 +1347,7 @@ void CBattleInterface::activate()
 		attackingHero->activate();
 	if(defendingHero)
 		defendingHero->activate();
-	if(settings.showQueue)
+	if(LOCPLINT->sysOpts.showQueue)
 		queue->activate();
 
 	LOCPLINT->cingconsole->activate();
@@ -1373,7 +1375,7 @@ void CBattleInterface::deactivate()
 		attackingHero->deactivate();
 	if(defendingHero)
 		defendingHero->deactivate();
-	if(settings.showQueue)
+	if(LOCPLINT->sysOpts.showQueue)
 		queue->deactivate();
 
 	LOCPLINT->cingconsole->deactivate();
@@ -1399,7 +1401,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	{
 		//showing background
 		blitAt(background, pos.x, pos.y, to);
-		if(settings.printCellBorders)
+		if(LOCPLINT->sysOpts.printCellBorders)
 		{
 			CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, to, &pos);
 		}
@@ -1436,7 +1438,7 @@ void CBattleInterface::show(SDL_Surface * to)
 				std::set<ui16> shaded = spToCast.rangeInHexes(b, schoolLevel);
 				for(std::set<ui16>::iterator it = shaded.begin(); it != shaded.end(); ++it) //for spells with range greater then one hex
 				{
-					if(settings.printMouseShadow && (*it % BFIELD_WIDTH != 0) && (*it % BFIELD_WIDTH != 16))
+					if(LOCPLINT->sysOpts.printMouseShadow && (*it % BFIELD_WIDTH != 0) && (*it % BFIELD_WIDTH != 16))
 					{
 						int x = 14 + ((*it/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(*it%BFIELD_WIDTH) + pos.x;
 						int y = 86 + 42 * (*it/BFIELD_WIDTH) + pos.y;
@@ -1444,7 +1446,7 @@ void CBattleInterface::show(SDL_Surface * to)
 					}
 				}
 			}
-			else if(settings.printMouseShadow) //when not casting spell
+			else if(LOCPLINT->sysOpts.printMouseShadow) //when not casting spell
 			{
 				int x = 14 + ((b/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(b%BFIELD_WIDTH) + pos.x;
 				int y = 86 + 42 * (b/BFIELD_WIDTH) + pos.y;
@@ -1479,7 +1481,7 @@ void CBattleInterface::show(SDL_Surface * to)
 		int x = ((obstacles[b].pos/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(obstacles[b].pos%BFIELD_WIDTH) + pos.x + shift.first;
 		int y = 86 + 42 * (obstacles[b].pos/BFIELD_WIDTH) + pos.y + shift.second;
 		std::vector<Cimage> &images = idToObstacle[obstacles[b].ID]->ourImages; //reference to animation of obstacle
-		blitAt(images[((animCount+1)/(4/settings.animSpeed))%images.size()].bitmap, x, y, to);
+		blitAt(images[((animCount+1)/(4/LOCPLINT->sysOpts.animSpeed))%images.size()].bitmap, x, y, to);
 	}
 
 	//showing hero animations
@@ -1597,7 +1599,7 @@ void CBattleInterface::show(SDL_Surface * to)
 
 	Rect posWithQueue = Rect(pos.x, pos.y, 800, 600);
 
-	if(settings.showQueue)
+	if(LOCPLINT->sysOpts.showQueue)
 	{
 		if(!queue->embedded)
 		{
@@ -1622,10 +1624,12 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key)
 {
 	if(key.keysym.sym == SDLK_q && key.state == SDL_PRESSED)
 	{
-		if(settings.showQueue) //hide queue
+		if(LOCPLINT->sysOpts.showQueue) //hide queue
 			hideQueue();
 		else
 			showQueue();
+
+		LOCPLINT->sysOpts.settingsChanged();
 	}
 	else if(key.keysym.sym == SDLK_ESCAPE && spellDestSelectMode)
 	{
@@ -2154,9 +2158,20 @@ void CBattleInterface::giveCommand(ui8 action, ui16 tile, ui32 stack, si32 addit
 	ba->destinationTile = tile;
 	ba->stackNumber = stack;
 	ba->additionalInfo = additional;
-	givenCommand->setn(ba);
+
+	//some basic validations
+	switch(action)
+	{
+	case 6:
+		assert(LOCPLINT->cb->battleGetStackByPos(additional)); //stack to attack must exist
+	case 2: case 7: case 9:
+		assert(tile < BFIELD_SIZE);
+		break;
+	}
+
 	myTurn = false;
 	activeStack = -1;
+	givenCommand->setn(ba);
 }
 
 bool CBattleInterface::isTileAttackable(const int & number) const
@@ -2616,20 +2631,23 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile)
 
 void CBattleInterface::setAnimSpeed(int set)
 {
-	settings.animSpeed = set;
+	LOCPLINT->sysOpts.animSpeed = set;
+	LOCPLINT->sysOpts.settingsChanged();
 }
 
 int CBattleInterface::getAnimSpeed() const
 {
-	return settings.animSpeed;
+	return LOCPLINT->sysOpts.animSpeed;
+	LOCPLINT->sysOpts.settingsChanged();
 }
 
 void CBattleInterface::activateStack()
 {
 	activeStack = stackToActivate;
-	queue->update();
 	stackToActivate = -1;
 	myTurn = true;
+	queue->update();
+	GH.fakeMouseMove();
 	redrawBackgroundWithHexes(activeStack);
 	bWait->block(vstd::contains(LOCPLINT->cb->battleGetStackByID(activeStack)->state,WAITING)); //block waiting button if stack has been already waiting
 
@@ -2648,7 +2666,7 @@ void CBattleInterface::activateStack()
 
 float CBattleInterface::getAnimSpeedMultiplier() const
 {
-	switch(settings.animSpeed)
+	switch(LOCPLINT->sysOpts.animSpeed)
 	{
 	case 1:
 		return 3.5f;
@@ -2679,7 +2697,7 @@ void CBattleInterface::showAliveStack(int ID, const std::map<int, CStack> & stac
 	const CStack &curStack = stacks.find(ID)->second;
 	int animType = creAnims[ID]->getType();
 
-	int affectingSpeed = settings.animSpeed;
+	int affectingSpeed = LOCPLINT->sysOpts.animSpeed;
 	if(animType == 1 || animType == 2) //standing stacks should not stand faster :)
 		affectingSpeed = 2;
 	if(animType == 3 || animType == 7 || animType == 8 || animType == 9 || animType == 10 || animType == 11 || animType == 12 || animType == 13) //defend & attack should be slower
@@ -2836,10 +2854,10 @@ void CBattleInterface::redrawBackgroundWithHexes(int activeStack)
 
 	//preparating background graphic with hexes and shaded hexes
 	blitAt(background, 0, 0, backgroundWithHexes);
-	if(settings.printCellBorders)
+	if(LOCPLINT->sysOpts.printCellBorders)
 		CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL);
 
-	if(settings.printStackRange)
+	if(LOCPLINT->sysOpts.printStackRange)
 	{
 		for(size_t m=0; m<shadedHexes.size(); ++m) //rows
 		{
@@ -2946,9 +2964,9 @@ void CBattleInterface::endAction(const BattleAction* action)
 
 void CBattleInterface::hideQueue()
 {
-	settings.showQueue = false;
-	//if(queue->active)
-		queue->deactivate();
+	LOCPLINT->sysOpts.showQueue = false;
+
+	queue->deactivate();
 
 	if(!queue->embedded)
 	{
@@ -2959,9 +2977,9 @@ void CBattleInterface::hideQueue()
 
 void CBattleInterface::showQueue()
 {
-	settings.showQueue = true;
-	//if(!queue->active)
-		queue->activate();
+	LOCPLINT->sysOpts.showQueue = true;
+
+	queue->activate();
 
 	if(!queue->embedded)
 	{
@@ -2970,6 +2988,75 @@ void CBattleInterface::showQueue()
 	}
 }
 
+void CBattleInterface::startAction(const BattleAction* action)
+{
+	const CStack *stack = LOCPLINT->cb->battleGetStackByID(action->stackNumber);
+
+	if(stack)
+	{
+		queue->update();
+	}
+	else
+	{
+		assert(action->actionType == 1); //only cast spell is valid action without acting stack number
+	}
+
+	if(action->actionType == 2 
+		|| (action->actionType == 6 && action->destinationTile != stack->position))
+	{
+		moveStarted = true;
+		if(creAnims[action->stackNumber]->framesInGroup(20))
+		{
+			pendingAnims.push_back(std::make_pair(new CBattleMoveStart(this, action->stackNumber), false));
+		}
+	}
+
+
+	deactivate();
+
+	char txt[400];
+
+	if(action->actionType == 1)
+	{
+		if(action->side)
+			defendingHero->setPhase(4);
+		else
+			attackingHero->setPhase(4);
+		return;
+	}
+	if(!stack)
+	{
+		tlog1<<"Something wrong with stackNumber in actionStarted. Stack number: "<<action->stackNumber<<std::endl;
+		return;
+	}
+
+	int txtid = 0;
+	switch(action->actionType)
+	{
+	case 3: //defend
+		txtid = 120;
+		break;
+	case 8: //wait
+		txtid = 136;
+		break;
+	case 11: //bad morale
+		txtid = -34; //negative -> no separate singular/plural form		
+		displayEffect(30,stack->position);
+		break;
+	}
+
+	if(txtid > 0  &&  stack->amount != 1)
+		txtid++; //move to plural text
+	else if(txtid < 0)
+		txtid = -txtid;
+
+	if(txtid)
+	{
+		sprintf(txt, CGI->generaltexth->allTexts[txtid].c_str(),  (stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str(), 0);
+		console->addText(txt);
+	}
+}
+
 void CBattleHero::show(SDL_Surface *to)
 {
 	//animation of flag
@@ -3441,7 +3528,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		{
 			int xPos = 235 - (br.casualties[step].size()*32 + (br.casualties[step].size() - 1)*10)/2; //increment by 42 with each picture
 			int yPos = 344 + step*97;
-			for(std::set<std::pair<ui32,si32> >::const_iterator it=br.casualties[step].begin(); it!=br.casualties[step].end(); ++it)
+			for(std::map<ui32,si32>::const_iterator it=br.casualties[step].begin(); it!=br.casualties[step].end(); ++it)
 			{
 				blitAt(graphics->smallImgs[it->first], xPos, yPos, background);
 				std::ostringstream amount;
@@ -3554,11 +3641,11 @@ CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInt
 	graphics->blueToPlayersAdv(background, LOCPLINT->playerID);
 
 	viewGrid = new CHighlightableButton(boost::bind(&CBattleInterface::setPrintCellBorders, owner, true), boost::bind(&CBattleInterface::setPrintCellBorders, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[427].first)(3,CGI->generaltexth->zelp[427].first), CGI->generaltexth->zelp[427].second, false, "sysopchk.def", NULL, 185, 140, false);
-	viewGrid->select(owner->settings.printCellBorders);
+	viewGrid->select(LOCPLINT->sysOpts.printCellBorders);
 	movementShadow = new CHighlightableButton(boost::bind(&CBattleInterface::setPrintStackRange, owner, true), boost::bind(&CBattleInterface::setPrintStackRange, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[428].first)(3,CGI->generaltexth->zelp[428].first), CGI->generaltexth->zelp[428].second, false, "sysopchk.def", NULL, 185, 173, false);
-	movementShadow->select(owner->settings.printStackRange);
+	movementShadow->select(LOCPLINT->sysOpts.printStackRange);
 	mouseShadow = new CHighlightableButton(boost::bind(&CBattleInterface::setPrintMouseShadow, owner, true), boost::bind(&CBattleInterface::setPrintMouseShadow, owner, false), boost::assign::map_list_of(0,CGI->generaltexth->zelp[429].first)(3,CGI->generaltexth->zelp[429].first), CGI->generaltexth->zelp[429].second, false, "sysopchk.def", NULL, 185, 207, false);
-	mouseShadow->select(owner->settings.printMouseShadow);
+	mouseShadow->select(LOCPLINT->sysOpts.printMouseShadow);
 
 	animSpeeds = new CHighlightableButtonsGroup(0);
 	animSpeeds->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[422].first),CGI->generaltexth->zelp[422].second, "sysopb9.def",188, 309, 1);
@@ -3891,13 +3978,4 @@ CStackQueue::StackBox::~StackBox()
 void CStackQueue::StackBox::hover( bool on )
 {
 
-}
-
-BattleSettings::BattleSettings()
-{
-	printCellBorders = true;
-	printStackRange = true;
-	animSpeed = 2;
-	printMouseShadow = true;
-	showQueue = true;
 }

+ 1 - 17
client/CBattleInterface.h

@@ -314,21 +314,6 @@ public:
 	void show(SDL_Surface * to = 0);
 };
 
-struct BattleSettings
-{
-	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 & showQueue;
-	}
-};
-
 struct SBattleEffect
 {
 	int x, y; //position on the screen
@@ -442,7 +427,6 @@ public:
 	~CBattleInterface(); //d-tor
 
 	//std::vector<TimeInterested*> timeinterested; //animation handling
-	static BattleSettings settings;
 	void setPrintCellBorders(bool set); //if true, cell borders will be printed
 	void setPrintStackRange(bool set); //if true,range of active stack will be printed
 	void setPrintMouseShadow(bool set); //if true, hex under mouse will be shaded
@@ -480,6 +464,7 @@ public:
 	void clickRight(tribool down, bool previousState);
 
 	//call-ins
+	void startAction(const BattleAction* action);
 	void newStack(int stackID); //new stack appeared on battlefield
 	void stackRemoved(int stackID); //stack disappeared from batlefiled
 	//void stackKilled(int ID, int dmg, int killed, int IDby, bool byShooting); //stack has been killed (but corpses remain)
@@ -500,7 +485,6 @@ public:
 	void endAction(const BattleAction* action);
 	void hideQueue();
 	void showQueue();
-
 	friend class CBattleHex;
 	friend class CBattleResultWindow;
 	friend class CPlayerInterface;

+ 20 - 57
client/CPlayerInterface.cpp

@@ -1024,60 +1024,7 @@ void CPlayerInterface::actionStarted(const BattleAction* action)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	curAction = new BattleAction(*action);
-	if( (action->actionType==2 || (action->actionType==6 && action->destinationTile!=cb->battleGetPos(action->stackNumber))) )
-	{
-		battleInt->moveStarted = true;
-		if(battleInt->creAnims[action->stackNumber]->framesInGroup(20))
-		{
-			battleInt->pendingAnims.push_back(std::make_pair(new CBattleMoveStart(battleInt, action->stackNumber), false));
-		}
-	}
-
-
-	battleInt->deactivate();
-
-	const CStack *stack = cb->battleGetStackByID(action->stackNumber);
-	char txt[400];
-
-	if(action->actionType == 1)
-	{
-		if(action->side)
-			battleInt->defendingHero->setPhase(4);
-		else
-			battleInt->attackingHero->setPhase(4);
-		return;
-	}
-	if(!stack)
-	{
-		tlog1<<"Something wrong with stackNumber in actionStarted. Stack number: "<<action->stackNumber<<std::endl;
-		return;
-	}
-
-	int txtid = 0;
-	switch(action->actionType)
-	{
-	case 3: //defend
-		txtid = 120;
-		break;
-	case 8: //wait
-		txtid = 136;
-		break;
-	case 11: //bad morale
-		txtid = -34; //negative -> no separate singular/plural form		
-		battleInt->displayEffect(30,stack->position);
-		break;
-	}
-
-	if(txtid > 0  &&  stack->amount != 1)
-		txtid++; //move to plural text
-	else if(txtid < 0)
-		txtid = -txtid;
-
-	if(txtid)
-	{
-		sprintf(txt, CGI->generaltexth->allTexts[txtid].c_str(),  (stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str(), 0);
-		LOCPLINT->battleInt->console->addText(txt);
-	}
+	battleInt->startAction(action);
 }
 
 void CPlayerInterface::actionFinished(const BattleAction* action)
@@ -1377,7 +1324,6 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
 {
 	h & playerID & serialID;
 	h & sysOpts;
-	h & CBattleInterface::settings;
 }
 
 void CPlayerInterface::serialize( COSer<CSaveFile> &h, const int version )
@@ -1632,7 +1578,24 @@ void SystemOptions::settingsChanged()
 
 void SystemOptions::apply()
 {
-	CGI->musich->setVolume(musicVolume);
-	CGI->soundh->setVolume(soundVolume);
+	if(CGI->musich->getVolume() != musicVolume)
+		CGI->musich->setVolume(musicVolume);
+	if(CGI->soundh->getVolume() != soundVolume)
+		CGI->soundh->setVolume(soundVolume);
+
 	settingsChanged();
 }
+
+SystemOptions::SystemOptions()
+{
+	heroMoveSpeed = 2;
+	mapScrollingSpeed = 2;
+	musicVolume = 88;
+	soundVolume = 88;
+
+	printCellBorders = true;
+	printStackRange = true;
+	animSpeed = 2;
+	printMouseShadow = true;
+	showQueue = true;
+}

+ 16 - 15
client/CPlayerInterface.h

@@ -74,32 +74,33 @@ namespace boost
 
 struct SystemOptions
 {
-	ui8 heroMoveSpeed; //speed of player's hero movement
-	//TODO: enemy hero speed
+
+	ui8 heroMoveSpeed;/*, enemyMoveSpeed*/ //speed of player's hero movement
 	ui8 mapScrollingSpeed; //map scrolling speed
 	ui8 musicVolume, soundVolume;
-
 	//TODO: rest of system options
 
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume;
-	}
-
-	SystemOptions()
-	{
-		heroMoveSpeed = 2;
-		mapScrollingSpeed = 2;
-		musicVolume = 88;
-		soundVolume = 88;
-	}
+	//battle settings
+	ui8 printCellBorders; //if true, cell borders will be printed
+	ui8 printStackRange; //if true,range of active stack will be printed
+	ui8 animSpeed; //speed of animation; 1 - slowest, 2 - medium, 4 - fastest
+	ui8 printMouseShadow; //if true, hex under mouse will be shaded
+	ui8 showQueue;
 
+	SystemOptions();
 	void setHeroMoveSpeed(int newSpeed); //set for the member above
 	void setMapScrollingSpeed(int newSpeed); //set the member above
 	void setMusicVolume(int newVolume);
 	void setSoundVolume(int newVolume);
 	void settingsChanged(); //updates file with "default" settings for next running of application
 	void apply();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume;
+
+		h & printCellBorders & printStackRange & animSpeed & printMouseShadow & showQueue;
+	}
 };
 
 extern SystemOptions GDefaultOptions; //defined and inited in CMT.cpp, stores default settings loaded with application

+ 21 - 7
client/GUIBase.cpp

@@ -292,24 +292,38 @@ void CGuiHandler::handleMouseMotion(SDL_Event *sEvent)
 		hlp[i]->hovered = true;
 	}
 
+	handleMoveInterested(sEvent->motion);
+}
+
+void CGuiHandler::simpleRedraw()
+{
+	//update only top interface and draw background
+	if(objsToBlit.size() > 1)
+		blitAt(screen2,0,0,screen); //blit background
+	objsToBlit.back()->show(screen); //blit active interface/window
+}
 
+void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion )
+{	
 	//sending active, MotionInterested objects mouseMoved() call
 	std::list<CIntObject*> miCopy = motioninterested;
 	for(std::list<CIntObject*>::iterator i=miCopy.begin(); i != miCopy.end();i++)
 	{
-		if ((*i)->strongInterest || isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
+		if ((*i)->strongInterest || isItIn(&(*i)->pos, motion.x, motion.y))
 		{
-			(*i)->mouseMoved(sEvent->motion);
+			(*i)->mouseMoved(motion);
 		}
 	}
 }
 
-void CGuiHandler::simpleRedraw()
+void CGuiHandler::fakeMouseMove()
 {
-	//update only top interface and draw background
-	if(objsToBlit.size() > 1)
-		blitAt(screen2,0,0,screen); //blit background
-	objsToBlit.back()->show(screen); //blit active interface/window
+	SDL_MouseMotionEvent sme = {SDL_MOUSEMOTION, 0, 0, 0, 0, 0, 0};
+	int x, y;
+	sme.state = SDL_GetMouseState(&x, &y);
+	sme.x = x;
+	sme.y = y;
+	handleMoveInterested(sme);
 }
 
 void CIntObject::activateLClick()

+ 2 - 2
client/GUIBase.h

@@ -453,9 +453,9 @@ public:
 	void updateTime(); //handles timeInterested
 	void handleEvents(); //takes events from queue and calls interested objects
 	void handleEvent(SDL_Event *sEvent);
-
 	void handleMouseMotion(SDL_Event *sEvent);
-
+	void handleMoveInterested( const SDL_MouseMotionEvent & motion );
+	void fakeMouseMove();
 	ui8 defActionsDef; //default auto actions
 	ui8 captureChildren; //all newly created objects will get their parents from stack and will be added to parents children list
 	std::list<CIntObject *> createdObj; //stack of objs being created

+ 16 - 13
client/GUIClasses.cpp

@@ -222,7 +222,8 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 				refr = true;
 				delete pom2;
 			}
-			else {
+			else 
+			{
 				// Only allow certain moves if troops aren't removable.
 				if (owner->removableUnits
 					|| (upg == 0 && (owner->highlighted->upg == 1 && !creature))
@@ -244,12 +245,13 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 						int last = -1;
 						if(upg != owner->highlighted->upg) //not splitting within same army
 						{
-							if(owner->highlighted->getObj()->army.slots.size() == 1
+							if(owner->highlighted->getObj()->army.slots.size() == 1 //we're splitting away the last stack
 								&& owner->highlighted->getObj()->needsLastStack() )
 							{
 								last = 0;
 							}
-							if(getObj()->army.slots.size() == 1
+							if(getObj()->army.slots.size() == 1 //destination army can't be emptied, unless we're rebalancing two stacks of same creature
+								&& owner->highlighted->creature == creature
 								&& getObj()->needsLastStack() )
 							{
 								last += 2;
@@ -276,7 +278,8 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
 							owner->highlighted->ID,ID);
 					}
 				}
-				else { // Highlight
+				else // Highlight
+				{ 
 					if(creature)
 						owner->highlighted = this;
 					show(screen2);
@@ -2829,24 +2832,24 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 	std::swap(backToMap->imgs[0][0], backToMap->imgs[0][1]);
 
 	heroMoveSpeed = new CHighlightableButtonsGroup(0);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[349].first),CGI->generaltexth->zelp[349].second, "sysopb1.def", 187, 134, 1);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].first),CGI->generaltexth->zelp[350].second, "sysopb2.def", 235, 134, 2);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].first),CGI->generaltexth->zelp[351].second, "sysopb3.def", 283, 134, 4);
-	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].first),CGI->generaltexth->zelp[352].second, "sysopb4.def", 331, 134, 8);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[349].second),CGI->generaltexth->zelp[349].second, "sysopb1.def", 187, 134, 1);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[350].second),CGI->generaltexth->zelp[350].second, "sysopb2.def", 235, 134, 2);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[351].second),CGI->generaltexth->zelp[351].second, "sysopb3.def", 283, 134, 4);
+	heroMoveSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[352].second),CGI->generaltexth->zelp[352].second, "sysopb4.def", 331, 134, 8);
 	heroMoveSpeed->select(owner->sysOpts.heroMoveSpeed, 1);
 	heroMoveSpeed->onChange = boost::bind(&SystemOptions::setHeroMoveSpeed, &owner->sysOpts, _1);
 
 	mapScrollSpeed = new CHighlightableButtonsGroup(0);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].first),CGI->generaltexth->zelp[357].second, "sysopb9.def", 187, 267, 1);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].first),CGI->generaltexth->zelp[358].second, "sysob10.def", 251, 267, 2);
-	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].first),CGI->generaltexth->zelp[359].second, "sysob11.def", 315, 267, 4);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[357].second),CGI->generaltexth->zelp[357].second, "sysopb9.def", 187, 267, 1);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[358].second),CGI->generaltexth->zelp[358].second, "sysob10.def", 251, 267, 2);
+	mapScrollSpeed->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[359].second),CGI->generaltexth->zelp[359].second, "sysob11.def", 315, 267, 4);
 	mapScrollSpeed->select(owner->sysOpts.mapScrollingSpeed, 1);
 	mapScrollSpeed->onChange = boost::bind(&SystemOptions::setMapScrollingSpeed, &owner->sysOpts, _1);
 
 	musicVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
 	{
-		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].first),CGI->generaltexth->zelp[326+i].second, "syslb.def", 188 + 19*i, 416, i*11);
+		musicVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[326+i].second),CGI->generaltexth->zelp[326+i].second, "syslb.def", 188 + 19*i, 416, i*11);
 	}
 	musicVolume->select(CGI->musich->getVolume(), 1);
 	musicVolume->onChange = boost::bind(&SystemOptions::setMusicVolume, &owner->sysOpts, _1);
@@ -2854,7 +2857,7 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &pos, CPlayerInterface
 	effectsVolume = new CHighlightableButtonsGroup(0, true);
 	for(int i=0; i<10; ++i)
 	{
-		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].first),CGI->generaltexth->zelp[336+i].second, "syslb.def", 188 + 19*i, 482, i*11);
+		effectsVolume->addButton(boost::assign::map_list_of(0,CGI->generaltexth->zelp[336+i].second),CGI->generaltexth->zelp[336+i].second, "syslb.def", 188 + 19*i, 482, i*11);
 	}
 	effectsVolume->select(CGI->soundh->getVolume(), 1);
 	effectsVolume->onChange = boost::bind(&SystemOptions::setSoundVolume, &owner->sysOpts, _1);

+ 4 - 3
hch/CObjectHandler.cpp

@@ -1054,11 +1054,11 @@ std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (BattleResult &battleR
 	if (necromancyLevel > 0) {
 		double necromancySkill = necromancyLevel*0.1
 			+ valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 12)/100.0;
-		const std::set<std::pair<ui32, si32> > &casualties = battleResult.casualties[!battleResult.winner];
+		const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner];
 		ui32 raisedUnits = 0;
 
 		// Get lost enemy hit points convertible to units.
-		for (std::set<std::pair<ui32, si32> >::const_iterator it = casualties.begin(); it != casualties.end(); it++)
+		for (std::map<ui32,si32>::const_iterator it = casualties.begin(); it != casualties.end(); it++)
 			raisedUnits += VLC->creh->creatures[it->first].hitPoints*it->second;
 		raisedUnits *= necromancySkill;
 
@@ -1071,7 +1071,8 @@ std::pair<ui32, si32> CGHeroInstance::calculateNecromancy (BattleResult &battleR
 
 		// Make room for new units.
 		int slot = army.getSlotFor(raisedUnitType->idNumber);
-		if (slot == -1) {
+		if (slot == -1) 
+		{
 			// If there's no room for unit, try it's upgraded version 2/3rds the size.
 			raisedUnitType = &VLC->creh->creatures[*raisedUnitType->upgrades.begin()];
 			raisedUnits = (raisedUnits*2)/3;

+ 5 - 9
lib/CGameState.cpp

@@ -2493,18 +2493,14 @@ ui32 BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, co
 		return range.first;
 }
 
-void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualties ) const
+void BattleInfo::calculateCasualties( std::map<ui32,si32> *casualties ) const
 {
 	for(unsigned int i=0; i<stacks.size();i++)//setting casualties
 	{
-		if(!stacks[i]->alive())
-		{
-			casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount));
-		}
-		else if(stacks[i]->amount != stacks[i]->baseAmount)
-		{
-			casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount - stacks[i]->amount));
-		}
+		const CStack * const st = stacks[i];
+		si32 killed = (st->alive() ? st->baseAmount - st->amount : st->baseAmount);
+		amax(killed, 0);
+		casualties[!st->attackerOwned][st->creature->idNumber] += killed;
 	}
 }
 

+ 1 - 1
lib/CGameState.h

@@ -154,7 +154,7 @@ struct DLL_EXPORT BattleInfo
 	static std::vector<int> neighbouringTiles(int hex);
 	static ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting)
 	static std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
-	void calculateCasualties(std::set<std::pair<ui32,si32> > *casualties) const;
+	void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
 	std::set<CStack*> getAttackedCreatures(const CSpell * s, const CGHeroInstance * caster, int destinationTile); //calculates stack affected by given spell
 	static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster);
 	CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield

+ 1 - 1
lib/NetPacks.h

@@ -807,7 +807,7 @@ struct BattleResult : public CPackForClient//3003
 
 	ui8 result; //0 - normal victory; 1 - escape; 2 - surrender
 	ui8 winner; //0 - attacker, 1 - defender, [2 - draw (should be possible?)]
-	std::set<std::pair<ui32,si32> > casualties[2]; //first => casualties of attackers - set of pairs crid<>number
+	std::map<ui32,si32> casualties[2]; //first => casualties of attackers - map crid => number
 	ui32 exp[2]; //exp for attacker and defender
 	std::set<ui32> artifacts; //artifacts taken from loser to winner
 

+ 2 - 2
server/CGameHandler.cpp

@@ -95,7 +95,7 @@ static void giveExp(BattleResult &r)
 {
 	r.exp[0] = 0;
 	r.exp[1] = 0;
-	for(std::set<std::pair<ui32,si32> >::iterator i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++)
+	for(std::map<ui32,si32>::iterator i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++)
 	{
 		r.exp[r.winner] += VLC->creh->creatures[i->first].hitPoints * i->second;
 	}
@@ -1406,7 +1406,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 			&& complain("Cannot move hero, destination tile is on water!")
 		|| (h->boat && t.tertype != TerrainTile::water && t.blocked)
 			&& complain("Cannot disembark hero, tile is blocked!")
-		|| (!h->movement && dst != h->pos)
+		|| (h->movement < cost  &&  dst != h->pos)
 			&& complain("Hero don't have any movement points left!")
 		|| states.checkFlag(h->tempOwner, &PlayerStatus::engagedIntoBattle)
 			&& complain("Cannot move hero during the battle"))