#textdomain wesnoth # This file contains general utility macros for WML authors. # # Later macros in this file are built using earlier ones, which # is why they live here rather than being broken out into topic-specific files. # ! in comments is used in generating HTML documentation, ignore it otherwise. #define QUANTITY NAME EASY_VALUE NORMAL_VALUE HARD_VALUE # Macro to define a 'quantity' differently based on difficulty levels. #ifdef EASY {NAME}={EASY_VALUE} #endif #ifdef NORMAL {NAME}={NORMAL_VALUE} #endif #ifdef HARD {NAME}={HARD_VALUE} #endif #enddef #define TURNS EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT # Macro to define number of turns for different difficulty levels. {QUANTITY turns {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT}} #enddef #define GOLD EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT # Macro which will let you say {GOLD x y z} to set # starting gold depending on easy/medium/hard - x/y/z {QUANTITY gold {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT}} #enddef #define INCOME EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT # Macro which will let you say {GOLD x y z} to set # per-turn income depending on easy/medium/hard - x/y/z {QUANTITY income {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT}} #enddef #define NO_INCOME # Used to specify when a side should not have any income # every turn. income=-2 #enddef #define ATTACK_DEPTH EASY_VALUE NORMAL_VALUE HARD_VALUE # Macro to define AI attack depth for different difficulty levels # (set it to 1-6) {QUANTITY attack_depth {EASY_VALUE} {NORMAL_VALUE} {HARD_VALUE}} #enddef #define NO_SCOUTS # Macro to make an AI team not recruit scouts. villages_per_scout=0 #enddef #define RANDOM THING # Macro to quickly pick a random value (in the $random variable, to avoid # cluttering up savegames with such temporary variables). [set_variable] name=random rand={THING} [/set_variable] #enddef #define VARIABLE VAR VALUE # Macro to initialize a variable. Strictly a syntatic shortcut. [set_variable] name={VAR} value={VALUE} [/set_variable] #enddef #define VARIABLE_OP VAR OP_NAME VALUE # Macro to do mathematical operations on variables. [set_variable] name={VAR} {OP_NAME}={VALUE} [/set_variable] #enddef #define CLEAR_VARIABLE VAR # Macro to clear a variable previously set. [clear_variable] name={VAR} [/clear_variable] #enddef # wmlindent: start ignoring #define FOREACH ARRAY VAR # Macro to begin a WML clause that iterates over an array. {VARIABLE {VAR} 0} [while] [variable] name={VAR} less_than=${ARRAY}.length [/variable] [do] #enddef #define NEXT VAR # Macro to end a WML clause that iterates over an array. [set_variable] name={VAR} add=1 [/set_variable] [/do] [/while] {CLEAR_VARIABLE {VAR}} #enddef # wmlindent: stop ignoring # wmlindent: opener "{FOREACH " # wmlindent: closer "{NEXT " #define REPEAT NUMBER BODY_WML # Macro to execute some WML a defined number of times. # # Example that causes screen to quake 5 times: #! {REPEAT 5 ( #! {QUAKE "rumble.ogg"} #! )} {VARIABLE REPEAT_i 0} [while] [variable] name=REPEAT_i less_than={NUMBER} [/variable] [do] {BODY_WML} {VARIABLE_OP REPEAT_i add 1} [/do] [/while] {CLEAR_VARIABLE REPEAT_i} #enddef #define LOOKUP_INDEX FROM_ARRAY WHERE_KEY WHERE_VALUE SAVE_AS #! Call this to lookup an array element that has a particular key-value pair #! then it saves the index of that element, or #! if the key-value pair is not found it saves the array's length {VARIABLE {SAVE_AS} 0} [while] [variable] name={SAVE_AS} less_than=${FROM_ARRAY}.length [/variable] [variable] name={FROM_ARRAY}[${SAVE_AS}].{WHERE_KEY} not_equals={WHERE_VALUE} [/variable] [do] {VARIABLE_OP {SAVE_AS} add 1} [/do] [/while] #enddef #define LOOKUP_VALUE FROM_ARRAY WHERE_KEY WHERE_VALUE SAVE_KEY DEFAULT SAVE_AS #! Call this to look up an array element that has a particular key-value pair #! then it saves another key from that same element. {LOOKUP_INDEX {FROM_ARRAY} {WHERE_KEY} {WHERE_VALUE} {SAVE_AS}} [if] [variable] name={SAVE_AS} numerical_equals=${FROM_ARRAY}.length [/variable] [then] {VARIABLE {SAVE_AS} {DEFAULT}} [/then] [else] {VARIABLE {SAVE_AS} ${FROM_ARRAY}[${SAVE_AS}].{SAVE_KEY}} [/else] [/if] #enddef #define DEBUG_MSG MESSAGE # Emit a debug message. Meant to be overridden with no-op definition # of the same name for production use. [message] speaker=narrator message={MESSAGE} image=wesnoth-icon.png [/message] #enddef #define MODIFY_UNIT FILTER VAR VALUE # 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} [store_unit] [filter] {FILTER} [/filter] variable=MODIFY_UNIT_store kill=yes [/store_unit] {FOREACH MODIFY_UNIT_store MODIFY_UNIT_i} [set_variable] name=MODIFY_UNIT_store[$MODIFY_UNIT_i].{VAR} value={VALUE} [/set_variable] [unstore_unit] variable=MODIFY_UNIT_store[$MODIFY_UNIT_i] find_vacant=no [/unstore_unit] {NEXT MODIFY_UNIT_i} {CLEAR_VARIABLE MODIFY_UNIT_store} #enddef #define MOVE_UNIT_BY FILTER X_OFFSET Y_OFFSET #TODO COMMENT [store_unit] [filter] {FILTER} [/filter] variable=MOVE_UNIT_store kill=yes [/store_unit] {FOREACH MOVE_UNIT_store unit} {VARIABLE_OP MOVE_UNIT_store[$unit].x add {X_OFFSET}} {VARIABLE_OP MOVE_UNIT_store[$unit].y add {Y_OFFSET}} [unstore_unit] variable=MOVE_UNIT_store[$unit] find_vacant=no [/unstore_unit] {NEXT unit} {CLEAR_VARIABLE MOVE_UNIT_store} #enddef #define MOVE_UNIT FILTER TO_X TO_Y # 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. [store_unit] [filter] {FILTER} [/filter] variable=MOVE_UNIT_temp kill=no [/store_unit] [scroll_to] x=$MOVE_UNIT_temp.x y=$MOVE_UNIT_temp.y [/scroll_to] {VARIABLE_OP MOVE_UNIT_path_coords_x format ("$MOVE_UNIT_temp.x|,{TO_X}")} {VARIABLE_OP MOVE_UNIT_path_coords_y format ("$MOVE_UNIT_temp.y|,{TO_Y}")} [if] [variable] name=MOVE_UNIT_temp.x less_than={TO_X} [/variable] [then] {VARIABLE MOVE_UNIT_temp.facing se} [/then] [else] [if] [variable] name=MOVE_UNIT_temp.x greater_than={TO_X} [/variable] [then] {VARIABLE MOVE_UNIT_temp.facing sw} [/then] [/if] [/else] [/if] {VARIABLE MOVE_UNIT_temp.x {TO_X}} {VARIABLE MOVE_UNIT_temp.y {TO_Y}} [kill] {FILTER} animate=no fire_event=no [/kill] [move_unit_fake] type=$MOVE_UNIT_temp.type gender=$MOVE_UNIT_temp.gender variation=$MOVE_UNIT_temp.variation side=$MOVE_UNIT_temp.side x=$MOVE_UNIT_path_coords_x y=$MOVE_UNIT_path_coords_y [/move_unit_fake] [unstore_unit] variable=MOVE_UNIT_temp find_vacant=yes [/unstore_unit] [redraw][/redraw] {CLEAR_VARIABLE MOVE_UNIT_temp} {CLEAR_VARIABLE MOVE_UNIT_path_coords_x} {CLEAR_VARIABLE MOVE_UNIT_path_coords_y} #enddef #define FULL_HEAL FILTER # This heals the specified unit(s) to full health. [store_unit] [filter] {FILTER} [/filter] variable=FULL_HEAL_temp [/store_unit] {FOREACH FULL_HEAL_temp FULL_HEAL_i} [set_variable] name=FULL_HEAL_temp[$FULL_HEAL_i].hitpoints value=$FULL_HEAL_temp[$FULL_HEAL_i].max_hitpoints [/set_variable] [unstore_unit] find_vacant=no variable=FULL_HEAL_temp[$FULL_HEAL_i] [/unstore_unit] {NEXT FULL_HEAL_i} {CLEAR_VARIABLE FULL_HEAL_temp} #enddef #define PUT_TO_RECALL_LIST FILTER # 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] [store_unit] [filter] {FILTER} [/filter] variable=PUT_TO_RECALL_LIST_temp kill=yes [/store_unit] {FOREACH PUT_TO_RECALL_LIST_temp i} {VARIABLE PUT_TO_RECALL_LIST_temp[$i].x "recall"} {VARIABLE PUT_TO_RECALL_LIST_temp[$i].y "recall"} [unstore_unit] variable=PUT_TO_RECALL_LIST_temp[$i] find_vacant=no [/unstore_unit] {NEXT i} {CLEAR_VARIABLE PUT_TO_RECALL_LIST_temp} #enddef # FIXME: Documentation for these is needed. #define MENU_IMG_TXT IMG TXT "&"+{IMG}+"="+{TXT}#enddef #define MENU_IMG_TXT2 IMG TXT1 TXT2 "&"+{IMG}+"="+{TXT1}+"="+{TXT2}#enddef #define TIME_ACTIONS CONTENT_WML # Measure (in milliseconds) the time arbitrary event WML takes to # execute. Afterwards, the time the enclosed WML took to execute # is found in the variable $timed_actions_ms. # # Example: #! [event] #! name=start #! #! {TIME_ACTIONS ( #! {MODIFY_UNIT race=orc name ( _ "Azir")} #! )} #! #! {DEBUG_MSG "Renaming all orcs to Azir took $timed_actions_ms|ms."} #! [/event] {VARIABLE_OP TIME_ACTIONS_time_begin time stamp} {CONTENT_WML} {VARIABLE_OP TIME_ACTIONS_time_end time stamp} {VARIABLE timed_actions_ms $TIME_ACTIONS_time_end} {VARIABLE_OP timed_actions_ms add "-$TIME_ACTIONS_time_begin"} {CLEAR_VARIABLE TIME_ACTIONS_time_begin} {CLEAR_VARIABLE TIME_ACTIONS_time_end} #enddef #define RECRUIT_UNIT_VARIATIONS SIDE TYPE VARIATIONS # Allows a side to seemingly recruit variations of a given unit, such as the # the Walking Corpse. # # An example which makes side 2 have a 50% chance of getting a normal WC # and a 50% chance of getting either a drake or dwarf variation: #! {RECRUIT_UNIT_VARIATIONS 2 "Walking Corpse" none,none,drake,dwarf} [event] name=prerecruit first_time_only=no [filter] side={SIDE} type={TYPE} [/filter] {VARIABLE_OP recruited_unit_random_variation rand {VARIATIONS}} [if] [variable] name=recruited_unit_random_variation not_equals=none [/variable] [then] [object] duration=forever silent=yes [filter] x,y=$x1,$y1 [/filter] [effect] apply_to=variation name=$recruited_unit_random_variation [/effect] [/object] [/then] [/if] [/event] #enddef #define NEUTRAL_SIDE SIDE # Allows a side to appear as if allied with every other side. # # Limitations: if we have three sides A, B and C, and C is a neutral side, then # sides B and C will appear as enemies during A's turn, and likewise A and C on # B's turn. # # IMPORTANT NOTE: the scenario needs victory_when_enemies_defeated=no to be set. [event] name=prestart {VARIABLE NEUTRAL_SIDE_i 1} [store_side] side=$NEUTRAL_SIDE_i variable=NEUTRAL_SIDE_side_store [/store_side] [while] [variable] name=NEUTRAL_SIDE_side_store.team_name not_equals=$empty [/variable] [do] {VARIABLE side_$NEUTRAL_SIDE_i|_original_team_name $NEUTRAL_SIDE_side_store.team_name} {VARIABLE side_$NEUTRAL_SIDE_i|_original_user_team_name $NEUTRAL_SIDE_side_store.user_team_name} {VARIABLE_OP NEUTRAL_SIDE_i add 1} {CLEAR_VARIABLE NEUTRAL_SIDE_side_store} [store_side] side=$NEUTRAL_SIDE_i variable=NEUTRAL_SIDE_side_store [/store_side] [/do] [/while] {CLEAR_VARIABLE NEUTRAL_SIDE_side_store} [/event] [event] name=side turn first_time_only=no [if] [variable] name=side_number not_equals={SIDE} [/variable] [then] {VARIABLE NEUTRAL_SIDE_i 1} [while] [variable] name=side_$NEUTRAL_SIDE_i|_original_team_name not_equals=$empty [/variable] [do] [modify_side] side=$NEUTRAL_SIDE_i team_name=$side_$NEUTRAL_SIDE_i|_original_team_name user_team_name=$side_$NEUTRAL_SIDE_i|_original_user_team_name [/modify_side] {VARIABLE_OP NEUTRAL_SIDE_i add 1} [/do] [/while] [store_side] side=$side_number variable=NEUTRAL_SIDE_side_store [/store_side] [modify_side] side={SIDE} team_name=$NEUTRAL_SIDE_side_store.team_name user_team_name=$side_{SIDE}_original_user_team_name [/modify_side] {CLEAR_VARIABLE NEUTRAL_SIDE_side_store} [/then] [else] {VARIABLE NEUTRAL_SIDE_i 1} [while] [variable] name=side_$NEUTRAL_SIDE_i|_original_team_name not_equals=$empty [/variable] [do] [modify_side] side=$NEUTRAL_SIDE_i team_name=friends_with_all user_team_name=$side_$NEUTRAL_SIDE_i|_original_user_team_name [/modify_side] {VARIABLE_OP NEUTRAL_SIDE_i add 1} [/do] [/while] [/else] [/if] [/event] #enddef #define SCATTER_UNITS NUMBER TYPES PADDING_RADIUS SLF UNIT # Scatters the given kind of units randomly on a given area on the map. # # An example which scatters some loyal elves on forest hexes in # x,y=10-30,20-40, at a minimum of three hexes apart from each other and # never on top of or adjacent to any already existing units: #! {SCATTER_UNITS 20 "Elvish Fighter,Elvish Archer,Elvish Shaman" 3 ( #! terrain=Gs^Fp #! x=10-30 #! y=20-40 #! #! [not] #! [filter] #! [/filter] #! [/not] #! #! [not] #! [filter_adjacent_location] #! [filter] #! [/filter] #! [/filter_adjacent_location] #! [/not] #! ) ( #! side=2 #! generate_name=yes #! random_traits=yes #! #! [modifications] #! {TRAIT_LOYAL} #! [/modifications] #! )} [store_locations] {SLF} variable=possible_unit_locations [/store_locations] [set_variables] name=unit_type_table [split] list={TYPES} key=type separator=, [/split] [/set_variables] {VARIABLE unit_type_table_i 0} {VARIABLE units_to_place {NUMBER}} [while] [variable] name=units_to_place greater_than=0 [/variable] [do] [set_variable] name=random_subscript rand=1..$possible_unit_locations.length [/set_variable] {VARIABLE_OP random_subscript add -1} [unit] type=$unit_type_table[$unit_type_table_i].type x,y=$possible_unit_locations[$random_subscript].x,$possible_unit_locations[$random_subscript].y {UNIT} [/unit] [store_locations] find_in=possible_unit_locations [not] x,y=$possible_unit_locations[$random_subscript].x,$possible_unit_locations[$random_subscript].y radius={PADDING_RADIUS} [/not] variable=possible_unit_locations [/store_locations] [if] [variable] name=possible_unit_locations.length less_than=1 [/variable] [then] {VARIABLE units_to_place 0} [/then] [/if] {VARIABLE_OP unit_type_table_i add 1} [if] [variable] name=unit_type_table_i numerical_equals=$unit_type_table.length [/variable] [then] {VARIABLE unit_type_table_i 0} [/then] [/if] {VARIABLE_OP units_to_place add -1} [/do] [/while] {CLEAR_VARIABLE unit_type_table,unit_type_table_i,possible_unit_locations,random_subscript} #enddef #define FORCE_CHANCE_TO_HIT FILTER FILTER_SECOND CTH EXTRA_CONDITIONS # Invisibly forces certain units to always have a specific chance to hit # when fighting against certain other units. # # Note that the player still only sees the regular damage calculations, so # this is useful if you need to give an invisible helping hand to the player # or AI. For example, if the player is forced to attack with only a couple # of units at the beginning of a scenario, you can use this to ensure that # simply having bad luck cannot ruin their attempt so easily. Also you might # have enemy leaders which the player is not supposed to fight or be able to # defeat due to storyline reasons, but could theoretically still kill with # some clever trick, AI mistake or sheer exceptional luck. # # An example which forces Konrad's attacks to always hit Li'sar, but only # after turn 10: #! {FORCE_CHANCE_TO_HIT id=Konrad id="Li'sar" 100 ( #! [variable] #! name=turn_number #! greater_than=10 #! [/variable] #! )} [event] name=attack first_time_only=no [filter] {FILTER} [/filter] [filter_second] {FILTER_SECOND} [/filter_second] [if] [and] {EXTRA_CONDITIONS} [/and] [then] {FOREACH unit.attack i} [if] [variable] name=unit.attack[$i].specials.chance_to_hit.length greater_than=0 [/variable] [then] [set_variables] name=unit.attack[$i].specials.original_chance_to_hit to_variable=unit.attack[$i].specials.chance_to_hit [/set_variables] {CLEAR_VARIABLE unit.attack[$i].specials.chance_to_hit} [/then] [/if] [set_variables] name=unit.attack[$i].specials.chance_to_hit [value] id=forced_cth value={CTH} cumulative=no [/value] [/set_variables] {NEXT i} [unstore_unit] variable=unit find_vacant=no [/unstore_unit] [event] name=attack end delayed_variable_substitution=yes {FOREACH unit.attack i} {CLEAR_VARIABLE unit.attack[$i].specials.chance_to_hit} [set_variables] name=unit.attack[$i].specials.chance_to_hit to_variable=unit.attack[$i].specials.original_chance_to_hit [/set_variables] {CLEAR_VARIABLE unit.attack[$i].specials.original_chance_to_hit} {NEXT i} [unstore_unit] variable=unit find_vacant=no [/unstore_unit] [/event] [/then] [/if] [/event] # The following event is a simple duplicates of the above ones, with the # primary and secondary units reversed so that the effect is applied also on # defense. [event] name=attack first_time_only=no [filter] {FILTER_SECOND} [/filter] [filter_second] {FILTER} [/filter_second] [if] [and] {EXTRA_CONDITIONS} [/and] [then] {FOREACH second_unit.attack i} [if] [variable] name=second_unit.attack[$i].specials.chance_to_hit.length greater_than=0 [/variable] [then] [set_variables] name=second_unit.attack[$i].specials.original_chance_to_hit to_variable=second_unit.attack[$i].specials.chance_to_hit [/set_variables] {CLEAR_VARIABLE second_unit.attack[$i].specials.chance_to_hit} [/then] [/if] [set_variables] name=second_unit.attack[$i].specials.chance_to_hit [value] id=forced_cth value={CTH} cumulative=no [/value] [/set_variables] {NEXT i} [unstore_unit] variable=second_unit find_vacant=no [/unstore_unit] [event] name=attack end delayed_variable_substitution=yes {FOREACH second_unit.attack i} {CLEAR_VARIABLE second_unit.attack[$i].specials.chance_to_hit} [set_variables] name=second_unit.attack[$i].specials.chance_to_hit to_variable=second_unit.attack[$i].specials.original_chance_to_hit [/set_variables] {CLEAR_VARIABLE second_unit.attack[$i].specials.original_chance_to_hit} {NEXT i} [unstore_unit] variable=second_unit find_vacant=no [/unstore_unit] [/event] [/then] [/if] [/event] #enddef