Difference between revisions of "WML Abilities"

From The Battle for Wesnoth Wiki
m (Blitz: typo)
(Events can now be included directly in ability and weapon special tags)
 
(22 intermediate revisions by 4 users not shown)
Line 1: Line 1:
Remember that you must include the WML ability code in every scenario where you intend them to work. Or include them in the unit file inside the [unit_type] tag.
+
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.
Some abilities require macros from [[WML_Utilities|Utilities]].
+
* One or multiple [event]s, which make things happen. These can be included directly in the ability or weapon special tag {{DevFeature1.19|4}}. For earlier versions, these [event]s must be added to the game ''explicitly'', in addition to the unit with the ability or weapon special!
  
 +
How to include [event]s?
 +
* Add it inside the ability or weapon special tag. {{DevFeature1.19|4}}
 +
* Add it directly to the scenario file.
 +
* Add it directly to the [era].
 +
* Add it inside a [unit_type] definition.
 +
* Add it directly in your [campaign] tag.
 +
* Add it inside a [resource], which is then loaded from your [campaign] tag or an individual scenario.
 +
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 [[ModificationWML#The_.5Bresource.5D_toplevel_tag|load_resource]].
  
 
=== Knockback  ===
 
=== Knockback  ===
Line 10: Line 22:
  
 
Use this to display the special correctly on the attacks you want:
 
Use this to display the special correctly on the attacks you want:
    #define WEAPON_SPECIAL_KNOCKBACK
+
<syntaxhighlight lang='wml'>
        [dummy]
+
#define WEAPON_SPECIAL_KNOCKBACK
            id=knockback
+
    [dummy]
            name= _ "knockback"
+
        id=knockback
            female_name= _ "female^knockback"
+
        name= _ "knockback"
            description=_ "Knockback:
+
        female_name= _ "female^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."
+
        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."
         [/dummy]
+
         active_on=offense
    #enddef
+
    [/dummy]
 +
#enddef
 +
</syntaxhighlight>
  
 
And insert this event to your [scenario], [multiplayer], [unit_type] or [era]:
 
And insert this event to your [scenario], [multiplayer], [unit_type] or [era]:
 +
<syntaxhighlight lang='wml'>
 +
[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_target_hex}
 +
        [/then]
 +
    [/if]
 +
[/event]
 +
</syntaxhighlight>
 +
 +
=== Charm ===
 +
 +
==== temporary ====
 +
 +
When a unit is hit by a ''charm'' attack, it instantly jumps to the attacker's side, and returns to its original side at the end of the 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}
 +
 +
<syntaxhighlight lang='wml'>
 +
#define CHARM FILTER WEAPON
 
     [event]
 
     [event]
 
         name=attacker hits
 
         name=attacker hits
 +
        # Works only as attacker.
 +
        # If you want to make a weapon special for this event, set:
 +
        # [dummy]active_on=offense, then the engine greys out the weapon special on defense.
 +
        first_time_only=no
 +
        id=charm_as_attacker
 +
 +
        [filter]
 +
            {FILTER}
 +
        [/filter]
 +
 +
        [filter_attack]
 +
            name={WEAPON}
 +
            # or special_id=charm, if you create a [dummy] weapon special
 +
        [/filter_attack]
 +
 +
        [filter_second]
 +
            # If the leader is charmed, it might end the scenario,
 +
            # as the other side is now considered defeated without a leader.
 +
            # Better exclude leaders.
 +
            canrecruit=no
 +
            # If the unit would die from the damage,
 +
            # we should not interfere with the event.
 +
            formula="self.hitpoints > 0"
 +
        [/filter_second]
 +
 +
        # Charm the unit
 +
        # Changing the side will also immediately stop the combat and grant both units XP
 +
        [modify_unit]
 +
            [filter]
 +
                x,y=$x2,$y2
 +
            [/filter]
 +
            [variables]
 +
                # to remember the original side
 +
                real_side=$second_unit.side
 +
            [/variables]
 +
            [status]
 +
                # optional, just to easier find the unit in the other event
 +
                charmed=yes
 +
            [/status]
 +
            side=$unit.side
 +
            moves=1
 +
            attacks_left=1
 +
        [/modify_unit]
 +
 +
        [floating_text]
 +
            x,y=$x2,$y2
 +
            # po: short text, only displayed for a moment
 +
            text="<span color='#ffc0cb'>" + _ "charm" + "</span>"
 +
        [/floating_text]
 +
    [/event]
 +
 +
    [event]
 +
        name=side turn end, scenario end
 +
        # Releasing the unit in the same turn has a few reasons:
 +
        # - a charmed unit cannot be charmed again
 +
        # - if the scenario ends, we can still correct the ownership
 +
        # - things like healing by allies work the usual way
 +
        first_time_only=no
 +
        id=charm_release
 +
 +
        [store_unit]
 +
            [filter]
 +
                side=$side_number
 +
                status=charmed
 +
            [/filter]
 +
            variable=charmed_units
 +
        [/store_unit]
 +
 +
        [foreach]
 +
            array=charmed_units
 +
            [do]
 +
                {VARIABLE this_item.side $this_item.variables.real_side}
 +
                {CLEAR_VARIABLE this_item.variables.real_side}
 +
                {CLEAR_VARIABLE this_item.status.charmed}
 +
                [unstore_unit]
 +
                    variable=this_item
 +
                [/unstore_unit]
 +
            [/do]
 +
        [/foreach]
 +
 +
        {CLEAR_VARIABLE charmed_units}
 +
    [/event]
 +
#enddef
 +
</syntaxhighlight>
 +
 +
==== permanent ====
 +
 +
This version of the weapon special is a gamble. You can obtain the other unit for good, but you also risk losing this unit. When you lose this unit, the opponent controls a unit with charm. You might regain it when he uses this weapon special.
 +
 +
<syntaxhighlight lang='wml'>
 +
#define WEAPON_SPECIAL_CHARM
 +
    # 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]
 +
#enddef
 +
 +
#define CHARMING_EVENTS
 +
    # 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
 +
        id=charm_detect
 +
 +
        [filter_attack]
 +
            special_id=weapon_charm
 +
        [/filter_attack]
 +
        [filter_second]
 +
            canrecruit=no
 +
            level=0,1
 +
            [not]
 +
                status=unplagueable
 +
            [/not]
 +
        [/filter_second]
 +
 +
        [modify_unit]
 +
            [filter]
 +
                id=$unit.id
 +
            [/filter]
 +
            [variables]
 +
                charm_has_worked=yes
 +
            [/variables]
 +
        [/modify_unit]
 +
    [/event]
 +
 +
    # Event that shifts a unit to the other side,
 +
    # if the defending unit:
 +
    #      - is lvl0 or lvl1
 +
    #      - and is not a leader unit
 +
    #      - and is a not a "non-living" creature
 +
    # Then:
 +
    # -> if the attacker missed all attacks, it goes to the defender’s side.
 +
    # -> if the attacker hit once at least, the defender goes to the attacker’s side.
 +
    [event]
 +
        name=attack_end
 
         first_time_only=no
 
         first_time_only=no
   
+
        id=charm_convert
 +
 
 
         [filter_attack]
 
         [filter_attack]
             special=knockback
+
             special_id=weapon_charm
 
         [/filter_attack]
 
         [/filter_attack]
   
 
 
         [filter_second]
 
         [filter_second]
 +
            canrecruit=no
 +
            level=0,1
 
             [not]
 
             [not]
                 [filter_location]
+
                 status=unplagueable
                    terrain=*^V*
 
                [/filter_location]
 
 
             [/not]
 
             [/not]
 
         [/filter_second]
 
         [/filter_second]
   
+
 
 
         [if]
 
         [if]
 
             [variable]
 
             [variable]
                 name=second_unit.hitpoints
+
                 name=unit.variables.charm_has_worked
                 greater_than=0
+
                 boolean_equals=no
 
             [/variable]
 
             [/variable]
   
+
            [then]
                [store_locations]
+
                 {VARIABLE unit.side $second_unit.side}
                    [not]
+
                [unstore_unit]
                        [filter]
+
                    variable=unit
                        [/filter]
+
                    text= _ "Charm failed!"
                    [/not]
+
                    {COLOR_HARM}
   
+
                [/unstore_unit]
                    [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]
 
             [/then]
 +
            [else]
 +
                {VARIABLE second_unit.side $unit.side}
 +
                [unstore_unit]
 +
                    variable=second_unit
 +
                    text= _ "Charmed!"
 +
                    {COLOR_HEAL}
 +
                [/unstore_unit]
 +
 +
                # The variable needs to be unset as well.
 +
                {CLEAR_VARIABLE unit.variables.charm_has_worked}
 +
                [unstore_unit]
 +
                    variable=unit
 +
                [/unstore_unit]
 +
            [/else]
 
         [/if]
 
         [/if]
 
     [/event]
 
     [/event]
 
+
#enddef
=== Charm ===
+
</syntaxhighlight>
 
 
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]
 
 
        [special_filter]
 
            weapon={WEAPON}
 
        [/special_filter]
 
 
        {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}
 
        {CLEAR_VARIABLE 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
 
                    kill=no
 
                [/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]
 
                                        find_vacant=no
 
                                    [/unstore_unit]
 
                                [/then]
 
                            )}
 
                        [/then]
 
                    )}
 
                {NEXT i}
 
 
                {CLEAR_VARIABLE possibly_charmed}
 
            [/then]
 
        )}
 
    [/event]
 
#enddef
 
  
 
=== Bloodlust ===
 
=== Bloodlust ===
  
Bloodlust is a very simple ability. If a unit that has bloodlust kills an enemy unit when attacking, it may attack again, provided that there are more enemy units adjacent to it.
+
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 209: Line 338:
 
  {BLOODLUST (type=Dwarvish Ulfserker)}
 
  {BLOODLUST (type=Dwarvish Ulfserker)}
  
#define BLOODLUST FILTER
+
<syntaxhighlight lang='wml'>
    [event]
+
#define BLOODLUST FILTER
        name=die
+
    [event]
        first_time_only=no
+
        name=die
+
        first_time_only=no
        [filter_second]
+
 
            {FILTER}
+
        [filter_second]
        [/filter_second]
+
            {FILTER}
+
        [/filter_second]
        {MODIFY_UNIT x,y=$x2,$y2 moves 0}
+
 
        {MODIFY_UNIT x,y=$x2,$y2 attacks_left 1}
+
        [modify_unit]
    [/event]
+
            [filter]
#enddef
+
                x,y=$x2,$y2
 +
            [/filter]
 +
            moves=0
 +
            attacks_left=1
 +
        [/modify_unit]
 +
    [/event]
 +
#enddef
 +
</syntaxhighlight>
  
 
=== Pickpocket ===
 
=== Pickpocket ===
  
This special could also be called loot. When a unit with this attack special sucessfully hits an enemy unit, it gains a certain amount of gold.
+
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:
  
<code>#define WEAPON_SPECIAL_PICKPOCKET<br />
+
<syntaxhighlight lang='wml'>
# Canned definition of the pickpocket ability to be included in a<br />
+
#define WEAPON_SPECIAL_PICKPOCKET
# [specials] clause.
+
    # Canned definition of the pickpocket ability to be included in a
# dummy weapon special used to describe the effect to the user
+
    # [specials] clause.
# and filter on special's id
+
    # dummy weapon special used to describe the effect to the user
[dummy]
+
    # and filter on special's id
  id=weapon_pickpocket
+
    [dummy]
  name= _ "pickpocket"
+
        id=weapon_pickpocket
  name_inactive= _ "pickpocket"
+
        name= _ "pickpocket"
  description= _ "pickpocket :
+
        description= _ "Gain money for attacking your foe. Each strike scores you one gold."
Gain money for attacking your foe. Each strike scores you one gold."
+
        apply_to=opponent
  description_inactive= _ "pickpocket :
+
        active_on=offense
Gain money for attacking your foe. Each strike scores you one gold."
+
    [/dummy]
  apply_to=opponent
+
[/specials]
[/dummy]
+
[/attack]
[/specials]
+
 
[/attack]
+
    # event that creates a "pickpocket has worked" variable
# event that creates a "pickpocket has worked" variable
+
    # and sets it to "yes" if the attacker hits at least once.
# and sets it to "yes" if the attacker hits at least once.
+
    [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=weapon_pickpocket
+
        [/filter_attack]
  [/filter_attack]  
+
        [store_unit]
  [store_unit]
+
            [filter]
      [filter]
+
                x,y=$x1,$y1
        x,y=$x1,$y1
+
            [/filter]
      [/filter]
+
            variable=unit_att_with_pickpocket
      variable=unit_att_with_pickpocket
+
            mode=append
      mode=append
+
        [/store_unit]
  [/store_unit]
+
        [set_variable]
  [set_variable]
+
            name=unit_att_with_pickpocket.variables.pickpocket_has_worked
      name=unit_att_with_pickpocket.variables.pickpocket_has_worked
+
            value=yes
      value=yes
+
        [/set_variable]
  [/set_variable]
+
        [unstore_unit]
  [unstore_unit]
+
            variable=unit_att_with_pickpocket
      variable=unit_att_with_pickpocket
+
        [/unstore_unit]
  [/unstore_unit]  
+
        {CLEAR_VARIABLE unit_att_with_pickpocket}
  {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=weapon_pickpocket
+
        [/filter_attack]
  [/filter_attack]  
+
        [store_unit]
  [store_unit]
+
            [filter]
      [filter]
+
                x,y=$x1,$y1
        x,y=$x1,$y1
+
            [/filter]
      [/filter]
+
            variable=pickpocketer
      variable=pickpocketer
+
            mode=append
      mode=append
+
        [/store_unit]   
  [/store_unit]   
+
        [store_unit]
  [store_unit]
+
            [filter]
      [filter]
+
                x,y=$x2,$y2
        x,y=$x2,$y2
+
            [/filter]
      [/filter]
 
      variable=pickpocketed
 
      mode=append
 
  [/store_unit] 
 
  [if]
 
      [variable]
 
        name=pickpocketer.variables.pickpocket_has_worked
 
        equals=yes
 
      [/variable]
 
      [then]
 
    [gold]
 
    side=$side_number
 
    amount=2
 
    [/gold]
 
        [unstore_unit]
 
 
             variable=pickpocketed
 
             variable=pickpocketed
             text="!"
+
             mode=append
            {COLOR_HEAL}
+
        [/store_unit]
        [/unstore_unit]
+
        [if]
      [/then]
+
            [variable]
  [/if]  
+
                name=pickpocketer.variables.pickpocket_has_worked
  {CLEAR_VARIABLE pickpocketer}
+
                boolean_equals=yes
  {CLEAR_VARIABLE pickpocketed}
+
            [/variable]
[/event]
+
            [then]
[+attack]
+
                [gold]
[+specials]
+
                    side=$side_number
#enddef</code>
+
                    amount=2
 +
                [/gold]
 +
                [unstore_unit]
 +
                    variable=pickpocketed
 +
                    text="!"
 +
                    {COLOR_HEAL}
 +
                [/unstore_unit]
 +
            [/then]
 +
        [/if]
 +
        {CLEAR_VARIABLE pickpocketer,pickpocketed}
 +
    [/event]
 +
    [+attack]
 +
    [+specials]
 +
#enddef
 +
</syntaxhighlight>
  
 
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
    <code>[gold]
+
[gold]
    side=$side_number
+
    side=$side_number
    amount='''X'''
+
    amount='''X'''
    [/gold]</code>
+
[/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
  <code>[event]
+
  [event]
  '''name=attacker_hits'''
+
    '''name=attacker_hits'''
  first_time_only=no</code>
+
    first_time_only=no</code>
  
 
to
 
to
  
  <code>[event]
+
  [event]
  '''name=attack_end'''
+
    '''name=attack_end'''
  first_time_only=no</code>
+
    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 (http://www.wesnoth.org/forum/viewtopic.php?f=19&t=20100).
+
Any unit with this will gain an additional point of damage per strike every time it kills an enemy. Made for Melon’s Youkai faction (https://r.wesnoth.org/t20100). A variant which uses this as weapon special is used in ageless era (https://r.wesnoth.org/t25274).
  
To give a unit the ability, place the following in any .cfg file loaded by the campaign or era:
+
One can add this as ability or as weapon special. As ability it will be used when the other unit is killed by any attack of this unit. Using it as weapon special is similar to the Necromancer’s plague staff: It will only take effect when the attack with the special is used to land the killing blow.
  
#define ABILITY_SOULTAKER
+
Place the following in any .cfg file loaded by the campaign or era:
[dummy]
 
id=soultaker
 
name= _ "soultaker"
 
description=_ "Soultaker:
 
This unit gains an additional point added to its maximum damage whenever it kills a living unit."
 
[/dummy]
 
 
[/abilities]
 
[event]
 
name=die
 
first_time_only=no
 
[filter]
 
[not]
 
[wml_filter]
 
[status]
 
not_living="yes"
 
[/status]
 
[/wml_filter]
 
[/not]
 
[/filter]
 
 
[filter_second]
 
ability=soultaker
 
[/filter_second]
 
 
[unstore_unit]
 
variable=second_unit
 
{COLOR_HEAL}
 
text= _ "+1 damage"
 
find_vacant=no
 
[/unstore_unit]
 
 
[object]
 
silent=yes
 
duration=forever
 
[filter]
 
x,y=$x2,$y2
 
[/filter]
 
 
[effect]
 
apply_to=attack
 
range=melee
 
increase_damage=1
 
increase=1
 
[/effect]
 
[/object]
 
[/event]
 
 
[+abilities]
 
#enddef
 
  
And the following in the unit's [abilities] tag:
+
<syntaxhighlight lang='wml'>
 +
#define SOULTAKER_DUMMY
 +
  [dummy]
 +
      id=soultaker
 +
      name= _ "soultaker"
 +
      description=_ "This unit gains an additional point added to its melee damage whenever it kills a living unit."
 +
  [/dummy]
 +
#enddef
  
{ABILITY_SOULTAKER}
+
#define SOULTAKER_EVENT
 +
    [event]
 +
        name=die
 +
        first_time_only=no
 +
        id=soultaker
  
===Soultaker (weapon special)===
+
        [filter]
 +
            [not]
 +
                status=undrainable
 +
            [/not]
 +
        [/filter]
  
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 (http://forums.wesnoth.org/viewtopic.php?f=19&t=25274) based on previous ability.
+
        # Use this check if you want to use Soultaker as ability.
 +
        [filter_second]
 +
            ability=soultaker
 +
        [/filter_second]
  
To give a unit the weapon special, load the following code:
+
        # To use Soultaker as weapon special, use this check INSTEAD of the above one.
 +
        # [filter_second_attack]
 +
        #    special_id=soultaker
 +
        # [/filter_second_attack]
  
#define WEAPON_SPECIAL_SOULTAKER
+
        [floating_text]
  [dummy]
+
            x,y=$x2,$y2
  id=soultaker
+
            text="<span color='#00ff00'>" + _ "+1 damage" + "</span>"
  name= _ "soultaker"
+
        [/floating_text]
  description=_"Soultaker:
 
This unit gains an additional point added to its maximum damage whenever it kills a living unit."
 
  [/dummy]
 
[/specials]
 
[/attack]
 
[event]
 
  name=die
 
  first_time_only=no
 
  id=soultaker_event
 
  [filter]
 
  [not]
 
    [filter_wml]
 
    [status]
 
      not_living="yes"
 
    [/status]
 
    [/filter_wml]
 
  [/not]
 
  [/filter]
 
  [filter_second_attack]
 
  special=soultaker
 
  [/filter_second_attack]
 
  [unstore_unit]
 
  variable=second_unit
 
  {COLOR_HEAL}
 
  text= _ "+1 damage"
 
  find_vacant=no
 
  [/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:
+
        [object]
 +
            silent=yes
 +
            duration=forever
 +
            [filter]
 +
                x,y=$x2,$y2
 +
            [/filter]
  
  {WEAPON_SPECIAL_SOULTAKER}
+
            [effect]
 +
                apply_to=attack
 +
                increase_damage=1
 +
                range=melee
 +
                # This will increase all melee attacks by 1. To only increase the attack used in this fight, use
 +
                # name=$second_weapon.name
 +
            [/effect]
 +
        [/object]
 +
    [/event]
 +
#enddef
 +
</syntaxhighlight>
  
===Charm (Type 2)===
+
And the following in the unit's [abilities] tag. If you changed the code to use the weapon special variant, add instead to the unit’s [attack] an [specials] tag, into which you place:
  
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, using 1.6.x syntax (and compatible with 1.7.x], made for Melon's Youkai faction (http://www.wesnoth.org/forum/viewtopic.php?f=21&t=22539)
+
{SOULTAKER_DUMMY}
  
 +
Add to the [campaign] or [era] tag:
  
  #define WEAPON_SPECIAL_CHARM
+
  {SOULTAKER_EVENT}
# 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"
 
name_inactive= _ "charm"
 
description= _ "Charm :
 
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 ennemy leader or a non-living creature."
 
description_inactive= _ "Charm :
 
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 ennemy leader or a non-living creature."
 
apply_to=opponent
 
[/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=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=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=weapon_charm
 
[/filter_attack]
 
[filter_second]
 
canrecruit=no
 
[and]
 
level=0
 
[or]
 
level=1
 
[/or]
 
[/and]
 
[and]
 
[not]
 
[filter_wml]
 
[status]
 
not_living=yes
 
[/status]
 
[/filter_wml]
 
[/not]
 
[/and]
 
[/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
 
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}
 
{CLEAR_VARIABLE charmed}
 
[/event]
 
 
[+attack]
 
[+specials]
 
 
#enddef
 
  
 
== Works ==
 
== Works ==
  
Unit with ability ''works'' will produce 1 gold per turn.
+
Unit with ability ''works'' will produce 1 gold per turn. This mechanism is used in the mainline multiplayer map »A New Land«.
  
 
Put this macro into you code before the last piece of code.
 
Put this macro into you code before the last piece of code.
<pre>
+
<syntaxhighlight lang='wml'>
 
#define ABILITY_WORKS
 
#define ABILITY_WORKS
    [leadership]
+
    [works]
          value=0
+
        id=peasant_works
          id=peasant_works
+
        name="works"
          cumulative=no
+
        description= _ "This unit produces 1 gold per turn."
          name="works"
+
     [/works]
          description= _ "Works:
+
#enddef
This unit produces 1 gold per turn."
+
</syntaxhighlight>
     [/leadership]
 
#enddef
 
</pre>
 
  
 
Put this event into your code.
 
Put this event into your code.
<pre>
+
<syntaxhighlight lang='wml'>
 
[event]
 
[event]
name=side turn
+
    name=side turn
first_time_only=no
+
    first_time_only=no
[store_unit]
+
    [store_unit]
[filter]
+
        [filter]
            #your filter here... for example type=Peasant
+
            ability=peasant_works
[/filter]
+
            side=$side_number
variable=worker
+
        [/filter]
kill=no
+
        variable=workers
[/store_unit]
+
    [/store_unit]
 +
 
 +
    [foreach]
 +
        array=workers
 +
        [do]
 +
            [gold]
 +
                side=$this_item.side
 +
                amount=1
 +
            [/gold]
 +
            [floating_text]
 +
                x,y=$this_item.x,$this_item.y
 +
                text="<span color='#ffff00'>" + _ "+1 gold" + "</span>"
 +
            [/floating_text]
 +
        [/do]
 +
    [/foreach]
  
{FOREACH worker i}
+
    {CLEAR_VARIABLE workers}
+
[/event]
[gold]
+
</syntaxhighlight>
                            side=$side_number
 
                            amount=1
 
                        [/gold]
 
[unstore_unit]
 
variable=worker[$i]
 
text="1"
 
        red,green,blue=255,255,0
 
[/unstore_unit]
 
 
{NEXT i}
 
 
{CLEAR_VARIABLE worker}
 
[/event]
 
</pre>
 
  
And give the unit the ability like this
+
And give the unit the ability like this:
<pre>
+
<syntaxhighlight lang='wml'>
[object]
+
[object]
    silent=yes
+
    silent=yes
    [effect]
+
    [effect]
apply_to=new_ability
+
        apply_to=new_ability
          [abilities]
+
        [abilities]
            {ABILITY_WORKS}
+
            {ABILITY_WORKS}
          [/abilities]
+
        [/abilities]
 
     [/effect]
 
     [/effect]
 
  [/object]
 
  [/object]
</pre>
+
</syntaxhighlight>
 
 
  
 
== Mind Flay  ==
 
== Mind Flay  ==
Line 698: Line 603:
 
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:
#define WEAPON_SPECIAL_MIND_FLAY
+
<syntaxhighlight lang='wml'>
 +
#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>
 +
 
 +
Include these events into your scenario:
 +
<syntaxhighlight lang='wml'>
 +
[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 sub $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]
 +
</syntaxhighlight>
 +
 
 +
== Initiative  ==
 +
 
 +
Initiative is an aura ability. Much like leadership, it affects adjacent allies but not the unit itself.
  
Include these events into your scenario.
+
The ability is used in HttT by Li’sar, you can copy the code from [https://github.com/wesnoth/wesnoth/blob/master/data/campaigns/Heir_To_The_Throne/utils/abilities.cfg data/campaigns/Heir_To_The_Throne/utils/abilities.cfg]
[event]
 
name=attack
 
first_time_only=no
 
[special_filter]
 
weapon=mind flay
 
[/special_filter]
 
{VARIABLE hit_number 0}
 
[/event]
 
[event]
 
name=attacker_hits
 
first_time_only=no
 
[special_filter]
 
weapon=mind flay
 
[/special_filter]
 
{VARIABLE_OP hit_number add 1}
 
[/event]
 
[event]
 
name=attack_end
 
first_time_only=no
 
[special_filter]
 
weapon=mind flay
 
[/special_filter]
 
{VARIABLE_OP second_unit.experience add -$hit_number}
 
{VARIABLE_OP unit.experience add $hit_number}
 
[unstore_unit]
 
variable=unit
 
find_vacant=no
 
text=$hit_number
 
blue=255
 
[/unstore_unit]
 
[unstore_unit]
 
variable=second_unit
 
find_vacant=no
 
[/unstore_unit]
 
{CLEAR_VARIABLE hit_number}
 
[/event]
 
  
Note: In dev version substitute "name=" instead of "weapon=".
+
== Blitz ==
  
== Initiative  ==
+
UtBS and TroW have with ''distract'' an ability, which lets adjacent units ignore the ZoC. It works similar to leadership and initiative, the bonus is valid ''while'' the unit is adjacent.
  
Initiative is an aura ability. Much like Leadership, it affects adjacent allies but not the unit itself.
+
This ability does the same, but it works similar to healing: The bonus is applied at the beginning of the turn and still valid when not anymore being adjacent.
  
#define AURA_INITIATIVE TYPE
+
But it also differs from the way healing works for allied units:
[dummy]
+
* With healing, it is useful if you move your injured unit to an ally, so that it is adjacent at the healing time.
id=initiative
+
* With this ability, the unit who wants the bonus must be adjacent at the start of <i>his own</i> turn.
name= _ "initiative"
 
description= _ "Initiative:
 
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.
+
<syntaxhighlight lang='wml'>
 +
#define ABILITY_BLITZ
 +
    [dummy]
 +
        id=blitz   
 +
        name= _ "blitz"
 +
        description= _ "Allies that start their turn adjacent to this unit are granted skirmisher for that turn."
 +
        special_note= _ "Instead of healing other units, this unit grants temporarily skirmisher for allied units at the beginning of their turn."
 +
        active_on=offense
 +
        affect_self=no
 +
        affect_allies=yes
 +
        [affect_adjacent][/affect_adjacent]
 +
    [/dummy]
 +
#enddef
 +
</syntaxhighlight>
  
== Blitz ==
+
<syntaxhighlight lang='wml'>
 +
#define ABILITY_BLITZ_EVENT
 +
    [event]
 +
        name=side turn
 +
        first_time_only=no
  
Blitz is an aura ability. Much like Leadership, it affects adjacent allies but not the unit itself.
+
        [modify_unit]
 +
            # The units adjacent to a unit with the blitz ability …
 +
            [filter]
 +
                side=$side_number
 +
                [filter_adjacent]
 +
                    is_enemy=no
 +
                    ability=blitz
 +
                [/filter_adjacent]
 +
            [/filter]
  
#define AURA_BLITZ TYPE
+
            # … receive temporarily this ability.
[dummy]
+
            [object]
id=blitz
+
                duration=turn end
name= _ "blitz"
+
                [effect]
description= _ "Blitz:
+
                    apply_to=new_ability
Allies that start their turn adjacent to this unit are granted Skirmisher for that turn."
+
                    [abilities]
[/dummy]
+
                        {ABILITY_SKIRMISHER}
[/abilities]
+
                    [/abilities]
[event]
+
                [/effect]
name=side turn
+
            [/object]
first_time_only=no
+
        [/modify_unit]
+
    [/event]
[store_unit]
+
#enddef
[filter]
+
</syntaxhighlight>
 
[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  "Skirmisher:
 
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 ==
 
== Immune to drain or plague or poison ==
This macro will grant immunity to drain for a unit. By changing a few specials' names and ability names, you can easily expand it for an immunity to plague or drain. This ''will not'' work properly if there is a unit with multiple weapon specials that might be affected (plague+drain, poison+plague, poison+drain, poison+drain+plague), in this case, it will grant immunity to all of them.
 
  
#define ABILITY_UNDRAINABLE
+
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'''.
#This is the ability tagging the unit for the events. Use it as an ability.
+
 
  [dummy]
+
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.
      id=undrainable
+
 
      name= _ "undrainable"
+
<syntaxhighlight lang='wml'>
      description= _ "this unit is cannot be drained.
+
#define TRAIT_UNDRAINABLE
It will still be damaged by draining attacks, but the enemy will not gain any HP."
+
    # We make vampires undrainable with a trait.
  [/dummy]
+
    # Traits show up in the help browser, thus they should have proper descriptions.
#enddef
+
    [trait]
+
        id=undrainable
#define UNDRAINABLE
+
        availability=musthave
#Place those in every scenario (or era) where these units will appear.
+
        male_name= _ "vampire"
#It makes the unit undead for the time of the attack (it cannot be seen),
+
        female_name= _ "female^vampire"
#if the particular weapon special was used.
+
        description= _ "This unit’s life force cannot be drained."
  [event]
+
        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."
      name=attack
+
        # vampire is not a good name for a trait, as you can give the trait to anybody
      first_time_only=no
+
        [effect]
      [filter_attack]
+
             apply_to=status
          special=drains
+
            add=undrainable
      [/filter_attack]
+
        [/effect]
      [filter_second]
+
        [effect]
          ability=undrainable
+
            apply_to=status
      [/filter_second]
+
            add=unplagueable
          [modify_unit]
+
        [/effect]
        [filter]
+
    [/trait]
            x,y=$x2,$y2
+
#enddef
        [/filter]
+
</syntaxhighlight>
            [status]
+
 
             not_living=yes
+
When you define a new [unit'''_type'''], add to it:
            [/status]
+
{TRAIT_UNDRAINABLE}
          [/modify_unit]
+
num_traits=3 # if you still want it to get 2 other traits
  [/event]
+
 
+
In other cases, if you want to make unit immune, i.e. one from mainline, give it the trait another way:
  [event]
+
<syntaxhighlight lang='wml'>
      name=attack_end
+
[event]
      first_time_only=no
+
    name=prerecruit
      [filter_attack]
+
    first_time_only=no
          special=drains
+
    [filter]
      [/filter_attack]
+
        type_adv_tree=Mage
      [filter_second]
+
        [or]
          ability=undrainable
+
            race=elf
      [/filter_second]
+
             side=2
          [modify_unit]
+
        [/or]
        [filter]
+
    [/filter]
            x,y=$x2,$y2
+
 
        [/filter]
+
    [modify_unit]
            [status]
+
        [filter]
            not_living=no
+
            id=$unit.id
            [/status]
+
        [/filter]
          [/modify_unit]
+
        {TRAIT_UNDRAINABLE}
  [/event]
 
 
  [event]
 
      name=attack
 
      first_time_only=no
 
      [filter_second_attack]
 
          special=drain
 
      [/filter_second_attack]
 
      [filter]
 
          ability=undrainable
 
      [/filter]
 
          [modify_unit]
 
        [filter]
 
            x,y=$x1,$y1
 
        [/filter]
 
            [status]
 
             not_living=yes
 
            [/status]
 
          [/modify_unit]
 
  [/event]
 
 
  [event]
 
      name=attack_end
 
      first_time_only=no
 
      [filter_second_attack]
 
          special=drain
 
      [/filter_second_attack]
 
      [filter]
 
          ability=undrainable
 
      [/filter]
 
          [modify_unit]
 
        [filter]
 
            x,y=$x1,$y1
 
        [/filter]
 
            [status]
 
            not_living=no
 
            [/status]
 
          [/modify_unit]
 
  [/event]
 
#enddef
 
  
A WML filter may be added instead of the type if you want some units of that type to not have the ability.
+
        # Or without trait / object:
 +
        # [status]
 +
        #    undrainable=yes
 +
        # [/status]
 +
    [/modify_unit]
 +
[/event]
 +
</syntaxhighlight>
  
 
== 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.
  
 
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 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.
+
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"
 
        name_inactive= _ "whirlwind"
 
        description= _ "Whirlwind: When this attack is used, all units adjacent the attacker take the damage, and cannot be countered."
 
        description_inactive= _ "Whirlwind: When this attack is used, all units adjacent the attacker take the damage, and cannot be countered."
 
        value=0
 
        apply_to=opponent
 
[/attacks]
 
 
 
  
 +
<syntaxhighlight lang='wml'>
 +
[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]
 +
</syntaxhighlight>
  
This is the event.
+
This is the event:
 +
<syntaxhighlight lang='wml'>
 
  [event]
 
  [event]
name=attacker_hits
+
    name=attacker_hits
first_time_only=no
+
    first_time_only=no
[filter_attack]
+
    [filter_attack]
        special=whirlwind
+
        special_id=whirlwind
[/filter_attack]
+
    [/filter_attack]
{VARIABLE has_drain 0} # Notifies the weapon specials
+
    {VARIABLE has_drain no}     # Notifies the weapon specials
{VARIABLE has_slow no}
+
    {VARIABLE has_slow no}
{VARIABLE has_poison no}
+
    {VARIABLE has_poison no}
{FOREACH weapon.specials.damage i}
+
    [if]
[if]
+
        [variable]
[variable]
+
            name=weapon.specials.drains.id
name=weapon.specials.drains.id
+
            equals=drains
equals=drains
+
        [/variable]
[/variable]
+
        [then]
[then]
+
            {VARIABLE has_drain yes}
{VARIABLE has_drain 1}
+
        [/then]
+
    [/if]
[/then]
+
    [if]
[/if]
+
        [variable]
{NEXT i}
+
            name=weapon.specials.poison.id
[if]
+
            equals=poison
[variable]
+
        [/variable]
name=weapon.specials.poison.id
+
        [then]
equals=poison
+
            {VARIABLE has_poison yes}
[/variable]
+
        [/then]
[then]
+
    [/if]
{VARIABLE has_poison yes}
+
    [if]
+
        [variable]
[/then]
+
            name=weapon.specials.slow.id
[/if]
+
            equals=slow
[if]
+
        [/variable]
[variable]
+
        [then]
name=weapon.specials.slow.id
+
            {VARIABLE has_slow yes}
equals=slow
+
        [/then]
[/variable]
+
    [/if]
[then]
+
    [if]
{VARIABLE has_slow yes}
+
        [variable]
+
            name=has_drain
  [/then]
+
            boolean_equals=yes
[/if]
+
        [/variable]
[if]
+
        [then]
[variable]
+
            [store_unit]       #We need to know how many units were drained, and what were their resistances
name=has_drain
+
                [filter]
equals=0
+
                    [filter_adjacent]
[/variable]
+
                        x,y=$x1,$y1
[else]
+
                    [/filter_adjacent]
[store_unit] #We need to know how many units were drained, and what were their resistances
+
                    [not]
  [filter]
+
                        side=$unit.side
[filter_adjacent]
+
                    [/not]
x,y=$x1,$y1
+
                    [not]        #The target unit is already hit by the attack
[/filter_adjacent]
+
                        x,y=$x2,$y2
[not]
+
                    [/not]
side=1             #If you want to use it in an era, use side=$unit.side instead
+
                    [not]
[/not]
+
                        status=undrainable,petrified
[not]        #The target unit is already hit by the attack
+
                    [/not]
x,y=$x2,$y2
+
                [/filter]
[/not]
+
                variable=units
[not]         #Undead are undrainable
+
            [/store_unit]
race=undead
+
            {VARIABLE healed_amount 0}
[/not]
+
            [foreach]
[/filter]
+
                array=units
variable=units
+
                [do]
[/store_unit]
+
                    [switch]            #Check the resistances
{VARIABLE healed_amount 0}
+
                        variable=weapon.type
  {FOREACH units i}
+
                        [case]
[switch]            #Check the resistances
+
                            value=arcane
variable=weapon.type
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.arcane*$weapon.damage)"}
[case]
+
                        [/case]
value=arcane
+
                        [case]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.arcane*$weapon.damage)"}
+
                            value=fire
[/case]
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.fire*$weapon.damage)"}
[case]
+
                        [/case]
value=fire
+
                        [case]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.fire*$weapon.damage)"}
+
                            value=cold
[/case]
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.cold*$weapon.damage)"}
[case]
+
                        [/case]
value=cold
+
                        [case]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.cold*$weapon.damage)"}
+
                            value=blade
[/case]
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.blade*$weapon.damage)"}
[case]
+
                        [/case]
value=blade
+
                        [case]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.blade*$weapon.damage)"}
+
                            value=pierce
[/case]
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.pierce*$weapon.damage)"}
[case]
+
                        [/case]
value=pierce
+
                        [case]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.pierce*$weapon.damage)"}
+
                            value=impact
[/case]
+
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.impact*$weapon.damage)"}
[case]
+
                        [/case]
value=impact
+
                    [/switch]
    {VARIABLE_OP healed_amount add "$($units[$i].resistance.impact*$weapon.damage)"}
+
                [/do]
[/case]
+
            [/foreach]
  [/switch]
+
            #Float the healed amount over the unit, like if it had drained
{NEXT i}
+
            [floating_text]        #Two numbers will float, the one from the regular hit and one from this
[store_unit]        #Float the healed amount over the unit, like if it had drained
+
                x,y=$x1,$y1       #Operating with huge numbers because rounding is a problem
[filter]   #Two numbers will float, the one from the regular hit and one from this
+
                text="<span color='#00ff00'>" + "$($healed_amount/200)" + "</span>"
x,y=$x1,$y1
+
            [/floating_text]
[/filter]
+
            [heal_unit]
kill=no
+
                [filter]
variable=FLOATING_TEXT_temp
+
                    x,y=$x1,$y1
[/store_unit]
+
                [/filter]
[unstore_unit]
+
                amount=$($healed_amount/200)
variable=FLOATING_TEXT_temp
+
                restore_statuses=no
find_vacant=no
+
                animate=no
red,green,blue=0,255,0
+
            [/heal_unit]
text=$($healed_amount/200)         #Operating with huge numbers because rounding is a problem
+
            {CLEAR_VARIABLE units,healed_amount}
[/unstore_unit]
+
        [/then]
{CLEAR_VARIABLE FLOATING_TEXT_temp}
+
    [/if]
[heal_unit]
+
    [harm_unit]
[filter]
+
        [filter]
x,y=$x1,&y1
+
            [filter_adjacent]
[/filter]
+
                x,y=$x1,$y1
amount=$($healed_amount/200)
+
            [/filter_adjacent]
animate=no
+
            [not]
[/heal_unit]
+
                side=$unit.side
{CLEAR_VARIABLE units}
+
            [/not]
{CLEAR_VARIABLE healed_amount}
+
            [not]
[/else]
+
                x,y=$x2,$y2
[/if]
+
            [/not]
[harm_unit]
+
            [not]
[filter]
+
                status=petrified
[filter_adjacent]
+
            [/not]
x,y=$x1,$y1
+
        [/filter]
[/filter_adjacent]
+
        [filter_second]
[not]
+
            x,y=$x1,$y1
side=1             #If you want to use it in an era, use side=$unit.side instead
+
        [/filter_second]
[/not]
+
        amount=$weapon.damage
[not]            #If you want to use it in an era, use side=$unit.side instead
+
        damage_type=$weapon.type
x,y=$x2,$y2
+
        fire_event=yes
[/not]
+
        experience=yes      #You will have to think about this
[/filter]
+
        poisoned=$has_poison  #We have detected these two effects before
[filter_second]
+
        slowed=$has_slow
x,y=$x1,$y1
+
    [/harm_unit]  
[/filter_second]
+
    {CLEAR_VARIABLE has_slow,has_poison,has_drain}
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}
 
{CLEAR_VARIABLE has_poison}
 
{CLEAR_VARIABLE has_drain}
 
 
  [/event]
 
  [/event]
 +
</syntaxhighlight>
  
 
== See Also ==
 
== See Also ==

Latest revision as of 21:10, 23 November 2024

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 can be included directly in the ability or weapon special tag (Version 1.19.4 and later only). For earlier versions, these [event]s must be added to the game explicitly, in addition to the unit with the ability or weapon special!

How to include [event]s?

  • Add it inside the ability or weapon special tag. (Version 1.19.4 and later only)
  • Add it directly to the scenario file.
  • Add it directly to the [era].
  • Add it inside a [unit_type] definition.
  • Add it directly in your [campaign] tag.
  • Add it inside a [resource], which is then loaded from your [campaign] tag or an individual scenario.

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.

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_target_hex}
        [/then]
    [/if]
[/event]

Charm

temporary

When a unit is hit by a charm attack, it instantly jumps to the attacker's side, and returns to its original side at the end of the 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
        # Works only as attacker.
        # If you want to make a weapon special for this event, set:
        # [dummy]active_on=offense, then the engine greys out the weapon special on defense.
        first_time_only=no
        id=charm_as_attacker

        [filter]
            {FILTER}
        [/filter]

        [filter_attack]
            name={WEAPON}
            # or special_id=charm, if you create a [dummy] weapon special
        [/filter_attack]

        [filter_second]
            # If the leader is charmed, it might end the scenario,
            # as the other side is now considered defeated without a leader.
            # Better exclude leaders.
            canrecruit=no
            # If the unit would die from the damage,
            # we should not interfere with the event.
            formula="self.hitpoints > 0"
        [/filter_second]

        # Charm the unit
        # Changing the side will also immediately stop the combat and grant both units XP
        [modify_unit]
            [filter]
                x,y=$x2,$y2
            [/filter]
            [variables]
                # to remember the original side
                real_side=$second_unit.side
            [/variables]
            [status]
                # optional, just to easier find the unit in the other event
                charmed=yes
            [/status]
            side=$unit.side
            moves=1
            attacks_left=1
        [/modify_unit]

        [floating_text]
            x,y=$x2,$y2
            # po: short text, only displayed for a moment
            text="<span color='#ffc0cb'>" + _ "charm" + "</span>"
        [/floating_text]
    [/event]

    [event]
        name=side turn end, scenario end
        # Releasing the unit in the same turn has a few reasons:
        # - a charmed unit cannot be charmed again
        # - if the scenario ends, we can still correct the ownership
        # - things like healing by allies work the usual way
        first_time_only=no
        id=charm_release

        [store_unit]
            [filter]
                side=$side_number
                status=charmed
            [/filter]
            variable=charmed_units
        [/store_unit]

        [foreach]
            array=charmed_units
            [do]
                {VARIABLE this_item.side $this_item.variables.real_side}
                {CLEAR_VARIABLE this_item.variables.real_side}
                {CLEAR_VARIABLE this_item.status.charmed}
                [unstore_unit]
                    variable=this_item
                [/unstore_unit]
            [/do]
        [/foreach]

        {CLEAR_VARIABLE charmed_units}
    [/event]
#enddef

permanent

This version of the weapon special is a gamble. You can obtain the other unit for good, but you also risk losing this unit. When you lose this unit, the opponent controls a unit with charm. You might regain it when he uses this weapon special.

#define WEAPON_SPECIAL_CHARM
    # 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]
#enddef

#define CHARMING_EVENTS
    # 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
        id=charm_detect

        [filter_attack]
            special_id=weapon_charm
        [/filter_attack]
        [filter_second]
            canrecruit=no
            level=0,1
            [not]
                status=unplagueable
            [/not]
        [/filter_second]

        [modify_unit]
            [filter]
                id=$unit.id
            [/filter]
            [variables]
                charm_has_worked=yes
            [/variables]
        [/modify_unit]
    [/event]

    # Event that shifts a unit to the other side,
    # if the defending unit:
    #       - is lvl0 or lvl1
    #       - and is not a leader unit 
    #       - and is a not a "non-living" creature
    # Then:
    # -> if the attacker missed all attacks, it goes to the defender’s side.
    # -> if the attacker hit once at least, the defender goes to the attacker’s side.
    [event]
        name=attack_end
        first_time_only=no
        id=charm_convert

        [filter_attack]
            special_id=weapon_charm
        [/filter_attack]
        [filter_second]
            canrecruit=no
            level=0,1
            [not]
                status=unplagueable
            [/not]
        [/filter_second]

        [if]
            [variable]
                name=unit.variables.charm_has_worked
                boolean_equals=no
            [/variable]
            [then]
                {VARIABLE unit.side $second_unit.side}
                [unstore_unit]
                    variable=unit
                    text= _ "Charm failed!"
                    {COLOR_HARM}
                [/unstore_unit]
            [/then]
            [else]
                {VARIABLE second_unit.side $unit.side}
                [unstore_unit]
                    variable=second_unit
                    text= _ "Charmed!"
                    {COLOR_HEAL}
                [/unstore_unit]

                # The variable needs to be unset as well.
                {CLEAR_VARIABLE unit.variables.charm_has_worked}
                [unstore_unit]
                    variable=unit
                [/unstore_unit]
            [/else]
        [/if]
    [/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 will gain an additional point of damage per strike every time it kills an enemy. Made for Melon’s Youkai faction (https://r.wesnoth.org/t20100). A variant which uses this as weapon special is used in ageless era (https://r.wesnoth.org/t25274).

One can add this as ability or as weapon special. As ability it will be used when the other unit is killed by any attack of this unit. Using it as weapon special is similar to the Necromancer’s plague staff: It will only take effect when the attack with the special is used to land the killing blow.

Place the following in any .cfg file loaded by the campaign or era:

#define SOULTAKER_DUMMY
   [dummy]
       id=soultaker
       name= _ "soultaker"
       description=_ "This unit gains an additional point added to its melee damage whenever it kills a living unit."
   [/dummy]
#enddef

#define SOULTAKER_EVENT
    [event]
        name=die
        first_time_only=no
        id=soultaker

        [filter]
            [not]
                status=undrainable
            [/not]
        [/filter]

        # Use this check if you want to use Soultaker as ability.
        [filter_second]
            ability=soultaker
        [/filter_second]

        # To use Soultaker as weapon special, use this check INSTEAD of the above one.
        # [filter_second_attack]
        #     special_id=soultaker
        # [/filter_second_attack]

        [floating_text]
            x,y=$x2,$y2
            text="<span color='#00ff00'>" + _ "+1 damage" + "</span>"
        [/floating_text]

        [object]
            silent=yes
            duration=forever
            [filter]
                x,y=$x2,$y2
            [/filter]

            [effect]
                apply_to=attack
                increase_damage=1
                range=melee
                # This will increase all melee attacks by 1. To only increase the attack used in this fight, use
                # name=$second_weapon.name
            [/effect]
        [/object]
    [/event]
#enddef

And the following in the unit's [abilities] tag. If you changed the code to use the weapon special variant, add instead to the unit’s [attack] an [specials] tag, into which you place:

{SOULTAKER_DUMMY}

Add to the [campaign] or [era] tag:

{SOULTAKER_EVENT}

Works

Unit with ability works will produce 1 gold per turn. This mechanism is used in the mainline multiplayer map »A New Land«.

Put this macro into you code before the last piece of code.

#define ABILITY_WORKS
    [works]
        id=peasant_works
        name="works"
        description= _ "This unit produces 1 gold per turn."
    [/works]
#enddef

Put this event into your code.

[event]
    name=side turn
    first_time_only=no
    [store_unit]
        [filter]
            ability=peasant_works
            side=$side_number
        [/filter]
        variable=workers
    [/store_unit]

    [foreach]
        array=workers
        [do]
            [gold]
                side=$this_item.side 
                amount=1
            [/gold] 
            [floating_text]
                x,y=$this_item.x,$this_item.y
                text="<span color='#ffff00'>" + _ "+1 gold" + "</span>"
            [/floating_text]
        [/do]
    [/foreach]

    {CLEAR_VARIABLE workers}
[/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 sub $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.

The ability is used in HttT by Li’sar, you can copy the code from data/campaigns/Heir_To_The_Throne/utils/abilities.cfg

Blitz

UtBS and TroW have with distract an ability, which lets adjacent units ignore the ZoC. It works similar to leadership and initiative, the bonus is valid while the unit is adjacent.

This ability does the same, but it works similar to healing: The bonus is applied at the beginning of the turn and still valid when not anymore being adjacent.

But it also differs from the way healing works for allied units:

  • With healing, it is useful if you move your injured unit to an ally, so that it is adjacent at the healing time.
  • With this ability, the unit who wants the bonus must be adjacent at the start of his own turn.
#define ABILITY_BLITZ
    [dummy]
        id=blitz    
        name= _ "blitz"
        description= _ "Allies that start their turn adjacent to this unit are granted skirmisher for that turn."
        special_note= _ "Instead of healing other units, this unit grants temporarily skirmisher for allied units at the beginning of their turn."
        active_on=offense
        affect_self=no
        affect_allies=yes
        [affect_adjacent][/affect_adjacent]
    [/dummy]
#enddef
#define ABILITY_BLITZ_EVENT
    [event]
        name=side turn
        first_time_only=no

        [modify_unit]
            # The units adjacent to a unit with the blitz ability …
            [filter]
                side=$side_number
                [filter_adjacent]
                    is_enemy=no
                    ability=blitz
                [/filter_adjacent]
            [/filter]

            # … receive temporarily this ability.
            [object]
                duration=turn end
                [effect]
                    apply_to=new_ability
                    [abilities]
                        {ABILITY_SKIRMISHER}
                    [/abilities]
                [/effect]
            [/object]
        [/modify_unit]
    [/event]
#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]
        {TRAIT_UNDRAINABLE}

        # Or without trait / object:
        # [status]
        #     undrainable=yes
        # [/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 no}      # Notifies the weapon specials
    {VARIABLE has_slow no}
    {VARIABLE has_poison no}
    [if]
        [variable]
            name=weapon.specials.drains.id
            equals=drains
        [/variable]
        [then]
            {VARIABLE has_drain yes}
        [/then]
    [/if]
    [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
            boolean_equals=yes
        [/variable]
        [then]
            [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=$unit.side
                    [/not]
                    [not]         #The target unit is already hit by the attack
                        x,y=$x2,$y2
                    [/not]
                    [not]
                        status=undrainable,petrified
                    [/not]
                [/filter]
                variable=units
            [/store_unit]
            {VARIABLE healed_amount 0}
            [foreach]
                array=units
                [do]
                    [switch]            #Check the resistances
                        variable=weapon.type
                        [case]
                            value=arcane
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.arcane*$weapon.damage)"}
                        [/case]
                        [case]
                            value=fire
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.fire*$weapon.damage)"}
                        [/case]
                        [case]
                            value=cold
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.cold*$weapon.damage)"}
                        [/case]
                        [case]
                            value=blade
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.blade*$weapon.damage)"}
                        [/case]
                        [case]
                            value=pierce
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.pierce*$weapon.damage)"}
                        [/case]
                        [case]
                            value=impact
                            {VARIABLE_OP healed_amount add "$($this_item.resistance.impact*$weapon.damage)"}
                        [/case]
                    [/switch]
                [/do]
            [/foreach]
            #Float the healed amount over the unit, like if it had drained
            [floating_text]        #Two numbers will float, the one from the regular hit and one from this
                x,y=$x1,$y1        #Operating with huge numbers because rounding is a problem
                text="<span color='#00ff00'>" + "$($healed_amount/200)" + "</span>"
            [/floating_text]
            [heal_unit]
                [filter]
                    x,y=$x1,$y1
                [/filter]
                amount=$($healed_amount/200)
                restore_statuses=no
                animate=no
            [/heal_unit]
            {CLEAR_VARIABLE units,healed_amount}
        [/then]
    [/if]
    [harm_unit]
        [filter]
            [filter_adjacent]
                x,y=$x1,$y1
            [/filter_adjacent]
            [not]
                side=$unit.side
            [/not]
            [not]
                x,y=$x2,$y2
            [/not]
            [not]
                status=petrified
            [/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]

See Also

This page was last edited on 23 November 2024, at 21:10.