Difference between revisions of "LuaWML/Units"
m (Fix syntax mishap) |
m (Fix formatting →wesnoth.unit_movement_cost) |
||
(42 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
+ | {{Template:LuaMove}} | ||
+ | |||
+ | <div class="tright"> __TOC__ </div> | ||
+ | |||
This page describes the [[LuaWML]] functions for handling units. | This page describes the [[LuaWML]] functions for handling units. | ||
A unit is a proxy table with the following fields: | A unit is a proxy table with the following fields: | ||
− | * '''x''', '''y''': integers (read only, read/write if the unit is not on the map) | + | * '''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) |
+ | * '''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. | ||
* '''side''': integer (read/write) | * '''side''': integer (read/write) | ||
* '''id''': string (read only) | * '''id''': string (read only) | ||
* '''type''': string (read only) | * '''type''': string (read only) | ||
* '''name''': translatable string (read only) | * '''name''': translatable string (read only) | ||
− | * ''' | + | * '''cost''' {{DevFeature1.13|10}}: integer (read) |
+ | * '''max_hitpoints''', '''max_experience''', '''max_moves''': integers (read only) | ||
* '''max_attacks''': integer (read only) | * '''max_attacks''': integer (read only) | ||
* '''attacks_left''': integer (read/write) Setting below 0 is limited to 0. | * '''attacks_left''': integer (read/write) Setting below 0 is limited to 0. | ||
Line 19: | Line 25: | ||
* '''petrified''', '''canrecruit''': booleans (read only) | * '''petrified''', '''canrecruit''': booleans (read only) | ||
* '''role''', '''facing''': strings (read/write) | * '''role''', '''facing''': strings (read/write) | ||
− | * '''status''': proxy associative table (read only, read/write fields) | + | * '''status''': proxy associative table (read only, read/write fields), provides fields like [[SingleUnitWML#Unit_State|poisoned, slowed, petrified, uncovered, guardian, unhealable, invulnerable]] |
* '''image_mods''': string (read only) | * '''image_mods''': string (read only) | ||
* '''upkeep''' {{DevFeature1.13|5}}: one of 'loyal', 'full' or a number (read/writre) | * '''upkeep''' {{DevFeature1.13|5}}: one of 'loyal', 'full' or a number (read/writre) | ||
Line 36: | Line 42: | ||
* '''valid''': string or nil (read only) | * '''valid''': string or nil (read only) | ||
* '''advancements''': {{DevFeature1.13|2}} an array of wml tables (read/write) | * '''advancements''': {{DevFeature1.13|2}} an array of wml tables (read/write) | ||
− | * '''__cfg''': WML table (dump) | + | * '''__cfg''': WML table (dump) ([[SingleUnitWML]]) |
* {{DevFeature1.13|2}} The following fields are unit methods synonymous to one of the functions described on this page: | * {{DevFeature1.13|2}} The following fields are unit methods synonymous to one of the functions described on this page: | ||
** '''[[#wesnoth.match_unit|matches]]''' | ** '''[[#wesnoth.match_unit|matches]]''' | ||
Line 46: | Line 52: | ||
** '''[[#wesnoth.advance_unit|advance]]''' | ** '''[[#wesnoth.advance_unit|advance]]''' | ||
** '''[[#wesnoth.add_modification|add_modification]]''' | ** '''[[#wesnoth.add_modification|add_modification]]''' | ||
+ | ** '''[[#wesnoth.remove_modifications|remove_modifications]]''' | ||
** '''[[#wesnoth.unit_resistance|resistance]]''' | ** '''[[#wesnoth.unit_resistance|resistance]]''' | ||
** '''[[#wesnoth.unit_defense|defense]]''' | ** '''[[#wesnoth.unit_defense|defense]]''' | ||
Line 58: | Line 65: | ||
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). | 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). | ||
− | |||
==== wesnoth.get_units ==== | ==== wesnoth.get_units ==== | ||
* '''wesnoth.get_units(''filter'')''' | * '''wesnoth.get_units(''filter'')''' | ||
+ | * {{DevFeature1.13|12}} '''wesnoth.get_units(''filter'', ''fake_location'')''' | ||
+ | * {{DevFeature1.13|12}} '''wesnoth.get_units(''filter'', ''other_unit'')''' | ||
− | 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. | + | 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. |
− | + | <syntaxhighlight lang='lua'> | |
− | + | local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true } | |
+ | local name_of_leader = leaders_on_side_two[1].name | ||
+ | </syntaxhighlight> | ||
==== wesnoth.get_unit ==== | ==== wesnoth.get_unit ==== | ||
* '''wesnoth.get_unit(''x'', ''y'')''' | * '''wesnoth.get_unit(''x'', ''y'')''' | ||
− | * '''wesnoth.get_unit('' | + | * '''wesnoth.get_unit(''id'')''' |
− | Returns the unit at the given location or with the given | + | Returns the unit at the given location or with the given ID. |
− | + | <syntaxhighlight lang='lua'> | |
− | + | local args = ... | |
+ | local unit = wesnoth.get_unit(args.x1, args.y1) | ||
+ | </syntaxhighlight> | ||
==== wesnoth.match_unit ==== | ==== wesnoth.match_unit ==== | ||
Line 89: | Line 101: | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | assert(unit.canrecruit == wesnoth.match_unit(unit, { canrecruit = true })) | ||
+ | </syntaxhighlight> | ||
==== wesnoth.put_unit ==== | ==== wesnoth.put_unit ==== | ||
* '''wesnoth.put_unit(''unit'')''' | * '''wesnoth.put_unit(''unit'')''' | ||
− | * '''wesnoth.put_unit(''x'', ''y'', ''unit'')''' | + | * '''wesnoth.put_unit(''x'', ''y'', ''unit'')''' -- Deprecated in {{DevFeature1.13|2}}, removed in {{DevFeature1.15|0}} |
− | * '''wesnoth.put_unit(''x'', ''y'')''' | + | * '''wesnoth.put_unit(''x'', ''y'')''' -- Deprecated in {{DevFeature1.13|2}}, removed in {{DevFeature1.15|0}} |
− | * {{DevFeature1.13|2}} '''wesnoth.put_unit(''unit'', ''x'', ''y'')''' | + | * {{DevFeature1.13|2}} '''wesnoth.put_unit(''unit'', ''x'', ''y'')''' |
* {{DevFeature1.13|2}} '''''unit'':to_map([''x'', ''y'']) | * {{DevFeature1.13|2}} '''''unit'':to_map([''x'', ''y'']) | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | -- create a unit with random traits, then erase it | |
− | + | wesnoth.put_unit(17, 42, { type = "Elvish Lady" }) | |
+ | wesnoth.put_unit(17, 42) | ||
+ | </syntaxhighlight> | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | -- move the leader back to the top-left corner | |
+ | wesnoth.put_unit(1, 1, wesnoth.get_units({ canrecruit = true })[1]) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | If x,y is a village, this function does not capture it (as of 1.14). Use [[LuaWML:Sides#wesnoth.set_village_owner|wesnoth.set_village_owner(x, y, unit.side)]] if that's what you want. | ||
+ | |||
+ | '''Caution:''' Using this function will trigger unit placed events. While this may be correct in most cases, there are some cases where it shouldn't, especially if the code is inside the definition of a custom WML tag — it would be an unexpected side-effect from the users point. To work around this, ''false'' can be passed additionally as the last argument. This is what is used internally for some tags such as [move_unit], [harm_unit], [unpetrify] or the feeding ability and is not officially part of the API. | ||
==== wesnoth.erase_unit ==== | ==== wesnoth.erase_unit ==== | ||
Line 133: | Line 155: | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | -- put the unit at location 17,42 on the recall list for side 2 | |
+ | wesnoth.put_recall_unit(wesnoth.get_units({ x= 17, y = 42 })[1], 2) | ||
+ | </syntaxhighlight> | ||
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. | 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. | ||
Line 144: | Line 168: | ||
Creates a private unit from a WML table. | Creates a private unit from a WML table. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local u = wesnoth.create_unit { type = "White Mage", gender = "female" } | ||
+ | </syntaxhighlight> | ||
==== wesnoth.copy_unit ==== | ==== wesnoth.copy_unit ==== | ||
Line 153: | Line 179: | ||
Creates a private unit from another unit. | Creates a private unit from another unit. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | -- extract a unit from the map | |
− | + | local u = wesnoth.copy_unit(wesnoth.get_units({ type = "Thug" })[1]) | |
− | + | wesnoth.erase_unit(u.x, u.y) | |
+ | -- u is still valid at this point | ||
+ | </syntaxhighlight> | ||
==== wesnoth.extract_unit ==== | ==== wesnoth.extract_unit ==== | ||
Line 165: | Line 193: | ||
Removes a unit from the map or from a recall list and makes it private. | Removes a unit from the map or from a recall list and makes it private. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | -- remove all the units from the recall list of side 1 and put them in a WML container | |
− | + | local l = {} | |
− | + | for i,u in ipairs(wesnoth.get_recall_units { side = 1 }) do | |
− | + | wesnoth.extract_unit(u) | |
− | + | table.insert(l, u.__cfg) | |
− | + | end | |
+ | helper.set_variable_array("player_recall_list", l) | ||
+ | </syntaxhighlight> | ||
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. | 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. | ||
Line 197: | Line 227: | ||
{{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). | {{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). | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | local T = wml.tag | |
+ | local u = wesnoth.get_units { canrecruit = true }[1] | ||
+ | local effects = { | ||
+ | id = "my_effect_id", | ||
+ | T.effect { | ||
+ | apply_to = "image_mod", | ||
+ | replace = "RC(red>blue)" | ||
+ | }, | ||
+ | T.effect { | ||
+ | apply_to = "new_animation", | ||
+ | T.standing_animation { | ||
+ | -- AnimationWML | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | wesnoth.add_modification(u, "object", effects) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== wesnoth.remove_modifications ==== | ||
+ | |||
+ | * {{DevFeature1.13|?}} '''wesnoth.remove_modifications(''unit'', ''cfg'' [, ''type''])''' | ||
+ | * {{DevFeature1.13|?}} '''''unit'':remove_modifications(''cfg'' [, ''type''])''' | ||
+ | |||
+ | 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>. | ||
+ | |||
+ | <syntaxhighlight lang='lua'> | ||
+ | local u = wesnoth.get_units { canrecruit = true }[1] | ||
+ | wesnoth.remove_modifications(u, { id = "my_effect_id" }) | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_resistance ==== | ==== wesnoth.unit_resistance ==== | ||
Line 207: | Line 265: | ||
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). | 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). | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local fire_resistance = 100 - wesnoth.unit_resistance(u, "fire") | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_defense ==== | ==== wesnoth.unit_defense ==== | ||
Line 216: | Line 276: | ||
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.) | 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.) | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local flat_defense = 100 - wesnoth.unit_defense(u, "Gt") | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_movement_cost ==== | ==== wesnoth.unit_movement_cost ==== | ||
Line 225: | Line 287: | ||
Returns the movement cost of a unit on a particular terrain. | Returns the movement cost of a unit on a particular terrain. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local move_cost = wesnoth.unit_movement_cost(u, "Gt") | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_vision_cost ==== | ==== wesnoth.unit_vision_cost ==== | ||
Line 234: | Line 298: | ||
Returns the vision cost of a unit on a particular terrain. | Returns the vision cost of a unit on a particular terrain. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local see_cost = wesnoth.unit_vision_cost(u, "Gt") | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_jamming_cost ==== | ==== wesnoth.unit_jamming_cost ==== | ||
Line 243: | Line 309: | ||
Returns the jamming cost of a unit on a particular terrain. | Returns the jamming cost of a unit on a particular terrain. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local jam_cost = wesnoth.unit_jamming_cost(u, "Gt") | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_ability ==== | ==== wesnoth.unit_ability ==== | ||
Line 252: | Line 320: | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | function has_teleport(u) | |
− | + | return wesnoth.unit_ability(u, "teleport") | |
+ | end | ||
+ | </syntaxhighlight> | ||
==== wesnoth.unit_types ==== | ==== wesnoth.unit_types ==== | ||
− | This is not a function but a table indexed by unit type ids. Its elements are proxy tables with these fields: | + | This is not a function but a read-only table indexed by unit type ids. Its elements are proxy tables with these fields: |
* '''id''': string | * '''id''': string | ||
* '''name''': translatable string (read only) | * '''name''': translatable string (read only) | ||
* '''max_moves''', '''max_experience''', '''max_hitpoints''', '''level''', '''cost''': integers (read only) | * '''max_moves''', '''max_experience''', '''max_hitpoints''', '''level''', '''cost''': integers (read only) | ||
− | * '''__cfg''': WML table (dump) | + | * '''abilities''': array of ability keys (strings), e.g. {"curing", "regenerates"} |
+ | * {{DevFeature1.13|11}} '''advances_to''': array of unit types to which unit can advance | ||
+ | * {{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. | ||
+ | * '''__cfg''': WML table (dump), see [[UnitTypeWML]] | ||
The metatable of these proxy tables appears as '''"unit type"'''. | The metatable of these proxy tables appears as '''"unit type"'''. | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | local lich_cost = wesnoth.unit_types["Ancient Lich"].cost | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 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. | ||
+ | Presuming correctly written add-ons, it is still safe to e.g. access any given unit or its properties. | ||
==== wesnoth.races ==== | ==== wesnoth.races ==== | ||
Line 281: | Line 359: | ||
* '''__cfg''': WML table (dump) | * '''__cfg''': WML table (dump) | ||
− | + | <syntaxhighlight lang='lua'> | |
+ | wesnoth.message(tostring(wesnoth.races["lizard"].name)) | ||
+ | </syntaxhighlight> | ||
==== wesnoth.get_traits ==== | ==== wesnoth.get_traits ==== | ||
Line 299: | Line 379: | ||
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"). | 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"). | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | local function display_stats(n, t) | |
− | + | wesnoth.message(string.format( | |
− | + | "Chance for the %s\n to be slowed: %f,\n to be poisoned: %f,\n to die: %f.\nAverage HP: %f.", | |
− | + | n, t.slowed, t.poisoned, t.hp_chance[0], t.average_hp)) | |
− | + | end | |
− | + | local att_stats, def_stats = wesnoth.simulate_combat(att, att_weapon, def, def_weapon) | |
− | + | display_stats("attacker", att_stats) | |
+ | display_stats("defender", def_stats) | ||
+ | </syntaxhighlight> | ||
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. | 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. | ||
Name is the wml name not the description. If there is no weapon, then name will be nil | Name is the wml name not the description. If there is no weapon, then name will be nil | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | local att_stats, def_stats, att_weapon, def_weapon = wesnoth.simulate_combat(attacker, att_weapon_number, defender) | |
− | + | wesnoth.message(string.format( | |
− | + | "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.", | |
+ | att_weapon.name, def_weapon.name or "no weapon", def_weapon.damage, def_weapon.chance_to_hit, def_weapon.rounds)) | ||
+ | </syntaxhighlight> | ||
==== wesnoth.transform_unit ==== | ==== wesnoth.transform_unit ==== | ||
Line 323: | Line 407: | ||
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. | 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. | ||
− | + | <syntaxhighlight lang='lua'> | |
− | + | local ev = wesnoth.current.event_context | |
− | + | local u = wesnoth.get_units{x=ev.x1, y=ev.y1}[1] | |
− | + | wesnoth.transform_unit(u, "Spearman") | |
− | + | -- If a full heal is desired: | |
− | + | u.hitpoints = u.max_hitpoints | |
+ | u.status.poisoned = false | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== wesnoth.add_known_unit ==== | ||
+ | |||
+ | * {{DevFeature1.13|10}} '''wesnoth.add_known_unit(''unit_type_id'')''' | ||
+ | |||
+ | adds the unit type with the given id to the list of known units (so that they appear in the help) | ||
==== wesnoth.create_animator ==== | ==== wesnoth.create_animator ==== | ||
Line 340: | Line 432: | ||
* '''animator:run()''' | * '''animator:run()''' | ||
− | Runs the animation. | + | Runs the animation. {{DevFeature1.15|0}} Implicitly clears the animator. |
* '''animator:clear()''' | * '''animator:clear()''' | ||
Line 351: | Line 443: | ||
* '''facing''': A location. The animation will be played with the unit facing that location. | * '''facing''': A location. The animation will be played with the unit facing that location. | ||
− | * '''value''': Either a number or a list of two numbers. | + | * '''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. |
* '''with_bars''': Whether to show HP bars and such while the animation plays. | * '''with_bars''': Whether to show HP bars and such while the animation plays. | ||
* '''text''': Text to float as the animation plays. | * '''text''': Text to float as the animation plays. | ||
Line 397: | Line 489: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Note that | + | Note that in order work properly effects must be registered before any units are created. This means it must be places into toplevel or scenario/era/modification level [lua] tag and in particular must not be done from within a preload event. |
+ | |||
{{DevFeature1.13|5}} | {{DevFeature1.13|5}} |
Latest revision as of 20:53, 5 June 2021
Contents
- 1 wesnoth.get_units
- 2 wesnoth.get_unit
- 3 wesnoth.match_unit
- 4 wesnoth.put_unit
- 5 wesnoth.erase_unit
- 6 wesnoth.get_recall_units
- 7 wesnoth.put_recall_unit
- 8 wesnoth.create_unit
- 9 wesnoth.copy_unit
- 10 wesnoth.extract_unit
- 11 wesnoth.advance_unit
- 12 wesnoth.add_modification
- 13 wesnoth.remove_modifications
- 14 wesnoth.unit_resistance
- 15 wesnoth.unit_defense
- 16 wesnoth.unit_movement_cost
- 17 wesnoth.unit_vision_cost
- 18 wesnoth.unit_jamming_cost
- 19 wesnoth.unit_ability
- 20 wesnoth.unit_types
- 21 wesnoth.races
- 22 wesnoth.get_traits
- 23 wesnoth.simulate_combat
- 24 wesnoth.transform_unit
- 25 wesnoth.add_known_unit
- 26 wesnoth.create_animator
- 27 wesnoth.effects
This page describes the LuaWML functions for handling units.
A unit is a proxy table with the following fields:
- x, y: integers (read only, read/write if the unit is not on the map. (Version 1.13.11 and later only) These are now read/write under all circumstances, including for on-map units)
- loc: (Version 1.13.11 and later only) 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.
- side: integer (read/write)
- id: string (read only)
- type: string (read only)
- name: translatable string (read only)
- cost (Version 1.13.10 and later only): integer (read)
- max_hitpoints, max_experience, max_moves: integers (read only)
- max_attacks: integer (read only)
- attacks_left: integer (read/write) Setting below 0 is limited to 0.
- extra_recruit: table (read/write)
- advances_to: table (read/write)
- hitpoints, experience: integer (read/write)
- moves: integer (read/write)
- level: (Version 1.13.5 and later only) integer (read/write)
- resting: boolean (read/write)
- hidden: boolean (read/write)
- petrified, canrecruit: booleans (read only)
- role, facing: strings (read/write)
- status: proxy associative table (read only, read/write fields), provides fields like poisoned, slowed, petrified, uncovered, guardian, unhealable, invulnerable
- image_mods: string (read only)
- upkeep (Version 1.13.5 and later only): one of 'loyal', 'full' or a number (read/writre)
- variables: proxy associative table (read only, read/write fields, including variables.__cfg), only toplevel named fields are proxied. (Version 1.13.2 and later only) subcontainers can be accessed by using the usual variable syntax:
unit.variables["a.b.c[6].d"]
- attacks: (Version 1.13.0 and later only)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:
- description: translatable string (read/write)
- name: string (read)
- type: string (read/write)
- range: string (read/write)
- damage: number(read/write)
- number: number(read/write)
- movement_used: number(read/write)
- attack_weight: number(read/write)
- defense_weight: number(read/write)
- specials wml table(read/write)
- valid: string or nil (read only)
- advancements: (Version 1.13.2 and later only) an array of wml tables (read/write)
- __cfg: WML table (dump) (SingleUnitWML)
- (Version 1.13.2 and later only) The following fields are unit methods synonymous to one of the functions described on this page:
The metatable of these proxy tables appears as "unit".
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.
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).
wesnoth.get_units
- wesnoth.get_units(filter)
- (Version 1.13.12 and later only) wesnoth.get_units(filter, fake_location)
- (Version 1.13.12 and later only) wesnoth.get_units(filter, other_unit)
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.
local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true }
local name_of_leader = leaders_on_side_two[1].name
wesnoth.get_unit
- wesnoth.get_unit(x, y)
- wesnoth.get_unit(id)
Returns the unit at the given location or with the given ID.
local args = ...
local unit = wesnoth.get_unit(args.x1, args.y1)
wesnoth.match_unit
- wesnoth.match_unit(unit, filter)
- (Version 1.13.2 and later only) wesnoth.match_unit(unit, filter, other_unit)
- (Version 1.13.2 and later only) unit:matches(filter, [other_unit])
- (Version 1.13.2 and later only) wesnoth.match_unit(unit, filter, location)
- (Version 1.13.2 and later only) unit:matches(filter, [location])
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.
assert(unit.canrecruit == wesnoth.match_unit(unit, { canrecruit = true }))
wesnoth.put_unit
- wesnoth.put_unit(unit)
- wesnoth.put_unit(x, y, unit) -- Deprecated in (Version 1.13.2 and later only), removed in (Version 1.15.0 and later only)
- wesnoth.put_unit(x, y) -- Deprecated in (Version 1.13.2 and later only), removed in (Version 1.15.0 and later only)
- (Version 1.13.2 and later only) wesnoth.put_unit(unit, x, y)
- (Version 1.13.2 and later only) unit:to_map([x, y])
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. (Version 1.13.2 and later only) This use is now deprecated; use wesnoth.erase_unit instead.
-- create a unit with random traits, then erase it
wesnoth.put_unit(17, 42, { type = "Elvish Lady" })
wesnoth.put_unit(17, 42)
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.
-- move the leader back to the top-left corner
wesnoth.put_unit(1, 1, wesnoth.get_units({ canrecruit = true })[1])
If x,y is a village, this function does not capture it (as of 1.14). Use wesnoth.set_village_owner(x, y, unit.side) if that's what you want.
Caution: Using this function will trigger unit placed events. While this may be correct in most cases, there are some cases where it shouldn't, especially if the code is inside the definition of a custom WML tag — it would be an unexpected side-effect from the users point. To work around this, false can be passed additionally as the last argument. This is what is used internally for some tags such as [move_unit], [harm_unit], [unpetrify] or the feeding ability and is not officially part of the API.
wesnoth.erase_unit
(Version 1.13.2 and later only)
- wesnoth.erase_unit(unit)
- wesnoth.erase_unit(x, y)
- unit:erase()
Erases a unit from the map. After calling this on a unit, the unit is no longer valid.
wesnoth.get_recall_units
- wesnoth.get_recall_units()
Returns an array of all the units on the recall lists matching the WML filter passed as the first argument.
wesnoth.put_recall_unit
- wesnoth.put_recall_unit(unit, [side])
- (Version 1.13.2 and later only) unit:to_recall([side])
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.
-- put the unit at location 17,42 on the recall list for side 2
wesnoth.put_recall_unit(wesnoth.get_units({ x= 17, y = 42 })[1], 2)
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.
wesnoth.create_unit
- wesnoth.create_unit(unit_info)
Creates a private unit from a WML table.
local u = wesnoth.create_unit { type = "White Mage", gender = "female" }
wesnoth.copy_unit
- wesnoth.copy_unit(unit)
- (Version 1.13.2 and later only) unit:clone()
Creates a private unit from another unit.
-- extract a unit from the map
local u = wesnoth.copy_unit(wesnoth.get_units({ type = "Thug" })[1])
wesnoth.erase_unit(u.x, u.y)
-- u is still valid at this point
wesnoth.extract_unit
- wesnoth.extract_unit(unit)
- (Version 1.13.2 and later only) unit:extract()
Removes a unit from the map or from a recall list and makes it private.
-- remove all the units from the recall list of side 1 and put them in a WML container
local l = {}
for i,u in ipairs(wesnoth.get_recall_units { side = 1 }) do
wesnoth.extract_unit(u)
table.insert(l, u.__cfg)
end
helper.set_variable_array("player_recall_list", l)
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.
wesnoth.advance_unit
- wesnoth.advance_unit(unit)
- (Version 1.13.2 and later only) unit:advance()
(Version 1.13.0 and later only) 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.
This function only works for units on the map.
This function can also trigger multiple advancements if the unit has enough xp.
wesnoth.add_modification
- wesnoth.add_modification(unit, type, effects, [write_to_mods])
- (Version 1.13.2 and later only) unit:add_modification(type, effects, [write_to_mods])
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.
(Version 1.13.2 and later only) In 1.13.2 and later, the "advance" type is replaced with "advancement", to match the equivalent tag in [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).
local T = wml.tag
local u = wesnoth.get_units { canrecruit = true }[1]
local effects = {
id = "my_effect_id",
T.effect {
apply_to = "image_mod",
replace = "RC(red>blue)"
},
T.effect {
apply_to = "new_animation",
T.standing_animation {
-- AnimationWML
}
}
}
wesnoth.add_modification(u, "object", effects)
wesnoth.remove_modifications
- (Version 1.13.? and later only) wesnoth.remove_modifications(unit, cfg [, type])
- (Version 1.13.? and later only) unit:remove_modifications(cfg [, type])
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 [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 "object", but you can also pass "trait" or "advancement".
local u = wesnoth.get_units { canrecruit = true }[1]
wesnoth.remove_modifications(u, { id = "my_effect_id" })
wesnoth.unit_resistance
- wesnoth.unit_resistance(unit, damage_type)
- (Version 1.13.2 and later only) unit:resistance(damage_type)
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).
local fire_resistance = 100 - wesnoth.unit_resistance(u, "fire")
wesnoth.unit_defense
- wesnoth.unit_defense(unit, terrain_code)
- (Version 1.13.2 and later only) unit:defense(terrain_code)
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.)
local flat_defense = 100 - wesnoth.unit_defense(u, "Gt")
wesnoth.unit_movement_cost
- wesnoth.unit_movement_cost(unit, terrain_code)
- (Version 1.13.2 and later only) unit:movement(terrain_code)
Returns the movement cost of a unit on a particular terrain.
local move_cost = wesnoth.unit_movement_cost(u, "Gt")
wesnoth.unit_vision_cost
- wesnoth.unit_vision_cost(unit, terrain_code)
- (Version 1.13.2 and later only) unit:vision(terrain_code)
Returns the vision cost of a unit on a particular terrain.
local see_cost = wesnoth.unit_vision_cost(u, "Gt")
wesnoth.unit_jamming_cost
- wesnoth.unit_jamming_cost(unit, terrain_code)
- (Version 1.13.2 and later only) unit:jamming(terrain_code)
Returns the jamming cost of a unit on a particular terrain.
local jam_cost = wesnoth.unit_jamming_cost(u, "Gt")
wesnoth.unit_ability
- wesnoth.unit_ability(unit, ability_tag)
- (Version 1.13.2 and later only) unit:ability(ability_tag)
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.
function has_teleport(u)
return wesnoth.unit_ability(u, "teleport")
end
wesnoth.unit_types
This is not a function but a read-only table indexed by unit type ids. Its elements are proxy tables with these fields:
- id: string
- name: translatable string (read only)
- max_moves, max_experience, max_hitpoints, level, cost: integers (read only)
- abilities: array of ability keys (strings), e.g. {"curing", "regenerates"}
- (Version 1.13.11 and later only) advances_to: array of unit types to which unit can advance
- (Version 1.13.11 and later only) 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.
- __cfg: WML table (dump), see UnitTypeWML
The metatable of these proxy tables appears as "unit type".
local lich_cost = wesnoth.unit_types["Ancient Lich"].cost
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. Presuming correctly written add-ons, it is still safe to e.g. access any given unit or its properties.
wesnoth.races
This is not a function but a table indexed by race ids. Its elements are proxy tables for all races the engine knows about. known fields of each element:
- id: string
- description, name, plural_name (translatable strings)
- num_traits (integer)
- ignore_global_traits (boolean)
- undead_variation (string)
(all read only)
- __cfg: WML table (dump)
wesnoth.message(tostring(wesnoth.races["lizard"].name))
wesnoth.get_traits
- wesnoth.get_traits()
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. Known fields and subtags of each element are the ones which were given in the wml definition of the trait.
wesnoth.message(tostring(wesnoth.get_traits().strong.male_name))
wesnoth.simulate_combat
- wesnoth.simulate_combat(attacker, [attacker_weapon_index], defender, [defender_weapon_index])
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.
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").
local function display_stats(n, t)
wesnoth.message(string.format(
"Chance for the %s\n to be slowed: %f,\n to be poisoned: %f,\n to die: %f.\nAverage HP: %f.",
n, t.slowed, t.poisoned, t.hp_chance[0], t.average_hp))
end
local att_stats, def_stats = wesnoth.simulate_combat(att, att_weapon, def, def_weapon)
display_stats("attacker", att_stats)
display_stats("defender", def_stats)
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. Name is the wml name not the description. If there is no weapon, then name will be nil
local att_stats, def_stats, att_weapon, def_weapon = wesnoth.simulate_combat(attacker, att_weapon_number, defender)
wesnoth.message(string.format(
"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.",
att_weapon.name, def_weapon.name or "no weapon", def_weapon.damage, def_weapon.chance_to_hit, def_weapon.rounds))
wesnoth.transform_unit
- wesnoth.transform_unit(unit, to_type)
- (Version 1.13.2 and later only) unit:transform(to_type)
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.
local ev = wesnoth.current.event_context
local u = wesnoth.get_units{x=ev.x1, y=ev.y1}[1]
wesnoth.transform_unit(u, "Spearman")
-- If a full heal is desired:
u.hitpoints = u.max_hitpoints
u.status.poisoned = false
wesnoth.add_known_unit
- (Version 1.13.10 and later only) wesnoth.add_known_unit(unit_type_id)
adds the unit type with the given id to the list of known units (so that they appear in the help)
wesnoth.create_animator
(Version 1.13.7 and later only)
- wesnoth.create_animator()
Returns an object that can be used to set up and run an animation. The object has three methods:
- animator:run()
Runs the animation. (Version 1.15.0 and later only) Implicitly clears the animator.
- animator:clear()
Clears any units previously added to the animation.
- animator:add(unit, flag, hits, params)
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:
- facing: A location. The animation will be played with the unit facing that location.
- 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.
- with_bars: Whether to show HP bars and such while the animation plays.
- text: Text to float as the animation plays.
- color: Color of the floating text - a list of red, green, blue.
- primary: The primary weapon to use for the animation. Must be a Lua unit attack proxy.
- secondary: The secondary weapon to use for the animation.
Normal usage would be to create it, call add one or more times, then call run.
wesnoth.effects
(Version 1.13.2 and later only)
This table contains the implementation of custom [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.
function wesnoth.effects.min_resistance(u, cfg)
local resistance_new = {}
local resistance_old = helper.parsed(helper.get_child(cfg, "resistance"))
for k,v in pairs(resistance_old) do
if type(k) == "string" and type(v) == "number" and wesnoth.unit_resistance(u, k) >= v then
resistance_new[k] = v
end
end
--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.
wesnoth.add_modification(u, "object", {
T.effect {
apply_to = "resistance",
replace = true,
T.resistance (resistance_new),
},
}, false)
end
The code above adds a new min_resistance
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 [object]):
[effect]
apply_to=min_resistance
[resistance]
cold=50
[/resistance]
[/effect]
Note that in order work properly effects must be registered before any units are created. This means it must be places into toplevel or scenario/era/modification level [lua] tag and in particular must not be done from within a preload event.
(Version 1.13.5 and later only)
Built-in effects are now present in the wesnoth.effects
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.
In addition, you can now specify description modifiers to be used if a custom effect is placed in a [trait]
tag. Instead of setting a function as the effect, you set a table with a __call
metafunction which does what the function would have done. The table can then have an additional __descr
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.