CutsceneWML

From The Battle for Wesnoth Wiki
Revision as of 06:39, 29 September 2009 by Solsword (talk | contribs) (Initial copy of the page macros)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.

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