Unit tests for [drains], [poison] and [slow], with apply_to=opponent
Slightly different to PR #6582, which was the 1.16 version of this. The five lines that were labelled `preserving known bug` are changed to test that it's been fixed. Here `apply_to=opponent` means that the weapon special gives the opponent the ability, the unit that should get poisoned or slowed is the unit that has the weapon special. There's a known bug in 1.16, that `apply_to=opponent` check the wrong unit to see it it's `unpoisonable`, `undrainable` etc. It also checks the wrong unit to see if it's already poisoned or slowed, so a battle between two units that both have reverse-poison results in at most one being poisoned. Most of the credit for this is Newfrenchy's, as he's already written a fix and a WML based test. This commit uses a Lua test instead to test more combinations of statuses. This adds a `COMMON_KEEP_A_B_UNIT_TEST` macro, which is a counterpart to the `GENERIC_UNIT_TEST` macro that starts the leaders next to each other, ready to attack. The `A_B` is because I'm planning a multiple-side variant too. There's no test for [petrify], as simulate_combat doesn't provide a stat for it. This tests only 3 of the 6 abilities whose behavior changed in650f70405f
. My thoughts on testing the others are: * [firststrike]'s test is in650f70405f
. * [drains], [poison] and [slow] are tested here. * [petrify] ends combat, it's also not exposed in simulate_combat's stats. * [plague] triggers after combat ends.
This commit is contained in:
parent
c5dec73784
commit
0f6a94f2a7
6 changed files with 372 additions and 0 deletions
43
data/test/macros/start_position_common_keep_a_b.cfg
Normal file
43
data/test/macros/start_position_common_keep_a_b.cfg
Normal file
|
@ -0,0 +1,43 @@
|
|||
#textdomain wesnoth-test
|
||||
|
||||
##
|
||||
# Starting state:
|
||||
# Side 1 leader Alice (Elvish Archer)
|
||||
# Side 2 leader Bob (Orcish Grunt)
|
||||
#
|
||||
# Both leaders are on a single keep, adjacent to each other.
|
||||
# There is no free castle hex to recruit onto.
|
||||
##
|
||||
#define COMMON_KEEP_A_B_UNIT_TEST NAME CONTENT
|
||||
[test]
|
||||
name=_ "Unit Test " + {NAME}
|
||||
map_file=test/maps/2p_single_castle.map
|
||||
turns=unlimited
|
||||
id={NAME}
|
||||
random_start_time=no
|
||||
is_unit_test=yes
|
||||
|
||||
{DAWN}
|
||||
|
||||
[side]
|
||||
side=1
|
||||
controller=human
|
||||
name=_ "Alice"
|
||||
type=Elvish Archer
|
||||
id=alice
|
||||
fog=no
|
||||
team_name=West
|
||||
[/side]
|
||||
[side]
|
||||
side=2
|
||||
controller=human
|
||||
name=_ "Bob"
|
||||
type=Orcish Grunt
|
||||
id=bob
|
||||
fog=no
|
||||
team_name=East
|
||||
[/side]
|
||||
|
||||
{CONTENT}
|
||||
[/test]
|
||||
#enddef
|
7
data/test/maps/2p_single_castle.map
Normal file
7
data/test/maps/2p_single_castle.map
Normal file
|
@ -0,0 +1,7 @@
|
|||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, 1 Ke, 2 Ke, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
||||
Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg, Gg
|
165
data/test/scenarios/reflexive_drains.cfg
Normal file
165
data/test/scenarios/reflexive_drains.cfg
Normal file
|
@ -0,0 +1,165 @@
|
|||
#textdomain wesnoth-test
|
||||
|
||||
#####
|
||||
# API(s) being tested: [drains]
|
||||
##
|
||||
# Actions:
|
||||
# This uses a "common keep" map, with Alice and Bob already in position to attack any of the other units.
|
||||
# In this test they're all Orcish Grunts, so the all have 9x2 melee attacks.
|
||||
# Set everyone to 20 hp, so that drains will show but no-one will die.
|
||||
# Give Alice drains.
|
||||
# Give Bob reflexive drains.
|
||||
# Give Dave the undrainable trait.
|
||||
# Simulate various combats and check the results.
|
||||
##
|
||||
# Expected end state:
|
||||
# Alice drains Bob and Charlie
|
||||
# Charlie drains when attacking Bob
|
||||
# Dave drains in the Bob v Dave fight
|
||||
#####
|
||||
[test]
|
||||
name = _ "Unit Test reflexive_drains"
|
||||
map_file=test/maps/4p_single_castle.map
|
||||
turns = unlimited
|
||||
id = reflexive_drains
|
||||
is_unit_test = yes
|
||||
|
||||
{DAWN}
|
||||
|
||||
[side]
|
||||
side=1
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Alice"
|
||||
type = Orcish Grunt
|
||||
id=alice
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=2
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Bob"
|
||||
type = Orcish Grunt
|
||||
id=bob
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=3
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Charlie"
|
||||
type = Orcish Grunt
|
||||
id=charlie
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=4
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Dave"
|
||||
type = Orcish Grunt
|
||||
id=dave
|
||||
[/leader]
|
||||
[/side]
|
||||
|
||||
[event]
|
||||
name=start
|
||||
|
||||
[object]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
[set_specials]
|
||||
mode=append
|
||||
{WEAPON_SPECIAL_DRAIN}
|
||||
[/set_specials]
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[object]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
[set_specials]
|
||||
mode=append
|
||||
[drains]
|
||||
id=drains
|
||||
name= _ "drains"
|
||||
description= _ "Reverse drains, gives the drain ability to the opponent."
|
||||
apply_to=opponent
|
||||
[/drains]
|
||||
[/set_specials]
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[object]
|
||||
[filter]
|
||||
id=dave
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=status
|
||||
add=undrainable
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[lua]
|
||||
code=<<
|
||||
local alice = wesnoth.units.find({id="alice"})[1]
|
||||
local bob = wesnoth.units.find({id="bob"})[1]
|
||||
local charlie = wesnoth.units.find({id="charlie"})[1]
|
||||
local dave = wesnoth.units.find({id="dave"})[1]
|
||||
|
||||
alice.hitpoints = 20
|
||||
bob.hitpoints = 20
|
||||
charlie.hitpoints = 20
|
||||
dave.hitpoints = 20
|
||||
|
||||
-- Everybody's an orcish grunt, and they're all on 60% terrain. The chance of {0,1,2} strikes hitting is:
|
||||
local hit_distribution = {}
|
||||
hit_distribution[0] = 0.6 ^ 2
|
||||
hit_distribution[1] = 0.4 * 0.6 + 0.6 * 0.4
|
||||
hit_distribution[2] = 0.4 ^ 2
|
||||
-- Strikes do 9 damage each, and therefore drain heals 4 hp. Starting with 20 hp, each combination of getting
|
||||
-- hit i times and healing j times leads to a unique amount of hp after the fight.
|
||||
local expected_no_drain = {}
|
||||
local expected_with_drain = {}
|
||||
for i, i_chance in pairs(hit_distribution) do
|
||||
expected_no_drain[20 - 9 * i] = i_chance
|
||||
for j, j_chance in pairs(hit_distribution) do
|
||||
expected_with_drain[20 - 9 * i + 4 * j] = i_chance * j_chance
|
||||
end
|
||||
end
|
||||
|
||||
function check_results(stats, expectation, log_message)
|
||||
for i,i_chance in pairs(expectation) do
|
||||
unit_test.assert_approx_equal(stats[i], i_chance, 0.001, log_message .. " expectation for " .. i .. " hp")
|
||||
end
|
||||
end
|
||||
|
||||
-- Alice has drain, Bob has reflexive drain, Dave is undrainable
|
||||
local att_stats, def_stats = wesnoth.simulate_combat(alice, bob)
|
||||
check_results(att_stats.hp_chance, expected_with_drain, "Alice v Bob att_stats")
|
||||
check_results(def_stats.hp_chance, expected_no_drain, "Alice v Bob def_stats")
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, charlie)
|
||||
check_results(att_stats.hp_chance, expected_with_drain, "Alice v Charlie att_stats")
|
||||
check_results(def_stats.hp_chance, expected_no_drain, "Alice v Charlie def_stats")
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, dave)
|
||||
check_results(att_stats.hp_chance, expected_no_drain, "Alice v Dave att_stats")
|
||||
check_results(def_stats.hp_chance, expected_no_drain, "Alice v Dave def_stats")
|
||||
att_stats, def_stats = wesnoth.simulate_combat(bob, charlie)
|
||||
check_results(att_stats.hp_chance, expected_no_drain, "Bob v Charlie att_stats")
|
||||
check_results(def_stats.hp_chance, expected_with_drain, "Bob v Charlie def_stats")
|
||||
att_stats, def_stats = wesnoth.simulate_combat(bob, dave)
|
||||
check_results(att_stats.hp_chance, expected_no_drain, "Bob v Dave att_stats")
|
||||
check_results(def_stats.hp_chance, expected_with_drain, "Bob v Dave def_stats")
|
||||
>>
|
||||
[/lua]
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
[/test]
|
77
data/test/scenarios/reflexive_poison.cfg
Normal file
77
data/test/scenarios/reflexive_poison.cfg
Normal file
|
@ -0,0 +1,77 @@
|
|||
#textdomain wesnoth-test
|
||||
|
||||
#####
|
||||
# API(s) being tested: [poison]apply_to=opponent
|
||||
##
|
||||
# Actions:
|
||||
# This uses a "common keep" map, so Alice and Bob are already in position to attack.
|
||||
# Give Alice a weapon that does reverse-poison (Alice gets poisoned if Bob hits her).
|
||||
# Simulate various combats using melee weapons.
|
||||
##
|
||||
# Expected end state:
|
||||
# Normal combat can poison Alice.
|
||||
# Making Alice unpoisonable works.
|
||||
# Making Bob unpoisonable does not affect Alice.
|
||||
# Making Bob poisoned before combat starts does not affect Alice.
|
||||
#####
|
||||
{COMMON_KEEP_A_B_UNIT_TEST reflexive_poison (
|
||||
[event]
|
||||
name=start
|
||||
|
||||
[object]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
[set_specials]
|
||||
mode=append
|
||||
[poison]
|
||||
id="reflexive_poison"
|
||||
name=_ "reflexive_poison"
|
||||
description=_ "When Alice attacks, her opponent’s weapons get the <i>poison</i> special."
|
||||
apply_to=opponent
|
||||
[/poison]
|
||||
[/set_specials]
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[lua]
|
||||
code=<<
|
||||
local alice = wesnoth.units.find({id="alice"})[1]
|
||||
local bob = wesnoth.units.find({id="bob"})[1]
|
||||
|
||||
-- Alice attacks with her sword, so Bob can counterattack. They're both on keeps, so 60% defense, and Bob gets 2 swings.
|
||||
local expected_chance = 1.0 - 0.6 ^ 2
|
||||
|
||||
-- Test that the weapon special works, before adding any complications about status conditions
|
||||
local att_stats, def_stats = wesnoth.simulate_combat(alice, 1, bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.poisoned, expected_chance, 0.01, "Alice should be at risk of poisoning")
|
||||
|
||||
-- Test the "unpoisonable" status on Alice (this works correctly, even in 1.16.x)
|
||||
local immune_alice = alice:clone()
|
||||
immune_alice.status.unpoisonable = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(immune_alice, 1, bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.poisoned, 0.0, 0.01, "Immune Alice should be unpoisonable")
|
||||
|
||||
-- Test the "unpoisonable" status on Bob
|
||||
local immune_bob = bob:clone()
|
||||
immune_bob.status.unpoisonable = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, 1, immune_bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.poisoned, expected_chance, 0.01, "Alice should be at risk of poisoning when attacking Immune Bob")
|
||||
|
||||
-- Test that Bob already being poisoned before combat starts doesn't affect the stats
|
||||
local affected_bob = bob:clone()
|
||||
affected_bob.status.poisoned = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, 1, affected_bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.poisoned, expected_chance, 0.01, "Alice should be at risk of poisoning when attacking Affected Bob")
|
||||
>>
|
||||
[/lua]
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
77
data/test/scenarios/reflexive_slow.cfg
Normal file
77
data/test/scenarios/reflexive_slow.cfg
Normal file
|
@ -0,0 +1,77 @@
|
|||
#textdomain wesnoth-test
|
||||
|
||||
#####
|
||||
# API(s) being tested: [slow]apply_to=opponent
|
||||
##
|
||||
# Actions:
|
||||
# This uses a "common keep" map, so Alice and Bob are already in position to attack.
|
||||
# Give Alice a weapon that does reverse-slow (Alice gets slowed if Bob hits her).
|
||||
# Simulate various combats using melee weapons.
|
||||
##
|
||||
# Expected end state:
|
||||
# Normal combat can slow Alice.
|
||||
# Making Alice unslowable works.
|
||||
# Making Bob unslowable does not affect Alice, except for the damage taken.
|
||||
# Making Bob slowed before combat starts does not affect Alice, except for the damage taken.
|
||||
#####
|
||||
{COMMON_KEEP_A_B_UNIT_TEST reflexive_slow (
|
||||
[event]
|
||||
name=start
|
||||
|
||||
[object]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
[set_specials]
|
||||
mode=append
|
||||
[slow]
|
||||
id="reflexive_slow"
|
||||
name=_ "reflexive_slow"
|
||||
description=_ "When Alice attacks, her opponent’s weapons get the <i>slow</i> special."
|
||||
apply_to=opponent
|
||||
[/slow]
|
||||
[/set_specials]
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[lua]
|
||||
code=<<
|
||||
local alice = wesnoth.units.find({id="alice"})[1]
|
||||
local bob = wesnoth.units.find({id="bob"})[1]
|
||||
|
||||
-- Alice attacks with her sword, so Bob can counterattack. They're both on keeps, so 60% defense, and Bob gets 2 swings.
|
||||
local expected_chance = 1.0 - 0.6 ^ 2
|
||||
|
||||
-- Test that the weapon special works, before adding any complications about status conditions
|
||||
local att_stats, def_stats = wesnoth.simulate_combat(alice, 1, bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.slowed, expected_chance, 0.01, "Alice should be at risk of slowing")
|
||||
|
||||
-- Test the "unslowable" status on Alice (this works correctly, even in 1.16.x)
|
||||
local immune_alice = alice:clone()
|
||||
immune_alice.status.unslowable = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(immune_alice, 1, bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.slowed, 0.0, 0.01, "Immune Alice should be unslowable")
|
||||
|
||||
-- Test the "unslowable" status on Bob
|
||||
local immune_bob = bob:clone()
|
||||
immune_bob.status.unslowable = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, 1, immune_bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.slowed, expected_chance, 0.01, "Alice should be at risk of slowing when attacking Immune Bob")
|
||||
|
||||
-- Test that Bob already being slowed before combat starts doesn't affect the stats, other than damage taken
|
||||
local affected_bob = bob:clone()
|
||||
affected_bob.status.slowed = true
|
||||
att_stats, def_stats = wesnoth.simulate_combat(alice, 1, affected_bob)
|
||||
unit_test.assert_approx_equal(att_stats.untouched, 1.0 - expected_chance, 0.01, "Test setup failed - Alice should be at risk of getting hit")
|
||||
unit_test.assert_approx_equal(att_stats.slowed, expected_chance, 0.01, "Alice should be at risk of slowing when attacking Affected Bob")
|
||||
>>
|
||||
[/lua]
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
|
@ -261,6 +261,9 @@
|
|||
0 backstab_inactive_with_triangular_formation
|
||||
0 backstab_inactive_with_statue_behind_bob
|
||||
0 backstab_inactive_with_bobs_ally_behind_bob
|
||||
0 reflexive_drains
|
||||
0 reflexive_poison
|
||||
0 reflexive_slow
|
||||
0 swarm_disables_upgrades
|
||||
0 swarm_disables_upgrades_with_abilities
|
||||
0 swarm_disables_upgrades_with_abilities_fail
|
||||
|
|
Loading…
Add table
Reference in a new issue