1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* 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
This commit is contained in:
Michał W. Urbańczyk 2009-02-05 09:49:45 +00:00
parent 218a3beaf6
commit e1d6ff54d7
15 changed files with 325 additions and 113 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

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

View File

@ -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);

View File

@ -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

View File

@ -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
* 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
@ -31,21 +60,21 @@ BATTLES
* 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

View File

@ -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:

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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};

View File

@ -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;

View File

@ -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
map.h
View File

@ -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:

View File

@ -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