https://wiki.wesnoth.org/api.php?action=feedcontributions&user=Jmichael&feedformat=atomThe Battle for Wesnoth Wiki - User contributions [en]2024-03-28T13:00:55ZUser contributionsMediaWiki 1.31.16https://wiki.wesnoth.org/index.php?title=MultiplayerContent&diff=71900MultiplayerContent2023-12-10T04:58:20Z<p>Jmichael: </p>
<hr />
<div>{{Translations}}<br />
{{Create}}<br />
<br />
This page is dedicated to the syntax modifications necessary to enable a scenario or campaign to work in multiplayer mode. Only the differences between multiplayer and singleplayer are documented here, so you might want to read [[BuildingScenarios]] and [[BuildingCampaigns]] first.<br />
<br />
<b>Note:</b> This page assumes you have a basic add-on set up as described in [[AddonStructure]], as well as some coded content.<br />
<br />
== Multiplayer scenarios ==<br />
<br />
You can play standalone scenarios in multiplayer mode without setting up a campaign (this isn't possible in singleplayer). Simply make the following modifications:<br />
<br />
* In the _main.cfg file, use <b>#ifdef MULTIPLAYER</b> as the load conditional<br />
* In the scenario configuration file, use a toplevel <b>[multiplayer]</b> tag instead of <b>[scenario]</b> tag<br />
<br />
== Multiplayer campaigns ==<br />
<br />
<b>Note:</b> This section is relevant for version '''1.11.6 and higher'''.<br />
<br />
Wesnoth has basic support for campaigns that can be played in multiplayer mode, with your friends, or even with an AI. There will still be some things that don't work as expected since some functionality for multiplayer campaigns is still being developed. While you can and should test your work in hotseat mode, there are a number of things that might behave differently under real network conditions (mostly affecting scenario and side parameters). Keep this in mind and make sure to test your campaign over the net, too.<br />
<br />
==== The Campaign Tag ====<br />
<br />
Multiplayer campaigns use the [campaign] tag, thus allowing the same campaign to work both in both modes. The only difference is that game also checks for a couple optional tags when campaign is used in multiplayer. They could be found on the [[CampaignWML]] page. Additionally, the '''type''' attribute should be explicitly set to '''mp''' or '''hybrid''' or otherwise a campaign would not be visible in multiplayer.<br />
<br />
==== The Scenario File ====<br />
<br />
A scenario in a multiplayer campaign is almost identical to a scenario in a singleplayer campaign. You only need to change the following:<br />
<br />
* Every scenario which should not be available to start directly, should have '''allow_new_game=no''' inside the [scenario] tag. This will prevent it from showing as an "entry point" in the multiplayer configuration screen. {{DevFeature1.13|?}} This is no longer true, '''allow_new_game=no''' is now the default<br />
* For scenarios that don't use '''allow_new_game=no''', one could make use of '''new_game_title''' to have a custom title. This is useful if the campaign is supposed to have multiple entry points such as chapters.<br />
* Each human-controlled side needs to have a '''controller=human''' key defined in its [side] tag to work in network mode.<br />
* Each human-controlled side also needs to have a '''save_id''' defined inside its [side] tag. Each side's save_id should be unique, and it should be the same in every scenario. Without this, your recall lists and leaders will not carry over from one scenario to the next. {{DevFeature1.13|?}} This is no longer true, '''save_id''' defaults to '''id''', just like in sp<br />
* If you want to carry over information for ai sides, they need to have a '''save_id''' defined and '''persistent=yes''' in their [side] tags. (The same is true for singleplayer campaigns)<br />
* {{DevFeature1.13|?}} You can use '''previous_save_id''' to tell the engine which player shodul control which side. For example if you have a multiplayer campaign and in scenario1 you have two sides with ids Kalenz and Landar, and in scenario2 you have sides with ids Konrad and Delfador you can use '''previous_save_id=Kalenz''' and '''previous_save_id=Landar''' in scenario2 to make sure that the player that previously controlled Kalenz now controls Konrad and the player that previously controlled Landar now controlls Delfador.<br />
<br />
== Other Notes ==<br />
<br />
* To ensure that your scenario settings won't get overwritten by the settings from the multiplayer configure dialog, you can set '''force_use_map_settings=true'''. However, if your campaign is not intended to be played strictly with default settings, you can set it to '''false'''.<br />
<br />
* If any settings are still taken from the multiplayer configure dialog, it is possible to overwrite them in the prestart event. Useful tags include [set_recruit], [modify_side], [modify_turns],[remove_shroud], and [lift_fog]. You might also want to use <b>experience_modifier=100</b> in the [scenario] tag.<br />
<br />
==== Synchronization ====<br />
<br />
When creating multiplayer content, you should be cautious of code that might change on one client but not the other players'. Not doing so can result in an [[OOS_(Out_of_Sync) | Out of Sync]] error. Most [event]s are synced across all clients, but a few still have issues. A full list of safe events can be found on the [[EventWML#Multiplayer_safety | EventWML]] page.<br />
<br />
== See Also ==<br />
<br />
* [[Create]]<br />
* [[BuildingScenarios]]<br />
* [[BuildingCampaigns]]<br />
<br />
[[Category:Create]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=DirectActionsWML&diff=56448DirectActionsWML2015-06-24T02:48:11Z<p>Jmichael: /* [endlevel] */</p>
<hr />
<div>{{WML Tags}}<br />
== Direct actions ==<br />
<br />
Direct actions are actions that have a direct effect on gameplay. They can be used inside of [[EventWML|events]].<br />
<br />
The following tags are actions:<br />
<br />
=== [endlevel] ===<br />
Ends the scenario.<br />
* '''result''': before the scenario is over, all events with ''name=result'' are triggered. If ''result=victory'', the player progresses to the next level (i.e., the next scenario in single player); if ''result=defeat'', the game returns to the main menu. <br />
<br />
When the result is "victory" the following keys can be used:<br />
* '''bonus''': whether the player should get bonus gold (maximum possible gold that could have been earned by waiting the level out). The default is bonus=yes.<br />
* '''carryover_report''': whether the player should receive a summary of the scenario outcome, the default is carryover_report=yes.<br />
* '''save''': whether a start-of-scenario save should be created for the next scenario, the default is save=yes. Do not confuse this with saving of replays for the current scenario.<br />
* '''replay_save''': whether a replay save for the current scenario is allowed, the default is replay_save=yes. If yes, the player's settings in preferences will be used to determine if a replay is saved. If no, will override and not save a replay.<br />
* '''linger_mode''': If ...=yes, the screen is greyed out and there's the possibility to save before advancing to the next scenario, the default is linger_mode=yes.<br />
* '''reveal_map''': (Multiplayer only) (Default is 'yes') If 'no', shroud doesn't disappear when game ended.<br />
* '''next_scenario''': (default specified in '''[scenario]''' tag) the ID of the next scenario that should be played. All units that side 1 controls at this point become available for recall in ''next_scenario''.<br />
* '''carryover_percentage''': by default 80% of the gold is carried over to the next scenario, with this key the amount can be changed.<br />
* '''carryover_add''': if true the gold will be added to the starting gold the next scenario, if false the next scenario will start with the amount of the current scenario (after taxes) or the minimum in the next scenario. Default is false.<br />
* '''music''': (default specified in '''[scenario]''' or '''[game_config]''' tags) a comma-separated list of music tracks from which one will be chosen and played once after any events related to the end of level result are executed; by default, victory_music is used on victory, and defeat_music on defeat.<br />
* '''end_credits''': Whether to display the credits screen at the end of a single-player campaign. Defaults to ''yes''. Note that this has cumulative effects over the campaign - it persists even if the endlevel does not trigger the end of the campaign. See also [[CampaignWML]].<br />
* '''end_text''': (translatable) Text that is shown centered in a black screen at the end of a campaign. Defaults to "The End". Note that this has cumulative effects over the campaign - it persists even if the endlevel does not trigger the end of the campaign. See also [[CampaignWML]].<br />
* '''end_text_duration''': Delay, in milliseconds, before displaying the game credits at the end of a campaign. In other words, for how much time '''end_text''' is displayed on screen. Defaults to 3500. Note that this has cumulative effects over the campaign - it persists even if the endlevel does not trigger the end of the campaign. See also [[CampaignWML]].<br />
* <strike>'''[next_scenario_settings]''': Any tags or attribute children of this optional argument to [endlevel] are merged into the scenario/multiplayer tag of the *next* scenario. This allows you to e.g. reconfigure the [side] tags or settings, just before load. </strike> This feature was removed in 1.11.17, it might be redesigned and reintroduced.<br />
* <strike>'''[next_scenario_append]''': Any tags of this optional argument are appended at high level to the next scenario. This is most appropriate for [event] tags, although you may find other uses. Example test scenario for these features: https://gna.org/support/download.php?file_id=20119 </strike> This feature was removed in 1.11.17, it might be redesigned and reintroduced.<br />
* '''[result]''' {{DevFeature1.13|0}} Allows specification of a side specific result, this is for competitive multiplayer scenarios/campaigns where it might happen that one player wins but another player loses. The following attributes are accepted and have the same effect as in '''[endlevel]''':<br />
** '''result'''<br />
** '''bonus'''<br />
** '''carryover_percentage'''<br />
** '''carryover_add'''<br />
<br />
And there is also<br />
** '''side''' The number of the side for which these results should apply.<br />
<br />
=== [unit] ===<br />
Places a unit on the map. For syntax see [[SingleUnitWML]].<br />
* {{Short Note:Predefined Macro|GENERIC_UNIT}}<br />
* '''to_variable''': spawn directly into a variable instead of on the map.<br />
<br />
=== [recall] ===<br />
Recalls a unit taking into account any [http://wiki.wesnoth.org/SingleUnitWML filter_recall] of the leader. The unit is recalled free of charge, and is placed near its leader, e.g., if multiple leaders are present, near the first found which would be able to normally recall it.<br />
<br />
If neither a valid map location is provided nor a leader on the map would be able to recall it, the tag is ignored.<br />
<br />
* [[StandardUnitFilter]]: the first matching unit will be recalled. If no units match this tag is ignored. Do not use a [filter] tag. If a comma separated list is given, every unit currently considered for recall is checked against all the types (not each single one of the types against all units).<br />
* '''x,y''': the unit is placed here instead of next to the leader.<br />
* '''show''': yes/no, default yes: whether the unit is animated (faded in) or instantly displayed<br />
* '''fire_event''': boolean yes|no (default no); whether any according prerecall or recall events shall be fired.<br />
* '''check_passability''': (boolean yes|no, default yes): If yes, checks for terrain passability when placing the unit (a nearby passable hex is chosen).<br />
<br />
=== [teleport] ===<br />
Teleports a unit on map. {{Short Note:Predefined Macro|TELEPORT_UNIT}}<br />
* '''[filter]''': [[StandardUnitFilter]] the first unit matching this filter will be teleported.<br />
* '''x,y''': the position to teleport to.<br />
* '''clear_shroud''': should shroud be cleared on arrival<br />
* '''animate''': should a teleport animation be played (if the unit doesn't have a teleport animation, it will fade out/fade in)<br />
* '''check_passability''': (boolean yes|no, default yes): normally, units will not be teleported into terrain that is impassable for them. Setting this attribute to "no" permits it.<br />
<br />
(Note: There is also a ability named teleport, see [[AbilitiesWML]].)<br />
<br />
=== [terrain_mask] ===<br />
Changes the terrain on the map. See [[TerrainMaskWML]].<br />
<br />
=== [terrain] ===<br />
Changes the terrain on the map.<br />
* '''terrain''': the character of the terrain to use. See [[TerrainCodesWML]] to see what letter a type of terrain uses.<br />
* [[StandardLocationFilter]]. This [[StandardLocationFilter]]'s terrain= key is used for the new terrain, filtering by terrain can be done with a nested [[StandardLocationFilter]]: [and]terrain=terrain_string_to_be_filtered_for.<br />
* '''layer''': (overlay|base|both, default=both) only change the specified layer.<br />
* '''replace_if_failed''': (default=no) When replacing just one layer failed, try to replace the whole terrain. If '''terrain''' is an overlay only terrain, use the default_base as base layer. If the terrain has no default base, do nothing.<br />
<br />
If you want to remove the overlays from a terrain and leave only the base, use:<br />
layer=overlay<br />
terrain="^"<br />
<br />
<b>Note:</b> When a hex changes from a village terrain to a non-village terrain, and a team owned that village it loses that village. When a hex changes from a non-village terrain to a village terrain and there is a unit on that hex it does not automatically capture the village. The reason for not capturing villages it that there are too many choices to make; should a unit loose its movement points, should capture events be fired. It is easier to do this as wanted by the author in WML.<br />
<br />
=== [gold] ===<br />
Gives sides gold.<br />
* '''amount''': the amount of gold to give.<br />
* '''side''': (default=1) the number of the side to give the gold to. Can be a comma-separated list of sides. note: Default side=1 for empty side= is deprecated.<br />
* [[StandardSideFilter]] tags and keys; default for empty side= is all sides, as usual in a SSF.<br />
<br />
=== [unstore_unit] ===<br />
Creates a unit from a game variable, and activates it on the playing field. This must be a specific variable describing a unit, and may not be an array -- to unstore an entire array, iterate over it. The variable is not cleared. See also [[InternalActionsWML#.5Bstore_unit.5D|[store_unit]]], [[ConditionalActionsWML#.5Bwhile.5D|[while]]] and [[InternalActionsWML#.5Bclear_variable.5D|[clear_variable]]].<br />
* '''variable''': the name of the variable.<br />
* '''find_vacant''': whether the unit should be placed on the nearest vacant tile to its specified location. If this is set to 'no'(default), then any unit on the same tile as the unit being unstored will be destroyed. <br />
* '''check_passability''': (boolean yes|no, default yes): If yes, checks for terrain passability when placing the unit. This key has no effect if find_vacant=no (no check performed then). Before 1.9 this key is always "no".<br />
* '''text''': (translatable) floating text to display above the unit, such as a damage amount<br />
* '''red''', '''green''', '''blue''': (default=0,0,0) the color to display the text in. Values vary from 0-255. You may find it convenient to use the {COLOR_HARM} or {COLOR_HEAL} macro instead. (Use {COLOR_HARM} or {COLOR_HEAL} instead of the whole red,green,blue= line.)<br />
* '''advance''': (default=true) if true the unit is advanced if it has enough XP. When modifying XP make sure to do it from inside a [[EventWML#Multiplayer_safety|synchronized event]] or it may lead to OOS errors especially when several advancement paths exist. Note that advance and post advance events are called, so infinite loops can happen.<br />
* '''fire_event''': (boolean yes|no, default no) Whether any advance/post advance events shall be fired if an advancement takes place, no effect otherwise.<br />
* '''animate''': (boolean yes|no, default yes) Whether "levelout" and "levelin" (or fade to white and back) animations shall be played if an advancement takes place, no effect otherwise.<br />
* '''x''' ,'''y''': override unit location, "x,y=recall,recall" will put the unit on the unit's side's recall list.<br />
Units can be unstored with negative (or zero) hit points. This can be useful if modifying a unit in its last_breath event (as the unit's death is already the next step), but tends to look wrong in other cases. In particular, it is possible to have units with negative hit points in play. Such units are aberrations, subject to unusual behavior as the game compensates for them. (For example, such units are currently automatically hit&ndash;and killed&ndash;in combat.) The details of the unusual behavior are subject to change between stable releases without warning.<br />
<br />
=== [allow_recruit] ===<br />
Allows a side to recruit units it couldn't previously recruit.<br />
* '''type''': the types of units that the side can now recruit.<br />
* '''side''': (default=1) the number of the side that is being allowed to recruit the units. This can be a comma-separated list note: Default side=1 for empty side= is deprecated.<br />
* [[StandardSideFilter]] tags and keys; default for empty side= is all sides, as usual in a SSF.<br />
<br />
=== [allow_extra_recruit] ===<br />
Allows a leader to recruit units it couldn't previously recruit.<br />
These types add to the types the leader can recruit because of [side]recruit=.<br />
* '''extra_recruit''': the types of units that the unit can now recruit.<br />
* '''[[StandardUnitFilter]]''': All units matching this filter are modified. Does not match on recall list units.<br />
<br />
=== [disallow_recruit] ===<br />
Prevents a side from recruiting units it could previously recruit.<br />
* '''type''': the types of units that the side can no longer recruit. {{DevFeature1.13|0}} If omitted, all recruits for matching sides will be disallowed.<br />
* '''side''': (default=1) the number of the side that may no longer recruit the units. This can be a comma-separated list note: Default side=1 for empty side= is deprecated.<br />
* [[StandardSideFilter]] tags and keys; default for empty side= is all sides, as usual in a SSF.<br />
<br />
=== [disallow_extra_recruit] ===<br />
Prevents a leader from recruiting units it could previously recruit.<br />
* '''extra_recruit''': the types of units that the side can no longer recruit.<br />
* '''[[StandardUnitFilter]]''': All units matching this filter are modified. Does not match on recall list units.<br />
<br />
=== [set_recruit] ===<br />
Sets the units a side can recruit.<br />
* '''recruit''': the types of units that the side can now recruit.<br />
* '''side''': (default=1) the number of the side that is having its recruitment set. This can be a comma-separated list. note: Default side=1 for empty side= is deprecated.<br />
* [[StandardSideFilter]] tags and keys; default for empty side= is all sides, as usual in a SSF.<br />
<br />
=== [set_extra_recruit] === <br />
Sets the units a leader can recruit.<br />
* '''extra_recruit''': the types of units that the leader can now recruit.<br />
* '''[[StandardUnitFilter]]''': All units matching this filter are modified. Does not match on recall list units.<br />
<br />
=== [modify_side] ===<br />
Modifies some details of a given side in the middle of a scenario. '''The following listed properties are the only properties that [modify_side] can affect!'''<br />
* '''side''': (default=1) the number of the side that is to be changed. note: Default side=1 for empty side= is deprecated.<br />
* '''[filter_side]''' with a [[StandardSideFilter]] as argument<br />
* '''income''': the income given at the begining of each turn.<br />
* '''recruit''': a list of unit types, replacing the side's current recruitment list.<br />
* '''team_name''': the team in which the side plays the scenario.<br />
* '''user_team_name''': a translatable string representing the team's description. This has no effect on alliances. Defaults to ''team_name''.<br />
* '''gold''': the amount of gold the side owns.<br />
* '''village_gold''': the income setting per village for the side.<br />
* '''controller''': the identifier string of the side's controller. Uses the same syntax of the ''controller'' key in the [[SideWML|[side]]] tag.<br />
* '''fog''': a boolean string (yes/no) describing the status of Fog for the side.<br />
* '''shroud''': a boolean string describing the status of Shroud for the side.<br />
* '''hidden''': a boolean string specifying whether side is shown in status table.<br />
* '''color''': a team color range specification, name (e.g. "red", "blue"), or number (e.g. "1", "2") for this side. The default color range names, numbers, and definitions can be found in data/core/team_colors.cfg.<br />
* '''[ai]''': sets/changes AI parameters for the side. Only parameters that are specified in the tag are changed, this does not reset others to their default values. Uses the same syntax as described in [[AiWML]]. Note that [modify_side][ai] works for all simple AI parameters and some, but not all, of the composite ones. If in doubt, use [http://wiki.wesnoth.org/AiWML#Adding_and_Deleting_Aspects_with_the_.5Bmodify_ai.5D_Tag [modify_ai]] instead, which always works.<br />
* '''switch_ai''': replaces a side ai with a new AI from specified file(ignoring those AI parameters above). Path to file follows the usual WML convention.<br />
* '''reset_maps''': If set to "yes", then the shroud is spread to all hexes, covering the parts of the map that had already been explored by the side, including hexes currently seen. (Seen hexes will be cleared at the end of most events; they can also be manually cleared with {{tag|InterfaceActionsWML|redraw}}.) This is only effective if shroud is on, but this is evaluated after shroud= (and before shroud_data=).<br />
* '''reset_view''': If set to "yes", then the fog of war is spread to all hexes, covering the parts of the map that had already been seen this turn by the side, including hexes currently seen, excluding hexes affected by multi-turn {{tag|DirectActionsWML|lift_fog}}. (Seen hexes will be cleared at the end of most events; they can also be manually cleared with {{tag|InterfaceActionsWML|redraw}}.) This is only effective if fog is on, but this is evaluated after fog=.<br />
* '''share_maps''': change the share_maps side attribute. Be sure to use shroud=yes for that side and have it as an ally<br />
* '''share_view''': change the share_view side attribute. Be sure to use fog=yes for that side and have it as an ally<br />
* '''shroud_data''': changes to the side's shroud, using the same format as when defining the [side].<br />
* '''suppress_end_turn_confirmation''': Boolean value controlling whether or not a player is asked for confirmation when skipping a turn.<br />
* '''scroll_to_leader''': Boolean value controlling whether or not the game view scrolls to the side leader at the start of their turn when present.<br />
* '''flag''': Flag animation for villages owned by this side (see [[SideWML|[side]]]).<br />
* '''flag_icon''': Flag icon used for this side in the status bar (see [[SideWML|[side]]]).<br />
* '''village_support''': The number of unit levels this side is able to support (does not pay upkeep on) per village it controls.<br />
<br />
=== [modify_turns] ===<br />
Modifies the turn limit in the middle of a scenario.<br />
* '''value''': the new turn limit.<br />
* '''add''': if used instead of ''value'', specifies the number of turns to add to the current limit (can be negative).<br />
* '''current''': changes the current turn number after applying turn limit modifications, if any. It is not possible to change the turn number to exceed the turn limit (1 <= current turns <= max turns).<br />
<br />
=== [allow_end_turn] ===<br />
Allows human players to end their turn through the user interface if they were previously affected by the '''[disallow_end_turn]''' action. This action doesn't take any arguments.<br />
<br />
=== [disallow_end_turn] ===<br />
Disallows human players to end their turn through the user interface. This action doesn't take any arguments.<br />
<br />
=== [capture_village] ===<br />
Changes the ownership of a village.<br />
* [[StandardLocationFilter]]: all village locations matching the filter are affected.<br />
* '''side''': the side that takes control of the village. This side needs to have a leader (canrecruit=yes). If the side key is not given, the village will become neutral (unless [filter_side] is present, in which case that side fiter decides, see below).<br />
* '''[filter_side]''' with [[StandardSideFilter]] tags and keys as arguments; if both this tag and inline side= are present it's an error. Otherwise, the first matching side gets ownership (or the village becomes neutral if none match).<br />
* '''fire_event''' (boolean yes|no, default: no): Whether any capture events shall be fired.<br />
<br />
=== [kill] ===<br />
Removes all units (including units in a recall list) that match the filter from the game.<br />
* [[StandardUnitFilter]]: Selection criterion; do not use a [filter] tag.<br />
* '''animate''': if 'yes', displays the unit dying (fading away).<br />
* '''fire_event''': if 'yes', triggers any appropriate 'die' events (See [[EventWML]]). Note that events are only fired for killed units that have been on the map (as opposed to recall list).<br />
* '''[secondary_unit]''' with a [[StandardUnitFilter]] as argument. Do not use a [filter] tag. Has an effect only if fire_event=yes. The first on-map unit matching the filter becomes second_unit in any fired die and last breath events. If an on-map unit matches and if there are several units killed with a single [kill] tag, second_unit is this same unit for all of them. If no on-map unit matches or [secondary_unit] isn't present, the variable second_unit in each of the die and last breath events is always the same as the variable unit (the dying unit).<br />
<br />
=== [move_unit] ===<br />
works like the MOVE_UNIT macro.<br />
* [[StandardUnitFilter]] as argument; do not use a [filter] tag. All units matching the filter are moved. If the target location is occupied, the nearest free location is chosen.<br />
* '''to_x''' (unsigned integer): The units are moved to this x coordinate. Can be a comma-separated list, in which case the unit follows this given path during the move.<br />
* '''to_y''' (unsigned integer): The units are moved to this y coordinate. Can be a comma-separated list.<br />
* '''fire_event''' (optional, boolean yes|no, default no): Whether any according moveto events shall be fired. The target location ($x1, $y1 in the event) may not be the same location that the unit was tried to be moved to, if the original target location is occupied or impassable.<br />
* '''check_passability''' (boolean yes|no, default yes): Whether the terrain the unit is moved to should be checked for suiting the unit. (If it does not, a nearby suitable hex is chosen.)<br />
* '''force_scroll''': Whether to scroll the map or not even when [[InterfaceActionsWML#.5Block_view.5D|[lock_view]]] is in effect or ''Follow Unit Actions'' is disabled in ''Advanced Preferences''. Defaults to using [[InterfaceActionsWML#.5Bmove_unit_fake.5D|[move_unit_fake]]]'s default value.<br />
<br />
=== [modify_ai] ===<br />
Changes AI objects (aspects, goals, candidate actions or stages) for a specified side. See [[AiWML#Adding_and_Deleting_Aspects_with_the_.5Bmodify_ai.5D_Tag|AiWML]] for full description.<br />
<br />
* '''action''' (string): Takes values 'add', 'change', 'delete' or 'try_delete' to do just that for the AI object.<br />
* '''path''' (string): Describes which AI object is to be modified. <br />
* '''[facet]''', '''[goal]''', '''[candidate_action]''' or '''[stage]''': Details about the AI object to be modified.<br />
* [[StandardSideFilter]] tags and keys; default for empty side= is all sides, as usual in a SSF.<br />
<br />
=== [modify_unit] ===<br />
works similar to the MODIFY_UNIT macro.<br />
* '''[filter]''' with a [[StandardUnitFilter]] as argument. All units matching this filter are modified. Matches on recall list units too.<br />
* Accepts generally the syntax inside of wml unit variables created by [store_unit] which can be viewed in a savefile or by using the :inspect command. It can add [trait]s and [object]s without needing to wrap them inside a [modifications] tag, and their effects are applied immediately. Cannot remove things. Subtags with the same name must be written in the correct order to match them with the tag they are supposed to modify.<br />
example usage (see also the test scenario):<br />
[modify_unit]<br />
[filter]<br />
x,y=38,6<br />
[/filter]<br />
hitpoints=10<br />
{TRAIT_HEALTHY}<br />
[/modify_unit]<br />
<br />
The unit which is currently modified is accessible via $this_unit, e.g. hitpoints = "$($this_unit.hitpoints / 2)" to set the hitpoints of all units to half of their particular maxima. This this_unit variable is independent from the this_unit variable available in the SUF used to determine which units to modify (first all matching units are gathered, and then all those are modified).<br />
<br />
note: The syntax allowed is somehow vague. Just try things and possibly correct/add/modify this documentation. (a [http://forums.wesnoth.org/viewtopic.php?f=21&t=31676& forum thread] discusses some related issues).<br />
<br />
=== [transform_unit] ===<br />
Transforms every unit matching the filter to the given unit type. Keeps intact hit points, experience and status. If the unit is transformed to a non-living type (undead or mechanical), it will be also unpoisoned. Hit points will be changed if necessary to respect the transformed unit's maximum hit points.<br />
* [[StandardUnitFilter]]: do not use a [filter] tag.<br />
* '''transform_to''': the unit type in which all the units matching the filter will be transformed. If missing, the units will follow their normal advancement.<br />
<br />
=== [petrify] ===<br />
<br />
* [[StandardUnitFilter]] as an argument. Do not use a [filter] tag. All units matching this filter are petrified. Recall list units are included.<br />
<br />
=== [unpetrify] ===<br />
* [[StandardUnitFilter]] as an argument. Do not use a [filter] tag. All units matching this filter are unpetrified. Recall list units are included.<br />
<br />
=== [object] ===<br />
Gives some unit an object which modifies their stats in some way.<br />
* '''id''': (Optional) when the object is picked up, a flag is set for ''id''. The object cannot be picked up if a flag for ''id'' has been set. This means that any object with an id can only be used once, even if first_time_only=no is set for the event. This restriction is per level. In a campaign objects with the same id can be assigned once per level.<br />
* '''delayed_variable_substitution''' (boolean yes|no, default no): If set to "yes", the wml block contained in this [object] is not variable-substituted at execution time of the event where this [object] is within. You need this to work around a bug when adding ABILITY_TELEPORT via an [object] or when using [object][effect][filter]with a $this_unit (see http://gna.org/bugs/index.php?18893).<br />
* '''[effect]''': one or more effect elements may be listed. See [[EffectWML]] for a description of [effect].<br />
* '''duration''':<br />
**if 'scenario', effects only last until the end of the level (note : 'level' is the scenario, so this doesn't mean it last until the unit levels-up).<br />
**if 'forever' or not set, effects never wear off.<br />
** if 'turn', effects only last until the start of the unit's next turn (when the unit refreshes movement and attacks). (Like other start-of-turn behavior, objects with a duration of "turn" won't expire before turn 2.)<br />
** {{DevFeature1.13|1}} if 'turn end' or 'turn_end', effects only last until the end of the unit's next turn (exactly like the slowed status).<br />
* '''[filter]''' with a [[StandardUnitFilter]] as argument. The first unit found that matches the filter will be given the object. Only on-map units are considered. If no unit matches or no [filter] is supplied, it is tried to apply the object to the unit at the $x1,$y1 location of the event where this [object] is in. The case of no unit being at that spot is handled in the same way as no unit matching a given filter ([else] commands executed, cannot_use_message displayed)<br />
* '''[then]''': a subtag that lets you execute actions if the filter conditions are met. The most common action that should be inside here is a '''[remove_item]''' tag, but you could probably put any tags that otherwise work in a [then] tag.<br />
* '''[else]''': a subtag that lets you execute actions if the filter conditions are *not* met.<br />
* '''silent''': whether or not messages should be suppressed. Default is "no".<br />
* '''image''': the displayed image of the object.<br />
* '''name''': (translatable) displayed as a caption of the image.<br />
<br />
* '''description''': (translatable) displayed as a message of the image.<br />
* '''cannot_use_message''': (translatable) displayed instead of '''description''' if no unit passes the filter test.<br />
* '''no_write''': {{DevFeature1.13|1}} (bool, default false). If true, the contents of [effect] will be applied to the relevant unit, but the [object] tag will not be written to unit.modifications. This renders duration= irrelevant.<br />
<br />
=== [remove_shroud] ===<br />
Removes some shroud from the map for a certain side (only relevant for sides that have shroud=yes).<br />
* '''side''': (default=1) the side for which to remove shroud. This can be a comma-separated list of sides. note: Default side=1 for empty side= is deprecated.<br />
* '''[filter_side]''' with a [[StandardSideFilter]] as argument<br />
* [[StandardLocationFilter]]: the range of tiles for which shroud should be removed<br />
<br />
=== [place_shroud] ===<br />
Places some shroud on the map for a certain side (only relevant for sides that have shroud=yes).<br />
* '''side''': (default=1) the side for which to place shroud. This can be a comma-separated list. note: Default side=1 for empty side= is deprecated.<br />
* '''[filter_side]''' with a [[StandardSideFilter]] as argument<br />
* [[StandardLocationFilter]]: the range of tiles on which shroud should be placed<br />
<br />
=== [lift_fog] ===<br />
Lifts the fog of war from parts of the map for a certain side (only relevant for sides that have fog=yes), allowing a player to witness what occurs there even if that player has no units within vision range.<br />
* '''[filter_side]''' with a [[StandardSideFilter]] indicating which sides should be affected.<br />
* [[StandardLocationFilter]]: the tiles from which fog should be lifted.<br />
* '''multiturn''': ''yes/no, default:no''. The default (not multiturn) causes fog to be removed in the same way that normal vision works; the cleared tiles will remain cleared until fog is recalculated (which normally happens when a side ends its turn). When multiturn is set to "yes", the cleared tiles remain clear until {{tag||reset_fog}} cancels the clearing. This allows tiles to remain clear for multiple turns, or to be refogged before the end of the current turn (without also refogging all tiles). Multiturn lifted fog is not shared with allies (even when share_view=yes).<br />
<br />
=== [reset_fog] ===<br />
The primary use of this tag is to remove multiturn lifted fog (created by {{tag||lift_fog}}), which causes the fog to reset to what it would have been had WML not interfered. (That is, hexes that a side's units could not see at any point this turn will be re-fogged, while seen hexes remain defogged.)<br />
* '''[filter_side]''' with a [[StandardSideFilter]] indicating which sides should be affected.<br />
* [[StandardLocationFilter]]: the fog reset will be restricted to these tiles.<br />
* '''reset_view''': ''yes/no, default: no'' If set to "yes", then in addition to removing multiturn fog, the side's current view is canceled (independent of the SLF). This means that all hexes will become fogged for the side unless multiturn fog exists outside the tiles selected by the SLF. Normally, one would want the currently seen hexes to become clear of fog; this is done automatically at the end of many events, and it can be done manually with {{tag|InterfaceActionsWML|redraw}}.<br />
Omitting both the SSF and the SLF would cancel all earlier uses of [lift_fog].<br />
Additionally setting reset_view="yes" would cause the side's entire map to be fogged (unless an ally keeps hexes clear by sharing its view).<br />
<br />
=== [allow_undo] ===<br />
<br />
Normally when an event with a handler fires, the player's undo stack is cleared, preventing all actions performed so far from being undone. Including this tag in the event handler prevents the stack from being cleared for this reason, allowing the player to undo actions. (However, the stack might still be cleared for other reasons, such as fog being cleared or combat occurring.) In the common cases, this means '''[allow_undo]''' allows the current action to be undone even though an event was handled. There is a less common case, though &mdash; specifically when handling a menu item, where there is no current action &mdash; and in this case, '''[allow_undo]''' means merely that earlier actions can still be undone.<br />
* Using this tag in a menu item has an additional side effect in 1.11. Starting with version 1.11.1, executing a WML menu item normally counts as doing something as far as the "you have not started your turn yet" dialog is concerned. However, a menu item whose handler includes '''[allow_undo]''' will not count.<br />
<br />
The types of actions that can be undone are movement, recruitment, recalling, and dismissing a unit from the recall list. If an action is undone, only the position (or existence) of the involved unit will be restored; any altered variables or changes to the game will remain changed after the action is undone. It is up to the scenario designer to avoid abusing this command.<br />
* Technically, if '''[allow_undo]''' is inside an '''[event]''' with ''first_time_only=yes'' (the default setting), and the user undoes the event, then the state of the game has changed in this way: the event will not fire a second time, even though the user undid the action the first time.<br />
<br />
Due to a bug in 1.12 (https://gna.org/bugs/?23323) '''[allow_undo]''' should not be used in events that use one of the following things brcause it might cause OOS: <br />
* [message] with [option]s<br />
* [get_global_variable]<br />
* wesnoth.syncronize_choice<br />
<br />
=== [heal_unit] ===<br />
Heal a unit. The variable '''$heal_amount''' will be set to the exact number of points healed (i.e can be lesser than the parameter '''amount''' if the unit is fully healed). $heal_amount contains only the number of hitpoints the first unit that was found got healed.<br />
* '''[filter]''': [[StandardUnitFilter]] All matching on-map units are healed. If no filter is supplied, it is tried to take the unit at $x1, $y1.<br />
* '''[filter_second]''': [[StandardUnitFilter]] all the units matching the filter ''and'' having the ''heals'' ability will have their animation played (if ''animate'' is set to true) for each of the units healed.<br />
* '''amount''': (integer, default full) the maximum points the unit(s) will be healed. Can't set below 1 or above max_hitpoints. If "full", sets hitpoints to max_hitpoints. Before 1.9 the default is 0.<br />
* '''animate''': a boolean which indicate if the healing animations must be played. (default no)<br />
* '''moves''': (integer, default 0) The maximum current movement points the units will be "healed". Can't set below 0 or above max_moves. If "full", sets moves to max_moves.<br />
* '''restore_attacks''': (boolean, default no) Whether the units' attacks_left should be reset to their max_attacks (usually 1).<br />
* '''restore_statuses''': (boolean, default yes) Whether standard statuses should be reset to "no". This affects poisoned, slowed, petrified and unhealable. Before 1.9 this is always "no".<br />
<br />
=== [harm_unit] ===<br />
Harms every unit matching the filter, for the specific damage amount.<br />
* '''[filter]''': [[StandardUnitFilter]] all matching units will be harmed (required).<br />
* '''[filter_second]''': [[StandardUnitFilter]] if present, the first matching unit will attack all the units matching the filter above.<br />
* '''amount''': the amount of damage that will be done (required).<br />
* '''alignment''': (default neutral) applies an alignment to the damage, this means that if alignment=chaotic, the damage will be increased at night and reduced at day.<br />
* '''damage_type''': if present, amount will be altered by unit resistance to the damage type specified.<br />
* '''kill''': (default yes) if yes, when a harmed unit goes to or below 0 HP, it is killed; if no its HP are set to 1.<br />
* '''fire_event''': (default no) if yes, when a unit is killed by harming, the corresponding events are fired. If yes, also the corresponding advance and post advance events are fired.<br />
* '''animate''': (default no) if yes, scrolls to each unit before harming it and plays its defense (or attack, if it's the harmer) and death animations. Special values supported, other than the usual yes and no, are "attacker", that means only the harmer will be animated, and "defender", that means only the harmed units will be animated. If the supplied value is yes, attacker or defender also advancement animations are played.<br />
* '''[primary_attack], [secondary_attack]''': these set the weapon against which the harmed units will defend, and that the harming unit will use to attack, respectively (notice this is the opposite of '''[filter]''' and '''[filter_second]''' above). This allows for playing specific defense and attack animations. Both tags are expected to contain a [[FilterWML#Filtering_Weapons|Standard Weapon Filter]].<br />
* '''delay''': if animate=yes, sets the delay (in milliseconds, default 500) between each unit harming.<br />
* '''variable''': if present, the damage caused to the unit, altered by resistances, will be stored in a WML array with the given name, under the "harm_amount" key.<br />
* '''poisoned, slowed, petrified, unhealable''': (default no) if yes, every harmed unit that doesn't already have such status will have it set.<br />
* '''experience''': if yes, and there is a harmer, experience will be attributed like in regular combat.<br />
* '''resistance_multiplier''': the harmed unit's resistance is multiplied by the supplied value; this means that a value lower than 1 increases it, and a value greater than 1 decreases it. Default value is 1, that means no modification.<br />
<br />
=== [time_area] ===<br />
How a day should progress in a given area. Everywhere not specified in a [time_area] tag is affected by the [time] tags in the [scenario] tag.<br />
* [[StandardLocationFilter]]: the locations to affect. ''note: only for [event][time_area]s - at scenario toplevel [time_area] does not support [[StandardLocationFilter]], only location ranges''<br />
* [[TimeWML]]: the new schedule.<br />
* '''id''': an unique identifier assigned to a time_area. Optional, unless you want to remove the time_area later. Can be a comma-separated list when removing time_areas, see below.<br />
* '''remove''': (boolean) yes/no value. Indicates whether the specified time_area should be removed. Requires an identifier. If no identifier is used, however, all time_areas are removed.<br />
* '''current_time''': The time slot number (starting with zero) active at the creation of the area.<br />
<br />
''Example:'' (caves in parts of a map)<br />
[time_area]<br />
x=1-2,4-5<br />
y=1-2,1-2<br />
{UNDERGROUND}<br />
[/time_area]<br />
<br />
=== [end_turn] ===<br />
End the current side's turn. The current event is finished before the turn is ended. Also, if the current event (where the tag appears) has been fired by another event, that event (and the complete stack of other possible parent events) is ended before [end_turn] comes into affect. Also, events following the event stack that fired [end_turn] are not omitted (e.g. [end_turn] is used by a side turn event and a turn refresh event does something afterwards).<br />
<br />
=== [replace_map] ===<br />
<br />
Replaces the entire map.<br />
* '''map''': Content of a wesnoth map file. example:<br />
map="{campaigns/Heir_To_The_Throne/maps/01_The_Elves_Besieged.map}"<br />
* '''expand''': if 'yes', allows the map size to increase. The expansion direction is currently always bottom-right.<br />
* '''shrink''': if 'yes', allows the map size to decrease. If the map size is reduced, any units that would no longer be on the map due to its coordinates no longer existing will be put into the recall list.<br />
Note: When a hex changes from a village terrain to a non-village terrain, and a team owned that village it loses that village. When a hex changes from a non-village terrain to a village terrain and there is a unit on that hex it does not automatically capture the village. The reason for not capturing villages it that there are too many choices to make; should a unit lose its movement points, should capture events be fired. It is easier to do this as wanted by the author in WML.<br />
<br />
=== [replace_schedule] ===<br />
Replace the time of day schedule of the entire scenario.<br />
* [[TimeWML]]: the new schedule.<br />
* '''current_time''': The time slot number (starting with zero) active at schedule replacement.<br />
<br />
=== [tunnel] ===<br />
<br />
Create a tunnel between some locations, later usable by units to move from source hex to target hex (using the movement cost of unit on the target terrain). ([http://forums.wesnoth.org/viewtopic.php?f=21&t=14749&p=405667&hilit=tunnel#p405667 source])<br />
<br />
* '''id''' identifier for the tunnel, to allow removing (optional).<br />
* '''remove''': (boolean) yes/no value. If yes, removes all defined tunnels with the same ID (then only id= is necessary). (default: no)<br />
* '''bidirectional''': (boolean) if yes, creates also a tunnel in the other direction. (default: yes)<br />
* '''always_visible''': (boolean) if yes, the possible movement of enemies under fog can be seen. (default: no)<br />
* '''[source]''': [[StandardLocationFilter]] the source hex(es) (required).<br />
* '''[target]''': [[StandardLocationFilter]] the target hex(es) (required).<br />
* '''[filter]''': [[StandardUnitFilter]] the units which can use the tunnel (required). Leave empty for "all units".<br />
<br />
(Note: The tunnel tag can also be used inside the [[AbilitiesWML|[teleport]]] ability, without remove= and id=).<br />
<br />
=== [do_command] ===<br />
<br />
{{DevFeature1.13|0}}<br />
<br />
Executes a command, specified using the same syntax as a [command] tag in [[ReplayWML]]. Not all [command]'s are valid: only these are accepted<br />
<br />
* [attack]<br />
* [move]<br />
* [recruit]<br />
* [recall]<br />
* [disband]<br />
* [fire_event]<br />
* [lua_ai]<br />
<br />
The tags corresponding to player actions generally use the same codepath as if a player had ordered it.<br />
<br />
One purpose of this tag is to allow scripting of noninteractive scenarios -- without a tag like this, this might require elaborate mechanisms to coerce ais in order to test these code paths.<br />
<br />
This command should always be replay safe.<br />
<br />
=== [put_to_recall_list] ===<br />
<br />
{{DevFeature1.13|0}}<br />
<br />
Puts a unit to the recall list of its side.<br />
* '''[[StandardUnitFilter]]''': the unit(s) to get put to the recall list.<br />
* '''heal''': (default=no) Whether the unit should be refreshed, similar to the unit moving to the recall list at the end of a scenario.<br />
<br />
<br />
== Useful Macros ==<br />
There are some predefined macros that you find useful for direct actions. You can find a complete list along with a detailed explanation of how they work [http://www.wesnoth.org/macro-reference.xhtml here].<br />
* '''{MOVE_UNIT}''': Moves a unit to another location in the map and the player sees the movement (unlike [teleport])<br />
* '''{FULL_HEAL}''': Brings a unit to full HP<br />
* '''{LOYAL_UNIT}''': Create a loyal unit<br />
* '''{MODIFY_TERRAIN_MASK}''': Modify an area of terrain<br />
<br />
== See Also ==<br />
<br />
* [[InternalActionsWML]]<br />
* [[InterfaceActionsWML]]<br />
* [[EventWML]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category: WML Reference]]<br />
[[Category: ActionsWML]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=BuildingScenariosBalancing&diff=56441BuildingScenariosBalancing2015-06-19T19:42:21Z<p>Jmichael: /* Unexpected reinforcements */</p>
<hr />
<div>==Introduction==<br />
<br />
I'm writing this wiki article to talk about some things that came up as I was balancing a couple of the scenarios in the South Guard Campaign. I'm going to respond to something that PDF wrote but that I think is a pretty common idea. I don't mean to single him out:<br />
<br />
The PDF wrote:<br />
"Adding a difficulty level does not necessarily requires much balancing;<br />
you can, for example, just add or remove gold."<br />
<br />
<br />
I'm going to argue that that is fundamentally a flawed way to balance campaigns, and should only be used in conjunction<br />
with other balancing methods.<br />
<br />
===A Wesnoth scenario as a tactical problem===<br />
<br />
Each scenario in Wesnoth presents a tactical problem. The problem is generally phrased something like this: "Kill the enemy leader in so much time while keeping your leader alive," and its most common variation being: "Move your leader to this hex in said amount time with out letting him die." Of course, there are subtleties to these tactical problems. Because of the bonus gold at the end you want to finish in less then the stated time. And since you can recall experienced units for harder scenarios you may not want your vetrans to die, and of course there might be other units that you have or want to keep alive. There may also be certain units you must kill in order to succeed. At its core however, each scenario is a relatively simple tactical problem.<br />
<br />
<br />
===Factors that influence the difficulty of a tactical problem in Wesnoth===<br />
<br />
I am going to briefly list the factors (that I perceive) which affect the difficulty of a tactical problem in Wesnoth. This list is probably not exhaustive, so if there's something I've missed, post it and I'll add it to the list. They are in no particular order:<br />
<br />
* Terrain<br />
* Player's starting gold<br />
* AI's starting gold<br />
* AI income<br />
* Player's recriutment list<br />
* AI's recriutment list<br />
* Player's recall list<br />
* Time limitations<br />
* Keep sizes<br />
* AI Parameters (more on this in a minute)<br />
* Distribution of villages<br />
* Unexpected reinforcements<br />
* Time of day<br />
* Items<br />
* WML effects<br />
<br />
Note that the AI's starting gold is one factor among many.<br />
<br />
===Varying the difficulty of a tactical problem presented to the player in a scenario===<br />
<br />
Considering the above factors, it is easy (if time consuming for the designer) to present tactical problems of differing difficulty in a single scenario. For most of the above factors, I'll give an example and explain in a sentence how if affects the tactical problem:<br />
<br />
====Terrain====<br />
If the map is predominantly a terrain that favors either the player's forces or the ai's forces, it will have a huge effect on the difficulty of the battle. For example, a player whose units are broadly loyalists will have a disastrous time trying to root Elves out of woods. The terrain is, however, generally fixed by the nature of the scenario (i.e. the designer can't really make a whole new map for each difficulty level). I would submit, however, that 80% of the actual<br />
fighting in a typical scenario occurs in less than 10% of the hexes. It is possible to change only one or two hexes and have a dramatic effect on the outcome of the battle if you can identify those "chokepoint" hexes.<br />
<br />
<br />
This can either be done by having three different maps (which I suspect is more work) or by using the WML tag [terrain].<br />
For example, the code:<br />
<br />
#ifdef EASY<br />
[terrain]<br />
x=4<br />
y=5<br />
letter=Ce<br />
[/terrain]<br />
#endif<br />
<br />
will turn the hex at (4,5) into an encampment.<br />
<br />
====Player's starting gold====<br />
If the player has more gold he can make more tactical mistakes because he can recruit more troops to replace the ones he's lost. Additionally, quantity has a quality all of its own; with sufficient starting gold the player can employ a swarm of cheap units to distract and overwhelm the AI forces. Balancing gold becomes difficult in campaigns because of gold carryover, which often has the exact opposite of the desired effect: struggling players will start with less gold (usually the minimum), while accomplished players will have a sizable treasury. This severely limits the designer's ability to balance the scenario by adjusting the player's starting gold.<br />
<br />
====AI starting gold====<br />
The easiest, and often first choice to balance a scenario, the AI starting gold primarily controls the size of the initial AI "rush". While adjusting this value clearly influences the difficulty of the scenario in a rather straightforward manner, it is often difficult to properly balance a scenario with gold alone. The relationship between AI starting gold and difficulty is not linear--a few more enemy units can make the first engagement impossible, while a few less can lead to a boring rout. Wesnoth was designed to balance well with equal gold, but unfortunately limitations of the AI generally require a significant gold advantage just to level the playing field. <br />
<br />
Setting the AI gold too high will require the player to perform perfectly, or in some cases, even better than perfect (meaning extreme luck with the RNG). Reducing the AI gold, however, does significantly reduce the difficulty of most scenarios (but try to avoid removing all the challenge). Unfortunately, due to the variation in player starting gold in a campaign, finding the optimal AI starting gold amount is nearly impossible.<br />
<br />
====AI income====<br />
Income provides a steady stream of gold, allowing the AI to mount a continued offensive. Income can be generated from villages, or supplied directly by the scenario designer using WML. A steady stream of income can help provide a more balanced challenge, instead of deciding the battle directly upon the first engagement. Extra income is generally less destabilizing than larger amounts of starting gold, making it a good candidate for fine-tuning scenario balance.<br />
<br />
====Player's recruitment list====<br />
If the player can only recruit units that will be effective against the enemy's forces, then the tactical problem is simplified. If I can only recruit heavy infantry to face a force of skeleton archers, then I have a less difficult tactical decision to make than if I can recruit heavy infantry and spearmen. Especially if I don't know the game well.<br />
<br />
While adjusting the player's recruitment list can prevent new players from recruiting "poor" units, that effect quickly levels off. Even a moderately inexperienced player quickly learns that heavy infantry "beat" skeleton archers. On the other hand, disallowing the best units can create a new tactical challenge. Either way, though, the player's recruitment list is a defining characteristic of the scenario, and usually does not lend itself to balancing due to limited options and lack of fine-grained control.<br />
<br />
====AI recruitment list====<br />
Restricting the enemy to a certain class of units (i.e. melee-only, ranged-only, impact-attacks-only) can simplify the tactical problem immensely. Consider an enemy who can only recruit thieves and thugs facing a predominantly loyalist force, who can recruit Bowmen and Spearmen. Bowmen are very good against thugs and thieves because they have a strong ranged attack. If the enemy can also recruit poachers and footpads, the tactical problem becomes more difficult because the player must balance the effectiveness of the Bowmen against the thieves and thugs with their (relative) weakness against footpads and poachers.<br />
<br />
More typically, the scenario designer can remove higher-level units to lower difficulty, and add them to raise difficulty. As an example, in '''Muff Malal Peninsula''' from HttT the AI is allowed to recruit Blood Bats on "hard", but only Vampire Bats on "normal" and "easy". Because the AI pays "full price" for their units, changing the units available doesn't significantly reduce the total power of the side, but it does limit the total power that can be brought to bear in a single turn. It can also be useful to limit defensive "bottlenecks" (weaker individual AI units allow the player to power through the defenders).<br />
<br />
====Player's recall list====<br />
A player's recall list is vital to the balance of the scenario, however the designer has little control over it. Because recalled troops are available at a ''significant'' discount, they can easily turn the tide of battle, especially against lower-level enemies. Unfortunately, as with the player's starting gold, the recall list is generally unbalanced: the accomplished players will have multiple high-level units, while the struggling players will have few. The situation is further complicated by another wrinkle: playing on "hard" often provides significantly more experience points than playing on easy, with the result that the "hard" recall list often has more high-level units than the "easy" list. <br />
<br />
At the beginning of a campaign, it is reasonable to assume few recalled troops (and those that are generally aren't too unbalancing). At the end of a mid-length campaign it is reasonable to assume that most of a player's troops will be level 3 and balance accordingly. In the middle of the campaign, however, it is nearly impossible to find a balance that suits all players. Some campaigns try to limit the player's recall list (such as '''Evacuation''' in EI), however they are rarely entirely successful (and often annoy players).<br />
<br />
====Time limitations====<br />
The turn limit controls to what extent the player is allowed to waste turns not directly accomplishing his mission. In the most difficult case scenario, a player must move his leader across the map and only enough turns are allotted such that if he ever moves in any other direction, time will run out. Increasing the turn limit allows an inexperienced player to make turn-costing mistakes and still be victorious.<br />
<br />
Extremely tight turn limits should be avoided. Because of the random nature of battle in Wesnoth, the player cannot guarantee that their four level-3 units will be able to take out the enemy leader in a single turn. Restricting the time limit to that level is equivalent to requiring good rolls from the RNG: something the player has no control over. In general, the time limit should serve to ''motivate'' the player, not as the driving challenge of the scenario.<br />
<br />
====Keep sizes====<br />
The size of a player's keep determines at what rate he can recruit troops. The size of the enemy's keep determines at what rate he can recruit troops. Divide one by the other and you get a relative-recruitment-rate (assuming both have lots of gold). If the relative recruitment rate favors the player, the tactical problem should be easier because he can replenish losses more easily than the AI player. If the AI player's keep is larger, then any losses by the human player will be that much more difficult to replace. <br />
<br />
Note that these parameters generally only have a significant influence on the first few turns--rarely will players (or AI) have sufficient gold to recruit a full keep of replacement troops (and almost never will they be able to do so on multiple turns in a row). In those first few turns, a smaller AI keep can "spread out" the initial rush, making it easier for the player to defend (but this effect can disappear entirely if the AI improves). The only time keep size is critical to the players is when they are limited to 'X' rounds of recruitment (either because their keep is forcibly removed or they are required to begin moving their leaders towards an objective). In short, while keep size can have an effect in some specific circumstances, it is generally not very useful for balancing scenarios.<br />
<br />
====AI Parameters====<br />
The AI parameters are perhaps the best way to alter a tactical problem. [[AiWML]] is a particularly important read for a scenario designer. I'd draw attention to aggression and [target] in particular. attack_depth is also very nice. These parameters are especially important for building scenarios with tactical problems that are fundamentally different from the basic kill-his-leader ones.<br />
<br />
====Distribution of villages====<br />
Using the [terrain] WML tag you can easily alter the distribution of villages in a scenario. Move the distribution towards the player, and things get easier. Move it towards the enemy, and they get harder.<br />
<br />
Villages are useful for three distinct reasons: they are defensible terrain, they grant income (and support), and they provide healing. Of those three, the first two can easily be replaced by other terrain and WML-granted income respectively. Village healing is necessary for the AI, and the player with no healers, however only two or three villages near the conflict are really useful for healing. With those limitations in mind, villages prove only slightly useful in balancing a scenario. For a subtle example, '''The Outpost''' in EI moves a village one hex closer on "easy" (putting it within convenient reach of the player's leader).<br />
<br />
====Unexpected reinforcements====<br />
Reinforcements, either friendly or hostile, can significantly complicate the tactical problem. They are a surprise that the player must accommodate with already deployed troops. Unfortunately, reinforcements often become [[tomato surprise]]s, meaning the best course of action for the player is to replay the scenario with foreknowledge of the "surprise". To avoid that result, reinforcements should generally be either foreshadowed, appear early in the scenario, or appear in moderation.<br />
<br />
As an example, the scenario '''Crossroads''' in HttT introduces enemy reinforcements that randomly appear in hill tiles. These reinforcements are explained near the beginning of the scenario, and the enemy units appear in moderation (a few at a time, and only when the player moves onto hill terrain). The scenario '''Drowned Plains''' in EI uses the same code, but adjusts the type of ambushing units depending on the difficulty level. On the other side, many scenarios give the player a few friendly units near the beginning, with the number and type adjusted for difficulty.<br />
<br />
====Time of day====<br />
Time of day can be an interesting factor (assuming the map is not a cave and the armies aren't entirely neutral). While you are normally not free to change the cycle, changing the initial time of day can have a significant impact on the outcome of a battle. In particular, the time of day when the first AI rush is engaged can be crucial. On smaller maps, the time of day for the first engagement is practically dictated by the scenario designer. On a large or open battlefield, the player can arrange for a more favorable first encounter, so the initial time of day is not nearly as important. Note that these effects are most pronounced when the two armies are of opposite alignment.<br />
<br />
====Items====<br />
Items can modify the attributes of one of the player's units, often providing an extra advantage. Some typical items are "Holy Water" (changes melee weapons to ''arcane'' damage) and "Storm Trident" (adds ranged ''fire'' attack), while WML allows for very detailed customization. Even adding a few hitpoints, an extra movement point, or resistance to a specific type of damage can sway the balance of a close engagement.<br />
<br />
====WML effects====<br />
This is sort of a catch-all entry. One typical use is via the [http://www.wesnoth.org/macro-reference.xhtml#file:side-utils.cfg LIMIT_CONTEMPORANEOUS_RECRUITS] [[Macro]], which prevents the AI from over-recruiting one specific type of unit. As another example, '''Unexpected Appearance''' in EI uses WML to force the ''chance-to-hit'' to 100% at the beginning of the scenario (to prevent the player from losing if the RNG is mildly uncooperative). Making significant and unexplained modifications to the behavior players have come to expect in Wesnoth should be avoided, but used sparingly WML offers exceptionally fine-grained control for balancing a stubborn scenario.<br />
<br />
== See Also ==<br />
<br />
* [[BuildingScenarios]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category:Create]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=BuildingScenariosBalancing&diff=56440BuildingScenariosBalancing2015-06-19T19:37:43Z<p>Jmichael: /* Player's recall list */</p>
<hr />
<div>==Introduction==<br />
<br />
I'm writing this wiki article to talk about some things that came up as I was balancing a couple of the scenarios in the South Guard Campaign. I'm going to respond to something that PDF wrote but that I think is a pretty common idea. I don't mean to single him out:<br />
<br />
The PDF wrote:<br />
"Adding a difficulty level does not necessarily requires much balancing;<br />
you can, for example, just add or remove gold."<br />
<br />
<br />
I'm going to argue that that is fundamentally a flawed way to balance campaigns, and should only be used in conjunction<br />
with other balancing methods.<br />
<br />
===A Wesnoth scenario as a tactical problem===<br />
<br />
Each scenario in Wesnoth presents a tactical problem. The problem is generally phrased something like this: "Kill the enemy leader in so much time while keeping your leader alive," and its most common variation being: "Move your leader to this hex in said amount time with out letting him die." Of course, there are subtleties to these tactical problems. Because of the bonus gold at the end you want to finish in less then the stated time. And since you can recall experienced units for harder scenarios you may not want your vetrans to die, and of course there might be other units that you have or want to keep alive. There may also be certain units you must kill in order to succeed. At its core however, each scenario is a relatively simple tactical problem.<br />
<br />
<br />
===Factors that influence the difficulty of a tactical problem in Wesnoth===<br />
<br />
I am going to briefly list the factors (that I perceive) which affect the difficulty of a tactical problem in Wesnoth. This list is probably not exhaustive, so if there's something I've missed, post it and I'll add it to the list. They are in no particular order:<br />
<br />
* Terrain<br />
* Player's starting gold<br />
* AI's starting gold<br />
* AI income<br />
* Player's recriutment list<br />
* AI's recriutment list<br />
* Player's recall list<br />
* Time limitations<br />
* Keep sizes<br />
* AI Parameters (more on this in a minute)<br />
* Distribution of villages<br />
* Unexpected reinforcements<br />
* Time of day<br />
* Items<br />
* WML effects<br />
<br />
Note that the AI's starting gold is one factor among many.<br />
<br />
===Varying the difficulty of a tactical problem presented to the player in a scenario===<br />
<br />
Considering the above factors, it is easy (if time consuming for the designer) to present tactical problems of differing difficulty in a single scenario. For most of the above factors, I'll give an example and explain in a sentence how if affects the tactical problem:<br />
<br />
====Terrain====<br />
If the map is predominantly a terrain that favors either the player's forces or the ai's forces, it will have a huge effect on the difficulty of the battle. For example, a player whose units are broadly loyalists will have a disastrous time trying to root Elves out of woods. The terrain is, however, generally fixed by the nature of the scenario (i.e. the designer can't really make a whole new map for each difficulty level). I would submit, however, that 80% of the actual<br />
fighting in a typical scenario occurs in less than 10% of the hexes. It is possible to change only one or two hexes and have a dramatic effect on the outcome of the battle if you can identify those "chokepoint" hexes.<br />
<br />
<br />
This can either be done by having three different maps (which I suspect is more work) or by using the WML tag [terrain].<br />
For example, the code:<br />
<br />
#ifdef EASY<br />
[terrain]<br />
x=4<br />
y=5<br />
letter=Ce<br />
[/terrain]<br />
#endif<br />
<br />
will turn the hex at (4,5) into an encampment.<br />
<br />
====Player's starting gold====<br />
If the player has more gold he can make more tactical mistakes because he can recruit more troops to replace the ones he's lost. Additionally, quantity has a quality all of its own; with sufficient starting gold the player can employ a swarm of cheap units to distract and overwhelm the AI forces. Balancing gold becomes difficult in campaigns because of gold carryover, which often has the exact opposite of the desired effect: struggling players will start with less gold (usually the minimum), while accomplished players will have a sizable treasury. This severely limits the designer's ability to balance the scenario by adjusting the player's starting gold.<br />
<br />
====AI starting gold====<br />
The easiest, and often first choice to balance a scenario, the AI starting gold primarily controls the size of the initial AI "rush". While adjusting this value clearly influences the difficulty of the scenario in a rather straightforward manner, it is often difficult to properly balance a scenario with gold alone. The relationship between AI starting gold and difficulty is not linear--a few more enemy units can make the first engagement impossible, while a few less can lead to a boring rout. Wesnoth was designed to balance well with equal gold, but unfortunately limitations of the AI generally require a significant gold advantage just to level the playing field. <br />
<br />
Setting the AI gold too high will require the player to perform perfectly, or in some cases, even better than perfect (meaning extreme luck with the RNG). Reducing the AI gold, however, does significantly reduce the difficulty of most scenarios (but try to avoid removing all the challenge). Unfortunately, due to the variation in player starting gold in a campaign, finding the optimal AI starting gold amount is nearly impossible.<br />
<br />
====AI income====<br />
Income provides a steady stream of gold, allowing the AI to mount a continued offensive. Income can be generated from villages, or supplied directly by the scenario designer using WML. A steady stream of income can help provide a more balanced challenge, instead of deciding the battle directly upon the first engagement. Extra income is generally less destabilizing than larger amounts of starting gold, making it a good candidate for fine-tuning scenario balance.<br />
<br />
====Player's recruitment list====<br />
If the player can only recruit units that will be effective against the enemy's forces, then the tactical problem is simplified. If I can only recruit heavy infantry to face a force of skeleton archers, then I have a less difficult tactical decision to make than if I can recruit heavy infantry and spearmen. Especially if I don't know the game well.<br />
<br />
While adjusting the player's recruitment list can prevent new players from recruiting "poor" units, that effect quickly levels off. Even a moderately inexperienced player quickly learns that heavy infantry "beat" skeleton archers. On the other hand, disallowing the best units can create a new tactical challenge. Either way, though, the player's recruitment list is a defining characteristic of the scenario, and usually does not lend itself to balancing due to limited options and lack of fine-grained control.<br />
<br />
====AI recruitment list====<br />
Restricting the enemy to a certain class of units (i.e. melee-only, ranged-only, impact-attacks-only) can simplify the tactical problem immensely. Consider an enemy who can only recruit thieves and thugs facing a predominantly loyalist force, who can recruit Bowmen and Spearmen. Bowmen are very good against thugs and thieves because they have a strong ranged attack. If the enemy can also recruit poachers and footpads, the tactical problem becomes more difficult because the player must balance the effectiveness of the Bowmen against the thieves and thugs with their (relative) weakness against footpads and poachers.<br />
<br />
More typically, the scenario designer can remove higher-level units to lower difficulty, and add them to raise difficulty. As an example, in '''Muff Malal Peninsula''' from HttT the AI is allowed to recruit Blood Bats on "hard", but only Vampire Bats on "normal" and "easy". Because the AI pays "full price" for their units, changing the units available doesn't significantly reduce the total power of the side, but it does limit the total power that can be brought to bear in a single turn. It can also be useful to limit defensive "bottlenecks" (weaker individual AI units allow the player to power through the defenders).<br />
<br />
====Player's recall list====<br />
A player's recall list is vital to the balance of the scenario, however the designer has little control over it. Because recalled troops are available at a ''significant'' discount, they can easily turn the tide of battle, especially against lower-level enemies. Unfortunately, as with the player's starting gold, the recall list is generally unbalanced: the accomplished players will have multiple high-level units, while the struggling players will have few. The situation is further complicated by another wrinkle: playing on "hard" often provides significantly more experience points than playing on easy, with the result that the "hard" recall list often has more high-level units than the "easy" list. <br />
<br />
At the beginning of a campaign, it is reasonable to assume few recalled troops (and those that are generally aren't too unbalancing). At the end of a mid-length campaign it is reasonable to assume that most of a player's troops will be level 3 and balance accordingly. In the middle of the campaign, however, it is nearly impossible to find a balance that suits all players. Some campaigns try to limit the player's recall list (such as '''Evacuation''' in EI), however they are rarely entirely successful (and often annoy players).<br />
<br />
====Time limitations====<br />
The turn limit controls to what extent the player is allowed to waste turns not directly accomplishing his mission. In the most difficult case scenario, a player must move his leader across the map and only enough turns are allotted such that if he ever moves in any other direction, time will run out. Increasing the turn limit allows an inexperienced player to make turn-costing mistakes and still be victorious.<br />
<br />
Extremely tight turn limits should be avoided. Because of the random nature of battle in Wesnoth, the player cannot guarantee that their four level-3 units will be able to take out the enemy leader in a single turn. Restricting the time limit to that level is equivalent to requiring good rolls from the RNG: something the player has no control over. In general, the time limit should serve to ''motivate'' the player, not as the driving challenge of the scenario.<br />
<br />
====Keep sizes====<br />
The size of a player's keep determines at what rate he can recruit troops. The size of the enemy's keep determines at what rate he can recruit troops. Divide one by the other and you get a relative-recruitment-rate (assuming both have lots of gold). If the relative recruitment rate favors the player, the tactical problem should be easier because he can replenish losses more easily than the AI player. If the AI player's keep is larger, then any losses by the human player will be that much more difficult to replace. <br />
<br />
Note that these parameters generally only have a significant influence on the first few turns--rarely will players (or AI) have sufficient gold to recruit a full keep of replacement troops (and almost never will they be able to do so on multiple turns in a row). In those first few turns, a smaller AI keep can "spread out" the initial rush, making it easier for the player to defend (but this effect can disappear entirely if the AI improves). The only time keep size is critical to the players is when they are limited to 'X' rounds of recruitment (either because their keep is forcibly removed or they are required to begin moving their leaders towards an objective). In short, while keep size can have an effect in some specific circumstances, it is generally not very useful for balancing scenarios.<br />
<br />
====AI Parameters====<br />
The AI parameters are perhaps the best way to alter a tactical problem. [[AiWML]] is a particularly important read for a scenario designer. I'd draw attention to aggression and [target] in particular. attack_depth is also very nice. These parameters are especially important for building scenarios with tactical problems that are fundamentally different from the basic kill-his-leader ones.<br />
<br />
====Distribution of villages====<br />
Using the [terrain] WML tag you can easily alter the distribution of villages in a scenario. Move the distribution towards the player, and things get easier. Move it towards the enemy, and they get harder.<br />
<br />
Villages are useful for three distinct reasons: they are defensible terrain, they grant income (and support), and they provide healing. Of those three, the first two can easily be replaced by other terrain and WML-granted income respectively. Village healing is necessary for the AI, and the player with no healers, however only two or three villages near the conflict are really useful for healing. With those limitations in mind, villages prove only slightly useful in balancing a scenario. For a subtle example, '''The Outpost''' in EI moves a village one hex closer on "easy" (putting it within convenient reach of the player's leader).<br />
<br />
====Unexpected reinforcements====<br />
Reinforcements, either friendly or hostile, can significantly complicate the tactical problem. They are a surprise that the player must accommodate with already deployed troops. Unfortunately, reinforcements often become [[tomato surprise]]s, meaning the best course of action for the player is to replay the scenario with foreknowledge of the "surprise". To avoid that result, reinforcements should generally be either foreshadowed, appear early in the scenario, or appear in moderation.<br />
<br />
As an example, the scenario '''Crossroads''' in HttT introduces enemy enemy reinforcements that randomly appear in hill tiles. These reinforcements are explained near the beginning of the scenario, and the enemy units appear in moderation (a few at a time, and only when the player moves onto hill terrain). The scenario '''Drowned Plains''' in EI uses the same code, but adjusts the type of ambushing units depending on the difficulty level. On the other side, many scenarios give the player a few friendly units near the beginning, with the number and type adjusted for difficulty.<br />
<br />
====Time of day====<br />
Time of day can be an interesting factor (assuming the map is not a cave and the armies aren't entirely neutral). While you are normally not free to change the cycle, changing the initial time of day can have a significant impact on the outcome of a battle. In particular, the time of day when the first AI rush is engaged can be crucial. On smaller maps, the time of day for the first engagement is practically dictated by the scenario designer. On a large or open battlefield, the player can arrange for a more favorable first encounter, so the initial time of day is not nearly as important. Note that these effects are most pronounced when the two armies are of opposite alignment.<br />
<br />
====Items====<br />
Items can modify the attributes of one of the player's units, often providing an extra advantage. Some typical items are "Holy Water" (changes melee weapons to ''arcane'' damage) and "Storm Trident" (adds ranged ''fire'' attack), while WML allows for very detailed customization. Even adding a few hitpoints, an extra movement point, or resistance to a specific type of damage can sway the balance of a close engagement.<br />
<br />
====WML effects====<br />
This is sort of a catch-all entry. One typical use is via the [http://www.wesnoth.org/macro-reference.xhtml#file:side-utils.cfg LIMIT_CONTEMPORANEOUS_RECRUITS] [[Macro]], which prevents the AI from over-recruiting one specific type of unit. As another example, '''Unexpected Appearance''' in EI uses WML to force the ''chance-to-hit'' to 100% at the beginning of the scenario (to prevent the player from losing if the RNG is mildly uncooperative). Making significant and unexplained modifications to the behavior players have come to expect in Wesnoth should be avoided, but used sparingly WML offers exceptionally fine-grained control for balancing a stubborn scenario.<br />
<br />
== See Also ==<br />
<br />
* [[BuildingScenarios]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category:Create]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=BuildingScenariosBalancing&diff=56439BuildingScenariosBalancing2015-06-19T19:31:40Z<p>Jmichael: /* Terrain */</p>
<hr />
<div>==Introduction==<br />
<br />
I'm writing this wiki article to talk about some things that came up as I was balancing a couple of the scenarios in the South Guard Campaign. I'm going to respond to something that PDF wrote but that I think is a pretty common idea. I don't mean to single him out:<br />
<br />
The PDF wrote:<br />
"Adding a difficulty level does not necessarily requires much balancing;<br />
you can, for example, just add or remove gold."<br />
<br />
<br />
I'm going to argue that that is fundamentally a flawed way to balance campaigns, and should only be used in conjunction<br />
with other balancing methods.<br />
<br />
===A Wesnoth scenario as a tactical problem===<br />
<br />
Each scenario in Wesnoth presents a tactical problem. The problem is generally phrased something like this: "Kill the enemy leader in so much time while keeping your leader alive," and its most common variation being: "Move your leader to this hex in said amount time with out letting him die." Of course, there are subtleties to these tactical problems. Because of the bonus gold at the end you want to finish in less then the stated time. And since you can recall experienced units for harder scenarios you may not want your vetrans to die, and of course there might be other units that you have or want to keep alive. There may also be certain units you must kill in order to succeed. At its core however, each scenario is a relatively simple tactical problem.<br />
<br />
<br />
===Factors that influence the difficulty of a tactical problem in Wesnoth===<br />
<br />
I am going to briefly list the factors (that I perceive) which affect the difficulty of a tactical problem in Wesnoth. This list is probably not exhaustive, so if there's something I've missed, post it and I'll add it to the list. They are in no particular order:<br />
<br />
* Terrain<br />
* Player's starting gold<br />
* AI's starting gold<br />
* AI income<br />
* Player's recriutment list<br />
* AI's recriutment list<br />
* Player's recall list<br />
* Time limitations<br />
* Keep sizes<br />
* AI Parameters (more on this in a minute)<br />
* Distribution of villages<br />
* Unexpected reinforcements<br />
* Time of day<br />
* Items<br />
* WML effects<br />
<br />
Note that the AI's starting gold is one factor among many.<br />
<br />
===Varying the difficulty of a tactical problem presented to the player in a scenario===<br />
<br />
Considering the above factors, it is easy (if time consuming for the designer) to present tactical problems of differing difficulty in a single scenario. For most of the above factors, I'll give an example and explain in a sentence how if affects the tactical problem:<br />
<br />
====Terrain====<br />
If the map is predominantly a terrain that favors either the player's forces or the ai's forces, it will have a huge effect on the difficulty of the battle. For example, a player whose units are broadly loyalists will have a disastrous time trying to root Elves out of woods. The terrain is, however, generally fixed by the nature of the scenario (i.e. the designer can't really make a whole new map for each difficulty level). I would submit, however, that 80% of the actual<br />
fighting in a typical scenario occurs in less than 10% of the hexes. It is possible to change only one or two hexes and have a dramatic effect on the outcome of the battle if you can identify those "chokepoint" hexes.<br />
<br />
<br />
This can either be done by having three different maps (which I suspect is more work) or by using the WML tag [terrain].<br />
For example, the code:<br />
<br />
#ifdef EASY<br />
[terrain]<br />
x=4<br />
y=5<br />
letter=Ce<br />
[/terrain]<br />
#endif<br />
<br />
will turn the hex at (4,5) into an encampment.<br />
<br />
====Player's starting gold====<br />
If the player has more gold he can make more tactical mistakes because he can recruit more troops to replace the ones he's lost. Additionally, quantity has a quality all of its own; with sufficient starting gold the player can employ a swarm of cheap units to distract and overwhelm the AI forces. Balancing gold becomes difficult in campaigns because of gold carryover, which often has the exact opposite of the desired effect: struggling players will start with less gold (usually the minimum), while accomplished players will have a sizable treasury. This severely limits the designer's ability to balance the scenario by adjusting the player's starting gold.<br />
<br />
====AI starting gold====<br />
The easiest, and often first choice to balance a scenario, the AI starting gold primarily controls the size of the initial AI "rush". While adjusting this value clearly influences the difficulty of the scenario in a rather straightforward manner, it is often difficult to properly balance a scenario with gold alone. The relationship between AI starting gold and difficulty is not linear--a few more enemy units can make the first engagement impossible, while a few less can lead to a boring rout. Wesnoth was designed to balance well with equal gold, but unfortunately limitations of the AI generally require a significant gold advantage just to level the playing field. <br />
<br />
Setting the AI gold too high will require the player to perform perfectly, or in some cases, even better than perfect (meaning extreme luck with the RNG). Reducing the AI gold, however, does significantly reduce the difficulty of most scenarios (but try to avoid removing all the challenge). Unfortunately, due to the variation in player starting gold in a campaign, finding the optimal AI starting gold amount is nearly impossible.<br />
<br />
====AI income====<br />
Income provides a steady stream of gold, allowing the AI to mount a continued offensive. Income can be generated from villages, or supplied directly by the scenario designer using WML. A steady stream of income can help provide a more balanced challenge, instead of deciding the battle directly upon the first engagement. Extra income is generally less destabilizing than larger amounts of starting gold, making it a good candidate for fine-tuning scenario balance.<br />
<br />
====Player's recruitment list====<br />
If the player can only recruit units that will be effective against the enemy's forces, then the tactical problem is simplified. If I can only recruit heavy infantry to face a force of skeleton archers, then I have a less difficult tactical decision to make than if I can recruit heavy infantry and spearmen. Especially if I don't know the game well.<br />
<br />
While adjusting the player's recruitment list can prevent new players from recruiting "poor" units, that effect quickly levels off. Even a moderately inexperienced player quickly learns that heavy infantry "beat" skeleton archers. On the other hand, disallowing the best units can create a new tactical challenge. Either way, though, the player's recruitment list is a defining characteristic of the scenario, and usually does not lend itself to balancing due to limited options and lack of fine-grained control.<br />
<br />
====AI recruitment list====<br />
Restricting the enemy to a certain class of units (i.e. melee-only, ranged-only, impact-attacks-only) can simplify the tactical problem immensely. Consider an enemy who can only recruit thieves and thugs facing a predominantly loyalist force, who can recruit Bowmen and Spearmen. Bowmen are very good against thugs and thieves because they have a strong ranged attack. If the enemy can also recruit poachers and footpads, the tactical problem becomes more difficult because the player must balance the effectiveness of the Bowmen against the thieves and thugs with their (relative) weakness against footpads and poachers.<br />
<br />
More typically, the scenario designer can remove higher-level units to lower difficulty, and add them to raise difficulty. As an example, in '''Muff Malal Peninsula''' from HttT the AI is allowed to recruit Blood Bats on "hard", but only Vampire Bats on "normal" and "easy". Because the AI pays "full price" for their units, changing the units available doesn't significantly reduce the total power of the side, but it does limit the total power that can be brought to bear in a single turn. It can also be useful to limit defensive "bottlenecks" (weaker individual AI units allow the player to power through the defenders).<br />
<br />
====Player's recall list====<br />
A player's recall list is vital to the balance of the scenario, however the designer has little control of it. Because recalled troops are available at a ''significant'' discount, they can easily turn the tide of battle, especially against lower-level enemies. Unfortunately, as with the player's starting gold, the recall list is generally unbalanced: the accomplished players will have multiple high-level units, while the struggling players will have few. The situation is further complicated by another wrinkle: playing on "hard" often provides significantly more experience points than playing on easy, with the result that the "hard" recall list often has more high-level units than the "easy" list. <br />
<br />
At the beginning of a campaign, it is reasonable to assume few recalled troops (and those that are generally aren't too unbalancing). At the end of a mid-length campaign it is reasonable to assume that most of a player's troops will be level 3 and balance accordingly. In the middle of the campaign, however, it is nearly impossible to find a balance that suits all players. Some campaigns try to limit the player's recall list (such as '''Evacuation''' in EI), however they are rarely entirely successful (and often annoy players).<br />
<br />
====Time limitations====<br />
The turn limit controls to what extent the player is allowed to waste turns not directly accomplishing his mission. In the most difficult case scenario, a player must move his leader across the map and only enough turns are allotted such that if he ever moves in any other direction, time will run out. Increasing the turn limit allows an inexperienced player to make turn-costing mistakes and still be victorious.<br />
<br />
Extremely tight turn limits should be avoided. Because of the random nature of battle in Wesnoth, the player cannot guarantee that their four level-3 units will be able to take out the enemy leader in a single turn. Restricting the time limit to that level is equivalent to requiring good rolls from the RNG: something the player has no control over. In general, the time limit should serve to ''motivate'' the player, not as the driving challenge of the scenario.<br />
<br />
====Keep sizes====<br />
The size of a player's keep determines at what rate he can recruit troops. The size of the enemy's keep determines at what rate he can recruit troops. Divide one by the other and you get a relative-recruitment-rate (assuming both have lots of gold). If the relative recruitment rate favors the player, the tactical problem should be easier because he can replenish losses more easily than the AI player. If the AI player's keep is larger, then any losses by the human player will be that much more difficult to replace. <br />
<br />
Note that these parameters generally only have a significant influence on the first few turns--rarely will players (or AI) have sufficient gold to recruit a full keep of replacement troops (and almost never will they be able to do so on multiple turns in a row). In those first few turns, a smaller AI keep can "spread out" the initial rush, making it easier for the player to defend (but this effect can disappear entirely if the AI improves). The only time keep size is critical to the players is when they are limited to 'X' rounds of recruitment (either because their keep is forcibly removed or they are required to begin moving their leaders towards an objective). In short, while keep size can have an effect in some specific circumstances, it is generally not very useful for balancing scenarios.<br />
<br />
====AI Parameters====<br />
The AI parameters are perhaps the best way to alter a tactical problem. [[AiWML]] is a particularly important read for a scenario designer. I'd draw attention to aggression and [target] in particular. attack_depth is also very nice. These parameters are especially important for building scenarios with tactical problems that are fundamentally different from the basic kill-his-leader ones.<br />
<br />
====Distribution of villages====<br />
Using the [terrain] WML tag you can easily alter the distribution of villages in a scenario. Move the distribution towards the player, and things get easier. Move it towards the enemy, and they get harder.<br />
<br />
Villages are useful for three distinct reasons: they are defensible terrain, they grant income (and support), and they provide healing. Of those three, the first two can easily be replaced by other terrain and WML-granted income respectively. Village healing is necessary for the AI, and the player with no healers, however only two or three villages near the conflict are really useful for healing. With those limitations in mind, villages prove only slightly useful in balancing a scenario. For a subtle example, '''The Outpost''' in EI moves a village one hex closer on "easy" (putting it within convenient reach of the player's leader).<br />
<br />
====Unexpected reinforcements====<br />
Reinforcements, either friendly or hostile, can significantly complicate the tactical problem. They are a surprise that the player must accommodate with already deployed troops. Unfortunately, reinforcements often become [[tomato surprise]]s, meaning the best course of action for the player is to replay the scenario with foreknowledge of the "surprise". To avoid that result, reinforcements should generally be either foreshadowed, appear early in the scenario, or appear in moderation.<br />
<br />
As an example, the scenario '''Crossroads''' in HttT introduces enemy enemy reinforcements that randomly appear in hill tiles. These reinforcements are explained near the beginning of the scenario, and the enemy units appear in moderation (a few at a time, and only when the player moves onto hill terrain). The scenario '''Drowned Plains''' in EI uses the same code, but adjusts the type of ambushing units depending on the difficulty level. On the other side, many scenarios give the player a few friendly units near the beginning, with the number and type adjusted for difficulty.<br />
<br />
====Time of day====<br />
Time of day can be an interesting factor (assuming the map is not a cave and the armies aren't entirely neutral). While you are normally not free to change the cycle, changing the initial time of day can have a significant impact on the outcome of a battle. In particular, the time of day when the first AI rush is engaged can be crucial. On smaller maps, the time of day for the first engagement is practically dictated by the scenario designer. On a large or open battlefield, the player can arrange for a more favorable first encounter, so the initial time of day is not nearly as important. Note that these effects are most pronounced when the two armies are of opposite alignment.<br />
<br />
====Items====<br />
Items can modify the attributes of one of the player's units, often providing an extra advantage. Some typical items are "Holy Water" (changes melee weapons to ''arcane'' damage) and "Storm Trident" (adds ranged ''fire'' attack), while WML allows for very detailed customization. Even adding a few hitpoints, an extra movement point, or resistance to a specific type of damage can sway the balance of a close engagement.<br />
<br />
====WML effects====<br />
This is sort of a catch-all entry. One typical use is via the [http://www.wesnoth.org/macro-reference.xhtml#file:side-utils.cfg LIMIT_CONTEMPORANEOUS_RECRUITS] [[Macro]], which prevents the AI from over-recruiting one specific type of unit. As another example, '''Unexpected Appearance''' in EI uses WML to force the ''chance-to-hit'' to 100% at the beginning of the scenario (to prevent the player from losing if the RNG is mildly uncooperative). Making significant and unexplained modifications to the behavior players have come to expect in Wesnoth should be avoided, but used sparingly WML offers exceptionally fine-grained control for balancing a stubborn scenario.<br />
<br />
== See Also ==<br />
<br />
* [[BuildingScenarios]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category:Create]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=BuildingScenariosBalancing&diff=56438BuildingScenariosBalancing2015-06-19T19:28:49Z<p>Jmichael: /* Varying the difficulty of a tactical problem presented to the player in a scenario */</p>
<hr />
<div>==Introduction==<br />
<br />
I'm writing this wiki article to talk about some things that came up as I was balancing a couple of the scenarios in the South Guard Campaign. I'm going to respond to something that PDF wrote but that I think is a pretty common idea. I don't mean to single him out:<br />
<br />
The PDF wrote:<br />
"Adding a difficulty level does not necessarily requires much balancing;<br />
you can, for example, just add or remove gold."<br />
<br />
<br />
I'm going to argue that that is fundamentally a flawed way to balance campaigns, and should only be used in conjunction<br />
with other balancing methods.<br />
<br />
===A Wesnoth scenario as a tactical problem===<br />
<br />
Each scenario in Wesnoth presents a tactical problem. The problem is generally phrased something like this: "Kill the enemy leader in so much time while keeping your leader alive," and its most common variation being: "Move your leader to this hex in said amount time with out letting him die." Of course, there are subtleties to these tactical problems. Because of the bonus gold at the end you want to finish in less then the stated time. And since you can recall experienced units for harder scenarios you may not want your vetrans to die, and of course there might be other units that you have or want to keep alive. There may also be certain units you must kill in order to succeed. At its core however, each scenario is a relatively simple tactical problem.<br />
<br />
<br />
===Factors that influence the difficulty of a tactical problem in Wesnoth===<br />
<br />
I am going to briefly list the factors (that I perceive) which affect the difficulty of a tactical problem in Wesnoth. This list is probably not exhaustive, so if there's something I've missed, post it and I'll add it to the list. They are in no particular order:<br />
<br />
* Terrain<br />
* Player's starting gold<br />
* AI's starting gold<br />
* AI income<br />
* Player's recriutment list<br />
* AI's recriutment list<br />
* Player's recall list<br />
* Time limitations<br />
* Keep sizes<br />
* AI Parameters (more on this in a minute)<br />
* Distribution of villages<br />
* Unexpected reinforcements<br />
* Time of day<br />
* Items<br />
* WML effects<br />
<br />
Note that the AI's starting gold is one factor among many.<br />
<br />
===Varying the difficulty of a tactical problem presented to the player in a scenario===<br />
<br />
Considering the above factors, it is easy (if time consuming for the designer) to present tactical problems of differing difficulty in a single scenario. For most of the above factors, I'll give an example and explain in a sentence how if affects the tactical problem:<br />
<br />
====Terrain====<br />
If the map is predominantly a terrain that favors either the player's forces or the ai's forces, it will have a huge effect on the difficulty of the battle. For example, a player whose units are broadly loyalists will have a disasterous time trying to root Elves out of woods. The terrain is, however, generally fixed by the nature of the scenario (i.e. the designer can't really make a whole new map for each difficulty level). I would submit, however, that 80% of the actual<br />
fighting in a typical scenario occurs in less than 10% of the hexes. It is possible to change only one or two hexes and have a dramatic effect on the outcome of the battle if you can identify those "chokepoint" hexes.<br />
<br />
<br />
This can either be done my having three different maps (which I suspect is more work) or by using the WML tag [terrain].<br />
For example, the code:<br />
<br />
#ifdef EASY<br />
[terrain]<br />
x=4<br />
y=5<br />
letter=Ce<br />
[/terrain]<br />
#endif<br />
<br />
will turn the hex at (4,5) into an encampment.<br />
<br />
====Player's starting gold====<br />
If the player has more gold he can make more tactical mistakes because he can recruit more troops to replace the ones he's lost. Additionally, quantity has a quality all of its own; with sufficient starting gold the player can employ a swarm of cheap units to distract and overwhelm the AI forces. Balancing gold becomes difficult in campaigns because of gold carryover, which often has the exact opposite of the desired effect: struggling players will start with less gold (usually the minimum), while accomplished players will have a sizable treasury. This severely limits the designer's ability to balance the scenario by adjusting the player's starting gold.<br />
<br />
====AI starting gold====<br />
The easiest, and often first choice to balance a scenario, the AI starting gold primarily controls the size of the initial AI "rush". While adjusting this value clearly influences the difficulty of the scenario in a rather straightforward manner, it is often difficult to properly balance a scenario with gold alone. The relationship between AI starting gold and difficulty is not linear--a few more enemy units can make the first engagement impossible, while a few less can lead to a boring rout. Wesnoth was designed to balance well with equal gold, but unfortunately limitations of the AI generally require a significant gold advantage just to level the playing field. <br />
<br />
Setting the AI gold too high will require the player to perform perfectly, or in some cases, even better than perfect (meaning extreme luck with the RNG). Reducing the AI gold, however, does significantly reduce the difficulty of most scenarios (but try to avoid removing all the challenge). Unfortunately, due to the variation in player starting gold in a campaign, finding the optimal AI starting gold amount is nearly impossible.<br />
<br />
====AI income====<br />
Income provides a steady stream of gold, allowing the AI to mount a continued offensive. Income can be generated from villages, or supplied directly by the scenario designer using WML. A steady stream of income can help provide a more balanced challenge, instead of deciding the battle directly upon the first engagement. Extra income is generally less destabilizing than larger amounts of starting gold, making it a good candidate for fine-tuning scenario balance.<br />
<br />
====Player's recruitment list====<br />
If the player can only recruit units that will be effective against the enemy's forces, then the tactical problem is simplified. If I can only recruit heavy infantry to face a force of skeleton archers, then I have a less difficult tactical decision to make than if I can recruit heavy infantry and spearmen. Especially if I don't know the game well.<br />
<br />
While adjusting the player's recruitment list can prevent new players from recruiting "poor" units, that effect quickly levels off. Even a moderately inexperienced player quickly learns that heavy infantry "beat" skeleton archers. On the other hand, disallowing the best units can create a new tactical challenge. Either way, though, the player's recruitment list is a defining characteristic of the scenario, and usually does not lend itself to balancing due to limited options and lack of fine-grained control.<br />
<br />
====AI recruitment list====<br />
Restricting the enemy to a certain class of units (i.e. melee-only, ranged-only, impact-attacks-only) can simplify the tactical problem immensely. Consider an enemy who can only recruit thieves and thugs facing a predominantly loyalist force, who can recruit Bowmen and Spearmen. Bowmen are very good against thugs and thieves because they have a strong ranged attack. If the enemy can also recruit poachers and footpads, the tactical problem becomes more difficult because the player must balance the effectiveness of the Bowmen against the thieves and thugs with their (relative) weakness against footpads and poachers.<br />
<br />
More typically, the scenario designer can remove higher-level units to lower difficulty, and add them to raise difficulty. As an example, in '''Muff Malal Peninsula''' from HttT the AI is allowed to recruit Blood Bats on "hard", but only Vampire Bats on "normal" and "easy". Because the AI pays "full price" for their units, changing the units available doesn't significantly reduce the total power of the side, but it does limit the total power that can be brought to bear in a single turn. It can also be useful to limit defensive "bottlenecks" (weaker individual AI units allow the player to power through the defenders).<br />
<br />
====Player's recall list====<br />
A player's recall list is vital to the balance of the scenario, however the designer has little control of it. Because recalled troops are available at a ''significant'' discount, they can easily turn the tide of battle, especially against lower-level enemies. Unfortunately, as with the player's starting gold, the recall list is generally unbalanced: the accomplished players will have multiple high-level units, while the struggling players will have few. The situation is further complicated by another wrinkle: playing on "hard" often provides significantly more experience points than playing on easy, with the result that the "hard" recall list often has more high-level units than the "easy" list. <br />
<br />
At the beginning of a campaign, it is reasonable to assume few recalled troops (and those that are generally aren't too unbalancing). At the end of a mid-length campaign it is reasonable to assume that most of a player's troops will be level 3 and balance accordingly. In the middle of the campaign, however, it is nearly impossible to find a balance that suits all players. Some campaigns try to limit the player's recall list (such as '''Evacuation''' in EI), however they are rarely entirely successful (and often annoy players).<br />
<br />
====Time limitations====<br />
The turn limit controls to what extent the player is allowed to waste turns not directly accomplishing his mission. In the most difficult case scenario, a player must move his leader across the map and only enough turns are allotted such that if he ever moves in any other direction, time will run out. Increasing the turn limit allows an inexperienced player to make turn-costing mistakes and still be victorious.<br />
<br />
Extremely tight turn limits should be avoided. Because of the random nature of battle in Wesnoth, the player cannot guarantee that their four level-3 units will be able to take out the enemy leader in a single turn. Restricting the time limit to that level is equivalent to requiring good rolls from the RNG: something the player has no control over. In general, the time limit should serve to ''motivate'' the player, not as the driving challenge of the scenario.<br />
<br />
====Keep sizes====<br />
The size of a player's keep determines at what rate he can recruit troops. The size of the enemy's keep determines at what rate he can recruit troops. Divide one by the other and you get a relative-recruitment-rate (assuming both have lots of gold). If the relative recruitment rate favors the player, the tactical problem should be easier because he can replenish losses more easily than the AI player. If the AI player's keep is larger, then any losses by the human player will be that much more difficult to replace. <br />
<br />
Note that these parameters generally only have a significant influence on the first few turns--rarely will players (or AI) have sufficient gold to recruit a full keep of replacement troops (and almost never will they be able to do so on multiple turns in a row). In those first few turns, a smaller AI keep can "spread out" the initial rush, making it easier for the player to defend (but this effect can disappear entirely if the AI improves). The only time keep size is critical to the players is when they are limited to 'X' rounds of recruitment (either because their keep is forcibly removed or they are required to begin moving their leaders towards an objective). In short, while keep size can have an effect in some specific circumstances, it is generally not very useful for balancing scenarios.<br />
<br />
====AI Parameters====<br />
The AI parameters are perhaps the best way to alter a tactical problem. [[AiWML]] is a particularly important read for a scenario designer. I'd draw attention to aggression and [target] in particular. attack_depth is also very nice. These parameters are especially important for building scenarios with tactical problems that are fundamentally different from the basic kill-his-leader ones.<br />
<br />
====Distribution of villages====<br />
Using the [terrain] WML tag you can easily alter the distribution of villages in a scenario. Move the distribution towards the player, and things get easier. Move it towards the enemy, and they get harder.<br />
<br />
Villages are useful for three distinct reasons: they are defensible terrain, they grant income (and support), and they provide healing. Of those three, the first two can easily be replaced by other terrain and WML-granted income respectively. Village healing is necessary for the AI, and the player with no healers, however only two or three villages near the conflict are really useful for healing. With those limitations in mind, villages prove only slightly useful in balancing a scenario. For a subtle example, '''The Outpost''' in EI moves a village one hex closer on "easy" (putting it within convenient reach of the player's leader).<br />
<br />
====Unexpected reinforcements====<br />
Reinforcements, either friendly or hostile, can significantly complicate the tactical problem. They are a surprise that the player must accommodate with already deployed troops. Unfortunately, reinforcements often become [[tomato surprise]]s, meaning the best course of action for the player is to replay the scenario with foreknowledge of the "surprise". To avoid that result, reinforcements should generally be either foreshadowed, appear early in the scenario, or appear in moderation.<br />
<br />
As an example, the scenario '''Crossroads''' in HttT introduces enemy enemy reinforcements that randomly appear in hill tiles. These reinforcements are explained near the beginning of the scenario, and the enemy units appear in moderation (a few at a time, and only when the player moves onto hill terrain). The scenario '''Drowned Plains''' in EI uses the same code, but adjusts the type of ambushing units depending on the difficulty level. On the other side, many scenarios give the player a few friendly units near the beginning, with the number and type adjusted for difficulty.<br />
<br />
====Time of day====<br />
Time of day can be an interesting factor (assuming the map is not a cave and the armies aren't entirely neutral). While you are normally not free to change the cycle, changing the initial time of day can have a significant impact on the outcome of a battle. In particular, the time of day when the first AI rush is engaged can be crucial. On smaller maps, the time of day for the first engagement is practically dictated by the scenario designer. On a large or open battlefield, the player can arrange for a more favorable first encounter, so the initial time of day is not nearly as important. Note that these effects are most pronounced when the two armies are of opposite alignment.<br />
<br />
====Items====<br />
Items can modify the attributes of one of the player's units, often providing an extra advantage. Some typical items are "Holy Water" (changes melee weapons to ''arcane'' damage) and "Storm Trident" (adds ranged ''fire'' attack), while WML allows for very detailed customization. Even adding a few hitpoints, an extra movement point, or resistance to a specific type of damage can sway the balance of a close engagement.<br />
<br />
====WML effects====<br />
This is sort of a catch-all entry. One typical use is via the [http://www.wesnoth.org/macro-reference.xhtml#file:side-utils.cfg LIMIT_CONTEMPORANEOUS_RECRUITS] [[Macro]], which prevents the AI from over-recruiting one specific type of unit. As another example, '''Unexpected Appearance''' in EI uses WML to force the ''chance-to-hit'' to 100% at the beginning of the scenario (to prevent the player from losing if the RNG is mildly uncooperative). Making significant and unexplained modifications to the behavior players have come to expect in Wesnoth should be avoided, but used sparingly WML offers exceptionally fine-grained control for balancing a stubborn scenario.<br />
<br />
== See Also ==<br />
<br />
* [[BuildingScenarios]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category:Create]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=FilterWML/Examples_-_How_to_use_Filter&diff=56437FilterWML/Examples - How to use Filter2015-06-17T03:31:13Z<p>Jmichael: /* Ordering and writing */</p>
<hr />
<div>== WML Filtering ==<br />
<br />
<br />
Filters are a very important part of WML language, and a fairly complex too for various <br />
reasons. Here, we shall try to explain how to use them with more details than in the reference <br />
wiki pages. But the goal is not to replace these pages, and we assume you have at least some <br />
knowledge of the various WML filters, namely unit and location filters. The examples given <br />
below aren’t always the best way to do things: the goal here is understanding filtering, not to <br />
give a complete WML course. <br />
<br />
<br />
<br />
=== Basics: How filters work. ===<br />
<br />
<br />
Filtering is narrowing a set of objects to a result set using criteria. Let’s give an example : <br />
given a set of cards, if one is asked to select the spades, (s)he will probably check the cards <br />
one by one and create two stacks : one containing only the spades, and another containing the <br />
unselected cards. This is a very simple filter, where the criterion is « this card has spades <br />
colour » and the result set is a card stack. <br />
The spades cards are said to ‘match’ the filter. <br />
If next we’re asked to find the king of spades, most probably, we will not restart from the <br />
beginning but only scan the spades stack to find the right card. This is an example of a two <br />
criteria filter. In WML we would write something like: <br />
[filter]<br />
colour=spades<br />
value=king<br />
[/filter]<br />
to describe the operation. Criteria are expressions evaluating to true or false. Is this card colour <br />
spades ? That’s how we must read the sentence: ‘colour=spades’. <br />
Now, stating both criteria must be met is not the only way to combine them: <br />
[filter] <br />
colour=spades,diamonds <br />
value=king <br />
[/filter]<br />
The first expression will be true if a card colour is ‘spades OR diamonds’. It would make no <br />
sense to state they should be ‘spades AND diamonds’, of course. <br />
In many languages, you must specify how criteria combine using the special keywords ‘OR’ <br />
and ‘AND’. In WML, you can do so, but most often, you’re not required to do so. Writing <br />
explicitly the logical operators would give something like:<br />
[filter] <br />
[and] <br />
colour=spades <br />
[or] <br />
colour=diamonds <br />
[/or] <br />
[/and] <br />
<br />
[and] <br />
value=king <br />
[/and] <br />
[/filter]<br />
or in natural speech: “is card (colour=spades OR colour=diamonds) AND value=king ?” Note <br />
here the use of parentheses. As in algebra, they mean their content must be evaluated prior to <br />
applying the last criterion. <br />
'''[and]''' and '''[or]''' subtags are WML equivalents of parentheses. They are not mandatory (like in some other languages), and this is a cool feature, but can be misleading in complex filters. <br />
The rule is: <br />
* listed criteria are ''ANDed'', in other words, they must all be true for the object to match the filter. <br />
* comma separated lists in a criterion are equivalent to ''ORed'' criteria. <br>In other words, one only is enough for the object to match the filter.<br />
<br />
<br />
'''There are some things important to note:'''<br />
<br />
* A complex filter can always be split into simpler filters applied in chain, each filter taking as starting set the result set of the former one. In our example, we applied the filter « value=king » to the result set of filter « color=spades ». This is important when building or debugging filters, because complex ones can easily be reduced to a chain of simpler ones.<br />
<br />
* Criteria order is not important from a logical point of view. We could have searched the kings first, obtaining the four kings in our result set, and the card of color spade next. The final result is the same. But in WML, for some reasons we shall study later, order can be important.<br />
<br />
* Result sets can contain any number of objects. One can’t assume the result set of our filter will contain a single card ‘THE king of spades’. A human being would probably stop the search when finding a king of spades, but filters don’t. If our starting card packet is not a complete set (some cards were lost, a cheater introduced some more, or anything else), you’ll find one, none or many kings of spades. It’s a common error in WML to assume filters will select a single object.<sup>1)</sup> <br />
* Any unit or location will match an empty filter like this one: <br />
[event] <br />
name=moveto <br />
[filter] <br />
[/filter] <br />
… <br />
[/event]<br />
This event will be triggered on every move of every unit on the map.<br />
<br />
________________________________________<br />
<br />
:<sup>1)</sup> One can be sure only when filtering with ID’s because they are unique.<br />
<br />
<br />
<br />
'''Here now are two versions of the same action, using filters and not.''' <br />
[modify_unit] <br />
[filter] <br />
side=2 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
side=1 <br />
[/modify_unit] <br />
This moves to side 1 all side 2 units except orcs. Please note how filter syntax is after all very close to natural speech. This is why we shall see a good way to design complex filters is writing an accurate sentence describing them first. <br />
Using no filter (and assuming ‘all_units’ is an array containing all created units in a scenario) we could write : <br />
{FOREACH all_units i} <br />
[if] <br />
[variable] <br />
name= all_units[$i].side <br />
equals=2 <br />
[/variable] <br />
[and] <br />
[variable] <br />
name= all_units[$i].race <br />
not_equals=orc <br />
[/variable] <br />
[/and] <br />
[then] <br />
[set_variable] <br />
name= all_units[$i].side <br />
value=1 <br />
[/set_variable] <br />
[unstore_unit] <br />
variable= all_units[$i] <br />
[/unstore_unit] <br />
[/then] <br />
[/if] <br />
{NEXT i} <br />
Of course, the first version is much more concise. One should note: <br />
* Filters are hidden loops<sup>2)</sup> fetching all elements of the starting set. <br />
* Criteria composition is more explicit in the second version. We have here an [and] tag which is missing in the first one. It shows clearly both conditions must be met. In filters, the [and] tag is most often implicit, as we have already seen.<br />
________________________________________<br />
<br />
:<sup>2) </sup>Internally, it’s not always the case, but we’ve not to deal with internal implementations.<br>Conceptually, filters can always be seen as hidden loops.<br />
<br />
<br />
At this point, a question arises: where are the starting and result sets we talked about? They show nowhere. The reply is most often we don’t need to see them, because we don’t need to create result sets explicitly. We need to use them to specify actions targets or conditions. In the '''‘modify_unit’''' example, we apply the action « change side » to the result set of the filter and then need it no more.<br />
The starting set is implicit too. Most of the time, it’s the larger available set of objects: e.g., all created units (sometimes including the recall list), or all hexes in the map. In the '''moveto''' event, it contains only the moving unit.<br />
Once again, we are not saying these sets really exist in Wesnoth engine code. But this is an accurate model of how the filters work in WML.<br />
<br />
=== Result sets and arrays. === <br />
<br />
<br />
A good way to explicitly get the result set is to use the '''‘store_...’''' tags. These actions create <br />
arrays containing the result set of the filter they take as parameter. This is very useful in many ways. The common use is of course to store units and locations for some processing or later use. But one can use this to split complex filters and inspect intermediate results. The former '''‘modify_unit’''' example could be written as: <br />
[store_unit] <br />
variable=temp1 <br />
[filter] <br />
side=2 <br />
[/filter] <br />
[/store_unit] <br />
<br />
[store_unit] <br />
variable=temp2 <br />
[filter] <br />
find_in=temp1 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
[/store_unit] <br />
<br />
[modify_unit] <br />
[filter] <br />
find_in=temp2 <br />
[/filter] <br />
side=1 <br />
[/modify_unit]<br />
This trivial example shows not only how to debug complex filters (inspecting the content of ''temp1'' and ''temp2'' arrays), but how to specify a starting set with the '''‘find_in’''' key. Without it, the second '''‘store_unit’''' tag would store all units except orcs. With it, we ask to apply the filter to the content of ''temp1'' array <u>only</u> (all side 2 units). It’s like our card example where we selected the spades first and next the king(s) in the spades stack. <br />
The '''‘find-in’''' key is really precious in many cases: often it’s easier to explicitly build an array containing the objects we want to select, than it is to create a complex filter to retrieve them. <br />
For example, if we want dying units to drop weapons and other units to retrieve them, it can be very difficult to create a filter allowing to select locations where the weapons were dropped. Instead, we can build an array containing their locations (and other information as well). Since this array's members have x and y (sub-)members, the '''find_in''' key of a location filter can use it<sup>3)</sup>. It would be:<br />
# in the die event <br />
[set_variables] <br />
name=weapons <br />
mode=append <br />
[value] <br />
x=$unit.x <br />
y=$unit.y <br />
… anything else, for instance the image name and item id. <br />
[/value] <br />
[/set_variables] <br />
<br />
# drop item, etc… <br />
<br />
<br />
# and in a moveto event<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
[filter_location] <br />
find_in=weapons <br />
[/filter_location] <br />
[/filter] <br />
… <br />
<br />
Let’s give another example. <br />
The scenario’s map features three temples at 10,10 20,20 30,30.<br />
We want to give some bonus gold to side 1 if any side 1 unit visits temple 1,2,3 exactly in that order.<br />
Here is a solution using result sets arrays : <br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1<br />
x,y=10,10 <br />
[not] <br />
find_in=temple_1 <br />
[/not] <br />
[/filter] <br />
[store_unit] <br />
mode=append <br />
variable=temple_1 <br />
[filter] <br />
id=$unit.id <br />
[/filter]<br />
[/store_unit] <br />
[/event]<br />
In temple_1, we store all side 1 units visiting temple_1, but only once (that’s why the '''[not] find_in''' tag, because units can visit the temple more than once). <br />
<br />
________________________________________<br />
<br />
:<sup>3) </sup>It’s surprising because this array doesn’t contain locations, but it’s a feature, not a side effect.<br />
<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
x,y=20,20 <br />
find_in=temple_1 <br />
[not] <br />
find_in=temple_2 <br />
[/not] <br />
[/filter]<br />
[store_unit] <br />
mode=append <br />
variable=temple_2 <br />
[filter] <br />
id=$unit.id <br />
[/filter] <br />
[/store_unit] <br />
[/event] <br />
<br />
This time, we store only units previously stored in temple_1 (= they already visited that temple).<br />
Then the last event is obviously : <br />
[event] <br />
name=moveto <br />
first_time_only=yes <br />
[filter] <br />
side=1 <br />
x,y=30,30 <br />
find_in=temple_2 <br />
[/filter] <br />
[gold] <br />
side=1 <br />
amount=1000 <br />
[/gold] <br />
{CLEAR_VARIABLE temple_1,temple_2} <br />
[/event]<br />
<br />
=== Ordering and writing === <br />
<br />
<br />
We said earlier that criteria order is not significant. That's right from a theoretical point of view. But, for syntactic reasons and particularly because the radius key in location filters, this is not fully true in WML. Actually conditions are applied to candidate objects until one proves to be false or all conditions are checked. Then, if some condition proves to be false, remaining conditions <u>are not evaluated</u> (so if there's some radius there, it will not be executed). In some cases, this may be important. One can assume conditions are evaluated in the order they are found except logical operators (and, or, not) which are evaluated <u>after</u> other conditions. It’s very important to note that evaluation order of top level conditions (those not embedded in and/or/not tags) is undocumented, which means <u>your code shouldn’t rely on it</u>. On the contrary, and/or/not tags are always executed last, in the order they are written. <br />
<br />
So in this example:<br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
[or] <br />
side=2 <br />
[/or] <br />
gender=female <br />
[/filter] <br />
will be executed using this order: <br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
gender=female <br />
[or] <br />
side=2 <br />
[/or] <br />
[/filter]<br />
One should be aware of this for some reasons:<br />
<br />
'''⇒''' Clarity: your code will be easier to understand and to debug if you avoid meddling conditions, nested filters and logical blocks, and write them in the order in which they are evaluated.<br />
<br />
'''⇒''' Performance.<br />
Most of the time, performance is not an issue. But it can be if you have a lot of units and use '''[filter_wml]'''. More generally, it’s good programming practice to execute the <u>more restrictive</u> test first.<br />
Consider this example:<br />
[filter]<br />
race=orc<br />
x,y=16,22<br />
[/filter]<br />
The first condition will be evaluated on all units. But the second one will be evaluated on all orcs. If instead we write:<br />
[filter]<br />
x,y=16,22<br />
race=orc<br />
[/filter]<br />
obviously, the second condition will be evaluated once at most, and filtering will be faster. (Strictly speaking, I should have wrapped the second condition in an '''and''' tag to force evaluation order).<br />
Remember too that logical operators '''(and, or, not)''' are not mandatory, but are allowed. So one can use them for clarity's sake, or to force an evaluation order. It’s particularly important when using '''or''' tags.<br />
<br />
In the example above one could expect the result set contains all women of sides 1 and 2 wielding a sword. But it contains actually all side 1 women wielding a sword plus <u>all side 2 units</u>.<br />
<br />
Filters work actually as if top level criteria were enclosed in an implicit '''and''' tag: so we should read:<br />
[filter]<br />
[and] '''#implicit'''<br />
has_weapon=sword<br />
side=1<br />
gender=female<br />
[/and]<br />
[or]<br />
side=2<br />
[/or]<br />
[/filter]<br />
and what we probably wanted is:<br />
[filter]<br />
has_weapon=sword<br />
gender=female<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
Note that it could be written:<br />
[filter]<br />
[and]<br />
has_weapon=sword<br />
gender=female<br />
[/and]<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
This syntax is correct and you can use it if you find it clearer.<br />
Remembering this implicit '''and''' tag (and writing it explicitly at will) is very important to understand or design complex filters.<br />
<br />
=== Writing complex filters. ===<br />
<br />
<br />
Here are some guidelines one can use when writing complex filters. Suppose we want to set up some kind of disease aura harming units standing close to some villages. We shall start writing a sentence describing the feature.<br />
<br />
'''We want to select units who:'''<br />
'''Are enemy to side 1'''<br />
'''stand on hexes which'''<br />
'''are near to'''<br />
'''villages'''<br />
'''with side 1 units standing on it'''<br />
Mark we put only one condition on each line to clearly separate them. Mark we sorted them, because some apply to units to be filtered, others to locations, and finally to other (enemy) units standing on locations. Next, we shall add logical operators and parenthesis to clearly specify what we want:<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">Are enemy to side 1</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now, we are ready to start building the filter. Since we want units who… it shall be a unit filter. In the standard unit filter, there’s no condition directly allowing to state the unit is enemy to side 1. So we have to replace this with something valid in the SUF context. Using parenthesis to avoid errors, we can replace the condition with a side filter because it’s valid in unit filters.<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">(belongs to a side which ('''<br />
'''is enemy to side 1) )</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now we can write the filter. Here we do it step by step to show how the translation is rather straightforward and how our parenthesis match exactly the subtags.<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
'''AND''' <br />
'''stand on locations which ( # we start to filter locations there'''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
'''AND'''<br />
'''have unit on it who (# to unit filter again'''<br />
'''Belongs to side 1'''<br />
''')'''<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
[and]<br />
[filter]<br />
side=1<br />
[/filter]<br />
[/and]<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
Here, we are done. The filter should work as it is, but it looks rather unusual because all these '''[and]''' blocks. Actually we can delete most of them using a simple rule: '''[and]''' tags are not needed when they contain one single criterion or a single block, (except if you want to set up an evaluation order). In our example, the '''filter_location''' block is alone in its '''and''' tag, and the embedded '''filter''' as well, so we can avoid those '''and''' tags and get finally:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[filter_location]<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
radius=3<br />
[/filter_location]<br />
[/filter]<br />
[harm_unit]<br />
[filter]<br />
id=$unit.id<br />
[/filter]<br />
amount=10<br />
animate=yes<br />
[/harm_unit]<br />
[/event]<br />
<br />
<br />
<br />
=== Filters uses: events actions and conditions. ===<br />
<br />
<br />
Here, we shall deal with filter uses in WML. The language is not fully consistent, mainly to simplify its syntax, so some points can be misleading.<br />
:<u>'''Using in actions'''</u><br />
Most actions take a filter as first parameter. The main difficulty here is to know if '''[filter]''' tags must be used or not. Actually, they’re used to avoid confusing keys and criteria when they have the same name<sup>4</sup>. For instance, the '''[kill]''' action needs a unit filter and has these keys:<br />
<br />
* '''animate:''' if 'yes', displays the unit dying (fading away).<br />
* '''fire_event:''' if 'yes', triggers any appropriate 'die' events (See EventWML). Note that events are only fired for killed units that have been on the map (as opposed to recall list).<br />
* '''[secondary_unit]''' with a StandardUnitFilter as argument. Do not use a [filter] tag. Has an effect only if fire_event=yes. The first on-map unit matching the filter.<br />
<br />
As we can see, none of these keys and tags are shared with unit filter keys and tags. This means the code parser needs no '''[filter]''' tag to know which key belongs to the filter and which to the action. But in the code, there’s no obvious distinction.<br />
<br />
________________________________________<br />
<br />
:<sup>4) </sup>Note there’s no specific top level tag for location filters.<br />
<br />
<br />
<br />
[kill]<br />
animate=yes '''# this belongs to the ‘kill’'''<br />
id=BadGuy_101 '''# this belongs to the unit filter'''<br />
[/kill]<br />
is the correct syntax. Note a common error is to use a '''[filter]''' tag here. Since this tag is unknown in the context, it is ignored, so the kill action is applied to the starting set, i.e. all units (including the recall lists). Same with the '''filter_side''' tag which is not needed everywhere (in '''store_sides''' particularly).<br />
Adversely, '''[modify_unit]''' obviously requires a '''[filter]''' tag, because all keys and criteria have the same name:<br />
[modify_unit]<br />
side=2<br />
side=1<br />
[/modify_unit]<br />
<br />
This would make no sense of course because one can’t find if side 1 units must be put into side 2 or the contrary. Anyway, there is an exception: the '''[store_unit]''' tag requires a '''[filter]''' tag even if there is no '''‘variable’''' key in units description. Another special case is when using terrain action, because the key '''terrain''' is valid both in location filters and terrain action. Since there is no special tag to delimit location filters, one should write:<br />
[terrain]<br />
[and]<br />
terrain=Wo* '''# here is the filter criterion'''<br />
[/and]<br />
terrain=Rr '''# and the new terrain'''<br />
[/terrain]<br />
The next example can be confusing:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
[/kill]<br />
When reading the '''‘kill’''' tag documentation, one will not find any '''‘filter_location’''' or location filter entry. Does this means it’s an undocumented feature ? No. But the '''‘filter_location’''' belongs to the implicit '''‘filter’''' tag of the '''‘kill’''' action and is documented there. It’s kind of:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
'''#'''[filter] '''we’re filtering units here, not locations'''<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
'''#'''[/filter]<br />
[/kill]<br />
<br />
=== Using filters in conditions. ===<br />
<br />
Filters can be used to create conditional expressions. They can be nested in '''[have_unit]''' or '''[have_location]''' tags or in nested filters. Here, the result set is not used directly, but its size must fall in the range defined by the '''‘count’''' key. This finally gives a Boolean result: true or false. So one can use them in '''[if] [show_if]''' conditional actions or in a '''[filter_condition]''' tag. They are widely used in nested filters too (see the special chapter on this).<br />
<br />
Let’s give some examples:<br />
<br />
[have_unit] '''# this piece of code evaluates to true when no more enemy leaders are alive'''<br />
canrecruit=yes<br />
[not]<br />
side=1<br />
[/not]<br />
count=0<br />
[/have_unit]<br />
<br />
[have_location] '''# this one is true if at least 5 side 1 units stand on a village'''<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
count=5-1000<br />
[/have_location]<br />
Note we could also write this condition:<br />
[have_unit]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
count=5-1000<br />
[/have_unit]<br />
Note we could use a '''[store_unit]''' instead, testing the ''length'' property of the array:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
[/filter]<br />
[/store_unit]<br />
#[if] or [filter_condition]<br />
[variable]<br />
name=temp.length<br />
greater_than=4<br />
[/variable]<br />
<br />
<br />
=== Using filters in events. ===<br />
In events, filters are always used in a conditional way, because they state if the event should fire or not. In any event, we can set '''[filter_condition]''' using '''have_unit''' or '''have_location''' (and a more ordinary variable condition, but this is off topic).<br />
Some events use filters in a special way: '''moveto''' and '''attack''' events particularly. In them, the filtering apply not on all units as usual, but only on units involved in the event action: one unit only is moving at a time, two units only are involved in a fight. Then the event fires if and only if the involved unit(s) match the filter.<br />
Note that one can use '''[filter]''' and '''[filter_condition]''' in the same moveto or attack event. Both conditions are then ANDed.<br />
<br />
'''Filtering units'''<br />
<br />
Criteria allowed to filter units (in standard unit filters) are listed below. First, the keys dealing with the unit properties (as usual, comma separated lists means conditions are ORed):<br />
:'''id:''' ''can be a comma-separated list, every unit with one of these ids matches''<br />
:'''type:''' ''can be a list of types''<br />
:'''race:''' ''(Version 1.11 and later only: this can be a comma-separated list)''<br />
:'''ability:''' ''unit has an ability with the given id (not name !)''<br />
:'''side:''' ''the unit is on the given side (can be a list). One can use a [filter_side] instead''<br />
:'''has_weapon:''' ''the unit has a weapon with the given name''<br />
:'''canrecruit:''' ''yes if the unit can recruit (i.e. is a leader)''<br />
:'''gender:''' ''female if the unit is female rather than the default of male''<br />
:'''role:''' ''the unit has been assigned the given role''<br />
:'''level:''' ''the level of the unit''<br />
:'''defense:''' ''current defense of the unit on current tile''<br />
:'''movement_cost:''' ''current movement cost of the unit on current tile''<br />
:'''x,y:''' ''the position of the unit. (Ranges ? probably because it works in moveto events)''<br />
<br />
<br />
Not all unit properties are listed here, but they can be used in a '''[filter_wml]''' sub-tag like this:<br />
[filter_wml]<br />
max_moves=7<br />
[/filter_wml]<br />
<br />
[filter_wml]<br />
[status]<br />
poisoned=yes<br />
[/status]<br />
[/filter_wml]<br />
Some of them accept comma separated lists or ranges. Some not, but it also possible to use them more than once with '''[and]''' and '''[or]''' subtags. For instance :<br />
[and]<br />
race= elf<br />
[or]<br />
race= merman<br />
[/or]<br />
[/and]<br />
Other sub tags are dealing with unit relationships:<br />
:'''⇒''' The hex on which they stand: '''[filter_location]''' which contains a standard location filter.<br />
:'''⇒''' The units adjacent to it: '''[filter_adjacent]''' which contains another standard unit filter<br />
:'''⇒''' Their visible status relating to a particular side: '''[filter_vision]'''<br />
These are nested filters ; or in other words, filters used to create conditions and not result sets (see earlier and later).<br />
<br />
Custom functions returning a boolean:<br />
:'''formula:''' FormulaAI like formula.<br />
:'''lua_function:''' lua function<br />
<br />
SUFs accept a '''find_in''' key too. As we saw earlier, this allows to restrict the starting set to the content of an array.<br />
<br />
=== this_unit ===<br />
<br />
This variable is a special variable defined only inside SUFs. Suppose we want to catch in a filter units at full health. We can use the '''hitpoints''', but the problem is we know not which value to use, because every unit type has its own:<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=?<br />
[/filter_wml]<br />
[/filter]<br />
This is why we could have the use of some way to specify the unit being fetched during the filtering.<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=$this_unit.max_hitpoints<br />
[/filter_wml]<br />
[/filter]<br />
This does the trick. The condition value will be updated according to unit properties before executing the check.<br />
<br />
<br />
=== Filtering locations ===<br />
<br />
'''find_in''': a location array specifying the starting set.<br />
<br />
'''time_of_day''': one of lawful, chaotic, neutral or liminal.<br />
'''time_of_day_id''': one or more from: dawn, morning, afternoon, dusk, first_watch, second_watch, indoors, underground and deep_underground.<br />
<br />
'''terrain''': comma separated list of terrains.<br />
'''x,y''': the same as in the unit filter; supports any range.<br />
'''owner_side''': If a valid side number, restricts stored locations to villages belonging to this side. If 0, restricts to all unowned locations (the whole map except villages which belong to some valid side). A hex is considered a village if and only if its [ terrain_type ] gives_income= parameter was set to yes (which means a side can own that hex).<br />
<br />
'''[filter_adjacent_location]''': a standard location filter; if present the correct number of adjacent locations must match this filter<br />
<br />
'''[filter]''' with a Standard Unit Filter as argument; if present a unit must also be there<br />
<br />
'''radius''': <span style="color:#FF0000;"><u>this is not strictly speaking a criterion.</u></span> It adds to the result set all hexes adjacent to a matching hex and is always applied last, when all criteria are checked. Remember the filtering process is a hidden loop where all candidates are fetched one by one. If a candidate match the filter, radius adds all adjacent hexes (matching the filter or not !). If it don't, it does nothing.<br />
This is why this example doesn't work:<br />
[filter] '''<span style="color:#FF0000;"># this example doesn’t work !</span>'''<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
The coder here expected the radius action to be performed just after selecting the 43,32 hex, and the '''[not]''' criterion applied to this hex and it's adjacent radius 5 set. But '''radius''' is always applied last, <u>even if written before some other conditions</u>. So when using '''radius''', a good rule is to create the filter without it at first and to see if it can catch something. Here, it would give:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
which is clearly non sense because the two conditions are mutually exclusive.<br />
<br />
The solution is to pack the conditions in two different filters:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[and]<br />
[filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/and]<br />
[/filter]<br />
or,<br />
[filter]<br />
side=1<br />
[filter_location]<br />
[and]<br />
x,y=43,32<br />
radius=5<br />
[/and]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
or, since the x,y keys are defined in '''[filter]''' too, it can be:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter]<br />
This is why '''[filter_radius]''' is useful. As we said, radius adds hexes without checking any condition (except proximity of course). If we want to put a condition on hexes added with radius (and them only), we would use it as in next example. Here we want to select forested hexes near villages:<br />
[filter_location]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/filter_location]<br />
But, this will not work exactly as in our previous example because radius extends outwards from matching locations one step at a time. Only the locations matching the '''filter_radius''' will be selected AND used to compute the next step. If there’s no forest hex near the village, the previous filter will return nothing, even if there are some forest hexes farther in the range.<br />
<br />
Note this filter selects the village too ! If we want not, this should be:<br />
[filter_location]<br />
[and]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/and]<br />
[not]<br />
terrain=*^V*<br />
[/not]<br />
[/filter_location]<br />
<br />
<br />
=== Nested filters ===<br />
<br />
Now, we are ready to study how to create nested filters, in other words, filters containing sub filters. In location or unit filters, the documentation says one can insert filters of various kind involving other objects. In this way, we can select unit adjacent to other units or standing on some terrains. Actually, they’re not exactly filters: they are conditions or criteria built on filters. In other words, they don’t produce a result set but are, like other criteria, expressions evaluating to true or false. That’s why many of them have additional keys, like '''‘count’''' (see the filter use in conditions).<br />
<br />
We shall discuss this on examples.<br />
<br />
<br />
=== Filter_adjacent. ===<br />
<br />
In a unit filter, this allow to create criteria stating which units must be adjacent to the tested unit. In this example, we shall implement an ability named ‘escape’. Units having this ability can teleport elsewhere when surrounded by more than 3 enemies. We shall set a '''moveto''' event to watch the ‘surround’ event.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition on the moving unit here'''<br />
[filter_adjacent]<br />
ability=escape '''# this part fetches the adjacent units, not the moving one.'''<br />
is_enemy=yes<br />
[/filter_adjacent] '''# this results to true if at least one unit is found.'''<br />
[/filter]<br />
…<br />
[/event]<br />
This will fire each time some enemy unit get close to our able unit. Note we have here not only a standard unit filter (the ability key), but special keys specifying relationships between units : the '''is_enemy key'''. Another special key is the '''‘count’''' key. It allows to specify how much adjacent units must be found. As far as the default is 1-6, we don’t need it here. But this means more than one able unit can be surrounded by a single move.<br />
Now, we must add the ‘surrounded’ condition. Here, we shall use a new '''filter_adjacent''' tag, but applying to the able unit (not the moving one):<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_adjacent]<br />
ability=escape '''# this part filters the adjacent units, not the moving one.'''<br />
[filter_adjacent]<br />
is_enemy=yes '''# this part finds adjacent units to the able one.'''<br />
count=4-6<br />
[/filter_adjacent]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Suppose we want now to apply one more condition for the ability to work : able unit must be adjacent to another unit sharing the same ability. We must use a new '''filter_adjacent''' tag, and since there is already one, use an '''‘and’''' tag to combine them.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
[/filter_adjacent]<br />
[/and]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Note you’ll find very few examples of such complex filters in events. Why ? It’s because we often need to catch the involved units to take some actions. In our example, we need not only to test if our able units are surrounded but make them teleport as well. As a result, the filtering process would probably be split in two, moving in the '''teleport''' filter the code which was in the '''filter_adjacent''' tag:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
is_enemy=yes<br />
'''# <span style="color:#0000FF;">here was a block …</span>'''<br />
[/filter_adjacent]<br />
[/filter]<br />
<br />
[teleport]<br />
[filter]<br />
ability=escape<br />
[filter_adjacent] '''# <span style="color:#0000FF;">… which was just moved here ! </span>'''<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
#count=1-6 '''# since it’s the default, we don’t need this'''<br />
[/filter_adjacent]<br />
[/and]<br />
[/filter]<br />
…<br />
[/teleport]<br />
[/event]<br />
<br />
=== Filter_location ===<br />
<br />
Filter_location allows to specify on which hex the unit must be standing. Of course, since we already have x,y keys in the standard unit filter, we don’t need to set a filter location for that. Suppose our former ability should work only in forests. The '''filter_location''' is fitted for that.<br />
[filter_location]<br />
terrain=*^F*<br />
[/filter_location]<br />
Where shall we put it ? Certainly not at the first level of the filter: this would make the ability to work if the moving unit (the enemy) stands on forest. So the right place is in level 2 and 3 where we are dealing with the able units.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
[/filter_adjacent]<br />
[/and]<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
<br />
=== pitfalls ===<br />
<br />
One thing to avoid designing filters is using redundant or mutually exclusive criteria. In other words, they specify a condition never or always met in any case. Let's give an example:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=1<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
It's pretty obvious this filter will always return an empty set because side 1 units can't be enemy to side 1. But this filter is valid and will raise no error ! Now, let's look at this one:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
Here, we can mark the '''is_enemy''' key is redundant because sides already define if the units are enemy or not. So, if side 1 and 2 are allied, the filter will always return an empty set. If they are not, it will always match, so the '''is_enemy''' criterion is useless. This filter:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
[/filter_adjacent]<br />
[/filter]<br />
will always return exactly the same result (except if sides configuration is modified during the scenario of course). Another example:<br />
[filter]<br />
type=Horseman,Knight<br />
[filter_location]<br />
terrain=M*<br />
[/filter_location]<br />
[/filter]<br />
The result set will always be empty because these units can't walk on mountains. So, there's no need to use such a filter and it's most probably a design error.<br />
<br />
Another common error is assuming a filter will always return a single unit or location. In conjunction with '''store_unit''' or '''store_locations''', the result set will be an array or a single variable:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
… anything<br />
[/filter]<br />
[/store_unit]<br />
<br />
[modify_unit]<br />
[filter]<br />
id=$temp.id<br />
[/filter]<br />
… something<br />
[/modify_unit]<br />
This will work only if the result set contains a single unit. Else, the temp.id will be taken from the first result, or empty if the result set was empty. Instead, one should use temp[$i].id in a FOREACH loop. Or better in this particular case: '''find_in'''=temp in the unit filter, because it handles correctly all the cases.<br />
<br />
=== Download as .pdf ===<br />
Wesnoth forum thread:<br />
[http://forums.wesnoth.org/viewtopic.php?f=21&t=38583#p553139 (WMLFiltering.pdf.zip)]<br />
<br />
For feedback please use the same thread.<br />
<br />
<br />
<br />
<br />
[[Category: WML Reference]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=FilterWML/Examples_-_How_to_use_Filter&diff=56436FilterWML/Examples - How to use Filter2015-06-17T03:28:49Z<p>Jmichael: /* Result sets and arrays. */</p>
<hr />
<div>== WML Filtering ==<br />
<br />
<br />
Filters are a very important part of WML language, and a fairly complex too for various <br />
reasons. Here, we shall try to explain how to use them with more details than in the reference <br />
wiki pages. But the goal is not to replace these pages, and we assume you have at least some <br />
knowledge of the various WML filters, namely unit and location filters. The examples given <br />
below aren’t always the best way to do things: the goal here is understanding filtering, not to <br />
give a complete WML course. <br />
<br />
<br />
<br />
=== Basics: How filters work. ===<br />
<br />
<br />
Filtering is narrowing a set of objects to a result set using criteria. Let’s give an example : <br />
given a set of cards, if one is asked to select the spades, (s)he will probably check the cards <br />
one by one and create two stacks : one containing only the spades, and another containing the <br />
unselected cards. This is a very simple filter, where the criterion is « this card has spades <br />
colour » and the result set is a card stack. <br />
The spades cards are said to ‘match’ the filter. <br />
If next we’re asked to find the king of spades, most probably, we will not restart from the <br />
beginning but only scan the spades stack to find the right card. This is an example of a two <br />
criteria filter. In WML we would write something like: <br />
[filter]<br />
colour=spades<br />
value=king<br />
[/filter]<br />
to describe the operation. Criteria are expressions evaluating to true or false. Is this card colour <br />
spades ? That’s how we must read the sentence: ‘colour=spades’. <br />
Now, stating both criteria must be met is not the only way to combine them: <br />
[filter] <br />
colour=spades,diamonds <br />
value=king <br />
[/filter]<br />
The first expression will be true if a card colour is ‘spades OR diamonds’. It would make no <br />
sense to state they should be ‘spades AND diamonds’, of course. <br />
In many languages, you must specify how criteria combine using the special keywords ‘OR’ <br />
and ‘AND’. In WML, you can do so, but most often, you’re not required to do so. Writing <br />
explicitly the logical operators would give something like:<br />
[filter] <br />
[and] <br />
colour=spades <br />
[or] <br />
colour=diamonds <br />
[/or] <br />
[/and] <br />
<br />
[and] <br />
value=king <br />
[/and] <br />
[/filter]<br />
or in natural speech: “is card (colour=spades OR colour=diamonds) AND value=king ?” Note <br />
here the use of parentheses. As in algebra, they mean their content must be evaluated prior to <br />
applying the last criterion. <br />
'''[and]''' and '''[or]''' subtags are WML equivalents of parentheses. They are not mandatory (like in some other languages), and this is a cool feature, but can be misleading in complex filters. <br />
The rule is: <br />
* listed criteria are ''ANDed'', in other words, they must all be true for the object to match the filter. <br />
* comma separated lists in a criterion are equivalent to ''ORed'' criteria. <br>In other words, one only is enough for the object to match the filter.<br />
<br />
<br />
'''There are some things important to note:'''<br />
<br />
* A complex filter can always be split into simpler filters applied in chain, each filter taking as starting set the result set of the former one. In our example, we applied the filter « value=king » to the result set of filter « color=spades ». This is important when building or debugging filters, because complex ones can easily be reduced to a chain of simpler ones.<br />
<br />
* Criteria order is not important from a logical point of view. We could have searched the kings first, obtaining the four kings in our result set, and the card of color spade next. The final result is the same. But in WML, for some reasons we shall study later, order can be important.<br />
<br />
* Result sets can contain any number of objects. One can’t assume the result set of our filter will contain a single card ‘THE king of spades’. A human being would probably stop the search when finding a king of spades, but filters don’t. If our starting card packet is not a complete set (some cards were lost, a cheater introduced some more, or anything else), you’ll find one, none or many kings of spades. It’s a common error in WML to assume filters will select a single object.<sup>1)</sup> <br />
* Any unit or location will match an empty filter like this one: <br />
[event] <br />
name=moveto <br />
[filter] <br />
[/filter] <br />
… <br />
[/event]<br />
This event will be triggered on every move of every unit on the map.<br />
<br />
________________________________________<br />
<br />
:<sup>1)</sup> One can be sure only when filtering with ID’s because they are unique.<br />
<br />
<br />
<br />
'''Here now are two versions of the same action, using filters and not.''' <br />
[modify_unit] <br />
[filter] <br />
side=2 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
side=1 <br />
[/modify_unit] <br />
This moves to side 1 all side 2 units except orcs. Please note how filter syntax is after all very close to natural speech. This is why we shall see a good way to design complex filters is writing an accurate sentence describing them first. <br />
Using no filter (and assuming ‘all_units’ is an array containing all created units in a scenario) we could write : <br />
{FOREACH all_units i} <br />
[if] <br />
[variable] <br />
name= all_units[$i].side <br />
equals=2 <br />
[/variable] <br />
[and] <br />
[variable] <br />
name= all_units[$i].race <br />
not_equals=orc <br />
[/variable] <br />
[/and] <br />
[then] <br />
[set_variable] <br />
name= all_units[$i].side <br />
value=1 <br />
[/set_variable] <br />
[unstore_unit] <br />
variable= all_units[$i] <br />
[/unstore_unit] <br />
[/then] <br />
[/if] <br />
{NEXT i} <br />
Of course, the first version is much more concise. One should note: <br />
* Filters are hidden loops<sup>2)</sup> fetching all elements of the starting set. <br />
* Criteria composition is more explicit in the second version. We have here an [and] tag which is missing in the first one. It shows clearly both conditions must be met. In filters, the [and] tag is most often implicit, as we have already seen.<br />
________________________________________<br />
<br />
:<sup>2) </sup>Internally, it’s not always the case, but we’ve not to deal with internal implementations.<br>Conceptually, filters can always be seen as hidden loops.<br />
<br />
<br />
At this point, a question arises: where are the starting and result sets we talked about? They show nowhere. The reply is most often we don’t need to see them, because we don’t need to create result sets explicitly. We need to use them to specify actions targets or conditions. In the '''‘modify_unit’''' example, we apply the action « change side » to the result set of the filter and then need it no more.<br />
The starting set is implicit too. Most of the time, it’s the larger available set of objects: e.g., all created units (sometimes including the recall list), or all hexes in the map. In the '''moveto''' event, it contains only the moving unit.<br />
Once again, we are not saying these sets really exist in Wesnoth engine code. But this is an accurate model of how the filters work in WML.<br />
<br />
=== Result sets and arrays. === <br />
<br />
<br />
A good way to explicitly get the result set is to use the '''‘store_...’''' tags. These actions create <br />
arrays containing the result set of the filter they take as parameter. This is very useful in many ways. The common use is of course to store units and locations for some processing or later use. But one can use this to split complex filters and inspect intermediate results. The former '''‘modify_unit’''' example could be written as: <br />
[store_unit] <br />
variable=temp1 <br />
[filter] <br />
side=2 <br />
[/filter] <br />
[/store_unit] <br />
<br />
[store_unit] <br />
variable=temp2 <br />
[filter] <br />
find_in=temp1 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
[/store_unit] <br />
<br />
[modify_unit] <br />
[filter] <br />
find_in=temp2 <br />
[/filter] <br />
side=1 <br />
[/modify_unit]<br />
This trivial example shows not only how to debug complex filters (inspecting the content of ''temp1'' and ''temp2'' arrays), but how to specify a starting set with the '''‘find_in’''' key. Without it, the second '''‘store_unit’''' tag would store all units except orcs. With it, we ask to apply the filter to the content of ''temp1'' array <u>only</u> (all side 2 units). It’s like our card example where we selected the spades first and next the king(s) in the spades stack. <br />
The '''‘find-in’''' key is really precious in many cases: often it’s easier to explicitly build an array containing the objects we want to select, than it is to create a complex filter to retrieve them. <br />
For example, if we want dying units to drop weapons and other units to retrieve them, it can be very difficult to create a filter allowing to select locations where the weapons were dropped. Instead, we can build an array containing their locations (and other information as well). Since this array's members have x and y (sub-)members, the '''find_in''' key of a location filter can use it<sup>3)</sup>. It would be:<br />
# in the die event <br />
[set_variables] <br />
name=weapons <br />
mode=append <br />
[value] <br />
x=$unit.x <br />
y=$unit.y <br />
… anything else, for instance the image name and item id. <br />
[/value] <br />
[/set_variables] <br />
<br />
# drop item, etc… <br />
<br />
<br />
# and in a moveto event<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
[filter_location] <br />
find_in=weapons <br />
[/filter_location] <br />
[/filter] <br />
… <br />
<br />
Let’s give another example. <br />
The scenario’s map features three temples at 10,10 20,20 30,30.<br />
We want to give some bonus gold to side 1 if any side 1 unit visits temple 1,2,3 exactly in that order.<br />
Here is a solution using result sets arrays : <br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1<br />
x,y=10,10 <br />
[not] <br />
find_in=temple_1 <br />
[/not] <br />
[/filter] <br />
[store_unit] <br />
mode=append <br />
variable=temple_1 <br />
[filter] <br />
id=$unit.id <br />
[/filter]<br />
[/store_unit] <br />
[/event]<br />
In temple_1, we store all side 1 units visiting temple_1, but only once (that’s why the '''[not] find_in''' tag, because units can visit the temple more than once). <br />
<br />
________________________________________<br />
<br />
:<sup>3) </sup>It’s surprising because this array doesn’t contain locations, but it’s a feature, not a side effect.<br />
<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
x,y=20,20 <br />
find_in=temple_1 <br />
[not] <br />
find_in=temple_2 <br />
[/not] <br />
[/filter]<br />
[store_unit] <br />
mode=append <br />
variable=temple_2 <br />
[filter] <br />
id=$unit.id <br />
[/filter] <br />
[/store_unit] <br />
[/event] <br />
<br />
This time, we store only units previously stored in temple_1 (= they already visited that temple).<br />
Then the last event is obviously : <br />
[event] <br />
name=moveto <br />
first_time_only=yes <br />
[filter] <br />
side=1 <br />
x,y=30,30 <br />
find_in=temple_2 <br />
[/filter] <br />
[gold] <br />
side=1 <br />
amount=1000 <br />
[/gold] <br />
{CLEAR_VARIABLE temple_1,temple_2} <br />
[/event]<br />
<br />
=== Ordering and writing === <br />
<br />
<br />
We said earlier that criteria order is not significant. That's right from a theoretical point of view. But, for syntactic reasons and particularly because the radius key in location filters, this is not fully true in WML. Actually conditions are applied to candidate objects until one proves to be false or all conditions are checked. Then, if some condition proves to be false, remaining conditions <u>are not evaluated</u> (so if there's some radius there, it will not be executed). In some cases, this may be important. One can assume conditions are evaluated in the order they are found except logical operators (and, or, not) which are evaluated <u>after</u> other conditions. It’s very important to note that evaluation order of top level conditions (those not embedded in and/or/not tags) is undocumented, which means <u>your code shouldn’t rely on it</u>. On the contrary, and/or/not tags are always executed last, in the order they are written. <br />
<br />
So in this example:<br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
[or] <br />
side=2 <br />
[/or] <br />
gender=female <br />
[/filter] <br />
will be executed using this order: <br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
gender=female <br />
[or] <br />
side=2 <br />
[/or] <br />
[/filter]<br />
One should be aware of this for some reasons:<br />
<br />
'''⇒''' Clarity: your code will be easier to understand and to debug if you avoid meddling conditions, nested filters and logical blocks, and write them in the order they are evaluated.<br />
<br />
'''⇒''' Performance.<br />
Most of time, performance is not an issue. But it can be if you have a lot of units and use '''[filter_wml]'''. More generally, it’s good programming practice to execute the <u>more restrictive</u> test first.<br />
Consider this example:<br />
[filter]<br />
race=orc<br />
x,y=16,22<br />
[/filter]<br />
The first condition will be evaluated on all units. But the second one will be evaluated on all orcs. Then if we write:<br />
[filter]<br />
x,y=16,22<br />
race=orc<br />
[/filter]<br />
obviously, the second condition will be evaluated once at most, and filtering will be faster. (Strictly speaking, I should have wrapped second condition in an '''and''' tag to force evaluation order).<br />
Remember too that logical operators '''(and, or, not)''' are not mandatory, but are allowed. So one can use them for clarity sake or to force an evaluation order. It’s particularly important when using '''or''' tags.<br />
<br />
In the example above one could expect the result set contains all women of sides 1 and 2 wielding a sword. But it contains actually all side 1 women wielding a sword plus <u>all side 2 units</u>.<br />
<br />
Filters work actually as if top level criteria were enclosed in an implicit '''and''' tag: so we should read:<br />
[filter]<br />
[and] '''#implicit'''<br />
has_weapon=sword<br />
side=1<br />
gender=female<br />
[/and]<br />
[or]<br />
side=2<br />
[/or]<br />
[/filter]<br />
and what we probably wanted is:<br />
[filter]<br />
has_weapon=sword<br />
gender=female<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
Note that it could be written:<br />
[filter]<br />
[and]<br />
has_weapon=sword<br />
gender=female<br />
[/and]<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
This syntax is correct and you can use it if you find it clearer.<br />
Remembering this implicit '''and''' tag (and writing it explicitly at will) is very important to understand or design complex filters.<br />
<br />
<br />
<br />
=== Writing complex filters. ===<br />
<br />
<br />
Here are some guidelines one can use when writing complex filters. Suppose we want to set up some kind of disease aura harming units standing close to some villages. We shall start writing a sentence describing the feature.<br />
<br />
'''We want to select units who:'''<br />
'''Are enemy to side 1'''<br />
'''stand on hexes which'''<br />
'''are near to'''<br />
'''villages'''<br />
'''with side 1 units standing on it'''<br />
Mark we put only one condition on each line to clearly separate them. Mark we sorted them, because some apply to units to be filtered, others to locations, and finally to other (enemy) units standing on locations. Next, we shall add logical operators and parenthesis to clearly specify what we want:<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">Are enemy to side 1</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now, we are ready to start building the filter. Since we want units who… it shall be a unit filter. In the standard unit filter, there’s no condition directly allowing to state the unit is enemy to side 1. So we have to replace this with something valid in the SUF context. Using parenthesis to avoid errors, we can replace the condition with a side filter because it’s valid in unit filters.<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">(belongs to a side which ('''<br />
'''is enemy to side 1) )</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now we can write the filter. Here we do it step by step to show how the translation is rather straightforward and how our parenthesis match exactly the subtags.<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
'''AND''' <br />
'''stand on locations which ( # we start to filter locations there'''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
'''AND'''<br />
'''have unit on it who (# to unit filter again'''<br />
'''Belongs to side 1'''<br />
''')'''<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
[and]<br />
[filter]<br />
side=1<br />
[/filter]<br />
[/and]<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
Here, we are done. The filter should work as it is, but it looks rather unusual because all these '''[and]''' blocks. Actually we can delete most of them using a simple rule: '''[and]''' tags are not needed when they contain one single criterion or a single block, (except if you want to set up an evaluation order). In our example, the '''filter_location''' block is alone in its '''and''' tag, and the embedded '''filter''' as well, so we can avoid those '''and''' tags and get finally:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[filter_location]<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
radius=3<br />
[/filter_location]<br />
[/filter]<br />
[harm_unit]<br />
[filter]<br />
id=$unit.id<br />
[/filter]<br />
amount=10<br />
animate=yes<br />
[/harm_unit]<br />
[/event]<br />
<br />
<br />
<br />
=== Filters uses: events actions and conditions. ===<br />
<br />
<br />
Here, we shall deal with filter uses in WML. The language is not fully consistent, mainly to simplify its syntax, so some points can be misleading.<br />
:<u>'''Using in actions'''</u><br />
Most actions take a filter as first parameter. The main difficulty here is to know if '''[filter]''' tags must be used or not. Actually, they’re used to avoid confusing keys and criteria when they have the same name<sup>4</sup>. For instance, the '''[kill]''' action needs a unit filter and has these keys:<br />
<br />
* '''animate:''' if 'yes', displays the unit dying (fading away).<br />
* '''fire_event:''' if 'yes', triggers any appropriate 'die' events (See EventWML). Note that events are only fired for killed units that have been on the map (as opposed to recall list).<br />
* '''[secondary_unit]''' with a StandardUnitFilter as argument. Do not use a [filter] tag. Has an effect only if fire_event=yes. The first on-map unit matching the filter.<br />
<br />
As we can see, none of these keys and tags are shared with unit filter keys and tags. This means the code parser needs no '''[filter]''' tag to know which key belongs to the filter and which to the action. But in the code, there’s no obvious distinction.<br />
<br />
________________________________________<br />
<br />
:<sup>4) </sup>Note there’s no specific top level tag for location filters.<br />
<br />
<br />
<br />
[kill]<br />
animate=yes '''# this belongs to the ‘kill’'''<br />
id=BadGuy_101 '''# this belongs to the unit filter'''<br />
[/kill]<br />
is the correct syntax. Note a common error is to use a '''[filter]''' tag here. Since this tag is unknown in the context, it is ignored, so the kill action is applied to the starting set, i.e. all units (including the recall lists). Same with the '''filter_side''' tag which is not needed everywhere (in '''store_sides''' particularly).<br />
Adversely, '''[modify_unit]''' obviously requires a '''[filter]''' tag, because all keys and criteria have the same name:<br />
[modify_unit]<br />
side=2<br />
side=1<br />
[/modify_unit]<br />
<br />
This would make no sense of course because one can’t find if side 1 units must be put into side 2 or the contrary. Anyway, there is an exception: the '''[store_unit]''' tag requires a '''[filter]''' tag even if there is no '''‘variable’''' key in units description. Another special case is when using terrain action, because the key '''terrain''' is valid both in location filters and terrain action. Since there is no special tag to delimit location filters, one should write:<br />
[terrain]<br />
[and]<br />
terrain=Wo* '''# here is the filter criterion'''<br />
[/and]<br />
terrain=Rr '''# and the new terrain'''<br />
[/terrain]<br />
The next example can be confusing:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
[/kill]<br />
When reading the '''‘kill’''' tag documentation, one will not find any '''‘filter_location’''' or location filter entry. Does this means it’s an undocumented feature ? No. But the '''‘filter_location’''' belongs to the implicit '''‘filter’''' tag of the '''‘kill’''' action and is documented there. It’s kind of:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
'''#'''[filter] '''we’re filtering units here, not locations'''<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
'''#'''[/filter]<br />
[/kill]<br />
<br />
=== Using filters in conditions. ===<br />
<br />
Filters can be used to create conditional expressions. They can be nested in '''[have_unit]''' or '''[have_location]''' tags or in nested filters. Here, the result set is not used directly, but its size must fall in the range defined by the '''‘count’''' key. This finally gives a Boolean result: true or false. So one can use them in '''[if] [show_if]''' conditional actions or in a '''[filter_condition]''' tag. They are widely used in nested filters too (see the special chapter on this).<br />
<br />
Let’s give some examples:<br />
<br />
[have_unit] '''# this piece of code evaluates to true when no more enemy leaders are alive'''<br />
canrecruit=yes<br />
[not]<br />
side=1<br />
[/not]<br />
count=0<br />
[/have_unit]<br />
<br />
[have_location] '''# this one is true if at least 5 side 1 units stand on a village'''<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
count=5-1000<br />
[/have_location]<br />
Note we could also write this condition:<br />
[have_unit]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
count=5-1000<br />
[/have_unit]<br />
Note we could use a '''[store_unit]''' instead, testing the ''length'' property of the array:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
[/filter]<br />
[/store_unit]<br />
#[if] or [filter_condition]<br />
[variable]<br />
name=temp.length<br />
greater_than=4<br />
[/variable]<br />
<br />
<br />
=== Using filters in events. ===<br />
In events, filters are always used in a conditional way, because they state if the event should fire or not. In any event, we can set '''[filter_condition]''' using '''have_unit''' or '''have_location''' (and a more ordinary variable condition, but this is off topic).<br />
Some events use filters in a special way: '''moveto''' and '''attack''' events particularly. In them, the filtering apply not on all units as usual, but only on units involved in the event action: one unit only is moving at a time, two units only are involved in a fight. Then the event fires if and only if the involved unit(s) match the filter.<br />
Note that one can use '''[filter]''' and '''[filter_condition]''' in the same moveto or attack event. Both conditions are then ANDed.<br />
<br />
'''Filtering units'''<br />
<br />
Criteria allowed to filter units (in standard unit filters) are listed below. First, the keys dealing with the unit properties (as usual, comma separated lists means conditions are ORed):<br />
:'''id:''' ''can be a comma-separated list, every unit with one of these ids matches''<br />
:'''type:''' ''can be a list of types''<br />
:'''race:''' ''(Version 1.11 and later only: this can be a comma-separated list)''<br />
:'''ability:''' ''unit has an ability with the given id (not name !)''<br />
:'''side:''' ''the unit is on the given side (can be a list). One can use a [filter_side] instead''<br />
:'''has_weapon:''' ''the unit has a weapon with the given name''<br />
:'''canrecruit:''' ''yes if the unit can recruit (i.e. is a leader)''<br />
:'''gender:''' ''female if the unit is female rather than the default of male''<br />
:'''role:''' ''the unit has been assigned the given role''<br />
:'''level:''' ''the level of the unit''<br />
:'''defense:''' ''current defense of the unit on current tile''<br />
:'''movement_cost:''' ''current movement cost of the unit on current tile''<br />
:'''x,y:''' ''the position of the unit. (Ranges ? probably because it works in moveto events)''<br />
<br />
<br />
Not all unit properties are listed here, but they can be used in a '''[filter_wml]''' sub-tag like this:<br />
[filter_wml]<br />
max_moves=7<br />
[/filter_wml]<br />
<br />
[filter_wml]<br />
[status]<br />
poisoned=yes<br />
[/status]<br />
[/filter_wml]<br />
Some of them accept comma separated lists or ranges. Some not, but it also possible to use them more than once with '''[and]''' and '''[or]''' subtags. For instance :<br />
[and]<br />
race= elf<br />
[or]<br />
race= merman<br />
[/or]<br />
[/and]<br />
Other sub tags are dealing with unit relationships:<br />
:'''⇒''' The hex on which they stand: '''[filter_location]''' which contains a standard location filter.<br />
:'''⇒''' The units adjacent to it: '''[filter_adjacent]''' which contains another standard unit filter<br />
:'''⇒''' Their visible status relating to a particular side: '''[filter_vision]'''<br />
These are nested filters ; or in other words, filters used to create conditions and not result sets (see earlier and later).<br />
<br />
Custom functions returning a boolean:<br />
:'''formula:''' FormulaAI like formula.<br />
:'''lua_function:''' lua function<br />
<br />
SUFs accept a '''find_in''' key too. As we saw earlier, this allows to restrict the starting set to the content of an array.<br />
<br />
=== this_unit ===<br />
<br />
This variable is a special variable defined only inside SUFs. Suppose we want to catch in a filter units at full health. We can use the '''hitpoints''', but the problem is we know not which value to use, because every unit type has its own:<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=?<br />
[/filter_wml]<br />
[/filter]<br />
This is why we could have the use of some way to specify the unit being fetched during the filtering.<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=$this_unit.max_hitpoints<br />
[/filter_wml]<br />
[/filter]<br />
This does the trick. The condition value will be updated according to unit properties before executing the check.<br />
<br />
<br />
=== Filtering locations ===<br />
<br />
'''find_in''': a location array specifying the starting set.<br />
<br />
'''time_of_day''': one of lawful, chaotic, neutral or liminal.<br />
'''time_of_day_id''': one or more from: dawn, morning, afternoon, dusk, first_watch, second_watch, indoors, underground and deep_underground.<br />
<br />
'''terrain''': comma separated list of terrains.<br />
'''x,y''': the same as in the unit filter; supports any range.<br />
'''owner_side''': If a valid side number, restricts stored locations to villages belonging to this side. If 0, restricts to all unowned locations (the whole map except villages which belong to some valid side). A hex is considered a village if and only if its [ terrain_type ] gives_income= parameter was set to yes (which means a side can own that hex).<br />
<br />
'''[filter_adjacent_location]''': a standard location filter; if present the correct number of adjacent locations must match this filter<br />
<br />
'''[filter]''' with a Standard Unit Filter as argument; if present a unit must also be there<br />
<br />
'''radius''': <span style="color:#FF0000;"><u>this is not strictly speaking a criterion.</u></span> It adds to the result set all hexes adjacent to a matching hex and is always applied last, when all criteria are checked. Remember the filtering process is a hidden loop where all candidates are fetched one by one. If a candidate match the filter, radius adds all adjacent hexes (matching the filter or not !). If it don't, it does nothing.<br />
This is why this example doesn't work:<br />
[filter] '''<span style="color:#FF0000;"># this example doesn’t work !</span>'''<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
The coder here expected the radius action to be performed just after selecting the 43,32 hex, and the '''[not]''' criterion applied to this hex and it's adjacent radius 5 set. But '''radius''' is always applied last, <u>even if written before some other conditions</u>. So when using '''radius''', a good rule is to create the filter without it at first and to see if it can catch something. Here, it would give:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
which is clearly non sense because the two conditions are mutually exclusive.<br />
<br />
The solution is to pack the conditions in two different filters:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[and]<br />
[filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/and]<br />
[/filter]<br />
or,<br />
[filter]<br />
side=1<br />
[filter_location]<br />
[and]<br />
x,y=43,32<br />
radius=5<br />
[/and]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
or, since the x,y keys are defined in '''[filter]''' too, it can be:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter]<br />
This is why '''[filter_radius]''' is useful. As we said, radius adds hexes without checking any condition (except proximity of course). If we want to put a condition on hexes added with radius (and them only), we would use it as in next example. Here we want to select forested hexes near villages:<br />
[filter_location]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/filter_location]<br />
But, this will not work exactly as in our previous example because radius extends outwards from matching locations one step at a time. Only the locations matching the '''filter_radius''' will be selected AND used to compute the next step. If there’s no forest hex near the village, the previous filter will return nothing, even if there are some forest hexes farther in the range.<br />
<br />
Note this filter selects the village too ! If we want not, this should be:<br />
[filter_location]<br />
[and]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/and]<br />
[not]<br />
terrain=*^V*<br />
[/not]<br />
[/filter_location]<br />
<br />
<br />
=== Nested filters ===<br />
<br />
Now, we are ready to study how to create nested filters, in other words, filters containing sub filters. In location or unit filters, the documentation says one can insert filters of various kind involving other objects. In this way, we can select unit adjacent to other units or standing on some terrains. Actually, they’re not exactly filters: they are conditions or criteria built on filters. In other words, they don’t produce a result set but are, like other criteria, expressions evaluating to true or false. That’s why many of them have additional keys, like '''‘count’''' (see the filter use in conditions).<br />
<br />
We shall discuss this on examples.<br />
<br />
<br />
=== Filter_adjacent. ===<br />
<br />
In a unit filter, this allow to create criteria stating which units must be adjacent to the tested unit. In this example, we shall implement an ability named ‘escape’. Units having this ability can teleport elsewhere when surrounded by more than 3 enemies. We shall set a '''moveto''' event to watch the ‘surround’ event.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition on the moving unit here'''<br />
[filter_adjacent]<br />
ability=escape '''# this part fetches the adjacent units, not the moving one.'''<br />
is_enemy=yes<br />
[/filter_adjacent] '''# this results to true if at least one unit is found.'''<br />
[/filter]<br />
…<br />
[/event]<br />
This will fire each time some enemy unit get close to our able unit. Note we have here not only a standard unit filter (the ability key), but special keys specifying relationships between units : the '''is_enemy key'''. Another special key is the '''‘count’''' key. It allows to specify how much adjacent units must be found. As far as the default is 1-6, we don’t need it here. But this means more than one able unit can be surrounded by a single move.<br />
Now, we must add the ‘surrounded’ condition. Here, we shall use a new '''filter_adjacent''' tag, but applying to the able unit (not the moving one):<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_adjacent]<br />
ability=escape '''# this part filters the adjacent units, not the moving one.'''<br />
[filter_adjacent]<br />
is_enemy=yes '''# this part finds adjacent units to the able one.'''<br />
count=4-6<br />
[/filter_adjacent]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Suppose we want now to apply one more condition for the ability to work : able unit must be adjacent to another unit sharing the same ability. We must use a new '''filter_adjacent''' tag, and since there is already one, use an '''‘and’''' tag to combine them.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
[/filter_adjacent]<br />
[/and]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Note you’ll find very few examples of such complex filters in events. Why ? It’s because we often need to catch the involved units to take some actions. In our example, we need not only to test if our able units are surrounded but make them teleport as well. As a result, the filtering process would probably be split in two, moving in the '''teleport''' filter the code which was in the '''filter_adjacent''' tag:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
is_enemy=yes<br />
'''# <span style="color:#0000FF;">here was a block …</span>'''<br />
[/filter_adjacent]<br />
[/filter]<br />
<br />
[teleport]<br />
[filter]<br />
ability=escape<br />
[filter_adjacent] '''# <span style="color:#0000FF;">… which was just moved here ! </span>'''<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
#count=1-6 '''# since it’s the default, we don’t need this'''<br />
[/filter_adjacent]<br />
[/and]<br />
[/filter]<br />
…<br />
[/teleport]<br />
[/event]<br />
<br />
=== Filter_location ===<br />
<br />
Filter_location allows to specify on which hex the unit must be standing. Of course, since we already have x,y keys in the standard unit filter, we don’t need to set a filter location for that. Suppose our former ability should work only in forests. The '''filter_location''' is fitted for that.<br />
[filter_location]<br />
terrain=*^F*<br />
[/filter_location]<br />
Where shall we put it ? Certainly not at the first level of the filter: this would make the ability to work if the moving unit (the enemy) stands on forest. So the right place is in level 2 and 3 where we are dealing with the able units.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
[/filter_adjacent]<br />
[/and]<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
<br />
=== pitfalls ===<br />
<br />
One thing to avoid designing filters is using redundant or mutually exclusive criteria. In other words, they specify a condition never or always met in any case. Let's give an example:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=1<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
It's pretty obvious this filter will always return an empty set because side 1 units can't be enemy to side 1. But this filter is valid and will raise no error ! Now, let's look at this one:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
Here, we can mark the '''is_enemy''' key is redundant because sides already define if the units are enemy or not. So, if side 1 and 2 are allied, the filter will always return an empty set. If they are not, it will always match, so the '''is_enemy''' criterion is useless. This filter:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
[/filter_adjacent]<br />
[/filter]<br />
will always return exactly the same result (except if sides configuration is modified during the scenario of course). Another example:<br />
[filter]<br />
type=Horseman,Knight<br />
[filter_location]<br />
terrain=M*<br />
[/filter_location]<br />
[/filter]<br />
The result set will always be empty because these units can't walk on mountains. So, there's no need to use such a filter and it's most probably a design error.<br />
<br />
Another common error is assuming a filter will always return a single unit or location. In conjunction with '''store_unit''' or '''store_locations''', the result set will be an array or a single variable:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
… anything<br />
[/filter]<br />
[/store_unit]<br />
<br />
[modify_unit]<br />
[filter]<br />
id=$temp.id<br />
[/filter]<br />
… something<br />
[/modify_unit]<br />
This will work only if the result set contains a single unit. Else, the temp.id will be taken from the first result, or empty if the result set was empty. Instead, one should use temp[$i].id in a FOREACH loop. Or better in this particular case: '''find_in'''=temp in the unit filter, because it handles correctly all the cases.<br />
<br />
=== Download as .pdf ===<br />
Wesnoth forum thread:<br />
[http://forums.wesnoth.org/viewtopic.php?f=21&t=38583#p553139 (WMLFiltering.pdf.zip)]<br />
<br />
For feedback please use the same thread.<br />
<br />
<br />
<br />
<br />
[[Category: WML Reference]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=FilterWML/Examples_-_How_to_use_Filter&diff=56435FilterWML/Examples - How to use Filter2015-06-17T03:26:49Z<p>Jmichael: /* Basics: How filters work. */</p>
<hr />
<div>== WML Filtering ==<br />
<br />
<br />
Filters are a very important part of WML language, and a fairly complex too for various <br />
reasons. Here, we shall try to explain how to use them with more details than in the reference <br />
wiki pages. But the goal is not to replace these pages, and we assume you have at least some <br />
knowledge of the various WML filters, namely unit and location filters. The examples given <br />
below aren’t always the best way to do things: the goal here is understanding filtering, not to <br />
give a complete WML course. <br />
<br />
<br />
<br />
=== Basics: How filters work. ===<br />
<br />
<br />
Filtering is narrowing a set of objects to a result set using criteria. Let’s give an example : <br />
given a set of cards, if one is asked to select the spades, (s)he will probably check the cards <br />
one by one and create two stacks : one containing only the spades, and another containing the <br />
unselected cards. This is a very simple filter, where the criterion is « this card has spades <br />
colour » and the result set is a card stack. <br />
The spades cards are said to ‘match’ the filter. <br />
If next we’re asked to find the king of spades, most probably, we will not restart from the <br />
beginning but only scan the spades stack to find the right card. This is an example of a two <br />
criteria filter. In WML we would write something like: <br />
[filter]<br />
colour=spades<br />
value=king<br />
[/filter]<br />
to describe the operation. Criteria are expressions evaluating to true or false. Is this card colour <br />
spades ? That’s how we must read the sentence: ‘colour=spades’. <br />
Now, stating both criteria must be met is not the only way to combine them: <br />
[filter] <br />
colour=spades,diamonds <br />
value=king <br />
[/filter]<br />
The first expression will be true if a card colour is ‘spades OR diamonds’. It would make no <br />
sense to state they should be ‘spades AND diamonds’, of course. <br />
In many languages, you must specify how criteria combine using the special keywords ‘OR’ <br />
and ‘AND’. In WML, you can do so, but most often, you’re not required to do so. Writing <br />
explicitly the logical operators would give something like:<br />
[filter] <br />
[and] <br />
colour=spades <br />
[or] <br />
colour=diamonds <br />
[/or] <br />
[/and] <br />
<br />
[and] <br />
value=king <br />
[/and] <br />
[/filter]<br />
or in natural speech: “is card (colour=spades OR colour=diamonds) AND value=king ?” Note <br />
here the use of parentheses. As in algebra, they mean their content must be evaluated prior to <br />
applying the last criterion. <br />
'''[and]''' and '''[or]''' subtags are WML equivalents of parentheses. They are not mandatory (like in some other languages), and this is a cool feature, but can be misleading in complex filters. <br />
The rule is: <br />
* listed criteria are ''ANDed'', in other words, they must all be true for the object to match the filter. <br />
* comma separated lists in a criterion are equivalent to ''ORed'' criteria. <br>In other words, one only is enough for the object to match the filter.<br />
<br />
<br />
'''There are some things important to note:'''<br />
<br />
* A complex filter can always be split into simpler filters applied in chain, each filter taking as starting set the result set of the former one. In our example, we applied the filter « value=king » to the result set of filter « color=spades ». This is important when building or debugging filters, because complex ones can easily be reduced to a chain of simpler ones.<br />
<br />
* Criteria order is not important from a logical point of view. We could have searched the kings first, obtaining the four kings in our result set, and the card of color spade next. The final result is the same. But in WML, for some reasons we shall study later, order can be important.<br />
<br />
* Result sets can contain any number of objects. One can’t assume the result set of our filter will contain a single card ‘THE king of spades’. A human being would probably stop the search when finding a king of spades, but filters don’t. If our starting card packet is not a complete set (some cards were lost, a cheater introduced some more, or anything else), you’ll find one, none or many kings of spades. It’s a common error in WML to assume filters will select a single object.<sup>1)</sup> <br />
* Any unit or location will match an empty filter like this one: <br />
[event] <br />
name=moveto <br />
[filter] <br />
[/filter] <br />
… <br />
[/event]<br />
This event will be triggered on every move of every unit on the map.<br />
<br />
________________________________________<br />
<br />
:<sup>1)</sup> One can be sure only when filtering with ID’s because they are unique.<br />
<br />
<br />
<br />
'''Here now are two versions of the same action, using filters and not.''' <br />
[modify_unit] <br />
[filter] <br />
side=2 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
side=1 <br />
[/modify_unit] <br />
This moves to side 1 all side 2 units except orcs. Please note how filter syntax is after all very close to natural speech. This is why we shall see a good way to design complex filters is writing an accurate sentence describing them first. <br />
Using no filter (and assuming ‘all_units’ is an array containing all created units in a scenario) we could write : <br />
{FOREACH all_units i} <br />
[if] <br />
[variable] <br />
name= all_units[$i].side <br />
equals=2 <br />
[/variable] <br />
[and] <br />
[variable] <br />
name= all_units[$i].race <br />
not_equals=orc <br />
[/variable] <br />
[/and] <br />
[then] <br />
[set_variable] <br />
name= all_units[$i].side <br />
value=1 <br />
[/set_variable] <br />
[unstore_unit] <br />
variable= all_units[$i] <br />
[/unstore_unit] <br />
[/then] <br />
[/if] <br />
{NEXT i} <br />
Of course, the first version is much more concise. One should note: <br />
* Filters are hidden loops<sup>2)</sup> fetching all elements of the starting set. <br />
* Criteria composition is more explicit in the second version. We have here an [and] tag which is missing in the first one. It shows clearly both conditions must be met. In filters, the [and] tag is most often implicit, as we have already seen.<br />
________________________________________<br />
<br />
:<sup>2) </sup>Internally, it’s not always the case, but we’ve not to deal with internal implementations.<br>Conceptually, filters can always be seen as hidden loops.<br />
<br />
<br />
At this point, a question arises: where are the starting and result sets we talked about? They show nowhere. The reply is most often we don’t need to see them, because we don’t need to create result sets explicitly. We need to use them to specify actions targets or conditions. In the '''‘modify_unit’''' example, we apply the action « change side » to the result set of the filter and then need it no more.<br />
The starting set is implicit too. Most of the time, it’s the larger available set of objects: e.g., all created units (sometimes including the recall list), or all hexes in the map. In the '''moveto''' event, it contains only the moving unit.<br />
Once again, we are not saying these sets really exist in Wesnoth engine code. But this is an accurate model of how the filters work in WML.<br />
<br />
=== Result sets and arrays. === <br />
<br />
<br />
A good way to get explicitly the result set is to use the '''‘store_...’''' tags. These actions create <br />
arrays containing the result set of the filter they take as parameter. This is very useful in many ways. The common use is of course to store units and locations for some processing or later use. But one can use this to split complex filters and inspect intermediate results. The former '''‘modify_unit’''' example could be written as: <br />
[store_unit] <br />
variable=temp1 <br />
[filter] <br />
side=2 <br />
[/filter] <br />
[/store_unit] <br />
<br />
[store_unit] <br />
variable=temp2 <br />
[filter] <br />
find_in=temp1 <br />
[not] <br />
race=orc <br />
[/not] <br />
[/filter] <br />
[/store_unit] <br />
<br />
[modify_unit] <br />
[filter] <br />
find_in=temp2 <br />
[/filter] <br />
side=1 <br />
[/modify_unit]<br />
This trivial example shows not only how to debug complex filters (inspecting the content of ''temp1'' and ''temp2'' arrays), but how to specify a starting set with the '''‘find_in’''' key. Without it, the second '''‘store_unit’''' tag would store all units except orcs. With it, we ask to apply the filter to the content of ''temp1'' array <u>only</u> (all side 2 units). It’s like our card example where we selected the spades first and next the king(s) in the spades stack. <br />
The '''‘find-in’''' key is really precious in many cases : often it’s easier to build explicitly an array containing the objects we want to select than creating complex filters to retrieve them. <br />
For example, if we want dying units to drop weapons and other units to retrieve them, it can be very difficult to create a filter allowing to select locations where the weapons where dropped. Instead, we can build an array containing their locations (and other informations at will). Since this array have x and y members, the '''find_in''' key of a location filter can use it<sup>3)</sup>. It would be:<br />
# in the die event <br />
[set_variables] <br />
name=weapons <br />
mode=append <br />
[value] <br />
x=$unit.x <br />
y=$unit.y <br />
… anything else, for instance the image name and item id. <br />
[/value] <br />
[/set_variables] <br />
<br />
# drop item, etc… <br />
<br />
<br />
# and in a moveto event<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
[filter_location] <br />
find_in=weapons <br />
[/filter_location] <br />
[/filter] <br />
… <br />
<br />
Let’s give another example. <br />
The scenario’s map features three temples at 10,10 20,20 30,30.<br />
We want to give some bonus gold to side 1 if any side 1 unit visits temple 1,2,3 exactly in that order.<br />
Here is a solution using result sets arrays : <br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1<br />
x,y=10,10 <br />
[not] <br />
find_in=temple_1 <br />
[/not] <br />
[/filter] <br />
[store_unit] <br />
mode=append <br />
variable=temple_1 <br />
[filter] <br />
id=$unit.id <br />
[/filter]<br />
[/store_unit] <br />
[/event]<br />
In temple_1, we store all side 1 units visiting temple_1, but only once (that’s why the '''[not] find_in''' tag, because units can visit the temple more than once). <br />
<br />
________________________________________<br />
<br />
:<sup>3) </sup>It’s surprising because this array doesn’t contain locations, but it’s a feature, not a side effect.<br />
<br />
[event] <br />
name=moveto <br />
first_time_only=no <br />
[filter] <br />
side=1 <br />
x,y=20,20 <br />
find_in=temple_1 <br />
[not] <br />
find_in=temple_2 <br />
[/not] <br />
[/filter]<br />
[store_unit] <br />
mode=append <br />
variable=temple_2 <br />
[filter] <br />
id=$unit.id <br />
[/filter] <br />
[/store_unit] <br />
[/event] <br />
<br />
This time, we store only units previously stored in temple_1 (= they already visited that temple).<br />
Then the last event is obviously : <br />
[event] <br />
name=moveto <br />
first_time_only=yes <br />
[filter] <br />
side=1 <br />
x,y=30,30 <br />
find_in=temple_2 <br />
[/filter] <br />
[gold] <br />
side=1 <br />
amount=1000 <br />
[/gold] <br />
{CLEAR_VARIABLE temple_1,temple_2} <br />
[/event]<br />
<br />
<br />
<br />
=== Ordering and writing === <br />
<br />
<br />
We said earlier that criteria order is not significant. That's right from a theoretical point of view. But, for syntactic reasons and particularly because the radius key in location filters, this is not fully true in WML. Actually conditions are applied to candidate objects until one proves to be false or all conditions are checked. Then, if some condition proves to be false, remaining conditions <u>are not evaluated</u> (so if there's some radius there, it will not be executed). In some cases, this may be important. One can assume conditions are evaluated in the order they are found except logical operators (and, or, not) which are evaluated <u>after</u> other conditions. It’s very important to note that evaluation order of top level conditions (those not embedded in and/or/not tags) is undocumented, which means <u>your code shouldn’t rely on it</u>. On the contrary, and/or/not tags are always executed last, in the order they are written. <br />
<br />
So in this example:<br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
[or] <br />
side=2 <br />
[/or] <br />
gender=female <br />
[/filter] <br />
will be executed using this order: <br />
[filter] <br />
has_weapon=sword <br />
side=1 <br />
gender=female <br />
[or] <br />
side=2 <br />
[/or] <br />
[/filter]<br />
One should be aware of this for some reasons:<br />
<br />
'''⇒''' Clarity: your code will be easier to understand and to debug if you avoid meddling conditions, nested filters and logical blocks, and write them in the order they are evaluated.<br />
<br />
'''⇒''' Performance.<br />
Most of time, performance is not an issue. But it can be if you have a lot of units and use '''[filter_wml]'''. More generally, it’s good programming practice to execute the <u>more restrictive</u> test first.<br />
Consider this example:<br />
[filter]<br />
race=orc<br />
x,y=16,22<br />
[/filter]<br />
The first condition will be evaluated on all units. But the second one will be evaluated on all orcs. Then if we write:<br />
[filter]<br />
x,y=16,22<br />
race=orc<br />
[/filter]<br />
obviously, the second condition will be evaluated once at most, and filtering will be faster. (Strictly speaking, I should have wrapped second condition in an '''and''' tag to force evaluation order).<br />
Remember too that logical operators '''(and, or, not)''' are not mandatory, but are allowed. So one can use them for clarity sake or to force an evaluation order. It’s particularly important when using '''or''' tags.<br />
<br />
In the example above one could expect the result set contains all women of sides 1 and 2 wielding a sword. But it contains actually all side 1 women wielding a sword plus <u>all side 2 units</u>.<br />
<br />
Filters work actually as if top level criteria were enclosed in an implicit '''and''' tag: so we should read:<br />
[filter]<br />
[and] '''#implicit'''<br />
has_weapon=sword<br />
side=1<br />
gender=female<br />
[/and]<br />
[or]<br />
side=2<br />
[/or]<br />
[/filter]<br />
and what we probably wanted is:<br />
[filter]<br />
has_weapon=sword<br />
gender=female<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
Note that it could be written:<br />
[filter]<br />
[and]<br />
has_weapon=sword<br />
gender=female<br />
[/and]<br />
[and]<br />
side=1<br />
[or]<br />
side=2<br />
[/or]<br />
[/and]<br />
[/filter]<br />
This syntax is correct and you can use it if you find it clearer.<br />
Remembering this implicit '''and''' tag (and writing it explicitly at will) is very important to understand or design complex filters.<br />
<br />
<br />
<br />
=== Writing complex filters. ===<br />
<br />
<br />
Here are some guidelines one can use when writing complex filters. Suppose we want to set up some kind of disease aura harming units standing close to some villages. We shall start writing a sentence describing the feature.<br />
<br />
'''We want to select units who:'''<br />
'''Are enemy to side 1'''<br />
'''stand on hexes which'''<br />
'''are near to'''<br />
'''villages'''<br />
'''with side 1 units standing on it'''<br />
Mark we put only one condition on each line to clearly separate them. Mark we sorted them, because some apply to units to be filtered, others to locations, and finally to other (enemy) units standing on locations. Next, we shall add logical operators and parenthesis to clearly specify what we want:<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">Are enemy to side 1</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now, we are ready to start building the filter. Since we want units who… it shall be a unit filter. In the standard unit filter, there’s no condition directly allowing to state the unit is enemy to side 1. So we have to replace this with something valid in the SUF context. Using parenthesis to avoid errors, we can replace the condition with a side filter because it’s valid in unit filters.<br />
'''Units, who ('''<br />
'''<span style="color:#0000FF;">(belongs to a side which ('''<br />
'''is enemy to side 1) )</span> AND''' <br />
'''stand on locations which ('''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
''')'''<br />
Now we can write the filter. Here we do it step by step to show how the translation is rather straightforward and how our parenthesis match exactly the subtags.<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
'''AND''' <br />
'''stand on locations which ( # we start to filter locations there'''<br />
'''( Are villages AND'''<br />
'''have unit on it who ('''<br />
'''Belongs to side 1'''<br />
''')'''<br />
''') OR'''<br />
'''are adjacent to THOSE villages radius 3'''<br />
''')''' <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
'''AND'''<br />
'''have unit on it who (# to unit filter again'''<br />
'''Belongs to side 1'''<br />
''')'''<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[and] <br />
[filter_location]<br />
terrain=*^V*<br />
[and]<br />
[filter]<br />
side=1<br />
[/filter]<br />
[/and]<br />
radius=3 '''# radius is a special case, see below'''<br />
[/filter_location]<br />
[/and] <br />
[/filter]<br />
Here, we are done. The filter should work as it is, but it looks rather unusual because all these '''[and]''' blocks. Actually we can delete most of them using a simple rule: '''[and]''' tags are not needed when they contain one single criterion or a single block, (except if you want to set up an evaluation order). In our example, the '''filter_location''' block is alone in its '''and''' tag, and the embedded '''filter''' as well, so we can avoid those '''and''' tags and get finally:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_side]<br />
[enemy_of]<br />
side=1<br />
[/enemy_of]<br />
[/filter_side]<br />
[filter_location]<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
radius=3<br />
[/filter_location]<br />
[/filter]<br />
[harm_unit]<br />
[filter]<br />
id=$unit.id<br />
[/filter]<br />
amount=10<br />
animate=yes<br />
[/harm_unit]<br />
[/event]<br />
<br />
<br />
<br />
=== Filters uses: events actions and conditions. ===<br />
<br />
<br />
Here, we shall deal with filter uses in WML. The language is not fully consistent, mainly to simplify its syntax, so some points can be misleading.<br />
:<u>'''Using in actions'''</u><br />
Most actions take a filter as first parameter. The main difficulty here is to know if '''[filter]''' tags must be used or not. Actually, they’re used to avoid confusing keys and criteria when they have the same name<sup>4</sup>. For instance, the '''[kill]''' action needs a unit filter and has these keys:<br />
<br />
* '''animate:''' if 'yes', displays the unit dying (fading away).<br />
* '''fire_event:''' if 'yes', triggers any appropriate 'die' events (See EventWML). Note that events are only fired for killed units that have been on the map (as opposed to recall list).<br />
* '''[secondary_unit]''' with a StandardUnitFilter as argument. Do not use a [filter] tag. Has an effect only if fire_event=yes. The first on-map unit matching the filter.<br />
<br />
As we can see, none of these keys and tags are shared with unit filter keys and tags. This means the code parser needs no '''[filter]''' tag to know which key belongs to the filter and which to the action. But in the code, there’s no obvious distinction.<br />
<br />
________________________________________<br />
<br />
:<sup>4) </sup>Note there’s no specific top level tag for location filters.<br />
<br />
<br />
<br />
[kill]<br />
animate=yes '''# this belongs to the ‘kill’'''<br />
id=BadGuy_101 '''# this belongs to the unit filter'''<br />
[/kill]<br />
is the correct syntax. Note a common error is to use a '''[filter]''' tag here. Since this tag is unknown in the context, it is ignored, so the kill action is applied to the starting set, i.e. all units (including the recall lists). Same with the '''filter_side''' tag which is not needed everywhere (in '''store_sides''' particularly).<br />
Adversely, '''[modify_unit]''' obviously requires a '''[filter]''' tag, because all keys and criteria have the same name:<br />
[modify_unit]<br />
side=2<br />
side=1<br />
[/modify_unit]<br />
<br />
This would make no sense of course because one can’t find if side 1 units must be put into side 2 or the contrary. Anyway, there is an exception: the '''[store_unit]''' tag requires a '''[filter]''' tag even if there is no '''‘variable’''' key in units description. Another special case is when using terrain action, because the key '''terrain''' is valid both in location filters and terrain action. Since there is no special tag to delimit location filters, one should write:<br />
[terrain]<br />
[and]<br />
terrain=Wo* '''# here is the filter criterion'''<br />
[/and]<br />
terrain=Rr '''# and the new terrain'''<br />
[/terrain]<br />
The next example can be confusing:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
[/kill]<br />
When reading the '''‘kill’''' tag documentation, one will not find any '''‘filter_location’''' or location filter entry. Does this means it’s an undocumented feature ? No. But the '''‘filter_location’''' belongs to the implicit '''‘filter’''' tag of the '''‘kill’''' action and is documented there. It’s kind of:<br />
[kill]'''# kill all units standing on deep water'''<br />
animate=yes<br />
'''#'''[filter] '''we’re filtering units here, not locations'''<br />
[filter_location]<br />
terrain=Wo<br />
[/filter_location]<br />
'''#'''[/filter]<br />
[/kill]<br />
<br />
=== Using filters in conditions. ===<br />
<br />
Filters can be used to create conditional expressions. They can be nested in '''[have_unit]''' or '''[have_location]''' tags or in nested filters. Here, the result set is not used directly, but its size must fall in the range defined by the '''‘count’''' key. This finally gives a Boolean result: true or false. So one can use them in '''[if] [show_if]''' conditional actions or in a '''[filter_condition]''' tag. They are widely used in nested filters too (see the special chapter on this).<br />
<br />
Let’s give some examples:<br />
<br />
[have_unit] '''# this piece of code evaluates to true when no more enemy leaders are alive'''<br />
canrecruit=yes<br />
[not]<br />
side=1<br />
[/not]<br />
count=0<br />
[/have_unit]<br />
<br />
[have_location] '''# this one is true if at least 5 side 1 units stand on a village'''<br />
terrain=*^V*<br />
[filter]<br />
side=1<br />
[/filter]<br />
count=5-1000<br />
[/have_location]<br />
Note we could also write this condition:<br />
[have_unit]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
count=5-1000<br />
[/have_unit]<br />
Note we could use a '''[store_unit]''' instead, testing the ''length'' property of the array:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
side=1<br />
[filter_location]<br />
terrain=*^V*<br />
[/filter_location]<br />
[/filter]<br />
[/store_unit]<br />
#[if] or [filter_condition]<br />
[variable]<br />
name=temp.length<br />
greater_than=4<br />
[/variable]<br />
<br />
<br />
=== Using filters in events. ===<br />
In events, filters are always used in a conditional way, because they state if the event should fire or not. In any event, we can set '''[filter_condition]''' using '''have_unit''' or '''have_location''' (and a more ordinary variable condition, but this is off topic).<br />
Some events use filters in a special way: '''moveto''' and '''attack''' events particularly. In them, the filtering apply not on all units as usual, but only on units involved in the event action: one unit only is moving at a time, two units only are involved in a fight. Then the event fires if and only if the involved unit(s) match the filter.<br />
Note that one can use '''[filter]''' and '''[filter_condition]''' in the same moveto or attack event. Both conditions are then ANDed.<br />
<br />
'''Filtering units'''<br />
<br />
Criteria allowed to filter units (in standard unit filters) are listed below. First, the keys dealing with the unit properties (as usual, comma separated lists means conditions are ORed):<br />
:'''id:''' ''can be a comma-separated list, every unit with one of these ids matches''<br />
:'''type:''' ''can be a list of types''<br />
:'''race:''' ''(Version 1.11 and later only: this can be a comma-separated list)''<br />
:'''ability:''' ''unit has an ability with the given id (not name !)''<br />
:'''side:''' ''the unit is on the given side (can be a list). One can use a [filter_side] instead''<br />
:'''has_weapon:''' ''the unit has a weapon with the given name''<br />
:'''canrecruit:''' ''yes if the unit can recruit (i.e. is a leader)''<br />
:'''gender:''' ''female if the unit is female rather than the default of male''<br />
:'''role:''' ''the unit has been assigned the given role''<br />
:'''level:''' ''the level of the unit''<br />
:'''defense:''' ''current defense of the unit on current tile''<br />
:'''movement_cost:''' ''current movement cost of the unit on current tile''<br />
:'''x,y:''' ''the position of the unit. (Ranges ? probably because it works in moveto events)''<br />
<br />
<br />
Not all unit properties are listed here, but they can be used in a '''[filter_wml]''' sub-tag like this:<br />
[filter_wml]<br />
max_moves=7<br />
[/filter_wml]<br />
<br />
[filter_wml]<br />
[status]<br />
poisoned=yes<br />
[/status]<br />
[/filter_wml]<br />
Some of them accept comma separated lists or ranges. Some not, but it also possible to use them more than once with '''[and]''' and '''[or]''' subtags. For instance :<br />
[and]<br />
race= elf<br />
[or]<br />
race= merman<br />
[/or]<br />
[/and]<br />
Other sub tags are dealing with unit relationships:<br />
:'''⇒''' The hex on which they stand: '''[filter_location]''' which contains a standard location filter.<br />
:'''⇒''' The units adjacent to it: '''[filter_adjacent]''' which contains another standard unit filter<br />
:'''⇒''' Their visible status relating to a particular side: '''[filter_vision]'''<br />
These are nested filters ; or in other words, filters used to create conditions and not result sets (see earlier and later).<br />
<br />
Custom functions returning a boolean:<br />
:'''formula:''' FormulaAI like formula.<br />
:'''lua_function:''' lua function<br />
<br />
SUFs accept a '''find_in''' key too. As we saw earlier, this allows to restrict the starting set to the content of an array.<br />
<br />
=== this_unit ===<br />
<br />
This variable is a special variable defined only inside SUFs. Suppose we want to catch in a filter units at full health. We can use the '''hitpoints''', but the problem is we know not which value to use, because every unit type has its own:<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=?<br />
[/filter_wml]<br />
[/filter]<br />
This is why we could have the use of some way to specify the unit being fetched during the filtering.<br />
[filter]<br />
side=1<br />
[filter_wml]<br />
hitpoints=$this_unit.max_hitpoints<br />
[/filter_wml]<br />
[/filter]<br />
This does the trick. The condition value will be updated according to unit properties before executing the check.<br />
<br />
<br />
=== Filtering locations ===<br />
<br />
'''find_in''': a location array specifying the starting set.<br />
<br />
'''time_of_day''': one of lawful, chaotic, neutral or liminal.<br />
'''time_of_day_id''': one or more from: dawn, morning, afternoon, dusk, first_watch, second_watch, indoors, underground and deep_underground.<br />
<br />
'''terrain''': comma separated list of terrains.<br />
'''x,y''': the same as in the unit filter; supports any range.<br />
'''owner_side''': If a valid side number, restricts stored locations to villages belonging to this side. If 0, restricts to all unowned locations (the whole map except villages which belong to some valid side). A hex is considered a village if and only if its [ terrain_type ] gives_income= parameter was set to yes (which means a side can own that hex).<br />
<br />
'''[filter_adjacent_location]''': a standard location filter; if present the correct number of adjacent locations must match this filter<br />
<br />
'''[filter]''' with a Standard Unit Filter as argument; if present a unit must also be there<br />
<br />
'''radius''': <span style="color:#FF0000;"><u>this is not strictly speaking a criterion.</u></span> It adds to the result set all hexes adjacent to a matching hex and is always applied last, when all criteria are checked. Remember the filtering process is a hidden loop where all candidates are fetched one by one. If a candidate match the filter, radius adds all adjacent hexes (matching the filter or not !). If it don't, it does nothing.<br />
This is why this example doesn't work:<br />
[filter] '''<span style="color:#FF0000;"># this example doesn’t work !</span>'''<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
The coder here expected the radius action to be performed just after selecting the 43,32 hex, and the '''[not]''' criterion applied to this hex and it's adjacent radius 5 set. But '''radius''' is always applied last, <u>even if written before some other conditions</u>. So when using '''radius''', a good rule is to create the filter without it at first and to see if it can catch something. Here, it would give:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
which is clearly non sense because the two conditions are mutually exclusive.<br />
<br />
The solution is to pack the conditions in two different filters:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[and]<br />
[filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/and]<br />
[/filter]<br />
or,<br />
[filter]<br />
side=1<br />
[filter_location]<br />
[and]<br />
x,y=43,32<br />
radius=5<br />
[/and]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter_location]<br />
[/filter]<br />
or, since the x,y keys are defined in '''[filter]''' too, it can be:<br />
[filter]<br />
side=1<br />
[filter_location]<br />
x,y=43,32<br />
radius=5<br />
[/filter_location]<br />
[not]<br />
x,y=43,32<br />
[/not]<br />
[/filter]<br />
This is why '''[filter_radius]''' is useful. As we said, radius adds hexes without checking any condition (except proximity of course). If we want to put a condition on hexes added with radius (and them only), we would use it as in next example. Here we want to select forested hexes near villages:<br />
[filter_location]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/filter_location]<br />
But, this will not work exactly as in our previous example because radius extends outwards from matching locations one step at a time. Only the locations matching the '''filter_radius''' will be selected AND used to compute the next step. If there’s no forest hex near the village, the previous filter will return nothing, even if there are some forest hexes farther in the range.<br />
<br />
Note this filter selects the village too ! If we want not, this should be:<br />
[filter_location]<br />
[and]<br />
terrain=*^V*<br />
radius=3<br />
[filter_radius]<br />
terrain=*^F*<br />
[/filter_radius]<br />
[/and]<br />
[not]<br />
terrain=*^V*<br />
[/not]<br />
[/filter_location]<br />
<br />
<br />
=== Nested filters ===<br />
<br />
Now, we are ready to study how to create nested filters, in other words, filters containing sub filters. In location or unit filters, the documentation says one can insert filters of various kind involving other objects. In this way, we can select unit adjacent to other units or standing on some terrains. Actually, they’re not exactly filters: they are conditions or criteria built on filters. In other words, they don’t produce a result set but are, like other criteria, expressions evaluating to true or false. That’s why many of them have additional keys, like '''‘count’''' (see the filter use in conditions).<br />
<br />
We shall discuss this on examples.<br />
<br />
<br />
=== Filter_adjacent. ===<br />
<br />
In a unit filter, this allow to create criteria stating which units must be adjacent to the tested unit. In this example, we shall implement an ability named ‘escape’. Units having this ability can teleport elsewhere when surrounded by more than 3 enemies. We shall set a '''moveto''' event to watch the ‘surround’ event.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition on the moving unit here'''<br />
[filter_adjacent]<br />
ability=escape '''# this part fetches the adjacent units, not the moving one.'''<br />
is_enemy=yes<br />
[/filter_adjacent] '''# this results to true if at least one unit is found.'''<br />
[/filter]<br />
…<br />
[/event]<br />
This will fire each time some enemy unit get close to our able unit. Note we have here not only a standard unit filter (the ability key), but special keys specifying relationships between units : the '''is_enemy key'''. Another special key is the '''‘count’''' key. It allows to specify how much adjacent units must be found. As far as the default is 1-6, we don’t need it here. But this means more than one able unit can be surrounded by a single move.<br />
Now, we must add the ‘surrounded’ condition. Here, we shall use a new '''filter_adjacent''' tag, but applying to the able unit (not the moving one):<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
[filter_adjacent]<br />
ability=escape '''# this part filters the adjacent units, not the moving one.'''<br />
[filter_adjacent]<br />
is_enemy=yes '''# this part finds adjacent units to the able one.'''<br />
count=4-6<br />
[/filter_adjacent]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Suppose we want now to apply one more condition for the ability to work : able unit must be adjacent to another unit sharing the same ability. We must use a new '''filter_adjacent''' tag, and since there is already one, use an '''‘and’''' tag to combine them.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
[/filter_adjacent]<br />
[/and]<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
Note you’ll find very few examples of such complex filters in events. Why ? It’s because we often need to catch the involved units to take some actions. In our example, we need not only to test if our able units are surrounded but make them teleport as well. As a result, the filtering process would probably be split in two, moving in the '''teleport''' filter the code which was in the '''filter_adjacent''' tag:<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
is_enemy=yes<br />
'''# <span style="color:#0000FF;">here was a block …</span>'''<br />
[/filter_adjacent]<br />
[/filter]<br />
<br />
[teleport]<br />
[filter]<br />
ability=escape<br />
[filter_adjacent] '''# <span style="color:#0000FF;">… which was just moved here ! </span>'''<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
#count=1-6 '''# since it’s the default, we don’t need this'''<br />
[/filter_adjacent]<br />
[/and]<br />
[/filter]<br />
…<br />
[/teleport]<br />
[/event]<br />
<br />
=== Filter_location ===<br />
<br />
Filter_location allows to specify on which hex the unit must be standing. Of course, since we already have x,y keys in the standard unit filter, we don’t need to set a filter location for that. Suppose our former ability should work only in forests. The '''filter_location''' is fitted for that.<br />
[filter_location]<br />
terrain=*^F*<br />
[/filter_location]<br />
Where shall we put it ? Certainly not at the first level of the filter: this would make the ability to work if the moving unit (the enemy) stands on forest. So the right place is in level 2 and 3 where we are dealing with the able units.<br />
[event]<br />
name=moveto<br />
first_time_only=no<br />
[filter]<br />
'''# we could set some additional condition here'''<br />
[filter_adjacent]<br />
ability=escape<br />
[filter_adjacent]<br />
is_enemy=yes<br />
count=4-6<br />
[/filter_adjacent]<br />
[and]<br />
[filter_adjacent]<br />
ability=escape '''# these are the needed helpers'''<br />
is_enemy=no<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
[/filter_adjacent]<br />
[/and]<br />
<span style="color:#0000FF;">[filter_location]<br />
terrain=*^F*<br />
[/filter_location]</span><br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
…<br />
[/event]<br />
<br />
=== pitfalls ===<br />
<br />
One thing to avoid designing filters is using redundant or mutually exclusive criteria. In other words, they specify a condition never or always met in any case. Let's give an example:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=1<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
It's pretty obvious this filter will always return an empty set because side 1 units can't be enemy to side 1. But this filter is valid and will raise no error ! Now, let's look at this one:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
is_enemy=yes<br />
[/filter_adjacent]<br />
[/filter]<br />
Here, we can mark the '''is_enemy''' key is redundant because sides already define if the units are enemy or not. So, if side 1 and 2 are allied, the filter will always return an empty set. If they are not, it will always match, so the '''is_enemy''' criterion is useless. This filter:<br />
[filter]<br />
side=1<br />
[filter_adjacent]<br />
side=2<br />
[/filter_adjacent]<br />
[/filter]<br />
will always return exactly the same result (except if sides configuration is modified during the scenario of course). Another example:<br />
[filter]<br />
type=Horseman,Knight<br />
[filter_location]<br />
terrain=M*<br />
[/filter_location]<br />
[/filter]<br />
The result set will always be empty because these units can't walk on mountains. So, there's no need to use such a filter and it's most probably a design error.<br />
<br />
Another common error is assuming a filter will always return a single unit or location. In conjunction with '''store_unit''' or '''store_locations''', the result set will be an array or a single variable:<br />
[store_unit]<br />
variable=temp<br />
[filter]<br />
… anything<br />
[/filter]<br />
[/store_unit]<br />
<br />
[modify_unit]<br />
[filter]<br />
id=$temp.id<br />
[/filter]<br />
… something<br />
[/modify_unit]<br />
This will work only if the result set contains a single unit. Else, the temp.id will be taken from the first result, or empty if the result set was empty. Instead, one should use temp[$i].id in a FOREACH loop. Or better in this particular case: '''find_in'''=temp in the unit filter, because it handles correctly all the cases.<br />
<br />
=== Download as .pdf ===<br />
Wesnoth forum thread:<br />
[http://forums.wesnoth.org/viewtopic.php?f=21&t=38583#p553139 (WMLFiltering.pdf.zip)]<br />
<br />
For feedback please use the same thread.<br />
<br />
<br />
<br />
<br />
[[Category: WML Reference]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=InternalActionsWML&diff=56434InternalActionsWML2015-06-17T02:37:45Z<p>Jmichael: /* [set_variable] */</p>
<hr />
<div>{{WML Tags}}<br />
<br />
Part of [[ActionWML]], Internal actions are actions that WML uses internally that do not directly affect game play (or, at least, are not readily apparent to the player). For example, storing a variable is an internal action.<br />
<br />
== Variable Actions ==<br />
<br />
These actions are focused, in one way or another, on [[VariablesWML|variables]]. Creating them, modifying them, capturing game data to them, you name it, these actions are all about the variables.<br />
<br />
=== [set_variable] ===<br />
<br />
The '''[set_variable]''' tag is used to create and manipulate WML variables. The [http://www.wesnoth.org/macro-reference.xhtml#VARIABLE VARIABLE] macro is a quick syntactic shortcut for simple variable creation and the [http://www.wesnoth.org/macro-reference.xhtml#VARIABLE_OP VARIABLE_OP] macro is a quick syntactic shortcut for performing simple mathematical operations on variables.<br />
<br />
* '''name''': the name of the variable to manipulate<br />
<br />
* '''value''': set the variable to the given value (can be numeric or string).Use literal for no substitution. (see [[VariablesWML]])<br />
<br />
* '''literal''': set the variable to the given value (can be numeric or string). This does not interpret any dollar signs.<br />
<br />
* '''to_variable''': set the variable to the value of the given variable, e.g. 'to_variable=temp' would be equivalent to 'value=$temp'.<br />
<br />
* '''add''': add the given amount to the variable.<br />
<br />
* '''sub''': subtract the given amount from the variable.<br />
<br />
* '''multiply''': multiply the variable by the given number. The result is a float.<br />To negate a number, multiply by -1. If you negate 0, the result is a floating-point negative zero -0. To display -0 as 0, use a second tag with add=0; it will flip -0 to 0 but not affect other numbers.<br />
<br />
* '''divide''': divide the variable by the given number. The result is a float. Wesnoth 1.9 and later no longer uses integer division. Use a second tag with round=floor if you relied on this.<br />
<br />
* '''modulo''': returns the remainder of a division.<br />
<br />
* '''rand''': the variable will be randomly set.<br>You may provide a comma separated list of possibilities, e.g. 'rand=Bob,Bill,Bella'.<br>You may provide a range of numbers (integers), e.g. 'rand=3..5'.<br>You may combine these, e.g. 'rand=100,1..9', in which case there would be 1/10th chance of getting 100, just like for each of 1 to 9. If a number or item is repeated, it is sampled more frequently as appropriate. See [[MultiplayerContent]] for more info on the MP case.<br>Using rand= will automatically result in the current action being non undoable. Ignoring possible [allow_undo].<br />
<br />
* '''time=stamp''': Retrieves a timestamp in milliseconds since wesnoth was started, can be used as timing aid. Don't try to use this as random value in MP since it will cause an OOS.<br />
<br />
* '''string_length''': Retrieves the length in characters of the string passed as this attribute's value; such string is parsed and variable substitution applied automatically (see [[VariablesWML]] for details).<br />
<br />
* '''[join]''' joins an array of strings to create a textual list<br />
** '''variable''': name of the array<br />
** '''key''': the key of each array element(array[$i].foo) in which the strings are stored<br />
** '''separator''': separator to connect the elements<br />
** '''remove_empty''': whether to ignore empty elements<br />
<br />
* '''ipart''': Assigns the integer part (the part to the left of the comma) of the referenced variable.<br />
<br />
* '''fpart''': Assigns the decimal part (the part to the right of the comma) of the referenced variable.<br />
<br />
* '''round''': Rounds the variable to the specified number of digits of precision. Negative precision works as expected (rounding 19517 to -2 = 19500). Special values:<br />
**'''round=ceil''': Rounds upward to the nearest integer.<br />
**'''round=floor''': Rounds down to the nearest integer.<br />
<br />
=== [set_variables] ===<br />
<br />
Manipulates a WML array or container<br />
<br />
* '''name''': the name of the array or container to manipulate<br />
<br />
* '''mode''': one of the following values:<br />
** ''replace'': will clean the array '''name''' and replace it with given data<br />
** ''append'': will append given data to the current array<br />
** ''merge'': will merge in the given data into '''name'''<br />
** ''insert'': will insert the given data at the index specified in the '''name''' attribute, such as name=my_array[1]. The default index is zero, which will insert to the front of the array. '''Note:''' if an invalid index is used, empty containers will be created before the insertion is performed. In other words, do not attempt to insert at an index greater than (or equal to) the array's current length. This limitation may be removed in future versions.<br />
<br />
* '''to_variable''': data will be set to the given array<br />
<br />
* '''[value]''': the WML inside the [value] tags will be stored in data, variables will be interpolated directly, use $| in order to escape the $ sign, you can store arrays of WML by supplying multiple [value] tags. ([[#Using_.5Bset_variables.5D_to_Create_Arrays_of_WML|See Example]])<br />
<br />
* '''[literal]''': same as '''[value]''', but variables will not be substituted, '''[literal]''' and '''[value]''' can not be used in the same [set_variables] tag, i.e. you can not create arrays by piling a mix of '''[value]''' and '''[literal]''' tags<br />
<br />
*'''[split]''' splits a textual list into an array which will then be set to data<br />
** '''list''': textual list to split<br />
** '''key''': the key of each array element(array[$i].foo) in which the strings are stored<br />
** '''separator''': separator to separate the elements<br />
** '''remove_empty''': whether to ignore empty elements<br />
<br />
=== Capturing Game Data ===<br />
<br />
These actions capture different bits of game data and store them to variables so they can be examined and/or manipulated.<br />
<br />
==== [store_gold] ====<br />
<br />
Stores a side's gold into a variable.<br />
<br />
* '''[[StandardSideFilter]]''': The first matching side's gold will be stored in the variable "variable".<br />
* '''variable''': (default='gold') the name of the variable to store the gold in<br />
<br />
==== [store_locations] ====<br />
<br />
Stores a series of locations that pass certain criteria into an array. Each member of the array has members 'x' and 'y' (the position) and 'terrain' (the terrain type) and 'owner_side' (villages only). The array will include any unreachable border hexes, if applicable.<br />
<br />
* [[StandardLocationFilter]]: a location or location range which specifies the locations to store. By default, all locations on the map are stored.<br />
<br />
* '''variable''': the name of the variable (array) into which to store the locations.<br />
<br />
==== [store_reachable_locations] ====<br />
<br />
Stores locations reachable by the given units. Can store either the movement, attack or vision ranges.<br />
<br />
* '''[filter]''': a [[StandardUnitFilter]]. The locations reachable by any of the matching units will be stored.<br />
* '''[filter_location]''': (optional) a [[StandardLocationFilter]]. Only locations which also match this filter will be stored.<br />
* '''range''': possible values ''movement'' (default), ''attack'', ''vision''. If ''movement'', stores the locations within the movement range of the unit, taking Zone of Control into account. If ''attack'', stores the attack range (movement range + 1 hex). If ''vision'', stores the vision range (movement range ignoring Zone of Control + 1 hex).<br />
* '''moves''': possible values ''current'' (default), ''max''. Specifies whether to use the current or maximum movement points when calculating the range.<br />
* '''viewing_side''': (optional) the side whose vision to use when calculating the reach. This only has meaning in the presence of fog, shroud, or units with the ambush ability. If left out, then fog, shroud and ambushers are ignored and the real reach of the units is stored.<br />
* '''variable''': the name of the variable (array) into which to store the locations.<br />
<br />
==== [store_map_dimensions] ====<br />
<br />
Stores the map dimensions in a variable.<br />
<br />
* '''variable''': the name of the variable where the values will be saved into. If it is skipped, a variable 'map_size' is used, and its contents overridden, if they existed already. The result is a container variable, with members ''width'' and ''height''.<br />
<br />
==== [store_side] ====<br />
<br />
Stores information about a certain side in a variable.<br />
<br />
'''Keys:'''<br />
* '''[[StandardSideFilter]]''': All matching sides are stored. (An array is created if several sides match - access it with side[2].team_name and so on.)<br />
* '''variable''': the name of the variable to store the information in (default: "side")<br />
<br />
'''Result'''<br />
<br />
Variable will contain following members:<br />
* '''color''': It indicates team color. Can be one of the following:<br />
{| border = 1<br />
| ''color''<br />
| red<br />
| blue<br />
| green<br />
| purple<br />
| black<br />
| brown<br />
| orange<br />
| white<br />
| teal<br />
|-<br />
| ''value''<br />
| 1<br />
| 2<br />
| 3<br />
| 4<br />
| 5<br />
| 6<br />
| 7<br />
| 8<br />
| 9<br />
|}<br />
* '''controller''': Indicates type of player that control this side. ''In networked multiplayer, the controller attribute is ambiguous. Be very careful or you have OOS errors.''<br />
** '''human''': Human player<br />
** '''ai''': If players assigns "Computer Player" to "Player/Type" in game lobby<br />
** '''network''': In multiplayer for sides that client does not control, both what would normally be human and ai. For host values are as usual, this is where OOS comes from.<br />
** '''null''': If players assigns "Empty" to "Player/Type" in game lobby<br />
* '''fog''': Indicates whether this side is affected by fog of war.<br />
* '''gold''': The amount of gold the side have.<br />
* '''hidden''': (boolean) If 'yes', side is not shown in status table.<br />
* '''income''': Base income for this side (without villages).<br />
* '''name''': Name of player.<br />
* '''recruit''': A comma-separated list of unit types that can be recruited by this side.<br />
* '''shroud''': Whether this side is affected by shroud.<br />
* '''side''': The $side_number of the side belonging to this container<br />
* '''team_name''': String representing the team's description.<br />
* '''user_team_name''': Translated string representing the team's description.<br />
* '''village_gold''': The amount of gold given to this side per village it controls per turn.<br />
* '''scroll_to_leader''': (boolean) Whether the game view scrolls to the side leader at the start of their turn.<br />
* '''flag''': Flag animation for villages owned by this side (see [[SideWML|[side]]]). Unless previously specified in [side] or changed with WML (see [[DirectActionsWML#.5Bmodify_side.5D|[modify_side]]]), this value may be empty for the default flag animation.<br />
* '''flag_icon''': Flag icon for the status bar for this side (see [[SideWML|[side]]]). Unless previously specified in [side] or changed with WML (see [[DirectActionsWML#.5Bmodify_side.5D|[modify_side]]]), this value may be empty for the default flag icon.<br />
* '''village_support''': The number of unit levels this side is able to support (does not pay upkeep on) per village it controls.<br />
<br />
==== [store_starting_location] ====<br />
<br />
Stores the starting location of a side's leader in a variable. The variable is a composite type which will have members 'x', 'y', 'terrain' and 'owner_side' (villages only)<br />
<br />
* [[StandardSideFilter]]: The starting locations of all matching sides will be stored. If multiple sides are matched, a WML array will be created.<br />
* '''variable''': (default='location'): the name of the variable to store the location in<br />
<br />
==== [store_time_of_day] ====<br />
<br />
Stores time of day information from the current scenario into a WML variable container.<br />
<br />
* '''x, y''': Location to store the time for. [[DirectActionsWML#.5Btime_area.5D|Time areas]] matter; illumination does not. If this is omitted, the global (location-independent) time is stored.<br />
<br />
* '''variable''': (default='time_of_day') name of the container on which to store the information. The container will be filled with the same attributes found on [[TimeWML]].<br />
<br />
* '''turn''': (defaults to the current turn number) changes the turn number for which time of day information should be retrieved.<br />
<br />
==== [store_turns] ====<br />
<br />
Stores the turn limit (the maximum number of turns). If there is no limit, this stores ''-1''.<br />
<br />
* '''variable''': (default='turns') the name of the variable in which to store the turn limit.<br />
<br />
==== [store_unit] ====<br />
<br />
Stores details about units into a [[VariablesWML#Container|container]] variable. When a unit is stored, all keys and tags in the unit definition may be manipulated, including some others, with [[InternalActionsWML#.5Bset_variable.5D|[set_variable]]]. A sample '''list of these tags and keys''' can be found at [[InternalActionsWMLUnitTags]].<br />
<br />
If you have a doubt about what keys are valid or what the valid value range is for each key, code a [store_unit] event, save the game, and examine what keys are in the file (or just examine the '''[unit]''' tag(s) in any save file). One can also use the [[CommandMode|:inspect]] command or the [[InterfaceActionsWML#.5Binspect.5D|[inspect]]] tag to open a game-state inspector dialog, which can be used to view unit properties.<br />
<br />
Common usage is to manipulate a unit by using '''[store_unit]''' to store it into a variable, followed by manipulation of the variable, and then [[DirectActionsWML#.5Bunstore_unit.5D|[unstore_unit]]] to re-create the unit with the modified variables.<br />
<br />
''Note: stored units also exist on the field, and modifying the stored variable will not automatically change the stats of the units. You need to use [unstore_unit]. See also [[DirectActionsWML#.5Bunstore_unit.5D|[unstore_unit]]] and [http://www.wesnoth.org/macro-reference.xhtml#FOREACH FOREACH].''<br />
<br />
* '''[filter]''' with a [[StandardUnitFilter]] as argument. All units matching this filter will be stored. If there are multiple units, they will be stored into an array of variables. The units will be stored in order of their internal ''underlying_id'' attribute, which is usually in creation order (but you normally should not depend on the order).<br />
<br />
* '''variable''': the name of the variable into which to store the unit(s)<br />
<br />
* '''mode''': defaults to ''always_clear'', which clears the variable, whether or not a match is found. If mode is set to ''replace'', the variable will not be cleared, and units which match the filter will overwrite existing elements at the start of the array, leaving any additional elements intact if the original array contained more elements than there are units matching the filter. If mode is set to ''append'', the variable will not be cleared, and units which match the filter will be added to the array after the existing elements.<br />
<br />
* '''kill''': if 'yes' the units that are stored will be removed from play. This is useful for instance to remove access to a player's recall list, with the intent to restore the recall list later.<br />
<br />
==== [store_unit_type] ====<br />
<br />
* '''type''': (required) the defined ID of the unit type, for example "Goblin Knight". Do not use a translation mark or it will not work correctly for different languages. A comma-separated list of IDs may also be used to store an array of unit types.<br />
<br />
* '''variable''': the name of the variable into which to store the unit type information (default "unit_type")<br />
<br />
==== [store_unit_type_ids] ====<br />
<br />
* '''variable''': the name of the variable into which to store a comma-separated list of all unit type IDs including all from all loaded addons<br />
<br />
==== [store_villages] ====<br />
<br />
Stores a series of locations of villages that pass certain criteria into an array. Each member of the result array will have members 'x' and 'y' (the position) and 'terrain' (the terrain type) and 'owner_side'. note: The only advantage/difference this tag has, in comparison to using [store_locations]terrain=*^V*, is that the amount of hexes which are considered for a possible match is previously restricted to those with villages.<br />
<br />
* '''variable''': the name of the variable (array) into which to store the locations (default: "location")<br />
* '''[[StandardLocationFilter]]''' tags and keys as arguments<br />
<br />
==== [store_items] ====<br />
<br />
Stores current items in the scenario into an array. Each entry has at least members x and y and can have all of the other keys listed in the documentation of [[InterfaceActionsWML#.5Bitem.5D|[item]]] (depending on what was set during creating the item).<br />
<br />
*'''variable''': name of the wml variable array to use (default "items")<br />
*'''[[StandardLocationFilter]]''' keys as arguments: only items on locations matching this [[StandardLocationFilter]] will be stored<br />
<br />
==== [store_relative_direction] ====<br />
<br />
{{DevFeature1.13|0}}<br />
<br />
Gets the relative direction from one hex to another. This is an interface to the function wesnoth uses to decide how a unit will face while it is moving / attacking / defending.<br />
<br />
* '''[source]''' x and y must describe a map location<br />
* '''[destination]''' similar<br />
* '''variable''' name of the variable to store string result in (one of 'n', 'nw', 'ne', 's', 'sw', 'se')<br />
* '''mode''' optional. 0 is the default setting corresponding to default wesnoth implementation used in animations. 1 is an alternate "radially symmetric" mode. The default mode breaks ties in the direction of south, since this makes more units face the player directly on screen. The radially symmetric mode breaks ties in the direction of counter-clockwise, and might be more appropriate in some cases.<br />
<br />
==== [find_path] ====<br />
<br />
A WML interface to the pathfinder. Calculates the path between a unit and a location and returns the result in a WML variable, that contains also an array for every step of the path.<br />
<br />
*'''[traveler]''': [[StandardUnitFilter]], only the first matching unit will be used for calculation<br />
*'''[destination]''': [[StandardLocationFilter]], only the first matching nearest hex will be used<br />
*'''variable''': the variable name where the result will be stored, if no value is supplied 'path' will be used as default name. Each step will be stored in a [step] array inside that variable.<br />
*'''allow_multiple_turns''': default no, if yes also moves that require more than one turn will be calculated.<br />
*'''check_visibility''': default no, if yes the path will not be computed if some hexes are not visible due to shroud.<br />
*'''check_teleport''': default yes, if no teleport won't be taken in account while computing path.<br />
*'''check_zoc''': default yes, if no unit ZOCs won't be considered while calculating the path.<br />
This is the structure of the variable returned by [find_path]:<br />
[path]<br />
hexes = the total length of the path<br />
if the path is calculated to an impassable hex, or the move requires multiple turns<br />
and allow_multiple_turns is no, its value will be 0.<br />
from_x, from_y = location of the unit<br />
to_x, to_y = destination<br />
movement_cost = total movement cost required by unit to reach that hex<br />
required_turns = total turns required by unit to reach that hex<br />
[step]<br />
x, y = location of the step<br />
terrain = terrain of the step<br />
movement_cost = movement cost required by unit to reach that hex<br />
required_turns = turns required by unit to reach that hex<br />
[/step]<br />
[/path]<br />
<br />
==== [unit_worth] ====<br />
Takes only an inline [[StandardUnitFilter]] (only the first matching unit will be used for calculation) and outputs the following variables: <br />
*''cost'', the current unit cost;<br />
*''next_cost'', the cost of the most expensive advancement;<br />
*''health'', the health of the unit in percentage;<br />
*''experience'', current experience in percentage;<br />
*''unit_worth'', how much the unit is worth.<br />
<br />
Mainly used for internal AI checks, but one could in theory just do anything with it.<br />
<br />
[event]<br />
name=moveto<br />
[unit_worth]<br />
x,y=$x1,$y1<br />
[/unit_worth]<br />
[message]<br />
id=$unit.id<br />
message=_"I cost $cost gold, with $health|% of my hitpoints and $experience|% on the way to cost $next_cost|.<br />
I am estimated to be worth $unit_worth"<br />
[/message]<br />
[clear_variable]<br />
name=cost,next_cost,health,experience,unit_worth<br />
[/clear_variable]<br />
[/event]<br />
<br />
=== [clear_variable] ===<br />
<br />
This will delete the given variable. This tag can delete a scalar or an entire array; it can also delete one container at an array index. The macro [http://www.wesnoth.org/macro-reference.xhtml#CLEAR_VARIABLE CLEAR_VARIABLE] is a shortcut for this tag.<br />
<br />
This action is good to use to clean up the set of variables; for example, a well-behaved scenario will delete any variables that should not be kept for the next scenario before the end of the scenario. One can also clear tags and variables of stored units; for example, one can remove [trait]s and [object]s.<br />
<br />
* '''name''': the name of the variable to clear. This can also be a comma-separated list of multiple variable names.<br />
** If a name ends with an array index, then it deletes that one container, and shifts the indexes of all subsequent containers. For example, <code>{CLEAR_VARIABLE my_awesome_array[2]}</code> deletes <code>my_awesome_array[2]</code>, but then moves <code>my_awesome_array[3]</code> to <code>my_awesome_array[2]</code>, moves <code>my_awesome_array[4]</code> to <code>my_awesome_array[3]</code>, and so on until the end of the array.<br />
** Note that <code>{CLEAR_VARIABLE my_awesome_array}</code> deletes the entire array, but <code>{CLEAR_VARIABLE my_awesome_array[0]}</code> deletes only the first container.<br />
<br />
=== [sync_variable] ===<br />
<br />
{{DevFeature1.13|0}}<br />
<br />
Sets one or multiple variables to the same value as on all clients and also on replays, it uses the value from the currently active side.<br />
* '''name''' the name of the variable to synchonize this can be a comma seperated list.<br />
<br />
== Other Internal Actions ==<br />
<br />
Believe it or not, there are some internal actions that are not focused primarily on variables. They are all grouped here.<br />
<br />
=== [fire_event] ===<br />
<br />
Trigger a WML event (used often for [[EventWML#Custom_events|custom events]])<br />
<br />
* '''name''': the name of event to trigger<br />
<br />
* '''[primary_unit]''': ''(Optional)'' Primary unit for the event. Will never match on a recall list unit. The first unit matching the filter will be chosen.<br />
**[[StandardUnitFilter]] as argument. Do not use a [filter] tag.<br />
<br />
* '''[secondary_unit]''': ''(Optional)'' Same as '''[primary_unit]''' except for the secondary unit.<br />
**[[StandardUnitFilter]] as argument. Do not use a [filter] tag.<br />
<br />
* '''[primary_attack]''': Information passed to the primary attack filter and $weapon variable on the new event.<br />
<br />
* '''[secondary_attack]''': Information passed to the second attack filter and $second_weapon variable on the new event.<br />
<br />
=== [remove_event] ===<br />
{{DevFeature1.13|0}}<br />
<br />
Removes the event with the specified id. Equivalent to <i>[event] id=foo remove=yes</i>. See [[EventWML#remove|EventWML]].<br />
<br />
* '''id''': the id of the event to remove. May be a comma separated list.<br />
<br />
=== [insert_tag] ===<br />
<br />
Inserts a variable as WML. In other words, the value of the passed [[VariablesWML#Container|container variable]] will be injected into the game as if they had been written out in WML form. ([[#.5Binsert_tag.5D_Example|See Example]]).<br />
<br />
Tag insertion is a special case in that it can be used in places where other ActionWML cannot be used. The basic rule is that anywhere that $variable syntax works, tag insertion will also work. In practice this means pretty much everywhere except directly within top-level scenario tags.<br />
<br />
*'''name''': The ["name"] to be given to the tag. This must be a tag which would be valid at the place where [insert_tag] is used, for anything to happen. (For example, if used as ActionWML, it should be a [[ActionWML]] tag name, and it may be a recognized subtag such as "option" when used within a [message]).<br />
<br />
*'''variable''': Name of the container variable which will have its value inserted into the tag.<br />
<br />
=== [role] ===<br />
<br />
Tries to find a unit to assign a role to.<br>This is useful if you want to choose a non-major character to say some things during the game. Once a role is assigned, you can use '''role=''' in a unit filter to identify the unit with that role (See [[FilterWML]]).<br>However, there is no guarantee that roles will ever be assigned. You can use '''[have_unit]''' (see [[ConditionalActionsWML#Condition_Tags|Condition Tags]]) to see whether a role was assigned. This tag uses a [[StandardUnitFilter]] (without [filter]) with the modification to order the search by type, mark only the first unit found with the role, and the role attribute is not used in the search. If for some reason you want to search for units that have or don't have existing roles, you can use one or more [not] filters. The will check recall lists in addition to units on the map. In normal use, you will probably want to include a ''side'' attribute to force the unit to be on a particular side.<br />
<br />
* '''role''': the value to store as the unit's role. This role is not used in the [[StandardUnitFilter]] when doing the search for the unit to assign this role to.<br />
<br />
* '''type''': a comma-separated list of possible types the unit can be. If any types are given, then units will be searched by type in the order listed. If no type is given, then no particular order with respect to type is guaranteed.<br />
<br />
* [[StandardUnitFilter]], do not use a [filter] sub-tag. SUF's role= and type= keys are not used: if you want to use them, use a nested SUF wrapped inside a [and] tag.<br />
<br />
== Examples ==<br />
<br />
=== Using [set_variables] to Create Arrays of WML ===<br />
<br />
[set_variables]<br />
name=arr<br />
mode=replace<br />
[value]<br />
foo=bar<br />
[/value]<br />
[value]<br />
foo=more<br />
[/value]<br />
[/set_variables]<br />
{DEBUG_MSG $arr[0].foo}<br />
{DEBUG_MSG $arr[1].foo}<br />
<br />
This will produce two output messages, first one saying '''bar''' and next one saying '''more'''.<br />
<br />
=== [insert_tag] Example ===<br />
<br />
[event]<br />
name=moveto<br />
<br />
[set_variable]<br />
name=temp.speaker<br />
value=Konrad<br />
[/set_variable]<br />
<br />
[set_variable]<br />
name=temp.message<br />
value= _ "Yo Kalenz!"<br />
[/set_variable] <br />
<br />
[insert_tag]<br />
name=message<br />
variable=temp<br />
[/insert_tag]<br />
[/event]<br />
<br />
This is effectively identical to:<br />
<br />
[event]<br />
name=moveto<br />
<br />
[message]<br />
speaker=Konrad<br />
message= _ "Yo Kalenz!"<br />
[/message]<br />
[/event]<br />
<br />
== See Also ==<br />
* [[VariablesWML]]<br />
* [[ActionWML]]<br />
** [[ConditionalWML]]<br />
** [[DirectActionsWML]]<br />
** [[InterfaceActionsWML]]<br />
* [[EventWML]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category: WML Reference]]<br />
[[Category: ActionsWML]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=UMCD&diff=56433UMCD2015-06-17T02:34:15Z<p>Jmichael: /* sql2cpp */</p>
<hr />
<div>=User Made Content Daemon (UMCD)=<br />
<br />
This page will give information on the UMCD project. It started as a [http://wiki.wesnoth.org/User:Trademark/GSoC_2013/Addon_Server:_Create_a_new_and_shiny_one GSoC project], this proposal should be regarded only as an archive.<br />
<br />
==Installation==<br />
<br />
===Dependencies on Linux===<br />
<br />
We must first install some required packets:<br />
<br />
sudo apt-get install mysql-server unixodbc-dev libmyodbc libboost-iostreams-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-thread-dev libboost-date-time-dev<br />
<br />
* mysql-server is the MySQL database.<br />
* unixodbc is a middleware API to access database (see http://en.wikipedia.org/wiki/ODBC).<br />
* libmyodbc is the MySQL driver to access the MySQL database.<br />
* Boost libraries are used to ease the development.<br />
<br />
===Database setup===<br />
<br />
We'll explain this step for the MySQL database but you can use any database that is ODBC compliant.<br />
<br />
====Initialize the MySQL database====<br />
<br />
Enter the MySQL prompt with:<br />
<br />
mysql -u root -p<br />
<br />
Then, we create the database:<br />
<br />
mysql> CREATE DATABASE IF NOT EXISTS umcd;<br />
mysql> USE umcd;<br />
<br />
We create the tables:<br />
<br />
mysql> source {wesnoth-directory}/data/umcd/database/create_database.sql<br />
<br />
We populate the database:<br />
<br />
mysql> source {wesnoth-directory}/data/umcd/database/populate_database.sql<br />
<br />
You can check that the tables are added with:<br />
<br />
mysql> show tables;<br />
<br />
Now we quit the mysql prompt:<br />
<br />
mysql> exit;<br />
<br />
====Install the ODBC driver====<br />
<br />
We must link the database to ODBC, so we'll retrieve the database connexion via a Data source name (DSN).<br />
<br />
First we must find the odbc.ini file:<br />
<br />
odbcinst -j<br />
<br />
In my computer it's in /etc/odbc.ini so I'll refer to this location. We must edit this file:<br />
<br />
sudo vim /etc/odbc.ini<br />
<br />
The file can be empty, we'll add these data:<br />
<br />
;<br />
; odbc.ini configuration for Connector/ODBC<br />
;<br />
<br />
[ODBC Data Sources]<br />
dbumcd = MyODBC Driver DSN for the UMCD database<br />
<br />
[dbumcd]<br />
Driver = /usr/lib/libmyodbc.so<br />
Description = Connector/ODBC Driver DSN for the UMCD database<br />
SERVER = localhost<br />
PORT =<br />
USER =<br />
Password =<br />
Database = umcd<br />
OPTION = 3<br />
SOCKET =<br />
<br />
The DSN of the umcd database will be "dbumcd". You can change the SERVER and PORT entry if it's an external database. Otherwise, let all the fields with a blank like in the example.<br />
<br />
Next we must install the driver with:<br />
<br />
odbcinst -f /etc/odbc.ini -d -i<br />
<br />
You can list all the ODBC drivers installed with:<br />
<br />
odbcinst -s -q<br />
<br />
That's all!<br />
<br />
==Want to join the development?==<br />
<br />
Everyone can join the development of the UMCD, but the most difficult task is to get in. We want to simplify this process and have written some tutorials and articles that will help you to understand the ''spirit'' of the code. Of course the Doxygen documentation is the reference and it's where you will search for class details.<br />
<br />
Anyways, your best option is to come on the IRC channel (or to contact us by email (ptalbot@hyc.io)) and explain why you want to participate, what skills you would like to acquire, or to share. So we will show you some easy task that would help you to dive into the project.<br />
<br />
==Directories==<br />
<br />
We explain here the meaning of the directories composing the UMCD project. The project is available on github in the branch [https://github.com/wesnoth/wesnoth/tree/asio_umcd asio_umcd]. See the sub-sections to know how the files are organized.<br />
<br />
===Data directory===<br />
<br />
All the WML, text or binary data files related to UMCD are stored in data/umcd/:<br />
<br />
{| style="border:1px dashed #AAA;border-collapse:collapse;"<br />
|-<br />
!style="border:1px dotted #AAA;padding:5px;"|Path (data/umcd/)<br />
!style="border:1px dotted #AAA;padding:5px;"|Description<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|.<br />
|style="border:1px dotted #AAA;padding:5px;"|All the data related to UMCD.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./database/<br />
|style="border:1px dotted #AAA;padding:5px;"|SQL script to generate and populate the database, and everything related to database in general.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./protocol_schema/<br />
|style="border:1px dotted #AAA;padding:5px;"|The WML schema of the protocol, the upload, download, list, ... requests have a specific format. It is the place to "formally" describe it.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./schema/<br />
|style="border:1px dotted #AAA;padding:5px;"|All the WML schema not related to the protocol, for example, it can describes configuration files.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./schema/types/<br />
|style="border:1px dotted #AAA;padding:5px;"|General types schema, for example string or integer.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./tests/<br />
|style="border:1px dotted #AAA;padding:5px;"|WML files related to tests, you can find example of good and bad client requests.<br />
|}<br />
<br />
===Documentation directory===<br />
<br />
Only one document is in this directory that describes the protocol. However a lot of Doxygen documentation is in the source files and this Wiki have some high level documentation.<br />
{| style="border:1px dashed #AAA;border-collapse:collapse;"<br />
|-<br />
!style="border:1px dotted #AAA;padding:5px;"|Path (doc)<br />
!style="border:1px dotted #AAA;padding:5px;"|Description<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./design/umcd/<br />
|style="border:1px dotted #AAA;padding:5px;"|There is some Latex files explaining the protocol design of UMCD, this is an important document because it contains the description of the protocol. The Latex root file is doc/design/umcd.tex (the one you need to compile).<br />
|}<br />
<br />
===Source directory===<br />
<br />
The source of all the cpp files composing the UMCD project is in src/.<br />
<br />
====umcd====<br />
<br />
The main files composing the Wesnoth UMCD project are in src/umcd/<br />
<br />
{| style="border:1px dashed #AAA;border-collapse:collapse;"<br />
|-<br />
!style="border:1px dotted #AAA;padding:5px;"|Path (src/umcd/)<br />
!style="border:1px dotted #AAA;padding:5px;"|Description<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|.<br />
|style="border:1px dotted #AAA;padding:5px;"|Files not sorted yet or that have no specific place to go. The main file is umcd.cpp.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./actions/<br />
|style="border:1px dotted #AAA;padding:5px;"|The request-specific handler are stored here, you can find details about what happens when a specific request is received.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./boost/<br />
|style="border:1px dotted #AAA;padding:5px;"|Some workaround of the Boost library, mainly for old version (for example you can add features that weren't there in old version).<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./client/<br />
|style="border:1px dotted #AAA;padding:5px;"|These files are specific to the client, it aims to be used in tests and Wesnoth client code.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./database/<br />
|style="border:1px dotted #AAA;padding:5px;"|Database-related files, it contains a connection pool and a query catalog.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./env/<br />
|style="border:1px dotted #AAA;padding:5px;"|The environment of the server, you should always try to keep dependency low with those file because any class directly using the environment won't be re-usable if the environment is not loaded. It's also a bridge between the configuration file and the classes.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./logger/<br />
|style="border:1px dotted #AAA;padding:5px;"|A thread-safe logger with specific formatting. You should use the asio_logger which is designed to work with Boost.Asio, however use it via the predefined macros.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./otl/<br />
|style="border:1px dotted #AAA;padding:5px;"|This is the header only file of the OTL library. Don't touch otlv4.h unless to update it from official source. You can configure the OTL library in the file otl.hpp.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./pod/<br />
|style="border:1px dotted #AAA;padding:5px;"|Auto-generated directory. You will only see it after the first build. It contains all the database tables in POD structure.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./protocol/<br />
|style="border:1px dotted #AAA;padding:5px;"|It contains files highly specialized for our protocol.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./protocol/client/<br />
|style="border:1px dotted #AAA;padding:5px;"|Specialization of the core files for the client side. (Nothing in there yet).<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./protocol/core/<br />
|style="border:1px dotted #AAA;padding:5px;"|Generic protocol file for both the client and server side.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./protocol/server/<br />
|style="border:1px dotted #AAA;padding:5px;"|Specialization of the core files for the server side. It also contains the action dispatcher and entry_point (request acceptor). You may want to look at these file to know how a request is dispatched.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./server/<br />
|style="border:1px dotted #AAA;padding:5px;"|Everything in there has no Wesnoth dependencies outside this directory. It's our generic server system. It also permits the transfer of data.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./server/detail/<br />
|style="border:1px dotted #AAA;padding:5px;"|Implementation details of some of the classes.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./server/multi_threaded/<br />
|style="border:1px dotted #AAA;padding:5px;"|The multi-threaded version of the server, configure it with the number of desired threads.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./server/traits/<br />
|style="border:1px dotted #AAA;padding:5px;"|Traits to add information on some types. For example, the event_slot add a slot function type to the event type. They must be specialized.<br />
|}<br />
<br />
====Test====<br />
<br />
We implemented functional tests and it uses the test files in data/umcd/tests.<br />
<br />
{| style="border:1px dashed #AAA;border-collapse:collapse;"<br />
|-<br />
!style="border:1px dotted #AAA;padding:5px;"|Path<br />
!style="border:1px dotted #AAA;padding:5px;"|Description<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|src/tests/umcd/<br />
|style="border:1px dotted #AAA;padding:5px;"|Test framework, it's designed to send test files. Data received are validated via a schema.<br />
|}<br />
<br />
====sql2cpp====<br />
<br />
This is a tool programmed with Boost.Spirit to keep in sync the database schema (in data/umcd/database/) and the classes that model the database table.<br />
<br />
{| style="border:1px dashed #AAA;border-collapse:collapse;"<br />
|-<br />
!style="border:1px dotted #AAA;padding:5px;"|Path (src/tools/code_generator/sql2cpp/)<br />
!style="border:1px dotted #AAA;padding:5px;"|Description<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|.<br />
|style="border:1px dotted #AAA;padding:5px;"|Main directory of the project. The main file is sql2cpp.cpp<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./cpp/<br />
|style="border:1px dotted #AAA;padding:5px;"|C++ generator, it generates the POD files and classes from the Abstract Syntax Tree (AST) created by the SQL parser.<br />
|-<br />
|style="border:1px dotted #AAA;padding:5px;color:#444;"|./sql/<br />
|style="border:1px dotted #AAA;padding:5px;"|SQL lexer and parser that create the SQL AST.<br />
|}<br />
<br />
==Server design==<br />
<br />
The server design is quite fixed since the proposal. Of course a lot of practical details has changed but it stayed conceptually the same. The main difference with typical server architecture you encountered in other language, such as Spring with Java, is that the whole thing is asynchronous. It's actually make a big difference in your way of thinking. You must constantly think: "Is this object, that I've just created, need to be on the heap?". Asynchronous call make the stack less useful and more things must be allocated on the heap. Keep that in mind. It also opens doors for memory (allocator) optimizations.<br />
<br />
There are just two basic diagrams to help you understand the architecture. The first is a flow diagram:<br />
<br />
http://www.hyc.io/wesnoth/doc/flow-diagram.png<br />
<br />
And the second is a class diagram:<br />
<br />
http://www.hyc.io/wesnoth/doc/class-diagram.png<br />
<br />
This class diagram is not a view of *all* the system but only a class subset that represents the best the architecture. All the class related to the event-driven programming are omitted.<br />
<br />
==For the client programmer==<br />
<br />
The client should be easy to program because much of the work is already done. You can look at the tests that show how to use the existing classes. The events are there to help you to interact with the GUI, that's mean that the GUI are function slots that you connect to the event classes. Of course, the code in umcd/ should know nothing about the GUI and this to respect a 3-tier architecture.<br />
<br />
==Event-driven programming with Boost.Asio==<br />
<br />
There is a series of articles on this paradigm, and there are directly related to the classes you will find inside this project.<br />
<br />
[http://www.hyc.io/event-driven-programming-in-cpp/ Part 1: Event-driven programming in C++]<br />
<br />
[http://www.hyc.io/event-driven-boost-asio-server/ Part 2: Event-driven Boost.Asio server]<br />
<br />
[http://www.hyc.io/event-driven-boost-asio-client/ Part 3: Event-driven Boost.Asio client]<br />
<br />
[http://www.hyc.io/event-driven-boost-asio-transfer/ Part 4: Event-driven Boost.Asio transfer]<br />
<br />
TODO: Need to archive these articles on the Wesnoth wiki.<br />
<br />
==Tasks list==<br />
<br />
===Building and installation script===<br />
<br />
As you noticed, the installation is not as simple as it could be. We would like to launch a simple command, such as make install to launch the process. A good building script would be really nice, I can think of several steps for it:<br />
<br />
* Install dependencies (such as unixODBC, Boost, ...).<br />
* Configure and install the database.<br />
* Configure and install the ODBC driver.<br />
* Create and populates the database tables.<br />
* Building the code of the UMCD.<br />
* Building the code of the UMCD tests.<br />
* Test the code.<br />
* Use this script with Travis to automate the building and tests with each commits.<br />
<br />
All these little steps should be implemented in different script files if possible. And one command/script should put together all these steps.<br />
<br />
There is room for improvement:<br />
<br />
* Support multi-platform (especially Windows)<br />
* Support multi-database configuration (you'll need to modify the OTL header too), this can be a bit tricky to automate.<br />
* Whatever you find useful.<br />
<br />
Always try to make these scripts generic enough to be re-usable with other server and/or application.<br />
<br />
===Testing===<br />
<br />
The testing is not an option and can always be improved, if you spot a bug, add a test. There are some improvements that can be made to the actual "test framework".<br />
<br />
* (easy) We currently don't check specific error, it validates even if the server returns a non-expected error. It's because we only check that the response validates the schema of the error. Don't add anything to the protocol to enable this check, use the error catalog and compares the resulting string message.<br />
* (easy) The test are always launched in the same database that is not destroyed at the end of the tests. It should be automated in a script file (create db, launch server, launch test, destroy db).<br />
* (medium) We're explicitly adding test, but it's nearly always a file with a schema (a response and an answer). We should be able to automate everything. Maybe you can describe a WML test format such as:<br />
<br />
test_name="upload UMC update with bad ID"<br />
[request]<br />
filename="data/umcd/tests/request_umc_upload/request_umc_upload_bad_id.cfg"<br />
[/request]<br />
[reply]<br />
filename="data/umcd/protocol_schema/error_reply.cfg"<br />
[error]<br />
value="bad_umc_id"<br />
[/error]<br />
[/reply]<br />
<br />
The requests, replies should be read in the order we need them to appear.<br />
<br />
Feel free to modify this format and do not forget to test the test (use a schema file to ensure your test file is correct).<br />
<br />
* (easy) Add tests, we need a lot of test, for each functionality and for each errors.<br />
<br />
===Documentation===<br />
<br />
The documentation is not only a secretary task, some code are involved in these tasks.<br />
<br />
* (medium-hard) Generate a visual representation of the database from its schema (or find a tool to do so). You can use the sql2cpp tool and add a part sql2xml if the image-schema generator waits for XML. I think about [http://code.google.com/p/wwwsqldesigner/ sqldesigner] that output great and colorful schema.<br />
* (medium) Transform the WML schema into a prettier and displayable form.<br />
* (easy) Incorporate the prettier WML schema directly inside the documentation (latex file).<br />
* (easy) Add the documentation building in the build chain. The documentation should rebuild if WML schema are modified.<br />
* (easy) Do something similar for the database schema image.<br />
<br />
===WML schema===<br />
<br />
We need WML schema that validates the most of a request.<br />
<br />
* (easy) Write WML schema for all the requests and replies.<br />
* (easy-medium) Add language type (this should be an enumeration), possibly auto-generated from the data/languages/ folder.<br />
* (medium-hard) Add regex to check the length of a field, possibly auto-generated (the database need it too, we must keep only one representation of data).<br />
* (medium) Generated the populate schema file from the language and UMC type enumerations. (Also used in the WML schema file).<br />
<br />
===Database===<br />
<br />
You can access the database with the library OTL but this one is not really well think for generic programming. We would like:<br />
<br />
* (easy) Consider all table as tuple, you can use BOOST_FUSION_ADAPT_STRUCT to adapt the pod generated.<br />
* (medium-hard) Implement database algorithms such as select, insert and delete that operate on these tuples, so we won't need to re-implement it for each classes. The difficulty is to make these algorithm generic enough to accept where, order by, ... and other SQL clauses. You can see what's SOCI offers that would simplify your life. The hardest part is to convince mordante.<br />
<br />
There is a lot of design work there and it should be really interesting.<br />
<br />
===Functionalities===<br />
<br />
This is the most important in the project, the functionalities, what the server can actually do.<br />
<br />
* (easy to hard): Nearly none of the requests (license request and a bit of the upload request) are actually implemented, but this doesn't mean that nothing is done. The design is in a good state and a lot of tools are available to re-use. In the section [http://wiki.wesnoth.org/Umcd#Event-driven_programming_with_Boost.Asio Event-driven programming], you can find a series of articles explaining facilities to send and receive data. It's easy if you only go to the simple side of the request (and you should do that first). But it can become harder if you implement new generic facilities (as the one's we speak in the [http://wiki.wesnoth.org/Umcd#Database database section]).<br />
* (easy): Create errors for each database errors that can occurs, add to them explicit messages. Add a test for each database error that can be created.<br />
* (probably hard): Add allocator system to well-chosen class, a first goal could be to allocate a chunk of memory for each client that connect. The size of the minimal memory block needed can be calculated but that wouldn't be generic. There is some resources such as in the book Modern C++ design - Chapter 4 - small object allocation.<br />
* (medium): It could be useful to administrate the server with specific administration packets, what is nice is the fact that every data input comes from a same entry (the network here). You can also bind signal to specific event, it's up to you. The goal is to control the server from the server prompt or even from outside.<br />
* (medium): Secure the transmission and more particularly when the passwords is transferred. This is one of the reason we extracted the pbl file inside the protocol itself and not in the files package. Try to open secure transmissions only with headers and to retrieve/send binary data in "normal mode".<br />
<br />
===Environment===<br />
<br />
The environment is really important and actually quite hard to implement properly. Typically the environment can be accessed from a lot of classes, and it makes think of a global variable. Of course you can put it in a singleton, it's still global and do not resolve the problem. But what is the problem?<br />
<br />
The problem of a global environment is that your classes inevitably become tightly coupled with it and they can't be re-usable anymore. For example, I first used the environment in the header_mutable_buffer class to retrieve the maximum size of a header. It makes this class impossible (or at least harder) to re-use in the client code.<br />
<br />
The first approach (and the currently implemented one's) is to consider each category of environment variable and aggregate these inside a common structure. For example you have the database variables, protocol, server, ... Each category is in fact a mono-state class. A [http://c2.com/cgi/wiki?MonostatePattern mono-state] is a class with static field that you access like if it was non-static. So it's feel like you don't manipulate a global variable, but it's still one. We need to reduce the number of class that instantiate these mono-state category.<br />
<br />
A way to do that is shown with the mono-state protocol_info and the class header_mutable_buffer. We use event to bind the static method set_header_max_size to the value store in protocol_info. Actually this is just a temporary hack to reduce coupling because I needed to use this class in the client (without loading an environment). By the way, the environment is loading by the environment_loader class and a friendship system allow it to access setter (but not private variable). So only this class can modify the state of the mono-state.<br />
<br />
Finally, what would be great is to design a system such that none of our class directly use the environment classes. They should be automatically updated whenever the environment change, probably with the event-driven programming we already introduced before.<br />
<br />
* (easy) Add event system to each classes for each field and bind them to the environment (as with the set_header_max_size() method). As a result, none of the class should use anymore the environment.<br />
* (medium - hard) Design a generic environment system to allow any user to add new categories without repeating all the code (as suggested in the first solution). A possible way could be to use tuple and Boost.Fusion with some smart macros that generates what should be generated. Also give a look to [http://www.boost.org/doc/libs/1_54_0/libs/fusion/doc/html/fusion/adapted/adapt_struct.html BOOST_FUSION_ADAPT_STRUCT].<br />
* (hard) Allow to bind a method/function with arbitrary number of category field, example:<br />
<br />
struct protocol_info{ int a; std::string b; float c; };<br />
function_callback(int, std::string);<br />
// boilerplate code...<br />
protocol_info info;<br />
info.add_event<fields<1, 2> >(function_callback);<br />
<br />
Or also to adapt a tuple inside another struct such as:<br />
<br />
struct my_protocol_info{ float a; std::string b; };<br />
function_callback(my_protocol_info);<br />
// boilerplate code...<br />
protocol_info info;<br />
info.add_event<as<my_protocol_info, 3, 2> >(function_callback);<br />
<br />
Of course all of this is added "on-the-fly". Feel free to modify interfaces, reduce the problem or augment it with your own idea.<br />
<br />
===External features===<br />
<br />
By external I mean the features that are not UMCD code but are Wesnoth code that the UMCD project uses.<br />
<br />
* (medium) Check out why the requested field in the schema are ignored. Is it a bug or a misused?<br />
* (medium to hard) Correct the memory errors generated by Valgrind when we validate a config file with the validate method of the schema_validator class. There is a lot.<br />
* (medium) The schema_validator returns message-based exceptions, it could be useful to have structure and enumeration describing the errors and let the user of the class to format them.<br />
<br />
===C++ code generator from SQL schema===<br />
<br />
* '''Where?''' {wesnoth-source-tree}/src/tools/code_generator/sql2cpp/<br />
* '''Ressources?'''<br />
<br />
The classes that contains the database table are automatically generated from the SQL schema. So we only have one representation of our database and we are automatically in synchronization.<br />
<br />
This is an aside project, not related to Wesnoth code, and I wish to keep this independence. There is a big room for improvement:<br />
<br />
* (easy-medium) The code doesn't support all the SQL grammar, of course it shouldn't, but a lot of keywords (not related to the generation) are missing, such as the index keywords. Improve the grammar!<br />
* (medium) The SQL parser is specific to the MySQL dialect, we should use a base class for standard SQL dialect and inherits from this parser to handle other dialect such as the MySQL, Oracle, ... For example the keyword AUTO_INCREMENT isn't standard.<br />
<br />
Improve the code generation:<br />
<br />
* (medium) Add CRUD operations inside the POD classes.<br />
* (hard) Maybe use Boost.Fusion to make any SQL statements generic (See [http://wiki.wesnoth.org/UMCD#Database generic features section]).<br />
* (medium) Add an option for the underlying database access library, here it's OTL, but it could be others such as SOCI.<br />
<br />
Some simple improvements:<br />
<br />
* (easy) Add a namespace options, so we can generate the code in a custom namespace, try to allow nested namespace (such as umcd::pod).<br />
* (easy) Add message when the parsing fails, currently, we need to activate the debug macro, this isn't good for simple SQL schema error.<br />
<br />
==FAQ==<br />
<br />
''Q: Why not using the Boost.Asio stream?''<br />
<br />
Beside the fact that the stream are easy to use and quite powerful they present a major drawback: they are synchronous. Making them asynchronous is possible with awful hack but the beauty of the streams then disappear.<br />
<br />
[[Category:Summer_of_Code_2013]]</div>Jmichaelhttps://wiki.wesnoth.org/index.php?title=ReplayWML&diff=56429ReplayWML2015-06-11T02:20:24Z<p>Jmichael: /* The [command] tag */</p>
<hr />
<div>{{WML Tags}}<br />
== The [command] tag ==<br />
<br />
The '''[command]''' tag is used to specify an action in a replay. It has the following attributes:<br />
<br />
* '''dependent''' if true, this command is not a lone-standing command and instead belongs to an earlier command, for example an advancement choice for an earlier attack command which issued an advancement.<br />
* '''from_side''' the side which issued the command. Only present for dependent commands.<br />
* '''sent''' used internally in networked mp, to know which commands were already sent to the other clients.<br />
The following tags are recognized for dependent=no(default):<br />
* '''[start]''': is used to initialize the replay so that generated random numbers can be saved.<br />
* '''[move]''': the player moved a unit.<br />
** '''x,y''': the path the unit walks.<br />
** '''skip_sighted''': whether the unit doesn't stop when discovering another unit, possible values are 'only_ally', 'all' or no, (default no). <br />
* '''[recruit]''': the player recruited a unit.<br />
** '''type''': the id of the type of unit recruited.<br />
** '''x''' and '''y''': the castle tile the unit is recruited on.<br />
** '''[from]'''<br />
*** '''x''' and '''y''': the keep tile the unit is recruited from.<br />
* '''[recall]''': the player recalled a unit. Same keys as [recruit], except that '''value''' is the id of the unit being recalled.<br />
* '''[attack]''': the player attacked.<br />
** '''weapon''': the index number of the weapon. Weapons are indexed by the unit designer.<br />
** '''defender_weapon''': the index number of the defenders weapon. Weapons are indexed by the unit designer, '-1' to choose the best weapon locally.<br />
** '''[source]''': the location of the attacking unit.<br />
** '''[destination]''': the location of the defending unit.<br />
* '''[disband]''': the player removes a unit from his recall list.<br />
** '''value''' the id of the removed unit.<br />
* '''[end_turn]''': the player ended his turn.<br />
* '''[init_side]''': new turn is starting for a side. This fires begin of turn events.<br />
* '''[fire_event]''': a specific event was raised<br />
** '''raise''': the name of the event<br />
** '''[source]''': the location of the event<br />
** '''[set_variable]''': set WML variable(s) before firing<br />
*** '''name''': the name of the variable<br />
*** '''value''': a string value (literal)<br />
* '''[lua_ai]''' <br />
** '''code''' the lua code that is executed.<br />
* '''[auto_shroud]''' a player toggled delayed shroud update<br />
** '''active''' whether automatic shroud updates will be active.<br />
* '''[update_shroud]''' a player manually updated shroud.<br />
<br />
Non dependent commands can have a [checkup] tag which is used to check whether the data generated in the replay matches the data generated during the original game, The [checkup] tag can contain different [result] tags whose content is different for the different actions.<br />
For [attack] commands the [result]s give information about the single hits and can have the following attributes:<br />
** '''chance''': the percent chance that the attack had to hit.<br />
** '''damage''': the amount of damage that the attack would do if it hits.<br />
** '''hits''': whether the attack hits.<br />
Or<br />
** '''dies''': whether the defender dies from the hit.<br />
<br />
The following tags are recognized for dependent=true:<br />
* '''[choose]''': the player was given an option by the scenario or for an advancement path.<br />
** '''value''': the index number of the option chosen. Index numbers are given by the scenario designer.<br />
* '''[input]''': if a lua code used [[LuaWML:Misc#wesnoth.synchronize_choice]] this tag contains the returned table.<br />
* '''[global_variable]''' a WML code used with [get_global_variable]<br />
* '''[random_seed]''' A user actions uses the rng and thus new random seed is needed.<br />
<br />
== See Also ==<br />
* [[SavefileWML]]<br />
* [[ReferenceWML]]<br />
<br />
<br />
[[Category: WML Reference]]</div>Jmichael