Difference between revisions of "LuaWML/Units"

From The Battle for Wesnoth Wiki
(Add new unit_vision_cost and unit_jamming_cost functions)
(Add information about unit methods for using the colon syntax)
Line 36: Line 36:
 
* '''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)
 +
* The following fields are unit methods synonymous to one of the functions described on this page:
 +
** '''[[#wesnoth.match_unit|matches]]'''
 +
** '''[[#wesnoth.put_recall_unit|to_recall]]'''
 +
** '''[[#wesnoth.put_unit|to_map]]'''
 +
** '''[[#wesnoth.erase_unit|erase]]'''
 +
** '''[[#wesnoth.copy_unit|clone]]'''
 +
** '''[[#wesnoth.extract_unit|extract]]'''
 +
** '''[[#wesnoth.advance_unit|advance]]'''
 +
** '''[[#wesnoth.add_modification|add_modification]]'''
 +
** '''[[#wesnoth.unit_resistance|resistance]]'''
 +
** '''[[#wesnoth.unit_defense|defense]]'''
 +
** '''[[#wesnoth.unit_movement_cost|movement]]'''
 +
** '''[[#wesnoth.unit_vision_cost|vision]]'''
 +
** '''[[#wesnoth.unit_jamming_cost|jamming]]'''
 +
** '''[[#wesnoth.unit_ability|ability]]'''
 +
** '''[[#wesnoth.transform_unit|transform]]'''
 
The metatable of these proxy tables appears as '''"unit"'''.
 
The metatable of these proxy tables appears as '''"unit"'''.
  
Line 65: Line 81:
  
 
* '''wesnoth.match_unit(''unit'', ''filter'')'''
 
* '''wesnoth.match_unit(''unit'', ''filter'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':matches(''filter'')'''
  
 
Returns true if the given unit matches the WML filter passed as the second argument.
 
Returns true if the given unit matches the WML filter passed as the second argument.
Line 76: Line 93:
 
* '''wesnoth.put_unit(''x'', ''y'')'''
 
* '''wesnoth.put_unit(''x'', ''y'')'''
 
* {{DevFeature1.13|2}} '''wesnoth.put_unit(''unit'', ''x'', ''y'')''' -- The above two forms are also deprecated.
 
* {{DevFeature1.13|2}} '''wesnoth.put_unit(''unit'', ''x'', ''y'')''' -- The above two forms are also deprecated.
 +
* {{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.
Line 94: Line 112:
 
* '''wesnoth.erase_unit(''unit'')'''
 
* '''wesnoth.erase_unit(''unit'')'''
 
* '''wesnoth.erase_unit(''x'', ''y'')'''
 
* '''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.
 
Erases a unit from the map. After calling this on a unit, the unit is no longer valid.
Line 106: Line 125:
  
 
* '''wesnoth.put_recall_unit(''unit'', [''side''])'''
 
* '''wesnoth.put_recall_unit(''unit'', [''side''])'''
 +
* {{DevFeature1.13|2}} '''''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.
 
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.
Line 125: Line 145:
  
 
* '''wesnoth.copy_unit(''unit'')'''
 
* '''wesnoth.copy_unit(''unit'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':clone()'''
  
 
Creates a private unit from another unit.
 
Creates a private unit from another unit.
Line 136: Line 157:
  
 
* '''wesnoth.extract_unit(''unit'')'''
 
* '''wesnoth.extract_unit(''unit'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':extract()'''
  
 
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.
Line 153: Line 175:
  
 
* '''wesnoth.advance_unit(''unit'')'''
 
* '''wesnoth.advance_unit(''unit'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':advance()'''
  
 
{{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.
 
{{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.
Line 164: Line 187:
  
 
* '''wesnoth.add_modification(''unit'', ''type'', ''effects'', [''write_to_mods''])'''
 
* '''wesnoth.add_modification(''unit'', ''type'', ''effects'', [''write_to_mods''])'''
 +
* {{DevFeature1.13|2}} '''''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.
 
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.
Line 175: Line 199:
  
 
* '''wesnoth.unit_resistance(''unit'', ''damage_type'')'''
 
* '''wesnoth.unit_resistance(''unit'', ''damage_type'')'''
 +
* {{DevFeature1.13|2}} '''''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).
 
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).
Line 183: Line 208:
  
 
* '''wesnoth.unit_defense(''unit'', ''terrain_code'')'''
 
* '''wesnoth.unit_defense(''unit'', ''terrain_code'')'''
 +
* {{DevFeature1.13|2}} '''''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.)
 
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.)
Line 191: Line 217:
  
 
* '''wesnoth.unit_movement_cost(''unit'', ''terrain_code'')'''
 
* '''wesnoth.unit_movement_cost(''unit'', ''terrain_code'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':movement(''terrain_code'')'''
  
 
Returns the movement cost of a unit on a particular terrain.
 
Returns the movement cost of a unit on a particular terrain.
Line 199: Line 226:
  
 
* '''wesnoth.unit_vision_cost(''unit'', ''terrain_code'')'''
 
* '''wesnoth.unit_vision_cost(''unit'', ''terrain_code'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':vision(''terrain_code'')'''
  
 
Returns the vision cost of a unit on a particular terrain.
 
Returns the vision cost of a unit on a particular terrain.
Line 207: Line 235:
  
 
* '''wesnoth.unit_jamming_cost(''unit'', ''terrain_code'')'''
 
* '''wesnoth.unit_jamming_cost(''unit'', ''terrain_code'')'''
 +
* {{DevFeature1.13|2}} '''''unit'':jamming(''terrain_code'')'''
  
 
Returns the jamming cost of a unit on a particular terrain.
 
Returns the jamming cost of a unit on a particular terrain.
Line 215: Line 244:
  
 
* '''wesnoth.unit_ability(''unit'', ''ability_tag'')'''
 
* '''wesnoth.unit_ability(''unit'', ''ability_tag'')'''
 +
* {{DevFeature1.13|2}} '''''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.
 
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.
Line 285: Line 315:
  
 
* '''wesnoth.transform_unit(''unit'', ''to_type'')'''
 
* '''wesnoth.transform_unit(''unit'', ''to_type'')'''
 +
* {{DevFeature1.13|2}} '''''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.
 
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.

Revision as of 03:35, 20 October 2015

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)
  • side: integer (read/write)
  • id: string (read only)
  • type: string (read only)
  • name: translatable string (read only)
  • max_hitpoints, experience, 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.2 and later only) integer (read only)
  • 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)
  • image_mods: string (read only)
  • variables: proxy associative table (read only, read/write fields, including variables.__cfg), only toplevel named fields are proxied
  • 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)
  • 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)

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.

local leaders_on_side_two = 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(underlying_id)

Returns the unit at the given location or with the given underlying ID.

local args = ...
local unit = wesnoth.get_unit(args.x1, args.y1)

wesnoth.match_unit

Returns true if the given unit matches the WML filter passed as the second argument.

assert(unit.canrecruit == wesnoth.match_unit(unit, { canrecruit = true }))

wesnoth.put_unit

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

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

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

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.put_unit(u.x, u.y)
-- u is still valid at this point

wesnoth.extract_unit

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

(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 u = wesnoth.get_units { canrecruit = true }[1]
wesnoth.add_modification(u, "object", { { "effect", { apply_to = "image_mod", replace = "RC(red>blue)" } } })

wesnoth.unit_resistance

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

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

Returns the movement cost of a unit on a particular terrain.

local move_cost = wesnoth.unit_movement_cost(u, "Gt")

wesnoth.unit_vision_cost

Returns the vision cost of a unit on a particular terrain.

local see_cost = wesnoth.unit_vision_cost(u, "Gt")

wesnoth.unit_jamming_cost

Returns the jamming cost of a unit on a particular terrain.

local jam_cost = wesnoth.unit_jamming_cost(u, "Gt")

wesnoth.unit_ability

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 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)
  • __cfg: WML table (dump)

The metatable of these proxy tables appears as "unit type".

local lich_cost = wesnoth.unit_types["Ancient Lich"].cost

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

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