Resistence penetrating special attack

The place to post your WML questions and answers.

Moderator: Forum Moderators

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Post Reply
Gibimbius
Posts: 14
Joined: June 3rd, 2018, 11:27 am

Resistence penetrating special attack

Post by Gibimbius »

Hello all, I'm a WML newbie, this is my first post. I'm looking forward making a new campaign with all custom units that will rely heavily on damage resistances ("armour").

I wish to implement a special attack macro for giving to some attacks the ability to penetrate damage resistances. The general idea for the macro is:

1 - Right before the combat, check the other unit's resistance to the type of damage of the attack with the special.
2 - Add a certain amount of damage percentage to that resistance.
3 - Check if the damage percentage is less or equal to 100. If that is the case, the result is the new resistance value. If it is bigger, set it to 100. This will prevent the macro for causing damage above 100% of the base damage.
4 - At the end of the combat, return the resistance to the original value.

Example 1: Unit A has an impact attack with base damage 10 and the ability "Penetrate 30". Unit B has a resistance value of 40 for the impact type (it takes 40% of that kind of damage). When A and B engage in combat, B's resistance value to impact will now be 40+30=70 (now it will take 70% of the weapon damage). Whenever A's attack hits B, it will cause 7 damage points instead of 4.

Example 2: The same A unit now attacks unit C. Unit B has a resistance value of 90 for the impact type (it takes 90% of that kind of damage). When A and C engage in combat, C's resistance value to impact will now be 100 (90+30=120, but 120 > 100, so the new value will be 100, it will take full damage). Whenever A's attack hits C, it will cause 10 damage points instead of 9.

Is this possible to make in an easy way?

P.S: Is there a way to make a unit skill (like leadership), that adds 10% defense to all units around it?
User avatar
Xara
Posts: 270
Joined: December 26th, 2014, 12:23 am
Location: Beijing

Re: Resistence penetrating special attack

Post by Xara »

The first one os straight forward. You put on an object to the unit at attack start and remove it at attack end.

For the second one you should check the Lisar's ability to give adjacentally Firststrike in HttT. In effect tag it's possible to change chance to hit.
It pronounces Sha'ha, not Zara.

Feedback Thread of my Add-ons
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Resistence penetrating special attack

Post by enclave »

Gibimbius wrote: June 3rd, 2018, 12:05 pm Hello all, I'm a WML newbie,
Hello, for a WML newbie, you asking very complicated things :) I've been coding add-ons for 2+ years, and I'm still not advanced enough to tell you the easiest way...
Gibimbius wrote: June 3rd, 2018, 12:05 pm P.S: Is there a way to make a unit skill (like leadership), that adds 10% defense to all units around it?
By saying defense you mean reduce enemy units damage or add 10% to all resistances? There are many meanings for defense..
FIRSTLY.. to help you a bit in future.. you should locate the file with core abilities.. so you could have them as a base examples and just modify them to your needs.. So on windows 7 it's here: C:\Program Files (x86)\Battle for Wesnoth 1.14.1\data\core\macros\abilities.cfg (open it and look it through briefly)
These are all core abilities for leadership etc...
SECONDLY.. to be able to modify the ability, you would need to know what you can modify... the info can be seen here: https://wiki.wesnoth.org/AbilitiesWML
So let's imagine that by increasing units defense you mean to decrease enemy damage by 10 (which is QUITE SAME, except your units with break through resistance abilities would not be able to break through that 10%, otherwise i don't see difference)...
In this case we could easily create a leadership that would decrease enemies damage by 10% around the leader, but not add 10% defense to allies around him..
so let's first look at core leadership ability:

Code: Select all

#define ABILITY_LEADERSHIP
    # Canned definition of the Leadership ability to be included in an
    # [abilities] clause.
    [leadership]
        id=leadership
        value="(25 * (level - other.level))"
        cumulative=no
        name= _ "leadership"
        female_name= _ "female^leadership"
        description= _ "This unit can lead your own units that are next to it, making them fight better.

Adjacent own units of lower level will do more damage in battle. When a unit adjacent to, of a lower level than, and on the same side as a unit with Leadership engages in combat, its attacks do 25% more damage times the difference in their levels."
        affect_self=no
        [affect_adjacent]
            [filter]
                formula="level < other.level"
            [/filter]
        [/affect_adjacent]
    [/leadership]
#enddef
This increases others damage by 25% dependiong on their level... value="(25 * (level - other.level))"
and it doesn't affect leader affect_self=no
but it affects units adjacent which have less level than holder of leadership:

Code: Select all

        [affect_adjacent]
            [filter]
                formula="level < other.level"
            [/filter]
        [/affect_adjacent]
I have no bloody idea to be honest why it doesn't affect all units, but only allies.. there is no code for this inside but I can assume, that it's what happens by default (later about this).
Now we need to modify all these parts to achieve what you want... id=leadership MUST rename it, to your liking. Also you may change name, female name and description. but it's not a must..
So change the value into value = -10 I assume this would reduce damage by 10, I'm not sure how you could affect only units that have smaller level... because here you are affecting enemies.. which may be level 3-4... so copying value from original leadership would not work perfect.. depends on how you want it to work.. idk
Now we need to go to help WML where it says "Common keys and tags for every ability" it then lists all the tags and keys for every ability..
So we can find:
affect_self: if equal to 'yes', the ability will affect the unit that has it.
affect_allies: if equal to 'yes', the ability will affect allies in the specified adjacent hexes.
affect_enemies: if equal to 'yes', the ability will affect enemies in the specified adjacent hexes.
add affect_enemies=yes to the code, so enemies around leader will be affected by affect_adjacent.

Code: Select all

        [affect_adjacent]
            [filter]
                [filter_side]
			[enemy_of]
				side=$other_unit.side
			[/enemy_of]
		[/filter_side]
            [/filter]
        [/affect_adjacent]
[filter]: a StandardUnitFilter. (Version 1.13.2 and later only) The variable $other_unit refers to the unit owning the ability.
it's strange that other_unit refers to unit owning ability.. I don't believe it.. so try to use $this_unit.side IF it doesn't work. Looks like to many "other" in these codes... other.level for some reason refers to units around, but $other_unit to ability holder? strange.. but whatever.

To be honest MAYBE we could achieve what you want using leadership template with very very very complicated filters... but its definitly quite a bad way to go.. The best what it can be using leadership is to give enemies surrounding leader -10% damage...

Gibimbius wrote: June 3rd, 2018, 12:05 pm Is this possible to make in an easy way?
There are harder and easier ways... But I could try to give advice on the easier way particularily for me.. the way I would approach it.
(to be honest I would not try to do anything of what you want with using abilities... it's just useless or too complicated)
The only way using abilities would go with resistance... as example we take a steadfast.. and you would have to apply this ability to all units (instead of unit with break-through-resistances ability) while that special unit will also need to have some ability to be identified in filter, for example:

Code: Select all

[ability]
[resistancebreaker]
id="break_through_resistances"
name="Breaker"
description="blabla"
[/resistancebreaker]
[/ability]

Code: Select all

#define ABILITY_STEADFAST
    # Canned definition of the Steadfast ability to be included in an [abilities]
    # clause.
    [resistance]
        id=steadfast
        multiply=2
        max_value=50
        # applies to any type if we leave it out
        #apply_to=blade,pierce,impact,fire,cold,arcane
        [filter_base_value]
            greater_than=0
            less_than=50
        [/filter_base_value]
        name= _ "steadfast"
        female_name= _ "female^steadfast"
        description= _ "This unit’s resistances are doubled, up to a maximum of 50%, when defending. Vulnerabilities are not affected."
        affect_self=yes
        active_on=defense
    [/resistance]
#enddef
Look "Extra keys and tags used by the [resistance] ability" in WML link I given before:
sub: subtracts from resistance.
this is what we need.. but I assume it will keep removing resistances to below 0 if you want to reduce by 30%, while unit only has 10%.
max_value: maximum resistance value. This value must be set in order for [resistance] to function.
well to be honest.. I don't know if it can be set to 0... maybe it would work the way we want.. or not work completely... whoknows....
So like before, CHANGE the id, change the name... and then:
replace multiply=2 with sub=30
try max_value=0 see if it gona work or not..
remove

Code: Select all

[filter_base_value]
            greater_than=0
            less_than=50
        [/filter_base_value]
you want it to work with any value, dont you? maybe you want it to work only with greater_than=0 (i'm not sure what you want)
remove active_on=defense so it will be active for attack too (any units that try to attack resistance breaker would lose 30 resistance) if that's what you want..
Now just add

Code: Select all

[filter_adjacent]
ability=break_through_resistances
[/filter_adjacent]
The problem I see if that it probably will remove 30 resistance if resistance-braker adjacent (even if it doesn't attack... there may be 6 enemies adjacent, one of them resistance breaker, and the ability will remove 30 resistance for all of them... which is im sure not what you want)
add name_inactive="" so that resistance ability was invisible until active.. and maybe set name="" too... then maybe it will become totally invisible.. and all you will see will be the resistance breaker ability..

So to summ up... using abilities will not make you achive what you want... forget about using abilities...
Let's look at damage specials... they should be able to achieve what you want better... but I wouldn't use them too probably..
https://wiki.wesnoth.org/AbilitiesWML#T ... als.5D_tag
I don't see a way to modify resistance.. apart from modifying damage to become 30% more or so... Also no way to add defense to the units near leader, apart from modifying the damage.
I'm really tired after writing abilities :D So i will continue with damage specials later... it really drained me...
There are at least 2 ways to do with attack specials (you could add special resistance type to all units, and then disable weapon [disable]: disables the weapon depending on what unit it is... very complicated for beginner, LIKE everything what you want to achieve really.. there is no easy way),
and the other way would be to use
[damage]: modifies the damage of a weapon
then you would just filter defender..
[filter_defender] the special will only be active if the defender matches this SUF.
or [filter_adjacent] or
[filter_adjacent_location]: like [filter_adjacent], but filters on locations instead of units. This is a shorthand for [filter_self][filter_location][filter_adjacent_location].
just very similar to what we done in abilities above... You might use radius to determine if there is a unit with leadership in range.. ask me if you are interested...

And finally the way I would really use defense +10 near leader would be something like this:

Code: Select all

[event]
name=moveto ## when unit stopped moving from somewhere..
first_time_only=no ## do it always
[filter]
ability="leadership" ## the unit that stopped moving has leadership ability
[filter_location] 
x,y=$x1,$y1 ## the coordinates of unit with leadership
[filter]
side=$side_number ## the units in radius=1 from coordinates (adjacent) are of same side as moving unit with leadership..
[/filter]
radius=1
[/filter_location]
[/filter]

[store_unit]
[filter]
[and]
[filter_location] 
x,y=$x1,$y1 ## the coordinates of unit with leadership
[filter]
side=$side_number ## the units in radius=1 from coordinates (adjacent) are of same side as moving unit with leadership..
[/filter]
radius=1
[/filter_location]
[/and]
[not]
ability="leadership" ## all adjacent units, apart from ones with leadership ability self..
[/not]
[/filter]
variable=all_units_affected_by_leadership
[/store_unit]
{FOREACH all_units_affected_by_leadership i}
{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.arcane add 10} ## add 10 resistance to arcane
{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.blade add 10} ## add 10 resistance to blade
	{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.cold add 10} ## add 10 resistance to cold
	{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.fire add 10} ## add 10 resistance to fire
	{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.impact add 10} ## add 10 resistance to impact
	{VARIABLE_OP all_units_affected_by_leadership[$i].resistance.pierce add 10} ## add 10 resistance to pierce
[unstore_unit]
variable=all_units_affected_by_leadership[$i]  ## maybe can do this after {NEXT i} for all units together, not sure.. need to try
[/unstore_unit]
{NEXT i}
[unstore_unit]
variable=all_units_affected_by_leadership  ## not sure that it would work, maybe need to do it inside for each... need to try
[/unstore_unit]
[/event]
The code above only adds 10 resistance (defense?) to adjacent units of same side... if leader moved..
Now we need to add code to add 10 resistance if other units moved... it's more simple.. but..

Code: Select all

[event]
name=moveto ## when unit stopped moving from somewhere..
first_time_only=no ## do it always
[filter]
x,y=$x1,$y1 ## any unit moved to a location
[filter_location] ## where there is
[filter]
ability="leadership" ## there is a unit with leadership ability
side=$side_number ## with same side
[/filter]
radius=1 ## adjacent (is in radius 1) you could try using [filter_adjacent] instead but I hate it.. it often doesn't work for me.
[/filter_location] 
[/filter]

[store_unit]
[filter]
x,y=$x1,$y1 ## the coordinates of unit that we filtered above
[/filter]
variable=unit_to_add_10_defense
[/store_unit]
{VARIABLE_OP unit_to_add_10_defense.resistance.arcane add 10} ## add 10 resistance to arcane
{VARIABLE_OP unit_to_add_10_defense.resistance.blade add 10} ## add 10 resistance to blade
	{VARIABLE_OP unit_to_add_10_defense.resistance.cold add 10} ## add 10 resistance to cold
	{VARIABLE_OP unit_to_add_10_defense.resistance.fire add 10} ## add 10 resistance to fire
	{VARIABLE_OP unit_to_add_10_defense.resistance.impact add 10} ## add 10 resistance to impact
	{VARIABLE_OP unit_to_add_10_defense.resistance.pierce add 10} ## add 10 resistance to pierce
[unstore_unit]
variable=unit_to_add_10_defense
[/unstore_unit]
[/event]
So now we just need to create 2 more events to decrease resistance by 10...
And then 2 DIE events with similar filters and codes..
As you can see ALL ways are just nightmare... Do you really want to mess with it as a "new" coder, I don't know...

The easiest way for you might be to add custom resistance type for all units and an additional weapon for resistance-breaker which would work against that resistance type better...

Good luck have fun :D

PS. Maybe somebody knows simple way, please tell... maybe i'm just stupid with all complicated examples above!!!
Xara wrote: June 3rd, 2018, 2:09 pm The first one os straight forward. You put on an object to the unit at attack start and remove it at attack end.
Yeah, I'm sure it's very simple for a WML newbie :) Just like all examples above.. very very simple..
By the way this way would break the replays if you would want to watch game as a replay, I'm quite sure.. at least from multiplayer observer server..
While it's totally not important for Campaigns. So it might be a way to go..

But I wouldn't expect that we will write you exactly how to do everything you want:D it's just a huge work and it probably took me more than 1 hr to just answer you above. All the codes I mentioned MAY NOT WORK, they all need testing.. So to help you properly one like me would need to spend some 3 hours, making up and testing things lol.. it's really not simple what you want. So if I were you I would just start from basic things.. And add this feature when everything else is already done.
User avatar
Xara
Posts: 270
Joined: December 26th, 2014, 12:23 am
Location: Beijing

Re: Resistence penetrating special attack

Post by Xara »

enclave wrote: June 3rd, 2018, 2:46 pm
Xara wrote: June 3rd, 2018, 2:09 pm The first one os straight forward. You put on an object to the unit at attack start and remove it at attack end.
Yeah, I'm sure it's very simple for a WML newbie :) Just like all examples above.. very very simple..
By the way this way would break the replays if you would want to watch game as a replay, I'm quite sure.. at least from multiplayer observer server..
While it's totally not important for Campaigns. So it might be a way to go..
Sorry, I was outdoor and phone-typing. Could have been more specific.

Why would it break multiplayer observer server? Is it because of the time point of attack-end being bug prone? Or is it something wrong with removing object?
It pronounces Sha'ha, not Zara.

Feedback Thread of my Add-ons
Gibimbius
Posts: 14
Joined: June 3rd, 2018, 11:27 am

Re: Resistence penetrating special attack

Post by Gibimbius »

Wow! Thank you enclave for you great answer, it certainly gave me some unexpectedly interesting stuff to study.

I'm sure nothing will be simple, but I have no haste anyway :D
The first one is straight forward. You put on an object to the unit at attack start and remove it at attack end.

For the second one you should check the Lisar's ability to give adjacentally Firststrike in HttT. In effect tag it's possible to change chance to hit.
Is there any other existing special attack out there in any addon I can use as reference for "putting on an object" to the other unit? The special attack is meant to be played in a single player campaign, so I don't care about multi-player.

Assuming unit A uses an attack with the {PENETRATION p} special against unit B: If I could get the value of unit B resistance to the damage type into a variable, I could calculate a corresponding damage increase to give me the desired damage increase without altering the actual resistance value right?

Playing a little bit with algebra I get that the needed damage increase is equal to:

i = (D*p)/R

where D=base weapon damage, p= penetration special value and R is the resistance value.

For instance: Unit A fights B with an attack that has 10 of base damage and {PENETRATION 30}. Lets assume unit B resistance to the attack is 40 (it only take 40% damage). Normally, A would cause 4 of damage. But we wish it to cause 7.
D=10, p=30, R=40: i = (10*30)/40. i = 7.5. Rounding it, i =7, So the damage takes a increase o 7.

New damage: 10 + 7 = 17. Target resistance remains the same 40. 0.4*17 = 0.68. Will it be rounded to 6 or 7? It doens't matter,It works well enough for me. If only I could get the R value. Any idea how can I get it at the start of a combat?


P.S.: Just forget about the leadership-like ability for now :lol:
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Resistence penetrating special attack

Post by enclave »

Xara wrote: June 3rd, 2018, 3:06 pm Why would it break multiplayer observer server? Is it because of the time point of attack-end being bug prone? Or is it something wrong with removing object?
no idea, just a bug like many other :)
Gibimbius wrote: June 3rd, 2018, 5:35 pm If only I could get the R value. Any idea how can I get it at the start of a combat?
some info here: https://wiki.wesnoth.org/Eventwml#.5Bfilter_attack.5D
[filter_attack]

Can be used to set additional filtering criteria based on the weapon used by the primary unit. This is usable in the events attack, attacker hits, attacker misses, defender hits, defender misses, attack end, last breath, and die. For more information and filter keys, see Filtering Weapons. The most commonly used keys are the following.

name: the name of the weapon used.
range: the range of the weapon used.
special: filter on the attack's special power.
this filter could be used to filter the attacker weapon special..

Code: Select all

[event]
name=attack
first_time_only=no
[filter]
ability=resistance_breaker
[/filter]
[store_unit]
[filter]
x,y=$x2,$y2
[/filter]
variable=defender
[/store_unit]
[/event]
$defender.resistance.impact now probably has the R you need...
User avatar
Celtic_Minstrel
Developer
Posts: 2166
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Resistence penetrating special attack

Post by Celtic_Minstrel »

enclave wrote: June 3rd, 2018, 2:46 pm

Code: Select all

        [affect_adjacent]
            [filter]
                [filter_side]
			[enemy_of]
				side=$other_unit.side
			[/enemy_of]
		[/filter_side]
            [/filter]
        [/affect_adjacent]
[filter]: a StandardUnitFilter. (Version 1.13.2 and later only) The variable $other_unit refers to the unit owning the ability.
it's strange that other_unit refers to unit owning ability.. I don't believe it.. so try to use $this_unit.side IF it doesn't work. Looks like to many "other" in these codes... other.level for some reason refers to units around, but $other_unit to ability holder? strange.. but whatever.
It's because it's an an adjacent filter. In a regular filter like [filter] or [filter_self], $this_unit of course refers to the ability holder. In an adjacent filter though, $other_unit refers to the $this_unit of the enclosing filter, while $this_unit now refers to the adjacent unit being filtered on. That means you can't get at the $other_unit of the enclosing filter from an adjacent filter. However, an ability filter doesn't have an $other_unit anyway (though an attack special filter does), so that probably doesn't matter.

In the leadership definition, other.level still refers to the ability holder, just like $other_unit. The other WFL variable and the $other_unit WML variable always refer to the same unit. So, using a formula with other in an adjacent unit will have it referring to the unit from the enclosing filter. Using it in a weapon special filter, however, has it referring to the unit involved in the attack that's not being currently filtered on - for example, in [filter_self] it refers to the opponent. But using it in [filter_adjacent] is equivalent to placing that [filter_adjacent] in a [filter_self], so now other and $other_unit refer to the unit owning the weapon, and there's no way to refer to the opponent.

I can definitely see how this could get confusing, but it's quite consistent - if you read carefully it should always be clear which unit is being referred to by $this_unit and which unit (if any) is being referred to by $other_unit.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
Post Reply