Difference between revisions of "AnimationWML"
(→recruiting) |
m (→Progressive strings) |
||
(82 intermediate revisions by 19 users not shown) | |||
Line 1: | Line 1: | ||
{{WML Tags}} | {{WML Tags}} | ||
− | |||
== 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 | + | * 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 | + | 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 20: | 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. | |
=== Frames === | === Frames === | ||
− | An animation is cut | + | An animation is cut into one or more sequences of frames. |
− | A frame typically describes an image, where | + | 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 | ||
=== 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 | + | 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 | + | 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. | + | 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 47: | 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. | ||
− | ==== | + | === 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. | |
− | [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 [[#Selection Algorithm|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 === | |
+ | |||
+ | ; {{anchor|event-standing|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. | ||
+ | |||
+ | ; {{anchor|event-selected|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: | ||
+ | :* <code>blend_ratio="0.0~0.3:100,0.3~0.0:200"</code> | ||
+ | :* <code>blend_color=255,255,255</code> | ||
+ | |||
+ | ; {{anchor|event-recruited|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: | ||
+ | :* <code>alpha=0~1:600</code> | ||
+ | |||
+ | ; {{anchor|event-recruiting|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. | ||
+ | |||
+ | ; {{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> | ||
+ | |||
+ | ; {{anchor|event-post_teleport|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: | ||
+ | :* <code>blend_ratio="0~1:200"</code> | ||
+ | :* <code>blend_color=255,255,255</code> | ||
+ | |||
+ | ; {{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. | ||
+ | |||
+ | ; {{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> | ||
+ | |||
+ | ; {{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> | ||
+ | |||
+ | ; {{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. | ||
+ | |||
+ | ; {{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 | ||
+ | |||
+ | ; {{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. | ||
+ | |||
+ | ; {{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. | ||
+ | |||
+ | ; {{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. | ||
− | + | ; {{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> | ||
− | + | ; {{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. | ||
− | + | ; {{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. | ||
− | + | ; {{anchor|event-draw_weapon|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 | ||
− | a | + | ; {{anchor|event-sheath_weapon|sheath_weapon}} |
+ | : This animation is played after a fight simultaneously for all surviving units. No default is provided by the engine. | ||
− | + | ; {{anchor|event-default|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 [[InterfaceActionsWML#.5Banimate_unit.5D|WML events]] or from [[LuaAPI/wesnoth/units#wesnoth.units.create_animator|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. | |
− | Within a unit | ||
− | Let's take an example. Suppose we have a unit which has the skirmisher ability. We have the | + | 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 normal walking animation | ||
* a swimming animation when the unit is in water | * a swimming animation when the unit is in water | ||
− | * a | + | * 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 | + | 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 | + | 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 | + | * Drop all animations with a criterion that fails on the current situation |
− | * Take the animations that have the most | + | * 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 | ||
− | * If all animations have been | + | * If all animations have been dropped, the engine will provide a smart default. |
− | here is a pseudo-code | + | here is a pseudo-code explanation of this algorithm: |
foreach animation | foreach animation | ||
Line 250: | 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) | ||
− | == | + | == 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 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. | ||
− | + | === 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 ==== | |
− | ==== | ||
− | |||
− | ''value | + | * '''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. | ||
− | + | ==== 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: | |
− | This | + | # 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. | ||
− | + | 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): |
− | if no | + | <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> | ||
− | + | 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: | |
− | + | <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> | ||
− | + | ==== 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. | |
− | ==== | + | {|class="wikitable mw-collapsible" style="table-layout: fixed; width: 100%;" |
− | This animation | + | !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. | ||
− | + | 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. | ||
+ | |} | ||
− | + | === 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. | |
− | This | ||
− | + | ==== 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 <code>1~5</code> – two numbers separated by a tilde. Reverse ranges like <code>5~1</code> are also permitted. | |
− | |||
− | + | 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. | |
− | + | A typical example would be a unit progressively fading out, becoming transparent. To do that, you could use: | |
− | + | <syntaxhighlight lang=wml> | |
− | + | [frame] | |
− | + | alpha=1~0 | |
+ | [/frame] | ||
+ | </syntaxhighlight> | ||
− | + | 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: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | <syntaxhighlight lang=wml> | |
+ | [frame] | ||
+ | alpha=1~0:300,0:300,0~1:300 | ||
+ | [/frame] | ||
+ | </syntaxhighlight> | ||
− | + | You could also specify it like this: | |
− | |||
− | |||
− | + | <syntaxhighlight lang=wml> | |
+ | [frame] | ||
+ | alpha=1~0,0,0~1 | ||
+ | [/frame] | ||
+ | </syntaxhighlight> | ||
− | + | 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: | |
− | + | {|class=wikitable valign=top | |
+ | ! This expression… !! …is equivalent to this… | ||
+ | |- | ||
+ | | <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> | ||
− | + | 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: | |
− | |||
− | |||
− | |||
− | '' | + | * 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”. | ||
− | '' | + | 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>. |
− | + | 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”. | |
− | |||
− | |||
− | '' | + | 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”. |
− | '' | + | 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 <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., | |
− | + | * “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 {{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”. |
− | + | 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. | |
+ | 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. | ||
− | + | 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). | |
− | |||
− | + | 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 <code>element[1~5]-subelement[3,2,1~3].png</code> would expand to the following list of values: | |
− | + | * <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> | ||
− | + | In particular, as you can see, it is ''not'' computed using a Cartesian product of all the square brackets. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | '' | ||
− | |||
− | '' | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | == Example == | ||
+ | <syntaxhighlight lang=wml highlight=5,11,29> | ||
+ | [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] | [if] | ||
− | hits= | + | hits=yes |
− | [ | + | [sound_frame] |
− | + | sound=bow.ogg | |
− | + | [/sound_frame] | |
− | |||
− | |||
− | [/ | ||
[/if] | [/if] | ||
[else] | [else] | ||
− | hits= | + | hits=no |
− | [ | + | [sound_frame] |
− | + | sound=bow-miss.ogg | |
− | + | [/sound_frame] | |
− | |||
− | |||
− | [/ | ||
[/else] | [/else] | ||
− | + | [/attack_anim] | |
− | + | </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. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== See Also == | == See Also == | ||
Line 619: | Line 778: | ||
* [[UnitTypeWML]] | * [[UnitTypeWML]] | ||
* [[ReferenceWML]] | * [[ReferenceWML]] | ||
− | + | * [[Advanced Animation Tutorial]] | |
[[Category: WML Reference]] | [[Category: WML Reference]] |
Latest revision as of 16:53, 11 July 2024
Contents
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.
- Units stay exactly 200ms in each hex. so you typically want to have an offset of
- 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
- an animation for water terrain
- an animation for SE on grassland
- an animation with no criteria
- an animation for N,NE,NW,S,SW,SE
- 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:
- 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 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 [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.