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
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