1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

CStack refactoring

* removed all occurrences of attackerOwned
* Use BattleSide enum
* more tweaks
This commit is contained in:
AlexVinS 2017-07-01 11:34:00 +03:00
parent 29e50cc21c
commit 4f8c7bd4bb
31 changed files with 304 additions and 273 deletions

View File

@ -24,7 +24,7 @@ struct CurrentOffensivePotential
{ {
for(auto stack : cbc->battleGetStacks()) for(auto stack : cbc->battleGetStacks())
{ {
if(stack->attackerOwned == !side) if(stack->side == side)
ourAttacks[stack] = PotentialTargets(stack); ourAttacks[stack] = PotentialTargets(stack);
else else
enemyAttacks[stack] = PotentialTargets(stack); enemyAttacks[stack] = PotentialTargets(stack);

View File

@ -18,7 +18,7 @@ PotentialTargets::PotentialTargets(const CStack *attacker, const HypotheticChang
for(const CStack *enemy : getCbc()->battleGetStacks()) for(const CStack *enemy : getCbc()->battleGetStacks())
{ {
//Consider only stacks of different owner //Consider only stacks of different owner
if(enemy->attackerOwned == attacker->attackerOwned) if(enemy->side == attacker->side)
continue; continue;
auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility

View File

@ -31,7 +31,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
for(const CStack *enemy : getCbc()->battleGetStacks()) for(const CStack *enemy : getCbc()->battleGetStacks())
{ {
//Consider only stacks of different owner //Consider only stacks of different owner
if(enemy->attackerOwned == endangered->attackerOwned) if(enemy->side == endangered->side)
continue; continue;
//Look-up which tiles can be melee-attacked //Look-up which tiles can be melee-attacked

View File

@ -855,7 +855,7 @@ void CClient::commenceTacticPhaseForInt(std::shared_ptr<CBattleGameInterface> ba
battleInt->yourTacticPhase(gs->curB->tacticDistance); 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) 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); sendRequest(&ma, battleInt->playerID);
} }
} }

View File

@ -338,7 +338,7 @@ bool CMeleeAttackAnimation::init()
static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP, static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP,
CCreatureAnim::ATTACK_FRONT, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_FRONT}; 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); int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
if(mutPos == -1 && attackingStack->doubleWide()) if(mutPos == -1 && attackingStack->doubleWide())
@ -988,7 +988,7 @@ bool CSpellEffectAnimation::init()
// Correction for 2-hex creatures. // Correction for 2-hex creatures.
if (destStack != nullptr && destStack->doubleWide()) 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 //Indicate if effect should be drawn on top of everything or just on top of the hex
be.position = destTile; be.position = destTile;

View File

@ -949,7 +949,7 @@ void CBattleInterface::bConsoleDownf()
void CBattleInterface::newStack(const CStack *stack) 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); 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; return state != EWallState::DESTROYED && state != EWallState::NONE;
} }
const CGHeroInstance *CBattleInterface::getActiveHero() const CGHeroInstance * CBattleInterface::getActiveHero()
{ {
const CStack *attacker = activeStack; const CStack *attacker = activeStack;
if (!attacker) if(!attacker)
{ {
return nullptr; return nullptr;
} }
if (attacker->attackerOwned) if(attacker->side == BattleSide::ATTACKER)
{ {
return attackingHeroInstance; return attackingHeroInstance;
} }
@ -1801,7 +1801,7 @@ void CBattleInterface::endAction(const BattleAction* action)
for (const CStack *s : stacks) 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()) && creAnims[s->ID]->isIdle())
{ {
addNewAnim(new CReverseAnimation(this, s, s->position, false)); addNewAnim(new CReverseAnimation(this, s, s->position, false));
@ -2033,10 +2033,10 @@ std::string formatDmgRange(std::pair<ui32, ui32> dmgRange)
return (boost::format("%d") % dmgRange.first).str(); 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<BattleHex> acc = curInt->cb->battleGetAvailableHexes (activeStack, false); std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes (activeStack, false);
int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1); BattleHex shiftedDest = myNumber + activeStack->destShiftDir();
if (vstd::contains(acc, myNumber)) if (vstd::contains(acc, myNumber))
return true; return true;
@ -2262,14 +2262,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
realizeAction = [=] realizeAction = [=]
{ {
if (activeStack->doubleWide()) if(activeStack->doubleWide())
{ {
std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false); std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1); BattleHex shiftedDest = myNumber + activeStack->destShiftDir();
if (vstd::contains(acc, myNumber)) if(vstd::contains(acc, myNumber))
giveCommand (Battle::WALK ,myNumber, activeStack->ID); giveCommand(Battle::WALK, myNumber, activeStack->ID);
else if (vstd::contains(acc, shiftedDest)) else if(vstd::contains(acc, shiftedDest))
giveCommand (Battle::WALK, shiftedDest, activeStack->ID); giveCommand(Battle::WALK, shiftedDest, activeStack->ID);
} }
else else
{ {
@ -2547,17 +2547,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
{ {
bool doubleWide = activeStack->doubleWide(); bool doubleWide = activeStack->doubleWide();
destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) + destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ) +
(activeStack->attackerOwned && doubleWide ? 1 : 0); (activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
if (vstd::contains(occupyableHexes, destHex)) if(vstd::contains(occupyableHexes, destHex))
return 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; return destHex+1;
} }
else //if we are defender else //if we are defender
{ {
if (vstd::contains(occupyableHexes, destHex-1)) if(vstd::contains(occupyableHexes, destHex-1))
return destHex-1; return destHex-1;
} }
break; break;
@ -2567,21 +2567,21 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH ); destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH-1 : GameConstants::BFIELD_WIDTH );
if (vstd::contains(occupyableHexes, destHex)) if (vstd::contains(occupyableHexes, destHex))
return 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; 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; return destHex-1;
} }
break; break;
} }
case 8: //from left case 8: //from left
{ {
if (activeStack->doubleWide() && !activeStack->attackerOwned) if(activeStack->doubleWide() && activeStack->side == activeStack->side == BattleSide::DEFENDER)
{ {
std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false); std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if (vstd::contains(acc, myNumber)) if (vstd::contains(acc, myNumber))
@ -2597,17 +2597,17 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
} }
case 9: //from top left case 9: //from top left
{ {
destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH+1 : GameConstants::BFIELD_WIDTH ); destHex = myNumber - ((myNumber/GameConstants::BFIELD_WIDTH) % 2 ? GameConstants::BFIELD_WIDTH + 1 : GameConstants::BFIELD_WIDTH);
if (vstd::contains(occupyableHexes, destHex)) if(vstd::contains(occupyableHexes, destHex))
return 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; return destHex+1;
} }
else //if we are defender else //if we are defender
{ {
if (vstd::contains(occupyableHexes, destHex-1)) if(vstd::contains(occupyableHexes, destHex-1))
return destHex-1; return destHex-1;
} }
break; break;
@ -2616,27 +2616,27 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
{ {
bool doubleWide = activeStack->doubleWide(); bool doubleWide = activeStack->doubleWide();
destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) + destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ) +
(activeStack->attackerOwned && doubleWide ? 1 : 0); (activeStack->side == BattleSide::ATTACKER && doubleWide ? 1 : 0);
if (vstd::contains(occupyableHexes, destHex)) if(vstd::contains(occupyableHexes, destHex))
return 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; return destHex+1;
} }
else //if we are defender else //if we are defender
{ {
if (vstd::contains(occupyableHexes, destHex-1)) if(vstd::contains(occupyableHexes, destHex-1))
return destHex-1; return destHex-1;
} }
break; break;
} }
case 11: //from right case 11: //from right
{ {
if (activeStack->doubleWide() && activeStack->attackerOwned) if(activeStack->doubleWide() && activeStack->side == BattleSide::ATTACKER)
{ {
std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false); std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if (vstd::contains(acc, myNumber)) if(vstd::contains(acc, myNumber))
return myNumber + 1; return myNumber + 1;
else else
return myNumber + 2; return myNumber + 2;
@ -2650,16 +2650,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
case 13: //from bottom case 13: //from bottom
{ {
destHex = myNumber + ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH+1 ); 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; 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; return destHex+1;
} }
else //if we are defender else //if we are defender
{ {
if (vstd::contains(occupyableHexes, destHex-1)) if(vstd::contains(occupyableHexes, destHex-1))
return destHex-1; return destHex-1;
} }
break; break;
@ -2669,14 +2669,14 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
destHex = myNumber - ( (myNumber/GameConstants::BFIELD_WIDTH)%2 ? GameConstants::BFIELD_WIDTH : GameConstants::BFIELD_WIDTH-1 ); 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; 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; return destHex+1;
} }
else //if we are defender else //if we are defender
{ {
if (vstd::contains(occupyableHexes, destHex-1)) if(vstd::contains(occupyableHexes, destHex-1))
return destHex-1; return destHex-1;
} }
break; break;
@ -3082,52 +3082,52 @@ void CBattleInterface::showAbsoluteObstacles(SDL_Surface *to)
void CBattleInterface::showHighlightedHexes(SDL_Surface *to) void CBattleInterface::showHighlightedHexes(SDL_Surface *to)
{ {
for (int b=0; b<GameConstants::BFIELD_SIZE; ++b) for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
{ {
if (bfield[b]->strictHovered && bfield[b]->hovered) if(bfield[b]->strictHovered && bfield[b]->hovered)
{ {
if (previouslyHoveredHex == -1) if(previouslyHoveredHex == -1)
previouslyHoveredHex = b; //something to start with previouslyHoveredHex = b; //something to start with
if (currentlyHoveredHex == -1) if(currentlyHoveredHex == -1)
currentlyHoveredHex = b; //something to start with currentlyHoveredHex = b; //something to start with
if (currentlyHoveredHex != b) //repair hover info if(currentlyHoveredHex != b) //repair hover info
{ {
previouslyHoveredHex = currentlyHoveredHex; previouslyHoveredHex = currentlyHoveredHex;
currentlyHoveredHex = b; currentlyHoveredHex = b;
} }
if (settings["battle"]["mouseShadow"].Bool()) if(settings["battle"]["mouseShadow"].Bool())
{ {
const ISpellCaster *caster = nullptr; const ISpellCaster *caster = nullptr;
const CSpell *spell = nullptr; const CSpell *spell = nullptr;
if (spellToCast)//hero casts spell if(spellToCast)//hero casts spell
{ {
spell = SpellID(spellToCast->additionalInfo).toSpell(); 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(); spell = SpellID(creatureSpellToCast).toSpell();
caster = activeStack; caster = activeStack;
} }
if (caster && spell) //when casting spell if(caster && spell) //when casting spell
{ {
//calculating spell school level //calculating spell school level
ui8 schoolLevel = caster->getSpellSchoolLevel(spell); ui8 schoolLevel = caster->getSpellSchoolLevel(spell);
// printing shaded hex(es) // printing shaded hex(es)
auto shaded = spell->rangeInHexes(currentlyHoveredHex, schoolLevel, curInt->cb->battleGetMySide()); 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); 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) && currentlyHoveredHex.getX() != GameConstants::BFIELD_WIDTH - 1)
showHighlightedHex(to, currentlyHoveredHex); 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<BattleHex> set = curInt->cb->battleGetAttackedHexes(activeStack, currentlyHoveredHex, attackingHex); std::set<BattleHex> set = curInt->cb->battleGetAttackedHexes(activeStack, currentlyHoveredHex, attackingHex);
for (BattleHex hex : set) for(BattleHex hex : set)
showHighlightedHex(to, hex); showHighlightedHex(to, hex);
// display the movement shadow of the stack at b (i.e. stack under mouse) // display the movement shadow of the stack at b (i.e. stack under mouse)
const CStack *const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false); const CStack * const shere = curInt->cb->battleGetStackByPos(currentlyHoveredHex, false);
if (shere && shere != activeStack && shere->alive()) if(shere && shere != activeStack && shere->alive())
{ {
std::vector<BattleHex> v = curInt->cb->battleGetAvailableHexes(shere, true ); std::vector<BattleHex> v = curInt->cb->battleGetAvailableHexes(shere, true );
for (BattleHex hex : v) for(BattleHex hex : v)
showHighlightedHex(to, hex); showHighlightedHex(to, hex);
} }
} }
@ -3321,13 +3321,15 @@ void CBattleInterface::showAliveStacks(SDL_Surface *to, std::vector<const CStack
//printing amount //printing amount
if (isAmountBoxVisible(stack)) if (isAmountBoxVisible(stack))
{ {
const BattleHex nextPos = stack->position + (stack->attackerOwned ? 1 : -1); const int sideShift = stack->side == BattleSide::ATTACKER ? 1 : -1;
const bool edge = stack->position % GameConstants::BFIELD_WIDTH == (stack->attackerOwned ? GameConstants::BFIELD_WIDTH - 2 : 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]; const bool moveInside = !edge && !stackCountOutsideHexes[nextPos];
int xAdd = (stack->attackerOwned ? 220 : 202) + int xAdd = (stack->side == BattleSide::ATTACKER ? 220 : 202) +
(stack->doubleWide() ? 44 : 0) *(stack->attackerOwned ? +1 : -1) + (stack->doubleWide() ? 44 : 0) * sideShift +
(moveInside ? amountNormal->w + 10 : 0) *(stack->attackerOwned ? -1 : +1); (moveInside ? amountNormal->w + 10 : 0) * reverseSideShift;
int yAdd = 260 + ((stack->attackerOwned || moveInside) ? 0 : -15); int yAdd = 260 + ((stack->side == BattleSide::ATTACKER || moveInside) ? 0 : -15);
//blitting amount background box //blitting amount background box
SDL_Surface *amountBG = amountNormal; SDL_Surface *amountBG = amountNormal;

View File

@ -384,7 +384,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
{ {
auto stacks = owner.cb->battleGetAllStacks(); auto stacks = owner.cb->battleGetAllStacks();
vstd::erase_if(stacks, [i](const CStack *stack) //erase stack of other side and not coming from garrison 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; }); auto best = vstd::maxElementByFun(stacks, [](const CStack *stack){ return stack->type->AIValue; });
if(best != stacks.end()) //should be always but to be safe... 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 //shifting position for double - hex creatures
if(stack->doubleWide()) if(stack->doubleWide())
{ {
if(stack->attackerOwned) if(stack->side == BattleSide::ATTACKER)
{ {
if(cbi->creDir[stack->ID]) if(cbi->creDir[stack->ID])
ret.x -= 44; ret.x -= 44;

View File

@ -15,8 +15,8 @@
#include "NetPacks.h" #include "NetPacks.h"
CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S) CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, ui8 Side, SlotID S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), : base(Base), ID(I), owner(O), slot(S), side(Side),
counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1), counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1),
firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) firstHPleft(-1), position(), shots(0), casts(0), resurrected(0)
{ {
@ -30,8 +30,8 @@ CStack::CStack()
init(); init();
setNodeType(STACK_BATTLE); setNodeType(STACK_BATTLE);
} }
CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S) CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S)
: base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), : base(nullptr), ID(I), owner(O), slot(S), side(Side),
counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1), counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1),
firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) firstHPleft(-1), position(), shots(0), casts(0), resurrected(0)
{ {
@ -49,7 +49,7 @@ void CStack::init()
firstHPleft = -1; firstHPleft = -1;
owner = PlayerColor::NEUTRAL; owner = PlayerColor::NEUTRAL;
slot = SlotID(255); slot = SlotID(255);
attackerOwned = false; side = 1;
position = BattleHex(); position = BattleHex();
counterAttacksPerformed = 0; counterAttacksPerformed = 0;
counterAttacksTotalCache = 0; counterAttacksTotalCache = 0;
@ -145,9 +145,9 @@ BattleHex CStack::occupiedHex() const
BattleHex CStack::occupiedHex(BattleHex assumedPos) const BattleHex CStack::occupiedHex(BattleHex assumedPos) const
{ {
if (doubleWide()) if(doubleWide())
{ {
if (attackerOwned) if(side == BattleSide::ATTACKER)
return assumedPos - 1; return assumedPos - 1;
else else
return assumedPos + 1; return assumedPos + 1;
@ -165,17 +165,17 @@ std::vector<BattleHex> CStack::getHexes() const
std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos) const std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos) const
{ {
return getHexes(assumedPos, doubleWide(), attackerOwned); return getHexes(assumedPos, doubleWide(), side);
} }
std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned) std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos, bool twoHex, ui8 side)
{ {
std::vector<BattleHex> hexes; std::vector<BattleHex> hexes;
hexes.push_back(assumedPos); hexes.push_back(assumedPos);
if (twoHex) if(twoHex)
{ {
if (AttackerOwned) if(side == BattleSide::ATTACKER)
hexes.push_back(assumedPos - 1); hexes.push_back(assumedPos - 1);
else else
hexes.push_back(assumedPos + 1); hexes.push_back(assumedPos + 1);
@ -193,10 +193,10 @@ std::vector<BattleHex> CStack::getSurroundingHexes(BattleHex attackerPos) const
{ {
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position
std::vector<BattleHex> hexes; std::vector<BattleHex> hexes;
if (doubleWide()) if(doubleWide())
{ {
const int WN = GameConstants::BFIELD_WIDTH; const int WN = GameConstants::BFIELD_WIDTH;
if(attackerOwned) if(side == BattleSide::ATTACKER)
{ //position is equal to front hex { //position is equal to front hex
BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes); BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes);
BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
@ -226,6 +226,21 @@ std::vector<BattleHex> 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<si32> CStack::activeSpells() const std::vector<si32> CStack::activeSpells() const
{ {
std::vector<si32> ret; std::vector<si32> ret;
@ -382,11 +397,11 @@ bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defen
return return
(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front (BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front
|| (attacker->doubleWide() //back <=> 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 || (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 || (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);
} }

View File

@ -24,7 +24,7 @@ public:
ui32 firstHPleft; //HP of first creature in stack ui32 firstHPleft; //HP of first creature in stack
PlayerColor owner; //owner - player colour (255 for neutrals) PlayerColor owner; //owner - player colour (255 for neutrals)
SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures) 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 BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
///how many times this stack has been counterattacked this round ///how many times this stack has been counterattacked this round
ui8 counterAttacksPerformed; ui8 counterAttacksPerformed;
@ -39,8 +39,8 @@ public:
//overrides //overrides
const CCreature* getCreature() const {return type;} const CCreature* getCreature() const {return type;}
CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor CStack(const CStackInstance *base, PlayerColor O, int I, ui8 Side, SlotID S); //c-tor
CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, ui8 Side, SlotID S = SlotID(255)); //c-tor
CStack(); //c-tor CStack(); //c-tor
~CStack(); ~CStack();
std::string nodeName() const override; 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 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<BattleHex> getHexes() const; //up to two occupied hexes, starting from front std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front
std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front static std::vector<BattleHex> 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 bool coversPos(BattleHex position) const; //checks also if unit is double-wide
std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
BattleHex::EDir destShiftDir() const;
std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP> std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled
@ -110,7 +112,7 @@ public:
assert(isIndependentNode()); assert(isIndependentNode());
h & static_cast<CBonusSystemNode&>(*this); h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CStackBasicDescriptor&>(*this); h & static_cast<CStackBasicDescriptor&>(*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; & shots & casts & count & resurrected;
const CArmedInstance *army = (base ? base->armyObj : nullptr); const CArmedInstance *army = (base ? base->armyObj : nullptr);

View File

@ -11,6 +11,7 @@
#include "battle/BattleHex.h" #include "battle/BattleHex.h"
#include "GameConstants.h"
#include "int3.h" #include "int3.h"
class CGTownInstance; class CGTownInstance;

View File

@ -1628,13 +1628,13 @@ struct BattleStacksRemoved : public CPackForClient
struct BattleStackAdded : public CPackForClient struct BattleStackAdded : public CPackForClient
{ {
BattleStackAdded() 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); DLL_LINKAGE void applyGs(CGameState *gs);
void applyCl(CClient *cl); void applyCl(CClient *cl);
int attacker; // if true, stack belongs to attacker ui8 side;
CreatureID creID; CreatureID creID;
int amount; int amount;
int pos; int pos;
@ -1645,7 +1645,7 @@ struct BattleStackAdded : public CPackForClient
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & attacker & creID & amount & pos & summoned; h & side & creID & amount & pos & summoned;
} }
}; };

View File

@ -1384,7 +1384,7 @@ void BattleStackMoved::applyGs(CGameState *gs)
{ {
SpellCreatedObstacle *sands = dynamic_cast<SpellCreatedObstacle*>(oi.get()); SpellCreatedObstacle *sands = dynamic_cast<SpellCreatedObstacle*>(oi.get());
assert(sands); assert(sands);
if(sands->casterSide != !s->attackerOwned) if(sands->casterSide != s->side)
sands->visibleForAnotherSide = true; sands->visibleForAnotherSide = true;
} }
} }
@ -1803,7 +1803,7 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
} }
CStackBasicDescriptor csbd(creID, amount); 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) if (summoned)
addedStack->state.insert(EBattleStackState::SUMMONED); addedStack->state.insert(EBattleStackState::SUMMONED);

View File

@ -10,16 +10,17 @@
#include "../StdInc.h" #include "../StdInc.h"
#include "AccessibilityInfo.h" #include "AccessibilityInfo.h"
#include "../CStack.h" #include "../CStack.h"
#include "../GameConstants.h"
bool AccessibilityInfo::accessible(BattleHex tile, const CStack * stack) const 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. // 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 the hex is out of range then the tile isn't accessible
if(!hex.isValid()) 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 // If we're no defender which step on gate and the hex isn't accessible, then the tile
// isn't accessible // isn't accessible
else if(at(hex) != EAccessibility::ACCESSIBLE && else if(at(hex) != EAccessibility::ACCESSIBLE &&
!(at(hex) == EAccessibility::GATE && !attackerOwned)) !(at(hex) == EAccessibility::GATE && side == BattleSide::DEFENDER))
{ {
return false; return false;
} }
} }
return true; 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;
}

View File

@ -9,6 +9,7 @@
*/ */
#pragma once #pragma once
#include "BattleHex.h" #include "BattleHex.h"
#include "../GameConstants.h"
class CStack; class CStack;
@ -29,7 +30,6 @@ typedef std::array<EAccessibility, GameConstants::BFIELD_SIZE> TAccessibilityArr
struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray 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, 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
}; };

View File

@ -27,7 +27,7 @@ BattleAction::BattleAction():
BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed) BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed)
{ {
BattleAction ba; BattleAction ba;
ba.side = !healer->attackerOwned; ba.side = healer->side;
ba.actionType = STACK_HEAL; ba.actionType = STACK_HEAL;
ba.stackNumber = healer->ID; ba.stackNumber = healer->ID;
ba.destinationTile = healed->position; ba.destinationTile = healed->position;
@ -37,7 +37,7 @@ BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed
BattleAction BattleAction::makeDefend(const CStack * stack) BattleAction BattleAction::makeDefend(const CStack * stack)
{ {
BattleAction ba; BattleAction ba;
ba.side = !stack->attackerOwned; ba.side = stack->side;
ba.actionType = DEFEND; ba.actionType = DEFEND;
ba.stackNumber = stack->ID; ba.stackNumber = stack->ID;
return ba; 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 BattleAction::makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom /*= BattleHex::INVALID*/)
{ {
BattleAction ba; BattleAction ba;
ba.side = !stack->attackerOwned; ba.side = stack->side;
ba.actionType = WALK_AND_ATTACK; ba.actionType = WALK_AND_ATTACK;
ba.stackNumber = stack->ID; ba.stackNumber = stack->ID;
ba.destinationTile = attackFrom; ba.destinationTile = attackFrom;
@ -58,7 +58,7 @@ BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack *
BattleAction BattleAction::makeWait(const CStack * stack) BattleAction BattleAction::makeWait(const CStack * stack)
{ {
BattleAction ba; BattleAction ba;
ba.side = !stack->attackerOwned; ba.side = stack->side;
ba.actionType = WAIT; ba.actionType = WAIT;
ba.stackNumber = stack->ID; ba.stackNumber = stack->ID;
return ba; return ba;
@ -67,7 +67,7 @@ BattleAction BattleAction::makeWait(const CStack * stack)
BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target) BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target)
{ {
BattleAction ba; BattleAction ba;
ba.side = !shooter->attackerOwned; ba.side = shooter->side;
ba.actionType = SHOOT; ba.actionType = SHOOT;
ba.stackNumber = shooter->ID; ba.stackNumber = shooter->ID;
ba.destinationTile = target->position; 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 BattleAction::makeMove(const CStack * stack, BattleHex dest)
{ {
BattleAction ba; BattleAction ba;
ba.side = !stack->attackerOwned; ba.side = stack->side;
ba.actionType = WALK; ba.actionType = WALK;
ba.stackNumber = stack->ID; ba.stackNumber = stack->ID;
ba.destinationTile = dest; ba.destinationTile = dest;

View File

@ -9,6 +9,7 @@
*/ */
#pragma once #pragma once
#include "BattleHex.h" #include "BattleHex.h"
#include "../GameConstants.h"
class CStack; class CStack;

View File

@ -9,6 +9,7 @@
*/ */
#include "../StdInc.h" #include "../StdInc.h"
#include "BattleHex.h" #include "BattleHex.h"
#include "../GameConstants.h"
BattleHex::BattleHex() : hex(INVALID) {} BattleHex::BattleHex() : hex(INVALID) {}
@ -99,6 +100,8 @@ BattleHex& BattleHex::moveInDirection(EDir dir, bool hasToBeValid)
case LEFT: case LEFT:
setXY(x-1, y, hasToBeValid); setXY(x-1, y, hasToBeValid);
break; break;
case NONE:
break;
default: default:
throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n"); throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n");
break; break;
@ -160,7 +163,7 @@ void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)
ret.push_back(tile); ret.push_back(tile);
} }
BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities) BattleHex BattleHex::getClosestTile(ui8 side, BattleHex initialPos, std::set<BattleHex> & possibilities)
{ {
std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :( std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
BattleHex initialHex = BattleHex(initialPos); BattleHex initialHex = BattleHex(initialPos);
@ -175,11 +178,11 @@ BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, st
return closestDistance < here.getDistance (initialPos, here); return closestDistance < here.getDistance (initialPos, here);
}; };
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting 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(left.getX() != right.getX())
{ {
if (attackerOwned) if(side == BattleSide::ATTACKER)
return left.getX() > right.getX(); //find furthest right return left.getX() > right.getX(); //find furthest right
else else
return left.getX() < right.getX(); //find furthest left return left.getX() < right.getX(); //find furthest left

View File

@ -8,14 +8,35 @@
* *
*/ */
#pragma once #pragma once
#include "../GameConstants.h"
//TODO: change to enum class
namespace BattleSide
{
enum
{
ATTACKER = 0,
DEFENDER = 1
};
}
typedef boost::optional<ui8> BattleSideOpt;
// for battle stacks' positions // for battle stacks' positions
struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design
{ {
si16 hex; si16 hex;
static const si16 INVALID = -1; 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();
BattleHex(si16 _hex); 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 signed char mutualPosition(BattleHex hex1, BattleHex hex2);
static char getDistance(BattleHex hex1, BattleHex hex2); static char getDistance(BattleHex hex1, BattleHex hex2);
static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret); static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
static BattleHex getClosestTile(bool attackerOwned, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad static BattleHex getClosestTile(ui8 side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
template <typename Handler> template <typename Handler>
void serialize(Handler &h, const int version) void serialize(Handler &h, const int version)

View File

@ -26,7 +26,7 @@ const CStack * BattleInfo::getNextStack() const
return nullptr; 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 twoHex = VLC->creh->creatures[creID]->isDoubleWide();
//bool flying = VLC->creh->creatures[creID]->isFlying(); //bool flying = VLC->creh->creatures[creID]->isFlying();
@ -36,7 +36,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia
pos = initialPos; pos = initialPos;
else //summon elementals depending on player side else //summon elementals depending on player side
{ {
if (attackerOwned) if(side == BattleSide::ATTACKER)
pos = 0; //top left pos = 0; //top left
else else
pos = GameConstants::BFIELD_WIDTH - 1; //top right pos = GameConstants::BFIELD_WIDTH - 1; //top right
@ -46,7 +46,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia
std::set<BattleHex> occupyable; std::set<BattleHex> occupyable;
for(int i = 0; i < accessibility.size(); i++) for(int i = 0; i < accessibility.size(); i++)
if(accessibility.accessible(i, twoHex, attackerOwned)) if(accessibility.accessible(i, twoHex, side))
occupyable.insert(i); occupyable.insert(i);
if (occupyable.empty()) 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::INVALID; //all tiles are covered
} }
return BattleHex::getClosestTile(attackerOwned, pos, occupyable); return BattleHex::getClosestTile(side, pos, occupyable);
} }
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack) std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack)
@ -104,28 +104,28 @@ void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount); si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount);
vstd::amax(killed, 0); vstd::amax(killed, 0);
if(killed) 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(); int stackID = getIdForNewStack();
PlayerColor owner = sides[attackerOwned ? 0 : 1].color; PlayerColor owner = sides[side].color;
assert((owner >= PlayerColor::PLAYER_LIMIT) || assert((owner >= PlayerColor::PLAYER_LIMIT) ||
(base.armyObj && base.armyObj->tempOwner == owner)); (base.armyObj && base.armyObj->tempOwner == owner));
auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); auto ret = new CStack(&base, owner, stackID, side, slot);
ret->position = getAvaliableHex (base.getCreatureID(), attackerOwned, position); //TODO: what if no free tile on battlefield was found? 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 ret->state.insert(EBattleStackState::ALIVE); //alive state indication
return ret; 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(); int stackID = getIdForNewStack();
PlayerColor owner = sides[attackerOwned ? 0 : 1].color; PlayerColor owner = sides[side].color;
auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); auto ret = new CStack(&base, owner, stackID, side, slot);
ret->position = position; ret->position = position;
ret->state.insert(EBattleStackState::ALIVE); //alive state indication ret->state.insert(EBattleStackState::ALIVE); //alive state indication
return ret; return ret;
@ -155,7 +155,7 @@ void BattleInfo::localInitStack(CStack * s)
} }
else //attach directly to obj to which stack belongs and creature type 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); s->attachTo(army);
assert(s->type); assert(s->type);
s->attachTo(const_cast<CCreature*>(s->type)); s->attachTo(const_cast<CCreature*>(s->type));
@ -486,7 +486,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
if(creatureBank && i->second->type->isDoubleWide()) if(creatureBank && i->second->type->isDoubleWide())
pos += side ? BattleHex::LEFT : BattleHex::RIGHT; 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); 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) 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]); creatureBank ? commanderBank[i] : commanderField[i]);
stacks.push_back(stack); stacks.push_back(stack);
} }
@ -506,15 +506,15 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL) if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
{ {
// keep tower // 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); stacks.push_back(stack);
if (curB->town->fortLevel() >= CGTownInstance::CASTLE) if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
{ {
// lower tower + upper tower // 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); 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); stacks.push_back(stack);
} }

View File

@ -55,28 +55,17 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
CGHeroInstance * battleGetFightingHero(ui8 side) const; CGHeroInstance * battleGetFightingHero(ui8 side) const;
const CStack * getNextStack() const; //which stack will have turn after current one const CStack * getNextStack() const; //which stack will have turn after current one
//void getStackQueue(std::vector<const CStack *> &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<BattleHex> & occupyable, bool flying, const CStack* stackToOmmit = nullptr) const; //send pointer to at least 187 allocated bytes int getAvaliableHex(CreatureID creID, ui8 side, int initialPos = -1) const; //find place for summon / clone effects
//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
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const CStack * stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const CStack * stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
//std::vector<BattleHex> getAccessibility(const CStack * stack, bool addOccupiable, std::vector<BattleHex> * 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<CObstacleInstance> getObstacleOnTile(BattleHex tile) const; std::shared_ptr<CObstacleInstance> getObstacleOnTile(BattleHex tile) const;
std::set<BattleHex> getStoppers(bool whichSidePerspective) const; std::set<BattleHex> 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) 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<ui32,si32> * casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) void calculateCasualties(std::map<ui32,si32> * 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 CStack * generateNewStack(const CStackInstance &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
//std::set<CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks CStack * generateNewStack(const CStackBasicDescriptor &base, ui8 side, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
//std::set<BattleHex> 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
int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack 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 const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player

View File

@ -103,10 +103,10 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con
return ESpellCastProblem::INVALID; return ESpellCastProblem::INVALID;
} }
const PlayerColor player = caster->getOwner(); const PlayerColor player = caster->getOwner();
const si8 side = playerToSide(player); const auto side = playerToSide(player);
if(side < 0) if(!side)
return ESpellCastProblem::INVALID; return ESpellCastProblem::INVALID;
if(!battleDoWeKnowAbout(side)) if(!battleDoWeKnowAbout(side.get()))
{ {
logGlobal->warnStream() << "You can't check if enemy can cast given spell!"; logGlobal->warnStream() << "You can't check if enemy can cast given spell!";
return ESpellCastProblem::INVALID; return ESpellCastProblem::INVALID;
@ -119,7 +119,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con
{ {
case ECastingMode::HERO_CASTING: case ECastingMode::HERO_CASTING:
{ {
if(battleCastSpells(side) > 0) if(battleCastSpells(side.get()) > 0)
return ESpellCastProblem::ALREADY_CASTED_THIS_TURN; return ESpellCastProblem::ALREADY_CASTED_THIS_TURN;
auto hero = dynamic_cast<const CGHeroInstance *>(caster); auto hero = dynamic_cast<const CGHeroInstance *>(caster);
@ -255,7 +255,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
int bestSpeed = fastest->Speed(turn); int bestSpeed = fastest->Speed(turn);
//FIXME: comparison between bool and integer. Logic does not makes sense either //FIXME: comparison between bool and integer. Logic does not makes sense either
if(fastest->attackerOwned != lastMoved) if(fastest->side != lastMoved)
{ {
ret = fastest; ret = fastest;
} }
@ -264,7 +264,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
for(j = i + 1; j < st.size(); j++) for(j = i + 1; j < st.size(); j++)
{ {
if(!st[j]) continue; 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; break;
} }
@ -288,7 +288,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
else else
st[j] = nullptr; st[j] = nullptr;
lastMoved = ret->attackerOwned; lastMoved = ret->side;
return ret; return ret;
}; };
@ -362,9 +362,9 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
{ {
//FIXME: both branches contain same code!!! //FIXME: both branches contain same code!!!
if(out.size() && out.front() == active) if(out.size() && out.front() == active)
lastMoved = active->attackerOwned; lastMoved = active->side;
else else
lastMoved = active->attackerOwned; lastMoved = active->side;
} }
else else
{ {
@ -418,7 +418,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack
if(!reachability.isReachable(i)) if(!reachability.isReachable(i))
continue; 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 //Stack has to perform tactic-phase movement -> can enter any reachable tile within given range
if(!isInTacticRange(i)) if(!isInTacticRange(i))
@ -453,7 +453,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const CStack
}); });
return availableNeighbor != ret.end(); return availableNeighbor != ret.end();
}; };
for(const CStack * otherSt : battleAliveStacks(stack->attackerOwned)) for(const CStack * otherSt : battleAliveStacks(1-stack->side))
{ {
if(!otherSt->isValidTarget(false)) if(!otherSt->isValidTarget(false))
continue; continue;
@ -800,7 +800,6 @@ std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(CRandomGenerator
RETURN_IF_NOT_BATTLE(std::make_pair(0, 0)); RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
//const bool shooting = battleCanShoot(bai.attacker, bai.defenderPosition); //TODO handle bonus bearer //const bool shooting = battleCanShoot(bai.attacker, bai.defenderPosition); //TODO handle bonus bearer
//const ui8 mySide = !attacker->attackerOwned;
TDmgRange ret = calculateDmgRange(bai); TDmgRange ret = calculateDmgRange(bai);
@ -965,7 +964,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
const int costToNeighbour = ret.distances[curHex] + 1; const int costToNeighbour = ret.distances[curHex] + 1;
for(BattleHex neighbour : curHex.neighbouringTiles()) 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]; const int costFoundSoFar = ret.distances[neighbour];
if(accessible && costToNeighbour < costFoundSoFar) if(accessible && costToNeighbour < costFoundSoFar)
@ -1001,7 +1000,7 @@ std::set<BattleHex> CBattleInfoCallback::getStoppers(BattlePerspective::BattlePe
return ret; return ret;
} }
std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const CStack * closest, BattleSideOpt side) const
{ {
auto reachability = getReachability(closest); auto reachability = getReachability(closest);
auto avHexes = battleGetAvailableHexes(closest, false); auto avHexes = battleGetAvailableHexes(closest, false);
@ -1018,7 +1017,7 @@ std::pair<const CStack *, BattleHex> CBattleInfoCallback::getNearestStack(const
std::vector<const CStack *> possibleStacks = battleGetStacksIf([=](const CStack * s) std::vector<const CStack *> 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) for(const CStack * st : possibleStacks)
@ -1064,7 +1063,7 @@ ReachabilityInfo CBattleInfoCallback::getReachability(const CStack *stack) const
{ {
ReachabilityInfo::Parameters params(stack); 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. //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. // 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++) 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.predecessors[i] = params.startPosition;
ret.distances[i] = BattleHex::getDistance(params.startPosition, i); ret.distances[i] = BattleHex::getDistance(params.startPosition, i);
@ -1103,7 +1102,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
{ {
//does not return hex attacked directly //does not return hex attacked directly
//TODO: apply rotation to two-hex attackers //TODO: apply rotation to two-hex attackers
bool isAttacker = attacker->attackerOwned; bool isAttacker = attacker->side == BattleSide::ATTACKER;
AttackableTiles at; AttackableTiles at;
RETURN_IF_NOT_BATTLE(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_FIRE:
case SpellID::PROTECTION_FROM_WATER: case SpellID::PROTECTION_FROM_WATER:
{ {
const ui8 enemySide = (ui8)subject->attackerOwned; const ui8 enemySide = 1 - subject->side;
//todo: only if enemy has spellbook //todo: only if enemy has spellbook
if (!battleHasHero(enemySide)) //only if there is enemy hero if (!battleHasHero(enemySide)) //only if there is enemy hero
continue; continue;
@ -1567,17 +1566,17 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const
if(!battleCanSurrender(Player)) if(!battleCanSurrender(Player))
return -1; return -1;
const si8 playerSide = playerToSide(Player); const auto side = playerToSide(Player);
if(playerSide < 0) if(!side)
return -1; return -1;
int ret = 0; int ret = 0;
double discount = 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 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; 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); discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT);
ret *= (100.0 - discount) / 100.0; ret *= (100.0 - discount) / 100.0;
@ -1616,7 +1615,7 @@ boost::optional<int> CBattleInfoCallback::battleIsFinished() const
{ {
if(stack->alive() && !stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) if(stack->alive() && !stack->hasBonusOfType(Bonus::SIEGE_WEAPON))
{ {
hasStack[1-stack->attackerOwned] = true; hasStack[stack->side] = true;
} }
} }

View File

@ -101,7 +101,7 @@ public:
AccessibilityInfo getAccesibility() const; AccessibilityInfo getAccesibility() const;
AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible. AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible.
AccessibilityInfo getAccesibility(const std::vector<BattleHex> & accessibleHexes) const; //given hexes will be marked as accessible AccessibilityInfo getAccesibility(const std::vector<BattleHex> & accessibleHexes) const; //given hexes will be marked as accessible
std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, BattleSideOpt side) const;
protected: protected:
ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const; ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const;
ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const; ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const;

View File

@ -67,7 +67,7 @@ bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const
for(const CStack * s : battleGetAllStacks()) 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; return true;
} }
@ -102,7 +102,7 @@ TStacks CBattleInfoEssentials::battleAliveStacks() const
TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const
{ {
return battleGetStacksIf([=](const CStack * s){ 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 bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
{ {
RETURN_IF_NOT_BATTLE(false); RETURN_IF_NOT_BATTLE(false);
const si8 mySide = playerToSide(player); const auto side = playerToSide(player);
const CGHeroInstance *myHero = battleGetFightingHero(mySide); if(!side)
return false;
const CGHeroInstance *myHero = battleGetFightingHero(side.get());
//current player have no hero //current player have no hero
if(!myHero) if(!myHero)
@ -249,7 +252,7 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
return false; return false;
//we are besieged defender //we are besieged defender
if(mySide == BattleSide::DEFENDER && battleGetSiegeLevel()) if(side.get() == BattleSide::DEFENDER && battleGetSiegeLevel())
{ {
auto town = battleGetDefendedTown(); auto town = battleGetDefendedTown();
if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD))
@ -259,23 +262,31 @@ bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const
return true; 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; }); int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; });
if(ret < 0) if(ret < 0)
logGlobal->warnStream() << "Cannot find side for player " << player; 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 bool CBattleInfoEssentials::playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const
{ {
RETURN_IF_NOT_BATTLE(false); RETURN_IF_NOT_BATTLE(false);
const si8 playerSide = playerToSide(player); const auto side = playerToSide(player);
if (playerSide >= 0) if(side)
{ {
if (getBattle()->sides[!playerSide].hero == h) if (getBattle()->sides[otherSide(side.get())].hero == h)
return true; return true;
} }
return false; return false;
@ -290,10 +301,12 @@ ui8 CBattleInfoEssentials::battleGetSiegeLevel() const
bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const
{ {
RETURN_IF_NOT_BATTLE(false); RETURN_IF_NOT_BATTLE(false);
ui8 mySide = playerToSide(player); const auto side = playerToSide(player);
bool iAmSiegeDefender = (mySide == BattleSide::DEFENDER && battleGetSiegeLevel()); if(!side)
return false;
bool iAmSiegeDefender = (side.get() == BattleSide::DEFENDER && battleGetSiegeLevel());
//conditions like for fleeing (except escape tunnel presence) + enemy must have a hero //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 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 const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const CStack * stack) const
{ {
RETURN_IF_NOT_BATTLE(nullptr); 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 bool CBattleInfoEssentials::battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness /* = false*/) const

View File

@ -9,6 +9,7 @@
*/ */
#pragma once #pragma once
#include "CCallbackBase.h" #include "CCallbackBase.h"
#include "BattleHex.h"
class CGTownInstance; class CGTownInstance;
class CGHeroInstance; class CGHeroInstance;
@ -32,12 +33,6 @@ namespace BattlePerspective
}; };
} }
namespace BattleSide
{
enum {ATTACKER = 0, DEFENDER = 1};
}
class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase
{ {
protected: protected:
@ -71,7 +66,8 @@ public:
si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?) si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?)
bool battleCanFlee(PlayerColor player) const; bool battleCanFlee(PlayerColor player) const;
bool battleCanSurrender(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; 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 ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
bool battleHasHero(ui8 side) const; bool battleHasHero(ui8 side) const;

View File

@ -17,16 +17,17 @@ ReachabilityInfo::Parameters::Parameters()
{ {
stack = nullptr; stack = nullptr;
perspective = BattlePerspective::ALL_KNOWING; perspective = BattlePerspective::ALL_KNOWING;
attackerOwned = doubleWide = flying = false; side = 0;
doubleWide = flying = false;
} }
ReachabilityInfo::Parameters::Parameters(const CStack * Stack) ReachabilityInfo::Parameters::Parameters(const CStack * Stack)
{ {
stack = Stack; stack = Stack;
perspective = (BattlePerspective::BattlePerspective)(!Stack->attackerOwned); perspective = (BattlePerspective::BattlePerspective)(Stack->side);
startPosition = Stack->position; startPosition = Stack->position;
doubleWide = stack->doubleWide(); doubleWide = stack->doubleWide();
attackerOwned = stack->attackerOwned; side = stack->side;
flying = stack->hasBonusOfType(Bonus::FLYING); flying = stack->hasBonusOfType(Bonus::FLYING);
knownAccessible = stack->getHexes(); knownAccessible = stack->getHexes();
} }

View File

@ -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 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 doubleWide;
bool flying; bool flying;
std::vector<BattleHex> 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) std::vector<BattleHex> 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)

View File

@ -104,7 +104,7 @@ std::vector<const CStack *> ChainLightningMechanics::calculateAffectedStacks(con
} }
if(possibleHexes.empty()) //not enough targets if(possibleHexes.empty()) //not enough targets
break; break;
lightningHex = BattleHex::getClosestTile(stack->attackerOwned, lightningHex, possibleHexes); lightningHex = BattleHex::getClosestTile(stack->side, lightningHex, possibleHexes);
} }
return res; return res;
@ -121,13 +121,12 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const
env->complain ("No target stack to clone!"); env->complain ("No target stack to clone!");
return; return;
} }
const int attacker = !(bool)parameters.casterSide;
BattleStackAdded bsa; BattleStackAdded bsa;
bsa.creID = clonedStack->type->idNumber; bsa.creID = clonedStack->type->idNumber;
bsa.attacker = attacker; bsa.side = parameters.casterSide;
bsa.summoned = true; 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; bsa.amount = clonedStack->count;
env->sendAndApply(&bsa); env->sendAndApply(&bsa);
@ -370,8 +369,11 @@ ESpellCastProblem::ESpellCastProblem EarthquakeMechanics::canBeCast(const CBattl
CSpell::TargetInfo ti(owner, caster->getSpellSchoolLevel(owner)); CSpell::TargetInfo ti(owner, caster->getSpellSchoolLevel(owner));
if(ti.smart) 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 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; return ESpellCastProblem::NO_APPROPRIATE_TARGET;
} }
@ -407,13 +409,13 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I
///ObstacleMechanics ///ObstacleMechanics
ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const
{ {
const si8 side = cb->playerToSide(ctx.caster->getOwner()); const auto side = cb->playerToSide(ctx.caster->getOwner());
if(side < 0) if(!side)
return ESpellCastProblem::INVALID; return ESpellCastProblem::INVALID;
bool hexesOutsideBattlefield = false; 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) for(const BattleHex & hex : tilesThatMustBeClear)
if(!isHexAviable(cb, hex, ctx.ti.clearAffected)) 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 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 //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()); const auto side = cb->playerToSide(caster->getOwner());
if(playerSide < 0) if(!side)
return ESpellCastProblem::INVALID; return ESpellCastProblem::INVALID;
const si8 otherSide = !playerSide; const ui8 otherSide = cb->otherSide(side.get());
if(cb->battleHasNativeStack(otherSide)) if(cb->battleHasNativeStack(otherSide))
return ESpellCastProblem::NO_APPROPRIATE_TARGET; return ESpellCastProblem::NO_APPROPRIATE_TARGET;
@ -887,9 +889,9 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, const
{ {
BattleStackAdded bsa; BattleStackAdded bsa;
bsa.creID = creatureToSummon; bsa.creID = creatureToSummon;
bsa.attacker = !(bool)parameters.casterSide; bsa.side = parameters.casterSide;
bsa.summoned = true; 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 //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; int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;

View File

@ -649,10 +649,10 @@ std::vector<const CStack *> DefaultSpellMechanics::calculateAffectedStacks(const
{ {
std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures
const si8 playerSide = cb->playerToSide(ctx.caster->getOwner()); const auto side = cb->playerToSide(ctx.caster->getOwner());
if(playerSide < 0) if(!side)
return std::vector<const CStack *>(); return std::vector<const CStack *>();
auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, playerSide); auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, side.get());
//hackfix for banned creature massive spells //hackfix for banned creature massive spells
if(!ctx.ti.massive && owner->getLevelInfo(ctx.schoolLvl).range == "X") if(!ctx.ti.massive && owner->getLevelInfo(ctx.schoolLvl).range == "X")

View File

@ -186,15 +186,15 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback
return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL; return ESpellCastProblem::ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL;
const PlayerColor player = caster->getOwner(); 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; return ESpellCastProblem::INVALID;
//effect like Recanter's Cloak. Blocks also passive casting. //effect like Recanter's Cloak. Blocks also passive casting.
//TODO: check creature abilities to block //TODO: check creature abilities to block
//TODO: check any possible caster //TODO: check any possible caster
if(cb->battleMaxSpellLevel(side) < level) if(cb->battleMaxSpellLevel(side.get()) < level)
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED; return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster); const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster);

View File

@ -138,11 +138,13 @@ static void giveExp(BattleResult &r)
} }
} }
static void summonGuardiansHelper(std::vector<BattleHex> & 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<BattleHex> & 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 x = targetPosition.getX();
int y = targetPosition.getY(); 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... 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); BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output);
else else
@ -1160,21 +1162,15 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
auto accessibility = getAccesibility(curStack); auto accessibility = getAccesibility(curStack);
//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there) //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) BattleHex shifted = dest.cloneInDirection(curStack->destShiftDir());
{
if (accessibility.accessible(dest+1, curStack)) if(accessibility.accessible(shifted, curStack))
dest += BattleHex::RIGHT; dest = shifted;
}
else
{
if (accessibility.accessible(dest-1, curStack))
dest += BattleHex::LEFT;
}
} }
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!"); complain("Given destination is not accessible!");
return 0; return 0;
@ -1182,7 +1178,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
bool canUseGate = false; bool canUseGate = false;
auto dbState = gs->curB->si.gateState; auto dbState = gs->curB->si.gateState;
if (battleGetSiegeLevel() > 0 && !curStack->attackerOwned && if(battleGetSiegeLevel() > 0 && curStack->side == BattleSide::DEFENDER &&
dbState != EGateState::DESTROYED && dbState != EGateState::DESTROYED &&
dbState != EGateState::BLOCKED) dbState != EGateState::BLOCKED)
{ {
@ -3857,7 +3853,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
if (battleTacticDist()) if (battleTacticDist())
{ {
if (stack && !stack->attackerOwned != battleGetTacticsSide()) if (stack && stack->side != battleGetTacticsSide())
{ {
complain("This is not a stack of side that has tactics!"); complain("This is not a stack of side that has tactics!");
return false; return false;
@ -3951,10 +3947,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName()); logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName());
if (stack->position != ba.destinationTile //we wasn't able to reach destination tile if(stack->position != ba.destinationTile //we wasn't able to reach destination tile
&& !(stack->doubleWide() && !(stack->doubleWide() && (stack->position == ba.destinationTile.cloneInDirection(stack->destShiftDir()))) //nor occupy specified hex
&& (stack->position == ba.destinationTile + (stack->attackerOwned ? +1 : -1))
) //nor occupy specified hex
) )
{ {
complain("We cannot move this stack to its destination " + stack->getCreature()->namePl); 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! CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream!
BattleStackAdded bsa; BattleStackAdded bsa;
bsa.attacker = summoner->attackerOwned; bsa.side = summoner->side;
bsa.creID = summonedType; bsa.creID = summonedType;
ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); 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.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; bsa.summoned = false;
if (bsa.amount) //there's rare possibility single creature cannot rise desired type 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 //helper info
const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&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 const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
if (obstacle.obstacleType == CObstacleInstance::MOAT) if (obstacle.obstacleType == CObstacleInstance::MOAT)
@ -5416,6 +5410,7 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat)
BattleStackAdded resurrectInfo; BattleStackAdded resurrectInfo;
resurrectInfo.pos = defender->position; resurrectInfo.pos = defender->position;
resurrectInfo.side = defender->side;
if (bonusAdditionalInfo != -1) if (bonusAdditionalInfo != -1)
resurrectInfo.creID = (CreatureID)bonusAdditionalInfo; resurrectInfo.creID = (CreatureID)bonusAdditionalInfo;
@ -5515,7 +5510,7 @@ void CGameHandler::makeStackDoNothing(const CStack * next)
doNothing.actionType = Battle::NO_ACTION; doNothing.actionType = Battle::NO_ACTION;
doNothing.additionalInfo = 0; doNothing.additionalInfo = 0;
doNothing.destinationTile = -1; doNothing.destinationTile = -1;
doNothing.side = !next->attackerOwned; doNothing.side = next->side;
doNothing.stackNumber = next->ID; doNothing.stackNumber = next->ID;
makeAutomaticAction(next, doNothing); makeAutomaticAction(next, doNothing);
@ -5708,16 +5703,16 @@ void CGameHandler::runBattle()
if (!guardianIsBig) if (!guardianIsBig)
targetHexes = stack->getSurroundingHexes(); targetHexes = stack->getSurroundingHexes();
else else
summonGuardiansHelper(targetHexes, stack->position, stack->attackerOwned, targetIsBig); summonGuardiansHelper(targetHexes, stack->position, stack->side, targetIsBig);
for (auto hex : targetHexes) 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; BattleStackAdded newStack;
newStack.amount = std::max(1, (int)(stack->count * 0.01 * summonInfo->val)); newStack.amount = std::max(1, (int)(stack->count * 0.01 * summonInfo->val));
newStack.creID = creatureData.num; newStack.creID = creatureData.num;
newStack.attacker = stack->attackerOwned; newStack.side = stack->side;
newStack.summoned = true; newStack.summoned = true;
newStack.pos = hex.hex; newStack.pos = hex.hex;
sendAndApply(&newStack); sendAndApply(&newStack);
@ -5808,7 +5803,7 @@ void CGameHandler::runBattle()
BattleAction ba; BattleAction ba;
ba.actionType = Battle::BAD_MORALE; ba.actionType = Battle::BAD_MORALE;
ba.additionalInfo = 1; ba.additionalInfo = 1;
ba.side = !next->attackerOwned; ba.side = next->side;
ba.stackNumber = next->ID; ba.stackNumber = next->ID;
makeAutomaticAction(next, ba); makeAutomaticAction(next, ba);
@ -5819,12 +5814,12 @@ void CGameHandler::runBattle()
if (next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk if (next->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE)) //while in berserk
{ {
logGlobal->debug("Handle Berserk effect"); logGlobal->debug("Handle Berserk effect");
std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::logic::indeterminate); std::pair<const CStack *, int> attackInfo = curB.getNearestStack(next, boost::none);
if (attackInfo.first != nullptr) if (attackInfo.first != nullptr)
{ {
BattleAction attack; BattleAction attack;
attack.actionType = Battle::WALK_AND_ATTACK; attack.actionType = Battle::WALK_AND_ATTACK;
attack.side = !next->attackerOwned; attack.side = next->side;
attack.stackNumber = next->ID; attack.stackNumber = next->ID;
attack.additionalInfo = attackInfo.first->position; attack.additionalInfo = attackInfo.first->position;
attack.destinationTile = attackInfo.second; attack.destinationTile = attackInfo.second;
@ -5847,7 +5842,7 @@ void CGameHandler::runBattle()
{ {
BattleAction attack; BattleAction attack;
attack.actionType = Battle::SHOOT; attack.actionType = Battle::SHOOT;
attack.side = !next->attackerOwned; attack.side = next->side;
attack.stackNumber = next->ID; attack.stackNumber = next->ID;
for (auto & elem : gs->curB->stacks) for (auto & elem : gs->curB->stacks)
@ -5880,7 +5875,7 @@ void CGameHandler::runBattle()
getRandomGenerator()); getRandomGenerator());
attack.actionType = Battle::CATAPULT; attack.actionType = Battle::CATAPULT;
attack.additionalInfo = 0; attack.additionalInfo = 0;
attack.side = !next->attackerOwned; attack.side = next->side;
attack.stackNumber = next->ID; attack.stackNumber = next->ID;
makeAutomaticAction(next, attack); makeAutomaticAction(next, attack);
@ -5910,7 +5905,7 @@ void CGameHandler::runBattle()
heal.actionType = Battle::STACK_HEAL; heal.actionType = Battle::STACK_HEAL;
heal.additionalInfo = 0; heal.additionalInfo = 0;
heal.destinationTile = toBeHealed->position; heal.destinationTile = toBeHealed->position;
heal.side = !next->attackerOwned; heal.side = next->side;
heal.stackNumber = next->ID; heal.stackNumber = next->ID;
makeAutomaticAction(next, heal); makeAutomaticAction(next, heal);

View File

@ -91,16 +91,16 @@ BOOST_AUTO_TEST_CASE(getClosestTile)
possibilities.insert(119); possibilities.insert(119);
possibilities.insert(186); possibilities.insert(186);
BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==3); BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==3);
mainHex = 139; mainHex = 139;
BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==119); BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==119);
mainHex = 16; mainHex = 16;
BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==100); BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==100);
mainHex = 166; mainHex = 166;
BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==186); BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==186);
mainHex = 76; mainHex = 76;
BOOST_TEST(mainHex.getClosestTile(false,mainHex,possibilities)==3); BOOST_TEST(mainHex.getClosestTile(1,mainHex,possibilities)==3);
BOOST_TEST(mainHex.getClosestTile(true,mainHex,possibilities)==100); BOOST_TEST(mainHex.getClosestTile(0,mainHex,possibilities)==100);
} }
BOOST_AUTO_TEST_CASE(moveEDir) BOOST_AUTO_TEST_CASE(moveEDir)
@ -118,6 +118,8 @@ BOOST_AUTO_TEST_CASE(moveEDir)
BOOST_TEST(mainHex==3); BOOST_TEST(mainHex==3);
mainHex.moveInDirection(BattleHex::EDir::BOTTOM_LEFT); mainHex.moveInDirection(BattleHex::EDir::BOTTOM_LEFT);
BOOST_TEST(mainHex==20); BOOST_TEST(mainHex==20);
mainHex.moveInDirection(BattleHex::EDir::NONE);
BOOST_TEST(mainHex==20);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()