From e1d6ff54d71a0d02b0e313db092ee76474e13a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Thu, 5 Feb 2009 09:49:45 +0000 Subject: [PATCH] * 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 --- CGameInterface.h | 2 + CGameState.cpp | 22 +++++-- CGameState.h | 5 +- CHeroWindow.cpp | 4 +- CPlayerInterface.cpp | 124 ++++++++++++++++++++++------------------ CPlayerInterface.h | 2 + ChangeLog | 77 +++++++++++++++++-------- client/Client.cpp | 3 + hch/CObjectHandler.cpp | 97 +++++++++++++++++++++++++------ lib/BattleAction.h | 2 +- lib/HeroBonus.h | 2 +- lib/NetPacks.h | 6 +- map.cpp | 5 ++ map.h | 5 ++ server/CGameHandler.cpp | 82 +++++++++++++++++++++++--- 15 files changed, 325 insertions(+), 113 deletions(-) diff --git a/CGameInterface.h b/CGameInterface.h index 3d11cbdbe..3d87d5c69 100644 --- a/CGameInterface.h +++ b/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 &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 diff --git a/CGameState.cpp b/CGameState.cpp index 05f028a1e..55e3db1c4 100644 --- a/CGameState.cpp +++ b/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(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(pack); - - //TODO: give exp, artifacts to winner, decrease armies (casualties) - for(unsigned i=0;istacks.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; } diff --git a/CGameState.h b/CGameState.h index 2bbcde71f..f57269b27 100644 --- a/CGameState.h +++ b/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 abilities; std::set 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 void save(Handler &h, const int version) { h & creature->idNumber; @@ -160,7 +163,7 @@ public: template 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 diff --git a/CHeroWindow.cpp b/CHeroWindow.cpp index e192895c6..b46107cbc 100644 --- a/CHeroWindow.cpp +++ b/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(); diff --git a/CPlayerInterface.cpp b/CPlayerInterface.cpp index 737e81dda..410dc4cc2 100644 --- a/CPlayerInterface.cpp +++ b/CPlayerInterface.cpp @@ -1887,19 +1887,12 @@ int3 CPlayerInterface::repairScreenPos(int3 pos) void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, int val) { boost::unique_lock 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 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"<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"< 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 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 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 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); diff --git a/CPlayerInterface.h b/CPlayerInterface.h index 50c283f20..1305dbbb7 100644 --- a/CPlayerInterface.h +++ b/CPlayerInterface.h @@ -380,6 +380,7 @@ public: void tileRevealed(const std::set &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 diff --git a/ChangeLog b/ChangeLog index 3fd873c4e..ccc4e6ccf 100644 --- a/ChangeLog +++ b/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 diff --git a/client/Client.cpp b/client/Client.cpp index 93379e679..cf0e8ff2a 100644 --- a/client/Client.cpp +++ b/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: diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 72388eb9c..7d5bc0ade 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -9,6 +9,7 @@ #include "CSpellHandler.h" #include #include +#include #include #include "CTownHandler.h" #include "CArtHandler.h" @@ -611,7 +612,7 @@ std::vector > CGHeroInstance::getCurrentMoraleModifie //various morale bonuses (from buildings, artifacts, etc) for(std::list::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 > CGHeroInstance::getCurrentLuckModifiers //various morale bonuses (from buildings, artifacts, etc) for(std::list::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(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(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(6,69); + gbonus.bdescr.replacements.push_back((gbonus.bonus.val<0 ? "-" : "+") + boost::lexical_cast(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(6,68); + break; + case 64: //Rally Flag + messageID = 111; + gbonus.bonus.type = HeroBonus::MORALE_AND_LUCK; + gbonus.bonus.val = 1; + gbonus.bdescr << std::pair(6,102); + bonusMove = 400; + break; + case 56: //oasis + messageID = 95; + gbonus.bonus.type = HeroBonus::MORALE; + gbonus.bonus.val = 1; + gbonus.bdescr << std::pair(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(6,97); + } + else + { + gbonus.bonus.val = 1; + gbonus.bdescr << std::pair(6,96); + } + break; + case 110://Watering Hole + messageID = 166; + gbonus.bonus.type = HeroBonus::MORALE; + gbonus.bonus.val = 1; + gbonus.bdescr << std::pair(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(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(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); } } diff --git a/lib/BattleAction.h b/lib/BattleAction.h index 33d6829e1..2228039cc 100644 --- a/lib/BattleAction.h +++ b/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 void serialize(Handler &h, const int version) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index e73369529..6a7760899 100644 --- a/lib/HeroBonus.h +++ b/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}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 5895eb589..a04cd2d2e 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -505,7 +505,7 @@ struct BattleStackAttacked : public CPack//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//3005 { return flags & 2; } + bool lucky() + { + return flags & 4; + } template void serialize(Handler &h, const int version) { h & stackAttacked & newAmount & newHP & flags & killedAmount & damageAmount & effect; diff --git a/map.cpp b/map.cpp index dc136fcfc..3d14e87d2 100644 --- a/map.cpp +++ b/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; diff --git a/map.h b/map.h index 49e7427d3..893617fe2 100644 --- a/map.h +++ b/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: diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4f0d95cc6..501b871ea 100644 --- a/server/CGameHandler.cpp +++ b/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 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 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 players, CConnection &c) @@ -2035,6 +2075,20 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army for(std::map >::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 >::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