Difference between revisions of "WML Utilities"

From The Battle for Wesnoth Wiki
m (Adding unit overlays with a filter instead of (x,y))
m (Determining opposite coordinates: Suggest using SLF's direction support instead)
 
(15 intermediate revisions by 7 users not shown)
Line 1: Line 1:
== General shortcuts ==
+
== Determining opposite coordinates ==
 
+
'''Obsolete?''' the Standard Location Filter now supports [[StandardLocationFilter#Directions|directions]], which might be able to replace any uses of this utility.
# MODIFY_UNIT alters a unit variable (such as unit.x, unit.type,
 
# unit.side), handling all the storing and unstoring.
 
#
 
# Example that flips all spearmen to side 2:
 
# {MODIFY_UNIT type=Spearman side 2}<br>
 
#define MODIFY_UNIT FILTER VAR VALUE
 
    [store_unit]
 
        [filter]
 
            {FILTER}
 
        [/filter]<br>
 
        variable=MODIFY_UNIT_store
 
        kill=yes
 
    [/store_unit]<br>
 
    {FOREACH MODIFY_UNIT_store MODIFY_UNIT_i}
 
        [set_variable]
 
            name=MODIFY_UNIT_store[$MODIFY_UNIT_i].{VAR}
 
            value={VALUE}
 
        [/set_variable]<br>
 
        [unstore_unit]
 
            variable=MODIFY_UNIT_store[$MODIFY_UNIT_i]
 
            find_vacant=no
 
        [/unstore_unit]
 
    {NEXT MODIFY_UNIT_i}<br>
 
    {CLEAR_VARIABLE MODIFY_UNIT_store}
 
#enddef
 
 
 
# Shortcut for IF statements testing the value of a variable. You still need to
 
# write [then] and [else] tags manually, though.
 
#
 
# You can use it like this:
 
#
 
# {IF some_variable equals yes (
 
#    [then]
 
#        # something
 
#    [/then]
 
#
 
#    [else]
 
#        # something else
 
#    [/else]
 
# )}<br>
 
#define IF VAR OP VAL CONTENTS
 
    [if]
 
        [variable]
 
            name={VAR}
 
            {OP}={VAL}
 
        [/variable]<br>
 
        {CONTENTS}
 
    [/if]
 
#enddef
 
 
 
# Just a variation of the above, testing [have_unit] instead of [variable].<br>
 
#define IF_HAVE_UNIT FILTER CONTENTS
 
    [if]
 
        [have_unit]
 
            {FILTER}
 
        [/have_unit]<br>
 
        {CONTENTS}
 
    [/if]
 
#enddef
 
 
 
# Stores an attribute of a unit to the given variable.
 
#
 
# Example that flips all orcs to whatever side James is on:
 
#
 
# {STORE_UNIT_VAR description=James side side_of_James}
 
# {MODIFY_UNIT race=orc side $side_of_James}<br>
 
#define STORE_UNIT_VAR FILTER VAR TO_VAR
 
    [store_unit]
 
        [filter]
 
            {FILTER}
 
        [/filter]<br>
 
        kill=no
 
        variable=STORE_UNIT_VAR_store
 
    [/store_unit]<br>
 
    {VARIABLE_OP {TO_VAR} format $STORE_UNIT_VAR_store.{VAR}}<br>
 
    {CLEAR_VARIABLE STORE_UNIT_VAR_store}
 
#enddef
 
 
 
# Moves a unit from its current location to the given location along a
 
# relatively straight line displaying the movement just like [move_unit_fake]
 
# does.
 
#
 
# Note that setting the destination on an existing unit does not kill either
 
# one, but causes the unit to move to the nearest vacant hex instead.
 
 
#define MOVE_UNIT FILTER TO_X TO_Y
 
    [store_unit]
 
        [filter]
 
            {FILTER}
 
        [/filter]<br>
 
        variable=MOVE_UNIT_temp
 
        kill=no
 
    [/store_unit]<br>
 
    [scroll_to]
 
        x=$MOVE_UNIT_temp.x
 
        y=$MOVE_UNIT_temp.y
 
    [/scroll_to]<br>
 
    [hide_unit]
 
        x=$MOVE_UNIT_temp.x
 
        y=$MOVE_UNIT_temp.y
 
    [/hide_unit]<br>
 
    {VARIABLE_OP x_coords format ("$MOVE_UNIT_temp.x|,{TO_X}")}
 
    {VARIABLE_OP y_coords format ("$MOVE_UNIT_temp.y|,{TO_Y}")}<br>
 
    [move_unit_fake]
 
        type=$MOVE_UNIT_temp.type
 
        x=$x_coords
 
        y=$y_coords
 
    [/move_unit_fake]<br>
 
    [teleport]
 
        [filter]
 
            {FILTER}
 
        [/filter]<br>
 
        x,y={TO_X},{TO_Y}
 
    [/teleport]<br>
 
    [unhide_unit][/unhide_unit]<br>
 
    [redraw][/redraw]
 
#enddef
 
 
 
# This is a way to check whether or not the terrain in the given coordinates
 
# is of the given type or types. Might be useful, since filtering by terrain
 
# isn't possible directly.
 
#
 
# You can use it for example like this:
 
#
 
# [event]
 
#    name=moveto
 
#    first_time_only=no
 
#
 
#    {IF_TERRAIN $x1 $y1 gfm (
 
#        [then]
 
#            {DEBUG_MSG "Stepped on grassland, forest or mountains!"}
 
#        [/then]
 
#    )}
 
# [/event]<br>
 
#define IF_TERRAIN X Y TYPES CONTENTS
 
    [store_locations]
 
        x={X}
 
        y={Y}
 
        terrain={TYPES}
 
        variable=IF_TERRAIN_temp
 
    [/store_locations]<br>
 
    [if]
 
        [variable]
 
            name=IF_TERRAIN_temp.length
 
            not_equals=0
 
        [/variable]<br>
 
        {CONTENTS}
 
    [/if]<br>
 
    {CLEAR_VARIABLE IF_TERRAIN_temp}
 
#enddef
 
 
 
# You can iterate through a range of numbers with this macro. The CONTENTS
 
# are repeated with every iteration, and you can use the VAR variable to
 
# insert the number of the current step into each iteration. Note that
 
# when using this, you must iterate from a smaller number to the bigger
 
# number, because the increment is always 1.
 
#
 
# Example that spawns a row of skeletons into the coordinates (4,5),
 
# (5,5), (6,5), (7,5), (8,5) and (9,5):
 
#
 
# {ITERATE 4 9 i (
 
#    [unit]
 
#        type=Skeleton
 
#        x=$i
 
#        y=5
 
#    [/unit]
 
# )}<br>
 
#define ITERATE FROM TO VAR CONTENTS
 
    {VARIABLE {VAR} {FROM}}<br>
 
    [while]
 
        [variable]
 
            name={VAR}
 
            less_than_equal_to={TO}
 
        [/variable]<br>
 
        [do]
 
            {CONTENTS}<br>
 
            {VARIABLE_OP {VAR} add 1}
 
        [/do]
 
    [/while]
 
#enddef
 
 
 
== More uncommon tasks ==
 
 
 
=== Adding unit overlays with a filter instead of (x,y) ===
 
 
 
# UNIT_OVERLAY adds an overlay to a unit, taking in a standard filter
 
#
 
# Example that gives all spearmen a book:
 
# {UNIT_OVERLAY type=Spearman items/book1.png}
 
 
#define UNIT_OVERLAY FILTER IMG
 
    [store_unit]
 
        [filter]
 
            {FILTER}
 
        [/filter]
 
        variable=UNIT_OVERLAY_store
 
        kill=no
 
    [/store_unit]
 
    {FOREACH UNIT_OVERLAY_store UNIT_OVERLAY_i}
 
{VARIABLE_OP UNIT_OVERLAY_tempx format $UNIT_OVERLAY_store[$UNIT_OVERLAY_i].x}
 
{VARIABLE_OP UNIT_OVERLAY_tempy format $UNIT_OVERLAY_store[$UNIT_OVERLAY_i].y}
 
        [unit_overlay]
 
            x=$UNIT_OVERLAY_tempx
 
            y=$UNIT_OVERLAY_tempy
 
image={IMG}
 
        [/unit_overlay]
 
    {NEXT UNIT_OVERLAY_i}
 
    {CLEAR_VARIABLE UNIT_OVERLAY_store}
 
#enddef
 
 
 
=== Returning a unit to the recall list ===
 
 
 
# This places a given unit back to the recall list of the side it is on.
 
# Note however, that the unit is not healed to full health, so when
 
# recalled (even if not until the next scenario) the unit may have less
 
# than his maximum hp left.
 
#
 
# An example, that returns all units stepping on (20,38) back to the recall
 
# list:
 
#
 
# [event]
 
#    name=moveto
 
#
 
#    [filter]
 
#        x,y=20,38
 
#    [/filter]
 
#
 
#    {PUT_TO_RECALL_LIST x,y=20,38}
 
# [/event]<br>
 
#define PUT_TO_RECALL_LIST FILTER
 
    [store_unit]
 
        [filter]
 
            {FILTER}
 
        [/filter]<br>
 
        variable=temp
 
        kill=yes
 
    [/store_unit]<br>
 
    {FOREACH temp i}
 
        {VARIABLE temp[$i].x "recall"}
 
        {VARIABLE temp[$i].y "recall"}<br>
 
        [unstore_unit]
 
            variable=temp[$i]
 
            find_vacant=no
 
        [/unstore_unit]
 
    {NEXT i}
 
#enddef
 
 
 
=== Determining opposite coordinates ===
 
  
 
  # Using this, you can determine the coordinates on the "opposite side" of a
 
  # Using this, you can determine the coordinates on the "opposite side" of a
Line 268: Line 20:
 
  #define OPPOSITE_SIDE CENTER_X CENTER_Y X Y VAR
 
  #define OPPOSITE_SIDE CENTER_X CENTER_Y X Y VAR
 
     {VARIABLE x_odd {X}}<br>
 
     {VARIABLE x_odd {X}}<br>
     {VARIABLE_OP x_odd multiply 0.5}
+
     {VARIABLE_OP x_odd modulo 2}<br>
    {VARIABLE_OP x_odd multiply 2}<br>
 
 
     {VARIABLE c_x {CENTER_X}}
 
     {VARIABLE c_x {CENTER_X}}
 
     {VARIABLE c_y {CENTER_Y}}
 
     {VARIABLE c_y {CENTER_Y}}
Line 276: Line 27:
 
     {VARIABLE result_x {CENTER_X}}
 
     {VARIABLE result_x {CENTER_X}}
 
     {VARIABLE result_y {CENTER_Y}}<br>
 
     {VARIABLE result_y {CENTER_Y}}<br>
     {IF s_x greater_than $c_x (
+
     {IF_VAR s_x greater_than $c_x (
 
         [then]
 
         [then]
 
             {VARIABLE_OP result_x add -1}
 
             {VARIABLE_OP result_x add -1}
 
         [/then]
 
         [/then]
 
     )}<br>
 
     )}<br>
     {IF s_x less_than $c_x (
+
     {IF_VAR s_x less_than $c_x (
 
         [then]
 
         [then]
 
             {VARIABLE_OP result_x add 1}
 
             {VARIABLE_OP result_x add 1}
 
         [/then]
 
         [/then]
 
     )}<br>
 
     )}<br>
     {IF s_x equals $c_x (
+
     {IF_VAR s_x equals $c_x (
 
         [then]
 
         [then]
             {IF s_y less_than $c_y (
+
             {IF_VAR s_y less_than $c_y (
 
                 [then]
 
                 [then]
 
                     {VARIABLE_OP result_y add 1}
 
                     {VARIABLE_OP result_y add 1}
 
                 [/then]
 
                 [/then]
 
             )}<br>
 
             )}<br>
             {IF s_y greater_than $c_y (
+
             {IF_VAR s_y greater_than $c_y (
 
                 [then]
 
                 [then]
 
                     {VARIABLE_OP result_y add -1}
 
                     {VARIABLE_OP result_y add -1}
Line 300: Line 51:
 
         [/then]
 
         [/then]
 
     )}<br>
 
     )}<br>
     {IF x_odd not_equals $s_x (
+
     {IF_VAR x_odd equals 1 (
 
         [then]
 
         [then]
             {IF s_y equals $c_y (
+
             {IF_VAR s_y equals $c_y (
 
                 [then]
 
                 [then]
 
                     {VARIABLE_OP result_y add 1}
 
                     {VARIABLE_OP result_y add 1}
Line 309: Line 60:
 
         [/then]<br>
 
         [/then]<br>
 
         [else]
 
         [else]
             {IF s_y equals $c_y (
+
             {IF_VAR s_y equals $c_y (
 
                 [then]
 
                 [then]
 
                     {VARIABLE_OP result_y add -1}
 
                     {VARIABLE_OP result_y add -1}
Line 315: Line 66:
 
             )}
 
             )}
 
         [/else]
 
         [/else]
     )}<br>
+
     )}
 
     {VARIABLE {VAR}.x $result_x}
 
     {VARIABLE {VAR}.x $result_x}
 
     {VARIABLE {VAR}.y $result_y}<br>
 
     {VARIABLE {VAR}.y $result_y}<br>
Line 325: Line 76:
 
     {CLEAR_VARIABLE result_y}
 
     {CLEAR_VARIABLE result_y}
 
     {CLEAR_VARIABLE x_odd}
 
     {CLEAR_VARIABLE x_odd}
 +
#enddef
 +
 +
== Find nearest hex(es) ==
 +
 +
#define FIND_NEARBY FILTER X Y LIMIT
 +
    # Does a search for a nearby location that matches the given filter.
 +
    # Basically just looks for such a location with increasing radius until it
 +
    # finds at least one. This is sadly inefficient, but implementing BFS in
 +
    # WML is... difficult. Once LIMIT is reached, the entire map is searched.
 +
    # This macro creates the 'nearby_locations' and 'nearby_distance'
 +
    # variables, which can be used to access a list of locations found and the
 +
    # distance to those locations, respectively. They should eventually be
 +
    # cleared, which can be accomplished using the CLEANUP_SEARCH macro.
 +
    [clear_variable]
 +
        name=nearby_locations
 +
    [/clear_variable]
 +
    [set_variable]
 +
        name=nearby_distance
 +
        value=0
 +
    [/set_variable]
 +
    [while]
 +
        [not]
 +
            [variable]
 +
                name=nearby_locations.length
 +
                greater_than=0
 +
            [/variable]
 +
        [/not]
 +
        [and]
 +
            [variable]
 +
                name=nearby_distance
 +
                less_than={LIMIT}
 +
            [/variable]
 +
        [/and]
 +
        [do]
 +
            {DEBUG "Searching depth $nearby_distance around ({X}, {Y})..."}
 +
            [store_locations]
 +
                variable=nearby_locations
 +
                {FILTER}
 +
                [and]
 +
                    x,y={X},{Y}
 +
                    radius=$nearby_distance
 +
                [/and]
 +
            [/store_locations]
 +
            {DEBUG "...found $nearby_locations.length locations."}
 +
            [set_variable]
 +
                name=nearby_distance
 +
                add=1
 +
            [/set_variable]
 +
        [/do]
 +
    [/while]
 +
    [if]
 +
        [variable]
 +
            name=nearby_locations.length
 +
            equals=0
 +
        [/variable]
 +
        [then]
 +
            [store_locations]
 +
                variable=nearby_locations
 +
                {FILTER}
 +
            [/store_locations]
 +
        [/then]
 +
    [/if]
 +
#enddef
 +
 +
#define CLEANUP_SEARCH
 +
    # Clears variables involved in searching (the FIND_NEARBY macro). Put this
 +
    # in your name=victory,defeat tag to clean up if you use FIND_NEARBY within
 +
    # a scenario.
 +
    [clear_variable]
 +
        name=nearby_locations, nearby_distance
 +
    [/clear_variable]
 
  #enddef
 
  #enddef
  
Line 330: Line 152:
  
 
* [[UsefulWMLFragments]]
 
* [[UsefulWMLFragments]]
 +
* [[ReferenceWML]]
 +
 +
[[Category: UsefulWMLFragments]]

Latest revision as of 21:20, 2 July 2019

Determining opposite coordinates

Obsolete? the Standard Location Filter now supports directions, which might be able to replace any uses of this utility.

# Using this, you can determine the coordinates on the "opposite side" of a
# central hex, relative to another hex adjacent to it. What this really means
# is illustrated below:
#       __            __            __
#    __/  \__      __/2 \__      __/  \__
#   /  \__/1 \    /  \__/  \    /2 \__/  \    C: central point
#   \__/C \__/    \__/C \__/    \__/C \__/    1: the hex to "mirror"
#   /2 \__/  \    /  \__/  \    /  \__/1 \    2: the result
#   \__/  \__/    \__/1 \__/    \__/  \__/
#      \__/          \__/          \__/
#
# The coordinates of the central point are given in {CENTER_X} and {CENTER_Y},
# and the coordinates of hex 1 in {X} and {Y}. The coordinates of hex 2 are
# then stored in {VAR}, which will have member variables x and y.
#
# Note that this uses the IF macro given earlier on this page.
#define OPPOSITE_SIDE CENTER_X CENTER_Y X Y VAR {VARIABLE x_odd {X}}
{VARIABLE_OP x_odd modulo 2}
{VARIABLE c_x {CENTER_X}} {VARIABLE c_y {CENTER_Y}} {VARIABLE s_x {X}} {VARIABLE s_y {Y}}
{VARIABLE result_x {CENTER_X}} {VARIABLE result_y {CENTER_Y}}
{IF_VAR s_x greater_than $c_x ( [then] {VARIABLE_OP result_x add -1} [/then] )}
{IF_VAR s_x less_than $c_x ( [then] {VARIABLE_OP result_x add 1} [/then] )}
{IF_VAR s_x equals $c_x ( [then] {IF_VAR s_y less_than $c_y ( [then] {VARIABLE_OP result_y add 1} [/then] )}
{IF_VAR s_y greater_than $c_y ( [then] {VARIABLE_OP result_y add -1} [/then] )} [/then] )}
{IF_VAR x_odd equals 1 ( [then] {IF_VAR s_y equals $c_y ( [then] {VARIABLE_OP result_y add 1} [/then] )} [/then]
[else] {IF_VAR s_y equals $c_y ( [then] {VARIABLE_OP result_y add -1} [/then] )} [/else] )} {VARIABLE {VAR}.x $result_x} {VARIABLE {VAR}.y $result_y}
{CLEAR_VARIABLE c_x} {CLEAR_VARIABLE c_y} {CLEAR_VARIABLE s_x} {CLEAR_VARIABLE s_y} {CLEAR_VARIABLE result_x} {CLEAR_VARIABLE result_y} {CLEAR_VARIABLE x_odd} #enddef

Find nearest hex(es)

#define FIND_NEARBY FILTER X Y LIMIT
    # Does a search for a nearby location that matches the given filter.
    # Basically just looks for such a location with increasing radius until it
    # finds at least one. This is sadly inefficient, but implementing BFS in
    # WML is... difficult. Once LIMIT is reached, the entire map is searched.
    # This macro creates the 'nearby_locations' and 'nearby_distance'
    # variables, which can be used to access a list of locations found and the
    # distance to those locations, respectively. They should eventually be
    # cleared, which can be accomplished using the CLEANUP_SEARCH macro.
    [clear_variable]
        name=nearby_locations
    [/clear_variable]
    [set_variable]
        name=nearby_distance
        value=0
    [/set_variable]
    [while]
        [not]
            [variable]
                name=nearby_locations.length
                greater_than=0
            [/variable]
        [/not]
        [and]
            [variable]
                name=nearby_distance
                less_than={LIMIT}
            [/variable]
        [/and]
        [do]
            {DEBUG "Searching depth $nearby_distance around ({X}, {Y})..."}
            [store_locations]
                variable=nearby_locations
                {FILTER}
                [and]
                    x,y={X},{Y}
                    radius=$nearby_distance
                [/and]
            [/store_locations]
            {DEBUG "...found $nearby_locations.length locations."}
            [set_variable]
                name=nearby_distance
                add=1
            [/set_variable]
        [/do]
    [/while]
    [if]
        [variable]
            name=nearby_locations.length
            equals=0
        [/variable]
        [then]
            [store_locations]
                variable=nearby_locations
                {FILTER}
            [/store_locations]
        [/then]
    [/if]
#enddef

#define CLEANUP_SEARCH
    # Clears variables involved in searching (the FIND_NEARBY macro). Put this
    # in your name=victory,defeat tag to clean up if you use FIND_NEARBY within
    # a scenario.
    [clear_variable]
        name=nearby_locations, nearby_distance
    [/clear_variable]
#enddef

See Also

This page was last edited on 2 July 2019, at 21:20.