|
if Platform.cmdline then return end |
|
|
|
if FirstLoad then |
|
debug_pass_draw = false |
|
end |
|
function OnMsg.DoneMap() |
|
debug_pass_draw = false |
|
rawset(_G, "g_APCostsShown", false) |
|
end |
|
|
|
DefineClass.DebugPassDraw = |
|
{ |
|
__parents = { "CObject" }, |
|
entity = "Floor_Planks", |
|
flags = { |
|
efSelectable = false, efWalkable = false, efCollision = false, |
|
efApplyToGrids = false, efShadow = false, efSunShadow = false, |
|
cfConstructible = false, |
|
cofComponentColorizationMaterial = true, |
|
cofComponentCollider = false, |
|
}, |
|
} |
|
|
|
function DbgDrawPFLevelPass(on, offsetz) |
|
debug_pass_draw = debug_pass_draw or {} |
|
for i = #debug_pass_draw, 0, -1 do |
|
if IsValid(debug_pass_draw[i]) then DoneObject(debug_pass_draw[i]) end |
|
debug_pass_draw[i] = nil |
|
end |
|
if on == false then |
|
return |
|
end |
|
local tile_size = const.SlabSizeX |
|
local tile_z = const.SlabSizeZ |
|
local scale = const.PassTileSize * 100 / const.SlabSizeX |
|
local color1_central = RGB(120,180,130) |
|
local color1_intermediate = RGB(150,220,160) |
|
offsetz = offsetz or 5*guic |
|
|
|
local function func(x, y, z) |
|
local o = PlaceObject("DebugPassDraw") |
|
table.insert(debug_pass_draw, o) |
|
NetTempObject(o) |
|
o:SetScale(scale) |
|
local vx, vy, vz = WorldToVoxel(x, y, z) |
|
local _x, _y, _z = VoxelToWorld(vx, vy, vz) |
|
local inside_tile = abs(x - _x) < tile_size / 2 and abs(y - _y) < tile_size / 2 |
|
if inside_tile then |
|
|
|
o:SetColorModifier(color1_central) |
|
else |
|
|
|
o:SetColorModifier(color1_intermediate) |
|
o:SetAngle(90*60) |
|
end |
|
o:SetPos(x, y, z + offsetz) |
|
end |
|
local sizex, sizey = terrain.GetMapSize() |
|
terrain.ForEachPassCenter(func, box(0, 0, 0, sizex, sizey, 100000)) |
|
return #debug_pass_draw |
|
end |
|
|
|
function DbgDrawPassSlabs(on, show_terrain_pass, offsetz, bbox) |
|
local sizex, sizey = terrain.GetMapSize() |
|
bbox = bbox or box(0, 0, 0, sizex, sizey, 100000) |
|
|
|
debug_pass_draw = debug_pass_draw or {} |
|
local i, n = 1, #debug_pass_draw |
|
while i <= n do |
|
local obj = debug_pass_draw[i] |
|
if not IsValid(obj) or obj:GetPos():InBox(bbox) then |
|
if IsValid(obj) then DoneObject(obj) end |
|
debug_pass_draw[i] = debug_pass_draw[n] |
|
debug_pass_draw[n] = nil |
|
n = n - 1 |
|
else |
|
i = i + 1 |
|
end |
|
end |
|
if on == false then |
|
return |
|
end |
|
|
|
local floor = IsEditorActive() and XEditorFilters:GetFilter("HideFloor") or 0 |
|
if floor == 0 then floor = 999 end |
|
|
|
local color = RGB(120, 180, 130) |
|
local colorWater = RGB(0, 0, 130) |
|
local scale = const.PassTileSize * 100 / const.SlabSizeX |
|
local scaleWater = scale * 60 / 100 |
|
local ptGrow = point(const.SlabSizeX, const.SlabSizeY, const.SlabSizeZ * 2) |
|
offsetz = offsetz or 5*guic |
|
local hide_roofs = IsEditorActive() and not LocalStorage.FilteredCategories["Roofs"] or nil |
|
|
|
local function func(x, y, z) |
|
if not z and not show_terrain_pass then |
|
return |
|
end |
|
if not terrain.IsPassable(x, y, z, 0) then |
|
return |
|
end |
|
local pt = point(x, y, (z or terrain.GetHeight(x, y)) + offsetz) |
|
local volume = Extend(empty_box, pt):grow(ptGrow) |
|
local room = EnumVolumes(volume, "smallest") |
|
local bld_meta = VolumeBuildingsMeta[room and room.building or false] |
|
if bld_meta and bld_meta[floor] and pt:z() >= bld_meta[floor].box:minz() - 5 * guim / 10 then |
|
return |
|
end |
|
if hide_roofs then |
|
local slab, z = WalkableSlabByPoint(pt, true, bnot(const.gofSolidShadow)) |
|
if GetPassSlab(x, y, z) then |
|
local threshold = IsKindOf(slab, "TerrainCollision") and 15 * guic or 7 * guic |
|
if abs(z - pt:z()) >= threshold then |
|
return |
|
end |
|
end |
|
end |
|
local o = PlaceObject("DebugPassDraw") |
|
table.insert(debug_pass_draw, o) |
|
NetTempObject(o) |
|
o:SetColorModifier(color) |
|
o:SetScale(scale) |
|
o:SetPos(pt) |
|
|
|
|
|
|
|
local pass_type = terrain.GetPassType(point(x, y, z)) |
|
if pass_type == pathfind_water_pass_type_idx then |
|
local o = PlaceObject("DebugPassDraw") |
|
table.insert(debug_pass_draw, o) |
|
NetTempObject(o) |
|
o:SetColorModifier(colorWater) |
|
o:SetScale(scaleWater) |
|
o:SetPos(pt:x(), pt:y(), pt:z() + offsetz) |
|
end |
|
end |
|
ForEachPassSlab(bbox, func) |
|
return #debug_pass_draw |
|
end |
|
|
|
function DbgDrawCombatNodes(combatPath, max_cost) |
|
DbgClearVectors() |
|
DbgClearTexts() |
|
local tile_size = const.SlabSizeX |
|
local ap_mul = const.Scale.AP |
|
local color = const.clrPaleBlue |
|
local offs = tile_size*25/100 |
|
local offsz = 10*guic |
|
|
|
for pk_pos, ap in pairs(combatPath and combatPath.paths_ap or empty_table) do |
|
if not max_cost or ap <= max_cost then |
|
local x1, y1, z1 = point_unpack(pk_pos) |
|
z1 = (z1 or terrain.GetHeight(x1, y1)) + offsz |
|
local txt |
|
if ap % ap_mul == 0 then |
|
txt = string.format("%d", ap / ap_mul) |
|
else |
|
txt = string.format("%d.%.2d", ap / ap_mul, Max(1, (ap % ap_mul) * 100 / ap_mul)) |
|
end |
|
local p = point(x1, y1, z1) |
|
DbgAddText(txt, p, color) |
|
local prev_pos = combatPath.paths_prev_pos[pk_pos] |
|
if prev_pos then |
|
local x2, y2, z2 = point_unpack(prev_pos) |
|
z2 = (z2 or terrain.GetHeight(x2, y2)) + offsz |
|
local dx = (x2 - x1) / tile_size |
|
local dy = (y2 - y1) / tile_size |
|
local p2 = point(x2, y2, z2) |
|
if dx == 0 or dy == 0 then |
|
p2 = p - SetLen(p - p2, tile_size) |
|
else |
|
for k = Min(abs(dx), abs(dy)), 2, -1 do |
|
if dx % k == 0 and dy % k == 0 then |
|
p2 = p + point(tile_size * dx / k, tile_size * dy / k, 0) |
|
break |
|
end |
|
end |
|
end |
|
local v = p - p2 |
|
local len = v:Len() |
|
DbgAddVector(p - SetLen(v, len - offs), SetLen(v, len - 2*offs), color) |
|
end |
|
end |
|
end |
|
end |
|
|
|
function DbgDrawCombatPositions(unit, max_cost) |
|
if not IsValid(unit) then return end |
|
local combatPath = PlaceObject("CombatPath") |
|
combatPath:RebuildPaths(unit, max_cost or unit.ActionPoints) |
|
DbgDrawCombatNodes(combatPath) |
|
DoneObject(combatPath) |
|
end |
|
|
|
function DbgRecalcDrawCombatNodes(unit) |
|
if not rawget(_G, "g_APCostsShown") then |
|
return |
|
end |
|
if unit ~= SelectedObj then |
|
return |
|
end |
|
if IsValid(SelectedObj) then |
|
DbgDrawCombatNodes(GetCombatPath(SelectedObj)) |
|
else |
|
DbgDrawCombatNodes(false) |
|
end |
|
end |
|
OnMsg.SelectedObjChange = DbgRecalcDrawCombatNodes |
|
OnMsg.UnitAPChanged = DbgRecalcDrawCombatNodes |
|
OnMsg.UnitMovementDone = DbgRecalcDrawCombatNodes |
|
|
|
function OnMsg.UnitAPChanged(unit) |
|
if unit == SelectedObj then |
|
DbgRecalcDrawCombatNodes() |
|
end |
|
end |
|
|
|
local function DbgCollapsePointsInBox(b, steps) |
|
local pts = {} |
|
ForEachPassSlab(b, function(x, y, z) |
|
pts[#pts + 1] = stance_pos_pack(x, y, z, 0) |
|
end) |
|
local collapsed = CollapsePoints(pts, steps) |
|
for _, pt in ipairs(pts) do |
|
local x, y, z = stance_pos_unpack(pt) |
|
if table.find(collapsed, pt) then |
|
ShowMe(point(x,y,z)) |
|
else |
|
end |
|
end |
|
end |
|
|
|
function DbgShowVisFieldCollapsedPositionsArroundCursor(steps, radius) |
|
ClearShowMe() |
|
DbgClear() |
|
steps = steps or 1 |
|
local cursor = GetCursorPos() |
|
radius = radius or 10*guim |
|
local b = box(cursor:x() - radius, cursor:y() - radius, 0, cursor:x() + radius + const.SlabSizeX, cursor:y() + radius + const.SlabSizeX, MapSlabsBBox_MaxZ) |
|
|
|
DbgCollapsePointsInBox(b, steps) |
|
end |