Difference between revisions of "PreprocessorRef"

From The Battle for Wesnoth Wiki
m (The WML preprocessor)
(Reorganize and partially rewrite most of this mess for easier linking of individual directives' descriptions from the forums, fix some minor inaccuracies, grammar, spelling, formatting.)
Line 1: Line 1:
 
{{WML Tags}}
 
{{WML Tags}}
== The WML preprocessor ==
+
== Overview ==
  
Wesnoth loads just one configuration file directly: '''data/_main.cfg'''.
+
Wesnoth loads just one configuration file directly: '''data/_main.cfg'''. However the '''WML preprocessor''' allows to include more files. Whenever a WML file is read by Wesnoth, it is passed through the preprocessor.
However the WML preprocessor allows to include more files.
 
Whenever a WML file is read by Wesnoth, it is passed through the preprocessor.
 
  
The preprocessor can interpret a simple language of string-expansions known as "macros." A macro should always be defined '''before''' the place where it needs to be used.
+
The preprocessor can interpret a simple language of string expansions known as ''macros''. A macro should always be defined '''before''' the place where it needs to be used.
  
The preprocessor is applied recursively, so included files will
+
The preprocessor is applied recursively, so included files will be parsed for macros, and after macro expansion will be parsed for macros again, and so on. As a result, you should not write a recursive macro that references itself, because it will cause errors (but, alas, not necessarily error messages).
be parsed for macros, and after macro expansion will be parsed for macros
 
again, and so on. As a result, you should not write a recursive macro,
 
because it will cause errors
 
(but, alas, not necessarily error messages).
 
  
The following directives are used to create and use ''macros'',
+
== Preprocessor directives ==
i.e. shortcuts which reduce repetition of information.
 
See [[UtilWML]], [[UsefulWMLFragments]] for examples and [http://www.wesnoth.org/macro-reference.xhtml the macro reference] for the predefined core macros.
 
* '''#define ''symbol'' [''parameters''] ''newline'' ''substitution'' #enddef''' all subsequent occurences of '''{''symbol'' [''arguments'']}''' (see below) will be replaced by ''substitution'' with all occurences of any parameter {''parameter''} within ''substitution'' replaced by the parameter's corresponding value in ''arguments''.  For example, the UNIT macro used below would be defined:
 
  
  #define UNIT TYPE X Y## the ordering is important here;
+
The following directives are used to create and use ''macros'', i.e. shortcuts which reduce repetition of information. See [http://www.wesnoth.org/macro-reference.xhtml the macro reference] for the list of predefined core macros.
                      ## since WML does not distinguish
+
 
                      ## data into different types, only
+
The preprocessor has changed several times, so don't expect old Wesnoth versions to behave exactly the same as the current stable and development series.
                      ## the ordering is used to determine which
+
 
                      ## arguments apply to which parameters.
+
'''Note:''' In multiplayer scenarios, these directives will appear to work only for the host and not for other clients. This is because the preprocessor is run only on the host, and the clients receive the resultant WML from the server. It's particularly important to keep this in mind before using preprocessor conditionals.
 +
 
 +
=== #define ===
 +
 
 +
'''Syntax: #define ''symbol'' [''parameters''] ''<newline>'' ''substitution'' #enddef'''
 +
 
 +
All subsequent occurences of '''{''symbol'' [''arguments'']}''' (see below) will be replaced by the contents of the ''substitution'' block, with all occurrences of any parameter {''parameter''} within ''substitution'' replaced by the corresponding value in ''arguments''.  For example, the ENEMY_UNIT macro for the [[#Macro inclusions|macro inclusion]] example below could be defined as follows:
 +
 
 +
  #define ENEMY_UNIT TYPE X Y
 +
## the ordering above is important, since the preprocessor does not distinguish
 +
## data into different types; only the ordering is used to determine which
 +
## arguments apply to which parameters.
 
  [unit]
 
  [unit]
type={TYPE}## the unit will be of type TYPE, so different
+
    type={TYPE} ## the unit will be of type TYPE, so different
            ## instantiations
+
                ## instantiations
            ## of this macro can create different units.
+
                ## of this macro can create different units.
x={X}
+
    x={X}
y={Y}
+
    y={Y}
side=2## the unit will be an enemy, regardless of the parameter
+
    side=2 ## the unit will be an enemy, regardless of the parameter
      ## values. This reduces "repetition of information",
+
            ## values. This reduces "repetition of information",
      ## since it is no longer necessary to specify
+
            ## since it is no longer necessary to specify
      ## each created unit as an enemy.
+
            ## each created unit as an enemy.
 
  [/unit]
 
  [/unit]
 
  #enddef
 
  #enddef
(See [[SingleUnitWML]] for information on creating units using WML.)
 
* '''{''symbol'' [''arguments'']}''' if ''symbol'' is defined, the preprocessor will replace this instruction by the expression ''symbol'' is defined as, using ''arguments'' as parameters.  You can create multiple word arguments by using parentheses to restrain the argument.  For example, while '''{UNIT Wolf Rider 18 24}''' will attempt to create a "Wolf" at (Rider,18), causing Wesnoth to crash, the macro '''{UNIT (Wolf Rider) 18 24}''' will create a "Wolf Rider" at (18,24) as it should. ('''UNIT''' is defined above).  See the ''#define'' preprocessor instruction above for information on defining symbols, including symbols with arguments.  Several symbols are defined in the normal game code; for reference they are listed in [[UtilWML]].
 
  
Note: Using the name-string of an existing macro as the name-string of a macro argument will always overwrite the original definition of the macro, e.g.:
+
(See [[SingleUnitWML]] for further information on creating units using WML.)
 +
 
 +
=== Inclusion directive {} ===
 +
 
 +
This directive can be used to include macros, single files or sets of files from a target directory.
 +
 
 +
==== File/directory inclusions ====
 +
 
 +
'''Syntax: {''path''}'''
 +
 
 +
Includes the file with the specified ''path'', which will in turn run the preprocessor on it and perform any required substitutions or inclusions within it. The ''path'' may not contain ''..'' or the inclusion will be skipped.
 +
 
 +
The exact location in which the ''path'' will be resolved will depend on its prefix:
 +
 
 +
* '''{''path''}''': If ''path'' isn't a known macro (see below), the game will assume it's a relative path to a file in the main game '''data/''' directory and include it.
 +
* '''{~''path''}''': As above, but instead of the game data directory, the path is resolved relative to the user '''data/''' directory, where user made add-ons can normally be found.
 +
* '''{./''path''}''': The path is resolved relative to the location of the current file containing this inclusion.
 +
 
 +
Information for locating the user data and game data directories can be found in [[EditingWesnoth]].
 +
 
 +
Forward slashes ('''/''') should '''always''' be used as the path delimiter, even if your platform uses a different symbol such as colons (''':''') or backslashes ('''\''')! It is also very important to respect the '''actual letter case''' used to name files and directories for compatibility with case-sensitive filesystems on Unix-based operating systems.
 +
 
 +
When ''path'' points to a directory instead of a file, the preprocessor will include all files under it, except subdirectories, in alphabetical order. Some files are handled in a special fashion:
 +
 
 +
* If there's a file named '''_main.cfg''' in the target directory, only that file will be included and preprocessed. It may include other files from its own directory or subdirectories within it, of course. This is used for managing WML directories as self-contained packages, like user made add-ons.
 +
* If there are files named '''_main.cfg''' in subdirectories of the target and there isn't one in the target itself, they will be all preprocessed. Given the following layout:
 +
dir/
 +
dir/a/_main.cfg
 +
dir/a/other.cfg
 +
dir/b/_main.cfg
 +
dir/b/other.cfg
 +
dir/other.cfg
 +
Using '''{dir}''' will cause dir/a/_main.cfg, dir/b/_main.cfg and dir/other.cfg to be included.
 +
* If there's a file named '''_final.cfg''' but no '''_main.cfg''', the file is guaranteed to be included and processed ''after'' all the other files in the directory.
 +
* If there's a file named '''_initial.cfg''' but no '''_main.cfg''', the file is guaranteed to be included and processed ''before'' all the other files in the directory.
 +
 
 +
==== Macro inclusions ====
 +
 
 +
'''Syntax: {''symbol'' [''arguments'']}'''
 +
 
 +
If the macro named ''symbol'' is defined, the preprocessor will replace this instruction by the expression ''symbol'' was previously defined as, using ''arguments'' as parameters. The number of arguments must be exactly the same as in the original definition or an error will occur.
 +
 
 +
You can create multiple word arguments by using parentheses to delimit the contents. For example, in '''{ENEMY_UNIT Wolf Rider 18 24}''' the four words will be interpreted as separate arguments and cause the preprocessor to fail since the macro was defined above with only three; instead, you should use '''{ENEMY_UNIT (Wolf Rider) 18 24}'''.
 +
 
 +
Using the name of an existing macro as the name of a macro argument is possible, but the argument will always take precedence over the original macro:
 +
 
 
  #define VARIABLE
 
  #define VARIABLE
 
  #enddef
 
  #enddef
 
  #define MACRO VARIABLE
 
  #define MACRO VARIABLE
  {VARIABLE} # is calling for the argument, not for the macro above
+
    {VARIABLE} # is calling for the argument, not for the macro above
 
  #enddef
 
  #enddef
  
 +
=== #ifdef and #ifndef ===
  
Unlike the other preprocessor directives, #ifdef and #ifndef are not
+
Unlike the other preprocessor directives, '''#ifdef''' and '''#ifndef''' are not mere conveniences. They are often necessary to distinguish between different gameplay modes or difficulties (see [[#Built-in macros|Built-in macros]] below).
mere conveniences.
+
 
They are necessary to distinguish between different modes of play.
+
'''Syntax:''' '''#ifdef ''symbol'' ''substitution-if-defined'' [#else ''substitution-if-not-defined'' ] #endif'''
* '''#ifdef ''symbol'' ''substitution-if-stored'' [#else ''substitution-if-not-stored''] #endif''' If ''symbol'' has been stored, the whole block will be replaced by ''substitution-if-stored''.  If not, it will be replaced by ''substitution-if-not-stored'' if it is available. ''symbol'' can take the following values:
+
 
** a campaign difficulty level.  Usually '''EASY''', '''NORMAL''', or '''HARD''', it is decided by the key ''difficulties'' (see [[CampaignWML]]).
+
If ''symbol'' has been defined with '''#define''' or as a built-in macro, the whole block will be replaced by ''substitution-if-stored''.  If not, it will be replaced by ''substitution-if-not-stored'' if it is available.
** a campaign ID. Each campaign stores a preprocessor ID.  See ''define'', [[CampaignWML]].
+
 
** '''MULTIPLAYER''' is stored when the player is playing or setting up a multiplayer game (i.e. a game with '''scenario_type=multiplayer''').
+
'''#ifndef''' is the exact opposite of '''#ifdef''', reversing the logic:
** '''DEBUG_MODE''' is stored when the player has launched wesnoth in debug mode (i.e. with '''-d''')
+
 
** '''TUTORIAL''' is stored when the player is playing the tutorial.
+
'''Syntax:''' '''#ifndef ''symbol'' ''substitution-if-not-stored'' [#else ''substitution-if-stored''] #endif'''
** '''APPLE''' is stored if the computer Wesnoth is being run on is an Apple. (this one is only available to the configuration of the game)
+
 
** '''WESNOTH_VERSION''' {{DevFeature1.9}} corresponds to the Wesnoth version that's running the WML preprocessor in 1.9.5 and later.
+
=== #undef ===
* '''#ifndef''' behaves exactly like '''#ifdef''', except that it inverts the test sense; guarded text is included only if ''symbol'' has not been stored.
+
 
''#undef'' can be useful too if ''#ifdef'' is not enough.
+
'''Syntax:''' '''#undef ''symbol'' '''
* '''#undef ''symbol'' '''  erases ''symbol''
+
 
* '''#ifhave''' {{DevFeature1.9}} checks for the existence of a file. Uses the same relative paths as include directives (see below). Example: '''#ifhave ~add-ons/MyAddon/_main.cfg''' note: For a multiplayer game, the #ifhave directive works only for the host, not for the other clients.
+
Removes the previous definition of the macro named ''symbol''.
* '''#ifnhave''' {{DevFeature1.9}} is the inverted version of '''#ifhave'''.
+
 
* '''#ifver ''symbol'' ''operator'' ''version-number''''' {{DevFeature1.9}} compares a version number defined in a macro against an argument for the purpose of conditional code inclusions, like ''#ifdef''. ''operator'' is one of ''=='' (equal), ''!='' (not equal), ''<'' (less), ''<='' (less or equal), ''>'' (greater), ''>='' (greater or equal). The specified ''symbol'' should have been defined as plain text without more macro inclusions within it, and it must not require any arguments. An example follows:
+
=== #ifhave and #ifnhave ===
 +
 
 +
{{DevFeature1.9}}
 +
 
 +
'''Syntax:''' '''#ifhave ''path'' ''substitution-if-path-exists'' [#else ''substitution-if-path-does-not-exist''] #endif'''
 +
 
 +
Checks for the existence of a file. Uses the same relative paths as include directives (see below).
 +
 
 +
Example:
 +
 
 +
#ifhave ~add-ons/My_Addon/_main.cfg
 +
    {MY_ADDON_MACROS}
 +
#endif
 +
 
 +
'''#ifnhave'''  does the opposite of '''#ifhave''':
 +
 
 +
'''Syntax:''' '''#ifnhave ''path'' ''substitution-if-path-does-not-exist'' [#else ''substitution-if-path-exists''] #endif'''
 +
 
 +
=== #ifver and #ifnver ===
 +
 
 +
{{DevFeature1.9}}
 +
 
 +
'''Syntax:''' '''#ifver ''symbol'' ''operator'' ''version-number'' ''<newline>'' ''substitution-if-condition-met'' [#else ''substitution-if-condition-not-met''] #endif'''
 +
 
 +
Compares a version number defined in a macro against an argument for conditional block inclusions, like ''#ifdef'' and ''#ifhave''. ''operator'' is one of ''=='' (equal), ''!='' (not equal), ''<'' (less), ''<='' (less or equal), ''>'' (greater), ''>='' (greater or equal). The specified ''symbol'' should have been previously defined as plain text without more macro inclusions within it, and it must not require any arguments.
 +
 
 +
Example:
 
  #ifver WESNOTH_VERSION >= 1.9.7+svn
 
  #ifver WESNOTH_VERSION >= 1.9.7+svn
 
     [message]
 
     [message]
Line 77: Line 149:
 
  #endif
 
  #endif
 
  #endif
 
  #endif
note: Similar as for #ifhave, for a multiplayer game, the #ifver directive works only for the host, not for the other clients; meaning that the host's evaluation result is used on the other clients too even if they have different versions (which could thus e.g. lead to the execution of invalid wml on such a client).
 
* '''#ifnver''' {{DevFeature1.9}} is the inverted version of '''#ifver'''.
 
  
These directives expand a file by including other files, ''filename'' may not contain '..' or the directive will be skipped:
+
'''#ifnver''' does the opposite of '''#ifver''':
* '''{''filename''}''' if ''filename'' isn't a predefined symbol (see above), Wesnoth will assume it's a path to a file in the main '''data/''' subdirectory of Wesnoth and include the file it points to here "as is".  Forward slashes('''/''') should be used to separate directories from their elements, even if your platform uses a different symbol such as colon (''':''') or backslash ('''\'''). 
 
* '''{~''filename''}''' similar to above, but the preprocessor assumes that the filename is relative to '''data/''' subdirectory of the user data directory.  The ''user data directory'' varies depending on platform. See [[EditingWesnoth#Where_is_my_user_data_directory.3F|Where_is_my_user_data_directory?]]
 
  
* '''{./''filename''}''' is similar to the '''{''filename''}''' directive, but is assumed to be relative to the directory which contains the file in which this appears.  For example, if used in game.cfg, it is equivalent to '''{''filename''}'''.
+
'''Syntax:''' '''#ifnver ''symbol'' ''operator'' ''version-number'' ''<newline>'' ''substitution-if-condition-not-met'' [#else ''substitution-if-condition-met''] #endif'''
* If ''filename'' points to a directory, the preprocessor will include all files in the directory ''filename'', except subdirectories, in alphabetical order. Some files in such directories are handled specially. 
 
  
If there is a file named ''dir/_main.cfg'' in a directory referenced as '''{''dir''}''', only that ''_main.cfg'' file is processed; it may include other files in itself, of course . This feature is useful for creating WML directories that are self-contained WML packages with their own initializations (like campaigns). This can replace the older convention of having a ''dir.cfg'' at the same level as ''dir'' which does '''{''dir''}''' somewhere in itself.
+
== Built-in macros ==
  
If there are files named ''dir/*/_main.cfg'' in a directory referenced as '''{''dir''}''', where * is any subdirectories ''dir'', then they all are processed (except if there is also a file named ''dir/_main.cfg''). This means that if you have a layout like this:
+
The following macros are automatically defined with empty contents (unless specified otherwise) by the game engine depending on the configuration or gameplay mode.
dir/
 
dir/a/_main.cfg
 
dir/a/other.cfg
 
dir/b/_main.cfg
 
dir/b/other.cfg
 
dir/other.cfg
 
Then '''{dir}''' will process (in alphabetical order): dir/a/_main.cfg, dir/b/_main.cfg, dir/other.cfg.
 
 
 
If there is a file named ''dir/_final.cfg'' in a directory referenced as '''{''dir''}''' (and no ''main.cfg'', so that all files in the directory are processed normally) the ''_final.cfg'' is guaranteed to be processed ''after'' all other files in the directory.
 
  
If there is a file named ''dir/_initial.cfg'' in a directory referenced as '''{''dir''}''' (and no ''main.cfg'', so that all files in the directory are processed normally) the ''_initial.cfg'' is guaranteed to be processed ''before'' all other files in the directory.
+
* A campaign define symbol (see ''define'' in [[CampaignWML]]): defined when playing a single-player campaign.
 +
* A campaign difficulty level, usually '''EASY''', '''NORMAL''' or '''HARD''' (see ''difficulties'' in [[CampaignWML]]): defined according to the chosen difficulty when starting a single-player campaign, also stored in saved games.
 +
* '''MULTIPLAYER''': defined when in multiplayer mode.
 +
* '''TUTORIAL''': defined when playing the tutorial campaign.
 +
* '''DEBUG_MODE''': defined when the game has been launched in debug mode (i.e. with '''-d''' or '''--debug''' in the command line).
 +
* '''APPLE''': defined while processing the main game data when running on Mac OS X.
 +
* '''WESNOTH_VERSION''' {{DevFeature1.9}}: defined with the game version number as its content when running the WML preprocessor on 1.9.5 and later. Due to a bug, this isn't defined when using the command-line version unless using 1.9.10 and later.
  
 +
== Command-line preprocessor ==
  
Note that the parser was changed various times, so don't expect older Wesnoth version to behave exactly the same.
+
{{DevFeature1.9}}
  
{{DevFeature1.9}} Including non-existent file is now a fatal error and halts the preprocessing. Use #ifhave to test for the existence of files that aren't a part of your own add-on.
+
Since Wesnoth 1.9.0, there is a new command line option available:
 +
'''--preprocess[=''define1,''define2'',...] ''<file/directory>'' ''<target directory>'' '''
 +
Or the short form:
 +
'''-p[=''define1'',''define2'',...] ''<file/directory>'' ''<target directory>'' '''
  
== The preprocessor command line ==
+
The define1,define2,... is a list of defines, to be added ''before'' anything is preprocessed.
{{DevFeature1.9}}
 
  
As of wesnoth 1.9, there is a new command line available:
+
The preprocess command will preprocess '''first''' the common config files in ''data/core'', and afterwards, the specified ones. You can specify a single file to be preprocessed (if you want to preprocess multiple separate files, you'll need to run a different command line for each one), or an entire directory, which will be preprocessed according to the rules used by the inclusion directive above.
--preprocess[=define1,define2,...] <file/folder> <target directory>
 
or the short form:
 
-p[=define1,define2,...] <file/folder> <target directory>
 
  
The define1,define2,... is a list of defines, to be added ''before'' anything is preprocessed.  
+
The resultant preprocessed files will be written in the target directory. There will be two types of files: .cfg files, and .plain files containing line markers and textdomain changes.
  
The preprocess command will '''preprocess first''' the common config files in ''data/core'', and afterwards, the specified ones. You can specifiy a single file to be preprocesses (if you want to preprocess multiple separate files, you have to invoke the command separately), or an entire folder (this will be preprocessed on the known preprocessor rules).
+
If <file/directory> and <target directory> are not absolute paths, they will be treated as relative to the game's executable path.
 
 
The resulted preprocessed files will be written in the target directory. There will be 2 types of files: the .cfg files and .plain files (this contain the line symbols and textdomain changes)
 
 
 
If <file/folder> and <target directory> are not in ''absolute path'' form, they will be treated like relative to the wesnoth's executable path.  
 
  
 
Some examples:
 
Some examples:
-p ~/wesnoth/data/campaigns/tutorial ~/result
 
will preprocess entire tutorial folder, and write the resulted files in the ~/result folder
 
-p=MULTIPLAYER data/campaigns/my_campaign/some_scenario.cfg data/result
 
will add the ''MULTIPLAYER'' define in defines list and then preprocess the scenario config file
 
-p=SINGLEPLAYER,HARD ~/wesnoth/data/campaigns/my_campaign ~/result
 
will add the ''SINGLEPLAYER'' and ''HARD'' defines before preprocessing the files.
 
  
Note! As you saw in the examples, the '=' character is mandatory if you want to specify any auxiliar defines
+
* Preprocess the entire tutorial dir, and write the results in the ~/result folder:
 +
-p ~/wesnoth/data/campaigns/tutorial ~/result
 +
* Add the ''MULTIPLAYER'' define to the list and preprocess a scenario's config file:
 +
-p=MULTIPLAYER ~/.wesnoth/data/add-ons/My_Campaign/scenarios/01_First_Scenario.cfg ~/result
 +
* Add the ''SINGLEPLAYER'' and ''HARD'' defines before preprocessing a campaign's files:
 +
-p=SINGLEPLAYER,HARD ~/.wesnoth/data/add-ons/My_Campaign ~/result
  
If you want a more detailed log, you can simply add the command: "--log-debug=all" or "--log-info=all" before the preprocessing command, so you will see how things are parsed,etc.
+
If you want a more detailed (and potentially overwhelming) log, you can simply add the switches '''--log-debug=all''' or '''--log-info=all''' to the command line, so you can see how things are preprocessed in detail.
  
 
== See Also ==
 
== See Also ==
 +
 
* [[SyntaxWML]]
 
* [[SyntaxWML]]
 
* [[ReferenceWML]]
 
* [[ReferenceWML]]
 
  
 
[[Category: WML Reference]]
 
[[Category: WML Reference]]

Revision as of 22:24, 29 November 2011

[edit]WML Tags

A:

abilities, about, achievement, achievement_group, add_ai_behavior, advanced_preference, advancefrom, advancement, advances, affect_adjacent, ai, allied_with, allow_end_turn, allow_extra_recruit, allow_recruit, allow_undo, and, animate, animate_unit, animation, aspect, attack (replay, weapon), attack_anim, attacks (special, stats), avoid;

B:

base_unit, background_layer, berserk, binary_path, break, brush;

C:

campaign, cancel_action, candidate_action, capture_village, case, chance_to_hit, change_theme, chat, checkbox, choice, choose, clear_global_variable, clear_menu_item, clear_variable, color_adjust, color_palette, color_range, command (action, replay), continue, credits_group, criteria;

D:

damage, death, deaths, default, defend, defends, defense, delay, deprecated_message, destination, difficulty, disable, disallow_end_turn, disallow_extra_recruit, disallow_recruit, do, do_command, drains, draw_weapon_anim;

E:

editor_group, editor_music, editor_times, effect, else (action, animation), elseif, endlevel, end_turn (action, replay), enemy_of, engine, entry (credits, options), era, event, experimental_filter_ability, experimental_filter_ability_active, experimental_filter_specials, extra_anim;

F:

facet, facing, fake_unit, false, feedback, female, filter (concept, event), filter_adjacent, filter_adjacent_location, filter_attack, filter_attacker, filter_base_value, filter_condition, filter_defender, filter_enemy, filter_location, filter_opponent, filter_own, filter_owner, filter_radius, filter_recall, filter_second, filter_second_attack, filter_self, filter_side, filter_student, filter_vision, filter_weapon, filter_wml, find_path, fire_event, firststrike, floating_text, found_item, for, foreach, frame;

G:

game_config, get_global_variable, goal, gold, gold_carryover;

H:

harm_unit, has_ally, has_attack, has_unit, has_achievement, have_location, have_unit, heal_on_hit, heal_unit, healed_anim, healing_anim, heals, hide_help, hide_unit, hides;

I:

idle_anim, if (action, animation, intro), illuminates, image (intro, terrain), init_side, insert_tag, inspect, item, item_group;

J:

jamming_costs, join;

K:

kill, killed;

L:

label, language, leader, leader_goal, leadership, leading_anim, levelin_anim, levelout_anim, lift_fog, limit, literal, load_resource, locale, lock_view, lua;

M:

male, menu_item, message, micro_ai, missile_frame, modification, modifications, modify_ai, modify_side, modify_turns, modify_unit, modify_unit_type, move, move_unit, move_unit_fake, move_units_fake, movement_anim, movement costs, movetype, multiplayer, multiplayer_side, music;

N:

not, note;

O:

object, objective, objectives, on_undo, open_help, option, options, or;

P:

part, petrifies, petrify, place_shroud, plague, poison, post_movement_anim, pre_movement_anim, primary_attack, primary_unit, print, progress_achievement, put_to_recall_list;

R:

race, random_placement, recall (action, replay), recalls, recruit, recruit_anim, recruiting_anim, recruits, redraw, regenerate, remove_event, remove_item, remove_object, remove_shroud, remove_sound_source, remove_time_area, remove_trait, remove_unit_overlay, repeat, replace_map, replace_schedule, replay, replay_start, reset_fog, resistance (ability, unit), resistance_defaults, resource, return, role, rule;

S:

save, scenario, screen_fade, scroll, scroll_to, scroll_to_unit, secondary_attack, secondary_unit, section, select_unit, sequence, set_achievement, set_extra_recruit, set_global_variable, set_menu_item, set_recruit, set_specials, set_variable, set_variables, sheath_weapon_anim, show_if (message, objective, set_menu_item), show_objectives, side, skirmisher, slider, slow, snapshot, sound, sound_source, source (replay, teleport), special_note, specials, split, stage, standing_anim, statistics, status, store_gold, store_items, store_locations, store_map_dimensions, store_reachable_locations, store_relative_direction, store_side, store_starting_location, store_time_of_day, store_turns, store_unit, store_unit_defense, store_unit_defense_on, store_unit_type, store_unit_type_ids, store_villages, story, swarm, sub_achievement, switch, sync_variable;

T:

target, team, teleport (ability, action), teleport_anim, terrain, terrain_defaults, terrain_graphics, terrain_mask, terrain_type, test, test_condition, test_do_attack_by_id, text_input, textdomain, theme, then, tile, time, time_area, topic, toplevel, trait, transform_unit, traveler, true, tunnel;

U:

unhide_unit, unit, unit_overlay, unit_type, unit_worth, units, unlock_view, unpetrify, unstore_unit, unsynced;

V:

value, variable, variables, variant, variation, victory_anim, village, vision_costs, volume;

W:

while, wml_message, wml_schema;

Z:

zoom;

Overview

Wesnoth loads just one configuration file directly: data/_main.cfg. However the WML preprocessor allows to include more files. Whenever a WML file is read by Wesnoth, it is passed through the preprocessor.

The preprocessor can interpret a simple language of string expansions known as macros. A macro should always be defined before the place where it needs to be used.

The preprocessor is applied recursively, so included files will be parsed for macros, and after macro expansion will be parsed for macros again, and so on. As a result, you should not write a recursive macro that references itself, because it will cause errors (but, alas, not necessarily error messages).

Preprocessor directives

The following directives are used to create and use macros, i.e. shortcuts which reduce repetition of information. See the macro reference for the list of predefined core macros.

The preprocessor has changed several times, so don't expect old Wesnoth versions to behave exactly the same as the current stable and development series.

Note: In multiplayer scenarios, these directives will appear to work only for the host and not for other clients. This is because the preprocessor is run only on the host, and the clients receive the resultant WML from the server. It's particularly important to keep this in mind before using preprocessor conditionals.

#define

Syntax: #define symbol [parameters] <newline> substitution #enddef

All subsequent occurences of {symbol [arguments]} (see below) will be replaced by the contents of the substitution block, with all occurrences of any parameter {parameter} within substitution replaced by the corresponding value in arguments. For example, the ENEMY_UNIT macro for the macro inclusion example below could be defined as follows:

#define ENEMY_UNIT TYPE X Y
## the ordering above is important, since the preprocessor does not distinguish
## data into different types; only the ordering is used to determine which
## arguments apply to which parameters.
[unit]
    type={TYPE} ## the unit will be of type TYPE, so different
                ## instantiations
                ## of this macro can create different units.
    x={X}
    y={Y}
    side=2 ## the unit will be an enemy, regardless of the parameter
           ## values. This reduces "repetition of information",
           ## since it is no longer necessary to specify
           ## each created unit as an enemy.
[/unit]
#enddef

(See SingleUnitWML for further information on creating units using WML.)

Inclusion directive {}

This directive can be used to include macros, single files or sets of files from a target directory.

File/directory inclusions

Syntax: {path}

Includes the file with the specified path, which will in turn run the preprocessor on it and perform any required substitutions or inclusions within it. The path may not contain .. or the inclusion will be skipped.

The exact location in which the path will be resolved will depend on its prefix:

  • {path}: If path isn't a known macro (see below), the game will assume it's a relative path to a file in the main game data/ directory and include it.
  • {~path}: As above, but instead of the game data directory, the path is resolved relative to the user data/ directory, where user made add-ons can normally be found.
  • {./path}: The path is resolved relative to the location of the current file containing this inclusion.

Information for locating the user data and game data directories can be found in EditingWesnoth.

Forward slashes (/) should always be used as the path delimiter, even if your platform uses a different symbol such as colons (:) or backslashes (\)! It is also very important to respect the actual letter case used to name files and directories for compatibility with case-sensitive filesystems on Unix-based operating systems.

When path points to a directory instead of a file, the preprocessor will include all files under it, except subdirectories, in alphabetical order. Some files are handled in a special fashion:

  • If there's a file named _main.cfg in the target directory, only that file will be included and preprocessed. It may include other files from its own directory or subdirectories within it, of course. This is used for managing WML directories as self-contained packages, like user made add-ons.
  • If there are files named _main.cfg in subdirectories of the target and there isn't one in the target itself, they will be all preprocessed. Given the following layout:
dir/
dir/a/_main.cfg
dir/a/other.cfg
dir/b/_main.cfg
dir/b/other.cfg
dir/other.cfg

Using {dir} will cause dir/a/_main.cfg, dir/b/_main.cfg and dir/other.cfg to be included.

  • If there's a file named _final.cfg but no _main.cfg, the file is guaranteed to be included and processed after all the other files in the directory.
  • If there's a file named _initial.cfg but no _main.cfg, the file is guaranteed to be included and processed before all the other files in the directory.

Macro inclusions

Syntax: {symbol [arguments]}

If the macro named symbol is defined, the preprocessor will replace this instruction by the expression symbol was previously defined as, using arguments as parameters. The number of arguments must be exactly the same as in the original definition or an error will occur.

You can create multiple word arguments by using parentheses to delimit the contents. For example, in {ENEMY_UNIT Wolf Rider 18 24} the four words will be interpreted as separate arguments and cause the preprocessor to fail since the macro was defined above with only three; instead, you should use {ENEMY_UNIT (Wolf Rider) 18 24}.

Using the name of an existing macro as the name of a macro argument is possible, but the argument will always take precedence over the original macro:

#define VARIABLE
#enddef
#define MACRO VARIABLE
    {VARIABLE} # is calling for the argument, not for the macro above
#enddef

#ifdef and #ifndef

Unlike the other preprocessor directives, #ifdef and #ifndef are not mere conveniences. They are often necessary to distinguish between different gameplay modes or difficulties (see Built-in macros below).

Syntax: #ifdef symbol substitution-if-defined [#else substitution-if-not-defined ] #endif

If symbol has been defined with #define or as a built-in macro, the whole block will be replaced by substitution-if-stored. If not, it will be replaced by substitution-if-not-stored if it is available.

#ifndef is the exact opposite of #ifdef, reversing the logic:

Syntax: #ifndef symbol substitution-if-not-stored [#else substitution-if-stored] #endif

#undef

Syntax: #undef symbol

Removes the previous definition of the macro named symbol.

#ifhave and #ifnhave

Template:DevFeature1.9

Syntax: #ifhave path substitution-if-path-exists [#else substitution-if-path-does-not-exist] #endif

Checks for the existence of a file. Uses the same relative paths as include directives (see below).

Example:

#ifhave ~add-ons/My_Addon/_main.cfg
    {MY_ADDON_MACROS}
#endif

#ifnhave does the opposite of #ifhave:

Syntax: #ifnhave path substitution-if-path-does-not-exist [#else substitution-if-path-exists] #endif

#ifver and #ifnver

Template:DevFeature1.9

Syntax: #ifver symbol operator version-number <newline> substitution-if-condition-met [#else substitution-if-condition-not-met] #endif

Compares a version number defined in a macro against an argument for conditional block inclusions, like #ifdef and #ifhave. operator is one of == (equal), != (not equal), < (less), <= (less or equal), > (greater), >= (greater or equal). The specified symbol should have been previously defined as plain text without more macro inclusions within it, and it must not require any arguments.

Example:

#ifver WESNOTH_VERSION >= 1.9.7+svn
    [message]
        speaker=narrator
        message= _ "I’m on Wesnoth 1.9.7+svn, 1.9.8 or later!"
    [/message]
#else
#ifver WESNOTH_VERSION == 1.9.7
    [message]
        speaker=narrator
        message= _ "I’m on Wesnoth 1.9.7, and I’ll include some workaround code for bug #9001!"
    [/message]
#endif
#endif

#ifnver does the opposite of #ifver:

Syntax: #ifnver symbol operator version-number <newline> substitution-if-condition-not-met [#else substitution-if-condition-met] #endif

Built-in macros

The following macros are automatically defined with empty contents (unless specified otherwise) by the game engine depending on the configuration or gameplay mode.

  • A campaign define symbol (see define in CampaignWML): defined when playing a single-player campaign.
  • A campaign difficulty level, usually EASY, NORMAL or HARD (see difficulties in CampaignWML): defined according to the chosen difficulty when starting a single-player campaign, also stored in saved games.
  • MULTIPLAYER: defined when in multiplayer mode.
  • TUTORIAL: defined when playing the tutorial campaign.
  • DEBUG_MODE: defined when the game has been launched in debug mode (i.e. with -d or --debug in the command line).
  • APPLE: defined while processing the main game data when running on Mac OS X.
  • WESNOTH_VERSION Template:DevFeature1.9: defined with the game version number as its content when running the WML preprocessor on 1.9.5 and later. Due to a bug, this isn't defined when using the command-line version unless using 1.9.10 and later.

Command-line preprocessor

Template:DevFeature1.9

Since Wesnoth 1.9.0, there is a new command line option available:

--preprocess[=define1,define2,...] <file/directory> <target directory> 

Or the short form:

-p[=define1,define2,...] <file/directory> <target directory> 

The define1,define2,... is a list of defines, to be added before anything is preprocessed.

The preprocess command will preprocess first the common config files in data/core, and afterwards, the specified ones. You can specify a single file to be preprocessed (if you want to preprocess multiple separate files, you'll need to run a different command line for each one), or an entire directory, which will be preprocessed according to the rules used by the inclusion directive above.

The resultant preprocessed files will be written in the target directory. There will be two types of files: .cfg files, and .plain files containing line markers and textdomain changes.

If <file/directory> and <target directory> are not absolute paths, they will be treated as relative to the game's executable path.

Some examples:

  • Preprocess the entire tutorial dir, and write the results in the ~/result folder:
-p ~/wesnoth/data/campaigns/tutorial ~/result
  • Add the MULTIPLAYER define to the list and preprocess a scenario's config file:
-p=MULTIPLAYER ~/.wesnoth/data/add-ons/My_Campaign/scenarios/01_First_Scenario.cfg ~/result
  • Add the SINGLEPLAYER and HARD defines before preprocessing a campaign's files:
-p=SINGLEPLAYER,HARD ~/.wesnoth/data/add-ons/My_Campaign ~/result

If you want a more detailed (and potentially overwhelming) log, you can simply add the switches --log-debug=all or --log-info=all to the command line, so you can see how things are preprocessed in detail.

See Also