CutsceneWML
From The Battle for Wesnoth Wiki
WML for cutscenes, including a fixed fake movement macro (for cases where the destination contains a unit) and macros for defining dialogue for units that activates when a "main character" stops south of them.
Contents
- 1 Fake Movement
- 2 Move to a point and then go to the recall list
- 3 Give a unit unlimited movement
- 4 Set up dialogue-based interaction
- 5 Set the main character
- 6 Set dialogue for a unit
- 7 Set dialogue for a unit, including a two-option question
- 8 Clear unit dialogue
- 9 Assign a role to a recalled unit with various back-ups
- 10 Assign a group of roles to several units with backups
- 11 See Also
Fake Movement
#define MOVE_TO FILTER X Y CONDITION
# Moves a unit to an empty space near (X, Y) (or to (X, Y) if possible).
# Ignores zones of control. Just like MOVE_UNIT, except doesn't glitch the
# movement animation when X,Y is occupied. The destination will satisfy
# CONDITION (a standard location filter), even if (X, Y) doesn't.
[store_unit]
variable=move_to_units
[filter]
{FILTER}
[and]
[not]
x=recall
[/not]
[/and]
[/filter]
[/store_unit]
{FOREACH move_to_units move_to_units_index}
[set_variable]
name=move_to_id
to_variable=move_to_units[$move_to_units_index].id
[/set_variable]
[store_unit]
variable=move_to_unit
[filter]
id=$move_to_id
[/filter]
[/store_unit]
# Get the starting x and y values:
[set_variable]
name=start_x
to_variable=move_to_unit.x
[/set_variable]
[set_variable]
name=start_y
to_variable=move_to_unit.y
[/set_variable]
# Find an open space near the destination:
{FIND_NEARBY ([not]
[filter]
[/filter]
[/not]
[and]
{CONDITION}
[/and]) {X} {Y} 30}
# Set real coordinates and end position:
[set_variable]
name=move_to_unit.x
to_variable=nearby_locations[0].x
[/set_variable]
[set_variable]
name=move_to_unit.y
to_variable=nearby_locations[0].y
[/set_variable]
[clear_variable]
name=nearby_locations
[/clear_variable]
[hide_unit]
x,y=move_to_unit.x,move_to_unit.y
[/hide_unit]
[set_variable]
name=end_x
to_variable=move_to_unit.x
[/set_variable]
[set_variable]
name=end_y
to_variable=move_to_unit.y
[/set_variable]
# Determine new facing for the unit:
[set_variable]
name=move_to_unit.facing
format=$(if(start_x < end_x, se, sw))
[/set_variable]
# Scroll to the old location:
[scroll_to]
x=$start_x
y=$start_y
[/scroll_to]
# Erase the old unit:
[kill]
id=$move_to_id
animate=no
fire_event=no
[/kill]
# Move the fake unit:
[move_unit_fake]
type=$move_to_unit.type
gender=$move_to_unit.gender
variation=$move_to_unit.variation
side=$move_to_unit.side
x=$start_x,$end_x
y=$start_y,$end_y
[/move_unit_fake]
# Place the real unit:
[unstore_unit]
variable=move_to_unit
find_vacant=yes
[/unstore_unit]
[redraw]
[/redraw]
{NEXT move_to_units_index}
[clear_variable]
name=move_to_units, move_to_unit, move_to_id, start_x, start_y, end_x, end_y
[/clear_variable]
#enddef
Move to a point and then go to the recall list
#define EXIT_STAGE_RECALL FILTER X Y
# Moves a unit matching the filter to X,Y, at which point it takes them
# off of the map and puts them on the recall list. Doesn't work for more
# than one unit at once.
{MOVE_TO {FILTER} {X} {Y} ()}
[store_unit]
variable=exit_stage_recall_unit
kill=yes
[filter]
{FILTER}
[/filter]
[/store_unit]
[unstore_unit]
variable=exit_stage_recall_unit
x,y=recall,recall
[/unstore_unit]
[clear_variable]
name=exit_stage_recall_unit
[/clear_variable]
#enddef
Give a unit unlimited movement
#define SET_UNLIMITED_MOVEMENT FILTER
# Useful for story-only situations, this macro defines an event that
# replenishes a unit's movement whenever that unit moves.
[event]
name=moveto
first_time_only=no
[filter]
{FILTER}
[/filter]
[store_unit]
kill=no
variable=temp
[filter]
{FILTER}
[/filter]
[/store_unit]
[set_variable]
name=temp.moves
to_variable=temp.max_moves
[/set_variable]
[unstore_unit]
find_vacant=no
variable=temp
[/unstore_unit]
[clear_variable]
name=temp
[/clear_variable]
[/event]
#enddef
Set up dialogue-based interaction
#define ENABLE_INTERACTION
# Sets up events to handle unit interaction. See the SET_MAIN_CHARACTER and
# UNIT_MESSAGE macros...
[event]
name=trigger_dialogue
first_time_only=no
[switch]
variable=second_unit.variables.dialogue.type
[case]
value=message
{M id=$second_unit.id $second_unit.variables.dialogue.message}
[if]
[variable]
name=second_unit.variables.dialogue.response
not_equals=$null
[/variable]
[then]
{M id=$unit.id $second_unit.variables.dialogue.response}
[/then]
[/if]
[if]
[variable]
name=second_unit.variables.dialogue.event
not_equals=$null
[/variable]
[then]
[fire_event]
name=$second_unit.variables.dialogue.event
[primary_unit]
id=$unit.id
[/primary_unit]
[secondary_unit]
id=$second_unit.id
[/secondary_unit]
[/fire_event]
[/then]
[/if]
[/case]
[case]
value=option
[message]
id=$second_unit.id
message=$second_unit.variables.dialogue.message
[option]
message=$second_unit.variables.dialogue.option1
[command]
[if]
[variable]
name=second_unit.variables.dialogue.event1
not_equals=$null
[/variable]
[then]
[fire_event]
name=$second_unit.variables.dialogue.event1
[primary_unit]
id=$unit.id
[/primary_unit]
[secondary_unit]
id=$second_unit.id
[/secondary_unit]
[/fire_event]
[/then]
[/if]
[/command]
[/option]
[option]
message=$second_unit.variables.dialogue.option2
[command]
[if]
[variable]
name=second_unit.variables.dialogue.event2
not_equals=$null
[/variable]
[then]
[fire_event]
name=$second_unit.variables.dialogue.event2
[primary_unit]
id=$unit.id
[/primary_unit]
[secondary_unit]
id=$second_unit.id
[/secondary_unit]
[/fire_event]
[/then]
[/if]
[/command]
[/option]
[/message]
[/case]
# Note: silent failure on unknown type is intentional.
[/switch]
[/event]
#enddef
Set the main character
#define SET_MAIN_CHARACTER FILTER
# Sets the filtered unit as the "main character" allowing them to talk to
# units that have an appropriate 'dialogue' variable. Interaction is
# triggered when the filtered unit moves to a space drectly south of a unit
# with dialogue. Note that the ENABLE_INTERACTION macro must be present in
# order for this macro to work. See also the UNIT_MESSAGE and UNIT_OPTION
# macros.
[event]
name=moveto
first_time_only=no
[filter]
{FILTER}
[and]
[filter_adjacent]
count=1
adjacent=n
[variable]
name=this_unit.variables.dialogue.type
not_equals=$null
[/variable]
[/filter_adjacent]
[/and]
[/filter]
[fire_event]
name=trigger_dialogue
[primary_unit]
{FILTER}
[/primary_unit]
[secondary_unit]
[filter_adjacent]
count=1
adjacent=s
{FILTER}
[/filter_adjacent]
[/secondary_unit]
[/fire_event]
[/event]
#enddef
Set dialogue for a unit
#define UNIT_MESSAGE MESSAGE RESPONSE EVENT
# Defines dialogue for a unit. When the main character (see the
# SET_MAIN_CHARACTER macro) triggers interaction with this unit, the
# message MESSAGE will be displayed, and the triggering unit will respond
# with RESPONSE, after which EVENT will be fired with the triggering unit
# as the primary unit and this unit as the secondary unit. Note that this
# macro should be placed within a [variables] tag inside of a [unit] tag.
[dialogue]
type=message
message={MESSAGE}
response={RESPONSE}
event={EVENT}
[/dialogue]
#enddef
#define SET_UNIT_MESSAGE FILTER MESSAGE RESPONSE EVENT
# Works just like the UNIT_MESSAGE macro, except that it takes a FILTER
# argument and applies the message to the all units matching the filter.
# UNIT_MESSAGE can only be used during unit creation, and must be placed
# within a [variables] tag. SET_UNIT_MESSAGE, on the other hand, can be
# used anywhere.
{SET_PROPERTY ({FILTER}) (variables.dialogue.type) ("message")}
{SET_PROPERTY ({FILTER}) (variables.dialogue.message) ({MESSAGE})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.response) ({RESPONSE})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.event) ({EVENT})}
#enddef
Set dialogue for a unit, including a two-option question
#define UNIT_OPTION MESSAGE OPTION1 OPTION2 EVENT1 EVENT2
# Defines dialogue for a unit that includes a choice to present to the
# player. When the main character (see the SET_MAIN_CHARACTER macro)
# triggers interaction with this unit, the message MESSAGE will be
# displayed, along with the options OPTION1 and OPTION2. Depending on which
# option the player chooses, either EVENT1 or EVENT2 will then be fired
# with the triggering unit as the primary unit and this unit as the
# secondary unit. Note that this macro should be placed within a
# [variables] tag inside of a [unit] tag.
[dialogue]
type=option
message={MESSAGE}
option1={OPTION1}
option2={OPTION2}
event1={EVENT1}
event2={EVENT2}
[/dialogue]
#enddef
#define SET_UNIT_OPTION FILTER MESSAGE OPTION1 OPTION2 EVENT1 EVENT2
# Works just like the UNIT_OPTION macro, except that it takes a FILTER
# argument and applies the message to all units matching the filter.
# UNIT_OPTION can only be used during unit creation, and must be placed
# within a [variables] tag. SET_UNIT_OPTION, on the other hand, can be
# used anywhere.
{SET_PROPERTY ({FILTER}) (variables.dialogue.type) ("option")}
{SET_PROPERTY ({FILTER}) (variables.dialogue.message) ({MESSAGE})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.option1) ({OPTION1})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.option2) ({OPTION2})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.event1) ({EVENT1})}
{SET_PROPERTY ({FILTER}) (variables.dialogue.event2) ({EVENT2})}
#enddef
Clear unit dialogue
#define CLEAR_UNIT_DIALOGUE FILTER
# Removes any dialogue that is assigned to units matching the given filter,
# including both messages and options.
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.type)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.message)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.response)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.option1)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.option2)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.event)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.event1)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue.event2)}
{CLEAR_PROPERTY ({FILTER}) (variables.dialogue)}
#enddef
Assign a role to a recalled unit with various back-ups
#define FILL_ROLE_RECALL SIDE ROLE FILTER BACKUP_FILTER TYPE X Y
# Recalls the first unit on side SIDE matching FILTER and assigns it the
# role ROLE. If no such unit exists, it tries to find a unit that matches
# the given BACKUP_FILTER on SIDE's recall list, and if it can't, it tries
# to find any unit on side SIDE's recall list. If SIDE has no units on its
# recall list, a unit is spawned of type TYPE to fill the role. If the role
# is already filled, this does nothing. If the given location is
# unavailable, the nearest empty location will be used.
[if]
[have_unit]
side={SIDE}
x,y={X},{Y}
[/have_unit]
[then]
{FIND_NEARBY ([not]
[filter]
[/filter]
[/not]) {X} {Y} 30}
[set_variable]
name=fill_role_recall_target_x
to_variable=nearby_locations[0].x
[/set_variable]
[set_variable]
name=fill_role_recall_target_y
to_variable=nearby_locations[0].y
[/set_variable]
[clear_variable]
name=nearby_locations
[/clear_variable]
[/then]
[else]
[set_variable]
name=fill_role_recall_target_x
value={X}
[/set_variable]
[set_variable]
name=fill_role_recall_target_y
value={Y}
[/set_variable]
[/else]
[/if]
[if]
[not]
[have_unit]
side={SIDE}
role={ROLE}
[/have_unit]
[/not]
[then]
[recall]
show=no
side={SIDE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
{FILTER}
[/recall]
[role]
role={ROLE}
side={SIDE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
{FILTER}
[/role]
[/then]
[/if]
[if]
[not]
[have_unit]
side={SIDE}
role={ROLE}
[/have_unit]
[/not]
[then]
[recall]
show=no
side={SIDE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
{BACKUP_FILTER}
[/recall]
[role]
role={ROLE}
side={SIDE}
{BACKUP_FILTER}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
[/role]
[/then]
[/if]
[if]
[not]
[have_unit]
side={SIDE}
role={ROLE}
[/have_unit]
[/not]
[then]
[recall]
show=no
side={SIDE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
[/recall]
[role]
role={ROLE}
side={SIDE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
[/role]
[/then]
[/if]
[if]
[not]
[have_unit]
side={SIDE}
role={ROLE}
[/have_unit]
[/not]
[then]
[unit]
side={SIDE}
type={TYPE}
x,y=$fill_role_recall_target_x,$fill_role_recall_target_y
generate_name=yes
random_gender=yes
random_traits=yes
role={ROLE}
[/unit]
[/then]
[/if]
[clear_variable]
name=fill_role_recall_target_x,fill_role_recall_target_y
[/clear_variable]
#enddef
Assign a group of roles to several units with backups
#define ASSIGN_ROLES ROLES DESIRED_ROLE_FILTER BACKUP_ROLE_FILTER
# Fills several roles at once from the pool of units specified by
# DESIRED_ROLE_FILTER, using units that match BACKUP_ROLE_FILTER if
# necessary. ROLES should be an ordered, comma-separated list of roles to
# fill. If not enough units match among both the DESIRED and BACKUP
# filters, not all of the roles will be assigned. The main advantage of
# ASSIGN_ROLES over [role] tags is that you don't have to use a bunch of
# [not] role= [/not] statements to ensure that each unit gets one role.
[set_variables]
name=roles_to_assign
[split]
list={ROLES}
key=value
separator=,
remove_empty=yes
[/split]
[/set_variables]
[set_variable]
name=role_assignment_index
value=0
[/set_variable]
[set_variable]
name=done_assigning_roles
value=no
[/set_variable]
[store_unit]
variable=primary_role_candidates
[filter]
{DESIRED_ROLE_FILTER}
[/filter]
[/store_unit]
[set_variable]
name=role_candidate_index
value=0
[/set_variable]
[while]
[variable]
name=done_assigning_roles
boolean_equals=no
[/variable]
[and]
[variable]
name=role_candidate_index
less_than=$primary_role_candidates.length
[/variable]
[/and]
[do]
# Assign a role:
[role]
role=$roles_to_assign[$($role_assignment_index)].value
id=$primary_role_candidates[$($role_candidate_index)].id
[/role]
# Increment loop variables:
[set_variable]
name=role_assignment_index
add=1
[/set_variable]
[set_variable]
name=role_candidate_index
add=1
[/set_variable]
# Check the break condition:
[if]
[not]
[variable]
name=role_assignment_index
less_than=$roles_to_assign.length
[/variable]
[/not]
[then]
[set_variable]
name=done_assigning_roles
value=yes
[/set_variable]
[/then]
[/if]
[/do]
[/while]
# Use backups if necessary:
[if]
[variable]
name=done_assigning_roles
boolean_equals=no
[/variable]
[then]
[store_unit]
variable=secondary_role_candidates
[filter]
{BACKUP_ROLE_FILTER}
[/filter]
[/store_unit]
[set_variable]
name=role_candidate_index
value=0
[/set_variable]
[while]
[variable]
name=done_assigning_roles
boolean_equals=no
[/variable]
[and]
[variable]
name=role_candidate_index
less_than=$secondary_role_candidates.length
[/variable]
[/and]
[do]
# Assign a role:
[role]
role=$roles_to_assign[$($role_assignment_index)].value
id=$secondary_role_candidates[$($role_candidate_index)].id
[/role]
# Increment loop variables:
[set_variable]
name=role_assignment_index
add=1
[/set_variable]
[set_variable]
name=role_candidate_index
add=1
[/set_variable]
# Check the break condition:
[if]
[not]
[variable]
name=role_assignment_index
less_than=$roles_to_assign.length
[/variable]
[/not]
[then]
[set_variable]
name=done_assigning_roles
value=yes
[/set_variable]
[/then]
[/if]
[/do]
[/while]
[/then]
[/if]
[clear_variable]
name=roles_to_assign, role_assignment_index, primary_role_candidates, secondary_role_candidates, role_candidate_index, done_assigning_roles
[/clear_variable]
#enddef
See Also
This page was last edited on 29 September 2009, at 06:50.