Add exclusive unit traits handling similar to require_amla (#7109)
This commit is contained in:
parent
a4e17ac867
commit
492c20baa6
8 changed files with 321 additions and 13 deletions
|
@ -217,6 +217,8 @@
|
||||||
{SIMPLE_KEY male_name t_string}
|
{SIMPLE_KEY male_name t_string}
|
||||||
{SIMPLE_KEY female_name t_string}
|
{SIMPLE_KEY female_name t_string}
|
||||||
{SIMPLE_KEY description t_string}
|
{SIMPLE_KEY description t_string}
|
||||||
|
{SIMPLE_KEY exclude_traits string_list}
|
||||||
|
{SIMPLE_KEY require_traits string_list}
|
||||||
{SIMPLE_KEY help_text t_string}
|
{SIMPLE_KEY help_text t_string}
|
||||||
[/tag]
|
[/tag]
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,9 @@
|
||||||
{test/scenarios/wml_tests/UnitsWML/AbilitiesWML}
|
{test/scenarios/wml_tests/UnitsWML/AbilitiesWML}
|
||||||
{test/scenarios/wml_tests/WesnothFormulaLanguage}
|
{test/scenarios/wml_tests/WesnothFormulaLanguage}
|
||||||
|
|
||||||
|
# Load test unit wml
|
||||||
|
{test/units.cfg}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DONT_RELOAD_CORE
|
#ifndef DONT_RELOAD_CORE
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
# wmllint: no translatables
|
||||||
|
|
||||||
|
#####
|
||||||
|
# API(s) being tested: [trait]exclude_traits=
|
||||||
|
##
|
||||||
|
# Actions:
|
||||||
|
# Bob is told to generate special random traits.
|
||||||
|
##
|
||||||
|
# Expected end state:
|
||||||
|
# One of the two special traits excludes another trait to be already had by Bob.
|
||||||
|
# Bob should either not have the trait or have only the excluded one.
|
||||||
|
#####
|
||||||
|
|
||||||
|
{GENERIC_UNIT_TEST "trait_exclusion_test" (
|
||||||
|
[event]
|
||||||
|
name=start
|
||||||
|
{UNIT 1 (Test Unit Exclude) 1 1 (id=excluder1)}
|
||||||
|
[/event]
|
||||||
|
[event]
|
||||||
|
name=side 1 turn 1
|
||||||
|
[store_unit]
|
||||||
|
[filter]
|
||||||
|
side=1
|
||||||
|
id=excluder1
|
||||||
|
[/filter]
|
||||||
|
variable=excluded
|
||||||
|
kill=no
|
||||||
|
[/store_unit]
|
||||||
|
[if]
|
||||||
|
# Check if the first randomly generated trait is the excluded one, then the
|
||||||
|
# next trait slot should be empty
|
||||||
|
{VARIABLE_CONDITIONAL excluded.modifications.trait[0].id equals "test_excluded"}
|
||||||
|
[then]
|
||||||
|
{ASSERT ({VARIABLE_CONDITIONAL excluded.modifications.trait[1].id equals $nil})}
|
||||||
|
[/then]
|
||||||
|
[else]
|
||||||
|
# Otherwise if it is "test_exclude" then the next slot should be empty as well
|
||||||
|
{ASSERT ({VARIABLE_CONDITIONAL excluded.modifications.trait[0].id equals "test_exclude"})}
|
||||||
|
{ASSERT ({VARIABLE_CONDITIONAL excluded.modifications.trait[1].id equals $nil})}
|
||||||
|
[/else]
|
||||||
|
[/if]
|
||||||
|
{SUCCEED}
|
||||||
|
[/event]
|
||||||
|
)}
|
||||||
|
|
||||||
|
#####
|
||||||
|
# API(s) being tested: [trait]require_traits=
|
||||||
|
##
|
||||||
|
# Actions:
|
||||||
|
# Bob is told to generate special random traits.
|
||||||
|
##
|
||||||
|
# Expected end state:
|
||||||
|
# One of the two special traits needs another trait to be already had by Bob.
|
||||||
|
# Bob only have the trait if it also has the one it requires one.
|
||||||
|
#####
|
||||||
|
|
||||||
|
|
||||||
|
{GENERIC_UNIT_TEST "trait_requirement_test" (
|
||||||
|
[event]
|
||||||
|
name=start
|
||||||
|
{UNIT 1 (Test Unit Require) 1 1 (id=requirer1)}
|
||||||
|
[/event]
|
||||||
|
[event]
|
||||||
|
name=side 1 turn 1
|
||||||
|
[store_unit]
|
||||||
|
[filter]
|
||||||
|
side=1
|
||||||
|
id=requirer1
|
||||||
|
[/filter]
|
||||||
|
variable=required
|
||||||
|
kill=no
|
||||||
|
[/store_unit]
|
||||||
|
# Check if the second randomly generated trait is the one requiring the other one
|
||||||
|
{ASSERT ({VARIABLE_CONDITIONAL required.modifications.trait[1].id equals "test_require"})}
|
||||||
|
{SUCCEED}
|
||||||
|
[/event]
|
||||||
|
)}
|
36
data/test/units.cfg
Normal file
36
data/test/units.cfg
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#textdomain wesnoth-test
|
||||||
|
|
||||||
|
[units]
|
||||||
|
{test/units/}
|
||||||
|
|
||||||
|
[race]
|
||||||
|
id=test
|
||||||
|
name= _ "test"
|
||||||
|
female_name= _ "test"
|
||||||
|
plural_name= _ "tests"
|
||||||
|
description= _ "test race"
|
||||||
|
num_traits=2
|
||||||
|
undead_variation=wolf
|
||||||
|
{ORCISH_NAMES}
|
||||||
|
[/race]
|
||||||
|
|
||||||
|
[movetype]
|
||||||
|
name=fly
|
||||||
|
flying=yes
|
||||||
|
[movement_costs]
|
||||||
|
{FLY_MOVE}
|
||||||
|
cave=3
|
||||||
|
fungus=3
|
||||||
|
[/movement_costs]
|
||||||
|
[defense]
|
||||||
|
{FLY_DEFENSE 50}
|
||||||
|
cave=80
|
||||||
|
fungus=-70
|
||||||
|
[/defense]
|
||||||
|
{FLY_RESISTANCE}
|
||||||
|
[special_note]
|
||||||
|
note={INTERNAL:SPECIAL_NOTES_DEFENSE_CAP}
|
||||||
|
[/special_note]
|
||||||
|
[/movetype]
|
||||||
|
|
||||||
|
[/units]
|
56
data/test/units/test_unit_exclude.cfg
Normal file
56
data/test/units/test_unit_exclude.cfg
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#textdomain wesnoth-test
|
||||||
|
|
||||||
|
#define TRAIT_TEST_EXCLUDE
|
||||||
|
# Test for exclude trait
|
||||||
|
[trait]
|
||||||
|
id=test_exclude
|
||||||
|
male_name= _ "test_exclude"
|
||||||
|
female_name= _ "female^test_exclude"
|
||||||
|
help_text= _ "Test exclude"
|
||||||
|
exclude_traits=test_excluded,strong,quick,intelligent,resilient
|
||||||
|
[effect]
|
||||||
|
apply_to=hitpoints
|
||||||
|
increase_total=1
|
||||||
|
[/effect]
|
||||||
|
[/trait]
|
||||||
|
#enddef
|
||||||
|
|
||||||
|
#define TRAIT_TEST_EXCLUDED
|
||||||
|
# Test for require trait
|
||||||
|
[trait]
|
||||||
|
id=test_excluded
|
||||||
|
male_name= _ "test_excluded"
|
||||||
|
female_name= _ "female^test_excluded"
|
||||||
|
help_text= _ "Test excluded"
|
||||||
|
[effect]
|
||||||
|
apply_to=hitpoints
|
||||||
|
increase_total=1
|
||||||
|
[/effect]
|
||||||
|
[/trait]
|
||||||
|
#enddef
|
||||||
|
|
||||||
|
# This unit is used for testing the exclude traits functionality.
|
||||||
|
|
||||||
|
[unit_type]
|
||||||
|
id=Test Unit Exclude
|
||||||
|
name=_ "dummy_unit^Test Unit Exclude"
|
||||||
|
race=test
|
||||||
|
image="misc/blank-hex.png"
|
||||||
|
ignore_race_traits=yes
|
||||||
|
{TRAIT_TEST_EXCLUDE}
|
||||||
|
{TRAIT_TEST_EXCLUDED}
|
||||||
|
hitpoints=1
|
||||||
|
movement_type=fly
|
||||||
|
movement=1
|
||||||
|
experience=1
|
||||||
|
level=1
|
||||||
|
alignment=neutral
|
||||||
|
advances_to=null
|
||||||
|
cost=1
|
||||||
|
usage=scout
|
||||||
|
hide_help=yes
|
||||||
|
do_not_list=yes
|
||||||
|
[/unit_type]
|
||||||
|
|
||||||
|
#undef TRAIT_TEST_EXCLUDE
|
||||||
|
#undef TRAIT_TEST_EXCLUDED
|
56
data/test/units/test_unit_require.cfg
Normal file
56
data/test/units/test_unit_require.cfg
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#textdomain wesnoth-test
|
||||||
|
|
||||||
|
#define TRAIT_TEST_REQUIRE
|
||||||
|
# Test for require trait
|
||||||
|
[trait]
|
||||||
|
id=test_require
|
||||||
|
male_name= _ "test_require"
|
||||||
|
female_name= _ "female^test_require"
|
||||||
|
help_text= _ "Test require"
|
||||||
|
require_traits=test_required
|
||||||
|
[effect]
|
||||||
|
apply_to=hitpoints
|
||||||
|
increase_total=1
|
||||||
|
[/effect]
|
||||||
|
[/trait]
|
||||||
|
#enddef
|
||||||
|
|
||||||
|
#define TRAIT_TEST_REQUIRED
|
||||||
|
# Test for require trait
|
||||||
|
[trait]
|
||||||
|
id=test_required
|
||||||
|
male_name= _ "test_required"
|
||||||
|
female_name= _ "female^test_required"
|
||||||
|
help_text= _ "Test required"
|
||||||
|
[effect]
|
||||||
|
apply_to=hitpoints
|
||||||
|
increase_total=1
|
||||||
|
[/effect]
|
||||||
|
[/trait]
|
||||||
|
#enddef
|
||||||
|
|
||||||
|
# This unit is used for testing the require traits functionality.
|
||||||
|
|
||||||
|
[unit_type]
|
||||||
|
id=Test Unit Require
|
||||||
|
name=_ "dummy_unit^Test Unit Require"
|
||||||
|
race=test
|
||||||
|
image="misc/blank-hex.png"
|
||||||
|
ignore_race_traits=yes
|
||||||
|
{TRAIT_TEST_REQUIRE}
|
||||||
|
{TRAIT_TEST_REQUIRED}
|
||||||
|
hitpoints=1
|
||||||
|
movement_type=fly
|
||||||
|
movement=1
|
||||||
|
experience=1
|
||||||
|
level=1
|
||||||
|
alignment=neutral
|
||||||
|
advances_to=null
|
||||||
|
cost=1
|
||||||
|
usage=scout
|
||||||
|
hide_help=yes
|
||||||
|
do_not_list=yes
|
||||||
|
[/unit_type]
|
||||||
|
|
||||||
|
#undef TRAIT_TEST_REQUIRE
|
||||||
|
#undef TRAIT_TEST_REQUIRED
|
|
@ -806,10 +806,47 @@ void unit::generate_traits(bool must_have_only)
|
||||||
LOG_UT << "Generating a trait for unit type " << type().log_id() << " with must_have_only " << must_have_only;
|
LOG_UT << "Generating a trait for unit type " << type().log_id() << " with must_have_only " << must_have_only;
|
||||||
const unit_type& u_type = type();
|
const unit_type& u_type = type();
|
||||||
|
|
||||||
// Calculate the unit's traits
|
|
||||||
config::const_child_itors current_traits = modifications_.child_range("trait");
|
config::const_child_itors current_traits = modifications_.child_range("trait");
|
||||||
std::vector<const config*> candidate_traits;
|
|
||||||
|
|
||||||
|
// Handle must-have only at the beginning
|
||||||
|
for(const config& t : u_type.possible_traits()) {
|
||||||
|
// Skip the trait if the unit already has it.
|
||||||
|
const std::string& tid = t["id"];
|
||||||
|
bool already = false;
|
||||||
|
for(const config& mod : current_traits) {
|
||||||
|
if(mod["id"] == tid) {
|
||||||
|
already = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(already) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Add the trait if it is mandatory.
|
||||||
|
const std::string& avl = t["availability"];
|
||||||
|
if(avl == "musthave") {
|
||||||
|
modifications_.add_child("trait", t);
|
||||||
|
current_traits = modifications_.child_range("trait");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(must_have_only) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const config*> candidate_traits;
|
||||||
|
std::vector<std::string> temp_require_traits;
|
||||||
|
std::vector<std::string> temp_exclude_traits;
|
||||||
|
|
||||||
|
// Now randomly fill out to the number of traits required or until
|
||||||
|
// there aren't any more traits.
|
||||||
|
int nb_traits = current_traits.size();
|
||||||
|
int max_traits = u_type.num_traits();
|
||||||
|
for(; nb_traits < max_traits; ++nb_traits)
|
||||||
|
{
|
||||||
|
current_traits = modifications_.child_range("trait");
|
||||||
|
candidate_traits.clear();
|
||||||
for(const config& t : u_type.possible_traits()) {
|
for(const config& t : u_type.possible_traits()) {
|
||||||
// Skip the trait if the unit already has it.
|
// Skip the trait if the unit already has it.
|
||||||
const std::string& tid = t["id"];
|
const std::string& tid = t["id"];
|
||||||
|
@ -824,35 +861,74 @@ void unit::generate_traits(bool must_have_only)
|
||||||
if(already) {
|
if(already) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Skip trait if trait requirements are not met
|
||||||
|
// or trait exclusions are present
|
||||||
|
temp_require_traits = utils::split(t["require_traits"]);
|
||||||
|
temp_exclude_traits = utils::split(t["exclude_traits"]);
|
||||||
|
|
||||||
// Add the trait if it is mandatory.
|
// See if the unit already has a trait that excludes the current one
|
||||||
const std::string& avl = t["availability"];
|
for(const config& mod : current_traits) {
|
||||||
if(avl == "musthave") {
|
if (mod["exclude_traits"] != "") {
|
||||||
modifications_.add_child("trait", t);
|
for (const auto& c: utils::split(mod["exclude_traits"])) {
|
||||||
current_traits = modifications_.child_range("trait");
|
temp_exclude_traits.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check for requirements
|
||||||
|
bool trait_req_met = true;
|
||||||
|
for(const std::string& s : temp_require_traits) {
|
||||||
|
bool has_trait = false;
|
||||||
|
for(const config& mod : current_traits) {
|
||||||
|
if (mod["id"] == s)
|
||||||
|
has_trait = true;
|
||||||
|
}
|
||||||
|
if(!has_trait) {
|
||||||
|
trait_req_met = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!trait_req_met) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now check for exclusionary traits
|
||||||
|
bool trait_exc_met = true;
|
||||||
|
|
||||||
|
for(const std::string& s : temp_exclude_traits) {
|
||||||
|
bool has_exclusionary_trait = false;
|
||||||
|
for(const config& mod : current_traits) {
|
||||||
|
if (mod["id"] == s)
|
||||||
|
has_exclusionary_trait = true;
|
||||||
|
}
|
||||||
|
if (tid == s) {
|
||||||
|
has_exclusionary_trait = true;
|
||||||
|
}
|
||||||
|
if(has_exclusionary_trait) {
|
||||||
|
trait_exc_met = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!trait_exc_met) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& avl = t["availability"];
|
||||||
// The trait is still available, mark it as a candidate for randomizing.
|
// The trait is still available, mark it as a candidate for randomizing.
|
||||||
// For leaders, only traits with availability "any" are considered.
|
// For leaders, only traits with availability "any" are considered.
|
||||||
if(!must_have_only && (!can_recruit() || avl == "any")) {
|
if(!must_have_only && (!can_recruit() || avl == "any")) {
|
||||||
candidate_traits.push_back(&t);
|
candidate_traits.push_back(&t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// No traits available anymore? Break
|
||||||
|
if(candidate_traits.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(must_have_only) return;
|
|
||||||
|
|
||||||
// Now randomly fill out to the number of traits required or until
|
|
||||||
// there aren't any more traits.
|
|
||||||
int nb_traits = current_traits.size();
|
|
||||||
int max_traits = u_type.num_traits();
|
|
||||||
for(; nb_traits < max_traits && !candidate_traits.empty(); ++nb_traits)
|
|
||||||
{
|
|
||||||
int num = randomness::generator->get_random_int(0,candidate_traits.size()-1);
|
int num = randomness::generator->get_random_int(0,candidate_traits.size()-1);
|
||||||
modifications_.add_child("trait", *candidate_traits[num]);
|
modifications_.add_child("trait", *candidate_traits[num]);
|
||||||
candidate_traits.erase(candidate_traits.begin() + num);
|
candidate_traits.erase(candidate_traits.begin() + num);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once random traits are added, don't do it again.
|
// Once random traits are added, don't do it again.
|
||||||
// Such as when restoring a saved character.
|
// Such as when restoring a saved character.
|
||||||
random_traits_ = false;
|
random_traits_ = false;
|
||||||
|
|
|
@ -309,6 +309,8 @@
|
||||||
0 unslowable_status_test
|
0 unslowable_status_test
|
||||||
0 unpetrifiable_status_test
|
0 unpetrifiable_status_test
|
||||||
0 test_force_chance_to_hit_macro
|
0 test_force_chance_to_hit_macro
|
||||||
|
0 trait_exclusion_test
|
||||||
|
0 trait_requirement_test
|
||||||
0 swarms_filter_student_by_type
|
0 swarms_filter_student_by_type
|
||||||
0 swarms_effects_not_checkable
|
0 swarms_effects_not_checkable
|
||||||
0 filter_special_id_active
|
0 filter_special_id_active
|
||||||
|
|
Loading…
Add table
Reference in a new issue