1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

Merge pull request #5726 from IvanSavenko/breath

Configurable multi-hex attacks
This commit is contained in:
Ivan Savenko
2025-05-21 17:10:59 +03:00
committed by GitHub
15 changed files with 408 additions and 191 deletions

View File

@@ -229,6 +229,16 @@ public:
return unit;
}
UnitFake & addCerberi(BattleHex hex, BattleSide side)
{
auto & unit = addRegularMelee(hex, side);
unit.addCreatureAbility(BonusType::THREE_HEADED_ATTACK);
unit.makeDoubleWide();
return unit;
}
UnitFake & addDragon(BattleHex hex, BattleSide side)
{
auto & unit = addRegularMelee(hex, side);
@@ -252,6 +262,91 @@ public:
}
};
//// CERBERI 3-HEADED ATTACKS
TEST_F(AttackableHexesTest, CerberiAttackerRight)
{
// #
// X A D
// #
UnitFake & attacker = addCerberi(35, BattleSide::ATTACKER);
UnitFake & defender = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::RIGHT), BattleSide::DEFENDER);
UnitFake & right = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
UnitFake & left = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::TOP_RIGHT), BattleSide::DEFENDER);
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));
}
TEST_F(AttackableHexesTest, CerberiAttackerTopRight)
{
// # D
// X A #
//
UnitFake & attacker = addCerberi(35, BattleSide::ATTACKER);
UnitFake & defender = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::TOP_RIGHT), BattleSide::DEFENDER);
UnitFake & right = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::RIGHT), BattleSide::DEFENDER);
UnitFake & left = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::TOP_LEFT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &right));
EXPECT_TRUE(vstd::contains(attacked, &left));
}
TEST_F(AttackableHexesTest, CerberiAttackerTopMiddle)
{
// # D #
// X A
//
UnitFake & attacker = addCerberi(35, BattleSide::ATTACKER);
UnitFake & defender = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::TOP_LEFT), BattleSide::DEFENDER);
UnitFake & right = addRegularMelee(attacker.getPosition().cloneInDirection(BattleHex::TOP_RIGHT), BattleSide::DEFENDER);
UnitFake & left = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::TOP_LEFT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &right));
EXPECT_TRUE(vstd::contains(attacked, &left));
}
TEST_F(AttackableHexesTest, CerberiAttackerTopLeft)
{
// D #
// # X A
//
UnitFake & attacker = addCerberi(40, BattleSide::ATTACKER);
UnitFake & defender = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::TOP_LEFT), BattleSide::DEFENDER);
UnitFake & right = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::TOP_RIGHT), BattleSide::DEFENDER);
UnitFake & left = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::LEFT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &right));
EXPECT_TRUE(vstd::contains(attacked, &left));
}
TEST_F(AttackableHexesTest, CerberiAttackerLeft)
{
// #
// D X A
// #
UnitFake & attacker = addCerberi(40, BattleSide::ATTACKER);
UnitFake & defender = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::LEFT), BattleSide::DEFENDER);
UnitFake & right = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::TOP_LEFT), BattleSide::DEFENDER);
UnitFake & left = addRegularMelee(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &right));
EXPECT_TRUE(vstd::contains(attacked, &left));
}
//// DRAGON BREATH AS ATTACKER
TEST_F(AttackableHexesTest, DragonRightRegular_RightHorithontalBreath)
{
// X A D #
@@ -261,6 +356,7 @@ TEST_F(AttackableHexesTest, DragonRightRegular_RightHorithontalBreath)
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &defender));
EXPECT_TRUE(vstd::contains(attacked, &next));
}
@@ -282,26 +378,26 @@ TEST_F(AttackableHexesTest, DragonDragonVerticalDownHead_VerticalDownBreathFromH
{
// X A
// D X target D
// #
// #
UnitFake & attacker = addDragon(35, BattleSide::ATTACKER);
UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER);
UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
UnitFake & next = addRegularMelee(defender.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &next));
}
TEST_F(AttackableHexesTest, DragonDragonVerticalDownHeadReverse_VerticalDownBreathFromHead)
TEST_F(AttackableHexesTest, DragonDragonVerticalDownHead_VerticalRightBreathFromHead)
{
// A X
// X D target D
// #
UnitFake & attacker = addDragon(36, BattleSide::DEFENDER);
UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::ATTACKER);
UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::ATTACKER);
// X A
// D X target X
// #
UnitFake & attacker = addDragon(35, BattleSide::ATTACKER);
UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::DEFENDER);
UnitFake & next = addRegularMelee(defender.occupiedHex().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex());
EXPECT_TRUE(vstd::contains(attacked, &next));
}
@@ -334,6 +430,36 @@ TEST_F(AttackableHexesTest, DragonDragonHeadBottomRight_BottomRightBreathFromHea
EXPECT_TRUE(vstd::contains(attacked, &next));
}
TEST_F(AttackableHexesTest, DragonLeftBottomDragonBackToBack_LeftBottomBreathFromBackHex)
{
// X A
// D X target X
// #
UnitFake & attacker = addDragon(8, BattleSide::ATTACKER);
UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT).cloneInDirection(BattleHex::LEFT), BattleSide::DEFENDER);
UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex());
EXPECT_TRUE(vstd::contains(attacked, &next));
}
//// DRAGON BREATH AS DEFENDER
TEST_F(AttackableHexesTest, DragonDragonVerticalDownHeadReverse_VerticalDownBreathFromHead)
{
// A X
// X D target D
// #
UnitFake & attacker = addDragon(36, BattleSide::DEFENDER);
UnitFake & defender = addDragon(attacker.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::ATTACKER);
UnitFake & next = addRegularMelee(defender.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT), BattleSide::ATTACKER);
auto attacked = getAttackedUnits(attacker, defender, defender.getPosition());
EXPECT_TRUE(vstd::contains(attacked, &next));
}
TEST_F(AttackableHexesTest, DragonVerticalDownDragonBackReverse_VerticalDownBreath)
{
// A X
@@ -361,28 +487,14 @@ TEST_F(AttackableHexesTest, DragonRightBottomDragonHeadReverse_RightBottomBreath
EXPECT_TRUE(vstd::contains(attacked, &next));
}
TEST_F(AttackableHexesTest, DragonLeftBottomDragonBackToBack_LeftBottomBreathFromBackHex)
{
// X A
// D X target X
// #
UnitFake & attacker = addDragon(8, BattleSide::ATTACKER);
UnitFake & defender = addDragon(attacker.occupiedHex().cloneInDirection(BattleHex::BOTTOM_LEFT).cloneInDirection(BattleHex::LEFT), BattleSide::DEFENDER);
UnitFake & next = addRegularMelee(defender.getPosition().cloneInDirection(BattleHex::BOTTOM_RIGHT), BattleSide::DEFENDER);
auto attacked = getAttackedUnits(attacker, defender, defender.occupiedHex());
EXPECT_TRUE(vstd::contains(attacked, &next));
}
TEST_F(AttackableHexesTest, DefenderPositionOverride_BreathCountsHypoteticDefenderPosition)
{
// # N
// X D target D
// A X
// # N
// X D target D
// A X
UnitFake & attacker = addDragon(35, BattleSide::DEFENDER);
UnitFake & defender = addDragon(8, BattleSide::ATTACKER);
UnitFake & next = addDragon(2, BattleSide::ATTACKER);
UnitFake & next = addDragon(1, BattleSide::ATTACKER);
startBattle();
redirectUnitsToFake();