https://wiki.wesnoth.org/api.php?action=feedcontributions&user=Jyrkive&feedformat=atomThe Battle for Wesnoth Wiki - User contributions [en]2024-03-29T09:21:16ZUser contributionsMediaWiki 1.31.16https://wiki.wesnoth.org/index.php?title=SoftwareTesting&diff=60112SoftwareTesting2018-12-20T05:05:35Z<p>Jyrkive: Update name of test build target</p>
<hr />
<div>The Wesnoth project uses a few different software testing systems. Especially for graphical and layout features, we use the '''test scenario''', a highly decorated test scenario with a ton of graphical features that we can click around in to quickly spot problems. For non-graphical issues, we also have automated tests. We (loosely) term these '''unit tests'''. In the software development industry at large, a unit test normally refers to a small, quick test of a single class or method. Unit tests guarantee that each unit is working individually. They are particularly valuable when used with continuous integration, allowing us to catch regressions as they occur. For continuous integration, we currently use [https://travis-ci.org/wesnoth/wesnoth Travis CI].<br />
<br />
This page is meant to explain how to use the tests and how to add new tests.<br />
<br />
== Overview ==<br />
<br />
The test systems which we currently use are:<br />
<br />
=== The test scenario === <br />
<br />
Located at [https://github.com/wesnoth/wesnoth/blob/master/data/scenario-test.cfg <code>data/scenario-test.cfg</code>].<br />
<br />
This is the default scenario used when <code>wesnoth</code> is run in test mode. This can be launched by running:<br />
<br />
./wesnoth -t<br />
<br />
=== C++ unit tests (game engine and API) ===<br />
<br />
Located at [https://github.com/wesnoth/wesnoth/tree/master/src/tests <code>src/tests/</code>].<br />
<br />
These can be run by compiling the '''test suite executable''', one of the possible targets when compiling with SCons or CMake. You will need to have the '''Boost.Test''' library development files installed, and, in CMake's case, pass <code>-DENABLE_TESTS=ON</code> during configuration.<br />
<br />
# Building with SCons:<br />
scons boost_unit_tests<br />
<br />
# Building with CMake:<br />
cmake .. -DENABLE_TESTS=ON<br />
make boost_unit_tests<br />
<br />
# Running the test suite:<br />
./boost_unit_tests<br />
<br />
=== WML unit tests (WML/Lua API) ===<br />
<br />
Located at [https://github.com/wesnoth/wesnoth/tree/master/data/test/scenarios <code>data/test/scenarios/</code>].<br />
<br />
These are Wesnoth test scenarios which contain events which run at the start, perform sanity checks, and then report victory or defeat immediately depending on the results. They are not meant to be interactive. They are run using the main '''Wesnoth game executable''', by means of a script. On Unix-based systems, this script is <code>run_wml_tests</code> located at the root of the repository. See the forums for alternative scripts/a method to add this to your Visual Studio project file.<br />
<br />
./run_wml_tests<br />
<br />
== Submitting unit tests with patches ==<br />
<br />
It is not mandatory in the project to accompany any patch with unit tests, however it is very welcome and encouraged, especially for patches that includes changes or additions to the engine's internal APIs or the public WML/Lua API. For many kinds of patches unit tests aren't appropriate, but it's always good practice to find ways to test your code. Submitting tests with your code helps to ensure that your contribution will keep working in the future, and makes everything easier to maintain.<br />
<br />
Similarly, if you add new graphical features, it might be a good idea to add them to the graphical test scenario.<br />
<br />
== How to add C++ unit tests ==<br />
<br />
To add a C++ unit test, create a .cpp file in <code>src/tests/</code>. A suitable template to start with might be [https://github.com/wesnoth/wesnoth/blob/master/src/tests/test_config.cpp <code>src/tests/test_config.cpp</code>].<br />
<br />
In the simplest case, you just have to do the following things:<br />
<br />
0. Include the boost unit test framework. <br />
<br />
#include <boost/test/unit_test.hpp><br />
<br />
1. Declare a "boost auto test suite" and give it an appropriate name. An auto test suite is a bundle of tests.<br />
<br />
BOOST_AUTO_TEST_SUITE( test_my_feature )<br />
<br />
[...]<br />
<br />
BOOST_AUTO_TEST_SUITE_END()<br />
<br />
2. Declare any number of test cases ''inside'' the test suite. A test case is <code>BOOST_AUTO_TEST_CASE( ... )</code> with an identifier as the argument, followed by a C++ method body. The method body should contain statements like <code>BOOST_CHECK_EQUAL( ... , ... )</code>. All of the checks should pass when the function is executed.<br />
<br />
For an example, you might check [https://github.com/wesnoth/wesnoth/blob/master/src/tests/test_config.cpp <code>src/tests/test_config.cpp</code>].<br />
<br />
Please consult the Boost.Test docs for other styles of checks that you can use:<br />
<br />
* http://www.boost.org/doc/libs/1_44_0/libs/test/doc/html/tutorials/hello-the-testing-world.html<br />
* http://www.boost.org/doc/libs/1_44_0/libs/test/doc/html/utf.html<br />
<br />
3. Finally, add entries for your new .cpp file in <code>src/SConscript</code> and <code>src/CMakeLists.txt</code> alongside the other test .cpp files, to ensure that it is compiled as part of the test executable. You do '''not''' need to register your test cases in any other way, as the unit testing framework will execute all of the tests that it finds.<br />
<br />
<br />
If a check fails, you will generally get the suite, the test case name, and the line number and the exact expression that prompted the fault. <code>BOOST_CHECK_EQUAL</code> will also give you the mismatched values. If the test code segfaults, you might get just the test case name, or only get the test suite name.<br />
<br />
=== Pro tip ===<br />
<br />
If you have many test cases which test an object which is very complicated to construct, and you don't want to construct and destroy it repeatedly, then you should use a "global fixture", which essentially puts the object at file scope where it will be available to all the test cases in the suite when they run. It's better to use the boost fixture system than to just put it at file scope, because then the boost system knows what is going on, and can report problems more easily. (There are probably other reasons as well.)<br />
<br />
You can see an example of a global fixture being used in [https://github.com/wesnoth/wesnoth/blob/master/src/tests/test_mp_connect.cpp <code>src/tests/test_mp_connect.cpp</code>].<br />
<br />
== How to add WML unit tests ==<br />
<br />
To add a WML unit test, make a test scenario .cfg file and place it in <code>data/test/scenarios/</code>. Any wesnoth scenario using the <code>[test]</code> tag can be a valid unit test. However, there are some macros to make writing unit tests simpler. Here's a [https://github.com/wesnoth/wesnoth/blob/master/data/test/scenarios/two_plus_two.cfg minimal example].<br />
<br />
Generally speaking, a unit test is just a scenario with events which cause it to end in response to the ''start'' event, without interaction from the user. If you know how to make a Wesnoth scenario with events, and use the <code>[endlevel]</code> tag, you know everything you need to know. There are [https://github.com/wesnoth/wesnoth/blob/master/data/test/macros/wml_unit_test_macros.cfg a handful of macros] which can make it a little more succinct.<br />
<br />
Test scenarios need to be registered in the test schedule file to be used by the continuous integration systems. The test schedule file also says what is supposed to be the result of the test -- is it supposed to pass? fail? timeout? result in a corrupted replay? The test schedule file is [https://github.com/wesnoth/wesnoth/blob/master/wml_test_schedule <code>wml_test_schedule</code>] at the root of the source tree.<br />
<br />
#<br />
# Sanity checks of the unit test system<br />
#<br />
0 test_return<br />
1 test_return_fail<br />
0 test_assert<br />
1 test_assert_fail<br />
1 test_assert_fail_two<br />
2 empty_test<br />
4 break_replay_with_lua_random<br />
0 fixed_lua_random_replay_with_sync_choice<br />
0 test_end_turn<br />
...<br />
<br />
Make a line with your test scenario id. The line should begin with a number, this is the result code. <br />
<br />
The schedule includes various kinds of expected failure to ensure that the system is working. Almost surely you want your own tests to pass, and should use code 0 for that.<br />
<br />
Consult the help output of the <code>wesnoth</code> executable (in particular, the description of the <code>-u</code> switch) to see the codes:<br />
<br />
./wesnoth --help<br />
[...]<br />
0 - PASS<br />
1 - FAIL<br />
2 - FAIL (TIMEOUT)<br />
3 - FAIL (INVALID REPLAY)<br />
4 - FAIL (ERRORED REPLAY)<br />
<br />
The <code>run_wml_tests</code> script also supports adding alternate test schedules and various other options, please consult its help text for more info:<br />
<br />
./run_wml_tests -h<br />
<br />
For more general info about WML unit tests please refer to this forum thread: http://forums.wesnoth.org/viewtopic.php?t=40449<br />
<br />
== See also ==<br />
<br />
* [[DeveloperGuide]]<br />
* [[PatchSubmissionGuidelines]]<br />
<br />
[[Category:Development]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=LuaWML/Units&diff=59948LuaWML/Units2018-09-09T15:52:50Z<p>Jyrkive: In 1.15.0-dev, animator:run() now clears the animation.</p>
<hr />
<div>This page describes the [[LuaWML]] functions for handling units.<br />
<br />
A unit is a proxy table with the following fields:<br />
* '''x''', '''y''': integers (read only, read/write if the unit is not on the map. {{DevFeature1.13|11}} These are now read/write under all circumstances, including for on-map units)<br />
* '''loc''': {{DevFeature1.13|11}} shortcut to get/set both x and y at once (read/write). Setting x and y individually would result in two moves, and there's the possibility that the intermediate move fails if the hex is occupied by another unit. In general, note that moving a unit by changing the proxy unit's coordinates does not work if the goal hex is occupied (it is not executed), so it is necessary to check if the hex is available first.<br />
* '''side''': integer (read/write)<br />
* '''id''': string (read only)<br />
* '''type''': string (read only)<br />
* '''name''': translatable string (read only)<br />
* '''cost''' {{DevFeature1.13|10}}: integer (read)<br />
* '''max_hitpoints''', '''max_experience''', '''max_moves''': integers (read only)<br />
* '''max_attacks''': integer (read only)<br />
* '''attacks_left''': integer (read/write) Setting below 0 is limited to 0.<br />
* '''extra_recruit''': table (read/write)<br />
* '''advances_to''': table (read/write)<br />
* '''hitpoints''', '''experience''': integer (read/write)<br />
* '''moves''': integer (read/write)<br />
* '''level''': {{DevFeature1.13|5}} integer (read/write)<br />
* '''resting''': boolean (read/write)<br />
* '''hidden''': boolean (read/write)<br />
* '''petrified''', '''canrecruit''': booleans (read only)<br />
* '''role''', '''facing''': strings (read/write)<br />
* '''status''': proxy associative table (read only, read/write fields), provides fields like [[SingleUnitWML#Unit_State|poisoned, slowed, petrified, uncovered, guardian, unhealable, invulnerable]]<br />
* '''image_mods''': string (read only)<br />
* '''upkeep''' {{DevFeature1.13|5}}: one of 'loyal', 'full' or a number (read/writre)<br />
* '''variables''': proxy associative table (read only, read/write fields, including ''variables.__cfg''), only toplevel named fields are proxied. {{DevFeature1.13|2}} subcontainers can be accessed by using the usual variable syntax: <syntaxhighlight inline lang='lua'>unit.variables["a.b.c[6].d"]</syntaxhighlight><br />
* '''attacks''': {{DevFeature1.13|0}}an object to access the units attacks, you can use the attacks index or the attacks name to index an attack. every attack has the following members:<br />
** '''description''': translatable string (read/write)<br />
** '''name''': string (read)<br />
** '''type''': string (read/write)<br />
** '''range''': string (read/write)<br />
** '''damage''': number(read/write)<br />
** '''number''': number(read/write)<br />
** '''movement_used''': number(read/write)<br />
** '''attack_weight''': number(read/write)<br />
** '''defense_weight''': number(read/write)<br />
** '''specials''' wml table(read/write)<br />
* '''valid''': string or nil (read only)<br />
* '''advancements''': {{DevFeature1.13|2}} an array of wml tables (read/write)<br />
* '''__cfg''': WML table (dump) ([[SingleUnitWML]])<br />
* {{DevFeature1.13|2}} The following fields are unit methods synonymous to one of the functions described on this page:<br />
** '''[[#wesnoth.match_unit|matches]]'''<br />
** '''[[#wesnoth.put_recall_unit|to_recall]]'''<br />
** '''[[#wesnoth.put_unit|to_map]]'''<br />
** '''[[#wesnoth.erase_unit|erase]]'''<br />
** '''[[#wesnoth.copy_unit|clone]]'''<br />
** '''[[#wesnoth.extract_unit|extract]]'''<br />
** '''[[#wesnoth.advance_unit|advance]]'''<br />
** '''[[#wesnoth.add_modification|add_modification]]'''<br />
** '''[[#wesnoth.remove_modification|remove_modification]]'''<br />
** '''[[#wesnoth.unit_resistance|resistance]]'''<br />
** '''[[#wesnoth.unit_defense|defense]]'''<br />
** '''[[#wesnoth.unit_movement_cost|movement]]'''<br />
** '''[[#wesnoth.unit_vision_cost|vision]]'''<br />
** '''[[#wesnoth.unit_jamming_cost|jamming]]'''<br />
** '''[[#wesnoth.unit_ability|ability]]'''<br />
** '''[[#wesnoth.transform_unit|transform]]'''<br />
The metatable of these proxy tables appears as '''"unit"'''.<br />
<br />
A unit can be either visible on the map ([[#wesnoth.get_units]], [[#wesnoth.put_unit]]), or on a recall list ([[#wesnoth.get_recall_units]], [[#wesnoth.put_recall_unit]]), or private to the Lua code ([[#wesnoth.create_unit]], [[#wesnoth.copy_unit]], [[#wesnoth.extract_unit]]). The Lua code has complete control over the private units; they will not be modified unless accessed through the proxy unit. Units on the map and on the recall lists, however, can be modified by the user, the engine, WML, independently of the Lua code. In particular, if a unit is killed, any further use of the proxy unit will cause an error. For units on the map, the proxy unit is valid as long as there is a unit on the map that has the same "underlying_id" WML field as the original one. The behavior is similar for units on the recall lists. The ''valid'' field reflects the unit availability by returning '''"map"''', '''"recall"''', '''"private"''', or ''nil''. The latter value is used for units that were removed (e.g. killed). In that case, the ''valid'' field is the only one that can be read without causing an error.<br />
<br />
The term "proxy", here in particular "proxy unit", means that the variable retrieved in the lua code (with get_units for example) is an accessor (reference) to the C++ object which represents that unit. This is very different from unit variables obtained by [store_unit] in wml. The fields marked as "writable" above can be modified without the need to use put_unit afterwards. This same reason explains that modifications to the unit from outside the lua code (like [kill] invalidating the proxy unit) have immediate effect on the lua code's proxy unit variable (with the exception of private proxy units).<br />
<br />
<br />
==== wesnoth.get_units ====<br />
<br />
* '''wesnoth.get_units(''filter'')'''<br />
* {{DevFeature1.13|12}} '''wesnoth.get_units(''filter'', ''fake_location'')'''<br />
* {{DevFeature1.13|12}} '''wesnoth.get_units(''filter'', ''other_unit'')'''<br />
<br />
Returns an array of all the units on the map matching the WML filter passed as the first argument. See [[StandardUnitFilter]] for details about filters. If a second unit is passed, it can be referenced via the $other_unit variable in the main filter as well as via the "other" variable in WFL formulas used in the main filter. If a location is passed, the filter is run as if the unit were at that location (rather than its real location). This affects things such as [filter_adjacent] and ability_active, and should work even for a unit on the recall list.<br />
<br />
<syntaxhighlight lang='lua'><br />
local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true }<br />
local name_of_leader = leaders_on_side_two[1].name<br />
</syntaxhighlight><br />
<br />
==== wesnoth.get_unit ====<br />
<br />
* '''wesnoth.get_unit(''x'', ''y'')'''<br />
* '''wesnoth.get_unit(''underlying_id'')'''<br />
<br />
Returns the unit at the given location or with the given underlying ID.<br />
<br />
<syntaxhighlight lang='lua'><br />
local args = ...<br />
local unit = wesnoth.get_unit(args.x1, args.y1)<br />
</syntaxhighlight><br />
<br />
==== wesnoth.match_unit ====<br />
<br />
* '''wesnoth.match_unit(''unit'', ''filter'')'''<br />
* {{DevFeature1.13|2}} '''wesnoth.match_unit(''unit'', ''filter'', ''other_unit'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':matches(''filter'', [''other_unit''])'''<br />
* {{DevFeature1.13|2}} '''wesnoth.match_unit(''unit'', ''filter'', ''location'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':matches(''filter'', [''location''])'''<br />
<br />
Returns true if the given unit matches the WML filter passed as the second argument. If ''other_unit'' is specified, it is used for the ''$other_unit'' auto-stored variable in the filter. Otherwise, this variable is not stored for the filter. If an extra ''location'' is specified, the filter matches as if the unit were at that location.<br />
<br />
<syntaxhighlight lang='lua'><br />
assert(unit.canrecruit == wesnoth.match_unit(unit, { canrecruit = true }))<br />
</syntaxhighlight><br />
<br />
==== wesnoth.put_unit ====<br />
<br />
* '''wesnoth.put_unit(''unit'')'''<br />
* '''wesnoth.put_unit(''x'', ''y'', ''unit'')'''<br />
* '''wesnoth.put_unit(''x'', ''y'')'''<br />
* {{DevFeature1.13|2}} '''wesnoth.put_unit(''unit'', ''x'', ''y'')''' -- The above two forms are also deprecated.<br />
* {{DevFeature1.13|2}} '''''unit'':to_map([''x'', ''y''])<br />
<br />
Places a unit on the map. This unit is described either by a WML table or by a proxy unit. Coordinates can be passed as the first two arguments, otherwise the table is expected to have two fields '''x''' and '''y''', which indicate where the unit will be placed. If the function is called with coordinates only, the unit on the map at the given coordinates is removed instead. {{DevFeature1.13|2}} This use is now deprecated; use wesnoth.erase_unit instead.<br />
<br />
<syntaxhighlight lang='lua'><br />
-- create a unit with random traits, then erase it<br />
wesnoth.put_unit(17, 42, { type = "Elvish Lady" })<br />
wesnoth.put_unit(17, 42)<br />
</syntaxhighlight><br />
<br />
When the argument is a proxy unit, no duplicate is created. In particular, if the unit was private or on a recall list, it no longer is; and if the unit was on the map, it has been moved to the new location. Note: passing a WML table is just a shortcut for calling [[#wesnoth.create_unit]] and then putting the resulting unit on the map.<br />
<br />
<syntaxhighlight lang='lua'><br />
-- move the leader back to the top-left corner<br />
wesnoth.put_unit(1, 1, wesnoth.get_units({ canrecruit = true })[1])<br />
</syntaxhighlight><br />
<br />
==== wesnoth.erase_unit ====<br />
<br />
{{DevFeature1.13|2}}<br />
<br />
* '''wesnoth.erase_unit(''unit'')'''<br />
* '''wesnoth.erase_unit(''x'', ''y'')'''<br />
* '''''unit'':erase()'''<br />
<br />
Erases a unit from the map. After calling this on a unit, the unit is no longer valid.<br />
<br />
==== wesnoth.get_recall_units ====<br />
<br />
* '''wesnoth.get_recall_units()'''<br />
<br />
Returns an array of all the units on the recall lists matching the WML filter passed as the first argument.<br />
<br />
==== wesnoth.put_recall_unit ====<br />
<br />
* '''wesnoth.put_recall_unit(''unit'', [''side''])'''<br />
* {{DevFeature1.13|2}} '''''unit'':to_recall([''side''])'''<br />
<br />
Places a unit on a recall list. This unit is described either by a WML table or by a proxy unit. The side of the recall list is given by the second argument, or by the side of the unit if missing.<br />
<br />
<syntaxhighlight lang='lua'><br />
-- put the unit at location 17,42 on the recall list for side 2<br />
wesnoth.put_recall_unit(wesnoth.get_units({ x= 17, y = 42 })[1], 2)<br />
</syntaxhighlight><br />
<br />
When the argument is a proxy unit, no duplicate is created. In particular, if the unit was private or on the map, it no longer is. Note: passing a WML table is just a shortcut for calling [[#wesnoth.create_unit]] and then putting the resulting unit on a recall list.<br />
<br />
==== wesnoth.create_unit ====<br />
<br />
* '''wesnoth.create_unit(''unit_info'')'''<br />
<br />
Creates a private unit from a WML table.<br />
<br />
<syntaxhighlight lang='lua'><br />
local u = wesnoth.create_unit { type = "White Mage", gender = "female" }<br />
</syntaxhighlight><br />
<br />
==== wesnoth.copy_unit ====<br />
<br />
* '''wesnoth.copy_unit(''unit'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':clone()'''<br />
<br />
Creates a private unit from another unit.<br />
<br />
<syntaxhighlight lang='lua'><br />
-- extract a unit from the map<br />
local u = wesnoth.copy_unit(wesnoth.get_units({ type = "Thug" })[1])<br />
wesnoth.erase_unit(u.x, u.y)<br />
-- u is still valid at this point<br />
</syntaxhighlight><br />
<br />
==== wesnoth.extract_unit ====<br />
<br />
* '''wesnoth.extract_unit(''unit'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':extract()'''<br />
<br />
Removes a unit from the map or from a recall list and makes it private.<br />
<br />
<syntaxhighlight lang='lua'><br />
-- remove all the units from the recall list of side 1 and put them in a WML container<br />
local l = {}<br />
for i,u in ipairs(wesnoth.get_recall_units { side = 1 }) do<br />
wesnoth.extract_unit(u)<br />
table.insert(l, u.__cfg)<br />
end<br />
helper.set_variable_array("player_recall_list", l)<br />
</syntaxhighlight><br />
<br />
Note: if the unit is on the map, it is just a shortcut for calling [[#wesnoth.copy_unit]] and then [[#wesnoth.put_unit]] without a unit. It is, however, the only way for removing a unit from a recall list without putting it on the map.<br />
<br />
<br />
==== wesnoth.advance_unit ====<br />
<br />
* '''wesnoth.advance_unit(''unit'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':advance()'''<br />
<br />
{{DevFeature1.13|0}} Advances the unit (and shows the advance unit dialog if needed) if the unit has enough xp. This function should be called after modifying the units experience directly. A similar function is called by wesnoth internally after unit combat. The second argument is a boodean value that specifies whether the advancement should be animated. The third agrument is a boodean value that specifies whether advancement related events should be fired.<br />
<br />
<br />
This function only works for units on the map.<br />
<br />
This function can also trigger multiple advancements if the unit has enough xp.<br />
<br />
==== wesnoth.add_modification ====<br />
<br />
* '''wesnoth.add_modification(''unit'', ''type'', ''effects'', [''write_to_mods''])'''<br />
* {{DevFeature1.13|2}} '''''unit'':add_modification(''type'', ''effects'', [''write_to_mods''])'''<br />
<br />
Modifies a given unit. It needs to be a proxy unit. The second argument is the type of the modification (one of "trait", "object", or "advance"). The option "advance" applies effects as if the unit would advance (e.g. AMLA effects). The third argument is a WML table describing the effect, so mostly containing '''[effect]''' children. See [[EffectWML]] for details about effects.<br />
<br />
{{DevFeature1.13|2}} In 1.13.2 and later, the "advance" type is replaced with "advancement", to match the equivalent tag in [[UnitTypeWML|[unit_type]]]. Also, it takes a fourth argument which, if false, causes it to not write the modification tag to the unit's [modifications] (as would be done with an [object] with no_write=true).<br />
<br />
<syntaxhighlight lang='lua'><br />
local u = wesnoth.get_units { canrecruit = true }[1]<br />
wesnoth.add_modification(u, "object", { { "effect", { apply_to = "image_mod", replace = "RC(red>blue)" } } })<br />
</syntaxhighlight><br />
<br />
==== wesnoth.remove_modifications ====<br />
<br />
* {{DevFeature1.13|?}} '''wesnoth.remove_modifications(''unit'', ''cfg'' [, ''type''])'''<br />
* {{DevFeature1.13|?}} '''''unit'':remove_modifications(''cfg'' [, ''type''])'''<br />
<br />
Modifies a given unit. The unit needs to be a proxy unit. The second argument is a filter for the modifications to remove. It takes the same syntax as [[FilterWML#Filtering_on_WML_data|[filter_wml]]]; all matching modifications will be removed. The third argument is the type (tag name) of the modifications to search for; it defaults to <tt>"object"</tt>, but you can also pass <tt>"trait"</tt> or <tt>"advancement"</tt>.<br />
<br />
<syntaxhighlight lang='lua'><br />
local u = wesnoth.get_units { canrecruit = true }[1]<br />
wesnoth.remove_modifications(u, { id = your_object_id })<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_resistance ====<br />
<br />
* '''wesnoth.unit_resistance(''unit'', ''damage_type'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':resistance(''damage_type'')'''<br />
<br />
Returns the resistance of a unit against an attack type. (Note: it is a WML resistance. So the higher it is, the weaker the unit is.) The third argument indicates whether the unit is the attacker. Last arguments are the coordinates of an optional map location (for the purpose of taking abilities into account).<br />
<br />
<syntaxhighlight lang='lua'><br />
local fire_resistance = 100 - wesnoth.unit_resistance(u, "fire")<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_defense ====<br />
<br />
* '''wesnoth.unit_defense(''unit'', ''terrain_code'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':defense(''terrain_code'')'''<br />
<br />
Returns the defense of a unit on a particular terrain. (Note: it is a WML defense. So the higher it is, the weaker the unit is.)<br />
<br />
<syntaxhighlight lang='lua'><br />
local flat_defense = 100 - wesnoth.unit_defense(u, "Gt")<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_movement_cost ====<br />
<br />
* '''wesnoth.unit_movement_cost(''unit'', ''terrain_code'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':movement(''terrain_code'')'''<br />
<br />
Returns the movement cost of a unit on a particular terrain.<br />
<br />
<syntaxhighlight lang='lua'><br />
local move_cost = wesnoth.unit_movement_cost(u, "Gt")<br />
<syntaxhighlight><br />
<br />
==== wesnoth.unit_vision_cost ====<br />
<br />
* '''wesnoth.unit_vision_cost(''unit'', ''terrain_code'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':vision(''terrain_code'')'''<br />
<br />
Returns the vision cost of a unit on a particular terrain.<br />
<br />
<syntaxhighlight lang='lua'><br />
local see_cost = wesnoth.unit_vision_cost(u, "Gt")<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_jamming_cost ====<br />
<br />
* '''wesnoth.unit_jamming_cost(''unit'', ''terrain_code'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':jamming(''terrain_code'')'''<br />
<br />
Returns the jamming cost of a unit on a particular terrain.<br />
<br />
<syntaxhighlight lang='lua'><br />
local jam_cost = wesnoth.unit_jamming_cost(u, "Gt")<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_ability ====<br />
<br />
* '''wesnoth.unit_ability(''unit'', ''ability_tag'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':ability(''ability_tag'')'''<br />
<br />
Returns true if the unit is currently under effect by an ability with this given TAG NAME. This means that the ability could be owned by the unit itself, or by an adjacent unit.<br />
<br />
<syntaxhighlight lang='lua'><br />
function has_teleport(u)<br />
return wesnoth.unit_ability(u, "teleport")<br />
end<br />
</syntaxhighlight><br />
<br />
==== wesnoth.unit_types ====<br />
<br />
This is not a function but a read-only table indexed by unit type ids. Its elements are proxy tables with these fields:<br />
<br />
* '''id''': string<br />
* '''name''': translatable string (read only)<br />
* '''max_moves''', '''max_experience''', '''max_hitpoints''', '''level''', '''cost''': integers (read only)<br />
* '''abilities''': array of ability keys (strings), e.g. {"curing", "regenerates"}<br />
* {{DevFeature1.13|11}} '''advances_to''': array of unit types to which unit can advance<br />
* {{DevFeature1.13|11}} '''advances_from''': array of unit types from which unit can advance. Note: this is OOS-unsafe in Multiplayer games. Different clients may have additional Era-s with units upgradable to this unit type.<br />
* '''__cfg''': WML table (dump), see [[UnitTypeWML]]<br />
<br />
The metatable of these proxy tables appears as '''"unit type"'''.<br />
<br />
<syntaxhighlight lang='lua'><br />
local lich_cost = wesnoth.unit_types["Ancient Lich"].cost<br />
</syntaxhighlight><br />
<br />
Note that different clients have different set of available units in a Multiplayer game. It is OOS-unsafe to e.g. count the number of units.<br />
Presuming correctly written add-ons, it is still safe to e.g. access any given unit or its properties.<br />
<br />
==== wesnoth.races ====<br />
<br />
This is not a function but a table indexed by race ids. Its elements are proxy tables for all races the engine knows about.<br />
known fields of each element:<br />
* '''id''': string<br />
* '''description''', '''name''', '''plural_name''' (translatable strings)<br />
* '''num_traits''' (integer)<br />
* '''ignore_global_traits''' (boolean)<br />
* '''undead_variation''' (string)<br />
(all read only)<br />
* '''__cfg''': WML table (dump)<br />
<br />
<syntaxhighlight lang='lua'><br />
wesnoth.message(tostring(wesnoth.races["lizard"].name))<br />
</syntaxhighlight><br />
<br />
==== wesnoth.get_traits ====<br />
<br />
* '''wesnoth.get_traits()'''<br />
<br />
Returns a table with named fields (trait id strings) holding the wml tables defining the traits. arguments: none. All global traits the engine knows about, race-specific traits are not included.<br />
Known fields and subtags of each element are the ones which were given in the wml definition of the [[SingleUnitWML|trait]].<br />
wesnoth.message(tostring(wesnoth.get_traits().strong.male_name))<br />
<br />
==== wesnoth.simulate_combat ====<br />
<br />
* '''wesnoth.simulate_combat(''attacker'', [''attacker_weapon_index''], ''defender'', [''defender_weapon_index''])'''<br />
<br />
Computes the hitpoint distribution and status chance after a combat between two units. The first unit is the attacker; it does not have to be on the map, though its location should be meaningful. The second unit is the defender; it has to be on the map.<br />
<br />
Optional integers can be passed after each unit to select a particular weapon, otherwise the "best" one is selected. When giving the weapon, the parameter is the weapon number (integer, starting at 1) and not an element from the table returned by helper.child_range(att, "attack").<br />
<br />
<syntaxhighlight lang='lua'><br />
local function display_stats(n, t)<br />
wesnoth.message(string.format(<br />
"Chance for the %s\n to be slowed: %f,\n to be poisoned: %f,\n to die: %f.\nAverage HP: %f.",<br />
n, t.slowed, t.poisoned, t.hp_chance[0], t.average_hp))<br />
end<br />
local att_stats, def_stats = wesnoth.simulate_combat(att, att_weapon, def, def_weapon)<br />
display_stats("attacker", att_stats)<br />
display_stats("defender", def_stats)<br />
</syntaxhighlight><br />
<br />
Returns 2 additional tables which contain information about the weapons and the effect of single hits with these keys: num_blows, damage, chance_to_hit, poisons, slows, petrifies, plagues, plague_type, backstabs, rounds, firststrike, drains, drain_constant, drain_percent, attack_num, name. <br />
Name is the wml name not the description. If there is no weapon, then name will be nil<br />
<br />
<syntaxhighlight lang='lua'><br />
local att_stats, def_stats, att_weapon, def_weapon = wesnoth.simulate_combat(attacker, att_weapon_number, defender)<br />
wesnoth.message(string.format(<br />
"The attack %s should be countered with %s, which does %d damage, has %d%% chance to hit and forces %d attack rounds due to its berserk ability.",<br />
att_weapon.name, def_weapon.name or "no weapon", def_weapon.damage, def_weapon.chance_to_hit, def_weapon.rounds))<br />
</syntaxhighlight><br />
<br />
==== wesnoth.transform_unit ====<br />
<br />
* '''wesnoth.transform_unit(''unit'', ''to_type'')'''<br />
* {{DevFeature1.13|2}} '''''unit'':transform(''to_type'')'''<br />
<br />
Changes the type of a unit and adjust attributes accordingly. Note that hit points are only changed if necessary to accommodate the new maximum hit points. Poison is automatically removed if the transformed unit is immune.<br />
<br />
<syntaxhighlight lang='lua'><br />
local ev = wesnoth.current.event_context<br />
local u = wesnoth.get_units{x=ev.x1, y=ev.y1}[1]<br />
wesnoth.transform_unit(u, "Spearman")<br />
-- If a full heal is desired:<br />
u.hitpoints = u.max_hitpoints<br />
u.status.poisoned = false<br />
</syntaxhighlight><br />
<br />
==== wesnoth.add_known_unit ====<br />
<br />
* {{DevFeature1.13|10}} '''wesnoth.add_known_unit(''unit_type_id'')'''<br />
<br />
adds the unit type with the given id to the list of known units (so that they appear in the help)<br />
<br />
==== wesnoth.create_animator ====<br />
<br />
{{DevFeature1.13|7}}<br />
<br />
* '''wesnoth.create_animator()'''<br />
<br />
Returns an object that can be used to set up and run an animation. The object has three methods:<br />
<br />
* '''animator:run()'''<br />
<br />
Runs the animation. {{DevFeature1.15|0}} Implicitly clears the animator.<br />
<br />
* '''animator:clear()'''<br />
<br />
Clears any units previously added to the animation.<br />
<br />
* '''animator:add(''unit'', ''flag'', ''hits'', ''params'')'''<br />
<br />
Adds a unit to the animation. The ''flag'' specifies which animation to play, and the ''hits'' parameter is required for attack animations to specify which variant of the animation to play. Possibly keys in ''params'' are:<br />
<br />
* '''facing''': A location. The animation will be played with the unit facing that location.<br />
* '''value''': Either a number or a list of two numbers. Use this to pass ''value'' and/or ''value_second'' to default animations that use them.<br />
* '''with_bars''': Whether to show HP bars and such while the animation plays.<br />
* '''text''': Text to float as the animation plays.<br />
* '''color''': Color of the floating text - a list of red, green, blue.<br />
* '''primary''': The primary weapon to use for the animation. Must be a Lua unit attack proxy.<br />
* '''secondary''': The secondary weapon to use for the animation.<br />
<br />
Normal usage would be to create it, call '''add''' one or more times, then call '''run'''.<br />
<br />
==== wesnoth.effects ====<br />
<br />
{{DevFeature1.13|2}}<br />
<br />
This table contains the implementation of custom [[EffectWML|[effect]]]s. Each value is a function that takes a unit and the effect config. Note that the default effects defined by the Wesnoth engine are not in this table. <br />
<br />
<syntaxhighlight lang='lua'><br />
function wesnoth.effects.min_resistance(u, cfg)<br />
local resistance_new = {}<br />
local resistance_old = helper.parsed(helper.get_child(cfg, "resistance"))<br />
for k,v in pairs(resistance_old) do<br />
if type(k) == "string" and type(v) == "number" and wesnoth.unit_resistance(u, k) >= v then<br />
resistance_new[k] = v<br />
end<br />
end<br />
--important: use wesnoth.add_modification(..., false) so that the function will only execute the effects of that object and not store the object in the unit.<br />
wesnoth.add_modification(u, "object", {<br />
T.effect {<br />
apply_to = "resistance",<br />
replace = true,<br />
T.resistance (resistance_new),<br />
},<br />
}, false)<br />
end<br />
</syntaxhighlight><br />
<br />
The code above adds a new <code>min_resistance</code> effect that will set the resistances to specific values if they are currently below that value. It can then be used like this (for example, in [[DirectActionsWML#.5Bobject.5D|[object]]]):<br />
<br />
<syntaxhighlight lang='wml'><br />
[effect]<br />
apply_to=min_resistance<br />
[resistance]<br />
cold=50<br />
[/resistance]<br />
[/effect]<br />
</syntaxhighlight><br />
<br />
Note that because currently all Lua code is executed after [unit]s in [side] are created, it is currently not possible to use these effects in [unit]s in [side]<br />
<br />
{{DevFeature1.13|5}}<br />
<br />
Built-in effects are now present in the <code>wesnoth.effects</code> table and can be called by custom effects or by other Lua code. They take the same two arguments that a custom effect function does - the unit, and the effect WML.<br />
<br />
In addition, you can now specify description modifiers to be used if a custom effect is placed in a <code>[trait]</code> tag. Instead of setting a function as the effect, you set a table with a <code>__call</code> metafunction which does what the function would have done. The table can then have an additional <code>__descr</code> metafunction which updates descriptions as necessary. The built-in effects all use this structure. This metafunction takes the same arguments as the regular effect function, but should not modify the unit. Instead, it returns a string to be appended to the trait's effect description.<br />
<br />
[[Category: Lua Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=LuaAPI&diff=59933LuaAPI2018-08-26T17:51:50Z<p>Jyrkive: Added a note that loadstring() is blocked in 1.15</p>
<hr />
<div><br />
'''''Note: This page is is a work-in-progress and only reflects the current stable version. For more complete documentation for 1.14, see [[LuaWML#Interface_to_the_engine_and_helper_functions|here]].'''''<br />
<br />
All Lua API functionality is available in one of several global module tables, as well as some global functions which form the basic Lua API. The following core Lua modules are available and are documented on the Lua website:<br />
<br />
* [http://www.lua.org/manual/5.3/manual.html#6.1 basic] — except <tt>dofile</tt>, <tt>require</tt>, and <tt>loadfile</tt>; also note that <tt>print</tt> redirects to the Lua console interface rather than standard output; you can use <tt>std_print</tt> if you need the default Lua behavior. Starting from 1.15, <tt>loadstring</tt> (deprecated in Lua in favor of <tt>load</tt>) isn't available either.<br />
* [http://www.lua.org/manual/5.3/manual.html#6.2 coroutine]<br />
* [http://www.lua.org/manual/5.3/manual.html#6.4 string]<br />
* [http://www.lua.org/manual/5.3/manual.html#6.5 utf8]<br />
* [http://www.lua.org/manual/5.3/manual.html#6.6 table]<br />
* [http://www.lua.org/manual/5.3/manual.html#6.7 math] — note that <tt>math.random</tt> is unsafe for MP usage; wesnoth provides a separate MP-safe random function<br />
* [http://www.lua.org/manual/5.3/manual.html#6.9 os] — only the <tt>clock</tt>, <tt>date</tt>, <tt>time</tt>, and <tt>difftime</tt> functions are available (but keep in mind that they are not MP safe)<br />
* [http://www.lua.org/manual/5.3/manual.html#6.10 debug] — only the <tt>traceback</tt> function is available<br />
<br />
Wesnoth also provides several modules of its own, some of which are loaded by default while others require you to load them as needed.<br />
<br />
=== Conventions used in this manual ===<br />
<br />
On this page and any page linked from it, parameters will be shown in italic type, while literal names will be shown in bold type. Return values will be indicated by a right arrow (&rarr;) followed by a list of names in italics. Optional portions will be enclosed in square brackets.<br />
<br />
For example, consider the following hypothetical definitions:<br />
<br />
* '''wesnoth.some_function'''(''some_parameter'' [, ''some_optional_parameter'']) &rarr; ''first_return_value'', ''second_return_value''<br />
This is a function that returns multiple values and has optional parameters<br />
* ''some_value'':'''some_method'''(''some_parameter'') &rarr; ''some_new_value''<br />
* ''some_value''.'''some_member''' &rarr; ''result''<br />
<br />
For member variables, the right arrow may be replaced with a left arrow (&larr;) if it is write-only, or a double-ended arrow (&harr;) if it is read-write.<br />
<br />
Many functions and variables are only available in certain contexts. Currently, there are three possible contexts - plugins, map generators, and the game. Most Lua will run in the game kernel. If a function is only available in certain contexts, that will be indicated in parentheses at the beginning of the definition. For example:<br />
<br />
* (game only) '''wesnoth.game_exclusive_function'''(''parameter'') &rarr; ''result''<br />
<br />
<!-- TODO: Consider implementing image icons for these? --><br />
<br />
Functions that take a variable number of parameters will use the string "..." to indicate this. For example:<br />
<br />
* '''wesnoth.variadic_function'''(''required_param'' [, ...])<br />
<br />
=== Wesnoth Modules ===<br />
<br />
* [[LuaAPI/wesnoth|Core]] - The <tt>wesnoth</tt> module contains most of the core Wesnoth API. It is always loaded and available.<br />
* [[LuaAPI/wml|WML]] - The <tt>wml</tt> module contains functions for working with WML tables.</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=CampaignServerWML&diff=59819CampaignServerWML2018-06-24T10:02:51Z<p>Jyrkive: The add-on client sets increase_downloads to false when downloading updates</p>
<hr />
<div><div class="tright"> __TOC__ </div><br />
<br />
This page describes the WML commands exchanged between the add-ons server (<b>campaignd</b>) and clients.<br />
<br />
For historical reasons, both the server software's name and the WML tags which are part of its communication protocol make exclusive reference to "campaigns" instead of "add-ons". However, in practice there is no real difference between any kind of add-ons as far as the server is concerned.<br />
<br />
== Common responses ==<br />
<br />
These are WML messages sent by the server in response to any of the commands listed below.<br />
<br />
=== [message]: Informational messages ===<br />
<br />
This response is typically sent to confirm that an operation succeeded.<br />
<br />
* '''[message]'''<br />
** '''message''': String containing a message for the client to display to the user.<br />
<br />
=== [error]: Error messages ===<br />
<br />
This response is sent when an operation has failed for any reason, whether it be a user error or an issue with the server. It is also sent upon receiving an unrecognized request.<br />
<br />
* '''[error]'''<br />
** '''message''': String containing an error message for the client to display to the user.<br />
** '''extra_data''': String containing additional information for the client to display. This is often a log of some kind (such a list of illegal filenames found during an upload request), so a client should be prepared for this to be an overlong string that might not completely fit in the screen.<br />
<br />
== Archive format ==<br />
<br />
'''[upload]''' and '''[request_campaign]''' both use WML to describe archives containing the directory structure and files for each add-on. The archive format supports only regular files and directories, and file contents need to be encoded in a special fashion so that they can be serialized as WML for network transfers and disk storage.<br />
<br />
Binary data cannot contain null characters, as well as certain special characters. In order to build escape sequences for them, a single-byte value of <code>0x01</code> is used followed by the actual byte value incremented by 1. For example, the byte sequence <code>0x01 0x01</code> is used to represent the null character <code>0x00</code>, and <code>0x01 0xFF</code> represents the special character <code>0xFE</code>.<br />
<br />
As of Wesnoth 1.14, the following byte values need to be escaped:<br />
<br />
<code>0x00</code> (null character), <code>0x01</code> (escape character), <code>0x0D</code> (carriage return), <code>0xFE</code> (used by the WML parser to encode <code>\xFEline</code> and <code>\xFEtextdomain</code> directives).<br />
<br />
=== [file]: Regular file ===<br />
<br />
* '''name''': Filename (not a relative or absolute path).<br />
* '''contents''': The contents of a file encoded as binary data as described above.<br />
<br />
=== [dir]: Directory ===<br />
<br />
* '''name''': Directory name (not a relative or absolute path).<br />
* '''[file]''', '''[dir]''': Regular files and subdirectories under this directory. The syntax for '''[dir]''' is recursive.<br />
<br />
== [request_campaign_list]: Listing server contents ==<br />
<br />
This request is used to retrieve a list of add-ons available on the server and some overview information about them.<br />
<br />
There are a few attributes both in the request and response WML that use timestamps. These are expected to be in secodns relative <br />
<br />
=== Request ===<br />
<br />
* '''[request_campaign_list]'''<br />
** '''name''': If specified and not empty, return information only for a single add-on matching this name.<br />
** '''language''': Return information only about add-ons that appear to be translated into this language. These names will normally be POSIX locale names. Typically you will want to either use a two letter language code (e.g. <code>de</code>) or a two letter language code followed by a two character region code (e.g. <code>pt_BR</code>). Note that <code>pt</code> will not match <code>pt_BR</code>.<br />
** '''times_relative_to''': <code>now</code> means that '''before''' and '''after''' below are understood to be measured in seconds relative to the time when the request is processed by campaignd. If unset or set to any other value, '''before''' and '''after''' are assumed to be in seconds relative to the [https://en.wikipedia.org/wiki/Unix_epoch Unix epoch].<br />
** '''after''': Only select add-ons last updated after the indicated time. For example, to select add-ons last updated after Mon Oct 10 21:42:41 2005 UTC, you could leave '''times_relative_to''' unset and specify <syntaxhighlight lang="wml" inline>after=1128980561</syntaxhighlight>.<br />
** '''before''': Only select add-ons last updated before the indicated time. For example, to select add-ons last updated over a day ago, you could specify <syntaxhighlight lang="wml" inline>times_relative_to="now"</syntaxhighlight> and <syntaxhighlight lang="wml" inline>before=-86400</syntaxhighlight>.<br />
<br />
=== Response ===<br />
<br />
* '''timestamp''': The time in seconds relative to the [https://en.wikipedia.org/wiki/Unix_epoch Unix epoch] that the add-ons server reported when generating the response. You shouldn't count on this as a guarantee that no new add-ons will appear with previous update times. This could be used to detect significant clock skew or possibly used as an approximate time for how far you need to look back for updated campaigns.<br />
* '''[campaigns]'''<br />
** '''[campaign]'''<br />
*** '''author''', '''dependencies''', '''description''', '''icon''', '''tags''', '''title''', '''translate''', '''type''', '''version''': See [[PblWML]].<br />
*** '''downloads''': The number of times the add-on (including previous versions) has been downloaded directly from the add-ons server. This does not include updates, which are handled by the official clients using the '''increase_downloads''' option to avoid bumping the downloads count; nor does it include downloads from the add-ons server web, which is handled by the web server independently.<br />
*** '''feedback_url''': URL to the add-on's feedback URL specified by the author in the [[PblWML#.5Bfeedback.5D|pbl [feedback] tag]] and formatted according to the server's own rules. For the current add-ons server, this is a forum thread URL in the format <code><nowiki>https://r.wesnoth.org/t<TOPIC_ID></nowiki></code>.<br />
*** '''filename''': The name of the file used to store the add-on archive on the server (currently the same as '''name''').<br />
*** '''name''': The name of the add-on. Note this is not the title and shouldn't have spaces in it. This corresponds to the add-on directory name on the client side.<br />
*** '''original_timestamp''': When the first version of the add-on was uploaded. If the add-on was previously deleted from the server, this will be the time of the first reupload after the last deletion.<br />
*** '''size''': The on-disk size of the add-on pack in bytes on the server. If the add-on pack is stored in a compressed format, this will be the compressed size, not the uncompressed size. Because the network format currently used to send the file uses the same compression and nearly identical structure, this also happens to be almost exactly the same size transferred over the network when downloading the add-on.<br />
*** '''timestamp''': When the current version of the add-on was uploaded.<br />
*** '''uploads''': Number of uploads, including the initial upload.<br />
*** '''[translation]'''<br />
**** '''language''': The name of a locale that this add-on appears to have been at least partially translated into. The following heuristic is used when an add-on is uploaded to extract a list of languages:<br>The list of directories in the uploaded campaign is recursively searched. If a directory named <code>LC_MESSAGES</code> is found then the name of the object containing this '''[dir]''' (normally the parent directory, but could be the add-on name if someone did something silly) is added to the list of translations. Directories named <code>LC_MESSAGES</code> are not searched for further matches.<br>The normal naming convention is to use POSIX locale names. This will usually be a two letter language code (e.g. <code>de</code>) or a two letter language code followed by a two letter region code (e.g. <code>pt_BR</code>). Since <code>en_US</code> is the base language, this language will not be listed as an available translation.<br />
<br />
== [request_campaign]: Downloading an add-on ==<br />
<br />
This command is used to download the specified add-on from the server.<br />
<br />
=== Request ===<br />
<br />
* '''[request_campaign]'''<br />
** '''name''': The name of the add-on. Note this is not the user-visible title and should not have spaces in it.<br />
** '''increase_downloads''': A boolean value specifying whether the download counter for the add-on should be increased or not. The game's built-in add-ons client sets this to ''false'' when downloading updates for already installed add-ons.<br />
<br />
=== Response ===<br />
<br />
* '''author''', '''description''', '''icon''', '''tags''', '''title''', '''type''', '''version''': See [[PblWML]].<br />
* '''campaign_name''': The name of the add-on. Note this is not the title and shouldn't have spaces in it. This corresponds to the add-on directory name on the client side.<br />
* '''name''': This field will always be empty. Client code would treat it as a directory name if it were not empty.<br />
* '''original_timestamp''': When the first version of the add-on was uploaded. If the add-on was previously deleted from the server, this will be the time of the first reupload after the last deletion.<br />
* '''timestamp''': When the current version of the add-on was uploaded.<br />
* '''[file]''', '''[dir]''': See [[#Archive format|archive format]].<br />
<br />
== [upload]: Uploading an add-on ==<br />
<br />
This command is used to upload a new add-on or an updated version of an existing one to the server.<br />
<br />
=== Request ===<br />
<br />
* '''[upload]'''<br />
** '''author''', '''dependencies''', '''description''', '''email''', '''icon''', '''passphrase''', '''tags''', '''title''', '''translate''', '''type''', '''[feedback]''': See [[PblWML]].<br />
** '''[data]'''<br />
*** '''[file]''', '''[dir]''': See [[#Archive format|archive format]].<br />
<br />
=== Response ===<br />
<br />
See [[#Common responses|Common responses]].<br />
<br />
== [change_passphrase]: Changing a passphrase ==<br />
<br />
This command is used to change the passphrase for an add-on. Currently the game does not have a user interface to make use of this function. However, the Python-based <code class="noframe">wesnoth_addon_manager</code> client included under <code class="noframe">data/tools</code> has a <code>--change-passphrase</code> switch that accomplishes the same purpose.<br />
<br />
=== Request ===<br />
<br />
* '''[change_passphrase]'''<br />
** '''name''': The name of the add-on.<br />
** '''passphrase''': The old passphrase.<br />
** '''new_passphrase''': The new passphrase.<br />
<br />
=== Response ===<br />
<br />
See [[#Common responses|Common responses]]. The <code>[message]</code> response confirms a successful passphrase change.<br />
<br />
== [delete]: Deleting an add-on ==<br />
<br />
This command is used to delete an existing add-on from the server.<br />
<br />
=== Request ===<br />
<br />
* '''[delete]'''<br />
** '''name''': The name of the add-on to delete.<br />
** '''passphrase''': The passphrase for the add-on. It must match the passphrase on record in order for the operation to succeed.<br />
<br />
=== Response ===<br />
<br />
See [[#Common responses|Common responses]]. The <code>[message]</code> response confirms the successful deletion of the add-on.<br />
<br />
== [request_terms]: Requesting upload terms ==<br />
<br />
Retrieves the upload terms that users must accept before uploading add-ons to the server. Wesnoth.org requires add-ons be licensed under the GNU GPL v2 or later, with assets allowed to be licensed under the same license or a Creative Commons license. See [[Wesnoth:Copyrights]] for more information.<br />
<br />
This action never fails.<br />
<br />
=== Request ===<br />
* '''[request_terms]'''<br />
<br />
=== Response ===<br />
<br />
See [[#Common responses|Common responses]]. The <code>[message]</code> response contains the upload terms.<br />
<br />
== See Also ==<br />
<br />
* [[Guide_to_UMC_Content]]<br />
* [[PblWML]]<br />
<br />
[[Category:WML Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=DeveloperGuide&diff=59778DeveloperGuide2018-06-11T13:44:41Z<p>Jyrkive: Removed reference to players_changelog</p>
<hr />
<div>This page describes guidelines developers with push access to the Wesnoth repository should follow. Most items apply to prospective contributors as well, in addition to the [[PatchSubmissionGuidelines|patch submission guidelines]].<br />
<br />
__TOC__<br />
<br />
== General ==<br />
<br />
{{NewDevsGoHereSidebox}}<br />
<br />
* [[WesnothRepository#Push_access|Set up developer access to the repository]], if applicable.<br />
* Subscribe to the [https://mail.gna.org/listinfo/wesnoth-dev wesnoth-dev mailing list], wherein major announcements are made and project-changing discussions take place.<br />
* Participate in the #wesnoth-dev IRC channel on irc.freenode.net to coordinate with other developers. Bots report commits and regression testing status, and other developers may ask you questions or provide feedback.<br />
<br />
== Commits ==<br />
<br />
* Any given branch should <b>compile without warnings</b> and run after every commit -- we use [https://travis-ci.org/wesnoth/wesnoth Travis CI], which will report any build failures on master and maintenance branches (1.12, 1.14, etc.) to the #wesnoth-dev IRC channel.<br />
* All <b>unit tests</b> should pass after commit -- Travis will also test this. To run the C++ unit tests on your own machine, compile the "test" executable and run it. To run the WML unit tests, run <code>run_wml_tests -u</code> in the checkout root. If a test times out spuriously on Travis, you can restart the build by <br />
*# Clicking on the link posted to IRC by the Travis bot:<br />
*#: 20180608 07:25:20-!- travis-ci [~travis-ci@ec2-54-204-112-72.compute-1.amazonaws.com] has joined #wesnoth-dev<br />
*#: 20180608 07:25:21< travis-ci> wesnoth/wesnoth#18505 (1.14 - 8722221 : Charles Dang): The build was broken.<br />
*#: 20180608 07:25:21< travis-ci> Build details : https://travis-ci.org/wesnoth/wesnoth/builds/389584322<br />
*#: 20180608 07:25:21-!- travis-ci [~travis-ci@ec2-54-204-112-72.compute-1.amazonaws.com] has left #wesnoth-dev []<br />
*# Clicking on the [http://i.imgur.com/mxwVKJP.png restart button] on the Travis website.<br />
* Do <b>not</b> disable or ignore unit tests without a very good reason!<br />
* A few small commits are better than a single large commit (which is hard to review), so when possible split it in working parts with info about where you are going<br />
* <b>Always</b> review your changes, both before committing locally and before pushing upstream (see [[WesnothRepository#Reviewing your changes]]).<br />
* <b>Never</b> use the "force" option when pushing to the upstream Wesnoth repositories (see [[WesnothRepository#Force-pushing_policy]]).<br />
<br />
=== Changelog and release notes ===<br />
<br />
We provide a <b>changelog</b> at the root of the Wesnoth distribution and Git checkouts. It contains all changes, except for those that are only relevant to mainline developers and which are not expected to impact players or creators in any way.<br />
<br />
It is your responsibility to update the changelog as you see fit for every commit. For large changes spread over long series of commits, you will probably prefer to commit your changelog additions in a separate last commit to avoid or mitigate conflicts when merging upstream.<br />
<br />
Important changes that might be expected to inconvenience or confuse players (including those building from source) or content creators, major bug fixes, and noteworthy feature additions for a future release should be mentioned and explained in <b>RELEASE_NOTES</b> as well, so that the release team can include this information in official announcements. See the contents of the file for instructions.<br />
<br />
=== Commit messages ===<br />
<br />
A good commit should not only contain good changes, but also include a helpful description of them for other developers, people tracking regressions, project maintainers, and even yourself in the future. There are many style guides on the Web describing best practices for documenting your Git commits.<br />
<br />
For Wesnoth in particular, the general consensus is that contributors should adhere to the following rules:<br />
<br />
* Every commit message should begin with a self-contained <b>summary/subject line</b> no more than 72 characters long. This is the first (and often the only) line someone using browsing tools will see. Like with email subjects, this line should not have a trailing stop.<br />
* If your message needs more than a leading summary line, separate it from the rest with a blank line.<br />
* Subsequent lines should also be no more than 72 characters long. Use blank lines to separate paragraphs and list items.<br />
* When working on a mainline campaign/scenario, start the commit message with its acronym. For example <code>HttT: </code> when working on HttT in general and <code>HttT S03: </code> if the commit only applies to the third scenario of HttT (see below).<br />
* Likewise, if you work on the code of a specific component, you may want to use it as a prefix. That is e.g., <code>gui2: </code> or <code>wesnothd: </code>.<br />
* If you are performing branch merges, Git may include a list of conflicted paths in the commit message template. Edit this out; it ceases to be interesting after the conflicts have been resolved.<br />
* If you need to mention commit SHA1 hashes (e.g. in revert messages), make sure to use hashes from the upstream repository that are unlikely to change due to a future local or remote merge or rebase operation.<br />
<br />
There are a few additional points to keep in mind for the wording of the contents:<br />
<br />
* Include "bug #1234" somewhere in your commit message if it addresses a bug or feature request from the Wesnoth bug tracker.<br />
* Use the project's [[Glossary#Wesnothian_Acronyms|standard abbreviations]] for campaigns and gameplay concepts, like HttT for <cite>Heir to the Throne</cite>, ZoC for "Zone of Control", etc. If referring to a particular campaign or scenario in your commit subject, you should use this convention: "HttT S01: Made Galdrad less suicidal"<br />
<br />
== Dependencies ==<br />
* Any changes to Wesnoth's build and run-time dependencies must be discussed on the developers mailing list first.<br />
* Update the CMake and SCons recipes accordingly.<br />
* Changes must be mentioned in the RELEASE_NOTES file, so they can be included in the forum announcement for the next release, and in the emails to packagers.<br />
<br />
== Bugs management ==<br />
* Change Status field of fixed bugs to "Fixed" after pushing upstream<br />
* Change Open/Closed field of fixed bugs to "Closed" after a release including the relevant commits has been published. See [[ReportingBugs#Bug_protocol]] for details.<br />
* Check regularly if there are new bugs relevant to your code and, if any, assign them to you.<br />
<br />
== See also ==<br />
* [[WesnothRepository]]<br />
* [[PatchSubmissionGuidelines]]<br />
* [[SoftwareTesting]]<br />
* [[Project]]<br />
<br />
[[Category:Development]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=DirectActionsWML&diff=59712DirectActionsWML2018-05-28T19:18:21Z<p>Jyrkive: Undo revision 59711 by Jyrkive (talk)</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. {{DevFeature1.13|2}} Alternatively, a number, defining the bonus multiple (1.0 meaning full).<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 yes the gold will be added to the starting gold the next scenario, if no the next scenario will start with the amount of the current scenario (after taxes) or the minimum in the next scenario. Default is no.<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 />
Creates a unit (either on the map, on a recall list, or into a variable for later use.) For syntax see [[SingleUnitWML]].<br />
* {{Short Note:Predefined Macro|GENERIC_UNIT}}<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 />
* '''[secondary_unit]''': {{DevFeature1.13|?}} If present and show=yes, a matching unit will be chosen and their recruiting animation played.<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 hex to teleport to. If that hex is occupied, the closest unoccupied hex will be used instead.<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 lose 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 />
* '''male_text''', '''female_text''': {{DevFeature1.13|2}} (translatable) gender-specific versions of the above<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=yes) if yes 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 />
* '''side_name''': {{DevFeature1.13|?}} a translatable string representing the side leader's description.<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. {{DevFeature1.13|?}} If this contains an '''ai_algorithm''', the AI parameters will be reset to those of the indicated AI before adding any additional parameters included in the tag. In other words, this allows replacing the AI config rather than appending to it.<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 />
* '''share_vision''': change both the above at the same time<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 />
* '''defeat_condition''' {{DevFeature1.13|0}}: When the side is considered defeated (see [[SideWML|[side]]]).<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''' (default 'no'): if 'yes', displays the unit dying (fading away). {{DevFeature1.13|8}} If '''[secondary_unit]''' is given, also plays the victory animation of that unit.<br />
* '''fire_event''' (default 'no'): 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 ({{DevFeature1.13|8}} or if it has a victory animation and animate=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 />
* '''[primary_attack]''', '''[secondary_attack]''' {{DevFeature1.13|8}} The attacks to use for matching the animation. Useful for example on the wose, whose death animation depends on the damage type it was killed by.<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 [[Modifying_AI_Components#The_.5Bmodify_ai.5D_Tag|Modifying AI Components]] 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 />
* '''[object]''', '''[trait]''', {{DevFeature1.13|5}} '''[advancement]''' - The given modifications will be immediately applied to all units matching the filter.<br />
** '''delayed_variable_substitution''' {{DevFeature1.13|5}} (boolean yes|no, default no): If set to "yes", the wml block contained in this [object], [trait], or [advancement] is not variable-substituted at execution time of the event containing this [modify_unit]. You need this for any effect that uses variable substitution or when using [effect][filter] with a $this_unit. {{DevFeature1.13|9}} This is no longer needed when adding ABILITY_TELEPORT, ABILITY_LEADERSHIP or SPECIAL_BACKSTAB.<br />
* '''[effect]''' {{DevFeature1.13|6}} Applies the effect directly to the unit.<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 [[CommandMode|inspect command]]. Cannot remove things or add/alter unit animations. Subtags with the same name must be written in the correct order to match them with the tag they are supposed to modify. Note that keys will be processed in arbitrary order, which may cause problems if you use formulas that depend on other formulas. To work around this you may need to use the tag twice with the same filter.<br />
example usage (see also the test scenario):<br />
<syntaxhighlight lang='wml'><br />
[modify_unit]<br />
[filter]<br />
x,y=38,6<br />
[/filter]<br />
hitpoints=10<br />
{TRAIT_HEALTHY}<br />
[/modify_unit]<br />
</syntaxhighlight><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 />
=== [transform_unit] ===<br />
Transforms every unit on the map 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) allows the item to be removed later. By default, an object with a defined ID can only be picked up once per scenario, even if it is removed later or first_time_only=no is set for the event. You can remove this restriction by setting take_only_once=no. For filtering objects, it might be simpler to use a custom key such as item_id.<br />
* '''take_only_once''': (default yes) {{DevFeature1.13|6}} If set to "no", the object's ID does not prevent it from being taken more than once.<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 for any effect that uses variable substitution or when using [effect][filter] with a $this_unit. {{DevFeature1.13|9}} This is no longer needed when adding ABILITY_TELEPORT, ABILITY_LEADERSHIP or SPECIAL_BACKSTAB.<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". {{DevFeature1.13|2}} If no description is provided, this defaults to yes, but can still be overridden.<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 />
<br />
=== [remove_object] ===<br />
<br />
{{DevFeature1.13|6}}<br />
<br />
Removes an object from matching units.<br />
<br />
* [[StandardUnitFilter]]: All units matching the filter have matching objects removed<br />
* '''object_id''': The id of the object to be removed.<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 />
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 because it might cause OOS: <br />
* [message] with [option]s<br />
* [get_global_variable]<br />
* wesnoth.synchronize_choice<br />
<br />
While in 1.13 using '''[allow_undo]''' together with those things won't give you a guaranteed OOS, there are some non-obvious situations where it will, for example assume the following event:<br />
<br />
[event]<br />
name="moveto"<br />
[message]<br />
message = "message"<br />
[option]<br />
message = "option 1"<br />
[command]<br />
[/command]<br />
[/option]<br />
[option]<br />
message = "option 2"<br />
[command]<br />
[/command]<br />
[/option]<br />
[/message]<br />
[allow_undo]<br />
[/allow_undo]<br />
[/event]<br />
<br />
It will cause OOS when the message is undone: since the event is already executed (erased) on one client only , the clients will disagree about how many choices happen during the next moveto action.<br />
<br />
=== [on_undo] ===<br />
{{DevFeature1.13|2}}<br />
Contains commands to execute when the player undoes the action which triggered the parent event.<br />
*'''delayed_variable_substitution''' {{DevFeature1.13|5}}: ''yes/no, default no (always no before 1.13.5)'' As in [[EventWML]], specifies whether to perform variable substitution when the parent event is run, or when the contents are run. If delayed substitution is used, [[SyntaxWML#Automatically_Stored_Variables|automatically stored variables]] from the parent event context are available, but may occasionally have unexpected values. (In particular, $unit.x and $unit.y may not have the expected value when undoing a move event, though $x1 and $y1 should be correct.)<br />
<br />
Note:<br />
It is not clear where whether the actionwml in [on_undo] in exceuted before or after the action is undone. Also, specially for enter/leave_hex events the units position when executing the [on_undo] code is usually different than when executing the original event. The reccomended way to wokr around these issues is to refer to the unit by is instead of position and store all other needed information variables as 'upvalues'. You can also move the actual undo code to an external event. For example<br />
[event]<br />
name="undo_blah"<br />
first_time_only=no<br />
[store_unit]<br />
id="$moved_unit_id"<br />
[/store_unit]<br />
... do undo stuff stuff<br />
[/event]<br />
<br />
...<br />
... in some other event<br />
[on_undo]<br />
# store ''upvalues<br />
{VARIABLE moved_unit_id $unit.id}<br />
# call actual undo handler<br />
[fire_event]<br />
name = "undo_blah"<br />
[/fire_event]<br />
[on_undo]<br />
<br />
=== [on_redo] ===<br />
{{DevFeature1.13|2}}<br />
Same as [on_undo], except executes the commands on redo. Note that the parent event is not triggered again on a redo.<br />
<br />
{{DevFeature1.13|8}} [on_redo] is deprecated and has no effect anymore.<br />
<br />
Note that [on_redo] is not guaranteed to be called when redoing an action, the engine might also decide to just fire the original events again.<br />
<br />
=== [cancel_action] ===<br />
Although Wesnoth 1.12 does not have this tag, it is the default behavior of {{tag|EventWML|enter_hex}}/{{tag|EventWML|leave_hex}} events in that version.<br />
<br />
{{DevFeature1.13|9}} In this version, [cancel_action] is recognised, but has no effect (a bug).<br />
<br />
{{DevFeature1.13|11}}<br />
In an {{tag|EventWML|enter_hex}}/{{tag|EventWML|leave_hex}} event, interrupt the movement, leaving the unit where it is. This is intended to be used with an event that gives the player new information, to let the player choose whether to change their plans. For example, if the player has commanded a unit to move from (1,1) to (3,3) and attack a unit on (4,4); then a [cancel_action] inside an [enter_hex] event on (2,2) would make the unit stop on (2,2). A [cancel_action] inside an [enter_hex] on (3,3) would let the player choose whether to attack.<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 less 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 yes) 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 />
* '''[time]''': one or more tags describing the new schedule, see [[TimeWML]].<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 />
=== [remove_time_area] ===<br />
<br />
{{DevFeature1.13|2}}<br />
<br />
This is a syntactic shortcut for [time_area] remove=.<br />
* '''id''': Comma-separated list of time area ids to remove.<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 />
* '''map_file''': {{DevFeature1.13|?}} Path to a Wesnoth map file; can be used instead of '''map'''. The file will be loaded when the tag is executed, rather than being embedded wholesale in the preprocessed WML.<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).<br />
<br />
'''Behavior Change as of Wesnoth 1.13.6:''' Vision is now possible (and enabled by default) through tunnels and allied units on the exit hex do not block a tunnel by default any more. This is done in order for moves through tunnels to be consistent with other moves. The previous behavior can still be accomplished by using the new optional keys listed below.<br />
<br />
* '''[filter]''': (required) [[StandardUnitFilter]] the units which can use the tunnel. Leave empty for "all units".<br />
* '''[source]''': (required) [[StandardLocationFilter]] the source hex(es).<br />
* '''[target]''': (required) [[StandardLocationFilter]] the target hex(es).<br />
* '''id''': (optional) identifier for the tunnel, to allow removing.<br />
* '''remove''': (boolean, default: no) If yes, removes all defined tunnels with the same ID (then only id= is necessary).<br />
* '''bidirectional''': (boolean, default: yes) If yes, creates also a tunnel in the other direction. <br />
* '''always_visible''': (boolean, default: no) If yes, the possible movement of enemies under fog can be seen.<br />
* '''allow_vision''': (boolean, default: yes) {{DevFeature1.13|6}} If no, vision through a tunnel is not possible. Note that in that case the tunnel cannot be used if the tunnel exit is under shroud (which previously was ''always'' the case).<br />
* '''pass_allied_units''': (boolean, default: yes) {{DevFeature1.13|6}} If no, allied (including own) units on the exit hex block a tunnel.<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] {{DevFeature1.13|12}} This has been removed and is replaced with [custom_command]<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>Jyrkivehttps://wiki.wesnoth.org/index.php?title=DirectActionsWML&diff=59711DirectActionsWML2018-05-28T08:04:23Z<p>Jyrkive: Rewrote the bit about unstoring <= 0 HP units to match the planned behavior</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. {{DevFeature1.13|2}} Alternatively, a number, defining the bonus multiple (1.0 meaning full).<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 yes the gold will be added to the starting gold the next scenario, if no the next scenario will start with the amount of the current scenario (after taxes) or the minimum in the next scenario. Default is no.<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 />
Creates a unit (either on the map, on a recall list, or into a variable for later use.) For syntax see [[SingleUnitWML]].<br />
* {{Short Note:Predefined Macro|GENERIC_UNIT}}<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 />
* '''[secondary_unit]''': {{DevFeature1.13|?}} If present and show=yes, a matching unit will be chosen and their recruiting animation played.<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 hex to teleport to. If that hex is occupied, the closest unoccupied hex will be used instead.<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 lose 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 />
* '''male_text''', '''female_text''': {{DevFeature1.13|2}} (translatable) gender-specific versions of the above<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=yes) if yes 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 />
<br />
There are two situations where a unit can be unstored with negative (or zero) hit points. You can do it when the unit is already dying, such as in the last_breath event. Alternatively, you can unstore a unit with negative HP as long as its HP is positive at the end of the event (e.g. if you heal it) and you don't trigger a nested event while its HP is still negative. In all other situations, unstoring a unit with negative HP is an error because the engine can't handle existence of such units.<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 />
* '''side_name''': {{DevFeature1.13|?}} a translatable string representing the side leader's description.<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. {{DevFeature1.13|?}} If this contains an '''ai_algorithm''', the AI parameters will be reset to those of the indicated AI before adding any additional parameters included in the tag. In other words, this allows replacing the AI config rather than appending to it.<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 />
* '''share_vision''': change both the above at the same time<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 />
* '''defeat_condition''' {{DevFeature1.13|0}}: When the side is considered defeated (see [[SideWML|[side]]]).<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''' (default 'no'): if 'yes', displays the unit dying (fading away). {{DevFeature1.13|8}} If '''[secondary_unit]''' is given, also plays the victory animation of that unit.<br />
* '''fire_event''' (default 'no'): 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 ({{DevFeature1.13|8}} or if it has a victory animation and animate=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 />
* '''[primary_attack]''', '''[secondary_attack]''' {{DevFeature1.13|8}} The attacks to use for matching the animation. Useful for example on the wose, whose death animation depends on the damage type it was killed by.<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 [[Modifying_AI_Components#The_.5Bmodify_ai.5D_Tag|Modifying AI Components]] 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 />
* '''[object]''', '''[trait]''', {{DevFeature1.13|5}} '''[advancement]''' - The given modifications will be immediately applied to all units matching the filter.<br />
** '''delayed_variable_substitution''' {{DevFeature1.13|5}} (boolean yes|no, default no): If set to "yes", the wml block contained in this [object], [trait], or [advancement] is not variable-substituted at execution time of the event containing this [modify_unit]. You need this for any effect that uses variable substitution or when using [effect][filter] with a $this_unit. {{DevFeature1.13|9}} This is no longer needed when adding ABILITY_TELEPORT, ABILITY_LEADERSHIP or SPECIAL_BACKSTAB.<br />
* '''[effect]''' {{DevFeature1.13|6}} Applies the effect directly to the unit.<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 [[CommandMode|inspect command]]. Cannot remove things or add/alter unit animations. Subtags with the same name must be written in the correct order to match them with the tag they are supposed to modify. Note that keys will be processed in arbitrary order, which may cause problems if you use formulas that depend on other formulas. To work around this you may need to use the tag twice with the same filter.<br />
example usage (see also the test scenario):<br />
<syntaxhighlight lang='wml'><br />
[modify_unit]<br />
[filter]<br />
x,y=38,6<br />
[/filter]<br />
hitpoints=10<br />
{TRAIT_HEALTHY}<br />
[/modify_unit]<br />
</syntaxhighlight><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 />
=== [transform_unit] ===<br />
Transforms every unit on the map 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) allows the item to be removed later. By default, an object with a defined ID can only be picked up once per scenario, even if it is removed later or first_time_only=no is set for the event. You can remove this restriction by setting take_only_once=no. For filtering objects, it might be simpler to use a custom key such as item_id.<br />
* '''take_only_once''': (default yes) {{DevFeature1.13|6}} If set to "no", the object's ID does not prevent it from being taken more than once.<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 for any effect that uses variable substitution or when using [effect][filter] with a $this_unit. {{DevFeature1.13|9}} This is no longer needed when adding ABILITY_TELEPORT, ABILITY_LEADERSHIP or SPECIAL_BACKSTAB.<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". {{DevFeature1.13|2}} If no description is provided, this defaults to yes, but can still be overridden.<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 />
<br />
=== [remove_object] ===<br />
<br />
{{DevFeature1.13|6}}<br />
<br />
Removes an object from matching units.<br />
<br />
* [[StandardUnitFilter]]: All units matching the filter have matching objects removed<br />
* '''object_id''': The id of the object to be removed.<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 />
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 because it might cause OOS: <br />
* [message] with [option]s<br />
* [get_global_variable]<br />
* wesnoth.synchronize_choice<br />
<br />
While in 1.13 using '''[allow_undo]''' together with those things won't give you a guaranteed OOS, there are some non-obvious situations where it will, for example assume the following event:<br />
<br />
[event]<br />
name="moveto"<br />
[message]<br />
message = "message"<br />
[option]<br />
message = "option 1"<br />
[command]<br />
[/command]<br />
[/option]<br />
[option]<br />
message = "option 2"<br />
[command]<br />
[/command]<br />
[/option]<br />
[/message]<br />
[allow_undo]<br />
[/allow_undo]<br />
[/event]<br />
<br />
It will cause OOS when the message is undone: since the event is already executed (erased) on one client only , the clients will disagree about how many choices happen during the next moveto action.<br />
<br />
=== [on_undo] ===<br />
{{DevFeature1.13|2}}<br />
Contains commands to execute when the player undoes the action which triggered the parent event.<br />
*'''delayed_variable_substitution''' {{DevFeature1.13|5}}: ''yes/no, default no (always no before 1.13.5)'' As in [[EventWML]], specifies whether to perform variable substitution when the parent event is run, or when the contents are run. If delayed substitution is used, [[SyntaxWML#Automatically_Stored_Variables|automatically stored variables]] from the parent event context are available, but may occasionally have unexpected values. (In particular, $unit.x and $unit.y may not have the expected value when undoing a move event, though $x1 and $y1 should be correct.)<br />
<br />
Note:<br />
It is not clear where whether the actionwml in [on_undo] in exceuted before or after the action is undone. Also, specially for enter/leave_hex events the units position when executing the [on_undo] code is usually different than when executing the original event. The reccomended way to wokr around these issues is to refer to the unit by is instead of position and store all other needed information variables as 'upvalues'. You can also move the actual undo code to an external event. For example<br />
[event]<br />
name="undo_blah"<br />
first_time_only=no<br />
[store_unit]<br />
id="$moved_unit_id"<br />
[/store_unit]<br />
... do undo stuff stuff<br />
[/event]<br />
<br />
...<br />
... in some other event<br />
[on_undo]<br />
# store ''upvalues<br />
{VARIABLE moved_unit_id $unit.id}<br />
# call actual undo handler<br />
[fire_event]<br />
name = "undo_blah"<br />
[/fire_event]<br />
[on_undo]<br />
<br />
=== [on_redo] ===<br />
{{DevFeature1.13|2}}<br />
Same as [on_undo], except executes the commands on redo. Note that the parent event is not triggered again on a redo.<br />
<br />
{{DevFeature1.13|8}} [on_redo] is deprecated and has no effect anymore.<br />
<br />
Note that [on_redo] is not guaranteed to be called when redoing an action, the engine might also decide to just fire the original events again.<br />
<br />
=== [cancel_action] ===<br />
Although Wesnoth 1.12 does not have this tag, it is the default behavior of {{tag|EventWML|enter_hex}}/{{tag|EventWML|leave_hex}} events in that version.<br />
<br />
{{DevFeature1.13|9}} In this version, [cancel_action] is recognised, but has no effect (a bug).<br />
<br />
{{DevFeature1.13|11}}<br />
In an {{tag|EventWML|enter_hex}}/{{tag|EventWML|leave_hex}} event, interrupt the movement, leaving the unit where it is. This is intended to be used with an event that gives the player new information, to let the player choose whether to change their plans. For example, if the player has commanded a unit to move from (1,1) to (3,3) and attack a unit on (4,4); then a [cancel_action] inside an [enter_hex] event on (2,2) would make the unit stop on (2,2). A [cancel_action] inside an [enter_hex] on (3,3) would let the player choose whether to attack.<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 less 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 yes) 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 />
* '''[time]''': one or more tags describing the new schedule, see [[TimeWML]].<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 />
=== [remove_time_area] ===<br />
<br />
{{DevFeature1.13|2}}<br />
<br />
This is a syntactic shortcut for [time_area] remove=.<br />
* '''id''': Comma-separated list of time area ids to remove.<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 />
* '''map_file''': {{DevFeature1.13|?}} Path to a Wesnoth map file; can be used instead of '''map'''. The file will be loaded when the tag is executed, rather than being embedded wholesale in the preprocessed WML.<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).<br />
<br />
'''Behavior Change as of Wesnoth 1.13.6:''' Vision is now possible (and enabled by default) through tunnels and allied units on the exit hex do not block a tunnel by default any more. This is done in order for moves through tunnels to be consistent with other moves. The previous behavior can still be accomplished by using the new optional keys listed below.<br />
<br />
* '''[filter]''': (required) [[StandardUnitFilter]] the units which can use the tunnel. Leave empty for "all units".<br />
* '''[source]''': (required) [[StandardLocationFilter]] the source hex(es).<br />
* '''[target]''': (required) [[StandardLocationFilter]] the target hex(es).<br />
* '''id''': (optional) identifier for the tunnel, to allow removing.<br />
* '''remove''': (boolean, default: no) If yes, removes all defined tunnels with the same ID (then only id= is necessary).<br />
* '''bidirectional''': (boolean, default: yes) If yes, creates also a tunnel in the other direction. <br />
* '''always_visible''': (boolean, default: no) If yes, the possible movement of enemies under fog can be seen.<br />
* '''allow_vision''': (boolean, default: yes) {{DevFeature1.13|6}} If no, vision through a tunnel is not possible. Note that in that case the tunnel cannot be used if the tunnel exit is under shroud (which previously was ''always'' the case).<br />
* '''pass_allied_units''': (boolean, default: yes) {{DevFeature1.13|6}} If no, allied (including own) units on the exit hex block a tunnel.<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] {{DevFeature1.13|12}} This has been removed and is replaced with [custom_command]<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>Jyrkivehttps://wiki.wesnoth.org/index.php?title=UsefulLinks&diff=59420UsefulLinks2018-04-14T18:45:19Z<p>Jyrkive: Removed Flattr link, it 404's</p>
<hr />
<div>__TOC__<br />
{|class=wikitable<br />
!colspan=2 style='text-align:left'|<br />
=== Battle for Wesnoth ===<br />
|-<br />
|Home Page || http://www.wesnoth.org/<br />
|-<br />
|GitHub Project Page || https://github.com/wesnoth/<br />
|-<br />
|Page on TheLinuxGameTome || http://happypenguin.org/show?Battle%20for%20Wesnoth<br />
|-<br />
|Freshmeat entry || http://freshmeat.net/projects/wesnoth/<br />
|-<br />
|Changelog || http://changelog.wesnoth.org/ [http://stable-changelog.wesnoth.org stable]<br />
|-<br />
|This wiki || http://wiki.wesnoth.org/<br />
|-<br />
|The manual || http://manual.wesnoth.org/<br />
|-<br />
|Translation statistics || http://gettext.wesnoth.org/<br />
|-<br />
|Add-on server web interface || http://addonlist.wesnoth.org/ [http://stable-addons.wesnoth.org stable] [http://dev-addons.wesnoth.org dev]<br />
|-<br />
|Multiplayer server statistics || http://wesnothd.wesnoth.org/<br />
|-<br />
|Multiplayer replays || http://replays.wesnoth.org<br />
|-<br />
|Unit advancement tree || http://units.wesnoth.org/<br />
|-<br />
|Forum for users and developers || http://forums.wesnoth.org/ [http://forums.wesnoth.org/search.php?search_id=newposts new posts]<br />
|-<br />
|List of Frequently Proposed Ideas || https://forums.wesnoth.org/viewtopic.php?f=12&t=34904<br />
|-<br />
|IRC channel (Freenode) || irc://irc.wesnoth.org/#wesnoth [http://webchat.freenode.net/?channels=wesnoth webchat]<br />
|-<br />
|Discord server || https://discord.gg/battleforwesnoth<br />
|-<br />
|IRC logs || https://wesnoth.org/irclogs/<br />
|-<br />
|Twitter Account || https://twitter.com/Wesnoth<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Developers ===<br />
|-|Git web interface || https://github.com/wesnoth/wesnoth<br />
|-<br />
|Git homepage || http://git-scm.com/<br />
|-<br />
|Git documentation || http://git-scm.com/documentation<br />
|-<br />
|Code documentation || http://devdocs.wesnoth.org/<br />
|-<br />
|Mailing lists || https://gna.org/mail/?group=wesnoth<br />
|-<br />
|Bug/Feature tracker || http://bugs.wesnoth.org/<br />
|-<br />
|Patch tracker || http://patches.wesnoth.org/<br />
|-<br />
|ohloh statistics || http://www.ohloh.net/p/wesnoth<br />
|-<br />
|wesnoth.org server stats || http://collectd.wesnoth.org<br />
|-<br />
|IRC channel (Freenode) || ircs://chat.freenode.net/wesnoth-dev [https://webchat.freenode.net/?channels=wesnoth-dev webchat]<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Other Links ===<br />
|-<br />
|1vs1 Wesnoth ladder || http://wesnoth.gamingladder.info/<br />
|-<br />
|Wesnoth matches with commentary || http://www.youtube.com/user/NekisBrutalWesnoth<br />
|-<br />
|Wesnoth campaign with commentary in german || https://www.youtube.com/playlist?list=PLc3DmuDuA2imW1vg9xXMNjGHm8VaXvEaz<br />
|-<br />
|WSNPP || http://wiki.wesnoth.org/WSNPP<br />
|-<br />
|Run Wesnoth online (Windows) || http://spoon.net/the-battle-for-wesnoth<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Multilingual Players' Sites ===<br />
|-<br />
|Community in China || http://wesnoth.cn/<br />
|}<br />
<br />
[[Category:Wesnoth Wiki]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=User:Shadowm/Stable_1.14_Announcement_Outline&diff=59362User:Shadowm/Stable 1.14 Announcement Outline2018-04-04T11:01:18Z<p>Jyrkive: Reworded the entry about SDL2 transition fixing incompatibility with Retina displays</p>
<hr />
<div>'''This is an outline for the 1.14 Press Release-type announcement page'''<br />
<br />
Add items you worked on or you know someone else worked on throughout 1.13.x. We don't want prose, we just want to enumerate items we'll talk about in the real release announcement. Someone else (or a plurality of someone elses) will magically turn your bullet points into marketing speech-style text for the announcement so you don't need to worry about doing that yourself.<br />
<br />
For each section, try to list items in descending order of importance. The announcement team might not follow the order of the outline but it'd greatly help us to have a rough idea of how visible each item should be.<br />
<br />
Tag platform specific features like this: "(<b>Windows only</b>) New user files location".<br />
<br />
== For Players ==<br />
<br />
=== Highlights ===<br />
<br />
* <b>Steam launch</b><br />
* New title screen music, refined background and overall UI design, new UI font.<br />
* New terrains<br />
* New unit animations<br />
* New portraits<br />
* Completely redesigned add-ons manager, more intuitive and featureful like.<br />
* Revamped Preferences dialog.<br />
* SDL 2.0, including:<br />
** (<b>macOS only</b>) Resolves issues like non-working controls on Retina displays<br />
** Borderless windowed instead of real fullscreen on all platforms<br />
** Performance improvements when scrolling<br />
** (TODO other stuff)<br />
<br />
=== Campaigns ===<br />
<br />
* <cite>Secrets of the Ancients</cite> addition to mainline<br />
* <cite>Under the Burning Suns</cite> playable units refresh<br />
* Refreshed maps in <cite>Northern Rebirth</cite><br />
* Ability to use mod add-ons in single-player campaigns<br />
<br />
=== Multiplayer ===<br />
<br />
* New MP UI and interaction flow<br />
* Khalifate renamed to Dunefolk, balancing changes to the faction<br />
* Addition of the ability to forfeit a game<br />
<br />
=== Others ===<br />
<br />
* Version info dialog with build info, paths, library versions, Copy to Clipboard option for bug reports<br />
* (<b>Windows only</b>) New user files location, need to document how to make a "portable Wesnoth" on the wiki somewhere as well<br />
* Possibility to change unit status orb colours to make it easier for people who require it to tell them apart.<br />
* Music is now silenced by default when the game window loses focus.<br />
<br />
== For Content Creators ==<br />
<br />
* Lua interpreter update from version ??? to 5.3.4 compared to 1.12<br />
* New WML preprocessor feature for named optional arguments (<code>#arg/#endarg</code>)<br />
* Lua console and redesigned gamestate inspector<br />
* Expand the Wesnoth Formula Language (WFL) with new syntax features and functions<br />
* Allow use of WFL in many new places, including all four types of filter, unit abilities, weapon specials, and even some Image path Functions<br />
* Several built-in predefined dialogs are now exposed to Lua, including the storyscreen<br />
* Many expansions and some simplifications to the specification of AI parameter in WML and the Lua AI API<br />
* [modification]s for singleplayer<br />
* [on_undo] to make gamechanging actions undoable.<br />
* New looping tags<br />
* custom mapgenerator written in lua<br />
* (<b>Windows only</b>) <code>--wconsole</code> to make Wesnoth's console output display in real time in a native console window<br />
* (<b>Windows only</b>) Log files folder and new location for logs<br />
* Add-on <b>and</b> mainline content licensing policy changes<br />
<br />
[[Category:Development]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=IGNFileFormat&diff=59268IGNFileFormat2018-03-17T14:53:45Z<p>Jyrkive: Slight changes</p>
<hr />
<div>__NOTOC__<br />
When you upload an add-on, some files and directories are ignored and not uploaded.<br />
<br />
If you would prefer to have more control over which files and directories should be ignored, you should provide a <b>_server.ign</b> file containing a list of filter patterns. Each file or directory matching any entry of the _server.ign file will not be included when uploaded to the add-ons server.<br />
<br />
== Where do I put the file, and what goes in it? ==<br />
<br />
The file should be placed within the root level of your add-on (<i>data/add-ons/addon_name/_server.ign</i>).<br />
<br />
A specific format is required:<br />
<br />
* {{DevFeature1.13|0}}: Blank lines and lines beginning with '<code># </code>' (space required) are ignored.<br />
* Each line should contain exactly one pattern.<br />
* <code>*</code> and <b>?</b> wildcards are supported (<b>?</b> stands for any single character, <code>*</code> stands for any number of any characters).<br />
* {{DevFeature1.13|12}}: <code>+</code> wildcards are also supported: it stands for one or more characters.<br />
* Patterns with a trailing forward slash '<code>/</code>' will match directories; the rest only match files.<br />
<br />
==== Default values ====<br />
<br />
If there is no custom _server.ign file present, a default filter will be used instead, which is as follows:<br />
<br />
.+<br />
.+/<br />
__MACOSX/<br />
#*#<br />
*~<br />
*-bak<br />
*.swp<br />
*.pbl<br />
*.ign<br />
_info.cfg<br />
*.exe<br />
*.bat<br />
*.cmd<br />
*.com<br />
*.scr<br />
*.sh<br />
*.js<br />
*.vbs<br />
*.o<br />
*.ini {{DevFeature1.13|12}}<br />
Thumbs.db<br />
*.wesnoth<br />
*.project<br />
<br />
If there is a custom _server.ign file, the contents of the file are used in addition to the defaults listed here.<br />
<br />
== See Also ==<br />
<br />
* [[PblWML]]<br />
<br />
[[Category:Create]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=IGNFileFormat&diff=59267IGNFileFormat2018-03-17T12:06:16Z<p>Jyrkive: Add the + wildcard</p>
<hr />
<div>__NOTOC__<br />
When you upload an add-on, some files and directories are ignored and not uploaded.<br />
<br />
If you would prefer to have more control over which files and directories should be ignored, you should provide a <b>_server.ign</b> file containing a list of filter patterns. Each file or directory matching any entry of the _server.ign file will not be included when uploaded to the add-ons server.<br />
<br />
== Where do I put the file, and what goes in it? ==<br />
<br />
The file should be placed within the root level of your add-on (<i>data/add-ons/addon_name/_server.ign</i>).<br />
<br />
A specific format is required:<br />
<br />
* {{DevFeature1.13|0}}: Blank lines and lines beginning with '<code># </code>' (space required) are ignored.<br />
* Each line should contain exactly one pattern.<br />
* <code>*</code> and <b>?</b> wildcards are supported (<b>?</b> stands for any single character, <code>*</code> stands for any number of any characters).<br />
* {{DevFeature1.13|12}}: <code>+</code> wildcards are also supported: it stands for one or more character.<br />
* Patterns with a trailing forward slash '<code>/</code>' will match directories; the rest only match files.<br />
<br />
==== Default values ====<br />
<br />
If there is no custom _server.ign file present, a default filter will be used instead, which is as follows:<br />
<br />
.*<br />
.*/<br />
__MACOSX/<br />
#*#<br />
*~<br />
*-bak<br />
*.swp<br />
*.pbl<br />
*.ign<br />
_info.cfg<br />
*.exe<br />
*.bat<br />
*.cmd<br />
*.com<br />
*.scr<br />
*.sh<br />
*.js<br />
*.vbs<br />
*.o<br />
*.ini {{DevFeature1.13|12}}<br />
Thumbs.db<br />
*.wesnoth<br />
*.project<br />
<br />
If there is a custom _server.ign file, the contents of the file are used in addition to the defaults listed here.<br />
<br />
== See Also ==<br />
<br />
* [[PblWML]]<br />
<br />
[[Category:Create]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=InterfaceActionsWML&diff=59230InterfaceActionsWML2018-02-27T17:18:57Z<p>Jyrkive: Documented repeat_on_hold</p>
<hr />
<div>{{WML Tags}}<br />
== Interface actions ==<br />
<br />
Part of [[ActionWML]], interface actions are actions that do not have a direct effect on gameplay;<br />
instead, they show something to the player. The main interface tags<br />
are '''[message]''' and '''[objectives]''', but several other tags affect<br />
the interface also.<br />
<br />
== [inspect] ==<br />
This user interface instantly displays the gamestate inspector dialog at the current scenario state (the same one that can be brought up with [[CommandMode|the ''':inspect''' command]]), which can be used to inspect the values of WML variables, AI configuration, recall lists, and more.<br />
<br />
* '''name''': optional attribute to specify the name of this gamestate inspector dialog. It is just a label to help differentiate between different invocations of gamestate inspector dialog.<br />
<br />
== [message] ==<br />
The most commonly used interface action is [message], which displays a message to the user in a dialog box. It can also be used to take input from the user.<br />
<br />
The following key/tags are accepted for [message]:<br />
* [[StandardUnitFilter]]: The unit whose profile and name are displayed. Do not use a [filter] tag. If no unit matching this filter is found, the message is not displayed (The unit has probably been killed).<br>[message] elements should be constructed so that it is either guaranteed that a certain unit is alive, or so that dialog flows smoothly even if the message isn't displayed.<br />
<br />
* '''speaker''': an alternative to standard unit filter. You may specify as the value of the speaker attribute a unit id or any of the following special values:<br />
** '''narrator''': the dialog box is displayed without a caption for the unit speaking or a unit image<br />
** '''unit''': the primary unit for the event is speaking<br />
** '''second_unit''': {{DevFeature1.13|6}} the secondary unit for the event is speaking<br />
<br />
* '''message''': (translatable) the text to display to the right of the image. ''message'' is sometimes multiple lines; if it is, be sure to use quotes(''' ' ''' or ''' " ''')<br />
* '''male_message''', '''female_message''': {{DevFeature1.13|2}} (translatable) Used instead of ''message'' if the unit's gender matches. Never used if there is no unit (ie ''speaker=narrator''). {{DevFeature1.13|6}} This matches the primary unit, not the secondary unit.<br />
* '''wait_description''': {{DevFeature1.13|2}} the description of this message displayed when other players in a mp game wait for one player doing input in a [message] (with [option]s or [text_input]).<br />
* '''[show_if]''': if present then this message will only be displayed if the conditional statement in this tag is passed (see [[ConditionalActionsWML#Condition_Tags|ConditionalActionsWML]])<br />
* '''side_for''': (default: all sides) comma-separated list of sides for who message is shown. This will <b>not</b> work with messages that take user input ([option]/[text_input]), which can only ever be shown to the current player. {{DevFeature1.13|0}} side_for= is now also accepted for messages with user input, it specifies on which side the message is shown (defaults to the currently playing side). For messages with input it does not accept a comma seperated list only a single number.<br />
* '''image''': (default: profile image of speaker) the image to display to the left of the message text. Append ~RIGHT() if you want the image to appear on the right side. <br />
** {{DevFeature1.13|0}} <b>none:</b> display no image<br />
* '''mirror''': {{DevFeature1.13|5}}whether to mirror the image specified by the '''image''' attribute.<br />
* '''second_image''': {{DevFeature1.13|6}}same as the '''image''' attribute, but the image is displayed on the right of the message text.<br />
* '''second_mirror''': {{DevFeature1.13|6}}same as '''mirror''', but for the '''second_image''' attribute.<br />
* '''image_pos''': {{DevFeature1.13|5}} whether to show the image on the left or right; supercedes the use of ~RIGHT() described above<br />
* '''caption''': (default: name of speaker) the caption to display beside the image. Name to be displayed. Note: use a translation mark to avoid wmllint errors.<br />
* '''scroll''': Boolean specifying whether the game view should scroll to the speaking unit. Defaults to ''yes''.<br />
* '''highlight''': {{DevFeature1.13|5}} Boolean specifying whether to highlight the speaker. Defaults to ''yes''.<br />
* '''duration''': (default: 10) the minimum number of frames for this message to be displayed. (A frame lasts about 30 milliseconds.) During this time any dialog decisions will be disregarded.<br />
* '''sound''': a sound effect (wav file) to play as the message is displayed. This can be a comma-separated list, from which one will be randomly chosen.<br />
* '''[option]''': No '''[option]''' elements have to be used. If '''[option]''' elements are present, then each option will be displayed in a menu for the user to select one option. ''Note: Messages with options will not be shown at all in prestart events''<br />
** '''message''': (translatable) the text displayed for the option (see [[DescriptionWML]]) {{DevFeature1.13|2}} This is now a synonym for '''label='''.<br />
** '''image''', '''label''', '''description''', '''default''': See [[DescriptionWML#WML_Format|DescriptionWML]].<br />
** '''[show_if]''': if present then this option will only be displayed if the conditional statement in this tag is passed (see [[ConditionalActionsWML#Condition_Tags|ConditionalActionsWML]])<br />
** '''[command]''': an element containing actions which are executed if the option is selected.<br />
* '''[text_input]''': there can be only one [text_input] tag. this adds a text input field to the message. ''Note: Messages with text_input will not be shown at all in prestart events''<br />
** '''variable''': the variable that the user's input will be written to<br />
** '''label''': a text label to the left of the input field<br />
** '''max_length''': the maximum number of characters that may be typed into the field<br />
** '''text''': text that is written into the field in the beginning<br />
* Check [[EventWML#Multiplayer_safety]] to find out in which events you can safely use '''[option]''' and '''[text_input]''' without causing OOS.<br />
<br />
=== Formatting ===<br />
'''[message]''' and other tags such as unit names (user_description), objectives, and floating text can make use of [http://developer.gnome.org/pango/unstable/PangoMarkupFormat.html Pango markup formatting codes].<br />
<br />
Remember to use single quotes (') instead of double quotes (") within the formatting string, as double quotes cannot be escaped, and the string will appear fragmented and possibly cause errors.<br />
<br />
For example, if you wanted to write "You are victorious!" in large, italic, gold letters, you might write it this way:<br />
<br />
<nowiki><span color='#BCB088' size='large' font-style='italic'>You are victorious!</span></nowiki><br />
<br />
<br />
These are the codes taken from the Pango markup formatting guide:<br />
<br />
*'''font''', '''font_desc''': A font description string, such as "Sans Italic 12".<br />
*'''font_family''', '''face''': A font family name.<br />
*'''font_size''', '''size''': Font size in 1024ths of a point, or one of the absolute sizes 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', or one of the relative sizes 'smaller' or 'larger'.<br />
*'''font_style''', '''style''': One of 'normal', 'oblique', 'italic'.<br />
*'''font_weight''', '''weight''': One of 'ultralight', 'light', 'normal', 'bold', 'ultrabold', 'heavy', or a numeric weight.<br />
*'''font_variant''', '''variant''': One of 'normal' or 'smallcaps'.<br />
*'''font_stretch''', '''stretch''': One of 'ultracondensed', 'extracondensed', 'condensed', 'semicondensed', 'normal', 'semiexpanded', 'expanded', 'extraexpanded', 'ultraexpanded'.<br />
*'''foreground''', '''fgcolor''', '''color''': An RGB color specification such as '#00FF00' or a color name such as 'red'.<br />
*'''background, bgcolor''': An RGB color specification such as '#00FF00' or a color name such as 'red'.<br />
*'''underline''': One of 'none', 'single', 'double', 'low', 'error'.<br />
*'''underline_color''': The color of underlines; an RGB color specification such as '#00FF00' or a color name such as 'red'.<br />
*'''rise''': Vertical displacement, in 10000ths of an em. Can be negative for subscript, positive for superscript.<br />
*'''strikethrough''': 'true' or 'false' whether to strike through the text.<br />
*'''strikethrough_color''': The color of strikethrough lines; an RGB color specification such as '#00FF00' or a color name such as 'red'<br />
*'''fallback''': 'true' or 'false' whether to enable fallback. If disabled, then characters will only be used from the closest matching font on the system. No fallback will be done to other fonts on the system that might contain the characters in the text. Fallback is enabled by default. Most applications should not disable fallback.<br />
*'''letter_spacing''': Inter-letter spacing in 1024ths of a point.<br />
*'''gravity''': One of 'south', 'east', 'north', 'west', 'auto'.<br />
*'''gravity_hint''': One of 'natural', 'strong', 'line'.<br />
<br />
The following pango attributes are also available directly as attributes of the '''[message]''' tag:<br />
{{DevFeature1.13|4}}<br />
<br />
*'''font'''<br />
*'''font_family'''<br />
*'''font_size'''<br />
*'''font_style'''<br />
*'''font_weight'''<br />
*'''font_variant'''<br />
*'''font_stretch'''<br />
*'''color'''<br />
*'''bgcolor'''<br />
*'''underline'''<br />
*'''underline_color'''<br />
*'''rise'''<br />
*'''strikethrough'''<br />
*'''strikethrough_color'''<br />
*'''fallback'''<br />
*'''letter_spacing'''<br />
*'''gravity'''<br />
*'''gravity_hint'''<br />
<br />
== [objectives] ==<br />
The other tag used for plot development is '''[objectives]'''.<br />
The '''[objectives]''' tag overwrites any previously set objectives,<br />
and displays text which should describe the objectives of the scenario.<br />
Scenario objectives are displayed on the player's first turn after the tag is used,<br />
or as part of the event if it triggers during that player's turn.<br />
Objectives can also be accessed at any time in a scenario using the<br />
"Scenario Objectives" game menu option, making this tag useful for<br />
scenario-specific information that the player may need to refer to during play.<br />
<br />
Attributes of '''[objectives]''':<br />
* '''side''': Default '0'. The side to set the objectives for. A value of 0 sets objectives for all sides. note: There are side-specific objectives and default objectives, which are used in case a side doesn't have specific ones. Specifying 0 sets the default ones.<br />
* '''[[StandardSideFilter]]''' tags and keys: Sets the objectives of all matching sides to these passed specifications (the rest of this [objectives] tag). If no sides (such as when passing side=0) or all sides match, sets the default objectives, and the side specific ones for the matching sides otherwise.<br />
* '''bullet''': Default '• '. Replaces the default bullet, with whatever is passed, for all objectives, gold carryover notes, and notes defined with [note].<br />
* '''summary''': Displayed first in the objectives text, this should describe the basic objective for the overall scenario. Can be omitted.<br />
* '''note''': Displayed last in the objectives text, this is sometimes used for hints or additional information. Can be omitted.<br />
* '''victory_string''': Default ' _ "Victory:"', this text precedes the victory objectives. Can be set to "" too.<br />
* '''defeat_string''': Default ' _ "Defeat:"', this text precedes the defeat objectives. Can be set to "" too.<br />
* '''gold_carryover_string''': Default ' _ "Gold carryover:"', this text precedes the gold carryover information.<br />
* '''notes_string''': Default ' _ "Notes:"', this text precedes the notes.<br />
* '''silent''': Default: not present. If set to "yes", the objectives are silently changed. Else, they will be shown to the user when appropriate.<br />
* '''delayed_variable_substitution''': {{DevFeature1.13|8}} If set to yes, any variables or [insert_tag] are not substituted right away. Instead, they are substituted whenever the objectives are actually viewed.<br />
<br />
Tags of '''[objectives]''':<br />
* '''[objective]''': describes a win or loss condition. Most scenarios have multiple win or loss conditions, so use a separate [objective] subtag for each line; this helps with translations.<br />
** '''bullet''': Default '• ' or whatever is set in the parent [objectives] block. Replaces the default bullet, with whatever is provided, for the objective defined by the [objective] block.<br />
** '''red''': Default '0' for winning objectives, '255' for losing objectives. Overrides the default red coloring of the entire objective, including the bullet.<br />
** '''green''': Default '255' for winning objectives, '0' for losing objectives. Overrides the default green coloring of the entire objective, including the bullet.<br />
** '''blue''': Default '0'. Overrides the default blue coloring of the entire objective, including the bullet.<br />
** '''description''': text for the specific win or loss condition.<br />
** '''caption''': a text which will be displayed above the ''description''. This can be used to display a subcategory of objectives below ''victory_string'' or ''defeat_string''.<br />
** '''condition''': The color and placement of the text. Values are 'win'(colored green, placed after ''victory_string'') and 'lose'(colored red, placed after ''defeat_string'').<br />
** '''show_turn_counter''': If set to yes, displays the number of turns remaining in the scenario. Default is no.<br />
** '''[show_if]''': A condition that disables the objective if it doesn't hold. Conditional objectives are refreshed at '''[show_objectives]''' time only, or when manually opening the scenario objectives.<br />
* '''[gold_carryover]''': describes how the gold carryover works in this scenario. This is intended to be a more convenient way of displaying carryover information than using the note= key in [objectives].<br />
** '''bullet''': Default '• ' or whatever is set in the parent [objectives] block. Replaces the default bullet with whatever is provided.<br />
** '''red''': Default '255'. Overrides the default red coloring of the entire objective, including the bullet.<br />
** '''green''': Default '255'. Overrides the default green coloring of the entire objective, including the bullet.<br />
** '''blue''': Default '192'. Overrides the default blue coloring of the entire objective, including the bullet.<br />
** '''bonus''' (boolean): whether an early finish bonus is granted. If omitted, early finish bonus is not mentioned.<br />
** '''carryover_percentage''': the amount of carryover gold. If omitted, the amount is not mentioned.<br />
** '''[show_if]''': {{DevFeature1.13|11}} Gold carryover will not be shown if the specified condition isn't met. Conditional gold carryover is refreshed at '''[show_objectives]''' time only.<br />
* '''[note]''': describes a note, usually used for hints or additional information. This is an easier way of adding several notes than concatenating them together into a single string to use with the ''note='' key.<br />
** '''bullet''': Default '• ' or whatever is set in the parent [objectives] block. Replaces the default bullet with whatever is provided for the note defined by the [note] block.<br />
** '''red''': Default '255'. Overrides the default red coloring of the entire note, including the bullet.<br />
** '''green''': Default '255'. Overrides the default green coloring of the entire note, including the bullet.<br />
** '''blue''': Default '255'. Overrides the default blue coloring of the entire note, including the bullet.<br />
** '''description''': the text of the note.<br />
** '''[show_if]''': The note will not be shown if the specified condition isn't met. Conditional notes are refreshed at '''[show_objectives]''' time only.<br />
<br />
== [set_menu_item] ==<br />
This tag is used to add a custom option in the right-click context menu which can then be used to trigger arbitrary WML commands. The menu items can be set and modified during any event, for example during "start" or "prestart" events. The user can also assign hotkeys to these WML commands unless specified otherwise. When the hotkey is pressed the event will be fired/filtered at the current mouse position.<br />
<br />
'''Note:''' Due to limitations in portable devices where there are no scroll bars for context menus, there is a hard-coded limit of 7 custom WML menu items. If you really need to have more than 7 menu items, try combining some of them in a submenu. {{DevFeature1.13|0}} This limitation is being removed in a [http://forums.wesnoth.org/viewtopic.php?p=572554#p572554 future version] of Wesnoth.<br />
<br />
* '''id''': the unique id for this menu item. If a menu item with this id already exists, it allows you to set specific changes to that item.<br />
* '''description''': the in-game text that will appear for this item in the menu.<br />
* '''image''': the image to display next to this item.<br />
* '''needs_select''': if ''yes'' (default ''no''), then the latest select event (see [[EventWML]]) that triggered before this menu item was chosen will be transmitted over the network before this menu item action will be. This only has any effect in networked multiplayer, and is intended to allow more elaborate menu item behaviour there without causing out of sync errors. If you don't know what this means, just leave it. {{DevFeature1.13|6}} ''needs_select=yes'' is deprecated, consider using manual variable syncing with [sync_variable].<br />
* '''synced''' {{DevFeature1.13|1}}: if ''no'' (default ''yes'') the command handler will only be run on the client that invoked the menu item; this means that changing the gamestate in a command handler of a menu item with ''synced=no'' will cause OOS<br />
* '''use_hotkey''': if ''no'' (default ''yes''), then the user cannot assign hotkeys to this menu item. If ''only'', the menu item is only accessible via hotkeys, not via right-click context; you can use this in combination with [default_hotkey] if you want custom hotkeys in your campaign/mp. <br />
* '''[show_if]''': If present, the menu item will only be available if the conditional statement (see [[ConditionalActionsWML#Condition_Tags|ConditionalActionsWML]]) within evaluates to true. When this is evaluated, the WML variables ''$x1'' and ''$y1'' will point to the location on which the context menu was invoked, so it's possible to for example only enable the option on empty hexes or on a particular unit.<br />
* '''[filter_location]''': contains a location filter similar to the one found inside Single Unit Filters (see [[FilterWML]]). The menu item will only be available on matching locations.<br />
* '''[default_hotkey]''': contains a hotkey WML to specify what hotkey to assign to this, '''if the user has no hotkey assigned to this yet'''. (Unlike the rest of a menu item definition, modifying this tag has no effect on the game; it is only effective when initially defining a menu item.) Hotkey WML matches the format in the preferences file and contains the following keys:<br />
** '''key''': a string that contains the key to assign to this.<br />
** '''alt''', '''shift''', '''cmd'''(apple only), '''ctrl''': boolean values.<br />
** '''repeat_on_hold''' {{DevFeature1.13|12}}: if ''yes'' (default ''no''), holding the hotkey will repeat the action continuously, unless it blocks input with something like '''[message]'''. Due to a bug, versions older than 1.13.12 always repeat the action, with no way to disable it.<br />
* '''[command]''': contains the WML actions to be executed when the menu item is selected. Again, the WML variables ''$x1'' and ''$y1'' will point to the location on which the context menu was invoked on.<br />
** '''delayed_variable_substitution ''' (boolean yes|no, default: yes): If no, forces a variable substitution run onto the wml included in this [command] block. Use this, if you want variables which are to substitute to get the values they have at execution time of the event where this set_menu_item appears. Other than that, they get the values they have at invocation time of the menu item.<br />
<br />
== [clear_menu_item] ==<br />
<br />
Removes a menu item from the scenario.<br />
Normally menu items are, including all their defining wml, automatically carried over between scenarios. This tag prevents this. (The behavior is comparable to set_variable/clear_variable).<br />
* '''id''': (string): id of the menu item to clear. Can be a comma-separated list.<br />
<br />
== Other interface tags ==<br />
<br />
The following tags are also action tags:<br />
<br />
=== [change_theme] ===<br />
<br />
{{DevFeature1.13|8}}<br />
<br />
Change the current interface theme.<br />
<br />
* '''theme''': The ID of the new theme. Use 'theme=' (empty key) to change to the theme that the player has selected in preferences.<br />
<br />
=== [item] ===<br />
Makes a graphical item appear on a certain hex. Note this only places the graphics for an item. It does not make the item do anything. Use a moveto event to make moving onto the item do something. <tt>''('''Hint:''' There are a number of predefined items that are used in various campaigns that you can make use of. You can find [http://www.wesnoth.org/macro-reference.xhtml#file:items.cfg a list of them] if you look into the items.cfg file in the wesnoth install directory (under /data/core/macros).)''</tt><br />
* '''x''', '''y''': the location to place the item. (only for [event][item]: full [[StandardLocationFilter|SLF]] support)<br />
* '''image''': the image (in ''images/'' as .png) to place on the hex. This image is aligned with the top-left of the hex (which is 72 pixels wide and 72 pixels tall). It is drawn underneath units. ''('''Hint:''' If using an image smaller than 72x72, then it might be useful to [[ImagePathFunctionWML#Blit_Function|BLIT]] the image onto <tt>misc/blank-hex.png</tt> (a blank 72x72 image).)''<br />
* '''halo''': an image to place centered on the hex. It is drawn on top of units. Use this instead of ''image'' if the image is bigger than the hex or if you want to animate an image.<br />
''Example (where the integer after the colon is the duration of each frame or square bracket expansion as per AnimationWML is used): halo=scenery/fire1.png:100,scenery/fire2.png:100,scenery/fire3.png:100,scenery/fire4.png:100,scenery/fire5.png:100,scenery/fire6.png:100,scenery/fire7.png:100,scenery/fire8.png:100''<br />
or equivalently (requires Wesnoth 1.11.2+):<br />
''halo=scenery/fire[1~8].png:100''<br />
* '''team_name''': name of the team for which the item is to be displayed (hidden for others). For multiple teams just put all the names in one string, for example separated by commas.<br />
* '''visible_in_fog''': whether the item should be visible through fog or not. Default yes.<br />
* '''redraw''': (boolean yes|no, default: yes): If no, disables implicit calls to [[InterfaceActionsWML#.5Bredraw.5D|[redraw]]] when placing the items.<br />
<br />
=== [remove_item] ===<br />
Removes any graphical items on a given hex.<br />
* [[StandardLocationFilter]]: the hexes to remove items off<br />
* '''image''' if specified, only removes the given image item (This image name must include any [[ImagePathFunctionWML|image path functions]] appended to the original image name.)<br />
<br />
=== [print] ===<br />
Displays a message across the screen. The message will disappear after a certain time.<br />
* '''text''': (translatable) the text to display.<br />
* '''size''': (default=12) the pointsize of the font to use<br />
* '''duration''': (default=50) the length of time to display the text for. This is measured in the number of 'frames'. A frame in Wesnoth is usually displayed for around 30ms.<br />
* '''red''', '''green''', '''blue''': (default=0,0,0) the color to display the text in. Values vary from 0-255. {{DevFeature1.13|x}} Use of red, green, blue is now deprecated; use color=0,0,0 instead.<br />
<br />
=== [move_unit_fake] ===<br />
Moves an image of a unit along a certain path on the map. The path does not need to be a continuous list of adjacent hexes, so for example only the start and end points can be given, in which case the straightest line between those points will be calculated and used.<br />
* '''type''': the type of the unit whose image to use<br />
* '''x''': a comma-separated list of x locations to move along<br />
* '''y''': a comma-separated list of y locations to move along (x and y values are matched pairs)<br />
* '''side''': the side of the fake unit, used for team-coloring the fake unit<br />
* '''gender''': the gender of the fake unit. Example: gender=female<br />
* '''variation''': the variation of the fake unit. Example: variation=undead<br />
* '''image_mods''': [[ImagePathFunctionWML|image path functions]] sequence to be applied on the fake unit.<br />
* '''force_scroll''': Whether to scroll the map or not even when [[#.5Block_view.5D|[lock_view]]] is in effect or ''Follow Unit Actions'' is disabled in ''Advanced Preferences''. Defaults to ''yes'' starting with version '''1.11.6'''; the attribute did not exist in previous versions and this action behaved as if ''no'' was passed instead.<br />
<br />
=== [move_units_fake] ===<br />
moves multiple images of units along paths on the map. These units are moved in lockstep.<br />
* '''[fake_unit]''': A fake unit to move<br />
** '''type''': the type of unit whose image to use<br />
** '''x''': a comma-separated list of x locations to move along<br />
** '''y''': a comma-separated list of y locations to move along (x and y values are matched pairs)<br />
** '''side''': the side of the fake unit, used for team-coloring the fake unit<br />
** '''skip_steps''': the number of steps to skip before this unit starts moving<br />
=== [hide_unit] ===<br />
Temporarily prevents the engine from displaying the given unit. The unit does not become invisible, as it would be with the '''[hides]''' ability; it is still the same plain unit, but without an image. Useful in conjunction with '''[move_unit_fake]''': to move a leader unit into position on-screen. Until 1.8 each '''[hide_unit]''' tag only hides one unit.<br />
* [[StandardUnitFilter]]: All matching units will be hidden<br />
<br />
=== [unhide_unit] ===<br />
Stops the currently hidden units from being hidden.<br />
* [[StandardUnitFilter]]: Only the matching units will be unhidden<br />
<br />
=== [lock_view] ===<br />
Locks gamemap view scrolling for human players, so they cannot scroll the gamemap view until it is unlocked. WML or Lua actions such as '''[scroll_to]''' will continue to work normally, as they ignore this restriction; the locked/unlocked state is preserved when saving the current game.<br />
<br />
This feature is generally intended to be used in cutscenes to prevent the player scrolling away from scripted actions.<br />
<br />
{{DevFeature1.13|8}} This now also blocks the player from zooming the gamemap view. WML or Lua zoom will continue to work normally.<br />
<br />
=== [unlock_view] ===<br />
Unlocks gamemap view scrolling for human players.<br />
<br />
=== [scroll] ===<br />
Scroll a certain number of pixels in a given direction. Useful for earthquake/shaking effects.<br />
* '''x''', '''y''': the number of pixels to scroll along the x and y axis<br />
* '''side''': the side or sides for which this should happen. By default, the [scroll] happens for everyone.<br />
* '''[filter_side]''': a [[StandardSideFilter]] to select the sides for which this should happen. By default, the [scroll] happens for everyone.<br />
<br />
=== [scroll_to] ===<br />
Scroll to a given hex<br />
* '''x''', '''y''': the hex to scroll to<br />
* [[StandardLocationFilter]], do not use a [filter_location] sub-tag. If more than one location matches the filter, only the first matching location will be used.<br />
* '''check_fogged''': whether to scroll even to locations covered in fog or shroud. Possible values ''yes'' (don't scroll to fog) and ''no'' (scroll even to fog), with ''no'' as the default.<br />
* '''immediate''': whether to instantly warp to the target hex regardless of the scroll speed setting in Preferences (defaults to ''no'').<br />
* '''highlight''': {{DevFeature1.13|5}} Whether to highlight the hex being scrolled to (defaults to ''no'').<br />
* '''side''': the side or sides for which this should happen. By default, the [scroll_to] happens for everyone.<br />
* '''[filter_side]''': a [[StandardSideFilter]] to select the sides for which this should happen. By default, the [scroll_to] happens for everyone.<br />
<br />
=== [scroll_to_unit] ===<br />
Scroll to a given unit<br />
* [[StandardUnitFilter]]<br />
* '''check_fogged''': whether to scroll even to locations covered in fog or shroud. Possible values ''yes'' (don't scroll to fog) and ''no'' (scroll even to fog), with ''no'' as the default.<br />
* '''immediate''': whether to instantly warp to the target hex regardless of the scroll speed setting in Preferences (defaults to ''no'').<br />
* '''highlight''': {{DevFeature1.13|5}} Whether to highlight the hex the unit is on (defaults to ''no'').<br />
* '''for_side''': the side or sides for which this should happen. By default, the [scroll_to_unit] happens for everyone.<br />
* '''[for_side]''': a [[StandardSideFilter]] to select the sides for which this should happen. By default, the [scroll_to_unit] happens for everyone.<br />
<br />
=== [select_unit] ===<br />
Selects a given unit.<br />
* [[StandardUnitFilter]]: The first unit found will be selected.<br />
* '''fire_event''': whether a ''select'' event should be triggered or not (def. ''no''). (Note that select events aren't multiplayer save.)<br />
* '''highlight''': whether the unit's current hex should be highlighted (def. ''yes'').<br />
<br />
=== [sound]===<br />
Plays a sound<br />
* '''name''': the filename of the sound to play (in ''sounds/'' as .wav or .ogg). This can be a comma-separated list, from which one sound will be chosen randomly.<br />
* '''repeat''': repeats the sound for a specified additional number of times (default=0)<br />
<br />
=== [sound_source] ===<br />
Creates a sound source. "Sound sources" is a general name for a mechanism which makes possible for map elements to emit sounds according to some rules, where "map elements" can be specific locations or terrain types. For now, only sound sources tied to locations are supported.<br />
* '''id''': a unique identification key of the sound source<br />
* '''sounds''': a list of comma separated, randomly played sounds associated with the sound source<br />
* '''delay''': a numerical value (in milliseconds) of the minimal delay between two playbacks of the source's sound if the source remains visible on the screen; if one scrolls out and back in, the source will be considered as ready to play<br />
* '''chance''': a percentage (a value from 0 to 100) describing the chance of the source being activated every second after the delay has passed or when the source's location appears on the screen (note that it cannot play more than one file at the same time)<br />
* '''check_fogged''': possible values ''yes'' and ''no'' - ''yes'' means the source will not play if its locations are fogged<br />
* '''check_shrouded''': possible values ''yes'' and ''no'' - ''yes'' means the source will not play if its locations are shrouded<br />
* '''x,y''': similar to x,y as found in a [[StandardLocationFilter]], these are the locations associated with the sound source<br />
* '''fade_range''' (default = 3): distance in hexes that determines a "circular" area around the one specified by '''full_range''' where sound volume fades out linearly<br />
* '''full_range''' (default = 14): distance in hexes that determines a "circular" area where source plays with full volume, relative to screen center<br />
* '''loop''': number of times a sound sample should be looped if it stays visible. -1 means infinite (~65000)<br />
<br />
=== [story] ===<br />
{{DevFeature1.13|8}}<br />
<br />
Shows the story screen.<br />
* '''title''': Default title used if a part does not specify one — unlike the intro storyscreen, the scenario name is not used as a default title.<br />
* '''[part]''': As [[IntroWML]].<br />
<br />
=== [remove_sound_source] ===<br />
Removes a previously defined sound source.<br />
* '''id''': the identification key of the sound source to remove<br />
<br />
=== [music]===<br />
Switches to playing different music<br />
* '''name''': the filename of the music to play (in ''music/'' as .ogg)<br />
* see [[MusicListWML]] for the correct syntax<br />
===[volume]===<br />
Changes the game volume to a percent of the preferences volume for the game being played. Values can go from 0 to 100: <br />
* '''music''': Changes the music volume.<br />
* '''sound''': Changes the sound volume.<br />
=== [color_adjust]===<br />
Tints the color of the screen.<br />
* '''red''', '''green''', '''blue''': values from -255 to 255, the amount to tint by for each color<br />
=== [delay] ===<br />
Pauses the game.<br />
* '''time''': the time to pause in milliseconds<br />
* '''accelerate ''' (boolean yes|no, default no): {{DevFeature1.13|0}} whether the delay is affected by acceleration. When [delay] is used to make an animation, this should be set to yes so that your animation matches the ones generated by the game.<br />
<br />
=== [redraw] ===<br />
Redraws the screen (this normally isn't done during events, although some of the other interface actions cause the screen or parts of it to be redrawn).<br />
* '''clear_shroud''' (boolean yes|no, default no): If yes, clears fog and shroud around existing units. Useful if you, for example, spawn friendly units in the middle of an event and want the shroud to update accordingly (otherwise units that spawn inside fog would remain invisible for the duration of the event, since the fog would not automatically get cleared around them).<br />
* '''[[StandardSideFilter]]''': the sides for which to recalculate fog and shroud.<br />
* '''side''': If used (forces clear_shroud=yes), clears fog and shroud for that side.<br />
<br />
=== [unit_overlay] ===<br />
Sets an image that will be drawn over a particular unit, and follow it around<br />
* [[StandardUnitFilter]]: All matching units will get the overlay (do not use [filter])<br />
* '''image''': the image to place on the unit<br />
<br />
=== [remove_unit_overlay] ===<br />
removes a particular overlayed image from a unit<br />
* [[StandardUnitFilter]]: The overlay will get removed from all matching units (do not use [filter])<br />
* '''image''': the image to remove from the unit<br />
<br />
=== [animate_unit] ===<br />
Uses an animation of a unit to animate it on screen (if the unit has the corresponding animation).<br />
* '''flag''': The key to find the custom animation in the unit description (see the '''[extra_anim]''' description in [[AnimationWML]]). Standard animations can be triggered with the following keywords: ''leading recruited standing idling levelout levelin healing healed poisoned movement defend attack death victory pre_teleport post_teleport''<br />
* '''[filter]''' with a [[StandardUnitFilter]] as argument, see [[FilterWML]]. By default, the unit at the event location will be animated. You can use this tag to choose any other unit to animate.<br />
* '''[primary_attack]''': If this tag is not present, the filter for animation will be triggered with no attack. If it is here, all attacks from the unit will be filtered, and a matching one will be used to filter the animation. Takes a weapon filter as argument, see [[FilterWML]].<br />
* '''[secondary_attack]''': Similar to '''[primary_attack]'''. May be needed to trigger a defense animation correctly, if there are more than one animations available for the defending unit.<br />
* '''hits''': yes/no/hit/miss/kill: which according variation of a attack/defense animation shall be chosen (required)<br />
* '''text''': a text to hover during the animation <br />
* '''male_text''', '''female_text''': {{DevFeature1.13|2}} (translatable) gender-specific versions of the above<br />
* '''red''': red value for the text color (0-255)<br />
* '''green''': green value for the text color<br />
* '''blue''': blue value for the text color<br />
* '''with_bars''': yes/no: whether to display the status bars during the animation (e.g. the hitpoint bar)<br />
* '''[animate]''': a sub block with the same syntax as '''[animate_unit]''' except that the '''[filter]''' block is mandatory to find the unit. This block will find and animate another unit simultaneously.<br />
* '''[facing]''': a [[StandardLocationFilter]] specifying what direction the unit should be facing when animated<br />
<br />
=== [label] ===<br />
Places a label on the map.<br />
* '''x''', '''y''': the location of the label. {{DevFeature1.13|1}} (only for [event][label]: full [[StandardLocationFilter|SLF]] support)<br />
* '''text''': what the label should say<br />
* '''team_name''': if specified, the label will only be visible to the given team.<br />
* '''color''': color of the label. The format is r,g,b; r, g and b are numbers between 0 and 255.<br />
* '''visible_in_fog''': whether the label should be visible through fog or not. Default yes.<br />
* '''visible_in_shroud''': whether the label should be visible through shroud or not. Default no.<br />
* '''immutable''': whether this label is protected from being removed or changed by players. Default yes.<br />
<br />
=== [floating_text]===<br />
Floats text (similar to the damage and healing numbers) on the given locations.<br />
* [[StandardLocationFilter]]: the text will be floated on all matching locations simultaneously.<br />
* '''text''': the text to display.<br />
<br />
The default text color is <span style="color: #6b8cff;">'''#6b8cff'''</span>. To change the color, use [[#Formatting|Pango markup]]. For example:<br />
<br />
<pre><br />
# Float some golden yellow text at 20,20.<br />
[floating_text]<br />
x,y=20,20<br />
text="<span color='#cccc33'>" + _ "Your text here" + "</span>"<br />
[/floating_text]<br />
</pre><br />
<br />
=== [deprecated_message] ===<br />
Shows a deprecated message in the message area, this feature is only intended to be used to warn about deprecated macros in mainline. The message is not translatable.<br />
* '''message''': the message to show.<br />
* '''level''': {{DevFeature1.13|10}} The deprecation level, a number from 1 to 3.<br />
* '''what''': {{DevFeature1.13|10}} The name of the thing being deprecated. Use this instead of '''message''' if possible; a stock message will be generated from it. Use '''message''' only if more information is required; it will be appended to the stock message. This should not be translatable<br />
* '''version''': {{DevFeature1.13|10}} For deprecation levels 2 and 3, this indicates the version in which the feature could be removed. It does ''not'' indicate the version in which it became deprecated. <br />
<br />
The meanings of the deprecation levels are as follows:<br />
<br />
# Deprecated, but will only be removed if absolutely necessary. The '''version''' key is ignored.<br />
# It will be removed no earlier than a specified version.<br />
# It will be removed in the next stable version<br />
# It has already been removed, leaving just a stub to inform users of how to update their code.<br />
<br />
Note that as of 1.13.11, deprecation messages show only in the log, not in the chat message area. The '''message''' can be translatable, but does not need to be.<br />
<br />
=== [wml_message] ===<br />
Outputs a message to Wesnoth's console output. Intended for campaign designers to output silent text to the console, without annoying the player; then, that text might contain information useful for later bug-reporting. The log domain for it is '''wml''', and the '''debug/dbg''' log level is available for use with the '''logger''' attribute. Depending on the current log level ('''error''' by default), which may be changed with the in-game :log command, or the --log-<level>=wml command line switch, the messages are echoed to the in-game chat.<br />
* '''message''': the message to show.<br />
* '''logger''': the Wesnoth engine output logger that should catch the text; this might be 'err' (the errors log level), 'warn'/'wrn' (the warnings log level) or anything else (the information log level). Not all information will be displayed depending on the log level chosen when starting Wesnoth.<br />
<br />
Note: As of 1.9.4/1.9.5 (r48805) the following "loggers" should work: If in [wml_message]: err/error, warn/wrn/warning, debug/dbg; using the :log command: Only the long forms error, warning, info and debug (this part gathered by trying rather than source inspecting). The long forms are most likely also the only ones working when starting wesnoth with --log-<level>=wml.<br />
For log level warning or error (as during normal play), only wml_messages with logger error or warning display (for both). With logger info or debug, additionally wml_messages with logger info or debug display (for both).<br />
<br />
=== [test_condition] ===<br />
<br />
{{DevFeature1.13|2}}<br />
<br />
Evaluates the contained conditional tags. If they evaluate to the expected value, it prints out a message to the console explaining which part of the condition caused this result in a way similar to [wml_message]. This can be used if your conditional test is failing and you're not sure why.<br />
<br />
* '''result''': Whether you expect the conditions to fail or succeed. If no (the default), a message will be printed if the conditional tags fail. If yes, a message will instead be printed if the conditional tags pass.<br />
* '''logger''': Same as for [wml_message]. Defaults to "warning".<br />
<br />
=== [open_help] ===<br />
Opens the in-game help.<br />
* '''topic''': the id of the topic to open<br />
=== [show_objectives] ===<br />
refreshes the objectives defined by [objectives] and its [show_if] tags, and displays them. (It is also called whenever the user explicitly asks for the objectives; this matters only if the tag was overridden by a [[LuaWML#register_wml_action|Lua]] script.)<br />
* '''side''': the side to show the objectives. If not set, all sides are used.<br />
* '''[[StandardSideFilter]]''' tags and keys: Tag affects the matching sides instead of just all or the one given by the integer value of the side= key.<br />
<br />
=== [chat] ===<br />
Displays a message in the chat area, not visible for observers. Alternative unconditionally visible for everyone: [[LuaWML:Display#wesnoth.message]]. {{DevFeature1.13|9}} can be visible for observers.<br />
* '''speaker''': (default="WML") A string for the name of the sender of the message.<br />
* '''message''': The message that should be displayed.<br />
* '''observable''' (boolean yes|no, default yes): {{DevFeature1.13|9}} Whether the message is displayed for observers.<br />
* '''[[StandardSideFilter]]''' tags and keys as argument; if the same client controls multiple sides that match, then the message will only be displayed once.<br />
<br />
=== [zoom] ===<br />
<br />
{{DevFeature1.13|8}}<br />
<br />
Changes the zoom level of the map.<br />
<br />
* '''factor''': The new zoom factor<br />
* '''relative''': If yes, zoom relative to current zoom level. Otherwise, set the absolute zoom level. Default no.<br />
<br />
== Useful Macros ==<br />
There are some predefined macros that you find useful for interface 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 />
* '''{HIGHLIGHT_UNIT}''' Highlight a unit on the map. Use this to show important units<br />
* '''{HIGHLIGHT_IMAGE}''' Places and highlights an image on the map. Use this to show important items or locations<br />
* '''{SET_IMAGE}''' Places an image on the map which has no other function.<br />
* '''{QUAKE <soundfile>}''' Creates a tremor-like screenshake and plays <soundfile>. For example, '''{QUAKE (rumble.ogg)}'''.<br />
* '''{FLASH_WHITE}''' Flash the screen white momentarily. You can also replace WHITE with RED, BLUE or GREEN for a different colour.<br />
<br />
== See Also ==<br />
* [[DirectActionsWML]]<br />
* [[InternalActionsWML]]<br />
* [[EventWML]]<br />
* [[ReferenceWML]]<br />
<br />
<br />
[[Category: WML Reference]]<br />
[[Category: ActionsWML]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=UsefulLinks&diff=59053UsefulLinks2017-12-07T04:07:37Z<p>Jyrkive: Changed the Discord link to the new custom one</p>
<hr />
<div>__TOC__<br />
{|class=wikitable<br />
!colspan=2 style='text-align:left'|<br />
=== Battle for Wesnoth ===<br />
|-<br />
|Home Page || http://www.wesnoth.org/<br />
|-<br />
|GitHub Project Page || https://github.com/wesnoth/<br />
|-<br />
|Page on TheLinuxGameTome || http://happypenguin.org/show?Battle%20for%20Wesnoth<br />
|-<br />
|Freshmeat entry || http://freshmeat.net/projects/wesnoth/<br />
|-<br />
|Changelog || http://changelog.wesnoth.org/ [http://stable-changelog.wesnoth.org stable]<br />
|-<br />
|This wiki || http://wiki.wesnoth.org/<br />
|-<br />
|The manual || http://manual.wesnoth.org/<br />
|-<br />
|Translation statistics || http://gettext.wesnoth.org/<br />
|-<br />
|Add-on server web interface || http://addonlist.wesnoth.org/ [http://stable-addons.wesnoth.org stable] [http://dev-addons.wesnoth.org dev]<br />
|-<br />
|Multiplayer server statistics || http://wesnothd.wesnoth.org/<br />
|-<br />
|Multiplayer replays || http://replays.wesnoth.org<br />
|-<br />
|Unit advancement tree || http://units.wesnoth.org/<br />
|-<br />
|Forum for users and developers || http://forums.wesnoth.org/ [http://forums.wesnoth.org/search.php?search_id=newposts new posts]<br />
|-<br />
|List of Frequently Proposed Ideas || https://forums.wesnoth.org/viewtopic.php?f=12&t=34904<br />
|-<br />
|IRC channel (Freenode) || irc://irc.wesnoth.org/#wesnoth [http://webchat.freenode.net/?channels=wesnoth webchat]<br />
|-<br />
|Discord server || https://discord.gg/battleforwesnoth<br />
|-<br />
|IRC logs || http://irclog.wesnoth.org/<br />
|-<br />
|Flattr Account || http://flattr.com/profile/wesnoth<br />
|-<br />
|Twitter Account || http://twitter.com/Wesnoth<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Developers ===<br />
|-|Git web interface || https://github.com/wesnoth/wesnoth<br />
|-<br />
|Git homepage || http://git-scm.com/<br />
|-<br />
|Git documentation || http://git-scm.com/documentation<br />
|-<br />
|Code documentation || http://devdocs.wesnoth.org/<br />
|-<br />
|Mailing lists || https://gna.org/mail/?group=wesnoth<br />
|-<br />
|Bug/Feature tracker || http://bugs.wesnoth.org/<br />
|-<br />
|Patch tracker || http://patches.wesnoth.org/<br />
|-<br />
|ohloh statistics || http://www.ohloh.net/p/wesnoth<br />
|-<br />
|wesnoth.org server stats || http://collectd.wesnoth.org<br />
|-<br />
|IRC channel (Freenode) || ircs://chat.freenode.net/wesnoth-dev [https://webchat.freenode.net/?channels=wesnoth-dev webchat]<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Other Links ===<br />
|-<br />
|1vs1 Wesnoth ladder || http://wesnoth.gamingladder.info/<br />
|-<br />
|Wesnoth matches with commentary || http://www.youtube.com/user/NekisBrutalWesnoth<br />
|-<br />
|Wesnoth campaign with commentary in german || https://www.youtube.com/playlist?list=PLc3DmuDuA2imW1vg9xXMNjGHm8VaXvEaz<br />
|-<br />
|WSNPP || http://wiki.wesnoth.org/WSNPP<br />
|-<br />
|Run Wesnoth online (Windows) || http://spoon.net/the-battle-for-wesnoth<br />
|-<br />
!colspan=2 style='text-align:left'|<br />
=== Multilingual Players' Sites ===<br />
|-<br />
|Community in China || http://wesnoth.cn/<br />
|}<br />
<br />
[[Category:Wesnoth Wiki]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=WesnothReviews&diff=58494WesnothReviews2017-05-20T13:40:54Z<p>Jyrkive: Added a Spanish Wesnoth review that the author suggested in IRC.</p>
<hr />
<div>Reviews of Battle For Wesnoth in 1.2 or later versions:<br />
<br />
== Chinese ==<br />
<br />
* Baidu Baike: [http://baike.baidu.com/view/1897293.htm 韦诺之战]. It seems like an earlier translation of Wesnoth manual.<br />
<br />
== English ==<br />
<br />
* 100% Seji: [http://my.opera.com/Seji/blog/now-playing-battle-for-wesnoth Battle for Wesnoth], May 2007, BfW 1.2.4<br />
* Phoronix: [http://www.phoronix.com/scan.php?page=article&item=890&num=1 The Battle For Wesnoth] October 2007, BfW 1.2.6<br />
* Simple Thoughts: [http://blog.taragana.com/index.php/archive/game-review-tips-battle-for-wesnoth/ Game Review & Tips: The Battle for Wesnoth] November 2007, BfW 1.2.4<br />
* TidBITS: [http://db.tidbits.com/article/9335 Open Source Mac Gaming: 10 Free Games Reviewed] December 2007<br />
* Linux Journal: [http://www.linuxjournal.com/video/review-battle-wesnoth Battle for Wesnoth] (video)<br />
* [http://vivapinkfloyd.blogspot.com/2008/05/battle-for-wesnoth-142-review.html Battle for Wesnoth Review], May 25, 2008, v1.4.2<br />
* RWDub's Reviews: [http://www.rwdubsreviews.com/2009/11/bjs-thoughts-on-wesnoth.html], November 6 2009<br />
* TuxArena Review: [http://tuxarena.blogspot.com/2010/09/wesnoth-19-brings-awesome-new-features.html Battle for Wesnoth 1.9 Brings Awesome New Features], September 16, 2010<br />
* Bit Maiden Review: [http://www.bitmaiden.com/indie/reviews/battle-for-wesnoth-review/ Battle for Wesnoth Review], February 3 2013<br />
* Techtravelandtalks Review: [http://techtravelandtalks.blogspot.in/2013/07/the-battle-for-wesnoth-more-than-just_13.html Battle for Wesnoth, more than just a game!], July 12 2013<br />
* TuxArena Review: [http://www.tuxarena.com/2014/11/battle-for-wesnoth-1-12-has-big-new-features-overview-whats-new-screenshots/ Battle for Wesnoth 1.12 Has Big New Features (Overview & Screenshots)], November 27, 2014<br />
<br />
== German ==<br />
* HOLARSE: [http://www.holarse-linuxgaming.de/wiki/battle_wesnoth The Battle for Wesnoth], July 2010, BfW 1.8 (it's a wiki, date is last update)<br />
<br />
== Hungarian ==<br />
* LinuxPortál TV-show: [http://wesnoth.fsf.hu/?q=node/99 high quality video] or [http://video.google.com/videoplay?docid=-2509111025528677061 low quality video]; General presentation of BfW, Oct 1. 2008.<br />
<br />
== Romanian ==<br />
<br />
* Fii liber: [http://fii-liber.ro/wesnoth-un-joc-de-strategie-liber-si-avansat Wesnoth - Un joc de strategie liber și avansat ], October 2011, General presentation of BfW 1.8.6<br />
<br />
== Slovak ==<br />
<br />
* Viliam Búr: [http://bur.blog.sme.sk/c/74037/Bitka-o-Wesnoth-verzia-12.html Bitka o Wesnoth, verzia 1.2], December 2006, BfW 1.2<br />
<br />
== Spanish ==<br />
<br />
* Meristation: [http://www.meristation.com/v3/des_analisis.php?id=cw4600fdf7c1637&idj=cw4600f9efed5b6&pic=PC Una batalla épica], March 2007, BfW 1.2.3<br />
* Freak Spot: [https://www.freakspot.net/la-batalla-por-wesnoth/ La batalla por Wesnoth], May 2017, BfW 1.12.6<br />
<br />
== Multilingual ==<br />
<br />
* Wikipedia ([http://bg.wikipedia.org/wiki/%D0%91%D0%B8%D1%82%D0%BA%D0%B0%D1%82%D0%B0_%D0%B7%D0%B0_%D0%A3%D0%B5%D1%81%D0%BD%D0%BE%D1%82 Bulgarian], [http://ca.wikipedia.org/wiki/The_battle_of_Wesnoth Catalan], [http://zh.wikipedia.org/wiki/%E9%9F%A6%E8%AF%BA%E4%B9%8B%E6%88%98 Chinese], [http://cs.wikipedia.org/wiki/Battle_for_Wesnoth Czech], [http://da.wikipedia.org/wiki/Battle_for_Wesnoth Danish], [http://nl.wikipedia.org/wiki/The_Battle_for_Wesnoth Dutch], [http://en.wikipedia.org/wiki/Battle_for_Wesnoth English], [http://eo.wikipedia.org/wiki/Batalo_por_Vesnot%27 Esperanto], [http://fi.wikipedia.org/wiki/Taistelu_Wesnothista Finnish], [http://fr.wikipedia.org/wiki/Battle_for_Wesnoth French], [http://gl.wikipedia.org/wiki/Battle_for_Wesnoth Galician], [http://de.wikipedia.org/wiki/Battle_for_Wesnoth German], [http://he.wikipedia.org/wiki/%D7%94%D7%A7%D7%A8%D7%91_%D7%A2%D7%9C_%D7%95%D7%A1%D7%A0%D7%95%D7%AA%27 Hebrew], [http://hu.wikipedia.org/wiki/The_Battle_for_Wesnoth Hungarian] [http://it.wikipedia.org/wiki/Battle_for_Wesnoth Italian], [http://ja.wikipedia.org/wiki/The_Battle_for_Wesnoth Japanese], [http://ko.wikipedia.org/wiki/%EC%9B%A8%EC%8A%A4%EB%85%B8%EC%8A%A4_%EC%A0%84%EC%9F%81 Korean], [http://lt.wikipedia.org/wiki/M%C5%AB%C5%A1is_d%C4%97l_Vesnoto Lithuanian], [http://no.wikipedia.org/wiki/Battle_for_Wesnoth Norwegian], [http://fa.wikipedia.org/wiki/%D9%86%D8%A8%D8%B1%D8%AF_%D8%A8%D8%B1%D8%A7%DB%8C_%D9%88%D8%B3%D9%86%D9%88%D8%AB Persian], [http://pl.wikipedia.org/wiki/Battle_for_Wesnoth Polish], [http://pt.wikipedia.org/wiki/The_Battle_for_Wesnoth Portuguese], [http://ru.wikipedia.org/wiki/Battle_for_Wesnoth Russian], [http://sk.wikipedia.org/wiki/Bitka_o_Wesnoth Slovak], [http://es.wikipedia.org/wiki/Battle_for_Wesnoth Spanish], [http://sv.wikipedia.org/wiki/Kampen_om_Wesnoth Swedish])<br />
<br />
== See Also ==<br />
* [[Voting]] - Sites where you can vote for and/or rate Wesnoth<br />
* [[OlderReviews]] - Reviews from before 1.2<br />
* [[WhatArePlayersSaying]]? - Quotes from newssites, blogs and players<br />
* [[Play]]<br />
<br />
[[Category:Showcase]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=UsefulLinks&diff=58454UsefulLinks2017-05-10T19:41:34Z<p>Jyrkive: Added the official Discord server.</p>
<hr />
<div>=== Battle for Wesnoth ===<br />
Home Page http://www.wesnoth.org/<br />
GitHub Project Page https://github.com/wesnoth/<br />
Gna! Project Page http://svn.wesnoth.org/ (bug tracker still in use)<br />
Page on TheLinuxGameTome http://happypenguin.org/show?Battle%20for%20Wesnoth<br />
Freshmeat entry http://freshmeat.net/projects/wesnoth/<br />
Changelog http://changelog.wesnoth.org/ [http://stable-changelog.wesnoth.org stable]<br />
This wiki http://wiki.wesnoth.org/<br />
The manual http://manual.wesnoth.org/<br />
Translation statistics http://gettext.wesnoth.org/<br />
Add-on server web interface http://addonlist.wesnoth.org/ [http://stable-addons.wesnoth.org stable] [http://dev-addons.wesnoth.org dev]<br />
Multiplayer server statistics http://wesnothd.wesnoth.org/<br />
http://wesnoth.yi.org/statistics/ <br />
Multiplayer replays http://replays.wesnoth.org<br />
Unit advancement tree http://units.wesnoth.org/<br />
Forum for users and developers http://forums.wesnoth.org/ [http://forums.wesnoth.org/search.php?search_id=newposts new posts]<br />
List of Frequently Proposed Ideas https://forums.wesnoth.org/viewtopic.php?f=12&t=34904<br />
IRC channel (Freenode) irc://irc.wesnoth.org/#wesnoth [http://webchat.freenode.net/?channels=wesnoth webchat]<br />
Discord server https://discord.gg/tSmJS2E<br />
IRC logs http://irclog.wesnoth.org/<br />
Flattr Account http://flattr.com/profile/wesnoth<br />
Twitter Account http://twitter.com/Wesnoth<br />
<br />
=== Developers ===<br />
Git web interface https://github.com/wesnoth/wesnoth<br />
Git homepage http://git-scm.com/<br />
Git documentation http://git-scm.com/documentation<br />
Code documentation http://devdocs.wesnoth.org/<br />
Mailing lists https://gna.org/mail/?group=wesnoth<br />
Bug/Feature tracker http://bugs.wesnoth.org/<br />
Patch tracker http://patches.wesnoth.org/<br />
ohloh statistics http://www.ohloh.net/p/wesnoth<br />
wesnoth.org server stats http://collectd.wesnoth.org<br />
IRC channel (Freenode) ircs://chat.freenode.net/wesnoth-dev [https://webchat.freenode.net/?channels=wesnoth-dev webchat]<br />
<br />
=== Other Links ===<br />
1vs1 Wesnoth ladder http://wesnoth.gamingladder.info/<br />
Wesnoth matches with commentary http://www.youtube.com/user/NekisBrutalWesnoth<br />
Wesnoth campaign with commentary <br />
in german https://www.youtube.com/playlist?list=PLc3DmuDuA2imW1vg9xXMNjGHm8VaXvEaz<br />
WSNPP http://wiki.wesnoth.org/WSNPP<br />
Run Wesnoth online (Windows) http://spoon.net/the-battle-for-wesnoth<br />
<br />
=== Multilingual Players' Sites ===<br />
Community in China http://wesnoth.cn/<br />
<br />
[[Category:Wesnoth Wiki]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=NotSoEasyCoding&diff=58426NotSoEasyCoding2017-05-05T02:31:06Z<p>Jyrkive: Unassigned OpenGL port from Aginor.</p>
<hr />
<div>This page lists projects which are considered a good idea by the developers but which have nobody working on them so far. If you think you've got the required skill for a task go on, implement it and you've got a high chance that it'll be accepted. The remaining barrier will only be that it's done well. :-)<br />
<br />
If you are such a person, you should feel free to edit this page.<br />
<br />
If you're not, you should post a feature request and discuss your idea on the forum or IRC. A coder with better knowledge of the code might give you the green light to add your feature here.<br />
<br />
Anybody should feel free to add "clues" to any tasks, that is entry points, traps to avoid, person to contact to discuss and so on.<br />
<br />
If you plan to work on a feature, write your name at the bottom of the feature, with the date. Note that if you spend too long at working on a feature we'll "free" it back (that is if you're not working on it. If you have problems implementing it, just tell us....). We sometimes forget to update this page so it is reccomended that, before you start to work on a task, that you ask in irc whether its still considered a good idea.<br />
<br />
If you are part of the wesnoth dev team you can also add new entries to the list, a new entry must contain at least<br />
<br />
1) A description of the problem.<br />
2) A motivation why or in which situlations this is important.<br />
3) A link to a corresping ticket on our bugtracker for discussion, if there is no ticket yet then make one.<br />
<br />
__TOC__<br />
<br />
== Game engine ==<br />
<br />
=== [[SDL 2 + OpenGL port]] ===<br />
<br />
Wesnoth currently depends on the unmaintained SDL 1.2.x code base and the 1.2.x-based versions of its companion libraries (SDL_image, SDL_mixer, SDL_net, SDL_ttf). This means Wesnoth can't benefit from features added in newer versions of SDL such as built-in support for Android and iOS, improved cross-platform Unicode support, built-in clipboard support, and any bug fixes made since SDL 1.2.15.<br />
<br />
The problem with porting Wesnoth to SDL 2.0 as explained by lipkab (who attempted the task as part of GSoC 2014) is that SDL 2.0's software rendering API lacks some things Wesnoth needs such as a specific alpha blending mode (TODO: link to conversations here).<br />
<br />
Furthermore, while software rendering is all good and nice, Wesnoth's approach does not scale well as display resolutions increase and the need for supporting high DPI configurations is factored in. One can easily see that our zoom functionality is clunky and inefficient. Having support for OpenGL and shaders would also enable us to implement fancier graphical gimmicks such as particle effects and atmospheric lighting. The downside of adding OpenGL to the mix is that we'd need people with a specific skillset to help us maintain the graphics engine in the long term, as hardware and driver-specific quirks are inevitable. Some users are also concerned about changing Wesnoth's hardware requirements so that they would be unable to play using old or unsupported hardware/OS configurations.<br />
<br />
For hardware rendering to be effective, in particular, Wesnoth would necessitate a complete rewrite of the graphics rendering code (the display and game_display classes), the image cache manager (used to abstract the process of loading images from disk and applying image path functions), and potentially GUI2's canvas code. All three would have to limit the amount of textures they allocate to the absolute minimum, using larger textures instead. For example, the image cache could be reimplemented to load images from an internal spritesheet generated during the game loading process. (WML/Lua support for spritesheets would be nice would defining an API for it would only distract from the actual task at hand.)<br />
<br />
The greatest difficulty in handling this task probably lies in writing a new optimized rendering engine and updating all code that relies on the old display/game_display API and semantics accordingly. The GUI2 framework code is presently unmaintained and it would take some time for somebody to study and change the current implementation. Finally, as we don't have a graphics engine maintainer at this time, this task involves an implicit long-term commitment to the project that extends beyond Wesnoth 1.14.x (or whichever series will see this project completed).<br />
<br />
=== Improve WML error reporting ===<br />
<br />
There are many programming bugs that give very unclear or cryptic error messages when using WML. Here are some examples:<br />
<br />
* <strike>When calling a macro with the wrong number of arguments, Wesnoth tells the number it expected, and the number it got, and at which line number it was instantiated. However, it would be helpful if it would also tell the line number of the definition of the macro. This might be helpful if someone is redefining macros.</strike><br />
<br />
* For places where a standard unit filter is expected, if one is not found, the game should point out a problem. For places where such a filter is expected but [filter] child should not appear, if one does it should report an error and a line number. Many users have a hard time with this kind of thing.<br />
<br />
* When doing scenario transitions in a campaign, if the side definitions don't matchup exactly the game tends to give unintelligible error messages. For instance, if one is transitioning and there is an AI side which does not have a leader (but it has a starting location in the map) it must have "no_leader = yes" in its [side] tag, or else when the team_builder objects enter stage two, the game will try to create a unit with an empty type, and the constructor of class unit will fail giving the error message "game::game_error tried to create unit with empty type". This really needs to be much better. For instance, giving a line number of a problem, or suggesting that no_leader should be used. (Note that, it is often possible to debug problems like this by turning on --log-debug=team_construction at commandline but I doubt that there are any users that know about this, you would only learn from reading the C++... without such debug info, fixing things like this can literally take hours.) For that matter it's some question why the team_builder doesn't just stop trying to build a unit when it sees it doesn't have enough info...<br />
<br />
* Recursive macros break the game, and not by stack overflow but by exhausting the heap (which usually takes forever on modern OSes and results in memory thrashing <i>before</i> resource starvation). It would be nice if we could catch this and report an error.<br />
<br />
Many WML users are students or beginners to programming, so any improvements to WML error reporting are a big help for their learning process -- many veterans would appreciate the extra help as well. In part we need fixits here and there, but more broadly we need a smarter system that can figure out what's really wrong when things go wrong, and give helpful suggestions.<br />
<br />
<br />
=== Don't store all events in savefiles ===<br />
<br />
Currently all events in a scenario are stored twice in each savefile (in [snapshot] and it [replay_start]), specially on wml-heavy umc content this can make savefiles quite big, The proposed solution is that events are not stored in savefiles when they are the same as in the sceanrio/era/modification file (e.g. not dynamically added with actionwml).<br />
In detail the savefile should for all events that have an id and are not generated dynamically ([event] actionwml), store dummy events like <br />
[event]<br />
id=event_id<br />
is_unchanged=yes<br />
[/event]<br />
in the savefiles (instead of full events), where the event is then read from the scenaro/era/modification tag when the scenario is loaded. This should only apply to campaigns/modifications/mp maps/eras that require download (which is usually the case for campaigns but not for mp maps)<br />
<br />
<br />
See also https://gna.org/bugs/?23917<br />
<br />
== Multiplayer/replay features ==<br />
<br />
=== Gold graph ===<br />
<br />
Make a graph feature, presumably in a dialog, that helps depict the history of a Wesnoth match for the benefit of spectators of a live game or replay. For example it could display army value, army + bank value, number of villages tagged, luck swings... This would also be particularly helpful for AI tweaking development, by helping coders identify the AI's weaknesses and strengths in the context of a particular scenario.<br />
<br />
A possible nice feature for this purpose would be to allow the user to click on the graph at key points which would trigger the "back to turn" mechanism to jump back in the replay, or automatically play the replay forwards from the beginning to that point... etc.<br />
<br />
Related email on dev-talk: https://mail.gna.org/public/wesnoth-dev/2014-02/msg00089.html<br />
<br />
=== Automatically link up to wesnothd server on a local network ===<br />
<br />
The feature request was to use ZeroConf technology, if available, to publish info of a running wesnothd instance to other machines on a local network. Then when they are searching to connect to an unofficial server, they won't have to learn the IP address.<br />
<br />
The extra code should be guarded with preprocessor flags so that ZeroConf does not become a mandatory build dependency of Wesnoth.<br />
<br />
https://gna.org/bugs/?13703<br />
<br />
=== Stock MP chat messages for language-independent communication ===<br />
<br />
Wesnoth has seen a large acceptance by international communities, and many players don't speak English. It would be nice we could have a set of messages that could be chatted, but which will be translated by our translators and displayed always in the current locale on each client, to make it easier to communicate in mp games / on the mp server.<br />
<br />
The easy part is adding the messages. They would ideally be stored in a WML file in the core data dir so they can be easily modified and translated like all other mainline WML. The hard part is devising the interaction mechanism so that a player can easily make use of these stock messages in the MP lobby or in game.<br />
<br />
<br />
== GUI2 framework ==<br />
<br />
As of 1.13.1+dev, GUI2's core and framework (including widgets and their APIs) are completely unmaintained. A prospective maintainer would have to be able to understand pretty advanced C++ making abundant use of template-based abstractions (most notably, the event handling code is almost completely implemented this way), multiple inheritance, and polymorphism. There is also some missing functionality (such as combo boxes and modeless dialogs) that the author did not get to implement, as well as work-in-progress functionality (new listbox and tooltip implementations, not enabled by default).<br />
<br />
=== Multi-line textbox, single-line textbox improvements ===<br />
<br />
GUI2 has a single-line textbox widget which works well for most purposes but falls short in a few niche cases. In particular, it's not possible to make it read-only without entirely disabling the user's ability to interact with the contents, which is inconvenient in places like the Game Paths dialog (or the About dialog in version 1.13.2 and later).<br />
<br />
The widget also does not support multi-line contents. For this reason, GUI2 dialogs that present multi-line (read-only) text to the user make use of the scroll_label+clipboard button convention introduced by shadowm in 1.12.x (see the Chat Log, Gamestate Inspector, and WML load error dialogs for examples). This has the obvious limitations of not allowing the user to select specific portions of the contents, and not supporting editing.<br />
<br />
=== Generic tab_container widget ===<br />
<br />
After 1.13.1 was released, shadowm discovered a trick to implement tabbed dialogs in GUI2, involving a general widget bug fix to make children consider their parents' visibility when processing events, and an addition to the stacked_widget API enabling users to hide all but a single active layer.<br />
<br />
The About dialog introduced in version 1.13.2 will use a horizontal listbox in combination with a stacked_widget, but ideally we should be able to reuse this pattern in more places without having to clutter up dialog implementations with the requisite code to synchronize the listbox and the stacked_widget's states. This hypothetical tab_container widget would combine the listbox and the stacked_widget to implement tabs in a cleaner fashion without having to expose the implementation details to individual dialog instances.<br />
<br />
By all means, this should be an EasyCoding task, but GUI2's unusual code layout and API design conventions (compared to the rest of Wesnoth) ''may'' make it harder than it's supposed to be.<br />
<br />
If you want to work on this, you should poke shadowm to make sure you don't duplicate efforts as he is working on a GUI2 port of the Preferences dialog and may take up this task after a while if no-one has done so first.<br />
<br />
=== GUI2 themable in-game UI ===<br />
<br />
The "themable" in-game UI refers to the gameplay UI including the sidebar, menu/status bar, and map view. Most of it is not implemented using proper GUI1 widgets at all, except for the various interactive buttons, menus (which are floating GUI1 listboxes), and the command and chat input boxes (floating GUI1 textboxes).<br />
<br />
Moving the theme UI to GUI2 would probably require finishing up theme support in GUI2 (which is largely incomplete), and may benefit from a rendering engine redesign as is also required for porting the game to SDL 2; in particular so that the GUI aspects are more clearly separated from the game board rendering aspect.<br />
<br />
'''Note:''' This task is here solely for the sake of completeness, and only somebody very well versed in the intricacies of Wesnoth's game rendering code and GUI2 should '''ever''' try to take it up. (And even then, I would not want to be anywhere near that person when they inevitably break down and fling one or more tables around upon realizing the sheer complexity of the task. -- shadowm)<br />
<br />
== Add-ons server and client ==<br />
<br />
=== Passphrase hashing ===<br />
<br />
The add-ons server (campaignd) uses a very dumb authentication scheme for uploading add-ons where an author sets a passphrase (or gets a random passphrase assigned by the game) the first time they upload to the server, and subsequent uploads of the same add-on are only allowed if the passphrase matches. The passphrase is stored in clear text form both client and server-side, which has [http://forums.wesnoth.org/viewtopic.php?f=62&t=42663 various implications].<br />
<br />
Ideally, the server would store all passphrases in a hashed form.<br />
<br />
=== Port redirection ===<br />
<br />
Wesnoth's MP server (wesnothd) uses a single port (15000) for servicing most requests and redirects clients to different ports according to the game client's version number. This allows the server administrators to reassign ports freely without having to modify the game client.<br />
<br />
The add-ons server (campaignd) and client instead have a hardcoded default port number for a Wesnoth version series. For example, 1.12.x is hardcoded to default to 15006. This becomes a maintainability issue as add-on servers get decommissioned and their port numbers can't be reused for fear of misdirecting obsolete client versions to the wrong instance with incompatible add-ons.<br />
<br />
=== Incremental upgrades and uploads ===<br />
<br />
Right now, whenever a new add-on version is published, players must download (or conversely, upload) the whole contents, even if the differences between both versions amount to a single line change. This is not a problem with small add-ons, but there is a substantial number of large (> 10 MiB) add-ons on the server. Large downloads and uploads are inconvenient for players on slow or metered connections, and also increase the server's traffic requirements.<br />
<br />
Ideally, for each add-on it attempts to upgrade or upload, the add-ons client should be able to send the server its local list of files with hashes, so that the server can identify which files need to be transmitted to/by the client, all archived on the fly like normal add-on downloads/uploads. The obvious downside is that the server would need to calculate an add-on's file hashes on demand, increasing CPU usage when servicing upgrades or uploads; this could be avoided by generating its file hashes when servicing uploads instead.<br />
<br />
See also: https://gna.org/bugs/?19972 (for uploads only; there should be one for downloads too but I couldn't find it -- shadowm)<br />
<br />
== Multiplayer server and client ==<br />
<br />
=== Improve server WML processing ===<br />
<br />
The server uses its own custom WML implementation separate from the main engine's to store and process WML objects for games -- see [[WesnothdDesign#simple_wml]]. This is intended to improve performance and reduce wesnothd's memory footprint with large numbers of games and complex scenarios. However, the simple_wml code sometimes messes up WML attribute translatability, resulting in MP scenarios/campaigns being untranslated for non-hosts in networked MP (http://gna.org/bugs/?22989). Fixing this is not easy due to the aforementioned performance requirements.<br />
<br />
== Data structures ==<br />
<br />
=== Make the Lua state persistent ===<br />
<br />
Wesnoth is designed to make all data files as simple and human-readable as possible, including savefiles. WML is designed so that it is always very easy to write the entire WML state as plaintext and reload it correctly. There are many benefits of this, but especially <br />
<br />
* It makes it easy for beginners / students to read the files and see what's going on<br />
* It makes it easy for us to debug problems without needing special tools to read the savefiles<br />
* It makes it easy to edit save files to work around any possible bugs. Users have even reported opening savefiles from version 1.10 in microsoft word (!) to correct minor bugs in mp and resaving them, as a policy for tournament games.<br />
<br />
If you use lua in your scenarios, things become more complicated because we can't save the lua state at all. If you rely on lua variables to save information, then when your scenario is saved and reloaded those variables will get wiped. The wesnoth engine provides places for lua to hook into "onsave" and "onload" so that the scenario developer can write their own serialization routines, but this is just punting the problem to scenario developers.<br />
<br />
A nice solution to this would be if we properly serialized the lua state alongside the WML in a savefile. The most suitable solution is probably this library: https://github.com/fnuecke/eris<br />
It would require significant additional work as well to find a good way to serialize the C functions that we expose to lua, and the userdata like units held by lua.<br />
<br />
More info here: http://lua-users.org/wiki/PlutoLibrary<br />
<br />
[[Category:Development]]<br />
[[Category:Future]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=Maintenance_tools&diff=58114Maintenance tools2017-01-17T20:40:07Z<p>Jyrkive: Since the tools have been converted to Python 3, I changed the page to refer to that version.</p>
<hr />
<div><div class="floatright"> __TOC__ </div><br />
<br />
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction & unit packs, and other WML resources. These<br />
are:<br />
<br />
; wmlscope: a cross-reference lister, useful for finding unresolved macro and resource-file references.<br />
<br />
; wmllint: a utility for sanity-checking WML syntax and porting your old WML to the current version of WML. <br />
<br />
; wmlindent: a utility for reindenting WML to a uniform style.<br />
<br />
; GUI.pyw: a graphical interface<br />
<br />
== General Information ==<br />
<br />
You will need a Python 3 interpreter on your system to use these tools. Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download<br />
from http://www.python.org. You will also need to know how to run command-line tools on your system.<br />
<br />
If you're working with ubuntu you might have to install the package wesnoth-1.10-tools (or the convenient version).<br />
sudo apt-get install wesnoth-1.10-tools<br />
<br />
<br />
All three tools will require you to supply a <i>directory list</i>. This is a set of directories containing the WML files you want to work on.<br />
<br />
This page is intended as ducementation for users. A developer's-eye discussion of the design constraints on these tools, and their limitations, can be found here [https://mail.gna.org/public/wesnoth-dev/2010-02/msg00078.html].<br />
<br />
<u>Note to Windows Users:</u> This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then "cmd" or "command" depending on your version of Windows.<br />
<br />
Example uses:<br />
python wmllint path\to\files<br />
python wmlindent path\to\files<br />
<br />
Another example:<br />
"C:\Program Files\Python3.6\python.exe" data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns <br />
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).<br />
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.<br />
<br />
<br />
'''A convenient way of running wmllint''' on Linux (Debian Lenny) and Windows (Xp) in comparison, '''Linux''':<br />
<br />
Assuming we're working with wesnoth 1.9 or more advanced versions (1.10 in this case).<br />
python3 /usr/share/games/wesnoth/1.10/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.10/data/core ~/.local/share/wesnoth/1.10/data/add-ons/A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log<br />
I have these commands inside of a file named<br />
wmllint_dryrun_ASC.sh<br />
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing<br />
bash wmllint_dryrun_ASC.sh<br />
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.<br />
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:<br />
E:\Python34\python.exe E:\Programme\Wesnoth_1.8_svn\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.8_svn\data\core E:\Programme\Wesnoth_1.8_svn\userdata\data\add-ons\A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log<br />
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.<br />
Since python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:<br />
"C:\Programs\Battle for Wesnoth 1.8\data\tools\wmllint"<br />
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only "python" or "C:\Python34\python.exe" or the like and interpret the error messages that you get. If you get an "unknown command", python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.<br />
<br />
== wmlscope ==<br />
<br />
The main use for <tt>wmlscope</tt> is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist. These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic. They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.<br />
<br />
<tt>wmlscope</tt> also checks macro invocations for consistency. It will complain<br />
if a macro is called with the wrong number of arguments. In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.<br />
<br />
<table border="1"><tr><br />
<th>Type</th><br />
<th>Meanining</th><br />
<th>Formals requiring this type</th><br />
<th>Literals of this type</th><br />
</tr><br />
<tr><br />
<td>side</td><br />
<td>a single side number</td><br />
<td>SIDE, *_SIDE, SIDE[0-9]</td><br />
<td>a numeric or "global"</td><br />
</tr><br />
<tr><br />
<td>numeric</td><br />
<td>a numeric integer literal</td><br />
<td>SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION</td><br />
<td>\-?[0-9]+</td><br />
</tr><br />
<tr><br />
<td>percentage</td><br />
<td>a percentage</td><br />
<td>*PERCENTAGE</td><br />
<td>a numeric or 0\.[0-9]+</td><br />
</tr><br />
<tr><br />
<td>position</td><br />
<td>a single x,y coordinate</td><br />
<td>POSITION, *_POSITION, BASE</td><br />
<td>-?[0-9]+,-?[0-9]+</td><br />
</tr><br />
<tr><br />
<td>span</td><br />
<td>a set of coordinates or coordinate ranges</td><br />
<td>*_SPAN</td><br />
<td>a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+</td><br />
</tr><br />
<tr><br />
<td>alliance</td><br />
<td>a set of side numbers</td><br />
<td>SIDES, *_SIDES</td><br />
<td>a span, or the empty string</td><br />
</tr><br />
<tr><br />
<td>range</td><br />
<td>an attack range</td><br />
<td>RANGE</td><br />
<td>"melee" or "ranged"</td><br />
</tr><br />
<tr><br />
<td>alignment</td><br />
<td>an alignment keyword</td><br />
<td>ALIGN</td><br />
<td>"lawful" or "neutral" or "chaotic"</td><br />
</tr><br />
<tr><br />
<td>types</td><br />
<td>a set of unit types</td><br />
<td>TYPES</td><br />
<td>a shortname, name, or anything that contains spaces and matches no other type</td><br />
</tr><br />
<tr><br />
<td>terrain_pattern</td><br />
<td>a set of terrain codes to filter</td><br />
<td>ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING</td><br />
<td>a terrain_code or name</td><br />
</tr><br />
<tr><br />
<td>terrain_code</td><br />
<td>a single terrain code, perhaps with overlay</td><br />
<td>TERRAIN*, *TERRAIN</td><br />
<td>a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?</td><br />
</tr><br />
<tr><br />
<td>shortname</td><br />
<td>a terrain code or a short, capitalized variable name</td><br />
<td></td><br />
<td>[A-Z][a-z][a-z]?</td><br />
<tr><br />
<td>name</td><br />
<td>a name or identifier</td><br />
<td>NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR</td><br />
<td>anything without spaces that matches no other type</td><br />
</tr><br />
<tr><br />
<td>optional_string</td><br />
<td>a string value (may be empty)</td><br />
<td>ID_STRING, NAME_STRING, DESCRIPTION, IPF</td><br />
<td>a string, or the empty string</td><br />
</tr><br />
<tr><br />
<td>string</td><br />
<td>a nonempty string not matching any of the preceding types</td><br />
<td>STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT</td><br />
<td>a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type</td><br />
</tr><br />
<tr><br />
<td>stringliteral</td><br />
<td>a string in doublequotes or a translated string</td><br />
<td></td><br />
<td>".*" or _.* but not _[a-z].*</td><br />
</tr><br />
<tr><br />
<td>image</td><br />
<td>an image path, perhaps with [[ImagePathFunctionWML|image path functions]]</td><br />
<td>*IMAGE, PROFILE</td><br />
<td>[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)</td><br />
</tr><br />
<tr><br />
<td>sound</td><br />
<td>a music or sound filename</td><br />
<td>MUSIC, SOUND</td><br />
<td>string ending with ".wav" or ".ogg"</td><br />
</tr><br />
<tr><br />
<td>filter</td><br />
<td>[[FilterWML|WML filter]]</td><br />
<td>FILTER</td><br />
<td>any non-quoted string containing "="</td><br />
</tr><br />
<tr><br />
<td>WML</td><br />
<td>arbitrary WML fragment</td><br />
<td>WML, *_WML</td><br />
<td>any non-quoted string containing "=", or the empty string</td><br />
</tr><br />
<tr><br />
<td>affix</td><br />
<td>a prefix, suffix, or infix for a variable name</td><br />
<td>AFFIX, *AFFIX, POSTFIX, ROTATION</td><br />
<td>a shortname or name, or the empty string</td><br />
</tr><br />
<tr><br />
<td>any</td><br />
<td>anything</td><br />
<td>*VALUE, [ARS][0-9]</td><br />
<td>anything</td><br />
</tr><br />
</table><br />
<br />
If the actual argument is a macro call {.*}, then it matches any formal Otherwise, if the formal has an identifiable type, <tt>wmlscope</tt> will complain if the actual literal does not match it.<br />
<br />
The argument type check only works in macro calls that fit on a single line.<br />
<br />
<tt>wmlscope</tt> has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers. Invocations for the most commonly useful reports it generates are included in <i>data/tools/Makefile</i> of the source distribution. Here are some of those reports:<br />
<br />
; make unresolved: Report on unresolved macro calls and resource references; also report macro argument-type mismatches. (This is what you are most likely to want to do). <br />
<br />
; make all: Report all macro and resource file references, not just unresolved ones.<br />
<br />
; make collisions: Report on duplicate resource files.<br />
<br />
For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of <tt>wmlscope</tt>'s options. Some of the more advanced options will require you to understand <br />
[http://docs.python.org/lib/re-syntax.html Python regular expressions].<br />
<br />
; -h, --help: Emit a help message and quit<br />
; -c, --crossreference: Report resolved macro references (implies -w 1)<br />
; -C, --collisions: Report duplicate resource files <br />
; -d, --deflist: Make definition list. (This one is for campaign server maintainers.)<br />
; -e <i>regexp</i>, --exclude <i>regexp</i>: Ignore files matching the specified regular expression. <br />
; -f <i>dir</i>, --from <i>dir</i>: Report only on macros defined under <i>dir</i><br />
; -l, --listfiles: List files that will be processed<br />
; -r <i>ddd</i>, --refcount=<i>ddd</i>: Report only on macros with references in exactly <i>ddd</i> files.<br />
; -u, --unresolved: Report unresolved macro references<br />
; -w, --warnlevel: Set to 1 to warn of duplicate macro definitions<br />
; --force-used reg: Ignore reference count 0 on names matching regexp<br />
; --extracthelp: Extract help from macro definition comments.<br />
<br />
== wmllint ==<br />
<br />
<tt>wmllint</tt> is a tool for migrating your WML to the current version. It handles two problems: <br />
<br />
* Resource files and macro names may change between versions of the game. <tt>wmllint</tt> knows about these changes and will tweak your WML to fit where it can.<br />
<br />
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. <tt>wmllint</tt> will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.<br />
<br />
<tt>wmllint</tt> also performs various sanity-checking operations, reporting:<br />
<br />
* unbalanced tags<br />
* strings that need a translation mark and do not have them<br />
* strings that have a translation mark and should not<br />
* translatable strings containing macro references <br />
* filter references by description= (id= in 1.5) not matched by an actual unit<br />
* abilities or traits without matching special notes, or vice-versa<br />
* consistency between recruit= and recruitment_pattern= instances<br />
* double space after punctuation in translatable strings.<br />
* unknown races or movement types in units<br />
<br />
<tt>wmllint</tt> takes a directory-path argument specifying the WML directories to work on. It will modify any cfg and map files under those directories that need to be changed. Here is a summary of its options:<br />
<br />
; -h, --help: Emit a help message and quit.<br />
; -d, --dryrun: List changes but don't perform them.<br />
; -v, --verbose: Set verbosity; more details below.<br />
; -c, --clean: Clean up -bak files.<br />
; -D, --diff: Show diffs between unconverted and unconverted files.<br />
; -r, --revert: Revert the conversion from the -bak files.<br />
; -n, --nolift: Suppress lifting, do sanity checks only <br />
<br />
The verbosity option works like this:<br />
<br />
; -v: lists changes.<br />
; -v -v: warns of maps already converted.<br />
; -v -v -v: names each file before it's processed.<br />
; -v -v -v -v: shows verbose parse details (developers only).<br />
<br />
The recommended procedure is this:<br />
<br />
# Run it with --dryrun first to see what it will do.<br />
# If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.<br />
# Eyeball the changes with the --diff option.<br />
# Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.<br />
# Test the conversion.<br />
# Use either --clean to remove the -bak files or --revert to undo the conversion.<br />
<br />
Additionally, wmllint tries to locate a spell checker on your system and spell-checks storyline and message strings. It will work automatically with either aspell, myspell, or ispell provided you have the <tt>enchant.py</tt> Python library installed.<br />
<br />
== wmlindent ==<br />
<br />
Call with no arguments to filter WML on standard input to reindented WML on<br />
standard output. If arguments are specified, they are taken to be files to be<br />
re-indented in place; interrupting will be safe, as each reindenting<br />
will be done to a copy that is atomically renamed when it's done. This<br />
code never modifies anything but blank lines and leading and trailing whitespace on non-blank lines.<br />
<br />
The indent unit is four spaces. Absence of an option to change this is<br />
deliberate; the purpose of this tool is to prevent style wars, not encourage<br />
them.<br />
<br />
If you don't apply this tool to your own WML, the mainline-campaign maintainers <br />
will do it when and if your code is accepted into the tree.<br />
<br />
Note: This tool does not include a parser. It will produce bad results on WML<br />
that is syntactically unbalanced. Unbalanced double quotes that aren't part<br />
of a multiline literal will also confuse it. You will receive warnings<br />
if there's an indent open at end of file or if a closer occurs with<br />
indent already zero; these two conditions strongly suggest unbalanced WML.<br />
<br />
== GUI.pyw ==<br />
<br />
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 2.7.0 (the 2.6.x series doesn't include the ttk widgets, and as such is unsuitable for this script).<br />
<br />
If you're on Linux, be sure to have installed the ''python-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:<br />
sudo apt-get install python-tk<br />
<br />
To start it, just double click on the GUI.pyw file. The interface is pretty much self explanatory, and allows you to run wmllint, wmlscope and wmlindent, modify their options, select an add-on, and save the tools' output as a text file.<br />
<br />
[[Category:Create]]<br />
[[Category:Tools]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=EasyCoding&diff=58080EasyCoding2016-11-25T04:28:01Z<p>Jyrkive: Unassigned the map diff tool from Henry Stone. He has most likely given up.</p>
<hr />
<div>'''We are permanently updating this page. Parts marked by <s>strikeout</s> will likely be deleted shortly.<br />
<br />
This page is here to document easy to do coding tasks. It is not here to double the feature request database, and should only be filled by people that know the code well enough to judge the difficulty of a given task. <br />
<br />
If you are such a person, you should feel free to edit this page.<br />
<br />
If you're not, you should post a feature request and discuss your idea on the forum or IRC. A coder with better knowledge of the code might give you the green light to add your feature here.<br />
<br />
Anybody should feel free to add "clues" to any tasks, that is entry points, traps to avoid, person to contact to discuss and so on.<br />
<br />
If you plan to work on a feature, write your name at the bottom of the feature, with the date. Note that if you are too long at working on a feature we'll "free" it back (that is if you're not working on it. If you have problems implementing it, just tell us....)<br />
<br />
If none of these are to your liking, feel free to check our [http://gna.org/bugs/?group=wesnoth bug tracker] with a broad spectrum of tasks.<br />
<br />
== Engine related features ==<br />
<br />
=== Make a "map diff" tool to visualizes changes between two version of a map ===<br />
<br />
Maintainers of maps often make small tweaks for balance purposes. For someone who doesn't know the map like the back of their hand it may be very difficult to spot exactly what changed. There isn't any really good way to do this right now -- using the 'diff' tool at command line doesn't give good results for .map files.<br />
<br />
Such a tool would be helpful for someone who maintains a map pool containing maps made by others, also for someone who wants to browse a repository and examine balance changes on several maps, which could be instructive for someone who wants to make and balance their own maps.<br />
<br />
There's any number of ways to actually do this, one might be to have a feature in the map editor that takes the name of another map and adds map labels to indicate what hexes changed. It could also be a command accessible in debug mode when playing the game, or it could be an additional command-line argument (--diff [map1] [map2] ?) for the game. (It could be a standalone application but that's probably going to end up with much more work for you.)<br />
<br />
== MP related features ==<br />
<br />
=== Add possibility to switch off mod dependency checking ===<br />
MP modifications can define dependencies. Currently there's no way to force a "broken" configuration, although overzealous use of the system by UMC authors could make this feature useful. Add an option to the MP game creation screen to switch on/off dependency checking. Ask lipk (forum)/lipkab (IRC).<br />
<br />
=== Make a header for the add-on when we organize scenarios by add-on in the MP create dialog ===<br />
<br />
Using an established GUI technique to signal when one block of scenarios for an add-on ends and the next begins would make it much easier to use this dialog, if the user has several add-ons.<br />
<br />
See one proposal here:<br />
https://gna.org/bugs/?21653<br />
<br />
Note that we still intend to move the mp lobby to gui2, it might make sense to adress this while doing that porting (which is not easy)<br />
<br />
<br />
=== Timer pause button ===<br />
<br />
Longstanding feature request here:<br />
https://gna.org/bugs/?16733<br />
<br />
Request is for either a button, or a simple chat command like :pause, :unpause, to allow players to put the timer on hold if they are interrupted.<br />
<br />
Ideally would be accompanied with a multiplayer-only scenario attribute "number_of_pauses" which decreases each time a client pauses the game. Pause should use a blindfold object to black out the screen, and send a chat message automatically when it occurs / resume occurs. For an example of how it might look, start a local hotseat mp game and enable the Preferences->General->Turn Dialog option. (This behavior was introduced in this commit: https://github.com/wesnoth/wesnoth/commit/bab03d554bea92f1ce6f585ef1123202173a3491)<br />
<br />
Wintermute/happygrue also has other ideas about how the timer should/could work. For example, instead of their client ending their turn, the networked opponents could be required to "confirm" the time out. (This is probably a networked MP-only idea.)<br />
<br />
A very fancy implementation might instead of a counter, allow the other players to vote on whether a pause request is granted.<br />
<br />
Make sure that the paused client still responds to network commands, so that it is possible to kick a person who pauses and then doesn't ever unpause.<br />
<br />
=== Add a "concede" button ===<br />
<br />
Often times in mp, players concede the game before their leader is actually killed. When the game ends 'officially' the map is revealed, which can sometimes be instructive for the losing player. Additionally, it makes it easier to analyze the results of many games, for purposes of balancing, or for maintaining a ladder. So while it may seem small, this would actually be a pretty useful feature.<br />
<br />
== WML-related features ==<br />
<br />
=== Support variable village healing, variable poison properties ===<br />
<br />
See a feature request here:<br />
<br />
https://gna.org/bugs/index.php?20919<br />
<br />
=== Enhancements to Existing Actions ===<br />
<br />
<s>* [fire_event] id=X - allow firing an event by id instead of by name</s><br />
<br />
=== Events Manipulation ===<br />
<br />
* [event] persistent="yes" - creates an event that lasts for the rest of the campaign<br />
* [disable_event][/disable_event] disables the event within which it appears (useful with conditionals). A more complex take on first_time_only=yes/no.<br />
''This is largely possible with event IDs, but could be useful when multiple copies of an event should be floating around''<br />
<br />
=== Game Information ===<br />
<br />
* [query_location] - queries a location from the user<br />
** variable<br />
** [filter_location]<br />
''Regarding [query_location]: probably a better name would be choose_location. This is used when a wml event is running and needs to get a target location hex from the user. For example, you right click on a catapult unit and choose the menu item "fire Catapult." Now a [message] says to click on any target within 5 hexes. Now [choose_location] is encountered and the mouse cursor changes to a target sign (possibly customizable). If the user's mouse drifts outside of the allowed locations it changes to a cancel icon. If the operation is cancelled then the variable is cleared and no location stored. --Sapient''<br />
<br />
''This can be implemented in lua by using wesnoth.game_events.on_mouse_action and wesnoth.syncronize_choice''<br />
<br />
==== Automatically stored variables ====<br />
<br />
* $side_info - auto-stored side information<br />
* $game_info - auto-stored game information<br />
* $map_info - auto-stored map information<br />
<br />
== Improvements to AI ==<br />
<br />
=== Existing bugs or undesirable features ===<br />
<br />
Most of these are probably relatively easy to fix, although we can, of course, not guarantee that.<br />
<br />
* Feature: Convert E_NOT_REACHED_DESTINATION from an error to a warning.<br />
<br />
* Bug: Adding a candidate action mid-turn does not include it where it belongs in terms of its evaluation score. Instead, it gets added at the end of the candidate action list and might not be executed on the current turn, because other CAs with lower scores get evaluated first.<br />
<br />
*Bug?: not all options/parameters of wesnoth.find_cost_map() work as described [[LuaWML:Pathfinder#wesnoth.find_cost_map|here]]. I don't know if that is a bug in the code or if the description on that page is wrong.<br />
<br />
* Bug: ai.get_new_enemy_dst_src and related functions have several problems:<br />
** They use the current moves of the enemy units, which generally are all zero.<br />
** Hexes occupied by other enemy units count as not reachable.<br />
** Working around this with a Lua wrapper makes this actually slower than ai_helper.get_enemy_dst_src, which makes the functions pretty much useless, as speed is the only thing speaking for them.<br />
<br />
=== Add new features to the AI ===<br />
<br />
* Add a cost function to the C++ path finding code that takes avoided hexes into account, that is, hexes defined in an [ai][avoid] tag onto which the AI never moves. Also add an option to the Lua wesnoth.find_path() function with which using this cost functions can be turned on and off.<br />
** The background behind this task is the way how the default AI's move-to-targets candidate action works. It does path finding for units without taking avoided hexes into account, and ''afterwards'' eliminates moves for which the end point falls onto an avoided hex. As a result, some units might not move at all, even if it is, in principle, easily possible to move around an avoided area. The inclusion of the new path-finding in the AI's move-to-targets phase could therefore be a follow-up task.<br />
<br />
* Add a Lua function ai.get_individual_attacks() that returns all the single attacks the side can do (as opposed to attack combinations). This needs to include an option to recalculate and take the current situation on the map into account (making the function believe that the game state has changed).<br />
<br />
* Set up an option the allows us to exclude units from the move-to-targets phase similar to [filter_own] for the combat phase (other than setting ai_special=guardian, which has a number of other side effects). Actually, having this option for all CA's would be nice.<br />
<br />
* Change the aspects "passive_leader", "passive_leader_shares_keep", "leader_ignores_keep" to work with multiple leaders. The idea is to change the type of those aspects from yes/no to string so both "passive_leader=yes" and "passive_leader=leader_id_1,leader_id_2" is possible.<br />
<br />
* Write a script that automatically changes WML from the deprecated [ai][target] to the new [ai][goal] syntax. Ideally, this would be part of wmllint.<br />
** There are [http://wiki.wesnoth.org/AiWML#Deprecated_AI_Targeting_Aspects more deprecated AI aspect] which should eventually be added to this, but the [target] -> [goal] change would be a good and important start.<br />
<br />
== GUI2 features ==<br />
<br />
Although mordante developed a new GUI system, GUI2, some dialogs still use the older GUI1 and could be improved or transferred. mordante has since disappeared, leaving GUI2's core code unmaintained; shadowm and vultraz should be able to answer most GUI2 (and even GUI1) related questions in his absence.<br />
<br />
* Information on the wiki can be found under [[GUIToolkit]] on the wiki.<br />
* The source code is under src/gui/<br />
* All mainline GUI2 WML resides in data/gui/default/<br />
<br />
<br />
* Make games sortable in the lobby (open slots, total number of players, era, XP modifier, gold per village, fog/shroud)<br />
<br />
<br />
== Miscellaneous ==<br />
<br />
=== Debug mode ===<br />
<br />
* New debug command functionality (setting additional status.variables, possibly terrain)<br />
<br />
=== wesnoth-optipng ===<br />
<br />
For lossless png compression we use wesnoth-optipng, a script using an external toolchain (ImageMagick, OptiPNG, Advdef). Perhaps the performance could be improved by using PNGOut instead of advdef. This task includes<br />
* a quick profiling of compression ratio and decompression speed for both tools<br />
* if it provides a higher compression while not significantly slowing down decompression, adapt wesnoth-optipng to use PNGOut<br />
<br />
== Bugs ==<br />
<br />
<br />
<br />
== See Also ==<br />
<br />
[[NotSoEasyCoding]]<br />
<br />
[[Category:Development]]<br />
[[Category:Future]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=CodingStandards&diff=57991CodingStandards2016-10-27T19:43:56Z<p>Jyrkive: Banned function-style casts.</p>
<hr />
<div>Wesnoth uses C++ that is portable to C++ compilers targeting various commonly used platforms.<br />
<br />
== C++ version ==<br />
<br />
Wesnoth uses C++ conforming to C++11. At the moment C++14 compiler support is still not widely available, therefore C++14 is not allowed.<br />
<br />
== Formatting ==<br />
<br />
When working on C++ for Wesnoth, indent your code with a tab character. After fully indenting, if you still need to line up the text with a specific character on the line above, you may further align it using space characters.<br />
<br />
You may use long lines.<br />
<br />
== Evil things to avoid ==<br />
<br />
=== Avoid implicit conversions ===<br />
<br />
Make all constructors which only take one argument that is of a different type to the class <tt>explicit</tt>.<br />
<br />
Do not use <tt>operator T()</tt> (where <tt>T</tt> is a type) to allow an implicit conversion to a different type. For example:<br />
<br />
t_string(const std::string&);<br />
<br />
This can cause many situations where a temporary t_string is implicitly created and then gets destroyed unexpectedly (reference [https://gna.org/bugs/index.php?20360 bug #20360]).<br />
<br />
=== Do not declare class data members as non-private ===<br />
<br />
It's okay to have a ''struct'' with only public members, if that's what you want.<br />
<br />
However, once something is a ''class'' with private data members, do not add public (or even protected) data members to the class. Doing this breaks encapsulation and can cause all kinds of confusing and evil things to happen.<br />
<br />
=== Destructors must not throw exceptions ===<br />
<br />
Do not allow exceptions to propogate from a destructor. Doing that is always bad in C++. Any code which does it should be treated as a bug and fixed. Doing this is a very easy way to cause memory leaks and crashes.<br />
<br />
It's okay to have exceptions thrown inside a destructor, just as long as you catch() them inside the destructor too and don't allow them to propagate out.<br />
<br />
In C++11, any destructor which throws an exception causes the program to be immediately terminated (except in special cases).<br />
<br />
You can read more about the issue here: http://c2.com/cgi/wiki?BewareOfExceptionsInTheDestructor<br />
<br />
== Naming ==<br />
<br />
=== End non-public class data members with an underscore ===<br />
<br />
All non-public data members of classes should have their names terminated with an underscore, to show that they are a class member. This makes for more readable code, once one is familiar with the convention.<br />
<br />
== Idioms ==<br />
<br />
=== Use references when a value may not be NULL ===<br />
<br />
If a value passed to a function can never be <tt>NULL</tt>, use a reference instead of a pointer. For example:<br />
<br />
void my_function(T& obj);<br />
<br />
rather than<br />
<br />
void my_function(T* obj);<br />
<br />
This more clearly shows prospective users of the function that <tt>obj</tt> may never be <tt>NULL</tt>, without them having to consult documentation or the implementation of the function.<br />
<br />
=== Use the const keyword ===<br />
<br />
The <tt>const</tt> keyword in C++ allows interfaces to more clearly specify how they treat objects. <br />
Always use <tt>const</tt> when you are not going to modify an object. For example:<br />
<br />
void my_function(const T& obj);<br />
<br />
This shows to the caller that <tt>obj</tt> will not be modified. If <tt>my_function()</tt> may modify <tt>obj</tt>, then use the following instead:<br />
<br />
void my_function(T& obj);<br />
<br />
Likewise, if a variable is not changed after initialization, make it <tt>const</tt>, and mark member functions as <tt>const</tt> if they do not modify their object.<br />
<br />
=== Know the behavior of const references when types differ ===<br />
<br />
If you assign something to a <tt>const</tt> reference of a different type, if necessary (if the type is different but there is a conversion) the compiler will create a temporary and guarantee it lasts for the lifetime of the reference. So<br />
<br />
char c = 0;<br />
const int& i = c;<br />
c = 5;<br />
<br />
will result in c == 5 and i == 0, which may not be what you expect.<br />
<br />
=== Write exception-safe code ===<br />
<br />
Wesnoth code should be exception-safe, even if you do not use exceptions directly. That is, you should be able to assume that an exception is thrown almost anywhere from within the code, with well-defined results (i.e. no resource leaks).<br />
<br />
Code that uses a pattern like the following is bad:<br />
<br />
{<br />
SDL_Surface* image = IMG_Load("image.bmp");<br />
...some code, which uses 'image'...<br />
SDL_FreeSurface(image);<br />
}<br />
<br />
The code may throw an exception, and <tt>image</tt> will never be freed. Instead, use wrapper objects which free the object in their destructor.<br />
<br />
For <tt>SDL_Surface</tt> objects, the <tt>surface</tt> type is used throughout the Wesnoth source code to achieve this purpose. So you could rewrite the above code as follows:<br />
<br />
{<br />
surface image(IMG_Load("image.bmp"));<br />
...some code, which uses 'image'...<br />
} ''the image is automatically freed here when 'image' is destroyed<br />
<br />
Instead of allocating memory directly using <tt>new[]</tt> or <tt>malloc()</tt>, use language-provided containers, such as vector.<br />
<br />
Similarly, avoid writing <tt>delete</tt> explicitly in your code. Oftentimes using a smart pointer or similar instead will improve the readability of your code, by making it obvious that you are managing memory correctly even if an exception is thrown. If you were deleting a pointer which is a member variable of an object, using a smart pointer instead may simplify your code by eliminating the need to write an explicit destructor in some cases.<br />
<br />
For more information, you can read about the (important) RAII idiom: http://c2.com/cgi/wiki?ResourceAcquisitionIsInitialization<br />
<br />
=== Do not use sprintf ===<br />
<br />
The <tt>sprintf()</tt> function does not check whether or not it is writing past the end of the space allocated. This is a security problem if someone other than the person running the game can cause <tt>sprintf()</tt> to write very long strings. In Wesnoth, this untrusted data could come potentially from other players in a multiplayer game, or from downloaded add-ons. Instead you should use <tt>snprintf()</tt> with the second argument being the <tt>sizeof</tt> of the buffer that will hold the result.<br />
<br />
== Standard C++ to avoid ==<br />
<br />
=== Do not use 0 or NULL when you mean nullptr ===<br />
<br />
Several Wesnoth developers, including Dave, find the number 0 to be very ambiguous when used in a non-numeric context. In keeping with the precedent that has already been established in the Wesnoth source code, you should avoid using literal zero for initializing and/or comparing null pointers.<br />
<br />
=== Do not use standard io functions ===<br />
This implies the std::fstream class and fopen. These functions do not support utf8 in windows which is our standard encoding for filepaths. Use our custom filesystem functions (filesystem.hpp) instead.<br />
<br />
== C legacy to be avoided ==<br />
<br />
=== Use std::array instead of C-style Arrays ===<br />
<br />
C-style arrays are very efficient, but their interface is ugly. Use <tt>std::array</tt> instead.<br />
<br />
=== Do not use C-style or function-style casts ===<br />
<br />
The following code,<br />
<br />
if(i->second.side() == (size_t)player_number_) {<br />
<br />
is considered bad practice in C++ since a C-style cast is overpowered -- if types change around it could end up casting away constness, or performing an implementation-defined data reinterpretation (basically a C-style cast is a compiler-generated combination of <tt>static_cast</tt>, <tt>reinterpret_cast</tt>, and <tt>const_cast</tt>).<br />
<br />
This syntax,<br />
<br />
if(i->second.side() == size_t(player_number_)) {<br />
<br />
is also a cast, and [http://stackoverflow.com/a/32224 semantically identical] to the C-style cast. It should also be avoided.<br />
<br />
Good programming style is to use the least powerful tool available that does what you want. For example:<br />
<br />
if(i->second.side() == static_cast<size_t>(player_number_)) {<br />
<br />
Alternatively, a constructor call may be used for non-built-in types.<br />
<br />
''Note: there may be some obscure cases where a C-style cast is desirable, such as converting a pointer to an integer type of unspecified size.''<br />
<br />
=== Do not use #define for constants ===<br />
<br />
<tt><nowiki>#</nowiki>define foo X</tt> is not a typesafe approach to define constants. Instead, you can something like the following (in an anonymous namespace) to achieve the same goal in a typesafe fashion.<br />
<br />
namespace {<br />
const T foo = X;<br />
}<br />
<br />
== Documentation ==<br />
<br />
=== Document config preconditions and postconditions ===<br />
<br />
In the Wesnoth code you will commonly encounter a data container type known as <tt>config</tt>, which contains hierarchical string data (such as WML contents or game settings). The tagged ''children'' of the <tt>config</tt> object and their string ''attributes'' are arranged in an ordered and mapped format, internally implemented using the C++ STL.<br />
<br />
Because <tt>config</tt> data is utilized in so many ways and places, it can be difficult to track across the scope of the entire program. Thus, you should document all public functions that take/return <tt>config</tt> objects, specifying content expectations and updating any related entries in the [[ReferenceWML]] wiki pages. In particular, if your function requires a <tt>config</tt> parameter, specify where/how the <tt>config</tt> object should be created. This will be a great help to any future coders who need to call or modify your function.<br />
<br />
=== Doxygen ===<br />
<br />
See [[Doxygen]] for tips on how to comment the code, so that Doxygen can nicely document it.<br />
<br />
== See also ==<br />
<br />
* [[HackingWesnoth]]<br />
<br />
[[Category:Development]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=LuaWML/Misc&diff=57984LuaWML/Misc2016-10-23T17:22:48Z<p>Jyrkive: Mention that wesnoth.random() uses the random seed in map generation scripts</p>
<hr />
<div>This page describes miscellaneous [[LuaWML]] objects and helpers.<br />
<br />
==== wesnoth.game_config ====<br />
<br />
Contrarily to the other values of the ''wesnoth'' table, ''game_config'' is simply a proxy table. Its fields offer an interface to the global settings of Wesnoth:<br />
<br />
* '''version''': string (read only)<br />
* '''base_income''': integer (read/write)<br />
* '''village_income''': integer (read/write)<br />
* '''poison_amount''': integer (read/write)<br />
* '''rest_heal_amount''': integer (read/write)<br />
* '''recall_cost''': integer (read/write)<br />
* '''kill_experience''': integer (read/write)<br />
* '''last_turn''': integer (read/write) turn limit, maximum number of turns<br />
* '''debug''': boolean (read only)<br />
* '''mp_debug''': boolean (read only)<br />
* '''campaign_type''': string (read only) Indicates what type of game this is, e.g. "multiplayer"<br />
* '''mp_settings''': table. In a multiplayer game, this is a proxy table which gives read only access to all MP-only configuration options which appear as attributes of [multiplayer] tag in a save game file:<br />
** '''active_mods''': string (read only) A list of all active modifications<br />
** '''hash''': string (read only) A hash of mp data<br />
** '''mp_campaign''': string (read only) Name of mp campaign<br />
** '''mp_scenario''': string (read only) ID of this mp scenario<br />
** '''mp_scenario_name''': string (read only) Name of this mp scenario<br />
** '''scenario''': string (read only) MP lobby title <br />
** '''difficulty_define''': string (read only) The campaign difficulty string for an mp campaign<br />
** '''mp_village_gold''': integer (read only) <br />
** '''mp_village_support''': integer (read only) <br />
** '''mp_num_turns''': integer (read only) <br />
** '''mp_era''': string (read only) The id of the chosen era<br />
** '''mp_eras''': string (read only) A list of all era ids<br />
** '''mp_fog''': boolean (read only) <br />
** '''mp_shroud''': boolean (read only) <br />
** '''mp_random_start_time''': boolean (read only) <br />
** '''experience_modifier''': integer (read only) <br />
** '''mp_use_map_settings''': boolean (read only) <br />
** '''mp_countdown''': boolean (read only) Whether the timer is enabled<br />
** '''mp_countdown_action_bonus''': integer (read only) <br />
** '''mp_countdown_init_time''': integer (read only) <br />
** '''mp_countdown_reservoir_time''': integer (read only) <br />
** '''mp_countdown_turn_bonus''': integer (read only) <br />
** '''observer''': boolean (read only) <br />
** '''shuffle_sides''': boolean (read only) <br />
** '''savegame''': boolean (read only) Whether this is a reloaded game<br />
** '''side_users''': string (read only) List of how sides are assigned to users (at game start)<br />
* '''era''': table. A proxy table for the entire era tag corresponding to the current era. Its id will always match wesnoth.game_config.mp_settings.mp_era<br />
Note: wesnoth.game_config.mp_settings, and wesnoth.game_config.era, will only exist if wesnoth.game_config.campaign_type == "multiplayer"<br />
<br />
<br />
-- Poison a bit weak? Let's boost it!<br />
wesnoth.game_config.poison_amount = 15<br />
<br />
-- Warn users when they use bad settings:<br />
if (wesnoth.game_config.mp_settings.shuffle_sides) then<br />
wesnoth.message("Warning: This scenario is not intended to be played with shuffle sides!")<br />
end<br />
<br />
-- Collect basic info about the current era:<br />
local era = wesnoth.game_config.era<br />
local helper = wesnoth.require("lua/helper.lua")<br />
local count = 0<br />
wesnoth.set_variable("era_name", era.name)<br />
for multiplayer_side in helper.child_range(era, "multiplayer_side") do<br />
count = count + 1<br />
wesnoth.set_variable("faction" .. tostring(count) .. "_name", multiplayer_side.name)<br />
wesnoth.set_variable("faction" .. tostring(count) .. "_recruit", multiplayer_side.recruit)<br />
end<br />
wesnoth.set_variable("num_factions", count)<br />
<br />
==== wesnoth.get_era ====<br />
<br />
* '''wesnoth.get_era(''id'')'''<br />
<br />
A function which takes one argument, an era id, and returns the entire era tag corresponding to that id. For a list of valid era ids, use wesnoth.game_config.mp_settings.mp_eras.<br />
<br />
==== wesnoth.current ====<br />
<br />
As with ''game_config'', ''current'' is a proxy table. Its fields are getter for game-related properties:<br />
<br />
* '''side''': integer (read only)<br />
* '''turn''': integer (read only)<br />
* '''event_context''': WML table with attributes ''name'', ''x1'', ''y1'', ''x2'', ''y2'', and children ''weapon'', ''second_weapon'', describing the trigger for the current event. {{DevFeature1.13|2}} ''unit_x'', ''unit_y'' contain the location of the primary unit involved in the event. Currently the only case where this cam be different from ''x1'' and ''y1'' are enter_hex and exit_hex events.<br />
* '''synced_state''' {{DevFeature1.13|0}} whether the current code runs in a synced contex, this returns a string, the possible values are:<br />
** '''synced''' the current code runs on all mp clients, this is the normal context, in which all gamestatechaning actions should take place.<br />
** '''unsynced''' for example during '''select''' events or during the calculation of a wesnoth.theme_items, don't change the gamestate in this context because the current code only runs on one machine, so changign the gamestate here will cause OOS. Typical things to do here are UI related things, or entering the synced state via '''[do_command]'''<br />
** '''local_choice''' the current code was invoked by wesnoth.synchronize_choice and runs only on one local client to calculate the return value for wesnoth.synchronize_choice. You cannot enter the synced context with '''[do_command]''' now.<br />
** '''preload''' we are currently running a preload event or an even earlier event, this behaves similar to '''local_choice'''<br />
<br />
wesnoth.message(string.format("Turn %d, side %d is playing.", wesnoth.current.turn, wesnoth.current.side))<br />
<br />
==== wesnoth.synchronize_choice ====<br />
<br />
* '''wesnoth.synchronize_choice(''function'', [''ai_function''])'''<br />
* {{DevFeature1.13|2}} '''wesnoth.synchronize_choice([''description''], ''function'', [''ai_function''], [''for_side''])'''<br />
<br />
Recovers a WML table that was computed on one client only or was stored in a replay. The actual computation is performed by the function passed as the first function argument, assuming that the client is the side currently playing. For all the other clients, the function will not be called. An optional second function can be passed; if present, it will be used instead of the first one when the client happens to be an AI (hence not enable to interact with a user interface).<br />
<br />
local result = wesnoth.synchronize_choice(<br />
function()<br />
-- Called only on the client handling the current side, if it is a human.<br />
local choice = 0<br />
wesnoth.show_dialog(<br />
some_dialog_cfg, nil,<br />
function()<br />
choice = wesnoth.get_dialog_value "some_list"<br />
end)<br />
return { value = choice }<br />
end,<br />
function()<br />
-- Called only on the client handling the current side, if it is an AI.<br />
return { value = math.random(some_list_size) }<br />
end)<br />
wesnoth.message(string.format("Selected item: %d", result.value))<br />
<br />
Note: The return value must be a valid WML table - the same kind of thing you could store to a WML variable, and not, for instance, a proxy unit, anything else that uses metatables, or a lua table with another table as the value of a string attribute. Unlike other lua functions, wesnoth.synchronize_choice will NOT throw an error if the table is invalid, but will silently strip out the contents of any invalid subtag.<br />
<br />
When wesnoth is running in debug mode (e.g. --debug flag on command line) synchronize_choice will chat a "Lua Warning" if it finds that the table returned was partially invalid.<br />
<br />
{{DevFeature1.13|2}}<br />
This function takes now takes these arguments:<br />
* An optional translatable string descibing the type of the user input. This is displayed to the other clients while one client executes the passeed function. Defaults to "input".<br />
* A function: (as before).<br />
* An optional function: for ai sides (as before).<br />
* An optional integer: on which side the function should be evaluated. Defaults to the currently playing side. If the specified side is empty/null controlled the engine will choose another side.<br />
<br />
==== wesnoth.synchronize_choices ====<br />
<br />
* '''wesnoth.synchronize_choices([''description''], ''function'', [''default_function''], [''for_sides''])'''<br />
<br />
{{DevFeature1.13|2}} Similar to the singular form above, this function takes a function parameter and evaluates it on the specified sides. It takes the following arguments:<br />
* An optional translatable string descibing the type of the user input. This is displayed to the other clients while the specified clients execute the passeed function. Defaults to "input"<br />
* A function that evaluates the choice returning a wml table. Unlike above, this function is called for ai and human sides (use if controller == "ai" for checking if it is a ai side)<br />
* An optional function for evaluating the choice in case this side was null controlled. If this function is called, it is called on all clients (unlike the first passed function) defaults to a function returning an empty table.<br />
* An array of integers specifying on which sides this function should be evaluated, the function is evaluated on all passed sides, each side may only appear once in this array. All specified sides execute the function simultaniously.<br />
<br />
This function returns a table with integer as keys and WML tables as values. the keys are the sides where that action was evaluated. The values are the values computed by the passed function.<br />
Example:<br />
[event]<br />
name = "start"<br />
[lua]<br />
code = <<<br />
wesnoth.set_variable("input1",nil)<br />
local result = wesnoth.synchronize_choices(<br />
function()<br />
local option1 = T.option { message = "No", T.command { T.set_variable { name = "input1", value = "No"}}}<br />
local option2 = T.option { message = "Yes", T.command { T.set_variable { name = "input1", value = "Yes"}}}<br />
wesnoth.fire(T.message{ message = "Are you sure you want to play this game?", option1, option2})<br />
return { value = wesnoth.get_variable("input1") }<br />
end,<br />
{1,2})<br />
wesnoth.set_variable("input1",nil)<br />
wesnoth.message("Player 1 wants to play: " .. result[1].value)<br />
wesnoth.message("Player 2 wants to play: " .. result[2].value)<br />
>><br />
[/lua]<br />
[/event]<br />
<br />
==== wesnoth.get_image_size ====<br />
<br />
* '''wesnoth.get_image_size(''filename'')'''<br />
<br />
Returns the width and height of an image.<br />
<br />
local w, h = wesnoth.get_image_size "units/transport/galleon.png"<br />
<br />
==== wesnoth.compare_versions ====<br />
<br />
* '''wesnoth.compare_versions(''version1'', ''operator'', ''version2'')'''<br />
<br />
Takes two versions strings and an operator, returns whether the comparison yields true.<br />
Follows the same rules like the #ifver preprocessor statement.<br />
<br />
local function version_is_sufficient(required)<br />
if not wesnoth.compare_versions then return false end<br />
return wesnoth.compare_versions(wesnoth.game_config.version, ">=", required)<br />
end<br />
local required = "1.9.6"<br />
if not version_is_sufficient(required) then wesnoth.message(string.format(<br />
"Your BfW version is insufficient, please get BfW %s or greater!", required)) end<br />
<br />
==== wesnoth.have_file ====<br />
<br />
* '''wesnoth.have_file(''filename'')'''<br />
<br />
Checks if the file (not necessarily a Lua file) or the directory passed as argument exists. Returns true if the file exists, false otherwise. Follows the same rules like the #ifhave preprocessor statement.<br />
<br />
-- Does the user have installed the UMC Music Book 1?<br />
local umc_music = wesnoth.have_file( "~add-ons/UMC_Music_Book_1/_main.cfg" )<br />
-- and if we want to check for the folder?<br />
local music_folder = wesnoth.have_file( "~add-ons/UMC_Music_Book_1/" )<br />
<br />
==== wesnoth.debug ====<br />
<br />
* '''wesnoth.debug(''wml_table'')'''<br />
<br />
Takes a userdata with metatable wml object or a wml table and dumps its content into a pretty string.<br />
wesnoth.set_variable("number", 100)<br />
local vconfig = wesnoth.tovconfig({ key = "$number", another_key = true,<br />
{"a_subtag", { a_key_in_the_subtag = "foo" }}<br />
})<br />
wesnoth.message(wesnoth.debug(vconfig))<br />
wesnoth.message(wesnoth.debug(vconfig.__literal))<br />
<br />
==== wesnoth.get_time_stamp ====<br />
<br />
* '''wesnoth.get_time_stamp()'''<br />
<br />
This function retrieves the current time stamp, that is the amount of milliseconds passed from when the SDL library was initialized. It takes no arguments and returns an integer.<br />
'''WARNING:''' this function uses the same code as [set_variable] time=stamp, and so it is MP-unsafe. It is provided only for benchmark purposes and AI development, although it should work inside wesnoth.synchronize_choice() as well.<br />
local stamp = wesnoth.get_time_stamp()<br />
<br />
==== wesnoth.random ====<br />
<br />
* '''wesnoth.random([''m'', [''n'']])'''<br />
<br />
{{DevFeature1.13|2}} This function returns a random number generated by the synced random generator which is also used by [set_variable]rand= (and thus also by helper.rand). This function has the same interface as [http://www.lua.org/manual/5.2/manual.html#pdf-math.random math.random] so it can take 0, 1 or 2 arguments.<br />
<br />
In map generation scripts, the values returned by this function depend on the seed the map author has input (if any).<br />
<br />
==== wesnoth.compile_formula ====<br />
<br />
* '''wesnoth.compile_formula(''formula'')'''<br />
<br />
{{DevFeature1.13|5}} Compiles a [[Wesnoth Formula Language]] formula into a Lua callable userdata. The callable value returned by this function can take an optional table as argument, which defines variables available to the formula. It can also take a unit proxy as argument, which evaluates the formula in that unit's context just as if it had been used in a [[StandardUnitFilter]].<br />
<br />
A compiled formula can be converted back to valid code using the built-in <code>tostring</code> function.<br />
<br />
==== wesnoth.eval_formula ====<br />
<br />
* '''wesnoth.eval_formula(''formula'', [''variables''])'''<br />
<br />
{{DevFeature1.13|5}} Equivalent to <syntaxhighlight lang='lua' inline>wesnoth.compile_formula(formula)(variables)</syntaxhighlight>. It can also evaluate an already-compiled formula, which is equivalent to calling the formula.<br />
<br />
==== wesnoth.name_generator ====<br />
<br />
* '''wesnoth.name_generator(''type'', ''definition'', [''additional params''])'''<br />
<br />
{{DevFeature1.13|5}} Constructs a name generator for use in generating names using either the Markov chain algorithm used in older versions of Wesnoth or the context-free grammar generator used since 1.13.5. The type parameter indicates which algorithm to use (either markov or cfg). The definition can be a string, just like it would be in a config file, or it can be formatted as a table. Additional parameters may be passed, depending on the type of generator. The function returns a callable userdata, which will return a new name each time it is called (with no parameters).<br />
<br />
* '''Markov chain''': A Markov chain generator works by analyzing a list of input names and noticing tendencies in the way the letters are strung together. It can then apply those tendencies to produce new similar names that were not in the original list. Longer lists give better results. The definition is a list of names, formatted either as a comma-separated string or as an array-like table. The Markov generator can take two additional parameters. The first additional parameter is ''chain_size'', and the second is ''max_length''.<br />
** A value greater than 1 for the chain_size causes the analyzer to consider the words in chunks, which is similar to analyzing them syllable by syllable instead of letter by letter. The default chain size is 2, meaning that the analyzer treats words as consisting of 2-character syllables.<br />
** The max_length places a cap on the total length of the name. The default value is 12 characters.<br />
* '''Context-free grammar''': A [[context-free grammar]] is a way of specifying how strings can be constructed. The definition may be specified as a multi-line string, just as described in the preceding link, or it can be formatted as a table where the keys are non-terminals and the values are what they expand to. The expansion of each non-terminal can be formatted either as a |-separated list as described in the preceding link, or as an array-like table. (Mixing the two forms is permissible too.) The context-free generator has no additional parameters.<br />
<br />
==== helper.set_wml_tag_metatable ====<br />
<br />
* '''helper.set_wml_tag_metatable{}'''<br />
<br />
Sets the metable of a table so that it can be used to create subtags with less brackets. Returns the table. The fields of the table are simple wrappers around table constructors.<br />
<br />
T = helper.set_wml_tag_metatable {}<br />
W.event { name = "new turn", T.message { speaker = "narrator", message = "?" } }<br />
<br />
==== helper.modify_unit ====<br />
<br />
* '''helper.modify_unit(''filter'', ''keys'')'''<br />
<br />
Modifies all the units satisfying the given filter (argument 1) with some WML attributes/objects (argument 2). This is a Lua implementation of the [http://www.wesnoth.org/macro-reference.xhtml MODIFY_UNIT] macro.<br />
<br />
helper.modify_unit({ id="Delfador" }, { moves=0 })<br />
<br />
'''Note:''' This appears to be less powerful than the [modify_unit] tag and may be removed at some point in the future.<br />
<br />
==== helper.move_unit_fake ====<br />
<br />
* '''helper.move_unit_fake(''unit'', ''destination'')'''<br />
<br />
Fakes the move of a unit satisfying the given filter (argument 1) to the given position (argument 2). This is a Lua implementation of the [http://www.wesnoth.org/macro-reference.xhtml MOVE_UNIT] macro.<br />
<br />
helper.move_unit_fake({ id="Delfador" }, 14, 8)<br />
<br />
==== helper.rand ====<br />
<br />
* '''helper.rand(''spec'')'''<br />
<br />
(A shortcut to [[InternalActionsWML#.5Bset_variable.5D|set_variable's rand=]] since math.rand is an OOS magnet and therefore disabled.) Pass a string like you would to set_variable's rand=.<br />
<br />
create a random unit at (1, 1) on side=1 :<br />
wesnoth.put_unit(1, 1, { type = helper.rand("Dwarvish Fighter,Dwarvish Thunderer,Dwarvish Scout") })<br />
<br />
==== helper.round ====<br />
<br />
* '''helper.round(''n'')'''<br />
<br />
Unlike other languages (Python, Perl, Javascript, ...), Lua does not include a round function. This helper function allows rounding numbers, following the "round half away from zero method", see Wikipedia [[http://en.wikipedia.org/wiki/Rounding_numbers#Round_half_away_from_zero]]. Returns the number rounded to the nearest integer.<br />
<br />
-- this number will be rounded up<br />
helper.round(345.67) -- returns 346<br />
-- this one will be rounded down<br />
helper.round(543.21) -- returns 543<br />
-- an integer stays integer<br />
helper.round(123) -- returns 123<br />
-- works also for negative numbers<br />
helper.round(-369.84) -- returns -370<br />
helper.round(-246.42) -- returns -246<br />
<br />
==== helper.shuffle ====<br />
<br />
* '''helper.shuffle(''array'')'''<br />
* {{DevFeature1.13|2}} '''helper.shuffle(''array'', ''[random_function]'')'''<br />
This function randomly sorts in place the elements of the table passed as argument, following the Fisher-Yates algorithm. It returns no value.<br />
'''WARNING:''' this function uses Lua's math.random(), and so it is '''not''' MP-safe. It is provided mainly for AI development, although it should work inside wesnoth.synchronize_choice() as well.<br />
<br />
local locs = wesnoth.get_locations( { terrain="G*" } )<br />
helper.shuffle( locs )<br />
<br />
{{DevFeature1.13|2}}<br />
This function now uses the synced RNG by default and should not cause OOS anymore. It is also possible now to pass a different random generator as a second argument; a random generator is a function that takes two integers <i>a</i> and <i>b</i> and returns a random integer in the range [<i>a</i>,<i>b</i>]. For example, math.random can be passed to get the 1.12 behavior:<br />
<br />
local locs = wesnoth.get_locations( { terrain="G*" } )<br />
helper.shuffle( locs, math.random )<br />
<br />
[[Category: Lua Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=SingleUnitWML&diff=57881SingleUnitWML2016-08-28T10:08:07Z<p>Jyrkive: Documented the invulnerable status.</p>
<hr />
<div>{{WML Tags}}<br />
== How to describe a single unit ==<br />
<br />
This tag, '''[unit]''', describes a single unit on the map, for example Konrad.<br />
It is different from the [unit_type] in [units], which describes a class of units. However it takes many of the same keys and thus can generally override the inherited properties from the associated [unit_type].<br />
<br />
[unit] can be used inside [side] ([[SideWML]]) for units present at start of the scenario, or as [[DirectActionsWML]] for units created during the game. (It is also used in save-files.)<br />
<br />
The following keys are recognized:<br />
* '''type''': the ID of the unit's unit type. See [[UnitTypeWML]].<br />
<br />
* '''parent_type''': overrides '''type''' if this is present. This is likely of little use to WML authors; it is automatically generated when needed by the game (to keep track of some [unit_type][variation]s).<br />
<br />
* '''side''': the side that the unit is on. It has to be an existing side, even if the unit is created in a variable.<br />
<br />
* '''gender''': can be set to male or female to designate the gender of the unit. Default is male, but if the unit has only a female variant it will be female.<br />
<br />
* '''x''', '''y''': the location of the unit. By default ( see '''placement''') if a location isn't provided and the side the unit will belong to has a recall list, the unit will be created on the recall list.<br />
<br />
* '''placement''': How the unit should be placed: can be one value or a comma-separated list of values. Default value is 'map,leader' for a leader given directly in [side], "" otherwise. By default, 'map,recall' is implicitly appended to the end of the list.<br />
** '''map''': If x,y are explicitly given and point to a valid on-map location - try to place the unit at the nearest free location to there, never overwriting existing units. Successful if x,y are given and a valid on-map vacant location near it can be found.<br />
** '''leader''': Try to place unit near the leader, if leader is not present or is in recall list - try to place unit near the start location for this side. Successful if a valid on-map vacant location can be found near leader or near start location.<br />
** '''recall''': Place unit on recall list. Always successful. <br />
** '''map_overwrite''': If x,y are explicitly given and point to a valid on-map location - try to place unit at this location, if there was a unit there - overwriting it, without firing events.<br />
** '''map_passable''': If x,y are explicitly given and point to a valid on-map location - try to place unit at this location; if the hex is of an impassable terrain for the unit being placed, or is already occupied by another unit, the new unit will be placed in the nearest vacant hex.<br />
** '''leader_passable''': Similar to "leader", with the additional restriction that the selected location is not impassable for the unit being placed.<br />
<br />
* '''to_variable''': creates the unit into the given variable instead of placing it on the map.<br />
<br />
* '''id''': a unique identifier for the unit. This is (usually) not displayed to the player, but is to be used only for identifying and filtering units. If not specified or when a unit is normally recruited, a random one will be generated for the unit to ensure that each unit has a unique ''id'' attribute. In older versions, the '''description''' attribute specified a unique ID. (The one instance when an id is displayed to the player is when the leader's id is used as the default for a [[SideWML|side]]'s '''current_player''' attribute.)<br />
<br />
* '''name''': the user-visible name of the unit. Note that the player may use the "rename unit" action to change this.<br />
<br />
* '''generate_name''': (default=yes) will generate a new name if there isn't one specifed for the unit, as if the unit were a freshly-recruited one<br />
<br />
* '''unrenamable''': if 'yes', the user-visible name of the unit cannot be changed by the player (which is only possible when the unit is on the player's side anyway).<br />
<br />
* '''traits_description''': the description of the unit's traits which is displayed. However if it is not specified explicitly, the unit's actual traits' names will be used instead, so it is normally not necessary to set this.<br />
<br />
* '''random_traits''': "no" will prevent random trait generation for units. You should only need to set this for placed nonleaders in multiplayer games or if you want to give a unit less traits than it would normally get for its unit type. When generating traits for a unit, first traits the unit has already been given are excluded. Then "musthave" traits (undead, mechanical) for the unit type are given. Then for leaders ('''canrecruit=yes''') traits that are not available to "any" (currently that's all of them to avoid a multiplayer OOS issue, but later will be restricted based on multiplayer play balance issues) are removed from consideration. Then traits are added randomly until the maximum allowed for the unit type is reached or there are no more available traits. Random traits can now be used in MP games but only when spawned in an event, so not for leaders and other units in the [side] definition.<br />
<br />
* '''random_gender''': "yes" will cause the gender of the unit with male and female variations to be male 50% of the time, female 50% of the time. If the unit has only one gender variant it will always be given the correct one.<br />
<br />
* '''canrecruit''': a special key for leaders.<br />
** '''no''': default. Unit cannot recruit.<br />
** '''yes''': unit can recruit.<br />
: Normally when a team controls no units with '''canrecruit=yes''', that team loses. However, even if your team has lost you continue to play with whatever units you still have until the scenario is over. Usually scenarios end when only one team is left with a leader that can recruit, but special victory conditions can be set up in campaigns. Normally you want to set the leader of a side with '''canrecruit=yes'''. If you don't want the leader to recruit, it is usually better to just not give him any unit types to recruit, than to make a special victory condition. Units with '''canrecruit=yes''' are exempt from upkeep costs. So that leaders do not need to be given the ''loyal'' trait.<br />
: More than one unit with '''canrecruit=yes''' for the same side (see [[SideWML]]) are allowed in single player, if the side is human-controlled.<br />
<br />
* '''extra_recruit''': a list of unit types which this unit can recruit in addition to the ones given by its [side]recruit=, only working for units with '''canrecruit=yes'''.<br />
<br />
* '''variation''': the variation of itself the unit should be created as.<br />
<br />
* '''upkeep''': the amount of upkeep the unit costs.<br />
** '''loyal''': no upkeep cost. Can be changed by the effect 'loyal' (see [[EffectWML]])<br />
** '''free''': synonymous with "loyal".<br />
** '''full''': unit costs ''level'' upkeep (see [[UnitTypeWML]]).<br />
** An integer can be used to set the upkeep cost to that number.<br />
** The default is "full".<br />
** Leaders (units with '''canrecruit=yes''') never pay upkeep no matter what upkeep is set to.<br />
** Normally you don't want to muck with this value. If you want to give a side units without upkeep costs, give those units the 'loyal' trait.<br />
<br />
* {{DevFeature1.13|0}} '''recall_cost''': the recall cost of this unit. Overrides the values specified by the unit's type ([[UnitTypeWML|[unit_type]]]), its side ([[SideWML|[side]]]), and the global [[GameConfigWML|[game_config]]] value. A value of -1 is equivalent to not specifying this attribute.<br />
<br />
* '''overlays''': a list of images that are overlayed on the unit.<br />
<br />
* '''goto_x''':, '''goto_y''': UI settings that control courses. Default is 0,0 i.e. the unit is not on a course.<br />
<br />
* '''hitpoints''': the HP of the unit. Default is the max HP for ''type''.<br />
<br />
* {{DevFeature1.13|6}} '''invulnerable''': a shorthand to set the ''invulnerable'' status.<br />
<br />
* '''hp_bar_scalling''': Overwrites the attribute in ([[GameConfigWML]]).<br />
<br />
* '''experience''': the XP of the unit. Default is 0.<br />
<br />
* '''moves''': number of movement points the unit has left. Default is the movement for its unit type.<br />
: '''Note:''' Do not assume that moves=max_moves on turns when a unit doesn't move. The wesnoth AIs sometimes manipulate the moves variable during its turn, for internal reasons.<br />
<br />
* '''attacks_left''': number of attacks the unit has left. Default is equal to the attacks key for its unit type, that usually is 1 (see ''max_attacks'' below).<br />
<br />
* '''resting''': whether the unit has not moved yet this turn. Used to decide whether to give a unit rest healing.<br />
<br />
* '''role''': used in standard unit filter ([[FilterWML]]). Can be set using [role] (see [[InternalActionsWML]]).<br />
<br />
* '''ai_special''': causes the unit to act differently<br />
** "guardian" the unit will not move, except to attack something in the turn it moves (so, it only can move if an enemy unit gets within range of it). Does the same as '''[status] guardian = 'yes''''.<br />
<br />
* '''facing''': which way the unit is facing (this only affects how the unit is displayed).<br />
** Possible values are '''se''', '''s''', '''sw''', '''nw''', '''n''', '''ne'''. Using '''sw''' is preferred for a "reversed" facing (looking to the left) and '''se''' for a normal (looking to the right) facing.<br />
<br />
* '''profile''': sets a portrait image for this unit. If the unit type already has a portrait set, this is used instead for this unit. When the unit advances, if the value of profile is different from the unit-type portrait, that value is preserved. If the profile field is empty or the same as the unit-type portrait, the level-advance changes the unit portrait to the default for the new level and type. See [[UnitTypeWML]] for the rules used for locating files.<br />
** "unit_image" if given instead of a filename, uses the unit's base image as the portrait (in the same manner that unit types without portraits do by default).<br />
<br />
* '''small_profile''': sets a small portrait image for this unit. See the '''profile''' attribute above for advancement and special values. As with [[UnitTypeWML]], the location heuristic of the '''profile''' attribute is disabled when the '''small_profile''' attribute is provided.<br />
<br />
* '''max_attacks''': Default is 1. The number of attacks the unit can have per turn.<br />
<br />
* '''max_experience''': The experience the unit needs to advance. If left out, this information will be taken from the unit's file.<br />
<br />
* '''max_hitpoints''': The maximum hitpoints the unit has. If left out, this information will be taken from the unit's file.<br />
<br />
* '''max_moves''': The maximum moves the unit has. If left out, this information will be taken from the unit's file.<br />
<br />
* '''animate''': if ''yes'', fades the unit in like when it's recruited/recalled.<br />
<br />
* '''[status]''' the status of the unit. This affects different features of the unit, for example whether the unit loses health each turn. Default for all keys is 'no', but this can be changed by the scenario or by special abilities (see [[AbilitiesWML]]). The Status Table displays the status of each unit using the three images '''misc/poisoned.png''', '''misc/slowed.png''' and '''misc/petrified.png'''; other keys do not appear in the Status Table.<br />
** '''poisoned''': if 'yes', the unit loses 8 HP each turn. See also ''heals'', ''cures'', [[AbilitiesWML]].<br />
** '''slowed''': if 'yes', the unit has 50% of its normal movement and does half damage. When the controller of the unit's turn is over, ''slowed'' is set to 'no'. <br />
** '''petrified''': if 'yes', the unit cannot move, attack, or be attacked.<br />
** '''uncovered''': if 'yes', the unit has performed an action (e.g. attacking) that causes it to no longer be hidden until the next turn.<br />
** '''guardian''': if 'yes', the unit will not move, except to attack something in the turn it moves (so, it only can move if an enemy unit gets within range of it). Does the same as '''ai_special = "guardian"'''.<br />
** '''unhealable''': if set to 'yes', the unit cannot be healed.<br />
** {{DevFeature1.13|6}} '''invulnerable''': if 'yes', attacks can't hit the unit.<br />
** One can add other keys to [status], but they must have boolean values. For example, a scenario can set unit.status.''my_custom_key'' to 'yes' or 'no'.<br />
<br />
* '''[variables]''' a set of variables that will be stored when this unit is stored (See [store_unit], [[InternalActionsWML]]). The attribute '''variable'''='''value''' means that when the unit is stored in the array ''unit'', the variable '''unit'''.variables.''variable'' will have the value ''value'' (See [[VariablesWML]]).<br />
<br />
* '''[modifications]''' changes that have been made to the unit.<br />
** '''[trait]''' a trait the unit has. Same format as [trait], [[UnitsWML]].<br />
** '''[object]''' an object the unit has. Same format as [object], [[DirectActionsWML]].<br />
** '''[advance]''' an advancement the unit has. Same format as [advancement], [[UnitTypeWML]]. Might be used if the unit type has some advancements, but this particular one is supposed to have some of them already taken. {{DevFeature1.13|2}} In 1.13.2 and later this has been renamed to [advancement], to match the UnitTypeWML tag of the same name.<br />
<br />
* '''[filter_recall]''' A leader can only recall those units which pass the SUF.<br />
**'''[[StandardUnitFilter]]''' tags and keys<br />
<br />
* '''unit_description''': overrides the unit type description for this unit. You will probably want to set up a ''post_advance'' [[EventWML|event]] to override the default description after promotions. Or better, use an object with a profile [[EffectWML|effect(s)]] to filter on unit type and change the unit description and/or portrait.<br />
<br />
* '''[event]''' The event is copied from this unit's wml description into the scenario. The event is carried along with the unit (it can advance etc) and inserted into every scenario where this unit is first created. A [unit][event] requires a non-empty id= attribute.<br />
<br />
* '''[ai]''' This affects how the computer will control this unit (currently only used by [[FormulaAI]]).<br />
** '''formula''': if set, the unit will execute this code during the "unit_formulas" stage, assuming that the "unit_formulas" stage has been enabled for this side<br />
** '''priority''', '''loop_formula''', '''[vars]''': see the [[FormulaAI]] documentation for details<br />
<br />
== See Also ==<br />
<br />
* [[UnitTypeWML]]<br />
* [[InternalActionsWMLUnitTags]]<br />
* [[ReferenceWML]]<br />
<br />
[[Category:WML Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=EventWML&diff=57848EventWML2016-08-05T08:40:34Z<p>Jyrkive: Undo revision 57847 by Jyrkive (talk)</p>
<hr />
<div>{{WML Tags}}<br />
== The [event] Tag ==<br />
<br />
This tag is a subtag of the [scenario], [unit_type] and [era] tags which is used to describe a set of [[ActionWML|actions]] which trigger at a certain point in a scenario. When used in a [scenario] tag (also includes [multiplayer], [tutorial] and [test]), the event only occurs in that scenario. When used in a [unit_type] tag, the event will occur in all scenarios in which a unit of that type appears in (only after such a unit appears during the scenario, however). When used in an [era], the event will occur in any scenario which is played using that era.<br />
<br />
This tag has keys and child tags that control when and if the event actions will be triggered. Most important of these is the '''name''' key. Without it, no error will be raised but the event will never fire. Therefore, from a practical standpoint, it can be considered mandatory. All of the others can be used or not and the event actions will fire either way.<br />
<br />
'''Lexicon side note:''' ''The word "event" in the [event] tag itself may be considered an abbreviation of the word "event handler" because it is technically not a game "event" but an event '''handler''' for the game events fired with the given 'name'. However, this distinction is usually unimportant in most discussions and the event handlers are therefore simply referred to as "events" in this documentation.''<br />
<br />
=== The 'name' Key (Mandatory) ===<br />
<br />
Usage:<br />
name=<value><br />
<br />
This key defines which game event or trigger your [event] tag will be handling. This 'name' key should not be confused with a descriptive comment; it is rather a precise value which must match the predefined game event's name to be valid.<br />
<br />
The '''name''' key can accept a list of comma separated values describing when the event will be triggered.* These values may be either predefined event types or custom event names not matching any predefined type.<br />
<br />
For example:<br />
<br />
name=attacker misses,defender misses<br />
<br />
''* Note that unless you use [[#first_time_only|first_time_only=no]], the event will fire only once, '''not''' once for each listed type.''<br />
<br />
All predefined event types are listed here along with a description of when this value will cause the event to be triggered. Any value ''not'' listed here is a custom event name which can be triggered only by a '''[fire_event]''' tag somewhere else. <br />
<br />
Spaces in event names can be interchanged with ''underscores'' (for example, '''name=new turn''' and '''name=new_turn''' are equivalent).<br />
<br />
==== Predefined Events Without Filters ====<br />
<br />
These events do not take filter parameters (except [filter_condition] which works for all events).<br />
<br />
===== preload =====<br />
<br />
Triggers before a scenario 'prestarts' and when loading a savegame -- before anything is shown on the screen at all. Can be used to set up the [[LuaWML|Lua]] environment: loading libraries, defining helper functions, etc.<br />
<br />
'''Note:''' Unlike prestart and start, the preload event '''must be able to fire more than once!''' This is because it is triggered each time a savegame is loaded in addition to the initial time when it loads before the scenario 'prestart'. This means that it is effectively ''mandatory'' to have the [[#first_time_only|first_time_only=no]] key value in a preload event. <br />
<br />
===== prestart =====<br />
<br />
Triggers before a scenario 'starts' -- before anything is shown on the screen at all. Can be used to set up things like village ownership. For things displayed on-screen such as character dialog, use '''start''' instead.<br />
<br />
'''Note:''' ''This value makes the [[#first_time_only|first_time_only]] key irrelevant since, by definition, it can only fire once.''<br />
<br />
===== start =====<br />
<br />
Triggers after the map is shown but before the scenario begins -- before players can 'do' anything.<br />
<br />
'''Note:''' ''This value makes the [[#first_time_only|first_time_only]] key irrelevant since, by definition, it can only fire once.''<br />
<br />
===== new turn =====<br />
<br />
Triggers at the start of every turn (not side turn). See also [[#first_time_only|first_time_only=no]]. Before any events of this type trigger, the value of the WML variable '''turn_number''' is set to the number of the turn that is beginning.<br />
<br />
===== turn end =====<br />
<br />
Triggers at the end of every turn (not side turn). See also [[#first_time_only|first_time_only=no]]. The WML variable '''side_number''' will contain the side that ended their turn.<br />
<br />
===== turn ''X'' end =====<br />
<br />
Triggers at the end of turn ''X''.<br />
<br />
===== side turn =====<br />
<br />
Triggers when a side is about to start its turn. Before events of this type trigger, the value of the WML variable '''side_number''' is set to the number of the side of the player about to take their turn. This is before any healing takes place for that side, before calculating income, and before restoring unit movement and status.<br />
<br />
===== ai turn =====<br />
<br />
Triggered just before the AI is invoked for a side. This is called after ''side turn'', and thus the WML variable '''side_number''' still holds the number of this side. Note that this event might be called several times per turn in case that fallbacks to human or droiding is involved. I.e. it happens at the middle of turn of human side 1 if the human player droids his side. It happens after the selection of ai to play the turn but before AI is told that new turn has come.<br />
<br />
'''Note:''' ''This event currently breaks replays since it is not explicitly saved in a replay and there is no AI involved in replays...''<br />
<br />
===== turn refresh =====<br />
<br />
Like '''side turn''', triggers just before a side is taking control but '''after''' healing, calculating income, and restoring unit movement and status.<br />
<br />
Note that the turn refresh event does occur on turn 1, even though healing, income and unit refreshing do not.<br />
<br />
===== turn ''X'' =====<br />
<br />
Triggers at the start of turn ''X''. It's the first side initialization event. <br />
<br />
Side initialization events go in the order of: <br />
<br />
# '''turn ''X''''' <br />
# '''new turn''' <br />
# '''side turn''' <br />
# '''side ''X'' turn''' <br />
# '''side turn ''X''''' <br />
# '''side ''X'' turn ''Y''''' <br />
# '''turn refresh''' <br />
# '''side ''X'' turn refresh''' <br />
# '''turn ''X'' refresh''' <br />
# '''side ''X'' turn ''Y'' refresh'''<br />
<br />
===== side ''X'' turn ''Y'' =====<br />
<br />
This event triggers at the start of turn ''Y'' of side X <br />
<br />
===== side ''X'' turn =====<br />
<br />
This event triggers at the start of any turn of side X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side turn ''X'' =====<br />
<br />
This event triggers at the start of any side on turn X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side X turn Y refresh =====<br />
<br />
This event triggers at the turn refresh for side X on turn Y<br />
<br />
===== side ''X'' turn refresh =====<br />
<br />
This event triggers at the turn refresh for side X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== turn ''X'' refresh =====<br />
<br />
This event triggers for any side at the refresh of turn X.<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side turn end =====<br />
<br />
Triggers after a side ends its turn. Like side turn, there are also some variations for specific combinations of side number and turn number. Here is the order in which the turn end events trigger:<br />
<br />
# '''side turn end''' <br />
# '''side ''X'' turn end''' <br />
# '''side turn ''X'' end''' <br />
# '''side ''X'' turn ''Y'' end''' <br />
# '''turn end''' <br />
# '''turn ''X'' end''' <br />
<br />
===== time over =====<br />
<br />
Triggers on turn ''turns''. (''turns'' is specified in [scenario])<br />
<br />
===== enemies defeated =====<br />
<br />
Triggers when all sides that are not defeated are allied and if there is at least one human (or human networked) side among them. Especially this event triggers in a situaltion that would normaly cause a victory due to enemies defeated. (regardless of whether this was disabled with victory_when_enemies_defeated=no). <br />
<br />
===== victory =====<br />
<br />
In this scenario, any tag of the form '''[endlevel] result=victory [/endlevel]''' will be automatically preceded by all actions in this tag. It helps debugging if the victory event allows you to safely advance to any of the possible next maps after using the ":n" command. Scenarios where key units are picked up before the victory, or where some action chosen earlier determines which map to advance to, make it hard to quickly test scenarios in a campaign. (See also: [endlevel], [[DirectActionsWML]]). This event is not synchonized in networked mp or in replays. The reason is that it is possible to have diffrent results in a mp game (one player wins so a '''victory''' event is fired on that client, other player loses so a '''defeat''' event is fired on his client)<br />
<br />
===== defeat =====<br />
<br />
In this scenario, any tag of the form '''[endlevel] result=defeat [/endlevel]''' will be automatically preceded by all actions in this tag. (See also [endlevel], [[DirectActionsWML]]). Like '''victory''', this event is not synchonized in networked mp or in replays.<br />
<br />
==== Predefined Events With Filters ====<br />
<br />
Filters (except [filter_condition] which is for all sorts of events) can be applied to the following event triggers (see [[FilterWML]]; see also below). The actions specified in the event tag will be executed only if the filter returns true. <br />
These event triggers are all actions by units ('''moveto''', '''attack''') or things that happen to units ('''recruit''', '''advance'''). When one of these events is triggered, the position of the active unit (referred to as the '''primary unit''') is stored in the variables '''x1''' and '''y1''' and the position of any unit that primary unit does something to is stored in the variables '''x2''' and '''y2''' (this unit is referred to as the '''secondary unit''' below). '' These units are also automatically stored in the variables 'unit' and 'second_unit' as if they had been stored using the '''[store_unit]''' tag. see [[SingleUnitWML]]. weapon and second_weapon variables are available inside attack, attacker_hits, defender_hits, die and last_breath events. See [[VariablesWML#Automatically_Stored_Variables|automatically stored variables]] for more information.<br />
<br />
===== moveto =====<br />
<br />
Triggers after the primary unit moves. Typically this is used when the primary unit gets to a particular location and a filter for the location of the primary unit is included; remember that this is the location that the primary unit lands on, not the location it started on or any location it travels on. If the unit moves to a village, the capture event will be fired before this event. <br />''An '''[allow_undo]''' tag anywhere within a moveto event will cancel any lack of undo functionality the event would have caused. Note that undo functionality will only move the unit back to its former location; it will not undo other changes to the game caused by the event. Thus it is up to the scenario designer to use this tag correctly.'' $x2 and $y2 refer to the hex the unit came from.<br />
<br />
===== sighted =====<br />
<br />
A '''sighted''' event is triggered by a unit becoming visible to a side (other than the unit's own side). This is mostly useful when the side seeing the unit uses [[fog of war]] or [[shroud]], but they still fire even when fog/shroud is not in use, and they do take into account the {{tag2|AbilitiesWML#The_.5Babilities.5D_tag|hides}} ability (for a moving unit and for ambushers). The ''primary unit'' is the unit that became visible, and the ''secondary unit'' belongs to the side that now sees the primary unit. In some cases, sighted events can be delayed from when they "should" occur. If that happens, the secondary unit will be filtered as if it was at the location where the event "should" have occurred, and ''x2,y2'' will store that location (not the current position of the secondary unit). To understand when sighted events fire, it is helpful to distinguish the times the acting unit sights other units from the times when the acting unit is sighted.<br />
<br />
An acting unit can sight other units when it is recruited, recalled, leveled, or moved, and when fog or shroud is cleared from occupied hexes as a result. In these cases, the acting unit is always the ''secondary unit''. For the first three actions, there are two events associated with the action; clearing occurs between these events, but any sighted events are fired after the second event. (For example, when a unit is recruited, the ''prerecruit'' event fires, then fog is cleared, then the ''recruit'' event fires, then ''sighted'' events fire.) For movement, the sighted events fire between ''enter_hex'' and ''exit_hex'' events, but sometimes sighted events are postponed until the moving unit reaches a good place to stop (e.g. not in an occupied hex). As a major exception to the above, players have the option to delay shroud (and fog) updates. If the player delays shroud updates, sighted events are also delayed until the shroud is updated.<br />
<br />
An acting unit can be sighted by other sides when it is recruited, recalled, leveled (in rare cases), or moved. In these cases, the acting unit is always the ''primary unit''. These events fire after sightings by the acting unit (unless the player delayed shroud updates). For the first two, the sighted event fires for all sides that can see the unit, other than the unit's own side (even if those sides use neither fog nor shroud). For leveling units, sides that could see the unit before it leveled are excluded. (This is why these events are rare &ndash; the leveling unit must have lost a [hides] ability as a result of leveling in order to be seen after, but not before, leveling.) For movement, a sighted event is fired for each side that could see the unit after movement, but not before. In particular, only the starting and ending hexes are considered; a unit that moves through seen hexes but ends movement in a fogged hex does not trigger a sighted event for itself. In all cases where the acting unit is sighted, a (single) ''secondary unit'' is chosen from the sighting team. This choice should be considered arbitrary, but units within their sight range of the acting unit are chosen in preference to units further away.<br />
<br />
Sighted events are not triggered by a ''hides'' ability becoming inactive, unless it becomes inactive due to that unit's movement or to that unit ambushing another. (To detect a ''nightstalk'' ability becoming inactive due to time of day, use a ''new_turn'' event. Custom ''hides'' abilities might need similar handling.)<br />
<br />
Sighted events have some special caveats for WML authors. First and foremost, {{tag|DirectActionsWML|allow_undo}} should generally be avoided in sighted events. It can be used if current unit positions have no bearing on the event, but otherwise it could cause a replay to go out of sync if a player delays shroud updates and undoes a move. This should not be an onerous restriction, though, as clearing fog will block the ability to undo, regardless of what happens within an event. Secondly, it is currently possible for WML to kill a unit involved in a sighted event before that event fires. If that happens, filters on the killed unit will not match anything and the event may seem to have not fired.<br />
<br />
===== enter_hex =====<br />
<br />
Triggers for each hex entered during movement, with $x1,$y1 identifying the hex entered and $x2,$y2 identifying the previous hex (just exited). If this event is handled without using {{tag|DirectActionsWML|allow_undo}}, then movement is interrupted, stopping the unit where it is.<br />
<br />
'''Note:''' This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the entered hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.<br />
<br />
===== exit_hex =====<br />
<br />
Triggers for each hex exited during movement, with $x1,$y1 identifying the hex exited and $x2,$y2 identifying the next hex (to be entered). If this event is handled without using {{tag|DirectActionsWML|allow_undo}}, then movement is interrupted, stopping the unit where it is.<br />
<br />
'''Note:''' This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the exited hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.<br />
<br />
<br />
===== attack =====<br />
<br />
Triggers when the primary unit attacks the secondary unit.<br />
<br />
===== attack end =====<br />
<br />
Similar to '''attack''', but is triggered ''after'' the fight instead of before. Note that if either unit is killed during the fight, this event triggers before any '''die''' events.<br />
<br />
===== attacker hits =====<br />
<br />
Triggers when the the primary unit (the attacker) hits the secondary unit (the defender). The value of the WML variable '''damage_inflicted''' is set to the number of hitpoints inflicted by the attacker.<br />
<br />
===== attacker misses =====<br />
<br />
Same as ''attacker hits'', but is triggered when the attacker misses.<br />
<br />
===== defender hits =====<br />
<br />
Triggers when the primary unit (the attacker) is hit in retaliation by the secondary unit (the defender). The value of the WML variable '''damage_inflicted''' is set to the number of hitpoints inflicted by the defender.<br />
<br />
===== defender misses =====<br />
<br />
Same as ''defender hits'', but is triggered when the defender misses.<br />
<br />
===== petrified =====<br />
Triggers when the primary unit is hit by an attack with the 'petrifies' ability (See ''stones'', [[AbilitiesWML]]) by the secondary unit (the unit with the 'petrifies' ability).<br />
<br />
===== last breath =====<br />
<br />
Triggers when the primary unit is killed by the secondary unit, but before the death animation is triggered. Use this instead of name=die when you want the primary unit to make a final [message]. <br />
<br />
===== die =====<br />
<br />
Triggers when the primary unit is killed by the secondary unit. ''Note: The primary unit is not removed from the game until the end of this event. The primary unit can still be manipulated, will block other units from taking its hex, and will still be found by standard unit filters (except [have_unit]). To prevent this behavior, you can use [kill] to remove the unit immediately. However, this will stop any (still unfired) other events that also match the unit from firing afterwards, so use with caution.'' If you want to the primary unit to make a final [message], use name=last_breath, see above.<br />
<br />
===== capture =====<br />
<br />
Triggers when the primary unit captures a village. The village may have been previously neutral, or previously owned by another side; merely moving into your own villages does not constitute a capture. This event will be fired before the moveto event. Villages becoming neutral (via [capture_village]) do not fire capture events. The variable $owner_side contains the previous owner side of the village. 0 means neutral.<br />
<br />
===== recruit =====<br />
<br />
Triggers when the primary unit is recruited (by the secondary unit). (That is, when a unit is recruited it will trigger this event and this event's filter will filter that unit.).<br />
<br />
===== prerecruit =====<br />
<br />
Triggers when the primary unit is recruited (by the secondary unit) but before it is displayed.<br />
<br />
===== recall =====<br />
<br />
Triggers after the primary unit is recalled (by the secondary unit).<br />
<br />
===== prerecall =====<br />
<br />
Triggers when the primary unit is recalled (by the secondary unit) but before it is displayed.<br />
<br />
===== advance =====<br />
<br />
Triggers just before the primary unit is going to advance to another unit, or advance by AMLA. (This is after the player selects which advancement, if there is a choice). If this event removes the unit, changes the unit's type, or reduces the unit's experience below what it needs to advance, then the advancement is aborted. This also applies to advancement by AMLA.<br />
<br />
===== pre advance =====<br />
<br />
{{DevFeature1.13|0}} Triggers before the unit advancement dialog is shown. If this event removes the unit or reduces the unit's experience below what it needs to advance, then the advancement is aborted.<br />
<br />
===== post advance =====<br />
<br />
Triggers just after the primary unit has advanced to another unit, or advance by AMLA.<br />
<br />
===== select =====<br />
<br />
Triggers when the primary unit is selected. Prior to version 1.11, this also triggered when a move was interrupted, as the game keeps the moving unit selected by selecting it again at the end of movement. ''Note: in networked multiplayer, these events are only executed by the client on which the event is triggered, leading to out of sync errors if you modify the game state in the event.''<br />
<br />
===== menu item ''X'' =====<br />
<br />
Triggers when a WML menu item with id=''X'' is selected. ''Note: if the menu item has a [command], this event may be executed before or after the command; there is no guarantee.''<br />
<br />
===== unit placed {{DevFeature1.13|3}}=====<br />
<br />
Triggers when the primary unit is placed on the map, regardless of method. This includes but might not be limited to:<br />
* Leaders and units placed in side definitions (fired once for every unit right before prestart events)<br />
* Recruited and recalled units<br />
* Units placed on the map with the [unit] tag ('''not''' units created directly onto a recall list or variable)<br />
* Units placed by the wesnoth.put_unit() Lua function<br />
* Units created via debug mode<br />
* Units created by plague<br />
* Every use of [unstore_unit]<br />
This event is solely intended for special cases where no other event types suffice, for example if you must immediately apply a modification to every unit that ever appears. The event does '''not''' keep track of which units it has previously fired for, but can fire an unlimited number of times for the same unit as long the unit is "placed" several times and the event filter doesn't prevent it.<br />
<br />
==== Custom events ====<br />
<br />
An event with a custom name may be invoked using the [[InternalActionsWML#.5Bfire_event.5D|[fire_event]]] tag. Normally you'll use such custom events as named subroutines to be called by events with predefined types. One common case of this, for example, is that more than one '''sighted''' events might fire the same custom event that changes the scenario objectives. Also, custom events come very handy in [[Wml_optimisation]].<br />
<br />
Example:<br />
#The following is the definition of a custom event "unit recruited"<br />
[event]<br />
name=unit_recruited<br />
first_time_only=no<br />
[message]<br />
speaker=unit<br />
message=_ "Reporting for duty!"<br />
[/message]<br />
[/event]<br />
<br />
#This is a standard recruit event that triggers whenever a unit is recruited by side 1<br />
[event]<br />
name=recruit<br />
first_time_only=no<br />
[filter]<br />
[/filter]<br />
[filter_second]<br />
side=1<br />
[/filter_second]<br />
<br />
#And now a fire_event tag is used to trigger the previously defined event<br />
[fire_event]<br />
name=unit_recruited<br />
[/fire_event]<br />
<br />
#As a result, every time side 1 recruits a unit, this unit says "Reporting for duty!"<br />
[/event]<br />
<br />
=== Optional Keys and Tags ===<br />
<br />
These keys and tags are more complex ways to filter when an event should trigger:<br />
<br />
==== first_time_only ====<br />
: Whether the event should be removed from the scenario after it is triggered. This key takes a [[ConditionalActionsWML#Boolean_Values|boolean]]; for example:<br />
: ''first_time_only=yes''<br />
:: Default behavior if key is omitted. The event will trigger the first time it can and never again.<br />
: ''first_time_only=no''<br />
:: The event will trigger every time the criteria are met instead of only the first time.<br />
<br />
==== id ====<br />
: If an id is specified, then the event will not be added if another event with the same id already exists. An id will also allow the event to be removed, see below. Supplying a non-empty id= is mandatory in case of a [unit_type][event].<br />
<br />
==== remove ====<br />
: Whether to remove an event instead of adding a new one. This key takes a [[ConditionalActionsWML#Boolean_Values|boolean]]; if true, then the contents of the event are ignored and the event with the specified id is removed instead. {{DevFeature1.13|0}} May be a comma separated list.<br />
<br />
[event]<br />
id=id_of_event_to_remove<br />
remove=yes<br />
[/event]<br />
<br />
<b>Note:</b> {{DevFeature1.13|0}} You can now use [[InternalActionsWML#.5Bremove_event.5D|[remove_event]]] instead (the [event] remove= syntax still works). It also accepts a comma separated list.<br />
<br />
[remove_event]<br />
id=id_of_event_to_remove<br />
[/remove_event]<br />
<br />
==== [filter] ====<br />
: The event will only trigger if the primary unit matches this filter.<br />
:* [[StandardUnitFilter]]: selection criteria<br />
<br />
==== [filter_second] ====<br />
: Like [filter], but for the secondary unit.<br />
:* [[StandardUnitFilter]]: selection criteria<br />
<br />
==== [filter_attack] ====<br />
: Can be used to set additional filtering criteria based on the weapon used by the primary unit. This is usable in the events ''attack'', ''attacker hits'', ''attacker misses'', ''defender hits'', ''defender misses'', ''attack end'', ''last breath'', and ''die''. For more information and filter keys, see [[FilterWML#Filtering Weapons|Filtering Weapons]]. The most commonly used keys are the following.<br />
:* '''name''': the name of the weapon used.<br />
:* '''range''': the range of the weapon used.<br />
:* '''special''': filter on the attack's special power.<br />
<br />
==== [filter_second_attack] ====<br />
: Like [filter_attack], but for the weapon used by the secondary unit.<br />
<br />
==== [filter_condition] ====<br />
: This tag makes sense inside any sort of event - even those that don't have units, or custom events,... The event will only trigger if this condition evaluates to true.<br />
:* [[ConditionalActionsWML#Condition_Tags|Condition Tags]]<br />
: note: This tag is meant to be used when the firing of an event shall be based on variables/conditions which cannot be retrieved from the filtered units.<br />
<br />
==== [filter_side] ====<br />
: The current side (usually the side $side_number) must match the passed [[StandardSideFilter]] for the event to fire.<br />
:* SSF tags and keys as arguments as described in [[StandardSideFilter]].<br />
: note: This tag makes most sense in side turn and turn refresh events. However, all wml events have a current side so one could also prevent e.g. a moveto event from firing if you put a [filter_side] tag there and the moving unit's side doesn't match.<br />
<br />
==== delayed_variable_substitution ====<br />
: This key is only relevant inside of a [[#Delayed Variable Substitution|nested event]] and controls when variable substitution will occur in those special case actions.<br />
<br />
=== Actions triggered by [event] ===<br />
<br />
After the trigger conditions have been met, all [[ActionWML|action tags]] within the [event] tag are executed in the order they are written in.<br />
<br />
There are 3 main types of actions:<br />
* direct actions ([[DirectActionsWML]]) which have a direct effect on gameplay<br />
* display actions ([[InterfaceActionsWML]]) which show something to the user<br />
* internal actions ([[InternalActionsWML]]) which are used by WML internally<br />
<br />
More details in [[ActionWML]].<br />
<br />
Several actions use standard filters to find out which units<br />
to execute the command on. These are denoted by the phrases<br />
"standard unit filter" and "standard location filter".<br />
<br />
=== Nested Events ===<br />
<br />
There is one special type of action: event creation. By placing an '''[event]''' tag inside another '''[event]''' tag, the nested event is spawned (created) when the parent (outer) event is encountered (when executing the contents of the parent event).<br />
<br />
([[#Nested Event Example|See Examples]])<br />
<br />
==== Delayed Variable Substitution ====<br />
<br />
Variable substitution for a nested event can happen either when it is spawned by the parent event or when it is triggered itself. This is controlled with the key '''delayed_variable_substitution''' which is used in the nested event.<br />
<br />
If this key is set to ''yes'', the variables in the nested event will contain values from the turn in which the ''nested'' event was triggered. ''This is the default behavior if the key is omitted.'' If set to ''no'', the variables in the nested event are set at the time the ''parent'' event is triggered.<br />
<br />
This behavior can be fine tuned with a special syntax when referencing variables. Instead of the normal '''$variable''' syntax, use '''$|variable''' to cause a variable to contain values relevant to the turn in which the nested event was triggered even when '''delayed_variable_substitution''' is set to ''no''. In this way you can have a mix of variables relevant to the parent and nested event trigger times.<br />
<br />
([[#Delayed Variable Substitution Example|See Examples]])<br />
<br />
== Multiplayer safety ==<br />
<br />
In multiplayer it is only safe to use WML that might require synchronization with other players because of input or random numbers (like [message] with input or options or [unstore_unit] where a unit might advance) in the following events. This is because in these cases WML needs data from other players to work right and/or do the same thing for all players. This data is only available after a network synchronization.<br />
<br />
List of synchronized events:<br />
* moveto<br />
* enter hex<br />
* exit hex<br />
* sighted<br />
* last breath <br />
* menu item X<br />
* die<br />
* capture <br />
* recruit<br />
* prerecruit <br />
* recall <br />
* prerecall <br />
* advance<br />
* pre advance<br />
* post advance <br />
* attack<br />
* attack end <br />
* attacker hits <br />
* attacker misses <br />
* defender hits<br />
* defender misses <br />
* start<br />
* prestart (prestart are synced but [message][option] & [unstore_unit] advancement choices will do a random decision because UI things don't work during prestart events.)<br />
* new turn <br />
* side turn <br />
* turn X <br />
* side X turn <br />
* side X turn Y <br />
* turn refresh<br />
* side turn end<br />
* side X turn end<br />
* side turn X end<br />
* side X turn Y end<br />
* turn end<br />
* turn X end<br />
* {{DevFeature1.13|0}} enemies defeated<br />
* {{DevFeature1.13|0}} time over<br />
The following are <b>not</b> synced:<br />
* select<br />
* preload<br />
* victory<br />
* defeat<br />
* ai turn<br />
<br />
If an event is not listed here, ask someone to be sure.<br />
<br />
There is also the possibility of events that are normally synchronized when fired by the engine but can be non-synchronized when fired by WML tags from non-synchronized event. So when you are using them you must be extra careful. For example [unstore_unit] may trigger a unit advancement that will fire ''advance'' and ''post advance'' events.<br />
<br />
== A Trap for the Unwary ==<br />
<br />
You need to beware of using macros to generate events. If you include a macro expanding to an event definition twice, the event will be executed twice (not once) each time the trigger condition fires. Consider this code:<br />
<br />
#define DOUBLE<br />
[event]<br />
name=multiply_by_2<br />
{VARIABLE_OP 2_becomes_4 multiply 2}<br />
[/event]<br />
#enddef<br />
<br />
{DOUBLE}<br />
{DOUBLE}<br />
<br />
{VARIABLE 2_becomes_4 2}<br />
<br />
[fire_event]<br />
name=multiply_by_2<br />
[/fire_event]<br />
<br />
{DEBUG_MSG "$2_becomes_4 should be 4"}<br />
<br />
After it executes, the debug message will reveal that the variable has been set to 8, not 4.<br />
<br />
=== Event IDs ===<br />
<br />
This problem can be avoided by setting an '''id''' on the event, i.e.:<br />
<br />
#define DOUBLE<br />
[event]<br />
name=multiply_by_2<br />
id=doubler_event<br />
{VARIABLE_OP 2_becomes_4 multiply 2}<br />
[/event]<br />
#enddef<br />
<br />
Events with the same ID will only be accepted once by the engine no matter how many times they are included, and will only be saved once to the scenario's savefile. Events with an ID can also be removed by using the '''remove''' key, i.e.:<br />
<br />
[event]<br />
id=doubler_event<br />
remove=yes<br />
[/event]<br />
<br />
After that WML is encountered (at toplevel or after created from another event), the event with this ID is removed from the scenario wml, thus firing it has no effect. After an event is removed, it can still be re-added later.<br />
<br />
== Miscellaneous Notes and Examples ==<br />
<br />
=== Primary/Secondary Unit Speaker Example ===<br />
<br />
In events, the primary unit can be referred to as '''unit''' and the secondary unit can be referred to as '''second_unit''' in [message] tags using the '''speaker''' key. For example:<br />
<br />
[event]<br />
name=die<br />
[message]<br />
speaker='''second_unit'''<br />
message= _ "Hahaha! I finally killed you!"<br />
[/message]<br />
<br />
[message]<br />
speaker='''unit'''<br />
message= _ "It's not over yet! I'll come back to haunt you!"<br />
[/message]<br />
[/event]<br />
<br />
=== Nested Event Example ===<br />
<br />
An event is created for a portal that opens on turn 10. The parent (or 'outer') event executes on turn 10 at which point the nested moveto event is created. This nested event executes when a player steps on a certain spot.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
# moving to 5,8 will trigger this event only on turn 10 and after<br />
[/event]<br />
[/event]<br />
<br />
An equivalent way of doing this would be to create a single moveto event with an '''[if]''' statement to check for turn number but using nested '''[event]''' tags is a convenient shortcut to accomplish this task without resorting to '''[if]''' statements.<br />
<br />
=== Delayed Variable Substitution Example ===<br />
<br />
This code will display a message showing the turn number on which the nested ''moveto'' event happens.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=yes<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
Since this is the default behavior for the '''delayed_variable_substitution''' key, the following example is identical.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
The following code will always display "Turn 10" when the nested ''moveto'' event happens. This is because the variable substitution is done when the parent event is triggered and spawns the nested event, ''not'' when the nested event is triggered.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=no<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
Finally, the following example is identical to the first two in that it will display a message showing the turn number on which the nested ''moveto'' event happens, despite the fact that the '''delayed_variable_substitution''' key is set to ''no''. This is because the special '''$|variable''' syntax is used.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=no<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $|turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
=== Multiple Nested Events ===<br />
<br />
Every delayed_variable_substitution=no causes a variable substitution run on the subevent where it occurs at the spawn time of this event and on all following subevents. For any specific event, variable substitution happens at least one time when the event is executed. For each delayed=no key appearing in itself or in an event of an "older" generation, which is not the toplevel event, an additional variable substitution run is made.<br />
[event]# parent<br />
name=turn 2<br />
#delayed_variable_substitution=no # In the parent event, delayed= has no effect.<br />
<br />
[event]# child<br />
name=turn 3<br />
delayed_variable_substitution=no # Causes variable substitution in the child, grandchild and great-grandchild event<br />
# at execution time of the parent event = spawn time of the child event.<br />
<br />
[event]# grandchild<br />
name=turn 4<br />
delayed_variable_substitution=yes # no variable substitution in the grandchild and great-grandchild event<br />
# at execution time of the child event = spawn time of the grandchild event<br />
<br />
[event]# great-grandchild<br />
name=turn 5<br />
{DEBUG_MSG $turn_number}# output: 2 - value from the variable substitution at execution time of the parent event,<br />
# caused by delayed=no in the child event<br />
<br />
{DEBUG_MSG $||turn_number}# output: "$turn_number"<br />
# Each variable substitution transforms a "$|" to a "$" (except when no | left).<br />
<br />
{DEBUG_MSG $|turn_number}# output: 5 - from the variable substitution at execution time<br />
# of the great-grandchild event<br />
[/event]<br />
[/event]<br />
[/event]<br />
[/event]<br />
<br />
== See Also ==<br />
<br />
* [[DirectActionsWML]]<br />
* [[InternalActionsWML]]<br />
* [[InterfaceActionsWML]]<br />
* [[FilterWML]]<br />
* [[ReferenceWML]]<br />
<br />
<br />
[[Category: WML Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=EventWML&diff=57847EventWML2016-08-05T08:32:54Z<p>Jyrkive: Replaced spaces in event names with underscores. (Note that nothing has changed semantically, as spaces and underscores are interchargeable.)</p>
<hr />
<div>{{WML Tags}}<br />
== The [event] Tag ==<br />
<br />
This tag is a subtag of the [scenario], [unit_type] and [era] tags which is used to describe a set of [[ActionWML|actions]] which trigger at a certain point in a scenario. When used in a [scenario] tag (also includes [multiplayer], [tutorial] and [test]), the event only occurs in that scenario. When used in a [unit_type] tag, the event will occur in all scenarios in which a unit of that type appears in (only after such a unit appears during the scenario, however). When used in an [era], the event will occur in any scenario which is played using that era.<br />
<br />
This tag has keys and child tags that control when and if the event actions will be triggered. Most important of these is the '''name''' key. Without it, no error will be raised but the event will never fire. Therefore, from a practical standpoint, it can be considered mandatory. All of the others can be used or not and the event actions will fire either way.<br />
<br />
'''Lexicon side note:''' ''The word "event" in the [event] tag itself may be considered an abbreviation of the word "event handler" because it is technically not a game "event" but an event '''handler''' for the game events fired with the given 'name'. However, this distinction is usually unimportant in most discussions and the event handlers are therefore simply referred to as "events" in this documentation.''<br />
<br />
=== The 'name' Key (Mandatory) ===<br />
<br />
Usage:<br />
name=<value><br />
<br />
This key defines which game event or trigger your [event] tag will be handling. This 'name' key should not be confused with a descriptive comment; it is rather a precise value which must match the predefined game event's name to be valid.<br />
<br />
The '''name''' key can accept a list of comma separated values describing when the event will be triggered.* These values may be either predefined event types or custom event names not matching any predefined type.<br />
<br />
For example:<br />
<br />
name=attacker_misses,defender_misses<br />
<br />
''* Note that unless you use [[#first_time_only|first_time_only=no]], the event will fire only once, '''not''' once for each listed type.''<br />
<br />
All predefined event types are listed here along with a description of when this value will cause the event to be triggered. Any value ''not'' listed here is a custom event name which can be triggered only by a '''[fire_event]''' tag somewhere else. <br />
<br />
Underscores in event names can be interchanged with ''spaces'' (for example, '''name=new turn''' and '''name=new_turn''' are equivalent).<br />
<br />
==== Predefined Events Without Filters ====<br />
<br />
These events do not take filter parameters (except [filter_condition] which works for all events).<br />
<br />
===== preload =====<br />
<br />
Triggers before a scenario 'prestarts' and when loading a savegame -- before anything is shown on the screen at all. Can be used to set up the [[LuaWML|Lua]] environment: loading libraries, defining helper functions, etc.<br />
<br />
'''Note:''' Unlike prestart and start, the preload event '''must be able to fire more than once!''' This is because it is triggered each time a savegame is loaded in addition to the initial time when it loads before the scenario 'prestart'. This means that it is effectively ''mandatory'' to have the [[#first_time_only|first_time_only=no]] key value in a preload event. <br />
<br />
===== prestart =====<br />
<br />
Triggers before a scenario 'starts' -- before anything is shown on the screen at all. Can be used to set up things like village ownership. For things displayed on-screen such as character dialog, use '''start''' instead.<br />
<br />
'''Note:''' ''This value makes the [[#first_time_only|first_time_only]] key irrelevant since, by definition, it can only fire once.''<br />
<br />
===== start =====<br />
<br />
Triggers after the map is shown but before the scenario begins -- before players can 'do' anything.<br />
<br />
'''Note:''' ''This value makes the [[#first_time_only|first_time_only]] key irrelevant since, by definition, it can only fire once.''<br />
<br />
===== new_turn =====<br />
<br />
Triggers at the start of every turn (not side turn). See also [[#first_time_only|first_time_only=no]]. Before any events of this type trigger, the value of the WML variable '''turn_number''' is set to the number of the turn that is beginning.<br />
<br />
===== turn_end =====<br />
<br />
Triggers at the end of every turn (not side turn). See also [[#first_time_only|first_time_only=no]]. The WML variable '''side_number''' will contain the side that ended their turn.<br />
<br />
===== turn_''X''_end =====<br />
<br />
Triggers at the end of turn ''X''.<br />
<br />
===== side_turn =====<br />
<br />
Triggers when a side is about to start its turn. Before events of this type trigger, the value of the WML variable '''side_number''' is set to the number of the side of the player about to take their turn. This is before any healing takes place for that side, before calculating income, and before restoring unit movement and status.<br />
<br />
===== ai_turn =====<br />
<br />
Triggered just before the AI is invoked for a side. This is called after ''side_turn'', and thus the WML variable '''side_number''' still holds the number of this side. Note that this event might be called several times per turn in case that fallbacks to human or droiding is involved. I.e. it happens at the middle of turn of human side 1 if the human player droids his side. It happens after the selection of ai to play the turn but before AI is told that new turn has come.<br />
<br />
'''Note:''' ''This event currently breaks replays since it is not explicitly saved in a replay and there is no AI involved in replays...''<br />
<br />
===== turn_refresh =====<br />
<br />
Like '''side_turn''', triggers just before a side is taking control but '''after''' healing, calculating income, and restoring unit movement and status.<br />
<br />
Note that the turn refresh event does occur on turn 1, even though healing, income and unit refreshing do not.<br />
<br />
===== turn_''X'' =====<br />
<br />
Triggers at the start of turn ''X''. It's the first side initialization event. <br />
<br />
Side initialization events go in the order of: <br />
<br />
# '''turn_''X''''' <br />
# '''new_turn''' <br />
# '''side_turn''' <br />
# '''side_''X''_turn''' <br />
# '''side_turn_''X''''' <br />
# '''side_''X''_turn_''Y''''' <br />
# '''turn_refresh''' <br />
# '''side_''X''_turn_refresh''' <br />
# '''turn_''X''_refresh''' <br />
# '''side_''X''_turn_''Y''_refresh'''<br />
<br />
===== side_''X''_turn_''Y'' =====<br />
<br />
This event triggers at the start of turn ''Y'' of side X <br />
<br />
===== side_''X''_turn =====<br />
<br />
This event triggers at the start of any turn of side X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side_turn_''X'' =====<br />
<br />
This event triggers at the start of any side on turn X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side_''X''_turn_''Y''_refresh =====<br />
<br />
This event triggers at the turn refresh for side X on turn Y<br />
<br />
===== side_''X''_turn_refresh =====<br />
<br />
This event triggers at the turn refresh for side X<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== turn_''X''_refresh =====<br />
<br />
This event triggers for any side at the refresh of turn X.<br />
<br />
'''Note:''' ''Of course, [[#first_time_only|first_time_only=no]] is needed for this event to be triggered more than once.''<br />
<br />
===== side_turn_end =====<br />
<br />
Triggers after a side ends its turn. Like side turn, there are also some variations for specific combinations of side number and turn number. Here is the order in which the turn end events trigger:<br />
<br />
# '''side_turn_end''' <br />
# '''side_''X''_turn_end''' <br />
# '''side_turn_''X''_end''' <br />
# '''side_''X''_turn_''Y''_end''' <br />
# '''turn_end''' <br />
# '''turn_''X''_end''' <br />
<br />
===== time_over =====<br />
<br />
Triggers on turn ''turns''. (''turns'' is specified in [scenario])<br />
<br />
===== enemies_defeated =====<br />
<br />
Triggers when all sides that are not defeated are allied and if there is at least one human (or human networked) side among them. Especially this event triggers in a situation that would normally cause a victory due to enemies defeated. (regardless of whether this was disabled with victory_when_enemies_defeated=no). <br />
<br />
===== victory =====<br />
<br />
In this scenario, any tag of the form '''[endlevel] result=victory [/endlevel]''' will be automatically preceded by all actions in this tag. It helps debugging if the victory event allows you to safely advance to any of the possible next maps after using the ":n" command. Scenarios where key units are picked up before the victory, or where some action chosen earlier determines which map to advance to, make it hard to quickly test scenarios in a campaign. (See also: [endlevel], [[DirectActionsWML]]). This event is not synchronized in networked mp or in replays. The reason is that it is possible to have different results in a mp game (one player wins so a '''victory''' event is fired on that client, other player loses so a '''defeat''' event is fired on his client)<br />
<br />
===== defeat =====<br />
<br />
In this scenario, any tag of the form '''[endlevel] result=defeat [/endlevel]''' will be automatically preceded by all actions in this tag. (See also [endlevel], [[DirectActionsWML]]). Like '''victory''', this event is not synchronized in networked mp or in replays.<br />
<br />
==== Predefined Events With Filters ====<br />
<br />
Filters (except [filter_condition] which is for all sorts of events) can be applied to the following event triggers (see [[FilterWML]]; see also below). The actions specified in the event tag will be executed only if the filter returns true. <br />
These event triggers are all actions by units ('''moveto''', '''attack''') or things that happen to units ('''recruit''', '''advance'''). When one of these events is triggered, the position of the active unit (referred to as the '''primary unit''') is stored in the variables '''x1''' and '''y1''' and the position of any unit that primary unit does something to is stored in the variables '''x2''' and '''y2''' (this unit is referred to as the '''secondary unit''' below). '' These units are also automatically stored in the variables 'unit' and 'second_unit' as if they had been stored using the '''[store_unit]''' tag. see [[SingleUnitWML]]. weapon and second_weapon variables are available inside attack, attacker_hits, defender_hits, die and last_breath events. See [[VariablesWML#Automatically_Stored_Variables|automatically stored variables]] for more information.<br />
<br />
===== moveto =====<br />
<br />
Triggers after the primary unit moves. Typically this is used when the primary unit gets to a particular location and a filter for the location of the primary unit is included; remember that this is the location that the primary unit lands on, not the location it started on or any location it travels on. If the unit moves to a village, the capture event will be fired before this event. <br />''An '''[allow_undo]''' tag anywhere within a moveto event will cancel any lack of undo functionality the event would have caused. Note that undo functionality will only move the unit back to its former location; it will not undo other changes to the game caused by the event. Thus it is up to the scenario designer to use this tag correctly.'' $x2 and $y2 refer to the hex the unit came from.<br />
<br />
===== sighted =====<br />
<br />
A '''sighted''' event is triggered by a unit becoming visible to a side (other than the unit's own side). This is mostly useful when the side seeing the unit uses [[fog of war]] or [[shroud]], but they still fire even when fog/shroud is not in use, and they do take into account the {{tag2|AbilitiesWML#The_.5Babilities.5D_tag|hides}} ability (for a moving unit and for ambushers). The ''primary unit'' is the unit that became visible, and the ''secondary unit'' belongs to the side that now sees the primary unit. In some cases, sighted events can be delayed from when they "should" occur. If that happens, the secondary unit will be filtered as if it was at the location where the event "should" have occurred, and ''x2,y2'' will store that location (not the current position of the secondary unit). To understand when sighted events fire, it is helpful to distinguish the times the acting unit sights other units from the times when the acting unit is sighted.<br />
<br />
An acting unit can sight other units when it is recruited, recalled, leveled, or moved, and when fog or shroud is cleared from occupied hexes as a result. In these cases, the acting unit is always the ''secondary unit''. For the first three actions, there are two events associated with the action; clearing occurs between these events, but any sighted events are fired after the second event. (For example, when a unit is recruited, the ''prerecruit'' event fires, then fog is cleared, then the ''recruit'' event fires, then ''sighted'' events fire.) For movement, the sighted events fire between ''enter_hex'' and ''exit_hex'' events, but sometimes sighted events are postponed until the moving unit reaches a good place to stop (e.g. not in an occupied hex). As a major exception to the above, players have the option to delay shroud (and fog) updates. If the player delays shroud updates, sighted events are also delayed until the shroud is updated.<br />
<br />
An acting unit can be sighted by other sides when it is recruited, recalled, leveled (in rare cases), or moved. In these cases, the acting unit is always the ''primary unit''. These events fire after sightings by the acting unit (unless the player delayed shroud updates). For the first two, the sighted event fires for all sides that can see the unit, other than the unit's own side (even if those sides use neither fog nor shroud). For leveling units, sides that could see the unit before it leveled are excluded. (This is why these events are rare &ndash; the leveling unit must have lost a [hides] ability as a result of leveling in order to be seen after, but not before, leveling.) For movement, a sighted event is fired for each side that could see the unit after movement, but not before. In particular, only the starting and ending hexes are considered; a unit that moves through seen hexes but ends movement in a fogged hex does not trigger a sighted event for itself. In all cases where the acting unit is sighted, a (single) ''secondary unit'' is chosen from the sighting team. This choice should be considered arbitrary, but units within their sight range of the acting unit are chosen in preference to units further away.<br />
<br />
Sighted events are not triggered by a ''hides'' ability becoming inactive, unless it becomes inactive due to that unit's movement or to that unit ambushing another. (To detect a ''nightstalk'' ability becoming inactive due to time of day, use a ''new_turn'' event. Custom ''hides'' abilities might need similar handling.)<br />
<br />
Sighted events have some special caveats for WML authors. First and foremost, {{tag|DirectActionsWML|allow_undo}} should generally be avoided in sighted events. It can be used if current unit positions have no bearing on the event, but otherwise it could cause a replay to go out of sync if a player delays shroud updates and undoes a move. This should not be an onerous restriction, though, as clearing fog will block the ability to undo, regardless of what happens within an event. Secondly, it is currently possible for WML to kill a unit involved in a sighted event before that event fires. If that happens, filters on the killed unit will not match anything and the event may seem to have not fired.<br />
<br />
===== enter_hex =====<br />
<br />
Triggers for each hex entered during movement, with $x1,$y1 identifying the hex entered and $x2,$y2 identifying the previous hex (just exited). If this event is handled without using {{tag|DirectActionsWML|allow_undo}}, then movement is interrupted, stopping the unit where it is.<br />
<br />
'''Note:''' This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the entered hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.<br />
<br />
===== exit_hex =====<br />
<br />
Triggers for each hex exited during movement, with $x1,$y1 identifying the hex exited and $x2,$y2 identifying the next hex (to be entered). If this event is handled without using {{tag|DirectActionsWML|allow_undo}}, then movement is interrupted, stopping the unit where it is.<br />
<br />
'''Note:''' This event behaves a bit unusually if the hex is occupied (and the moving unit is simply passing through). When this happens, $x1,$y1 is still the hex where the event was triggered, but the moving unit (stored in $unit) will be located somewhere earlier in the route (the most recent unoccupied hex). That is, $x1,$y1 will not equal $unit.x,$unit.y (a condition that can be used to detect when the exited hex is occupied). The moving unit will have already spent its movement points to enter the event's hex even though it is has not actually moved from the most recent unoccupied hex.<br />
<br />
<br />
===== attack =====<br />
<br />
Triggers when the primary unit attacks the secondary unit.<br />
<br />
===== attack_end =====<br />
<br />
Similar to '''attack''', but is triggered ''after'' the fight instead of before. Note that if either unit is killed during the fight, this event triggers before any '''die''' events.<br />
<br />
===== attacker_hits =====<br />
<br />
Triggers when the the primary unit (the attacker) hits the secondary unit (the defender). The value of the WML variable '''damage_inflicted''' is set to the number of hitpoints inflicted by the attacker.<br />
<br />
===== attacker_misses =====<br />
<br />
Same as ''attacker_hits'', but is triggered when the attacker misses.<br />
<br />
===== defender_hits =====<br />
<br />
Triggers when the primary unit (the attacker) is hit in retaliation by the secondary unit (the defender). The value of the WML variable '''damage_inflicted''' is set to the number of hitpoints inflicted by the defender.<br />
<br />
===== defender_misses =====<br />
<br />
Same as ''defender_hits'', but is triggered when the defender misses.<br />
<br />
===== petrified =====<br />
Triggers when the primary unit is hit by an attack with the 'petrifies' ability (See ''stones'', [[AbilitiesWML]]) by the secondary unit (the unit with the 'petrifies' ability).<br />
<br />
===== last_breath =====<br />
<br />
Triggers when the primary unit is killed by the secondary unit, but before the death animation is triggered. Use this instead of name=die when you want the primary unit to make a final [message]. <br />
<br />
===== die =====<br />
<br />
Triggers when the primary unit is killed by the secondary unit. ''Note: The primary unit is not removed from the game until the end of this event. The primary unit can still be manipulated, will block other units from taking its hex, and will still be found by standard unit filters (except [have_unit]). To prevent this behavior, you can use [kill] to remove the unit immediately. However, this will stop any (still unfired) other events that also match the unit from firing afterwards, so use with caution.'' If you want to the primary unit to make a final [message], use name=last_breath, see above.<br />
<br />
===== capture =====<br />
<br />
Triggers when the primary unit captures a village. The village may have been previously neutral, or previously owned by another side; merely moving into your own villages does not constitute a capture. This event will be fired before the moveto event. Villages becoming neutral (via [capture_village]) do not fire capture events. The variable $owner_side contains the previous owner side of the village. 0 means neutral.<br />
<br />
===== recruit =====<br />
<br />
Triggers when the primary unit is recruited (by the secondary unit). (That is, when a unit is recruited it will trigger this event and this event's filter will filter that unit.).<br />
<br />
===== prerecruit =====<br />
<br />
Triggers when the primary unit is recruited (by the secondary unit) but before it is displayed.<br />
<br />
===== recall =====<br />
<br />
Triggers after the primary unit is recalled (by the secondary unit).<br />
<br />
===== prerecall =====<br />
<br />
Triggers when the primary unit is recalled (by the secondary unit) but before it is displayed.<br />
<br />
===== advance =====<br />
<br />
Triggers just before the primary unit is going to advance to another unit, or advance by AMLA. (This is after the player selects which advancement, if there is a choice). If this event removes the unit, changes the unit's type, or reduces the unit's experience below what it needs to advance, then the advancement is aborted. This also applies to advancement by AMLA.<br />
<br />
===== pre_advance =====<br />
<br />
{{DevFeature1.13|0}} Triggers before the unit advancement dialog is shown. If this event removes the unit or reduces the unit's experience below what it needs to advance, then the advancement is aborted.<br />
<br />
===== post_advance =====<br />
<br />
Triggers just after the primary unit has advanced to another unit, or advance by AMLA.<br />
<br />
===== select =====<br />
<br />
Triggers when the primary unit is selected. Prior to version 1.11, this also triggered when a move was interrupted, as the game keeps the moving unit selected by selecting it again at the end of movement. ''Note: in networked multiplayer, these events are only executed by the client on which the event is triggered, leading to out of sync errors if you modify the game state in the event.''<br />
<br />
===== menu_item_''X'' =====<br />
<br />
Triggers when a WML menu item with id=''X'' is selected. ''Note: if the menu item has a [command], this event may be executed before or after the command; there is no guarantee.''<br />
<br />
===== unit_placed {{DevFeature1.13|3}}=====<br />
<br />
Triggers when the primary unit is placed on the map, regardless of method. This includes but might not be limited to:<br />
* Leaders and units placed in side definitions (fired once for every unit right before prestart events)<br />
* Recruited and recalled units<br />
* Units placed on the map with the [unit] tag ('''not''' units created directly onto a recall list or variable)<br />
* Units placed by the wesnoth.put_unit() Lua function<br />
* Units created via debug mode<br />
* Units created by plague<br />
* Every use of [unstore_unit]<br />
This event is solely intended for special cases where no other event types suffice, for example if you must immediately apply a modification to every unit that ever appears. The event does '''not''' keep track of which units it has previously fired for, but can fire an unlimited number of times for the same unit as long the unit is "placed" several times and the event filter doesn't prevent it.<br />
<br />
==== Custom events ====<br />
<br />
An event with a custom name may be invoked using the [[InternalActionsWML#.5Bfire_event.5D|[fire_event]]] tag. Normally you'll use such custom events as named subroutines to be called by events with predefined types. One common case of this, for example, is that more than one '''sighted''' events might fire the same custom event that changes the scenario objectives. Also, custom events come very handy in [[Wml_optimisation]].<br />
<br />
Example:<br />
#The following is the definition of a custom event "unit_recruited"<br />
[event]<br />
name=unit_recruited<br />
first_time_only=no<br />
[message]<br />
speaker=unit<br />
message=_ "Reporting for duty!"<br />
[/message]<br />
[/event]<br />
<br />
#This is a standard recruit event that triggers whenever a unit is recruited by side 1<br />
[event]<br />
name=recruit<br />
first_time_only=no<br />
[filter]<br />
[/filter]<br />
[filter_second]<br />
side=1<br />
[/filter_second]<br />
<br />
#And now a fire_event tag is used to trigger the previously defined event<br />
[fire_event]<br />
name=unit_recruited<br />
[/fire_event]<br />
<br />
#As a result, every time side 1 recruits a unit, this unit says "Reporting for duty!"<br />
[/event]<br />
<br />
=== Optional Keys and Tags ===<br />
<br />
These keys and tags are more complex ways to filter when an event should trigger:<br />
<br />
==== first_time_only ====<br />
: Whether the event should be removed from the scenario after it is triggered. This key takes a [[ConditionalActionsWML#Boolean_Values|boolean]]; for example:<br />
: ''first_time_only=yes''<br />
:: Default behavior if key is omitted. The event will trigger the first time it can and never again.<br />
: ''first_time_only=no''<br />
:: The event will trigger every time the criteria are met instead of only the first time.<br />
<br />
==== id ====<br />
: If an id is specified, then the event will not be added if another event with the same id already exists. An id will also allow the event to be removed, see below. Supplying a non-empty id= is mandatory in case of a [unit_type][event].<br />
<br />
==== remove ====<br />
: Whether to remove an event instead of adding a new one. This key takes a [[ConditionalActionsWML#Boolean_Values|boolean]]; if true, then the contents of the event are ignored and the event with the specified id is removed instead. {{DevFeature1.13|0}} May be a comma separated list.<br />
<br />
[event]<br />
id=id_of_event_to_remove<br />
remove=yes<br />
[/event]<br />
<br />
<b>Note:</b> {{DevFeature1.13|0}} You can now use [[InternalActionsWML#.5Bremove_event.5D|[remove_event]]] instead (the [event] remove= syntax still works). It also accepts a comma separated list.<br />
<br />
[remove_event]<br />
id=id_of_event_to_remove<br />
[/remove_event]<br />
<br />
==== [filter] ====<br />
: The event will only trigger if the primary unit matches this filter.<br />
:* [[StandardUnitFilter]]: selection criteria<br />
<br />
==== [filter_second] ====<br />
: Like [filter], but for the secondary unit.<br />
:* [[StandardUnitFilter]]: selection criteria<br />
<br />
==== [filter_attack] ====<br />
: Can be used to set additional filtering criteria based on the weapon used by the primary unit. This is usable in the events ''attack'', ''attacker hits'', ''attacker misses'', ''defender hits'', ''defender misses'', ''attack end'', ''last breath'', and ''die''. For more information and filter keys, see [[FilterWML#Filtering Weapons|Filtering Weapons]]. The most commonly used keys are the following.<br />
:* '''name''': the name of the weapon used.<br />
:* '''range''': the range of the weapon used.<br />
:* '''special''': filter on the attack's special power.<br />
<br />
==== [filter_second_attack] ====<br />
: Like [filter_attack], but for the weapon used by the secondary unit.<br />
<br />
==== [filter_condition] ====<br />
: This tag makes sense inside any sort of event - even those that don't have units, or custom events,... The event will only trigger if this condition evaluates to true.<br />
:* [[ConditionalActionsWML#Condition_Tags|Condition Tags]]<br />
: note: This tag is meant to be used when the firing of an event shall be based on variables/conditions which cannot be retrieved from the filtered units.<br />
<br />
==== [filter_side] ====<br />
: The current side (usually the side $side_number) must match the passed [[StandardSideFilter]] for the event to fire.<br />
:* SSF tags and keys as arguments as described in [[StandardSideFilter]].<br />
: note: This tag makes most sense in side turn and turn refresh events. However, all wml events have a current side so one could also prevent e.g. a moveto event from firing if you put a [filter_side] tag there and the moving unit's side doesn't match.<br />
<br />
==== delayed_variable_substitution ====<br />
: This key is only relevant inside of a [[#Delayed Variable Substitution|nested event]] and controls when variable substitution will occur in those special case actions.<br />
<br />
=== Actions triggered by [event] ===<br />
<br />
After the trigger conditions have been met, all [[ActionWML|action tags]] within the [event] tag are executed in the order they are written in.<br />
<br />
There are 3 main types of actions:<br />
* direct actions ([[DirectActionsWML]]) which have a direct effect on gameplay<br />
* display actions ([[InterfaceActionsWML]]) which show something to the user<br />
* internal actions ([[InternalActionsWML]]) which are used by WML internally<br />
<br />
More details in [[ActionWML]].<br />
<br />
Several actions use standard filters to find out which units<br />
to execute the command on. These are denoted by the phrases<br />
"standard unit filter" and "standard location filter".<br />
<br />
=== Nested Events ===<br />
<br />
There is one special type of action: event creation. By placing an '''[event]''' tag inside another '''[event]''' tag, the nested event is spawned (created) when the parent (outer) event is encountered (when executing the contents of the parent event).<br />
<br />
([[#Nested Event Example|See Examples]])<br />
<br />
==== Delayed Variable Substitution ====<br />
<br />
Variable substitution for a nested event can happen either when it is spawned by the parent event or when it is triggered itself. This is controlled with the key '''delayed_variable_substitution''' which is used in the nested event.<br />
<br />
If this key is set to ''yes'', the variables in the nested event will contain values from the turn in which the ''nested'' event was triggered. ''This is the default behavior if the key is omitted.'' If set to ''no'', the variables in the nested event are set at the time the ''parent'' event is triggered.<br />
<br />
This behavior can be fine tuned with a special syntax when referencing variables. Instead of the normal '''$variable''' syntax, use '''$|variable''' to cause a variable to contain values relevant to the turn in which the nested event was triggered even when '''delayed_variable_substitution''' is set to ''no''. In this way you can have a mix of variables relevant to the parent and nested event trigger times.<br />
<br />
([[#Delayed Variable Substitution Example|See Examples]])<br />
<br />
== Multiplayer safety ==<br />
<br />
In multiplayer it is only safe to use WML that might require synchronization with other players because of input or random numbers (like [message] with input or options or [unstore_unit] where a unit might advance) in the following events. This is because in these cases WML needs data from other players to work right and/or do the same thing for all players. This data is only available after a network synchronization.<br />
<br />
List of synchronized events:<br />
* moveto<br />
* enter hex<br />
* exit hex<br />
* sighted<br />
* last breath <br />
* menu item X<br />
* die<br />
* capture <br />
* recruit<br />
* prerecruit <br />
* recall <br />
* prerecall <br />
* advance<br />
* pre advance<br />
* post advance <br />
* attack<br />
* attack end <br />
* attacker hits <br />
* attacker misses <br />
* defender hits<br />
* defender misses <br />
* start<br />
* prestart (prestart are synced but [message][option] & [unstore_unit] advancement choices will do a random decision because UI things don't work during prestart events.)<br />
* new turn <br />
* side turn <br />
* turn X <br />
* side X turn <br />
* side X turn Y <br />
* turn refresh<br />
* side turn end<br />
* side X turn end<br />
* side turn X end<br />
* side X turn Y end<br />
* turn end<br />
* turn X end<br />
* {{DevFeature1.13|0}} enemies defeated<br />
* {{DevFeature1.13|0}} time over<br />
The following are <b>not</b> synced:<br />
* select<br />
* preload<br />
* victory<br />
* defeat<br />
* ai turn<br />
<br />
If an event is not listed here, ask someone to be sure.<br />
<br />
There is also the possibility of events that are normally synchronized when fired by the engine but can be non-synchronized when fired by WML tags from non-synchronized event. So when you are using them you must be extra careful. For example [unstore_unit] may trigger a unit advancement that will fire ''advance'' and ''post advance'' events.<br />
<br />
== A Trap for the Unwary ==<br />
<br />
You need to beware of using macros to generate events. If you include a macro expanding to an event definition twice, the event will be executed twice (not once) each time the trigger condition fires. Consider this code:<br />
<br />
#define DOUBLE<br />
[event]<br />
name=multiply_by_2<br />
{VARIABLE_OP 2_becomes_4 multiply 2}<br />
[/event]<br />
#enddef<br />
<br />
{DOUBLE}<br />
{DOUBLE}<br />
<br />
{VARIABLE 2_becomes_4 2}<br />
<br />
[fire_event]<br />
name=multiply_by_2<br />
[/fire_event]<br />
<br />
{DEBUG_MSG "$2_becomes_4 should be 4"}<br />
<br />
After it executes, the debug message will reveal that the variable has been set to 8, not 4.<br />
<br />
=== Event IDs ===<br />
<br />
This problem can be avoided by setting an '''id''' on the event, i.e.:<br />
<br />
#define DOUBLE<br />
[event]<br />
name=multiply_by_2<br />
id=doubler_event<br />
{VARIABLE_OP 2_becomes_4 multiply 2}<br />
[/event]<br />
#enddef<br />
<br />
Events with the same ID will only be accepted once by the engine no matter how many times they are included, and will only be saved once to the scenario's savefile. Events with an ID can also be removed by using the '''remove''' key, i.e.:<br />
<br />
[event]<br />
id=doubler_event<br />
remove=yes<br />
[/event]<br />
<br />
After that WML is encountered (at toplevel or after created from another event), the event with this ID is removed from the scenario wml, thus firing it has no effect. After an event is removed, it can still be re-added later.<br />
<br />
== Miscellaneous Notes and Examples ==<br />
<br />
=== Primary/Secondary Unit Speaker Example ===<br />
<br />
In events, the primary unit can be referred to as '''unit''' and the secondary unit can be referred to as '''second_unit''' in [message] tags using the '''speaker''' key. For example:<br />
<br />
[event]<br />
name=die<br />
[message]<br />
speaker='''second_unit'''<br />
message= _ "Hahaha! I finally killed you!"<br />
[/message]<br />
<br />
[message]<br />
speaker='''unit'''<br />
message= _ "It's not over yet! I'll come back to haunt you!"<br />
[/message]<br />
[/event]<br />
<br />
=== Nested Event Example ===<br />
<br />
An event is created for a portal that opens on turn 10. The parent (or 'outer') event executes on turn 10 at which point the nested moveto event is created. This nested event executes when a player steps on a certain spot.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
# moving to 5,8 will trigger this event only on turn 10 and after<br />
[/event]<br />
[/event]<br />
<br />
An equivalent way of doing this would be to create a single moveto event with an '''[if]''' statement to check for turn number but using nested '''[event]''' tags is a convenient shortcut to accomplish this task without resorting to '''[if]''' statements.<br />
<br />
=== Delayed Variable Substitution Example ===<br />
<br />
This code will display a message showing the turn number on which the nested ''moveto'' event happens.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=yes<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
Since this is the default behavior for the '''delayed_variable_substitution''' key, the following example is identical.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
The following code will always display "Turn 10" when the nested ''moveto'' event happens. This is because the variable substitution is done when the parent event is triggered and spawns the nested event, ''not'' when the nested event is triggered.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=no<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
Finally, the following example is identical to the first two in that it will display a message showing the turn number on which the nested ''moveto'' event happens, despite the fact that the '''delayed_variable_substitution''' key is set to ''no''. This is because the special '''$|variable''' syntax is used.<br />
<br />
[event]<br />
name=turn 10<br />
<br />
[event]<br />
name=moveto<br />
delayed_variable_substitution=no<br />
<br />
[filter]<br />
x,y=5,8<br />
[/filter]<br />
<br />
{DEBUG_MSG "Turn $|turn_number"} <br />
[/event]<br />
[/event]<br />
<br />
=== Multiple Nested Events ===<br />
<br />
Every delayed_variable_substitution=no causes a variable substitution run on the subevent where it occurs at the spawn time of this event and on all following subevents. For any specific event, variable substitution happens at least one time when the event is executed. For each delayed=no key appearing in itself or in an event of an "older" generation, which is not the toplevel event, an additional variable substitution run is made.<br />
[event]# parent<br />
name=turn 2<br />
#delayed_variable_substitution=no # In the parent event, delayed= has no effect.<br />
<br />
[event]# child<br />
name=turn 3<br />
delayed_variable_substitution=no # Causes variable substitution in the child, grandchild and great-grandchild event<br />
# at execution time of the parent event = spawn time of the child event.<br />
<br />
[event]# grandchild<br />
name=turn 4<br />
delayed_variable_substitution=yes # no variable substitution in the grandchild and great-grandchild event<br />
# at execution time of the child event = spawn time of the grandchild event<br />
<br />
[event]# great-grandchild<br />
name=turn 5<br />
{DEBUG_MSG $turn_number}# output: 2 - value from the variable substitution at execution time of the parent event,<br />
# caused by delayed=no in the child event<br />
<br />
{DEBUG_MSG $||turn_number}# output: "$turn_number"<br />
# Each variable substitution transforms a "$|" to a "$" (except when no | left).<br />
<br />
{DEBUG_MSG $|turn_number}# output: 5 - from the variable substitution at execution time<br />
# of the great-grandchild event<br />
[/event]<br />
[/event]<br />
[/event]<br />
[/event]<br />
<br />
== See Also ==<br />
<br />
* [[DirectActionsWML]]<br />
* [[InternalActionsWML]]<br />
* [[InterfaceActionsWML]]<br />
* [[FilterWML]]<br />
* [[ReferenceWML]]<br />
<br />
<br />
[[Category: WML Reference]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=NotSoEasyCoding&diff=57839NotSoEasyCoding2016-08-01T17:45:27Z<p>Jyrkive: Removed the Monte Carlo damage calculation mode now when it has been merged.</p>
<hr />
<div>This page lists projects which are considered a good idea by the developers but which have nobody working on them so far. If you think you've got the required skill for a task go on, implement it and you've got a high chance that it'll be accepted. The remaining barrier will only be that it's done well. :-)<br />
<br />
If you are such a person, you should feel free to edit this page.<br />
<br />
If you're not, you should post a feature request and discuss your idea on the forum or IRC. A coder with better knowledge of the code might give you the green light to add your feature here.<br />
<br />
Anybody should feel free to add "clues" to any tasks, that is entry points, traps to avoid, person to contact to discuss and so on.<br />
<br />
If you plan to work on a feature, write your name at the bottom of the feature, with the date. Note that if you spend too long at working on a feature we'll "free" it back (that is if you're not working on it. If you have problems implementing it, just tell us....)<br />
<br />
__TOC__<br />
<br />
== Game engine ==<br />
<br />
=== [[SDL 2 + OpenGL port]] ===<br />
<br />
Wesnoth currently depends on the unmaintained SDL 1.2.x code base and the 1.2.x-based versions of its companion libraries (SDL_image, SDL_mixer, SDL_net, SDL_ttf). This means Wesnoth can't benefit from features added in newer versions of SDL such as built-in support for Android and iOS, improved cross-platform Unicode support, built-in clipboard support, and any bug fixes made since SDL 1.2.15.<br />
<br />
The problem with porting Wesnoth to SDL 2.0 as explained by lipkab (who attempted the task as part of GSoC 2014) is that SDL 2.0's software rendering API lacks some things Wesnoth needs such as a specific alpha blending mode (TODO: link to conversations here).<br />
<br />
Furthermore, while software rendering is all good and nice, Wesnoth's approach does not scale well as display resolutions increase and the need for supporting high DPI configurations is factored in. One can easily see that our zoom functionality is clunky and inefficient. Having support for OpenGL and shaders would also enable us to implement fancier graphical gimmicks such as particle effects and atmospheric lighting. The downside of adding OpenGL to the mix is that we'd need people with a specific skillset to help us maintain the graphics engine in the long term, as hardware and driver-specific quirks are inevitable. Some users are also concerned about changing Wesnoth's hardware requirements so that they would be unable to play using old or unsupported hardware/OS configurations.<br />
<br />
For hardware rendering to be effective, in particular, Wesnoth would necessitate a complete rewrite of the graphics rendering code (the display and game_display classes), the image cache manager (used to abstract the process of loading images from disk and applying image path functions), and potentially GUI2's canvas code. All three would have to limit the amount of textures they allocate to the absolute minimum, using larger textures instead. For example, the image cache could be reimplemented to load images from an internal spritesheet generated during the game loading process. (WML/Lua support for spritesheets would be nice would defining an API for it would only distract from the actual task at hand.)<br />
<br />
The greatest difficulty in handling this task probably lies in writing a new optimized rendering engine and updating all code that relies on the old display/game_display API and semantics accordingly. The GUI2 framework code is presently unmaintained and it would take some time for somebody to study and change the current implementation. Finally, as we don't have a graphics engine maintainer at this time, this task involves an implicit long-term commitment to the project that extends beyond Wesnoth 1.14.x (or whichever series will see this project completed).<br />
<br />
-- currently worked on by Aginor - 3 August 2015<br />
<br />
=== Improve WML error reporting ===<br />
<br />
There are many programming bugs that give very unclear or cryptic error messages when using WML. Here are some examples:<br />
<br />
* <strike>When calling a macro with the wrong number of arguments, Wesnoth tells the number it expected, and the number it got, and at which line number it was instantiated. However, it would be helpful if it would also tell the line number of the definition of the macro. This might be helpful if someone is redefining macros.</strike><br />
<br />
* For places where a standard unit filter is expected, if one is not found, the game should point out a problem. For places where such a filter is expected but [filter] child should not appear, if one does it should report an error and a line number. Many users have a hard time with this kind of thing.<br />
<br />
* When doing scenario transitions in a campaign, if the side definitions don't matchup exactly the game tends to give unintelligible error messages. For instance, if one is transitioning and there is an AI side which does not have a leader (but it has a starting location in the map) it must have "no_leader = yes" in its [side] tag, or else when the team_builder objects enter stage two, the game will try to create a unit with an empty type, and the constructor of class unit will fail giving the error message "game::game_error tried to create unit with empty type". This really needs to be much better. For instance, giving a line number of a problem, or suggesting that no_leader should be used. (Note that, it is often possible to debug problems like this by turning on --log-debug=team_construction at commandline but I doubt that there are any users that know about this, you would only learn from reading the C++... without such debug info, fixing things like this can literally take hours.) For that matter it's some question why the team_builder doesn't just stop trying to build a unit when it sees it doesn't have enough info...<br />
<br />
* Recursive macros break the game, and not by stack overflow but by exhausting the heap (which usually takes forever on modern OSes and results in memory thrashing <i>before</i> resource starvation). It would be nice if we could catch this and report an error.<br />
<br />
Many WML users are students or beginners to programming, so any improvements to WML error reporting are a big help for their learning process -- many veterans would appreciate the extra help as well. In part we need fixits here and there, but more broadly we need a smarter system that can figure out what's really wrong when things go wrong, and give helpful suggestions.<br />
<br />
== Multiplayer/replay features ==<br />
<br />
=== Gold graph ===<br />
<br />
Make a graph feature, presumably in a dialog, that helps depict the history of a Wesnoth match for the benefit of spectators of a live game or replay. For example it could display army value, army + bank value, number of villages tagged, luck swings... This would also be particularly helpful for AI tweaking development, by helping coders identify the AI's weaknesses and strengths in the context of a particular scenario.<br />
<br />
A possible nice feature for this purpose would be to allow the user to click on the graph at key points which would trigger the "back to turn" mechanism to jump back in the replay, or automatically play the replay forwards from the beginning to that point... etc.<br />
<br />
Related email on dev-talk: https://mail.gna.org/public/wesnoth-dev/2014-02/msg00089.html<br />
<br />
=== Automatically link up to wesnothd server on a local network ===<br />
<br />
The feature request was to use ZeroConf technology, if available, to publish info of a running wesnothd instance to other machines on a local network. Then when they are searching to connect to an unofficial server, they won't have to learn the IP address.<br />
<br />
The extra code should be guarded with preprocessor flags so that ZeroConf does not become a mandatory build dependency of Wesnoth.<br />
<br />
https://gna.org/bugs/?13703<br />
<br />
=== Stock MP chat messages for language-independent communication ===<br />
<br />
Wesnoth has seen a large acceptance by international communities, and many players don't speak English. It would be nice we could have a set of messages that could be chatted, but which will be translated by our translators and displayed always in the current locale on each client, to make it easier to communicate in mp games / on the mp server.<br />
<br />
The easy part is adding the messages. They would ideally be stored in a WML file in the core data dir so they can be easily modified and translated like all other mainline WML. The hard part is devising the interaction mechanism so that a player can easily make use of these stock messages in the MP lobby or in game.<br />
<br />
<br />
== GUI2 framework ==<br />
<br />
As of 1.13.1+dev, GUI2's core and framework (including widgets and their APIs) are completely unmaintained. A prospective maintainer would have to be able to understand pretty advanced C++ making abundant use of template-based abstractions (most notably, the event handling code is almost completely implemented this way), multiple inheritance, and polymorphism. There is also some missing functionality (such as combo boxes and modeless dialogs) that the author did not get to implement, as well as work-in-progress functionality (new listbox and tooltip implementations, not enabled by default).<br />
<br />
=== Multi-line textbox, single-line textbox improvements ===<br />
<br />
GUI2 has a single-line textbox widget which works well for most purposes but falls short in a few niche cases. In particular, it's not possible to make it read-only without entirely disabling the user's ability to interact with the contents, which is inconvenient in places like the Game Paths dialog (or the About dialog in version 1.13.2 and later).<br />
<br />
The widget also does not support multi-line contents. For this reason, GUI2 dialogs that present multi-line (read-only) text to the user make use of the scroll_label+clipboard button convention introduced by shadowm in 1.12.x (see the Chat Log, Gamestate Inspector, and WML load error dialogs for examples). This has the obvious limitations of not allowing the user to select specific portions of the contents, and not supporting editing.<br />
<br />
=== Generic tab_container widget ===<br />
<br />
After 1.13.1 was released, shadowm discovered a trick to implement tabbed dialogs in GUI2, involving a general widget bug fix to make children consider their parents' visibility when processing events, and an addition to the stacked_widget API enabling users to hide all but a single active layer.<br />
<br />
The About dialog introduced in version 1.13.2 will use a horizontal listbox in combination with a stacked_widget, but ideally we should be able to reuse this pattern in more places without having to clutter up dialog implementations with the requisite code to synchronize the listbox and the stacked_widget's states. This hypothetical tab_container widget would combine the listbox and the stacked_widget to implement tabs in a cleaner fashion without having to expose the implementation details to individual dialog instances.<br />
<br />
By all means, this should be an EasyCoding task, but GUI2's unusual code layout and API design conventions (compared to the rest of Wesnoth) ''may'' make it harder than it's supposed to be.<br />
<br />
If you want to work on this, you should poke shadowm to make sure you don't duplicate efforts as he is working on a GUI2 port of the Preferences dialog and may take up this task after a while if no-one has done so first.<br />
<br />
=== GUI2 menu ===<br />
<br />
GUI2 most notably lacks a menu display (either for the game's menu bar or context menus). It is not known at this time how much work would have to be done in GUI2's core and framework to allow implementing this widget -- in particular considering how GUI2 widgets are never used without a parent dialog, which menus might not necessarily have, at least with the current gameplay UI implementation.<br />
<br />
GUI2 menu FR: https://gna.org/bugs/?22820<br />
<br />
=== GUI2 themable in-game UI ===<br />
<br />
The "themable" in-game UI refers to the gameplay UI including the sidebar, menu/status bar, and map view. Most of it is not implemented using proper GUI1 widgets at all, except for the various interactive buttons, menus (which are floating GUI1 listboxes), and the command and chat input boxes (floating GUI1 textboxes).<br />
<br />
Moving the theme UI to GUI2 would probably require finishing up theme support in GUI2 (which is largely incomplete), and may benefit from a rendering engine redesign as is also required for porting the game to SDL 2; in particular so that the GUI aspects are more clearly separated from the game board rendering aspect.<br />
<br />
'''Note:''' This task is here solely for the sake of completeness, and only somebody very well versed in the intricacies of Wesnoth's game rendering code and GUI2 should '''ever''' try to take it up. (And even then, I would not want to be anywhere near that person when they inevitably break down and fling one or more tables around upon realizing the sheer complexity of the task. -- shadowm)<br />
<br />
== Add-ons server and client ==<br />
<br />
=== Passphrase hashing ===<br />
<br />
The add-ons server (campaignd) uses a very dumb authentication scheme for uploading add-ons where an author sets a passphrase (or gets a random passphrase assigned by the game) the first time they upload to the server, and subsequent uploads of the same add-on are only allowed if the passphrase matches. The passphrase is stored in clear text form both client and server-side, which has [http://forums.wesnoth.org/viewtopic.php?f=62&t=42663 various implications].<br />
<br />
Ideally, the server would store all passphrases in a hashed form.<br />
<br />
=== Port redirection ===<br />
<br />
Wesnoth's MP server (wesnothd) uses a single port (15000) for servicing most requests and redirects clients to different ports according to the game client's version number. This allows the server administrators to reassign ports freely without having to modify the game client.<br />
<br />
The add-ons server (campaignd) and client instead have a hardcoded default port number for a Wesnoth version series. For example, 1.12.x is hardcoded to default to 15006. This becomes a maintainability issue as add-on servers get decommissioned and their port numbers can't be reused for fear of misdirecting obsolete client versions to the wrong instance with incompatible add-ons.<br />
<br />
=== Incremental upgrades and uploads ===<br />
<br />
Right now, whenever a new add-on version is published, players must download (or conversely, upload) the whole contents, even if the differences between both versions amount to a single line change. This is not a problem with small add-ons, but there is a substantial number of large (> 10 MiB) add-ons on the server. Large downloads and uploads are inconvenient for players on slow or metered connections, and also increase the server's traffic requirements.<br />
<br />
Ideally, for each add-on it attempts to upgrade or upload, the add-ons client should be able to send the server its local list of files with hashes, so that the server can identify which files need to be transmitted to/by the client, all archived on the fly like normal add-on downloads/uploads. The obvious downside is that the server would need to calculate an add-on's file hashes on demand, increasing CPU usage when servicing upgrades or uploads; this could be avoided by generating its file hashes when servicing uploads instead.<br />
<br />
See also: https://gna.org/bugs/?19972 (for uploads only; there should be one for downloads too but I couldn't find it -- shadowm)<br />
<br />
=== Porting the server to Asio ===<br />
<br />
Although the client uses Boost.Asio for communications, the server continues to use SDL_net instead. This means, amongst other things, that we are limited to IPv4 and an unmaintained codebase full of hacks waiting to break apart in the future (Wesnoth's SDL_net code does, amongst other things, tamper with what are supposed to be opaque data structures maintained by SDL_net).<br />
<br />
There was an attempt to build a new add-ons server (umcd) using Asio as part of GSoC, but it ultimately failed and the developers are no longer involved with the project. The umcd codebase status as of this writing is unknown.<br />
<br />
== Multiplayer server and client ==<br />
<br />
=== Improve server WML processing ===<br />
<br />
The server uses its own custom WML implementation separate from the main engine's to store and process WML objects for games -- see [[WesnothdDesign#simple_wml]]. This is intended to improve performance and reduce wesnothd's memory footprint with large numbers of games and complex scenarios. However, the simple_wml code sometimes messes up WML attribute translatability, resulting in MP scenarios/campaigns being untranslated for non-hosts in networked MP (http://gna.org/bugs/?22989). Fixing this is not easy due to the aforementioned performance requirements.<br />
<br />
== Data structures ==<br />
<br />
=== Make the Lua state persistent ===<br />
<br />
Wesnoth is designed to make all data files as simple and human-readable as possible, including savefiles. WML is designed so that it is always very easy to write the entire WML state as plaintext and reload it correctly. There are many benefits of this, but especially <br />
<br />
* It makes it easy for beginners / students to read the files and see what's going on<br />
* It makes it easy for us to debug problems without needing special tools to read the savefiles<br />
* It makes it easy to edit save files to work around any possible bugs. Users have even reported opening savefiles from version 1.10 in microsoft word (!) to correct minor bugs in mp and resaving them, as a policy for tournament games.<br />
<br />
This was unfortunately somewhat lost when we added lua. If you use lua in your scenarios, things become more complicated because we can't save the lua state at all. If you rely on lua variables to save information, then when your scenario is saved and reloaded those variables will get wiped. The wesnoth engine provides places for lua to hook into "onsave" and "onload" so that the scenario developer can write their own serialization routines, but this is just punting the problem to scenario developers.<br />
<br />
We also provide the "preload" event type specifically to work around the issue -- unlike all other events, preload is called exactly once for every time the scenario is loaded. The purpose is to give another way that lua users can initialize a table of lua functions used by their addon and guarantee that this happens before the functions are called. But to non-lua users this event is basically unsafe for normal use otherwise -- if there is a preload event which e.g. increments a WML variable, then the WML state now depends on how many times the scenario is saved or reloaded. In a normal scenario, save / reload events should be invisible, and normally if this isn't the case it indicates a bug similar to an OOS.<br />
<br />
A nice solution to this would be if we properly serialized the lua state alongside the WML in a savefile. The most suitable solution is probably this library: https://github.com/fnuecke/eris<br />
It would require significant additional work as well to find a good way to serialize the C functions that we expose to lua, and the userdata like units held by lua.<br />
<br />
More info here: http://lua-users.org/wiki/PlutoLibrary<br />
<br />
<br />
[[Category:Development]]<br />
[[Category:Future]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=NotSoEasyCoding&diff=57805NotSoEasyCoding2016-07-17T12:58:57Z<p>Jyrkive: Marked the Monte Carlo damage calculation task as done</p>
<hr />
<div>This page lists projects which are considered a good idea by the developers but which have nobody working on them so far. If you think you've got the required skill for a task go on, implement it and you've got a high chance that it'll be accepted. The remaining barrier will only be that it's done well. :-)<br />
<br />
If you are such a person, you should feel free to edit this page.<br />
<br />
If you're not, you should post a feature request and discuss your idea on the forum or IRC. A coder with better knowledge of the code might give you the green light to add your feature here.<br />
<br />
Anybody should feel free to add "clues" to any tasks, that is entry points, traps to avoid, person to contact to discuss and so on.<br />
<br />
If you plan to work on a feature, write your name at the bottom of the feature, with the date. Note that if you spend too long at working on a feature we'll "free" it back (that is if you're not working on it. If you have problems implementing it, just tell us....)<br />
<br />
__TOC__<br />
<br />
== Game engine ==<br />
<br />
=== [[SDL 2 + OpenGL port]] ===<br />
<br />
Wesnoth currently depends on the unmaintained SDL 1.2.x code base and the 1.2.x-based versions of its companion libraries (SDL_image, SDL_mixer, SDL_net, SDL_ttf). This means Wesnoth can't benefit from features added in newer versions of SDL such as built-in support for Android and iOS, improved cross-platform Unicode support, built-in clipboard support, and any bug fixes made since SDL 1.2.15.<br />
<br />
The problem with porting Wesnoth to SDL 2.0 as explained by lipkab (who attempted the task as part of GSoC 2014) is that SDL 2.0's software rendering API lacks some things Wesnoth needs such as a specific alpha blending mode (TODO: link to conversations here).<br />
<br />
Furthermore, while software rendering is all good and nice, Wesnoth's approach does not scale well as display resolutions increase and the need for supporting high DPI configurations is factored in. One can easily see that our zoom functionality is clunky and inefficient. Having support for OpenGL and shaders would also enable us to implement fancier graphical gimmicks such as particle effects and atmospheric lighting. The downside of adding OpenGL to the mix is that we'd need people with a specific skillset to help us maintain the graphics engine in the long term, as hardware and driver-specific quirks are inevitable. Some users are also concerned about changing Wesnoth's hardware requirements so that they would be unable to play using old or unsupported hardware/OS configurations.<br />
<br />
For hardware rendering to be effective, in particular, Wesnoth would necessitate a complete rewrite of the graphics rendering code (the display and game_display classes), the image cache manager (used to abstract the process of loading images from disk and applying image path functions), and potentially GUI2's canvas code. All three would have to limit the amount of textures they allocate to the absolute minimum, using larger textures instead. For example, the image cache could be reimplemented to load images from an internal spritesheet generated during the game loading process. (WML/Lua support for spritesheets would be nice would defining an API for it would only distract from the actual task at hand.)<br />
<br />
The greatest difficulty in handling this task probably lies in writing a new optimized rendering engine and updating all code that relies on the old display/game_display API and semantics accordingly. The GUI2 framework code is presently unmaintained and it would take some time for somebody to study and change the current implementation. Finally, as we don't have a graphics engine maintainer at this time, this task involves an implicit long-term commitment to the project that extends beyond Wesnoth 1.14.x (or whichever series will see this project completed).<br />
<br />
-- currently worked on by Aginor - 3 August 2015<br />
<br />
=== Improve WML error reporting ===<br />
<br />
There are many programming bugs that give very unclear or cryptic error messages when using WML. Here are some examples:<br />
<br />
* <strike>When calling a macro with the wrong number of arguments, Wesnoth tells the number it expected, and the number it got, and at which line number it was instantiated. However, it would be helpful if it would also tell the line number of the definition of the macro. This might be helpful if someone is redefining macros.</strike><br />
<br />
* For places where a standard unit filter is expected, if one is not found, the game should point out a problem. For places where such a filter is expected but [filter] child should not appear, if one does it should report an error and a line number. Many users have a hard time with this kind of thing.<br />
<br />
* When doing scenario transitions in a campaign, if the side definitions don't matchup exactly the game tends to give unintelligible error messages. For instance, if one is transitioning and there is an AI side which does not have a leader (but it has a starting location in the map) it must have "no_leader = yes" in its [side] tag, or else when the team_builder objects enter stage two, the game will try to create a unit with an empty type, and the constructor of class unit will fail giving the error message "game::game_error tried to create unit with empty type". This really needs to be much better. For instance, giving a line number of a problem, or suggesting that no_leader should be used. (Note that, it is often possible to debug problems like this by turning on --log-debug=team_construction at commandline but I doubt that there are any users that know about this, you would only learn from reading the C++... without such debug info, fixing things like this can literally take hours.) For that matter it's some question why the team_builder doesn't just stop trying to build a unit when it sees it doesn't have enough info...<br />
<br />
* Recursive macros break the game, and not by stack overflow but by exhausting the heap (which usually takes forever on modern OSes and results in memory thrashing <i>before</i> resource starvation). It would be nice if we could catch this and report an error.<br />
<br />
Many WML users are students or beginners to programming, so any improvements to WML error reporting are a big help for their learning process -- many veterans would appreciate the extra help as well. In part we need fixits here and there, but more broadly we need a smarter system that can figure out what's really wrong when things go wrong, and give helpful suggestions.<br />
<br />
=== <s>Add a new damage stats calculation mode to support scenarios with extremely high stats</s> ===<br />
<br />
<b>Done by Jyrki Vesterinen: https://github.com/wesnoth/wesnoth/pull/702</b><br />
<br />
Most players use the damage calculation window constantly when they play so that they can see what to expect from various attacks. The way the game is currently set up, when a unit is selected, as soon as another unit is moused over the stats for an attack will be calculated, whether or not the user actually brings up the window.<br />
<br />
The probabilities for the various outcomes are calculated by explicitly calculating the distribution of attacker and defender hp / slowed status after each swing by either party, one swing at a time. Thus at the end we get exact probabilities of the possible outcomes.<br />
<br />
However, this comes at a cost -- the running time of the calculation can in the worst case be something like O( attacker_hp * defender_hp * total_number_of_strikes). In default era wesnoth (and in much of UMC) this is fine because units don't get extremely high hp values. However in some UMC units do get extremely high hp, and many will also have specials like drain and berzerk, and may have many different weapons as well. At some point these scenarios become unplayable even on a machine of moderate specs because mousing over any unit causes > 10 seconds of lag during which the game becomes unresponsive, trying to calculate attack outcomes.<br />
<br />
In these cases, it would be better to give an "inexact" damage prediction, based on just simulating a few thousand attacks and plotting the results. Since it's a monte carlo algorithm rather than an exact algorithm, there will be some error, but if it makes the game playable then it's clearly an improvement, and anyways for "extreme stats" battles with high strikes and damage, the outcome is in a sense less random anyways because the outcomes with inordinate amounts of hits and misses are more and more rare (law of large numbers), so the error shouldn't actually be terribly significant.<br />
<br />
If there were an advanced preference that caused the predictions to use such an algorithm instead of the current one, so that the user could turn it on if they start to get problems, it would eliminate a significant headache for some UMC.<br />
<br />
<br />
== Multiplayer/replay features ==<br />
<br />
=== Gold graph ===<br />
<br />
Make a graph feature, presumably in a dialog, that helps depict the history of a Wesnoth match for the benefit of spectators of a live game or replay. For example it could display army value, army + bank value, number of villages tagged, luck swings... This would also be particularly helpful for AI tweaking development, by helping coders identify the AI's weaknesses and strengths in the context of a particular scenario.<br />
<br />
A possible nice feature for this purpose would be to allow the user to click on the graph at key points which would trigger the "back to turn" mechanism to jump back in the replay, or automatically play the replay forwards from the beginning to that point... etc.<br />
<br />
Related email on dev-talk: https://mail.gna.org/public/wesnoth-dev/2014-02/msg00089.html<br />
<br />
=== Automatically link up to wesnothd server on a local network ===<br />
<br />
The feature request was to use ZeroConf technology, if available, to publish info of a running wesnothd instance to other machines on a local network. Then when they are searching to connect to an unofficial server, they won't have to learn the IP address.<br />
<br />
The extra code should be guarded with preprocessor flags so that ZeroConf does not become a mandatory build dependency of Wesnoth.<br />
<br />
https://gna.org/bugs/?13703<br />
<br />
=== Stock MP chat messages for language-independent communication ===<br />
<br />
Wesnoth has seen a large acceptance by international communities, and many players don't speak English. It would be nice we could have a set of messages that could be chatted, but which will be translated by our translators and displayed always in the current locale on each client, to make it easier to communicate in mp games / on the mp server.<br />
<br />
The easy part is adding the messages. They would ideally be stored in a WML file in the core data dir so they can be easily modified and translated like all other mainline WML. The hard part is devising the interaction mechanism so that a player can easily make use of these stock messages in the MP lobby or in game.<br />
<br />
<br />
== GUI2 framework ==<br />
<br />
As of 1.13.1+dev, GUI2's core and framework (including widgets and their APIs) are completely unmaintained. A prospective maintainer would have to be able to understand pretty advanced C++ making abundant use of template-based abstractions (most notably, the event handling code is almost completely implemented this way), multiple inheritance, and polymorphism. There is also some missing functionality (such as combo boxes and modeless dialogs) that the author did not get to implement, as well as work-in-progress functionality (new listbox and tooltip implementations, not enabled by default).<br />
<br />
=== Multi-line textbox, single-line textbox improvements ===<br />
<br />
GUI2 has a single-line textbox widget which works well for most purposes but falls short in a few niche cases. In particular, it's not possible to make it read-only without entirely disabling the user's ability to interact with the contents, which is inconvenient in places like the Game Paths dialog (or the About dialog in version 1.13.2 and later).<br />
<br />
The widget also does not support multi-line contents. For this reason, GUI2 dialogs that present multi-line (read-only) text to the user make use of the scroll_label+clipboard button convention introduced by shadowm in 1.12.x (see the Chat Log, Gamestate Inspector, and WML load error dialogs for examples). This has the obvious limitations of not allowing the user to select specific portions of the contents, and not supporting editing.<br />
<br />
=== Generic tab_container widget ===<br />
<br />
After 1.13.1 was released, shadowm discovered a trick to implement tabbed dialogs in GUI2, involving a general widget bug fix to make children consider their parents' visibility when processing events, and an addition to the stacked_widget API enabling users to hide all but a single active layer.<br />
<br />
The About dialog introduced in version 1.13.2 will use a horizontal listbox in combination with a stacked_widget, but ideally we should be able to reuse this pattern in more places without having to clutter up dialog implementations with the requisite code to synchronize the listbox and the stacked_widget's states. This hypothetical tab_container widget would combine the listbox and the stacked_widget to implement tabs in a cleaner fashion without having to expose the implementation details to individual dialog instances.<br />
<br />
By all means, this should be an EasyCoding task, but GUI2's unusual code layout and API design conventions (compared to the rest of Wesnoth) ''may'' make it harder than it's supposed to be.<br />
<br />
If you want to work on this, you should poke shadowm to make sure you don't duplicate efforts as he is working on a GUI2 port of the Preferences dialog and may take up this task after a while if no-one has done so first.<br />
<br />
=== GUI2 menu ===<br />
<br />
GUI2 most notably lacks a menu display (either for the game's menu bar or context menus). It is not known at this time how much work would have to be done in GUI2's core and framework to allow implementing this widget -- in particular considering how GUI2 widgets are never used without a parent dialog, which menus might not necessarily have, at least with the current gameplay UI implementation.<br />
<br />
GUI2 menu FR: https://gna.org/bugs/?22820<br />
<br />
=== GUI2 themable in-game UI ===<br />
<br />
The "themable" in-game UI refers to the gameplay UI including the sidebar, menu/status bar, and map view. Most of it is not implemented using proper GUI1 widgets at all, except for the various interactive buttons, menus (which are floating GUI1 listboxes), and the command and chat input boxes (floating GUI1 textboxes).<br />
<br />
Moving the theme UI to GUI2 would probably require finishing up theme support in GUI2 (which is largely incomplete), and may benefit from a rendering engine redesign as is also required for porting the game to SDL 2; in particular so that the GUI aspects are more clearly separated from the game board rendering aspect.<br />
<br />
'''Note:''' This task is here solely for the sake of completeness, and only somebody very well versed in the intricacies of Wesnoth's game rendering code and GUI2 should '''ever''' try to take it up. (And even then, I would not want to be anywhere near that person when they inevitably break down and fling one or more tables around upon realizing the sheer complexity of the task. -- shadowm)<br />
<br />
== Add-ons server and client ==<br />
<br />
=== Passphrase hashing ===<br />
<br />
The add-ons server (campaignd) uses a very dumb authentication scheme for uploading add-ons where an author sets a passphrase (or gets a random passphrase assigned by the game) the first time they upload to the server, and subsequent uploads of the same add-on are only allowed if the passphrase matches. The passphrase is stored in clear text form both client and server-side, which has [http://forums.wesnoth.org/viewtopic.php?f=62&t=42663 various implications].<br />
<br />
Ideally, the server would store all passphrases in a hashed form.<br />
<br />
=== Port redirection ===<br />
<br />
Wesnoth's MP server (wesnothd) uses a single port (15000) for servicing most requests and redirects clients to different ports according to the game client's version number. This allows the server administrators to reassign ports freely without having to modify the game client.<br />
<br />
The add-ons server (campaignd) and client instead have a hardcoded default port number for a Wesnoth version series. For example, 1.12.x is hardcoded to default to 15006. This becomes a maintainability issue as add-on servers get decommissioned and their port numbers can't be reused for fear of misdirecting obsolete client versions to the wrong instance with incompatible add-ons.<br />
<br />
=== Incremental upgrades and uploads ===<br />
<br />
Right now, whenever a new add-on version is published, players must download (or conversely, upload) the whole contents, even if the differences between both versions amount to a single line change. This is not a problem with small add-ons, but there is a substantial number of large (> 10 MiB) add-ons on the server. Large downloads and uploads are inconvenient for players on slow or metered connections, and also increase the server's traffic requirements.<br />
<br />
Ideally, for each add-on it attempts to upgrade or upload, the add-ons client should be able to send the server its local list of files with hashes, so that the server can identify which files need to be transmitted to/by the client, all archived on the fly like normal add-on downloads/uploads. The obvious downside is that the server would need to calculate an add-on's file hashes on demand, increasing CPU usage when servicing upgrades or uploads; this could be avoided by generating its file hashes when servicing uploads instead.<br />
<br />
See also: https://gna.org/bugs/?19972 (for uploads only; there should be one for downloads too but I couldn't find it -- shadowm)<br />
<br />
=== Porting the server to Asio ===<br />
<br />
Although the client uses Boost.Asio for communications, the server continues to use SDL_net instead. This means, amongst other things, that we are limited to IPv4 and an unmaintained codebase full of hacks waiting to break apart in the future (Wesnoth's SDL_net code does, amongst other things, tamper with what are supposed to be opaque data structures maintained by SDL_net).<br />
<br />
There was an attempt to build a new add-ons server (umcd) using Asio as part of GSoC, but it ultimately failed and the developers are no longer involved with the project. The umcd codebase status as of this writing is unknown.<br />
<br />
== Multiplayer server and client ==<br />
<br />
=== Improve server WML processing ===<br />
<br />
The server uses its own custom WML implementation separate from the main engine's to store and process WML objects for games -- see [[WesnothdDesign#simple_wml]]. This is intended to improve performance and reduce wesnothd's memory footprint with large numbers of games and complex scenarios. However, the simple_wml code sometimes messes up WML attribute translatability, resulting in MP scenarios/campaigns being untranslated for non-hosts in networked MP (http://gna.org/bugs/?22989). Fixing this is not easy due to the aforementioned performance requirements.<br />
<br />
== Data structures ==<br />
<br />
=== Make the Lua state persistent ===<br />
<br />
Wesnoth is designed to make all data files as simple and human-readable as possible, including savefiles. WML is designed so that it is always very easy to write the entire WML state as plaintext and reload it correctly. There are many benefits of this, but especially <br />
<br />
* It makes it easy for beginners / students to read the files and see what's going on<br />
* It makes it easy for us to debug problems without needing special tools to read the savefiles<br />
* It makes it easy to edit save files to work around any possible bugs. Users have even reported opening savefiles from version 1.10 in microsoft word (!) to correct minor bugs in mp and resaving them, as a policy for tournament games.<br />
<br />
This was unfortunately somewhat lost when we added lua. If you use lua in your scenarios, things become more complicated because we can't save the lua state at all. If you rely on lua variables to save information, then when your scenario is saved and reloaded those variables will get wiped. The wesnoth engine provides places for lua to hook into "onsave" and "onload" so that the scenario developer can write their own serialization routines, but this is just punting the problem to scenario developers.<br />
<br />
We also provide the "preload" event type specifically to work around the issue -- unlike all other events, preload is called exactly once for every time the scenario is loaded. The purpose is to give another way that lua users can initialize a table of lua functions used by their addon and guarantee that this happens before the functions are called. But to non-lua users this event is basically unsafe for normal use otherwise -- if there is a preload event which e.g. increments a WML variable, then the WML state now depends on how many times the scenario is saved or reloaded. In a normal scenario, save / reload events should be invisible, and normally if this isn't the case it indicates a bug similar to an OOS.<br />
<br />
A nice solution to this would be if we properly serialized the lua state alongside the WML in a savefile. The most suitable solution is probably this library: https://github.com/fnuecke/eris<br />
It would require significant additional work as well to find a good way to serialize the C functions that we expose to lua, and the userdata like units held by lua.<br />
<br />
More info here: http://lua-users.org/wiki/PlutoLibrary<br />
<br />
<br />
[[Category:Development]]<br />
[[Category:Future]]</div>Jyrkivehttps://wiki.wesnoth.org/index.php?title=NotSoEasyCoding&diff=57730NotSoEasyCoding2016-07-07T19:56:26Z<p>Jyrkive: Assigned the "Monte Carlo simulation mode for damage calculation" task to myself</p>
<hr />
<div>This page lists projects which are considered a good idea by the developers but which have nobody working on them so far. If you think you've got the required skill for a task go on, implement it and you've got a high chance that it'll be accepted. The remaining barrier will only be that it's done well. :-)<br />
<br />
If you are such a person, you should feel free to edit this page.<br />
<br />
If you're not, you should post a feature request and discuss your idea on the forum or IRC. A coder with better knowledge of the code might give you the green light to add your feature here.<br />
<br />
Anybody should feel free to add "clues" to any tasks, that is entry points, traps to avoid, person to contact to discuss and so on.<br />
<br />
If you plan to work on a feature, write your name at the bottom of the feature, with the date. Note that if you spend too long at working on a feature we'll "free" it back (that is if you're not working on it. If you have problems implementing it, just tell us....)<br />
<br />
__TOC__<br />
<br />
== Game engine ==<br />
<br />
=== [[SDL 2 + OpenGL port]] ===<br />
<br />
Wesnoth currently depends on the unmaintained SDL 1.2.x code base and the 1.2.x-based versions of its companion libraries (SDL_image, SDL_mixer, SDL_net, SDL_ttf). This means Wesnoth can't benefit from features added in newer versions of SDL such as built-in support for Android and iOS, improved cross-platform Unicode support, built-in clipboard support, and any bug fixes made since SDL 1.2.15.<br />
<br />
The problem with porting Wesnoth to SDL 2.0 as explained by lipkab (who attempted the task as part of GSoC 2014) is that SDL 2.0's software rendering API lacks some things Wesnoth needs such as a specific alpha blending mode (TODO: link to conversations here).<br />
<br />
Furthermore, while software rendering is all good and nice, Wesnoth's approach does not scale well as display resolutions increase and the need for supporting high DPI configurations is factored in. One can easily see that our zoom functionality is clunky and inefficient. Having support for OpenGL and shaders would also enable us to implement fancier graphical gimmicks such as particle effects and atmospheric lighting. The downside of adding OpenGL to the mix is that we'd need people with a specific skillset to help us maintain the graphics engine in the long term, as hardware and driver-specific quirks are inevitable. Some users are also concerned about changing Wesnoth's hardware requirements so that they would be unable to play using old or unsupported hardware/OS configurations.<br />
<br />
For hardware rendering to be effective, in particular, Wesnoth would necessitate a complete rewrite of the graphics rendering code (the display and game_display classes), the image cache manager (used to abstract the process of loading images from disk and applying image path functions), and potentially GUI2's canvas code. All three would have to limit the amount of textures they allocate to the absolute minimum, using larger textures instead. For example, the image cache could be reimplemented to load images from an internal spritesheet generated during the game loading process. (WML/Lua support for spritesheets would be nice would defining an API for it would only distract from the actual task at hand.)<br />
<br />
The greatest difficulty in handling this task probably lies in writing a new optimized rendering engine and updating all code that relies on the old display/game_display API and semantics accordingly. The GUI2 framework code is presently unmaintained and it would take some time for somebody to study and change the current implementation. Finally, as we don't have a graphics engine maintainer at this time, this task involves an implicit long-term commitment to the project that extends beyond Wesnoth 1.14.x (or whichever series will see this project completed).<br />
<br />
-- currently worked on by Aginor - 3 August 2015<br />
<br />
=== Improve WML error reporting ===<br />
<br />
There are many programming bugs that give very unclear or cryptic error messages when using WML. Here are some examples:<br />
<br />
* <strike>When calling a macro with the wrong number of arguments, Wesnoth tells the number it expected, and the number it got, and at which line number it was instantiated. However, it would be helpful if it would also tell the line number of the definition of the macro. This might be helpful if someone is redefining macros.</strike><br />
<br />
* For places where a standard unit filter is expected, if one is not found, the game should point out a problem. For places where such a filter is expected but [filter] child should not appear, if one does it should report an error and a line number. Many users have a hard time with this kind of thing.<br />
<br />
* When doing scenario transitions in a campaign, if the side definitions don't matchup exactly the game tends to give unintelligible error messages. For instance, if one is transitioning and there is an AI side which does not have a leader (but it has a starting location in the map) it must have "no_leader = yes" in its [side] tag, or else when the team_builder objects enter stage two, the game will try to create a unit with an empty type, and the constructor of class unit will fail giving the error message "game::game_error tried to create unit with empty type". This really needs to be much better. For instance, giving a line number of a problem, or suggesting that no_leader should be used. (Note that, it is often possible to debug problems like this by turning on --log-debug=team_construction at commandline but I doubt that there are any users that know about this, you would only learn from reading the C++... without such debug info, fixing things like this can literally take hours.) For that matter it's some question why the team_builder doesn't just stop trying to build a unit when it sees it doesn't have enough info...<br />
<br />
* Recursive macros break the game, and not by stack overflow but by exhausting the heap (which usually takes forever on modern OSes and results in memory thrashing <i>before</i> resource starvation). It would be nice if we could catch this and report an error.<br />
<br />
Many WML users are students or beginners to programming, so any improvements to WML error reporting are a big help for their learning process -- many veterans would appreciate the extra help as well. In part we need fixits here and there, but more broadly we need a smarter system that can figure out what's really wrong when things go wrong, and give helpful suggestions.<br />
<br />
=== Add a new damage stats calculation mode to support scenarios with extremely high stats ===<br />
<br />
-- Jyrki Vesterinen started work on this task on 7 July 2016<br />
<br />
Most players use the damage calculation window constantly when they play so that they can see what to expect from various attacks. The way the game is currently set up, when a unit is selected, as soon as another unit is moused over the stats for an attack will be calculated, whether or not the user actually brings up the window.<br />
<br />
The probabilities for the various outcomes are calculated by explicitly calculating the distribution of attacker and defender hp / slowed status after each swing by either party, one swing at a time. Thus at the end we get exact probabilities of the possible outcomes.<br />
<br />
However, this comes at a cost -- the running time of the calculation can in the worst case be something like O( attacker_hp * defender_hp * total_number_of_strikes). In default era wesnoth (and in much of UMC) this is fine because units don't get extremely high hp values. However in some UMC units do get extremely high hp, and many will also have specials like drain and berzerk, and may have many different weapons as well. At some point these scenarios become unplayable even on a machine of moderate specs because mousing over any unit causes > 10 seconds of lag during which the game becomes unresponsive, trying to calculate attack outcomes.<br />
<br />
In these cases, it would be better to give an "inexact" damage prediction, based on just simulating a few thousand attacks and plotting the results. Since it's a monte carlo algorithm rather than an exact algorithm, there will be some error, but if it makes the game playable then it's clearly an improvement, and anyways for "extreme stats" battles with high strikes and damage, the outcome is in a sense less random anyways because the outcomes with inordinate amounts of hits and misses are more and more rare (law of large numbers), so the error shouldn't actually be terribly significant.<br />
<br />
If there were an advanced preference that caused the predictions to use such an algorithm instead of the current one, so that the user could turn it on if they start to get problems, it would eliminate a significant headache for some UMC.<br />
<br />
<br />
== Multiplayer/replay features ==<br />
<br />
=== Gold graph ===<br />
<br />
Make a graph feature, presumably in a dialog, that helps depict the history of a Wesnoth match for the benefit of spectators of a live game or replay. For example it could display army value, army + bank value, number of villages tagged, luck swings... This would also be particularly helpful for AI tweaking development, by helping coders identify the AI's weaknesses and strengths in the context of a particular scenario.<br />
<br />
A possible nice feature for this purpose would be to allow the user to click on the graph at key points which would trigger the "back to turn" mechanism to jump back in the replay, or automatically play the replay forwards from the beginning to that point... etc.<br />
<br />
Related email on dev-talk: https://mail.gna.org/public/wesnoth-dev/2014-02/msg00089.html<br />
<br />
=== Automatically link up to wesnothd server on a local network ===<br />
<br />
The feature request was to use ZeroConf technology, if available, to publish info of a running wesnothd instance to other machines on a local network. Then when they are searching to connect to an unofficial server, they won't have to learn the IP address.<br />
<br />
The extra code should be guarded with preprocessor flags so that ZeroConf does not become a mandatory build dependency of Wesnoth.<br />
<br />
https://gna.org/bugs/?13703<br />
<br />
=== Stock MP chat messages for language-independent communication ===<br />
<br />
Wesnoth has seen a large acceptance by international communities, and many players don't speak English. It would be nice we could have a set of messages that could be chatted, but which will be translated by our translators and displayed always in the current locale on each client, to make it easier to communicate in mp games / on the mp server.<br />
<br />
The easy part is adding the messages. They would ideally be stored in a WML file in the core data dir so they can be easily modified and translated like all other mainline WML. The hard part is devising the interaction mechanism so that a player can easily make use of these stock messages in the MP lobby or in game.<br />
<br />
<br />
== GUI2 framework ==<br />
<br />
As of 1.13.1+dev, GUI2's core and framework (including widgets and their APIs) are completely unmaintained. A prospective maintainer would have to be able to understand pretty advanced C++ making abundant use of template-based abstractions (most notably, the event handling code is almost completely implemented this way), multiple inheritance, and polymorphism. There is also some missing functionality (such as combo boxes and modeless dialogs) that the author did not get to implement, as well as work-in-progress functionality (new listbox and tooltip implementations, not enabled by default).<br />
<br />
=== Multi-line textbox, single-line textbox improvements ===<br />
<br />
GUI2 has a single-line textbox widget which works well for most purposes but falls short in a few niche cases. In particular, it's not possible to make it read-only without entirely disabling the user's ability to interact with the contents, which is inconvenient in places like the Game Paths dialog (or the About dialog in version 1.13.2 and later).<br />
<br />
The widget also does not support multi-line contents. For this reason, GUI2 dialogs that present multi-line (read-only) text to the user make use of the scroll_label+clipboard button convention introduced by shadowm in 1.12.x (see the Chat Log, Gamestate Inspector, and WML load error dialogs for examples). This has the obvious limitations of not allowing the user to select specific portions of the contents, and not supporting editing.<br />
<br />
=== Generic tab_container widget ===<br />
<br />
After 1.13.1 was released, shadowm discovered a trick to implement tabbed dialogs in GUI2, involving a general widget bug fix to make children consider their parents' visibility when processing events, and an addition to the stacked_widget API enabling users to hide all but a single active layer.<br />
<br />
The About dialog introduced in version 1.13.2 will use a horizontal listbox in combination with a stacked_widget, but ideally we should be able to reuse this pattern in more places without having to clutter up dialog implementations with the requisite code to synchronize the listbox and the stacked_widget's states. This hypothetical tab_container widget would combine the listbox and the stacked_widget to implement tabs in a cleaner fashion without having to expose the implementation details to individual dialog instances.<br />
<br />
By all means, this should be an EasyCoding task, but GUI2's unusual code layout and API design conventions (compared to the rest of Wesnoth) ''may'' make it harder than it's supposed to be.<br />
<br />
If you want to work on this, you should poke shadowm to make sure you don't duplicate efforts as he is working on a GUI2 port of the Preferences dialog and may take up this task after a while if no-one has done so first.<br />
<br />
=== GUI2 menu ===<br />
<br />
GUI2 most notably lacks a menu display (either for the game's menu bar or context menus). It is not known at this time how much work would have to be done in GUI2's core and framework to allow implementing this widget -- in particular considering how GUI2 widgets are never used without a parent dialog, which menus might not necessarily have, at least with the current gameplay UI implementation.<br />
<br />
GUI2 menu FR: https://gna.org/bugs/?22820<br />
<br />
=== GUI2 themable in-game UI ===<br />
<br />
The "themable" in-game UI refers to the gameplay UI including the sidebar, menu/status bar, and map view. Most of it is not implemented using proper GUI1 widgets at all, except for the various interactive buttons, menus (which are floating GUI1 listboxes), and the command and chat input boxes (floating GUI1 textboxes).<br />
<br />
Moving the theme UI to GUI2 would probably require finishing up theme support in GUI2 (which is largely incomplete), and may benefit from a rendering engine redesign as is also required for porting the game to SDL 2; in particular so that the GUI aspects are more clearly separated from the game board rendering aspect.<br />
<br />
'''Note:''' This task is here solely for the sake of completeness, and only somebody very well versed in the intricacies of Wesnoth's game rendering code and GUI2 should '''ever''' try to take it up. (And even then, I would not want to be anywhere near that person when they inevitably break down and fling one or more tables around upon realizing the sheer complexity of the task. -- shadowm)<br />
<br />
== Add-ons server and client ==<br />
<br />
=== Passphrase hashing ===<br />
<br />
The add-ons server (campaignd) uses a very dumb authentication scheme for uploading add-ons where an author sets a passphrase (or gets a random passphrase assigned by the game) the first time they upload to the server, and subsequent uploads of the same add-on are only allowed if the passphrase matches. The passphrase is stored in clear text form both client and server-side, which has [http://forums.wesnoth.org/viewtopic.php?f=62&t=42663 various implications].<br />
<br />
Ideally, the server would store all passphrases in a hashed form.<br />
<br />
=== Port redirection ===<br />
<br />
Wesnoth's MP server (wesnothd) uses a single port (15000) for servicing most requests and redirects clients to different ports according to the game client's version number. This allows the server administrators to reassign ports freely without having to modify the game client.<br />
<br />
The add-ons server (campaignd) and client instead have a hardcoded default port number for a Wesnoth version series. For example, 1.12.x is hardcoded to default to 15006. This becomes a maintainability issue as add-on servers get decommissioned and their port numbers can't be reused for fear of misdirecting obsolete client versions to the wrong instance with incompatible add-ons.<br />
<br />
=== Incremental upgrades and uploads ===<br />
<br />
Right now, whenever a new add-on version is published, players must download (or conversely, upload) the whole contents, even if the differences between both versions amount to a single line change. This is not a problem with small add-ons, but there is a substantial number of large (> 10 MiB) add-ons on the server. Large downloads and uploads are inconvenient for players on slow or metered connections, and also increase the server's traffic requirements.<br />
<br />
Ideally, for each add-on it attempts to upgrade or upload, the add-ons client should be able to send the server its local list of files with hashes, so that the server can identify which files need to be transmitted to/by the client, all archived on the fly like normal add-on downloads/uploads. The obvious downside is that the server would need to calculate an add-on's file hashes on demand, increasing CPU usage when servicing upgrades or uploads; this could be avoided by generating its file hashes when servicing uploads instead.<br />
<br />
See also: https://gna.org/bugs/?19972 (for uploads only; there should be one for downloads too but I couldn't find it -- shadowm)<br />
<br />
=== Porting the server to Asio ===<br />
<br />
Although the client uses Boost.Asio for communications, the server continues to use SDL_net instead. This means, amongst other things, that we are limited to IPv4 and an unmaintained codebase full of hacks waiting to break apart in the future (Wesnoth's SDL_net code does, amongst other things, tamper with what are supposed to be opaque data structures maintained by SDL_net).<br />
<br />
There was an attempt to build a new add-ons server (umcd) using Asio as part of GSoC, but it ultimately failed and the developers are no longer involved with the project. The umcd codebase status as of this writing is unknown.<br />
<br />
== Multiplayer server and client ==<br />
<br />
=== Improve server WML processing ===<br />
<br />
The server uses its own custom WML implementation separate from the main engine's to store and process WML objects for games -- see [[WesnothdDesign#simple_wml]]. This is intended to improve performance and reduce wesnothd's memory footprint with large numbers of games and complex scenarios. However, the simple_wml code sometimes messes up WML attribute translatability, resulting in MP scenarios/campaigns being untranslated for non-hosts in networked MP (http://gna.org/bugs/?22989). Fixing this is not easy due to the aforementioned performance requirements.<br />
<br />
== Data structures ==<br />
<br />
=== Make the Lua state persistent ===<br />
<br />
Wesnoth is designed to make all data files as simple and human-readable as possible, including savefiles. WML is designed so that it is always very easy to write the entire WML state as plaintext and reload it correctly. There are many benefits of this, but especially <br />
<br />
* It makes it easy for beginners / students to read the files and see what's going on<br />
* It makes it easy for us to debug problems without needing special tools to read the savefiles<br />
* It makes it easy to edit save files to work around any possible bugs. Users have even reported opening savefiles from version 1.10 in microsoft word (!) to correct minor bugs in mp and resaving them, as a policy for tournament games.<br />
<br />
This was unfortunately somewhat lost when we added lua. If you use lua in your scenarios, things become more complicated because we can't save the lua state at all. If you rely on lua variables to save information, then when your scenario is saved and reloaded those variables will get wiped. The wesnoth engine provides places for lua to hook into "onsave" and "onload" so that the scenario developer can write their own serialization routines, but this is just punting the problem to scenario developers.<br />
<br />
We also provide the "preload" event type specifically to work around the issue -- unlike all other events, preload is called exactly once for every time the scenario is loaded. The purpose is to give another way that lua users can initialize a table of lua functions used by their addon and guarantee that this happens before the functions are called. But to non-lua users this event is basically unsafe for normal use otherwise -- if there is a preload event which e.g. increments a WML variable, then the WML state now depends on how many times the scenario is saved or reloaded. In a normal scenario, save / reload events should be invisible, and normally if this isn't the case it indicates a bug similar to an OOS.<br />
<br />
A nice solution to this would be if we properly serialized the lua state alongside the WML in a savefile. The most suitable solution is probably this library: https://github.com/fnuecke/eris<br />
It would require significant additional work as well to find a good way to serialize the C functions that we expose to lua, and the userdata like units held by lua.<br />
<br />
More info here: http://lua-users.org/wiki/PlutoLibrary<br />
<br />
<br />
[[Category:Development]]<br />
[[Category:Future]]</div>Jyrkive