From e2dcdf131bc3d958268d3f0a47050c36882d6bd4 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:39:52 -0800 Subject: [PATCH 01/11] Network link entities to client --- lua/acf/menu/operations/acf_menu.lua | 75 +++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/lua/acf/menu/operations/acf_menu.lua b/lua/acf/menu/operations/acf_menu.lua index 2e771423e..d1d934991 100644 --- a/lua/acf/menu/operations/acf_menu.lua +++ b/lua/acf/menu/operations/acf_menu.lua @@ -9,6 +9,71 @@ do -- Generic Spawner/Linker operation creator local NameFormat = "%s[ID: %s]" local PlayerEnts = {} + local CL_InLinkState = false + + if CLIENT then + net.Receive("ACF_MenuLinking", function() + local StateChange = net.ReadUInt(2) + if StateChange == 0 then -- Entering link state + CL_InLinkState = true + table.Empty(PlayerEnts) + elseif StateChange == 1 then -- Exiting link state + CL_InLinkState = false + table.Empty(PlayerEnts) + elseif StateChange == 2 then -- Adding entity to link table + PlayerEnts[net.ReadEntity()] = true + elseif StateChange == 3 then -- Removing entity from link table + PlayerEnts[net.ReadEntity()] = false + end + end) + end + + local function S2C_EnterLinkState(ply) + if CLIENT then return end + + net.Start("ACF_MenuLinking") + net.WriteUInt(0, 2) + net.Send(ply) + end + + local function S2C_ExitLinkState(ply) + if CLIENT then return end + + net.Start("ACF_MenuLinking") + net.WriteUInt(1, 2) + net.Send(ply) + end + + local function S2C_AddLinkEnt(ply, ent) + if CLIENT then return end + + net.Start("ACF_MenuLinking") + net.WriteUInt(2, 2) + net.WriteEntity(ent) + net.Send(ply) + end + + local function S2C_RemoveLinkEnt(ply, ent) + if CLIENT then return end + + net.Start("ACF_MenuLinking") + net.WriteUInt(3, 2) + net.WriteEntity(ent) + net.Send(ply) + end + + function ACF.ToolCL_InLinkState() + return CL_InLinkState + end + + function ACF.ToolCL_GetLinkedEnts() + return PlayerEnts + end + + if SERVER then + util.AddNetworkString("ACF_MenuLinking") + end + local function GetPlayerEnts(Player) local Ents = PlayerEnts[Player] @@ -86,6 +151,7 @@ do -- Generic Spawner/Linker operation creator Entity:SetColor(EntColor) Ents[Entity] = nil + S2C_RemoveLinkEnt(Player, Entity) if not next(Ents) then Tool:SetMode("Spawner", Name) @@ -100,9 +166,11 @@ do -- Generic Spawner/Linker operation creator if not next(Ents) then Tool:SetMode("Linker", Name) + S2C_EnterLinkState(Player) end Ents[Entity] = Entity:GetColor() + S2C_AddLinkEnt(Player, Entity) Entity:CallOnRemove("ACF_ToolLinking", UnselectEntity, Name, Tool) Entity:SetColor(Green) @@ -263,14 +331,15 @@ do -- Generic Spawner/Linker operation creator if Trace.HitWorld then Tool:Holster() return true end local Entity = Trace.Entity + local Player = Tool:GetOwner() - if not IsValid(Entity) then return false end + if not IsValid(Entity) then S2C_ExitLinkState(Player) return false end - local Player = Tool:GetOwner() local Ents = GetPlayerEnts(Player) if not Player:KeyDown(IN_SPEED) then LinkEntities(Player, Name, Tool, Entity, Ents) + S2C_ExitLinkState(Player) return true end @@ -292,6 +361,8 @@ do -- Generic Spawner/Linker operation creator for Entity in pairs(Ents) do UnselectEntity(Entity, Name, Tool) end + + S2C_ExitLinkState(Player) end, }) From 8f9e352d36b24ef0a58be51182b7ac805e1ecd91 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:40:09 -0800 Subject: [PATCH 02/11] Link gizmo renderer (needs work) --- lua/acf/core/utilities/util_cl.lua | 104 +++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 1c8530407..ad9223991 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -935,4 +935,108 @@ do -- Default turret menus Menu:AddLabel(MassText:format(Data.Mass)) end end +end + +-- Link distance gizmo stuff +do + local EntGizmoDifferences = {} + + function ACF.ToolCL_RegisterLinkGizmoData(from, to, callback) + EntGizmoDifferences[from] = EntGizmoDifferences[from] or {} + EntGizmoDifferences[to] = EntGizmoDifferences[to] or {} + + EntGizmoDifferences[from][to] = callback + EntGizmoDifferences[to][from] = callback + end + + function ACF.ToolCL_GetLinkGizmoData(entFrom, entTo) + local fromTbl = EntGizmoDifferences[entFrom:GetClass()] + if not fromTbl then return end + + local toTbl = fromTbl[entTo:GetClass()] + if not toTbl then return end + + return true, toTbl(entFrom, entTo) + end + + function ACF.ToolCL_CanLink(from, to) + if not IsValid(from) then return false, "Link target not valid!" end + if not IsValid(to) then return false, "Target not valid!" end + local hadData, canLink, whyNot = ACF.ToolCL_GetLinkGizmoData(from, to) + if not hadData then return false, "No link data." end + return canLink == nil and true or canLink, whyNot + end + + ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", function(from, to) + if from:GetPos():Distance(to:GetPos()) > ACF.LinkDistance then return false, "The entity is too far away." end + end) + + ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", function(from, to) + if from:GetPos():Distance(to:GetPos()) > ACF.MobilityLinkDistance then return false, "The entity is too far away." end + end) + + local COLOR_Black = Color(0, 0, 0, 255) + local COLOR_Link_OK = Color(55, 235, 55, 255) + local COLOR_Link_Fail = Color(235, 99, 99, 255) + local COLOR_Link = Color(205, 235, 255, 255) + + local HUDText = {} + + local function DrawText(text, color, x, y) + if not y then + local xy = x:ToScreen() + x, y = xy.x, xy.y + end + + HUDText[#HUDText + 1] = {Text = text, X = x, Y = y, Color = color} + end + + local distText = "Distance: %.1f units" + local distTextOK = "✓ OK" + local distTextNo = "✗ Cannot link: %s" + + hook.Add("PostDrawTranslucentRenderables", "ACF_PostDrawTranslucentRenderables_LinkDistanceVis", function() + if not ACF.ToolCL_InLinkState() then return end + table.Empty(HUDText) + + local eyeTrace = LocalPlayer():GetEyeTrace() + local lookEnt = eyeTrace.Entity + local lookPos = eyeTrace.HitPos + local lookingAtEntity = IsValid(lookEnt) + local linkEnts = ACF.ToolCL_GetLinkedEnts() + + for ent in pairs(linkEnts) do + if IsValid(ent) then + local targPos = lookingAtEntity and lookEnt:GetPos() or lookPos + local entPos = ent:GetPos() + + local inbetween = (entPos + targPos) / 2 + local dist = entPos:Distance(targPos) + + local linkcolor = COLOR_Link + if lookingAtEntity then + local canLink, why = ACF.ToolCL_CanLink(ent, lookEnt, dist) + linkcolor = canLink and COLOR_Link_OK or COLOR_Link_Fail + local linkText = canLink and distTextOK or distTextNo:format(why) + DrawText(linkText, linkcolor, inbetween) + else + DrawText(distText:format(dist), linkcolor, inbetween) + end + + render.SetColorMaterial() + render.DepthRange(0, 0) + render.DrawBeam(entPos, targPos, 2, 0, 1, COLOR_Black) + render.DrawBeam(entPos, targPos, 1, 0, 1, linkcolor) + render.DepthRange(0, 1) + end + end + end) + + hook.Add("HUDPaint", "ACF_HUDPaint_LinkDistanceVis", function() + if not ACF.ToolCL_InLinkState() then return end + + for _, v in ipairs(HUDText) do + draw.SimpleTextOutlined(v.Text, "ACF_Title", v.X, v.Y, v.Color or color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, color_black) + end + end) end \ No newline at end of file From 28bcf2ba4c6eba0ebc01440bc53fdc9bb923ccc3 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:55:02 -0800 Subject: [PATCH 03/11] Custom renderers for gizmo --- lua/acf/core/utilities/util_cl.lua | 67 +++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index ad9223991..244053559 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -941,6 +941,14 @@ end do local EntGizmoDifferences = {} + + local COLOR_Black = Color(0, 0, 0, 255) + local COLOR_Link_OK = Color(55, 235, 55, 255) + local COLOR_Link_Fail = Color(255, 88, 88) + local COLOR_Link_FailDistMissed = Color(175, 0, 0) + local COLOR_Link = Color(205, 235, 255, 255) + + function ACF.ToolCL_RegisterLinkGizmoData(from, to, callback) EntGizmoDifferences[from] = EntGizmoDifferences[from] or {} EntGizmoDifferences[to] = EntGizmoDifferences[to] or {} @@ -962,24 +970,43 @@ do function ACF.ToolCL_CanLink(from, to) if not IsValid(from) then return false, "Link target not valid!" end if not IsValid(to) then return false, "Target not valid!" end - local hadData, canLink, whyNot = ACF.ToolCL_GetLinkGizmoData(from, to) + local hadData, canLink, whyNot, renderData = ACF.ToolCL_GetLinkGizmoData(from, to) if not hadData then return false, "No link data." end - return canLink == nil and true or canLink, whyNot + return canLink == nil and true or canLink, whyNot, renderData end + local ACF_LinkDistanceTooFar = { + Text = "The entity is too far away.", + Renderer = function(data, from, to) + local fromPos, toPos = data.fromPos, data.toPos + local normal = (toPos - fromPos):GetNormalized() + local toMaxDist = fromPos + (normal * data.maxdist) + local dist = data.dist + + render.SetColorMaterial() + render.DepthRange(0, 0) + render.DrawBeam(fromPos, toMaxDist, 2, 0, 1, COLOR_Black) + render.DrawBeam(toMaxDist, toPos, 2, 0, 1, COLOR_Black) + render.DrawBeam(fromPos, toMaxDist, 1, 0, 1, COLOR_Link_Fail) + render.DrawBeam(toMaxDist, toPos, 1, 0, 1, COLOR_Link_FailDistMissed) + render.DepthRange(0, 1) + end + } + ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", function(from, to) - if from:GetPos():Distance(to:GetPos()) > ACF.LinkDistance then return false, "The entity is too far away." end + local fromPos, toPos = from:GetPos(), to:GetPos() + local dist = fromPos:Distance(toPos) + local maxdist = ACF.LinkDistance + if dist > maxdist then return false, ACF_LinkDistanceTooFar, {fromPos = fromPos, toPos = toPos, dist = dist, maxdist = maxdist} end end) ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", function(from, to) - if from:GetPos():Distance(to:GetPos()) > ACF.MobilityLinkDistance then return false, "The entity is too far away." end + local fromPos, toPos = from:GetPos(), to:GetPos() + local dist = fromPos:Distance(toPos) + local maxdist = ACF.MobilityLinkDistance + if dist > maxdist then return false, ACF_LinkDistanceTooFar, {fromPos = fromPos, toPos = toPos, dist = dist, maxdist = maxdist} end end) - local COLOR_Black = Color(0, 0, 0, 255) - local COLOR_Link_OK = Color(55, 235, 55, 255) - local COLOR_Link_Fail = Color(235, 99, 99, 255) - local COLOR_Link = Color(205, 235, 255, 255) - local HUDText = {} local function DrawText(text, color, x, y) @@ -1014,20 +1041,28 @@ do local dist = entPos:Distance(targPos) local linkcolor = COLOR_Link + local renderOverride, renderData + if lookingAtEntity then - local canLink, why = ACF.ToolCL_CanLink(ent, lookEnt, dist) + local canLink, why, data = ACF.ToolCL_CanLink(ent, lookEnt, dist) linkcolor = canLink and COLOR_Link_OK or COLOR_Link_Fail - local linkText = canLink and distTextOK or distTextNo:format(why) + local linkText = canLink and distTextOK or distTextNo:format(why.Text) + renderOverride = why.Renderer + renderData = data DrawText(linkText, linkcolor, inbetween) else DrawText(distText:format(dist), linkcolor, inbetween) end - render.SetColorMaterial() - render.DepthRange(0, 0) - render.DrawBeam(entPos, targPos, 2, 0, 1, COLOR_Black) - render.DrawBeam(entPos, targPos, 1, 0, 1, linkcolor) - render.DepthRange(0, 1) + if renderOverride then + renderOverride(renderData, from, to) + else + render.SetColorMaterial() + render.DepthRange(0, 0) + render.DrawBeam(entPos, targPos, 2, 0, 1, COLOR_Black) + render.DrawBeam(entPos, targPos, 1, 0, 1, linkcolor) + render.DepthRange(0, 1) + end end end end) From 47670596a3cdfce457fbe09d6dc7a1e6539a4a3a Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:56:27 -0800 Subject: [PATCH 04/11] Fix linter issues --- lua/acf/core/utilities/util_cl.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 244053559..a4d563197 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -977,11 +977,10 @@ do local ACF_LinkDistanceTooFar = { Text = "The entity is too far away.", - Renderer = function(data, from, to) + Renderer = function(data, _, _) local fromPos, toPos = data.fromPos, data.toPos local normal = (toPos - fromPos):GetNormalized() local toMaxDist = fromPos + (normal * data.maxdist) - local dist = data.dist render.SetColorMaterial() render.DepthRange(0, 0) From 6c3dcdac70404829ce8f416b5bcb916d83f179ec Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 16:12:32 -0800 Subject: [PATCH 05/11] Make text stay within screen bounds --- lua/acf/core/utilities/util_cl.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index a4d563197..359d11aa2 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -1025,6 +1025,7 @@ do if not ACF.ToolCL_InLinkState() then return end table.Empty(HUDText) + local playerPos = LocalPlayer():GetPos() local eyeTrace = LocalPlayer():GetEyeTrace() local lookEnt = eyeTrace.Entity local lookPos = eyeTrace.HitPos @@ -1036,8 +1037,9 @@ do local targPos = lookingAtEntity and lookEnt:GetPos() or lookPos local entPos = ent:GetPos() - local inbetween = (entPos + targPos) / 2 local dist = entPos:Distance(targPos) + local player2targ = math.Clamp(playerPos:Distance(targPos) / 1.5, 0, dist / 2) + local inbetween = targPos + ((entPos - targPos):GetNormalized() * math.Clamp(dist, 0, player2targ)) local linkcolor = COLOR_Link local renderOverride, renderData @@ -1069,8 +1071,17 @@ do hook.Add("HUDPaint", "ACF_HUDPaint_LinkDistanceVis", function() if not ACF.ToolCL_InLinkState() then return end + local w, h = ScrW(), ScrH() + local padding = 16 + for _, v in ipairs(HUDText) do - draw.SimpleTextOutlined(v.Text, "ACF_Title", v.X, v.Y, v.Color or color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, color_black) + surface.SetFont("ACF_Title") + local tX, tY = surface.GetTextSize(v.Text) + tX = tX / 2 + tY = tY / 2 + local x, y = math.Clamp(v.X, tX + padding, w - tX - padding), math.Clamp(v.Y, tY + padding, h - tY - padding) + + draw.SimpleTextOutlined(v.Text, "ACF_Title", x, y, v.Color or color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, color_black) end end) end \ No newline at end of file From e11480877da01a9c0c90d54327f01b0c7e1d6ff4 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 16:14:40 -0800 Subject: [PATCH 06/11] Add link to self check --- lua/acf/core/utilities/util_cl.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 359d11aa2..e62c73fd7 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -970,6 +970,9 @@ do function ACF.ToolCL_CanLink(from, to) if not IsValid(from) then return false, "Link target not valid!" end if not IsValid(to) then return false, "Target not valid!" end + + if from == to then return false, "Cannot link an entity to itself!" end + local hadData, canLink, whyNot, renderData = ACF.ToolCL_GetLinkGizmoData(from, to) if not hadData then return false, "No link data." end return canLink == nil and true or canLink, whyNot, renderData @@ -1047,7 +1050,7 @@ do if lookingAtEntity then local canLink, why, data = ACF.ToolCL_CanLink(ent, lookEnt, dist) linkcolor = canLink and COLOR_Link_OK or COLOR_Link_Fail - local linkText = canLink and distTextOK or distTextNo:format(why.Text) + local linkText = canLink and distTextOK or distTextNo:format(why.Text and why.Text or why) renderOverride = why.Renderer renderData = data DrawText(linkText, linkcolor, inbetween) From 66f10d9b8e18358faa51aadca5415bc89c4083e8 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 20:07:09 -0800 Subject: [PATCH 07/11] Fix nil indexing --- lua/acf/core/utilities/util_cl.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index e62c73fd7..7abe6b36f 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -941,14 +941,12 @@ end do local EntGizmoDifferences = {} - local COLOR_Black = Color(0, 0, 0, 255) local COLOR_Link_OK = Color(55, 235, 55, 255) local COLOR_Link_Fail = Color(255, 88, 88) local COLOR_Link_FailDistMissed = Color(175, 0, 0) local COLOR_Link = Color(205, 235, 255, 255) - function ACF.ToolCL_RegisterLinkGizmoData(from, to, callback) EntGizmoDifferences[from] = EntGizmoDifferences[from] or {} EntGizmoDifferences[to] = EntGizmoDifferences[to] or {} @@ -1051,8 +1049,10 @@ do local canLink, why, data = ACF.ToolCL_CanLink(ent, lookEnt, dist) linkcolor = canLink and COLOR_Link_OK or COLOR_Link_Fail local linkText = canLink and distTextOK or distTextNo:format(why.Text and why.Text or why) - renderOverride = why.Renderer - renderData = data + if not canLink then + renderOverride = why.Renderer + renderData = data + end DrawText(linkText, linkcolor, inbetween) else DrawText(distText:format(dist), linkcolor, inbetween) From 4cf367bc357d7d28e46f84c1da7d117fb47ff274 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Sat, 11 Jan 2025 20:26:09 -0800 Subject: [PATCH 08/11] Yellow line --- lua/acf/core/utilities/util_cl.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 7abe6b36f..711222e25 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -944,7 +944,7 @@ do local COLOR_Black = Color(0, 0, 0, 255) local COLOR_Link_OK = Color(55, 235, 55, 255) local COLOR_Link_Fail = Color(255, 88, 88) - local COLOR_Link_FailDistMissed = Color(175, 0, 0) + local COLOR_Link_FailDistMissed = Color(255, 200, 81) local COLOR_Link = Color(205, 235, 255, 255) function ACF.ToolCL_RegisterLinkGizmoData(from, to, callback) @@ -987,8 +987,8 @@ do render.DepthRange(0, 0) render.DrawBeam(fromPos, toMaxDist, 2, 0, 1, COLOR_Black) render.DrawBeam(toMaxDist, toPos, 2, 0, 1, COLOR_Black) - render.DrawBeam(fromPos, toMaxDist, 1, 0, 1, COLOR_Link_Fail) - render.DrawBeam(toMaxDist, toPos, 1, 0, 1, COLOR_Link_FailDistMissed) + render.DrawBeam(fromPos, toMaxDist, 1, 0, 1, COLOR_Link_FailDistMissed) + render.DrawBeam(toMaxDist, toPos, 1, 0, 1, COLOR_Link_Fail) render.DepthRange(0, 1) end } From 7d6610ec6685b5305396fab37027ff8425e66cda Mon Sep 17 00:00:00 2001 From: thecraftianman <64441307+thecraftianman@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:00:11 -0500 Subject: [PATCH 09/11] Code style cleanup --- lua/acf/core/utilities/util_cl.lua | 183 +++++++++++++-------------- lua/acf/menu/operations/acf_menu.lua | 56 +++----- 2 files changed, 109 insertions(+), 130 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 711222e25..22d447212 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -937,134 +937,133 @@ do -- Default turret menus end end --- Link distance gizmo stuff -do +do -- Link distance gizmo stuff local EntGizmoDifferences = {} - local COLOR_Black = Color(0, 0, 0, 255) - local COLOR_Link_OK = Color(55, 235, 55, 255) - local COLOR_Link_Fail = Color(255, 88, 88) - local COLOR_Link_FailDistMissed = Color(255, 200, 81) - local COLOR_Link = Color(205, 235, 255, 255) + local ColorLinkOk = Color(55, 235, 55, 255) + local ColorLinkFail = Color(255, 88, 88) + local ColorLinkFailDistMissed = Color(255, 200, 81) + local ColorLink = Color(205, 235, 255, 255) - function ACF.ToolCL_RegisterLinkGizmoData(from, to, callback) - EntGizmoDifferences[from] = EntGizmoDifferences[from] or {} - EntGizmoDifferences[to] = EntGizmoDifferences[to] or {} + function ACF.ToolCL_RegisterLinkGizmoData(From, To, Callback) + EntGizmoDifferences[From] = EntGizmoDifferences[From] or {} + EntGizmoDifferences[To] = EntGizmoDifferences[To] or {} - EntGizmoDifferences[from][to] = callback - EntGizmoDifferences[to][from] = callback + EntGizmoDifferences[From][To] = Callback + EntGizmoDifferences[To][From] = Callback end - function ACF.ToolCL_GetLinkGizmoData(entFrom, entTo) - local fromTbl = EntGizmoDifferences[entFrom:GetClass()] - if not fromTbl then return end + function ACF.ToolCL_GetLinkGizmoData(EntFrom, EntTo) + local FromTbl = EntGizmoDifferences[EntFrom:GetClass()] + if not FromTbl then return end - local toTbl = fromTbl[entTo:GetClass()] - if not toTbl then return end + local ToTbl = FromTbl[EntTo:GetClass()] + if not ToTbl then return end - return true, toTbl(entFrom, entTo) + return true, ToTbl(EntFrom, EntTo) end - function ACF.ToolCL_CanLink(from, to) - if not IsValid(from) then return false, "Link target not valid!" end - if not IsValid(to) then return false, "Target not valid!" end + function ACF.ToolCL_CanLink(From, To) + if not IsValid(From) then return false, "Link target not valid!" end + if not IsValid(To) then return false, "Target not valid!" end - if from == to then return false, "Cannot link an entity to itself!" end + if From == To then return false, "Cannot link an entity to itself!" end - local hadData, canLink, whyNot, renderData = ACF.ToolCL_GetLinkGizmoData(from, to) - if not hadData then return false, "No link data." end - return canLink == nil and true or canLink, whyNot, renderData + local HadData, CanLink, WhyNot, RenderData = ACF.ToolCL_GetLinkGizmoData(From, To) + if not HadData then return false, "No link data." end + return CanLink == nil and true or CanLink, WhyNot, RenderData end - local ACF_LinkDistanceTooFar = { + local LinkDistanceTooFar = { Text = "The entity is too far away.", - Renderer = function(data, _, _) - local fromPos, toPos = data.fromPos, data.toPos - local normal = (toPos - fromPos):GetNormalized() - local toMaxDist = fromPos + (normal * data.maxdist) + Renderer = function(Data) + local FromPos, ToPos = Data.FromPos, Data.ToPos + local Normal = (ToPos - FromPos):GetNormalized() + local ToMaxDist = FromPos + (Normal * Data.MaxDist) render.SetColorMaterial() render.DepthRange(0, 0) - render.DrawBeam(fromPos, toMaxDist, 2, 0, 1, COLOR_Black) - render.DrawBeam(toMaxDist, toPos, 2, 0, 1, COLOR_Black) - render.DrawBeam(fromPos, toMaxDist, 1, 0, 1, COLOR_Link_FailDistMissed) - render.DrawBeam(toMaxDist, toPos, 1, 0, 1, COLOR_Link_Fail) + render.DrawBeam(FromPos, ToMaxDist, 2, 0, 1, color_black) + render.DrawBeam(ToMaxDist, ToPos, 2, 0, 1, color_black) + render.DrawBeam(FromPos, ToMaxDist, 1, 0, 1, ColorLinkFailDistMissed) + render.DrawBeam(ToMaxDist, ToPos, 1, 0, 1, ColorLinkFail) render.DepthRange(0, 1) end } - ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", function(from, to) - local fromPos, toPos = from:GetPos(), to:GetPos() - local dist = fromPos:Distance(toPos) - local maxdist = ACF.LinkDistance - if dist > maxdist then return false, ACF_LinkDistanceTooFar, {fromPos = fromPos, toPos = toPos, dist = dist, maxdist = maxdist} end + ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", function(From, To) + local FromPos, ToPos = From:GetPos(), To:GetPos() + local Dist = FromPos:Distance(ToPos) + local MaxDist = ACF.LinkDistance + if Dist > MaxDist then return false, LinkDistanceTooFar, {FromPos = FromPos, ToPos = ToPos, Dist = Dist, MaxDist = MaxDist} end end) - ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", function(from, to) - local fromPos, toPos = from:GetPos(), to:GetPos() - local dist = fromPos:Distance(toPos) - local maxdist = ACF.MobilityLinkDistance - if dist > maxdist then return false, ACF_LinkDistanceTooFar, {fromPos = fromPos, toPos = toPos, dist = dist, maxdist = maxdist} end + ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", function(From, To) + local FromPos, ToPos = From:GetPos(), To:GetPos() + local Dist = FromPos:Distance(ToPos) + local MaxDist = ACF.MobilityLinkDistance + if Dist > MaxDist then return false, LinkDistanceTooFar, {FromPos = FromPos, ToPos = ToPos, Dist = Dist, MaxDist = MaxDist} end end) local HUDText = {} - local function DrawText(text, color, x, y) - if not y then - local xy = x:ToScreen() - x, y = xy.x, xy.y + local function DrawText(Text, Color, X, Y) + if not Y then + local XY = X:ToScreen() + X, Y = XY.x, XY.y end - HUDText[#HUDText + 1] = {Text = text, X = x, Y = y, Color = color} + HUDText[#HUDText + 1] = {Text = Text, X = X, Y = Y, Color = Color} end - local distText = "Distance: %.1f units" - local distTextOK = "✓ OK" - local distTextNo = "✗ Cannot link: %s" + local DistText = "Distance: %.1f units" + local DistTextOK = "✓ OK" + local DistTextNo = "✗ Cannot link: %s" hook.Add("PostDrawTranslucentRenderables", "ACF_PostDrawTranslucentRenderables_LinkDistanceVis", function() if not ACF.ToolCL_InLinkState() then return end table.Empty(HUDText) - local playerPos = LocalPlayer():GetPos() - local eyeTrace = LocalPlayer():GetEyeTrace() - local lookEnt = eyeTrace.Entity - local lookPos = eyeTrace.HitPos - local lookingAtEntity = IsValid(lookEnt) - local linkEnts = ACF.ToolCL_GetLinkedEnts() - - for ent in pairs(linkEnts) do - if IsValid(ent) then - local targPos = lookingAtEntity and lookEnt:GetPos() or lookPos - local entPos = ent:GetPos() - - local dist = entPos:Distance(targPos) - local player2targ = math.Clamp(playerPos:Distance(targPos) / 1.5, 0, dist / 2) - local inbetween = targPos + ((entPos - targPos):GetNormalized() * math.Clamp(dist, 0, player2targ)) - - local linkcolor = COLOR_Link - local renderOverride, renderData - - if lookingAtEntity then - local canLink, why, data = ACF.ToolCL_CanLink(ent, lookEnt, dist) - linkcolor = canLink and COLOR_Link_OK or COLOR_Link_Fail - local linkText = canLink and distTextOK or distTextNo:format(why.Text and why.Text or why) - if not canLink then - renderOverride = why.Renderer - renderData = data + local LocalPly = LocalPlayer() + local PlayerPos = LocalPly:GetPos() + local EyeTrace = LocalPly:GetEyeTrace() + local LookEnt = EyeTrace.Entity + local LookPos = EyeTrace.HitPos + local LookingAtEntity = IsValid(LookEnt) + local LinkEnts = ACF.ToolCL_GetLinkedEnts() + + for Ent in pairs(LinkEnts) do + if IsValid(Ent) then + local TargPos = LookingAtEntity and LookEnt:GetPos() or LookPos + local EntPos = Ent:GetPos() + + local Dist = EntPos:Distance(TargPos) + local PlayerToTarget = math.Clamp(PlayerPos:Distance(TargPos) / 1.5, 0, Dist / 2) + local InBetween = TargPos + ((EntPos - TargPos):GetNormalized() * math.Clamp(Dist, 0, PlayerToTarget)) + + local LinkColor = ColorLink + local RenderOverride, RenderData + + if LookingAtEntity then + local CanLink, Why, Data = ACF.ToolCL_CanLink(Ent, LookEnt, Dist) + LinkColor = CanLink and ColorLinkOk or ColorLinkFail + local linkText = CanLink and DistTextOK or DistTextNo:format(Why.Text and Why.Text or Why) + if not CanLink then + RenderOverride = Why.Renderer + RenderData = Data end - DrawText(linkText, linkcolor, inbetween) + DrawText(linkText, LinkColor, InBetween) else - DrawText(distText:format(dist), linkcolor, inbetween) + DrawText(DistText:format(Dist), LinkColor, InBetween) end - if renderOverride then - renderOverride(renderData, from, to) + if RenderOverride then + RenderOverride(RenderData, From, To) else render.SetColorMaterial() render.DepthRange(0, 0) - render.DrawBeam(entPos, targPos, 2, 0, 1, COLOR_Black) - render.DrawBeam(entPos, targPos, 1, 0, 1, linkcolor) + render.DrawBeam(EntPos, TargPos, 2, 0, 1, color_black) + render.DrawBeam(EntPos, TargPos, 1, 0, 1, LinkColor) render.DepthRange(0, 1) end end @@ -1074,17 +1073,17 @@ do hook.Add("HUDPaint", "ACF_HUDPaint_LinkDistanceVis", function() if not ACF.ToolCL_InLinkState() then return end - local w, h = ScrW(), ScrH() - local padding = 16 + local W, H = ScrW(), ScrH() + local Padding = 16 - for _, v in ipairs(HUDText) do + for _, V in ipairs(HUDText) do surface.SetFont("ACF_Title") - local tX, tY = surface.GetTextSize(v.Text) - tX = tX / 2 - tY = tY / 2 - local x, y = math.Clamp(v.X, tX + padding, w - tX - padding), math.Clamp(v.Y, tY + padding, h - tY - padding) + local TX, TY = surface.GetTextSize(V.Text) + TX = TX / 2 + TY = TY / 2 + local X, Y = math.Clamp(V.X, TX + Padding, W - TX - Padding), math.Clamp(V.Y, TY + Padding, H - TY - Padding) - draw.SimpleTextOutlined(v.Text, "ACF_Title", x, y, v.Color or color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, color_black) + draw.SimpleTextOutlined(V.Text, "ACF_Title", X, Y, V.Color or color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 2, color_black) end end) end \ No newline at end of file diff --git a/lua/acf/menu/operations/acf_menu.lua b/lua/acf/menu/operations/acf_menu.lua index d1d934991..464fc25c2 100644 --- a/lua/acf/menu/operations/acf_menu.lua +++ b/lua/acf/menu/operations/acf_menu.lua @@ -9,16 +9,17 @@ do -- Generic Spawner/Linker operation creator local NameFormat = "%s[ID: %s]" local PlayerEnts = {} - local CL_InLinkState = false + local InLinkState = false if CLIENT then net.Receive("ACF_MenuLinking", function() local StateChange = net.ReadUInt(2) + if StateChange == 0 then -- Entering link state - CL_InLinkState = true + InLinkState = true table.Empty(PlayerEnts) elseif StateChange == 1 then -- Exiting link state - CL_InLinkState = false + InLinkState = false table.Empty(PlayerEnts) elseif StateChange == 2 then -- Adding entity to link table PlayerEnts[net.ReadEntity()] = true @@ -28,42 +29,21 @@ do -- Generic Spawner/Linker operation creator end) end - local function S2C_EnterLinkState(ply) - if CLIENT then return end - - net.Start("ACF_MenuLinking") - net.WriteUInt(0, 2) - net.Send(ply) - end - - local function S2C_ExitLinkState(ply) - if CLIENT then return end - - net.Start("ACF_MenuLinking") - net.WriteUInt(1, 2) - net.Send(ply) - end - - local function S2C_AddLinkEnt(ply, ent) + local function UpdateLinkState(Player, State, Entity) if CLIENT then return end net.Start("ACF_MenuLinking") - net.WriteUInt(2, 2) - net.WriteEntity(ent) - net.Send(ply) - end + net.WriteUInt(State, 2) - local function S2C_RemoveLinkEnt(ply, ent) - if CLIENT then return end + if State > 1 then + net.WriteEntity(Entity) + end - net.Start("ACF_MenuLinking") - net.WriteUInt(3, 2) - net.WriteEntity(ent) - net.Send(ply) + net.Send(Player) end function ACF.ToolCL_InLinkState() - return CL_InLinkState + return InLinkState end function ACF.ToolCL_GetLinkedEnts() @@ -151,7 +131,7 @@ do -- Generic Spawner/Linker operation creator Entity:SetColor(EntColor) Ents[Entity] = nil - S2C_RemoveLinkEnt(Player, Entity) + UpdateLinkState(Player, 3, Entity) if not next(Ents) then Tool:SetMode("Spawner", Name) @@ -166,11 +146,11 @@ do -- Generic Spawner/Linker operation creator if not next(Ents) then Tool:SetMode("Linker", Name) - S2C_EnterLinkState(Player) + UpdateLinkState(Player, 0) end Ents[Entity] = Entity:GetColor() - S2C_AddLinkEnt(Player, Entity) + UpdateLinkState(Player, 2, Entity) Entity:CallOnRemove("ACF_ToolLinking", UnselectEntity, Name, Tool) Entity:SetColor(Green) @@ -333,13 +313,13 @@ do -- Generic Spawner/Linker operation creator local Entity = Trace.Entity local Player = Tool:GetOwner() - if not IsValid(Entity) then S2C_ExitLinkState(Player) return false end + if not IsValid(Entity) then UpdateLinkState(Player, 1) return false end - local Ents = GetPlayerEnts(Player) + local Ents = GetPlayerEnts(Player) if not Player:KeyDown(IN_SPEED) then LinkEntities(Player, Name, Tool, Entity, Ents) - S2C_ExitLinkState(Player) + UpdateLinkState(Player, 1) return true end @@ -362,7 +342,7 @@ do -- Generic Spawner/Linker operation creator UnselectEntity(Entity, Name, Tool) end - S2C_ExitLinkState(Player) + UpdateLinkState(Player, 1) end, }) From 133f090e7128f813392e571384ea3f6161869ea6 Mon Sep 17 00:00:00 2001 From: thecraftianman <64441307+thecraftianman@users.noreply.github.com> Date: Wed, 5 Mar 2025 17:21:39 -0500 Subject: [PATCH 10/11] Register more class links --- lua/acf/core/utilities/util_cl.lua | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 22d447212..76d83216b 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -991,19 +991,36 @@ do -- Link distance gizmo stuff end } - ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", function(From, To) + local function GenericLinkDistanceCheck(From, To) local FromPos, ToPos = From:GetPos(), To:GetPos() local Dist = FromPos:Distance(ToPos) local MaxDist = ACF.LinkDistance if Dist > MaxDist then return false, LinkDistanceTooFar, {FromPos = FromPos, ToPos = ToPos, Dist = Dist, MaxDist = MaxDist} end - end) + end - ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", function(From, To) + local function MobilityLinkDistanceCheck(From, To) local FromPos, ToPos = From:GetPos(), To:GetPos() local Dist = FromPos:Distance(ToPos) local MaxDist = ACF.MobilityLinkDistance if Dist > MaxDist then return false, LinkDistanceTooFar, {FromPos = FromPos, ToPos = ToPos, Dist = Dist, MaxDist = MaxDist} end - end) + end + + local function AlwaysLinkableCheck() + return true + end + + ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_gun", GenericLinkDistanceCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_ammo", "acf_rack", GenericLinkDistanceCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_turret", "acf_turret_motor", GenericLinkDistanceCheck) -- TODO: Make this use the actual link distance check used in turrets + ACF.ToolCL_RegisterLinkGizmoData("acf_turret", "acf_turret_gyro", GenericLinkDistanceCheck) + + ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", MobilityLinkDistanceCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_engine", "acf_fueltank", MobilityLinkDistanceCheck) + + ACF.ToolCL_RegisterLinkGizmoData("acf_gun", "acf_turret_computer", AlwaysLinkableCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_gun", "acf_computer", AlwaysLinkableCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_rack", "acf_computer", AlwaysLinkableCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_rack", "acf_radar", AlwaysLinkableCheck) local HUDText = {} From 3fc2f906d466c230fad74003ba2731217459e8f7 Mon Sep 17 00:00:00 2001 From: thecraftianman <64441307+thecraftianman@users.noreply.github.com> Date: Wed, 5 Mar 2025 19:21:52 -0500 Subject: [PATCH 11/11] WIP driveshaft angle check --- lua/acf/core/utilities/util_cl.lua | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lua/acf/core/utilities/util_cl.lua b/lua/acf/core/utilities/util_cl.lua index 76d83216b..6cb44f198 100644 --- a/lua/acf/core/utilities/util_cl.lua +++ b/lua/acf/core/utilities/util_cl.lua @@ -1014,7 +1014,24 @@ do -- Link distance gizmo stuff ACF.ToolCL_RegisterLinkGizmoData("acf_turret", "acf_turret_motor", GenericLinkDistanceCheck) -- TODO: Make this use the actual link distance check used in turrets ACF.ToolCL_RegisterLinkGizmoData("acf_turret", "acf_turret_gyro", GenericLinkDistanceCheck) - ACF.ToolCL_RegisterLinkGizmoData("acf_gearbox", "acf_engine", MobilityLinkDistanceCheck) + ACF.ToolCL_RegisterLinkGizmoData("acf_engine", "acf_gearbox", function(From, To) + --[[ + local Out = From.Out + + if From:GetClass() == "acf_gearbox" then + local InPos = To.In and To.In.Pos or Vector() + local InPosWorld = To:LocalToWorld(InPos) + + Out = From:WorldToLocal(InPosWorld).y < 0 and From.OutL or From.OutR + end + + if ACF.IsDriveshaftAngleExcessive(To, To.In, From, Out) then + return false, { Text = "The driveshaft angle is excessive." }, {FromPos = From:GetPos(), ToPos = To:GetPos()} + end + ]] + return MobilityLinkDistanceCheck(From, To) + end) + ACF.ToolCL_RegisterLinkGizmoData("acf_engine", "acf_fueltank", MobilityLinkDistanceCheck) ACF.ToolCL_RegisterLinkGizmoData("acf_gun", "acf_turret_computer", AlwaysLinkableCheck)