wesnoth/data/utils.cfg
Lari Nieminen 2fb4fce399 Changed the MODIFY_UNIT and MOVE_UNIT macros slightly...
...and added two new macros to limit recruiting (mainly for AI balancing).
2006-09-18 19:10:27 +00:00

781 lines
15 KiB
INI

# This file contains utility macros for scenario writers
#### TABLE OF CONTENTS ####
# variable operations
# RANDOM
# VARIABLE
# VARIABLE_OP
# CLEAR_VARIABLE
# FOREACH
# NEXT
# IF_TERRAIN
# DEBUG_MSG
# scenario/campaign setup
# DEFAULT_MUSIC_PLAYLIST
# SCENARIO_MUSIC
# STARTING_VILLAGES
# DOT
# CROSS
# MENU_IMG_TXT
# MENU_IMG_TXT2
# LIMIT_RECRUITS
# LIMIT_CONTEMPORANEOUS_RECRUITS
# side setup
# TURNS
# GOLD
# INCOME
# ATTACK_DEPTH
# NO_SCOUTS
# in-scenario actions
# PLACE_IMAGE
# TREMOR
# IS_HERO
# UNIT
# UNDEAD_UNIT
# MOVE_UNIT
# MODIFY_UNIT
# STORE_UNIT_VAR
# PUT_TO_RECALL_LIST
# utilities not intended for general use
# QUANTITY
# MAGENTA_IS_THE_TEAM_COLOR
# COLOR_HEAL
# COLOR_HARM
# COLOR_WHITE
# IMAGE_DEFENSIVE
# IMAGE_DEFENSIVE_RANGE
# IMAGE_DEFENSIVE_SHORT
#### END OF TABLE OF CONTENTS ####
#macro to define a 'quantity' differently based on difficulty levels
#define QUANTITY ATTRIBUTE ON_EASY ON_NORMAL ON_HARD
#ifdef EASY
{ATTRIBUTE}={ON_EASY}
#endif
#ifdef NORMAL
{ATTRIBUTE}={ON_NORMAL}
#endif
#ifdef HARD
{ATTRIBUTE}={ON_HARD}
#endif
#enddef
#macro to define number of turns for different difficulty levels
#define TURNS ON_EASY ON_NORMAL ON_HARD
{QUANTITY turns {ON_EASY} {ON_NORMAL} {ON_HARD}}
#enddef
#macro which will let you go {GOLD x y z} to set
#the gold depending on easy/medium/hard - x/y/z
#define GOLD ON_EASY ON_NORMAL ON_HARD
{QUANTITY gold {ON_EASY} {ON_NORMAL} {ON_HARD}}
#enddef
#define INCOME ON_EASY ON_NORMAL ON_HARD
{QUANTITY income {ON_EASY} {ON_NORMAL} {ON_HARD}}
#enddef
#macro to define AI attack depth for different difficulty levels (set it to 1-6)
#define ATTACK_DEPTH ON_EASY ON_NORMAL ON_HARD
{QUANTITY attack_depth {ON_EASY} {ON_NORMAL} {ON_HARD}}
#enddef
#macro to make an AI team not recruit scouts
#define NO_SCOUTS
villages_per_scout=0
#enddef
#macro to add a hero icon to the unit
#define IS_HERO
overlays="misc/hero-icon.png"
#enddef
#define DOT X Y
[image]
x,y={X},{Y}
file=misc/dot.png
delay=500
[/image]
#enddef
#define CROSS X Y
[image]
x,y={X},{Y}
file=misc/cross.png
delay=500
[/image]
#enddef
#macro to quickly pick a random value (in the $random variable, to avoid
#cluterring up savegames with such temporary variables)
#define RANDOM RANGE
[set_variable]
name=random
random={RANGE}
[/set_variable]
#enddef
#macro to initialize a variable
#define VARIABLE VAR VALUE
[set_variable]
name={VAR}
value={VALUE}
[/set_variable]
#enddef
#macro to do mathematical operations on variables
#define VARIABLE_OP VAR OP ARG
[set_variable]
name={VAR}
{OP}={ARG}
[/set_variable]
#enddef
#define CLEAR_VARIABLE VAR
[clear_variable]
name={VAR}
[/clear_variable]
#enddef
#macro to iterate over an array
#define FOREACH ARRAY VAR
{VARIABLE {VAR} 0}
[while]
[variable]
name={VAR}
less_than=${ARRAY}.length
[/variable]
[do]
#enddef
#define NEXT VAR
[set_variable]
name={VAR}
add=1
[/set_variable]
[/do]
[/while]
{CLEAR_VARIABLE {VAR}}
#enddef
#define DEBUG_MSG MSG
[message]
speaker=narrator
message={MSG}
[/message]
#enddef
# 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}
#define MODIFY_UNIT FILTER VAR VALUE
[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}
format={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
# Stores an attribute of a unit to the given variable.
#
# Example where this is used to flip 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}
#define STORE_UNIT_VAR FILTER VAR TO_VAR
[store_unit]
[filter]
{FILTER}
[/filter]
kill=no
variable=STORE_UNIT_VAR_store
[/store_unit]
{VARIABLE_OP {TO_VAR} format $STORE_UNIT_VAR_store.{VAR}}
{CLEAR_VARIABLE STORE_UNIT_VAR_store}
#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]
#define IF_TERRAIN X Y TYPES CONTENTS
[store_locations]
x={X}
y={Y}
terrain={TYPES}
variable=IF_TERRAIN_temp
[/store_locations]
[if]
[variable]
name=IF_TERRAIN_temp.length
not_equals=0
[/variable]
{CONTENTS}
[/if]
{CLEAR_VARIABLE IF_TERRAIN_temp}
#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]
variable=MOVE_UNIT_temp
kill=yes
[/store_unit]
[scroll_to]
x=$MOVE_UNIT_temp.x
y=$MOVE_UNIT_temp.y
[/scroll_to]
{VARIABLE_OP x_coords format ("$MOVE_UNIT_temp.x|,{TO_X}")}
{VARIABLE_OP y_coords format ("$MOVE_UNIT_temp.y|,{TO_Y}")}
{VARIABLE MOVE_UNIT_temp.x {TO_X}}
{VARIABLE MOVE_UNIT_temp.y {TO_Y}}
[move_unit_fake]
type=$MOVE_UNIT_temp.type
x=$x_coords
y=$y_coords
side=$MOVE_UNIT_temp.side
[/move_unit_fake]
[unstore_unit]
variable=MOVE_UNIT_temp
[/unstore_unit]
#enddef
# 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]
#define PUT_TO_RECALL_LIST FILTER
[store_unit]
[filter]
{FILTER}
[/filter]
variable=PUT_TO_RECALL_LIST_temp
kill=yes
[/store_unit]
{FOREACH 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}
#enddef
#macro to make a side start a scenario with villages
#define STARTING_VILLAGES SIDE RADIUS
[event]
name=prestart
[store_starting_location]
side={SIDE}
variable=temp_starting_location
[/store_starting_location]
[store_locations]
x,y=$temp_starting_location.x,$temp_starting_location.y
radius={RADIUS}
variable=temp_starting_locs
#all the types of villages
terrain=AaBbDeLptUvVZY
[/store_locations]
{FOREACH temp_starting_locs i}
{VARIABLE_OP temp_x_var to_variable temp_starting_locs[$i].x}
{VARIABLE_OP temp_y_var to_variable temp_starting_locs[$i].y}
[capture_village]
side={SIDE}
x,y=$temp_x_var,$temp_y_var
[/capture_village]
{NEXT i}
{CLEAR_VARIABLE temp_x_var}
{CLEAR_VARIABLE temp_y_var}
{CLEAR_VARIABLE temp_starting_location}
{CLEAR_VARIABLE temp_starting_locs}
{CLEAR_VARIABLE i}
[/event]
#enddef
#define MENU_IMG_TXT IMG TXT
"&"+{IMG}+"="+{TXT}#enddef
#define MENU_IMG_TXT2 IMG TXT1 TXT2
"&"+{IMG}+"="+{TXT1}+"="+{TXT2}#enddef
#
#USAGE {UNIT (Elvish Fighter) (Myname) ( _ "Myname") 1 1 1}
#
#define UNIT TYPE DESCRIPTION UDESCRIPTION SIDE X Y
[unit]
type={TYPE}
description={DESCRIPTION}
user_description={UDESCRIPTION}
side={SIDE}
x={X}
y={Y}
[modifications]
{TRAIT_LOYAL}
[/modifications]
[/unit]
#enddef
#define UNDEAD_UNIT TYPE SIDE X Y
[unit]
type={TYPE}
side={SIDE}
x={X}
y={Y}
[modifications]
{TRAIT_UNDEAD}
{TRAIT_LOYAL}
[/modifications]
[/unit]
#enddef
#define PLACE_IMAGE IMAGE_FILE X Y
[item]
x={X}
y={Y}
image={IMAGE_FILE}
[/item]
#enddef
#define TREMOR
[sound]
name="rumble.ogg"
[/sound]
[scroll]
x=5
y=0
[/scroll]
[scroll]
x=-10
y=0
[/scroll]
[scroll]
x=5
y=5
[/scroll]
[scroll]
x=0
y=-10
[/scroll]
[scroll]
x=0
y=5
[/scroll]
#enddef
#macro to easily replace image_defensive and image_defensive_range
#define IMAGE_DEFENSIVE IMAGE_FILE
[defend]
[frame]
begin=-150
end=150
image={IMAGE_FILE}
[/frame]
[/defend]
#enddef
#define IMAGE_DEFENSIVE_RANGE IMAGE_FILE
[defend]
range=ranged
[frame]
begin=-150
end=150
image={IMAGE_FILE}
[/frame]
[/defend]
#enddef
#define IMAGE_DEFENSIVE_SHORT IMAGE_FILE
[defend]
range=melee
[frame]
begin=-150
end=150
image={IMAGE_FILE}
[/frame]
[/defend]
#enddef
# a macro to define a common set of magenta color values which different
# units can be color shifted by using the team color system
#define MAGENTA_IS_THE_TEAM_COLOR
flag_rgb=magenta
#enddef
# Music macros. As of 1.1.3, music is parsed as follows:
# 1. the [scenario]-level [music] tag
# 2. the [story]-level music key
# 3. any [event]-level [music] tags
#
# If you change the music at a lower level, the tags above it will NOT be re-parsed
# and your scenario will sound wrong. For example, if you set scenario music
# with the normal [scenario]-level music tag but change the music during the [story],
# it is never reset back to normal. Therefore, these macros are intended to catch
# instances like that. Of course, if you want something more elaborate, code it
# manually.
# A macro to define a standard playlist suitable for any level. The music is defined
# twice to catch instances where music is changed in a story and not set back.
#define DEFAULT_MUSIC_PLAYLIST
[music]
name=gameplay01.ogg
ms_before=12000
[/music]
[music]
name=gameplay02.ogg
ms_before=12000
append=yes
[/music]
[music]
name=gameplay03.ogg
ms_before=12000
append=yes
[/music]
[music]
name=main_menu.ogg
ms_before=12000
append=yes
[/music]
[music]
name=wesnoth-1.ogg
ms_before=12000
append=yes
[/music]
[music]
name=wesnoth-2.ogg
ms_before=12000
append=yes
[/music]
[event]
name=prestart
[music]
name=gameplay01.ogg
ms_before=12000
[/music]
[music]
name=gameplay02.ogg
ms_before=12000
append=yes
[/music]
[music]
name=gameplay03.ogg
ms_before=12000
append=yes
[/music]
[music]
name=main_menu.ogg
ms_before=12000
append=yes
[/music]
[music]
name=wesnoth-1.ogg
ms_before=12000
append=yes
[/music]
[music]
name=wesnoth-2.ogg
ms_before=12000
append=yes
[/music]
[/event]
#enddef
#
# This music macro ensures that the correct music is selected
# for a scenario just in case it is changed by [story].
# It should be positioned at the top of the scenario file
# so it can be overridden by other prestart or start events.
#
# It also allows for the convenient use of a standardized
# intra-scenario music, should we decide to use one.
#
#define SCENARIO_MUSIC MUSIC
[music]
name="wesnoth-2.ogg"
[/music]
[event]
name=prestart
[music]
name={MUSIC}
[/music]
[/event]
#enddef
#define COLOR_HEAL
green=255
#enddef
#define COLOR_HARM
red=255
#enddef
#define COLOR_WHITE
red=255
green=255
blue=255
#enddef
# With this, you can easily limit the number of units of a certain type a side
# can have simultaneously. When the number of units of type TYPE the side has
# reaches or exceeds LIMIT, that side is prevented from recruiting more until
# the number of units of that type drops below LIMIT again.
#
# This one is probably most useful in balancing AI behaviour - to for example
# give an AI side the possibility to recruit high level units but not to have
# too many of them at the same time.
#define LIMIT_CONTEMPORANEOUS_RECRUITS SIDE TYPE LIMIT
[event]
name=side turn
first_time_only=no
[if]
[variable]
name=side_number
equals={SIDE}
[/variable]
[then]
[store_unit]
[filter]
side={SIDE}
type={TYPE}
[/filter]
kill=no
variable=LIMIT_CONTEMPORANEOUS_RECRUITS_temp
[/store_unit]
[if]
[variable]
name=LIMIT_CONTEMPORANEOUS_RECRUITS_temp.length
greater_than_equal_to={LIMIT}
[/variable]
[then]
[disallow_recruit]
side={SIDE}
type={TYPE}
[/disallow_recruit]
[/then]
[else]
[allow_recruit]
side={SIDE}
type={TYPE}
[/allow_recruit]
[/else]
[/if]
{CLEAR_VARIABLE LIMIT_CONTEMPORANEOUS_RECRUITS_temp}
[/then]
[/if]
[/event]
[event]
name=recruit
first_time_only=no
[filter]
side={SIDE}
type={TYPE}
[/filter]
[store_unit]
[filter]
side={SIDE}
type={TYPE}
[/filter]
kill=no
variable=LIMIT_CONTEMPORANEOUS_RECRUITS_temp
[/store_unit]
[if]
[variable]
name=LIMIT_CONTEMPORANEOUS_RECRUITS_temp.length
greater_than_equal_to={LIMIT}
[/variable]
[then]
[disallow_recruit]
side={SIDE}
type={TYPE}
[/disallow_recruit]
[/then]
[/if]
{CLEAR_VARIABLE LIMIT_CONTEMPORANEOUS_RECRUITS_temp}
[/event]
#enddef
# With this, you can easily limit the overall number of units of a given type a
# given side can recruit in the scenario. The side can only recruit the set
# amount of units of the given type.
#define LIMIT_RECRUITS SIDE TYPE LIMIT
[event]
name=prestart
{VARIABLE side_{SIDE}_limited_recruits_length -1}
[/event]
[event]
name=recruit
first_time_only=yes
[filter]
side={SIDE}
type={TYPE}
[/filter]
{VARIABLE_OP side_{SIDE}_limited_recruits_length add 1}
{VARIABLE_OP LIMIT_RECRUITS_temp1 format "side_{SIDE}_limited_recruits[$side_{SIDE}_limited_recruits_length|].type"}
{VARIABLE $LIMIT_RECRUITS_temp1 {TYPE}}
{CLEAR_VARIABLE LIMIT_RECRUITS_temp1}
[/event]
[event]
name=recruit
first_time_only=no
[filter]
side={SIDE}
type={TYPE}
[/filter]
{VARIABLE_OP LIMIT_RECRUITS_temp2 format "side_{SIDE}_limited_recruits[$side_{SIDE}_limited_recruits_length|].recruited"}
{VARIABLE_OP $LIMIT_RECRUITS_temp2 add 1}
{CLEAR_VARIABLE LIMIT_RECRUITS_temp2}
{FOREACH side_{SIDE}_limited_recruits i}
[if]
[variable]
name=side_{SIDE}_limited_recruits[$i].type
equals={TYPE}
[/variable]
[variable]
name=side_{SIDE}_limited_recruits[$i].recruited
equals={LIMIT}
[/variable]
[then]
[disallow_recruit]
side={SIDE}
type={TYPE}
[/disallow_recruit]
[/then]
[/if]
{NEXT i}
[/event]
#enddef