Difference between revisions of "AnimationWML"

From The Battle for Wesnoth Wiki
m (Example Syntax)
m (Progressive strings)
 
(46 intermediate revisions by 11 users not shown)
Line 3: Line 3:
 
== Introduction ==
 
== Introduction ==
  
This page covers unit animations. At any point in game, units are playing an animation. Even when the unit is standing, it is actually playing a single frame animation.
+
This page covers unit animations. At any point in a game, units are playing an animation. Even when the unit is standing, it is actually playing a single frame animation.
  
This page will deal with the two problems of animations
+
This page will deal with the two problems of animations:
 
* How are animations chosen
 
* How are animations chosen
 
* What exactly is drawn
 
* What exactly is drawn
 
+
* Defining animations with WML
 
 
  
 
== How animations are drawn ==
 
== How animations are drawn ==
 
=== Animations ===
 
=== Animations ===
Any unit has a huge set of animations to choose from. Animations are WML blocks in the unit type, enclosed in either the generic type '''[animation]''' or some more specific tags such as '''[attack_anim]''' '''[idle_anim]''' and the like. An animation is what a unit displays when a given event is triggered, and a certain set of conditions are met such as
+
Any unit has a huge set of animations to choose from. An animation is what a unit displays when a given event is triggered, and a certain set of conditions are met, such as:
 
* unit is attacking
 
* unit is attacking
 
* unit is idling
 
* unit is idling
Line 19: Line 18:
 
* unit is attacking (event) and uses a melee weapon (condition) and opponent can't retaliate (condition)
 
* unit is attacking (event) and uses a melee weapon (condition) and opponent can't retaliate (condition)
  
events and conditions are described entirely in the [animation] block, and will be discussed in the animation choice section.
+
Events and conditions are described entirely in the [animation] block, and will be discussed in the animation choice section.
  
 
=== Frames ===
 
=== Frames ===
 
      
 
      
An animation is cut in frames. Frames are WML blocks that are contained either in '''[frame]''' WML blocks or in generic '''[xxx_frame]''' block. (the xxx part can be any string not starting with an underscore) the xxx part in the frame name is called the frame prefix. frames of the type '''[frame]''' are said to have an empty prefix
+
An animation is cut into one or more sequences of frames.
  
A frame typically describes an image, where an how to render it. It can contain such things as
+
A frame typically describes an image, where and how to render it. It can contain such things as:
 
* the image to display
 
* the image to display
 
* how transparent should that image be
 
* how transparent should that image be
 
* where to draw that image.
 
* where to draw that image.
  
At any given time, an animation will display one frame for each frame prefix it can find. I.e if your animation has both '''[frame]''' and '''[missile_frame]''' blocks, both will be displayed at the same time
+
At any given time, an animation will display one frame for each frame prefix it can find. I.e. if your animation has both '''[frame]''' and '''[missile_frame]''' blocks, both will be displayed at the same time.
  
 
+
The frame with the empty prefix is special in many ways. It is assumed to contain the image representing the unit itself, and as such, the engine will heavily tamper with it in multiple ways, such as providing a default image if none is available, or forcing the image to be green when the unit is poisoned.
The frame with the empty prefix is special in many way. It is assumed to contain the image representing the unit itself, and as such the engine will heavily temper with it in multiple ways, such as providing a default image if none is available, or forcing the image to be green when the unit is poisoned
 
  
 
=== Timing, Clock, Multiple animations ===
 
=== Timing, Clock, Multiple animations ===
When an animation is played it has an internal clock that is run. The step of this clock is the milisecond, and each frame is played for a given duration. Each animation also has a starting time which tells the animation engine at what value the animation clock should start
+
When an animation is played, it has an internal clock that is run. The step of this clock is the millisecond, and each frame is played for a given duration. Each animation also has a starting time which tells the animation engine at what value the animation clock should start.
  
This starting time is very important when multiple animations from different units are played synchronously  Typically a fight involves a unit playing its defense animation while the opponent plays its attack animation (and a third might play its leading animation, too). In that case all animations have a common clock, and are played concurrently.
+
This starting time is very important when multiple animations from different units are played synchronously. Typically a fight involves a unit playing its defense animation while the opponent plays its attack animation (and a third might play its leading animation, too). In that case all animations have a common clock, and are played concurrently.
  
The convention is that the "important time" (usually the time of the hit) is at time 0. everything before should be at negative time, everything after at positive time. This is a WML convention that is not enforced by the engine.
+
The convention is that the "important time" (usually the time of the hit) is at time 0. Everything before should be at negative time, everything after at positive time. This is a WML convention that is not enforced by the engine.
  
 
In that case, it is very important that these animations (which can have different lengths) be synchronized. Fight animations are synchronized around the point of impact, so they all reach time 0 when the attack lands (or misses). This is accomplished through the use of the '''start_time''' key of the '''[animation]''' tag.
 
In that case, it is very important that these animations (which can have different lengths) be synchronized. Fight animations are synchronized around the point of impact, so they all reach time 0 when the attack lands (or misses). This is accomplished through the use of the '''start_time''' key of the '''[animation]''' tag.
Line 46: Line 44:
 
Just like the '''[frame]''' tag can have a prefix, so can the '''start_time''' key. A '''start_time''' key without prefix will affect a '''[frame]''' tag without prefix, while a '''start_time''' key with a prefix will affect a '''[frame]''' tag with the same prefix.
 
Just like the '''[frame]''' tag can have a prefix, so can the '''start_time''' key. A '''start_time''' key without prefix will affect a '''[frame]''' tag without prefix, while a '''start_time''' key with a prefix will affect a '''[frame]''' tag with the same prefix.
  
==== Example Syntax ====
+
=== Layering ===
    [attack_anim]
+
The ''layer'' progressive parameter allows the animation writer to choose in what order the animation should be drawn.
        [filter_attack]
+
 
            name=bow
+
This value must be between 0 and 100.
        [/filter_attack]
+
 
        '''missile_start_time'''=-150
+
* The back of haloes is drawn with a value of 10.
        [missile_frame]
+
* When unspecified, a animation is drawn with a value of 40.
            duration=150
+
* Terrain elements that are supposed to go in front of units (castles) are drawn with a value of 50.
            image="projectiles/missile-n.png"
+
* Orbs and status bars are drawn on layer 80.
            image_diagonal="projectiles/missile-ne.png"
+
* When unspecified, missile frames are drawn on layer 90.
        [/missile_frame]
+
 
        '''start_time'''=-200
+
By changing these values, it is easy to have the unit display the way you want.
        [frame]
+
 
            image=units/bowshooter.png:25  #because defense animations could possibly
+
=== Related Parameter handling ===
                                          #be longer and if so this image will display
+
All drawing related parameters in '''[animation]''' can be matched with a corresponding parameter in  a '''[frame]''' block (or a '''[xxx_frame]''' block) The value provided in the animation will be used if no value is provided in the frame.
                                          #Also gives a bit of a pause before attacking
+
 
        [/frame]
+
The point of these two level of parameters is to easily be able to specify a parameter at the animation level and override it for a special case of frame.
        [frame]
+
 
            image=units/bowshooter-prepare-bow.png:25
+
Note that xxx_ parameters (e.g. missile_offset) are shortcuts to access parameters from outside of their corresponding frame and have no effect inside of them.
        [/frame]
+
 
        #now the time is synchronized with the missile frame for the arrow
+
== How animations are chosen ==
        [frame]
+
 
            image=units/bowshooter-fire-bow[1~3].png:50
+
All animations are triggered by a specific event, given by the '''apply_to=''' field of the animation (or the '''flag=''' field in an '''[extra_anim]'''). When an event triggers, all animations linked to that event are gathered and [[#Selection Algorithm|tested]] to see if they are viable.
        [/frame]
+
 
        [frame]
+
Unless otherwise specified, the ''value'' and ''value_second'' for an event are always 0. If only one is specified, the other is always 0.
            image=units/bowshooter.png:25  #because defense animations could possibly
+
 
                                          #be longer and if so this image will display
+
=== Common animation events used by the engine ===
                                          #Also gives a bit of a pause after attacking
+
 
        [/frame]
+
; {{anchor|event-standing|standing}}
        '''sound_start_time'''=-150
+
: This animation is triggered whenever any other animation is finished, and is played in loop until another animation is triggered. This is the default "standing image" for the unit. If no standing animation is set for a unit type, a single frame animation based on the enclosing unit's '''image=''' field is created.
        [if]
+
 
            hits=yes
+
; {{anchor|event-selected|selected}}
            [sound_frame]
+
: This animation is triggered whenever the unit is selected. If no animation is available for this event, the default uses the standing animation(s) with the following modifications:
                sound=bow.ogg
+
:* <code>blend_ratio="0.0~0.3:100,0.3~0.0:200"</code>
            [/sound_frame]
+
:* <code>blend_color=255,255,255</code>
        [/if]
+
 
        [else]
+
; {{anchor|event-recruited|recruited}}
            hits=no
+
: This animation is triggered whenever a unit is recruited or recalled. If no animation is available, the default uses the standing animation(s) with the following modification:
            [sound_frame]
+
:* <code>alpha=0~1:600</code>
                sound=bow-miss.ogg
+
 
            [/sound_frame]
+
; {{anchor|event-recruiting|recruiting}}
        [/else]
+
: This animation is triggered for the leader when a unit is recruited or recalled. No default animation is generated. Note that is not triggered for WML triggered recruits.
    [/attack_anim]
+
 
 +
; {{anchor|event-levelout|levelout}}
 +
: This animation is triggered whenever the unit levels, before the unit is replaced by the new unit. If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>blend_ratio="0~1:600"</code>
 +
:* <code>blend_color=255,255,255</code>
 +
 
 +
; {{anchor|event-levelin|levelin}}
 +
: This animation is triggered whenever the unit levels, after the unit is replaced by the new unit.  no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>blend_ratio="1~0:600"</code>
 +
:* <code>blend_color=255,255,255</code>
 +
 
 +
; {{anchor|event-movement|movement}}
 +
: This animation is used whenever a unit moves. There are multiple things to consider when dealing with movement animations:
 +
:* Units stay ''exactly'' 200ms in each hex. so you typically want to have an offset of <code>0~1:200,0~1:200,0~1:200,0~1:200,0~1:200</code> or something similar.
 +
:* When a unit enters a hex, the current animation is tested again.
 +
:** If the current animation is still valid (i.e it passes all its filters), then it is kept, whatever the final score is.
 +
:** If it fails, a new movement animation is found.
 +
:* ''value='' contains the number of steps already taken.
 +
:* ''value_second='' contains the number of steps left.
 +
:If no animation is available, the default uses the standing animation(s) with the following modification:
 +
:* <code>offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"</code>
 +
 
 +
; {{anchor|event-pre_movement|pre_movement}}
 +
: This animation is used before any unit movement begins.
 +
 
 +
; {{anchor|event-post_movement|post_movement}}
 +
: This animation is used after any unit movement completes.
 +
 
 +
; {{anchor|event-pre_teleport|pre_teleport}}
 +
: This animation is triggered whenever the unit teleports, before it has moved to the target hex. If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>blend_ratio="1~0:200"</code>
 +
:* <code>blend_color=255,255,255</code>
  
=== The content of a frame ===
+
; {{anchor|event-post_teleport|post_teleport}}
==== Syntax summary ====
+
: This animation is triggered whenever the unit teleports, after it has moved to the target hex. If no animation is available, the default uses the standing animation(s) with the following modifications:
[frame]
+
:* <code>blend_ratio="0~1:200"</code>
    duration=<integer>
+
:* <code>blend_color=255,255,255</code>
    begin=<deprecated,integer>
 
    end=<deprecated,integer>
 
    image=<string, dev feature 1.11.2 - progressive string>
 
    image_diagonal=<string, dev feature 1.11.2 - progressive string>
 
    image_mod=<string>
 
    sound=<string>
 
    halo=<progressive string>
 
    halo_x=<progressive int>
 
    halo_y=<progressive int>
 
    halo_mod=<string>
 
    alpha=<progressive float>
 
    offset=<progressive float>
 
    blend_color=<red, green, blue>
 
    blend_ratio=<progressive float>
 
    text=<string>
 
    text_color=< red, green, blue >
 
    submerge=<progressive float>
 
    x=<progressive int>
 
    y=<progressive int>
 
    directional_x=<progressive int>
 
    directional_y=<progressive int>
 
    layer=<progressive int>
 
    auto_hflip=<boolean>
 
    auto_vflip=<boolean>
 
    primary=<boolean>
 
[/frame]
 
  
==== Progressive parameters ====
+
; {{anchor|event-healing|healing}}
 +
: This animation is triggered when the unit has healing powers and uses them. No default is provided by the engine.
 +
:* ''value='' is the number of points healed.
  
Some parameters above are marked as ''progressive'' This means that you can specify that during the time the frame is displayed, the parameter should smoothly slide from one value to an other.
+
; {{anchor|event-healed|healed}}
 +
: This animation is triggered whenever the unit is healed for any reason.
 +
:* ''value='' is the number of points healed
 +
: If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30"</code>
 +
:* <code>blend_color=255,255,255</code>
 +
:* <code>sound=heal.wav</code>
  
A typical example would be a unit progressively fading out, becoming transparent
+
; {{anchor|event-poisoned|poisoned}}
 +
: This animation is triggered whenever the unit suffers poison damage
 +
:* ''value='' is the number of hitpoints lost.
 +
: If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30"</code>
 +
:* <code>blend_color=0,255,0</code>
 +
:* <code>sound=poison.ogg</code>
  
To do that, you could use:
+
; {{anchor|event-defend|defend}}
 +
: This animation is triggered during a strike, on the unit receiving the blow. No default is provided by the engine.
 +
:* ''value='' is the number of hitpoints lost, if any.
 +
:* ''value_second='' is the number of the swing, starting from 1.
  
alpha=1~0
+
; {{anchor|event-attack|attack}}
 +
This animation is triggered during a strike, of the unit giving the blow
 +
:* ''value='' is the amount of damage dealt, if any.
 +
:* ''value_second='' is the number of the swing, starting from 1.
 +
: If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>range=melee</code>
 +
:* <code>offset="0~0.6:200,0.6~0:200"</code>
 +
: No default is provided for non-melee attacks
  
To make a frame, which is 900ms long, slide to transparent for 300ms, stay transparent for another 300ms and finally fade back to normal for 300ms, use:
+
; {{anchor|event-leading|leading}}
 +
: This animation is triggered for units with the leading special ability, when the unit they are leading is attacking. No default is provided by the engine.
 +
:* ''value_second='' is the number of the swing, starting from 1.
  
alpha=1~0:300,0:300,0~1:300
+
; {{anchor|event-teaching|teaching}}
 +
: {{DevFeature1.15|11}} This animation is triggered for units with weapon special abilities used like leadership, when the unit they are teaching is attacking. No default is provided by the engine.
 +
:* ''value_second='' is the number of the swing, starting from 1.
  
you could also specify it like that
+
; {{anchor|event-resistance|resistance}}
 +
: This animation is triggered for units with the resistance special ability affecting neighbouring fights, when the unit they are helping is defending. No default is provided by the engine.
 +
:* ''value_second='' is the number of the swing, starting from 1.
  
alpha=1~0,0,0~1
+
; {{anchor|event-death|death}}
 +
: This animation is triggered when a unit dies. If no animation is available, the default uses the standing animation(s) with the following modifications:
 +
:* <code>alpha=1~0:600</code>
 +
:* <code>sound=<die_sound= of the enclosing unit></code>
  
when a timing is missing, the engine will do its best to fill in in a fair way.  
+
; {{anchor|event-victory|victory}}
 +
: This animation is triggered when a unit finishes a fight by killing the other unit. No default is provided by the engine.
  
a progressive string has an expanded syntax:
+
; {{anchor|event-idling|idling}}
 +
: This animation is called when the unit has been using its standing animation for a random duration. No default is provided by the engine.
  
image=image1.png:100,image2.png:100,image3.png:100
+
; {{anchor|event-draw_weapon|draw_weapon}}
halo=halo1.png:100,halo2.png:200,halo3.png:300
+
: This animation is played before a fight begins. No default is provided by the engine.
 +
:* ''hits='' is set to HIT for the attacker and MISS for the defender
  
or (from Wesnoth 1.11.2+) equivalently,
+
; {{anchor|event-sheath_weapon|sheath_weapon}}
image=image[1~3]:100
+
: This animation is played after a fight simultaneously for all surviving units. No default is provided by the engine.
halo=halo[1~3]:[100,200,300]
 
  
for convenience, the square bracket expansion works like so:
+
; {{anchor|event-default|default}}
halo=halo[3,5,7].png  is equivalent to  halo=halo3.png,halo5.png,halo7.png
+
: This animation is never triggered, but is used as a base to create new animations. A single animation made of the base frame is provided by the engine if no default animation is available.
halo=halo[07~10].png  is equivalent to halo=halo07.png,halo08.png,halo09.png,halo10.png
 
halo=halo[5~3,1].png  is equivalent to  halo=halo5.png,halo4.png,halo3.png,halo1.png
 
image=image[1~2]-ex[4~5].png:[100,200]  is equivalent to  image=image1-ex4.png:100,image2-ex5:200.png
 
image=image[1~4].png:[10,20*3]          is equivalent to  image1.png:10,image2.png:20,image3.png:20,image4.png:20
 
  
==== Field Description ====
+
=== Custom animation events ===
  
** '''begin''': (deprecated) will be replaced by '''duration= <end - begin >'''
+
Other animation events are never triggered by the engine. However, they can be triggered by [[InterfaceActionsWML#.5Banimate_unit.5D|WML events]] or from [[LuaAPI/wesnoth/units#wesnoth.units.create_animator|Lua]], allowing custom animations.
** '''end''': (deprecated) see '''begin''' and also [[AnimationWML#Timing.2C_Clock.2C_Multiple_animations|Timing, Clock, Multiple animations]] section for coverage of '''start_time''' which combines with '''duration''' to replace '''begin=''' '''end='''.
 
** '''duration''': how long the frame should be displayed. Use instead of '''begin=''' and '''end='''. If specified by timing in a progressive string, such as a halo, this can be left out and will be automatically calculated.
 
** '''image''': the image, or sequence of images, to display during the frame.
 
** '''image_diagonal''': the image, or sequence of images, to display when the attack occurs diagonally (directions ne,se,sw,nw).
 
** '''image_mod''': a string modifications (see [[ImagePathFunctionWML]] ) that will be applied to all images
 
** '''sound''': the sound to play when the frame begins. Can be a comma-separated list of sounds, in which case one of them is chosen randomly every time.
 
** '''halo''': the halo to display at the time.
 
** '''halo_x''': the position the halo is displayed in pixel relative to the unit's center.
 
** '''halo_y''': the position the halo is displayed in pixel relative to the unit's center.
 
** '''halo_mod''': a string modifications (see [[ImagePathFunctionWML]] ) that will be applied to all haloes
 
** '''alpha''': transparency level to apply to the frame. This is a floating point progressive parameter ranging from 0.0 to 1.0.
 
** '''offset''': the position of the image relative to the hex the unit is facing, 0.0 will display the unit in the center of the hex, 1.0 in the center of the faced hex, -1.0 at the center of the hex behind you. This is a progressive parameter.
 
** '''blend_color''': a comma separated list of numbers representing a color in RGB (0-255); this color will be mixed to the frame to give it a tint.
 
** '''blend_ratio''': this is a progressive parameter ranging from 0 to 1: 0 means no tint, 1 means target color only.
 
** '''text''': if specified, floats a label with the given text above the unit (identical to the floating damage and healing numbers).
 
** '''text_color''': the color of the above floating label.
 
** '''submerge''': the part of the unit that should drawn with 50% opacity (for units in water)
 
** '''x''': x offset applied to the frame
 
** '''y''': y offset applied to the frame
 
** '''directional_x''': the x offset, in pixels, applied to the frame in the direction the unit is facing
 
** '''directional_y''': the y offset, in pixels, applied to the frame in the direction the unit is facing
 
** '''layer''': layer used to draw the frame, see discussion bellow
 
** '''auto_hflip''': should the image flip horizontally depending on sprite orientation
 
** '''auto_vflip''': should the image flip vertically depending on sprite orientation
 
** '''primary''': should the engine consider that frame as a primary frame (affected by visual effects like stone and poison)
 
  
=== Drawing related animation content ===
+
The ''value='', ''value_second='', and ''hits='' values are all set from the WML event or Lua animation call.
==== Syntax summary ====
 
[animation]
 
  <animation choice related content>
 
  [frame]
 
    <frame content>
 
  [/frame]
 
  [frame]
 
    <frame content>
 
  [/frame]
 
  start_time=<integer>
 
  offset=<progressive float>
 
  image_mod=<string>
 
  blend_color=<r,g,b>
 
  blend_ratio=<progressive float>
 
  halo=<progressive_string>
 
  halo_x=<progressive int>
 
  halo_y=<progressive int>
 
  halo_mod=<string>
 
  submerge=<progressive float>
 
  x=<progressive int>
 
  y=<progressive int>
 
  directional_x=<progressive int>
 
  directional_y=<progressive int>
 
  layer=<progressive int>
 
 
 
  [xxx_frame]
 
    <frame content>
 
  [/xxx_frame]
 
  xxx_start_time=<integer>
 
  xxx_image_mod=<string>
 
  xxx_offset=<progressive float>
 
  xxx_blend_color=<r,g,b>
 
  xxx_blend_ratio=<progressive float>
 
  xxx_halo=<progressive_string>
 
  xxx_halo_x=<progressive int>
 
  xxx_halo_y=<progressive int>
 
  xxx_halo_mod=<string>
 
  xxx_submerge=<progressive float>
 
  xxx_x=<progressive int>
 
  xxx_y=<progressive int>
 
  xxx_directional_x=<progressive int>
 
  xxx_directional_y=<progressive int>
 
  xxx_layer=<progressive int>
 
 
[/animation]
 
  
==== Parameter handling ====
+
Note that WML events and Lua can also trigger standard animations.
All drawing related parameters in '''[animation]''' can be matched with a corresponding parameter in  a '''[frame]''' block (or a '''[xxx_frame]''' block) The value provided in the animation will be used if no value is provided in the frame.  
 
  
The point of these two level of parameters is to easily be able to specify a parameter at the animation level and override it for a special case of frame.
+
=== Selection Algorithm ===
  
== How animations are chosen ==
 
 
Within a unit description block there are multiple animations. Each animation is meant to be played in a very special set of circumstances. We will discuss here how to tell the animation engine when a given animation should be played.  
 
Within a unit description block there are multiple animations. Each animation is meant to be played in a very special set of circumstances. We will discuss here how to tell the animation engine when a given animation should be played.  
  
Line 250: Line 212:
 
To solve that question, each animation has a number of filtering criteria. The engine follow the following rules to select an animation
 
To solve that question, each animation has a number of filtering criteria. The engine follow the following rules to select an animation
 
* Start with all animations
 
* Start with all animations
* Drop all animations with a criteria that fails on the current situation
+
* Drop all animations with a criterion that fails on the current situation
 
* Take the animations that have the most matching criteria
 
* Take the animations that have the most matching criteria
 
* If there are more than one animation remaining, take an animation randomly
 
* If there are more than one animation remaining, take an animation randomly
Line 291: Line 253:
 
* on other grasslands, it will be 4. (4. or 5. if NW)
 
* on other grasslands, it will be 4. (4. or 5. if NW)
  
=== Generic animation filters available for all animations ===
+
== Defining an Animation ==
* '''apply_to''': a list of comma separated keywords describing what event should trigger the animation (movement, attack...) the complete list is given below
 
* '''value''': a list of comma separated integer, the meaning depends on the triggering event. the meaning is given with the list of event
 
* '''value_second''': a list of comma separated integer, the meaning depends on the triggering event. the meaning is given with the list of event
 
* '''terrain''': a list of comma separated terrain letters, the animation will only be used on these terrains.  See [[FilterWML#Filtering Terrains|Filtering Terrains]].  this has been replaced by '''terrain_type'''
 
* '''terrain_type''': a list of comma separated terrain codes, the animation will only be used on matching terrains.  See [[FilterWML#Filtering Terrains|Filtering Terrains]].
 
* '''[filter]''': this will filter using a [[StandardUnitFilter|standard unit filter]] on the animated unit. Note that matching all criterias in the filter will only earn you one point for animation selection, but that you can have multiple '''[filter]''' blocks in an animation.
 
* '''[filter_second]''': this will filter using a [[StandardUnitFilter|standard unit filter]] on the unit in the hex faced. Note that matching all criteria in the filter will only earn you one point for animation selection, but that you can have multiple '''[filter_second]''' blocks in an animation. Also note that if the faced hex is empty, the whole filter will fail.
 
* '''direction''': a list of directions (n,ne,se,s,sw,nw), the animation will only be used when acting or moving in this direction (the attack direction for attack animations, moving direction for movement animations, direction of lead unit for leading animations, and so on).
 
* '''frequency''': this integer value allows to easily tweak the matching frequency of animations. The filter will fail n-1 times out of n, randomly. Note that unlike every other filter, matching this filter won't give an extra point.
 
* '''[filter_attack]''': a standard attack filter to match on the attacker's attack. See  [[FilterWML#Filtering Weapons|Filtering Weapons]].
 
* '''[filter_second_attack]''': a standard attack filter to match on the defender's attack. See [[FilterWML#Filtering Weapons|Filtering Weapons]]. Also note that if the defender doesn't have any weapon to retaliate with, this filter will always fail.
 
* '''hits''': filters attack animations based on whether the attack hits, misses or kills. Accepts a list of the following:
 
** '''hit''': the attack hits, defender survives.
 
** '''no''' or '''miss''': the attack misses.
 
** '''kill''': the attack hits, defender dies.
 
** '''yes''': is an alias of '''hit,kill'''.
 
* '''swing''': a list of numbers representing the swing numbers this animation should match (numbered from 1). this has been replaced by value_second
 
* '''base_score''': a number that will always be added to the score. Use with caution
 
  
=== Events triggering animations and default animations ===
+
=== The [frame] tag ===
==== standing ====
 
This animation is triggered whenever any other animation is finished, and is played in loop until another animation is triggered
 
  
this is the default "standing image" for the unit
+
Animation frames are defined using either the '''[frame]''' tag or a prefixed version of it, like '''[xxx_frame]'''. The '''xxx''' can be any sequence of alphanumeric characters. However, prefixes beginning with an underscore are reserve by the engine for internal use, and should generally ''not'' be used in user code. A frame of the type '''[frame]''' is said to have an empty prefix.
  
''value='' is not used, and always contains 0
+
The following keys can be used in frame tags:
  
''value_second='' is not used, and always contains 0
+
* '''duration''': (''integer'') The duration of the frame, in milliseconds. If you use timings in a progressive parameter, you can omit the duration, and it will be inferred from the progressive timings.
 +
** ''Note: Older animations used '''begin''' and '''end''' instead. This is now equivalent to setting the '''duration''' to '''end''' - '''begin'''.
 +
* '''image''': ([[#Progressive strings|''progressive string'']]) The image to play for the frame.
 +
* '''image_diagonal''': ([[#Progressive strings|''progressive string'']]) the image, or sequence of images, to display when the attack occurs diagonally (directions ne,se,sw,nw).
 +
* '''image_mod''': (''string'') A string of [[ImagePathFunctions]] that is always appended to '''image'''.
 +
* '''sound''': (''string'') The sound to play when the frame begins. Can be a comma-separated list of sounds, in which case one of them is chosen randomly every time.
 +
* '''halo''': ([[#Progressive strings|''progressive string'']]) A halo image to play for the frame.
 +
* '''halo_x''', '''halo_y''': ([[#Progressive numbers|''progressive integer'']]) The X and Y offset to play the halo at, relative to the unit's center. This takes into account the direction the unit is facing.
 +
* '''halo_mod''': (''string'') A string of [[ImagePathFunctions]] that is always appended to '''halo'''.
 +
* '''alpha''': ([[#Progressive numbers|''progressive real'']]) The transparency of the image. Should stay within the range of 0.0 to 1.0.
 +
* '''offset''': ([[#Progressive numbers|''progressive real'']]) The position of the image relative to the hex the unit is facing – 0.0 will display the unit in the center of the hex it occupies, 1.0 in the center of the faced hex, and -1.0 at the center of the hex behind you.
 +
* '''blend_color''': (''color triple'') The image will be tinted in this colour. It should be a comma-separate list of red, green, and blue values in the 0 to 255 range.
 +
* '''blend_ratio''': ([[#Progressive numbers|''progressive real'']]) Determine the strength of the tint, ranges from 0.0 to 1.0. A value of 0.0 means no tint, while 1.0 will yield only the tint color as a solid flat color.
 +
* '''text''': (''string'') [[InterfaceActionsWML#.5Bfloating_text.5D|Floating text]] to be played above the unit when the frame begins.
 +
* '''text_color''': (''color triple'') The floating text color. Same format as '''blend_color'''.
 +
* '''submerge''': ([[#Progressive numbers|''progressive real'']]) The percentage of the image's base that should be faded out (at 50% opacity) as if submerged in the terrain. Only used on terrains that have the submerge setting, such as water terrains.
 +
* '''x''', '''y''': ([[#Progressive numbers|''progressive integer'']]) The X and Y offset of the main image, in pixels.
 +
* '''directional_x''', '''directional_y''': ([[#Progressive numbers|''progressive integer'']]) Like above, but aligned with the direction the unit is facing, rather than an absolute offset.
 +
* '''layer''': ([[#Progressive numbers|''progressive integer'']]) The layer used to draw the frame. See the discussion [[Layering|below]].
 +
* '''auto_hflip''': (''boolean'') Should the image flip horizontally depending on sprite orientation?
 +
* '''auto_vflip''': (''boolean'') Should the image flip vertically depending on sprite orientation?
 +
* '''primary''': (''boolean'') Should the engine consider this frame to be a primary frame? The primary frame is affected by visual effects like stone and poison. Usually, this is '''yes''' on unprefixed frames and '''no''' on prefixed frames.
  
if no standing animation is set a single frame animation based on the enclosing unit's '''image=''' field is used
+
=== The [animation] tag ===
==== selected ====
 
This animation is triggered whenever the unit is selected
 
  
''value='' is not used, and always contains 0
+
Animations are defined by WML tags in the unit type. They can be defined either with the generic '''[animation]''' tag, or with some more specific tags, such as '''[attack_anim]''' or '''[idle_anim]'''.
  
''value_second='' is not used, and always contains 0
+
The content of an animation is divided into two parts: filter data, which determines '''''when''''' the animation can be played, and frame data, which defines '''''what''''' is played. In addition, there is support for conditional branches, which is in effect a shorthand for defining multiple similar animations in a single block, as well as some general settings.
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="0.0~0.3:100,0.3~0.0:200" blend_color=255,255,255''
+
==== Animation Filtering ====
==== recruited ====
 
This animation is triggered whenever the unit is recruited or recalled
 
  
''value='' is not used, and always contains 0
+
* '''apply_to''': A list of comma separated keywords describing what event should trigger the animation (movement, attack, etc). The complete list is given [[#Common animation events used by the engine|below]]. Custom events can also be used; these can be triggered from [[InterfaceActionsWML#.5Banimate_unit.5D|ActionWML]] using the '''flag''' key.
 +
* '''value''': A list of comma separated integers. The animation will only be triggered if the event's primary value matches one of these integers. The meaning of the value depends on the event and is described in more detail in the list of events.
 +
* '''value_second''': A list of comma separated integers. The animation will only be triggered if the event's secondary value matches one of these integers. The meaning of the second value depends on the event and is described in more detail in the list of events.
 +
* '''terrain_type''': A list of comma separated terrain codes. The animation will only be used on matching terrains. See [[FilterWML#Filtering Terrains|Filtering Terrains]].
 +
* '''[filter]''': This will filter using a [[StandardUnitFilter|standard unit filter]] on the animated unit. Note that matching all criterias in the filter will only earn you one point for animation selection, but that you can have multiple '''[filter]''' blocks in an animation.
 +
* '''[filter_second]''': This will filter using a [[StandardUnitFilter|standard unit filter]] on the unit in the hex faced. Note that matching all criteria in the filter will only earn you one point for animation selection, but that you can have multiple '''[filter_second]''' blocks in an animation. Also note that if the faced hex is empty, the whole filter will fail.
 +
* '''direction''': A list of directions (n,ne,se,s,sw,nw). The animation will only be used when acting or moving in this direction (the attack direction for attack animations, moving direction for movement animations, direction of lead unit for leading animations, and so on).
 +
* '''frequency''': This integer value allows you to easily tweak the matching frequency of animations. The filter will fail n-1 times out of n, randomly. Note that unlike every other filter, matching this filter won't give an extra point.
 +
* '''[filter_attack]''': A standard attack filter to match on the attacker's attack. See  [[FilterWML#Filtering Weapons|Filtering Weapons]].
 +
* '''[filter_second_attack]''': A standard attack filter to match on the defender's attack. See [[FilterWML#Filtering Weapons|Filtering Weapons]]. Also note that if the defender doesn't have any weapon to retaliate with, this filter will always fail.
 +
* '''hits''': Filters attack animations based on whether the attack hits, misses or kills. Accepts a list of the following:
 +
** '''hit''': The attack hits, defender survives.
 +
** '''no''' or '''miss''': The attack misses.
 +
** '''kill''': The attack hits, defender dies.
 +
** '''yes''': Is an alias of '''hit,kill'''.
 +
* '''base_score''': A number that will always be added to the score, making this animation play more often than others for the same event. Use with caution.
  
''value_second='' is not used, and always contains 0
+
==== Frame Data ====
  
if no animation is available, the default uses the standing animation(s) with ''alpha=0~1:600''
+
Frame data consists of optionally prefixed keys, in addition to the optionally-prefixed '''[frame]''' tags. Keys and tags with the same prefix work together.
  
==== recruiting ====
+
Frame data consists of three parts:
  
This animation is triggered for the leader when a unit is recruited or recalled
+
# Frame timing, specified by the '''start_time''' key. This is an integer specifying when the sequence of frames with the given prefix should start, in milliseconds. Typically, the start time will be negative.
 +
# Default frame data, specified using keys that normally appear in the frame. These specify the default value of a setting for all frames with the given prefix that don't explicitly specify the setting. The following keys are supported for default frame data (they are defined [[#The .5Bframe.5D tag|above]]).
 +
#* '''offset'''
 +
#* '''image_mod'''
 +
#* '''blend_color'''
 +
#* '''blend_ratio'''
 +
#* '''halo'''
 +
#* '''halo_x''', '''halo_y'''
 +
#* '''halo_mod'''
 +
#* '''alpha'''
 +
#* '''submerge'''
 +
#* '''x''', '''y'''
 +
#* '''directional_x''', '''directional_y'''
 +
#* '''layer'''
 +
# The frames themselves. This is the core of the frame data, contained within the [[#The .5Bframe.5D tag|'''[frame]''']] tag.
  
''value='' is not used, and always contains 0
+
All frame data can be either prefixed or unprefixed.
  
''value_second='' is not used, and always contains 0
+
==== Other Settings ====
  
no default animation is needed
+
These are general settings in the animation that are related neither to filtering nor to the content of the frames.
  
note that is not triggered for WML triggered animations
+
* '''offscreen''': (boolean) Specifies whether the unit's animation should be played when the unit is offscreen. You want the animation to play offscreen if the animation contains some useful sound effects. This attribute defaults to true (play offscreen) except for standing and idle animations where it defaults to false.
 +
* '''cycles''': (boolean) If set to true, the animation will cycle forever until it is replaced. That animation will not influence the end time of all related animations (for example, a cycling defense animation will play normally, but the overall duration of the fight animation will be decided only by the attack animation).
  
==== levelout ====
+
==== Conditional Branches ====
This animation is triggered whenever the unit levels, before the unit is replaced by the new unit.
 
  
''value='' is not used, and always contains 0
+
Often, you need to do very slight variations in an animation (like a different sound depending on whether the unit hits or misses its attack). The '''[if]''' and '''[else]''' tags are meant to help you do that.  ('''Attention:''' These do not have the same syntax as the action tag [if] and its subtag [else] in [[ConditionalActionsWML]].)
  
''value_second='' is not used, and always contains 0
+
Using these in an animation is equivalent to having multiple animations, one with the '''[if]''' block and one with each of the '''[else]''' blocks. In other words, an '''[if]''' or '''[else]''' tag has the same possible content as the '''[animation]''' tag. Any filtering flags in these blocks will replace the toplevel filters of the same type. You can have multiple '''[if]''' blocks in an animation, and from 1.11.7+ you can nest an '''[if]''' inside another. These should be written directly inside the '''[animation]''' block. The following example would make the '''[frame]''' inside the '''[if]''' be played when the attack misses, and the '''[frame]''' inside the '''[else]''' be played when the attack hits (producing a different sound):
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="0~1:600" blend_color=255,255,255''
+
<syntaxhighlight lang=wml>
 +
[animation]
 +
    start_time=-100
 +
    [if]
 +
        hits=no
 +
        [frame]
 +
            image="units/dwarves/lord-attack.png:200"
 +
            sound={SOUND_LIST:MISS}
 +
        [/frame]
 +
    [/if]
 +
    [else]
 +
        hits=yes
 +
        [frame]
 +
            image="units/dwarves/lord-attack.png:200"
 +
            sound=axe.ogg
 +
        [/frame]
 +
    [/else]
 +
[/animation]
 +
</syntaxhighlight>
  
==== levelin ====
+
Note that this is very close to preprocessing and should be considered similar to such, especially with regard to scoring and animation selection. When the animations are parsed from the unit type, these if/else blocks are removed, creating multiple similar animations.
This animation is triggered whenever the unit levels, after the unit is replaced by the new unit.
 
  
''value='' is not used, and always contains 0
+
A macro exists under macros/sound-util.cfg called SOUND:HIT_AND_MISS to simplify this type of animation, which is equivalent to:
  
''value_second='' is not used, and always contains 0
+
<syntaxhighlight lang=wml>
 +
[animation]
 +
    start_time=-100
 +
    [frame]
 +
        image="units/dwarves/lord-attack.png:200"
 +
    [/frame]
 +
    {SOUND:HIT_AND_MISS axe.ogg {SOUND_LIST:MISS} -100}
 +
[/animation]
 +
</syntaxhighlight>
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="1~0:600" blend_color=255,255,255''
+
==== Shorthand Animation Tags ====
  
==== movement ====
+
To simplify the most common animation cases, you can use different blocks instead of the generic '''[animation]''' block. These are also here for backward compatibility, but are not deprecated and are fully supported.
This animation is used whenever a unit moves. There are multiple things to consider when dealing with movement animation
 
* unit stay ''exactly'' 200ms in each hex. so you typically want to have an offset of ''0~1:200,0~1:200,0~1:200,0~1:200,0~1:200'' or something similar
 
* when a unit enters a hex, the current anim is tested again.
 
** if the current animation is still valid (i.e it passes all its filter) it is kept, whatever the final score is
 
** if it fails, a new movement anim is searched
 
  
''value='' contains the number of steps already taken
+
Some of these support keys or tags that are not allowed in '''[animation]''' but are translated into standard keys. The meaning of these keys or tags and how they are translated will be described here.
  
''value_second='' contains the number of steps left
+
{|class="wikitable mw-collapsible" style="table-layout: fixed; width: 100%;"
 +
!style="width:20%"|Tag
 +
!style="width:40%"|Expansion
 +
!style="width"40%"|Notes
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-leading|[leading_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=leading
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-leading|leading]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-recruit|[recruit_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=recruited
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-recruited|recruited]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-recruiting|[recruiting_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=recruiting
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-recruiting|recruiting]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-standing|[standing_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=standing,default
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
| This animation will be used to build [[#event-default|default]] animations in addition to being used as the [[#event-standing|standing]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-idle|[idle_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=idling
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|An [[#event-idling|idling]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-levelout|[levelout_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=levelout
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-levelout|level out]] advancement animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-levelin|[levelin_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=levelin
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-levelin|level in]] advancement animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-healing|[healing_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=healing
 +
    value=<damage= value>
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-healing|healing]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-healed|[healed_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=healed
 +
    value=<healing= value>
 +
    [_healed_sound_frame]
 +
        sound=heal.wav
 +
    [/_healed_sound_frame]
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-healed|healed]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-poison|[poison_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=poisoned
 +
    value=<damage= value>
 +
    [_poison_sound_frame]
 +
        sound=poison.ogg
 +
    [/_poison_sound_frame]
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-poisoned|poisoned]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-movement|[movement_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=movement
 +
    offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-movement|movement]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-pre_movement|[pre_movement_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=pre_movement
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-pre_movement|pre-movement]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-post_movement|[post_movement_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=post_movement
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-post_movement|post-movement]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-draw_weapon|[draw_weapon_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=draw_weapon
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-draw_weapon|draw weapon]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-sheath_weapon|[sheath_weapon_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=sheath_weapon
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-sheath_weapon|sheath weapon]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-defend|[defend]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=defend
 +
    value=<damage= value>
 +
    {WML content}
 +
    [frame]
 +
        blend_color=255,0,0
 +
        blend_ratio="0.5:50,0.0:50"
 +
    [/frame]
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-defend|defense]] animation.
  
if no animation is available, the default uses the standing animation(s) with ''offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"''
+
There are some subtle changes compared to what is described here to avoid the red flash when value=0, but it should work as expected as far as WML author is concerned.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-attack|[attack_anim]}}
 +
| If the animation contains a missile frame:
 +
<syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=attack
 +
    missile_offset="0~0.8"
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
Otherwise:
 +
<syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=attack
 +
    offset="0~0.6,0.6~0"
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|An [[#event-attack|attack]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-death|[death]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=death
 +
    [_death_sound_frame]
 +
        sound=<die_sound= of the enclosing [unit] tag>
 +
    [/_death_sound_frame]
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-death|death]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-victory|[victory_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=victory
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|A [[#event-victory|victory]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-extra|[extra_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=<flag= value of the anim>
 +
    {WML content}
 +
[/animation]
 +
</syntaxhighlight>
 +
|An [[#Custom animation events|extra custom]] animation.
 +
|- style="vertical-align:top;"
 +
! {{anchor|short-teleport|[teleport_anim]}}
 +
| <syntaxhighlight lang=wml>
 +
[animation]
 +
    apply_to=pre_teleport
 +
    {WML content before 0}
 +
[/animation]
 +
[animation]
 +
    apply_to=post_teleport
 +
    {WML content after 0}
 +
[/animation]
 +
</syntaxhighlight>
 +
|Will be cut into two at clock-time 0. The first half defines the [[#event-pre_teleport|pre-teleport]] animation, and the second half defines the [[#event-post_teleport|post-teleport]] animation.
 +
|}
  
==== pre_movement ====
+
=== Progressive parameters ===
This animation is used before any unit movement
 
  
''value='' is not used, and always contains 0
+
Some parameters above are marked as ''progressive''. This means that you can specify that during the time the frame is displayed, the parameter should smoothly slide from one value to an other.
  
''value_second='' is not used, and always contains 0
+
==== Progressive numbers ====
  
==== post_movement ====
+
Progressive numbers consist of a comma-separated list of values, each value of which is either a range or a single number with optional timing. If the type is specified as ''progressive integer'', then it can only contain integers, ie with no decimal point. On the other hand, if the type is specified as ''progressive real'', then any number is allowed.
This animation is used after any unit movement
 
  
''value='' is not used, and always contains 0
+
A range is specified as <code>1~5</code> – two numbers separated by a tilde. Reverse ranges like <code>5~1</code> are also permitted.
  
''value_second='' is not used, and always contains 0
+
The timing is specified by appending a colon and the number of milliseconds that the slide should take. For example, <code>1~5:300</code> will slide smoothly from 1 to 5 over a period of 300 milliseconds, while <code>0.5:200</code> will hold constant at 0.5 for 200 milliseconds.
  
==== pre_teleport ====
+
A typical example would be a unit progressively fading out, becoming transparent. To do that, you could use:
This animation is triggered whenever the unit teleports, but before it has moved to the target hex
 
  
''value='' is not used, and always contains 0
+
<syntaxhighlight lang=wml>
 +
[frame]
 +
    alpha=1~0
 +
[/frame]
 +
</syntaxhighlight>
  
''value_second='' is not used, and always contains 0
+
To make a frame, which is 900ms long, slide to transparent for 300ms, stay transparent for another 300ms and finally fade back to normal for 300ms, use:
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="1~0:200" blend_color=255,255,255''
+
<syntaxhighlight lang=wml>
==== post_teleport ====
+
[frame]
This animation is triggered whenever the unit teleports, but after it has moved to the target hex
+
    alpha=1~0:300,0:300,0~1:300
 +
[/frame]
 +
</syntaxhighlight>
  
''value='' is not used, and always contains 0
+
You could also specify it like this:
  
''value_second='' is not used, and always contains 0
+
<syntaxhighlight lang=wml>
 +
[frame]
 +
    alpha=1~0,0,0~1
 +
[/frame]
 +
</syntaxhighlight>
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="0~1:200" blend_color=255,255,255''
+
When a timing is missing, the engine will do its best to fill in in a fair way.
==== healing ====
 
This animation is triggered when the unit has healing powers and uses them
 
  
''value='' is the number of points healed
+
==== Progressive strings ====
  
''value_second='' is not used, and always contains 0
+
The square bracket syntax for progressive strings is a syntactic shortcut that, for convenience, works like so:
  
No default is provided by the engine
+
{|class=wikitable valign=top
==== healed ====
+
! This expression… !! …is equivalent to this…
This animation is triggered whenever the unit is healed for any reason
+
|-
 +
| <tt>halo=halo[3,5,7].png</tt> || <tt>halo=halo3.png,halo5.png,halo7.png</tt>
 +
|-
 +
| <tt>halo=halo[07~10].png</tt> || <tt>halo=halo07.png,halo08.png,halo09.png,halo10.png</tt><sup>*</sup>
 +
|-
 +
| <tt>halo=halo[001~100].png</tt> || <tt>halo=halo001.png,halo002.png,...,halo099.png,halo100.png</tt>
 +
|-
 +
| <tt>halo=halo[5~3,1].png</tt> || <tt>halo=halo5.png,halo4.png,halo3.png,halo1.png</tt>
 +
|-
 +
| <tt>image=image[1~2]-ex[4~5].png:[100,200]</tt> || <tt>image=image1-ex4.png:100,image2-ex5.png:200</tt>
 +
|-
 +
| <tt>image=image[1~4].png:[10,20*3]</tt> || <tt>image1.png:10,image2.png:20,image3.png:20,image4.png:20</tt>
 +
|-
 +
| <tt>image=image[1~3].png:50,other.png:40</tt> || <tt>image1.png:50,image2.png:50,image3.png:50,other.png:40</tt>
 +
|}
 +
<sup>*</sup><small>(if you include leading zeros, the digits will be padded throughout that square bracket expansion to match)</small>
  
''value='' is the number of points healed
+
Each set of [[#square-bracket-expansion|square brackets]] (‘[’ and ‘]’) contains a comma-separated list — an '''''expansion list''''' — of which each element can be one of the following:
  
''value_second='' is not used, and always contains 0
+
* A '''plain string''' — e.g., “abc” or “123”. A plain string just expands to itself.
 +
* A [[#range-expansion|'''range''']], expressed as two radix-10 integers, separated by a tilde — e.g., “1~5”. This is similar to a [[#Progressive numbers|progressive integer]], but with some differences described below.
 +
* A [[#repetition-expansion|'''repetition''']], notated as a string and a radix-10 integer, separated by an asterisk — e.g., “a*3”.
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30" blend_color=255,255,255''
+
Each {{anchor|range-expansion|'''range'''}} ''<var>x</var>~<var>y</var>'' is expanded within its expansion list to a comma-separated list of radix-10 integers (which are plain strings), starting from the first endpoint <var>x</var> and ending at the second endpoint <var>y</var>.
and plays the sound ''heal.wav''
 
==== poisoned ====
 
This animation is triggered whenever the unit suffers poison damage
 
  
''value='' is the number of points lost
+
If '''<var>x</var> < <var>y</var>''', then the numbers in this list increase by increments of 1; if '''<var>x</var> > <var>y</var>''', then the numbers in this list decrease by decrements of 1. E.g., “1~3” would become “1,2,3”, and “3~1” would become “3,2,1”.
  
''value_second='' is not used, and always contains 0
+
If '''<var>x</var> = <var>y</var>''', then this list has only one element, which is <var>x</var> (and is also <var>y</var>, of course). E.g., “2~2” would become “2”.
  
if no animation is available, the default uses the standing animation(s) with ''blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30" blend_color=0,255,0''
+
As with progressive numbers, ranges can not only be from a lesser number to a greater number (e.g., “1~5”), but can also be '''backward''' — from a greater number to a lesser number (e.g., “5~1”).
and plays the sound 'poison.ogg''
 
  
==== defend ====
+
If either <var>x</var> or <var>y</var> (or both) are padded with '''leading zeros''', then each number in the resultant list will be padded with leading zeros such that it is expressed in at least <var>z</var>+1 digits, where <var>z</var> is the quantity of leading zeros with which one or both endpoints are padded. E.g.,
This animation is triggered during a strike, of the unit receiving the blow
+
* “07~11” would become “07,08,09,10,11”,
 +
* “098~100” would become “98,99,100”,
 +
* “003~1” (or “3~001”) would become “003,002,001”, and
 +
* “0098~100” would become “098,099,100” (although poor form), and
 +
* “01~100” would become “01,02,...,10,11,...,99,100”.
  
''value='' is the number of point lost, if any
+
If both endpoints are padded with leading zeros, they should both be padded with the same quantity of leading zeros.
  
''value_second='' is the number of the swing, starting from 1
+
Each {{anchor|repetition-expansion|'''repetition'''}} ''<var>s</var>*<var>n</var>'' is expanded within its expansion list to a comma-separated list with <var>n</var> elements, each of which is the string <var>s</var>. E.g., “abc*3” would become “abc,abc,abc”.
  
No default is provided by the engine
+
A single {{anchor|square-bracket-expansion|'''progressive string value'''}} may contain multiple '''square-bracketed expansion lists''', which must all be of the same length in elements (after range and repetition expansion). This length shall be referred to as <var>L</var> for the remainder of this section.
==== attack ====
 
This animation is triggered during a strike, of the unit giving the blow
 
  
''value='' is the number of damage dealt, if any
+
When a progressive string that contains expansion lists is processed to expand its expansion lists, it is transformed into a comma-separated list with <var>L</var> elements.
  
''value_second='' is the number of the swing, starting from 1
+
For each integer <var>n</var> from 1 to <var>L</var>, the <var>n</var>-th element of this list shall be a copy of the progressive string, with each expansion list it contains replaced with that expansion list’s <var>n</var>-th element (after range and repetition expansion).
  
if no animation is available, the default uses the standing animation(s) with ''offset="0~0.6:200,0.6~0:200"'' for melee attacks
+
When a progressive string that contains expansion lists is processed it is transformed at each bracket pair into a comma-separated list, which is generated as follows (in pseudo code):
No default is provided for range attacks
+
for each comma separated string entry
==== leading ====
+
for all square brackets entries at once
This animation is triggered for units with the leading special ability, when the unit they are leading is attacking
+
expand outwards such that S[A~B]T expands to SAT, ..., SBT preserving any leading zeros
 +
expand outwards such that S[A,B]T expands to SAT, SBT
 +
expand outwards such that S[A*N]T expands to SAT, ..., SAT (n times)
 +
and any combination of above separated by commas executed sequentially
 +
next
 +
next
  
''value='' is not used, and always contains 0
+
So, for example, the progressive string <code>element[1~5]-subelement[3,2,1~3].png</code> would expand to the following list of values:
  
''value_second='' is the number of the swing, starting from 1
+
* <code>element1-subelement3.png</code>
 +
* <code>element2-subelement2.png</code>
 +
* <code>element3-subelement1.png</code>
 +
* <code>element4-subelement2.png</code>
 +
* <code>element5-subelement3.png</code>
  
No default is provided by the engine
+
In particular, as you can see, it is ''not'' computed using a Cartesian product of all the square brackets.
  
 
+
== Example ==
==== resistance ====
+
<syntaxhighlight lang=wml highlight=5,11,29>
This animation is triggered for units with the resistance special ability affecting neighbouring fights, when the unit they are helping is defending
+
[attack_anim]
 
+
    [filter_attack]
''value='' is not used, and always contains 0
+
        name=bow
 
+
    [/filter_attack]
''value_second='' is the number of the swing, starting from 1
+
    missile_start_time=-150
 
+
    [missile_frame]
No default is provided by the engine
+
        duration=150
 
+
        image="projectiles/missile-n.png"
==== death ====
+
        image_diagonal="projectiles/missile-ne.png"
This animation is triggered when a unit die.
+
    [/missile_frame]
 
+
    start_time=-200
''value='' is not used, and always contains 0
+
    [frame]
 
+
        image=units/bowshooter.png:25  #because defense animations could possibly
''value_second='' is not used, and always contains 0
+
                                      #be longer and if so this image will display
 
+
                                      #Also gives a bit of a pause before attacking
if no animation is available, the default uses the standing animation(s) with ''alpha=1~0:600'' and plays the sound in ''die_sound='' of the enclosing unit
+
    [/frame]
==== victory ====
+
    [frame]
This animation is triggered when a unit finishes a fight by killing the other unit
+
        image=units/bowshooter-prepare-bow.png:25
 
+
    [/frame]
''value='' is not used, and always contains 0
+
     #now the time is synchronized with the missile frame for the arrow
 
+
    [frame]
''value_second='' is not used, and always contains 0
+
        image=units/bowshooter-fire-bow[1~3].png:50
 
+
    [/frame]
No default is provided by the engine
+
    [frame]
==== idling ====
+
        image=units/bowshooter.png:25  #because defense animations could possibly
This animation is called when the unit has been using its standing animation for a random duration.
+
                                      #be longer and if so this image will display
 
+
                                      #Also gives a bit of a pause after attacking
''value='' is not used, and always contains 0
+
    [/frame]
 
+
     sound_start_time=-150
''value_second='' is not used, and always contains 0
 
 
 
No default is provided by the engine
 
 
 
==== draw_weapon ====
 
This animation is played before a fight
 
 
 
''hit='' is set to HIT for the attacker and MISS for the defender
 
 
 
No default is provided by the engine
 
 
 
==== sheath_weapon ====
 
This animation is played after a fight simultaneously for all surviving units
 
 
 
No default is provided by the engine
 
 
 
==== default ====
 
 
 
This animation is never triggered, but is used as a base to create new animations
 
 
 
''value='' is used depending of the type of animation it replaces
 
 
 
''value_second='' is used depending of the type of animation it replaces
 
 
 
A single animation made of the base frame is provided by the engine if no default animation is available
 
 
 
==== extra animations ====
 
Other values are never called by the engine. However they can be triggered by WML events allowing custom animations.
 
 
 
Note that WML events can also trigger standard animations.
 
''value='' is set from the WML event
 
 
 
''value_second='' is set from the WML event
 
 
 
== Shortcuts, tricks and quirks ==
 
 
 
=== ''[if]'' and ''[else]'' ===
 
 
 
Often, you need to do very slight variations in an animation (like a different sound depending on whether the unit hits or misses its attack), the '''[if]''' and '''[else]''' tags are meant to help you do that. ('''Attention:''' These do not have the same syntax as the action tag [if] and its subtag [else] in [[ConditionalActionsWML]].)
 
      
 
Using these in an animation is equivalent to having multiple animations, one with the '''[if]''' block and one with each of the ''[else]'' blocks. Any filtering flags in these blocks will replace the toplevel filters. You can have multiple '''[if]''' blocks in an animation, but you can't nest an '''[if]''' inside another. These should be written directly inside the '''[animation]''' block. The following example would make the '''[frame]''' inside the '''[if]''' be played when the attack misses, and the '''[frame]''' inside the '''[else]''' be played when the attack hits (producing a different sound):
 
 
 
     start_time=-100
 
 
     [if]
 
     [if]
         hits=no
+
         hits=yes
         [frame]
+
         [sound_frame]
             image="units/dwarves/lord-attack.png:200"
+
             sound=bow.ogg
            sound={SOUND_LIST:MISS}
+
         [/sound_frame]
         [/frame]
 
 
     [/if]
 
     [/if]
 
     [else]
 
     [else]
         hits=yes
+
         hits=no
         [frame]
+
         [sound_frame]
             image="units/dwarves/lord-attack.png:200"
+
             sound=bow-miss.ogg
            sound=axe.ogg
+
         [/sound_frame]
         [/frame]
 
 
     [/else]
 
     [/else]
 
+
[/attack_anim]
note that this is very close to preprocessing and should be considered as such, especially with regard to scoring and animation selection
+
</syntaxhighlight>
 
+
Note: that the arrow hits the target at time=0. Defense hit sounds are typically played at time=-25, slightly before the arrow hits the centre of the hex (the unit would feel it slightly before). It is the same idea for sword swishes and such, that 'hit' the outside of the unit before they hit the centre of the hex.
A macro exists under macros/sound-util.cfg called SOUND:HIT_AND_MISS to simplify this type of animation, which is equivalent to:
 
 
 
    start_time=-100
 
    [frame]
 
        image="units/dwarves/lord-attack.png:200"
 
    [/frame]
 
    {SOUND:HIT_AND_MISS axe.ogg {SOUND_LIST:MISS} -100}
 
 
 
=== Simplified animation blocks ===
 
To simplify the most common animation cases, you can use different blocks instead of the generic '''[animation]''' block. These are also here for backward compatibility, but are not deprecated and fully supported
 
 
 
some of these use extra tags which are translated in the normal ''value='' tag
 
 
 
some of these define '''[xxx_frame]''' blocks where the frame prefix starts with an underscore. This is not allowed in normal WML (prefix starting with underscore is reserved for the engine internal use). It is here for clarity purpose
 
 
 
* '''[leading_anim]''' is an animation with the following parameters automatically set
 
apply_to=leading
 
* '''[recruit_anim]''' is an animation with the following parameters automatically set
 
apply_to=recruited
 
* '''[recruiting_anim]''' is an animation with the following parameters automatically set
 
apply_to=recruiting
 
* '''[standing_anim]''' is an animation with the following parameters automatically set
 
apply_to=standing,default
 
note for 1.4 :''default'' doesn't exist in 1.4, but the semantic is the same.
 
 
 
i.e: the animation will be used to build default animations
 
* '''[idle_anim]''' is an animation with the following parameters automatically set
 
apply_to=idling
 
* '''[levelout_anim]''' is an animation with the following parameters automatically set
 
apply_to=levelout
 
* '''[levelin_anim]''' is an animation with the following parameters automatically set
 
apply_to=levelin
 
* '''[healing_anim]''' is an animation with the following parameters automatically set
 
apply_to=healing
 
value=<damage= value>
 
* '''[healed_anim]''' is an animation with the following parameters automatically set
 
apply_to=healed
 
value=<healing= value>
 
[_healed_sound_frame]
 
    sound=heal.wav
 
[/_healed_sound_frame]
 
* '''[poison_anim]''' is an animation with the following parameters automatically set
 
apply_to=poisoned
 
value=<damage= value>
 
[_poison_sound_frame]
 
    sound=poison.ogg
 
[/_poison_sound_frame]
 
* '''[movement_anim]''' is an animation with the following parameters automatically set
 
apply_to=movement
 
offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"
 
* '''[pre_movement_anim]''' is an animation with the following parameters automatically set
 
apply_to=pre_movement
 
* '''[post_movement_anim]''' is an animation with the following parameters automatically set
 
apply_to=post_movement
 
* '''[draw_weapon_anim]''' is an animation with the following parameters automatically set
 
apply_to=draw_weapon
 
* '''[sheath_weapon_anim]''' is an animation with the following parameters automatically set
 
apply_to=sheath_weapon_movement
 
* '''[defend]''' is an animation with the following parameters automatically set
 
apply_to=defend
 
value=<damage= value>
 
<WML content>
 
[frame]
 
    blend_color=255,0,0
 
    blend_ratio="0.5:50,0.0:50"
 
[/frame]
 
 
 
there are some subtil change compared to what is described above to avoid the red flash when value=0, but it should work as expected as far as WML author are concerned
 
 
* '''[attack_anim]''' is an animation with the following parameters automatically set
 
if the animation contains a missile frame
 
 
 
apply_to=attack
 
missile_offset="0~0.8"
 
 
 
else
 
 
 
apply_to=attack
 
offset="0~0.6,0.6~0"
 
 
 
* '''[death]''' is an animation with the following parameters automatically set
 
apply_to=death
 
[_death_sound_frame]
 
    sound=<die_sound= of the enclosing [unit] tag>
 
[/_death_sound_frame]
 
* '''[victory_anim]''' is an animation with the following parameters automatically set
 
apply_to=victory
 
* '''[extra_anim]''' is an animation with the following parameters automatically set
 
apply_to=<flag= value of the anim>
 
* '''[teleport_anim]''' will be cut into two at the clock-time 0
 
everything before zero becomes an animation with the following parameters automatically set
 
apply_to=pre_teleport
 
everything after zero becomes an animation with the following parameters automatically set
 
apply_to=post_teleport
 
 
 
== Layering ==
 
The ''layer'' progressive parameter allows the animation writer to choose in what order the animation should be drawn.
 
 
 
this value must be between 0 and 100
 
 
 
* the back of haloes is drawn with a value of 10
 
* when unspecified, a animation is drawn with a value of 40
 
* terrain elements that are supposed to go in front of units (castles) are drawn with a value of 50
 
* orbs and status bars are drawn on layer 80
 
* when unspecified missile frames are drawn on layer 90
 
 
 
by changing these values, it is easy to have the unit display the way you want
 
 
 
== Optimization ==
 
 
 
there is a special flag that goes into the animation block (not the frame)  
 
 
 
* ''offscreen'' a boolean saying if the unit's animation should be played when the unit is offscreen. You want the animation to play offscreen if the animation contains some usefull sound effects.This attribute defaults to true (play offscreen) except for standing and idle animations where it defaults to false
 
 
 
== Cycling ==
 
 
 
there is a special boolean parameter that can be put in an animation block (not the frame)
 
 
 
* ''cycles'' a boolean value. if set to true the animation will cycles forever until it is replaced. That animation will not influence the end time of all related animations (for example a cycling defense animation will play normally but the overall duration of the fight animation will be only decided by the attack animation)
 
  
 
== See Also ==
 
== See Also ==
Line 680: Line 778:
 
* [[UnitTypeWML]]
 
* [[UnitTypeWML]]
 
* [[ReferenceWML]]
 
* [[ReferenceWML]]
 
+
* [[Advanced Animation Tutorial]]
  
 
[[Category: WML Reference]]
 
[[Category: WML Reference]]

Latest revision as of 16:53, 11 July 2024

[edit]WML Tags

A:

abilities, about, achievement, achievement_group, add_ai_behavior, advanced_preference, advancefrom, advancement, advances, affect_adjacent, ai, allied_with, allow_end_turn, allow_extra_recruit, allow_recruit, allow_undo, and, animate, animate_unit, animation, aspect, attack (replay, weapon), attack_anim, attacks (special, stats), avoid;

B:

base_unit, background_layer, berserk, binary_path, break, brush;

C:

campaign, cancel_action, candidate_action, capture_village, case, chance_to_hit, change_theme, chat, checkbox, choice, choose, clear_global_variable, clear_menu_item, clear_variable, color_adjust, color_palette, color_range, command (action, replay), continue, core, credits_group, criteria;

D:

damage, damage_type, death, deaths, default, defend, defends, defense, delay, deprecated_message, destination, difficulty, disable, disallow_end_turn, disallow_extra_recruit, disallow_recruit, do, do_command, drains, draw_weapon_anim;

E:

editor_group, editor_music, editor_times, effect, else (action, animation), elseif, endlevel, end_turn (action, replay), enemy_of, engine, entry (credits, options), era, event, experimental_filter_ability, experimental_filter_ability_active, experimental_filter_specials, extra_anim;

F:

facet, facing, fake_unit, false, feedback, female, filter (concept, event), filter_adjacent, filter_adjacent_location, filter_attack, filter_attacker, filter_base_value, filter_condition, filter_defender, filter_enemy, filter_location, filter_opponent, filter_own, filter_owner, filter_radius, filter_recall, filter_second, filter_second_attack, filter_self, filter_side, filter_student, filter_vision, filter_weapon, filter_wml, find_path, fire_event, firststrike, floating_text, fonts, for, foreach, found_item, frame;

G:

game_config, get_global_variable, goal, gold, gold_carryover;

H:

harm_unit, has_ally, has_attack, has_unit, has_achievement, have_location, have_unit, heal_on_hit, heal_unit, healed_anim, healing_anim, heals, hide_help, hide_unit, hides;

I:

idle_anim, if (action, animation, intro), illuminates, image (intro, terrain), init_side, insert_tag, inspect, item, item_group;

J:

jamming_costs, join;

K:

kill, killed;

L:

label, language, leader, leader_goal, leadership, leading_anim, levelin_anim, levelout_anim, lift_fog, limit, literal, load_resource, locale, lock_view, lua;

M:

male, menu_item, message, micro_ai, missile_frame, modification, modifications, modify_ai, modify_side, modify_turns, modify_unit, modify_unit_type, move, move_unit, move_unit_fake, move_units_fake, movement_anim, movement costs, movetype, multiplayer, multiplayer_side, music;

N:

not, note;

O:

object, objective, objectives, on_undo, open_help, option, options, or;

P:

part, petrifies, petrify, place_shroud, plague, poison, post_movement_anim, pre_movement_anim, primary_attack, primary_unit, print, progress_achievement, put_to_recall_list;

R:

race, random_placement, recall (action, replay), recalls, recruit, recruit_anim, recruiting_anim, recruits, redraw, regenerate, remove_event, remove_item, remove_object, remove_shroud, remove_sound_source, remove_time_area, remove_trait, remove_unit_overlay, repeat, replace_map, replace_schedule, replay, replay_start, reset_fog, resistance (ability, unit), resistance_defaults, resolution, resource, return, role, rule;

S:

save, scenario, screen_fade, scroll, scroll_to, scroll_to_unit, secondary_attack, secondary_unit, section, select_unit, sequence, set_achievement, set_extra_recruit, set_global_variable, set_menu_item, set_recruit, set_specials, set_variable, set_variables, sheath_weapon_anim, show_if (message, objective, set_menu_item), show_objectives, side, skirmisher, slider, slow, snapshot, sound, sound_source, source (replay, teleport), special_note, specials, split, stage, standing_anim, statistics, status, store_gold, store_items, store_locations, store_map_dimensions, store_reachable_locations, store_relative_direction, store_side, store_starting_location, store_time_of_day, store_turns, store_unit, store_unit_defense, store_unit_defense_on, store_unit_type, store_unit_type_ids, store_villages, story, swarm, sub_achievement, switch, sync_variable;

T:

target, team, teleport (ability, action), teleport_anim, terrain, terrain_defaults, terrain_graphics, terrain_mask, terrain_type, test, test_condition, test_do_attack_by_id, text_input, textdomain, theme, then, tile, time, time_area, topic, toplevel, trait, transform_unit, traveler, true, tunnel;

U:

unhide_unit, unit (action, scenario), unit_overlay, unit_type, unit_worth, units, unlock_view, unpetrify, unstore_unit, unsynced;

V:

value, variable, variables, variant, variation, victory_anim, village, vision_costs, volume;

W:

while, wml_message, wml_schema;

Z:

zoom;

Introduction

This page covers unit animations. At any point in a game, units are playing an animation. Even when the unit is standing, it is actually playing a single frame animation.

This page will deal with the two problems of animations:

  • How are animations chosen
  • What exactly is drawn
  • Defining animations with WML

How animations are drawn

Animations

Any unit has a huge set of animations to choose from. An animation is what a unit displays when a given event is triggered, and a certain set of conditions are met, such as:

  • unit is attacking
  • unit is idling
  • unit is attacking (event) and uses a melee weapon (condition)
  • unit is attacking (event) and uses a melee weapon (condition) and opponent can't retaliate (condition)

Events and conditions are described entirely in the [animation] block, and will be discussed in the animation choice section.

Frames

An animation is cut into one or more sequences of frames.

A frame typically describes an image, where and how to render it. It can contain such things as:

  • the image to display
  • how transparent should that image be
  • where to draw that image.

At any given time, an animation will display one frame for each frame prefix it can find. I.e. if your animation has both [frame] and [missile_frame] blocks, both will be displayed at the same time.

The frame with the empty prefix is special in many ways. It is assumed to contain the image representing the unit itself, and as such, the engine will heavily tamper with it in multiple ways, such as providing a default image if none is available, or forcing the image to be green when the unit is poisoned.

Timing, Clock, Multiple animations

When an animation is played, it has an internal clock that is run. The step of this clock is the millisecond, and each frame is played for a given duration. Each animation also has a starting time which tells the animation engine at what value the animation clock should start.

This starting time is very important when multiple animations from different units are played synchronously. Typically a fight involves a unit playing its defense animation while the opponent plays its attack animation (and a third might play its leading animation, too). In that case all animations have a common clock, and are played concurrently.

The convention is that the "important time" (usually the time of the hit) is at time 0. Everything before should be at negative time, everything after at positive time. This is a WML convention that is not enforced by the engine.

In that case, it is very important that these animations (which can have different lengths) be synchronized. Fight animations are synchronized around the point of impact, so they all reach time 0 when the attack lands (or misses). This is accomplished through the use of the start_time key of the [animation] tag.

Just like the [frame] tag can have a prefix, so can the start_time key. A start_time key without prefix will affect a [frame] tag without prefix, while a start_time key with a prefix will affect a [frame] tag with the same prefix.

Layering

The layer progressive parameter allows the animation writer to choose in what order the animation should be drawn.

This value must be between 0 and 100.

  • The back of haloes is drawn with a value of 10.
  • When unspecified, a animation is drawn with a value of 40.
  • Terrain elements that are supposed to go in front of units (castles) are drawn with a value of 50.
  • Orbs and status bars are drawn on layer 80.
  • When unspecified, missile frames are drawn on layer 90.

By changing these values, it is easy to have the unit display the way you want.

Related Parameter handling

All drawing related parameters in [animation] can be matched with a corresponding parameter in a [frame] block (or a [xxx_frame] block) The value provided in the animation will be used if no value is provided in the frame.

The point of these two level of parameters is to easily be able to specify a parameter at the animation level and override it for a special case of frame.

Note that xxx_ parameters (e.g. missile_offset) are shortcuts to access parameters from outside of their corresponding frame and have no effect inside of them.

How animations are chosen

All animations are triggered by a specific event, given by the apply_to= field of the animation (or the flag= field in an [extra_anim]). When an event triggers, all animations linked to that event are gathered and tested to see if they are viable.

Unless otherwise specified, the value and value_second for an event are always 0. If only one is specified, the other is always 0.

Common animation events used by the engine

standing
This animation is triggered whenever any other animation is finished, and is played in loop until another animation is triggered. This is the default "standing image" for the unit. If no standing animation is set for a unit type, a single frame animation based on the enclosing unit's image= field is created.
selected
This animation is triggered whenever the unit is selected. If no animation is available for this event, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="0.0~0.3:100,0.3~0.0:200"
  • blend_color=255,255,255
recruited
This animation is triggered whenever a unit is recruited or recalled. If no animation is available, the default uses the standing animation(s) with the following modification:
  • alpha=0~1:600
recruiting
This animation is triggered for the leader when a unit is recruited or recalled. No default animation is generated. Note that is not triggered for WML triggered recruits.
levelout
This animation is triggered whenever the unit levels, before the unit is replaced by the new unit. If no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="0~1:600"
  • blend_color=255,255,255
levelin
This animation is triggered whenever the unit levels, after the unit is replaced by the new unit. no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="1~0:600"
  • blend_color=255,255,255
movement
This animation is used whenever a unit moves. There are multiple things to consider when dealing with movement animations:
  • Units stay exactly 200ms in each hex. so you typically want to have an offset of 0~1:200,0~1:200,0~1:200,0~1:200,0~1:200 or something similar.
  • When a unit enters a hex, the current animation is tested again.
    • If the current animation is still valid (i.e it passes all its filters), then it is kept, whatever the final score is.
    • If it fails, a new movement animation is found.
  • value= contains the number of steps already taken.
  • value_second= contains the number of steps left.
If no animation is available, the default uses the standing animation(s) with the following modification:
  • offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"
pre_movement
This animation is used before any unit movement begins.
post_movement
This animation is used after any unit movement completes.
pre_teleport
This animation is triggered whenever the unit teleports, before it has moved to the target hex. If no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="1~0:200"
  • blend_color=255,255,255
post_teleport
This animation is triggered whenever the unit teleports, after it has moved to the target hex. If no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="0~1:200"
  • blend_color=255,255,255
healing
This animation is triggered when the unit has healing powers and uses them. No default is provided by the engine.
  • value= is the number of points healed.
healed
This animation is triggered whenever the unit is healed for any reason.
  • value= is the number of points healed
If no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30"
  • blend_color=255,255,255
  • sound=heal.wav
poisoned
This animation is triggered whenever the unit suffers poison damage
  • value= is the number of hitpoints lost.
If no animation is available, the default uses the standing animation(s) with the following modifications:
  • blend_ratio="0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30"
  • blend_color=0,255,0
  • sound=poison.ogg
defend
This animation is triggered during a strike, on the unit receiving the blow. No default is provided by the engine.
  • value= is the number of hitpoints lost, if any.
  • value_second= is the number of the swing, starting from 1.
attack

This animation is triggered during a strike, of the unit giving the blow

  • value= is the amount of damage dealt, if any.
  • value_second= is the number of the swing, starting from 1.
If no animation is available, the default uses the standing animation(s) with the following modifications:
  • range=melee
  • offset="0~0.6:200,0.6~0:200"
No default is provided for non-melee attacks
leading
This animation is triggered for units with the leading special ability, when the unit they are leading is attacking. No default is provided by the engine.
  • value_second= is the number of the swing, starting from 1.
teaching
(Version 1.15.11 and later only) This animation is triggered for units with weapon special abilities used like leadership, when the unit they are teaching is attacking. No default is provided by the engine.
  • value_second= is the number of the swing, starting from 1.
resistance
This animation is triggered for units with the resistance special ability affecting neighbouring fights, when the unit they are helping is defending. No default is provided by the engine.
  • value_second= is the number of the swing, starting from 1.
death
This animation is triggered when a unit dies. If no animation is available, the default uses the standing animation(s) with the following modifications:
  • alpha=1~0:600
  • sound=<die_sound= of the enclosing unit>
victory
This animation is triggered when a unit finishes a fight by killing the other unit. No default is provided by the engine.
idling
This animation is called when the unit has been using its standing animation for a random duration. No default is provided by the engine.
draw_weapon
This animation is played before a fight begins. No default is provided by the engine.
  • hits= is set to HIT for the attacker and MISS for the defender
sheath_weapon
This animation is played after a fight simultaneously for all surviving units. No default is provided by the engine.
default
This animation is never triggered, but is used as a base to create new animations. A single animation made of the base frame is provided by the engine if no default animation is available.

Custom animation events

Other animation events are never triggered by the engine. However, they can be triggered by WML events or from Lua, allowing custom animations.

The value=, value_second=, and hits= values are all set from the WML event or Lua animation call.

Note that WML events and Lua can also trigger standard animations.

Selection Algorithm

Within a unit description block there are multiple animations. Each animation is meant to be played in a very special set of circumstances. We will discuss here how to tell the animation engine when a given animation should be played.

Let's take an example. Suppose we have a unit which has the skirmisher ability. We have the following movement animations :

  • a normal walking animation
  • a swimming animation when the unit is in water
  • a tiptoeing animation when moving next to an enemy unit

Ok. most of the time it's easy to guess what animation should be. However if you are both in water and next to an enemy unit, the animation to play is not obvious.

To solve that question, each animation has a number of filtering criteria. The engine follow the following rules to select an animation

  • Start with all animations
  • Drop all animations with a criterion that fails on the current situation
  • Take the animations that have the most matching criteria
  • If there are more than one animation remaining, take an animation randomly
  • If all animations have been dropped, the engine will provide a smart default.

here is a pseudo-code explanation of this algorithm:

foreach animation
   animation_score = 0
   foreach filter-criteria
     if <criteria-fails> 
         animation_score = -1;
         break;
     elsif <criteria-matches> 
         animation_score++
   endfor
   if animation_score > max_score
       <empty animation list>
       max_score = animation_score;
       push(animation,animation_list);
   elsif animation_score = max_score
       push(animation,animation_list);
endfor
<choose an animation randomly from animation_list>


Note that all animations don't have all the filters...

so, if we have a unit with

  1. an animation for water terrain
  2. an animation for SE on grassland
  3. an animation with no criteria
  4. an animation for N,NE,NW,S,SW,SE
  5. an animation for NW
  • 3. will never be taken, because 4. will always trump it
  • on water going NW, it can be 1. 4. 5.
  • on water, any direction but NW, it will be 1. or 4.
  • on SE grassland it will be 2.
  • on other grasslands, it will be 4. (4. or 5. if NW)

Defining an Animation

The [frame] tag

Animation frames are defined using either the [frame] tag or a prefixed version of it, like [xxx_frame]. The xxx can be any sequence of alphanumeric characters. However, prefixes beginning with an underscore are reserve by the engine for internal use, and should generally not be used in user code. A frame of the type [frame] is said to have an empty prefix.

The following keys can be used in frame tags:

  • duration: (integer) The duration of the frame, in milliseconds. If you use timings in a progressive parameter, you can omit the duration, and it will be inferred from the progressive timings.
    • Note: Older animations used begin and end instead. This is now equivalent to setting the duration to end - begin.
  • image: (progressive string) The image to play for the frame.
  • image_diagonal: (progressive string) the image, or sequence of images, to display when the attack occurs diagonally (directions ne,se,sw,nw).
  • image_mod: (string) A string of ImagePathFunctions that is always appended to image.
  • sound: (string) The sound to play when the frame begins. Can be a comma-separated list of sounds, in which case one of them is chosen randomly every time.
  • halo: (progressive string) A halo image to play for the frame.
  • halo_x, halo_y: (progressive integer) The X and Y offset to play the halo at, relative to the unit's center. This takes into account the direction the unit is facing.
  • halo_mod: (string) A string of ImagePathFunctions that is always appended to halo.
  • alpha: (progressive real) The transparency of the image. Should stay within the range of 0.0 to 1.0.
  • offset: (progressive real) The position of the image relative to the hex the unit is facing – 0.0 will display the unit in the center of the hex it occupies, 1.0 in the center of the faced hex, and -1.0 at the center of the hex behind you.
  • blend_color: (color triple) The image will be tinted in this colour. It should be a comma-separate list of red, green, and blue values in the 0 to 255 range.
  • blend_ratio: (progressive real) Determine the strength of the tint, ranges from 0.0 to 1.0. A value of 0.0 means no tint, while 1.0 will yield only the tint color as a solid flat color.
  • text: (string) Floating text to be played above the unit when the frame begins.
  • text_color: (color triple) The floating text color. Same format as blend_color.
  • submerge: (progressive real) The percentage of the image's base that should be faded out (at 50% opacity) as if submerged in the terrain. Only used on terrains that have the submerge setting, such as water terrains.
  • x, y: (progressive integer) The X and Y offset of the main image, in pixels.
  • directional_x, directional_y: (progressive integer) Like above, but aligned with the direction the unit is facing, rather than an absolute offset.
  • layer: (progressive integer) The layer used to draw the frame. See the discussion below.
  • auto_hflip: (boolean) Should the image flip horizontally depending on sprite orientation?
  • auto_vflip: (boolean) Should the image flip vertically depending on sprite orientation?
  • primary: (boolean) Should the engine consider this frame to be a primary frame? The primary frame is affected by visual effects like stone and poison. Usually, this is yes on unprefixed frames and no on prefixed frames.

The [animation] tag

Animations are defined by WML tags in the unit type. They can be defined either with the generic [animation] tag, or with some more specific tags, such as [attack_anim] or [idle_anim].

The content of an animation is divided into two parts: filter data, which determines when the animation can be played, and frame data, which defines what is played. In addition, there is support for conditional branches, which is in effect a shorthand for defining multiple similar animations in a single block, as well as some general settings.

Animation Filtering

  • apply_to: A list of comma separated keywords describing what event should trigger the animation (movement, attack, etc). The complete list is given below. Custom events can also be used; these can be triggered from ActionWML using the flag key.
  • value: A list of comma separated integers. The animation will only be triggered if the event's primary value matches one of these integers. The meaning of the value depends on the event and is described in more detail in the list of events.
  • value_second: A list of comma separated integers. The animation will only be triggered if the event's secondary value matches one of these integers. The meaning of the second value depends on the event and is described in more detail in the list of events.
  • terrain_type: A list of comma separated terrain codes. The animation will only be used on matching terrains. See Filtering Terrains.
  • [filter]: This will filter using a standard unit filter on the animated unit. Note that matching all criterias in the filter will only earn you one point for animation selection, but that you can have multiple [filter] blocks in an animation.
  • [filter_second]: This will filter using a standard unit filter on the unit in the hex faced. Note that matching all criteria in the filter will only earn you one point for animation selection, but that you can have multiple [filter_second] blocks in an animation. Also note that if the faced hex is empty, the whole filter will fail.
  • direction: A list of directions (n,ne,se,s,sw,nw). The animation will only be used when acting or moving in this direction (the attack direction for attack animations, moving direction for movement animations, direction of lead unit for leading animations, and so on).
  • frequency: This integer value allows you to easily tweak the matching frequency of animations. The filter will fail n-1 times out of n, randomly. Note that unlike every other filter, matching this filter won't give an extra point.
  • [filter_attack]: A standard attack filter to match on the attacker's attack. See Filtering Weapons.
  • [filter_second_attack]: A standard attack filter to match on the defender's attack. See Filtering Weapons. Also note that if the defender doesn't have any weapon to retaliate with, this filter will always fail.
  • hits: Filters attack animations based on whether the attack hits, misses or kills. Accepts a list of the following:
    • hit: The attack hits, defender survives.
    • no or miss: The attack misses.
    • kill: The attack hits, defender dies.
    • yes: Is an alias of hit,kill.
  • base_score: A number that will always be added to the score, making this animation play more often than others for the same event. Use with caution.

Frame Data

Frame data consists of optionally prefixed keys, in addition to the optionally-prefixed [frame] tags. Keys and tags with the same prefix work together.

Frame data consists of three parts:

  1. Frame timing, specified by the start_time key. This is an integer specifying when the sequence of frames with the given prefix should start, in milliseconds. Typically, the start time will be negative.
  2. Default frame data, specified using keys that normally appear in the frame. These specify the default value of a setting for all frames with the given prefix that don't explicitly specify the setting. The following keys are supported for default frame data (they are defined above).
    • offset
    • image_mod
    • blend_color
    • blend_ratio
    • halo
    • halo_x, halo_y
    • halo_mod
    • alpha
    • submerge
    • x, y
    • directional_x, directional_y
    • layer
  3. The frames themselves. This is the core of the frame data, contained within the [frame] tag.

All frame data can be either prefixed or unprefixed.

Other Settings

These are general settings in the animation that are related neither to filtering nor to the content of the frames.

  • offscreen: (boolean) Specifies whether the unit's animation should be played when the unit is offscreen. You want the animation to play offscreen if the animation contains some useful sound effects. This attribute defaults to true (play offscreen) except for standing and idle animations where it defaults to false.
  • cycles: (boolean) If set to true, the animation will cycle forever until it is replaced. That animation will not influence the end time of all related animations (for example, a cycling defense animation will play normally, but the overall duration of the fight animation will be decided only by the attack animation).

Conditional Branches

Often, you need to do very slight variations in an animation (like a different sound depending on whether the unit hits or misses its attack). The [if] and [else] tags are meant to help you do that. (Attention: These do not have the same syntax as the action tag [if] and its subtag [else] in ConditionalActionsWML.)

Using these in an animation is equivalent to having multiple animations, one with the [if] block and one with each of the [else] blocks. In other words, an [if] or [else] tag has the same possible content as the [animation] tag. Any filtering flags in these blocks will replace the toplevel filters of the same type. You can have multiple [if] blocks in an animation, and from 1.11.7+ you can nest an [if] inside another. These should be written directly inside the [animation] block. The following example would make the [frame] inside the [if] be played when the attack misses, and the [frame] inside the [else] be played when the attack hits (producing a different sound):

[animation]
    start_time=-100
    [if]
        hits=no
        [frame]
            image="units/dwarves/lord-attack.png:200"
            sound={SOUND_LIST:MISS}
        [/frame]
    [/if]
    [else]
        hits=yes
        [frame]
            image="units/dwarves/lord-attack.png:200"
            sound=axe.ogg
        [/frame]
    [/else]
[/animation]

Note that this is very close to preprocessing and should be considered similar to such, especially with regard to scoring and animation selection. When the animations are parsed from the unit type, these if/else blocks are removed, creating multiple similar animations.

A macro exists under macros/sound-util.cfg called SOUND:HIT_AND_MISS to simplify this type of animation, which is equivalent to:

[animation]
    start_time=-100
    [frame]
        image="units/dwarves/lord-attack.png:200"
    [/frame]
    {SOUND:HIT_AND_MISS axe.ogg {SOUND_LIST:MISS} -100}
[/animation]

Shorthand Animation Tags

To simplify the most common animation cases, you can use different blocks instead of the generic [animation] block. These are also here for backward compatibility, but are not deprecated and are fully supported.

Some of these support keys or tags that are not allowed in [animation] but are translated into standard keys. The meaning of these keys or tags and how they are translated will be described here.

Tag Expansion Notes
[leading_anim]
[animation]
    apply_to=leading
    {WML content}
[/animation]
A leading animation.
[recruit_anim]
[animation]
    apply_to=recruited
    {WML content}
[/animation]
A recruited animation.
[recruiting_anim]
[animation]
    apply_to=recruiting
    {WML content}
[/animation]
A recruiting animation.
[standing_anim]
[animation]
    apply_to=standing,default
    {WML content}
[/animation]
This animation will be used to build default animations in addition to being used as the standing animation.
[idle_anim]
[animation]
    apply_to=idling
    {WML content}
[/animation]
An idling animation.
[levelout_anim]
[animation]
    apply_to=levelout
    {WML content}
[/animation]
A level out advancement animation.
[levelin_anim]
[animation]
    apply_to=levelin
    {WML content}
[/animation]
A level in advancement animation.
[healing_anim]
[animation]
    apply_to=healing
    value=<damage= value>
    {WML content}
[/animation]
A healing animation.
[healed_anim]
[animation]
    apply_to=healed
    value=<healing= value>
    [_healed_sound_frame]
        sound=heal.wav
    [/_healed_sound_frame]
    {WML content}
[/animation]
A healed animation.
[poison_anim]
[animation]
    apply_to=poisoned
    value=<damage= value>
    [_poison_sound_frame]
        sound=poison.ogg
    [/_poison_sound_frame]
    {WML content}
[/animation]
A poisoned animation.
[movement_anim]
[animation]
    apply_to=movement
    offset="0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200,0~1:200"
    {WML content}
[/animation]
A movement animation.
[pre_movement_anim]
[animation]
    apply_to=pre_movement
    {WML content}
[/animation]
A pre-movement animation.
[post_movement_anim]
[animation]
    apply_to=post_movement
    {WML content}
[/animation]
A post-movement animation.
[draw_weapon_anim]
[animation]
    apply_to=draw_weapon
    {WML content}
[/animation]
A draw weapon animation.
[sheath_weapon_anim]
[animation]
    apply_to=sheath_weapon
    {WML content}
[/animation]
A sheath weapon animation.
[defend]
[animation]
    apply_to=defend
    value=<damage= value>
    {WML content}
    [frame]
        blend_color=255,0,0
        blend_ratio="0.5:50,0.0:50"
    [/frame]
[/animation]
A defense animation.

There are some subtle changes compared to what is described here to avoid the red flash when value=0, but it should work as expected as far as WML author is concerned.

[attack_anim] If the animation contains a missile frame:
[animation]
    apply_to=attack
    missile_offset="0~0.8"
    {WML content}
[/animation]

Otherwise:

[animation]
    apply_to=attack
    offset="0~0.6,0.6~0"
    {WML content}
[/animation]
An attack animation.
[death]
[animation]
    apply_to=death
    [_death_sound_frame]
        sound=<die_sound= of the enclosing [unit] tag>
    [/_death_sound_frame]
    {WML content}
[/animation]
A death animation.
[victory_anim]
[animation]
    apply_to=victory
    {WML content}
[/animation]
A victory animation.
[extra_anim]
[animation]
    apply_to=<flag= value of the anim>
    {WML content}
[/animation]
An extra custom animation.
[teleport_anim]
[animation]
    apply_to=pre_teleport
    {WML content before 0}
[/animation]
[animation]
    apply_to=post_teleport
    {WML content after 0}
[/animation]
Will be cut into two at clock-time 0. The first half defines the pre-teleport animation, and the second half defines the post-teleport animation.

Progressive parameters

Some parameters above are marked as progressive. This means that you can specify that during the time the frame is displayed, the parameter should smoothly slide from one value to an other.

Progressive numbers

Progressive numbers consist of a comma-separated list of values, each value of which is either a range or a single number with optional timing. If the type is specified as progressive integer, then it can only contain integers, ie with no decimal point. On the other hand, if the type is specified as progressive real, then any number is allowed.

A range is specified as 1~5 – two numbers separated by a tilde. Reverse ranges like 5~1 are also permitted.

The timing is specified by appending a colon and the number of milliseconds that the slide should take. For example, 1~5:300 will slide smoothly from 1 to 5 over a period of 300 milliseconds, while 0.5:200 will hold constant at 0.5 for 200 milliseconds.

A typical example would be a unit progressively fading out, becoming transparent. To do that, you could use:

[frame]
    alpha=1~0
[/frame]

To make a frame, which is 900ms long, slide to transparent for 300ms, stay transparent for another 300ms and finally fade back to normal for 300ms, use:

[frame]
    alpha=1~0:300,0:300,0~1:300
[/frame]

You could also specify it like this:

[frame]
    alpha=1~0,0,0~1
[/frame]

When a timing is missing, the engine will do its best to fill in in a fair way.

Progressive strings

The square bracket syntax for progressive strings is a syntactic shortcut that, for convenience, works like so:

This expression… …is equivalent to this…
halo=halo[3,5,7].png halo=halo3.png,halo5.png,halo7.png
halo=halo[07~10].png halo=halo07.png,halo08.png,halo09.png,halo10.png*
halo=halo[001~100].png halo=halo001.png,halo002.png,...,halo099.png,halo100.png
halo=halo[5~3,1].png halo=halo5.png,halo4.png,halo3.png,halo1.png
image=image[1~2]-ex[4~5].png:[100,200] image=image1-ex4.png:100,image2-ex5.png:200
image=image[1~4].png:[10,20*3] image1.png:10,image2.png:20,image3.png:20,image4.png:20
image=image[1~3].png:50,other.png:40 image1.png:50,image2.png:50,image3.png:50,other.png:40

*(if you include leading zeros, the digits will be padded throughout that square bracket expansion to match)

Each set of square brackets (‘[’ and ‘]’) contains a comma-separated list — an expansion list — of which each element can be one of the following:

  • A plain string — e.g., “abc” or “123”. A plain string just expands to itself.
  • A range, expressed as two radix-10 integers, separated by a tilde — e.g., “1~5”. This is similar to a progressive integer, but with some differences described below.
  • A repetition, notated as a string and a radix-10 integer, separated by an asterisk — e.g., “a*3”.

Each range x~y is expanded within its expansion list to a comma-separated list of radix-10 integers (which are plain strings), starting from the first endpoint x and ending at the second endpoint y.

If x < y, then the numbers in this list increase by increments of 1; if x > y, then the numbers in this list decrease by decrements of 1. E.g., “1~3” would become “1,2,3”, and “3~1” would become “3,2,1”.

If x = y, then this list has only one element, which is x (and is also y, of course). E.g., “2~2” would become “2”.

As with progressive numbers, ranges can not only be from a lesser number to a greater number (e.g., “1~5”), but can also be backward — from a greater number to a lesser number (e.g., “5~1”).

If either x or y (or both) are padded with leading zeros, then each number in the resultant list will be padded with leading zeros such that it is expressed in at least z+1 digits, where z is the quantity of leading zeros with which one or both endpoints are padded. E.g.,

  • “07~11” would become “07,08,09,10,11”,
  • “098~100” would become “98,99,100”,
  • “003~1” (or “3~001”) would become “003,002,001”, and
  • “0098~100” would become “098,099,100” (although poor form), and
  • “01~100” would become “01,02,...,10,11,...,99,100”.

If both endpoints are padded with leading zeros, they should both be padded with the same quantity of leading zeros.

Each repetition s*n is expanded within its expansion list to a comma-separated list with n elements, each of which is the string s. E.g., “abc*3” would become “abc,abc,abc”.

A single progressive string value may contain multiple square-bracketed expansion lists, which must all be of the same length in elements (after range and repetition expansion). This length shall be referred to as L for the remainder of this section.

When a progressive string that contains expansion lists is processed to expand its expansion lists, it is transformed into a comma-separated list with L elements.

For each integer n from 1 to L, the n-th element of this list shall be a copy of the progressive string, with each expansion list it contains replaced with that expansion list’s n-th element (after range and repetition expansion).

When a progressive string that contains expansion lists is processed it is transformed at each bracket pair into a comma-separated list, which is generated as follows (in pseudo code):

for each comma separated string entry
	for all square brackets entries at once
		expand outwards such that S[A~B]T expands to SAT, ..., SBT preserving any leading zeros
		expand outwards such that S[A,B]T expands to SAT, SBT
		expand outwards such that S[A*N]T expands to SAT, ..., SAT (n times)
		and any combination of above separated by commas executed sequentially
	next
next

So, for example, the progressive string element[1~5]-subelement[3,2,1~3].png would expand to the following list of values:

  • element1-subelement3.png
  • element2-subelement2.png
  • element3-subelement1.png
  • element4-subelement2.png
  • element5-subelement3.png

In particular, as you can see, it is not computed using a Cartesian product of all the square brackets.

Example

[attack_anim]
    [filter_attack]
        name=bow
    [/filter_attack]
    missile_start_time=-150
    [missile_frame]
        duration=150
        image="projectiles/missile-n.png"
        image_diagonal="projectiles/missile-ne.png"
    [/missile_frame]
    start_time=-200
    [frame]
        image=units/bowshooter.png:25  #because defense animations could possibly
                                       #be longer and if so this image will display
                                       #Also gives a bit of a pause before attacking
    [/frame]
    [frame]
        image=units/bowshooter-prepare-bow.png:25
    [/frame]
    #now the time is synchronized with the missile frame for the arrow
    [frame]
        image=units/bowshooter-fire-bow[1~3].png:50
    [/frame]
    [frame]
        image=units/bowshooter.png:25  #because defense animations could possibly
                                       #be longer and if so this image will display
                                       #Also gives a bit of a pause after attacking
    [/frame]
    sound_start_time=-150
    [if]
        hits=yes
        [sound_frame]
            sound=bow.ogg
        [/sound_frame]
    [/if]
    [else]
        hits=no
        [sound_frame]
            sound=bow-miss.ogg
        [/sound_frame]
    [/else]
[/attack_anim]

Note: that the arrow hits the target at time=0. Defense hit sounds are typically played at time=-25, slightly before the arrow hits the centre of the hex (the unit would feel it slightly before). It is the same idea for sword swishes and such, that 'hit' the outside of the unit before they hit the centre of the hex.

See Also

This page was last edited on 11 July 2024, at 16:53.