mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-21 21:17:49 +02:00
Stack reversing logic now matches H3
This commit is contained in:
parent
52fc5b3c39
commit
45aa841fb6
@ -411,15 +411,14 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
|
|||||||
if (!attackedInfo.attacker)
|
if (!attackedInfo.attacker)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool needsReverse =
|
// In H3, attacked stack will not reverse on ranged attack
|
||||||
owner.curInt->cb->isToReverse(
|
if (attackedInfo.indirectAttack)
|
||||||
attackedInfo.defender->getPosition(),
|
continue;
|
||||||
attackedInfo.attacker->getPosition(),
|
|
||||||
facingRight(attackedInfo.defender),
|
|
||||||
attackedInfo.attacker->doubleWide(),
|
|
||||||
facingRight(attackedInfo.attacker));
|
|
||||||
|
|
||||||
if (needsReverse)
|
// defender need to face in direction opposited to out attacker
|
||||||
|
bool needsReverse = shouldAttackFacingRight(attackedInfo.attacker, attackedInfo.defender) == facingRight(attackedInfo.defender);
|
||||||
|
|
||||||
|
if (needsReverse && !attackedInfo.defender->isFrozen())
|
||||||
{
|
{
|
||||||
owner.executeOnAnimationCondition(EAnimationEvents::MOVEMENT, true, [=]()
|
owner.executeOnAnimationCondition(EAnimationEvents::MOVEMENT, true, [=]()
|
||||||
{
|
{
|
||||||
@ -494,23 +493,38 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH
|
|||||||
owner.waitForAnimationCondition(EAnimationEvents::ACTION, false);
|
owner.waitForAnimationCondition(EAnimationEvents::ACTION, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BattleStacksController::shouldAttackFacingRight(const CStack * attacker, const CStack * defender)
|
||||||
|
{
|
||||||
|
bool mustReverse = owner.curInt->cb->isToReverse(
|
||||||
|
attacker->getPosition(),
|
||||||
|
attacker,
|
||||||
|
defender);
|
||||||
|
|
||||||
|
if (attacker->side == BattleSide::ATTACKER)
|
||||||
|
return !mustReverse;
|
||||||
|
else
|
||||||
|
return mustReverse;
|
||||||
|
}
|
||||||
|
|
||||||
void BattleStacksController::stackAttacking( const StackAttackInfo & info )
|
void BattleStacksController::stackAttacking( const StackAttackInfo & info )
|
||||||
{
|
{
|
||||||
assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false);
|
assert(owner.getAnimationCondition(EAnimationEvents::ACTION) == false);
|
||||||
|
|
||||||
bool needsReverse =
|
|
||||||
owner.curInt->cb->isToReverse(
|
|
||||||
info.attacker->getPosition(),
|
|
||||||
info.defender->getPosition(),
|
|
||||||
facingRight(info.attacker),
|
|
||||||
info.attacker->doubleWide(),
|
|
||||||
facingRight(info.defender));
|
|
||||||
|
|
||||||
auto attacker = info.attacker;
|
auto attacker = info.attacker;
|
||||||
auto defender = info.defender;
|
auto defender = info.defender;
|
||||||
auto tile = info.tile;
|
auto tile = info.tile;
|
||||||
auto spellEffect = info.spellEffect;
|
auto spellEffect = info.spellEffect;
|
||||||
auto multiAttack = !info.secondaryDefender.empty();
|
auto multiAttack = !info.secondaryDefender.empty();
|
||||||
|
bool needsReverse = false;
|
||||||
|
|
||||||
|
if (info.indirectAttack)
|
||||||
|
{
|
||||||
|
needsReverse = shouldRotate(attacker, attacker->position, info.tile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
needsReverse = shouldAttackFacingRight(attacker, defender) != facingRight(attacker);
|
||||||
|
}
|
||||||
|
|
||||||
if (needsReverse)
|
if (needsReverse)
|
||||||
{
|
{
|
||||||
|
@ -100,6 +100,8 @@ class BattleStacksController
|
|||||||
|
|
||||||
std::vector<const CStack *> selectHoveredStacks();
|
std::vector<const CStack *> selectHoveredStacks();
|
||||||
|
|
||||||
|
bool shouldAttackFacingRight(const CStack * attacker, const CStack * defender);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BattleStacksController(BattleInterface & owner);
|
BattleStacksController(BattleInterface & owner);
|
||||||
|
|
||||||
|
@ -151,12 +151,12 @@ std::vector<BattleHex> BattleHex::allNeighbouringTiles() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
signed char BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
|
BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
|
||||||
{
|
{
|
||||||
for(EDir dir = EDir(0); dir <= EDir(5); dir = EDir(dir+1))
|
for(EDir dir = EDir(0); dir <= EDir(5); dir = EDir(dir+1))
|
||||||
if(hex2 == hex1.cloneInDirection(dir,false))
|
if(hex2 == hex1.cloneInDirection(dir,false))
|
||||||
return dir;
|
return dir;
|
||||||
return INVALID;
|
return NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char BattleHex::getDistance(BattleHex hex1, BattleHex hex2)
|
char BattleHex::getDistance(BattleHex hex1, BattleHex hex2)
|
||||||
|
@ -15,7 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
namespace BattleSide
|
namespace BattleSide
|
||||||
{
|
{
|
||||||
enum
|
enum Type
|
||||||
{
|
{
|
||||||
ATTACKER = 0,
|
ATTACKER = 0,
|
||||||
DEFENDER = 1
|
DEFENDER = 1
|
||||||
@ -47,13 +47,14 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
|
|||||||
static const si16 INVALID = -1;
|
static const si16 INVALID = -1;
|
||||||
enum EDir
|
enum EDir
|
||||||
{
|
{
|
||||||
|
NONE = -1,
|
||||||
|
|
||||||
TOP_LEFT,
|
TOP_LEFT,
|
||||||
TOP_RIGHT,
|
TOP_RIGHT,
|
||||||
RIGHT,
|
RIGHT,
|
||||||
BOTTOM_RIGHT,
|
BOTTOM_RIGHT,
|
||||||
BOTTOM_LEFT,
|
BOTTOM_LEFT,
|
||||||
LEFT,
|
LEFT,
|
||||||
NONE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BattleHex();
|
BattleHex();
|
||||||
@ -82,7 +83,7 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
|
|||||||
/// order of returned tiles matches EDir enim
|
/// order of returned tiles matches EDir enim
|
||||||
std::vector<BattleHex> allNeighbouringTiles() const;
|
std::vector<BattleHex> allNeighbouringTiles() const;
|
||||||
|
|
||||||
static signed char mutualPosition(BattleHex hex1, BattleHex hex2);
|
static EDir 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(ui8 side, 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
|
||||||
|
@ -1382,8 +1382,12 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const battl
|
|||||||
const int WN = GameConstants::BFIELD_WIDTH;
|
const int WN = GameConstants::BFIELD_WIDTH;
|
||||||
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : attacker->getPosition(); //real or hypothetical (cursor) position
|
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : attacker->getPosition(); //real or hypothetical (cursor) position
|
||||||
|
|
||||||
|
auto defender = battleGetUnitByPos(hex, true);
|
||||||
|
if (!defender)
|
||||||
|
return at; // can't attack thin air
|
||||||
|
|
||||||
//FIXME: dragons or cerbers can rotate before attack, making their base hex different (#1124)
|
//FIXME: dragons or cerbers can rotate before attack, making their base hex different (#1124)
|
||||||
bool reverse = isToReverse(hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker);
|
bool reverse = isToReverse(destinationTile, attacker, defender);
|
||||||
if(reverse && attacker->doubleWide())
|
if(reverse && attacker->doubleWide())
|
||||||
{
|
{
|
||||||
hex = attacker->occupiedHex(hex); //the other hex stack stands on
|
hex = attacker->occupiedHex(hex); //the other hex stack stands on
|
||||||
@ -1537,60 +1541,50 @@ std::set<const CStack*> CBattleInfoCallback::getAttackedCreatures(const CStack*
|
|||||||
return attackedCres;
|
return attackedCres;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: this should apply also to mechanics and cursor interface
|
static bool isHexInFront(BattleHex hex, BattleHex testHex, BattleSide::Type side )
|
||||||
bool CBattleInfoCallback::isToReverseHlp (BattleHex hexFrom, BattleHex hexTo, bool curDir) const
|
|
||||||
{
|
{
|
||||||
int fromX = hexFrom.getX();
|
static const std::set<BattleHex::EDir> rightDirs { BattleHex::BOTTOM_RIGHT, BattleHex::TOP_RIGHT, BattleHex::RIGHT };
|
||||||
int fromY = hexFrom.getY();
|
static const std::set<BattleHex::EDir> leftDirs { BattleHex::BOTTOM_LEFT, BattleHex::TOP_LEFT, BattleHex::LEFT };
|
||||||
int toX = hexTo.getX();
|
|
||||||
int toY = hexTo.getY();
|
|
||||||
|
|
||||||
if (curDir) // attacker, facing right
|
auto mutualPos = BattleHex::mutualPosition(hex, testHex);
|
||||||
{
|
|
||||||
if (fromX < toX)
|
|
||||||
return false;
|
|
||||||
if (fromX > toX)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (fromY % 2 == 0 && toY % 2 == 1)
|
if (side == BattleSide::ATTACKER)
|
||||||
|
return rightDirs.count(mutualPos);
|
||||||
return true;
|
else
|
||||||
return false;
|
return leftDirs.count(mutualPos);
|
||||||
}
|
|
||||||
else // defender, facing left
|
|
||||||
{
|
|
||||||
if(fromX < toX)
|
|
||||||
return true;
|
|
||||||
if(fromX > toX)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (fromY % 2 == 1 && toY % 2 == 0)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: this should apply also to mechanics and cursor interface
|
//TODO: this should apply also to mechanics and cursor interface
|
||||||
bool CBattleInfoCallback::isToReverse (BattleHex hexFrom, BattleHex hexTo, bool curDir, bool toDoubleWide, bool toDir) const
|
bool CBattleInfoCallback::isToReverse (BattleHex attackerHex, const battle::Unit * attacker, const battle::Unit * defender) const
|
||||||
{
|
{
|
||||||
if (hexTo < 0 || hexFrom < 0) //turret
|
if (attackerHex < 0 ) //turret
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (toDoubleWide)
|
BattleHex defenderHex = defender->getPosition();
|
||||||
|
|
||||||
|
if (isHexInFront(attackerHex, defenderHex, BattleSide::Type(attacker->unitSide())))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (defender->doubleWide())
|
||||||
{
|
{
|
||||||
if (isToReverseHlp (hexFrom, hexTo, curDir))
|
if (isHexInFront(attackerHex,defender->occupiedHex(), BattleSide::Type(attacker->unitSide())))
|
||||||
{
|
|
||||||
if (toDir)
|
|
||||||
return isToReverseHlp (hexFrom, hexTo-1, curDir);
|
|
||||||
else
|
|
||||||
return isToReverseHlp (hexFrom, hexTo+1, curDir);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (attacker->doubleWide())
|
||||||
{
|
{
|
||||||
return isToReverseHlp(hexFrom, hexTo, curDir);
|
if (isHexInFront(attacker->occupiedHex(), defenderHex, BattleSide::Type(attacker->unitSide())))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a bit weird case since here defender is slightly behind attacker, so reversing seems preferable,
|
||||||
|
// but this is how H3 handles it which is important, e.g. for direction of dragon breath attacks
|
||||||
|
if (attacker->doubleWide() && defender->doubleWide())
|
||||||
|
{
|
||||||
|
if (isHexInFront(attacker->occupiedHex(), defender->occupiedHex(), BattleSide::Type(attacker->unitSide())))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReachabilityInfo::TDistances CBattleInfoCallback::battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const
|
ReachabilityInfo::TDistances CBattleInfoCallback::battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const
|
||||||
|
@ -136,8 +136,7 @@ public:
|
|||||||
AttackableTiles getPotentiallyShootableHexes(const battle::Unit* attacker, BattleHex destinationTile, BattleHex attackerPos) const;
|
AttackableTiles getPotentiallyShootableHexes(const battle::Unit* attacker, BattleHex destinationTile, BattleHex attackerPos) const;
|
||||||
std::vector<const battle::Unit *> getAttackedBattleUnits(const battle::Unit* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
std::vector<const battle::Unit *> getAttackedBattleUnits(const battle::Unit* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
||||||
std::set<const CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
std::set<const CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
||||||
bool isToReverse(BattleHex hexFrom, BattleHex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir) const; //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo)
|
bool isToReverse(BattleHex attackerHex, const battle::Unit *attacker, const battle::Unit *defender) const; //determines if attacker standing at attackerHex should reverse in order to attack defender
|
||||||
bool isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse
|
|
||||||
|
|
||||||
ReachabilityInfo getReachability(const battle::Unit * unit) const;
|
ReachabilityInfo getReachability(const battle::Unit * unit) const;
|
||||||
ReachabilityInfo getReachability(const ReachabilityInfo::Parameters & params) const;
|
ReachabilityInfo getReachability(const ReachabilityInfo::Parameters & params) const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user