diff --git a/data/campaigns/Two_Brothers/lua/muff_toras_move.lua b/data/campaigns/Two_Brothers/lua/muff_toras_move.lua new file mode 100644 index 000000000000..3339555283bd --- /dev/null +++ b/data/campaigns/Two_Brothers/lua/muff_toras_move.lua @@ -0,0 +1,113 @@ +local M = wesnoth.map +local AH = wesnoth.require "ai/lua/ai_helper.lua" +local BC = wesnoth.require "ai/lua/battle_calcs.lua" + +local muff_toras_move = {} + +function muff_toras_move:evaluation() + local muff_toras = wesnoth.get_units { id = 'Muff Toras' }[1] + + if muff_toras and (muff_toras.moves > 0) then + return 15000 + end + + return 0 +end + +function muff_toras_move:execution() + local muff_toras = wesnoth.get_units { id = 'Muff Toras' }[1] + local units = wesnoth.get_units { side = 3, { 'not', { id = 'Muff Toras' } } } + local enemies = AH.get_attackable_enemies() + local enemy_attack_map = BC.get_attack_map(enemies) + + local go_to = AH.find_best_move(muff_toras, function(x, y) + -- Note: we use no_random = false, which adds contribution of up to 0.01. + -- This means all differences between deterministic ratings need to be + -- larger than that + + local rating = -10000 -- This is the base rating if no other units are left + + -- Main rating is distance from the closest own unit + local min_dist + for _,unit in ipairs(units) do + local dist = M.distance_between(x, y, unit.x, unit.y) + if (not min_dist) or (dist < min_dist) then + min_dist = dist + end + end + if min_dist then rating = -100 * min_dist end + + -- Enemy threat is next most important. We could put a scaling factor + -- in front of this, but using 1 seems to work pretty well + local enemy_hp = enemy_attack_map.hitpoints:get(x,y) + if enemy_hp then + rating = rating - enemy_hp + end + + -- All else being equal, go with good terrain + local hit_chance = muff_toras:defense(wesnoth.get_terrain(x, y)) + rating = rating - hit_chance + + -- Finally, we want to run away from enemies if there are no other factors. + -- This mostly kicks in after the other AI units have been killed. + local closest_enemy_dist + for _,enemy in ipairs(enemies) do + local dist = M.distance_between(x, y, enemy.x, enemy.y) + if (not closest_enemy_dist) or (dist < closest_enemy_dist) then + closest_enemy_dist = dist + end + end + rating = rating + closest_enemy_dist + + return rating + end) + + if ((go_to[1] ~= muff_toras.x) or (go_to[2] ~= muff_toras.y)) then + AH.robust_move_and_attack(ai, muff_toras, go_to) + else + AH.checked_stopunit_moves(ai, muff_toras) + end + + -- Test whether an attack without retaliation or with little damage is possible + if (muff_toras.attacks_left <= 0) or (#muff_toras.attacks == 0) then + return + end + + local targets = AH.get_attackable_enemies { { "filter_adjacent", { id = muff_toras.id } } } + + local max_rating, best_target, best_weapon = -9e99 + for _,target in ipairs(targets) do + for n_weapon,weapon in ipairs(muff_toras.attacks) do + local att_stats, def_stats = wesnoth.simulate_combat(muff_toras, n_weapon, target) + + local rating = -9e99 + -- This is an acceptable attack if: + -- 1. There is no counter attack + -- 2. Probability of death is >=67% for enemy, 0% for attacker (default values) + + local enemy_death_chance = 0.67 + local muff_toras_death_chance = 0 + + if (att_stats.hp_chance[muff_toras.hitpoints] == 1) + or (def_stats.hp_chance[0] >= enemy_death_chance) and (att_stats.hp_chance[0] <= muff_toras_death_chance) + then + rating = target.max_hitpoints + def_stats.hp_chance[0] * 100 + att_stats.average_hp - def_stats.average_hp + end + + if (rating > max_rating) then + max_rating, best_target, best_weapon = rating, target, n_weapon + end + end + end + + if best_target then + AH.checked_attack(ai, muff_toras, best_target, best_weapon) + end + if (not muff_toras) or (not muff_toras.valid) then + return + end + + AH.checked_stopunit_attacks(ai, muff_toras) +end + +return muff_toras_move diff --git a/data/campaigns/Two_Brothers/scenarios/02_The_Chase.cfg b/data/campaigns/Two_Brothers/scenarios/02_The_Chase.cfg index 9348469b52ad..12e0078bebc6 100644 --- a/data/campaigns/Two_Brothers/scenarios/02_The_Chase.cfg +++ b/data/campaigns/Two_Brothers/scenarios/02_The_Chase.cfg @@ -176,7 +176,7 @@ Besides... I want my brother back." x,y=9,4 [ai] - leader_ignores_keep=yes + passive_leader=yes village_value=0 [modify_ai] @@ -426,6 +426,16 @@ Besides... I want my brother back." hidden=no [/modify_side] + {MODIFY_AI_ADD_CANDIDATE_ACTION 3 main_loop ( + [candidate_action] + engine=lua + name=muff_toras_move + id=muff_toras_move + max_score=15000 + location="campaigns/Two_Brothers/lua/muff_toras_move.lua" + [/candidate_action] + )} + [remove_shroud] side=1 x=0-20