Support negative numbers in ranges
Adds support for using these in the weapons and ability filters: * "-1", which was previously treated as an parse error (no number before the separator). * "-3--1" * "-infinity" as the lower number in the range, provided a different upper number is given. This treats "-infinity" (with no other number), "-infinity--infinity", "infinity" (with no other number) and "infinity-infinity" as errors. It seems unlikely that someone would intend to use a filter that can't match any reasonable number. The range "-infinity-infinity" will be parsed successfully. I don't see a use case for that, but nor do I see a reason to add extra C++ to reject it. However, it's not added to the schema, as I think it's good for the schema to give a warning when someone creates a filter which will accept every value (including accepting the default, so "-infinity-infinity" accepts the unset value too). Includes new unit tests for the C++ and the Lua stringx.parse_range functions. The next commit adds more WML tests, but is kept separate to credit the author. This started as a change to move common filter functions from unit.cpp to somewhere that they could be reused for other config-based filters. In the process a missing feature was found and added, the move is still included in a single Git commit because the move was required in order to make these functions accessible to the Boost unit tests. Two CodeBlocks project files additionally get src/utils/any.hpp added, which was in one of them but missing from the other two. I noticed because these are alphabetically at the start of the src/utils file list. Thanks to @CelticMinstrel for the review comments and Xcode project updates.
This commit is contained in:
parent
76246291cd
commit
cc8dddea6e
35 changed files with 552 additions and 166 deletions
|
@ -83,6 +83,9 @@ config_cache/test_macrosubstitution
|
|||
config_cache/test_transaction
|
||||
config_cache/test_define_loading
|
||||
config_cache/test_lead_spaces_loading
|
||||
config_filters/test_int_add_sub_filter
|
||||
config_filters/test_int_positive_filter
|
||||
config_filters/test_int_signed_filter
|
||||
filesystem/test_fs_game_path_reverse_engineering
|
||||
filesystem/test_fs_base
|
||||
filesystem/test_fs_enum
|
||||
|
|
2
changelog_entries/config_filters.md
Normal file
2
changelog_entries/config_filters.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
### WML Engine
|
||||
* Add support for filters to match negative values
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
#define AI_ASPECT_FILTERS
|
||||
{SIMPLE_KEY turns range_list}
|
||||
{SIMPLE_KEY turns unsigned_range_list}
|
||||
{SIMPLE_KEY time_of_day string_list}
|
||||
#enddef
|
||||
|
||||
|
|
|
@ -82,21 +82,21 @@
|
|||
glob_on_location_id=*
|
||||
[/not]
|
||||
[then]
|
||||
{REQUIRED_KEY x s_range_list}
|
||||
{REQUIRED_KEY y s_range_list}
|
||||
{REQUIRED_KEY x s_unsigned_range_list}
|
||||
{REQUIRED_KEY y s_unsigned_range_list}
|
||||
[/then]
|
||||
[else]
|
||||
{REQUIRED_KEY location_id string_list}
|
||||
[/else]
|
||||
[/if]
|
||||
{REQUIRED_KEYS_LOC_OR_XY enemy string_list s_range_list}
|
||||
{REQUIRED_KEYS_LOC_OR_XY enemy string_list s_unsigned_range_list}
|
||||
{DEFAULT_KEY active_side_leader s_bool no}
|
||||
{DEFAULT_KEY ca_score s_unsigned 300000}
|
||||
{SIMPLE_KEY healer_x s_range_list}
|
||||
{SIMPLE_KEY healer_y s_range_list}
|
||||
{SIMPLE_KEY healer_x s_unsigned_range_list}
|
||||
{SIMPLE_KEY healer_y s_unsigned_range_list}
|
||||
{SIMPLE_KEY healer_loc string_list}
|
||||
{SIMPLE_KEY leadership_x s_range_list}
|
||||
{SIMPLE_KEY leadership_y s_range_list}
|
||||
{SIMPLE_KEY leadership_x s_unsigned_range_list}
|
||||
{SIMPLE_KEY leadership_y s_unsigned_range_list}
|
||||
{SIMPLE_KEY leadership_loc string_list}
|
||||
[/case]
|
||||
[case]
|
||||
|
@ -205,7 +205,7 @@
|
|||
value=messenger_escort
|
||||
{FILTER_TAG "filter" unit min=1}
|
||||
{DEPRECATED_KEY id string}
|
||||
{REQUIRED_KEYS_LOC_OR_XY waypoint string_list s_range_list}
|
||||
{REQUIRED_KEYS_LOC_OR_XY waypoint string_list s_unsigned_range_list}
|
||||
{DEFAULT_KEY ca_score s_unsigned 300000}
|
||||
{DEFAULT_KEY enemy_death_chance s_real 0.67}
|
||||
{DEFAULT_KEY messenger_death_chance s_bool 0.0}
|
||||
|
@ -216,7 +216,7 @@
|
|||
value=patrol
|
||||
{FILTER_TAG "filter" unit min=1}
|
||||
{DEPRECATED_KEY id string}
|
||||
{REQUIRED_KEYS_LOC_OR_XY waypoint string_list s_range_list}
|
||||
{REQUIRED_KEYS_LOC_OR_XY waypoint string_list s_unsigned_range_list}
|
||||
{SIMPLE_KEY attack string_list}
|
||||
{DEFAULT_KEY attack_range s_int 1}
|
||||
{DEFAULT_KEY attack_invisible_enemies s_bool no}
|
||||
|
|
|
@ -268,7 +268,7 @@
|
|||
max=infinite
|
||||
{INSERT_TAG}
|
||||
{FILTER_TAG "filter_side" side {INSERT_TAG}}
|
||||
{SIMPLE_KEY side s_range_list}
|
||||
{SIMPLE_KEY side s_unsigned_range_list}
|
||||
{SIMPLE_KEY income s_int}
|
||||
{SIMPLE_KEY recruit string_list}
|
||||
{SIMPLE_KEY team_name string}
|
||||
|
@ -340,8 +340,8 @@
|
|||
max=infinite
|
||||
super="$filter_unit"
|
||||
{INSERT_TAG}
|
||||
{SIMPLE_KEY to_x s_range_list}
|
||||
{SIMPLE_KEY to_y s_range_list}
|
||||
{SIMPLE_KEY to_x s_unsigned_range_list}
|
||||
{SIMPLE_KEY to_y s_unsigned_range_list}
|
||||
{SIMPLE_KEY to_location string}
|
||||
{SIMPLE_KEY dir dir_count_list}
|
||||
{DEFAULT_KEY clear_shroud s_bool no}
|
||||
|
@ -407,7 +407,7 @@
|
|||
max=infinite
|
||||
super="$filter_location"
|
||||
{INSERT_TAG}
|
||||
{SIMPLE_KEY side s_range_list}
|
||||
{SIMPLE_KEY side s_unsigned_range_list}
|
||||
{FILTER_TAG "filter_side" side {INSERT_TAG}}
|
||||
[/tag]
|
||||
[tag]
|
||||
|
@ -535,8 +535,8 @@
|
|||
[tag]
|
||||
name="move"
|
||||
max=infinite
|
||||
{SIMPLE_KEY x s_range_list}
|
||||
{SIMPLE_KEY y s_range_list}
|
||||
{SIMPLE_KEY x s_unsigned_range_list}
|
||||
{SIMPLE_KEY y s_unsigned_range_list}
|
||||
{SIMPLE_KEY skip_sighted string} # TODO: Make this an enum type
|
||||
[/tag]
|
||||
[tag]
|
||||
|
@ -624,7 +624,7 @@
|
|||
{SIMPLE_KEY male_message s_t_string}
|
||||
{SIMPLE_KEY female_message s_t_string}
|
||||
{SIMPLE_KEY wait_description s_t_string}
|
||||
{SIMPLE_KEY side_for s_range_list}
|
||||
{SIMPLE_KEY side_for s_unsigned_range_list}
|
||||
{SIMPLE_KEY image string}
|
||||
{SIMPLE_KEY mirror s_bool}
|
||||
{SIMPLE_KEY second_image string}
|
||||
|
@ -772,8 +772,8 @@
|
|||
name="$fake_unit"
|
||||
max=0
|
||||
{SIMPLE_KEY type string}
|
||||
{SIMPLE_KEY x s_range_list}
|
||||
{SIMPLE_KEY y s_range_list}
|
||||
{SIMPLE_KEY x s_unsigned_range_list}
|
||||
{SIMPLE_KEY y s_unsigned_range_list}
|
||||
{SIMPLE_KEY side s_unsigned}
|
||||
{SIMPLE_KEY gender gender,subst}
|
||||
{SIMPLE_KEY variation string}
|
||||
|
|
|
@ -71,8 +71,8 @@
|
|||
[tag]
|
||||
name="fog_override"
|
||||
max=infinite
|
||||
{SIMPLE_KEY x range_list}
|
||||
{SIMPLE_KEY y range_list}
|
||||
{SIMPLE_KEY x unsigned_range_list}
|
||||
{SIMPLE_KEY y unsigned_range_list}
|
||||
[/tag]
|
||||
[tag]
|
||||
name="ai"
|
||||
|
@ -441,8 +441,8 @@
|
|||
[tag]
|
||||
name="label"
|
||||
max=infinite
|
||||
{SIMPLE_KEY x s_range_list}
|
||||
{SIMPLE_KEY y s_range_list}
|
||||
{SIMPLE_KEY x s_unsigned_range_list}
|
||||
{SIMPLE_KEY y s_unsigned_range_list}
|
||||
{SIMPLE_KEY text s_t_string}
|
||||
{SIMPLE_KEY tooltip s_t_string} # Is this documented?
|
||||
{SIMPLE_KEY immutable s_bool}
|
||||
|
@ -456,8 +456,8 @@
|
|||
[tag]
|
||||
name="item"
|
||||
max="infinite"
|
||||
{SIMPLE_KEY x range_list}
|
||||
{SIMPLE_KEY y range_list}
|
||||
{SIMPLE_KEY x unsigned_range_list}
|
||||
{SIMPLE_KEY y unsigned_range_list}
|
||||
{SIMPLE_KEY image string}
|
||||
{SIMPLE_KEY halo string}
|
||||
{SIMPLE_KEY team_name string}
|
||||
|
@ -481,8 +481,8 @@
|
|||
[tag]
|
||||
name="time_area"
|
||||
max=infinite
|
||||
{SIMPLE_KEY x s_range_list}
|
||||
{SIMPLE_KEY y s_range_list}
|
||||
{SIMPLE_KEY x s_unsigned_range_list}
|
||||
{SIMPLE_KEY y s_unsigned_range_list}
|
||||
{DEFAULT_KEY current_time int 0}
|
||||
{LINK_TAG "scenario/time"}
|
||||
[/tag]
|
||||
|
@ -523,8 +523,8 @@
|
|||
{SIMPLE_KEY chance s_unsigned}
|
||||
{SIMPLE_KEY check_fogged s_bool}
|
||||
{SIMPLE_KEY check_shrouded s_bool}
|
||||
{SIMPLE_KEY x s_range_list}
|
||||
{SIMPLE_KEY y s_range_list}
|
||||
{SIMPLE_KEY x s_unsigned_range_list}
|
||||
{SIMPLE_KEY y s_unsigned_range_list}
|
||||
{DEFAULT_KEY fade_range s_unsigned 3}
|
||||
{DEFAULT_KEY full_range s_unsigned 14}
|
||||
{SIMPLE_KEY loop s_int}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#define CHAMBER_TAG_CONTENTS
|
||||
max=infinite
|
||||
{SIMPLE_KEY id string}
|
||||
{SIMPLE_KEY x range_list}
|
||||
{SIMPLE_KEY y range_list}
|
||||
{SIMPLE_KEY x unsigned_range_list}
|
||||
{SIMPLE_KEY y unsigned_range_list}
|
||||
{SIMPLE_KEY size unsigned}
|
||||
{SIMPLE_KEY jagged unsigned}
|
||||
{DEFAULT_KEY chance unsigned 100}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
{SIMPLE_KEY overwrite_specials string_list}
|
||||
{SIMPLE_KEY apply_to string_list}
|
||||
{SIMPLE_KEY active_on string_list}
|
||||
{SIMPLE_KEY value s_range_list}
|
||||
{SIMPLE_KEY add s_range_list}
|
||||
{SIMPLE_KEY sub s_range_list}
|
||||
{SIMPLE_KEY value s_int_range_list}
|
||||
{SIMPLE_KEY add s_int_range_list}
|
||||
{SIMPLE_KEY sub s_int_range_list}
|
||||
{SIMPLE_KEY multiply s_real_range_list}
|
||||
{SIMPLE_KEY divide s_real_range_list}
|
||||
{SIMPLE_KEY affect_adjacent s_bool}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{SIMPLE_KEY y s_coordinates}
|
||||
{SIMPLE_KEY area string}
|
||||
{SIMPLE_KEY include_borders s_bool}
|
||||
{DEPRECATED_KEY owner_side s_range_list}
|
||||
{DEPRECATED_KEY owner_side s_unsigned_range_list}
|
||||
{SIMPLE_KEY find_in string}
|
||||
{SIMPLE_KEY radius s_int}
|
||||
{SIMPLE_KEY formula formula}
|
||||
|
@ -28,6 +28,6 @@
|
|||
name="$filter_adjacent_location"
|
||||
max=0
|
||||
super="$filter_location"
|
||||
{SIMPLE_KEY count s_range_list}
|
||||
{SIMPLE_KEY count s_unsigned_range_list}
|
||||
{SIMPLE_KEY adjacent s_dir_list}
|
||||
[/tag]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
[tag]
|
||||
name="$filter_side"
|
||||
max=0
|
||||
{SIMPLE_KEY side s_range_list}
|
||||
{SIMPLE_KEY side s_unsigned_range_list}
|
||||
{SIMPLE_KEY team_name string}
|
||||
{SIMPLE_KEY controller string}
|
||||
{SIMPLE_KEY formula formula}
|
||||
|
|
|
@ -15,23 +15,23 @@
|
|||
{SIMPLE_KEY ability string_list}
|
||||
{SIMPLE_KEY trait string_list}
|
||||
{SIMPLE_KEY status string_list}
|
||||
{SIMPLE_KEY side s_range_list}
|
||||
{SIMPLE_KEY side s_unsigned_range_list}
|
||||
{DEPRECATED_KEY has_weapon string}
|
||||
{SIMPLE_KEY canrecruit s_bool}
|
||||
{SIMPLE_KEY gender gender}
|
||||
{SIMPLE_KEY role string}
|
||||
{SIMPLE_KEY level s_range_list}
|
||||
{SIMPLE_KEY defense s_range_list}
|
||||
{SIMPLE_KEY movement_cost s_range_list}
|
||||
{SIMPLE_KEY vision_cost s_range_list}
|
||||
{SIMPLE_KEY jamming_cost s_range_list}
|
||||
{SIMPLE_KEY level s_unsigned_range_list}
|
||||
{SIMPLE_KEY defense s_unsigned_range_list}
|
||||
{SIMPLE_KEY movement_cost s_unsigned_range_list}
|
||||
{SIMPLE_KEY vision_cost s_unsigned_range_list}
|
||||
{SIMPLE_KEY jamming_cost s_unsigned_range_list}
|
||||
{SIMPLE_KEY x s_coordinates}
|
||||
{SIMPLE_KEY y s_coordinates}
|
||||
{SIMPLE_KEY find_in string}
|
||||
{SIMPLE_KEY formula formula}
|
||||
{SIMPLE_KEY lua_function string}
|
||||
{SIMPLE_KEY ai_special string}
|
||||
{SIMPLE_KEY recall_cost s_range_list}
|
||||
{SIMPLE_KEY recall_cost s_unsigned_range_list}
|
||||
{SIMPLE_KEY upkeep upkeep}
|
||||
{SIMPLE_KEY usage string}
|
||||
{SIMPLE_KEY alignment alignment}
|
||||
|
@ -56,7 +56,7 @@
|
|||
name="$filter_adjacent"
|
||||
max=0
|
||||
super="$filter_unit"
|
||||
{SIMPLE_KEY count s_range_list}
|
||||
{SIMPLE_KEY count s_unsigned_range_list}
|
||||
{SIMPLE_KEY adjacent dir_list}
|
||||
{SIMPLE_KEY is_enemy s_bool}
|
||||
[/tag]
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
{SIMPLE_KEY special_type string_list}
|
||||
{SIMPLE_KEY special_type_active string_list}
|
||||
{SIMPLE_KEY formula formula}
|
||||
{SIMPLE_KEY damage s_range_list}
|
||||
{SIMPLE_KEY number s_range_list}
|
||||
{SIMPLE_KEY parry s_range_list}
|
||||
{SIMPLE_KEY accuracy s_range_list}
|
||||
{SIMPLE_KEY movement_used s_range_list}
|
||||
{SIMPLE_KEY attacks_used s_range_list}
|
||||
{SIMPLE_KEY damage s_unsigned_range_list}
|
||||
{SIMPLE_KEY number s_unsigned_range_list}
|
||||
{SIMPLE_KEY parry s_unsigned_range_list}
|
||||
{SIMPLE_KEY accuracy s_unsigned_range_list}
|
||||
{SIMPLE_KEY movement_used s_unsigned_range_list}
|
||||
{SIMPLE_KEY attacks_used s_unsigned_range_list}
|
||||
{FILTER_BOOLEAN_OPS weapon}
|
||||
[/tag]
|
||||
|
||||
|
|
|
@ -80,8 +80,8 @@
|
|||
[/type]
|
||||
[/union]
|
||||
[/type]
|
||||
# definition of range_list and s_range_list, before macros are expanded
|
||||
{LIST_TYPE_COMPLEX range (
|
||||
# definition of unsigned_range_list and s_unsigned_range_list, before macros are expanded
|
||||
{LIST_TYPE_COMPLEX unsigned_range (
|
||||
[element]
|
||||
value="\d+"
|
||||
[/element]
|
||||
|
@ -92,16 +92,42 @@
|
|||
value="\d+-infinity"
|
||||
[/element]
|
||||
)}
|
||||
# definition of int_range_list and s_int_range_list, before macros are expanded
|
||||
{LIST_TYPE_COMPLEX int_range (
|
||||
[element]
|
||||
value="(-?)\d+"
|
||||
[/element]
|
||||
[element]
|
||||
value="-\d+-(-?)\d+"
|
||||
[/element]
|
||||
[element]
|
||||
# If the first number is positive, the second one has to be too
|
||||
value="\d+-\d+"
|
||||
[/element]
|
||||
[element]
|
||||
value="-infinity-(-?)\d+"
|
||||
[/element]
|
||||
[element]
|
||||
value="(-?)\d+-infinity"
|
||||
[/element]
|
||||
)}
|
||||
# definition of real_range_list and s_real_range_list, before macros are expanded
|
||||
{LIST_TYPE_COMPLEX real_range (
|
||||
[element]
|
||||
value="\d+(\.\d+)?"
|
||||
value="(-?)\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
value="-\d+(\.\d+)?-(-?)\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
# If the first number is positive, the second one has to be too
|
||||
value="\d+(\.\d+)?-\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
value="\d+(\.\d+)?-infinity"
|
||||
value="-infinity-(-?)\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
value="(-)?\d+(\.\d+)?-infinity"
|
||||
[/element]
|
||||
)}
|
||||
[type]
|
||||
|
@ -369,7 +395,7 @@
|
|||
name="coordinates"
|
||||
[union]
|
||||
[type]
|
||||
link=range_list
|
||||
link=unsigned_range_list
|
||||
[/type]
|
||||
[type]
|
||||
value="recall"
|
||||
|
@ -390,7 +416,8 @@
|
|||
[/type]
|
||||
{SUBST_TYPE coordinate}
|
||||
{SUBST_TYPE coordinates}
|
||||
{SUBST_TYPE range_list}
|
||||
{SUBST_TYPE unsigned_range_list}
|
||||
{SUBST_TYPE int_range_list}
|
||||
{SUBST_TYPE real_range_list}
|
||||
{SUBST_TYPE terrain_code}
|
||||
{SUBST_TYPE terrain_list}
|
||||
|
@ -471,4 +498,4 @@
|
|||
{DATA_TAG args 0 1 any}
|
||||
[/tag]
|
||||
[/tag]
|
||||
[/wml_schema]
|
||||
[/wml_schema]
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
max=0
|
||||
|
||||
# PART 1: Filters
|
||||
{SIMPLE_KEY value range_list}
|
||||
{SIMPLE_KEY value_second range_list}
|
||||
{SIMPLE_KEY value unsigned_range_list}
|
||||
{SIMPLE_KEY value_second unsigned_range_list}
|
||||
{SIMPLE_KEY terrain_type terrain_list}
|
||||
{SIMPLE_KEY direction dir_list}
|
||||
{SIMPLE_KEY frequency int}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
git doesn't allow adding empty directories; delete this once any test is added
|
49
data/test/scenarios/lua_tests/stringx/test_parse_range.cfg
Normal file
49
data/test/scenarios/lua_tests/stringx/test_parse_range.cfg
Normal file
|
@ -0,0 +1,49 @@
|
|||
# wmllint: no translatables
|
||||
|
||||
#####
|
||||
# API(s) being tested: stringx.parse_range
|
||||
##
|
||||
# Actions:
|
||||
# Directly call this API function, check that it converts strings to the expected numbers.
|
||||
##
|
||||
# Expected end state:
|
||||
# No asserts failed.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST "test_parse_range" (
|
||||
[event]
|
||||
name = start
|
||||
[lua]
|
||||
code = <<
|
||||
local lo,hi = stringx.parse_range("1.5-2.5", false)
|
||||
unit_test.assert_equal(lo, 1, "positive int: low number didn't match")
|
||||
unit_test.assert_equal(hi, 2, "positive int: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("1.5-2.5", true)
|
||||
unit_test.assert_approx_equal(lo, 1.5, 0.0001, "positive double: low number didn't match")
|
||||
unit_test.assert_approx_equal(hi, 2.5, 0.0001, "positive double: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("-3.5--2.5", true)
|
||||
unit_test.assert_approx_equal(lo, -3.5, 0.0001, "negative double: low number didn't match")
|
||||
unit_test.assert_approx_equal(hi, -2.5, 0.0001, "negative double: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("0-infinity", false)
|
||||
unit_test.assert_equal(lo, 0, "int to infinity: low number didn't match")
|
||||
unit_test.assert_greater(hi, 1000000, "int to infinity: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("0-infinity", true)
|
||||
unit_test.assert_equal(lo, 0, "double to infinity: low number didn't match")
|
||||
unit_test.assert_greater(hi, 1000000, "double to infinity: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("-infinity--1", false)
|
||||
unit_test.assert_less(lo, -1000000, "int from infinity: low number didn't match")
|
||||
unit_test.assert_equal(hi, -1, "int from infinity: high number didn't match")
|
||||
|
||||
lo,hi = stringx.parse_range("-infinity--1", true)
|
||||
unit_test.assert_less(lo, -1000000, "double from infinity: low number didn't match")
|
||||
unit_test.assert_approx_equal(hi, -1, 0.0001, "double from infinity: high number didn't match")
|
||||
>>
|
||||
[/lua]
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
|
@ -1138,6 +1138,9 @@
|
|||
<Unit filename="../../src/units/udisplay.hpp" />
|
||||
<Unit filename="../../src/units/unit.cpp" />
|
||||
<Unit filename="../../src/units/unit.hpp" />
|
||||
<Unit filename="../../src/utils/any.hpp" />
|
||||
<Unit filename="../../src/utils/config_filters.cpp" />
|
||||
<Unit filename="../../src/utils/config_filters.hpp" />
|
||||
<Unit filename="../../src/utils/const_clone.hpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.cpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.hpp" />
|
||||
|
|
|
@ -1199,6 +1199,7 @@
|
|||
<Unit filename="../../src/tests/test_commandline_options.cpp" />
|
||||
<Unit filename="../../src/tests/test_config.cpp" />
|
||||
<Unit filename="../../src/tests/test_config_cache.cpp" />
|
||||
<Unit filename="../../src/tests/test_config_filters.cpp" />
|
||||
<Unit filename="../../src/tests/test_filesystem.cpp" />
|
||||
<Unit filename="../../src/tests/test_formula_core.cpp" />
|
||||
<Unit filename="../../src/tests/test_formula_function.cpp" />
|
||||
|
@ -1278,6 +1279,8 @@
|
|||
<Unit filename="../../src/units/unit.hpp" />
|
||||
<Unit filename="../../src/units/unit_alignments.hpp" />
|
||||
<Unit filename="../../src/utils/any.hpp" />
|
||||
<Unit filename="../../src/utils/config_filters.cpp" />
|
||||
<Unit filename="../../src/utils/config_filters.hpp" />
|
||||
<Unit filename="../../src/utils/const_clone.hpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.cpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.hpp" />
|
||||
|
|
|
@ -1249,6 +1249,9 @@
|
|||
<Unit filename="../../src/units/unit.cpp" />
|
||||
<Unit filename="../../src/units/unit.hpp" />
|
||||
<Unit filename="../../src/units/unit_alignments.hpp" />
|
||||
<Unit filename="../../src/utils/any.hpp" />
|
||||
<Unit filename="../../src/utils/config_filters.cpp" />
|
||||
<Unit filename="../../src/utils/config_filters.hpp" />
|
||||
<Unit filename="../../src/utils/const_clone.hpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.cpp" />
|
||||
<Unit filename="../../src/utils/context_free_grammar_generator.hpp" />
|
||||
|
|
|
@ -888,6 +888,10 @@
|
|||
91B622191B76C0A600B00E0F /* libpangoft2-1.0.0.dylib in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = EC5C243018EF07B4001FA499 /* libpangoft2-1.0.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
91B6221A1B76C0A600B00E0F /* libpixman-1.0.dylib in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = B513B2280ED36BFB0006E551 /* libpixman-1.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
91B6221B1B76C0A600B00E0F /* libpng16.16.dylib in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = EC5C243118EF07B4001FA499 /* libpng16.16.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
91BE22982A826AC700864F64 /* test_config_filters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BE22972A826AC700864F64 /* test_config_filters.cpp */; };
|
||||
91BE229E2A826AF600864F64 /* config_filters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BE229D2A826AF600864F64 /* config_filters.cpp */; };
|
||||
91BE22A42A826AFA00864F64 /* config_filters.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 91BE22A32A826AFA00864F64 /* config_filters.hpp */; };
|
||||
91BE22C62A83BBB700864F64 /* config_filters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91BE229D2A826AF600864F64 /* config_filters.cpp */; };
|
||||
91C548C31D8866ED00FE6A7B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D2A99514DAED0E00CAFF31 /* CoreFoundation.framework */; };
|
||||
91C548DE1D886E0A00FE6A7B /* config.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599AA80EC62181008DD061 /* config.cpp */; };
|
||||
91C548DF1D886E2100FE6A7B /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5599A020EC62181008DD061 /* log.cpp */; };
|
||||
|
@ -1533,14 +1537,14 @@
|
|||
000000000000000000000004 /* achievements_dialog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = achievements_dialog.hpp; sourceTree = "<group>"; };
|
||||
000000000000000000000009 /* network_download_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = network_download_file.cpp; sourceTree = "<group>"; };
|
||||
000000000000000000000010 /* network_download_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = network_download_file.hpp; sourceTree = "<group>"; };
|
||||
00574699A982AA23F12B39E0 /* edit_pbl_translation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = edit_pbl_translation.cpp; path = edit_pbl_translation.cpp; sourceTree = "<group>"; };
|
||||
09A440B1A671C45BE2924FB4 /* carryover_show_gold.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = carryover_show_gold.cpp; path = carryover_show_gold.cpp; sourceTree = "<group>"; };
|
||||
00574699A982AA23F12B39E0 /* edit_pbl_translation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = edit_pbl_translation.cpp; sourceTree = "<group>"; };
|
||||
09A440B1A671C45BE2924FB4 /* carryover_show_gold.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = carryover_show_gold.cpp; sourceTree = "<group>"; };
|
||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
1234567890ABCDEF12345680 /* file_progress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_progress.cpp; sourceTree = "<group>"; };
|
||||
1234567890ABCDEF12345681 /* file_progress.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = file_progress.hpp; sourceTree = "<group>"; };
|
||||
1C58BBDF21822A930078D25A /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
20E644DC98F26C756364EC2C /* choose_addon.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = choose_addon.cpp; path = choose_addon.cpp; sourceTree = "<group>"; };
|
||||
27764FB68F02032F1C0B6748 /* statistics_record.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = statistics_record.cpp; path = statistics_record.cpp; sourceTree = "<group>"; };
|
||||
20E644DC98F26C756364EC2C /* choose_addon.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = choose_addon.cpp; sourceTree = "<group>"; };
|
||||
27764FB68F02032F1C0B6748 /* statistics_record.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = statistics_record.cpp; sourceTree = "<group>"; };
|
||||
460D897824DC7830000B1ABC /* game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = game.cpp; path = ../../src/server/wesnothd/game.cpp; sourceTree = SOURCE_ROOT; };
|
||||
460D897924DC7830000B1ABC /* ban.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ban.hpp; path = ../../src/server/wesnothd/ban.hpp; sourceTree = SOURCE_ROOT; };
|
||||
460D897A24DC7830000B1ABC /* player_network.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = player_network.hpp; path = ../../src/server/wesnothd/player_network.hpp; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -2159,8 +2163,8 @@
|
|||
62D24F311519987400350848 /* context_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = context_manager.cpp; sourceTree = "<group>"; };
|
||||
62D24F331519994300350848 /* palette_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = palette_manager.hpp; sourceTree = "<group>"; };
|
||||
62D24F341519995200350848 /* palette_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = palette_manager.cpp; sourceTree = "<group>"; };
|
||||
67044415B63F5888193BD7A6 /* prompt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = prompt.cpp; path = prompt.cpp; sourceTree = "<group>"; };
|
||||
6FA542D78393E8FF067775DA /* edit_pbl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = edit_pbl.cpp; path = edit_pbl.cpp; sourceTree = "<group>"; };
|
||||
67044415B63F5888193BD7A6 /* prompt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = prompt.cpp; sourceTree = "<group>"; };
|
||||
6FA542D78393E8FF067775DA /* edit_pbl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = edit_pbl.cpp; sourceTree = "<group>"; };
|
||||
84234C54BB84519421FD4136 /* general.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = general.cpp; sourceTree = "<group>"; };
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8D1107320486CEB800E47090 /* The Battle for Wesnoth.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "The Battle for Wesnoth.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -2309,6 +2313,9 @@
|
|||
91B621EF1B76BB3200B00E0F /* mapgen_lua_kernel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = mapgen_lua_kernel.hpp; sourceTree = "<group>"; };
|
||||
91B621F01B76BB3500B00E0F /* push_check.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = push_check.hpp; sourceTree = "<group>"; };
|
||||
91B621F11B76BBC500B00E0F /* rect.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rect.hpp; sourceTree = "<group>"; };
|
||||
91BE22972A826AC700864F64 /* test_config_filters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_config_filters.cpp; sourceTree = "<group>"; };
|
||||
91BE229D2A826AF600864F64 /* config_filters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_filters.cpp; sourceTree = "<group>"; };
|
||||
91BE22A32A826AFA00864F64 /* config_filters.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config_filters.hpp; sourceTree = "<group>"; };
|
||||
91BE78371DD8F5DE00528C21 /* catalog.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catalog.hpp; sourceTree = "<group>"; };
|
||||
91BE78381DD8F5DE00528C21 /* catalog_metadata.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catalog_metadata.hpp; sourceTree = "<group>"; };
|
||||
91BE78391DD8F5DE00528C21 /* default_plural_forms_compiler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = default_plural_forms_compiler.hpp; sourceTree = "<group>"; };
|
||||
|
@ -2356,7 +2363,7 @@
|
|||
95EB8A51287A02B800B09F95 /* draw_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = draw_manager.hpp; sourceTree = "<group>"; };
|
||||
95EB8A53287A02EC00B09F95 /* top_level_drawable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = top_level_drawable.hpp; path = gui/core/top_level_drawable.hpp; sourceTree = "<group>"; };
|
||||
95EB8A54287A02EC00B09F95 /* top_level_drawable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = top_level_drawable.cpp; path = gui/core/top_level_drawable.cpp; sourceTree = "<group>"; };
|
||||
B2CC45FEA71445AE817CAA6B /* edit_pbl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = edit_pbl.hpp; path = edit_pbl.hpp; sourceTree = "<group>"; };
|
||||
B2CC45FEA71445AE817CAA6B /* edit_pbl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = edit_pbl.hpp; sourceTree = "<group>"; };
|
||||
B508D191100146E300B12852 /* engine_fai.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = engine_fai.cpp; sourceTree = "<group>"; };
|
||||
B508D192100146E300B12852 /* engine_fai.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = engine_fai.hpp; sourceTree = "<group>"; };
|
||||
B513B2270ED36BFB0006E551 /* libcairo.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcairo.2.dylib; path = lib/libcairo.2.dylib; sourceTree = "<group>"; };
|
||||
|
@ -2679,9 +2686,9 @@
|
|||
B5BB6EFD0F93B83500444FBF /* SDLMain.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = SDLMain.nib; path = Resources/SDLMain.nib; sourceTree = "<group>"; };
|
||||
B5CE46F712A0417D00D665EE /* side_filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = side_filter.cpp; sourceTree = "<group>"; };
|
||||
B5CE46F812A0417D00D665EE /* side_filter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = side_filter.hpp; sourceTree = "<group>"; };
|
||||
C679447D91FD3623CC852FF8 /* edit_pbl_translation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = edit_pbl_translation.hpp; path = edit_pbl_translation.hpp; sourceTree = "<group>"; };
|
||||
D4594633BF3F8A06D6AE752F /* prompt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = prompt.hpp; path = prompt.hpp; sourceTree = "<group>"; };
|
||||
D9A141EAAE90E98B6F6171D6 /* choose_addon.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = choose_addon.hpp; path = choose_addon.hpp; sourceTree = "<group>"; };
|
||||
C679447D91FD3623CC852FF8 /* edit_pbl_translation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = edit_pbl_translation.hpp; sourceTree = "<group>"; };
|
||||
D4594633BF3F8A06D6AE752F /* prompt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prompt.hpp; sourceTree = "<group>"; };
|
||||
D9A141EAAE90E98B6F6171D6 /* choose_addon.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = choose_addon.hpp; sourceTree = "<group>"; };
|
||||
EC0341DF1ECF46FE000F2E2B /* config_attribute_value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config_attribute_value.cpp; sourceTree = "<group>"; };
|
||||
EC0341E01ECF46FE000F2E2B /* config_attribute_value.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config_attribute_value.hpp; sourceTree = "<group>"; };
|
||||
EC0680231EA920A300EEE03B /* random_deterministic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = random_deterministic.cpp; sourceTree = "<group>"; };
|
||||
|
@ -4388,6 +4395,8 @@
|
|||
91EF6BF01C9E217C00E2A733 /* utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
91BE229D2A826AF600864F64 /* config_filters.cpp */,
|
||||
91BE22A32A826AFA00864F64 /* config_filters.hpp */,
|
||||
91EF6BFC1C9E22E400E2A733 /* const_clone.hpp */,
|
||||
91C55DA21CC078820040012E /* context_free_grammar_generator.cpp */,
|
||||
91C55DA31CC078820040012E /* context_free_grammar_generator.hpp */,
|
||||
|
@ -4746,6 +4755,7 @@
|
|||
91E356091CACA6CB00774252 /* test_addons.cpp */,
|
||||
91E3560A1CACA6CB00774252 /* test_commandline_options.cpp */,
|
||||
B597C4A80FACD42E00CE81F5 /* test_config_cache.cpp */,
|
||||
91BE22972A826AC700864F64 /* test_config_filters.cpp */,
|
||||
91E3560B1CACA6CB00774252 /* test_config.cpp */,
|
||||
91E3560C1CACA6CB00774252 /* test_filesystem.cpp */,
|
||||
91DCA68F1C9360610030F8D0 /* test_formula_core.cpp */,
|
||||
|
@ -5057,6 +5067,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
91BE22A42A826AFA00864F64 /* config_filters.hpp in Headers */,
|
||||
BC624F2F923836331DCFD078 /* choose_addon.hpp in Headers */,
|
||||
E2F24C0CBC863C3DC8A041EF /* edit_pbl.hpp in Headers */,
|
||||
9B4B41D29C90B05F03DE21B0 /* edit_pbl_translation.hpp in Headers */,
|
||||
|
@ -5751,6 +5762,7 @@
|
|||
B5599AEB0EC62181008DD061 /* id.cpp in Sources */,
|
||||
B5599AEA0EC62181008DD061 /* map.cpp in Sources */,
|
||||
6295C3D9150FC9EB0077D8C5 /* unit_palette.cpp in Sources */,
|
||||
91BE229E2A826AF600864F64 /* config_filters.cpp in Sources */,
|
||||
B5599AE90EC62181008DD061 /* types.cpp in Sources */,
|
||||
B5599AF00EC62181008DD061 /* unit.cpp in Sources */,
|
||||
B553B6BA12189C5900CC8C58 /* utility.cpp in Sources */,
|
||||
|
@ -6022,6 +6034,7 @@
|
|||
46E2D99025022BF5003D99F3 /* lua_widget.cpp in Sources */,
|
||||
91E356811CACC71A00774252 /* save_index.cpp in Sources */,
|
||||
91E356821CACC71A00774252 /* saved_game.cpp in Sources */,
|
||||
91BE22C62A83BBB700864F64 /* config_filters.cpp in Sources */,
|
||||
46BDBBEB217C6F6200D2820C /* lua_terrainfilter.cpp in Sources */,
|
||||
46F92E0C2174F6A400602C1C /* formula_debugger.cpp in Sources */,
|
||||
91E356831CACC71A00774252 /* savegame.cpp in Sources */,
|
||||
|
@ -6359,6 +6372,7 @@
|
|||
91A215711CAD6E7500927AEA /* manager.cpp in Sources */,
|
||||
46BDBBED217C6F6200D2820C /* lua_terrainmap.cpp in Sources */,
|
||||
91A215721CAD6E7500927AEA /* mapgen_lua_kernel.cpp in Sources */,
|
||||
91BE22982A826AC700864F64 /* test_config_filters.cpp in Sources */,
|
||||
46F92E662174F6A400602C1C /* tree_view.cpp in Sources */,
|
||||
46F92E9E2174F6A400602C1C /* menu_button.cpp in Sources */,
|
||||
46F92DAE2174F6A300602C1C /* gamestate_inspector.cpp in Sources */,
|
||||
|
|
|
@ -7,6 +7,7 @@ tests/test_addons.cpp
|
|||
tests/test_commandline_options.cpp
|
||||
tests/test_config.cpp
|
||||
tests/test_config_cache.cpp
|
||||
tests/test_config_filters.cpp
|
||||
tests/test_filesystem.cpp
|
||||
tests/test_formula_core.cpp
|
||||
tests/test_formula_function.cpp
|
||||
|
|
|
@ -393,6 +393,7 @@ units/race.cpp
|
|||
units/types.cpp
|
||||
units/udisplay.cpp
|
||||
units/unit.cpp
|
||||
utils/config_filters.cpp
|
||||
utils/context_free_grammar_generator.cpp
|
||||
utils/irdya_datetime.cpp
|
||||
utils/markov_generator.cpp
|
||||
|
|
|
@ -47,7 +47,7 @@ static lg::log_domain log_wml("wml");
|
|||
namespace game_events {
|
||||
|
||||
namespace builtin_conditions {
|
||||
std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-infinity");
|
||||
std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-infinity");
|
||||
|
||||
bool have_unit(const vconfig& cfg)
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ namespace builtin_conditions {
|
|||
return false;
|
||||
}
|
||||
std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
|
||||
? utils::parse_ranges(cfg["count"]) : default_counts;
|
||||
? utils::parse_ranges_unsigned(cfg["count"]) : default_counts;
|
||||
int match_count = 0;
|
||||
const unit_filter ufilt(cfg);
|
||||
for(const unit &i : resources::gameboard->units()) {
|
||||
|
@ -92,7 +92,7 @@ namespace builtin_conditions {
|
|||
terrain_filter(cfg, resources::filter_con, false).get_locations(res);
|
||||
|
||||
std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
|
||||
? utils::parse_ranges(cfg["count"]) : default_counts;
|
||||
? utils::parse_ranges_unsigned(cfg["count"]) : default_counts;
|
||||
return in_ranges<int>(res.size(), counts);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <cassert>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
@ -822,24 +823,59 @@ std::vector<std::string> quoted_split(const std::string& val, char c, int flags,
|
|||
return res;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Internal common code for parse_range and parse_range_real.
|
||||
*
|
||||
* If str contains two elements and a separator such as "a-b", returns a and b.
|
||||
* Otherwise, returns the original string and std::nullopt.
|
||||
*/
|
||||
std::pair<std::string, std::optional<std::string>> parse_range_internal_separator(const std::string& str)
|
||||
{
|
||||
// If turning this into a list with additional options, ensure that "-" (if present) is last. Otherwise a
|
||||
// range such as "-2..-1" might be incorrectly split as "-2..", "1".
|
||||
static const auto separator = std::string{"-"};
|
||||
|
||||
// Starting from the second character means that it won't interpret the minus
|
||||
// sign on a negative number as the separator.
|
||||
// No need to check the string length first, as str.find() already does that.
|
||||
auto pos = str.find(separator, 1);
|
||||
auto length = separator.size();
|
||||
|
||||
if(pos != std::string::npos && pos + length < str.size()) {
|
||||
return {str.substr(0, pos), str.substr(pos + length)};
|
||||
}
|
||||
|
||||
return {str, std::nullopt};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::pair<int, int> parse_range(const std::string& str)
|
||||
{
|
||||
const std::string::const_iterator dash = std::find(str.begin(), str.end(), '-');
|
||||
const std::string a(str.begin(), dash);
|
||||
const std::string b = dash != str.end() ? std::string(dash + 1, str.end()) : a;
|
||||
std::pair<int,int> res {0,0};
|
||||
auto [a, b] = parse_range_internal_separator(str);
|
||||
std::pair<int, int> res{0, 0};
|
||||
try {
|
||||
if (b == "infinity") {
|
||||
res = std::pair(std::stoi(a), std::numeric_limits<int>::max());
|
||||
if(a == "-infinity" && b) {
|
||||
// The "&& b" is so that we treat parse_range("-infinity") the same as parse_range("infinity"),
|
||||
// both of those will report an invalid range.
|
||||
res.first = std::numeric_limits<int>::min();
|
||||
} else {
|
||||
res = std::pair(std::stoi(a), std::stoi(b));
|
||||
res.first = std::stoi(a);
|
||||
}
|
||||
|
||||
if (res.second < res.first) {
|
||||
if(!b) {
|
||||
res.second = res.first;
|
||||
} else if(*b == "infinity") {
|
||||
res.second = std::numeric_limits<int>::max();
|
||||
} else {
|
||||
res.second = std::stoi(*b);
|
||||
if(res.second < res.first) {
|
||||
res.second = res.first;
|
||||
}
|
||||
}
|
||||
} catch(const std::invalid_argument&) {
|
||||
ERR_GENERAL << "Invalid range: "<< str;
|
||||
ERR_GENERAL << "Invalid range: " << str;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -847,32 +883,42 @@ std::pair<int, int> parse_range(const std::string& str)
|
|||
|
||||
std::pair<double, double> parse_range_real(const std::string& str)
|
||||
{
|
||||
const std::string::const_iterator dash = std::find(str.begin(), str.end(), '-');
|
||||
const std::string a(str.begin(), dash);
|
||||
const std::string b = dash != str.end() ? std::string(dash + 1, str.end()) : a;
|
||||
std::pair<double,double> res {0,0};
|
||||
auto [a, b] = parse_range_internal_separator(str);
|
||||
std::pair<double, double> res{0, 0};
|
||||
try {
|
||||
if(b == "infinity") {
|
||||
res = std::pair(std::stod(a), std::numeric_limits<double>::infinity());
|
||||
if(a == "-infinity" && b) {
|
||||
// There's already a static-assert for is_iec559 in random.cpp, so this isn't limiting the architectures
|
||||
// that Wesnoth can run on.
|
||||
static_assert(std::numeric_limits<double>::is_iec559,
|
||||
"Don't know how negative infinity is treated on this architecture");
|
||||
res.first = -std::numeric_limits<double>::infinity();
|
||||
} else {
|
||||
res = std::pair(std::stod(a), std::stod(b));
|
||||
res.first = std::stod(a);
|
||||
}
|
||||
|
||||
if(res.second < res.first) {
|
||||
if(!b) {
|
||||
res.second = res.first;
|
||||
} else if(*b == "infinity") {
|
||||
res.second = std::numeric_limits<double>::infinity();
|
||||
} else {
|
||||
res.second = std::stod(*b);
|
||||
if(res.second < res.first) {
|
||||
res.second = res.first;
|
||||
}
|
||||
}
|
||||
} catch(const std::invalid_argument&) {
|
||||
ERR_GENERAL << "Invalid range: "<< str;
|
||||
ERR_GENERAL << "Invalid range: " << str;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> parse_ranges(const std::string& str)
|
||||
std::vector<std::pair<int, int>> parse_ranges_unsigned(const std::string& str)
|
||||
{
|
||||
std::vector<std::pair<int, int>> to_return;
|
||||
for(const std::string& r : utils::split(str)) {
|
||||
to_return.push_back(parse_range(r));
|
||||
auto to_return = parse_ranges_int(str);
|
||||
if(std::any_of(to_return.begin(), to_return.end(), [](const std::pair<int, int>& r) { return r.first < 0; })) {
|
||||
ERR_GENERAL << "Invalid range (expected values to be zero or positive): " << str;
|
||||
return {};
|
||||
}
|
||||
|
||||
return to_return;
|
||||
|
@ -888,6 +934,16 @@ std::vector<std::pair<double, double>> parse_ranges_real(const std::string& str)
|
|||
return to_return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> parse_ranges_int(const std::string& str)
|
||||
{
|
||||
std::vector<std::pair<int, int>> to_return;
|
||||
for(const std::string& r : utils::split(str)) {
|
||||
to_return.push_back(parse_range(r));
|
||||
}
|
||||
|
||||
return to_return;
|
||||
}
|
||||
|
||||
void ellipsis_truncate(std::string& str, const std::size_t size)
|
||||
{
|
||||
const std::size_t prev_size = str.length();
|
||||
|
|
|
@ -282,10 +282,41 @@ std::string bullet_list(const T& v, std::size_t indent = 4, const std::string& b
|
|||
*/
|
||||
std::string indent(const std::string& string, std::size_t indent_size = 4);
|
||||
|
||||
/**
|
||||
* Recognises the following patterns, and returns a {min, max} pair.
|
||||
*
|
||||
* * "1" returns {1, 1}
|
||||
* * "1-3" returns {1, 3}
|
||||
* * "1-infinity" returns {1, maximum int}
|
||||
* * "-1" returns {-1, -1}
|
||||
* * "-3--1" returns {-3, -1}
|
||||
*
|
||||
* Note that:
|
||||
*
|
||||
* * "3-1" returns {3, 3} and does not log an error
|
||||
* * "-1--3" returns {-1, -1} and does not log an error
|
||||
* * Although "-infinity--1", "2-infinity" and "-infinity-infinity" are all supported,
|
||||
* * ranges that can't match a reasonable number, e.g. "-infinity" or "infinity..infinity", may be treated as errors.
|
||||
*/
|
||||
std::pair<int, int> parse_range(const std::string& str);
|
||||
|
||||
std::vector<std::pair<int, int>> parse_ranges(const std::string& str);
|
||||
/**
|
||||
* Handles a comma-separated list of inputs to parse_range, in a context that does not expect
|
||||
* negative values. Will return an empty list if any of the ranges have a minimum that's below
|
||||
* zero.
|
||||
*/
|
||||
std::vector<std::pair<int, int>> parse_ranges_unsigned(const std::string& str);
|
||||
|
||||
/**
|
||||
* Handles a comma-separated list of inputs to parse_range.
|
||||
*/
|
||||
std::vector<std::pair<int, int>> parse_ranges_int(const std::string& str);
|
||||
|
||||
/**
|
||||
* Recognises similar patterns to parse_range, and returns a {min, max} pair.
|
||||
*
|
||||
* For this function, "infinity" results in std::numeric_limits<double>::infinity.
|
||||
*/
|
||||
std::pair<double, double> parse_range_real(const std::string& str);
|
||||
|
||||
std::vector<std::pair<double, double>> parse_ranges_real(const std::string& str);
|
||||
|
|
|
@ -73,7 +73,7 @@ std::vector<int> side_filter::get_teams() const
|
|||
|
||||
static bool check_side_number(const team &t, const std::string &str)
|
||||
{
|
||||
std::vector<std::pair<int,int>> ranges = utils::parse_ranges(str);
|
||||
std::vector<std::pair<int,int>> ranges = utils::parse_ranges_unsigned(str);
|
||||
int side_number = t.side();
|
||||
|
||||
std::vector<std::pair<int,int>>::const_iterator range, range_end = ranges.end();
|
||||
|
|
|
@ -249,9 +249,9 @@ bool terrain_filter::match_internal(const map_location& loc, const unit* ref_uni
|
|||
}
|
||||
}
|
||||
}
|
||||
static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-6");
|
||||
static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6");
|
||||
std::vector<std::pair<int,int>> counts = (*i).has_attribute("count")
|
||||
? utils::parse_ranges((*i)["count"]) : default_counts;
|
||||
? utils::parse_ranges_unsigned((*i)["count"]) : default_counts;
|
||||
if(!in_ranges(match_count, counts)) {
|
||||
return false;
|
||||
}
|
||||
|
|
87
src/tests/test_config_filters.cpp
Normal file
87
src/tests/test_config_filters.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright (C) 2023
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth-test"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
// Work around a Boost<1.67 bug fixed in 1.67: Include test_case.hpp after
|
||||
// unit_tests.hpp. https://svn.boost.org/trac10/ticket/13387
|
||||
#include <boost/test/data/test_case.hpp> // for parametrized test
|
||||
|
||||
#include "utils/config_filters.hpp"
|
||||
|
||||
using namespace utils::config_filters;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(config_filters)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_int_positive_filter)
|
||||
{
|
||||
// These can be used both as the filter and as the input to be filtered,
|
||||
// but for the unsigned_matches_if_present function only one of them is
|
||||
// a valid filter.
|
||||
config minus_3 {"x", -3};
|
||||
config plus_3 {"x", 3};
|
||||
|
||||
BOOST_ASSERT(!unsigned_matches_if_present(minus_3, minus_3, "x"));
|
||||
BOOST_ASSERT(unsigned_matches_if_present(plus_3, plus_3, "x"));
|
||||
|
||||
BOOST_ASSERT(!unsigned_matches_if_present(minus_3, plus_3, "x"));
|
||||
BOOST_ASSERT(!unsigned_matches_if_present(plus_3, minus_3, "x"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_int_signed_filter)
|
||||
{
|
||||
// These can be used both as the filter and as the input to be filtered.
|
||||
config minus_3 {"x", -3};
|
||||
config plus_3 {"x", 3};
|
||||
|
||||
config any_negative {"x", "-infinity--1"};
|
||||
config any_positive {"x", "1-infinity"};
|
||||
config any_number {"x", "-infinity-infinity"};
|
||||
|
||||
BOOST_ASSERT(int_matches_if_present(minus_3, minus_3, "x"));
|
||||
BOOST_ASSERT(int_matches_if_present(plus_3, plus_3, "x"));
|
||||
|
||||
BOOST_ASSERT(!int_matches_if_present(minus_3, plus_3, "x"));
|
||||
BOOST_ASSERT(!int_matches_if_present(plus_3, minus_3, "x"));
|
||||
|
||||
BOOST_ASSERT(int_matches_if_present(any_negative, minus_3, "x"));
|
||||
BOOST_ASSERT(!int_matches_if_present(any_positive, minus_3, "x"));
|
||||
BOOST_ASSERT(int_matches_if_present(any_number, minus_3, "x"));
|
||||
|
||||
BOOST_ASSERT(!int_matches_if_present(any_negative, plus_3, "x"));
|
||||
BOOST_ASSERT(int_matches_if_present(any_positive, plus_3, "x"));
|
||||
BOOST_ASSERT(int_matches_if_present(any_number, plus_3, "x"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_int_add_sub_filter)
|
||||
{
|
||||
config add_minus_3 {"add", -3};
|
||||
config sub_3 {"sub", 3};
|
||||
|
||||
BOOST_ASSERT(int_matches_if_present(add_minus_3, add_minus_3, "add"));
|
||||
BOOST_ASSERT(int_matches_if_present_or_negative(add_minus_3, add_minus_3, "add", "sub"));
|
||||
BOOST_ASSERT(int_matches_if_present(add_minus_3, add_minus_3, "some_other_key"));
|
||||
|
||||
// A range, and one that only matches positive numbers
|
||||
config add_2_to_4 {"add", "2-4"};
|
||||
config sub_2_to_4 {"sub", "2-4"};
|
||||
|
||||
BOOST_ASSERT(!int_matches_if_present(add_2_to_4, add_minus_3, "add"));
|
||||
BOOST_ASSERT(int_matches_if_present(add_2_to_4, add_minus_3, "some_other_key"));
|
||||
BOOST_ASSERT(!int_matches_if_present(sub_2_to_4, add_minus_3, "sub"));
|
||||
BOOST_ASSERT(int_matches_if_present_or_negative(sub_2_to_4, add_minus_3, "sub", "add"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -389,7 +389,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
|
|||
if (i["count"].empty() && count != dirs.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
|
||||
if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +414,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
|
|||
if (i["count"].empty() && count != dirs.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
|
||||
if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1690,7 +1690,7 @@ bool attack_type::special_active_impl(
|
|||
if (i["count"].empty() && count != dirs.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
|
||||
if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1713,7 +1713,7 @@ bool attack_type::special_active_impl(
|
|||
if (i["count"].empty() && count != dirs.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
|
||||
if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,22 +123,22 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
|
||||
return false;
|
||||
|
||||
if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges(filter_damage)) )
|
||||
if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges_unsigned(filter_damage)) )
|
||||
return false;
|
||||
|
||||
if (!filter_attacks.empty() && !in_ranges(attack.num_attacks(), utils::parse_ranges(filter_attacks)))
|
||||
if (!filter_attacks.empty() && !in_ranges(attack.num_attacks(), utils::parse_ranges_unsigned(filter_attacks)))
|
||||
return false;
|
||||
|
||||
if (!filter_accuracy.empty() && !in_ranges(attack.accuracy(), utils::parse_ranges(filter_accuracy)))
|
||||
if (!filter_accuracy.empty() && !in_ranges(attack.accuracy(), utils::parse_ranges_int(filter_accuracy)))
|
||||
return false;
|
||||
|
||||
if (!filter_parry.empty() && !in_ranges(attack.parry(), utils::parse_ranges(filter_parry)))
|
||||
if (!filter_parry.empty() && !in_ranges(attack.parry(), utils::parse_ranges_int(filter_parry)))
|
||||
return false;
|
||||
|
||||
if (!filter_movement.empty() && !in_ranges(attack.movement_used(), utils::parse_ranges(filter_movement)))
|
||||
if (!filter_movement.empty() && !in_ranges(attack.movement_used(), utils::parse_ranges_unsigned(filter_movement)))
|
||||
return false;
|
||||
|
||||
if (!filter_attacks_used.empty() && !in_ranges(attack.attacks_used(), utils::parse_ranges(filter_attacks_used)))
|
||||
if (!filter_attacks_used.empty() && !in_ranges(attack.attacks_used(), utils::parse_ranges_unsigned(filter_attacks_used)))
|
||||
return false;
|
||||
|
||||
if ( !filter_name.empty() && std::find(filter_name.begin(), filter_name.end(), attack.id()) == filter_name.end() )
|
||||
|
|
|
@ -151,9 +151,9 @@ struct unit_filter_adjacent : public unit_filter_base
|
|||
++match_count;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-6");
|
||||
static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6");
|
||||
config::attribute_value i_count = cfg_["count"];
|
||||
return in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts);
|
||||
return in_ranges(match_count, !i_count.blank() ? utils::parse_ranges_unsigned(i_count) : default_counts);
|
||||
}
|
||||
|
||||
const unit_filter_compound child_;
|
||||
|
@ -603,7 +603,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["recall_cost"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
for(auto cost : ranges) {
|
||||
|
@ -616,7 +616,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["level"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
for(auto lvl : ranges) {
|
||||
|
@ -629,7 +629,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["defense"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
|
||||
|
@ -643,7 +643,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["movement_cost"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
|
||||
|
@ -657,7 +657,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["vision_cost"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
|
||||
|
@ -671,7 +671,7 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
);
|
||||
|
||||
create_attribute(literal["jamming_cost"],
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
|
||||
[](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
|
||||
[](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
|
||||
{
|
||||
int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "units/id.hpp"
|
||||
#include "units/map.hpp" // for unit_map, etc
|
||||
#include "units/types.hpp"
|
||||
#include "utils/config_filters.hpp"
|
||||
#include "variable.hpp" // for vconfig, etc
|
||||
|
||||
#include <cassert> // for assert
|
||||
|
@ -1420,52 +1421,6 @@ void unit::remove_ability_by_id(const std::string& ability)
|
|||
}
|
||||
}
|
||||
|
||||
static bool bool_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, bool def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return filter[attribute].to_bool() == cfg[attribute].to_bool(def);
|
||||
}
|
||||
|
||||
static bool string_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, const std::string& def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::string> filter_attribute = utils::split(filter[attribute]);
|
||||
return ( std::find(filter_attribute.begin(), filter_attribute.end(), cfg[attribute].str(def)) != filter_attribute.end() );
|
||||
}
|
||||
|
||||
static bool int_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(cfg[attribute].empty() && (attribute == "add" || attribute == "sub")){
|
||||
if(attribute == "add"){
|
||||
return in_ranges<int>(-cfg["sub"].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
} else if(attribute == "sub"){
|
||||
return in_ranges<int>(-cfg["add"].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return in_ranges<int>(cfg[attribute].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
}
|
||||
|
||||
static bool double_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_ranges<double>(cfg[attribute].to_double(1), utils::parse_ranges_real(filter[attribute].str()));
|
||||
}
|
||||
|
||||
static bool type_value_if_present(const config& filter, const config& cfg)
|
||||
{
|
||||
if(filter["type_value"].empty()) {
|
||||
|
@ -1490,6 +1445,8 @@ static bool type_value_if_present(const config& filter, const config& cfg)
|
|||
|
||||
static bool matches_ability_filter(const config & cfg, const std::string& tag_name, const config & filter)
|
||||
{
|
||||
using namespace utils::config_filters;
|
||||
|
||||
if(!filter["affect_adjacent"].empty()){
|
||||
bool adjacent = cfg.has_child("affect_adjacent");
|
||||
if(filter["affect_adjacent"].to_bool() != adjacent){
|
||||
|
@ -1528,10 +1485,10 @@ static bool matches_ability_filter(const config & cfg, const std::string& tag_na
|
|||
if(!int_matches_if_present(filter, cfg, "value"))
|
||||
return false;
|
||||
|
||||
if(!int_matches_if_present(filter, cfg, "add"))
|
||||
if(!int_matches_if_present_or_negative(filter, cfg, "add", "sub"))
|
||||
return false;
|
||||
|
||||
if(!int_matches_if_present(filter, cfg, "sub"))
|
||||
if(!int_matches_if_present_or_negative(filter, cfg, "sub", "add"))
|
||||
return false;
|
||||
|
||||
if(!double_matches_if_present(filter, cfg, "multiply"))
|
||||
|
|
85
src/utils/config_filters.cpp
Normal file
85
src/utils/config_filters.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2023
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/config_filters.hpp"
|
||||
|
||||
#include "serialization/string_utils.hpp" // for utils::split
|
||||
#include "utils/math.hpp" // for in_ranges
|
||||
|
||||
bool utils::config_filters::bool_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, bool def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return filter[attribute].to_bool() == cfg[attribute].to_bool(def);
|
||||
}
|
||||
|
||||
bool utils::config_filters::string_matches_if_present(
|
||||
const config& filter, const config& cfg, const std::string& attribute, const std::string& def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::string> filter_attribute = utils::split(filter[attribute]);
|
||||
return (
|
||||
std::find(filter_attribute.begin(), filter_attribute.end(), cfg[attribute].str(def)) != filter_attribute.end());
|
||||
}
|
||||
|
||||
bool utils::config_filters::unsigned_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_ranges<int>(cfg[attribute].to_int(0), utils::parse_ranges_unsigned(filter[attribute].str()));
|
||||
}
|
||||
|
||||
bool utils::config_filters::int_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_ranges<int>(cfg[attribute].to_int(0), utils::parse_ranges_int(filter[attribute].str()));
|
||||
}
|
||||
|
||||
bool utils::config_filters::int_matches_if_present_or_negative(
|
||||
const config& filter, const config& cfg, const std::string& attribute, const std::string& opposite)
|
||||
{
|
||||
if(int_matches_if_present(filter, cfg, attribute)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't check for !cfg[opposite].empty(), as this assumes a default value of zero (similarly to
|
||||
// int_matches_if_present)
|
||||
if(cfg[attribute].empty()) {
|
||||
return in_ranges<int>(-cfg[opposite].to_int(0), utils::parse_ranges_int(filter[attribute].str()));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool utils::config_filters::double_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_ranges<double>(cfg[attribute].to_double(1), utils::parse_ranges_real(filter[attribute].str()));
|
||||
}
|
64
src/utils/config_filters.hpp
Normal file
64
src/utils/config_filters.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright (C) 2003 - 2023
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
/**
|
||||
* Utility functions for implementing [filter], [filter_ability], [filter_weapon], etc.
|
||||
*
|
||||
* For example, a filter of `x=1` puts a requirement on the value of `x` but accepts any value of `y`.
|
||||
*
|
||||
* Both `int` and `double` assume a default value of zero, so a filter that accepts zero will match an
|
||||
* unset value as well as a present value.
|
||||
*/
|
||||
namespace utils::config_filters
|
||||
{
|
||||
/**
|
||||
* Checks whether the filter matches the value of @a cfg[@a attribute]. If @a cfg doesn't have that
|
||||
* attribute, assume that an unset value is equivalent to @a def.
|
||||
*
|
||||
* Always returns true if the filter puts no restriction on the value of @a cfg[@a attribute].
|
||||
*/
|
||||
bool bool_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, bool def);
|
||||
|
||||
bool double_matches_if_present(const config& filter, const config& cfg, const std::string& attribute);
|
||||
bool int_matches_if_present(const config& filter, const config& cfg, const std::string& attribute);
|
||||
|
||||
/**
|
||||
* Restricts filters to only looking for values that are zero or more.
|
||||
*
|
||||
* A filter such as "-1-10" or "-10--1,1-10" is considered invalid and never matches anything, not
|
||||
* even a positive value that would be accepted by a stricter subset of the filter.
|
||||
*/
|
||||
bool unsigned_matches_if_present(const config& filter, const config& cfg, const std::string& attribute);
|
||||
|
||||
/**
|
||||
* Supports filters using "add" and "sub" attributes, for example a filter `add=1` matching a cfg containing either
|
||||
* `add=1` or `sub=-1`; this assumes that code elsewhere has already checked that cfg contains at most one of those
|
||||
* keys.
|
||||
*
|
||||
* This only checks for the presence of @a attribute in the filter, so the caller should call this function a second
|
||||
* time, with @a attribute and @a opposite reversed.
|
||||
*
|
||||
* The function is named "negative" in case we later want to add a "reciprocal" for the "multiply"/"divide" pair.
|
||||
*/
|
||||
bool int_matches_if_present_or_negative(
|
||||
const config& filter, const config& cfg, const std::string& attribute, const std::string& opposite);
|
||||
|
||||
bool string_matches_if_present(
|
||||
const config& filter, const config& cfg, const std::string& attribute, const std::string& def);
|
||||
|
||||
} // namespace utils::config_filters
|
|
@ -242,6 +242,7 @@
|
|||
0 test_wml_actions
|
||||
0 test_wml_conditionals
|
||||
0 lua_wml_tagnames
|
||||
0 test_parse_range
|
||||
0 test_scoped_array
|
||||
0 test_scoped_scalar
|
||||
0 as_text
|
||||
|
|
Loading…
Add table
Reference in a new issue