#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 QUANTITY4 NAME EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE # Four-difficulty version of QUANTITY #ifdef EASY {NAME}={EASY_VALUE} #endif #ifdef NORMAL {NAME}={NORMAL_VALUE} #endif #ifdef HARD {NAME}={HARD_VALUE} #endif #ifdef NIGHTMARE {NAME}={NIGHTMARE_VALUE} #endif #enddef # No tab or space-based indentation for these macros to avoid trouble when these macros are used # in the middle of a quoted string literal # # wmlindent: start ignoring # wmlscope: start conditionals #ifdef EASY #define ON_DIFFICULTY EASY_VALUE NORMAL_VALUE HARD_VALUE {EASY_VALUE}#enddef #endif #ifdef NORMAL #define ON_DIFFICULTY EASY_VALUE NORMAL_VALUE HARD_VALUE {NORMAL_VALUE}#enddef #endif #ifdef HARD #define ON_DIFFICULTY EASY_VALUE NORMAL_VALUE HARD_VALUE {HARD_VALUE}#enddef #endif # wmlscope: stop conditionals # wmlindent: stop ignoring # wmlscope: prune ON_DIFFICULTY # No tab or space-based indentation for these macros to avoid trouble when these macros are used # in the middle of a quoted string literal # # wmlindent: start ignoring # wmlscope: start conditionals #ifdef EASY #define ON_DIFFICULTY4 EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE {EASY_VALUE}#enddef #endif #ifdef NORMAL #define ON_DIFFICULTY4 EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE {NORMAL_VALUE}#enddef #endif #ifdef HARD #define ON_DIFFICULTY4 EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE {HARD_VALUE}#enddef #endif #ifdef NIGHTMARE #define ON_DIFFICULTY4 EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE {NIGHTMARE_VALUE}#enddef #endif # wmlscope: stop conditionals # wmlindent: stop ignoring # wmlscope: prune ON_DIFFICULTY4 #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 TURNS4 EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT NIGHTMARE_AMOUNT # Four-difficulty version of TURNS {QUANTITY4 turns {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT} {NIGHTMARE_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 GOLD4 EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT NIGHTMARE_AMOUNT # Four-difficulty version of GOLD {QUANTITY4 gold {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT} {NIGHTMARE_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 INCOME4 EASY_AMOUNT NORMAL_AMOUNT HARD_AMOUNT NIGHTMARE_AMOUNT # Four-difficulty version of INCOME {QUANTITY4 income {EASY_AMOUNT} {NORMAL_AMOUNT} {HARD_AMOUNT} {NIGHTMARE_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 ATTACK_DEPTH4 EASY_VALUE NORMAL_VALUE HARD_VALUE NIGHTMARE_VALUE # Four-difficulty version of ATTACK_DEPTH {QUANTITY4 attack_depth {EASY_VALUE} {NORMAL_VALUE} {HARD_VALUE} {NIGHTMARE_VALUE}} #enddef #define NO_SCOUTS # Macro to make an AI team not recruit scouts. villages_per_scout=0 #enddef #define RANDOM THING_VALUE # 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_VALUE} [/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 GLOBAL_VARIABLE NAMESPACE LOCAL_VAR_NAME GLOBAL_VAR_NAME SIDE #Assigns a persistent variable with the contents of a standard variable. [set_global_variable] namespace={NAMESPACE} from_local={LOCAL_VAR_NAME} to_global={GLOBAL_VAR_NAME} side={SIDE} [/set_global_variable] #enddef #define VARIABLE_FROM_GLOBAL NAMESPACE GLOBAL_VAR_NAME LOCAL_VAR_NAME SIDE #Retrieves the contents of a persistent variable and stores them in a standard variable. [get_global_variable] namespace={NAMESPACE} from_global={GLOBAL_VAR_NAME} to_local={LOCAL_VAR_NAME} side={SIDE} #immediate=no [/get_global_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 VARIABLE_CONDITIONAL VAR OP_NAME VALUE # Macro to do conditional operations on variables. [variable] name={VAR} {OP_NAME}={VALUE} [/variable] #enddef #define CLEAR_VARIABLE VAR_NAME # Macro to clear a variable previously set. [clear_variable] name={VAR_NAME} [/clear_variable] #enddef #define CLEAR_GLOBAL_VARIABLE NAMESPACE MY_VARIABLE_NAME SIDE # Clears a persistent variable entirely. [clear_global_variable] namespace={NAMESPACE} global={MY_VARIABLE_NAME} side={SIDE} immediate=no [/clear_global_variable] #enddef # wmlindent: start ignoring #define FOREACH ARRAY_VALUE VAR # Macro to begin a WML clause that iterates over an array. {VARIABLE {VAR} 0} [while] [variable] name={VAR} less_than=${ARRAY_VALUE}.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_NAME WHERE_KEY_NAME WHERE_VALUE SAVE_AS_NAME # 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_NAME} 0} [while] [variable] name={SAVE_AS_NAME} less_than=${FROM_ARRAY_NAME}.length [/variable] [variable] name={FROM_ARRAY_NAME}[${SAVE_AS_NAME}].{WHERE_KEY_NAME} not_equals={WHERE_VALUE} [/variable] [do] {VARIABLE_OP {SAVE_AS_NAME} add 1} [/do] [/while] #enddef #define LOOKUP_VALUE FROM_ARRAY_NAME WHERE_KEY_NAME WHERE_VALUE SAVE_KEY_NAME DEFAULT_VALUE SAVE_AS_NAME # 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_NAME} {WHERE_KEY_NAME} {WHERE_VALUE} {SAVE_AS_NAME}} [if] [variable] name={SAVE_AS_NAME} numerical_equals=${FROM_ARRAY_NAME}.length [/variable] [then] {VARIABLE {SAVE_AS_NAME} {DEFAULT_VALUE}} [/then] [else] {VARIABLE {SAVE_AS_NAME} ${FROM_ARRAY_NAME}[${SAVE_AS_NAME}].{SAVE_KEY_NAME}} [/else] [/if] #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,MODIFY_UNIT_i} #enddef #define MOVE_UNIT_BY FILTER OFFSET_X OFFSET_Y #TODO COMMENT [store_unit] [filter] {FILTER} [/filter] variable=MOVE_UNIT_store kill=yes [/store_unit] {FOREACH MOVE_UNIT_store MOVE_UNIT_BY_i} {VARIABLE_OP MOVE_UNIT_store[$MOVE_UNIT_BY_i].x add {OFFSET_X}} {VARIABLE_OP MOVE_UNIT_store[$MOVE_UNIT_BY_i].y add {OFFSET_Y}} [unstore_unit] variable=MOVE_UNIT_store[$MOVE_UNIT_BY_i] find_vacant=no [/unstore_unit] {NEXT MOVE_UNIT_BY_i} {CLEAR_VARIABLE MOVE_UNIT_store,MOVE_UNIT_BY_i} #enddef #define MOVE_UNIT FILTER TO_X TO_Y # Moves a unit from its current location to the given location, displaying # movement normally. # # 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. [move_unit] {FILTER} to_x={TO_X} to_y={TO_Y} fire_event=no [/move_unit] #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,FULL_HEAL_i} #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 PUT_TO_RECALL_LIST_i} {VARIABLE PUT_TO_RECALL_LIST_temp[$PUT_TO_RECALL_LIST_i].x "recall"} {VARIABLE PUT_TO_RECALL_LIST_temp[$PUT_TO_RECALL_LIST_i].y "recall"} [unstore_unit] variable=PUT_TO_RECALL_LIST_temp[$PUT_TO_RECALL_LIST_i] find_vacant=no [/unstore_unit] {NEXT PUT_TO_RECALL_LIST_i} {CLEAR_VARIABLE PUT_TO_RECALL_LIST_temp,PUT_TO_RECALL_LIST_i} #enddef # FIXME: Documentation for these is needed. #define MENU_IMG_TXT IMAGE TEXT "&"+{IMAGE}+"="+{TEXT}#enddef #define MENU_IMG_TXT2 IMAGE FIRST_TEXT_VALUE SECOND_TEXT_VALUE "&"+{IMAGE}+"="+{FIRST_TEXT_VALUE}+"="+{SECOND_TEXT_VALUE}#enddef #define RECRUIT_UNIT_VARIATIONS SIDE TYPE VARIATIONS_VALUE # 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_VALUE}} [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] [effect] apply_to=hitpoints heal_full=yes [/effect] [/object] [/then] [/if] [/event] #enddef #define SCATTER_UNITS NUMBER TYPES PADDING_RADIUS FILTER UNIT_WML # 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_map_dimensions] variable=scatter_units_map_dimensions [/store_map_dimensions] [store_locations] [and] {FILTER} [/and] # This (and the above wrapper [and]) is used to filter out border hexes; # if a more straightforward way to exclude border hexes is implemented, # then this should be replaced with that [not] x="0, $($scatter_units_map_dimensions.width + 1)" [or] y="0, $($scatter_units_map_dimensions.height + 1)" [/or] [/not] 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 sub 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_WML} [/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 sub 1} [/do] [/while] {CLEAR_VARIABLE unit_type_table,unit_type_table_i,scatter_units_map_dimensions,possible_unit_locations,random_subscript,units_to_place} #enddef #define FORCE_CHANCE_TO_HIT FILTER SECOND_FILTER CTH_NUMBER EXTRA_CONDITIONS_WML # 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] {SECOND_FILTER} [/filter_second] [if] [and] {EXTRA_CONDITIONS_WML} [/and] [then] {FOREACH unit.attack i} [if] #This is to mute a warning message about retrieving a member of non-existant wml container. [variable] name=unit.attack[$i].specials.length greater_than=0 [/variable] [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_NUMBER} 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] {SECOND_FILTER} [/filter] [filter_second] {FILTER} [/filter_second] [if] [and] {EXTRA_CONDITIONS_WML} [/and] [then] {FOREACH second_unit.attack i} [if] [variable] name=second_unit.attack[$i].specials.length greater_than=0 [/variable] [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_NUMBER} 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 #define LOOT AMOUNT SIDE {VARIABLE amount_gold {AMOUNT}} #TODO add message for the other players! [message] side_for={SIDE} speaker=narrator message= _ "You retrieve $amount_gold pieces of gold." image=wesnoth-icon.png sound=gold.ogg [/message] {CLEAR_VARIABLE amount_gold} [gold] side={SIDE} amount={AMOUNT} [/gold] #enddef