|
if Platform.cmdline then return end |
|
|
|
MapVar("g_dbgCoversShown", false) |
|
MapVar("s_CoversUpdateThread", false) |
|
MapVar("s_CoversThreadBBox", false) |
|
MapVar("s_Diff", false) |
|
MapVar("s_CoversUpdateOperationInProgress", false) |
|
|
|
MapVar("s_DbgDrawLOS", false) |
|
MapVar("s_DbgDrawLOF", false) |
|
MapVar("s_DbgDrawLOF_Objects", {}) |
|
MapVar("s_DbgDrawLOF_EYE", false) |
|
MapVar("s_DbgDrawIgnoreSmoke", false) |
|
MapVar("s_DbgDrawLastTarget", false) |
|
MapVar("s_DbgDrawTargetDummies", false) |
|
|
|
DefineClass.DebugCoverDraw = { |
|
__parents = {"CObject"}, |
|
flags = {efSelectable = false, gofPermanent = false}, |
|
} |
|
|
|
DefineClass.DebugCoverDrawNorthLow = {__parents = {"DebugCoverDraw"}, entity = "CoverNorth"} |
|
DefineClass.DebugCoverDrawEastLow = {__parents = {"DebugCoverDraw"}, entity = "CoverEast"} |
|
DefineClass.DebugCoverDrawSouthLow = {__parents = {"DebugCoverDraw"}, entity = "CoverSouth"} |
|
DefineClass.DebugCoverDrawWestLow = {__parents = {"DebugCoverDraw"}, entity = "CoverWest"} |
|
DefineClass.DebugCoverDrawNorthHigh = {__parents = {"DebugCoverDraw"}, entity = "CoverNorth_High"} |
|
DefineClass.DebugCoverDrawEastHigh = {__parents = {"DebugCoverDraw"}, entity = "CoverEast_High"} |
|
DefineClass.DebugCoverDrawSouthHigh = {__parents = {"DebugCoverDraw"}, entity = "CoverSouth_High"} |
|
DefineClass.DebugCoverDrawWestHigh = {__parents = {"DebugCoverDraw"}, entity = "CoverWest_High"} |
|
|
|
local function StopCoversUpdateThread() |
|
DeleteThread(s_CoversUpdateThread) |
|
s_CoversUpdateThread = false |
|
s_CoversThreadBBox = false |
|
end |
|
|
|
local function HashPoint(pt) |
|
return string.format("point(%d,%d,%d)", pt:x(), pt:y(), pt:z()) |
|
end |
|
|
|
function AreCoversShown() |
|
return not not g_dbgCoversShown |
|
end |
|
|
|
local debugcover_offset = const.SlabSizeX / 2 - 10 * guim / 100 |
|
local debugcovers = { |
|
{ |
|
[const.CoverLow] = { cover = "DebugCoverDrawNorthLow", offset = point(0, -debugcover_offset, 0), angle = 90 * 60 }, |
|
[const.CoverHigh] = { cover = "DebugCoverDrawNorthHigh", offset = point(0, -debugcover_offset, 0), angle = 90 * 60 }, |
|
}, |
|
{ |
|
[const.CoverLow] = { cover = "DebugCoverDrawEastLow", offset = point(debugcover_offset, 0, 0), angle = 2 * 90 * 60 }, |
|
[const.CoverHigh] = { cover = "DebugCoverDrawEastHigh", offset = point(debugcover_offset, 0, 0), angle = 2 * 90 * 60 }, |
|
}, |
|
{ |
|
[const.CoverLow] = { cover = "DebugCoverDrawSouthLow", offset = point(0, debugcover_offset, 0), angle = 3 * 90 * 60 }, |
|
[const.CoverHigh] = { cover = "DebugCoverDrawSouthHigh", offset = point(0, debugcover_offset, 0), angle = 3 * 90 * 60 }, |
|
}, |
|
{ |
|
[const.CoverLow] = { cover = "DebugCoverDrawWestLow", offset = point(-debugcover_offset, 0, 0), angle = 0 }, |
|
[const.CoverHigh] = { cover = "DebugCoverDrawWestHigh", offset = point(-debugcover_offset, 0, 0), angle = 0 }, |
|
} |
|
} |
|
|
|
function DbgDrawCovers(dbg, bbox, dont_toggle, dont_rebuild) |
|
local total = GetClock() |
|
dbg = dbg or "" |
|
|
|
local old = MapGet(s_CoversUpdateOperationInProgress and bbox or "map", "DebugCoverDraw") |
|
for _, dbg in ipairs(old) do |
|
DoneObject(dbg) |
|
end |
|
|
|
|
|
if not dont_toggle then |
|
g_dbgCoversShown = not g_dbgCoversShown |
|
end |
|
if not g_dbgCoversShown then |
|
DbgDrawPassSlabs(false) |
|
StopCoversUpdateThread() |
|
if not IsEditorActive() then |
|
StartWallInvisibilityThread() |
|
HideFloorsAbove(999) |
|
else |
|
XEditorFiltersUpdateVisibility() |
|
end |
|
Msg("DbgCoversUpdated", bbox) |
|
return |
|
end |
|
g_dbgCoversShown = dbg or g_dbgCoversShown |
|
|
|
local dbg_draw_pass_objs = DbgDrawPassSlabs("on", "terrain pass", nil, bbox) |
|
|
|
local rebuild = GetClock() |
|
bbox = bbox or GetMapBox() |
|
if not dont_rebuild then |
|
bbox = RebuildCovers(bbox) |
|
end |
|
rebuild = GetClock() - rebuild |
|
|
|
local floor = IsEditorActive() and XEditorFilters:GetFilter("HideFloor") or 0 |
|
if floor == 0 then floor = 999 end |
|
BuildBuildingsData() |
|
|
|
local ptExtend = point(const.SlabSizeX, const.SlabSizeY, const.SlabSizeZ * 2) |
|
ForEachCover(bbox, -1, function(x, y, z, up, right, down, left) |
|
z = z or terrain.GetHeight(x, y) |
|
local pt = point(x, y, z) |
|
local bbox_room = Extend(empty_box, pt):grow(ptExtend) |
|
local room = EnumVolumes(bbox_room, "smallest") |
|
local bld_meta = VolumeBuildingsMeta[room and room.building or false] |
|
if bld_meta and bld_meta[floor] and z >= bld_meta[floor].box:minz() - 5 * guim / 10 then |
|
return |
|
end |
|
for i, val in ipairs{ up, right, down, left } do |
|
local t = debugcovers[i][val] |
|
if t then |
|
local cover = PlaceObject(t.cover) |
|
cover:SetPos(pt + t.offset) |
|
cover:SetAngle(t.angle) |
|
end |
|
end |
|
end) |
|
|
|
if string.match(dbg, "box") then |
|
local pt1 = bbox:min() |
|
local x, y, z = bbox:sizexyz() |
|
local xx, yy, zz = point(x, 0, 0), point(0, y, 0), point(0, 0, z) |
|
DbgAddPoly({ |
|
pt1, pt1 + xx, pt1 + xx + yy, pt1 + yy,pt1, |
|
pt1 + zz, pt1 + zz + xx, pt1 + zz + xx + yy, pt1 + zz + yy, pt1 + zz}) |
|
DbgAddPoly({pt1 + xx, pt1 + xx + zz}) |
|
DbgAddPoly({pt1 + xx + yy, pt1 + xx + yy + zz}) |
|
DbgAddPoly({pt1 + yy, pt1 + yy + zz}) |
|
end |
|
|
|
total = GetClock() - total |
|
|
|
if not s_CoversUpdateOperationInProgress then |
|
local count = MapCount(bbox, "DebugCoverDraw") |
|
print(string.format("Covers: %d, DebugPassDraw objects: %d, Rebuild: %dms, Debug Info: %dms, Total: %dms", count, dbg_draw_pass_objs, rebuild, total - rebuild, total)) |
|
end |
|
|
|
if not IsEditorActive() then |
|
StartWallInvisibilityThread() |
|
end |
|
Msg("DbgCoversUpdated", bbox) |
|
end |
|
|
|
local function DbgCoverUpdate(op_finished, bbox, objects) |
|
if not g_dbgCoversShown then return end |
|
|
|
bbox = GetCoversVoxelPatchAlignedTaskBox(bbox) |
|
s_CoversThreadBBox = s_CoversThreadBBox and AddRects(s_CoversThreadBBox, bbox) or bbox |
|
if op_finished then |
|
DbgDrawCovers(g_dbgCoversShown, s_CoversThreadBBox, "don't toggle") |
|
if s_CoversUpdateOperationInProgress then |
|
s_CoversUpdateOperationInProgress = false |
|
StopCoversUpdateThread() |
|
end |
|
return |
|
end |
|
|
|
s_CoversUpdateOperationInProgress = true |
|
DeleteThread(s_CoversUpdateThread) |
|
s_CoversUpdateThread = CreateMapRealTimeThread(function() |
|
Sleep(200) |
|
local resume = ArePassEditsForEditOpSuspended() |
|
if resume then |
|
ResumePassEditsForEditOp() |
|
end |
|
for _, obj in ipairs(objects) do |
|
obj:ApplySurfaces() |
|
end |
|
if resume then |
|
SuspendPassEditsForEditOp() |
|
end |
|
terrain.RebuildPassability(s_CoversThreadBBox) |
|
DbgDrawCovers(g_dbgCoversShown, s_CoversThreadBBox, "don't toggle") |
|
s_CoversUpdateThread = false |
|
s_CoversThreadBBox = false |
|
end) |
|
end |
|
|
|
function OnMsg.EditorCategoryFilterChanged(c, filter) |
|
if c == "HideFloor" and g_dbgCoversShown then |
|
DbgDrawCovers(nil, nil, "dont_toggle") |
|
end |
|
end |
|
|
|
function OnMsg.EditorHeightChanged(op_finished, bbox) |
|
DbgCoverUpdate(op_finished, bbox) |
|
end |
|
|
|
function OnMsg.OnPassabilityChanged(bbox) |
|
if not s_CoversUpdateOperationInProgress then |
|
DelayedCall(0, DbgCoverUpdate, true, bbox) |
|
end |
|
end |
|
|
|
local function CoversUpdate(op_finished, objects) |
|
local bbox = GetObjectsBBox(objects):SetInvalidZ() |
|
local ptExtend = point(const.SlabSizeX, const.SlabSizeY) |
|
bbox = box(bbox:min() - ptExtend, bbox:max() + ptExtend) |
|
DbgCoverUpdate(op_finished, bbox, objects) |
|
end |
|
|
|
function OnMsg.EditorObjectOperation(op_finished, objects) |
|
CoversUpdate(op_finished, objects) |
|
end |
|
|
|
function OnMsg.EditorCallbackPreUndoRedo(objects) |
|
CoversUpdate(false, objects) |
|
end |
|
|
|
function OnMsg.CombatObjectDied(obj, bbox) |
|
DbgCoverUpdate(false, bbox) |
|
end |
|
|
|
local function DbgCoverRemoveInvalidWallSlabs(maps) |
|
if not IsRealTimeThread() then |
|
CreateRealTimeThread(DbgCoverRemoveInvalidWallSlabs, maps) |
|
return |
|
end |
|
|
|
maps = maps or ListMaps() |
|
if type(maps) == "string" and string.match(maps, "current") then |
|
maps = {GetMapName()} |
|
end |
|
ForEachMap(maps, function() |
|
local to_remove = setmetatable({}, weak_values_meta) |
|
MapForEach("map", "WallSlab", function(obj) |
|
if obj.class == "WallSlab" and not IsValidEntity(obj:GetEntity()) then |
|
table.insert(to_remove, obj) |
|
end |
|
end) |
|
for _, obj in ipairs(to_remove) do |
|
DoneObject(obj) |
|
end |
|
print(string.format("Removed %d invalid WallSlab objects.", #to_remove)) |
|
SaveMap("no backup") |
|
end) |
|
end |
|
|
|
DefineClass.DebugVisDummy = |
|
{ |
|
__parents = { "AppearanceObject" }, |
|
flags = { |
|
efSelectable = false, efWalkable = false, efCollision = false, |
|
efApplyToGrids = false, efShadow = false, efSunShadow = false, |
|
cfConstructible = false, |
|
cofComponentColorizationMaterial = true, |
|
cofComponentCollider = false, |
|
}, |
|
attack_color_modifier = RGB(0, 200, 100), |
|
target_color_modifier = RGB(0, 100, 200), |
|
template_id = "Barry", |
|
|
|
__toluacode = Object.__toluacode, |
|
|
|
CloneFrom = function(self, obj) |
|
local appearance = obj and obj.Appearance or ChooseUnitAppearance(self.template_id, self.handle) |
|
self:ApplyAppearance(appearance) |
|
if obj then |
|
self.stance = obj.stance |
|
self.current_weapon = obj.current_weapon |
|
end |
|
local weapon1, weapon2 |
|
if obj then |
|
weapon1, weapon2 = obj:GetActiveWeapons() |
|
else |
|
weapon1 = Firearm |
|
end |
|
local wobj1 = IsKindOf(weapon1, "Firearm") and weapon1:CreateVisualObj(self) |
|
local wobj2 = IsKindOf(weapon2, "Firearm") and weapon2:CreateVisualObj(self) |
|
|
|
local attached_weapons = self:GetAttaches("WeaponVisual") |
|
for i, o in ipairs(attached_weapons or empty_table) do |
|
if o ~= wobj1 and o ~= wobj2 then |
|
DoneObject(o) |
|
end |
|
end |
|
if wobj1 then |
|
self:Attach(wobj1, self:GetSpotBeginIndex("Weaponr")) |
|
end |
|
if wobj2 then |
|
self:Attach(wobj2, self:GetSpotBeginIndex("Weaponl")) |
|
end |
|
|
|
self:SetState(obj:GetState(), 0, 0) |
|
local phase = self:GetAnimMoment(self:GetStateText(), "hit") or 0 |
|
self:SetAnimPhase(1, phase) |
|
self:SetAngle(obj:GetAngle()) |
|
self:SetPos(obj:GetPos()) |
|
end, |
|
} |
|
|
|
local function DbgPlaceAttackDummy(obj, pos, angle, anim, phase, target_pos) |
|
local o = PlaceObject("DebugVisDummy") |
|
o:CloneFrom(obj) |
|
o:SetColorModifier(o.attack_color_modifier) |
|
for i, attach in ipairs(o:GetAttaches()) do |
|
attach:SetColorModifier(o.attack_color_modifier) |
|
end |
|
pos = pos or obj:GetPos() |
|
angle = target_pos and CalcOrientation(pos, target_pos) or angle or obj:GetAngle() |
|
anim = anim or obj:GetActionRandomAnim("Fire", obj.stance or "Standing") |
|
phase = phase or obj:GetAnimMoment(anim, "hit") or 0 |
|
o:SetState(anim, 0, 0) |
|
o:SetAnimPhase(1, phase) |
|
o:SetAnimSpeed(1, 0) |
|
o:SetAngle(angle) |
|
o:SetPos(pos) |
|
if target_pos then |
|
local ikCmp = o:GetAnimComponentIndexFromLabel(1, "AimIK") |
|
if ikCmp ~= 0 then |
|
o:SetAnimComponentTarget(1, ikCmp, target_pos, InvalidPos(), 0, 0) |
|
end |
|
end |
|
return o |
|
end |
|
|
|
local function DbgPlaceTargetDummy(obj, dummy) |
|
local o = PlaceObject("DebugVisDummy") |
|
o:CloneFrom(obj) |
|
o:SetColorModifier(o.target_color_modifier) |
|
for i, attach in ipairs(o:GetAttaches()) do |
|
attach:SetColorModifier(o.target_color_modifier) |
|
end |
|
if not IsValid(dummy) and type(dummy) == "table" then |
|
if dummy.anim then |
|
o:SetStateText(dummy.anim, 0, 0) |
|
local phase = o:GetAnimMoment(dummy.anim, "hit") or 0 |
|
o:SetAnimPhase(1, phase) |
|
end |
|
if dummy.phase then |
|
o:SetAnimPhase(1, dummy.phase) |
|
end |
|
if dummy.angle then |
|
o:SetAngle(dummy.angle) |
|
end |
|
if dummy.pos then |
|
o:SetPos(dummy.pos) |
|
end |
|
end |
|
o:SetAnimSpeed(1, 0) |
|
return o |
|
end |
|
|
|
local function ClearTargetObjects() |
|
local dummies = s_DbgDrawLOF_Objects |
|
for k, obj in ipairs(dummies) do |
|
if IsValid(obj) then |
|
DoneObject(obj) |
|
end |
|
end |
|
table.iclear(dummies) |
|
end |
|
|
|
local clrClearHit = RGB(0, 200, 100) |
|
local clrObstructionHit = RGB(250, 100, 0) |
|
local clrStuckToTarget = RGB(30, 30, 30) |
|
local clrAttackPos = RGB(0, 50, 255) |
|
local clrForcedTargetHit = RGB(0, 130, 150) |
|
local clrCollisionTarget = RGB(0, 255, 100) |
|
local clrCollisionPierce = RGB(255, 100, 0) |
|
local clrCollisionIgnored = RGB(160, 160, 160) |
|
local clrCollisionStuckPower = RGB(255, 255, 0) |
|
local clrCollisionImpenetrable = RGB(255, 0, 0) |
|
local clrLOSLines = { |
|
const.clrWhite, |
|
const.clrRed, |
|
const.clrGreen, |
|
const.clrCyan, |
|
const.clrBlue, |
|
const.clrPink, |
|
const.clrYellow, |
|
const.clrOrange, |
|
const.clrMagenta, |
|
} |
|
|
|
local function AddLine(p1, p2, color) |
|
local path = pstr("") |
|
path:AppendVertex(p1, color) |
|
path:AppendVertex(p2) |
|
|
|
local line = PlaceObject("Polyline") |
|
table.insert(s_DbgDrawLOF_Objects, line) |
|
line:SetPos(p1) |
|
line:SetMesh(path) |
|
end |
|
|
|
local function AddCollision(pos, clr, power) |
|
local collision = PlaceObject("Mesh") |
|
collision:SetMesh(CreateSphereVertices(3*guic, clr)) |
|
table.insert(s_DbgDrawLOF_Objects, collision) |
|
collision:SetPos(pos) |
|
if power then |
|
local text = PlaceObject("Text") |
|
table.insert(s_DbgDrawLOF_Objects, text) |
|
text:SetText(string.format("%d", power)) |
|
text:SetPos(pos + point(0, 0, 5 * guim / 100)) |
|
text:SetTextStyle("BugReportScreenshot") |
|
text:SetColor(clr) |
|
end |
|
end |
|
|
|
local function DbgLOFGetTargets(unit) |
|
local targets = MapGet("map", {"ExplosiveContainer", "Landmine"}) or {} |
|
for i = #targets, 1, -1 do |
|
if targets[i]:IsDead() then |
|
table.remove(targets, i) |
|
end |
|
end |
|
local enemies = GetEnemies(unit) |
|
for _, enemy in ipairs(enemies) do |
|
if not enemy:IsDead() then |
|
table.insert(targets, enemy) |
|
end |
|
end |
|
table.sortby_field(targets, "handle") |
|
return targets |
|
end |
|
|
|
function DbgDrawLOF(targets, attacker, pos) |
|
ClearTargetObjects() |
|
if targets == false then |
|
return |
|
end |
|
if attacker == nil and IsKindOf(SelectedObj, "Unit") then |
|
attacker = SelectedObj |
|
end |
|
if not IsValid(attacker) then |
|
return |
|
end |
|
if IsValid(targets) then |
|
targets = { targets } |
|
end |
|
if not targets and IsKindOf(attacker, "Unit") then |
|
targets = DbgLOFGetTargets(attacker) |
|
end |
|
if not targets or #targets == 0 then |
|
return |
|
end |
|
local default_attack = attacker:GetDefaultAttackAction() |
|
local weapons |
|
if default_attack then |
|
local weapon1, weapon2 |
|
weapon1, weapon2, weapons = default_attack:GetAttackWeapons(attacker) |
|
weapons = weapons or { weapon1 } |
|
end |
|
|
|
local lof_params = { |
|
obj = attacker, |
|
step_pos = GetPassSlab(pos or attacker) or pos or attacker:GetPos(), |
|
action_id = default_attack.id, |
|
weapon = weapons[1], |
|
stance = attacker.stance, |
|
prediction = true, |
|
output_collisions = true, |
|
group_spots = false, |
|
can_use_covers = true, |
|
output_ignored_hits = true, |
|
force_hit_seen_target = not config.DisableForcedHitSeenTarget, |
|
ignore_smoke = s_DbgDrawIgnoreSmoke, |
|
output_all_segments = true, |
|
} |
|
if IsKindOf(weapons[1], "FirearmBase") and weapons[1].emplacement_weapon then |
|
lof_params.emplacement_weapon = true |
|
end |
|
|
|
local attack_dummies = GetAttackDummies(lof_params) |
|
|
|
local all_targets_attack_data = {} |
|
|
|
for attack_dummy_idx, dummy in ipairs(attack_dummies) do |
|
lof_params.stance = dummy.stance |
|
lof_params.los_stance = attacker.stance == "Crouch" and "Crouch" or dummy.stance |
|
lof_params.step_pos = dummy.step_pos |
|
lof_params.angle = dummy.angle |
|
lof_params.cone_angle = dummy.cone_angle |
|
lof_params.can_use_covers = false |
|
|
|
for k, weapon in ipairs(weapons) do |
|
lof_params.weapon = weapon |
|
local targets_attack_data = GetLoFData(attacker, targets, lof_params) |
|
all_targets_attack_data[attack_dummy_idx] = all_targets_attack_data[attack_dummy_idx] or targets_attack_data |
|
|
|
for target_idx, target in ipairs(targets) do |
|
local attack_data = targets_attack_data[target_idx] |
|
if attack_data and attack_data.lof and not (attack_dummy_idx > 1 and attacker.stance == "Crouch" and all_targets_attack_data[1][target_idx].los == 0) then |
|
if not attack_data.emplacement_weapon then |
|
local target_pos = attack_data.target_pos |
|
if not target_pos then |
|
local idx = table.find(attack_data.lof, "target_spot", "Torso") or 1 |
|
target_pos = attack_data.lof[1] and attack_data.lof[1].lof_pos2 |
|
end |
|
local o = DbgPlaceAttackDummy(attacker, attack_data.step_pos, attack_data.angle, attack_data.anim, attack_data.phase, target_pos) |
|
table.insert(s_DbgDrawLOF_Objects, o) |
|
end |
|
for j, line_data in ipairs(attack_data.lof) do |
|
if (line_data.eye_hit or false) == s_DbgDrawLOF_EYE then |
|
local pos = line_data.lof_pos1 |
|
AddLine(pos, line_data.attack_pos, clrAttackPos) |
|
pos = line_data.attack_pos |
|
local target_pos = line_data.target_pos |
|
local clrLine = clrClearHit |
|
for k, collision_data in ipairs(line_data.hits) do |
|
local hit_pos = collision_data.pos |
|
AddLine(pos, hit_pos, clrLine) |
|
if collision_data.ignored then |
|
if not IsKindOf(collision_data.obj, "SmokeObj") then |
|
clrLine = clrForcedTargetHit |
|
end |
|
else |
|
clrLine = clrObstructionHit |
|
end |
|
local clrHit |
|
if k == 1 and collision_data.obj == target then |
|
clrHit = clrCollisionTarget |
|
elseif collision_data.ignored and IsKindOf(collision_data.obj, "SmokeObj") then |
|
clrHit = clrClearHit |
|
elseif k < #line_data.hits then |
|
clrHit = clrCollisionPierce |
|
else |
|
clrHit = clrCollisionStuckPower |
|
end |
|
AddCollision(hit_pos, clrHit) |
|
pos = hit_pos |
|
end |
|
if IsCloser(line_data.lof_pos1, pos, target_pos) then |
|
AddLine(pos, target_pos, clrStuckToTarget) |
|
end |
|
end |
|
end |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
function DbgDrawLOS(targets, attacker) |
|
ClearTargetObjects() |
|
if targets == false then |
|
return |
|
end |
|
if attacker == nil and IsKindOf(SelectedObj, "Unit") then |
|
attacker = SelectedObj |
|
end |
|
if IsValid(targets) then |
|
targets = { targets } |
|
end |
|
if targets == nil and attacker and IsKindOf(attacker, "Unit") then |
|
targets = DbgLOFGetTargets(attacker) |
|
end |
|
if not attacker or not targets or #targets == 0 then |
|
return |
|
end |
|
local attacker_pos = GetPassSlab(attacker) or attacker:GetPos() |
|
local lof_params = { |
|
obj = attacker, |
|
step_pos = attacker_pos, |
|
stance = attacker.stance, |
|
can_use_covers = true, |
|
} |
|
local attack_dummies = GetAttackDummies(lof_params) |
|
if attacker.stance == "Crouch" then |
|
attack_dummies = { attack_dummies[1] } |
|
end |
|
for i, dummy in ipairs(attack_dummies) do |
|
if dummy.step_pos ~= attacker_pos then |
|
local dummy_obj = DbgPlaceAttackDummy(attacker, dummy.step_pos, dummy.angle, dummy.anim, dummy.phase, dummy.target_pos) |
|
table.insert(s_DbgDrawLOF_Objects, dummy_obj) |
|
end |
|
end |
|
local los = DebugLOS(targets, attacker, -1, attacker.stance) |
|
for i, line_data in ipairs(los) do |
|
local start_pos = line_data.pos1 |
|
local target_pos = line_data.pos2 |
|
local clr = line_data.los_level == 1 and const.clrGreen or const.clrBlue |
|
if line_data.hits and #line_data.hits > 0 then |
|
local hit_pos = line_data.hits[1].pos |
|
AddLine(start_pos, hit_pos, clr) |
|
AddLine(hit_pos, target_pos, clrStuckToTarget) |
|
for k, collision_data in ipairs(line_data.hits) do |
|
AddCollision(collision_data.pos, clrStuckToTarget) |
|
end |
|
else |
|
AddLine(start_pos, target_pos, clr) |
|
AddCollision(target_pos, clr) |
|
end |
|
end |
|
end |
|
|
|
local function DbgUpdateLOFLines(unit) |
|
if unit == nil then |
|
unit = SelectedObj |
|
end |
|
if s_DbgDrawLOF then |
|
if unit and unit == SelectedObj then |
|
DbgDrawLOF(nil, unit) |
|
elseif not SelectedObj then |
|
DbgDrawLOF(false) |
|
end |
|
end |
|
if s_DbgDrawLOS then |
|
if unit and unit == SelectedObj then |
|
DbgDrawLOS(nil, unit) |
|
elseif not SelectedObj then |
|
DbgDrawLOS(false) |
|
end |
|
end |
|
end |
|
|
|
OnMsg.UnitMovementDone = DbgUpdateLOFLines |
|
OnMsg.UnitStanceChanged = DbgUpdateLOFLines |
|
OnMsg.SelectionChange = DbgUpdateLOFLines |
|
|
|
function DbgDrawToggleLOS() |
|
PauseInfiniteLoopDetection("DebugLOSVis") |
|
if s_DbgDrawLOS then |
|
s_DbgDrawLOS = false |
|
DbgDrawLOS(false) |
|
else |
|
if s_DbgDrawLOF then |
|
DbgDrawToggleLOF() |
|
end |
|
s_DbgDrawLOS = true |
|
DbgDrawLOS() |
|
end |
|
ResumeInfiniteLoopDetection("DebugLOSVis") |
|
end |
|
|
|
function DbgDrawToggleLOF() |
|
PauseInfiniteLoopDetection("DebugLOFVis") |
|
if s_DbgDrawLOF then |
|
s_DbgDrawLOF = false |
|
DbgDrawLOF(false) |
|
else |
|
if s_DbgDrawLOS then |
|
DbgDrawToggleLOS() |
|
end |
|
s_DbgDrawLOF = true |
|
DbgDrawLOF() |
|
end |
|
ResumeInfiniteLoopDetection("DebugLOFVis") |
|
end |
|
|
|
function DbgDrawLOFNext() |
|
if s_DbgDrawLOS then |
|
DbgDrawToggleLOS() |
|
end |
|
s_DbgDrawLOF = true |
|
local targets = DbgLOFGetTargets(SelectedObj) |
|
s_DbgDrawLastTarget = targets[(table.find(targets, s_DbgDrawLastTarget) or 0) + 1] or targets[1] or false |
|
DbgDrawLOF(s_DbgDrawLastTarget, SelectedObj) |
|
end |
|
|
|
|
|
local function UpdateCoverDebug() |
|
if g_dbgCoversShown then |
|
DbgDrawCovers(nil, nil, "dont_toggle") |
|
end |
|
end |
|
|
|
OnMsg.GameEnterEditor = UpdateCoverDebug |
|
OnMsg.GameExitEditor = UpdateCoverDebug |
|
|
|
local target_dummy_color_modifier = RGB(30, 0, 100) |
|
local function ShowTargetDummy(obj) |
|
if not obj then return end |
|
obj:SetColorModifier(target_dummy_color_modifier) |
|
for i, attach in ipairs(obj:GetAttaches()) do |
|
attach:SetColorModifier(target_dummy_color_modifier) |
|
end |
|
obj:SetVisible(true) |
|
|
|
end |
|
|
|
function DbgDrawShowTargetDummies(show) |
|
s_DbgDrawTargetDummies = show or false |
|
if show then |
|
MapForEach("map", "TargetDummy", ShowTargetDummy) |
|
else |
|
MapForEach("map", "TargetDummy", Object.SetVisible, false) |
|
end |
|
end |
|
|
|
function DbgDrawToggleTargetDummies() |
|
DbgDrawShowTargetDummies(not s_DbgDrawTargetDummies) |
|
end |
|
|
|
OnMsg.NewTargetDummy = Object.SetVisible |
|
function OnMsg.NewTargetDummy(obj) |
|
if s_DbgDrawTargetDummies then |
|
ShowTargetDummy(obj) |
|
end |
|
end |
|
|
|
function DumpStepVectors() |
|
local entities = { "EquipmentBarry_Top", "EquipmentLivewire_Top", "Animal_Hyena", "Animal_Crocodile", "Animal_Hen" } |
|
local filename = string.format("TmpData/AnimStepData.log") |
|
local f = io.open(filename, "w+") |
|
local log = {} |
|
for i, entity in ipairs(entities) do |
|
local states = GetStates(entity) |
|
for j, anim in ipairs(states) do |
|
local step_len = GetStepLength(entity, anim) |
|
if step_len ~= 0 then |
|
local duration = GetAnimDuration(entity, anim) |
|
local compensate = GetAnimCompensate(entity, anim) |
|
local txt = string.format('Entity "%s", Anim: "%s", Duration: %d, %s, Step Lenght: %d\n', entity, anim, duration, compensate, step_len) |
|
table.insert(log, txt) |
|
local last_step = point30 |
|
for phase = 1, duration do |
|
local step = GetEntityStepVector(entity, anim, 0, phase) |
|
if step ~= last_step then |
|
last_step = step |
|
table.insert(log, string.format("%10d : %5d, %d, %d\n", phase, step:xyz())) |
|
end |
|
end |
|
table.insert(log, "\n") |
|
f:write(log) |
|
table.iclear(log) |
|
end |
|
end |
|
end |
|
f:close() |
|
end |
|
|