diff --git a/CCallback.cpp b/CCallback.cpp index 062ee85cd..d30290cd3 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -1111,3 +1111,37 @@ CBattleCallback::ESpellCastProblem CBattleCallback::battleCanCastThisSpell( cons return OK; } + +si8 CBattleCallback::battleGetTacticDist() +{ + if (!gs->curB) + { + tlog1 << "battleGetTacticDist called when no battle!\n"; + return 0; + } + + if (gs->curB->sides[gs->curB->tacticsSide] == player) + { + return gs->curB->tacticDistance; + } + return 0; +} + +ui8 CBattleCallback::battleGetMySide() +{ + if (!gs->curB) + { + tlog1 << "battleGetMySide called when no battle!\n"; + return 0; + } + + return gs->curB->sides[1] == player; +} + +bool CBattleCallback::battleMakeTacticAction( BattleAction * action ) +{ + MakeAction ma; + ma.ba = *action; + sendRequest(&ma); + return true; +} diff --git a/CCallback.h b/CCallback.h index 9cd53beec..1c47e1c79 100644 --- a/CCallback.h +++ b/CCallback.h @@ -105,6 +105,9 @@ public: virtual si8 battleHasDistancePenalty(const CStack * stack, THex destHex) =0; //checks if given stack has distance penalty virtual si8 battleHasWallPenalty(const CStack * stack, THex destHex) =0; //checks if given stack has wall penalty virtual si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) =0; //checks if teleportation of given stack to given position can take place + virtual si8 battleGetTacticDist() =0; //returns tactic distance for calling player or 0 if player is not in tactic phase + virtual ui8 battleGetMySide() =0; //return side of player in battle (attacker/defender) + virtual bool battleMakeTacticAction(BattleAction * action) =0; // performs tactic phase actions }; class ICallback : public virtual IBattleCallback @@ -234,6 +237,9 @@ public: si8 battleHasDistancePenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has distance penalty si8 battleHasWallPenalty(const CStack * stack, THex destHex) OVERRIDE; //checks if given stack has wall penalty si8 battleCanTeleportTo(const CStack * stack, THex destHex, int telportLevel) OVERRIDE; //checks if teleportation of given stack to given position can take place + si8 battleGetTacticDist() OVERRIDE; //returns tactic distance for calling player or 0 if player is not in tactic phase + ui8 battleGetMySide() OVERRIDE; //return side of player in battle (attacker/defender) + bool battleMakeTacticAction(BattleAction * action) OVERRIDE; // performs tactic phase actions friend class CCallback; friend class CClient; diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 5efaf796a..b64cca03e 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -104,7 +104,7 @@ bool CBattleAnimation::isEarliest(bool perStackConcurrency) if(perStackConcurrency && stAnim && thAnim && stAnim->stack->ID != thAnim->stack->ID) continue; - if(sen && thSen && perStackConcurrency) + if(sen && thSen && sen != thSen && perStackConcurrency) continue; CReverseAnim * revAnim = dynamic_cast(stAnim); @@ -1214,6 +1214,13 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe console->pos.y = 560 + pos.y; console->pos.w = 406; console->pos.h = 38; + if(curInt->cb->battleGetTacticDist()) + { + btactNext = new AdventureMapButton(std::string(), std::string(), boost::bind(&CBattleInterface::bTacticNextStack,this), 213 + pos.x, 560 + pos.y, "icm011.def", SDLK_SPACE); + btactEnd = new AdventureMapButton(std::string(), std::string(), boost::bind(&CBattleInterface::bEndTacticPhase,this), 419 + pos.x, 560 + pos.y, "icm012.def", SDLK_RETURN); + bDefence->block(true); + bWait->block(true); + } //loading hero animations if(hero1) // attacking hero @@ -1331,6 +1338,20 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe { children.push_back(&bfield[i]); } + + if(curInt->cb->battleGetTacticDist()) + { + BOOST_FOREACH(const CStack *s, curInt->cb->battleGetStacks()) + { + if(s->owner == curInt->playerID) + { + active = 1; + stackActivated(s); + active = 0; + break; + } + } + } } CBattleInterface::~CBattleInterface() @@ -3387,6 +3408,18 @@ void CBattleInterface::waitForAnims() LOCPLINT->pim->lock(); } +void CBattleInterface::bEndTacticPhase() +{ + BattleAction endt = BattleAction::makeEndOFTacticPhase(curInt->cb->battleGetMySide()); + curInt->cb->battleMakeTacticAction(&endt); + +} + +void CBattleInterface::bTacticNextStack() +{ + +} + void CBattleHero::show(SDL_Surface *to) { //animation of flag diff --git a/client/CBattleInterface.h b/client/CBattleInterface.h index 62553f72b..875e90ca5 100644 --- a/client/CBattleInterface.h +++ b/client/CBattleInterface.h @@ -379,7 +379,7 @@ class CBattleInterface : public CIntObject private: SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes; AdventureMapButton * bOptions, * bSurrender, * bFlee, * bAutofight, * bSpell, - * bWait, * bDefence, * bConsoleUp, * bConsoleDown; + * bWait, * bDefence, * bConsoleUp, * bConsoleDown, *btactNext, *btactEnd; CBattleConsole * console; CBattleHero * attackingHero, * defendingHero; //fighting heroes CStackQueue *queue; @@ -479,6 +479,8 @@ public: void bDefencef(); void bConsoleUpf(); void bConsoleDownf(); + void bTacticNextStack(); + void bEndTacticPhase(); //end of button handle funcs //napisz tu klase odpowiadajaca za wyswietlanie bitwy i obsluge uzytkownika, polecenia ma przekazywac callbackiem void activate(); diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index dd4dfca51..bccc0adfd 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -1705,14 +1705,14 @@ void CRecruitmentWindow::Cancel() void CRecruitmentWindow::sliderMoved(int to) { buy->block(!to); + redraw(); } void CRecruitmentWindow::clickLeft(tribool down, bool previousState) { - int curx = 192 + 51 - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); for(int i=0;imotion.x,GH.current->motion.y)) + if(isItIn(&(Rect(creatures[i].pos) + pos),GH.current->motion.x,GH.current->motion.y)) { which = i; int newAmount = std::min(amounts[i],creatures[i].amount); @@ -1725,18 +1725,9 @@ void CRecruitmentWindow::clickLeft(tribool down, bool previousState) slider->moveTo(newAmount); else slider->moveTo(slider->value); - curx = 192 + 51 - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); - for(int j=0;jactivate(); - max->activate(); - cancel->activate(); - slider->activate(); - GH.statusbar = bar; -} - -void CRecruitmentWindow::deactivate() -{ - deactivateLClick(); - deactivateRClick(); - buy->deactivate(); - max->deactivate(); - cancel->deactivate(); - slider->deactivate(); -} - -void CRecruitmentWindow::show(SDL_Surface * to) -{ - blitAt(bitmap,pos.x,pos.y,to); - buy->show(to); - max->show(to); - cancel->show(to); - slider->show(to); - + CIntObject::showAll(to); + char pom[15]; SDL_itoa(creatures[which].amount-slider->value,pom,10); //available - printAtMiddle(pom,pos.x+205,pos.y+253,FONT_SMALL,zwykly,to); + printAtMiddleLoc(pom,205,253,FONT_SMALL,zwykly,to); SDL_itoa(slider->value,pom,10); //recruit - printAtMiddle(pom,pos.x+279,pos.y+253,FONT_SMALL,zwykly,to); - printAtMiddle(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID]->namePl,pos.x+243,pos.y+32,FONT_BIG,tytulowy,to); //eg "Recruit Dragon flies" + printAtMiddleLoc(pom,279,253,FONT_SMALL,zwykly,to); + printAtMiddleLoc(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID]->namePl,243,32,FONT_BIG,tytulowy,to); //eg "Recruit Dragon flies" - int curx = pos.x+122-creatures[which].res.size()*24; + int curx = 122-creatures[which].res.size()*24; for(int i=creatures[which].res.size()-1; i>=0; i--)// decrement used to make gold displayed as first res { - blitAt(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx,pos.y+243,to); - blitAt(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx+258,pos.y+243,to); + blitAtLoc(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx,243,to); + blitAtLoc(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx+258,243,to); SDL_itoa(creatures[which].res[i].second,pom,10); - printAtMiddle(pom,curx+15,pos.y+287,FONT_SMALL,zwykly,to); + printAtMiddleLoc(pom,curx+15,287,FONT_SMALL,zwykly,to); SDL_itoa(creatures[which].res[i].second * slider->value,pom,10); - printAtMiddle(pom,curx+15+258,pos.y+287,FONT_SMALL,zwykly,to); + printAtMiddleLoc(pom,curx+15+258,287,FONT_SMALL,zwykly,to); curx+=32+16;//size of bitmap + distance between them } - for(int i=0; ishow(to); - - bar->show(to); + for(int j=0;j &Recruit, int y_offset) :recruit(Recruit), dwelling(Dwelling), level(Level), dst(Dst) { + used = LCLICK | RCLICK; + OBJ_CONSTRUCTION_CAPTURING_ALL; + which = 0; - SDL_Surface *hhlp = BitmapHandler::loadBitmap("TPRCRT.bmp"); - graphics->blueToPlayersAdv(hhlp,LOCPLINT->playerID); - bitmap = SDL_ConvertSurface(hhlp,screen->format,0); - SDL_SetColorKey(bitmap,SDL_SRCCOLORKEY,SDL_MapRGB(bitmap->format,0,255,255)); - SDL_FreeSurface(hhlp); - pos.x = screen->w/2 - bitmap->w/2; - pos.y = screen->h/2 - bitmap->h/2+y_offset; - pos.w = bitmap->w; - pos.h = bitmap->h; - bar = new CStatusBar(pos.x+8, pos.y+370, "APHLFTRT.bmp", 471); - max = new AdventureMapButton(CGI->generaltexth->zelp[553],boost::bind(&CRecruitmentWindow::Max,this),pos.x+134,pos.y+313,"IRCBTNS.DEF",SDLK_m); - buy = new AdventureMapButton(CGI->generaltexth->zelp[554],boost::bind(&CRecruitmentWindow::Buy,this),pos.x+212,pos.y+313,"IBY6432.DEF",SDLK_RETURN); - cancel = new AdventureMapButton(CGI->generaltexth->zelp[555],boost::bind(&CRecruitmentWindow::Cancel,this),pos.x+290,pos.y+313,"ICN6432.DEF",SDLK_ESCAPE); - slider = new CSlider(pos.x+176,pos.y+279,135,boost::bind(&CRecruitmentWindow::sliderMoved,this, _1),0,0,0,true); + + bitmap = new CPicture("TPRCRT.bmp"); + bitmap->colorizeAndConvert(LOCPLINT->playerID); + bitmap->center(); + pos = (bitmap->pos += Point(0, y_offset)); + + bar = new CGStatusBar(8, 370, "APHLFTRT.bmp", 471); + max = new AdventureMapButton(CGI->generaltexth->zelp[553],boost::bind(&CRecruitmentWindow::Max,this),134,313,"IRCBTNS.DEF",SDLK_m); + buy = new AdventureMapButton(CGI->generaltexth->zelp[554],boost::bind(&CRecruitmentWindow::Buy,this),212,313,"IBY6432.DEF",SDLK_RETURN); + cancel = new AdventureMapButton(CGI->generaltexth->zelp[555],boost::bind(&CRecruitmentWindow::Cancel,this),290,313,"ICN6432.DEF",SDLK_ESCAPE); + slider = new CSlider(176,279,135,0,0,0,0,true); + slider->moved = boost::bind(&CRecruitmentWindow::sliderMoved,this, _1); initCres(); - printAtMiddle(CGI->generaltexth->allTexts[346],113,232,FONT_SMALL,zwykly,bitmap); //cost per troop t - printAtMiddle(CGI->generaltexth->allTexts[465],205,233,FONT_SMALL,zwykly,bitmap); //available t - printAtMiddle(CGI->generaltexth->allTexts[16],279,233,FONT_SMALL,zwykly,bitmap); //recruit t - printAtMiddle(CGI->generaltexth->allTexts[466],371,232,FONT_SMALL,zwykly,bitmap); //total cost t - drawBorder(bitmap,172,222,67,42,int3(239,215,123)); - drawBorder(bitmap,246,222,67,42,int3(239,215,123)); - drawBorder(bitmap,64,222,99,76,int3(239,215,123)); - drawBorder(bitmap,322,222,99,76,int3(239,215,123)); - drawBorder(bitmap,133,312,66,34,int3(173,142,66)); - drawBorder(bitmap,211,312,66,34,int3(173,142,66)); - drawBorder(bitmap,289,312,66,34,int3(173,142,66)); + printAtMiddle(CGI->generaltexth->allTexts[346],113,232,FONT_SMALL,zwykly,*bitmap); //cost per troop t + printAtMiddle(CGI->generaltexth->allTexts[465],205,233,FONT_SMALL,zwykly,*bitmap); //available t + printAtMiddle(CGI->generaltexth->allTexts[16],279,233,FONT_SMALL,zwykly,*bitmap); //recruit t + printAtMiddle(CGI->generaltexth->allTexts[466],371,232,FONT_SMALL,zwykly,*bitmap); //total cost t + drawBorder(*bitmap,172,222,67,42,int3(239,215,123)); + drawBorder(*bitmap,246,222,67,42,int3(239,215,123)); + drawBorder(*bitmap,64,222,99,76,int3(239,215,123)); + drawBorder(*bitmap,322,222,99,76,int3(239,215,123)); + drawBorder(*bitmap,133,312,66,34,int3(173,142,66)); + drawBorder(*bitmap,211,312,66,34,int3(173,142,66)); + drawBorder(*bitmap,289,312,66,34,int3(173,142,66)); //border for creatures int curx = 192 + 50 - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); for(int i=0;icreh->creatures[creatures[i].ID]); + creatures[i].pos.x = curx-1; + creatures[i].pos.y = 65 - 1; + creatures[i].pos.w = 100 + 2; + creatures[i].pos.h = 130 + 2; +// if(which==i) +// drawBorder(*bitmap,curx-1,64,CREATURE_WIDTH,132,int3(255,0,0)); +// else +// drawBorder(*bitmap,curx-1,64,CREATURE_WIDTH,132,int3(239,215,123)); + creatures[i].pic = new CCreaturePic(curx, 65, CGI->creh->creatures[creatures[i].ID]); curx += TOTAL_CREATURE_WIDTH; } @@ -1871,18 +1841,13 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, co CRecruitmentWindow::~CRecruitmentWindow() { - cleanCres(); - delete max; - delete buy; - delete cancel; - SDL_FreeSurface(bitmap); - delete slider; - delete bar; } void CRecruitmentWindow::initCres() { - cleanCres(); + creatures.clear(); + amounts.clear(); + for(int i=0; icreatures.size(); i++) { if(level >= 0 && i != level) @@ -1907,16 +1872,6 @@ void CRecruitmentWindow::initCres() slider->setAmount(std::min(amounts[which],creatures[which].amount)); } -void CRecruitmentWindow::cleanCres() -{ - for(int i=0;igeneraltexth->arraytxt[noneTxtId]; else { - if (node->nodeType == CBonusSystemNode::STACK && + if (node->nodeType == CBonusSystemNode::STACK_INSTANCE && (node->hasBonusOfType (Bonus::UNDEAD) || node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING))) //it's a creature window { text += CGI->generaltexth->arraytxt[113]; //unaffected by morale diff --git a/client/GUIClasses.h b/client/GUIClasses.h index b10959bce..9d9436531 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -471,8 +471,8 @@ public: boost::function recruit; //void (int ID, int amount) <-- call to recruit creatures CSlider *slider; //for selecting amount AdventureMapButton *max, *buy, *cancel; - SDL_Surface *bitmap; //background - CStatusBar *bar; + CPicture *bitmap; //background + CGStatusBar *bar; int which; //which creature is active const CGDwelling *dwelling; @@ -486,11 +486,7 @@ public: void sliderMoved(int to); void clickLeft(tribool down, bool previousState); void clickRight(tribool down, bool previousState); - void activate(); - void deactivate(); - void show(SDL_Surface * to); - void showAll(SDL_Surface * to){show(to);}; - void cleanCres(); + void showAll(SDL_Surface * to); void initCres(); CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function & Recruit, int y_offset = 0); //creatures - pairs //c-tor ~CRecruitmentWindow(); //d-tor diff --git a/lib/BattleAction.cpp b/lib/BattleAction.cpp index bc636649e..e4ab83070 100644 --- a/lib/BattleAction.cpp +++ b/lib/BattleAction.cpp @@ -69,4 +69,12 @@ BattleAction BattleAction::makeMove(const CStack *stack, THex dest) ba.stackNumber = stack->ID; ba.destinationTile = dest; return ba; -} \ No newline at end of file +} + +BattleAction BattleAction::makeEndOFTacticPhase(ui8 side) +{ + BattleAction ba; + ba.side = side; + ba.actionType = END_TACTIC_PHASE; + return ba; +} diff --git a/lib/BattleAction.h b/lib/BattleAction.h index ef5f0694f..37f7da6c8 100644 --- a/lib/BattleAction.h +++ b/lib/BattleAction.h @@ -21,7 +21,7 @@ struct DLL_EXPORT BattleAction ui32 stackNumber;//stack ID, -1 left hero, -2 right hero, enum ActionType { - INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL + END_TACTIC_PHASE = -2, INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL }; ui8 actionType; //use ActionType enum for values //10 = Monster casts a spell (i.e. Faerie Dragons) 11 - Bad morale freeze 12 - stacks heals another stack @@ -39,5 +39,6 @@ struct DLL_EXPORT BattleAction static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, THex attackFrom = THex::INVALID); static BattleAction makeShotAttack(const CStack *shooter, const CStack *target); static BattleAction makeMove(const CStack *stack, THex dest); + static BattleAction makeEndOFTacticPhase(ui8 side); }; #endif // __BATTLEACTION_H__ diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 91dcf3a44..671e4e6be 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -315,11 +315,15 @@ std::vector BattleInfo::getAccessibility(const CStack * stack, bool addOcc } } - for (int i=0; i < BFIELD_SIZE ; ++i) { - if( - ( ( !addOccupiable && dist[i] <= stack->Speed() && ac[i] ) || ( addOccupiable && dist[i] <= stack->Speed() && isAccessible(i, ac, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), true) ) )//we can reach it - || (vstd::contains(occupyable, i) && ( dist[ i + (stack->attackerOwned ? 1 : -1 ) ] <= stack->Speed() ) && - ac[i + (stack->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex + for (int i=0; i < BFIELD_SIZE ; ++i) + { + bool rangeFits = tacticDistance + ? isInTacticRange(i) + : dist[i] <= stack->Speed(); + + if( ( !addOccupiable && rangeFits && ac[i] ) + || ( addOccupiable && rangeFits && isAccessible(i, ac, stack->doubleWide(), stack->attackerOwned, stack->hasBonusOfType(Bonus::FLYING), true) )//we can reach it + || (vstd::contains(occupyable, i) && (!tacticDistance && dist[ i + (stack->attackerOwned ? 1 : -1 ) ] <= stack->Speed() ) && ac[i + (stack->attackerOwned ? 1 : -1 )] ) //it's occupyable and we can reach adjacent hex ) { ret.push_back(i); @@ -501,17 +505,17 @@ TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* d { if(shooting) { - additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 1) / 100.0f; + additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARCHERY) / 100.0f; } else { - additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 22) / 100.0f; + additiveBonus += attackerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::OFFENCE) / 100.0f; } } if(defendingHero) { - multBonus *= (std::max(0, 100-defendingHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 23))) / 100.0f; + multBonus *= (std::max(0, 100-defendingHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARMORER))) / 100.0f; } //handling hate effect @@ -838,7 +842,7 @@ ui32 BattleInfo::calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const C //applying sorcery secondary skill if(caster) { - ret *= (100.f + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 25)) / 100.0f; //sorcery + ret *= (100.f + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::SORCERY)) / 100.0f; ret *= (100.f + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, sp->id)) / 100.0f; if(sp->air) @@ -1588,12 +1592,28 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const curB->addNewBonus(makeFeature(Bonus::PRIMARY_SKILL, Bonus::ONE_BATTLE, PrimarySkill::DEFENSE, 1, Bonus::TERRAIN_NATIVE)->addLimiter(nativeTerrain)); ////////////////////////////////////////////////////////////////////////// + int tacticLvls[2] = {0}; + for(int i = 0; i < ARRAY_COUNT(tacticLvls); i++) + { + if(heroes[i]) + tacticLvls[i] += heroes[i]->getSecSkillLevel(CGHeroInstance::TACTICS); + } + + if(int diff = tacticLvls[0] - tacticLvls[1]) + { + curB->tacticsSide = diff < 0; + curB->tacticDistance = std::abs(diff)*2 + 1; + } + return curB; } +bool BattleInfo::isInTacticRange( THex dest ) const +{ - - + return ((tacticsSide && dest.getX() > 0 && dest.getX() <= tacticDistance) + || (!tacticsSide && dest.getX() < BFIELD_WIDTH - 1 && dest.getX() >= BFIELD_WIDTH - tacticDistance - 1)); +} CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S) : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), @@ -1602,11 +1622,13 @@ CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S) assert(base); type = base->type; count = baseAmount = base->count; + nodeType = STACK_BATTLE; } CStack::CStack() { init(); + nodeType = STACK_BATTLE; } CStack::CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S) @@ -1614,6 +1636,7 @@ CStack::CStack(const CStackBasicDescriptor *stack, int O, int I, bool AO, int S) { type = stack->type; count = baseAmount = stack->count; + nodeType = STACK_BATTLE; } void CStack::init() diff --git a/lib/BattleState.h b/lib/BattleState.h index 0a0b581f7..59ade847f 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -63,12 +63,16 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode SiegeInfo si; si32 battlefieldType; + ui8 tacticsSide; //which side is requested to play tactics phase + ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line) + template void serialize(Handler &h, const int version) { h & sides & round & activeStack & siege & town & tile & stacks & belligerents & obstacles & castSpells & si & battlefieldType; h & heroes; h & usedSpellsHistory; + h & tacticsSide & tacticDistance; h & static_cast(*this); } @@ -117,6 +121,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode si8 battleMaxSpellLevel() const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, SPELL_LEVELS is returned void localInit(); static BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ); + bool isInTacticRange( THex dest ) const; }; class DLL_EXPORT CStack : public CBonusSystemNode, public CStackBasicDescriptor diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index c55c48406..113fc864e 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -585,23 +585,23 @@ void CArtHandler::addBonuses() giveArtBonus(53,Bonus::SIGHT_RADIOUS,+1);//Spyglass //necromancy bonus - giveArtBonus(54,Bonus::SECONDARY_SKILL_PREMY,+5,12, Bonus::ADDITIVE_VALUE);//Amulet of the Undertaker - giveArtBonus(55,Bonus::SECONDARY_SKILL_PREMY,+10,12, Bonus::ADDITIVE_VALUE);//Vampire's Cowl - giveArtBonus(56,Bonus::SECONDARY_SKILL_PREMY,+15,12, Bonus::ADDITIVE_VALUE);//Dead Man's Boots + giveArtBonus(54,Bonus::SECONDARY_SKILL_PREMY,+5, CGHeroInstance::NECROMANCY, Bonus::ADDITIVE_VALUE);//Amulet of the Undertaker + giveArtBonus(55,Bonus::SECONDARY_SKILL_PREMY,+10, CGHeroInstance::NECROMANCY, Bonus::ADDITIVE_VALUE);//Vampire's Cowl + giveArtBonus(56,Bonus::SECONDARY_SKILL_PREMY,+15, CGHeroInstance::NECROMANCY, Bonus::ADDITIVE_VALUE);//Dead Man's Boots giveArtBonus(57,Bonus::MAGIC_RESISTANCE,+5);//Garniture of Interference giveArtBonus(58,Bonus::MAGIC_RESISTANCE,+10);//Surcoat of Counterpoise giveArtBonus(59,Bonus::MAGIC_RESISTANCE,+15);//Boots of Polarity //archery bonus - giveArtBonus(60,Bonus::SECONDARY_SKILL_PREMY,+5,1, Bonus::ADDITIVE_VALUE);//Bow of Elven Cherrywood - giveArtBonus(61,Bonus::SECONDARY_SKILL_PREMY,+10,1, Bonus::ADDITIVE_VALUE);//Bowstring of the Unicorn's Mane - giveArtBonus(62,Bonus::SECONDARY_SKILL_PREMY,+15,1, Bonus::ADDITIVE_VALUE);//Angel Feather Arrows + giveArtBonus(60,Bonus::SECONDARY_SKILL_PREMY,+5, CGHeroInstance::ARCHERY, Bonus::ADDITIVE_VALUE);//Bow of Elven Cherrywood + giveArtBonus(61,Bonus::SECONDARY_SKILL_PREMY,+10,CGHeroInstance::ARCHERY, Bonus::ADDITIVE_VALUE);//Bowstring of the Unicorn's Mane + giveArtBonus(62,Bonus::SECONDARY_SKILL_PREMY,+15,CGHeroInstance::ARCHERY, Bonus::ADDITIVE_VALUE);//Angel Feather Arrows //eagle eye bonus - giveArtBonus(63,Bonus::SECONDARY_SKILL_PREMY,+5,11, Bonus::ADDITIVE_VALUE);//Bird of Perception - giveArtBonus(64,Bonus::SECONDARY_SKILL_PREMY,+10,11, Bonus::ADDITIVE_VALUE);//Stoic Watchman - giveArtBonus(65,Bonus::SECONDARY_SKILL_PREMY,+15,11, Bonus::ADDITIVE_VALUE);//Emblem of Cognizance + giveArtBonus(63,Bonus::SECONDARY_SKILL_PREMY,+5, CGHeroInstance::EAGLE_EYE, Bonus::ADDITIVE_VALUE);//Bird of Perception + giveArtBonus(64,Bonus::SECONDARY_SKILL_PREMY,+10, CGHeroInstance::EAGLE_EYE, Bonus::ADDITIVE_VALUE);//Stoic Watchman + giveArtBonus(65,Bonus::SECONDARY_SKILL_PREMY,+15, CGHeroInstance::EAGLE_EYE, Bonus::ADDITIVE_VALUE);//Emblem of Cognizance //reducing cost of surrendering giveArtBonus(66,Bonus::SURRENDER_DISCOUNT,+10);//Statesman's Medal diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 6c4ab3496..d4087a988 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -939,7 +939,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src loadToIt (curVal, src, it, 4); if (curVal == 1) { - b.limiter.reset (new ExpRankLimiter(i)); + b.limiter.reset (new RankRangeLimiter(i)); bl.push_back(new Bonus(b)); break; //never turned off it seems } @@ -954,7 +954,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src if (curVal > lastVal) //threshold, add last bonus { b.val = lastVal; - b.limiter.reset (new ExpRankLimiter(i)); + b.limiter.reset (new RankRangeLimiter(i)); bl.push_back(new Bonus(b)); lastLev = i; //start new range from here, i = previous rank } diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 941d01cab..6c2caa536 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -422,7 +422,7 @@ void CStackInstance::init() type = NULL; idRand = -1; _armyObj = NULL; - nodeType = STACK; + nodeType = STACK_INSTANCE; } int CStackInstance::getQuantityID() const diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index fd49d9989..e224d1cc7 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -690,13 +690,11 @@ int CGHeroInstance::maxMovePoints(bool onLand) const double modifier = 0; if(onLand) { - //logistics: - modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 2) / 100.0f; + modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, LOGISTICS) / 100.0f; } else { - //navigation: - modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 5) / 100.0f; + modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, NAVIGATION) / 100.0f; } return int(base + base*modifier) + bonus; } @@ -1215,20 +1213,21 @@ void CGHeroInstance::updateSkill(int which, int val) case 27: //First Aid skillVal = 25 + 25*val; break; } - if (skillVal) //we don't need bonuses of other types here + + + int skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN; + if(Bonus * b = bonuses.getFirst(Selector::typeSybtype(Bonus::SECONDARY_SKILL_PREMY, which) && Selector::sourceType(Bonus::SECONDARY_SKILL))) //only local hero bonus { - Bonus * b = bonuses.getFirst(Selector::typeSybtype(Bonus::SECONDARY_SKILL_PREMY, which) && Selector::sourceType(Bonus::SECONDARY_SKILL)); - if (b) //only local hero bonus - { - b->val = skillVal; - } - else - { - Bonus *bonus = new Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, id, skillVal, ID, which, Bonus::BASE_NUMBER); - bonus->source = Bonus::SECONDARY_SKILL; - addNewBonus(bonus); - } + b->val = skillVal; + b->valType = skillValType; } + else + { + Bonus *bonus = new Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, id, skillVal, ID, which, skillValType); + bonus->source = Bonus::SECONDARY_SKILL; + addNewBonus(bonus); + } + } void CGHeroInstance::setPropertyDer( ui8 what, ui32 val ) { @@ -1312,7 +1311,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b // Hero knows necromancy. if (necromancyLevel > 0) { - double necromancySkill = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 12)/100.0; + double necromancySkill = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, NECROMANCY)/100.0; amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all... const std::map &casualties = battleResult.casualties[!battleResult.winner]; ui32 raisedUnits = 0; diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index 36c96e785..8e748c409 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -11,6 +11,7 @@ #include #include "CHeroHandler.h" #include "CGeneralTextHandler.h" +#include "BattleState.h" #define FOREACH_CONST_PARENT(pname) TCNodes parents; getParents(parents); BOOST_FOREACH(const CBonusSystemNode *pname, parents) #define FOREACH_PARENT(pname) TNodes parents; getParents(parents); BOOST_FOREACH(CBonusSystemNode *pname, parents) @@ -29,6 +30,8 @@ int DLL_EXPORT BonusList::totalValue() const int additive = 0; int indepMax = 0; bool hasIndepMax = false; + int indepMin = 0; + bool hasIndepMin = false; BOOST_FOREACH(Bonus *i, *this) { @@ -57,16 +60,33 @@ int DLL_EXPORT BonusList::totalValue() const amax(indepMax, i->val); } + break; + case Bonus::INDEPENDENT_MIN: + if (!indepMin) + { + indepMin = i->val; + hasIndepMin = true; + } + else + { + amax(indepMin, i->val); + } + break; } } int modifiedBase = base + (base * percentToBase) / 100; modifiedBase += additive; int valFirst = (modifiedBase * (100 + percentToAll)) / 100; + + if(hasIndepMin && hasIndepMax) + assert(indepMin < indepMax); if (hasIndepMax) - return std::max(valFirst, indepMax); - else - return valFirst; + amax(valFirst, indepMax); + if (hasIndepMax) + amin(valFirst, indepMin); + + return valFirst; } const DLL_EXPORT Bonus * BonusList::getFirst(const CSelector &selector) const { @@ -600,15 +620,29 @@ namespace Selector } } +const CStackInstance * retreiveStackInstance(const CBonusSystemNode *node) +{ + switch(node->nodeType) + { + case CBonusSystemNode::STACK_INSTANCE: + return (static_cast(node)); + case CBonusSystemNode::STACK_BATTLE: + return (static_cast(node))->base; + default: + return NULL; + } +} + const CCreature * retrieveCreature(const CBonusSystemNode *node) { switch(node->nodeType) { case CBonusSystemNode::CREATURE: return (static_cast(node)); - case CBonusSystemNode::STACK: - return (static_cast(node))->type; default: + const CStackInstance *csi = retreiveStackInstance(node); + if(csi) + return csi->type; return NULL; } } @@ -655,24 +689,11 @@ bool ILimiter::limit(const Bonus *b, const CBonusSystemNode &node) const /*retur bool CCreatureTypeLimiter::limit(const Bonus *b, const CBonusSystemNode &node) const { - switch (node.nodeType) - { - case CBonusSystemNode::STACK: - { - const CCreature *c = (static_cast(&node))->type; - return c != creature && (!includeUpgrades || !creature->isMyUpgrade(c)); - } //drop bonus if it's not our creature and (we dont check upgrades or its not our upgrade) - break; - case CBonusSystemNode::CREATURE: - { - const CCreature *c = (static_cast(&node)); - return c != creature && (!includeUpgrades || !creature->isMyUpgrade(c)); - } - break; - default: - return true; - } + const CCreature *c = retrieveCreature(&node); + return c != creature && (!includeUpgrades || !creature->isMyUpgrade(c)); + //drop bonus if it's not our creature and (we dont check upgrades or its not our upgrade) } + CCreatureTypeLimiter::CCreatureTypeLimiter(const CCreature &Creature, ui8 IncludeUpgrades /*= true*/) :creature(&Creature), includeUpgrades(IncludeUpgrades) { @@ -773,12 +794,15 @@ bool CreatureAlignmentLimiter::limit(const Bonus *b, const CBonusSystemNode &nod } } -ExpRankLimiter::ExpRankLimiter(ui8 Rank) - :rank(Rank) +RankRangeLimiter::RankRangeLimiter(ui8 Min, ui8 Max) + :minRank(Min), maxRank(Max) { } -RankRangeLimiter::RankRangeLimiter(ui8 Min, ui8 Max) - :min(Min), max(Max) +bool RankRangeLimiter::limit( const Bonus *b, const CBonusSystemNode &node ) const { + const CStackInstance *csi = retreiveStackInstance(&node); + if(csi) + return csi->getExpRank() < minRank || csi->getExpRank() > maxRank; + return true; } \ No newline at end of file diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 8317a01c3..8d7974311 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -213,7 +213,8 @@ struct DLL_EXPORT Bonus BASE_NUMBER, PERCENT_TO_ALL, PERCENT_TO_BASE, - INDEPENDENT_MAX //used for SPELL bonus + INDEPENDENT_MAX, //used for SPELL bonus + INDEPENDENT_MIN //used for SECONDARY_SKILL_PREMY bonus }; ui16 duration; //uses BonusDuration values @@ -433,7 +434,7 @@ public: enum ENodeTypes { - UNKNOWN, STACK, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO + UNKNOWN, STACK_INSTANCE, STACK_BATTLE, SPECIALITY, ARTIFACT, CREATURE, ARTIFACT_INSTANCE, HERO }; }; @@ -618,29 +619,17 @@ public: } }; -class DLL_EXPORT ExpRankLimiter : public ILimiter //applies to creatures with Rank >= rank -{ -public: - ui8 rank; - - ExpRankLimiter(ui8 Rank); - - template void serialize(Handler &h, const int version) - { - h & rank; - } -}; - class DLL_EXPORT RankRangeLimiter : public ILimiter //applies to creatures with min <= Rank <= max { public: - ui8 min, max; + ui8 minRank, maxRank; - RankRangeLimiter(ui8 Min, ui8 Max); + RankRangeLimiter(ui8 Min, ui8 Max = 255); + bool limit(const Bonus *b, const CBonusSystemNode &node) const OVERRIDE; template void serialize(Handler &h, const int version) { - h & min & max; + h & minRank & maxRank; } }; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index fba39b8d1..2e665cccf 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -243,8 +243,10 @@ DLL_EXPORT void RemoveObject::applyGs( CGameState *gs ) if(obj->ID==HEROI_TYPE) { CGHeroInstance *h = static_cast(obj); + PlayerState *p = gs->getPlayer(h->tempOwner); gs->map->heroes -= h; - gs->getPlayer(h->tempOwner)->heroes -= h; + p->heroes -= h; + h->detachFrom(p); h->tempOwner = 255; //no one owns beaten hero if(CGTownInstance *t = const_cast(h->visitedTown)) @@ -972,6 +974,12 @@ DLL_EXPORT void StartAction::applyGs( CGameState *gs ) { CStack *st = gs->curB->getStack(ba.stackNumber); + if(ba.actionType == BattleAction::END_TACTIC_PHASE) + { + gs->curB->tacticDistance = 0; + return; + } + if(ba.actionType != BattleAction::HERO_SPELL) //don't check for stack if it's custom action by hero { assert(st); @@ -1100,6 +1108,8 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs ) csi->setArmyObj(h); CStack * summonedStack = gs->curB->generateNewStack(*csi, gs->curB->stacks.size(), !side, 255, pos); summonedStack->state.insert(SUMMONED); + summonedStack->attachTo(csi); + summonedStack->postInit(); //summonedStack->addNewBonus( makeFeature(HeroBonus::SUMMONED, HeroBonus::ONE_BATTLE, 0, 0, HeroBonus::BONUS_FROM_HERO) ); gs->curB->stacks.push_back(summonedStack); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 036c3cf75..d02878320 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -352,10 +352,12 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer ui8 loser = sides[!battleResult.data->winner]; CasualtiesAfterBattle cab1(bEndArmy1, gs->curB), cab2(bEndArmy2, gs->curB); //calculate casualties before deleting battle + + sendAndApply(battleResult.data); //Eagle Eye secondary skill handling - const CGHeroInstance *vistoriousHero = gs->curB->heroes[battleResult.data->winner]; + /*const CGHeroInstance *vistoriousHero = gs->curB->heroes[battleResult.data->winner]; if(0 && vistoriousHero) { if(int eagleEyeLevel = vistoriousHero->getSecSkillLevel(CGHeroInstance::EAGLE_EYE)) @@ -403,7 +405,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer } } } - + */ if(!duel) { @@ -617,7 +619,7 @@ void CGameHandler::handleConnection(std::set players, CConnection &c) tlog1 << "Ended handling connection\n"; } -int CGameHandler::moveStack(int stack, int dest) +int CGameHandler::moveStack(int stack, THex dest) { int ret = 0; @@ -627,6 +629,11 @@ int CGameHandler::moveStack(int stack, int dest) assert(curStack); assert(dest < BFIELD_SIZE); + if (gs->curB->tacticDistance) + { + assert(gs->curB->isInTacticRange(dest)); + } + //initing necessary tables bool accessibility[BFIELD_SIZE]; std::vector accessible = gs->curB->getAccessibility(curStack, false); @@ -645,12 +652,12 @@ int CGameHandler::moveStack(int stack, int dest) if(curStack->attackerOwned) { if(accessibility[dest+1]) - dest+=1; + dest += THex::RIGHT; } else { if(accessibility[dest-1]) - dest-=1; + dest += THex::LEFT; } } @@ -675,9 +682,11 @@ int CGameHandler::moveStack(int stack, int dest) ret = path.second; + int creSpeed = gs->curB->tacticDistance ? BFIELD_SIZE : curStack->Speed(); + if(curStack->hasBonusOfType(Bonus::FLYING)) { - if(path.second <= curStack->Speed() && path.first.size() > 0) + if(path.second <= creSpeed && path.first.size() > 0) { //inform clients about move BattleStackMoved sm; @@ -691,7 +700,7 @@ int CGameHandler::moveStack(int stack, int dest) } else //for non-flying creatures { - int tilesToMove = std::max((int)(path.first.size() - curStack->Speed()), 0); + int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0); for(int v=path.first.size()-1; v>=tilesToMove; --v) { //inform clients about move @@ -3099,8 +3108,15 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) { tlog1 << "\tMaking action of type " << ba.actionType << std::endl; bool ok = true; + switch(ba.actionType) { + case BattleAction::END_TACTIC_PHASE: //wait + { + sendAndApply(&StartAction(ba)); + sendAndApply(&EndAction()); + break; + } case BattleAction::WALK: //walk { sendAndApply(&StartAction(ba)); //start movement @@ -4844,12 +4860,8 @@ void CGameHandler::runBattle() //tactic round { - if( (gs->curB->heroes[0] && gs->curB->heroes[0]->getSecSkillLevel(CGHeroInstance::TACTICS)>0) || - ( gs->curB->heroes[1] && gs->curB->heroes[1]->getSecSkillLevel(CGHeroInstance::TACTICS)>0) )//someone has tactics - { - //TODO: tactic round (round -1) - NEW_ROUND; - } + while(gs->curB->tacticDistance) + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); } //spells opening battle diff --git a/server/CGameHandler.h b/server/CGameHandler.h index b2d53690e..c2c5505f3 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -104,7 +104,7 @@ public: bool isAllowedExchange(int id1, int id2); void giveSpells(const CGTownInstance *t, const CGHeroInstance *h); - int moveStack(int stack, int dest); //returned value - travelled distance + int moveStack(int stack, THex dest); //returned value - travelled distance void startBattle(const CArmedInstance *armies[2], int3 tile, const CGHeroInstance *heroes[2], bool creatureBank, boost::function cb, const CGTownInstance *town = NULL); //use hero=NULL for no hero void runBattle(); void checkLossVictory(ui8 player); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 0d4527a94..fe284e041 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -6,6 +6,7 @@ #include "../lib/map.h" #include "../lib/CGameState.h" #include "../lib/BattleState.h" +#include "../lib/BattleAction.h" #define PLAYER_OWNS(id) (gh->getPlayerAt(c)==gh->getOwner(id)) @@ -209,6 +210,15 @@ bool MakeAction::applyGh( CGameHandler *gh ) { if(!GS(gh)->curB) ERROR_AND_RETURN; if(gh->connections[GS(gh)->curB->getStack(GS(gh)->curB->activeStack)->owner] != c) ERROR_AND_RETURN; + + if(GS(gh)->curB->tacticDistance) + { + if(ba.actionType != BattleAction::WALK && ba.actionType != BattleAction::END_TACTIC_PHASE) + ERROR_AND_RETURN; + if(gh->connections[GS(gh)->curB->sides[GS(gh)->curB->tacticsSide]] != c) + ERROR_AND_RETURN; + } + return gh->makeBattleAction(ba); }