mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-22 01:07:22 +00:00
Merge b0ebcce7621d3505a0dbd0ee742b555271826627 into 2d472aaa8f0905fc754e8fe0cda2171d302faf65
This commit is contained in:
commit
a68c0a8bca
2
changelog_entries/add_affect_distant.md
Normal file
2
changelog_entries/add_affect_distant.md
Normal file
@ -0,0 +1,2 @@
|
||||
### WML Engine
|
||||
* add a [affect_distant]radius= tag for affect units distant to owner of ability.
|
@ -128,109 +128,21 @@
|
||||
#enddef
|
||||
|
||||
#define ABILITY_SHADOW_VEIL
|
||||
[dummy]
|
||||
[hides]
|
||||
id=did_shadow_veil
|
||||
name= _ "shadow veil"
|
||||
name_inactive= _ "shadow veil"
|
||||
description= _ "Allied undead units within a 5 hex radius are hidden."
|
||||
description_inactive= _ "Allied undead units within a 5 hex radius are hidden."
|
||||
{DID_SHADOW_VEIL_HANDLER}
|
||||
[/dummy]
|
||||
#enddef
|
||||
|
||||
#define ABILITY_SHADOW_VEIL_HELPER SIDE
|
||||
[hides]
|
||||
id=did_shadow_veil_helper
|
||||
[filter]
|
||||
[filter_location]
|
||||
radius=5
|
||||
[filter]
|
||||
ability=did_shadow_veil
|
||||
[filter_side]
|
||||
[allied_with]
|
||||
side={SIDE}
|
||||
[/allied_with]
|
||||
[/filter_side]
|
||||
[/filter]
|
||||
[/filter_location]
|
||||
[/filter]
|
||||
[/hides]
|
||||
#enddef
|
||||
|
||||
#define DID_SHADOW_VEIL_HANDLER
|
||||
[event]
|
||||
id=did_shadow_veil_unit_placed
|
||||
name=unit placed
|
||||
first_time_only=no
|
||||
[filter]
|
||||
side=1
|
||||
race=undead
|
||||
[not]
|
||||
ability=did_shadow_veil_helper
|
||||
[/not]
|
||||
[/filter]
|
||||
[filter_condition]
|
||||
[have_unit]
|
||||
ability=did_shadow_veil
|
||||
side=1
|
||||
[/have_unit]
|
||||
[/filter_condition]
|
||||
[modify_unit]
|
||||
[filter]
|
||||
x,y=$x1,$y1
|
||||
[not]
|
||||
ability=did_shadow_veil_helper
|
||||
[or]
|
||||
ability=did_shadow_veil
|
||||
[/or]
|
||||
[/not]
|
||||
[/filter]
|
||||
[object]
|
||||
duration=scenario
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
{ABILITY_SHADOW_VEIL_HELPER $unit.side}
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[/object]
|
||||
[/modify_unit]
|
||||
[/event]
|
||||
[event]
|
||||
id=did_shadow_veil_malkeshar
|
||||
name=post advance
|
||||
first_time_only=yes
|
||||
[filter]
|
||||
type=Mal Keshar
|
||||
ability=did_shadow_veil
|
||||
[/filter]
|
||||
[modify_unit]
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
[affect_distant]
|
||||
radius=5
|
||||
[filter]
|
||||
race=undead
|
||||
[not]
|
||||
ability=did_shadow_veil_helper
|
||||
[or]
|
||||
ability=did_shadow_veil
|
||||
[/or]
|
||||
[/not]
|
||||
[filter_side]
|
||||
[allied_with]
|
||||
side=$unit.side
|
||||
[/allied_with]
|
||||
[/filter_side]
|
||||
[/filter]
|
||||
[object]
|
||||
take_only_once=no
|
||||
duration=scenario
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
{ABILITY_SHADOW_VEIL_HELPER $this_unit.side}
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[/object]
|
||||
[/modify_unit]
|
||||
[/event]
|
||||
[/affect_distant]
|
||||
[/hides]
|
||||
#enddef
|
||||
|
||||
#define WEAPON_SPECIAL_FROST_NOVA
|
||||
|
@ -40,6 +40,11 @@
|
||||
[/key]
|
||||
{FILTER_TAG "filter" unit ()}
|
||||
[/tag]
|
||||
[tag]
|
||||
name="affect_distant"
|
||||
{REQUIRED_KEY radius s_int}
|
||||
{FILTER_TAG "filter" unit ()}
|
||||
[/tag]
|
||||
{FILTER_TAG "filter_self" unit ()}
|
||||
{FILTER_TAG "filter_adjacent" adjacent ()}
|
||||
{FILTER_TAG "filter_adjacent_location" adjacent_location ()}
|
||||
|
74
data/test/macros/start_position_separate_keep_a_b_c_d.cfg
Normal file
74
data/test/macros/start_position_separate_keep_a_b_c_d.cfg
Normal file
@ -0,0 +1,74 @@
|
||||
#textdomain wesnoth-test
|
||||
|
||||
##
|
||||
# Starting state:
|
||||
#
|
||||
# Side 1 leader Alice (Orcish Grunt)
|
||||
# Side 2 leader Bob (Orcish Grunt)
|
||||
# Side 3 leader Charlie (Orcish Grunt)
|
||||
# Side 4 leader Dave (Orcish Grunt)
|
||||
#
|
||||
# None of the sides are allied.
|
||||
#
|
||||
# On the default map (used unless overridden with the MAP_FILE argument:
|
||||
# * All four leaders are on a single keep, with Alice and Bob already in position to attack any of the other units.
|
||||
# * There is no free castle hex to recruit onto.
|
||||
##
|
||||
#define SEPARATE_KEEP_A_B_C_D_UNIT_TEST NAME CONTENT
|
||||
|
||||
#arg SIDE_LEADER
|
||||
Orcish Grunt#endarg
|
||||
|
||||
#arg MAP_FILE
|
||||
test/maps/4p_separate_castles.map#endarg
|
||||
|
||||
[test]
|
||||
name=_ "Unit Test " + {NAME}
|
||||
map_file={MAP_FILE}
|
||||
turns=unlimited
|
||||
id={NAME}
|
||||
random_start_time=no
|
||||
is_unit_test=yes
|
||||
|
||||
{DAWN}
|
||||
|
||||
[side]
|
||||
side=1
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Alice"
|
||||
type = {SIDE_LEADER}
|
||||
id=alice
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=2
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Bob"
|
||||
type = {SIDE_LEADER}
|
||||
id=bob
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=3
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Charlie"
|
||||
type = {SIDE_LEADER}
|
||||
id=charlie
|
||||
[/leader]
|
||||
[/side]
|
||||
[side]
|
||||
side=4
|
||||
controller=human
|
||||
[leader]
|
||||
name = _ "Dave"
|
||||
type = {SIDE_LEADER}
|
||||
id=dave
|
||||
[/leader]
|
||||
[/side]
|
||||
|
||||
{CONTENT}
|
||||
[/test]
|
||||
#enddef
|
@ -0,0 +1,54 @@
|
||||
#textdomain wesnoth-test
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give charlie an ability specialX, which is affect alice if she in 10 hexes of distance of alex.
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX should affect alice.
|
||||
#####
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_active" (
|
||||
[event]
|
||||
name=start
|
||||
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[damage]
|
||||
id=specialX
|
||||
name=_ "specialX"
|
||||
description=_ "specialX is active if and only if one unit within 10 hex radius is alice"
|
||||
value=100
|
||||
apply_to=self
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[affect_distant]
|
||||
radius=10
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[/affect_distant]
|
||||
[/damage]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=charlie
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
{ASSERT (
|
||||
[have_unit]
|
||||
id=alice
|
||||
ability_id_active=specialX
|
||||
[/have_unit]
|
||||
)}
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
@ -0,0 +1,121 @@
|
||||
#textdomain wesnoth-test
|
||||
#define TEST_AFFECT_DISTANT_ACTIVE_WITH_SECOND_ABILITY FILTER
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=1,1
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=bobby
|
||||
name=_"Bobby"
|
||||
x,y=1,5
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=carlos
|
||||
name=_"Carlos"
|
||||
x,y=1,12
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[leadership]
|
||||
id=specialY
|
||||
name=_ "specialY"
|
||||
description=_ "specialY affect units within radius of 7"
|
||||
value=25
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[affect_distant]
|
||||
radius=7
|
||||
[/affect_distant]
|
||||
[/leadership]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=carlos
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[damage]
|
||||
id=specialX
|
||||
name=_ "specialX"
|
||||
description=_ "specialX is active if within radius of specialY ability"
|
||||
value=100
|
||||
apply_to=self
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[filter]
|
||||
{FILTER}
|
||||
[/filter]
|
||||
[affect_distant]
|
||||
radius=5
|
||||
[/affect_distant]
|
||||
[/damage]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=bobby
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
{ASSERT (
|
||||
[have_unit]
|
||||
id=alex
|
||||
ability_id_active=specialX
|
||||
[/have_unit]
|
||||
)}
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
#enddef
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give carlos a leadership ability, specialY, that affects all units in a 7 hex radius
|
||||
# Give bobby a damage ability, specialX, that affects all units in a 5 hex radius when affected by carlos' ability
|
||||
# Place alex within range of bobby's ability but out of range of carlos' ability
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX should be active and affect alex.
|
||||
#####
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_active_with_second_ability_type" (
|
||||
{TEST_AFFECT_DISTANT_ACTIVE_WITH_SECOND_ABILITY (ability_type_active=leadership)}
|
||||
)}
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give carlos a leadership ability, specialY, that affects all units in a 7 hex radius
|
||||
# Give bobby a damage ability, specialX, that affects all units in a 5 hex radius when affected by carlos' ability
|
||||
# Place alex within range of bobby's ability but out of range of carlos' ability
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX should be active and affect alex.
|
||||
#####
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_active_with_second_ability" (
|
||||
{TEST_AFFECT_DISTANT_ACTIVE_WITH_SECOND_ABILITY (ability_id_active=specialY)}
|
||||
)}
|
||||
|
||||
#undef TEST_AFFECT_DISTANT_ACTIVE_WITH_SECOND_ABILITY
|
@ -0,0 +1,57 @@
|
||||
#textdomain wesnoth-test
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give charlie an ability specialX, which is affect alice if she in 1 hexes of distance of alex.
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX shouln'd be affect alice.
|
||||
#####
|
||||
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_inactive" (
|
||||
[event]
|
||||
name=start
|
||||
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[damage]
|
||||
id=specialX
|
||||
name=_ "specialX"
|
||||
description=_ "specialX is active if and only if one unit within 1 hex radius is alice"
|
||||
value=100
|
||||
apply_to=self
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[affect_distant]
|
||||
radius=1
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[/affect_distant]
|
||||
[/damage]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=charlie
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
{ASSERT (
|
||||
[not]
|
||||
[have_unit]
|
||||
id=alice
|
||||
ability_id_active=specialX
|
||||
[/have_unit]
|
||||
[/not]
|
||||
)}
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
@ -0,0 +1,125 @@
|
||||
#textdomain wesnoth-test
|
||||
#define TEST_AFFECT_DISTANT_INACTIVE_WITH_SECOND_ABILITY FILTER
|
||||
[event]
|
||||
name=start
|
||||
[unit]
|
||||
id=alex
|
||||
name=_"Alex"
|
||||
x,y=1,1
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=bobby
|
||||
name=_"Bobby"
|
||||
x,y=1,5
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
[unit]
|
||||
id=carlos
|
||||
name=_"Carlos"
|
||||
x,y=1,12
|
||||
type=Elvish Hero
|
||||
side=1
|
||||
[/unit]
|
||||
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[leadership]
|
||||
id=specialY
|
||||
name=_ "specialY"
|
||||
description=_ "specialY affect units within radius of 5"
|
||||
value=25
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[affect_distant]
|
||||
radius=5
|
||||
[/affect_distant]
|
||||
[/leadership]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=carlos
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[damage]
|
||||
id=specialX
|
||||
name=_ "specialX"
|
||||
description=_ "specialX is active if within radius of specialY ability"
|
||||
value=100
|
||||
apply_to=self
|
||||
affect_self=no
|
||||
affect_allies=yes
|
||||
affect_enemies=yes
|
||||
[filter]
|
||||
{FILTER}
|
||||
[/filter]
|
||||
[affect_distant]
|
||||
radius=5
|
||||
[/affect_distant]
|
||||
[/damage]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=bobby
|
||||
[/filter]
|
||||
[/object]
|
||||
|
||||
{ASSERT (
|
||||
[not]
|
||||
[have_unit]
|
||||
id=alex
|
||||
ability_id_active=specialX
|
||||
[/have_unit]
|
||||
[/not]
|
||||
)}
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
#enddef
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give carlos a leadership ability, specialY, that affects all units in a 5 hex radius
|
||||
# Give bobby a damage ability, specialX, that affects all units in a 5 hex radius when affected by carlos' ability
|
||||
# Place alex within range of bobby's ability but out of range of carlos' ability
|
||||
# Place bobby out of range of carlos' ability
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX shouln'd be affect alex because inactive.
|
||||
#####
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_inactive_with_second_ability_type" (
|
||||
{TEST_AFFECT_DISTANT_INACTIVE_WITH_SECOND_ABILITY (ability_type_active=leadership)}
|
||||
)}
|
||||
|
||||
#####
|
||||
# API(s) being tested: ability[affect_distant]radius=
|
||||
##
|
||||
# Actions:
|
||||
# Give carlos a leadership ability, specialY, that affects all units in a 5 hex radius
|
||||
# Give bobby a damage ability, specialX, that affects all units in a 5 hex radius when affected by carlos' ability
|
||||
# Place alex within range of bobby's ability but out of range of carlos' ability
|
||||
# Place bobby out of range of carlos' ability
|
||||
# Test whether the ability is active.
|
||||
##
|
||||
# Expected end state:
|
||||
# specialX shouln'd be affect alex because inactive.
|
||||
#####
|
||||
{SEPARATE_KEEP_A_B_C_D_UNIT_TEST "affect_distant_inactive_with_second_ability" (
|
||||
{TEST_AFFECT_DISTANT_INACTIVE_WITH_SECOND_ABILITY (ability_id_active=specialY)}
|
||||
)}
|
||||
|
||||
#undef TEST_AFFECT_DISTANT_INACTIVE_WITH_SECOND_ABILITY
|
@ -55,25 +55,6 @@ void move_action::write(config & cfg) const
|
||||
child["goto_y"] = goto_hex.wml_y();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset halo of adjacent units when undo move.
|
||||
*/
|
||||
static void reset_adjacent(bool& halo_adjacent, unit_map& units, const map_location& loc)
|
||||
{
|
||||
if(halo_adjacent){
|
||||
unit_map::iterator u = units.find(loc);
|
||||
const auto adjacent = get_adjacent_tiles(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 == &*u )
|
||||
continue;
|
||||
it->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Undoes this action.
|
||||
* @return true on success; false on an error.
|
||||
@ -103,17 +84,10 @@ bool move_action::undo(int)
|
||||
|
||||
// Move the unit.
|
||||
unit_display::move_unit(rev_route, u.get_shared_ptr(), true, starting_dir);
|
||||
bool halo_adjacent = false;
|
||||
for(const auto [_, cfg] : u->abilities().all_children_view()){
|
||||
if(!cfg["halo_image"].empty() && cfg.has_child("affect_adjacent")){
|
||||
halo_adjacent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
reset_adjacent(halo_adjacent, units, rev_route.front());
|
||||
u->anim_comp().reset_affect_distant(gui);
|
||||
units.move(u->get_location(), rev_route.back());
|
||||
unit::clear_status_caches();
|
||||
reset_adjacent(halo_adjacent, units, rev_route.back());
|
||||
u->anim_comp().reset_affect_distant(gui);
|
||||
|
||||
// Restore the unit's old state.
|
||||
u = units.find(rev_route.back());
|
||||
|
@ -182,6 +182,27 @@ void game_board::check_victory(bool& continue_level,
|
||||
continue_level = false;
|
||||
}
|
||||
|
||||
void game_board::set_affect_distant_max_radius(utils::optional<int> value, const std::string& tag_name)
|
||||
{
|
||||
if(value){
|
||||
if(!affect_distant_max_radius_[tag_name] || *affect_distant_max_radius_[tag_name] < *value){
|
||||
affect_distant_max_radius_[tag_name] = value;
|
||||
}
|
||||
if(!affect_distant_max_radius_for_filtering_ || *affect_distant_max_radius_for_filtering_ < *value){
|
||||
affect_distant_max_radius_for_filtering_ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_board::set_affect_distant_max_radius_image(utils::optional<int> value)
|
||||
{
|
||||
if(value){
|
||||
if(!affect_distant_max_radius_for_image_ || *affect_distant_max_radius_for_image_ < *value){
|
||||
affect_distant_max_radius_for_image_ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unit_map::iterator game_board::find_visible_unit(const map_location& loc, const team& current_team, bool see_all)
|
||||
{
|
||||
if(!map_->on_board(loc)) {
|
||||
|
@ -52,6 +52,17 @@ class game_board : public display_context
|
||||
n_unit::id_manager unit_id_manager_;
|
||||
unit_map units_;
|
||||
|
||||
/**
|
||||
* Variables used for define radius for abilities [affect_distant]radius= checking.
|
||||
*
|
||||
* @ affect_distant_max_radius_ is used for checking in abilities of defined type.
|
||||
* @ affect_distant_max_radius_for_filtering_ used when checking abilities in [filter] or animations.
|
||||
* @ affect_distant_max_radius_for_image_ define radius for images in abilities.
|
||||
**/
|
||||
std::map<std::string, utils::optional<int>> affect_distant_max_radius_;
|
||||
utils::optional<int> affect_distant_max_radius_for_filtering_;
|
||||
utils::optional<int> affect_distant_max_radius_for_image_;
|
||||
|
||||
/**
|
||||
* Temporary unit move structs:
|
||||
*
|
||||
@ -131,6 +142,13 @@ public:
|
||||
|
||||
friend void swap(game_board & one, game_board & other);
|
||||
|
||||
//when used define radius max for check unit who own a ability with [affect_distant] tag.
|
||||
utils::optional<int> affect_distant_max_radius(const std::string& value){return affect_distant_max_radius_[value];}
|
||||
utils::optional<int> affect_distant_max_radius_for_filtering() const {return affect_distant_max_radius_for_filtering_;}
|
||||
utils::optional<int> affect_distant_max_radius_for_image() const {return affect_distant_max_radius_for_image_;}
|
||||
void set_affect_distant_max_radius(utils::optional<int> value, const std::string& tag_name = "");
|
||||
void set_affect_distant_max_radius_image(utils::optional<int> value);
|
||||
|
||||
// Saving
|
||||
|
||||
void write_config(config & cfg) const;
|
||||
|
@ -2750,6 +2750,62 @@ int game_lua_kernel::intf_set_floating_label(lua_State* L, bool spawn)
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const unit_map& get_unit_map()
|
||||
{
|
||||
// Used if we're in the game, including during the construction of the display_context
|
||||
if(resources::gameboard) {
|
||||
return resources::gameboard->units();
|
||||
}
|
||||
|
||||
// If we get here, we're in the scenario editor
|
||||
assert(display::get_singleton());
|
||||
return display::get_singleton()->context().units();
|
||||
}
|
||||
void reset_affect_distant(const unit& u_)
|
||||
{
|
||||
bool affect_distant = false;
|
||||
bool affect_adjacent = false;
|
||||
for(const auto [key, cfg] : u_.abilities().all_children_view()) {
|
||||
bool image_or_hides = (key == "hides" || cfg.has_attribute("halo_image") || cfg.has_attribute("overlay_image"));
|
||||
if(!affect_adjacent && image_or_hides && cfg.has_child("affect_adjacent")){
|
||||
affect_adjacent = true;
|
||||
}
|
||||
if(!affect_distant && image_or_hides && cfg.has_child("affect_distant")){
|
||||
affect_distant = true;
|
||||
}
|
||||
if(affect_adjacent && affect_distant){
|
||||
break;
|
||||
}
|
||||
}
|
||||
const unit_map& units = get_unit_map();
|
||||
if(affect_adjacent){
|
||||
const auto adjacent = get_adjacent_tiles(u_.get_location());
|
||||
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 == &u_ )
|
||||
continue;
|
||||
it->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = u_.affect_distant_max_radius();
|
||||
if(max_radius && affect_distant){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(u_.get_location(), (*max_radius + 1), surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == &u_) {
|
||||
continue;
|
||||
}
|
||||
unit_itor->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_lua_kernel::put_unit_helper(const map_location& loc)
|
||||
{
|
||||
if(game_display_) {
|
||||
@ -2795,6 +2851,7 @@ int game_lua_kernel::intf_put_unit(lua_State *L)
|
||||
put_unit_helper(loc);
|
||||
u.put_map(loc);
|
||||
u.get_shared()->anim_comp().set_standing();
|
||||
reset_affect_distant(*u);
|
||||
} else if(!lua_isnoneornil(L, 1)) {
|
||||
const vconfig* vcfg = nullptr;
|
||||
config cfg = luaW_checkconfig(L, 1, vcfg);
|
||||
@ -2810,8 +2867,10 @@ int game_lua_kernel::intf_put_unit(lua_State *L)
|
||||
put_unit_helper(loc);
|
||||
u->set_location(loc);
|
||||
units().insert(u);
|
||||
reset_affect_distant(*u);
|
||||
}
|
||||
|
||||
|
||||
// Fire event if using the deprecated version or if the final argument is not false
|
||||
// If the final boolean argument is omitted, the actual final argument (the unit or location) will always yield true.
|
||||
if(luaW_toboolean(L, -1)) {
|
||||
@ -2838,6 +2897,7 @@ int game_lua_kernel::intf_erase_unit(lua_State *L)
|
||||
if (!map().on_board(loc)) {
|
||||
return luaL_argerror(L, 1, "invalid location");
|
||||
}
|
||||
reset_affect_distant(*u);
|
||||
} else if (int side = u.on_recall_list()) {
|
||||
team &t = board().get_team(side);
|
||||
// Should it use underlying ID instead?
|
||||
@ -2900,6 +2960,7 @@ int game_lua_kernel::intf_put_recall_unit(lua_State *L)
|
||||
units().erase(u->get_location());
|
||||
resources::whiteboard->on_kill_unit();
|
||||
u->anim_comp().clear_haloes();
|
||||
reset_affect_distant(*u);
|
||||
}
|
||||
lu->lua_unit::~lua_unit();
|
||||
new(lu) lua_unit(side, uid);
|
||||
@ -2924,6 +2985,7 @@ int game_lua_kernel::intf_extract_unit(lua_State *L)
|
||||
u = units().extract(u->get_location());
|
||||
assert(u);
|
||||
u->anim_comp().clear_haloes();
|
||||
reset_affect_distant(*u);
|
||||
} else if (int side = lu->on_recall_list()) {
|
||||
team &t = board().get_team(side);
|
||||
unit_ptr v = u->clone();
|
||||
@ -2956,6 +3018,7 @@ int game_lua_kernel::intf_find_vacant_tile(lua_State *L)
|
||||
const vconfig* vcfg = nullptr;
|
||||
config cfg = luaW_checkconfig(L, 2, vcfg);
|
||||
u = unit::create(cfg, false, vcfg);
|
||||
reset_affect_distant(*u);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3000,6 +3063,9 @@ static int intf_create_unit(lua_State *L)
|
||||
config cfg = luaW_checkconfig(L, 1, vcfg);
|
||||
unit_ptr u = unit::create(cfg, true, vcfg);
|
||||
luaW_pushunit(L, u);
|
||||
if (u->get_location().valid()) {
|
||||
reset_affect_distant(*u);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3159,6 +3225,9 @@ static int intf_transform_unit(lua_State *L)
|
||||
utp = &utp->get_variation(m2);
|
||||
}
|
||||
u.advance_to(*utp);
|
||||
if (u.get_location().valid()) {
|
||||
reset_affect_distant(u);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -213,6 +213,22 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = affect_distant_max_radius(tag_name);
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(loc, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == this) {
|
||||
continue;
|
||||
}
|
||||
for(const config& i : unit_itor->abilities_.child_range(tag_name)) {
|
||||
if(get_dist_ability_bool(i, tag_name, loc, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
@ -249,6 +265,22 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = affect_distant_max_radius(tag_name);
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(loc, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == this) {
|
||||
continue;
|
||||
}
|
||||
for(const config& i : unit_itor->abilities_.child_range(tag_name)) {
|
||||
if(get_dist_ability_bool(i, tag_name, loc, *unit_itor, surrounding[j])){
|
||||
res.emplace_back(&i, loc, surrounding[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
@ -521,6 +553,44 @@ bool unit::ability_affects_adjacent(const std::string& ability, const config& cf
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unit::ability_affects_distant(const std::string& ability, const config& cfg, const map_location& loc, const unit& from, const map_location& from_loc) const
|
||||
{
|
||||
unsigned int radius = 0;
|
||||
bool illuminates = ability == "illuminates";
|
||||
for (const config &i : cfg.child_range("affect_distant"))
|
||||
{
|
||||
radius = i["radius"].to_int(0);
|
||||
if(radius == 0){
|
||||
continue;
|
||||
}
|
||||
unsigned int distance = distance_between(from_loc, loc);
|
||||
if(distance > radius){
|
||||
continue;
|
||||
}
|
||||
auto filter = i.optional_child("filter");
|
||||
if (!filter || //filter tag given
|
||||
unit_filter(vconfig(*filter)).set_use_flat_tod(illuminates).matches(*this, loc, from) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unit::get_dist_ability_bool(const config& cfg, const std::string& ability, const map_location& loc, const unit& from, const map_location& from_loc) const
|
||||
{
|
||||
auto filter_lock = from.update_variables_recursion(cfg);
|
||||
if(!filter_lock) {
|
||||
show_recursion_warning(from, cfg);
|
||||
return false;
|
||||
}
|
||||
return (ability_affects_distant(ability, cfg, loc, from, from_loc) && affects_side(cfg, side(), from.side()) && from.ability_active_impl(ability, cfg, from_loc));
|
||||
}
|
||||
|
||||
bool unit::get_dist_ability_bool_weapon(const config& special, const std::string& tag_name, const map_location& loc, const unit& from, const map_location& from_loc, const const_attack_ptr& weapon, const const_attack_ptr& opp_weapon) const
|
||||
{
|
||||
return (get_dist_ability_bool(special, tag_name, loc, from, from_loc) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
|
||||
}
|
||||
|
||||
bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
|
||||
{
|
||||
auto filter = cfg.optional_child("filter_self");
|
||||
@ -553,13 +623,24 @@ bool unit::has_ability_type(const std::string& ability) const
|
||||
return !abilities_.child_range(ability).empty();
|
||||
}
|
||||
|
||||
//these two functions below are used in order to add to the unit
|
||||
//these functions below are used in order to add to the unit
|
||||
//a second set of halo encoded in the abilities (like illuminates halo in [illuminates] ability for example)
|
||||
static void add_string_to_vector(std::vector<std::string>& image_list, const config& cfg, const std::string& attribute_name)
|
||||
namespace
|
||||
{
|
||||
auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
|
||||
if(ret == image_list.end()){
|
||||
image_list.push_back(cfg[attribute_name].str());
|
||||
void add_string_to_vector(std::vector<std::string>& image_list, const config& cfg, const std::string& attribute_name)
|
||||
{
|
||||
auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
|
||||
if(ret == image_list.end()){
|
||||
image_list.push_back(cfg[attribute_name].str());
|
||||
}
|
||||
}
|
||||
|
||||
utils::optional<int> affect_distant_max_radius_image()
|
||||
{
|
||||
if(resources::gameboard) {
|
||||
return resources::gameboard->affect_distant_max_radius_for_image();
|
||||
}
|
||||
return utils::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,6 +677,23 @@ std::vector<std::string> unit::halo_or_icon_abilities(const std::string& image_t
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = affect_distant_max_radius_image();
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(loc_, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == this) {
|
||||
continue;
|
||||
}
|
||||
for(const auto [key, cfg] : unit_itor->abilities_.all_children_view()) {
|
||||
if(!cfg[image_type + "_image"].str().empty() && get_dist_ability_bool(cfg, key, loc_, *unit_itor, surrounding[j]))
|
||||
{
|
||||
add_string_to_vector(image_list, cfg, image_type + "_image");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//rearranges vector alphabetically when its size equals or exceeds two.
|
||||
if(image_list.size() >= 2){
|
||||
std::sort(image_list.begin(), image_list.end());
|
||||
@ -1785,6 +1883,26 @@ bool attack_type::check_adj_abilities_impl(const const_attack_ptr& self_attack,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool attack_type::check_dist_abilities(const config& cfg, const std::string& special, const unit& from, const map_location& from_loc) const
|
||||
{
|
||||
return check_dist_abilities_impl(shared_from_this(), other_attack_, cfg, self_, from, self_loc_, from_loc, AFFECT_SELF, special, "filter_student");
|
||||
}
|
||||
|
||||
bool attack_type::check_dist_abilities_impl(const const_attack_ptr& self_attack, const const_attack_ptr& other_attack, const config& special, const unit_const_ptr& u, const unit& from, const map_location& loc, const map_location& from_loc, AFFECTS whom, const std::string& tag_name, bool leader_bool)
|
||||
{
|
||||
if(tag_name == "leadership" && leader_bool){
|
||||
if((*u).get_dist_ability_bool_weapon(special, tag_name, loc, from, from_loc, self_attack, other_attack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if((*u).checking_tags().count(tag_name) != 0){
|
||||
if((*u).get_dist_ability_bool(special, tag_name, loc, from, from_loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, "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
|
||||
@ -1795,6 +1913,7 @@ bool attack_type::has_weapon_ability(const std::string& special, bool special_id
|
||||
{
|
||||
const unit_map& units = get_unit_map();
|
||||
if(self_){
|
||||
utils::optional<int> max_radius = self_->affect_distant_max_radius(special_tags ? special : "");
|
||||
std::vector<special_match> special_tag_matches_self;
|
||||
std::vector<special_match> special_id_matches_self;
|
||||
get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
|
||||
@ -1839,9 +1958,39 @@ bool attack_type::has_weapon_ability(const std::string& special, bool special_id
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(self_loc_, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated())
|
||||
continue;
|
||||
if ( &*unit_itor == self_.get() )
|
||||
continue;
|
||||
|
||||
std::vector<special_match> special_tag_matches_dist;
|
||||
std::vector<special_match> special_id_matches_dist;
|
||||
get_ability_children(special_tag_matches_dist, special_id_matches_dist, unit_itor->abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches_dist) {
|
||||
if(check_dist_abilities(*entry.cfg, entry.tag_name, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches_dist) {
|
||||
if(check_dist_abilities(*entry.cfg, entry.tag_name, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(other_){
|
||||
utils::optional<int> max_radius = other_->affect_distant_max_radius(special_tags ? special : "");
|
||||
std::vector<special_match> special_tag_matches_other;
|
||||
std::vector<special_match> special_id_matches_other;
|
||||
get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
|
||||
@ -1888,6 +2037,35 @@ bool attack_type::has_weapon_ability(const std::string& special, bool special_id
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(other_loc_, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated())
|
||||
continue;
|
||||
if ( &*unit_itor == other_.get() )
|
||||
continue;
|
||||
|
||||
std::vector<special_match> special_tag_matches_odist;
|
||||
std::vector<special_match> special_id_matches_odist;
|
||||
get_ability_children(special_tag_matches_odist, special_id_matches_odist, unit_itor->abilities(), special, special_id , special_tags);
|
||||
if(special_tags){
|
||||
for(const special_match& entry : special_tag_matches_odist) {
|
||||
if(check_dist_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *unit_itor, other_loc_, surrounding[j], AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(special_id){
|
||||
for(const special_match& entry : special_id_matches_odist) {
|
||||
if(check_dist_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *unit_itor, other_loc_, surrounding[j], AFFECT_OTHER, entry.tag_name)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -2141,6 +2319,7 @@ bool attack_type::has_ability_with_filter(const config & filter) const
|
||||
}
|
||||
const unit_map& units = get_unit_map();
|
||||
if(self_){
|
||||
utils::optional<int> max_radius = self_->affect_distant_max_radius();
|
||||
for(const auto [key, cfg] : (*self_).abilities().all_children_view()) {
|
||||
if(self_->ability_matches_filter(cfg, key, filter)){
|
||||
if(check_self_abilities(cfg, key)){
|
||||
@ -2163,9 +2342,27 @@ bool attack_type::has_ability_with_filter(const config & filter) const
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(self_loc_, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated())
|
||||
continue;
|
||||
if ( &*unit_itor == self_.get() )
|
||||
continue;
|
||||
|
||||
for(const auto [key, cfg] : unit_itor->abilities().all_children_view()) {
|
||||
if(unit_itor->ability_matches_filter(cfg, key, filter) && check_dist_abilities(cfg, key, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(other_){
|
||||
utils::optional<int> max_radius = other_->affect_distant_max_radius();
|
||||
for(const auto [key, cfg] : (*other_).abilities().all_children_view()) {
|
||||
if(other_->ability_matches_filter(cfg, key, filter) && check_self_abilities_impl(other_attack_, shared_from_this(), cfg, other_, other_loc_, AFFECT_OTHER, key)){
|
||||
return true;
|
||||
@ -2186,6 +2383,23 @@ bool attack_type::has_ability_with_filter(const config & filter) const
|
||||
}
|
||||
}
|
||||
}
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(other_loc_, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated())
|
||||
continue;
|
||||
if ( &*unit_itor == other_.get() )
|
||||
continue;
|
||||
|
||||
for(const auto [key, cfg] : unit_itor->abilities().all_children_view()) {
|
||||
if(unit_itor->ability_matches_filter(cfg, key, filter) && check_dist_abilities_impl(other_attack_, shared_from_this(), cfg, other_, *unit_itor, other_loc_, surrounding[j], AFFECT_OTHER, key)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -17,9 +17,12 @@
|
||||
|
||||
#include "config.hpp"
|
||||
#include "display.hpp"
|
||||
#include "game_board.hpp"
|
||||
#include "map/map.hpp"
|
||||
#include "preferences/preferences.hpp"
|
||||
#include "random.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "terrain/filter.hpp"
|
||||
#include "units/unit.hpp"
|
||||
#include "units/types.hpp"
|
||||
|
||||
@ -196,6 +199,48 @@ void unit_animation_component::reset_after_advance(const unit_type * newtype)
|
||||
anim_.reset();
|
||||
}
|
||||
|
||||
void unit_animation_component::reset_affect_distant(const display & disp)
|
||||
{
|
||||
bool affect_distant = false;
|
||||
bool affect_adjacent = false;
|
||||
for(const auto [key, cfg] : u_.abilities().all_children_view()) {
|
||||
bool image_or_hides = (key == "hides" || cfg.has_attribute("halo_image") || cfg.has_attribute("overlay_image"));
|
||||
if(!affect_adjacent && image_or_hides && cfg.has_child("affect_adjacent")){
|
||||
affect_adjacent = true;
|
||||
}
|
||||
if(!affect_distant && image_or_hides && cfg.has_child("affect_distant")){
|
||||
affect_distant = true;
|
||||
}
|
||||
if(affect_adjacent && affect_distant){
|
||||
break;
|
||||
}
|
||||
}
|
||||
const unit_map& units = disp.context().units();
|
||||
if(affect_adjacent){
|
||||
const auto adjacent = get_adjacent_tiles(u_.get_location());
|
||||
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 == &u_ )
|
||||
continue;
|
||||
it->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = u_.affect_distant_max_radius();
|
||||
if(max_radius && affect_distant){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(u_.get_location(), (*max_radius + 1), surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == &u_) {
|
||||
continue;
|
||||
}
|
||||
unit_itor->anim_comp().set_standing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unit_animation_component::apply_new_animation_effect(const config & effect) {
|
||||
if(effect["id"].empty()) {
|
||||
unit_animation::add_anims(animations_, effect);
|
||||
|
@ -106,6 +106,9 @@ public:
|
||||
/** Resets the animations list after the unit is advanced. */
|
||||
void reset_after_advance(const unit_type * newtype = nullptr);
|
||||
|
||||
/** Refresh map around unit if has ability with [affect_adjacent/distant] tag */
|
||||
void reset_affect_distant(const display & disp);
|
||||
|
||||
/** Adds an animation described by a config. Uses an internal cache to avoid redoing work. */
|
||||
void apply_new_animation_effect(const config & effect);
|
||||
|
||||
|
@ -271,6 +271,14 @@ private:
|
||||
* @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;
|
||||
/** check_dist_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 from unit distant to self_ is checked.
|
||||
* @param from_loc location of @ from
|
||||
*/
|
||||
bool check_dist_abilities(const config& cfg, const std::string& special, const unit& from, const map_location& from_loc) const;
|
||||
bool special_active(const config& special, AFFECTS whom, const std::string& tag_name,
|
||||
bool in_abilities_tag = false) const;
|
||||
|
||||
@ -358,6 +366,32 @@ private:
|
||||
bool leader_bool=false
|
||||
);
|
||||
|
||||
/** check_dist_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 distant to @a u is checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param from_loc location of the unit distant to @a u.
|
||||
* @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_dist_abilities_impl(
|
||||
const const_attack_ptr& self_attack,
|
||||
const const_attack_ptr& other_attack,
|
||||
const config& special,
|
||||
const unit_const_ptr& u,
|
||||
const unit& from,
|
||||
const map_location& loc,
|
||||
const map_location& from_loc,
|
||||
AFFECTS whom,
|
||||
const std::string& tag_name,
|
||||
bool leader_bool = false
|
||||
);
|
||||
|
||||
static bool special_active_impl(
|
||||
const const_attack_ptr& self_attack,
|
||||
const const_attack_ptr& other_attack,
|
||||
|
@ -441,6 +441,24 @@ void unit_filter_compound::fill(const vconfig& cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = args.u.affect_distant_max_radius();
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(args.loc, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == args.u.shared_from_this().get()) {
|
||||
continue;
|
||||
}
|
||||
std::vector<ability_match> ability_id_matches_dist;
|
||||
get_ability_children_id(ability_id_matches_dist, unit_itor->abilities(), ability);
|
||||
for(const ability_match& entry : ability_id_matches_dist) {
|
||||
if(args.u.get_dist_ability_bool(*entry.cfg, entry.tag_name, args.loc, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -818,6 +836,22 @@ void unit_filter_compound::fill(const vconfig& cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::optional<int> max_radius = args.u.affect_distant_max_radius();
|
||||
if(max_radius){
|
||||
std::vector<map_location> surrounding;
|
||||
get_tiles_in_radius(args.loc, *max_radius, surrounding);
|
||||
for(unsigned j = 0; j < surrounding.size(); ++j){
|
||||
unit_map::const_iterator unit_itor = units.find(surrounding[j]);
|
||||
if (unit_itor == units.end() || unit_itor->incapacitated() || &(*unit_itor) == (args.u.shared_from_this()).get()) {
|
||||
continue;
|
||||
}
|
||||
for(const auto [key, cfg] : unit_itor->abilities().all_children_view()) {
|
||||
if(args.u.get_dist_ability_bool(cfg, key, args.loc, *unit_itor, surrounding[j])){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -90,6 +90,7 @@ void teleport_unit_between(const map_location& a, const map_location& b, unit& t
|
||||
animator.add_animation(temp_unit.shared_from_this(),"pre_teleport",a);
|
||||
animator.start_animations();
|
||||
animator.wait_for_end();
|
||||
temp_unit.anim_comp().reset_affect_distant(disp);
|
||||
}
|
||||
|
||||
temp_unit.set_location(b);
|
||||
@ -104,6 +105,7 @@ void teleport_unit_between(const map_location& a, const map_location& b, unit& t
|
||||
animator.add_animation(temp_unit.shared_from_this(),"post_teleport",b);
|
||||
animator.start_animations();
|
||||
animator.wait_for_end();
|
||||
temp_unit.anim_comp().reset_affect_distant(disp);
|
||||
}
|
||||
|
||||
temp_unit.anim_comp().set_standing();
|
||||
@ -260,6 +262,7 @@ void unit_mover::start(const unit_ptr& u)
|
||||
if ( !can_draw_ )
|
||||
return;
|
||||
// If no animation then hide unit until end of movement
|
||||
(*u).anim_comp().reset_affect_distant(*disp_);
|
||||
if ( !animate_ ) {
|
||||
was_hidden_ = u->get_hidden();
|
||||
u->set_hidden(true);
|
||||
@ -380,6 +383,7 @@ void unit_mover::proceed_to(const unit_ptr& u, std::size_t path_index, bool upda
|
||||
u->anim_comp().set_standing(false); // Need to reset u's animation so the new facing takes effect.
|
||||
// Remember the unit to unhide when the animation finishes.
|
||||
shown_unit_ = u;
|
||||
(*u).anim_comp().reset_affect_distant(*disp_);
|
||||
if ( wait )
|
||||
wait_for_anims();
|
||||
}
|
||||
@ -464,6 +468,7 @@ void unit_mover::finish(const unit_ptr& u, map_location::direction dir)
|
||||
// Switch the display back to the real unit.
|
||||
u->set_hidden(was_hidden_);
|
||||
temp_unit_ptr_->set_hidden(true);
|
||||
(*u).anim_comp().reset_affect_distant(*disp_);
|
||||
|
||||
if(events::mouse_handler* mousehandler = events::mouse_handler::get_singleton()) {
|
||||
mousehandler->invalidate_reachmap();
|
||||
@ -589,6 +594,7 @@ void unit_die(const map_location& loc, unit& loser,
|
||||
if(events::mouse_handler* mousehandler = events::mouse_handler::get_singleton()) {
|
||||
mousehandler->invalidate_reachmap();
|
||||
}
|
||||
loser.anim_comp().reset_affect_distant(*disp);
|
||||
}
|
||||
|
||||
|
||||
@ -837,6 +843,7 @@ void unit_recruited(const map_location& loc,const map_location& leader_loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
(*u).anim_comp().reset_affect_distant(*disp);
|
||||
animator.add_animation(u.get_shared_ptr(), "recruited", loc, leader_loc);
|
||||
animator.start_animations();
|
||||
animator.wait_for_end();
|
||||
|
@ -406,6 +406,36 @@ unit::unit(unit_ctor_t)
|
||||
{
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void set_affect_distant_max_radius(utils::optional<int> value, const std::string& tag_name)
|
||||
{
|
||||
if(resources::gameboard) {
|
||||
resources::gameboard->set_affect_distant_max_radius(value, tag_name);
|
||||
}
|
||||
}
|
||||
|
||||
void set_affect_distant_max_radius(const config& cfg, const std::string& tag_name)
|
||||
{
|
||||
for(const config& affect_distant : cfg.child_range("affect_distant")) {
|
||||
if(affect_distant.has_attribute("radius")){
|
||||
set_affect_distant_max_radius(affect_distant["radius"].to_int(), tag_name);
|
||||
if(resources::gameboard && (cfg.has_attribute("halo_image") || cfg.has_attribute("overlay_image"))){
|
||||
resources::gameboard->set_affect_distant_max_radius_image(affect_distant["radius"].to_int());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils::optional<int> unit::affect_distant_max_radius(const std::string& tag_name) const
|
||||
{
|
||||
if(resources::gameboard) {
|
||||
return !tag_name.empty() ? resources::gameboard->affect_distant_max_radius(tag_name) : resources::gameboard->affect_distant_max_radius_for_filtering();
|
||||
}
|
||||
return utils::nullopt;
|
||||
}
|
||||
|
||||
void unit::init(const config& cfg, bool use_traits, const vconfig* vcfg)
|
||||
{
|
||||
loc_ = map_location(cfg["x"], cfg["y"], wml_loc());
|
||||
@ -448,6 +478,15 @@ void unit::init(const config& cfg, bool use_traits, const vconfig* vcfg)
|
||||
for(const vconfig& ability_event : ability_events) {
|
||||
events_.add_child("event", ability_event.get_config());
|
||||
}
|
||||
const vconfig::child_list& affect_distants = child.get_children("affect_distant");
|
||||
for(const vconfig& affect_distant : affect_distants) {
|
||||
if(affect_distant.has_attribute("radius")){
|
||||
set_affect_distant_max_radius(affect_distant["radius"].to_int(), key);
|
||||
if(resources::gameboard && (child.has_attribute("halo_image") || child.has_attribute("overlay_image"))){
|
||||
resources::gameboard->set_affect_distant_max_radius_image(affect_distant["radius"].to_int());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const vconfig::child_list& attacks = vcfg->get_children("attack");
|
||||
@ -473,6 +512,7 @@ void unit::init(const config& cfg, bool use_traits, const vconfig* vcfg)
|
||||
for(const config& ability_event : ability.child_range("event")) {
|
||||
events_.add_child("event", ability_event);
|
||||
}
|
||||
set_affect_distant_max_radius(ability, key);
|
||||
}
|
||||
}
|
||||
for(const config& attack : cfg.child_range("attack")) {
|
||||
@ -1098,6 +1138,7 @@ void unit::advance_to(const unit_type& u_type, bool use_traits)
|
||||
for(const config& ability_event : ability.child_range("event")) {
|
||||
events.add_child("event", ability_event);
|
||||
}
|
||||
set_affect_distant_max_radius(ability, key);
|
||||
}
|
||||
}
|
||||
for(const config& attack : cfg.child_range("attack")) {
|
||||
@ -2251,6 +2292,7 @@ void unit::apply_builtin_effect(const std::string& apply_to, const config& effec
|
||||
for(const config& event : cfg.child_range("event")) {
|
||||
events.add_child("event", event);
|
||||
}
|
||||
set_affect_distant_max_radius(cfg, key);
|
||||
}
|
||||
}
|
||||
abilities_.append(to_append);
|
||||
|
@ -1788,6 +1788,27 @@ public:
|
||||
*/
|
||||
bool get_adj_ability_bool_weapon(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from, const const_attack_ptr& weapon=nullptr, const const_attack_ptr& opp_weapon = nullptr) const;
|
||||
|
||||
/** Checks whether this unit is affected by a given ability, and that that ability is active.
|
||||
* @return True if the ability @a tag_name is active.
|
||||
* @param cfg the const config to one of abilities @a ability checked.
|
||||
* @param ability name of ability type checked.
|
||||
* @param loc location of the unit checked.
|
||||
* @param from unit distant to @a this is checked in case of [affect_distant] abilities.
|
||||
* @param from_loc the 'other unit' location.
|
||||
*/
|
||||
bool get_dist_ability_bool(const config& cfg, const std::string& ability, const map_location& loc, const unit& from, const map_location& from_loc) 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_distant] abilities.
|
||||
* @param from_loc location of the @a from unit.
|
||||
* @param weapon the attack used by unit checked in this function.
|
||||
* @param opp_weapon the attack used by opponent to unit checked.
|
||||
*/
|
||||
bool get_dist_ability_bool_weapon(const config& special, const std::string& tag_name, const map_location& loc, const unit& from, const map_location& from_loc, const const_attack_ptr& weapon, const const_attack_ptr& opp_weapon) 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
|
||||
@ -1878,6 +1899,11 @@ public:
|
||||
*/
|
||||
bool ability_matches_filter(const config & cfg, const std::string& tag_name, const config & filter) const;
|
||||
|
||||
/**
|
||||
* @returns game_board affect_distant_max_radius_[tag_name] or affect_distant_max_radius_for_filtering_
|
||||
* @param tag_name the type of ability corresponding to the variable used, if empty return affect_distant_max_radius_for_filtering_.
|
||||
*/
|
||||
utils::optional<int> affect_distant_max_radius(const std::string& tag_name = "") const;
|
||||
|
||||
private:
|
||||
|
||||
@ -1947,6 +1973,15 @@ private:
|
||||
*/
|
||||
bool ability_affects_adjacent(const std::string& ability, const config& cfg, int dir, const map_location& loc, const unit& from) const;
|
||||
|
||||
/**
|
||||
* Check if an ability affects distant units.
|
||||
* @param ability The type (tag name) of the ability
|
||||
* @param cfg an ability WML structure
|
||||
* @param loc The location on which to resolve the ability
|
||||
* @param from The "other unit" for filter matching
|
||||
* @param from_loc the "other unit" location
|
||||
*/
|
||||
bool ability_affects_distant(const std::string& ability, const config& cfg, const map_location& loc, const unit& from, const map_location& from_loc) const;
|
||||
/**
|
||||
* Check if an ability affects the owning unit.
|
||||
* @param ability The type (tag name) of the ability
|
||||
|
@ -422,6 +422,12 @@
|
||||
0 filter_adjacent_direction_inactive
|
||||
0 filter_special_id_active
|
||||
0 filter_ability_special_id_active
|
||||
0 affect_distant_active
|
||||
0 affect_distant_inactive
|
||||
0 affect_distant_active_with_second_ability
|
||||
0 affect_distant_active_with_second_ability_type
|
||||
0 affect_distant_inactive_with_second_ability
|
||||
0 affect_distant_inactive_with_second_ability_type
|
||||
0 filter_special_id_not_exists
|
||||
0 special_id_active_lua_function
|
||||
0 leadership_when_other_has_special
|
||||
|
Loading…
x
Reference in New Issue
Block a user