Difference between revisions of "AnimationWML"

From The Battle for Wesnoth Wiki
(Rewrite page to give a clearer summary of the available syntax)
(Shorthand Animation Tags: Strip some _anim suffixes from anchors)
Line 255: Line 255:
 
!style="width"40%"|Notes
 
!style="width"40%"|Notes
 
|- style="vertical-align:top;"
 
|- style="vertical-align:top;"
! {{anchor|short-leading_anim|[leading_anim]}}
+
! {{anchor|short-leading|[leading_anim]}}
 
| <syntaxhighlight lang=wml>
 
| <syntaxhighlight lang=wml>
 
[animation]
 
[animation]
Line 264: Line 264:
 
|A [[#event-leading|leading]] animation.
 
|A [[#event-leading|leading]] animation.
 
|- style="vertical-align:top;"
 
|- style="vertical-align:top;"
! {{anchor|short-recruit_anim|[recruit_anim]}}
+
! {{anchor|short-recruit|[recruit_anim]}}
 
| <syntaxhighlight lang=wml>
 
| <syntaxhighlight lang=wml>
 
[animation]
 
[animation]
Line 273: Line 273:
 
|A [[#event-recruited|recruited]] animation.
 
|A [[#event-recruited|recruited]] animation.
 
|- style="vertical-align:top;"
 
|- style="vertical-align:top;"
! {{anchor|short-recruiting_anim|[recruiting_anim]}}
+
! {{anchor|short-recruiting|[recruiting_anim]}}
 
| <syntaxhighlight lang=wml>
 
| <syntaxhighlight lang=wml>
 
[animation]
 
[animation]
Line 282: Line 282:
 
|A [[#event-recruiting|recruiting]] animation.
 
|A [[#event-recruiting|recruiting]] animation.
 
|- style="vertical-align:top;"
 
|- style="vertical-align:top;"
! {{anchor|short-standing_anim|[standing_anim]}}
+
! {{anchor|short-standing|[standing_anim]}}
 
| <syntaxhighlight lang=wml>
 
| <syntaxhighlight lang=wml>
 
[animation]
 
[animation]
Line 291: Line 291:
 
| This animation will be used to build [[#event-default|default]] animations in addition to being used as the [[#event-standing|standing]] animation.
 
| 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;"
 
|- style="vertical-align:top;"
! {{anchor|short-idle_anim|[idle_anim]}}
+
! {{anchor|short-idle|[idle_anim]}}
 
| <syntaxhighlight lang=wml>
 
| <syntaxhighlight lang=wml>
 
[animation]
 
[animation]

Revision as of 06:34, 26 February 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

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. Any filtering flags in these blocks will replace the toplevel filters. 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:200.png
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 becomes “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.

Animation Events

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]).

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.

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