Merge branch 'wml_tag_porting'
This commit is contained in:
commit
8822dacd92
13 changed files with 690 additions and 498 deletions
28
changelog
28
changelog
|
@ -27,8 +27,30 @@ Version 1.13.6+dev:
|
|||
* Updated translations: British English, Czech, German, Portuguese (Brazil),
|
||||
Scottish Gaelic
|
||||
* Lua API:
|
||||
* New attributes in side proxy:
|
||||
num_units, num_villages, total_upkeep, expenses, net_income
|
||||
* New wesnoth.set_side_id function can change flag, color, or both; it automatically
|
||||
updates cached flag info for you (but you may still need to redraw to see it).
|
||||
* The wesnoth.place_shroud and wesnoth.clear_shroud functions can alter shroud data
|
||||
for a single side. They accept a list of locations, a shroud data string, or the
|
||||
special value "all".
|
||||
* New wesnoth.is_fogged and wesnoth.is_shrouded calls test the visibility level
|
||||
of a hex for a particular side.
|
||||
* New wesnoth.create_animator call produces an object that can be used to run
|
||||
animations for a group of units
|
||||
* New Lua API functions for altering AI:
|
||||
* wesnoth.switch_ai replaces the entire AI with a definition from a file
|
||||
* wesnoth.append_ai appends AI parameters to the configuation; supports goals,
|
||||
stages, and simple aspects.
|
||||
(Aspect tags are not fully parsed; only the id and facet subtags are used.)
|
||||
* wesnoth.add_ai_component, delete_ai_component, change_ai_component
|
||||
These do the work of the [modify_ai] tag for a single side.
|
||||
* Side proxy changes:
|
||||
* flag and flag_icon are never an empty string
|
||||
* New mutable keys: suppress_end_turn_confirmation, share_vision
|
||||
* New read-only keys: share_maps, share_view
|
||||
num_units, num_villages, total_upkeep, expenses, net_income
|
||||
* Existing keys made mutable: shroud, fog, flag, flag_icon
|
||||
* wesnoth.scroll_to_tile now accepts a third boolean argument - if true, the scroll
|
||||
is skipped when the tile is already visible onscreen.
|
||||
* User Interface:
|
||||
* List boxes now keep the selected item visible when you change the sorting
|
||||
option.
|
||||
|
@ -41,6 +63,8 @@ Version 1.13.6+dev:
|
|||
* Add ~CHAN() IPF that allows altering images according to formulas
|
||||
It takes up to 4 WFL formulas, one for each channel (red, green,
|
||||
blue, alpha); each works the same as the ~ADJUST_ALPHA() formula.
|
||||
* New ability_type key in standard unit filters matches if the unit has any
|
||||
ability of the specified type (tag name).
|
||||
* Miscellaneous and bug fixes:
|
||||
* Fixed severe lag/freeze on slow PCs (bug #25356)
|
||||
* Updated wmlscope to handle the square braces syntax in file paths
|
||||
|
|
|
@ -13,6 +13,7 @@ end
|
|||
|
||||
wesnoth.require "lua/wml-flow.lua"
|
||||
wesnoth.require "lua/wml/objectives.lua"
|
||||
wesnoth.require "lua/wml/animate_unit.lua"
|
||||
wesnoth.require "lua/wml/items.lua"
|
||||
wesnoth.require "lua/wml/message.lua"
|
||||
wesnoth.require "lua/wml/object.lua"
|
||||
|
@ -632,7 +633,67 @@ function wml_actions.unpetrify(cfg)
|
|||
end
|
||||
|
||||
function wml_actions.heal_unit(cfg)
|
||||
wesnoth.heal_unit(cfg)
|
||||
local healers = helper.get_child("filter_second")
|
||||
if healers then
|
||||
healers = wesnoth.get_units{
|
||||
ability_type = "heals",
|
||||
T["and"](healers)
|
||||
}
|
||||
else
|
||||
healers = {}
|
||||
end
|
||||
|
||||
local who = helper.get_child("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"
|
||||
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 = math.min(math.max(1, cfg.amount), heal_amount)
|
||||
u.hitpoints = u.hitpoints + heal_amount
|
||||
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 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
|
||||
wesnoth.animate_unit{
|
||||
T.filter(healers),
|
||||
flag = "healing"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function wml_actions.transform_unit(cfg)
|
||||
|
@ -674,11 +735,36 @@ function wml_actions.store_side(cfg)
|
|||
end
|
||||
end
|
||||
|
||||
-- This is the port of the old [modify_ai] into lua. It is different from wesnoth.modify_ai in that it uses a standard side filter.
|
||||
-- I don't know why these functions were made to behave differently, but this seems to be the more powerful and useful one according
|
||||
-- to mattsc's comments
|
||||
function wml_actions.modify_ai(cfg)
|
||||
wesnoth.modify_ai_wml(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 = string.find(cfg.path, '[', start, true) - 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)
|
||||
|
@ -817,10 +903,6 @@ function wml_actions.scroll(cfg)
|
|||
wesnoth.scroll(cfg)
|
||||
end
|
||||
|
||||
function wml_actions.animate_unit(cfg)
|
||||
wesnoth.animate_unit(cfg)
|
||||
end
|
||||
|
||||
function wml_actions.color_adjust(cfg)
|
||||
wesnoth.color_adjust(cfg)
|
||||
end
|
||||
|
@ -861,8 +943,109 @@ 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)
|
||||
wesnoth.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_team_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.clear_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.clear_shroud(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)
|
||||
|
|
|
@ -50,19 +50,27 @@ function utils.vwriter.write(self, container)
|
|||
self.index = self.index + 1
|
||||
end
|
||||
|
||||
function utils.get_sides(cfg, key_name, filter_name)
|
||||
key_name = key_name or "side"
|
||||
filter_name = filter_name or "filter_side"
|
||||
local filter = helper.get_child(cfg, filter_name)
|
||||
if filter then
|
||||
if cfg[key_name] then
|
||||
wesnoth.log('warn', "ignoring duplicate side filter information (inline side=)")
|
||||
end
|
||||
return wesnoth.get_sides(filter)
|
||||
else
|
||||
return wesnoth.get_sides{side = cfg[key_name]}
|
||||
end
|
||||
end
|
||||
|
||||
function utils.optional_side_filter(cfg, key_name, filter_name)
|
||||
local key_name = key_name or "side"
|
||||
local sides = cfg[key_name]
|
||||
local filter_name = filter_name or "filter_side"
|
||||
local filter_side = helper.get_child(cfg, filter_name)
|
||||
if filter_side then
|
||||
sides = wesnoth.get_sides(filter_side)
|
||||
elseif sides then
|
||||
local dummy_cfg = {side=sides}
|
||||
sides = wesnoth.get_sides(dummy_cfg)
|
||||
else
|
||||
if cfg[key_name] == nil and helper.get_child(cfg, filter_name) == nil then
|
||||
return true
|
||||
end
|
||||
local sides = utils.get_sides(cfg, key_name, filter_name)
|
||||
for index,side in ipairs(sides) do
|
||||
if side.controller == "human" then
|
||||
return true
|
||||
|
|
114
data/lua/wml/animate_unit.lua
Normal file
114
data/lua/wml/animate_unit.lua
Normal file
|
@ -0,0 +1,114 @@
|
|||
local helper = wesnoth.require "lua/helper.lua"
|
||||
local T = helper.set_wml_tag_metatable{}
|
||||
|
||||
local function get_fake_attack(unit, cfg)
|
||||
-- This hacky-looking code is because the only way to create an
|
||||
-- attack object in Lua is by adding the attack to a unit.
|
||||
-- In this case, it's immediately deleted afterwards.
|
||||
local n = #unit.attacks + 1
|
||||
unit.attacks[n] = cfg
|
||||
local atk = unit.attacks[n]
|
||||
unit.attacks[n] = nil
|
||||
return atk
|
||||
end
|
||||
|
||||
local function get_real_attack(unit, filter)
|
||||
for i, atk in ipairs(unit.attacks) do
|
||||
if atk:matches(filter) then return atk end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_animation(anim, cfg)
|
||||
cfg = helper.shallow_parsed(cfg)
|
||||
local filter = helper.get_child(cfg, "filter")
|
||||
local unit
|
||||
if filter then
|
||||
unit = wesnoth.get_units{
|
||||
limit = 1,
|
||||
T["and"](filter)
|
||||
}[1]
|
||||
else
|
||||
unit = wesnoth.get_unit(
|
||||
wesnoth.current.event_context.x1,
|
||||
wesnoth.current.event_context.y1
|
||||
)
|
||||
end
|
||||
|
||||
if unit and not wesnoth.is_fogged(wesnoth.current.side, u.loc) then
|
||||
local primary = helper.get_child(cfg, "primary_attack")
|
||||
local secondary = helper.get_child(cfg, "secondary_attack")
|
||||
local get_attack = get_real_attack
|
||||
if cfg.flag == "death" or cfg.flag == "victory" then
|
||||
-- death and victory animations need a special case
|
||||
-- In order to correctly fire certain animations, a dummy attack
|
||||
-- is required. This is especially evident in Wose death animations.
|
||||
get_attack = get_fake_attack
|
||||
end
|
||||
if primary then
|
||||
primary = get_attack(unit, primary)
|
||||
end
|
||||
if secondary then
|
||||
secondary = get_attack(unit, secondary)
|
||||
end
|
||||
|
||||
local hits = cfg.hits
|
||||
if hits == true then
|
||||
hits = 'hit'
|
||||
elseif hits == false then
|
||||
hits = 'miss'
|
||||
end
|
||||
|
||||
local color = {0xff, 0xff, 0xff}
|
||||
if cfg.red or cfg.green or cfg.blue then
|
||||
-- This tonumber() or 0 is to ensure they're all definitely numbers
|
||||
-- It works because tonumber() returns nil if its argument is not a number
|
||||
color = {
|
||||
tonumber(cfg.red) or 0,
|
||||
tonumber(cfg.green) or 0,
|
||||
tonumber(cfg.blue) or 0
|
||||
}
|
||||
end
|
||||
|
||||
-- TODO: The last argument is currently unused
|
||||
-- (should make the game not scroll if view locked or prefs disables it)
|
||||
wesnoth.scroll_to_tile(unit.x, unit.y, true, false, true, false)
|
||||
|
||||
local facing = helper.get_child(cfg, "facing")
|
||||
if facing then
|
||||
local facing_loc = wesnoth.get_locations(facing)[1]
|
||||
if facing_loc then
|
||||
local dir = wesnoth.map_location_ops.get_relative_dir(unit.x, unit.y, facing_loc[1], facing_loc[2])
|
||||
facing = wesnoth.map_location_ops.get_direction(unit.x, unit.y, dir)
|
||||
else
|
||||
facing = nil
|
||||
end
|
||||
end
|
||||
|
||||
local text = cfg.text
|
||||
if cfg.female_text and unit.gender == 'female' then
|
||||
text = cfg.female_text
|
||||
elseif cfg.male_text and unit.gender == 'male' then
|
||||
text = cfg.male_text
|
||||
end
|
||||
|
||||
anim:add(unit, cfg.flag, hits, {
|
||||
facing = facing,
|
||||
value = {tonumber(cfg.value) or 0, tonumber(cfg.value_second) or 0},
|
||||
with_bars = not not cfg.with_bars,
|
||||
text = text,
|
||||
color = color,
|
||||
primary = primary,
|
||||
secondary = secondary
|
||||
})
|
||||
end
|
||||
|
||||
for c in helper.child_range("animate") do
|
||||
add_animation(anim, c)
|
||||
end
|
||||
end
|
||||
|
||||
function wesnoth.wml_actions.animate_unit(cfg)
|
||||
local anim = wesnoth.create_animator()
|
||||
add_animation(anim, cfg)
|
||||
anim:run()
|
||||
end
|
|
@ -354,7 +354,7 @@ function wesnoth.wml_actions.message(cfg)
|
|||
else
|
||||
-- Check ~= false, because the default if omitted should be true
|
||||
if cfg.scroll ~= false then
|
||||
wesnoth.scroll_to_tile(speaker.x, speaker.y)
|
||||
wesnoth.scroll_to_tile(speaker.x, speaker.y, false, false, true)
|
||||
end
|
||||
|
||||
wesnoth.highlight_hex(speaker.x, speaker.y)
|
||||
|
|
|
@ -148,33 +148,6 @@ ai_composite& holder::get_ai_ref()
|
|||
}
|
||||
|
||||
|
||||
void holder::modify_side_ai_config(config cfg)
|
||||
{
|
||||
// only handle aspects
|
||||
// transform ai_parameters to new-style config
|
||||
|
||||
configuration::expand_simplified_aspects(this->side_, cfg);
|
||||
cfg = cfg.child("ai");
|
||||
//at this point we have a single config which contains [aspect][facet] tags
|
||||
DBG_AI_MANAGER << "after transforming [modify_side][ai] into new syntax, config contains:"<< std::endl << cfg << std::endl;
|
||||
|
||||
// TODO: Also add [goal] tags. And what about [stage] or [engine] tags? (Maybe they're not important.)
|
||||
if (this->readonly_context_ == nullptr) {
|
||||
// if not initialized, append that config to the bottom of base cfg
|
||||
// then, merge aspects with the same id
|
||||
cfg_.append_children(cfg);
|
||||
cfg_.merge_children_by_attribute("aspect","id");
|
||||
} else {
|
||||
// else run 'add_facet' command on each [aspect][facet]
|
||||
for (const config &cfg_a : cfg.child_range("aspect")) {
|
||||
for (const config &cfg_f : cfg_a.child_range("facet")) {
|
||||
readonly_context_->add_facet(cfg_a["id"],cfg_f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void holder::modify_ai(const config &cfg)
|
||||
{
|
||||
if (!this->ai_) {
|
||||
|
@ -182,9 +155,9 @@ void holder::modify_ai(const config &cfg)
|
|||
get_ai_ref();
|
||||
}
|
||||
const std::string &act = cfg["action"];
|
||||
LOG_AI_MOD << "side "<< side_ << " [modify_ai] "<<act<<" \""<<cfg["path"]<<"\""<<std::endl;
|
||||
LOG_AI_MOD << "side "<< side_ << " "<<act<<"_ai_component \""<<cfg["path"]<<"\""<<std::endl;
|
||||
DBG_AI_MOD << std::endl << cfg << std::endl;
|
||||
DBG_AI_MOD << "side "<< side_ << " before [modify_ai]"<<std::endl << to_config() << std::endl;
|
||||
DBG_AI_MOD << "side "<< side_ << " before "<<act<<"_ai_component"<<std::endl << to_config() << std::endl;
|
||||
bool res = false;
|
||||
if (act == "add") {
|
||||
res = component_manager::add_component(&*this->ai_,cfg["path"],cfg);
|
||||
|
@ -192,24 +165,39 @@ void holder::modify_ai(const config &cfg)
|
|||
res = component_manager::change_component(&*this->ai_,cfg["path"],cfg);
|
||||
} else if (act == "delete") {
|
||||
res = component_manager::delete_component(&*this->ai_,cfg["path"]);
|
||||
} else if (act == "try_delete") {
|
||||
res = component_manager::delete_component(&*this->ai_,cfg["path"]);
|
||||
if (!res) {
|
||||
LOG_AI_MOD << "[modify_ai] "<<act<<" failed, ignoring because it's a try_delete"<< std::endl;
|
||||
res = true;
|
||||
}
|
||||
} else {
|
||||
ERR_AI_MOD << "modify_ai tag has invalid 'action' attribute " << act << std::endl;
|
||||
}
|
||||
DBG_AI_MOD << "side "<< side_ << " after [modify_ai]"<<act<<std::endl << to_config() << std::endl;
|
||||
if (!res) {
|
||||
LOG_AI_MOD << "[modify_ai] "<<act<<" failed"<< std::endl;
|
||||
LOG_AI_MOD << act << "_ai_component failed"<< std::endl;
|
||||
} else {
|
||||
LOG_AI_MOD << "[modify_ai] "<<act<<" success"<< std::endl;
|
||||
LOG_AI_MOD << act << "_ai_component success"<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void holder::append_ai(const config& cfg)
|
||||
{
|
||||
if(!this->ai_) {
|
||||
get_ai_ref();
|
||||
}
|
||||
for(const config& aspect : cfg.child_range("aspect")) {
|
||||
const std::string& id = aspect["id"];
|
||||
for(const config& facet : aspect.child_range("facet")) {
|
||||
ai_->add_facet(id, facet);
|
||||
}
|
||||
}
|
||||
for(const config& goal : cfg.child_range("goal")) {
|
||||
ai_->add_goal(goal);
|
||||
}
|
||||
for(const config& stage : cfg.child_range("stage")) {
|
||||
if(stage["name"] != "empty") {
|
||||
ai_->add_stage(stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config holder::to_config() const
|
||||
{
|
||||
if (!this->ai_) {
|
||||
|
@ -704,20 +692,6 @@ void manager::clear_ais()
|
|||
ai_map_.clear();
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Work with active AI parameters
|
||||
// =======================================================================
|
||||
|
||||
void manager::modify_active_ai_config_old_for_side ( side_number side, const config::const_child_itors &ai_parameters )
|
||||
{
|
||||
config cfgs;
|
||||
for (const config& cfg : ai_parameters) {
|
||||
cfgs.add_child("ai", cfg);
|
||||
}
|
||||
cfgs.child_or_add("ai").add_child("stage")["name"] = "empty";
|
||||
get_active_ai_holder_for_side(side).modify_side_ai_config(cfgs);
|
||||
}
|
||||
|
||||
|
||||
void manager::modify_active_ai_for_side ( side_number side, const config &cfg )
|
||||
{
|
||||
|
@ -729,6 +703,14 @@ void manager::modify_active_ai_for_side ( side_number side, const config &cfg )
|
|||
}
|
||||
|
||||
|
||||
void manager::append_active_ai_for_side(side_number side, const config& cfg)
|
||||
{
|
||||
if(!ai_info_) {
|
||||
return;
|
||||
}
|
||||
get_active_ai_holder_for_side(side).append_ai(cfg);
|
||||
}
|
||||
|
||||
std::string manager::get_active_ai_overview_for_side( side_number side)
|
||||
{
|
||||
return get_active_ai_holder_for_side(side).get_ai_overview();
|
||||
|
|
|
@ -63,7 +63,9 @@ public:
|
|||
config to_config() const;
|
||||
|
||||
void modify_ai(const config& cfg);
|
||||
void modify_side_ai_config(config cfg);
|
||||
|
||||
|
||||
void append_ai(const config& cfg);
|
||||
|
||||
|
||||
const std::string get_ai_overview();
|
||||
|
@ -428,18 +430,6 @@ public:
|
|||
// SET active AI parameters
|
||||
// =======================================================================
|
||||
|
||||
|
||||
/**
|
||||
* Modifies AI parameters for active AI of the given @a side.
|
||||
* This function is provided for backward-compatibility with [modify_side][ai]...[/ai][/modify_side]
|
||||
* It can only add new facets to aspects
|
||||
* @param side side_number (1-based, as in game_info).
|
||||
* @param ai_parameters AI parameters to be modified.
|
||||
*/
|
||||
static void modify_active_ai_config_old_for_side ( side_number side, const config::const_child_itors &ai_parameters );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Modifies AI parameters for active AI of the given @a side.
|
||||
* This function is a backend for [modify_ai] tag
|
||||
|
@ -449,6 +439,15 @@ public:
|
|||
|
||||
static void modify_active_ai_for_side( ai::side_number side, const config &cfg );
|
||||
|
||||
/**
|
||||
* Appends AI parameters to active AI of the given @a side.
|
||||
* This function is a backend for [modify_side][ai] tag
|
||||
* @param side side_number (1-based, as in game_info).
|
||||
* @param cfg - content of [modify_side][ai] tag
|
||||
*/
|
||||
|
||||
static void append_active_ai_for_side( ai::side_number side, const config &cfg );
|
||||
|
||||
// =======================================================================
|
||||
// PROXY
|
||||
// =======================================================================
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
#include "variable.hpp" // for vconfig, etc
|
||||
#include "variable_info.hpp"
|
||||
#include "wml_exception.hpp"
|
||||
#include "config_assign.hpp"
|
||||
|
||||
#include "utils/functional.hpp" // for bind_t, bind
|
||||
#include <boost/range/algorithm/copy.hpp> // boost::copy
|
||||
|
@ -332,16 +333,145 @@ static int intf_get_viewing_side(lua_State *L)
|
|||
}
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_animate_unit(lua_State *L)
|
||||
{
|
||||
// if (game_display_)
|
||||
{
|
||||
events::command_disabler disable_commands;
|
||||
unit_display::wml_animation(luaW_checkvconfig(L, 1), get_event_info().loc1);
|
||||
}
|
||||
static const char animatorKey[] = "unit animator";
|
||||
|
||||
static int impl_animator_collect(lua_State* L) {
|
||||
unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
|
||||
anim.set_all_standing();
|
||||
anim.~unit_animator();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_add_animation(lua_State* L)
|
||||
{
|
||||
unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
|
||||
unit& u = luaW_checkunit(L, 2);
|
||||
std::string which = luaL_checkstring(L, 3);
|
||||
|
||||
using hit_type = unit_animation::hit_type;
|
||||
std::string hits_str = luaL_checkstring(L, 4);
|
||||
hit_type hits = hit_type::string_to_enum(hits_str, hit_type::INVALID);
|
||||
|
||||
map_location dest;
|
||||
int v1 = 0, v2 = 0;
|
||||
bool bars = false;
|
||||
std::string text;
|
||||
color_t color{255, 255, 255};
|
||||
const_attack_ptr primary, secondary;
|
||||
|
||||
if(lua_istable(L, 5)) {
|
||||
lua_getfield(L, 5, "facing");
|
||||
if(!luaW_tolocation(L, -1, dest)) {
|
||||
// luaW_tolocation may set the location to (0,0) if it fails
|
||||
dest = map_location();
|
||||
if(!lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, "location table");
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "value");
|
||||
if(lua_isnumber(L, -1)) {
|
||||
v1 = lua_tonumber(L, -1);
|
||||
} else if(lua_istable(L, -1)) {
|
||||
lua_rawgeti(L, 1, 1);
|
||||
v1 = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, 1, 2);
|
||||
v2 = lua_tonumber(L, -1);
|
||||
lua_pop(L, 2);
|
||||
} else if(!lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, "number or array of two numbers");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "with_bars");
|
||||
if(lua_isboolean(L, -1)) {
|
||||
bars = luaW_toboolean(L, -1);
|
||||
} else if(!lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, lua_typename(L, LUA_TBOOLEAN));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "text");
|
||||
if(lua_isstring(L, -1)) {
|
||||
text = lua_tostring(L, -1);
|
||||
} else if(!lua_isnoneornil(L, 01)) {
|
||||
return luaW_type_error(L, -1, lua_typename(L, LUA_TSTRING));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "color");
|
||||
if(lua_istable(L, -1) && lua_rawlen(L, -1) == 3) {
|
||||
lua_rawgeti(L, 1, 1); // red @ -3
|
||||
lua_rawgeti(L, 1, 2); // green @ -2
|
||||
lua_rawgeti(L, 1, 3); // blue @ -1
|
||||
color = color_t(lua_tonumber(L, -3), lua_tonumber(L, -2), lua_tonumber(L, -1));
|
||||
lua_pop(L, 3);
|
||||
} else if(!lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, "array of three numbers");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "primary");
|
||||
primary = luaW_toweapon(L, -1);
|
||||
if(!primary && !lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, "weapon");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 5, "secondary");
|
||||
secondary = luaW_toweapon(L, -1);
|
||||
if(!secondary && !lua_isnoneornil(L, -1)) {
|
||||
return luaW_type_error(L, -1, "weapon");
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
anim.add_animation(&u, which, u.get_location(), dest, v1, bars, text, color, hits, primary.get(), secondary.get(), v2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_run_animation(lua_State* L)
|
||||
{
|
||||
unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
|
||||
anim.start_animations();
|
||||
anim.wait_for_end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_clear_animation(lua_State* L)
|
||||
{
|
||||
unit_animator& anim = *static_cast<unit_animator*>(luaL_checkudata(L, 1, animatorKey));
|
||||
anim.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_animator_get(lua_State* L)
|
||||
{
|
||||
const char* m = lua_tostring(L, 2);
|
||||
return luaW_getmetafield(L, 1, m);
|
||||
}
|
||||
|
||||
static int intf_create_animator(lua_State* L)
|
||||
{
|
||||
new(L) unit_animator;
|
||||
if(luaL_newmetatable(L, animatorKey)) {
|
||||
luaL_Reg metafuncs[] = {
|
||||
{"__gc", impl_animator_collect},
|
||||
{"__index", impl_animator_get},
|
||||
{"add", impl_add_animation},
|
||||
{"run", impl_run_animation},
|
||||
{"clear", impl_clear_animation},
|
||||
};
|
||||
luaL_setfuncs(L, metafuncs, 0);
|
||||
lua_pushstring(L, "__metatable");
|
||||
lua_setfield(L, -2, animatorKey);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_gamestate_inspector(lua_State *L)
|
||||
{
|
||||
if (game_display_) {
|
||||
|
@ -695,21 +825,29 @@ int game_lua_kernel::intf_set_next_scenario(lua_State *L)
|
|||
|
||||
int game_lua_kernel::intf_shroud_op(lua_State *L, bool place_shroud)
|
||||
{
|
||||
vconfig cfg = luaW_checkvconfig(L, 1);
|
||||
|
||||
// Filter the sides.
|
||||
std::vector<int> sides = get_sides_vector(cfg);
|
||||
size_t index;
|
||||
int side_num = luaL_checkinteger(L, 1);
|
||||
|
||||
// Filter the locations.
|
||||
std::set<map_location> locs;
|
||||
const terrain_filter filter(cfg, &game_state_);
|
||||
filter.get_locations(locs, true);
|
||||
|
||||
for (const int &side_num : sides)
|
||||
{
|
||||
index = side_num - 1;
|
||||
team &t = teams()[index];
|
||||
if(!lua_isstring(L, 2)) {
|
||||
std::string data = lua_tostring(L, 2);
|
||||
// Special case - using a shroud_data string, or "all"
|
||||
team& side = teams()[side_num - 1];
|
||||
if(place_shroud) {
|
||||
side.reshroud();
|
||||
}
|
||||
if(data != "all") {
|
||||
side.merge_shroud_map_data(data);
|
||||
} else if(!place_shroud) {
|
||||
bool was_shrouded = side.uses_shroud();
|
||||
side.set_shroud(false);
|
||||
actions::clear_shroud(side.side());
|
||||
side.set_shroud(was_shrouded);
|
||||
}
|
||||
return 0;
|
||||
} else if(lua_istable(L, 2)) {
|
||||
std::vector<map_location> locs_v = lua_check<std::vector<map_location>>(L, 2);
|
||||
std::set<map_location> locs(locs_v.begin(), locs_v.end());
|
||||
team &t = teams()[side_num - 1];
|
||||
|
||||
for (map_location const &loc : locs)
|
||||
{
|
||||
|
@ -719,6 +857,8 @@ int game_lua_kernel::intf_shroud_op(lua_State *L, bool place_shroud)
|
|||
t.clear_shroud(loc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return luaL_argerror(L, 2, "expected list of locations or shroud data string");
|
||||
}
|
||||
|
||||
game_display_->labels().recalculate_shroud();
|
||||
|
@ -1777,88 +1917,6 @@ int game_lua_kernel::intf_find_cost_map(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_heal_unit(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
|
||||
const game_events::queued_event &event_info = get_event_info();
|
||||
|
||||
unit_map & temp = units();
|
||||
unit_map* units = & temp;
|
||||
|
||||
const vconfig & healers_filter = cfg.child("filter_second");
|
||||
std::vector<unit*> healers;
|
||||
if (!healers_filter.null()) {
|
||||
const unit_filter ufilt(healers_filter, &game_state_);
|
||||
for (unit& u : *units) {
|
||||
if ( ufilt(u) && u.has_ability_type("heals") ) {
|
||||
healers.push_back(&u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const config::attribute_value amount = cfg["amount"];
|
||||
const config::attribute_value moves = cfg["moves"];
|
||||
const bool restore_attacks = cfg["restore_attacks"].to_bool(false);
|
||||
const bool restore_statuses = cfg["restore_statuses"].to_bool(true);
|
||||
const bool animate = cfg["animate"].to_bool(false);
|
||||
|
||||
const vconfig & healed_filter = cfg.child("filter");
|
||||
bool only_unit_at_loc1 = healed_filter.null();
|
||||
bool heal_amount_to_set = true;
|
||||
|
||||
const unit_filter ufilt(healed_filter, &game_state_);
|
||||
for(unit_map::unit_iterator u = units->begin(); u != units->end(); ++u) {
|
||||
if (only_unit_at_loc1)
|
||||
{
|
||||
u = units->find(event_info.loc1);
|
||||
if(!u.valid()) return 0;
|
||||
}
|
||||
else if ( !ufilt(*u) ) continue;
|
||||
|
||||
int heal_amount = u->max_hitpoints() - u->hitpoints();
|
||||
if(amount.blank() || amount == "full") u->set_hitpoints(u->max_hitpoints());
|
||||
else {
|
||||
heal_amount = lexical_cast_default<int, config::attribute_value> (amount, heal_amount);
|
||||
const int new_hitpoints = std::max(1, std::min(u->max_hitpoints(), u->hitpoints() + heal_amount));
|
||||
heal_amount = new_hitpoints - u->hitpoints();
|
||||
u->set_hitpoints(new_hitpoints);
|
||||
}
|
||||
|
||||
if(!moves.blank()) {
|
||||
if(moves == "full") u->set_movement(u->total_movement());
|
||||
else {
|
||||
// set_movement doesn't set below 0
|
||||
u->set_movement(std::min<int>(
|
||||
u->total_movement(),
|
||||
u->movement_left() + lexical_cast_default<int, config::attribute_value> (moves, 0)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if(restore_attacks) u->set_attacks(u->max_attacks());
|
||||
|
||||
if(restore_statuses)
|
||||
{
|
||||
u->set_state(unit::STATE_POISONED, false);
|
||||
u->set_state(unit::STATE_SLOWED, false);
|
||||
u->set_state(unit::STATE_PETRIFIED, false);
|
||||
u->set_state(unit::STATE_UNHEALABLE, false);
|
||||
u->anim_comp().set_standing();
|
||||
}
|
||||
|
||||
if (heal_amount_to_set)
|
||||
{
|
||||
heal_amount_to_set = false;
|
||||
gamedata().get_variable("heal_amount") = heal_amount;
|
||||
}
|
||||
|
||||
if(animate) unit_display::unit_healing(*u, healers, heal_amount);
|
||||
if(only_unit_at_loc1) return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_print(lua_State *L) {
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
|
||||
|
@ -2439,15 +2497,22 @@ int game_lua_kernel::intf_play_sound(lua_State *L)
|
|||
* - Arg 1: location.
|
||||
* - Arg 2: boolean preventing scroll to fog.
|
||||
* - Arg 3: boolean specifying whether to warp instantly.
|
||||
* - Arg 4: boolean specifying whether to skip if already onscreen
|
||||
*/
|
||||
int game_lua_kernel::intf_scroll_to_tile(lua_State *L)
|
||||
{
|
||||
map_location loc = luaW_checklocation(L, 1);
|
||||
bool check_fogged = luaW_toboolean(L, 2);
|
||||
bool immediate = luaW_toboolean(L, 3);
|
||||
game_display::SCROLL_TYPE scroll = luaW_toboolean(L, 4)
|
||||
? luaW_toboolean(L, 3)
|
||||
? game_display::WARP
|
||||
: game_display::SCROLL
|
||||
: luaW_toboolean(L, 3)
|
||||
? game_display::ONSCREEN_WARP
|
||||
: game_display::ONSCREEN
|
||||
;
|
||||
if (game_display_) {
|
||||
game_display_->scroll_to_tile(loc,
|
||||
immediate ? game_display::WARP : game_display::SCROLL, check_fogged);
|
||||
game_display_->scroll_to_tile(loc, scroll, check_fogged);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -2781,173 +2846,77 @@ int game_lua_kernel::intf_match_side(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_modify_ai_wml(lua_State *L)
|
||||
int game_lua_kernel::intf_set_side_id(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
int team_i = luaL_checkinteger(L, 1) - 1;
|
||||
std::string flag = luaL_optlstring(L, 2, "", nullptr);
|
||||
std::string color = luaL_optlstring(L, 3, "", nullptr);
|
||||
|
||||
side_filter ssf(cfg, &game_state_);
|
||||
std::vector<int> sides = ssf.get_teams();
|
||||
for (const int &side_num : sides)
|
||||
{
|
||||
ai::manager::modify_active_ai_for_side(side_num,cfg.get_parsed_config());
|
||||
if(flag.empty() && color.empty()) {
|
||||
return 0;
|
||||
}
|
||||
if(team_i < 0 || static_cast<size_t>(team_i) >= teams().size()) {
|
||||
return luaL_error(L, "set_side_id: side number %d out of range", team_i);
|
||||
}
|
||||
team& side = teams()[team_i];
|
||||
|
||||
if(!color.empty()) {
|
||||
side.set_color(color);
|
||||
}
|
||||
if(!flag.empty()) {
|
||||
side.set_flag(flag);
|
||||
}
|
||||
|
||||
game_display_->reinit_flags_for_side(team_i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_modify_ai(lua_State *L, const char* action)
|
||||
{
|
||||
int side_num = luaL_checkinteger(L, 1);
|
||||
std::string path = luaL_checkstring(L, 2);
|
||||
config cfg = config_of("action", action)("path", path);
|
||||
if(strcmp(action, "delete") == 0) {
|
||||
ai::manager::modify_active_ai_for_side(side_num, cfg);
|
||||
return 0;
|
||||
}
|
||||
config component = luaW_checkconfig(L, 3);
|
||||
size_t open_brak = path.find_last_of('[');
|
||||
size_t dot = path.find_last_of('.');
|
||||
cfg.add_child(path.substr(dot + 1, open_brak - dot - 1), component);
|
||||
ai::manager::modify_active_ai_for_side(side_num, cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intf_switch_ai(lua_State *L)
|
||||
{
|
||||
int side_num = luaL_checkinteger(L, 1);
|
||||
std::string file = luaL_checkstring(L, 2);
|
||||
if(!ai::manager::add_ai_for_side_from_file(side_num, file)) {
|
||||
std::string err = formatter() << "Could not load AI for side " << side_num + 1 << " from file " << file;
|
||||
lua_pushlstring(L, err.c_str(), err.length());
|
||||
return lua_error(L);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_modify_side(lua_State *L)
|
||||
static int intf_append_ai(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
|
||||
bool invalidate_screen = false;
|
||||
|
||||
std::string team_name = cfg["team_name"];
|
||||
std::string user_team_name = cfg["user_team_name"];
|
||||
std::string controller = cfg["controller"];
|
||||
std::string defeat_condition = cfg["defeat_condition"];
|
||||
std::string recruit_str = cfg["recruit"];
|
||||
std::string shroud_data = cfg["shroud_data"];
|
||||
std::string village_support = cfg["village_support"];
|
||||
const config& parsed = cfg.get_parsed_config();
|
||||
const config::const_child_itors &ai = parsed.child_range("ai");
|
||||
std::string switch_ai = cfg["switch_ai"];
|
||||
|
||||
std::vector<int> sides = get_sides_vector(cfg);
|
||||
size_t team_index;
|
||||
|
||||
for(const int &side_num : sides)
|
||||
{
|
||||
team_index = side_num - 1;
|
||||
|
||||
team & tm = teams()[team_index];
|
||||
|
||||
LOG_LUA << "modifying side: " << side_num << "\n";
|
||||
if(!team_name.empty()) {
|
||||
LOG_LUA << "change side's team to team_name '" << team_name << "'\n";
|
||||
tm.change_team(team_name,
|
||||
user_team_name);
|
||||
} else if(!user_team_name.empty()) {
|
||||
LOG_LUA << "change side's user_team_name to '" << user_team_name << "'\n";
|
||||
tm.change_team(tm.team_name(),
|
||||
user_team_name);
|
||||
}
|
||||
// Modify recruit list (override)
|
||||
if (!recruit_str.empty()) {
|
||||
tm.set_recruits(utils::set_split(recruit_str));
|
||||
}
|
||||
// Modify income
|
||||
config::attribute_value income = cfg["income"];
|
||||
if (!income.empty()) {
|
||||
tm.set_base_income(income.to_int() + game_config::base_income);
|
||||
}
|
||||
// Modify total gold
|
||||
config::attribute_value gold = cfg["gold"];
|
||||
if (!gold.empty()) {
|
||||
tm.set_gold(gold);
|
||||
}
|
||||
// Set controller
|
||||
if (!controller.empty()) {
|
||||
tm.change_controller_by_wml(controller);
|
||||
}
|
||||
// Set defeat_condition
|
||||
if (!defeat_condition.empty()) {
|
||||
tm.set_defeat_condition_string(defeat_condition);
|
||||
}
|
||||
// Set shroud
|
||||
config::attribute_value shroud = cfg["shroud"];
|
||||
if (!shroud.empty()) {
|
||||
tm.set_shroud(shroud.to_bool(true));
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Reset shroud
|
||||
if ( cfg["reset_maps"].to_bool(false) ) {
|
||||
tm.reshroud();
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Merge shroud data
|
||||
if (!shroud_data.empty()) {
|
||||
tm.merge_shroud_map_data(shroud_data);
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Set whether team is hidden in status table
|
||||
config::attribute_value hidden = cfg["hidden"];
|
||||
if (!hidden.empty()) {
|
||||
tm.set_hidden(hidden.to_bool(true));
|
||||
}
|
||||
// Set fog
|
||||
config::attribute_value fog = cfg["fog"];
|
||||
if (!fog.empty()) {
|
||||
tm.set_fog(fog.to_bool(true));
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Reset fog
|
||||
if ( cfg["reset_view"].to_bool(false) ) {
|
||||
tm.refog();
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Set income per village
|
||||
config::attribute_value village_gold = cfg["village_gold"];
|
||||
if (!village_gold.empty()) {
|
||||
tm.set_village_gold(village_gold);
|
||||
}
|
||||
// Set support (unit levels supported per village, for upkeep purposes)
|
||||
if (!village_support.empty()) {
|
||||
tm.set_village_support(lexical_cast_default<int>(village_support, game_config::village_support));
|
||||
}
|
||||
// Redeploy ai from location (this ignores current AI parameters)
|
||||
if (!switch_ai.empty()) {
|
||||
ai::manager::add_ai_for_side_from_file(side_num,switch_ai,true);
|
||||
}
|
||||
// Override AI parameters
|
||||
if (!ai.empty()) {
|
||||
ai::manager::modify_active_ai_config_old_for_side(side_num,ai);
|
||||
}
|
||||
// Change team color
|
||||
config::attribute_value color = cfg["color"];
|
||||
if(!color.empty()) {
|
||||
tm.set_color(color);
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// Change flag imageset
|
||||
config::attribute_value flag = cfg["flag"];
|
||||
if(!flag.empty()) {
|
||||
tm.set_flag(flag);
|
||||
// Needed especially when map isn't animated.
|
||||
invalidate_screen = true;
|
||||
}
|
||||
// If either the flag set or the team color changed, we need to
|
||||
// rebuild the team's flag cache to reflect the changes. Note that
|
||||
// this is not required for flag icons (used by the theme UI only).
|
||||
if((!color.empty() || !flag.empty()) && game_display_) {
|
||||
game_display_->reinit_flags_for_side(team_index);
|
||||
}
|
||||
// Change flag icon
|
||||
config::attribute_value flag_icon = cfg["flag_icon"];
|
||||
if(!flag_icon.empty()) {
|
||||
tm.set_flag_icon(flag_icon);
|
||||
// Not needed.
|
||||
//invalidate_screen = true;
|
||||
}
|
||||
tm.handle_legacy_share_vision(cfg.get_parsed_config());
|
||||
// Suppress end turn confirmations?
|
||||
config::attribute_value setc = cfg["suppress_end_turn_confirmation"];
|
||||
if ( !setc.empty() ) {
|
||||
tm.set_no_turn_confirmation(setc.to_bool());
|
||||
}
|
||||
|
||||
// Change leader scrolling options
|
||||
config::attribute_value stl = cfg["scroll_to_leader"];
|
||||
if ( !stl.empty()) {
|
||||
tm.set_scroll_to_leader(stl.to_bool(true));
|
||||
}
|
||||
int side_num = luaL_checkinteger(L, 1);
|
||||
config cfg = luaW_checkconfig(L, 2);
|
||||
if(!cfg.has_child("ai")) {
|
||||
cfg = config_of("ai", cfg);
|
||||
}
|
||||
|
||||
// Flag an update of the screen, if needed.
|
||||
if ( invalidate_screen && game_display_) {
|
||||
game_display_->recalculate_minimap();
|
||||
game_display_->invalidate_all();
|
||||
bool added_dummy_stage = false;
|
||||
if(!cfg.child("ai").has_child("stage")) {
|
||||
added_dummy_stage = true;
|
||||
cfg.child("ai").add_child("stage", config_of("name", "empty"));
|
||||
}
|
||||
|
||||
|
||||
ai::configuration::expand_simplified_aspects(side_num, cfg);
|
||||
if(added_dummy_stage) {
|
||||
// TODO: Delete the dummy stage
|
||||
}
|
||||
ai::manager::append_active_ai_for_side(side_num, cfg.child("ai"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3431,11 +3400,12 @@ static int intf_get_time_stamp(lua_State *L)
|
|||
* Lua frontend to the modify_ai functionality
|
||||
* - Arg 1: config.
|
||||
*/
|
||||
static int intf_modify_ai(lua_State *L)
|
||||
static int intf_modify_ai_old(lua_State *L)
|
||||
{
|
||||
config cfg;
|
||||
luaW_toconfig(L, 1, cfg);
|
||||
int side = cfg["side"];
|
||||
WRN_LUA << "wesnoth.modify_ai is deprecated\n";
|
||||
ai::manager::modify_active_ai_for_side(side, cfg);
|
||||
return 0;
|
||||
}
|
||||
|
@ -3930,6 +3900,20 @@ int game_lua_kernel::intf_log(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_get_fog_or_shroud(lua_State *L, bool fog)
|
||||
{
|
||||
int side = luaL_checknumber(L, 1);
|
||||
map_location loc = luaW_checklocation(L, 2);
|
||||
if(side < 1 || static_cast<size_t>(side) > teams().size()) {
|
||||
std::string error = "side " + std::to_string(side) + " does not exist";
|
||||
return luaL_argerror(L, 1, error.c_str());
|
||||
}
|
||||
|
||||
team& t = teams()[side - 1];
|
||||
lua_pushboolean(L, fog ? t.fogged(loc) : t.shrouded(loc));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the lifting and resetting of fog via WML.
|
||||
* Keeping affect_normal_fog as false causes only the fog override to be affected.
|
||||
|
@ -4044,6 +4028,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "add_modification", &intf_add_modification },
|
||||
{ "advance_unit", &intf_advance_unit },
|
||||
{ "copy_unit", &intf_copy_unit },
|
||||
{ "create_animator", &intf_create_animator },
|
||||
{ "create_unit", &intf_create_unit },
|
||||
{ "debug", &intf_debug },
|
||||
{ "debug_ai", &intf_debug_ai },
|
||||
|
@ -4053,7 +4038,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "get_time_stamp", &intf_get_time_stamp },
|
||||
{ "get_traits", &intf_get_traits },
|
||||
{ "get_viewing_side", &intf_get_viewing_side },
|
||||
{ "modify_ai", &intf_modify_ai },
|
||||
{ "modify_ai", &intf_modify_ai_old },
|
||||
{ "remove_modifications", &intf_remove_modifications },
|
||||
{ "set_music", &intf_set_music },
|
||||
{ "transform_unit", &intf_transform_unit },
|
||||
|
@ -4070,7 +4055,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "add_sound_source", &dispatch<&game_lua_kernel::intf_add_sound_source > },
|
||||
{ "allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn > },
|
||||
{ "allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
|
||||
{ "animate_unit", &dispatch<&game_lua_kernel::intf_animate_unit > },
|
||||
{ "append_ai", &intf_append_ai },
|
||||
{ "clear_menu_item", &dispatch<&game_lua_kernel::intf_clear_menu_item > },
|
||||
{ "clear_messages", &dispatch<&game_lua_kernel::intf_clear_messages > },
|
||||
{ "color_adjust", &dispatch<&game_lua_kernel::intf_color_adjust > },
|
||||
|
@ -4107,7 +4092,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "get_villages", &dispatch<&game_lua_kernel::intf_get_villages > },
|
||||
{ "get_village_owner", &dispatch<&game_lua_kernel::intf_get_village_owner > },
|
||||
{ "get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit > },
|
||||
{ "heal_unit", &dispatch<&game_lua_kernel::intf_heal_unit > },
|
||||
{ "highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex > },
|
||||
{ "is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy > },
|
||||
{ "kill", &dispatch<&game_lua_kernel::intf_kill > },
|
||||
|
@ -4118,8 +4102,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "match_side", &dispatch<&game_lua_kernel::intf_match_side > },
|
||||
{ "match_unit", &dispatch<&game_lua_kernel::intf_match_unit > },
|
||||
{ "message", &dispatch<&game_lua_kernel::intf_message > },
|
||||
{ "modify_ai_wml", &dispatch<&game_lua_kernel::intf_modify_ai_wml > },
|
||||
{ "modify_side", &dispatch<&game_lua_kernel::intf_modify_side > },
|
||||
{ "open_help", &dispatch<&game_lua_kernel::intf_open_help > },
|
||||
{ "play_sound", &dispatch<&game_lua_kernel::intf_play_sound > },
|
||||
{ "print", &dispatch<&game_lua_kernel::intf_print > },
|
||||
|
@ -4139,16 +4121,20 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "deselect_hex", &dispatch<&game_lua_kernel::intf_deselect_hex > },
|
||||
{ "select_unit", &dispatch<&game_lua_kernel::intf_select_unit > },
|
||||
{ "skip_messages", &dispatch<&game_lua_kernel::intf_skip_messages > },
|
||||
{ "is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true > },
|
||||
{ "is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false > },
|
||||
{ "is_skipping_messages", &dispatch<&game_lua_kernel::intf_is_skipping_messages > },
|
||||
{ "set_end_campaign_credits", &dispatch<&game_lua_kernel::intf_set_end_campaign_credits > },
|
||||
{ "set_end_campaign_text", &dispatch<&game_lua_kernel::intf_set_end_campaign_text > },
|
||||
{ "set_menu_item", &dispatch<&game_lua_kernel::intf_set_menu_item > },
|
||||
{ "set_next_scenario", &dispatch<&game_lua_kernel::intf_set_next_scenario > },
|
||||
{ "set_side_id", &dispatch<&game_lua_kernel::intf_set_side_id > },
|
||||
{ "set_terrain", &dispatch<&game_lua_kernel::intf_set_terrain > },
|
||||
{ "set_variable", &dispatch<&game_lua_kernel::intf_set_variable > },
|
||||
{ "set_side_variable", &dispatch<&game_lua_kernel::intf_set_side_variable > },
|
||||
{ "set_village_owner", &dispatch<&game_lua_kernel::intf_set_village_owner > },
|
||||
{ "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
|
||||
{ "switch_ai", &intf_switch_ai },
|
||||
{ "synchronize_choice", &intf_synchronize_choice },
|
||||
{ "synchronize_choices", &intf_synchronize_choices },
|
||||
{ "teleport", &dispatch<&game_lua_kernel::intf_teleport > },
|
||||
|
@ -4158,15 +4144,18 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "remove_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, false > },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
/*
|
||||
lua_cpp::Reg const cpp_callbacks[] = {
|
||||
std::vector<lua_cpp::Reg> const cpp_callbacks = {
|
||||
{"add_ai_component", std::bind(intf_modify_ai, _1, "add")},
|
||||
{"delete_ai_component", std::bind(intf_modify_ai, _1, "delete")},
|
||||
{"change_ai_component", std::bind(intf_modify_ai, _1, "change")},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
*/
|
||||
lua_getglobal(L, "wesnoth");
|
||||
if (!lua_istable(L,-1)) {
|
||||
lua_newtable(L);
|
||||
}
|
||||
luaL_setfuncs(L, callbacks, 0);
|
||||
lua_cpp::set_functions(L, cpp_callbacks);
|
||||
|
||||
if(play_controller_.get_classification().campaign_type == game_classification::CAMPAIGN_TYPE::TEST) {
|
||||
static luaL_Reg const test_callbacks[] = {
|
||||
|
@ -4175,8 +4164,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
};
|
||||
luaL_setfuncs(L, test_callbacks , 0);
|
||||
}
|
||||
|
||||
//lua_cpp::set_functions(L, cpp_callbacks);
|
||||
|
||||
lua_setglobal(L, "wesnoth");
|
||||
|
||||
// Create the getside metatable.
|
||||
|
|
|
@ -141,8 +141,8 @@ class game_lua_kernel : public lua_kernel_base
|
|||
int intf_get_villages(lua_State *L);
|
||||
int intf_match_location(lua_State *L);
|
||||
int intf_match_side(lua_State *L);
|
||||
int intf_set_side_id(lua_State *L);
|
||||
int intf_modify_ai_wml(lua_State *L);
|
||||
int intf_modify_side(lua_State *L);
|
||||
int intf_get_sides(lua_State* L);
|
||||
int intf_add_tile_overlay(lua_State *L);
|
||||
int intf_remove_tile_overlay(lua_State *L);
|
||||
|
@ -170,6 +170,7 @@ class game_lua_kernel : public lua_kernel_base
|
|||
int intf_get_sound_source(lua_State *L);
|
||||
int intf_log(lua_State *L);
|
||||
int intf_toggle_fog(lua_State *L, const bool clear);
|
||||
int intf_get_fog_or_shroud(lua_State *L, bool fog);
|
||||
|
||||
//private helpers
|
||||
std::string synced_state();
|
||||
|
|
|
@ -66,8 +66,8 @@ static int impl_side_get(lua_State *L)
|
|||
return_bool_attrib("shroud", t.uses_shroud());
|
||||
return_bool_attrib("hidden", t.hidden());
|
||||
return_bool_attrib("scroll_to_leader", t.get_scroll_to_leader());
|
||||
return_string_attrib("flag", t.flag());
|
||||
return_string_attrib("flag_icon", t.flag_icon());
|
||||
return_string_attrib("flag", t.flag().empty() ? game_config::images::flag : t.flag());
|
||||
return_string_attrib("flag_icon", t.flag_icon().empty() ? game_config::images::flag_icon : t.flag_icon());
|
||||
return_tstring_attrib("user_team_name", t.user_team_name());
|
||||
return_string_attrib("team_name", t.team_name());
|
||||
return_string_attrib("faction", t.faction());
|
||||
|
@ -81,6 +81,10 @@ static int impl_side_get(lua_State *L)
|
|||
return_bool_attrib("carryover_add", t.carryover_add());
|
||||
return_bool_attrib("lost", t.lost());
|
||||
return_bool_attrib("persistent", t.persistent());
|
||||
return_bool_attrib("suppress_end_turn_confirmation", t.no_turn_confirmation());
|
||||
return_string_attrib("share_vision", t.share_vision().to_string());
|
||||
return_bool_attrib("share_maps", t.share_maps());
|
||||
return_bool_attrib("share_view", t.share_view());
|
||||
|
||||
if (strcmp(m, "recruit") == 0) {
|
||||
std::set<std::string> const &recruits = t.recruits();
|
||||
|
@ -141,6 +145,18 @@ static int impl_side_set(lua_State *L)
|
|||
modify_bool_attrib("carryover_add", t.set_carryover_add(value));
|
||||
modify_bool_attrib("lost", t.set_lost(value));
|
||||
modify_bool_attrib("persistent", t.set_persistent(value));
|
||||
modify_bool_attrib("suppress_end_turn_confirmation", t.set_no_turn_confirmation(value));
|
||||
modify_bool_attrib("shroud", t.set_shroud(value));
|
||||
modify_bool_attrib("fog", t.set_fog(value));
|
||||
modify_string_attrib("flag_icon", t.set_flag_icon(value));
|
||||
modify_string_attrib("share_vision", {
|
||||
team::SHARE_VISION v;
|
||||
if(v.parse(value)) {
|
||||
t.set_share_vision(v);
|
||||
} else {
|
||||
return luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
|
||||
}
|
||||
});
|
||||
|
||||
if (strcmp(m, "carryover_bonus") == 0) {
|
||||
t.set_carryover_bonus(luaL_checknumber(L, 3));
|
||||
|
|
|
@ -376,6 +376,19 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
|
|||
if (!match) return false;
|
||||
}
|
||||
|
||||
if (!vcfg["ability_type"].empty())
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
for (const std::string& ability : utils::split(vcfg["ability_type"])) {
|
||||
if (u.has_ability_type(ability)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) return false;
|
||||
}
|
||||
|
||||
if (!vcfg["race"].empty()) {
|
||||
std::vector<std::string> races = utils::split(vcfg["race"]);
|
||||
if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) {
|
||||
|
|
|
@ -766,129 +766,4 @@ void unit_healing(unit &healed, const std::vector<unit *> &healers, int healing,
|
|||
animator.set_all_standing();
|
||||
}
|
||||
|
||||
void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location = map_location::null_location());
|
||||
|
||||
void wml_animation(const vconfig &cfg, const map_location &default_location)
|
||||
{
|
||||
game_display &disp = *resources::screen;
|
||||
if (disp.video().update_locked() || disp.video().faked()) return;
|
||||
unit_animator animator;
|
||||
wml_animation_internal(animator, cfg, default_location);
|
||||
animator.start_animations();
|
||||
animator.wait_for_end();
|
||||
animator.set_all_standing();
|
||||
}
|
||||
|
||||
void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location)
|
||||
{
|
||||
unit_const_ptr u;
|
||||
|
||||
unit_map::const_iterator u_it = resources::gameboard->units().find(default_location);
|
||||
if (u_it.valid()) {
|
||||
u = u_it.get_shared_ptr();
|
||||
}
|
||||
|
||||
// Search for a valid unit filter,
|
||||
// and if we have one, look for the matching unit
|
||||
vconfig filter = cfg.child("filter");
|
||||
if(!filter.null()) {
|
||||
const unit_filter ufilt(filter, resources::filter_con);
|
||||
u = ufilt.first_match_on_map();
|
||||
}
|
||||
|
||||
// We have found a unit that matches the filter
|
||||
if (u && !resources::screen->fogged(u->get_location()))
|
||||
{
|
||||
attack_type *primary = nullptr;
|
||||
attack_type *secondary = nullptr;
|
||||
color_t text_color;
|
||||
unit_animation::hit_type hits= unit_animation::hit_type::INVALID;
|
||||
const_attack_itors attacks = u->attacks();
|
||||
const_attack_itors::const_iterator itor;
|
||||
attack_ptr dummy_primary;
|
||||
attack_ptr dummy_secondary;
|
||||
|
||||
// death and victory animations are handled here because usually
|
||||
// the code iterates through all the unit's attacks
|
||||
// but in these two specific cases we need to create dummy attacks
|
||||
// to fire correctly certain animations
|
||||
// this is especially evident with the Wose's death animations
|
||||
if (cfg["flag"] == "death" || cfg["flag"] == "victory") {
|
||||
filter = cfg.child("primary_attack");
|
||||
if(!filter.null()) {
|
||||
dummy_primary.reset(new attack_type(filter.get_config()));
|
||||
primary = dummy_primary.get();
|
||||
}
|
||||
filter = cfg.child("secondary_attack");
|
||||
if(!filter.null()) {
|
||||
dummy_secondary.reset(new attack_type(filter.get_config()));
|
||||
secondary = dummy_secondary.get();
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
filter = cfg.child("primary_attack");
|
||||
if(!filter.null()) {
|
||||
for(itor = attacks.begin(); itor != attacks.end(); ++itor){
|
||||
if(itor->matches_filter(filter.get_parsed_config())) {
|
||||
primary = &*itor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter = cfg.child("secondary_attack");
|
||||
if(!filter.null()) {
|
||||
for(itor = attacks.begin(); itor != attacks.end(); ++itor){
|
||||
if(itor->matches_filter(filter.get_parsed_config())) {
|
||||
secondary = &*itor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg["hits"] == "yes" || cfg["hits"] == "hit") {
|
||||
hits = unit_animation::hit_type::HIT;
|
||||
}
|
||||
if(cfg["hits"] == "no" || cfg["hits"] == "miss") {
|
||||
hits = unit_animation::hit_type::MISS;
|
||||
}
|
||||
if( cfg["hits"] == "kill" ) {
|
||||
hits = unit_animation::hit_type::KILL;
|
||||
}
|
||||
if(cfg["red"].empty() && cfg["green"].empty() && cfg["blue"].empty()) {
|
||||
text_color = color_t(0xff,0xff,0xff);
|
||||
} else {
|
||||
text_color = color_t(cfg["red"], cfg["green"], cfg["blue"]);
|
||||
}
|
||||
resources::screen->scroll_to_tile(u->get_location(), game_display::ONSCREEN, true, false);
|
||||
vconfig t_filter_data = cfg.child("facing");
|
||||
map_location secondary_loc = map_location::null_location();
|
||||
if(!t_filter_data.empty()) {
|
||||
terrain_filter t_filter(t_filter_data, resources::filter_con);
|
||||
std::set<map_location> locs;
|
||||
t_filter.get_locations(locs);
|
||||
if (!locs.empty() && u->get_location() != *locs.begin()) {
|
||||
map_location::DIRECTION dir =u->get_location().get_relative_dir(*locs.begin());
|
||||
u->set_facing(dir);
|
||||
secondary_loc = u->get_location().get_direction(dir);
|
||||
}
|
||||
}
|
||||
config::attribute_value text = u->gender() == unit_race::FEMALE ? cfg["female_text"] : cfg["male_text"];
|
||||
if(text.blank()) {
|
||||
text = cfg["text"];
|
||||
}
|
||||
animator.add_animation(&*u, cfg["flag"], u->get_location(),
|
||||
secondary_loc, cfg["value"], cfg["with_bars"].to_bool(),
|
||||
text.str(), text_color, hits, primary, secondary,
|
||||
cfg["value_second"]);
|
||||
}
|
||||
const vconfig::child_list sub_anims = cfg.get_children("animate");
|
||||
vconfig::child_list::const_iterator anim_itor;
|
||||
for(anim_itor = sub_anims.begin(); anim_itor != sub_anims.end();++anim_itor) {
|
||||
wml_animation_internal(animator, *anim_itor);
|
||||
}
|
||||
|
||||
}
|
||||
} // end unit_display namespace
|
||||
|
|
|
@ -132,17 +132,6 @@ void unit_recruited(const map_location& loc,
|
|||
void unit_healing(unit &healed, const std::vector<unit *> &healers, int healing,
|
||||
const std::string & extra_text="");
|
||||
|
||||
|
||||
/**
|
||||
* Parse a standard WML for animations and play the corresponding animation.
|
||||
* Returns once animation is played.
|
||||
*
|
||||
* This is used for the animate_unit action, but can easily be generalized if
|
||||
* other wml-described animations are needed.
|
||||
*/
|
||||
void wml_animation(const vconfig &cfg,
|
||||
const map_location& default_location=map_location::null_location());
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue