myspace / Lua /Tactical /AITactics.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
11.6 kB
MapVar("g_TacticalMap", false)
function AITacticCalcPathDistances(unit, context, disable_bias)
context.apply_bias = not disable_bias
-- make sure we have a distance to optimal - if there's no path use raw distance
AICalcPathDistances(context)
local cur_dest = GetPackedPosAndStance(unit)
for _, dest in ipairs(context.destinations) do
context.dest_dist[dest] = stance_pos_dist(context.best_dest, dest)
end
context.total_dist = stance_pos_dist(cur_dest, context.best_dest)
end
function AITacticSelectEndTurnPolicies(unit, context)
local archetype = unit:GetCurrentArchetype() -- take from "base" archetype ignoring the current (script) one
if not archetype then return end
for _, behavior in ipairs(archetype.Behaviors) do
if behavior.Fallback and #(behavior.EndTurnPolicies or empty_table) > 0 then
return behavior.EndTurnPolicies
end
end
if archetype and #(archetype.EndTurnPolicies or empty_table) > 0 then
return archetype.EndTurnPolicies
end
end
function AITacticSelectSignatureActions(unit, context)
local archetype = unit:GetCurrentArchetype() -- take from "base" archetype ignoring the current (script) one
if archetype and #(archetype.SignatureActions or empty_table) > 0 then
return archetype.SignatureActions
end
end
function tac_area_ids(areas)
if not g_TacticalMap then return empty_func end
local bit = 0
areas = areas or 0
return function()
while bit < 64 do
local mask = shift(1, bit)
bit = bit + 1
if band(areas, mask) ~= 0 then
return g_TacticalMap.individual_areas[bit] -- the list is base 1 so the +1 above matches the needs
end
end
end
end
DefineClass.TacticalMap = {
__parents = { "GameDynamicSpawnObject" },
area_to_marker = false,
area_to_positions = false,
ppos_to_individual_area = false,
id_to_individual_area = false, -- [id] = flag
individual_areas = false,
-- area assignment: [unit] = area(s)
assigned_individual_areas = false,
-- area assignment priority: [unit] = { [priority] = area(s) }
assigned_area_priority = false,
PriorityHigh = 1,
PriorityMedium = 2,
PriorityLow = 3,
}
function TacticalMap:Init()
self.area_to_marker = {}
self.area_to_positions = {}
self.ppos_to_individual_area = {}
self.individual_areas = {}
self.id_to_individual_area = {}
self.assigned_individual_areas = {}
self.assigned_area_priority = {}
self:SetPos(point(terrain.GetMapSize()) / 2)
local markers = MapGetMarkers()
local bit = 0
local seen = {}
for _, marker in ipairs(markers) do
if marker.FightAreaId ~= "" then
if seen[marker.FightAreaId] then
StoreErrorSource(marker, string.format("Marker Fight Area Id '%s' already in use, ignored", marker.FightAreaId))
else
seen[marker.FightAreaId] = true
if bit >= 64 then
StoreErrorSource(marker, string.format("Trying to register more than 64 Individual Fight Areas, Fight Area '%s' ignored", marker.FightAreaId))
else
local flag = shift(1, bit)
self.id_to_individual_area[marker.FightAreaId] = flag
self.individual_areas[bit + 1] = marker.FightAreaId
self:RegisterIndividualArea(marker, marker.FightAreaId, marker.FightArea3d)
bit = bit + 1
end
end
end
end
end
function TacticalMap:AssignUnit(unit, areas, reset, priority)
if not areas then
self.id_to_individual_area[unit] = nil
self.assigned_area_priority[unit] = nil
return
end
if type(areas) == "table" then
for _, area in ipairs(areas) do
self:AssignUnit(unit, area, reset, priority)
end
return
end
local flag = self.id_to_individual_area[areas] or 0
self.assigned_individual_areas[unit] = bor(reset and 0 or self.assigned_individual_areas[unit] or 0, flag)
if type(priority) == "number" then
priority = Clamp(priority, self.PriorityHigh, self.PriorityLow)
self.assigned_area_priority[unit] = self.assigned_area_priority[unit] or {}
self.assigned_area_priority[unit][priority] = bor(reset and 0 or self.assigned_area_priority[unit][priority] or 0, flag)
end
end
function TacticalMap:GetAssignedAreas(unit, priority)
local areas = self.assigned_area_priority[unit]
if areas and priority then
areas = areas[priority]
end
return areas
end
function TacticalMap:ShowAssignedArea(unit, time)
time = time or 2000
CreateRealTimeThread(function()
local areas = self.assigned_individual_areas[unit]
for id in tac_area_ids(areas) do
local marker = self.area_to_marker[id]
marker.ground_visuals = true
marker:ShowArea()
end
Sleep(time)
for id in tac_area_ids(areas) do
local marker = self.area_to_marker[id]
marker.ground_visuals = false
marker:HideArea()
end
end)
end
function TacticalMap:GetUnitAreas(unit)
local x, y, z = unit:GetPosXYZ()
local pos = point_pack(x, y, z)
return self.ppos_to_individual_area[pos] or 0
end
function TacticalMap:GetUnitPrimaryArea(unit)
local x, y, z = unit:GetPosXYZ()
local pos = point_pack(x, y, z)
local areas = self.ppos_to_individual_area[pos] or 0
local area, mindist
for id in tac_area_ids(areas) do
local marker = self.area_to_marker[id]
local dist = unit:GetDist(marker)
if not area or dist < mindist then
area, mindist = id, dist
end
end
return area
end
function TacticalMap:GetUnitsInArea(area_id)
local units = {}
for _, unit in ipairs(g_Units) do
local x, y, z = unit:GetPosXYZ()
local pos = point_pack(x, y, z)
local areas = self.ppos_to_individual_area[pos] or 0
for id in tac_area_ids(areas) do
if id == area_id then
units[#units + 1] = unit
break
end
end
end
return units
end
function TacticalMap:GetNearestArea(unit, ...)
local n = select("#", ...)
local area, mindist
for i = 1, n do
local area_id = select(i, ...)
local marker = self.area_to_marker[area_id]
if marker then
local dist = unit:GetDist(marker)
if not area or dist < mindist then
area, mindist = area_id, dist
end
end
end
return area
end
function TacticalMap:CountUnitsInAreas()
local player_units_in_area = {}
local enemy_units_in_area = {}
for _, unit in ipairs(g_Units) do
local area = self:GetUnitPrimaryArea(unit)
if area then
if unit.team.player_team then
player_units_in_area[area] = (player_units_in_area[area] or 0) + 1
elseif unit.team.player_enemy then
enemy_units_in_area[area] = (enemy_units_in_area[area] or 0) + 1
end
end
end
return player_units_in_area, enemy_units_in_area
end
function TacticalMap:FindOptimalLocationInAssignedArea(unit, context)
local positions
local assigned_area = self.assigned_individual_areas[unit] or 0
local cur_pos = point_pack(unit:GetPos())
if assigned_area ~= 0 then
local area = self.ppos_to_individual_area[cur_pos]
if band(area, assigned_area) ~= 0 then
-- already in one of the areas
context.best_dest = GetPackedPosAndStance(unit)
return true
end
positions = {}
if self.assigned_area_priority[unit] then
for p = self.PriorityHigh, self.PriorityLow do
for id in tac_area_ids(self.assigned_area_priority[unit][p]) do
local flag = self.id_to_individual_area[id]
if band(assigned_area, flag) ~= 0 then
positions = table.union(positions, self.area_to_positions[id])
end
end
if #positions > 0 then break end
end
end
if #positions == 0 then
for id, flag in pairs(self.id_to_individual_area) do
if band(assigned_area, flag) ~= 0 then
positions = table.union(positions, self.area_to_positions[id])
end
end
end
end
positions = positions or empty_table
if #positions > 0 then
local goto_pos = table.interaction_rand(positions, "Behavior")
local x, y, z = point_unpack(goto_pos)
context.best_dest = stance_pos_pack(x, y, z, StancesList.Standing)
context.optimal_dest_in_assigned_area = true
return true
end
end
function TacticalMap:EnumDestsInAssignedArea(unit, context)
AIFindDestinations(unit, context)
local marker_area = self.assigned_individual_areas[unit] or 0
if marker_area ~= 0 then
local function dest_in_marker_filter(idx, dest, area)
local x, y, z = stance_pos_unpack(dest)
local ppos = point_pack(x, y, z)
return band(self.ppos_to_individual_area[ppos] or 0, area) ~= 0
end
if self.assigned_area_priority[unit] then
for p = self.PriorityHigh, self.PriorityLow do
local marker_areas = self.assigned_area_priority[unit][p] or 0
if marker_areas ~= 0 then
local dests = table.ifilter(context.destinations, dest_in_marker_filter, marker_areas)
local all_dests = table.ifilter(context.all_destinations, dest_in_marker_filter, marker_areas)
if #dests > 0 and #all_dests > 0 then
context.destinations = dests
context.all_destinations = all_dests
return true
end
end
end
end
local dests = table.ifilter(context.destinations, dest_in_marker_filter, marker_area)
local all_dests = table.ifilter(context.all_destinations, dest_in_marker_filter, marker_area)
if #dests > 0 and #all_dests > 0 then
context.destinations = dests
context.all_destinations = all_dests
return true
else
context.assigned_area_unreachable = true
end
end
return true
end
function TacticalMap:GetDynamicData(data)
data.assigned_individual_areas = {}
for unit, area in pairs(self.assigned_individual_areas) do
if unit then
data.assigned_individual_areas[unit:GetHandle()] = area
end
end
data.assigned_area_priority = {}
for unit, priorities in pairs(self.assigned_area_priority) do
if unit then
local handle = unit:GetHandle()
for p = self.PriorityHigh, self.PriorityLow do
local areas = priorities[p] or 0
if areas ~= 0 then
data.assigned_area_priority[handle] = data.assigned_area_priority[handle] or {}
data.assigned_area_priority[handle][p] = areas
end
end
end
end
end
function TacticalMap:SetDynamicData(data)
self.assigned_individual_areas = {}
for handle, area in pairs(data.assigned_individual_areas) do
local unit = HandleToObject[handle] or false
self.assigned_individual_areas[unit] = area
end
self.assigned_area_priority = {}
for handle, priorities in pairs(data.assigned_area_priority) do
local unit = HandleToObject[handle] or false
for p = self.PriorityHigh, self.PriorityLow do
local areas = priorities[p] or 0
if areas ~= 0 then
self.assigned_area_priority[unit] = self.assigned_area_priority[unit] or {}
self.assigned_area_priority[unit][p] = areas
end
end
end
end
function TacticalMap:RegisterIndividualArea(marker, area, mode_3d)
local positions = marker:GetAreaPositions(true)
local area_flag = self.id_to_individual_area[area]
self.area_to_marker[area] = marker
self.area_to_positions[area] = positions
for _, ppos in ipairs(positions) do
local x, y, z = SnapToPassSlabXYZ(point_unpack(ppos))
if x then
ppos = point_pack(x, y, z)
end
self.ppos_to_individual_area[ppos] = bor(self.ppos_to_individual_area[ppos] or 0, area_flag)
end
if mode_3d then
local bbox = marker:GetBBox()
local min, max = bbox:min():SetZ(0), bbox:max():SetZ(1000*guim)
bbox = Extend(Extend(bbox, min), max)
ForEachPassSlab(bbox, function(x, y, z, positions, ppos_to_individual_area, area_flag)
local ppos = point_pack(x, y, z)
ppos_to_individual_area[ppos] = bor(ppos_to_individual_area[ppos] or 0, area_flag)
end, self.area_to_positions[area], self.ppos_to_individual_area, area_flag)
end
end
local function InitTacticalMap()
g_TacticalMap = g_TacticalMap or TacticalMap:new()
end
function OnMsg.EnterSector(game_start, load_game)
if not load_game then
InitTacticalMap()
end
end
function OnMsg.CombatStart(dynamic_data)
if not dynamic_data then
InitTacticalMap()
end
end