File size: 8,298 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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
if (const.pfPassTypeGridBits or 0) == 0 then
return
end
local pass_tile = const.PassTileSize
local pass_type_tile = const.PassTypeTileSize
local overlap_dist = pass_type_tile * 3 / 2
local InvalidZ = const.InvalidZ
local gofAny = const.gofSyncObject | const.gofPermanent
local GetPosXYZ = CObject.GetPosXYZ
local IsValidPos = CObject.IsValidPos
local PassTypeCircleOr = terrain.PassTypeCircleOr
local PassTypeCircleSet = terrain.PassTypeCircleSet
local PassTypeInvalidate = terrain.PassTypeInvalidate
local GetMapCenter = terrain.GetMapCenter
local InplaceExtend = box().InplaceExtend
local IsEmpty = box().IsEmpty
local point_pack, point_unpack = point_pack, point_unpack
function OnMsg.Autorun()
local pass_type_bits = rawget(_G, "pass_type_bits")
if not next(pass_type_bits) then
return
end
local PassTypeComboName = function(bit_names)
table.sort(bit_names)
return table.concat(bit_names, "|")
end
assert(#pass_type_bits == (const.pfPassTypeGridBits or 0))
pathfind_pass_types = { "cost_default" } -- the first type is the default type
local ptype_name_to_value = {}
local bit_count = Min(const.pfPassTypeGridBits or 0, #pass_type_bits)
local ptypes_combos = (1<<bit_count) - 1
for value=1,ptypes_combos do
local names = {}
for k=1,bit_count do
if value & (1<<(k-1)) ~= 0 then
names[#names + 1] = pass_type_bits[k]
end
end
pathfind_pass_types[#pathfind_pass_types + 1] = PassTypeComboName(names)
end
for idx, pfc in pairs(pathfind) do
for value=1,ptypes_combos do
local names = {}
local cost_max = 0
for k=1,bit_count do
if value & (1<<(k-1)) ~= 0 then
local name = pass_type_bits[k]
names[#names + 1] = name
cost_max = Max(cost_max, pfc[name] or PF_DEFAULT_COST)
end
end
local name = PassTypeComboName(names)
pfc[name] = cost_max
--print("\t", pfc.name, ":", name, cost_max)
end
end
pathfind_pass_grid_types = pathfind_pass_types
end
function GetPassGridType(PassTypeName)
return (PassTypeName or "") ~= "" and ((table.find(pathfind_pass_grid_types, PassTypeName) or 1) - 1)
end
local GetPassGridType = GetPassGridType
function PassTypesCombo()
local items = { "" }
return table.iappend(items, pass_type_bits or empty_table)
end
----
if FirstLoad then
PassTypeMaxRadius = -1
PassTypeMaxCount = 0
PassTypesDisabled = false
end
local function AddPassTypeMaxRadius(radius)
if radius == PassTypeMaxRadius then
PassTypeMaxCount = PassTypeMaxCount + 1
elseif radius > PassTypeMaxRadius then
PassTypeMaxRadius = radius
PassTypeMaxCount = 1
end
end
local function UpdatePassTypeMaxRadius(obj)
local radius = obj.PassTypeRadius
if radius > 0 and obj.pass_type_applied and GetPassGridType(obj.PassTypeName) ~= 0 then
return AddPassTypeMaxRadius(radius)
end
end
local function RemovePassTypeMaxRadius(radius)
assert(radius <= PassTypeMaxRadius)
if radius == PassTypeMaxRadius then
assert(PassTypeMaxCount > 0)
PassTypeMaxCount = Max(0, PassTypeMaxCount - 1)
if PassTypeMaxCount == 0 then
PassTypeMaxRadius = -1
MapForEach("map", "PassTypeObj", nil, nil, nil, gofAny, UpdatePassTypeMaxRadius)
end
end
end
local function ReapplyCost(obj, inv, reapply)
return obj:SetCostRadius(nil, nil, inv, reapply)
end
local function OnPassTypeOverlap(obj, target, radius, x0, y0, z0, inv)
if obj == target or not obj.pass_type_applied then return end
local _, _, z = GetPosXYZ(obj)
if z ~= z0 then return end -- different pass grid
local dist = radius + obj.PassTypeRadius + overlap_dist
if not obj:IsCloser2D(x0, y0, dist) then return end
return ReapplyCost(obj, inv, "overlap")
end
function RemoveCost(obj)
return obj:SetCostRadius(-1)
end
local function ReapplyAllPassTypes()
local inv = box()
PassTypeMaxRadius = -1
PassTypeMaxCount = 0
MapForEach("map", "PassTypeObj", nil, nil, nil, gofAny, ReapplyCost, inv, "rebuild")
terrain.PassTypeInvalidate(inv)
end
local function ClearAllPassTypes()
PassTypeMaxRadius = -1
PassTypeMaxCount = 0
terrain.PassTypeClear()
end
function DisablePassTypes()
if not PassTypesDisabled then
PassTypesDisabled = true
ClearAllPassTypes()
end
end
function EnablePassTypes()
if PassTypesDisabled then
PassTypesDisabled = false
ReapplyAllPassTypes()
end
end
-- the pass type grid isn't persisted, restore it
OnMsg.LoadGameObjectsUnpersisted = ReapplyAllPassTypes
OnMsg.DoneMap = ClearAllPassTypes
----
DefineClass.PassTypeObj = {
__parents = { "Object" },
properties = {
{ id = "PassTypeRadius", name = "Pass Radius", editor = "number", default = 0, scale = "m" },
{ id = "PassTypeName", name = "Pass Type", editor = "choice", default = "", items = PassTypesCombo },
},
pass_type_applied = false,
}
function PassTypeObj:IsVirtual()
return self:GetGameFlags(gofAny) == 0
end
AutoResolveMethods.ApplyPassCostOnTerrain = "or"
PassTypeObj.ApplyPassCostOnTerrain = empty_func
function PassTypeObj:SetCostRadius(radius, name, inv, reapply)
--DbgClear(true) DbgSetVectorZTest(false)
if PassTypesDisabled then
return
end
local applied = self.pass_type_applied
if not applied and reapply then
return
end
local prev_radius = self.PassTypeRadius
radius = radius or prev_radius
local prev_name = self.PassTypeName
name = name or prev_name
local pass_type = GetPassGridType(name)
local valid = radius >= 0 and pass_type ~= 0 and IsValidPos(self) and not self:IsVirtual()
if not applied and not valid then
return
end
local xc, yc = GetMapCenter()
local x, y, z, x0, y0, z0, apply
if valid then
x, y, z = GetPosXYZ(self)
if z and self:ApplyPassCostOnTerrain() then
z = InvalidZ
end
apply = point_pack(x - xc, y - yc, z)
end
if applied == apply and radius == prev_radius and name == prev_name and not reapply then
return
end
local moved = applied and applied ~= apply
local shrinked = applied and radius < prev_radius
local enlarged = apply and (not applied or reapply == "rebuild" or prev_radius < radius)
local type_changed = applied and apply and name ~= prev_name
if radius ~= prev_radius then
self.PassTypeRadius = radius
end
if name ~= prev_name then
self.PassTypeName = name
end
if applied ~= apply then
self.pass_type_applied = apply
end
inv = inv or box()
if not reapply and (shrinked or moved or type_changed) then
-- clear the previous mark
x0, y0, z0 = point_unpack(applied)
x0, y0 = xc + x0, yc + y0
InplaceExtend(inv, PassTypeCircleSet(x0, y0, z0 or InvalidZ, prev_radius, 0))
--DbgAddCircle(point(x0, y0, z0), prev_radius, red)
if shrinked or moved then
if shrinked then
RemovePassTypeMaxRadius(prev_radius)
end
-- reapply only the affected surrounding objects on the same pf level
local enum_radius = overlap_dist + prev_radius + PassTypeMaxRadius
MapForEach(x0, y0, z0 or InvalidZ, enum_radius, "PassTypeObj", nil, nil, nil, gofAny, OnPassTypeOverlap, self, prev_radius, x0, y0, z0, inv)
end
elseif enlarged then
AddPassTypeMaxRadius(radius)
end
if apply then
--DbgAddCircle(self, radius, green)
InplaceExtend(inv, PassTypeCircleOr(x, y, z or InvalidZ, radius, pass_type))
end
--DbgAddBox(inv)
if not reapply and not IsEmpty(inv) then
NetUpdateHash("SetCostRadius", x or x0, y or y0, z or z0, radius, name, inv)
PassTypeInvalidate(inv)
end
return inv
end
function PassTypeObj:SetPassTypeRadius(value)
self:SetCostRadius(value)
end
function PassTypeObj:SetPassTypeName(value)
self:SetCostRadius(nil, value)
end
function PassTypeObj:Done()
ExecuteProcess("Passability", "RemoveCost", self)
end
function PassTypeObj:GameInit()
self:SetCostRadius()
end
function PassTypeObj:CompleteElementConstruction()
self:SetCostRadius()
end
----
DefineClass.PassTypeMarker = {
__parents = { "PassTypeObj", "RadiusMarker", "EditorColorObject" },
entity = "NoteMarker",
radius_prop = "PassTypeRadius",
editor_text_member = "PassTypeName",
editor_text_offset = point(0, 0, 3*guim),
}
function PassTypeMarker:EditorGetColor()
local grid_type = GetPassGridType(self.PassTypeName)
if grid_type == 0 then
return white
end
local color = pass_type_colors and pass_type_colors[self.PassTypeName]
return color or RandColor(xxhash(grid_type))
end
function PassTypeMarker:EditorGetTextColor()
return self:EditorGetColor()
end
function PassTypeMarker:EditorCallbackMove()
self:SetCostRadius()
end |