/
ca_hang_out.lua
140 lines (121 loc) · 5.29 KB
/
ca_hang_out.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local LS = wesnoth.require "lua/location_set.lua"
local ca_hang_out = {}
function ca_hang_out:evaluation(ai, cfg, self)
cfg = cfg or {}
-- Return 0 if the mobilize condition has previously been met
for mobilze in H.child_range(self.data, "hangout_mobilize_units") do
if (mobilze.id == cfg.ca_id) then
return 0
end
end
-- Otherwise check if any of the mobilize conditions are now met
if (cfg.mobilize_condition and wesnoth.eval_conditional(cfg.mobilize_condition))
or (cfg.mobilize_on_gold_less_than and (wesnoth.sides[wesnoth.current.side].gold < cfg.mobilize_on_gold_less_than))
then
table.insert(self.data, { "hangout_mobilize_units" , { id = cfg.ca_id } } )
-- Need to unmark all units also
local units = wesnoth.get_units { side = wesnoth.current.side, { "and", cfg.filter } }
for i,u in ipairs(units) do
u.variables.mai_hangout_moved = nil
end
return 0
end
local units = wesnoth.get_units { side = wesnoth.current.side,
{ "and", cfg.filter }, formula = '$this_unit.moves > 0'
}
if units[1] then
return cfg.ca_score
end
return 0
end
function ca_hang_out:execution(ai, cfg, self)
cfg = cfg or {}
local units = wesnoth.get_units { side = wesnoth.current.side,
{ "and", cfg.filter }, formula = '$this_unit.moves > 0'
}
--print('#unit', #units)
-- Get the locations close to which the units should hang out
-- cfg.filter_location defaults to the location of the side leader(s)
local filter_location = cfg.filter_location or {
{ "filter", { side = wesnoth.current.side, canrecruit = "yes" } }
}
local width, height = wesnoth.get_map_size()
local locs = wesnoth.get_locations {
x = '1-' .. width,
y = '1-' .. height,
{ "and", filter_location }
}
--print('#locs', #locs)
-- Get map of locations to be avoided.
-- Use [micro_ai][avoid] tag with priority over [ai][avoid].
-- If neither is given, default to all castle terrain.
-- 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')
else
avoid_tag = { terrain = 'C*,C*^*,*^C*' }
end
end
end
end
local avoid_map = LS.of_pairs(wesnoth.get_locations(avoid_tag))
local best_hex, best_unit, max_rating = {}, {}, -9e99
for i,u in ipairs(units) do
-- Only consider units that have not been marked yet
if (not u.variables.mai_hangout_moved) then
local best_hex_unit, max_rating_unit = {}, -9e99
-- Check out all unoccupied hexes the unit can reach
local reach_map = AH.get_reachable_unocc(u)
reach_map:iter( function(x, y, v)
if (not avoid_map:get(x, y)) then
for k,l in ipairs(locs) do
-- Main rating is the distance from any of the goal hexes
local rating = -H.distance_between(x, y, l[1], l[2])
-- Fastest unit moves first
rating = rating + u.max_moves / 100.
-- Minor penalty for distance from current position of unit
-- so that there's not too much shuffling around
local rating = rating - H.distance_between(x, y, u.x, u.y) / 1000.
if (rating > max_rating_unit) then
max_rating_unit = rating
best_hex_unit = {x, y}
end
end
end
end)
-- Only consider a unit if the best hex found for it is not its current location
if (best_hex_unit[1] ~= u.x) or (best_hex_unit[2] ~= u.y) then
if (max_rating_unit > max_rating) then
max_rating = max_rating_unit
best_hex, best_unit = best_hex_unit, u
end
end
end
end
--print(best_unit.id, best_unit.x, best_unit.y, best_hex[1], best_hex[2], max_rating)
-- If no valid locations/units were found or all units are in their
-- respective best locations already, we take moves away from all units
if (max_rating == -9e99) then
for i,u in ipairs(units) do
AH.checked_stopunit_moves(ai, u)
-- Also remove the markers
if u and u.valid then u.variables.mai_hangout_moved = nil end
end
else
-- Otherwise move unit and mark as having been used
AH.checked_move(ai, best_unit, best_hex[1], best_hex[2])
if best_unit and best_unit.valid then best_unit.variables.mai_hangout_moved = true end
end
end
return ca_hang_out