File size: 5,575 Bytes
b6a38d7 |
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 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
DefineClass.CombatPath = {
__parents = { "InitDone" },
unit = false,
start_pos = false,
stance = false,
ap = 0,
move_modifier = 0,
restrict_area = false,
destinations = empty_table, -- map: packed_pos -> true (possibly occupied points are not present here)
paths_ap = empty_table, -- map: packed_pos -> cost ap
paths_prev_pos = empty_table, -- map: pos -> previous pos (start pos -> false)
closest_free_pos = false, -- closest not occupied pos
destination_stances = false, -- for merged combat path it holds the stance to arrive at that destination
}
function CombatPath:RebuildPaths(unit, ap, pos, stance, ignore_occupied, move_through_occupied, action_id)
stance = stance or unit and unit.stance or "Standing"
ap = ap or unit and unit.ActionPoints or 0
self.unit = unit
self.stance = stance
self.ap = ap
self.start_pos = pos or unit and unit:GetPos()
if ap < 0 then
self.destinations = nil
self.paths_ap = nil
self.paths_prev_pos = nil
self.closest_free_pos = nil
return
end
local consts = Presets.ConstDef["Action Point Costs"]
local side = unit and unit.team and unit.team.side
local player_controlled = (side == "player1" or side == "player2")
local stance_modifier = 0
if stance == "Crouch" then
stance_modifier = consts["CrouchModifier"].value
elseif stance == "Prone" then
stance_modifier = consts["ProneModifier"].value
end
local tunnel_mask
if side and side ~= "neutral" and unit.body_type == "Human" then
if stance == "Prone" then
tunnel_mask = player_controlled and const.TunnelMaskPlayerProne or const.TunnelMaskAIProne
else
tunnel_mask = player_controlled and const.TunnelMaskPlayerStanding or const.TunnelMaskAIStanding
end
end
self.move_modifier = unit and unit:GetMoveModifier(stance, action_id) or 0
local move_modifier = Max(-100, self.move_modifier)
local walk_modifier = stance_modifier + move_modifier
local vertical_move_modifier = 0
if HasPerk(unit, "DeathFromAbove") then
vertical_move_modifier = CharacterEffectDefs.DeathFromAbove:ResolveValue("vertical_cost_modifier")
end
local tunnel_param = {
unit = unit,
player_controlled = player_controlled,
move_modifier = move_modifier,
walk_modifier = walk_modifier,
walk_stairs_modifier = Max(-100, walk_modifier + vertical_move_modifier),
ladder_modifier = Max(-100, move_modifier + vertical_move_modifier),
drop_down_modifier = Max(-100, move_modifier + vertical_move_modifier),
climb_up_modifier = Max(-100, move_modifier + vertical_move_modifier),
}
local walk_cost = consts["Walk"].value * (100 + walk_modifier) / 100
local avoid_mines = unit and not player_controlled
self.destinations, self.paths_ap, self.paths_prev_pos, self.closest_free_pos = GetCombatPathPositions(unit, self.start_pos, ap, walk_cost, tunnel_param, tunnel_mask, self.restrict_area, ignore_occupied, move_through_occupied, avoid_mines)
end
function CombatPath:GetAP(pos, endStance)
if not pos then return end
local pos_type = type(pos)
local ap, stance
if pos_type == "number" then
ap = self.paths_ap[pos]
stance = self.stance_at_end or (self.destination_stances and self.destination_stances[pos])
if endStance and stance and stance ~= endStance then
return ap + GetStanceToStanceAP(stance, endStance)
end
return ap
elseif pos_type == "table" then
local min_cost
for i, p in ipairs(pos) do
local c = self:GetAP(p)
if c and (not min_cost or c < min_cost) then
min_cost = c
end
end
return min_cost
end
pos = point_pack(pos)
ap = self.paths_ap[pos]
stance = self.stance_at_end or (self.destination_stances and self.destination_stances[pos])
if ap and endStance and stance and stance ~= endStance then
return ap + GetStanceToStanceAP(stance, endStance)
end
return ap
end
function CombatPath:GetCombatPathFromPos(pos)
if not pos then return end
local p = type(pos) == "number" and pos or point_pack(pos)
local paths_prev_pos = self.paths_prev_pos
if not paths_prev_pos[p] then
return
end
local path = {}
while p do
table.insert(path, p)
p = paths_prev_pos[p]
end
return path
end
function CombatPath:GetReachableMeleeRangePositions(target, check_occupied, min_ap)
local list = GetMeleeRangePositions(self.unit, target, nil, check_occupied)
local paths_ap = self.paths_ap
for i = list and #list or 0, 1, -1 do
local ap = paths_ap[list[i]]
if not ap or min_ap and ap < min_ap or not self.destinations[list[i]] then
table.remove(list, i)
end
end
return list
end
function CombatPath:GetClosestMeleeRangePos(target, target_pos, check_free, interaction)
local list = GetMeleeRangePositions(self.unit, target, target_pos, check_free)
local paths_ap = self.paths_ap
local closest, min_ap
for i, packed_pos in ipairs(list) do
local ap = paths_ap[packed_pos]
if ap and (not min_ap or ap < min_ap) and (not check_free or self.destinations[packed_pos]) then
if interaction or IsMeleeRangeTarget(self.unit, packed_pos, nil, target, target_pos) then
closest = packed_pos
min_ap = ap
end
end
end
if closest then
return point(point_unpack(closest))
end
end
function GetCombatPathLen(path, obj)
local len = 0
if #path > 0 then
local x1, y1, z1 = point_unpack(path[1])
for i = 2, #path do
local x2, y2, z2 = point_unpack(path[i])
len = len + GetLen(x2 - x1, y2 - y1, (z1 or z2) and (z2 or terrain.GetHeight(x2, y2)) - (z1 or terrain.GetHeight(x1, y1)) or 0)
x1, y1, z1 = x2, y2, z2
end
if IsValid(obj) and obj:IsValidPos() then
len = len + obj:GetVisualDist(x1, y1, z1)
end
end
return len
end
|