Difference between revisions of "WML Abilities"
(Add detailed Introduction) |
(changed to abilities and whitespace changes) |
||
Line 1: | Line 1: | ||
− | The more complex abilities and weapon often consist of two parts: | + | The more complex abilities and weapon specials often consist of two parts: |
* The ability / weapon special, which is only a dummy to provide a description and to see whether a unit has this ability. It is something one gives to a unit. Mainline abilities / weapon specials only need this part. | * The ability / weapon special, which is only a dummy to provide a description and to see whether a unit has this ability. It is something one gives to a unit. Mainline abilities / weapon specials only need this part. | ||
− | * One or multiple [event]s, which make things happen. These [event]s must be added to the game ''explicitly'', in addition to the ability / weapon special! | + | * One or multiple [event]s, which make things happen. These [event]s must be added to the game ''explicitly'', in addition to the unit with the ability / weapon special! |
How to include [event]s? | How to include [event]s? | ||
Line 26: | Line 26: | ||
female_name= _ "female^knockback" | female_name= _ "female^knockback" | ||
description=_ "When a unit is hit with a knockback attack, it is immediately pushed back one hex away from the attacker. Units cannot be knocked back into an occupied hex, out of villages or onto terrain they normally could not move to. Only works on offense." | description=_ "When a unit is hit with a knockback attack, it is immediately pushed back one hex away from the attacker. Units cannot be knocked back into an occupied hex, out of villages or onto terrain they normally could not move to. Only works on offense." | ||
+ | active_on=offense | ||
[/dummy] | [/dummy] | ||
#enddef | #enddef | ||
Line 136: | Line 137: | ||
<syntaxhighlight lang='wml'> | <syntaxhighlight lang='wml'> | ||
#define CHARM FILTER WEAPON | #define CHARM FILTER WEAPON | ||
− | [event] | + | [event] |
name=attacker_hits | name=attacker_hits | ||
first_time_only=no | first_time_only=no | ||
Line 144: | Line 145: | ||
[/filter] | [/filter] | ||
− | [ | + | [filter_attack] |
− | + | name={WEAPON} | |
− | [/ | + | [/filter_attack] |
{STORE_UNIT_VAR x,y=$x1,$y1 side charmer_side} | {STORE_UNIT_VAR x,y=$x1,$y1 side charmer_side} | ||
Line 184: | Line 185: | ||
variable=possibly_charmed | variable=possibly_charmed | ||
− | |||
[/store_unit] | [/store_unit] | ||
Line 199: | Line 199: | ||
[unstore_unit] | [unstore_unit] | ||
variable=possibly_charmed[$i] | variable=possibly_charmed[$i] | ||
− | |||
[/unstore_unit] | [/unstore_unit] | ||
[/then] | [/then] | ||
Line 216: | Line 215: | ||
=== Bloodlust === | === Bloodlust === | ||
− | Bloodlust is a very simple ability. If a unit | + | Bloodlust is a very simple ability. If a unit having bloodlust kills an enemy unit when attacking, it may attack again, provided that there are more enemy units adjacent to it. |
This would give the bloodlust ability to all Dwarvish Ulfserkers (making them insanely powerful): | This would give the bloodlust ability to all Dwarvish Ulfserkers (making them insanely powerful): | ||
Line 232: | Line 231: | ||
[/filter_second] | [/filter_second] | ||
− | + | [modify_unit] | |
− | + | [filter] | |
+ | x,y=$x2,$y2 | ||
+ | [/filter] | ||
+ | moves=0 | ||
+ | attacks_left=1 | ||
+ | [/modify_unit] | ||
[/event] | [/event] | ||
#enddef | #enddef | ||
Line 240: | Line 244: | ||
=== Pickpocket === | === Pickpocket === | ||
− | This special could also be called loot. When a unit with this attack special | + | This special could also be called loot. When a unit with this attack special successfully hits an enemy unit, it gains a certain amount of gold. |
To do this, use this code: | To do this, use this code: | ||
Line 255: | Line 259: | ||
description= _ "Gain money for attacking your foe. Each strike scores you one gold." | description= _ "Gain money for attacking your foe. Each strike scores you one gold." | ||
apply_to=opponent | apply_to=opponent | ||
+ | active_on=offense | ||
[/dummy] | [/dummy] | ||
[/specials] | [/specials] | ||
Line 266: | Line 271: | ||
[filter_attack] | [filter_attack] | ||
special_id=weapon_pickpocket | special_id=weapon_pickpocket | ||
− | [/filter_attack] | + | [/filter_attack] |
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 280: | Line 285: | ||
[unstore_unit] | [unstore_unit] | ||
variable=unit_att_with_pickpocket | variable=unit_att_with_pickpocket | ||
− | [/unstore_unit] | + | [/unstore_unit] |
− | + | {CLEAR_VARIABLE unit_att_with_pickpocket} | |
[/event] | [/event] | ||
[event] | [event] | ||
name=attacker_hits | name=attacker_hits | ||
− | first_time_only=no | + | first_time_only=no |
[filter_attack] | [filter_attack] | ||
special_id=weapon_pickpocket | special_id=weapon_pickpocket | ||
− | [/filter_attack] | + | [/filter_attack] |
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 302: | Line 307: | ||
variable=pickpocketed | variable=pickpocketed | ||
mode=append | mode=append | ||
− | [/store_unit] | + | [/store_unit] |
[if] | [if] | ||
[variable] | [variable] | ||
Line 319: | Line 324: | ||
[/unstore_unit] | [/unstore_unit] | ||
[/then] | [/then] | ||
− | [/if] | + | [/if] |
{CLEAR_VARIABLE pickpocketer,pickpocketed} | {CLEAR_VARIABLE pickpocketer,pickpocketed} | ||
[/event] | [/event] | ||
Line 328: | Line 333: | ||
It can be placed after the [unit_type] tag, or in its own .cfg file. | It can be placed after the [unit_type] tag, or in its own .cfg file. | ||
− | |||
To change the amount of gold given per hit, change | To change the amount of gold given per hit, change | ||
Line 336: | Line 340: | ||
[/gold] | [/gold] | ||
Where '''X''' is the amount of gold you want. | Where '''X''' is the amount of gold you want. | ||
− | |||
If you want the gold to be constant, given at the end of the turn if at least one of the attack hits, instead of '''X''' amount of gold per hit, change | If you want the gold to be constant, given at the end of the turn if at least one of the attack hits, instead of '''X''' amount of gold per hit, change | ||
Line 349: | Line 352: | ||
first_time_only=no | first_time_only=no | ||
− | ===Soultaker=== | + | === Soultaker === |
Any unit with this ability will gain an additional point of damage per strike every time it kills an enemy. Coder(s) unknown, made for Melon's Youkai faction (https://r.wesnoth.org/t20100). | Any unit with this ability will gain an additional point of damage per strike every time it kills an enemy. Coder(s) unknown, made for Melon's Youkai faction (https://r.wesnoth.org/t20100). | ||
Line 406: | Line 409: | ||
{ABILITY_SOULTAKER} | {ABILITY_SOULTAKER} | ||
− | ===Soultaker (weapon special)=== | + | === Soultaker (weapon special) === |
Unit with this weapon special will gain an additional point of damage per strike for that weapon every time it kills an enemy with this attack. Coder Ravana, made for ageless era (https://r.wesnoth.org/t25274) based on previous ability. | Unit with this weapon special will gain an additional point of damage per strike for that weapon every time it kills an enemy with this attack. Coder Ravana, made for ageless era (https://r.wesnoth.org/t25274) based on previous ability. | ||
Line 434: | Line 437: | ||
special_id=soultaker | special_id=soultaker | ||
[/filter_second_attack] | [/filter_second_attack] | ||
+ | |||
[unstore_unit] | [unstore_unit] | ||
variable=second_unit | variable=second_unit | ||
Line 456: | Line 460: | ||
#enddef | #enddef | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
And the following in the attack's [specials] tag: | And the following in the attack's [specials] tag: | ||
Line 462: | Line 465: | ||
{WEAPON_SPECIAL_SOULTAKER} | {WEAPON_SPECIAL_SOULTAKER} | ||
− | ===Charm (Type 2)=== | + | === Charm (Type 2) === |
− | |||
− | |||
+ | An attack special. If the attack hits, and the target is level 0 or 1, the target is converted to the attacker's side. However, if it misses, the attacker is converted to the defender's side. Maintainer is krotop, made for Melon's Youkai faction (https://r.wesnoth.org/t22539) | ||
<syntaxhighlight lang='wml'> | <syntaxhighlight lang='wml'> | ||
Line 477: | Line 479: | ||
id=weapon_charm | id=weapon_charm | ||
name= _ "charm" | name= _ "charm" | ||
− | description= _ "Turns a living level 1 or level 0 unit to your side. Beware | + | description= _ "Turns a living level 1 or level 0 unit to your side. Beware, if all of your attacks miss, the charm user turns to the defender side, even if it is your leader. You can not charm an enemy leader or a non-living creature." |
apply_to=opponent | apply_to=opponent | ||
+ | active_on=offense | ||
[/dummy] | [/dummy] | ||
Line 488: | Line 491: | ||
name=attack | name=attack | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter_attack] | [filter_attack] | ||
special_id=weapon_charm | special_id=weapon_charm | ||
[/filter_attack] | [/filter_attack] | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 507: | Line 510: | ||
variable=unit_att_with_charm | variable=unit_att_with_charm | ||
[/unstore_unit] | [/unstore_unit] | ||
− | + | ||
{CLEAR_VARIABLE unit_att_with_charm} | {CLEAR_VARIABLE unit_att_with_charm} | ||
[/event] | [/event] | ||
Line 516: | Line 519: | ||
name=attacker_hits | name=attacker_hits | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter_attack] | [filter_attack] | ||
special_id=weapon_charm | special_id=weapon_charm | ||
[/filter_attack] | [/filter_attack] | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 535: | Line 538: | ||
variable=unit_att_with_charm | variable=unit_att_with_charm | ||
[/unstore_unit] | [/unstore_unit] | ||
− | + | ||
{CLEAR_VARIABLE unit_att_with_charm} | {CLEAR_VARIABLE unit_att_with_charm} | ||
[/event] | [/event] | ||
Line 550: | Line 553: | ||
name=attack_end | name=attack_end | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter_attack] | [filter_attack] | ||
special_id=weapon_charm | special_id=weapon_charm | ||
Line 556: | Line 559: | ||
[filter_second] | [filter_second] | ||
canrecruit=no | canrecruit=no | ||
− | + | level=0,1 | |
− | + | [not] | |
− | + | status=not_living | |
− | + | [/not] | |
− | |||
− | [ | ||
− | |||
− | |||
− | |||
− | |||
− | |||
[/filter_second] | [/filter_second] | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 576: | Line 572: | ||
mode=append | mode=append | ||
[/store_unit] | [/store_unit] | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 584: | Line 580: | ||
mode=append | mode=append | ||
[/store_unit] | [/store_unit] | ||
− | + | ||
[if] | [if] | ||
[variable] | [variable] | ||
Line 647: | Line 643: | ||
[filter] | [filter] | ||
# your filter here... for example type=Peasant | # your filter here... for example type=Peasant | ||
+ | side=$side_number | ||
[/filter] | [/filter] | ||
variable=worker | variable=worker | ||
[/store_unit] | [/store_unit] | ||
− | + | [foreach] | |
− | + | array=worker | |
+ | [do] | ||
[gold] | [gold] | ||
− | side=$ | + | side=$this_item.side |
amount=1 | amount=1 | ||
[/gold] | [/gold] | ||
[unstore_unit] | [unstore_unit] | ||
− | variable= | + | variable=this_item |
text="1" | text="1" | ||
red,green,blue=255,255,0 | red,green,blue=255,255,0 | ||
[/unstore_unit] | [/unstore_unit] | ||
− | + | [/do] | |
− | + | [/foreach] | |
− | + | ||
{CLEAR_VARIABLE worker} | {CLEAR_VARIABLE worker} | ||
[/event] | [/event] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | And give the unit the ability like this | + | And give the unit the ability like this: |
− | |||
<syntaxhighlight lang='wml'> | <syntaxhighlight lang='wml'> | ||
[object] | [object] | ||
Line 687: | Line 684: | ||
The weapon special gives an attacker 1 point of exp taken from a defender for each hit. This will violate minimum experience (i.e. defender can go below 0). | The weapon special gives an attacker 1 point of exp taken from a defender for each hit. This will violate minimum experience (i.e. defender can go below 0). | ||
− | Give this special to the attack(s) you want it to have | + | Give this special to the attack(s) you want it to have: |
<syntaxhighlight lang='wml'> | <syntaxhighlight lang='wml'> | ||
#define WEAPON_SPECIAL_MIND_FLAY | #define WEAPON_SPECIAL_MIND_FLAY | ||
[mindflay] | [mindflay] | ||
id=mind_flay | id=mind_flay | ||
− | name="Mind Flay" | + | name= _ "Mind Flay" |
− | description="When used offensively, each hit of the mind flay attack takes 1 point of experience from the defender and gives it to the attacker." | + | description= _ "When used offensively, each hit of the mind flay attack takes 1 point of experience from the defender and gives it to the attacker." |
+ | active_on=offense | ||
[/mindflay] | [/mindflay] | ||
#enddef | #enddef | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Include these events into your scenario: | |
− | Include these events into your scenario | ||
<syntaxhighlight lang='wml'> | <syntaxhighlight lang='wml'> | ||
[event] | [event] | ||
name=attack | name=attack | ||
first_time_only=no | first_time_only=no | ||
− | [ | + | [filter_attack] |
− | + | special_id=mind flay | |
− | [/ | + | [/filter_attack] |
{VARIABLE hit_number 0} | {VARIABLE hit_number 0} | ||
[/event] | [/event] | ||
Line 712: | Line 709: | ||
name=attacker_hits | name=attacker_hits | ||
first_time_only=no | first_time_only=no | ||
− | [ | + | [filter_attack] |
− | + | special_id=mind flay | |
− | [/ | + | [/filter_attack] |
{VARIABLE_OP hit_number add 1} | {VARIABLE_OP hit_number add 1} | ||
[/event] | [/event] | ||
Line 720: | Line 717: | ||
name=attack_end | name=attack_end | ||
first_time_only=no | first_time_only=no | ||
− | [ | + | [filter_attack] |
− | + | special_id=mind flay | |
− | [/ | + | [/filter_attack] |
{VARIABLE_OP second_unit.experience add -$hit_number} | {VARIABLE_OP second_unit.experience add -$hit_number} | ||
{VARIABLE_OP unit.experience add $hit_number} | {VARIABLE_OP unit.experience add $hit_number} | ||
[unstore_unit] | [unstore_unit] | ||
variable=unit | variable=unit | ||
− | |||
text=$hit_number | text=$hit_number | ||
blue=255 | blue=255 | ||
Line 733: | Line 729: | ||
[unstore_unit] | [unstore_unit] | ||
variable=second_unit | variable=second_unit | ||
− | |||
[/unstore_unit] | [/unstore_unit] | ||
{CLEAR_VARIABLE hit_number} | {CLEAR_VARIABLE hit_number} | ||
[/event] | [/event] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
== Initiative == | == Initiative == | ||
Line 757: | Line 750: | ||
name=attack | name=attack | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter] | [filter] | ||
[filter_adjacent] | [filter_adjacent] | ||
Line 768: | Line 761: | ||
{VARIABLE unit.attack[$i].specials.firststrike.id "firststrike"} | {VARIABLE unit.attack[$i].specials.firststrike.id "firststrike"} | ||
{NEXT i} | {NEXT i} | ||
− | + | ||
[unstore_unit] | [unstore_unit] | ||
variable=unit | variable=unit | ||
Line 776: | Line 769: | ||
name=attack | name=attack | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter_second] | [filter_second] | ||
[filter_adjacent] | [filter_adjacent] | ||
Line 783: | Line 776: | ||
[/filter_adjacent] | [/filter_adjacent] | ||
[/filter_second] | [/filter_second] | ||
− | + | ||
{FOREACH second_unit.attack i} | {FOREACH second_unit.attack i} | ||
{VARIABLE second_unit.attack[$i].specials.firststrike.id "firststrike"} | {VARIABLE second_unit.attack[$i].specials.firststrike.id "firststrike"} | ||
{NEXT i} | {NEXT i} | ||
− | + | ||
[unstore_unit] | [unstore_unit] | ||
variable=second_unit | variable=second_unit | ||
Line 795: | Line 788: | ||
name=attack_end | name=attack_end | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter] | [filter] | ||
[wml_filter] | [wml_filter] | ||
Line 807: | Line 800: | ||
[/wml_filter] | [/wml_filter] | ||
[/filter] | [/filter] | ||
− | + | ||
{FOREACH unit.attack i} | {FOREACH unit.attack i} | ||
{CLEAR_VARIABLE unit.attack[$i].specials.firststrike} | {CLEAR_VARIABLE unit.attack[$i].specials.firststrike} | ||
{NEXT i} | {NEXT i} | ||
− | + | ||
[unstore_unit] | [unstore_unit] | ||
variable=unit | variable=unit | ||
Line 819: | Line 812: | ||
name=attack_end | name=attack_end | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[filter_second] | [filter_second] | ||
[wml_filter] | [wml_filter] | ||
Line 831: | Line 824: | ||
[/wml_filter] | [/wml_filter] | ||
[/filter_second] | [/filter_second] | ||
− | + | ||
{FOREACH second_unit.attack i} | {FOREACH second_unit.attack i} | ||
{CLEAR_VARIABLE second_unit.attack[$i].specials.firststrike} | {CLEAR_VARIABLE second_unit.attack[$i].specials.firststrike} | ||
{NEXT i} | {NEXT i} | ||
− | + | ||
[unstore_unit] | [unstore_unit] | ||
variable=second_unit | variable=second_unit | ||
Line 862: | Line 855: | ||
name=side turn | name=side turn | ||
first_time_only=no | first_time_only=no | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 871: | Line 864: | ||
variable=blitz_refresh | variable=blitz_refresh | ||
[/store_unit] | [/store_unit] | ||
− | + | ||
{FOREACH blitz_refresh i} | {FOREACH blitz_refresh i} | ||
{CLEAR_VARIABLE blitz_refresh[$i].abilities.skirmisher} | {CLEAR_VARIABLE blitz_refresh[$i].abilities.skirmisher} | ||
Line 879: | Line 872: | ||
[/unstore_unit] | [/unstore_unit] | ||
{NEXT i} | {NEXT i} | ||
− | + | ||
[store_unit] | [store_unit] | ||
[filter] | [filter] | ||
Line 890: | Line 883: | ||
variable=blitzed | variable=blitzed | ||
[/store_unit] | [/store_unit] | ||
− | + | ||
{FOREACH blitzed i} | {FOREACH blitzed i} | ||
[if] | [if] | ||
Line 972: | Line 965: | ||
== Whirlwind Attack == | == Whirlwind Attack == | ||
+ | |||
This attack is supposed to be an attack when you spin with your weapons in arms, hitting all nearby enemies, while they cannot counter. It should be used with the magical weapon special, because the spinning weapons are not easy to dodge and the player would pick to attack the unit with the lowest defence without it. It works with drain, slow and poison weapon specials. The attack is supposed to be attack-only, but it can be easily edited to work also on defence. | This attack is supposed to be an attack when you spin with your weapons in arms, hitting all nearby enemies, while they cannot counter. It should be used with the magical weapon special, because the spinning weapons are not easy to dodge and the player would pick to attack the unit with the lowest defence without it. It works with drain, slow and poison weapon specials. The attack is supposed to be attack-only, but it can be easily edited to work also on defence. | ||
Line 984: | Line 978: | ||
value=0 | value=0 | ||
apply_to=opponent | apply_to=opponent | ||
+ | active_on=offense | ||
[/attacks] | [/attacks] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
This is the event: | This is the event: | ||
Line 1,007: | Line 1,001: | ||
[then] | [then] | ||
{VARIABLE has_drain 1} | {VARIABLE has_drain 1} | ||
− | |||
[/then] | [/then] | ||
[/if] | [/if] | ||
Line 1,018: | Line 1,011: | ||
[then] | [then] | ||
{VARIABLE has_poison yes} | {VARIABLE has_poison yes} | ||
− | |||
[/then] | [/then] | ||
[/if] | [/if] | ||
Line 1,028: | Line 1,020: | ||
[then] | [then] | ||
{VARIABLE has_slow yes} | {VARIABLE has_slow yes} | ||
− | |||
[/then] | [/then] | ||
[/if] | [/if] | ||
Line 1,053: | Line 1,044: | ||
[/filter] | [/filter] | ||
variable=units | variable=units | ||
− | [/store_unit] | + | [/store_unit] |
{VARIABLE healed_amount 0} | {VARIABLE healed_amount 0} | ||
{FOREACH units i} | {FOREACH units i} | ||
Line 1,088: | Line 1,079: | ||
x,y=$x1,$y1 | x,y=$x1,$y1 | ||
[/filter] | [/filter] | ||
− | |||
variable=FLOATING_TEXT_temp | variable=FLOATING_TEXT_temp | ||
[/store_unit] | [/store_unit] | ||
[unstore_unit] | [unstore_unit] | ||
variable=FLOATING_TEXT_temp | variable=FLOATING_TEXT_temp | ||
− | |||
red,green,blue=0,255,0 | red,green,blue=0,255,0 | ||
text=$($healed_amount/200) #Operating with huge numbers because rounding is a problem | text=$($healed_amount/200) #Operating with huge numbers because rounding is a problem | ||
Line 1,101: | Line 1,090: | ||
[filter] | [filter] | ||
x,y=$x1,&y1 | x,y=$x1,&y1 | ||
− | [/filter] | + | [/filter] |
amount=$($healed_amount/200) | amount=$($healed_amount/200) | ||
animate=no | animate=no |
Revision as of 14:43, 13 September 2022
The more complex abilities and weapon specials often consist of two parts:
- The ability / weapon special, which is only a dummy to provide a description and to see whether a unit has this ability. It is something one gives to a unit. Mainline abilities / weapon specials only need this part.
- One or multiple [event]s, which make things happen. These [event]s must be added to the game explicitly, in addition to the unit with the ability / weapon special!
How to include [event]s?
- Add it directly to the scenario file.
- Add it directly to the [era].
- Add it inside a [unit_type] definition.
If you add it both via an [era] and and the scenario, they are added twice … i.e. the pickpocket ability would give the gold twice! To avoid that, give each event an id. This is even mandatory when adding it in a [unit_type].
Even better would be to use a [resource]. Resources have also an id, so there will never be duplicates:
- Instead of adding it to the scenario / era, you add the event code inside a [resource].
- The same way you read the scenario / era file, you also read the file containing the [resource] tag.
- In the scenario / era, use load_resource.
Contents
Knockback
When a unit is hit with a knockback attack, it is immediately pushed back one hex away from the attacker. Units cannot be knocked back into an occupied hex, out of villages or onto terrain they normally could not move to. Only works on offense.
Use this to display the special correctly on the attacks you want:
#define WEAPON_SPECIAL_KNOCKBACK
[dummy]
id=knockback
name= _ "knockback"
female_name= _ "female^knockback"
description=_ "When a unit is hit with a knockback attack, it is immediately pushed back one hex away from the attacker. Units cannot be knocked back into an occupied hex, out of villages or onto terrain they normally could not move to. Only works on offense."
active_on=offense
[/dummy]
#enddef
And insert this event to your [scenario], [multiplayer], [unit_type] or [era]:
[event]
name=attacker hits
first_time_only=no
[filter_attack]
special_id=knockback
[/filter_attack]
[filter_second]
[not]
[filter_location]
terrain=*^V*
[/filter_location]
[/not]
[/filter_second]
[if]
[variable]
name=second_unit.hitpoints
greater_than=0
[/variable]
[store_locations]
[not]
[filter]
[/filter]
[/not]
[filter_adjacent_location]
x,y=$x2,$y2
adjacent=-$unit.facing
[/filter_adjacent_location]
variable=knockback_target_hex
[/store_locations]
[if]
[variable]
name=knockback_target_hex.length
greater_than=0
[/variable]
[then]
[teleport]
[filter]
x,y=$x2,$y2
[/filter]
x,y=$knockback_target_hex.x,$knockback_target_hex.y
ignore_passability=no
[/teleport]
[if]
[have_unit]
x,y=$knockback_target_hex.x,$knockback_target_hex.y
[/have_unit]
[then]
[sound]
name=fist.ogg
[/sound]
# the knockbacked unit doesn't seem to receive experience by default,
# so we need to add it manually
[store_unit]
[filter]
x,y=$knockback_target_hex.x,$knockback_target_hex.y
[/filter]
kill=yes
variable=knockbacked
[/store_unit]
{VARIABLE_OP knockbacked.experience add $unit.level}
[unstore_unit]
variable=knockbacked
text= _ "knockback"
{COLOR_HARM}
advance=true
[/unstore_unit]
{CLEAR_VARIABLE knockbacked}
[/then]
[/if]
[/then]
[/if]
{CLEAR_VARIABLE knockback_direction,knockback_target_hex}
[/then]
[/if]
[/event]
Charm
When a unit is hit with a charm attack, it instantly jumps to the attacker's side, and returns to it's original side at the beginning of that side's turn. A charmed unit has 1 movement point and can attack.
Example that makes all Troll Whelps have charm on their attack:
{CHARM (type=Troll Whelp) fist}
#define CHARM FILTER WEAPON
[event]
name=attacker_hits
first_time_only=no
[filter]
{FILTER}
[/filter]
[filter_attack]
name={WEAPON}
[/filter_attack]
{STORE_UNIT_VAR x,y=$x1,$y1 side charmer_side}
{STORE_UNIT_VAR x,y=$x2,$y2 side charmed_side}
{IF_VAR charmer_side not_equals $charmed_side (
[then]
{MODIFY_UNIT x,y=$x2,$y2 variables.real_side $charmed_side}
{MODIFY_UNIT x,y=$x2,$y2 side $charmer_side}
{MODIFY_UNIT x,y=$x2,$y2 moves 1}
{MODIFY_UNIT x,y=$x2,$y2 attacks_left 1}
{VARIABLE_OP varname format "side_$charmed_side|_units_charmed"}
{VARIABLE $varname yes}
{CLEAR_VARIABLE varname}
[/then]
)}
{CLEAR_VARIABLE charmer_side,charmed_side}
[/event]
[event]
name=side turn
first_time_only=no
{VARIABLE_OP this_side_charmed to_variable "side_$side_number|_units_charmed"}
{IF_VAR this_side_charmed equals yes (
[then]
[store_unit]
[filter]
[not]
side=$side_number
[/not]
[/filter]
variable=possibly_charmed
[/store_unit]
{FOREACH possibly_charmed i}
{VARIABLE_OP real_side format "0$possibly_charmed[$i].variables.real_side"}
{IF_VAR real_side not_equals "0" (
[then]
{IF_VAR side_number equals $possibly_charmed[$i].variables.real_side (
[then]
{CLEAR_VARIABLE possibly_charmed[$i].variables.real_side}
{VARIABLE possibly_charmed[$i].side $side_number}
[unstore_unit]
variable=possibly_charmed[$i]
[/unstore_unit]
[/then]
)}
[/then]
)}
{NEXT i}
{CLEAR_VARIABLE possibly_charmed}
[/then]
)}
[/event]
#enddef
Bloodlust
Bloodlust is a very simple ability. If a unit having bloodlust kills an enemy unit when attacking, it may attack again, provided that there are more enemy units adjacent to it.
This would give the bloodlust ability to all Dwarvish Ulfserkers (making them insanely powerful):
{BLOODLUST (type=Dwarvish Ulfserker)}
#define BLOODLUST FILTER
[event]
name=die
first_time_only=no
[filter_second]
{FILTER}
[/filter_second]
[modify_unit]
[filter]
x,y=$x2,$y2
[/filter]
moves=0
attacks_left=1
[/modify_unit]
[/event]
#enddef
Pickpocket
This special could also be called loot. When a unit with this attack special successfully hits an enemy unit, it gains a certain amount of gold.
To do this, use this code:
#define WEAPON_SPECIAL_PICKPOCKET
# Canned definition of the pickpocket ability to be included in a
# [specials] clause.
# dummy weapon special used to describe the effect to the user
# and filter on special's id
[dummy]
id=weapon_pickpocket
name= _ "pickpocket"
description= _ "Gain money for attacking your foe. Each strike scores you one gold."
apply_to=opponent
active_on=offense
[/dummy]
[/specials]
[/attack]
# event that creates a "pickpocket has worked" variable
# and sets it to "yes" if the attacker hits at least once.
[event]
name=attacker_hits
first_time_only=no
[filter_attack]
special_id=weapon_pickpocket
[/filter_attack]
[store_unit]
[filter]
x,y=$x1,$y1
[/filter]
variable=unit_att_with_pickpocket
mode=append
[/store_unit]
[set_variable]
name=unit_att_with_pickpocket.variables.pickpocket_has_worked
value=yes
[/set_variable]
[unstore_unit]
variable=unit_att_with_pickpocket
[/unstore_unit]
{CLEAR_VARIABLE unit_att_with_pickpocket}
[/event]
[event]
name=attacker_hits
first_time_only=no
[filter_attack]
special_id=weapon_pickpocket
[/filter_attack]
[store_unit]
[filter]
x,y=$x1,$y1
[/filter]
variable=pickpocketer
mode=append
[/store_unit]
[store_unit]
[filter]
x,y=$x2,$y2
[/filter]
variable=pickpocketed
mode=append
[/store_unit]
[if]
[variable]
name=pickpocketer.variables.pickpocket_has_worked
boolean_equals=yes
[/variable]
[then]
[gold]
side=$side_number
amount=2
[/gold]
[unstore_unit]
variable=pickpocketed
text="!"
{COLOR_HEAL}
[/unstore_unit]
[/then]
[/if]
{CLEAR_VARIABLE pickpocketer,pickpocketed}
[/event]
[+attack]
[+specials]
#enddef
It can be placed after the [unit_type] tag, or in its own .cfg file.
To change the amount of gold given per hit, change
[gold] side=$side_number amount=X [/gold]
Where X is the amount of gold you want.
If you want the gold to be constant, given at the end of the turn if at least one of the attack hits, instead of X amount of gold per hit, change
[event] name=attacker_hits first_time_only=no
to
[event] name=attack_end first_time_only=no
Soultaker
Any unit with this ability will gain an additional point of damage per strike every time it kills an enemy. Coder(s) unknown, made for Melon's Youkai faction (https://r.wesnoth.org/t20100).
To give a unit the ability, place the following in any .cfg file loaded by the campaign or era:
#define ABILITY_SOULTAKER
[dummy]
id=soultaker
name= _ "soultaker"
description=_ "This unit gains an additional point added to its melee damage whenever it kills a living unit."
[/dummy]
[/abilities]
[event]
name=die
first_time_only=no
[filter]
[not]
status=not_living
[/not]
[/filter]
[filter_second]
ability=soultaker
[/filter_second]
[unstore_unit]
variable=second_unit
{COLOR_HEAL}
text= _ "+1 damage"
[/unstore_unit]
[object]
silent=yes
duration=forever
[filter]
x,y=$x2,$y2
[/filter]
[effect]
apply_to=attack
range=melee
increase_damage=1
[/effect]
[/object]
[/event]
[+abilities]
#enddef
And the following in the unit's [abilities] tag:
{ABILITY_SOULTAKER}
Soultaker (weapon special)
Unit with this weapon special will gain an additional point of damage per strike for that weapon every time it kills an enemy with this attack. Coder Ravana, made for ageless era (https://r.wesnoth.org/t25274) based on previous ability.
To give a unit the weapon special, load the following code:
#define WEAPON_SPECIAL_SOULTAKER
[dummy]
id=soultaker
name= _ "soultaker"
description=_"This unit gains an additional point added to its damage whenever it kills a living unit."
[/dummy]
[/specials]
[/attack]
[event]
name=die
first_time_only=no
id=soultaker_event
[filter]
[not]
status=not_living
[/not]
[/filter]
[filter_second_attack]
special_id=soultaker
[/filter_second_attack]
[unstore_unit]
variable=second_unit
{COLOR_HEAL}
text= _ "+1 damage"
[/unstore_unit]
[object]
silent=yes
duration=forever
[filter]
x,y=$x2,$y2
[/filter]
[effect]
apply_to=attack
name=$second_weapon.name
increase_damage=1
[/effect]
[/object]
[/event]
[+attack]
[+specials]
#enddef
And the following in the attack's [specials] tag:
{WEAPON_SPECIAL_SOULTAKER}
Charm (Type 2)
An attack special. If the attack hits, and the target is level 0 or 1, the target is converted to the attacker's side. However, if it misses, the attacker is converted to the defender's side. Maintainer is krotop, made for Melon's Youkai faction (https://r.wesnoth.org/t22539)
#define WEAPON_SPECIAL_CHARM
# Canned definition of the Charm ability to be included in a
# [specials] clause.
# dummy weapon special used to describe the effect to the user
# and filter on special's id
[dummy]
id=weapon_charm
name= _ "charm"
description= _ "Turns a living level 1 or level 0 unit to your side. Beware, if all of your attacks miss, the charm user turns to the defender side, even if it is your leader. You can not charm an enemy leader or a non-living creature."
apply_to=opponent
active_on=offense
[/dummy]
[/specials]
[/attack]
# event that creates a variable at the beginning of the fight to check if the attacker hit at least once by the end.
[event]
name=attack
first_time_only=no
[filter_attack]
special_id=weapon_charm
[/filter_attack]
[store_unit]
[filter]
x,y=$x1,$y1
[/filter]
variable=unit_att_with_charm
mode=append
[/store_unit]
[set_variable]
name=unit_att_with_charm.variables.charm_has_worked
value=no
[/set_variable]
[unstore_unit]
variable=unit_att_with_charm
[/unstore_unit]
{CLEAR_VARIABLE unit_att_with_charm}
[/event]
# event that creates a "charm has worked" variable
# and sets it to "yes" if the attacker hits at least once.
[event]
name=attacker_hits
first_time_only=no
[filter_attack]
special_id=weapon_charm
[/filter_attack]
[store_unit]
[filter]
x,y=$x1,$y1
[/filter]
variable=unit_att_with_charm
mode=append
[/store_unit]
[set_variable]
name=unit_att_with_charm.variables.charm_has_worked
value=yes
[/set_variable]
[unstore_unit]
variable=unit_att_with_charm
[/unstore_unit]
{CLEAR_VARIABLE unit_att_with_charm}
[/event]
# event that shifts a unit to the other side
# if the defending unit
# - was not lvl1 or lvl0
# - and was not a recruiting unit
# - and was a not a "non-living" creature
# then :
# -> if the attacker missed all attacks, it goes to the defender side.
# -> if the attacker hit once at least, the defender goes to the attacker side.
[event]
name=attack_end
first_time_only=no
[filter_attack]
special_id=weapon_charm
[/filter_attack]
[filter_second]
canrecruit=no
level=0,1
[not]
status=not_living
[/not]
[/filter_second]
[store_unit]
[filter]
x,y=$x1,$y1
[/filter]
variable=charmer
mode=append
[/store_unit]
[store_unit]
[filter]
x,y=$x2,$y2
[/filter]
variable=charmed
mode=append
[/store_unit]
[if]
[variable]
name=charmer.variables.charm_has_worked
boolean_equals=no
[/variable]
[then]
[set_variable]
name=charmer.side
value=$charmed.side
[/set_variable]
[unstore_unit]
variable=charmer
text="Charm failed!"
{COLOR_HARM}
[/unstore_unit]
[/then]
[else]
[set_variable]
name=charmed.side
value=$charmer.side
[/set_variable]
[unstore_unit]
variable=charmed
text="Charmed!"
{COLOR_HEAL}
[/unstore_unit]
[/else]
[/if]
{CLEAR_VARIABLE charmer,charmed}
[/event]
[+attack]
[+specials]
#enddef
Works
Unit with ability works will produce 1 gold per turn.
Put this macro into you code before the last piece of code.
#define ABILITY_WORKS
[leadership]
value=0
id=peasant_works
cumulative=no
name="works"
description= _ "This unit produces 1 gold per turn."
[/leadership]
#enddef
Put this event into your code.
[event]
name=side turn
first_time_only=no
[store_unit]
[filter]
# your filter here... for example type=Peasant
side=$side_number
[/filter]
variable=worker
[/store_unit]
[foreach]
array=worker
[do]
[gold]
side=$this_item.side
amount=1
[/gold]
[unstore_unit]
variable=this_item
text="1"
red,green,blue=255,255,0
[/unstore_unit]
[/do]
[/foreach]
{CLEAR_VARIABLE worker}
[/event]
And give the unit the ability like this:
[object]
silent=yes
[effect]
apply_to=new_ability
[abilities]
{ABILITY_WORKS}
[/abilities]
[/effect]
[/object]
Mind Flay
The weapon special gives an attacker 1 point of exp taken from a defender for each hit. This will violate minimum experience (i.e. defender can go below 0).
Give this special to the attack(s) you want it to have:
#define WEAPON_SPECIAL_MIND_FLAY
[mindflay]
id=mind_flay
name= _ "Mind Flay"
description= _ "When used offensively, each hit of the mind flay attack takes 1 point of experience from the defender and gives it to the attacker."
active_on=offense
[/mindflay]
#enddef
Include these events into your scenario:
[event]
name=attack
first_time_only=no
[filter_attack]
special_id=mind flay
[/filter_attack]
{VARIABLE hit_number 0}
[/event]
[event]
name=attacker_hits
first_time_only=no
[filter_attack]
special_id=mind flay
[/filter_attack]
{VARIABLE_OP hit_number add 1}
[/event]
[event]
name=attack_end
first_time_only=no
[filter_attack]
special_id=mind flay
[/filter_attack]
{VARIABLE_OP second_unit.experience add -$hit_number}
{VARIABLE_OP unit.experience add $hit_number}
[unstore_unit]
variable=unit
text=$hit_number
blue=255
[/unstore_unit]
[unstore_unit]
variable=second_unit
[/unstore_unit]
{CLEAR_VARIABLE hit_number}
[/event]
Initiative
Initiative is an aura ability. Much like Leadership, it affects adjacent allies but not the unit itself.
#define AURA_INITIATIVE TYPE
[dummy]
id=initiative
name= _ "initiative"
description= _ "Adjacent allies are granted Firststrike with all weapons."
[/dummy]
[/abilities]
[event]
name=attack
first_time_only=no
[filter]
[filter_adjacent]
type={TYPE}
is_enemy=no
[/filter_adjacent]
[/filter]
{FOREACH unit.attack i}
{VARIABLE unit.attack[$i].specials.firststrike.id "firststrike"}
{NEXT i}
[unstore_unit]
variable=unit
[/unstore_unit]
[/event]
[event]
name=attack
first_time_only=no
[filter_second]
[filter_adjacent]
type={TYPE}
is_enemy=no
[/filter_adjacent]
[/filter_second]
{FOREACH second_unit.attack i}
{VARIABLE second_unit.attack[$i].specials.firststrike.id "firststrike"}
{NEXT i}
[unstore_unit]
variable=second_unit
[/unstore_unit]
[/event]
[event]
name=attack_end
first_time_only=no
[filter]
[wml_filter]
[attack]
[specials]
[firststrike]
id=firststrike
[/firststrike]
[/specials]
[/attack]
[/wml_filter]
[/filter]
{FOREACH unit.attack i}
{CLEAR_VARIABLE unit.attack[$i].specials.firststrike}
{NEXT i}
[unstore_unit]
variable=unit
[/unstore_unit]
[/event]
[event]
name=attack_end
first_time_only=no
[filter_second]
[wml_filter]
[attack]
[specials]
[firststrike]
id=firststrike
[/firststrike]
[/specials]
[/attack]
[/wml_filter]
[/filter_second]
{FOREACH second_unit.attack i}
{CLEAR_VARIABLE second_unit.attack[$i].specials.firststrike}
{NEXT i}
[unstore_unit]
variable=second_unit
[/unstore_unit]
[/event]
[+abilities]
#enddef
A WML filter may be added instead of the type if you want some units of that type to not have the ability.
Blitz
Blitz is an aura ability. Much like Leadership, it affects adjacent allies but not the unit itself.
#define AURA_BLITZ TYPE
[dummy]
id=blitz
name= _ "blitz"
description= _ "Allies that start their turn adjacent to this unit are granted Skirmisher for that turn."
[/dummy]
[/abilities]
[event]
name=side turn
first_time_only=no
[store_unit]
[filter]
[wml_filter]
blitzed=1
[/wml_filter]
[/filter]
variable=blitz_refresh
[/store_unit]
{FOREACH blitz_refresh i}
{CLEAR_VARIABLE blitz_refresh[$i].abilities.skirmisher}
{VARIABLE blitz_refresh[$i].blitzed 0}
[unstore_unit]
variable=blitz_refresh[$i]
[/unstore_unit]
{NEXT i}
[store_unit]
[filter]
side=$side_number
[filter_adjacent]
type={TYPE}
is_enemy=no
[/filter_adjacent]
[/filter]
variable=blitzed
[/store_unit]
{FOREACH blitzed i}
[if]
[variable]
name=blitzed[$i].abilities.skirmisher.id
not_equals="skirmisher"
[/variable]
[then]
{VARIABLE blitzed[$i].blitzed 1}
{VARIABLE blitzed[$i].abilities.skirmisher.id skirmisher}
{VARIABLE blitzed[$i].abilities.skirmisher.name "skirmisher"}
{VARIABLE blitzed[$i].abilities.skirmisher.description "This unit is skilled in moving past enemies quickly, and ignores all enemy Zones of Control."}
[unstore_unit]
variable=blitzed[$i]
[/unstore_unit]
[/then]
[/if]
{NEXT i}
[/event]
[+abilities]
#enddef
Immune to drain or plague or poison
To make a unit immune to plague, poison or/and draining of life force, set the right [status] for this unit: One or multiple of unpoisonable, undrainable, unplagueable.
There are many ways to change a status, it can be set directly with [modify_unit], at recruitment via a [trait], or by giving the unit an [object]. In mainline, a [trait] is used to make undead units immune. Traits also have a name and description, which can be used to make it visible to the player that this unit is immune.
#define TRAIT_UNDRAINABLE
# We make vampires undrainable with a trait.
# Traits show up in the help browser, thus they should have proper descriptions.
[trait]
id=undrainable
availability=musthave
male_name= _ "vampire"
female_name= _ "female^vampire"
description= _ "This unit’s life force cannot be drained."
help_text= _ "Vampires are like Undead immune to drain and plague, but still susceptible to poison. While this trait is usually seen among vampire units, other mythical beings have also been seen with it. Even some Mages managed to acquire it."
# vampire is not a good name for a trait, as you can give the trait to anybody
[effect]
apply_to=status
add=undrainable
[/effect]
[effect]
apply_to=status
add=unplagueable
[/effect]
[/trait]
#enddef
When you define a new [unit_type], add to it:
{TRAIT_UNDRAINABLE} num_traits=3 # if you still want it to get 2 other traits
In other cases, if you want to make unit immune, i.e. one from mainline, give it the trait another way:
[event]
name=prerecruit
first_time_only=no
[filter]
type_adv_tree=Mage
[or]
race=elf
side=2
[/or]
[/filter]
[modify_unit]
[filter]
id=$unit.id
[/filter]
[status]
{TRAIT_UNDRAINABLE}
[/status]
[/modify_unit]
[/event]
Whirlwind Attack
This attack is supposed to be an attack when you spin with your weapons in arms, hitting all nearby enemies, while they cannot counter. It should be used with the magical weapon special, because the spinning weapons are not easy to dodge and the player would pick to attack the unit with the lowest defence without it. It works with drain, slow and poison weapon specials. The attack is supposed to be attack-only, but it can be easily edited to work also on defence.
This is a pair of two macros, one is a weapon special that makes the enemy unable to counter (lowers the number of attacks by 10; for the case if there was a boss or something). The other one is an event that damages the units, that should be placed into every scenario where the unit appears (or the era), preferably through a macro.
This is the part that is the weapon special that marks it:
[attacks] #This can be changed to a dummy tag if you don't want it to do anything.
id=whirlwind
name= _ "whirlwind"
description= _ "When this attack is used, all units adjacent the attacker take the damage, and cannot be countered."
value=0
apply_to=opponent
active_on=offense
[/attacks]
This is the event:
[event]
name=attacker_hits
first_time_only=no
[filter_attack]
special_id=whirlwind
[/filter_attack]
{VARIABLE has_drain 0} # Notifies the weapon specials
{VARIABLE has_slow no}
{VARIABLE has_poison no}
{FOREACH weapon.specials.damage i}
[if]
[variable]
name=weapon.specials.drains.id
equals=drains
[/variable]
[then]
{VARIABLE has_drain 1}
[/then]
[/if]
{NEXT i}
[if]
[variable]
name=weapon.specials.poison.id
equals=poison
[/variable]
[then]
{VARIABLE has_poison yes}
[/then]
[/if]
[if]
[variable]
name=weapon.specials.slow.id
equals=slow
[/variable]
[then]
{VARIABLE has_slow yes}
[/then]
[/if]
[if]
[variable]
name=has_drain
equals=0
[/variable]
[else]
[store_unit] #We need to know how many units were drained, and what were their resistances
[filter]
[filter_adjacent]
x,y=$x1,$y1
[/filter_adjacent]
[not]
side=1 #If you want to use it in an era, use side=$unit.side instead
[/not]
[not] #The target unit is already hit by the attack
x,y=$x2,$y2
[/not]
[not] #Undead are undrainable
race=undead
[/not]
[/filter]
variable=units
[/store_unit]
{VARIABLE healed_amount 0}
{FOREACH units i}
[switch] #Check the resistances
variable=weapon.type
[case]
value=arcane
{VARIABLE_OP healed_amount add "$($units[$i].resistance.arcane*$weapon.damage)"}
[/case]
[case]
value=fire
{VARIABLE_OP healed_amount add "$($units[$i].resistance.fire*$weapon.damage)"}
[/case]
[case]
value=cold
{VARIABLE_OP healed_amount add "$($units[$i].resistance.cold*$weapon.damage)"}
[/case]
[case]
value=blade
{VARIABLE_OP healed_amount add "$($units[$i].resistance.blade*$weapon.damage)"}
[/case]
[case]
value=pierce
{VARIABLE_OP healed_amount add "$($units[$i].resistance.pierce*$weapon.damage)"}
[/case]
[case]
value=impact
{VARIABLE_OP healed_amount add "$($units[$i].resistance.impact*$weapon.damage)"}
[/case]
[/switch]
{NEXT i}
[store_unit] #Float the healed amount over the unit, like if it had drained
[filter] #Two numbers will float, the one from the regular hit and one from this
x,y=$x1,$y1
[/filter]
variable=FLOATING_TEXT_temp
[/store_unit]
[unstore_unit]
variable=FLOATING_TEXT_temp
red,green,blue=0,255,0
text=$($healed_amount/200) #Operating with huge numbers because rounding is a problem
[/unstore_unit]
{CLEAR_VARIABLE FLOATING_TEXT_temp}
[heal_unit]
[filter]
x,y=$x1,&y1
[/filter]
amount=$($healed_amount/200)
animate=no
[/heal_unit]
{CLEAR_VARIABLE units,healed_amount}
[/else]
[/if]
[harm_unit]
[filter]
[filter_adjacent]
x,y=$x1,$y1
[/filter_adjacent]
[not]
side=1 #If you want to use it in an era, use side=$unit.side instead
[/not]
[not] #If you want to use it in an era, use side=$unit.side instead
x,y=$x2,$y2
[/not]
[/filter]
[filter_second]
x,y=$x1,$y1
[/filter_second]
amount=$weapon.damage
damage_type=$weapon.type
fire_event=yes
experience=yes #You will have to think about this
poisoned=$has_poison #We have detected these two effects before
slowed=$has_slow
[/harm_unit]
{CLEAR_VARIABLE has_slow,has_poison,has_drain}
[/event]