Skip to content

Commit

Permalink
replace 'replacement_type' with 'neg_alternative_type'
Browse files Browse the repository at this point in the history
The solution of the can_bool loop is not very convincing and that is why I change strategy by allowing [damage] to only return combined types so that the original type can still be detected in [filter_attack /weapon]
neg_alternative_type is similar to alternative_type with the difference that this type is used when it inflicts less damage than the base type. However, if an alternative_type is used at the same time in the list, it is this one which will have priority over the 'negative' type.
  • Loading branch information
newfrenchy83 committed Nov 1, 2023
1 parent c2a5ec9 commit 9f84dd3
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 107 deletions.
2 changes: 1 addition & 1 deletion changelog_entries/[damage]_special_changelog.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
### WML Engine
* Add a 'replacement_type' and 'alternative_type' attribute to [damage] to change the type of attack under specific conditions (terrain, time of day, leadership etc...)
* Add a 'neg_alternative__type' and 'alternative_type' attribute to [damage] to change the type of attack under specific conditions (terrain, time of day, leadership etc...)
2 changes: 1 addition & 1 deletion data/schema/filters/abilities.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{SIMPLE_KEY overwrite_specials ability_overwrite}
{SIMPLE_KEY apply_to string_list}
{SIMPLE_KEY type string_list}
{SIMPLE_KEY replacement_type string_list}
{SIMPLE_KEY neg_alternative_type string_list}
{SIMPLE_KEY alternative_type string_list}
{SIMPLE_KEY active_on ability_context}
{SIMPLE_KEY value s_int_range_list}
Expand Down
8 changes: 0 additions & 8 deletions data/schema/filters/weapon.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,3 @@
{FILTER_BOOLEAN_OPS weapon}
[/tag]

[tag]
name="$filter_weapon_by_special"
max=0
super="$filter_weapon"
{SIMPLE_KEY modified_type string_list}
{FILTER_BOOLEAN_OPS weapon}
[/tag]

8 changes: 4 additions & 4 deletions data/schema/units/abilities.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@
name="resistance"
max=infinite
super="units/unit_type/abilities/~value~"
{FILTER_TAG "filter_weapon" weapon_by_special ()}
{FILTER_TAG "filter_second_weapon" weapon_by_special ()}
{FILTER_TAG "filter_weapon" weapon ()}
{FILTER_TAG "filter_second_weapon" weapon ()}
[/tag]
[tag]
name="leadership"
Expand All @@ -95,8 +95,8 @@
{SIMPLE_KEY sub f_int}
{SIMPLE_KEY multiply f_int}
{SIMPLE_KEY divide f_int}
{FILTER_TAG "filter_weapon" weapon_by_special ()}
{FILTER_TAG "filter_second_weapon" weapon_by_special ()}
{FILTER_TAG "filter_weapon" weapon ()}
{FILTER_TAG "filter_second_weapon" weapon ()}
[/tag]
[tag]
name="illuminates"
Expand Down
8 changes: 4 additions & 4 deletions data/schema/units/specials.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

{FILTER_TAG "filter_adjacent" adjacent ()}
{FILTER_TAG "filter_adjacent_location" adjacent_location ()}
{FILTER_TAG "filter_self" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}}
{FILTER_TAG "filter_opponent" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}}
{FILTER_TAG "filter_attacker" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}}
{FILTER_TAG "filter_defender" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}}
{FILTER_TAG "filter_self" unit {FILTER_TAG "filter_weapon" weapon ()}}
{FILTER_TAG "filter_opponent" unit {FILTER_TAG "filter_weapon" weapon ()}}
{FILTER_TAG "filter_attacker" unit {FILTER_TAG "filter_weapon" weapon ()}}
{FILTER_TAG "filter_defender" unit {FILTER_TAG "filter_weapon" weapon ()}}
{WML_MERGE_KEYS}
[/tag]
# A few specials inheriting from ~generic~ are included here so that unit abilities can then inherit from them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
apply_to=resistance
replace=yes
[resistance]
arcane=50
fire=200
blade=100
[/resistance]
[/effect]
[effect]
Expand All @@ -40,15 +40,15 @@
value=12
[/damage]
[damage]
replacement_type=fire
neg_alternative_type=fire
{FILTER_WEAPON}
[/damage]
[damage]
replacement_type=cold
neg_alternative_type=cold
{FILTER_WEAPON}
[/damage]
[damage]
replacement_type=cold
neg_alternative_type=cold
{FILTER_WEAPON}
[/damage]
[chance_to_hit]
Expand All @@ -66,8 +66,8 @@
apply_to=resistance
replace=yes
[resistance]
cold=200
fire=50
cold=50
fire=200
[/resistance]
[/effect]
[effect]
Expand All @@ -81,22 +81,22 @@
value=12
[/damage]
[damage]
replacement_type=cold
neg_alternative_type=cold
[/damage]
[damage]
replacement_type=arcane
neg_alternative_type=arcane
[/damage]
[damage]
replacement_type=arcane
neg_alternative_type=arcane
[/damage]
[damage]
replacement_type=fire
neg_alternative_type=fire
[/damage]
[damage]
replacement_type=fire
neg_alternative_type=fire
[/damage]
[damage]
replacement_type=fire
neg_alternative_type=fire
[/damage]
[chance_to_hit]
value=100
Expand Down Expand Up @@ -157,42 +157,41 @@
[/filter]
variable=b
[/store_unit]
#damage without modification are 12, if test fail hitpoints !=76
#if succed then damage by alice 24(bob vulnerable to fire and fire prioritized)
#if succed then damage by bob 24(alice vulnerable to cold, cold priority is 1)
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 76})}
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 76})}
#if succed then damage by alice 12(bob resistant to fire and fire prioritized)
#if succed then damage by bob 6(alice resistant to cold)
{ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 94})}
{ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 88})}
{SUCCEED}
[/event]
#enddef

#####
# API(s) being tested: [damage]replacement_type=
# API(s) being tested: [damage]neg_alternative_type=
##
# Actions:
# Give both Alice and Bob 100% chance to hit.
# Give Bob one [damage] with replacement_type=fire and two [damage] with replacement_type=cold
# change resistance of Bob to arcane to 50% and fire to -100%.
# Give Alice one [damage] with replacement_type=cold, two [damage] with replacement_type=arcane and three with replacement_type=fire
# and change Alice resistance to cold to -100% and fire to 50%.
# Give Bob one [damage] with neg_alternative_type=fire and two [damage] with neg_alternative_type=cold
# change resistance of Bob to fire to -100%.
# Give Alice one [damage] with neg_alternative_type=cold, two [damage] with neg_alternative_type=arcane and three with neg_alternative_type=fire
# and change Alice resistance to cold to -50% and fire to -100%.
# Move Alice next to Bob, and have Alice attack Bob.
##
# Expected end state:
# Alice attack with fire and Bob use cold.
# Alice attack with blade and Bob use cold.
#####
{GENERIC_UNIT_TEST "damage_type_test" (
{DAMAGE_TYPE_TEST ()}
)}

#####
# API(s) being tested: [damage]replacement_type=[filter_self][filter_weapon]type=blade
# API(s) being tested: [damage]neg_alternative_type=[filter_self][filter_weapon]type=blade
##
# Actions:
# Give both Alice and Bob 100% chance to hit.
# Give Bob one [damage] with replacement_type=fire and two [damage] with replacement_type=cold and filter by type=blade
# change resistance of Bob to arcane to 50% and fire to -100%.
# Give Alice one [damage] with replacement_type=cold, two [damage] with replacement_type=arcane and three with replacement_type=fire
# and change Alice resistance to cold to -100% and fire to 50%.
# Give Bob one [damage] with neg_alternative_type=fire and two [damage] with neg_alternative_type=cold and filter [damage] with type=blade
# change resistance of Bob to fire to -100%.
# Give Alice one [damage] with neg_alternative_type=cold, two [damage] with neg_alternative_type=arcane and three with neg_alternative_type=fire
# and change Alice resistance to cold to -50% and fire to -100%.
# Move Alice next to Bob, and have Alice attack Bob.
##
# Expected end state:
Expand Down
12 changes: 8 additions & 4 deletions src/gui/dialogs/attack_predictions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,15 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker
}

std::pair<std::string, std::string> types = weapon->damage_type();
std::string type_bis = types.second;
if (!type_bis.empty()) {
type_bis = ", " + string_table["type_" + type_bis];
std::string neg_alt_type = types.first;
std::string pos_alt_type = types.second;
if (!neg_alt_type.empty()) {
neg_alt_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_type]);
}
ss << string_table["type_" + types.first] + type_bis;
if (!pos_alt_type.empty()) {
pos_alt_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_type]);
}
ss << string_table["type_" + weapon->type()] + neg_alt_type + pos_alt_type;

set_label_helper("resis_label", ss.str());

Expand Down
32 changes: 20 additions & 12 deletions src/gui/dialogs/unit_attack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,30 @@ void unit_attack::pre_show(window& window)
);

std::pair<std::string, std::string> types = attacker_weapon.damage_type();
std::string attw_type_second = types.second;
std::string attw_type = !(types.first).empty() ? types.first : attacker_weapon.type();
std::string neg_alt_attw_type = types.first;
std::string pos_alt_attw_type = types.second;
std::string attw_type = attacker_weapon.type();
if (!attw_type.empty()) {
attw_type = string_table["type_" + attw_type];
}
if (!attw_type_second.empty()) {
attw_type_second = ", " + string_table["type_" + attw_type_second];
if (!neg_alt_attw_type.empty()) {
neg_alt_attw_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_attw_type]);
}
if (!pos_alt_attw_type.empty()) {
pos_alt_attw_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_attw_type]);
}
std::pair<std::string, std::string> def_types = defender_weapon.damage_type();
std::string defw_type_second = def_types.second;
std::string defw_type = !(def_types.first).empty() ? def_types.first : defender_weapon.type();
std::string neg_alt_defw_type = def_types.first;
std::string pos_alt_defw_type = types.second;
std::string defw_type = defender_weapon.type();
if (!defw_type.empty()) {
defw_type = string_table["type_" + defw_type];
}
if (!defw_type_second.empty()) {
defw_type_second = ", " + string_table["type_" + defw_type_second];
if (!neg_alt_defw_type.empty()) {
neg_alt_defw_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_defw_type]);
}
if (!pos_alt_defw_type.empty()) {
pos_alt_defw_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_defw_type]);
}

const std::set<std::string> checking_tags_other = {"disable", "berserk", "drains", "heal_on_hit", "plague", "slow", "petrifies", "firststrike", "poison"};
Expand Down Expand Up @@ -182,26 +190,26 @@ void unit_attack::pre_show(window& window)

// Use attacker/defender.num_blows instead of attacker/defender_weapon.num_attacks() because the latter does not consider the swarm weapon special
attacker_stats << "<b>" << attw_name << "</b>" << "\n"
<< attw_type << attw_type_second << "\n"
<< attw_type << neg_alt_attw_type << pos_alt_attw_type << "\n"
<< attacker.damage << font::weapon_numbers_sep << attacker.num_blows
<< attw_specials << "\n"
<< font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>";

attacker_tooltip << _("Weapon: ") << "<b>" << attw_name << "</b>" << "\n"
<< _("Type: ") << attw_type << attw_type_second << "\n"
<< _("Type: ") << attw_type << neg_alt_attw_type << pos_alt_attw_type << "\n"
<< _("Damage: ") << attacker.damage << "<i>" << attw_specials_dmg << "</i>" << "\n"
<< _("Attacks: ") << attacker.num_blows << "<i>" << attw_specials_atk << "</i>" << "\n"
<< _("Chance to hit: ") << font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>"<< "<i>" << attw_specials_cth << "</i>"
<< attw_specials_others;

defender_stats << "<b>" << defw_name << "</b>" << "\n"
<< defw_type << defw_type_second << "\n"
<< defw_type << neg_alt_defw_type << pos_alt_defw_type << "\n"
<< defender.damage << font::weapon_numbers_sep << defender.num_blows
<< defw_specials << "\n"
<< font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>";

defender_tooltip << _("Weapon: ") << "<b>" << defw_name << "</b>" << "\n"
<< _("Type: ") << defw_type << defw_type_second << "\n"
<< _("Type: ") << defw_type << neg_alt_defw_type << pos_alt_defw_type << "\n"
<< _("Damage: ") << defender.damage << "<i>" << defw_specials_dmg << "</i>" << "\n"
<< _("Attacks: ") << defender.num_blows << "<i>" << defw_specials_atk << "</i>" << "\n"
<< _("Chance to hit: ") << font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>"<< "<i>" << defw_specials_cth << "</i>"
Expand Down
5 changes: 4 additions & 1 deletion src/movetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,10 @@ utils::string_map_res movetype::resistances::damage_table() const
int movetype::resistances::resistance_against(const attack_type & attack) const
{
std::pair<std::string, std::string> types = attack.damage_type();
int res = resistance_against(types.first);
int res = resistance_against(attack.type());
if(!(types.first).empty()){
res = std::min(res, resistance_against(types.first));
}
if(!(types.second).empty()){
res = std::max(res, resistance_against(types.second));
}
Expand Down
30 changes: 20 additions & 10 deletions src/reports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,21 +877,27 @@ static int attack_info(const reports::context& rc, const attack_type &at, config

std::string range = string_table["range_" + at.range()];
std::pair<std::string, std::string> types = at.damage_type();
std::string secondary_lang_type = types.second;
if (!secondary_lang_type.empty()) {
secondary_lang_type = ", " + string_table["type_" + secondary_lang_type];
std::string neg_alt_lang_type = types.first;
if (!neg_alt_lang_type.empty()) {
neg_alt_lang_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_lang_type]);
}
std::string lang_type = string_table["type_" + types.first] + secondary_lang_type;
std::string pos_alt_lang_type = types.second;
if (!pos_alt_lang_type.empty()) {
pos_alt_lang_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_lang_type]);
}
std::string lang_type = string_table["type_" + at.type()] + neg_alt_lang_type + pos_alt_lang_type;

// SCALE_INTO() is needed in case the 72x72 images/misc/missing-image.png is substituted.
const std::string range_png = std::string("icons/profiles/") + at.range() + "_attack.png~SCALE_INTO(16,16)";
const std::string type_png = std::string("icons/profiles/") + types.first + ".png~SCALE_INTO(16,16)";
const std::string secondary_type_png = !(types.second).empty() ? std::string("icons/profiles/") + types.second + ".png~SCALE_INTO(16,16)" : "";
const std::string type_png = std::string("icons/profiles/") + at.type() + ".png~SCALE_INTO(16,16)";
const std::string neg_type_png = !(types.first).empty() ? std::string("icons/profiles/") + types.first + ".png~SCALE_INTO(16,16)" : "";
const std::string pos_type_png = !(types.second).empty() ? std::string("icons/profiles/") + types.second + ".png~SCALE_INTO(16,16)" : "";
const bool range_png_exists = image::locator(range_png).file_exists();
const bool type_png_exists = image::locator(type_png).file_exists();
const bool secondary_type_png_exists = image::locator(secondary_type_png).file_exists();
const bool neg_type_png_exists = image::locator(neg_type_png).file_exists();
const bool pos_type_png_exists = image::locator(pos_type_png).file_exists();

if(!range_png_exists || !type_png_exists || (!secondary_type_png_exists && !secondary_lang_type.empty())) {
if(!range_png_exists || !type_png_exists || (!neg_type_png_exists && !neg_alt_lang_type.empty()) || (!pos_type_png_exists && !pos_alt_lang_type.empty())) {
str << span_color(font::weapon_details_color) << " " << " "
<< range << font::weapon_details_sep
<< lang_type << "</span>\n";
Expand Down Expand Up @@ -948,8 +954,12 @@ static int attack_info(const reports::context& rc, const attack_type &at, config
const std::string spacer = "misc/blank.png~CROP(0, 0, 16, 21)"; // 21 == 16+5
add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip);
add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
if(secondary_type_png_exists){
add_image(res, spacer + "~BLIT(" + secondary_type_png + ",0,5)", damage_versus.tooltip);
add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
if(neg_type_png_exists){
add_image(res, spacer + "~BLIT(" + neg_type_png + ",0,5)", damage_versus.tooltip);
}
if(pos_type_png_exists){
add_image(res, spacer + "~BLIT(" + pos_type_png + ",0,5)", damage_versus.tooltip);
}
add_text(res, damage_and_num_attacks.str, damage_and_num_attacks.tooltip);
add_text(res, damage_versus.str, damage_versus.tooltip); // This string is usually empty
Expand Down
16 changes: 8 additions & 8 deletions src/units/abilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ bool unit::ability_affects_weapon(const config& cfg, const_attack_ptr weapon, bo
if(!weapon) {
return false;
}
return weapon->matches_filter(filter, true);
return weapon->matches_filter(filter);
}

bool unit::has_ability_type(const std::string& ability) const
Expand Down Expand Up @@ -1199,15 +1199,15 @@ std::pair<std::string, std::string> attack_type::damage_type() const
{
unit_ability_list abil_list = get_specials_and_abilities("damage");
if(abil_list.empty()){
return {type(), ""};
return {"", ""};
}

std::vector<std::string> type_list = damage_type_list(abil_list, "replacement_type");
std::vector<std::string> type_list = damage_type_list(abil_list, "neg_alternative_type");
std::vector<std::string> added_type_list = damage_type_list(abil_list, "alternative_type");
std::string type_damage, sec_type_damage;
type_damage = !type_list.empty() ? type_list.front() : type();
sec_type_damage = !added_type_list.empty() ? added_type_list.front() : "";
return {type_damage, sec_type_damage};
std::string neg_type_damage, pos_type_damage;
neg_type_damage = !type_list.empty() ? type_list.front() : "";
pos_type_damage = !added_type_list.empty() ? added_type_list.front() : "";
return {neg_type_damage, pos_type_damage};
}


Expand Down Expand Up @@ -1312,7 +1312,7 @@ namespace { // Helpers for attack_type::special_active()

// Check for a weapon match.
if (auto filter_weapon = filter_child->optional_child("filter_weapon") ) {
if ( !weapon || !weapon->matches_filter(*filter_weapon, true) )
if ( !weapon || !weapon->matches_filter(*filter_weapon) )
return false;
}

Expand Down
Loading

0 comments on commit 9f84dd3

Please sign in to comment.