Merge branch 'master' into sdl2
This commit is contained in:
commit
a4b957c347
8 changed files with 129 additions and 67 deletions
|
@ -1,5 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local LS = wesnoth.require "lua/location_set.lua"
|
||||
|
||||
-- Functions to perform fast evaluation of attacks and attack combinations.
|
||||
-- The emphasis with all of this is on speed, not elegance.
|
||||
|
@ -14,6 +15,37 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|||
|
||||
local ca_fast_attack_utils = {}
|
||||
|
||||
function ca_fast_attack_utils.get_avoid_map(cfg)
|
||||
-- Get map of locations to be avoided.
|
||||
-- Use [micro_ai][avoid] tag with priority over [ai][avoid].
|
||||
-- If neither is given, return an empty location set.
|
||||
-- Note that ai.get_avoid() cannot be used as it always returns an array,
|
||||
-- even when the aspect is not set, and an empty array could also mean that
|
||||
-- no hexes match the filter
|
||||
|
||||
local avoid_tag
|
||||
|
||||
if cfg.avoid then
|
||||
avoid_tag = cfg.avoid
|
||||
else
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'avoid') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
avoid_tag = H.get_child(facet, 'value')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if avoid_tag then
|
||||
return LS.of_pairs(wesnoth.get_locations(avoid_tag))
|
||||
else
|
||||
return LS.create()
|
||||
end
|
||||
end
|
||||
|
||||
function ca_fast_attack_utils.gamedata_setup()
|
||||
-- Keep game data in a table for faster access.
|
||||
-- This is currently re-done on every move. Could be optimized by only
|
||||
|
|
|
@ -9,10 +9,25 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
self.data.move_cache = { turn = wesnoth.current.turn }
|
||||
self.data.gamedata = FAU.gamedata_setup()
|
||||
|
||||
local filter_own = cfg.filter
|
||||
local filter_enemy = cfg.filter_second
|
||||
if (not filter_own) and (not filter_enemy) then
|
||||
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
|
||||
for aspect in H.child_range(ai_tag, 'aspect') do
|
||||
if (aspect.id == 'attacks') then
|
||||
local facet = H.get_child(aspect, 'facet')
|
||||
if facet then
|
||||
filter_own = H.get_child(facet, 'filter_own')
|
||||
filter_enemy = H.get_child(facet, 'filter_enemy')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not self.data.fast_combat_units) or (not self.data.fast_combat_units[1]) then
|
||||
self.data.fast_combat_units = wesnoth.get_units {
|
||||
side = wesnoth.current.side,
|
||||
{ "and", cfg.filter }
|
||||
{ "and", filter_own }
|
||||
}
|
||||
if (not self.data.fast_combat_units[1]) then return 0 end
|
||||
|
||||
|
@ -26,11 +41,11 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
|
||||
local excluded_enemies_map = LS.create()
|
||||
|
||||
-- Exclude enemies not matching [filter_second]
|
||||
if (cfg.filter_second) then
|
||||
-- Exclude enemies not matching [filter_enemy]
|
||||
if filter_enemy then
|
||||
local excluded_enemies = wesnoth.get_units {
|
||||
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
|
||||
{ "not", cfg.filter_second }
|
||||
{ "not", filter_enemy }
|
||||
}
|
||||
|
||||
for _,e in ipairs(excluded_enemies) do
|
||||
|
@ -54,6 +69,9 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
if (aggression > 1) then aggression = 1 end
|
||||
local own_value_weight = 1. - aggression
|
||||
|
||||
-- Get the locations to be avoided
|
||||
local avoid_map = FAU.get_avoid_map(cfg)
|
||||
|
||||
for i = #self.data.fast_combat_units,1,-1 do
|
||||
local unit = self.data.fast_combat_units[i]
|
||||
local unit_info = FAU.get_unit_info(unit, self.data.gamedata)
|
||||
|
@ -65,7 +83,9 @@ function ca_fast_combat:evaluation(ai, cfg, self)
|
|||
if (#attacks > 0) then
|
||||
local max_rating, best_target, best_dst = -9e99
|
||||
for _,attack in ipairs(attacks) do
|
||||
if (not excluded_enemies_map:get(attack.target.x, attack.target.y)) then
|
||||
if (not excluded_enemies_map:get(attack.target.x, attack.target.y))
|
||||
and (not avoid_map:get(attack.dst.x, attack.dst.y))
|
||||
then
|
||||
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
|
||||
local target_info = FAU.get_unit_info(target, self.data.gamedata)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
local FAU = wesnoth.require "ai/micro_ais/cas/ca_fast_attack_utils.lua"
|
||||
|
||||
local ca_fast_move = {}
|
||||
|
||||
|
@ -14,6 +15,9 @@ function ca_fast_move:execution(ai, cfg, self)
|
|||
local move_cost_factor = cfg.move_cost_factor or 2.0
|
||||
if (move_cost_factor < 1.1) then move_cost_factor = 1.1 end
|
||||
|
||||
-- Get the locations to be avoided
|
||||
local avoid_map = FAU.get_avoid_map(cfg)
|
||||
|
||||
local all_units_MP = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
|
||||
local units = {}
|
||||
for _,unit in ipairs(all_units_MP) do
|
||||
|
@ -29,16 +33,18 @@ function ca_fast_move:execution(ai, cfg, self)
|
|||
if leader and (village_value > 0) then
|
||||
local villages = wesnoth.get_villages()
|
||||
|
||||
-- Eliminate villages owned by a side that is not an enemy
|
||||
-- Eliminate villages in avoid_map and those owned by an allied side
|
||||
-- Also remove unowned villages if the AI has no leader
|
||||
for i = #villages,1,-1 do
|
||||
local owner = wesnoth.get_village_owner(villages[i][1], villages[i][2])
|
||||
if owner and (not wesnoth.is_enemy(owner, wesnoth.current.side)) then
|
||||
table.remove(villages, i)
|
||||
end
|
||||
|
||||
if (not leader) and (not owner) then
|
||||
if avoid_map:get(villages[i][1], villages[i][2]) then
|
||||
table.remove(villages, i)
|
||||
else
|
||||
local owner = wesnoth.get_village_owner(villages[i][1], villages[i][2])
|
||||
if owner and (not wesnoth.is_enemy(owner, wesnoth.current.side)) then
|
||||
table.remove(villages, i)
|
||||
elseif (not leader) and (not owner) then
|
||||
table.remove(villages, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -110,8 +116,10 @@ function ca_fast_move:execution(ai, cfg, self)
|
|||
end
|
||||
|
||||
for i_el,enemy_leader in ipairs(enemy_leaders) do
|
||||
local goal = { x = enemy_leader.x, y = enemy_leader.y }
|
||||
table.insert(goals, goal)
|
||||
if (not avoid_map:get(enemy_leader.x, enemy_leader.y)) then
|
||||
local goal = { x = enemy_leader.x, y = enemy_leader.y }
|
||||
table.insert(goals, goal)
|
||||
end
|
||||
end
|
||||
|
||||
-- Putting information about all the units into the goals
|
||||
|
@ -196,35 +204,37 @@ function ca_fast_move:execution(ai, cfg, self)
|
|||
local pre_ratings = {}
|
||||
local max_rating, best_hex = -9e99
|
||||
for _,loc in ipairs(reach) do
|
||||
local rating = - H.distance_between(loc[1], loc[2], short_goal[1], short_goal[2])
|
||||
local other_rating = - H.distance_between(loc[1], loc[2], goal.x, goal.y) / 10.
|
||||
rating = rating + other_rating
|
||||
if (not avoid_map:get(loc[1], loc[2])) then
|
||||
local rating = - H.distance_between(loc[1], loc[2], short_goal[1], short_goal[2])
|
||||
local other_rating = - H.distance_between(loc[1], loc[2], goal.x, goal.y) / 10.
|
||||
rating = rating + other_rating
|
||||
|
||||
local unit_in_way
|
||||
if (rating > max_rating) then
|
||||
unit_in_way = wesnoth.get_unit(loc[1], loc[2])
|
||||
if (unit_in_way == unit) then unit_in_way = nil end
|
||||
local unit_in_way
|
||||
if (rating > max_rating) then
|
||||
unit_in_way = wesnoth.get_unit(loc[1], loc[2])
|
||||
if (unit_in_way == unit) then unit_in_way = nil end
|
||||
|
||||
if unit_in_way and (unit_in_way.side == unit.side) then
|
||||
local reach = AH.get_reachable_unocc(unit_in_way)
|
||||
if (reach:size() > 1) then
|
||||
unit_in_way = nil
|
||||
rating = rating - 0.01
|
||||
other_rating = other_rating - 0.01
|
||||
if unit_in_way and (unit_in_way.side == unit.side) then
|
||||
local reach = AH.get_reachable_unocc(unit_in_way)
|
||||
if (reach:size() > 1) then
|
||||
unit_in_way = nil
|
||||
rating = rating - 0.01
|
||||
other_rating = other_rating - 0.01
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not unit_in_way) then
|
||||
if cfg.dungeon_mode then
|
||||
table.insert(pre_ratings, {
|
||||
rating = rating,
|
||||
other_rating = other_rating,
|
||||
x = loc[1], y = loc[2]
|
||||
})
|
||||
else
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_hex = rating, { loc[1], loc[2] }
|
||||
if (not unit_in_way) then
|
||||
if cfg.dungeon_mode then
|
||||
table.insert(pre_ratings, {
|
||||
rating = rating,
|
||||
other_rating = other_rating,
|
||||
x = loc[1], y = loc[2]
|
||||
})
|
||||
else
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_hex = rating, { loc[1], loc[2] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -460,7 +460,7 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
|
||||
elseif (cfg.ai_type == 'fast_ai') then
|
||||
optional_keys = {
|
||||
"attack_hidden_enemies", "dungeon_mode", "filter", "filter_second",
|
||||
"attack_hidden_enemies", "avoid", "dungeon_mode", "filter", "filter_second",
|
||||
"include_occupied_attack_hexes", "leader_weight", "move_cost_factor",
|
||||
"weak_units_first", "skip_combat_ca", "skip_move_ca"
|
||||
}
|
||||
|
|
|
@ -27,18 +27,18 @@ configure_engine::configure_engine(saved_game& state) :
|
|||
}
|
||||
|
||||
set_use_map_settings(use_map_settings_default());
|
||||
if(state_.classification().get_tagname() == "scenario") {
|
||||
BOOST_FOREACH(const config& scenario,
|
||||
game_config_manager::get()->game_config().child_range(state_.classification().get_tagname())) {
|
||||
|
||||
BOOST_FOREACH(const config& scenario,
|
||||
game_config_manager::get()->game_config().child_range(state_.classification().get_tagname())) {
|
||||
if (scenario["allow_new_game"].to_bool(true) || game_config::debug) {
|
||||
|
||||
if (!scenario["campaign_id"].empty() &&
|
||||
(scenario["allow_new_game"].to_bool(true) || game_config::debug)) {
|
||||
const std::string& title = (!scenario["new_game_title"].empty()) ?
|
||||
scenario["new_game_title"] : scenario["name"];
|
||||
|
||||
const std::string& title = (!scenario["new_game_title"].empty()) ?
|
||||
scenario["new_game_title"] : scenario["name"];
|
||||
|
||||
entry_points_.push_back(&scenario);
|
||||
entry_point_titles_.push_back(title);
|
||||
entry_points_.push_back(&scenario);
|
||||
entry_point_titles_.push_back(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ static void add_multiplayer_classification(config& multiplayer, saved_game& stat
|
|||
config initial_level_config(saved_game& state)
|
||||
{
|
||||
const mp_game_settings& params = state.mp_settings();
|
||||
state.set_defaults();
|
||||
//Also impliers state.expand_scenario()
|
||||
//We need to call this before expand_mp_events/options oterwise they might be overwritten
|
||||
state.expand_random_scenario();
|
||||
|
|
|
@ -878,13 +878,6 @@ void terrain_builder::parse_config(const config &cfg, bool local)
|
|||
if (const config::attribute_value *v = tc.get("y")) {
|
||||
loc.y = *v;
|
||||
}
|
||||
if (const config::attribute_value *v = tc.get("loc")) {
|
||||
std::vector<std::string> sloc = utils::split(*v);
|
||||
if(sloc.size() == 2) {
|
||||
loc.x = atoi(sloc[0].c_str());
|
||||
loc.y = atoi(sloc[1].c_str());
|
||||
}
|
||||
}
|
||||
if(loc.valid()) {
|
||||
add_constraints(pbr.constraints, loc, tc, br);
|
||||
}
|
||||
|
|
|
@ -1005,11 +1005,15 @@ namespace { // Helpers for set_config()
|
|||
const boost::regex fai_identifier("[a-zA-Z_]+");
|
||||
|
||||
template<typename MoveT>
|
||||
void patch_movetype(MoveT& mt, const std::string& new_key, const std::string& formula_str, int default_val) {
|
||||
void patch_movetype(MoveT& mt, const std::string& new_key, const std::string& formula_str, int default_val, bool replace) {
|
||||
config temp_cfg, original_cfg;
|
||||
mt.write(original_cfg);
|
||||
if (!replace && !original_cfg[new_key].blank()) {
|
||||
// Don't replace if the key already exists in the config (even if empty).
|
||||
return;
|
||||
}
|
||||
gui2::tformula<int> formula(formula_str);
|
||||
game_logic::map_formula_callable original;
|
||||
mt.write(original_cfg);
|
||||
boost::sregex_iterator m(formula_str.begin(), formula_str.end(), fai_identifier);
|
||||
BOOST_FOREACH(const boost::sregex_iterator::value_type& p, std::make_pair(m, boost::sregex_iterator())) {
|
||||
const std::string var_name = p.str();
|
||||
|
@ -1056,14 +1060,15 @@ void unit_type_data::set_config(config &cfg)
|
|||
if (mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
|
||||
continue;
|
||||
}
|
||||
patch_movetype(movement_types_[mt].get_resistances(), dmg_type, attr.second, 100);
|
||||
patch_movetype(movement_types_[mt].get_resistances(), dmg_type, attr.second, 100, true);
|
||||
}
|
||||
if (r.has_attribute("default")) {
|
||||
BOOST_FOREACH(movement_type_map::value_type &mt, movement_types_) {
|
||||
// Don't apply a default if a value is explicitly specified.
|
||||
if (r.has_attribute(mt.first)) {
|
||||
continue;
|
||||
}
|
||||
patch_movetype(mt.second.get_resistances(), dmg_type, r["default"], 100);
|
||||
patch_movetype(mt.second.get_resistances(), dmg_type, r["default"], 100, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1085,28 +1090,29 @@ void unit_type_data::set_config(config &cfg)
|
|||
continue;
|
||||
}
|
||||
if (tag == "defense") {
|
||||
patch_movetype(movement_types_[mt].get_defense(), ter_type, attr.second, 100);
|
||||
patch_movetype(movement_types_[mt].get_defense(), ter_type, attr.second, 100, true);
|
||||
} else if (tag == "vision") {
|
||||
patch_movetype(movement_types_[mt].get_vision(), ter_type, attr.second, 99);
|
||||
patch_movetype(movement_types_[mt].get_vision(), ter_type, attr.second, 99, true);
|
||||
} else if (tag == "movement") {
|
||||
patch_movetype(movement_types_[mt].get_movement(), ter_type, attr.second, 99);
|
||||
patch_movetype(movement_types_[mt].get_movement(), ter_type, attr.second, 99, true);
|
||||
} else if (tag == "jamming") {
|
||||
patch_movetype(movement_types_[mt].get_jamming(), ter_type, attr.second, 99);
|
||||
patch_movetype(movement_types_[mt].get_jamming(), ter_type, attr.second, 99, true);
|
||||
}
|
||||
}
|
||||
if (info.has_attribute("default")) {
|
||||
BOOST_FOREACH(movement_type_map::value_type &mt, movement_types_) {
|
||||
// Don't apply a default if a value is explicitly specified.
|
||||
if (info.has_attribute(mt.first)) {
|
||||
continue;
|
||||
}
|
||||
if (tag == "defense") {
|
||||
patch_movetype(mt.second.get_defense(), ter_type, info["default"], 100);
|
||||
patch_movetype(mt.second.get_defense(), ter_type, info["default"], 100, false);
|
||||
} else if (tag == "vision") {
|
||||
patch_movetype(mt.second.get_vision(), ter_type, info["default"], 99);
|
||||
patch_movetype(mt.second.get_vision(), ter_type, info["default"], 99, false);
|
||||
} else if (tag == "movement") {
|
||||
patch_movetype(mt.second.get_movement(), ter_type, info["default"], 99);
|
||||
patch_movetype(mt.second.get_movement(), ter_type, info["default"], 99, false);
|
||||
} else if (tag == "jamming") {
|
||||
patch_movetype(mt.second.get_jamming(), ter_type, info["default"], 99);
|
||||
patch_movetype(mt.second.get_jamming(), ter_type, info["default"], 99, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue