mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-13 01:20:34 +02:00
Merge pull request #5739 from IvanSavenko/battleai_fix
Fix handling of double-wide creatures by BattleAI
This commit is contained in:
@ -385,7 +385,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
||||
}
|
||||
|
||||
auto turnsToReach = (distance - 1) / speed + 1;
|
||||
const BattleHexArray & hexes = enemy->getSurroundingHexes();
|
||||
const BattleHexArray & hexes = enemy->getAttackableHexes(activeStack);
|
||||
auto enemySpeed = enemy->getMovementRange();
|
||||
auto speedRatio = speed / static_cast<float>(enemySpeed);
|
||||
auto multiplier = (speedRatio > 1 ? 1 : speedRatio) * penaltyMultiplier;
|
||||
@ -416,8 +416,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
||||
logAi->trace("New high score");
|
||||
#endif
|
||||
|
||||
for(BattleHex enemyHex : enemy->getAttackableHexes(activeStack))
|
||||
{
|
||||
BattleHex enemyHex = hex;
|
||||
while(!flying && dists.distances[enemyHex.toInt()] > speed && dists.predecessors.at(enemyHex.toInt()).isValid())
|
||||
{
|
||||
enemyHex = dists.predecessors.at(enemyHex.toInt());
|
||||
@ -425,14 +424,17 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
||||
if(dists.accessibility[enemyHex.toInt()] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
auto defenderToBypass = hb->battleGetUnitByPos(enemyHex);
|
||||
|
||||
if(defenderToBypass)
|
||||
assert(defenderToBypass != nullptr);
|
||||
auto attackHex = dists.predecessors[enemyHex.toInt()];
|
||||
|
||||
if(defenderToBypass &&
|
||||
defenderToBypass != enemy &&
|
||||
vstd::contains(defenderToBypass->getAttackableHexes(activeStack), attackHex))
|
||||
{
|
||||
#if BATTLE_TRACE_LEVEL >= 1
|
||||
logAi->trace("Found target to bypass at %d", enemyHex.toInt());
|
||||
#endif
|
||||
|
||||
auto attackHex = dists.predecessors[enemyHex.toInt()];
|
||||
|
||||
auto baiBypass = BattleAttackInfo(activeStack, defenderToBypass, 0, cb->battleCanShoot(activeStack));
|
||||
auto attackBypass = AttackPossibility::evaluate(baiBypass, attackHex, damageCache, hb);
|
||||
|
||||
@ -461,9 +463,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
|
||||
}
|
||||
}
|
||||
|
||||
result.positions.insert(enemyHex);
|
||||
}
|
||||
|
||||
result.positions.insert(enemyHex);
|
||||
result.cachedAttack = attack;
|
||||
result.turnsToReach = turnsToReach;
|
||||
}
|
||||
|
@ -1338,7 +1338,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
|
||||
attackOriginHex = attacker->occupiedHex(attackOriginHex);
|
||||
|
||||
if (!vstd::contains(defender->getSurroundingHexes(defenderPos), attackOriginHex))
|
||||
throw std::runtime_error("!!!");
|
||||
throw std::runtime_error("Atempt to attack from invalid position!");
|
||||
|
||||
auto attackDirection = BattleHex::mutualPosition(attackOriginHex, defenderPos);
|
||||
|
||||
|
@ -68,22 +68,28 @@ const BattleHexArray & Unit::getSurroundingHexes(const BattleHex & position, boo
|
||||
|
||||
BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
|
||||
{
|
||||
const BattleHexArray & defenderHexes = getHexes();
|
||||
|
||||
BattleHexArray targetableHexes;
|
||||
|
||||
for(const auto & defenderHex : defenderHexes)
|
||||
if (!attacker->doubleWide())
|
||||
{
|
||||
auto hexes = battle::Unit::getHexes(defenderHex);
|
||||
|
||||
if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
|
||||
hexes.pop_back();
|
||||
|
||||
for(const auto & hex : hexes)
|
||||
targetableHexes.insert(hex.getNeighbouringTiles());
|
||||
return getSurroundingHexes();
|
||||
}
|
||||
else
|
||||
{
|
||||
BattleHexArray result;
|
||||
|
||||
return targetableHexes;
|
||||
for (const auto & attackOrigin : getSurroundingHexes())
|
||||
{
|
||||
if (!coversPos(attacker->occupiedHex(attackOrigin)) && attackOrigin.isAvailable())
|
||||
result.insert(attackOrigin);
|
||||
|
||||
bool isAttacker = attacker->unitSide() == BattleSide::ATTACKER;
|
||||
BattleHex::EDir headDirection = isAttacker ? BattleHex::RIGHT : BattleHex::LEFT;
|
||||
BattleHex headHex = attackOrigin.cloneInDirection(headDirection);
|
||||
|
||||
if (!coversPos(headHex) && headHex.isAvailable())
|
||||
result.insert(headHex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool Unit::coversPos(const BattleHex & pos) const
|
||||
|
@ -132,6 +132,8 @@ public:
|
||||
virtual std::string getDescription() const;
|
||||
|
||||
const BattleHexArray & getSurroundingHexes(const BattleHex & assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
|
||||
|
||||
/// Returns list of hexes from which attacker can attack this unit
|
||||
BattleHexArray getAttackableHexes(const Unit * attacker) const;
|
||||
static const BattleHexArray & getSurroundingHexes(const BattleHex & position, bool twoHex, BattleSide side);
|
||||
|
||||
|
@ -262,6 +262,96 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
///// getAttackableHexes tests
|
||||
|
||||
TEST_F(AttackableHexesTest, getAttackableHexes_SingleWideAttacker_SingleWideDefender)
|
||||
{
|
||||
UnitFake & attacker = addRegularMelee(60, BattleSide::ATTACKER);
|
||||
UnitFake & defender = addRegularMelee(90, BattleSide::DEFENDER);
|
||||
|
||||
static const BattleHexArray expectedDef =
|
||||
{
|
||||
72,
|
||||
73,
|
||||
89,
|
||||
91,
|
||||
106,
|
||||
107
|
||||
};
|
||||
|
||||
auto attackable = defender.getAttackableHexes(&attacker);
|
||||
attackable.sort([](const auto & l, const auto & r) { return l < r; });
|
||||
EXPECT_EQ(expectedDef, attackable);
|
||||
}
|
||||
|
||||
TEST_F(AttackableHexesTest, getAttackableHexes_SingleWideAttacker_DoubleWideDefender)
|
||||
{
|
||||
UnitFake & attacker = addRegularMelee(60, BattleSide::ATTACKER);
|
||||
UnitFake & defender = addDragon(90, BattleSide::DEFENDER);
|
||||
|
||||
static const BattleHexArray expectedDef =
|
||||
{
|
||||
72,
|
||||
73,
|
||||
74,
|
||||
89,
|
||||
92,
|
||||
106,
|
||||
107,
|
||||
108
|
||||
};
|
||||
|
||||
auto attackable = defender.getAttackableHexes(&attacker);
|
||||
attackable.sort([](const auto & l, const auto & r) { return l < r; });
|
||||
EXPECT_EQ(expectedDef, attackable);
|
||||
}
|
||||
|
||||
TEST_F(AttackableHexesTest, getAttackableHexes_DoubleWideAttacker_SingleWideDefender)
|
||||
{
|
||||
UnitFake & attacker = addDragon(60, BattleSide::ATTACKER);
|
||||
UnitFake & defender = addRegularMelee(90, BattleSide::DEFENDER);
|
||||
|
||||
static const BattleHexArray expectedDef =
|
||||
{
|
||||
72,
|
||||
73,
|
||||
74,
|
||||
89,
|
||||
92,
|
||||
106,
|
||||
107,
|
||||
108
|
||||
};
|
||||
|
||||
auto attackable = defender.getAttackableHexes(&attacker);
|
||||
attackable.sort([](const auto & l, const auto & r) { return l < r; });
|
||||
EXPECT_EQ(expectedDef, attackable);
|
||||
}
|
||||
|
||||
TEST_F(AttackableHexesTest, getAttackableHexes_DoubleWideAttacker_DoubleWideDefender)
|
||||
{
|
||||
UnitFake & attacker = addDragon(60, BattleSide::ATTACKER);
|
||||
UnitFake & defender = addDragon(90, BattleSide::DEFENDER);
|
||||
|
||||
static const BattleHexArray expectedDef =
|
||||
{
|
||||
72,
|
||||
73,
|
||||
74,
|
||||
75,
|
||||
89,
|
||||
93,
|
||||
106,
|
||||
107,
|
||||
108,
|
||||
109
|
||||
};
|
||||
|
||||
auto attackable = defender.getAttackableHexes(&attacker);
|
||||
attackable.sort([](const auto & l, const auto & r) { return l < r; });
|
||||
EXPECT_EQ(expectedDef, attackable);
|
||||
}
|
||||
|
||||
//// CERBERI 3-HEADED ATTACKS
|
||||
|
||||
TEST_F(AttackableHexesTest, CerberiAttackerRight)
|
||||
@ -276,7 +366,6 @@ TEST_F(AttackableHexesTest, CerberiAttackerRight)
|
||||
|
||||
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
|
||||
|
||||
EXPECT_TRUE(vstd::contains(attacked, &defender));
|
||||
EXPECT_TRUE(vstd::contains(attacked, &right));
|
||||
EXPECT_TRUE(vstd::contains(attacked, &left));
|
||||
}
|
||||
@ -356,7 +445,6 @@ TEST_F(AttackableHexesTest, DragonRightRegular_RightHorithontalBreath)
|
||||
|
||||
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
|
||||
|
||||
EXPECT_TRUE(vstd::contains(attacked, &defender));
|
||||
EXPECT_TRUE(vstd::contains(attacked, &next));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user