Ver Fonte

* morale/luck support in battles
* minor improvements/fixes
* updated changelog
More objects supported:
* Faerie Ring
* Swan Pond
* Idol of Fortune
* Fountain of Fortune
* Rally Flag
* Oasis
* Temple
* Watering Hole
* Fountain of Youth

Michał W. Urbańczyk há 16 anos atrás
pai
commit
e1d6ff54d7
15 ficheiros alterados com 325 adições e 113 exclusões
  1. 2 0
      CGameInterface.h
  2. 18 4
      CGameState.cpp
  3. 4 1
      CGameState.h
  4. 2 2
      CHeroWindow.cpp
  5. 69 55
      CPlayerInterface.cpp
  6. 2 0
      CPlayerInterface.h
  7. 54 23
      ChangeLog
  8. 3 0
      client/Client.cpp
  9. 79 18
      hch/CObjectHandler.cpp
  10. 1 1
      lib/BattleAction.h
  11. 1 1
      lib/HeroBonus.h
  12. 5 1
      lib/NetPacks.h
  13. 5 0
      map.cpp
  14. 5 0
      map.h
  15. 75 7
      server/CGameHandler.cpp

+ 2 - 0
CGameInterface.h

@@ -23,6 +23,7 @@ struct BattleResult;
 struct BattleAttack;
 struct BattleStackAttacked;
 struct SpellCasted;
+struct HeroBonus;
 class CObstacle
 {
 	int ID;
@@ -67,6 +68,7 @@ public:
 	virtual void tileRevealed(const std::set<int3> &pos){};
 	virtual void yourTurn(){};
 	virtual void availableCreaturesChanged(const CGTownInstance *town){};
+	virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
 
 	//battle call-ins
 	virtual void actionFinished(const BattleAction *action){};//occurs AFTER every action taken by any stack or by the hero

+ 18 - 4
CGameState.cpp

@@ -362,6 +362,20 @@ const CStack::StackEffect * CStack::getEffect(ui16 id) const
 			return &effects[i];
 	return NULL;
 }
+
+si8 CStack::Morale() const
+{
+	si8 ret = morale;
+	//premies from spells/other effects
+	return ret;
+}
+
+si8 CStack::Luck() const
+{
+	si8 ret = luck;
+	//premies from spells/other effects
+	return ret;
+}
 CGHeroInstance* CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, int notThatOne)
 {
 	if(player<0 || player>=PLAYER_LIMIT)
@@ -781,14 +795,14 @@ void CGameState::applyNL(IPack * pack)
 		{
 			BattleSetActiveStack *ns = static_cast<BattleSetActiveStack*>(pack);
 			curB->activeStack = ns->stack;
+			CStack *st = curB->getStack(ns->stack);
+			if(vstd::contains(st->state,MOVED))
+				st->state.insert(HAD_MORALE);
 			break;
 		}
 	case 3003:
 		{
 			BattleResult *br = static_cast<BattleResult*>(pack);
-
-			//TODO: give exp, artifacts to winner, decrease armies (casualties)
-
 			for(unsigned i=0;i<curB->stacks.size();i++)
 				delete curB->stacks[i];
 			delete curB;
@@ -834,7 +848,7 @@ void CGameState::applyNL(IPack * pack)
 			case 8:
 				st->state.insert(WAITING);
 				break;
-			case 2: case 6: case 7: case 9: case 10:
+			case 2: case 6: case 7: case 9: case 10: case 11:
 				st->state.insert(MOVED);
 				break;
 			}

+ 4 - 1
CGameState.h

@@ -127,6 +127,7 @@ public:
 	ui16 position; //position on battlefield
 	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
 	si16 shots; //how many shots left
+	si8 morale, luck; //base stack luck/morale
 
 	std::set<EAbilities> abilities;
 	std::set<ECombatInfo> state;
@@ -146,6 +147,8 @@ public:
 	CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(1), effects(), state(), abilities(){}
 	const StackEffect * getEffect(ui16 id) const; //effect id (SP)
 	ui32 speed() const;
+	si8 Morale() const;
+	si8 Luck() const;
 	template <typename Handler> void save(Handler &h, const int version)
 	{
 		h & creature->idNumber;
@@ -160,7 +163,7 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & ID & amount & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
-			& shots;
+			& shots & morale & luck;
 		if(h.saving)
 			save(h,version);
 		else

+ 2 - 2
CHeroWindow.cpp

@@ -462,8 +462,8 @@ void CHeroWindow::deactivate()
 	portraitArea->deactivate();
 	expArea->deactivate();
 	spellPointsArea->deactivate();
-	morale->activate();
-	luck->activate();
+	morale->deactivate();
+	luck->deactivate();
 
 	garInt->deactivate();
 

+ 69 - 55
CPlayerInterface.cpp

@@ -1887,19 +1887,12 @@ int3 CPlayerInterface::repairScreenPos(int3 pos)
 void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	SDL_FreeSurface(graphics->heroWins[hero->subID]);//TODO: moznaby zmieniac jedynie fragment bitmapy zwiazany z dana umiejetnoscia
-	graphics->heroWins[hero->subID] = infoWin(hero); //a nie przerysowywac calosc. Troche roboty, obecnie chyba nie wartej swieczki.
-	if (adventureInt->selection == hero)
-		adventureInt->infoBar.draw();
-	return;
+	redrawHeroWin(hero);
 }
 void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
-	SDL_FreeSurface(graphics->heroWins[hero->subID]);//TODO: moznaby zmieniac jedynie fragment bitmapy zwiazany z dana umiejetnoscia
-	graphics->heroWins[hero->subID] = infoWin(hero); //a nie przerysowywac calosc. Troche roboty, obecnie chyba nie wartej swieczki.
-	if (adventureInt->selection == hero)
-		adventureInt->infoBar.draw();
+	redrawHeroWin(hero);
 }
 void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
 {
@@ -2079,10 +2072,13 @@ void CPlayerInterface::actionStarted(const BattleAction* action)
 	{
 		battleInt->creAnims[action->stackNumber]->setType(20);
 	}
-	//if((action->actionType==2 || (action->actionType==6 && action->destinationTile!=cb->battleGetPos(action->stackNumber)))) //deactivating interface when move is started
-	{
-		battleInt->deactivate();
-	}
+
+
+	battleInt->deactivate();
+
+	CStack *stack = cb->battleGetStackByID(action->stackNumber);
+	char txt[400];
+
 	if(action->actionType == 1)
 	{
 		if(action->side)
@@ -2090,47 +2086,36 @@ void CPlayerInterface::actionStarted(const BattleAction* action)
 		else
 			battleInt->attackingHero->setPhase(4);
 	}
-	if(action->actionType == 3) //defend
+	if(!stack)
 	{
-		char txt[2000];
-		CStack * stack = cb->battleGetStackByID(action->stackNumber);
-		if(stack)
-		{
-			if(stack->amount == 1)
-			{
-				sprintf(txt, CGI->generaltexth->allTexts[120].c_str(), stack->creature->nameSing.c_str(), 0);
-			}
-			else
-			{
-				sprintf(txt, CGI->generaltexth->allTexts[121].c_str(), stack->creature->namePl.c_str(), 0);
-			}
-			LOCPLINT->battleInt->console->addText(txt);
-		}
-		else
-		{
-			tlog1<<"Somthing wrong with stackNumber in actionStarted -> actionType 3"<<std::endl;
-		}
+		tlog1<<"Something wrong with stackNumber in actionStarted"<<std::endl;
+		return;
 	}
-	if(action->actionType == 8) //wait
+
+	int txtid = 0;
+	switch(action->actionType)
 	{
-		char txt[2000];
-		CStack * stack = cb->battleGetStackByID(action->stackNumber);
-		if(stack)
-		{
-			if(stack->amount == 1)
-			{
-				sprintf(txt, CGI->generaltexth->allTexts[136].c_str(), stack->creature->nameSing.c_str());
-			}
-			else
-			{
-				sprintf(txt, CGI->generaltexth->allTexts[137].c_str(), stack->creature->namePl.c_str());
-			}
-			LOCPLINT->battleInt->console->addText(txt);
-		}
-		else
-		{
-			tlog1<<"Somthing wrong with stackNumber in actionStarted -> actionType 8"<<std::endl;
-		}
+	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);
 	}
 }
 
@@ -2156,6 +2141,16 @@ BattleAction CPlayerInterface::activeStack(int stackID) //called when it's turn
 	CBattleInterface *b = battleInt;
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*pim);
+
+		CStack *stack = cb->battleGetStackByID(stackID);
+		if(vstd::contains(stack->state,MOVED)) //this stack has moved and makes second action -> high morale
+		{
+			std::string hlp = CGI->generaltexth->allTexts[33];
+			boost::algorithm::replace_first(hlp,"%s",(stack->amount != 1) ? stack->creature->namePl : stack->creature->nameSing);
+			battleInt->displayEffect(20,stack->position);
+			battleInt->console->addText(hlp);
+		}
+
 		b->stackActivated(stackID);
 	}
 	//wait till BattleInterface sets its command
@@ -2215,14 +2210,18 @@ void CPlayerInterface::battleStackAttacked(BattleStackAttacked * bsa)
 void CPlayerInterface::battleAttack(BattleAttack *ba)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	if(ba->bsa.lucky()) //lucky hit
+	{
+		CStack *stack = cb->battleGetStackByID(ba->stackAttacking);
+		std::string hlp = CGI->generaltexth->allTexts[45];
+		boost::algorithm::replace_first(hlp,"%s",(stack->amount != 1) ? stack->creature->namePl.c_str() : stack->creature->nameSing.c_str());
+		battleInt->console->addText(hlp);
+		battleInt->displayEffect(18,stack->position);
+	}
 	if(ba->shot())
 		battleInt->stackIsShooting(ba->stackAttacking,cb->battleGetPos(ba->bsa.stackAttacked));
 	else
 		battleInt->stackAttacking( ba->stackAttacking, ba->counter() ? curAction->destinationTile : curAction->additionalInfo );
-	/*if(ba->killed())
-		battleInt->stackKilled(ba->bsa.stackAttacked, ba->bsa.damageAmount, ba->bsa.killedAmount, ba->stackAttacking, ba->shot());
-	else
-		battleInt->stackIsAttacked(ba->bsa.stackAttacked, ba->bsa.damageAmount, ba->bsa.killedAmount, ba->stackAttacking, ba->shot());*/
 }
 void CPlayerInterface::showComp(SComponent comp)
 {
@@ -2359,6 +2358,21 @@ void CPlayerInterface::availableCreaturesChanged( const CGTownInstance *town )
 			fs->draw(castleInt,false);
 	}
 }
+
+void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const HeroBonus &bonus, bool gain )
+{
+	boost::unique_lock<boost::recursive_mutex> un(*pim);
+	redrawHeroWin(hero);
+}
+
+void CPlayerInterface::redrawHeroWin(const CGHeroInstance * hero)
+{
+	SDL_FreeSurface(graphics->heroWins[hero->subID]);
+	graphics->heroWins[hero->subID] = infoWin(hero); 
+	if (adventureInt->selection == hero)
+		adventureInt->infoBar.draw();
+}
+
 CStatusBar::CStatusBar(int x, int y, std::string name, int maxw)
 {
 	bg=BitmapHandler::loadBitmap(name);

+ 2 - 0
CPlayerInterface.h

@@ -380,6 +380,7 @@ public:
 	void tileRevealed(const std::set<int3> &pos);
 	void yourTurn();
 	void availableCreaturesChanged(const CGTownInstance *town);
+	void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it
 	//for battles
 	void actionFinished(const BattleAction* action);//occurs AFTER action taken by active stack or by the hero
 	void actionStarted(const BattleAction* action);//occurs BEFORE action taken by active stack or by the hero
@@ -396,6 +397,7 @@ public:
 
 
 	//-------------//
+	void redrawHeroWin(const CGHeroInstance * hero);
 	void updateWater();
 	void showComp(SComponent comp);
 	void openTownWindow(const CGTownInstance * town); //shows townscreen

+ 54 - 23
ChangeLog

@@ -1,22 +1,51 @@
-0.64 -> 0.next (???)  [as for r689]
+0.7 -> 0.71 (as for r707)
+GENERAL:
+* morale/luck system and corresponding sec. skills supported 
+* fixed crash when hero get level and has less than two sec. skills to choose between 
+
+ADVENTURE INTERFACE:
+* added missing path arrows
+* corrected centering on hero's position 
+
+BATTLES:
+* spell books won't be placed in War Machine slots after battle
+
+TOWN INTERFACE:
+* Rampart's Treasury requires Miner's Guild 
+
+OBJECTS:
+New objects supported:
+ *	Faerie Ring
+ *	Swan Pond
+ *	Idol of Fortune
+ *	Fountain of Fortune
+ *	Rally Flag
+ *	Oasis
+ *	Temple
+ *	Watering Hole
+ *	Fountain of Youth
+
+
+0.64 -> 0.7 (Feb 01 2009)
 GENERAL:
 * move some settings to the config/settings.txt file
 * partial support for new screen resolutions
-* /Data and /Sprites subfolders can be used for adding files not present in .lod archives 
+* it's possible to set game resolution in pregame (type 'resolution' in the console) 
+* /Data and /Sprites subfolders can be used for adding files not present in .lod archives
 * fixed crashbug occuring when hero levelled above 15 level
 * support for non-standard screen resolutions
-* F4 toggles between full-screen and windowed mode (experimental)
-* splitting stacks the shift+click
+* F4 toggles between full-screen and windowed mode
 * minor improvements in creature card window
+* splitting stacks with the shift+click 
+* creature card window contains info about modified speed 
 
 ADVENTURE INTERFACE:
-* smooth map scrolling on hero movement
 * added water animation
 * speed of scrolling map and hero movement can be adjusted in the System Options Window
 * partial handling r-clicks on adventure map
 
 TOWN INTERFACE:
-* the scroll tab won't remain hanged to our mouse position if we move the mouse away from the scroll bar
+* the scroll tab won't remain hanged to our mouse position if we move the mouse is away from the scroll bar
 * fixed cloning creatures bug in garrisons (and related issues)
 
 BATTLES
@@ -27,26 +56,26 @@ BATTLES
 * spell effect animation displaying improvements
 * positive/negative spells cannot be cast on hostile/our stacks
 * showing spell effects affecting stack in creature info window
-* more appropriate coloring of stack amount box when stack is affected by a spell 
+* more appropriate coloring of stack amount box when stack is affected by a spell
 * battle console displays notifications about wait/defend commands 
 * several reported bugs fixed
 * new spells supported:
-	a) Haste 
-	b) lightning bolt 
-	c) ice bolt
-	d) slow 
-	e) implosion
-	f) forgetfulness
-	g) shield
-	h) air shield
-	i) bless
-	j) curse
-	k) bloodlust
-	l) weakness
-	m) stone skin
-	n) prayer 	
-	o) frenzy	
-	
+a) Haste
+b) lightning bolt
+c) ice bolt
+d) slow
+e) implosion
+f) forgetfulness
+g) shield
+h) air shield
+i) bless
+j) curse
+k) bloodlust
+l) weakness
+m) stone skin
+n) prayer
+o) frenzy
+
 AI PLAYER:
 * Genius AI (first VCMI AI) will control computer creatures during the combat.
 
@@ -54,9 +83,11 @@ OBJECTS:
 * Guardians property for resources is handled
 * support for Witch Hut
 * support for Arena
+* support for Library of Enlightenment 
 
 And a lot of minor fixes
 
+
 0.63 -> 0.64 (Nov 01 2008)
 GENERAL:
 * sprites from /Sprites folder are handled correctly

+ 3 - 0
client/Client.cpp

@@ -212,6 +212,9 @@ void CClient::process(int what)
 			*serv >> gb;
 			tlog5 << "Hero receives bonus\n";
 			gs->apply(&gb);
+			CGHeroInstance *h = gs->getHero(gb.hid);
+			if(vstd::contains(playerint,h->tempOwner))
+				playerint[h->tempOwner]->heroBonusChanged(h,h->bonuses.back(),true);
 			break;
 		}
 	case 500:

+ 79 - 18
hch/CObjectHandler.cpp

@@ -9,6 +9,7 @@
 #include "CSpellHandler.h"
 #include <boost/bind.hpp>
 #include <boost/algorithm/string/replace.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/random/linear_congruential.hpp>
 #include "CTownHandler.h"
 #include "CArtHandler.h"
@@ -611,7 +612,7 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
 
 	//various morale bonuses (from buildings, artifacts, etc)
 	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-		if(i->type == HeroBonus::MORALE)
+		if(i->type == HeroBonus::MORALE   ||   i->type == HeroBonus::MORALE_AND_LUCK)
 			ret.push_back(std::make_pair(i->val, i->description));
 
 	//leadership
@@ -688,7 +689,7 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentLuckModifiers
 
 	//various morale bonuses (from buildings, artifacts, etc)
 	for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
-		if(i->type == HeroBonus::LUCK)
+		if(i->type == HeroBonus::LUCK   ||   i->type == HeroBonus::MORALE_AND_LUCK)
 			ret.push_back(std::make_pair(i->val, i->description));
 
 	//luck skill
@@ -1608,48 +1609,108 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 {
 	bool visited = h->getBonus(HeroBonus::OBJECT,ID);
 	int messageID, bonusType, bonusVal;
+	int bonusMove = 0;
 	InfoWindow iw;
 	iw.player = h->tempOwner;
+	GiveBonus gbonus;
+	gbonus.hid = h->id;
+	gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
+	gbonus.bonus.source = HeroBonus::OBJECT;
+	gbonus.bonus.id = ID;
 
 	switch(ID)
 	{
 	case 14: //swan pond
 		messageID = 29;
-		bonusType = HeroBonus::LUCK;
-		bonusVal = 2;
+		gbonus.bonus.type = HeroBonus::LUCK;
+		gbonus.bonus.val = 2;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,67);
+		bonusMove = -h->movement;
+		break;
 	case 28: //Faerie Ring
 		messageID = 49;
-		bonusType = HeroBonus::LUCK;
-		bonusVal = 1;
+		gbonus.bonus.type = HeroBonus::LUCK;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,71);
 		break;
 	case 30: //fountain of fortune
 		messageID = 55;
-		bonusType = HeroBonus::LUCK;
-		bonusVal = rand()%5 - 1;
+		gbonus.bonus.type = HeroBonus::LUCK;
+		gbonus.bonus.val = rand()%5 - 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,69);
+		gbonus.bdescr.replacements.push_back((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast<std::string>(gbonus.bonus.val));
 		break;
 	case 38: //idol of fortune
 		messageID = 62;
-		bonusType = HeroBonus::IDOL_OF_FORTUNE_BONUS;
-		bonusVal = 1;
+		if(cb->getDate(1) == 7) //7th day of week
+			gbonus.bonus.type = HeroBonus::MORALE_AND_LUCK;
+		else
+			gbonus.bonus.type = (cb->getDate(1)%2) ? HeroBonus::LUCK : HeroBonus::MORALE;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,68);
+		break;
+	case 64: //Rally Flag
+		messageID = 111;
+		gbonus.bonus.type = HeroBonus::MORALE_AND_LUCK;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,102);
+		bonusMove = 400;
+		break;
+	case 56: //oasis
+		messageID = 95;
+		gbonus.bonus.type = HeroBonus::MORALE;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,95);
+		bonusMove = 800;
+		break;
+	case 96: //temple
+		messageID = 140;
+		gbonus.bonus.type = HeroBonus::MORALE;
+		if(cb->getDate(1)==7) //sunday
+		{
+			gbonus.bonus.val = 2;
+			gbonus.bdescr <<  std::pair<ui8,ui32>(6,97);
+		}
+		else
+		{
+			gbonus.bonus.val = 1;
+			gbonus.bdescr <<  std::pair<ui8,ui32>(6,96);
+		}
+		break;
+	case 110://Watering Hole
+		messageID = 166;
+		gbonus.bonus.type = HeroBonus::MORALE;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,100);
+		bonusMove = 400;
+		break;
+	case 31: //Fountain of Youth
+		messageID = 57;
+		gbonus.bonus.type = HeroBonus::MORALE;
+		gbonus.bonus.val = 1;
+		gbonus.bdescr <<  std::pair<ui8,ui32>(6,103);
+		bonusMove = 400;
 		break;
 	}
 	if(visited)
 	{
-		messageID++;
+		if(ID==64 || ID==96  ||  ID==56)
+			messageID--;
+		else
+			messageID++;
 	}
 	else
 	{
-		iw.components.push_back(Component(9,0,1,0));
-		GiveBonus gbonus;
-		gbonus.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT, bonusVal, ID,"");
-		gbonus.hid = h->id;
-		gbonus.bdescr <<  std::pair<ui8,ui32>(6,71);
+		if(gbonus.bonus.type == HeroBonus::MORALE   ||   gbonus.bonus.type == HeroBonus::MORALE_AND_LUCK)
+			iw.components.push_back(Component(8,0,gbonus.bonus.val,0));
+		if(gbonus.bonus.type == HeroBonus::LUCK   ||   gbonus.bonus.type == HeroBonus::MORALE_AND_LUCK)
+			iw.components.push_back(Component(9,0,gbonus.bonus.val,0));
 		cb->giveHeroBonus(&gbonus);
-		if(ID==14) //swan pond - take all move points
+		if(bonusMove) //swan pond - take all move points
 		{
 			SetMovePoints smp;
 			smp.hid = h->id;
-			smp.val = 0;
+			smp.val = h->movement + bonusMove;
 			cb->setMovePoints(&smp);
 		}
 	}

+ 1 - 1
lib/BattleAction.h

@@ -4,7 +4,7 @@ struct BattleAction
 {
 	ui8 side; //who made this action: false - left, true - right player
 	ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
-	ui8 actionType; //    0 = Cancel BattleAction   1 = Hero cast a spell   2 = Walk   3 = Defend   4 = Retreat from the battle   5 = Surrender   6 = Walk and Attack   7 = Shoot    8 = Wait   9 = Catapult 10 = Monster casts a spell (i.e. Faerie Dragons)
+	ui8 actionType; //    0 = No action;   1 = Hero cast a spell   2 = Walk   3 = Defend   4 = Retreat from the battle   5 = Surrender   6 = Walk and Attack   7 = Shoot    8 = Wait   9 = Catapult 10 = Monster casts a spell (i.e. Faerie Dragons)	11 - Bad morale freeze
 	ui16 destinationTile;
 	si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 1 - 1
lib/HeroBonus.h

@@ -4,7 +4,7 @@
 
 struct DLL_EXPORT HeroBonus
 {
-	enum BonusType{NONE, MOVEMENT, MORALE, LUCK, IDOL_OF_FORTUNE_BONUS};
+	enum BonusType{NONE, MOVEMENT, LAND_MOVEMENT, SEA_MOVEMENT, MORALE, LUCK, MORALE_AND_LUCK};
 	enum BonusDuration{PERMANENT, ONE_BATTLE, ONE_DAY, ONE_WEEK};
 	enum BonusSource{ARTIFACT, OBJECT};
 

+ 5 - 1
lib/NetPacks.h

@@ -505,7 +505,7 @@ struct BattleStackAttacked : public CPack<BattleStackAttacked>//3005
 {
 	ui32 stackAttacked;
 	ui32 newAmount, newHP, killedAmount, damageAmount;
-	ui8 flags; //1 - is stack killed; 2 - is there special effect to be shown
+	ui8 flags; //1 - is stack killed; 2 - is there special effect to be shown; 4 - lucky hit
 	ui32 effect; //set only if flag 2 is present
 
 
@@ -518,6 +518,10 @@ struct BattleStackAttacked : public CPack<BattleStackAttacked>//3005
 	{
 		return flags & 2;
 	}
+	bool lucky()
+	{
+		return flags & 4;
+	}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & stackAttacked & newAmount & newHP & flags & killedAmount & damageAmount & effect;

+ 5 - 0
map.cpp

@@ -1781,6 +1781,11 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 		case 14: //Swan pond
 		case 38: //idol of fortune
 		case 30: //Fountain of Fortune
+		case 64: //Rally Flag
+		case 56: //oasis
+		case 96: //temple
+		case 110://Watering Hole
+		case 31: //Fountain of Youth
 			{
 				nobj = new CGBonusingObject();
 				break;

+ 5 - 0
map.h

@@ -499,6 +499,11 @@ struct DLL_EXPORT Mapa : public CMapHeader
 			case 14: //Swan pond
 			case 38: //idol of fortune
 			case 30: //Fountain of Fortune
+			case 64: //Rally Flag
+			case 56: //oasis
+			case 96: //temple
+			case 110://Watering Hole
+			case 31: //Fountain of Youth
 				SERIALIZE(CGBonusingObject);
 				break;
 			default:

+ 75 - 7
server/CGameHandler.cpp

@@ -311,14 +311,49 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
 		CStack *next;
 		while(!battleResult.get() && (next=gs->curB->getNextStack()))
 		{
-			BattleSetActiveStack sas;
-			sas.stack = next->ID;
-			sendAndApply(&sas);
-			boost::unique_lock<boost::mutex> lock(battleMadeAction.mx);
-			while(!battleMadeAction.data  &&  !battleResult.get()) //active stack hasn't made its action and battle is still going
-				battleMadeAction.cond.wait(lock);
-			battleMadeAction.data = false;
+			next->state -= WAITING; //if stack was waiting it'll now make move, so it won't be "waiting" anymore
+
+			//check for bad morale => freeze
+			if(next->Morale() < 0)
+			{
+				if( rand()%24   <   (-next->Morale())*2 )
+				{
+					//unit loses its turn - empty freeze action
+					BattleAction ba;
+					ba.actionType = 11;
+					ba.additionalInfo = 1;
+					ba.side = !next->attackerOwned;
+					ba.stackNumber = next->ID;
+					sendAndApply(&StartAction(ba));
+					sendDataToClients(ui16(3008));
+					checkForBattleEnd(stacks); //check if this "action" ended the battle (not likely but who knows...)
+					continue;
+				}
+			}
+
+askInterfaceForMove:
+			//ask interface and wait for answer
+			{
+				BattleSetActiveStack sas;
+				sas.stack = next->ID;
+				sendAndApply(&sas);
+				boost::unique_lock<boost::mutex> lock(battleMadeAction.mx);
+				while(!battleMadeAction.data  &&  !battleResult.get()) //active stack hasn't made its action and battle is still going
+					battleMadeAction.cond.wait(lock);
+				battleMadeAction.data = false;
+			}
+			//we're after action, all results applied
 			checkForBattleEnd(stacks); //check if this action ended the battle
+
+			//check for good morale
+			if(!vstd::contains(next->state,HAD_MORALE)  //only one extra move per turn possible
+				&& !vstd::contains(next->state,DEFENDING)
+				&& !vstd::contains(next->state,WAITING)
+				&&  next->alive()
+				&&  next->Morale() > 0
+			)
+				if(rand()%24 < next->Morale()) //this stack hasn't got morale this turn
+					goto askInterfaceForMove; //move this stack once more
 		}
 	}
 
@@ -395,6 +430,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, CStack *att, CStack *def)
 	bat.stackAttacking = att->ID;
 	bat.bsa.stackAttacked = def->ID;
 	bat.bsa.damageAmount = BattleInfo::calculateDmg(att, def, gs->getHero(att->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), gs->getHero(def->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), bat.shot());//counting dealt damage
+	if(att->Luck() > 0  &&  rand()%24 < att->Luck())
+	{
+		bat.bsa.damageAmount *= 2;
+		bat.bsa.flags |= 4;
+	}
 	prepareAttacked(bat.bsa,def);
 }
 void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
@@ -2035,6 +2075,20 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army
 	for(std::map<si32,std::pair<ui32,si32> >::iterator i = army1.slots.begin(); i!=army1.slots.end(); i++)
 	{
 		stacks.push_back(new CStack(&VLC->creh->creatures[i->second.first],i->second.second,hero1->tempOwner, stacks.size(), true,i->first));
+		
+		//base luck/morale calculations
+		//TODO: check if terrain is native, add bonuses for neutral stacks, bonuses from town
+		if(hero1)
+		{
+			stacks.back()->morale = hero1->getCurrentMorale(i->first,false);
+			stacks.back()->luck = hero1->getCurrentLuck(i->first,false);
+		}
+		else
+		{
+			stacks.back()->morale = 0;
+			stacks.back()->luck = 0;
+		}
+
 		stacks[stacks.size()-1]->ID = stacks.size()-1;
 	}
 	//initialization of positions
@@ -2068,7 +2122,21 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army
 			stacks[b]->position = attackerLoose[army1.slots.size()-1][b];
 		}
 	for(std::map<si32,std::pair<ui32,si32> >::iterator i = army2.slots.begin(); i!=army2.slots.end(); i++)
+	{
 		stacks.push_back(new CStack(&VLC->creh->creatures[i->second.first],i->second.second,hero2 ? hero2->tempOwner : 255, stacks.size(), false, i->first));
+		//base luck/morale calculations
+		//TODO: check if terrain is native, add bonuses for neutral stacks, bonuses from town
+		if(hero2)
+		{
+			stacks.back()->morale = hero2->getCurrentMorale(i->first,false);
+			stacks.back()->luck = hero2->getCurrentLuck(i->first,false);
+		}
+		else
+		{
+			stacks.back()->morale = 0;
+			stacks.back()->luck = 0;
+		}
+	}
 
 	if(army2.formation)
 		for(int b=0; b<army2.slots.size(); ++b) //tight