Split several of the larger WML tags into their own file
This commit is contained in:
parent
690acf9b79
commit
e1233fd0f2
9 changed files with 635 additions and 616 deletions
|
@ -13,6 +13,9 @@ end
|
|||
|
||||
wesnoth.require "wml-flow"
|
||||
wesnoth.require "wml"
|
||||
-- Note: When adding new WML tags, unless they're very simple, it's preferred to
|
||||
-- add a new file in the "data/lua/wml" directory. The file will then automatically
|
||||
-- be loaded by the above require statement.
|
||||
|
||||
local helper = wesnoth.require "helper"
|
||||
local location_set = wesnoth.require "location_set"
|
||||
|
@ -288,58 +291,6 @@ function wml_actions.volume(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
-- This is mainly for use in unit test macros, but maybe it can be useful elsewhere too
|
||||
function wml_actions.test_condition(cfg)
|
||||
local logger = cfg.logger or "warning"
|
||||
|
||||
-- This function returns true if it managed to explain the failure
|
||||
local function explain(current_cfg, expect)
|
||||
for i,t in ipairs(current_cfg) do
|
||||
local tag, this_cfg = t[1], t[2]
|
||||
-- Some special cases
|
||||
if tag == "or" or tag == "and" then
|
||||
if explain(this_cfg, expect) then
|
||||
return true
|
||||
end
|
||||
elseif tag == "not" then
|
||||
if explain(this_cfg, not expect) then
|
||||
return true
|
||||
end
|
||||
elseif tag == "true" or tag == "false" then
|
||||
-- We don't explain these ones.
|
||||
return true
|
||||
elseif wesnoth.eval_conditional{t} == expect then
|
||||
local explanation = "The following conditional test %s:"
|
||||
if expect then
|
||||
explanation = explanation:format("passed")
|
||||
else
|
||||
explanation = explanation:format("failed")
|
||||
end
|
||||
explanation = string.format("%s\n\t[%s]", explanation, tag)
|
||||
for k,v in pairs(this_cfg) do
|
||||
if type(k) ~= "number" then
|
||||
local format = "%s\n\t\t%s=%s"
|
||||
local literal = tostring(helper.literal(this_cfg)[k])
|
||||
if literal ~= v then
|
||||
format = format .. "=%s"
|
||||
end
|
||||
explanation = string.format(format, explanation, k, literal, tostring(v))
|
||||
end
|
||||
end
|
||||
explanation = string.format("%s\n\t[/%s]", explanation, tag)
|
||||
if tag == "variable" then
|
||||
explanation = string.format("%s\n\tNote: The variable %s currently has the value %q.", explanation, this_cfg.name, tostring(wesnoth.get_variable(this_cfg.name)))
|
||||
end
|
||||
wesnoth.log(logger, explanation, true)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Use not twice here to convert nil to false
|
||||
explain(cfg, not not cfg.result)
|
||||
end
|
||||
|
||||
function wml_actions.scroll_to(cfg)
|
||||
local loc = wesnoth.get_locations( cfg )[1]
|
||||
if not loc then return end
|
||||
|
@ -520,69 +471,6 @@ function wml_actions.unhide_unit(cfg)
|
|||
wml_actions.redraw {}
|
||||
end
|
||||
|
||||
function wml_actions.move_unit(cfg)
|
||||
local coordinate_error = "invalid coordinate in [move_unit]"
|
||||
local to_x = tostring(cfg.to_x or helper.wml_error(coordinate_error))
|
||||
local to_y = tostring(cfg.to_y or helper.wml_error(coordinate_error))
|
||||
local fire_event = cfg.fire_event
|
||||
local muf_force_scroll = cfg.force_scroll
|
||||
local check_passability = cfg.check_passability
|
||||
if check_passability == nil then check_passability = true end
|
||||
cfg = helper.literal(cfg)
|
||||
cfg.to_x, cfg.to_y, cfg.fire_event = nil, nil, nil
|
||||
local units = wesnoth.get_units(cfg)
|
||||
|
||||
local pattern = "[^%s,]+"
|
||||
for current_unit_index, current_unit in ipairs(units) do
|
||||
if not fire_event or current_unit.valid then
|
||||
local xs, ys = string.gmatch(to_x, pattern), string.gmatch(to_y, pattern)
|
||||
local move_string_x = current_unit.x
|
||||
local move_string_y = current_unit.y
|
||||
local pass_check = nil
|
||||
if check_passability then pass_check = current_unit end
|
||||
|
||||
local x, y = xs(), ys()
|
||||
local prevX, prevY = tonumber(current_unit.x), tonumber(current_unit.y)
|
||||
while true do
|
||||
x = tonumber(x) or helper.wml_error(coordinate_error)
|
||||
y = tonumber(y) or helper.wml_error(coordinate_error)
|
||||
if not (x == prevX and y == prevY) then x, y = wesnoth.find_vacant_tile(x, y, pass_check) end
|
||||
if not x or not y then helper.wml_error("Could not find a suitable hex near to one of the target hexes in [move_unit].") end
|
||||
move_string_x = string.format("%s,%u", move_string_x, x)
|
||||
move_string_y = string.format("%s,%u", move_string_y, y)
|
||||
local next_x, next_y = xs(), ys()
|
||||
if not next_x and not next_y then break end
|
||||
prevX, prevY = x, y
|
||||
x, y = next_x, next_y
|
||||
end
|
||||
|
||||
if current_unit.x < x then current_unit.facing = "se"
|
||||
elseif current_unit.x > x then current_unit.facing = "sw"
|
||||
end
|
||||
|
||||
wesnoth.extract_unit(current_unit)
|
||||
local current_unit_cfg = current_unit.__cfg
|
||||
wml_actions.move_unit_fake {
|
||||
type = current_unit_cfg.type,
|
||||
gender = current_unit_cfg.gender,
|
||||
variation = current_unit_cfg.variation,
|
||||
image_mods = current_unit.image_mods,
|
||||
side = current_unit_cfg.side,
|
||||
x = move_string_x,
|
||||
y = move_string_y,
|
||||
force_scroll = muf_force_scroll
|
||||
}
|
||||
local x2, y2 = current_unit.x, current_unit.y
|
||||
current_unit.x, current_unit.y = x, y
|
||||
wesnoth.put_unit(current_unit)
|
||||
|
||||
if fire_event then
|
||||
wesnoth.fire_event("moveto", x, y, x2, y2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.capture_village(cfg)
|
||||
local side = cfg.side
|
||||
local filter_side = helper.get_child(cfg, "filter_side")
|
||||
|
@ -651,74 +539,6 @@ function wml_actions.unpetrify(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
function wml_actions.heal_unit(cfg)
|
||||
local healers = helper.get_child(cfg, "filter_second")
|
||||
if healers then
|
||||
healers = wesnoth.get_units{
|
||||
ability_type = "heals",
|
||||
T["and"](healers)
|
||||
}
|
||||
else
|
||||
healers = {}
|
||||
end
|
||||
|
||||
local who = helper.get_child(cfg, "filter")
|
||||
if who then
|
||||
who = wesnoth.get_units(who)
|
||||
else
|
||||
who = wesnoth.get_units{
|
||||
x = wesnoth.current.event_context.x1,
|
||||
y = wesnoth.current.event_context.y1
|
||||
}
|
||||
end
|
||||
|
||||
local heal_full = cfg.amount == "full" or cfg.amount == nil
|
||||
local moves_full = cfg.moves == "full"
|
||||
local heal_amount_set = false
|
||||
for i,u in ipairs(who) do
|
||||
local heal_amount = u.max_hitpoints - u.hitpoints
|
||||
if heal_full then
|
||||
u.hitpoints = u.max_hitpoints
|
||||
else
|
||||
heal_amount = tonumber(cfg.amount) or heal_amount
|
||||
local new_hitpoints = math.max(1, math.min(u.max_hitpoints, u.hitpoints + heal_amount))
|
||||
heal_amount = new_hitpoints - u.hitpoints
|
||||
u.hitpoints = new_hitpoints
|
||||
end
|
||||
|
||||
if moves_full then
|
||||
u.moves = u.max_moves
|
||||
else
|
||||
u.moves = math.min(u.max_moves, u.moves + (cfg.moves or 0))
|
||||
end
|
||||
|
||||
if cfg.restore_attacks then
|
||||
u.attacks_left = u.max_attacks
|
||||
end
|
||||
|
||||
if cfg.restore_statuses == true or cfg.restore_statuses == nil then
|
||||
u.status.poisoned = false
|
||||
u.status.petrified = false
|
||||
u.status.slowed = false
|
||||
u.status.unhealable = false
|
||||
end
|
||||
|
||||
if not heal_amount_set then
|
||||
heal_amount_set = true
|
||||
wesnoth.set_variable("heal_amount", heal_amount)
|
||||
end
|
||||
|
||||
if cfg.animate then
|
||||
local animator = wesnoth.create_animator()
|
||||
animator:add(u, 'healed', 'hits', {value = heal_amount})
|
||||
if #healers > 0 then
|
||||
animator:add(healers[1], 'healing', 'hits', {value = heal_amount})
|
||||
end
|
||||
animator:run()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.transform_unit(cfg)
|
||||
local transform_to = cfg.transform_to
|
||||
|
||||
|
@ -764,39 +584,6 @@ function wml_actions.store_side(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
function wml_actions.modify_ai(cfg)
|
||||
local sides = utils.get_sides(cfg)
|
||||
local component, final
|
||||
if cfg.action == "add" or cfg.action == "change" then
|
||||
local start = string.find(cfg.path, "[a-z_]+%[[a-z0-9_*]*%]$")
|
||||
final = start and (string.find(cfg.path, '[', start, true) - 1) or -1
|
||||
start = start or string.find(cfg.path, "[^.]*$") or 1
|
||||
local comp_type = string.sub(cfg.path, start, final)
|
||||
component = helper.get_child(cfg, comp_type)
|
||||
if component == nil then
|
||||
helper.wml_error("Missing component definition in [modify_ai]")
|
||||
end
|
||||
component = helper.parsed(component)
|
||||
end
|
||||
for i = 1, #sides do
|
||||
if cfg.action == "add" then
|
||||
wesnoth.add_ai_component(sides[i].side, cfg.path, component)
|
||||
elseif cfg.action == "delete" or cfg.action == "try_delete" then
|
||||
wesnoth.delete_ai_component(sides[i].side, cfg.path)
|
||||
elseif cfg.action == "change" then
|
||||
local id_start = final + 2
|
||||
local id_final = string.len(cfg.path) - 1
|
||||
local id = string.sub(cfg.path, id_start, id_final)
|
||||
if id == "*" then
|
||||
helper.wml_error("[modify_ai] can only change one component at a time")
|
||||
elseif not component.id and not id:match("[0-9]+") then
|
||||
component.id = id
|
||||
end
|
||||
wesnoth.change_ai_component(sides[i].side, cfg.path, component)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.add_ai_behavior(cfg)
|
||||
local unit = wesnoth.get_units(helper.get_child(cfg, "filter"))[1] or
|
||||
helper.wml_error("[add_ai_behavior]: no unit specified")
|
||||
|
@ -978,75 +765,6 @@ function wml_actions.inspect(cfg)
|
|||
wesnoth.gamestate_inspector(cfg)
|
||||
end
|
||||
|
||||
local kill_recursion_preventer = location_set.create()
|
||||
function wml_actions.kill(cfg)
|
||||
local number_killed = 0
|
||||
local secondary_unit = helper.get_child(cfg, "secondary_unit")
|
||||
local killer_loc = {0, 0}
|
||||
if secondary_unit then
|
||||
secondary_unit = wesnoth.get_units(secondary_unit)[1]
|
||||
if cfg.fire_event then
|
||||
if secondary_unit then
|
||||
killer_loc = {secondary_unit.loc}
|
||||
else
|
||||
wesnoth.log("warn", "failed to match [secondary_unit] in [kill] with a single on-board unit")
|
||||
end
|
||||
end
|
||||
end
|
||||
local dead_men_walking = wesnoth.get_units(cfg)
|
||||
for i,unit in ipairs(dead_men_walking) do
|
||||
local death_loc = {x = tonumber(unit.x) or 0, y = tonumber(unit.y) or 0}
|
||||
if not secondary_unit then killer_loc = death_loc end
|
||||
local can_fire = false
|
||||
|
||||
local recursion = (kill_recursion_preventer:get(death_loc.x, death_loc.y) or 0) + 1
|
||||
if cfg.fire_event then
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
can_fire = true
|
||||
if death_loc.x == wesnoth.current.event.x1 and death_loc.y == wesnoth.current.event.y1 then
|
||||
if wesnoth.current.event.name == "die" or wesnoth.current.event.name == "last breath" then
|
||||
if recursion >= 10 then
|
||||
can_fire = false;
|
||||
wesnoth.log("error", "tried to fire 'die' or 'last breath' event on unit from the unit's 'die' or 'last breath' event with first_time_only=no!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if can_fire then
|
||||
wesnoth.fire_event("last breath", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.animate then
|
||||
wesnoth.scroll_to_tile(death_loc)
|
||||
local anim = wesnoth.create_animator()
|
||||
-- Possible TODO: Add weapon selection? (That's kinda a pain right now; see animate_unit defn)
|
||||
anim:add(unit, "death", "kill")
|
||||
if secondary_unit then
|
||||
anim:add(secondary_unit, "victory", "kill")
|
||||
end
|
||||
anim:run()
|
||||
end
|
||||
wml_actions.redraw{}
|
||||
|
||||
if can_fire then
|
||||
wesnoth.fire_event("die", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.fire_event then
|
||||
if recursion <= 1 then
|
||||
kill_recursion_preventer:remove(death_loc.x, death_loc.y)
|
||||
else
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
end
|
||||
end
|
||||
-- Test that it's valid (and still on the map) first, in case the event erased (or extracted) it.
|
||||
if unit.valid == "map" then unit:erase() end
|
||||
|
||||
number_killed = number_killed + 1
|
||||
end
|
||||
|
||||
-- TODO: Do I need to check recall lists or was that covered by the above loop?
|
||||
return number_killed
|
||||
end
|
||||
|
||||
function wml_actions.label( cfg )
|
||||
local new_cfg = helper.parsed( cfg )
|
||||
for index, location in ipairs( wesnoth.get_locations( cfg ) ) do
|
||||
|
@ -1055,111 +773,6 @@ function wml_actions.label( cfg )
|
|||
end
|
||||
end
|
||||
|
||||
local side_changes_needing_redraw = {
|
||||
'shroud', 'fog', 'reset_map', 'reset_view', 'shroud_data',
|
||||
'share_vision', 'share_maps', 'share_view',
|
||||
'color', 'flag',
|
||||
}
|
||||
function wml_actions.modify_side(cfg)
|
||||
local sides = utils.get_sides(cfg)
|
||||
for i,side in ipairs(sides) do
|
||||
if cfg.team_name then
|
||||
side.team_name = cfg.team_name
|
||||
end
|
||||
if cfg.user_team_name then
|
||||
side.user_team_name = cfg.user_team_name
|
||||
end
|
||||
if cfg.controller then
|
||||
side.controller = cfg.controller
|
||||
end
|
||||
if cfg.defeat_condition then
|
||||
side.defeat_condition = cfg.defeat_condition
|
||||
end
|
||||
if cfg.recruit then
|
||||
local recruits = {}
|
||||
for recruit in utils.split(cfg.recruit) do
|
||||
table.insert(recruits, recruit)
|
||||
end
|
||||
side.recruit = recruits
|
||||
end
|
||||
if cfg.village_support then
|
||||
side.village_support = cfg.village_support
|
||||
end
|
||||
if cfg.village_gold then
|
||||
side.village_gold = cfg.village_gold
|
||||
end
|
||||
if cfg.income then
|
||||
side.base_income = cfg.income
|
||||
end
|
||||
if cfg.gold then
|
||||
side.gold = cfg.gold
|
||||
end
|
||||
|
||||
if cfg.hidden ~= nil then
|
||||
side.hidden = cfg.hidden
|
||||
end
|
||||
if cfg.color or cfg.flag then
|
||||
wesnoth.set_side_id(side.side, cfg.flag, cfg.color)
|
||||
end
|
||||
if cfg.flag_icon then
|
||||
side.flag_icon = cfg.flag_icon
|
||||
end
|
||||
if cfg.suppress_end_turn_confirmation ~= nil then
|
||||
side.suppress_end_turn_confirmation = cfg.suppress_end_turn_confirmation
|
||||
end
|
||||
if cfg.scroll_to_leader ~= nil then
|
||||
side.scroll_to_leader = cfg.scroll_to_leader
|
||||
end
|
||||
|
||||
if cfg.shroud ~= nil then
|
||||
side.shroud = cfg.shroud
|
||||
end
|
||||
if cfg.reset_maps then
|
||||
wesnoth.remove_shroud(side.side, "all")
|
||||
end
|
||||
if cfg.fog ~= nil then
|
||||
side.fog = cfg.fog
|
||||
end
|
||||
if cfg.reset_view then
|
||||
wesnoth.add_fog(side.side, {}, true)
|
||||
end
|
||||
if cfg.shroud_data then
|
||||
wesnoth.remove_shroud(side.side, cfg.shroud_data)
|
||||
end
|
||||
|
||||
if cfg.share_vision then
|
||||
side.share_vision = cfg.share_vision
|
||||
end
|
||||
-- Legacy support
|
||||
if cfg.share_view ~= nil or cfg.share_maps ~= nil then
|
||||
if cfg.share_view then
|
||||
side.share_vision = 'all'
|
||||
elseif cfg.share_maps then
|
||||
side.share_vision = 'shroud'
|
||||
else
|
||||
side.share_vision = 'none'
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.switch_ai then
|
||||
wesnoth.switch_ai(side.side, cfg.switch_ai)
|
||||
end
|
||||
local ai = {}
|
||||
for next_ai in helper.child_range(cfg, "ai") do
|
||||
table.insert(ai, T.ai(next_ai))
|
||||
end
|
||||
if #ai > 0 then
|
||||
wesnoth.append_ai(side.side, ai)
|
||||
end
|
||||
end
|
||||
for i,key in ipairs(side_changes_needing_redraw) do
|
||||
if cfg[key] ~= nil then
|
||||
wml_actions.redraw{}
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.open_help(cfg)
|
||||
wesnoth.open_help(cfg.topic)
|
||||
end
|
||||
|
@ -1179,115 +792,6 @@ function wml_actions.print(cfg)
|
|||
wesnoth.print(cfg)
|
||||
end
|
||||
|
||||
function wml_actions.role(cfg)
|
||||
-- role= and type= are handled differently than in other tags,
|
||||
-- so we need to remove them from the filter
|
||||
local role = cfg.role
|
||||
local filter = helper.shallow_literal(cfg)
|
||||
|
||||
if role == nil then
|
||||
helper.wml_error("missing role= in [role]")
|
||||
end
|
||||
|
||||
local types = {}
|
||||
|
||||
if cfg.type then
|
||||
for value in utils.split(cfg.type) do
|
||||
table.insert(types, utils.trim(value))
|
||||
end
|
||||
end
|
||||
|
||||
filter.role, filter.type = nil, nil
|
||||
local search_map, search_recall, reassign = true, true, true
|
||||
if cfg.search_recall_list == "only" then
|
||||
search_map = false
|
||||
elseif cfg.search_recall_list ~= nil then
|
||||
search_recall = not not cfg.search_recall_list
|
||||
end
|
||||
if cfg.reassign ~= nil then
|
||||
reassign = not not cfg.reassign
|
||||
end
|
||||
|
||||
-- pre-build a new [recall] from the [auto_recall]
|
||||
-- copy only recall-specific attributes, no SUF at all
|
||||
-- the SUF will be id= which we will add in a moment
|
||||
-- keep this in sync with the C++ recall function!!!
|
||||
local recall = nil
|
||||
local child = helper.get_child(cfg, "auto_recall")
|
||||
if child ~= nil then
|
||||
if helper.get_nth_child(cfg, "auto_recall", 2) ~= nil then
|
||||
wesnoth.log("debug", "More than one [auto_recall] found within [role]", true)
|
||||
end
|
||||
local original = helper.shallow_literal(child)
|
||||
recall = {}
|
||||
recall.x = original.x
|
||||
recall.y = original.y
|
||||
recall.show = original.show
|
||||
recall.fire_event = original.fire_event
|
||||
recall.check_passability = original.check_passability
|
||||
end
|
||||
|
||||
if not reassign then
|
||||
if search_map then
|
||||
local unit = wesnoth.get_units{role=role}[1]
|
||||
if unit then
|
||||
return
|
||||
end
|
||||
end
|
||||
if recall and search_recall then
|
||||
local unit = wesnoth.get_recall_units{role=role}[1]
|
||||
if unit then
|
||||
recall.id = unit.id
|
||||
wml_actions.recall(recall)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if search_map then
|
||||
-- first attempt to match units on the map
|
||||
local i = 1
|
||||
repeat
|
||||
-- give precedence based on the order specified in type=
|
||||
if #types > 0 then
|
||||
filter.type = types[i]
|
||||
end
|
||||
local unit = wesnoth.get_units(filter)[1]
|
||||
if unit then
|
||||
unit.role = role
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
until #types == 0 or i > #types
|
||||
end
|
||||
|
||||
if search_recall then
|
||||
-- then try to match units on the recall lists
|
||||
i = 1
|
||||
repeat
|
||||
if #types > 0 then
|
||||
filter.type = types[i]
|
||||
end
|
||||
local unit = wesnoth.get_recall_units(filter)[1]
|
||||
if unit then
|
||||
unit.role = role
|
||||
if recall then
|
||||
recall.id = unit.id
|
||||
wml_actions.recall(recall)
|
||||
end
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
until #types == 0 or i > #types
|
||||
end
|
||||
|
||||
-- no matching unit found, try the [else] tags
|
||||
for else_child in helper.child_range(cfg, "else") do
|
||||
local action = utils.handle_event_commands(else_child, "conditional")
|
||||
if action ~= "none" then return end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.unsynced(cfg)
|
||||
wesnoth.unsynced(function ()
|
||||
wml_actions.command(cfg)
|
||||
|
@ -1389,123 +893,6 @@ function wml_actions.reset_fog(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
function wml_actions.set_variable(cfg)
|
||||
local name = cfg.name or helper.wml_error "trying to set a variable with an empty name"
|
||||
|
||||
if cfg.value ~= nil then -- check for nil because user may try to set a variable as false
|
||||
wesnoth.set_variable(name, cfg.value)
|
||||
end
|
||||
|
||||
if cfg.literal ~= nil then
|
||||
wesnoth.set_variable(name, helper.shallow_literal(cfg).literal)
|
||||
end
|
||||
|
||||
if cfg.to_variable then
|
||||
wesnoth.set_variable(name, wesnoth.get_variable(cfg.to_variable))
|
||||
end
|
||||
|
||||
if cfg.add then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) + (tonumber(cfg.add) or 0))
|
||||
end
|
||||
|
||||
if cfg.sub then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) - (tonumber(cfg.sub) or 0))
|
||||
end
|
||||
|
||||
if cfg.multiply then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) * (tonumber(cfg.multiply) or 0))
|
||||
end
|
||||
|
||||
if cfg.divide then
|
||||
local divide = tonumber(cfg.divide) or 0
|
||||
if divide == 0 then helper.wml_error("division by zero on variable " .. name) end
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) / divide)
|
||||
end
|
||||
|
||||
if cfg.modulo then
|
||||
local modulo = tonumber(cfg.modulo) or 0
|
||||
if modulo == 0 then helper.wml_error("division by zero on variable " .. name) end
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) % modulo)
|
||||
end
|
||||
|
||||
if cfg.abs then
|
||||
wesnoth.set_variable(name, math.abs(tonumber(wesnoth.get_variable(name)) or 0))
|
||||
end
|
||||
|
||||
if cfg.root then
|
||||
if cfg.root == "square" then
|
||||
local radicand = tonumber(wesnoth.get_variable(name)) or 0
|
||||
if radicand < 0 then helper.wml_error("square root of negative number on variable " .. name) end
|
||||
wesnoth.set_variable(name, math.sqrt(radicand))
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.power then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) ^ (tonumber(cfg.power) or 0))
|
||||
end
|
||||
|
||||
if cfg.round then
|
||||
local var = tonumber(wesnoth.get_variable(name) or 0)
|
||||
local round_val = cfg.round
|
||||
if round_val == "ceil" then
|
||||
wesnoth.set_variable(name, math.ceil(var))
|
||||
elseif round_val == "floor" then
|
||||
wesnoth.set_variable(name, math.floor(var))
|
||||
else
|
||||
local decimals, discarded = math.modf(tonumber(round_val) or 0)
|
||||
local value = var * (10 ^ decimals)
|
||||
value = helper.round(value)
|
||||
value = value * (10 ^ -decimals)
|
||||
wesnoth.set_variable(name, value)
|
||||
end
|
||||
end
|
||||
|
||||
-- unlike the other math operations, ipart and fpart do not act on
|
||||
-- the value already contained in the variable
|
||||
-- but on the value assigned to the respective key
|
||||
if cfg.ipart then
|
||||
local ivalue, fvalue = math.modf(tonumber(cfg.ipart) or 0)
|
||||
wesnoth.set_variable(name, ivalue)
|
||||
end
|
||||
|
||||
if cfg.fpart then
|
||||
local ivalue, fvalue = math.modf(tonumber(cfg.fpart) or 0)
|
||||
wesnoth.set_variable(name, fvalue)
|
||||
end
|
||||
|
||||
if cfg.string_length ~= nil then
|
||||
wesnoth.set_variable(name, string.len(tostring(cfg.string_length)))
|
||||
end
|
||||
|
||||
if cfg.time then
|
||||
if cfg.time == "stamp" then
|
||||
wesnoth.set_variable(name, wesnoth.get_time_stamp())
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.rand then
|
||||
wesnoth.set_variable(name, helper.rand(tostring(cfg.rand)))
|
||||
end
|
||||
|
||||
local join_child = helper.get_child(cfg, "join")
|
||||
if join_child then
|
||||
local array_name = join_child.variable or helper.wml_error "missing variable= attribute in [join]"
|
||||
local separator = join_child.separator
|
||||
local key_name = join_child.key or "value"
|
||||
local remove_empty = join_child.remove_empty
|
||||
|
||||
local string_to_join = {}
|
||||
|
||||
for i, element in ipairs(helper.get_variable_array(array_name)) do
|
||||
if element[key_name] ~= nil or (not remove_empty) then
|
||||
table.insert(string_to_join, tostring(element[key_name]))
|
||||
end
|
||||
end
|
||||
|
||||
wesnoth.set_variable(name, table.concat(string_to_join, separator))
|
||||
end
|
||||
end
|
||||
|
||||
function wesnoth.wml_actions.change_theme(cfg)
|
||||
wesnoth.game_config.theme = cfg.theme
|
||||
end
|
||||
|
|
70
data/lua/wml/heal_unit.lua
Normal file
70
data/lua/wml/heal_unit.lua
Normal file
|
@ -0,0 +1,70 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
local T = helper.set_wml_tag_metatable {}
|
||||
|
||||
function wesnoth.wml_actions.heal_unit(cfg)
|
||||
local healers = helper.get_child(cfg, "filter_second")
|
||||
if healers then
|
||||
healers = wesnoth.get_units{
|
||||
ability_type = "heals",
|
||||
T["and"](healers)
|
||||
}
|
||||
else
|
||||
healers = {}
|
||||
end
|
||||
|
||||
local who = helper.get_child(cfg, "filter")
|
||||
if who then
|
||||
who = wesnoth.get_units(who)
|
||||
else
|
||||
who = wesnoth.get_units{
|
||||
x = wesnoth.current.event_context.x1,
|
||||
y = wesnoth.current.event_context.y1
|
||||
}
|
||||
end
|
||||
|
||||
local heal_full = cfg.amount == "full" or cfg.amount == nil
|
||||
local moves_full = cfg.moves == "full"
|
||||
local heal_amount_set = false
|
||||
for i,u in ipairs(who) do
|
||||
local heal_amount = u.max_hitpoints - u.hitpoints
|
||||
if heal_full then
|
||||
u.hitpoints = u.max_hitpoints
|
||||
else
|
||||
heal_amount = tonumber(cfg.amount) or heal_amount
|
||||
local new_hitpoints = math.max(1, math.min(u.max_hitpoints, u.hitpoints + heal_amount))
|
||||
heal_amount = new_hitpoints - u.hitpoints
|
||||
u.hitpoints = new_hitpoints
|
||||
end
|
||||
|
||||
if moves_full then
|
||||
u.moves = u.max_moves
|
||||
else
|
||||
u.moves = math.min(u.max_moves, u.moves + (cfg.moves or 0))
|
||||
end
|
||||
|
||||
if cfg.restore_attacks then
|
||||
u.attacks_left = u.max_attacks
|
||||
end
|
||||
|
||||
if cfg.restore_statuses == true or cfg.restore_statuses == nil then
|
||||
u.status.poisoned = false
|
||||
u.status.petrified = false
|
||||
u.status.slowed = false
|
||||
u.status.unhealable = false
|
||||
end
|
||||
|
||||
if not heal_amount_set then
|
||||
heal_amount_set = true
|
||||
wesnoth.set_variable("heal_amount", heal_amount)
|
||||
end
|
||||
|
||||
if cfg.animate then
|
||||
local animator = wesnoth.create_animator()
|
||||
animator:add(u, 'healed', 'hits', {value = heal_amount})
|
||||
if #healers > 0 then
|
||||
animator:add(healers[1], 'healing', 'hits', {value = heal_amount})
|
||||
end
|
||||
animator:run()
|
||||
end
|
||||
end
|
||||
end
|
72
data/lua/wml/kill.lua
Normal file
72
data/lua/wml/kill.lua
Normal file
|
@ -0,0 +1,72 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
local location_set = wesnoth.require "location_set"
|
||||
|
||||
local kill_recursion_preventer = location_set.create()
|
||||
|
||||
function wesnoth.wml_actions.kill(cfg)
|
||||
local number_killed = 0
|
||||
local secondary_unit = helper.get_child(cfg, "secondary_unit")
|
||||
local killer_loc = {0, 0}
|
||||
if secondary_unit then
|
||||
secondary_unit = wesnoth.get_units(secondary_unit)[1]
|
||||
if cfg.fire_event then
|
||||
if secondary_unit then
|
||||
killer_loc = {secondary_unit.loc}
|
||||
else
|
||||
wesnoth.log("warn", "failed to match [secondary_unit] in [kill] with a single on-board unit")
|
||||
end
|
||||
end
|
||||
end
|
||||
local dead_men_walking = wesnoth.get_units(cfg)
|
||||
for i,unit in ipairs(dead_men_walking) do
|
||||
local death_loc = {x = tonumber(unit.x) or 0, y = tonumber(unit.y) or 0}
|
||||
if not secondary_unit then killer_loc = death_loc end
|
||||
local can_fire = false
|
||||
|
||||
local recursion = (kill_recursion_preventer:get(death_loc.x, death_loc.y) or 0) + 1
|
||||
if cfg.fire_event then
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
can_fire = true
|
||||
if death_loc.x == wesnoth.current.event.x1 and death_loc.y == wesnoth.current.event.y1 then
|
||||
if wesnoth.current.event.name == "die" or wesnoth.current.event.name == "last breath" then
|
||||
if recursion >= 10 then
|
||||
can_fire = false;
|
||||
wesnoth.log("error", "tried to fire 'die' or 'last breath' event on unit from the unit's 'die' or 'last breath' event with first_time_only=no!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if can_fire then
|
||||
wesnoth.fire_event("last breath", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.animate then
|
||||
wesnoth.scroll_to_tile(death_loc)
|
||||
local anim = wesnoth.create_animator()
|
||||
-- Possible TODO: Add weapon selection? (That's kinda a pain right now; see animate_unit defn)
|
||||
anim:add(unit, "death", "kill")
|
||||
if secondary_unit then
|
||||
anim:add(secondary_unit, "victory", "kill")
|
||||
end
|
||||
anim:run()
|
||||
end
|
||||
wesnoth.wml_actions.redraw{}
|
||||
|
||||
if can_fire then
|
||||
wesnoth.fire_event("die", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.fire_event then
|
||||
if recursion <= 1 then
|
||||
kill_recursion_preventer:remove(death_loc.x, death_loc.y)
|
||||
else
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
end
|
||||
end
|
||||
-- Test that it's valid (and still on the map) first, in case the event erased (or extracted) it.
|
||||
if unit.valid == "map" then unit:erase() end
|
||||
|
||||
number_killed = number_killed + 1
|
||||
end
|
||||
|
||||
-- TODO: Do I need to check recall lists or was that covered by the above loop?
|
||||
return number_killed
|
||||
end
|
35
data/lua/wml/modify_ai.lua
Normal file
35
data/lua/wml/modify_ai.lua
Normal file
|
@ -0,0 +1,35 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
local utils = wesnoth.require "wml-utils"
|
||||
|
||||
function wesnoth.wml_actions.modify_ai(cfg)
|
||||
local sides = utils.get_sides(cfg)
|
||||
local component, final
|
||||
if cfg.action == "add" or cfg.action == "change" then
|
||||
local start = string.find(cfg.path, "[a-z_]+%[[a-z0-9_*]*%]$")
|
||||
final = start and (string.find(cfg.path, '[', start, true) - 1) or -1
|
||||
start = start or string.find(cfg.path, "[^.]*$") or 1
|
||||
local comp_type = string.sub(cfg.path, start, final)
|
||||
component = helper.get_child(cfg, comp_type)
|
||||
if component == nil then
|
||||
helper.wml_error("Missing component definition in [modify_ai]")
|
||||
end
|
||||
component = helper.parsed(component)
|
||||
end
|
||||
for i = 1, #sides do
|
||||
if cfg.action == "add" then
|
||||
wesnoth.add_ai_component(sides[i].side, cfg.path, component)
|
||||
elseif cfg.action == "delete" or cfg.action == "try_delete" then
|
||||
wesnoth.delete_ai_component(sides[i].side, cfg.path)
|
||||
elseif cfg.action == "change" then
|
||||
local id_start = final + 2
|
||||
local id_final = string.len(cfg.path) - 1
|
||||
local id = string.sub(cfg.path, id_start, id_final)
|
||||
if id == "*" then
|
||||
helper.wml_error("[modify_ai] can only change one component at a time")
|
||||
elseif not component.id and not id:match("[0-9]+") then
|
||||
component.id = id
|
||||
end
|
||||
wesnoth.change_ai_component(sides[i].side, cfg.path, component)
|
||||
end
|
||||
end
|
||||
end
|
109
data/lua/wml/modify_side.lua
Normal file
109
data/lua/wml/modify_side.lua
Normal file
|
@ -0,0 +1,109 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
local utils = wesnoth.require "wml-utils"
|
||||
local T = helper.set_wml_tag_metatable {}
|
||||
|
||||
local side_changes_needing_redraw = {
|
||||
'shroud', 'fog', 'reset_map', 'reset_view', 'shroud_data',
|
||||
'share_vision', 'share_maps', 'share_view',
|
||||
'color', 'flag',
|
||||
}
|
||||
|
||||
function wesnoth.wml_actions.modify_side(cfg)
|
||||
local sides = utils.get_sides(cfg)
|
||||
for i,side in ipairs(sides) do
|
||||
if cfg.team_name then
|
||||
side.team_name = cfg.team_name
|
||||
end
|
||||
if cfg.user_team_name then
|
||||
side.user_team_name = cfg.user_team_name
|
||||
end
|
||||
if cfg.controller then
|
||||
side.controller = cfg.controller
|
||||
end
|
||||
if cfg.defeat_condition then
|
||||
side.defeat_condition = cfg.defeat_condition
|
||||
end
|
||||
if cfg.recruit then
|
||||
local recruits = {}
|
||||
for recruit in utils.split(cfg.recruit) do
|
||||
table.insert(recruits, recruit)
|
||||
end
|
||||
side.recruit = recruits
|
||||
end
|
||||
if cfg.village_support then
|
||||
side.village_support = cfg.village_support
|
||||
end
|
||||
if cfg.village_gold then
|
||||
side.village_gold = cfg.village_gold
|
||||
end
|
||||
if cfg.income then
|
||||
side.base_income = cfg.income
|
||||
end
|
||||
if cfg.gold then
|
||||
side.gold = cfg.gold
|
||||
end
|
||||
|
||||
if cfg.hidden ~= nil then
|
||||
side.hidden = cfg.hidden
|
||||
end
|
||||
if cfg.color or cfg.flag then
|
||||
wesnoth.set_side_id(side.side, cfg.flag, cfg.color)
|
||||
end
|
||||
if cfg.flag_icon then
|
||||
side.flag_icon = cfg.flag_icon
|
||||
end
|
||||
if cfg.suppress_end_turn_confirmation ~= nil then
|
||||
side.suppress_end_turn_confirmation = cfg.suppress_end_turn_confirmation
|
||||
end
|
||||
if cfg.scroll_to_leader ~= nil then
|
||||
side.scroll_to_leader = cfg.scroll_to_leader
|
||||
end
|
||||
|
||||
if cfg.shroud ~= nil then
|
||||
side.shroud = cfg.shroud
|
||||
end
|
||||
if cfg.reset_maps then
|
||||
wesnoth.remove_shroud(side.side, "all")
|
||||
end
|
||||
if cfg.fog ~= nil then
|
||||
side.fog = cfg.fog
|
||||
end
|
||||
if cfg.reset_view then
|
||||
wesnoth.add_fog(side.side, {}, true)
|
||||
end
|
||||
if cfg.shroud_data then
|
||||
wesnoth.remove_shroud(side.side, cfg.shroud_data)
|
||||
end
|
||||
|
||||
if cfg.share_vision then
|
||||
side.share_vision = cfg.share_vision
|
||||
end
|
||||
-- Legacy support
|
||||
if cfg.share_view ~= nil or cfg.share_maps ~= nil then
|
||||
if cfg.share_view then
|
||||
side.share_vision = 'all'
|
||||
elseif cfg.share_maps then
|
||||
side.share_vision = 'shroud'
|
||||
else
|
||||
side.share_vision = 'none'
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.switch_ai then
|
||||
wesnoth.switch_ai(side.side, cfg.switch_ai)
|
||||
end
|
||||
local ai = {}
|
||||
for next_ai in helper.child_range(cfg, "ai") do
|
||||
table.insert(ai, T.ai(next_ai))
|
||||
end
|
||||
if #ai > 0 then
|
||||
wesnoth.append_ai(side.side, ai)
|
||||
end
|
||||
end
|
||||
for i,key in ipairs(side_changes_needing_redraw) do
|
||||
if cfg[key] ~= nil then
|
||||
wesnoth.wml_actions.redraw{}
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
64
data/lua/wml/move_unit.lua
Normal file
64
data/lua/wml/move_unit.lua
Normal file
|
@ -0,0 +1,64 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
|
||||
function wesnoth.wml_actions.move_unit(cfg)
|
||||
local coordinate_error = "invalid coordinate in [move_unit]"
|
||||
local to_x = tostring(cfg.to_x or helper.wml_error(coordinate_error))
|
||||
local to_y = tostring(cfg.to_y or helper.wml_error(coordinate_error))
|
||||
local fire_event = cfg.fire_event
|
||||
local muf_force_scroll = cfg.force_scroll
|
||||
local check_passability = cfg.check_passability
|
||||
if check_passability == nil then check_passability = true end
|
||||
cfg = helper.literal(cfg)
|
||||
cfg.to_x, cfg.to_y, cfg.fire_event = nil, nil, nil
|
||||
local units = wesnoth.get_units(cfg)
|
||||
|
||||
local pattern = "[^%s,]+"
|
||||
for current_unit_index, current_unit in ipairs(units) do
|
||||
if not fire_event or current_unit.valid then
|
||||
local xs, ys = string.gmatch(to_x, pattern), string.gmatch(to_y, pattern)
|
||||
local move_string_x = current_unit.x
|
||||
local move_string_y = current_unit.y
|
||||
local pass_check = nil
|
||||
if check_passability then pass_check = current_unit end
|
||||
|
||||
local x, y = xs(), ys()
|
||||
local prevX, prevY = tonumber(current_unit.x), tonumber(current_unit.y)
|
||||
while true do
|
||||
x = tonumber(x) or helper.wml_error(coordinate_error)
|
||||
y = tonumber(y) or helper.wml_error(coordinate_error)
|
||||
if not (x == prevX and y == prevY) then x, y = wesnoth.find_vacant_tile(x, y, pass_check) end
|
||||
if not x or not y then helper.wml_error("Could not find a suitable hex near to one of the target hexes in [move_unit].") end
|
||||
move_string_x = string.format("%s,%u", move_string_x, x)
|
||||
move_string_y = string.format("%s,%u", move_string_y, y)
|
||||
local next_x, next_y = xs(), ys()
|
||||
if not next_x and not next_y then break end
|
||||
prevX, prevY = x, y
|
||||
x, y = next_x, next_y
|
||||
end
|
||||
|
||||
if current_unit.x < x then current_unit.facing = "se"
|
||||
elseif current_unit.x > x then current_unit.facing = "sw"
|
||||
end
|
||||
|
||||
wesnoth.extract_unit(current_unit)
|
||||
local current_unit_cfg = current_unit.__cfg
|
||||
wesnoth.wml_actions.move_unit_fake {
|
||||
type = current_unit_cfg.type,
|
||||
gender = current_unit_cfg.gender,
|
||||
variation = current_unit_cfg.variation,
|
||||
image_mods = current_unit.image_mods,
|
||||
side = current_unit_cfg.side,
|
||||
x = move_string_x,
|
||||
y = move_string_y,
|
||||
force_scroll = muf_force_scroll
|
||||
}
|
||||
local x2, y2 = current_unit.x, current_unit.y
|
||||
current_unit.x, current_unit.y = x, y
|
||||
wesnoth.put_unit(current_unit)
|
||||
|
||||
if fire_event then
|
||||
wesnoth.fire_event("moveto", x, y, x2, y2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
111
data/lua/wml/role.lua
Normal file
111
data/lua/wml/role.lua
Normal file
|
@ -0,0 +1,111 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
local utils = wesnoth.require "wml-utils"
|
||||
|
||||
function wesnoth.wml_actions.role(cfg)
|
||||
-- role= and type= are handled differently than in other tags,
|
||||
-- so we need to remove them from the filter
|
||||
local role = cfg.role
|
||||
local filter = helper.shallow_literal(cfg)
|
||||
|
||||
if role == nil then
|
||||
helper.wml_error("missing role= in [role]")
|
||||
end
|
||||
|
||||
local types = {}
|
||||
|
||||
if cfg.type then
|
||||
for value in utils.split(cfg.type) do
|
||||
table.insert(types, utils.trim(value))
|
||||
end
|
||||
end
|
||||
|
||||
filter.role, filter.type = nil, nil
|
||||
local search_map, search_recall, reassign = true, true, true
|
||||
if cfg.search_recall_list == "only" then
|
||||
search_map = false
|
||||
elseif cfg.search_recall_list ~= nil then
|
||||
search_recall = not not cfg.search_recall_list
|
||||
end
|
||||
if cfg.reassign ~= nil then
|
||||
reassign = not not cfg.reassign
|
||||
end
|
||||
|
||||
-- pre-build a new [recall] from the [auto_recall]
|
||||
-- copy only recall-specific attributes, no SUF at all
|
||||
-- the SUF will be id= which we will add in a moment
|
||||
-- keep this in sync with the C++ recall function!!!
|
||||
local recall = nil
|
||||
local child = helper.get_child(cfg, "auto_recall")
|
||||
if child ~= nil then
|
||||
if helper.get_nth_child(cfg, "auto_recall", 2) ~= nil then
|
||||
wesnoth.log("debug", "More than one [auto_recall] found within [role]", true)
|
||||
end
|
||||
local original = helper.shallow_literal(child)
|
||||
recall = {}
|
||||
recall.x = original.x
|
||||
recall.y = original.y
|
||||
recall.show = original.show
|
||||
recall.fire_event = original.fire_event
|
||||
recall.check_passability = original.check_passability
|
||||
end
|
||||
|
||||
if not reassign then
|
||||
if search_map then
|
||||
local unit = wesnoth.get_units{role=role}[1]
|
||||
if unit then
|
||||
return
|
||||
end
|
||||
end
|
||||
if recall and search_recall then
|
||||
local unit = wesnoth.get_recall_units{role=role}[1]
|
||||
if unit then
|
||||
recall.id = unit.id
|
||||
wesnoth.wml_actions.recall(recall)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if search_map then
|
||||
-- first attempt to match units on the map
|
||||
local i = 1
|
||||
repeat
|
||||
-- give precedence based on the order specified in type=
|
||||
if #types > 0 then
|
||||
filter.type = types[i]
|
||||
end
|
||||
local unit = wesnoth.get_units(filter)[1]
|
||||
if unit then
|
||||
unit.role = role
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
until #types == 0 or i > #types
|
||||
end
|
||||
|
||||
if search_recall then
|
||||
-- then try to match units on the recall lists
|
||||
i = 1
|
||||
repeat
|
||||
if #types > 0 then
|
||||
filter.type = types[i]
|
||||
end
|
||||
local unit = wesnoth.get_recall_units(filter)[1]
|
||||
if unit then
|
||||
unit.role = role
|
||||
if recall then
|
||||
recall.id = unit.id
|
||||
wesnoth.wml_actions.recall(recall)
|
||||
end
|
||||
return
|
||||
end
|
||||
i = i + 1
|
||||
until #types == 0 or i > #types
|
||||
end
|
||||
|
||||
-- no matching unit found, try the [else] tags
|
||||
for else_child in helper.child_range(cfg, "else") do
|
||||
local action = utils.handle_event_commands(else_child, "conditional")
|
||||
if action ~= "none" then return end
|
||||
end
|
||||
end
|
118
data/lua/wml/set_variable.lua
Normal file
118
data/lua/wml/set_variable.lua
Normal file
|
@ -0,0 +1,118 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
|
||||
function wesnoth.wml_actions.set_variable(cfg)
|
||||
local name = cfg.name or helper.wml_error "trying to set a variable with an empty name"
|
||||
|
||||
if cfg.value ~= nil then -- check for nil because user may try to set a variable as false
|
||||
wesnoth.set_variable(name, cfg.value)
|
||||
end
|
||||
|
||||
if cfg.literal ~= nil then
|
||||
wesnoth.set_variable(name, helper.shallow_literal(cfg).literal)
|
||||
end
|
||||
|
||||
if cfg.to_variable then
|
||||
wesnoth.set_variable(name, wesnoth.get_variable(cfg.to_variable))
|
||||
end
|
||||
|
||||
if cfg.add then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) + (tonumber(cfg.add) or 0))
|
||||
end
|
||||
|
||||
if cfg.sub then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) - (tonumber(cfg.sub) or 0))
|
||||
end
|
||||
|
||||
if cfg.multiply then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) * (tonumber(cfg.multiply) or 0))
|
||||
end
|
||||
|
||||
if cfg.divide then
|
||||
local divide = tonumber(cfg.divide) or 0
|
||||
if divide == 0 then helper.wml_error("division by zero on variable " .. name) end
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) / divide)
|
||||
end
|
||||
|
||||
if cfg.modulo then
|
||||
local modulo = tonumber(cfg.modulo) or 0
|
||||
if modulo == 0 then helper.wml_error("division by zero on variable " .. name) end
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) % modulo)
|
||||
end
|
||||
|
||||
if cfg.abs then
|
||||
wesnoth.set_variable(name, math.abs(tonumber(wesnoth.get_variable(name)) or 0))
|
||||
end
|
||||
|
||||
if cfg.root then
|
||||
if cfg.root == "square" then
|
||||
local radicand = tonumber(wesnoth.get_variable(name)) or 0
|
||||
if radicand < 0 then helper.wml_error("square root of negative number on variable " .. name) end
|
||||
wesnoth.set_variable(name, math.sqrt(radicand))
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.power then
|
||||
wesnoth.set_variable(name, (tonumber(wesnoth.get_variable(name)) or 0) ^ (tonumber(cfg.power) or 0))
|
||||
end
|
||||
|
||||
if cfg.round then
|
||||
local var = tonumber(wesnoth.get_variable(name) or 0)
|
||||
local round_val = cfg.round
|
||||
if round_val == "ceil" then
|
||||
wesnoth.set_variable(name, math.ceil(var))
|
||||
elseif round_val == "floor" then
|
||||
wesnoth.set_variable(name, math.floor(var))
|
||||
else
|
||||
local decimals, discarded = math.modf(tonumber(round_val) or 0)
|
||||
local value = var * (10 ^ decimals)
|
||||
value = helper.round(value)
|
||||
value = value * (10 ^ -decimals)
|
||||
wesnoth.set_variable(name, value)
|
||||
end
|
||||
end
|
||||
|
||||
-- unlike the other math operations, ipart and fpart do not act on
|
||||
-- the value already contained in the variable
|
||||
-- but on the value assigned to the respective key
|
||||
if cfg.ipart then
|
||||
local ivalue, fvalue = math.modf(tonumber(cfg.ipart) or 0)
|
||||
wesnoth.set_variable(name, ivalue)
|
||||
end
|
||||
|
||||
if cfg.fpart then
|
||||
local ivalue, fvalue = math.modf(tonumber(cfg.fpart) or 0)
|
||||
wesnoth.set_variable(name, fvalue)
|
||||
end
|
||||
|
||||
if cfg.string_length ~= nil then
|
||||
wesnoth.set_variable(name, string.len(tostring(cfg.string_length)))
|
||||
end
|
||||
|
||||
if cfg.time then
|
||||
if cfg.time == "stamp" then
|
||||
wesnoth.set_variable(name, wesnoth.get_time_stamp())
|
||||
end
|
||||
end
|
||||
|
||||
if cfg.rand then
|
||||
wesnoth.set_variable(name, helper.rand(tostring(cfg.rand)))
|
||||
end
|
||||
|
||||
local join_child = helper.get_child(cfg, "join")
|
||||
if join_child then
|
||||
local array_name = join_child.variable or helper.wml_error "missing variable= attribute in [join]"
|
||||
local separator = join_child.separator
|
||||
local key_name = join_child.key or "value"
|
||||
local remove_empty = join_child.remove_empty
|
||||
|
||||
local string_to_join = {}
|
||||
|
||||
for i, element in ipairs(helper.get_variable_array(array_name)) do
|
||||
if element[key_name] ~= nil or (not remove_empty) then
|
||||
table.insert(string_to_join, tostring(element[key_name]))
|
||||
end
|
||||
end
|
||||
|
||||
wesnoth.set_variable(name, table.concat(string_to_join, separator))
|
||||
end
|
||||
end
|
53
data/lua/wml/test_condition.lua
Normal file
53
data/lua/wml/test_condition.lua
Normal file
|
@ -0,0 +1,53 @@
|
|||
local helper = wesnoth.require "helper"
|
||||
|
||||
-- This function returns true if it managed to explain the failure
|
||||
local function explain(current_cfg, expect)
|
||||
for i,t in ipairs(current_cfg) do
|
||||
local tag, this_cfg = t[1], t[2]
|
||||
-- Some special cases
|
||||
if tag == "or" or tag == "and" then
|
||||
if explain(this_cfg, expect) then
|
||||
return true
|
||||
end
|
||||
elseif tag == "not" then
|
||||
if explain(this_cfg, not expect) then
|
||||
return true
|
||||
end
|
||||
elseif tag == "true" or tag == "false" then
|
||||
-- We don't explain these ones.
|
||||
return true
|
||||
elseif wesnoth.eval_conditional{t} == expect then
|
||||
local explanation = "The following conditional test %s:"
|
||||
if expect then
|
||||
explanation = explanation:format("passed")
|
||||
else
|
||||
explanation = explanation:format("failed")
|
||||
end
|
||||
explanation = string.format("%s\n\t[%s]", explanation, tag)
|
||||
for k,v in pairs(this_cfg) do
|
||||
if type(k) ~= "number" then
|
||||
local format = "%s\n\t\t%s=%s"
|
||||
local literal = tostring(helper.literal(this_cfg)[k])
|
||||
if literal ~= v then
|
||||
format = format .. "=%s"
|
||||
end
|
||||
explanation = string.format(format, explanation, k, literal, tostring(v))
|
||||
end
|
||||
end
|
||||
explanation = string.format("%s\n\t[/%s]", explanation, tag)
|
||||
if tag == "variable" then
|
||||
explanation = string.format("%s\n\tNote: The variable %s currently has the value %q.", explanation, this_cfg.name, tostring(wesnoth.get_variable(this_cfg.name)))
|
||||
end
|
||||
wesnoth.log(logger, explanation, true)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- This is mainly for use in unit test macros, but maybe it can be useful elsewhere too
|
||||
function wesnoth.wml_actions.test_condition(cfg)
|
||||
local logger = cfg.logger or "warning"
|
||||
|
||||
-- Use not twice here to convert nil to false
|
||||
explain(cfg, not not cfg.result)
|
||||
end
|
Loading…
Add table
Reference in a new issue