PreprocessorRef

From The Battle for Wesnoth Wiki
Revision as of 03:27, 23 March 2011 by Shadowm (talk | contribs) (#ifver: more on syntax details)

[edit]WML Tags

A:

abilities, about, add_ai_behavior, advance, 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, attack_anim, attacks, avoid;

B:

base_unit, berserk, binary_path, break, brush;

C:

campaign, cancel_action, candidate_action, capture_village, case, chance_to_hit, change_theme, chat, choose, clear_global_variable, clear_menu_item, clear_variable, color_adjust, color_range, command (action, replay), continue, 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, era, event, 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_vision, filter_weapon, filter_wml, find_path, fire_event, firststrike, floating_text, for, foreach, frame, full_heal;

G:

game_config, get_global_variable, goal, gold, gold_carryover;

H:

harm_unit, has_ally, has_attack, has_unit, 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), illuminates, image, 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, portrait, post_movement_anim, pre_movement_anim, primary_attack, primary_unit, print, 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_unit_overlay, repeat, replace_map, replace_schedule, replay, replay_start, reset_fog, resistance (ability, unit), resistance_defaults, resource, return, role, rule;

S:

save, scenario, scroll, scroll_to, scroll_to_unit, secondary_attack, secondary_unit, section, select_unit, sequence, set_extra_recruit, set_global_variable, set_menu_item, set_recruit, set_specials, set_variable, set_variables, sheath_weapon_anim, show_if (message, set_menu_item), show_objectives, side, skirmisher, slow, snapshot, sound, sound_source, source (replay, teleport), 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_type, store_unit_type_ids, store_villages, story, swarm, switch, sync_variable;

T:

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

U:

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

V:

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

W:

while, wml_message, wml_schema;

Z:

zoom;

The WML preprocessor

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, because it will cause errors (but, alas, not necessarily error messages).

The following directives are used to create and use macros, i.e. shortcuts which reduce repetition of information. See UtilWML, UsefulWMLFragments for examples and 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;
                     ## since WML 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 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.:

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


Unlike the other preprocessor directives, #ifdef and #ifndef are not mere conveniences. They are necessary to distinguish between different modes of play.

  • #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).
    • 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).
    • 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.
    • 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 Template:DevFeature1.9 corresponds to the Wesnoth version that's running the WML preprocessor in 1.9.5 and later.
  • #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.

  • #undef symbol erases symbol
  • #ifhave Template: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.
  • #ifnhave Template:DevFeature1.9 is the inverted version of #ifhave.
  • #ifver symbol operator version-number Template: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:
#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

These directives expand a file by including other files, filename may not contain '..' or the directive will be skipped:

  • {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 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}.
  • 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.

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:

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.


Note that the parser was changed various times, so don't expect older Wesnoth version to behave exactly the same.

Template: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.

The preprocessor command line

Template:DevFeature1.9

As of wesnoth 1.9, there is a new command line available:

--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 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).

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:

-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

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.

See Also