|
if Platform.cmdline then return end |
|
|
|
if FirstLoad then |
|
path_waypoints = false |
|
debug_pass_vectors = false |
|
end |
|
function OnMsg.DoneMap() |
|
path_waypoints = false |
|
debug_pass_vectors = false |
|
end |
|
|
|
function DrawWayPointReset() |
|
if path_waypoints and #path_waypoints > 0 then |
|
for i = 1, #path_waypoints do |
|
DoneObject(path_waypoints[i]) |
|
end |
|
end |
|
path_waypoints = {} |
|
end |
|
|
|
function DrawWayPointPath(pt_or_obj, pt_end, anim_time) |
|
DrawWayPointReset() |
|
if not pt_or_obj then |
|
pf.DbgDrawFindPath() |
|
return |
|
end |
|
local function DiagDistEx(p1, p2) |
|
local x1, y1 = p1:xy() |
|
local x2, y2 = p2:xy() |
|
local x = abs(x1 - x2) |
|
local y = abs(y1 - y2) |
|
if x > y then |
|
return y * 1448 / 1024 + x - y |
|
else |
|
return x * 1448 / 1024 + y - x |
|
end |
|
end |
|
|
|
local v = pf.DbgDrawFindPath(pt_or_obj, pt_end or GetTerrainCursor():SetInvalidZ(), anim_time or 5000) |
|
if not v then return "no path" end |
|
local len = #v |
|
local path_len = 0 |
|
for i = 1, len do |
|
local o = PlaceObject("WayPoint") |
|
path_waypoints[i] = o |
|
local pos = v[len+1-i] |
|
o:SetPos(pos) |
|
o:SetAngle(i) |
|
|
|
|
|
|
|
end |
|
end |
|
|
|
|
|
|
|
|
|
function DrawPath(obj) |
|
local path = IsValid(obj) and obj:GetComponentFlags(const.cofComponentPath) ~= 0 and obj:GetPath() or empty_table |
|
path_waypoints = path_waypoints or {} |
|
local index = 1 |
|
for i = 1, #path do |
|
local pos = path[i] |
|
if pos:IsValid() then |
|
local o = path_waypoints[index] |
|
if not IsValid(o) then |
|
o = PlaceObject("WayPoint") |
|
NetTempObject(o) |
|
path_waypoints[index] = o |
|
o:SetScale(40) |
|
end |
|
o:SetPos(pos) |
|
o:SetAngle(index) |
|
index = index + 1 |
|
end |
|
end |
|
for i = #path_waypoints, index, -1 do |
|
DoneObject(path_waypoints[i]) |
|
path_waypoints[i] = nil |
|
end |
|
return "" |
|
end |
|
|
|
function DbgDrawPath(path, color, offset) |
|
if IsValid(path) then |
|
local pt1 = path:GetPos() |
|
local pt0 = path:GetVisualPos() |
|
if not pt1:z() then pt0 = pt0:SetInvalidZ() end |
|
local pts = {pt0} |
|
if pt1 ~= pt0 then |
|
pts[#pts + 1] = pt1 |
|
end |
|
for i=path:GetPathPointCount(),1,-1 do |
|
local pt = path:GetPathPoint(i) |
|
if IsValidPos(pt) and pts[#pts] ~= pt then |
|
pts[#pts + 1] = pt |
|
end |
|
end |
|
path = pts |
|
end |
|
if #(path or "") < 2 then |
|
return |
|
end |
|
offset = offset or 5*guic |
|
color = color or white |
|
local pos0 = ValidateZ(path[1], offset) |
|
for i = 2, #path do |
|
local pos1 = ValidateZ(path[i], offset) |
|
DbgAddSegment(pos1, pos0, color) |
|
pos0 = pos1 |
|
end |
|
end |
|
|
|
function DbgSetTerrainBoxHeight(h, clip) |
|
clip = clip or box(0, 0, terrain.GetMapSize()) |
|
local htile = const.HeightTileSize |
|
local minx = clip:minx() / htile * htile |
|
local maxx = clip:maxx() / htile * htile |
|
local miny = clip:miny() / htile * htile |
|
local maxy = clip:maxy() / htile * htile |
|
local f = terrain.SetHeight |
|
for y = miny, maxy, htile do |
|
for x = minx, maxx, htile do |
|
f(x, y, h) |
|
end |
|
end |
|
end |
|
|
|
function DbgSnapTerrainToSlab(clip) |
|
clip = clip or box(0, 0, terrain.GetMapSize()) |
|
local htile = const.HeightTileSize |
|
local minx = clip:minx() / htile * htile |
|
local maxx = clip:maxx() / htile * htile |
|
local miny = clip:miny() / htile * htile |
|
local maxy = clip:maxy() / htile * htile |
|
local GetHeight = terrain.GetHeight |
|
local SetHeight = terrain.SetHeight |
|
local snapz = (const.SlabSizeZ or guim) / 2 |
|
local offsetz = -1 |
|
for y = miny, maxy, htile do |
|
for x = minx, maxx, htile do |
|
SetHeight(x, y, (GetHeight(x, y) + snapz/2) / snapz * snapz + offsetz) |
|
end |
|
end |
|
end |
|
|
|
function DbgRamp(pos, ptSize, angle2D, anglez) |
|
local htile = const.HeightTileSize |
|
local ztile = const.SlabSizeZ or guim |
|
local x, y, z = pos:xyz() |
|
x = (x + htile/2) / htile * htile |
|
y = (y + htile/2) / htile * htile |
|
z = (z + ztile/2) / ztile * ztile |
|
|
|
local dx1, dy1 = Rotate(point(ptSize:x(), 0), angle2D * 60):xy() |
|
dx1 = (dx1 + htile/2) / htile * htile |
|
dy1 = (dy1 + htile/2) / htile * htile |
|
|
|
local dx2, dy2 = Rotate(point(0, ptSize:y()), angle2D * 60):xy() |
|
dx2 = (dx2 + htile/2) / htile * htile |
|
dy2 = (dy2 + htile/2) / htile * htile |
|
|
|
local sz, cz = sincos(anglez*60) |
|
local dz = ptSize:y() * sz / cz |
|
dz = (dz + ztile/2) / ztile * ztile |
|
|
|
local poly = { |
|
point(x, y, z), |
|
point(x + dx1, y + dy1, z), |
|
point(x + dx1 + dx2, y + dy1 + dy2, z + dz), |
|
point(x + dx2, y + dy2, z + dz), |
|
} |
|
local spans = DbgGetPolySpans(poly) |
|
DbgSetTerraceHeightSpans(spans) |
|
end |
|
|
|
function DbgGetPolySpans(poly) |
|
if type(poly) ~= "table" or #poly == 0 then |
|
return |
|
end |
|
local xtile = const.HeightTileSize |
|
local ztile = const.SlabSizeZ or guim |
|
local offsz = -5*guic |
|
local spans = {} |
|
local miny, maxy |
|
local p1, p2 |
|
local function f(p) |
|
local x, y = p:xy() |
|
local x1, y1, z1 = p1:xyz() |
|
local x2, y2, z2 = p2:xyz() |
|
local dz = 0 |
|
if abs(x2 - x1) < abs(y2 - y1) then |
|
dz = ((z2 - z1) * (y - y1) + (y2 - y1) / 2) / (y2 - y1) |
|
elseif x2 ~= x1 then |
|
dz = ((z2 - z1) * (x - x1) + (x2 - x1) / 2) / (x2 - x1) |
|
end |
|
local pt = point(x * xtile, y * xtile, z1 + dz) |
|
local span = spans[y * xtile] |
|
if span then |
|
if pt:x() < span[1]:x() then |
|
span[1] = pt |
|
elseif pt:x() > span[2]:x() then |
|
span[2] = pt |
|
end |
|
else |
|
span = { pt, pt } |
|
spans[y * xtile] = span |
|
end |
|
if not miny or miny > y then miny = y end |
|
if not maxy or maxy < y then maxy = y end |
|
end |
|
|
|
for i = 1, #poly do |
|
local x1, y1, z1 = poly[i]:xyz() |
|
local x2, y2, z2 = poly[i == #poly and 1 or i + 1]:xyz() |
|
p1 = point((x1 + xtile/2) / xtile, (y1 + xtile/2) / xtile, z1) |
|
p2 = point((x2 + xtile/2) / xtile, (y2 + xtile/2) / xtile, z2) |
|
f(p1) |
|
if p1:x() ~= p2:x() or p1:y() ~= p2:y() then |
|
RasterizeLine(p1, p2, f) |
|
end |
|
end |
|
return spans |
|
end |
|
|
|
function DbgSetTerraceHeightSpans(spans) |
|
local SetHeight = terrain.SetHeight |
|
for y, span in pairs(spans) do |
|
for i = 1, #span, 2 do |
|
local x1, y1, z1 = span[i]:xyz() |
|
local x2, y2, z2 = span[i+1]:xyz() |
|
if x1 == x2 then |
|
SetHeight(x1, y, z1) |
|
else |
|
for x = x1, x2 do |
|
local z = z1 + ((z2 - z1) * (x - x1) + (x2 - x1) / 2) / (x2 - x1) |
|
SetHeight(x, y, z) |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
function DbgSetAnimPhase(unit, phase, anim) |
|
if anim and unit:GetStateText() ~= anim then |
|
unit:SetStateText(anim) |
|
end |
|
local v0 = unit:GetStepVector(anim, unit:GetAngle(), 0, unit:GetAnimPhase(1)) |
|
local v1 = unit:GetStepVector(anim, unit:GetAngle(), 0, phase) |
|
unit:SetPos(unit:GetPos() + (v1 - v0)) |
|
unit:SetAnimPhase(1, phase) |
|
unit:SetAnimSpeed(1, 0) |
|
end |
|
|
|
function ToggleDrawTunnels() |
|
DbgDrawTunnels(not debug_pass_vectors) |
|
end |
|
|
|
function DbgDrawTunnels(show) |
|
local count = 0 |
|
if show ~= false then |
|
debug_pass_vectors = debug_pass_vectors or {} |
|
local zoffset = (const.SlabSizeZ or guim) / 2 |
|
local invalid_z = const.InvalidZ |
|
local function f(obj, x1, y1, z1, x2, y2, z2, weight, tunnel_type, flags, param) |
|
if z1 == invalid_z then |
|
z1 = terrain.GetHeight(x1, y1) |
|
end |
|
if z2 == invalid_z then |
|
z2 = terrain.GetHeight(x2, y2) |
|
end |
|
count = count + 1 |
|
local vector = debug_pass_vectors[count] |
|
if not IsValid(vector) then |
|
vector = Vector:new() |
|
debug_pass_vectors[count] = vector |
|
vector:SetDepthTest(true) |
|
end |
|
local v1 = point(x1, y1, z1 + zoffset + obj.dbg_tunnel_zoffset) |
|
local v2 = point(x2, y2, z2 + zoffset + obj.dbg_tunnel_zoffset) |
|
vector:Set(v1, v2, obj.dbg_tunnel_color) |
|
end |
|
pf.ForEachTunnel("map", f) |
|
end |
|
for i = #(debug_pass_vectors or empty_table), count + 1, -1 do |
|
DoneObject(debug_pass_vectors[i]) |
|
debug_pass_vectors[i] = nil |
|
end |
|
if show == false then |
|
debug_pass_vectors = false |
|
end |
|
end |
|
|
|
function OnMsg.OnPassabilityChanged(clip) |
|
CreateMapRealTimeThread(function() |
|
if debug_pass_vectors then |
|
DbgDrawTunnels(true) |
|
end |
|
end) |
|
end |
|
|
|
function FollowForcedPath(unit, path) |
|
pf.SetForcedPath(unit, path) |
|
unit:Goto(path[#path], "sl") |
|
end |
|
|
|
function turn_test(angle, dist, unit, on_start, on_end) |
|
DbgClear() |
|
DbgSetVectorZTest(false) |
|
unit = unit or SelectedObj |
|
unit = GetTopmostParent(unit) |
|
angle = angle or -90 * 60 |
|
dist = dist or Max(2*unit:GetStepLength(), 4*unit:GetRadius()) |
|
if not unit then |
|
return |
|
end |
|
unit:SetCommand(function(self, angle, on_start, on_end) |
|
table.change(config, "turn_test", { DebugObjMovement = true }) |
|
if on_start then on_start(self) end |
|
local p0 = self:GetPos() |
|
local step = self:GetRelativePoint(dist, 0, 0) - self:GetVisualPos() |
|
local ps = p0 + SetLen(step, 10) |
|
local pts = {ps, p0 + step / 2} |
|
local n = (360*60) / abs(angle) |
|
if n * abs(angle) == 360*60 then |
|
n = n - 1 |
|
end |
|
for i=1,n do |
|
step = Rotate(step, angle) |
|
pts[#pts + 1] = pts[#pts] + step |
|
end |
|
pts[#pts + 1] = p0 |
|
DbgAddPoly(pts) |
|
self:SetPos(ps) |
|
FollowForcedPath(self, pts) |
|
if on_end then on_end(self) end |
|
table.restore(config, "turn_test") |
|
end, angle, on_start, on_end) |
|
end |
|
|
|
function OnMsg.SelectedObjChange(obj, prev) |
|
if config.DebugObjMovement then |
|
if IsValid(prev) and pf.GetDebugEnabled(prev) then |
|
pf.SetDebugEnabled(prev, false) |
|
rawset(prev, "auto_show_path", nil) |
|
end |
|
if IsValid(obj) then |
|
pf.SetDebugEnabled(obj, true) |
|
if pf.GetDebugEnabled(obj) then |
|
rawset(obj, "auto_show_path", false) |
|
DbgClearVectors() |
|
local path = pf.GetPath(obj) |
|
if path then |
|
DbgSetVectorZTest(false) |
|
DbgAddPoly(path, white, true); |
|
for i = 1,#path do |
|
DbgAddVector(path[i], const.PassTileSize, 0xff888888) |
|
end |
|
local dest, maxr, minr = pf.GetPathDest(obj) |
|
for _, pt in ipairs(dest) do |
|
DbgAddVector(pt, guim, 0xff0088ff) |
|
if maxr > 0 then |
|
DbgAddCircle(pt, maxr, 0xff88ff00) |
|
end |
|
if minr > 0 then |
|
DbgAddCircle(pt, minr, 0xffff8800) |
|
end |
|
end |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MapVar("DbgDrawPathObjs", false) |
|
|
|
function DbgTogglePaths() |
|
DbgDrawPaths(not DbgDrawPathObjs) |
|
end |
|
|
|
local function DbgMarkPathObj(obj, time) |
|
local info = DbgDrawPathObjs[obj] |
|
if not info then |
|
info = { hashes = {} } |
|
DbgDrawPathObjs[obj] = info |
|
end |
|
info.time = time or GameTime() |
|
end |
|
|
|
local function DbgSetPathObj(parent, name, dbg_obj, attach) |
|
local info = DbgDrawPathObjs[parent] |
|
if not info then |
|
return |
|
end |
|
local prev_dbg_obj = info[name] |
|
info[name] = dbg_obj |
|
if dbg_obj and attach then |
|
dbg_obj:SetScale(10000/parent:GetScale()) |
|
parent:Attach(dbg_obj) |
|
end |
|
DoneObjects(prev_dbg_obj) |
|
if IsValid(prev_dbg_obj) then |
|
DoneObject(prev_dbg_obj) |
|
end |
|
end |
|
|
|
local function DbgUpdatePathObj(obj, name, hash) |
|
local prev_hash = table.get(DbgDrawPathObjs, obj, "hashes", name) |
|
if prev_hash == hash then return end |
|
table.set(DbgDrawPathObjs, obj, "hashes", name, hash) |
|
return true |
|
end |
|
|
|
function DbgDrawPaths(draw) |
|
if not draw then |
|
for obj, info in pairs(DbgDrawPathObjs) do |
|
for name, dbg_obj in pairs(info) do |
|
if type(dbg_obj) == "table" then |
|
DoneObjects(dbg_obj) |
|
if IsValid(dbg_obj) then |
|
DoneObject(dbg_obj) |
|
end |
|
end |
|
end |
|
end |
|
DbgDrawPathObjs = false |
|
return |
|
end |
|
print("Cyan: destlock, Green: resting, Yellow: collision, White: path, Pink: flight") |
|
DbgDrawPathObjs = {} |
|
CreateGameTimeThread(function(paths) |
|
while DbgDrawPathObjs == paths do |
|
local time = GameTime() |
|
MapForEach("map", nil, const.efDestlock, function(obj) |
|
DbgMarkPathObj(obj, time) |
|
local pos = obj:GetPos() |
|
local hash = xxhash(pos) |
|
if DbgUpdatePathObj(obj, "destlock", hash) then |
|
local circle = PlaceCircle(ValidateZ(pos), pf.GetDestlockRadius(obj), cyan, false) |
|
DbgSetPathObj(obj, "destlock", circle) |
|
end |
|
if DbgUpdatePathObj(obj, "path", hash) then |
|
local vector = PlaceVector(ValidateZ(pos), 2*guim, cyan, false) |
|
DbgSetPathObj(obj, "path", vector) |
|
end |
|
end) |
|
MapForEach("map", nil, const.efUnit, function(obj) |
|
if obj:GetComponentFlags(const.cofComponentPath) == 0 or not IsValidPos(obj) then |
|
return |
|
end |
|
DbgMarkPathObj(obj, time) |
|
|
|
local resting = obj:GetEnumFlags(const.efResting) ~= 0 |
|
local destlock_radius = pf.GetDestlockRadius(obj) |
|
local collision_radius = pf.GetCollisionRadius(obj) |
|
local scale = obj:GetScale() |
|
local resting_hash = xxhash(resting, destlock_radius, collision_radius, scale) |
|
if DbgUpdatePathObj(obj, "resting", resting_hash) then |
|
local collision_circle, destlock_circle |
|
if resting then |
|
destlock_circle = CreateCircleMesh(destlock_radius, green) |
|
else |
|
collision_circle = CreateCircleMesh(collision_radius, yellow) |
|
end |
|
DbgSetPathObj(obj, "collision", collision_circle, true) |
|
DbgSetPathObj(obj, "destlock", destlock_circle, true) |
|
end |
|
|
|
local flight_path = obj.flying and obj.flight_path or empty_table |
|
local path = pf.GetPath(obj) or empty_table |
|
local path_hash = obj:GetPathHash() |
|
if DbgUpdatePathObj(obj, "path", path_hash) then |
|
local destlock = obj:GetDestlock() |
|
local path_line, path_spline, path_steps |
|
if #flight_path > 0 then |
|
path_spline = PlaceSplines(obj.flight_path, 0xff00ff, false, obj.flight_spline_idx) |
|
else |
|
local path = pf.GetPath(obj) or "" |
|
if #path > 0 then |
|
for i=2,#path do |
|
local pt = path[i] |
|
if IsValidPos(pt) then |
|
pt = ValidateZ(pt) |
|
path_steps = table.create_add(path_steps, PlacePolyLine({pt:AddZ(-100), pt:AddZ(100)}, yellow, false)) |
|
end |
|
end |
|
path[#path + 1] = obj:GetVisualPos() |
|
path_line = PlacePolyLine(path, white, false) |
|
elseif IsValidPos(destlock) then |
|
path_line = PlacePolyLine({obj:GetVisualPos(), destlock:GetVisualPos()}, white, false) |
|
end |
|
end |
|
DbgSetPathObj(obj, "path_steps", path_steps) |
|
DbgSetPathObj(obj, "path", path_line) |
|
DbgSetPathObj(obj, "spline_path", path_spline) |
|
end |
|
end) |
|
for obj, info in pairs(DbgDrawPathObjs) do |
|
if info.time ~= time then |
|
for name, mesh in pairs(info) do |
|
if IsValid(mesh) then |
|
DoneObject(mesh) |
|
end |
|
end |
|
DbgDrawPathObjs[obj] = nil |
|
end |
|
end |
|
Sleep(100) |
|
end |
|
end, DbgDrawPathObjs) |
|
end |
|
|
|
|
|
function OnMsg.DbgObjCallback(method, obj, ...) |
|
print("DbgObjCallback:", obj.class, method, ...) |
|
end |
|
|