From 4f8c7bd4bbf184a3b5b8e90e43d5a8fa152762bd Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 1 Jul 2017 11:34:00 +0300 Subject: [PATCH 1/3] CStack refactoring * removed all occurrences of attackerOwned * Use BattleSide enum * more tweaks --- AI/BattleAI/BattleAI.h | 2 +- AI/BattleAI/PotentialTargets.cpp | 2 +- AI/BattleAI/ThreatMap.cpp | 2 +- client/Client.cpp | 2 +- client/battle/CBattleAnimations.cpp | 4 +- client/battle/CBattleInterface.cpp | 134 +++++++++++----------- client/battle/CBattleInterfaceClasses.cpp | 4 +- lib/CStack.cpp | 47 +++++--- lib/CStack.h | 12 +- lib/IGameEventsReceiver.h | 1 + lib/NetPacks.h | 6 +- lib/NetPacksLib.cpp | 4 +- lib/battle/AccessibilityInfo.cpp | 24 +--- lib/battle/AccessibilityInfo.h | 4 +- lib/battle/BattleAction.cpp | 12 +- lib/battle/BattleAction.h | 1 + lib/battle/BattleHex.cpp | 9 +- lib/battle/BattleHex.h | 27 ++++- lib/battle/BattleInfo.cpp | 36 +++--- lib/battle/BattleInfo.h | 17 +-- lib/battle/CBattleInfoCallback.cpp | 47 ++++---- lib/battle/CBattleInfoCallback.h | 2 +- lib/battle/CBattleInfoEssentials.cpp | 46 +++++--- lib/battle/CBattleInfoEssentials.h | 10 +- lib/battle/ReachabilityInfo.cpp | 7 +- lib/battle/ReachabilityInfo.h | 2 +- lib/spells/BattleSpellMechanics.cpp | 28 ++--- lib/spells/CDefaultSpellMechanics.cpp | 6 +- lib/spells/CSpellHandler.cpp | 6 +- server/CGameHandler.cpp | 59 +++++----- test/Battlefield.cpp | 14 ++- 31 files changed, 304 insertions(+), 273 deletions(-) diff --git a/AI/BattleAI/BattleAI.h b/AI/BattleAI/BattleAI.h index 73e42a12c..c44e0a71c 100644 --- a/AI/BattleAI/BattleAI.h +++ b/AI/BattleAI/BattleAI.h @@ -24,7 +24,7 @@ struct CurrentOffensivePotential { for(auto stack : cbc->battleGetStacks()) { - if(stack->attackerOwned == !side) + if(stack->side == side) ourAttacks[stack] = PotentialTargets(stack); else enemyAttacks[stack] = PotentialTargets(stack); diff --git a/AI/BattleAI/PotentialTargets.cpp b/AI/BattleAI/PotentialTargets.cpp index 669de85e3..8ec8a4f42 100644 --- a/AI/BattleAI/PotentialTargets.cpp +++ b/AI/BattleAI/PotentialTargets.cpp @@ -18,7 +18,7 @@ PotentialTargets::PotentialTargets(const CStack *attacker, const HypotheticChang for(const CStack *enemy : getCbc()->battleGetStacks()) { //Consider only stacks of different owner - if(enemy->attackerOwned == attacker->attackerOwned) + if(enemy->side == attacker->side) continue; auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility diff --git a/AI/BattleAI/ThreatMap.cpp b/AI/BattleAI/ThreatMap.cpp index a704fef82..77fd3d462 100644 --- a/AI/BattleAI/ThreatMap.cpp +++ b/AI/BattleAI/ThreatMap.cpp @@ -31,7 +31,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered) for(const CStack *enemy : getCbc()->battleGetStacks()) { //Consider only stacks of different owner - if(enemy->attackerOwned == endangered->attackerOwned) + if(enemy->side == endangered->side) continue; //Look-up which tiles can be melee-attacked diff --git a/client/Client.cpp b/client/Client.cpp index ebb23c26f..26adfdb62 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -855,7 +855,7 @@ void CClient::commenceTacticPhaseForInt(std::shared_ptr ba battleInt->yourTacticPhase(gs->curB->tacticDistance); if(gs && !!gs->curB && gs->curB->tacticDistance) //while awaiting for end of tactics phase, many things can happen (end of battle... or game) { - MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID))); + MakeAction ma(BattleAction::makeEndOFTacticPhase(gs->curB->playerToSide(battleInt->playerID).get())); sendRequest(&ma, battleInt->playerID); } } diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 08d2bf88a..f4e7eebfe 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -338,7 +338,7 @@ bool CMeleeAttackAnimation::init() static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_FRONT, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_FRONT}; - int revShiftattacker = (attackingStack->attackerOwned ? -1 : 1); + int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1); int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest); if(mutPos == -1 && attackingStack->doubleWide()) @@ -988,7 +988,7 @@ bool CSpellEffectAnimation::init() // Correction for 2-hex creatures. if (destStack != nullptr && destStack->doubleWide()) - be.x += (destStack->attackerOwned ? -1 : 1)*tilePos.w/2; + be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2; //Indicate if effect should be drawn on top of everything or just on top of the hex be.position = destTile; diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 828b9c426..cb44da49b 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -949,7 +949,7 @@ void CBattleInterface::bConsoleDownf() void CBattleInterface::newStack(const CStack *stack) { - creDir[stack->ID] = stack->attackerOwned; // must be set before getting stack position + creDir[stack->ID] = stack->side == BattleSide::ATTACKER; // must be set before getting stack position Point coords = CClickableHex::getXYUnitAnim(stack->position, stack, this); @@ -1180,15 +1180,15 @@ bool CBattleInterface::isCatapultAttackable(BattleHex hex) const return state != EWallState::DESTROYED && state != EWallState::NONE; } -const CGHeroInstance *CBattleInterface::getActiveHero() +const CGHeroInstance * CBattleInterface::getActiveHero() { const CStack *attacker = activeStack; - if (!attacker) + if(!attacker) { return nullptr; } - if (attacker->attackerOwned) + if(attacker->side == BattleSide::ATTACKER) { return attackingHeroInstance; } @@ -1801,7 +1801,7 @@ void CBattleInterface::endAction(const BattleAction* action) for (const CStack *s : stacks) { - if (s && creDir[s->ID] != bool(s->attackerOwned) && s->alive() + if (s && creDir[s->ID] != (s->side == BattleSide::ATTACKER) && s->alive() && creAnims[s->ID]->isIdle()) { addNewAnim(new CReverseAnimation(this, s, s->position, false)); @@ -2033,10 +2033,10 @@ std::string formatDmgRange(std::pair dmgRange) return (boost::format("%d") % dmgRange.first).str(); } -bool CBattleInterface::canStackMoveHere (const CStack *activeStack, BattleHex myNumber) +bool CBattleInterface::canStackMoveHere(const CStack * activeStack, BattleHex myNumber) { std::vector acc = curInt->cb->battleGetAvailableHexes (activeStack, false); - int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1); + BattleHex shiftedDest = myNumber + activeStack->destShiftDir(); if (vstd::contains(acc, myNumber)) return true; @@ -2262,14 +2262,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) realizeAction = [=] { - if (activeStack->doubleWide()) + if(activeStack->doubleWide()) { std::vector acc = curInt->cb->battleGetAvailableHexes(activeStack, false); - int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1); - if (vstd::contains(acc, myNumber)) - giveCommand (Battle::WALK ,myNumber, activeStack->ID); - else if (vstd::contains(acc, shiftedDest)) - giveCommand (Battle::WALK, shiftedDest, activeStack->ID); + BattleHex shiftedDest = myNumber + activeStack->destShiftDir(); + if(vstd::contains(acc, myNumber)) + giveCommand(Battle::WALK, myNumber, activeStack->ID); + else if(vstd::contains(acc, shiftedDest)) + giveCommand(Battle::WALK, shiftedDest, activeStack->ID); } else { @@ -2547,17 +2547,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) { bool doubleWide = activeStack->doubleWide(); destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) + - (activeStack->attackerOwned && doubleWide ? 1 : 0); - if (vstd::contains(occupyableHexes, destHex)) + (activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0); + if(vstd::contains(occupyableHexes, destHex)) return destHex; - else if (activeStack->attackerOwned) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { if (vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } else //if we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; @@ -2567,21 +2567,21 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH ); if (vstd::contains(occupyableHexes, destHex)) return destHex; - else if (activeStack->attackerOwned) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { - if (vstd::contains(occupyableHexes, destHex+1)) + if(vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } - else //if we are defender + else //we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; } case 8: //from left { - if (activeStack->doubleWide() && !activeStack->attackerOwned) + if(activeStack->doubleWide() && activeStack->side == activeStack->side == BattleSide::DEFENDER) { std::vector acc = curInt->cb->battleGetAvailableHexes(activeStack, false); if (vstd::contains(acc, myNumber)) @@ -2597,17 +2597,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) } case 9: //from top left { - destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH+1 : GameConstants::BFIELD_WIDTH ); - if (vstd::contains(occupyableHexes, destHex)) + destHex = myNumber - ((myNumber/GameConstants::BFIELD_WIDTH) % 2 ? GameConstants::BFIELD_WIDTH + 1 : GameConstants::BFIELD_WIDTH); + if(vstd::contains(occupyableHexes, destHex)) return destHex; - else if (activeStack->attackerOwned) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { - if (vstd::contains(occupyableHexes, destHex+1)) + if(vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } else //if we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; @@ -2616,27 +2616,27 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) { bool doubleWide = activeStack->doubleWide(); destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) + - (activeStack->attackerOwned && doubleWide ? 1 : 0); - if (vstd::contains(occupyableHexes, destHex)) + (activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0); + if(vstd::contains(occupyableHexes, destHex)) return destHex; - else if (activeStack->attackerOwned) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { - if (vstd::contains(occupyableHexes, destHex+1)) + if(vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } else //if we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; } case 11: //from right { - if (activeStack->doubleWide() && activeStack->attackerOwned) + if(activeStack->doubleWide() && activeStack->side == BattleSide::ATTACKER) { std::vector acc = curInt->cb->battleGetAvailableHexes(activeStack, false); - if (vstd::contains(acc, myNumber)) + if(vstd::contains(acc, myNumber)) return myNumber + 1; else return myNumber + 2; @@ -2650,16 +2650,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) case 13: //from bottom { destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ); - if (vstd::contains(occupyableHexes, destHex)) + if(vstd::contains(occupyableHexes, destHex)) return destHex; - else if (attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { - if (vstd::contains(occupyableHexes, destHex+1)) + if(vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } else //if we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; @@ -2669,14 +2669,14 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ); if (vstd::contains(occupyableHexes, destHex)) return destHex; - else if (attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker + else if(activeStack->side == BattleSide::ATTACKER) { - if (vstd::contains(occupyableHexes, destHex+1)) + if(vstd::contains(occupyableHexes, destHex+1)) return destHex+1; } else //if we are defender { - if (vstd::contains(occupyableHexes, destHex-1)) + if(vstd::contains(occupyableHexes, destHex-1)) return destHex-1; } break; @@ -3082,52 +3082,52 @@ void CBattleInterface::showAbsoluteObstacles(SDL_Surface *to) void CBattleInterface::showHighlightedHexes(SDL_Surface *to) { - for (int b=0; bstrictHovered && bfield[b]->hovered) + if(bfield[b]->strictHovered && bfield[b]->hovered) { - if (previouslyHoveredHex == -1) + if(previouslyHoveredHex == -1) previouslyHoveredHex = b; //something to start with - if (currentlyHoveredHex == -1) + if(currentlyHoveredHex == -1) currentlyHoveredHex = b; //something to start with - if (currentlyHoveredHex != b) //repair hover info + if(currentlyHoveredHex != b) //repair hover info { previouslyHoveredHex = currentlyHoveredHex; currentlyHoveredHex = b; } - if (settings["battle"]["mouseShadow"].Bool()) + if(settings["battle"]["mouseShadow"].Bool()) { const ISpellCaster *caster = nullptr; const CSpell *spell = nullptr; - if (spellToCast)//hero casts spell + if(spellToCast)//hero casts spell { spell = SpellID(spellToCast->additionalInfo).toSpell(); - caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance; + caster = activeStack->side == BattleSide::ATTACKER ? attackingHeroInstance : defendingHeroInstance; } - else if (creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell + else if(creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell { spell = SpellID(creatureSpellToCast).toSpell(); caster = activeStack; } - if (caster && spell) //when casting spell + if(caster && spell) //when casting spell { //calculating spell school level ui8 schoolLevel = caster->getSpellSchoolLevel(spell); // printing shaded hex(es) auto shaded = spell->rangeInHexes(currentlyHoveredHex, schoolLevel, curInt->cb->battleGetMySide()); - for (BattleHex shadedHex : shaded) + for(BattleHex shadedHex : shaded) { - if ((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH -1)) + if((shadedHex.getX() != 0) && (shadedHex.getX() != GameConstants::BFIELD_WIDTH - 1)) showHighlightedHex(to, shadedHex); } } - else if (active)//always highlight pointed hex + else if(active)//always highlight pointed hex { - if (currentlyHoveredHex.getX() != 0 + if(currentlyHoveredHex.getX() != 0 && currentlyHoveredHex.getX() != GameConstants::BFIELD_WIDTH - 1) showHighlightedHex(to, currentlyHoveredHex); } @@ -3135,18 +3135,18 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface *to) } } - if (activeStack && settings["battle"]["stackRange"].Bool()) + if(activeStack && settings["battle"]["stackRange"].Bool()) { std::set set = curInt->cb->battleGetAttackedHexes(activeStack, currentlyHoveredHex, attackingHex); - for (BattleHex hex : set) + for(BattleHex hex : set) showHighlightedHex(to, hex); // display the movement shadow of the stack at b (i.e. stack under mouse) - const CStack *const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false); - if (shere && shere != activeStack && shere->alive()) + const CStack * const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false); + if(shere && shere != activeStack && shere->alive()) { std::vector v = curInt->cb->battleGetAvailableHexes(shere, true ); - for (BattleHex hex : v) + for(BattleHex hex : v) showHighlightedHex(to, hex); } } @@ -3321,13 +3321,15 @@ void CBattleInterface::showAliveStacks(SDL_Surface *to, std::vectorposition + (stack->attackerOwned ? 1 : -1); - const bool edge = stack->position % GameConstants::BFIELD_WIDTH == (stack->attackerOwned ? GameConstants::BFIELD_WIDTH - 2 : 1); + const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1; + const int reverseSideShift = stack->side == BattleSide::ATTACKER ? -1 : 1; + const BattleHex nextPos = stack->position + sideShift; + const bool edge = stack->position % GameConstants::BFIELD_WIDTH == (stack->side == BattleSide::ATTACKER ? GameConstants::BFIELD_WIDTH - 2 : 1); const bool moveInside = !edge && !stackCountOutsideHexes[nextPos]; - int xAdd = (stack->attackerOwned ? 220 : 202) + - (stack->doubleWide() ? 44 : 0) *(stack->attackerOwned ? +1 : -1) + - (moveInside ? amountNormal->w + 10 : 0) *(stack->attackerOwned ? -1 : +1); - int yAdd = 260 + ((stack->attackerOwned || moveInside) ? 0 : -15); + int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) + + (stack->doubleWide() ? 44 : 0) * sideShift + + (moveInside ? amountNormal->w + 10 : 0) * reverseSideShift; + int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15); //blitting amount background box SDL_Surface *amountBG = amountNormal; diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index 1c474380b..cafe71699 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -384,7 +384,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect { auto stacks = owner.cb->battleGetAllStacks(); vstd::erase_if(stacks, [i](const CStack *stack) //erase stack of other side and not coming from garrison - { return stack->attackerOwned == i || !stack->base; }); + { return stack->side != i || !stack->base; }); auto best = vstd::maxElementByFun(stacks, [](const CStack *stack){ return stack->type->AIValue; }); if(best != stacks.end()) //should be always but to be safe... @@ -548,7 +548,7 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt //shifting position for double - hex creatures if(stack->doubleWide()) { - if(stack->attackerOwned) + if(stack->side == BattleSide::ATTACKER) { if(cbi->creDir[stack->ID]) ret.x -= 44; diff --git a/lib/CStack.cpp b/lib/CStack.cpp index bfc613e47..7d8c8126e 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -15,8 +15,8 @@ #include "NetPacks.h" -CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S) - : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), +CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, ui8 Side, SlotID S) + : base(Base), ID(I), owner(O), slot(S), side(Side), counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1), firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) { @@ -30,8 +30,8 @@ CStack::CStack() init(); setNodeType(STACK_BATTLE); } -CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S) - : base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), +CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S) + : base(nullptr), ID(I), owner(O), slot(S), side(Side), counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1), firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) { @@ -49,7 +49,7 @@ void CStack::init() firstHPleft = -1; owner = PlayerColor::NEUTRAL; slot = SlotID(255); - attackerOwned = false; + side = 1; position = BattleHex(); counterAttacksPerformed = 0; counterAttacksTotalCache = 0; @@ -145,9 +145,9 @@ BattleHex CStack::occupiedHex() const BattleHex CStack::occupiedHex(BattleHex assumedPos) const { - if (doubleWide()) + if(doubleWide()) { - if (attackerOwned) + if(side == BattleSide::ATTACKER) return assumedPos - 1; else return assumedPos + 1; @@ -165,17 +165,17 @@ std::vector CStack::getHexes() const std::vector CStack::getHexes(BattleHex assumedPos) const { - return getHexes(assumedPos, doubleWide(), attackerOwned); + return getHexes(assumedPos, doubleWide(), side); } -std::vector CStack::getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned) +std::vector CStack::getHexes(BattleHex assumedPos, bool twoHex, ui8 side) { std::vector hexes; hexes.push_back(assumedPos); - if (twoHex) + if(twoHex) { - if (AttackerOwned) + if(side == BattleSide::ATTACKER) hexes.push_back(assumedPos - 1); else hexes.push_back(assumedPos + 1); @@ -193,10 +193,10 @@ std::vector CStack::getSurroundingHexes(BattleHex attackerPos) const { BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position std::vector hexes; - if (doubleWide()) + if(doubleWide()) { const int WN = GameConstants::BFIELD_WIDTH; - if(attackerOwned) + if(side == BattleSide::ATTACKER) { //position is equal to front hex BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes); BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); @@ -226,6 +226,21 @@ std::vector CStack::getSurroundingHexes(BattleHex attackerPos) const } } +BattleHex::EDir CStack::destShiftDir() const +{ + if(doubleWide()) + { + if(side == BattleSide::ATTACKER) + return BattleHex::EDir::RIGHT; + else + return BattleHex::EDir::LEFT; + } + else + { + return BattleHex::EDir::NONE; + } +} + std::vector CStack::activeSpells() const { std::vector ret; @@ -382,11 +397,11 @@ bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defen return (BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front || (attacker->doubleWide() //back <=> front - && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0) + && BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos) >= 0) || (defender->doubleWide() //front <=> back - && BattleHex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0) + && BattleHex::mutualPosition(attackerPos, defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0) || (defender->doubleWide() && attacker->doubleWide()//back <=> back - && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0); + && BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0); } diff --git a/lib/CStack.h b/lib/CStack.h index 2edc9c341..b5309d5f2 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -24,7 +24,7 @@ public: ui32 firstHPleft; //HP of first creature in stack PlayerColor owner; //owner - player colour (255 for neutrals) SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures) - bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle) + ui8 side; BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower ///how many times this stack has been counterattacked this round ui8 counterAttacksPerformed; @@ -39,8 +39,8 @@ public: //overrides const CCreature* getCreature() const {return type;} - CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor - CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor + CStack(const CStackInstance *base, PlayerColor O, int I, ui8 Side, SlotID S); //c-tor + CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S = SlotID(255)); //c-tor CStack(); //c-tor ~CStack(); std::string nodeName() const override; @@ -73,10 +73,12 @@ public: BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1 std::vector getHexes() const; //up to two occupied hexes, starting from front std::vector getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front - static std::vector getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front + static std::vector getHexes(BattleHex assumedPos, bool twoHex, ui8 side); //up to two occupied hexes, starting from front bool coversPos(BattleHex position) const; //checks also if unit is double-wide std::vector getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size + BattleHex::EDir destShiftDir() const; + std::pair countKilledByAttack(int damageReceived) const; //returns pair void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional customCount = boost::none) const; //requires bsa.damageAmout filled @@ -110,7 +112,7 @@ public: assert(isIndependentNode()); h & static_cast(*this); h & static_cast(*this); - h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed + h & ID & baseAmount & firstHPleft & owner & slot & side & position & state & counterAttacksPerformed & shots & casts & count & resurrected; const CArmedInstance *army = (base ? base->armyObj : nullptr); diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index cfa5e619f..c6f228bdd 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -11,6 +11,7 @@ #include "battle/BattleHex.h" +#include "GameConstants.h" #include "int3.h" class CGTownInstance; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 8f162047c..474c3d927 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1628,13 +1628,13 @@ struct BattleStacksRemoved : public CPackForClient struct BattleStackAdded : public CPackForClient { BattleStackAdded() - : attacker(0), amount(0), pos(0), summoned(0), newStackID(0) + : side(0), amount(0), pos(0), summoned(0), newStackID(0) {}; DLL_LINKAGE void applyGs(CGameState *gs); void applyCl(CClient *cl); - int attacker; // if true, stack belongs to attacker + ui8 side; CreatureID creID; int amount; int pos; @@ -1645,7 +1645,7 @@ struct BattleStackAdded : public CPackForClient template void serialize(Handler &h, const int version) { - h & attacker & creID & amount & pos & summoned; + h & side & creID & amount & pos & summoned; } }; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 7ba96e5a5..aa24778da 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1384,7 +1384,7 @@ void BattleStackMoved::applyGs(CGameState *gs) { SpellCreatedObstacle *sands = dynamic_cast(oi.get()); assert(sands); - if(sands->casterSide != !s->attackerOwned) + if(sands->casterSide != s->side) sands->visibleForAnotherSide = true; } } @@ -1803,7 +1803,7 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs) } CStackBasicDescriptor csbd(creID, amount); - CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks? + CStack * addedStack = gs->curB->generateNewStack(csbd, side, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks? if (summoned) addedStack->state.insert(EBattleStackState::SUMMONED); diff --git a/lib/battle/AccessibilityInfo.cpp b/lib/battle/AccessibilityInfo.cpp index 48b86f9a8..44d518448 100644 --- a/lib/battle/AccessibilityInfo.cpp +++ b/lib/battle/AccessibilityInfo.cpp @@ -10,16 +10,17 @@ #include "../StdInc.h" #include "AccessibilityInfo.h" #include "../CStack.h" +#include "../GameConstants.h" bool AccessibilityInfo::accessible(BattleHex tile, const CStack * stack) const { - return accessible(tile, stack->doubleWide(), stack->attackerOwned); + return accessible(tile, stack->doubleWide(), stack->side); } -bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const +bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, ui8 side) const { // All hexes that stack would cover if standing on tile have to be accessible. - for(auto hex : CStack::getHexes(tile, doubleWide, attackerOwned)) + for(auto hex : CStack::getHexes(tile, doubleWide, side)) { // If the hex is out of range then the tile isn't accessible if(!hex.isValid()) @@ -27,25 +28,10 @@ bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attacke // If we're no defender which step on gate and the hex isn't accessible, then the tile // isn't accessible else if(at(hex) != EAccessibility::ACCESSIBLE && - !(at(hex) == EAccessibility::GATE && !attackerOwned)) + !(at(hex) == EAccessibility::GATE && side == BattleSide::DEFENDER)) { return false; } } return true; } - -bool AccessibilityInfo::occupiable(const CStack * stack, BattleHex tile) const -{ - //obviously, we can occupy tile by standing on it - if(accessible(tile, stack)) - return true; - if(stack->doubleWide()) - { - //Check the tile next to -> if stack stands there, it'll also occupy considered hex - const BattleHex anotherTile = tile + (stack->attackerOwned ? BattleHex::RIGHT : BattleHex::LEFT); - if(accessible(anotherTile, stack)) - return true; - } - return false; -} diff --git a/lib/battle/AccessibilityInfo.h b/lib/battle/AccessibilityInfo.h index 32f8b14d3..1162eefee 100644 --- a/lib/battle/AccessibilityInfo.h +++ b/lib/battle/AccessibilityInfo.h @@ -9,6 +9,7 @@ */ #pragma once #include "BattleHex.h" +#include "../GameConstants.h" class CStack; @@ -29,7 +30,6 @@ typedef std::array TAccessibilityArr struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray { - bool occupiable(const CStack * stack, BattleHex tile) const; bool accessible(BattleHex tile, const CStack * stack) const; //checks for both tiles if stack is double wide - bool accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const; //checks for both tiles if stack is double wide + bool accessible(BattleHex tile, bool doubleWide, ui8 side) const; //checks for both tiles if stack is double wide }; diff --git a/lib/battle/BattleAction.cpp b/lib/battle/BattleAction.cpp index 72c093f6e..38692e946 100644 --- a/lib/battle/BattleAction.cpp +++ b/lib/battle/BattleAction.cpp @@ -27,7 +27,7 @@ BattleAction::BattleAction(): BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed) { BattleAction ba; - ba.side = !healer->attackerOwned; + ba.side = healer->side; ba.actionType = STACK_HEAL; ba.stackNumber = healer->ID; ba.destinationTile = healed->position; @@ -37,7 +37,7 @@ BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed BattleAction BattleAction::makeDefend(const CStack * stack) { BattleAction ba; - ba.side = !stack->attackerOwned; + ba.side = stack->side; ba.actionType = DEFEND; ba.stackNumber = stack->ID; return ba; @@ -47,7 +47,7 @@ BattleAction BattleAction::makeDefend(const CStack * stack) BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom /*= BattleHex::INVALID*/) { BattleAction ba; - ba.side = !stack->attackerOwned; + ba.side = stack->side; ba.actionType = WALK_AND_ATTACK; ba.stackNumber = stack->ID; ba.destinationTile = attackFrom; @@ -58,7 +58,7 @@ BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack * BattleAction BattleAction::makeWait(const CStack * stack) { BattleAction ba; - ba.side = !stack->attackerOwned; + ba.side = stack->side; ba.actionType = WAIT; ba.stackNumber = stack->ID; return ba; @@ -67,7 +67,7 @@ BattleAction BattleAction::makeWait(const CStack * stack) BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target) { BattleAction ba; - ba.side = !shooter->attackerOwned; + ba.side = shooter->side; ba.actionType = SHOOT; ba.stackNumber = shooter->ID; ba.destinationTile = target->position; @@ -77,7 +77,7 @@ BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * BattleAction BattleAction::makeMove(const CStack * stack, BattleHex dest) { BattleAction ba; - ba.side = !stack->attackerOwned; + ba.side = stack->side; ba.actionType = WALK; ba.stackNumber = stack->ID; ba.destinationTile = dest; diff --git a/lib/battle/BattleAction.h b/lib/battle/BattleAction.h index e2ae825ef..6640602c1 100644 --- a/lib/battle/BattleAction.h +++ b/lib/battle/BattleAction.h @@ -9,6 +9,7 @@ */ #pragma once #include "BattleHex.h" +#include "../GameConstants.h" class CStack; diff --git a/lib/battle/BattleHex.cpp b/lib/battle/BattleHex.cpp index 328257bad..781cc9e6e 100644 --- a/lib/battle/BattleHex.cpp +++ b/lib/battle/BattleHex.cpp @@ -9,6 +9,7 @@ */ #include "../StdInc.h" #include "BattleHex.h" +#include "../GameConstants.h" BattleHex::BattleHex() : hex(INVALID) {} @@ -99,6 +100,8 @@ BattleHex& BattleHex::moveInDirection(EDir dir, bool hasToBeValid) case LEFT: setXY(x-1, y, hasToBeValid); break; + case NONE: + break; default: throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n"); break; @@ -160,7 +163,7 @@ void BattleHex::checkAndPush(BattleHex tile, std::vector & ret) ret.push_back(tile); } -BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, std::set & possibilities) +BattleHex BattleHex::getClosestTile(ui8 side, BattleHex initialPos, std::set & possibilities) { std::vector sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :( BattleHex initialHex = BattleHex(initialPos); @@ -175,11 +178,11 @@ BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, st return closestDistance < here.getDistance (initialPos, here); }; vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting - auto compareHorizontal = [attackerOwned, initialPos](const BattleHex left, const BattleHex right) -> bool + auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool { if(left.getX() != right.getX()) { - if (attackerOwned) + if(side == BattleSide::ATTACKER) return left.getX() > right.getX(); //find furthest right else return left.getX() < right.getX(); //find furthest left diff --git a/lib/battle/BattleHex.h b/lib/battle/BattleHex.h index 18ba8d599..c2e1b70e6 100644 --- a/lib/battle/BattleHex.h +++ b/lib/battle/BattleHex.h @@ -8,14 +8,35 @@ * */ #pragma once -#include "../GameConstants.h" + +//TODO: change to enum class + +namespace BattleSide +{ + enum + { + ATTACKER = 0, + DEFENDER = 1 + }; +} + +typedef boost::optional BattleSideOpt; // for battle stacks' positions struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design { si16 hex; static const si16 INVALID = -1; - enum EDir { TOP_LEFT, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, LEFT}; + enum EDir + { + TOP_LEFT, + TOP_RIGHT, + RIGHT, + BOTTOM_RIGHT, + BOTTOM_LEFT, + LEFT, + NONE + }; BattleHex(); BattleHex(si16 _hex); @@ -39,7 +60,7 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f static signed char mutualPosition(BattleHex hex1, BattleHex hex2); static char getDistance(BattleHex hex1, BattleHex hex2); static void checkAndPush(BattleHex tile, std::vector & ret); - static BattleHex getClosestTile(bool attackerOwned, BattleHex initialPos, std::set & possibilities); //TODO: vector or set? copying one to another is bad + static BattleHex getClosestTile(ui8 side, BattleHex initialPos, std::set & possibilities); //TODO: vector or set? copying one to another is bad template void serialize(Handler &h, const int version) diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index f9ea7453f..a50973baf 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -26,7 +26,7 @@ const CStack * BattleInfo::getNextStack() const return nullptr; } -int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos) const +int BattleInfo::getAvaliableHex(CreatureID creID, ui8 side, int initialPos) const { bool twoHex = VLC->creh->creatures[creID]->isDoubleWide(); //bool flying = VLC->creh->creatures[creID]->isFlying(); @@ -36,7 +36,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia pos = initialPos; else //summon elementals depending on player side { - if (attackerOwned) + if(side == BattleSide::ATTACKER) pos = 0; //top left else pos = GameConstants::BFIELD_WIDTH - 1; //top right @@ -46,7 +46,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia std::set occupyable; for(int i = 0; i < accessibility.size(); i++) - if(accessibility.accessible(i, twoHex, attackerOwned)) + if(accessibility.accessible(i, twoHex, side)) occupyable.insert(i); if (occupyable.empty()) @@ -54,7 +54,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia return BattleHex::INVALID; //all tiles are covered } - return BattleHex::getClosestTile(attackerOwned, pos, occupyable); + return BattleHex::getClosestTile(side, pos, occupyable); } std::pair< std::vector, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack) @@ -104,28 +104,28 @@ void BattleInfo::calculateCasualties(std::map * casualties) const si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount); vstd::amax(killed, 0); if(killed) - casualties[!st->attackerOwned][st->getCreature()->idNumber] += killed; + casualties[st->side][st->getCreature()->idNumber] += killed; } } -CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const +CStack * BattleInfo::generateNewStack(const CStackInstance & base, ui8 side, SlotID slot, BattleHex position) const { int stackID = getIdForNewStack(); - PlayerColor owner = sides[attackerOwned ? 0 : 1].color; + PlayerColor owner = sides[side].color; assert((owner >= PlayerColor::PLAYER_LIMIT) || (base.armyObj && base.armyObj->tempOwner == owner)); - auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); - ret->position = getAvaliableHex (base.getCreatureID(), attackerOwned, position); //TODO: what if no free tile on battlefield was found? + auto ret = new CStack(&base, owner, stackID, side, slot); + ret->position = getAvaliableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found? ret->state.insert(EBattleStackState::ALIVE); //alive state indication return ret; } -CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const +CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor & base, ui8 side, SlotID slot, BattleHex position) const { int stackID = getIdForNewStack(); - PlayerColor owner = sides[attackerOwned ? 0 : 1].color; - auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); + PlayerColor owner = sides[side].color; + auto ret = new CStack(&base, owner, stackID, side, slot); ret->position = position; ret->state.insert(EBattleStackState::ALIVE); //alive state indication return ret; @@ -155,7 +155,7 @@ void BattleInfo::localInitStack(CStack * s) } else //attach directly to obj to which stack belongs and creature type { - CArmedInstance *army = battleGetArmyObject(!s->attackerOwned); + CArmedInstance *army = battleGetArmyObject(s->side); s->attachTo(army); assert(s->type); s->attachTo(const_cast(s->type)); @@ -486,7 +486,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType if(creatureBank && i->second->type->isDoubleWide()) pos += side ? BattleHex::LEFT : BattleHex::RIGHT; - CStack * stack = curB->generateNewStack(*i->second, !side, i->first, pos); + CStack * stack = curB->generateNewStack(*i->second, side, i->first, pos); stacks.push_back(stack); } } @@ -496,7 +496,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType { if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive) { - CStack * stack = curB->generateNewStack (*heroes[i]->commander, !i, SlotID::COMMANDER_SLOT_PLACEHOLDER, + CStack * stack = curB->generateNewStack (*heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, creatureBank ? commanderBank[i] : commanderField[i]); stacks.push_back(stack); } @@ -506,15 +506,15 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL) { // keep tower - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -2); + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -2); stacks.push_back(stack); if (curB->town->fortLevel() >= CGTownInstance::CASTLE) { // lower tower + upper tower - CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -4); + CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -4); stacks.push_back(stack); - stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), false, SlotID::ARROW_TOWERS_SLOT, -3); + stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -3); stacks.push_back(stack); } diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index a2b202eb1..07c9d0481 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -55,28 +55,17 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb CGHeroInstance * battleGetFightingHero(ui8 side) const; const CStack * getNextStack() const; //which stack will have turn after current one - //void getStackQueue(std::vector &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action - //void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set & occupyable, bool flying, const CStack* stackToOmmit = nullptr) const; //send pointer to at least 187 allocated bytes - //static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS - int getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects - //void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result + int getAvaliableHex(CreatureID creID, ui8 side, int initialPos = -1) const; //find place for summon / clone effects std::pair< std::vector, int > getPath(BattleHex start, BattleHex dest, const CStack * stack); //returned value: pair; length may be different than number of elements in path since flying vreatures jump between distant hexes - //std::vector getAccessibility(const CStack * stack, bool addOccupiable, std::vector * attackable = nullptr, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range) - - //bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const; std::shared_ptr getObstacleOnTile(BattleHex tile) const; std::set getStoppers(bool whichSidePerspective) const; ui32 calculateDmg(const CStack * attacker, const CStack * defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting) void calculateCasualties(std::map * casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) - //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee - //std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks - //std::set getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks - - CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield - CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield + CStack * generateNewStack(const CStackInstance &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield + CStack * generateNewStack(const CStackBasicDescriptor &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 712b05ffc..c7ea0157a 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -103,10 +103,10 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con return ESpellCastProblem::INVALID; } const PlayerColor player = caster->getOwner(); - const si8 side = playerToSide(player); - if(side < 0) + const auto side = playerToSide(player); + if(!side) return ESpellCastProblem::INVALID; - if(!battleDoWeKnowAbout(side)) + if(!battleDoWeKnowAbout(side.get())) { logGlobal->warnStream() << "You can't check if enemy can cast given spell!"; return ESpellCastProblem::INVALID; @@ -119,7 +119,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con { case ECastingMode::HERO_CASTING: { - if(battleCastSpells(side) > 0) + if(battleCastSpells(side.get()) > 0) return ESpellCastProblem::ALREADY_CASTED_THIS_TURN; auto hero = dynamic_cast(caster); @@ -255,7 +255,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, int bestSpeed = fastest->Speed(turn); //FIXME: comparison between bool and integer. Logic does not makes sense either - if(fastest->attackerOwned != lastMoved) + if(fastest->side != lastMoved) { ret = fastest; } @@ -264,7 +264,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, for(j = i + 1; j < st.size(); j++) { if(!st[j]) continue; - if(st[j]->attackerOwned != lastMoved || st[j]->Speed(turn) != bestSpeed) + if(st[j]->side != lastMoved || st[j]->Speed(turn) != bestSpeed) break; } @@ -288,7 +288,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, else st[j] = nullptr; - lastMoved = ret->attackerOwned; + lastMoved = ret->side; return ret; }; @@ -362,9 +362,9 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, { //FIXME: both branches contain same code!!! if(out.size() && out.front() == active) - lastMoved = active->attackerOwned; + lastMoved = active->side; else - lastMoved = active->attackerOwned; + lastMoved = active->side; } else { @@ -418,7 +418,7 @@ std::vector CBattleInfoCallback::battleGetAvailableHexes(const CStack if(!reachability.isReachable(i)) continue; - if(battleTacticDist() && battleGetTacticsSide() == !stack->attackerOwned) + if(battleTacticDist() && battleGetTacticsSide() == stack->side) { //Stack has to perform tactic-phase movement -> can enter any reachable tile within given range if(!isInTacticRange(i)) @@ -453,7 +453,7 @@ std::vector CBattleInfoCallback::battleGetAvailableHexes(const CStack }); return availableNeighbor != ret.end(); }; - for(const CStack * otherSt : battleAliveStacks(stack->attackerOwned)) + for(const CStack * otherSt : battleAliveStacks(1-stack->side)) { if(!otherSt->isValidTarget(false)) continue; @@ -800,7 +800,6 @@ std::pair CBattleInfoCallback::battleEstimateDamage(CRandomGenerator RETURN_IF_NOT_BATTLE(std::make_pair(0, 0)); //const bool shooting = battleCanShoot(bai.attacker, bai.defenderPosition); //TODO handle bonus bearer - //const ui8 mySide = !attacker->attackerOwned; TDmgRange ret = calculateDmgRange(bai); @@ -965,7 +964,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi const int costToNeighbour = ret.distances[curHex] + 1; for(BattleHex neighbour : curHex.neighbouringTiles()) { - const bool accessible = accessibility.accessible(neighbour, params.doubleWide, params.attackerOwned); + const bool accessible = accessibility.accessible(neighbour, params.doubleWide, params.side); const int costFoundSoFar = ret.distances[neighbour]; if(accessible && costToNeighbour < costFoundSoFar) @@ -1001,7 +1000,7 @@ std::set CBattleInfoCallback::getStoppers(BattlePerspective::BattlePe return ret; } -std::pair CBattleInfoCallback::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const +std::pair CBattleInfoCallback::getNearestStack(const CStack * closest, BattleSideOpt side) const { auto reachability = getReachability(closest); auto avHexes = battleGetAvailableHexes(closest, false); @@ -1018,7 +1017,7 @@ std::pair CBattleInfoCallback::getNearestStack(const std::vector possibleStacks = battleGetStacksIf([=](const CStack * s) { - return s->isValidTarget(false) && s != closest && (boost::logic::indeterminate(attackerOwned) || s->attackerOwned == attackerOwned); + return s->isValidTarget(false) && s != closest && (!side || side.get() == s->side); }); for(const CStack * st : possibleStacks) @@ -1064,7 +1063,7 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const CStack *stack) const { ReachabilityInfo::Parameters params(stack); - if(!battleDoWeKnowAbout(!stack->attackerOwned)) + if(!battleDoWeKnowAbout(stack->side)) { //Stack is held by enemy, we can't use his perspective to check for reachability. // Happens ie. when hovering enemy stack for its range. The arg could be set properly, but it's easier to fix it here. @@ -1089,7 +1088,7 @@ ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityIn for(int i = 0; i < GameConstants::BFIELD_SIZE; i++) { - if(ret.accessibility.accessible(i, params.doubleWide, params.attackerOwned)) + if(ret.accessibility.accessible(i, params.doubleWide, params.side)) { ret.predecessors[i] = params.startPosition; ret.distances[i] = BattleHex::getDistance(params.startPosition, i); @@ -1103,7 +1102,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack { //does not return hex attacked directly //TODO: apply rotation to two-hex attackers - bool isAttacker = attacker->attackerOwned; + bool isAttacker = attacker->side == BattleSide::ATTACKER; AttackableTiles at; RETURN_IF_NOT_BATTLE(at); @@ -1482,7 +1481,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c case SpellID::PROTECTION_FROM_FIRE: case SpellID::PROTECTION_FROM_WATER: { - const ui8 enemySide = (ui8)subject->attackerOwned; + const ui8 enemySide = 1 - subject->side; //todo: only if enemy has spellbook if (!battleHasHero(enemySide)) //only if there is enemy hero continue; @@ -1567,17 +1566,17 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const if(!battleCanSurrender(Player)) return -1; - const si8 playerSide = playerToSide(Player); - if(playerSide < 0) + const auto side = playerToSide(Player); + if(!side) return -1; int ret = 0; double discount = 0; - for(const CStack *s : battleAliveStacks(playerSide)) + for(const CStack * s : battleAliveStacks(side.get())) if(s->base) //we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines ret += s->getCreature()->cost[Res::GOLD] * s->count; - if(const CGHeroInstance * h = battleGetFightingHero(playerSide)) + if(const CGHeroInstance * h = battleGetFightingHero(side.get())) discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT); ret *= (100.0 - discount) / 100.0; @@ -1616,7 +1615,7 @@ boost::optional CBattleInfoCallback::battleIsFinished() const { if(stack->alive() && !stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) { - hasStack[1-stack->attackerOwned] = true; + hasStack[stack->side] = true; } } diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index fdd727a11..947cf5319 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -101,7 +101,7 @@ public: AccessibilityInfo getAccesibility() const; AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible. AccessibilityInfo getAccesibility(const std::vector & accessibleHexes) const; //given hexes will be marked as accessible - std::pair getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; + std::pair getNearestStack(const CStack * closest, BattleSideOpt side) const; protected: ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const; ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const; diff --git a/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp index 1c0e22000..81fe7317f 100644 --- a/lib/battle/CBattleInfoEssentials.cpp +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -67,7 +67,7 @@ bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const for(const CStack * s : battleGetAllStacks()) { - if(s->attackerOwned == !side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType)) + if(s->side == side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType)) return true; } @@ -102,7 +102,7 @@ TStacks CBattleInfoEssentials::battleAliveStacks() const TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const { return battleGetStacksIf([=](const CStack * s){ - return s->isValidTarget(false) && s->attackerOwned == !side; + return s->isValidTarget(false) && s->side == side; }); } @@ -237,8 +237,11 @@ const IBonusBearer * CBattleInfoEssentials::getBattleNode() const bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const { RETURN_IF_NOT_BATTLE(false); - const si8 mySide = playerToSide(player); - const CGHeroInstance *myHero = battleGetFightingHero(mySide); + const auto side = playerToSide(player); + if(!side) + return false; + + const CGHeroInstance *myHero = battleGetFightingHero(side.get()); //current player have no hero if(!myHero) @@ -249,7 +252,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const return false; //we are besieged defender - if(mySide == BattleSide::DEFENDER && battleGetSiegeLevel()) + if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel()) { auto town = battleGetDefendedTown(); if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) @@ -259,23 +262,31 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const return true; } -si8 CBattleInfoEssentials::playerToSide(PlayerColor player) const +BattleSideOpt CBattleInfoEssentials::playerToSide(PlayerColor player) const { - RETURN_IF_NOT_BATTLE(-1); + RETURN_IF_NOT_BATTLE(boost::none); int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; }); if(ret < 0) logGlobal->warnStream() << "Cannot find side for player " << player; - return ret; + return BattleSideOpt(ret); +} + +ui8 CBattleInfoEssentials::otherSide(ui8 side) const +{ + if(side == BattleSide::ATTACKER) + return BattleSide::DEFENDER; + else + return BattleSide::ATTACKER; } bool CBattleInfoEssentials::playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const { RETURN_IF_NOT_BATTLE(false); - const si8 playerSide = playerToSide(player); - if (playerSide >= 0) + const auto side = playerToSide(player); + if(side) { - if (getBattle()->sides[!playerSide].hero == h) + if (getBattle()->sides[otherSide(side.get())].hero == h) return true; } return false; @@ -290,10 +301,12 @@ ui8 CBattleInfoEssentials::battleGetSiegeLevel() const bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const { RETURN_IF_NOT_BATTLE(false); - ui8 mySide = playerToSide(player); - bool iAmSiegeDefender = (mySide == BattleSide::DEFENDER && battleGetSiegeLevel()); + const auto side = playerToSide(player); + if(!side) + return false; + bool iAmSiegeDefender = (side.get() == BattleSide::DEFENDER && battleGetSiegeLevel()); //conditions like for fleeing (except escape tunnel presence) + enemy must have a hero - return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(!mySide); + return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(otherSide(side.get())); } bool CBattleInfoEssentials::battleHasHero(ui8 side) const @@ -334,7 +347,10 @@ PlayerColor CBattleInfoEssentials::battleGetOwner(const CStack * stack) const const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const CStack * stack) const { RETURN_IF_NOT_BATTLE(nullptr); - return getBattle()->sides.at(playerToSide(battleGetOwner(stack))).hero; + const auto side = playerToSide(battleGetOwner(stack)); + if(!side) + return nullptr; + return getBattle()->sides.at(side.get()).hero; } bool CBattleInfoEssentials::battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness /* = false*/) const diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h index 7eaa37517..5086318b8 100644 --- a/lib/battle/CBattleInfoEssentials.h +++ b/lib/battle/CBattleInfoEssentials.h @@ -9,6 +9,7 @@ */ #pragma once #include "CCallbackBase.h" +#include "BattleHex.h" class CGTownInstance; class CGHeroInstance; @@ -32,12 +33,6 @@ namespace BattlePerspective }; } -namespace BattleSide -{ - enum {ATTACKER = 0, DEFENDER = 1}; -} - - class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase { protected: @@ -71,7 +66,8 @@ public: si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?) bool battleCanFlee(PlayerColor player) const; bool battleCanSurrender(PlayerColor player) const; - si8 playerToSide(PlayerColor player) const; + ui8 otherSide(ui8 side) const; + BattleSideOpt playerToSide(PlayerColor player) const; bool playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const; ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle bool battleHasHero(ui8 side) const; diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp index eb1c32490..de1ea6ba0 100644 --- a/lib/battle/ReachabilityInfo.cpp +++ b/lib/battle/ReachabilityInfo.cpp @@ -17,16 +17,17 @@ ReachabilityInfo::Parameters::Parameters() { stack = nullptr; perspective = BattlePerspective::ALL_KNOWING; - attackerOwned = doubleWide = flying = false; + side = 0; + doubleWide = flying = false; } ReachabilityInfo::Parameters::Parameters(const CStack * Stack) { stack = Stack; - perspective = (BattlePerspective::BattlePerspective)(!Stack->attackerOwned); + perspective = (BattlePerspective::BattlePerspective)(Stack->side); startPosition = Stack->position; doubleWide = stack->doubleWide(); - attackerOwned = stack->attackerOwned; + side = stack->side; flying = stack->hasBonusOfType(Bonus::FLYING); knownAccessible = stack->getHexes(); } diff --git a/lib/battle/ReachabilityInfo.h b/lib/battle/ReachabilityInfo.h index cf70ee375..e48418001 100644 --- a/lib/battle/ReachabilityInfo.h +++ b/lib/battle/ReachabilityInfo.h @@ -27,7 +27,7 @@ struct DLL_LINKAGE ReachabilityInfo { const CStack * stack; //stack for which calculation is mage => not required (kept for debugging mostly), following variables are enough - bool attackerOwned; + ui8 side; bool doubleWide; bool flying; std::vector knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself) diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index b4c69cbb4..ddbfad26d 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -104,7 +104,7 @@ std::vector ChainLightningMechanics::calculateAffectedStacks(con } if(possibleHexes.empty()) //not enough targets break; - lightningHex = BattleHex::getClosestTile(stack->attackerOwned, lightningHex, possibleHexes); + lightningHex = BattleHex::getClosestTile(stack->side, lightningHex, possibleHexes); } return res; @@ -121,13 +121,12 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const env->complain ("No target stack to clone!"); return; } - const int attacker = !(bool)parameters.casterSide; BattleStackAdded bsa; bsa.creID = clonedStack->type->idNumber; - bsa.attacker = attacker; + bsa.side = parameters.casterSide; bsa.summoned = true; - bsa.pos = parameters.cb->getAvaliableHex(bsa.creID, attacker); //TODO: unify it + bsa.pos = parameters.cb->getAvaliableHex(bsa.creID, parameters.casterSide); bsa.amount = clonedStack->count; env->sendAndApply(&bsa); @@ -370,8 +369,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl CSpell::TargetInfo ti(owner, caster->getSpellSchoolLevel(owner)); if(ti.smart) { + const auto side = cb->playerToSide(caster->getOwner()); + if(!side) + return ESpellCastProblem::INVALID; //if spell targeting is smart, then only attacker can use it - if(cb->playerToSide(caster->getOwner()) != BattleSide::ATTACKER) + if(side.get() != BattleSide::ATTACKER) return ESpellCastProblem::NO_APPROPRIATE_TARGET; } @@ -407,13 +409,13 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I ///ObstacleMechanics ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { - const si8 side = cb->playerToSide(ctx.caster->getOwner()); - if(side < 0) + const auto side = cb->playerToSide(ctx.caster->getOwner()); + if(!side) return ESpellCastProblem::INVALID; bool hexesOutsideBattlefield = false; - auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side, &hexesOutsideBattlefield); + auto tilesThatMustBeClear = owner->rangeInHexes(ctx.destination, ctx.schoolLvl, side.get(), &hexesOutsideBattlefield); for(const BattleHex & hex : tilesThatMustBeClear) if(!isHexAviable(cb, hex, ctx.ti.clearAffected)) @@ -499,11 +501,11 @@ void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env ESpellCastProblem::ESpellCastProblem LandMineMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { //LandMine are useless if enemy has native stack and can see mines, check for LandMine damage immunity is done in general way by CSpell - const si8 playerSide = cb->playerToSide(caster->getOwner()); - if(playerSide < 0) + const auto side = cb->playerToSide(caster->getOwner()); + if(!side) return ESpellCastProblem::INVALID; - const si8 otherSide = !playerSide; + const ui8 otherSide = cb->otherSide(side.get()); if(cb->battleHasNativeStack(otherSide)) return ESpellCastProblem::NO_APPROPRIATE_TARGET; @@ -887,9 +889,9 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const { BattleStackAdded bsa; bsa.creID = creatureToSummon; - bsa.attacker = !(bool)parameters.casterSide; + bsa.side = parameters.casterSide; bsa.summoned = true; - bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, !(bool)parameters.casterSide); //TODO: unify it + bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, parameters.casterSide); //TODO: unify it //TODO stack casting -> probably power will be zero; set the proper number of creatures manually int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 185bdd0ab..5bfac9c44 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -649,10 +649,10 @@ std::vector DefaultSpellMechanics::calculateAffectedStacks(const { std::set attackedCres;//std::set to exclude multiple occurrences of two hex creatures - const si8 playerSide = cb->playerToSide(ctx.caster->getOwner()); - if(playerSide < 0) + const auto side = cb->playerToSide(ctx.caster->getOwner()); + if(!side) return std::vector(); - auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, playerSide); + auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, side.get()); //hackfix for banned creature massive spells if(!ctx.ti.massive && owner->getLevelInfo(ctx.schoolLvl).range == "X") diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 648d50d8e..fc76ca561 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -186,15 +186,15 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; const PlayerColor player = caster->getOwner(); - const si8 side = cb->playerToSide(player); + const auto side = cb->playerToSide(player); - if(side < 0) + if(!side) return ESpellCastProblem::INVALID; //effect like Recanter's Cloak. Blocks also passive casting. //TODO: check creature abilities to block //TODO: check any possible caster - if(cb->battleMaxSpellLevel(side) < level) + if(cb->battleMaxSpellLevel(side.get()) < level) return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED; const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index d0428f00d..5edaeaa5f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -138,11 +138,13 @@ static void giveExp(BattleResult &r) } } -static void summonGuardiansHelper(std::vector & output, const BattleHex & targetPosition, bool targetIsAttacker, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard +static void summonGuardiansHelper(std::vector & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard { int x = targetPosition.getX(); int y = targetPosition.getY(); + const bool targetIsAttacker = side == BattleSide::ATTACKER; + if (targetIsAttacker) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3... BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output); else @@ -1160,21 +1162,15 @@ int CGameHandler::moveStack(int stack, BattleHex dest) auto accessibility = getAccesibility(curStack); //shifting destination (if we have double wide stack and we can occupy dest but not be exactly there) - if (!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack)) + if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack)) { - if (curStack->attackerOwned) - { - if (accessibility.accessible(dest+1, curStack)) - dest += BattleHex::RIGHT; - } - else - { - if (accessibility.accessible(dest-1, curStack)) - dest += BattleHex::LEFT; - } + BattleHex shifted = dest.cloneInDirection(curStack->destShiftDir()); + + if(accessibility.accessible(shifted, curStack)) + dest = shifted; } - if ((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility.accessible(dest, curStack)) + if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility.accessible(dest, curStack)) { complain("Given destination is not accessible!"); return 0; @@ -1182,7 +1178,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest) bool canUseGate = false; auto dbState = gs->curB->si.gateState; - if (battleGetSiegeLevel() > 0 && !curStack->attackerOwned && + if(battleGetSiegeLevel() > 0 && curStack->side == BattleSide::DEFENDER && dbState != EGateState::DESTROYED && dbState != EGateState::BLOCKED) { @@ -3857,7 +3853,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) if (battleTacticDist()) { - if (stack && !stack->attackerOwned != battleGetTacticsSide()) + if (stack && stack->side != battleGetTacticsSide()) { complain("This is not a stack of side that has tactics!"); return false; @@ -3951,10 +3947,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName()); - if (stack->position != ba.destinationTile //we wasn't able to reach destination tile - && !(stack->doubleWide() - && (stack->position == ba.destinationTile + (stack->attackerOwned ? +1 : -1)) - ) //nor occupy specified hex + if(stack->position != ba.destinationTile //we wasn't able to reach destination tile + && !(stack->doubleWide() && (stack->position == ba.destinationTile.cloneInDirection(stack->destShiftDir()))) //nor occupy specified hex ) { complain("We cannot move this stack to its destination " + stack->getCreature()->namePl); @@ -4292,7 +4286,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream! BattleStackAdded bsa; - bsa.attacker = summoner->attackerOwned; + bsa.side = summoner->side; bsa.creID = summonedType; ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); @@ -4303,7 +4297,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) bsa.amount = std::min(canRiseAmount, destStack->baseAmount); - bsa.pos = gs->curB->getAvaliableHex(bsa.creID, bsa.attacker, destStack->position); + bsa.pos = gs->curB->getAvaliableHex(bsa.creID, bsa.side, destStack->position); bsa.summoned = false; if (bsa.amount) //there's rare possibility single creature cannot rise desired type @@ -4661,7 +4655,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c //helper info const SpellCreatedObstacle *spellObstacle = dynamic_cast(&obstacle); //not nice but we may need spell params - const ui8 side = !curStack->attackerOwned; //if enemy is defending (false = 0), side of enemy hero is 1 (true) + const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true) const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower if (obstacle.obstacleType == CObstacleInstance::MOAT) @@ -5416,6 +5410,7 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) BattleStackAdded resurrectInfo; resurrectInfo.pos = defender->position; + resurrectInfo.side = defender->side; if (bonusAdditionalInfo != -1) resurrectInfo.creID = (CreatureID)bonusAdditionalInfo; @@ -5515,7 +5510,7 @@ void CGameHandler::makeStackDoNothing(const CStack * next) doNothing.actionType = Battle::NO_ACTION; doNothing.additionalInfo = 0; doNothing.destinationTile = -1; - doNothing.side = !next->attackerOwned; + doNothing.side = next->side; doNothing.stackNumber = next->ID; makeAutomaticAction(next, doNothing); @@ -5708,16 +5703,16 @@ void CGameHandler::runBattle() if (!guardianIsBig) targetHexes = stack->getSurroundingHexes(); else - summonGuardiansHelper(targetHexes, stack->position, stack->attackerOwned, targetIsBig); + summonGuardiansHelper(targetHexes, stack->position, stack->side, targetIsBig); for (auto hex : targetHexes) { - if (accessibility.accessible(hex, guardianIsBig, stack->attackerOwned)) //without this multiple creatures can occupy one hex + if (accessibility.accessible(hex, guardianIsBig, stack->side)) //without this multiple creatures can occupy one hex { BattleStackAdded newStack; newStack.amount = std::max(1, (int)(stack->count * 0.01 * summonInfo->val)); newStack.creID = creatureData.num; - newStack.attacker = stack->attackerOwned; + newStack.side = stack->side; newStack.summoned = true; newStack.pos = hex.hex; sendAndApply(&newStack); @@ -5808,7 +5803,7 @@ void CGameHandler::runBattle() BattleAction ba; ba.actionType = Battle::BAD_MORALE; ba.additionalInfo = 1; - ba.side = !next->attackerOwned; + ba.side = next->side; ba.stackNumber = next->ID; makeAutomaticAction(next, ba); @@ -5819,12 +5814,12 @@ void CGameHandler::runBattle() if (next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk { logGlobal->debug("Handle Berserk effect"); - std::pair attackInfo = curB.getNearestStack(next, boost::logic::indeterminate); + std::pair attackInfo = curB.getNearestStack(next, boost::none); if (attackInfo.first != nullptr) { BattleAction attack; attack.actionType = Battle::WALK_AND_ATTACK; - attack.side = !next->attackerOwned; + attack.side = next->side; attack.stackNumber = next->ID; attack.additionalInfo = attackInfo.first->position; attack.destinationTile = attackInfo.second; @@ -5847,7 +5842,7 @@ void CGameHandler::runBattle() { BattleAction attack; attack.actionType = Battle::SHOOT; - attack.side = !next->attackerOwned; + attack.side = next->side; attack.stackNumber = next->ID; for (auto & elem : gs->curB->stacks) @@ -5880,7 +5875,7 @@ void CGameHandler::runBattle() getRandomGenerator()); attack.actionType = Battle::CATAPULT; attack.additionalInfo = 0; - attack.side = !next->attackerOwned; + attack.side = next->side; attack.stackNumber = next->ID; makeAutomaticAction(next, attack); @@ -5910,7 +5905,7 @@ void CGameHandler::runBattle() heal.actionType = Battle::STACK_HEAL; heal.additionalInfo = 0; heal.destinationTile = toBeHealed->position; - heal.side = !next->attackerOwned; + heal.side = next->side; heal.stackNumber = next->ID; makeAutomaticAction(next, heal); diff --git a/test/Battlefield.cpp b/test/Battlefield.cpp index a60726bd1..ac09ca65e 100644 --- a/test/Battlefield.cpp +++ b/test/Battlefield.cpp @@ -91,16 +91,16 @@ BOOST_AUTO_TEST_CASE(getClosestTile) possibilities.insert(119); possibilities.insert(186); - BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==3); + BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==3); mainHex = 139; - BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==119); + BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==119); mainHex = 16; - BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==100); + BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==100); mainHex = 166; - BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==186); + BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==186); mainHex = 76; - BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==3); - BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==100); + BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==3); + BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==100); } BOOST_AUTO_TEST_CASE(moveEDir) @@ -118,6 +118,8 @@ BOOST_AUTO_TEST_CASE(moveEDir) BOOST_TEST(mainHex==3); mainHex.moveInDirection(BattleHex::EDir::BOTTOM_LEFT); BOOST_TEST(mainHex==20); + mainHex.moveInDirection(BattleHex::EDir::NONE); + BOOST_TEST(mainHex==20); } BOOST_AUTO_TEST_SUITE_END() From e4c14c4cc2a976c5c0fb3856545d8827c11de70d Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Mon, 3 Jul 2017 12:59:33 +0300 Subject: [PATCH 2/3] avoid assertions in stack position checks --- client/battle/CBattleInterface.cpp | 4 ++-- server/CGameHandler.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index cb44da49b..51b164609 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2036,7 +2036,7 @@ std::string formatDmgRange(std::pair dmgRange) bool CBattleInterface::canStackMoveHere(const CStack * activeStack, BattleHex myNumber) { std::vector acc = curInt->cb->battleGetAvailableHexes (activeStack, false); - BattleHex shiftedDest = myNumber + activeStack->destShiftDir(); + BattleHex shiftedDest = myNumber.cloneInDirection(activeStack->destShiftDir(), false); if (vstd::contains(acc, myNumber)) return true; @@ -2265,7 +2265,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) if(activeStack->doubleWide()) { std::vector acc = curInt->cb->battleGetAvailableHexes(activeStack, false); - BattleHex shiftedDest = myNumber + activeStack->destShiftDir(); + BattleHex shiftedDest = myNumber.cloneInDirection(activeStack->destShiftDir(), false); if(vstd::contains(acc, myNumber)) giveCommand(Battle::WALK, myNumber, activeStack->ID); else if(vstd::contains(acc, shiftedDest)) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5edaeaa5f..608d9d12c 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1164,7 +1164,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest) //shifting destination (if we have double wide stack and we can occupy dest but not be exactly there) if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack)) { - BattleHex shifted = dest.cloneInDirection(curStack->destShiftDir()); + BattleHex shifted = dest.cloneInDirection(curStack->destShiftDir(), false); if(accessibility.accessible(shifted, curStack)) dest = shifted; @@ -3948,7 +3948,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName()); if(stack->position != ba.destinationTile //we wasn't able to reach destination tile - && !(stack->doubleWide() && (stack->position == ba.destinationTile.cloneInDirection(stack->destShiftDir()))) //nor occupy specified hex + && !(stack->doubleWide() && (stack->position == ba.destinationTile.cloneInDirection(stack->destShiftDir(), false))) //nor occupy specified hex ) { complain("We cannot move this stack to its destination " + stack->getCreature()->namePl); From 3ce238e4d39228648888891321ce6382b920cc68 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Mon, 3 Jul 2017 13:40:22 +0300 Subject: [PATCH 3/3] Tweak --- client/battle/CBattleInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 51b164609..913777c28 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -3104,12 +3104,12 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface *to) if(spellToCast)//hero casts spell { spell = SpellID(spellToCast->additionalInfo).toSpell(); - caster = activeStack->side == BattleSide::ATTACKER ? attackingHeroInstance : defendingHeroInstance; + caster = getActiveHero(); } else if(creatureSpellToCast >= 0 && stackCanCastSpell && creatureCasting)//stack casts spell { - spell = SpellID(creatureSpellToCast).toSpell(); - caster = activeStack; + spell = SpellID(creatureSpellToCast).toSpell(); + caster = activeStack; } if(caster && spell) //when casting spell