Extend special_id_active and special_type_active to account for abilities used as specials
This commit is contained in:
parent
f671d2a678
commit
05b2ea2262
19 changed files with 1357 additions and 33 deletions
|
@ -26,6 +26,8 @@
|
|||
* Added information about the build's (not runtime) target CPU architecture to the game version info dialog and --report.
|
||||
* Added terminal-style command history browsing with up-down keys for in-game consoles used by debug mode, ai and search floating textboxes.
|
||||
### WML Engine
|
||||
* Extent 'special_id_active' and 'special_type_active' to abilities used like weapon and to [leadership] abilities.
|
||||
* abilities used like weapon can call [leading_anim] now.
|
||||
### Miscellaneous and Bug Fixes
|
||||
* Fixed display zoom not being taken into account when using the `x`, `y`, `directional_x` and `directional_y` attributes in unit animations.
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
num_traits=0
|
||||
image="units/human-battleprincess-resting.png"
|
||||
{LEADING_ANIM "units/human-battleprincess-leading-1.png" "units/human-battleprincess-leading-2.png" 22,-22}
|
||||
{INITIATIVE_ANIM "units/human-battleprincess-leading-1.png" "units/human-battleprincess-leading-2.png"}
|
||||
hitpoints=62
|
||||
movement_type=smallfoot
|
||||
[resistance]
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
image="units/human-princess.png"
|
||||
{DEFENSE_ANIM "units/human-princess-defend-2.png" "units/human-princess-defend-1.png" {SOUND_LIST:HUMAN_FEMALE_HIT} }
|
||||
{LEADING_ANIM "units/human-princess-leading-2.png" "units/human-princess-leading-1.png" 22,-22}
|
||||
{INITIATIVE_ANIM "units/human-princess-leading-2.png" "units/human-princess-leading-1.png"}
|
||||
hitpoints=48
|
||||
movement_type=smallfoot
|
||||
[resistance]
|
||||
|
|
|
@ -15,6 +15,19 @@
|
|||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/firststrike]
|
||||
[firststrike]
|
||||
id=initiative_anim
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
active_on=defense
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
special_id_active=initiative
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/firststrike]
|
||||
#enddef
|
||||
|
||||
#define NOTE_INITIATIVE
|
||||
|
@ -22,3 +35,21 @@
|
|||
note=_"This unit’s grasp of melee tactics allows adjacent allies to strike the first blow even when defending."
|
||||
[/special_note]
|
||||
#enddef
|
||||
|
||||
#define INITIATIVE_ANIM FULL_IMAGE HALFWAYS_IMAGE
|
||||
[leading_anim]
|
||||
[filter_attack]
|
||||
special_id_active=initiative_anim
|
||||
[not]
|
||||
special_type_active=leadership
|
||||
[/not]
|
||||
[not]
|
||||
special_id_active=firststrike
|
||||
[/not]
|
||||
[/filter_attack]
|
||||
start_time=-126
|
||||
[frame]
|
||||
image={HALFWAYS_IMAGE}:1,{FULL_IMAGE}:250,{HALFWAYS_IMAGE}:1
|
||||
[/frame]
|
||||
[/leading_anim]
|
||||
#enddef
|
||||
|
|
|
@ -25,20 +25,43 @@
|
|||
# Define an animation of a unit waving/raising their weapon,
|
||||
# with a gleam of light reflecting off it at the point specified by
|
||||
# OFFSET_POSITION
|
||||
[leading_anim]
|
||||
start_time=-126
|
||||
[frame]
|
||||
image={HALFWAYS_IMAGE}:1,{FULL_IMAGE}:250,{HALFWAYS_IMAGE}:1
|
||||
[/frame]
|
||||
{LEADING_ANIM_FILTER {FULL_IMAGE} {HALFWAYS_IMAGE} {OFFSET_POSITION} special_type_active=leadership}
|
||||
#enddef
|
||||
|
||||
halo_start_time=-100
|
||||
[halo_frame]
|
||||
halo="halo/misc/leadership-flare-[1~13].png:20"
|
||||
halo_x,halo_y={OFFSET_POSITION}
|
||||
[/halo_frame]
|
||||
#define LEADING_ANIM_FILTER FULL_IMAGE HALFWAYS_IMAGE OFFSET_POSITION ATTACK
|
||||
# Define an animation of a unit waving/raising their weapon,
|
||||
# with a gleam of light reflecting off it at the point specified by
|
||||
# OFFSET_POSITION
|
||||
[leading_anim]
|
||||
[filter_attack]
|
||||
{ATTACK}
|
||||
[/filter_attack]
|
||||
{LEADING_BASE {FULL_IMAGE} {HALFWAYS_IMAGE} {OFFSET_POSITION}}
|
||||
[/leading_anim]
|
||||
#enddef
|
||||
|
||||
#define HELPING_ANIM FULL_IMAGE HALFWAYS_IMAGE OFFSET_POSITION
|
||||
# Define an animation of a unit waving/raising their weapon,
|
||||
# with a gleam of light reflecting off it at the point specified by
|
||||
# OFFSET_POSITION
|
||||
[resistance_anim]
|
||||
{LEADING_BASE {FULL_IMAGE} {HALFWAYS_IMAGE} {OFFSET_POSITION}}
|
||||
[/resistance_anim]
|
||||
#enddef
|
||||
|
||||
#define LEADING_BASE FULL_IMAGE HALFWAYS_IMAGE OFFSET_POSITION
|
||||
start_time=-126
|
||||
[frame]
|
||||
image={HALFWAYS_IMAGE}:1,{FULL_IMAGE}:250,{HALFWAYS_IMAGE}:1
|
||||
[/frame]
|
||||
|
||||
halo_start_time=-100
|
||||
[halo_frame]
|
||||
halo="halo/misc/leadership-flare-[1~13].png:20"
|
||||
halo_x,halo_y={OFFSET_POSITION}
|
||||
[/halo_frame]
|
||||
#enddef
|
||||
|
||||
#define DEFENSE_ANIM REACTION_IMAGE BASE_IMAGE HIT_SOUND
|
||||
# Define a defensive animation moving from a specified BASE_IMAGE
|
||||
# to REACTION_IMAGE, with HIT_SOUND playing only if a hit occurs.
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
|
@ -81,6 +84,7 @@
|
|||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
|
@ -155,3 +159,6 @@
|
|||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_BOB
|
||||
#undef FILTER_ALICE
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
# This unit test defines a WML object based implementation of the "unupgradable" ability
|
||||
# https://github.com/ProditorMagnus/Ageless-for-1-14/blob/52c1eaf31722bb58046a1b459d4f29daa8d88487/data/general_data/weapon_specials/unupgradable.cfg
|
||||
# and checks that it works. What is being tested here is that
|
||||
# [swarm] with swarm_attacks_max=swarm_attacks_min prevents strike changes
|
||||
# - through [attacks]
|
||||
# - through [effect] increase_attacks
|
||||
|
||||
{GENERIC_UNIT_TEST "swarm_disables_upgrades_with_abilities_adjacent" (
|
||||
#define FILTER_BOB
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=orc
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=12,4
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=ben
|
||||
name=_"Ben"
|
||||
x,y=14,3
|
||||
type=Orcish Warrior
|
||||
side=2
|
||||
[/unit]
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[swarm]
|
||||
swarm_attacks_max=1
|
||||
swarm_attacks_min=1
|
||||
{FILTER_BOB}
|
||||
[/swarm]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_BOB}
|
||||
[/attacks]
|
||||
[attacks]
|
||||
add=13
|
||||
{FILTER_BOB}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_BOB}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
value=100
|
||||
{FILTER_BOB}
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=ben
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_ALICE}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alex
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
increase_attacks=12
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=12,3
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 99})}
|
||||
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 90})}
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
|
@ -0,0 +1,181 @@
|
|||
# This unit test defines a WML object based implementation of the "unupgradable" ability
|
||||
# https://github.com/ProditorMagnus/Ageless-for-1-14/blob/52c1eaf31722bb58046a1b459d4f29daa8d88487/data/general_data/weapon_specials/unupgradable.cfg
|
||||
# and checks that it works. What is being tested here is that
|
||||
# [swarm] with swarm_attacks_max=swarm_attacks_min prevents strike changes
|
||||
# - through [attacks]
|
||||
# - through [effect] increase_attacks
|
||||
|
||||
{GENERIC_UNIT_TEST "swarm_disables_upgrades_with_abilities_adjacent_fail" (
|
||||
#define FILTER_BOB
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=orc
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=12,4
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=ben
|
||||
name=_"Ben"
|
||||
x,y=14,3
|
||||
type=Orcish Warrior
|
||||
side=2
|
||||
[/unit]
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[swarm]
|
||||
swarm_attacks_max=1
|
||||
swarm_attacks_min=1
|
||||
{FILTER_BOB}
|
||||
[/swarm]
|
||||
[attacks]
|
||||
value=10
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
value=100
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=ben
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_ALICE}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_fail_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alex
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=12,3
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 90})}
|
||||
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 90})}
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_BOB
|
||||
#undef FILTER_ALICE
|
|
@ -0,0 +1,220 @@
|
|||
# This unit test defines a WML object based implementation of the "unupgradable" ability
|
||||
# https://github.com/ProditorMagnus/Ageless-for-1-14/blob/52c1eaf31722bb58046a1b459d4f29daa8d88487/data/general_data/weapon_specials/unupgradable.cfg
|
||||
# and checks that it works. What is being tested here is that
|
||||
# [swarm] with swarm_attacks_max=swarm_attacks_min prevents strike changes
|
||||
# - through [attacks]
|
||||
# - through [effect] increase_attacks
|
||||
|
||||
{GENERIC_UNIT_TEST "swarm_disables_upgrades_with_abilities_adjacent_leadership" (
|
||||
#define FILTER_BOB
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[and]
|
||||
special_id_active=leader_test_bob
|
||||
[/and]
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=orc
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=12,4
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=ben
|
||||
name=_"Ben"
|
||||
x,y=14,3
|
||||
type=Orcish Warrior
|
||||
side=2
|
||||
[/unit]
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[swarm]
|
||||
swarm_attacks_max=1
|
||||
swarm_attacks_min=1
|
||||
{FILTER_BOB}
|
||||
[/swarm]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_BOB}
|
||||
[/attacks]
|
||||
[attacks]
|
||||
add=13
|
||||
{FILTER_BOB}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_BOB}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
value=100
|
||||
{FILTER_BOB}
|
||||
[/chance_to_hit]
|
||||
[leadership]
|
||||
id=leader_test_bob
|
||||
value=100
|
||||
cumulative=no
|
||||
affect_self=no
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[/filter_weapon]
|
||||
[filter_second_weapon]
|
||||
special_id_active=test_cth
|
||||
[/filter_second_weapon]
|
||||
[affect_adjacent]
|
||||
[filter]
|
||||
formula="level < other.level"
|
||||
[/filter]
|
||||
[/affect_adjacent]
|
||||
[/leadership]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=ben
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_ALICE}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
[leadership]
|
||||
id=leader_test_alice
|
||||
value=100
|
||||
cumulative=no
|
||||
affect_self=no
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[affect_adjacent]
|
||||
[filter]
|
||||
formula="level < other.level"
|
||||
[/filter]
|
||||
[/affect_adjacent]
|
||||
[/leadership]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alex
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
[effect]
|
||||
apply_to=attack
|
||||
increase_attacks=12
|
||||
[/effect]
|
||||
[/object]
|
||||
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=12,3
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 98})}
|
||||
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 80})}
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_BOB
|
||||
#undef FILTER_ALICE
|
|
@ -0,0 +1,201 @@
|
|||
# This unit test defines a WML object based implementation of the "unupgradable" ability
|
||||
# https://github.com/ProditorMagnus/Ageless-for-1-14/blob/52c1eaf31722bb58046a1b459d4f29daa8d88487/data/general_data/weapon_specials/unupgradable.cfg
|
||||
# and checks that it works. What is being tested here is that
|
||||
# [swarm] with swarm_attacks_max=swarm_attacks_min prevents strike changes
|
||||
# - through [attacks]
|
||||
# - through [effect] increase_attacks
|
||||
|
||||
{GENERIC_UNIT_TEST "swarm_disables_upgrades_with_abilities_adjacent_leadership_fail" (
|
||||
#define FILTER_BOB
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[and]
|
||||
special_id_active=leader_test_bob
|
||||
[/and]
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=orc
|
||||
[/filter_opponent]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
#enddef
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=12,4
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=ben
|
||||
name=_"Ben"
|
||||
x,y=14,3
|
||||
type=Orcish Warrior
|
||||
side=2
|
||||
[/unit]
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[swarm]
|
||||
swarm_attacks_max=1
|
||||
swarm_attacks_min=1
|
||||
{FILTER_BOB}
|
||||
[/swarm]
|
||||
[attacks]
|
||||
value=10
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
value=100
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_adjacent]
|
||||
[/affect_adjacent]
|
||||
[/chance_to_hit]
|
||||
[leadership]
|
||||
id=leader_test_fail_bob
|
||||
value=100
|
||||
cumulative=no
|
||||
affect_self=no
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[/filter_weapon]
|
||||
[filter_second_weapon]
|
||||
special_id_active=test_cth
|
||||
[/filter_second_weapon]
|
||||
[affect_adjacent]
|
||||
[filter]
|
||||
formula="level < other.level"
|
||||
[/filter]
|
||||
[/affect_adjacent]
|
||||
[/leadership]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=ben
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_ALICE}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alex
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=12,3
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 80})}
|
||||
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 90})}
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_BOB
|
||||
#undef FILTER_ALICE
|
|
@ -0,0 +1,147 @@
|
|||
# This unit test defines a WML object based implementation of the "unupgradable" ability
|
||||
# https://github.com/ProditorMagnus/Ageless-for-1-14/blob/52c1eaf31722bb58046a1b459d4f29daa8d88487/data/general_data/weapon_specials/unupgradable.cfg
|
||||
# and checks that it works. What is being tested here is that
|
||||
# [swarm] with swarm_attacks_max=swarm_attacks_min prevents strike changes
|
||||
# - through [attacks]
|
||||
# - through [effect] increase_attacks
|
||||
|
||||
{GENERIC_UNIT_TEST "swarm_disables_upgrades_with_abilities_fail" (
|
||||
#define FILTER_BOB
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=elf
|
||||
[filter_weapon]
|
||||
special_id_active=test_cth #for testing if special_id_active work with abilities used like weapons
|
||||
[/filter_weapon]
|
||||
[/filter_opponent]
|
||||
#enddef
|
||||
#define FILTER_ALICE
|
||||
[filter_student]
|
||||
[filter_weapon]
|
||||
type=blade,pierce
|
||||
[/filter_weapon]
|
||||
[/filter_student]
|
||||
[filter_opponent]
|
||||
race=orc
|
||||
[/filter_opponent]
|
||||
#enddef
|
||||
[event]
|
||||
name=start
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[swarm]
|
||||
swarm_attacks_max=1
|
||||
swarm_attacks_min=1
|
||||
{FILTER_BOB}
|
||||
[/swarm]
|
||||
[attacks]
|
||||
value=10
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
value=100
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[attacks]
|
||||
value=10
|
||||
{FILTER_ALICE}
|
||||
[/attacks]
|
||||
[damage]
|
||||
value=1
|
||||
{FILTER_ALICE}
|
||||
[/damage]
|
||||
[chance_to_hit]
|
||||
id=test_fail_cth
|
||||
value=100
|
||||
{FILTER_ALICE}
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=$b.x,$b.y
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 90})}
|
||||
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 90})}
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_BOB
|
||||
#undef FILTER_ALICE
|
|
@ -1102,7 +1102,7 @@ bool attack::perform_hit(bool attacker_turn, statistics::attack_context& stats)
|
|||
damage,
|
||||
*attacker_stats->weapon, defender_stats->weapon,
|
||||
abs_n, float_text.str(), drains_damage, "",
|
||||
&extra_hit_sounds
|
||||
&extra_hit_sounds, attacker_turn
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1171,14 +1171,197 @@ unit_ability_list attack_type::get_special_ability(const std::string& ability) c
|
|||
return abil_list;
|
||||
}
|
||||
|
||||
bool attack_type::bool_ability(const std::string& ability) const
|
||||
{
|
||||
bool abil_bool = get_special_bool(ability);
|
||||
unit_ability_list abil = list_ability(ability);
|
||||
if(!abil.empty()) {
|
||||
abil_bool = true;
|
||||
/**
|
||||
* Gets the children of parent (which should be the abilities for an
|
||||
* attack_type) and places the ones whose tag or id= matches @a id into
|
||||
* @a tag_result and @a id_result.
|
||||
* @param tag_result receive the children whose tag matches @a id
|
||||
* @param id_result receive the children whose id matches @a id
|
||||
* @param parent the tags whose contain children (abilities here)
|
||||
* @param id tag or id of child tested
|
||||
* @param special_id if true, children check by id
|
||||
* @param special_tags if true, children check by tags
|
||||
*/
|
||||
static void get_ability_children(std::vector<special_match>& tag_result,
|
||||
std::vector<special_match>& id_result,
|
||||
const config& parent, const std::string& id,
|
||||
bool special_id=true, bool special_tags=true) {
|
||||
if(special_id && special_tags){
|
||||
get_special_children(tag_result, id_result, parent, id);
|
||||
} else if(special_id && !special_tags){
|
||||
get_special_children_id(id_result, parent, id);
|
||||
} else if(!special_id && special_tags){
|
||||
get_special_children_tags(tag_result, parent, id);
|
||||
}
|
||||
return abil_bool;
|
||||
}
|
||||
|
||||
bool unit::get_self_ability_bool(const config& special, const std::string& tag_name, const map_location& loc) const
|
||||
{
|
||||
return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
|
||||
}
|
||||
|
||||
bool unit::get_adj_ability_bool(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from) const
|
||||
{
|
||||
const auto adjacent = get_adjacent_tiles(loc);
|
||||
return (affects_side(special, side(), from.side()) && from.ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
|
||||
}
|
||||
|
||||
bool unit::get_self_ability_bool_weapon(const config& special, const std::string& tag_name, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
|
||||
{
|
||||
return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
|
||||
}
|
||||
|
||||
bool unit::get_adj_ability_bool_weapon(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
|
||||
{
|
||||
return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
|
||||
}
|
||||
|
||||
bool attack_type::check_self_abilities(const config& cfg, const std::string& special) const
|
||||
{
|
||||
return check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, special, true);
|
||||
}
|
||||
|
||||
bool attack_type::check_self_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config& special, unit_const_ptr u, const map_location& loc, AFFECTS whom, const std::string& tag_name, bool leader_bool)
|
||||
{
|
||||
if(tag_name == "leadership" && leader_bool){
|
||||
if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if((*u).checking_tags().count(tag_name) != 0){
|
||||
if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true, "filter_student")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool attack_type::check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const
|
||||
{
|
||||
return check_adj_abilities_impl(shared_from_this(), other_attack_, cfg, self_, from, dir, self_loc_, AFFECT_SELF, special, true);
|
||||
}
|
||||
|
||||
bool attack_type::check_adj_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config& special, unit_const_ptr u, const unit& from, int dir, const map_location& loc, AFFECTS whom, const std::string& tag_name, bool leader_bool)
|
||||
{
|
||||
if(tag_name == "leadership" && leader_bool){
|
||||
if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if((*u).checking_tags().count(tag_name) != 0){
|
||||
if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true, "filter_student")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns whether or not @a *this has a special ability with a tag or id equal to
|
||||
* @a special. the Check is for a special ability
|
||||
* active in the current context (see set_specials_context), including
|
||||
* specials obtained from the opponent's attack.
|
||||
*/
|
||||
bool attack_type::get_special_ability_bool(const std::string& special, bool special_id, bool special_tags) const
|
||||
{
|
||||
assert(display::get_singleton());
|
||||
const unit_map& units = display::get_singleton()->get_units();
|
||||
if(self_){
|
||||
std::vector<special_match> special_tag_matches;
|
||||
std::vector<special_match> special_id_matches;
|
||||
get_ability_children(special_tag_matches, special_id_matches, (*self_).abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches) {
|
||||
if(check_self_abilities(*entry.cfg, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches) {
|
||||
if(check_self_abilities(*entry.cfg, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto adjacent = get_adjacent_tiles(self_loc_);
|
||||
for(unsigned i = 0; i < adjacent.size(); ++i) {
|
||||
const unit_map::const_iterator it = units.find(adjacent[i]);
|
||||
if (it == units.end() || it->incapacitated())
|
||||
continue;
|
||||
if ( &*it == self_.get() )
|
||||
continue;
|
||||
|
||||
get_ability_children(special_tag_matches, special_id_matches, it->abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches) {
|
||||
if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches) {
|
||||
if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(other_){
|
||||
std::vector<special_match> special_tag_matches;
|
||||
std::vector<special_match> special_id_matches;
|
||||
get_ability_children(special_tag_matches, special_id_matches, (*other_).abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches) {
|
||||
if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches) {
|
||||
if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto adjacent = get_adjacent_tiles(other_loc_);
|
||||
for(unsigned i = 0; i < adjacent.size(); ++i) {
|
||||
const unit_map::const_iterator it = units.find(adjacent[i]);
|
||||
if (it == units.end() || it->incapacitated())
|
||||
continue;
|
||||
if ( &*it == other_.get() )
|
||||
continue;
|
||||
|
||||
get_ability_children(special_tag_matches, special_id_matches, it->abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches) {
|
||||
if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches) {
|
||||
if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool attack_type::bool_ability(const std::string& special, bool special_id, bool special_tags) const
|
||||
{
|
||||
return (get_special_bool(special, false, special_id, special_tags) || get_special_ability_bool(special, special_id, special_tags));
|
||||
}
|
||||
//end of emulate weapon special functions.
|
||||
|
||||
|
@ -1273,7 +1456,7 @@ bool attack_type::special_active_impl(const_attack_ptr self_attack, const_attack
|
|||
return false;
|
||||
}
|
||||
if (tag_name == "firststrike" && !is_attacker && other_attack &&
|
||||
other_attack->get_special_bool("firststrike", false)) {
|
||||
other_attack->bool_ability("firststrike")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
if(!filter_special_id_active.empty()) {
|
||||
bool found = false;
|
||||
for(auto& special : filter_special_id_active) {
|
||||
if(attack.get_special_bool(special, false, true, false)) {
|
||||
if(attack.bool_ability(special, true, false)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
if(!filter_special_type_active.empty()) {
|
||||
bool found = false;
|
||||
for(auto& special : filter_special_type_active) {
|
||||
if(attack.get_special_bool(special, false, false)) {
|
||||
if(attack.bool_ability(special, false)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -92,8 +92,20 @@ public:
|
|||
unit_ability_list list_ability(const std::string& ability) const;
|
||||
/** Returns list who contains list_ability and get_specials list for each ability type */
|
||||
unit_ability_list get_special_ability(const std::string& ability) const;
|
||||
/** return an boolean value for abilities like poison slow firstrike or petrifies */
|
||||
bool bool_ability(const std::string& ability) const;
|
||||
/** used for abilities used like weapon
|
||||
* @return True if the ability @a special is active.
|
||||
* @param special The special being checked.
|
||||
* @param special_id If true, match @a special against the @c id of special tags.
|
||||
* @param special_tags If true, match @a special against the tag name of special tags.
|
||||
*/
|
||||
bool get_special_ability_bool(const std::string& special, bool special_id=true, bool special_tags=true) const;
|
||||
/** used for abilities used like weapon and true specials
|
||||
* @return True if the ability @a special is active.
|
||||
* @param special The special being checked.
|
||||
* @param special_id If true, match @a special against the @c id of special tags.
|
||||
* @param special_tags If true, match @a special against the tag name of special tags.
|
||||
*/
|
||||
bool bool_ability(const std::string& special, bool special_id=true, bool special_tags=true) const;
|
||||
|
||||
// In unit_types.cpp:
|
||||
|
||||
|
@ -113,9 +125,72 @@ private:
|
|||
|
||||
// Configured as a bit field, in case that is useful.
|
||||
enum AFFECTS { AFFECT_SELF=1, AFFECT_OTHER=2, AFFECT_EITHER=3 };
|
||||
/** check_self_abilities : return an boolean value for checking of activities of abilities used like weapon
|
||||
* @return True if the special @a special is active.
|
||||
* @param cfg the config to one special ability checked.
|
||||
* @param special The special ability type who is being checked.
|
||||
*/
|
||||
bool check_self_abilities(const config& cfg, const std::string& special) const;
|
||||
/** check_adj_abilities : return an boolean value for checking of activities of abilities used like weapon
|
||||
* @return True if the special @a special is active.
|
||||
* @param cfg the config to one special ability checked.
|
||||
* @param special The special ability type who is being checked.
|
||||
* @param dir direction to research a unit adjacent to self_.
|
||||
* @param from unit adjacent to self_ is checked.
|
||||
*/
|
||||
bool check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const;
|
||||
bool special_active(const config& special, AFFECTS whom, const std::string& tag_name,
|
||||
bool include_backstab=true, const std::string& filter_self ="filter_self") const;
|
||||
|
||||
/** check_self_abilities_impl : return an boolean value for checking of activities of abilities used like weapon
|
||||
* @return True if the special @a tag_name is active.
|
||||
* @param self_attack the attack used by unit checked in this function.
|
||||
* @param other_attack the attack used by opponent to unit checked.
|
||||
* @param special the config to one special ability checked.
|
||||
* @param u the unit checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param whom determine if unit affected or not by special ability.
|
||||
* @param tag_name The special ability type who is being checked.
|
||||
* @param leader_bool If true, [leadership] abilities are checked.
|
||||
*/
|
||||
static bool check_self_abilities_impl(
|
||||
const_attack_ptr self_attack,
|
||||
const_attack_ptr other_attack,
|
||||
const config& special,
|
||||
unit_const_ptr u,
|
||||
const map_location& loc,
|
||||
AFFECTS whom,
|
||||
const std::string& tag_name,
|
||||
bool leader_bool=false
|
||||
);
|
||||
|
||||
|
||||
/** check_adj_abilities_impl : return an boolean value for checking of activities of abilities used like weapon in unit adjacent to fighter
|
||||
* @return True if the special @a tag_name is active.
|
||||
* @param self_attack the attack used by unit who fight.
|
||||
* @param other_attack the attack used by opponent.
|
||||
* @param special the config to one special ability checked.
|
||||
* @param u the unit who is or not affected by an abilities owned by @a from.
|
||||
* @param from unit adjacent to @a u is checked.
|
||||
* @param dir direction to research a unit adjacent to @a u.
|
||||
* @param loc location of the unit checked.
|
||||
* @param whom determine if unit affected or not by special ability.
|
||||
* @param tag_name The special ability type who is being checked.
|
||||
* @param leader_bool If true, [leadership] abilities are checked.
|
||||
*/
|
||||
static bool check_adj_abilities_impl(
|
||||
const_attack_ptr self_attack,
|
||||
const_attack_ptr other_attack,
|
||||
const config& special,
|
||||
unit_const_ptr u,
|
||||
const unit& from,
|
||||
int dir,
|
||||
const map_location& loc,
|
||||
AFFECTS whom,
|
||||
const std::string& tag_name,
|
||||
bool leader_bool=false
|
||||
);
|
||||
|
||||
static bool special_active_impl(
|
||||
const_attack_ptr self_attack,
|
||||
const_attack_ptr other_attack,
|
||||
|
|
|
@ -599,7 +599,8 @@ void unit_die(const map_location& loc, unit& loser,
|
|||
void unit_attack(display * disp, game_board & board,
|
||||
const map_location& a, const map_location& b, int damage,
|
||||
const attack_type& attack, const_attack_ptr secondary_attack,
|
||||
int swing,const std::string& hit_text,int drain_amount,const std::string& att_text, const std::vector<std::string>* extra_hit_sounds)
|
||||
int swing,const std::string& hit_text,int drain_amount,const std::string& att_text, const std::vector<std::string>* extra_hit_sounds,
|
||||
bool attacking)
|
||||
{
|
||||
if(do_not_show_anims(disp) || (disp->fogged(a) && disp->fogged(b)) || !preferences::show_combat()) {
|
||||
return;
|
||||
|
@ -620,6 +621,13 @@ void unit_attack(display * disp, game_board & board,
|
|||
assert(def.valid());
|
||||
unit &defender = *def;
|
||||
int def_hitpoints = defender.hitpoints();
|
||||
const_attack_ptr weapon = attack.shared_from_this();
|
||||
auto ctx = weapon->specials_context(attacker.shared_from_this(), defender.shared_from_this(), a, b, attacking, secondary_attack);
|
||||
std::optional<decltype(ctx)> opp_ctx;
|
||||
|
||||
if(secondary_attack) {
|
||||
opp_ctx.emplace(secondary_attack->specials_context(defender.shared_from_this(), attacker.shared_from_this(), b, a, !attacking, weapon));
|
||||
}
|
||||
|
||||
att->set_facing(a.get_relative_dir(b));
|
||||
def->set_facing(b.get_relative_dir(a));
|
||||
|
@ -640,16 +648,20 @@ void unit_attack(display * disp, game_board & board,
|
|||
unit_animator animator;
|
||||
|
||||
animator.add_animation(attacker.shared_from_this(), "attack", att->get_location(), def->get_location(), damage, true, text_2,
|
||||
(drain_amount >= 0) ? color_t(0, 255, 0) : color_t(255, 0, 0), hit_type, attack.shared_from_this(),
|
||||
(drain_amount >= 0) ? color_t(0, 255, 0) : color_t(255, 0, 0), hit_type, weapon,
|
||||
secondary_attack, swing);
|
||||
|
||||
// note that we take an anim from the real unit, we'll use it later
|
||||
const unit_animation* defender_anim = def->anim_comp().choose_animation(*disp, def->get_location(), "defend",
|
||||
att->get_location(), damage, hit_type, attack.shared_from_this(), secondary_attack, swing);
|
||||
att->get_location(), damage, hit_type, weapon, secondary_attack, swing);
|
||||
|
||||
animator.add_animation(defender.shared_from_this(), defender_anim, def->get_location(), true, text, {255, 0, 0});
|
||||
|
||||
for(const unit_ability& ability : attacker.get_abilities_weapons("leadership", attack.shared_from_this(), secondary_attack)) {
|
||||
unit_ability_list abilities = attacker.get_abilities_weapons("leadership", weapon, secondary_attack);
|
||||
for(auto& special : attacker.checking_tags()) {
|
||||
abilities.append(weapon->list_ability(special));
|
||||
}
|
||||
for(const unit_ability& ability : abilities) {
|
||||
if(ability.teacher_loc == a) {
|
||||
continue;
|
||||
}
|
||||
|
@ -663,10 +675,10 @@ void unit_attack(display * disp, game_board & board,
|
|||
leader->set_facing(ability.teacher_loc.get_relative_dir(a));
|
||||
animator.add_animation(leader.get_shared_ptr(), "leading", ability.teacher_loc,
|
||||
att->get_location(), damage, true, "", {0,0,0},
|
||||
hit_type, attack.shared_from_this(), secondary_attack, swing);
|
||||
hit_type, weapon, secondary_attack, swing);
|
||||
}
|
||||
|
||||
for(const unit_ability& ability : defender.get_abilities_weapons("resistance", secondary_attack, attack.shared_from_this())) {
|
||||
for(const unit_ability& ability : defender.get_abilities_weapons("resistance", secondary_attack, weapon)) {
|
||||
if(ability.teacher_loc == a) {
|
||||
continue;
|
||||
}
|
||||
|
@ -680,7 +692,7 @@ void unit_attack(display * disp, game_board & board,
|
|||
helper->set_facing(ability.teacher_loc.get_relative_dir(b));
|
||||
animator.add_animation(helper.get_shared_ptr(), "resistance", ability.teacher_loc,
|
||||
def->get_location(), damage, true, "", {0,0,0},
|
||||
hit_type, attack.shared_from_this(), secondary_attack, swing);
|
||||
hit_type, weapon, secondary_attack, swing);
|
||||
}
|
||||
|
||||
|
||||
|
@ -717,7 +729,11 @@ void reset_helpers(const unit *attacker,const unit *defender)
|
|||
display* disp = display::get_singleton();
|
||||
const unit_map& units = disp->get_units();
|
||||
if(attacker) {
|
||||
for(const unit_ability& ability : attacker->get_abilities("leadership")) {
|
||||
unit_ability_list attacker_abilities = attacker->get_abilities("leadership");
|
||||
for(auto& special : attacker->checking_tags()) {
|
||||
attacker_abilities.append(attacker->get_abilities(special));
|
||||
}
|
||||
for(const unit_ability& ability : attacker_abilities) {
|
||||
unit_map::const_iterator leader = units.find(ability.teacher_loc);
|
||||
assert(leader != units.end());
|
||||
leader->anim_comp().set_standing();
|
||||
|
@ -725,7 +741,11 @@ void reset_helpers(const unit *attacker,const unit *defender)
|
|||
}
|
||||
|
||||
if(defender) {
|
||||
for(const unit_ability& ability : defender->get_abilities("resistance")) {
|
||||
unit_ability_list defender_abilities = defender->get_abilities("resistance");
|
||||
for(auto& special : defender->checking_tags()) {
|
||||
defender_abilities.append(defender->get_abilities(special));
|
||||
}
|
||||
for(const unit_ability& ability : defender_abilities) {
|
||||
unit_map::const_iterator helper = units.find(ability.teacher_loc);
|
||||
assert(helper != units.end());
|
||||
helper->anim_comp().set_standing();
|
||||
|
|
|
@ -119,7 +119,8 @@ void unit_sheath_weapon( const map_location& loc, unit_ptr u=unit_ptr(), const_a
|
|||
void unit_attack(display * disp, game_board & board, //TODO: Would be nice if this could be purely a display function and defer damage dealing to its caller
|
||||
const map_location& a, const map_location& b, int damage,
|
||||
const attack_type& attack, const_attack_ptr secondary_attack,
|
||||
int swing, const std::string& hit_text, int drain_amount, const std::string& att_text, const std::vector<std::string>* extra_hit_sounds=nullptr);
|
||||
int swing, const std::string& hit_text, int drain_amount, const std::string& att_text, const std::vector<std::string>* extra_hit_sounds=nullptr,
|
||||
bool attacking=true);
|
||||
|
||||
|
||||
void unit_recruited(const map_location& loc,
|
||||
|
|
|
@ -1649,6 +1649,43 @@ public:
|
|||
return get_ability_bool(tag_name, loc_);
|
||||
}
|
||||
|
||||
/** Checks whether this unit currently possesses a given ability used like weapon
|
||||
* @return True if the ability @a tag_name is active.
|
||||
* @param special the const config to one of abilities @a tag_name checked.
|
||||
* @param tag_name name of ability type checked.
|
||||
* @param loc location of the unit checked.
|
||||
*/
|
||||
bool get_self_ability_bool(const config& special, const std::string& tag_name, const map_location& loc) const;
|
||||
/** Checks whether this unit currently possesses a given ability of leadership type
|
||||
* @return True if the ability @a tag_name is active.
|
||||
* @param special the const config to one of abilities @a tag_name checked.
|
||||
* @param tag_name name of ability type checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param weapon the attack used by unit checked in this function.
|
||||
* @param opp_weapon the attack used by opponent to unit checked.
|
||||
*/
|
||||
bool get_self_ability_bool_weapon(const config& special, const std::string& tag_name, const map_location& loc, const_attack_ptr weapon = nullptr, const_attack_ptr opp_weapon = nullptr) const;
|
||||
/** Checks whether this unit is affected by a given ability used like weapon
|
||||
* @return True if the ability @a tag_name is active.
|
||||
* @param special the const config to one of abilities @a tag_name checked.
|
||||
* @param tag_name name of ability type checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param from unit adjacent to @a this is checked in case of [affect_adjacent] abilities.
|
||||
* @param dir direction to research a unit adjacent to @a this.
|
||||
*/
|
||||
bool get_adj_ability_bool(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from) const;
|
||||
/** Checks whether this unit is affected by a given ability of leadership type
|
||||
* @return True if the ability @a tag_name is active.
|
||||
* @param special the const config to one of abilities @a tag_name checked.
|
||||
* @param tag_name name of ability type checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param from unit adjacent to @a this is checked in case of [affect_adjacent] abilities.
|
||||
* @param dir direction to research a unit adjacent to @a this.
|
||||
* @param weapon the attack used by unit checked in this function.
|
||||
* @param opp_weapon the attack used by opponent to unit checked.
|
||||
*/
|
||||
bool get_adj_ability_bool_weapon(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon = nullptr) const;
|
||||
|
||||
/**
|
||||
* Gets the unit's active abilities of a particular type if it were on a specified location.
|
||||
* @param tag_name The type of ability to check for
|
||||
|
@ -1674,6 +1711,10 @@ public:
|
|||
return get_abilities_weapons(tag_name, loc_, weapon, opp_weapon);
|
||||
}
|
||||
|
||||
const config &abilities() const { return abilities_; }
|
||||
|
||||
const std::set<std::string>& checking_tags() const { return checking_tags_; };
|
||||
|
||||
/**
|
||||
* Gets the names and descriptions of this unit's abilities. Location-independent variant
|
||||
* with all abilities shown as active.
|
||||
|
@ -1723,6 +1764,8 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
|
||||
const std::set<std::string> checking_tags_{"damage", "chance_to_hit", "berserk", "swarm", "drains", "heal_on_hit", "plague", "slow", "petrifies", "firststrike", "poison"};
|
||||
/**
|
||||
* Check if an ability is active.
|
||||
* @param ability The type (tag name) of the ability
|
||||
|
|
|
@ -169,6 +169,11 @@
|
|||
0 feeding
|
||||
0 swarm_disables_upgrades
|
||||
0 swarm_disables_upgrades_with_abilities
|
||||
0 swarm_disables_upgrades_with_abilities_fail
|
||||
0 swarm_disables_upgrades_with_abilities_adjacent
|
||||
0 swarm_disables_upgrades_with_abilities_adjacent_fail
|
||||
0 swarm_disables_upgrades_with_abilities_adjacent_leadership
|
||||
0 swarm_disables_upgrades_with_abilities_adjacent_leadership_fail
|
||||
0 test_force_chance_to_hit_macro
|
||||
#
|
||||
# Deterministic unit facing tests
|
||||
|
|
Loading…
Add table
Reference in a new issue