From 08e309763d206d63378318e9fbc30f79bc657618 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 27 Aug 2025 22:23:08 +0200 Subject: [PATCH 01/50] intprop --- .../gmod_wire_characterlcd/cl_init.lua | 40 ++ lua/entities/gmod_wire_characterlcd/init.lua | 70 ++- .../gmod_wire_consolescreen/cl_init.lua | 41 ++ lua/entities/gmod_wire_consolescreen/init.lua | 424 ++++++++++-------- lua/entities/gmod_wire_interactiveprop.lua | 84 ++-- lua/wire/stools/interactiveprop.lua | 4 +- 6 files changed, 440 insertions(+), 223 deletions(-) diff --git a/lua/entities/gmod_wire_characterlcd/cl_init.lua b/lua/entities/gmod_wire_characterlcd/cl_init.lua index 90061c3720..e28f2ca533 100644 --- a/lua/entities/gmod_wire_characterlcd/cl_init.lua +++ b/lua/entities/gmod_wire_characterlcd/cl_init.lua @@ -1,5 +1,6 @@ include("shared.lua") + function ENT:Initialize() local mem = {} self.Memory = mem @@ -7,6 +8,18 @@ function ENT:Initialize() mem[i] = 0 end + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + local interactive_model = WireLib.GetInteractiveModel(self:GetModel()) + self.IsInteractive = false + if interactive_model then + self.IsInteractive = true + for i=1, #WireLib.GetInteractiveModel(self:GetModel()).widgets do + self.InteractiveData[i] = 0 + end + end + -- Screen control: -- [1003] - Background red -- [1004] - Background green @@ -74,10 +87,37 @@ function ENT:Initialize() GPULib.ClientCacheCallback(self,function(Address,Value) self:WriteCell(Address,Value) end) + + WireLib.netRegister(self) end +local panel + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end + +function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) +end + + +function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button +end + function ENT:OnRemove() self.GPU:Finalize() end diff --git a/lua/entities/gmod_wire_characterlcd/init.lua b/lua/entities/gmod_wire_characterlcd/init.lua index 6f5261409f..e1f929a335 100644 --- a/lua/entities/gmod_wire_characterlcd/init.lua +++ b/lua/entities/gmod_wire_characterlcd/init.lua @@ -1,16 +1,36 @@ -AddCSLuaFile("cl_init.lua") +AddCSLuaFile("cl_init.lua") AddCSLuaFile("shared.lua") include("shared.lua") ENT.WireDebugName = "CharacterLcdScreen" +function ENT:InitInteractive() + local model = self:GetModel() + local outputs = {"Memory"} + local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + for i=1, #interactivemodel.widgets do + outputs[i+1] = interactivemodel.widgets[i].name + end + self.BlockInput = false + self.NextPrompt = 0 + self.Outputs=WireLib.CreateOutputs(self,outputs) + self.IsInteractive = true +end + function ENT:Initialize() self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) self.Inputs = WireLib.CreateInputs(self, { "CharAddress", "Char (ASCII/Unicode)", "Contrast", "Clk", "Reset" }) - self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) + + self.InteractiveData = {} + self.IsInteractive = false + if WireLib.IsValidInteractiveModel(self:GetModel()) then + self:InitInteractive() + else + self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) + end self.Memory = {} @@ -179,4 +199,50 @@ function ENT:ClientWriteCell(Address, value) end end + + +function ENT:ReceiveData() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end + +function ENT:UpdateOverlay() -- required by interactiveprop functions + +end + + +function ENT:Prompt( ply ) + if not self.IsInteractive then return end + if ply then + if CurTime() < self.NextPrompt then return end -- anti spam + self.NextPrompt = CurTime() + 0.1 + + if IsValid( self.User ) then + WireLib.AddNotify(ply,"That interactive prop is in use by another player!",NOTIFY_ERROR,5,6) + return + end + + self.User = ply + + net.Start( "wire_interactiveprop_show" ) + net.WriteEntity( self ) + net.Send( ply ) + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +function ENT:Use(ply) + if not IsValid( ply ) then return end + self:Prompt( ply ) +end + +function ENT:Unprompt() + if not self.IsInteractive then return end + self.User = nil +end + duplicator.RegisterEntityClass("gmod_wire_characterlcd", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight") diff --git a/lua/entities/gmod_wire_consolescreen/cl_init.lua b/lua/entities/gmod_wire_consolescreen/cl_init.lua index 5b1222f146..7de763341b 100644 --- a/lua/entities/gmod_wire_consolescreen/cl_init.lua +++ b/lua/entities/gmod_wire_consolescreen/cl_init.lua @@ -1,11 +1,25 @@ include("shared.lua") + + function ENT:Initialize() self.Memory1 = {} self.Memory2 = {} for i = 0, 2047 do self.Memory1[i] = 0 end + + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + local interactive_model = WireLib.GetInteractiveModel(self:GetModel()) + self.IsInteractive = false + if interactive_model then + self.IsInteractive = true + for i=1, #WireLib.GetInteractiveModel(self:GetModel()).widgets do + self.InteractiveData[i] = 0 + end + end -- Caching control: -- [2020] - Force cache refresh @@ -694,3 +708,30 @@ end function ENT:IsTranslucent() return true end + + + +local panel + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end + +function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) +end + + +function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button +end \ No newline at end of file diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua index f9eb661953..ce0f0d1502 100644 --- a/lua/entities/gmod_wire_consolescreen/init.lua +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -4,95 +4,159 @@ include('shared.lua') ENT.WireDebugName = "ConsoleScreen" +function ENT:InitInteractive() + local model = self:GetModel() + local outputs = {"Memory"} + local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + for i=1, #interactivemodel.widgets do + outputs[i+1] = interactivemodel.widgets[i].name + end + self.BlockInput = false + self.NextPrompt = 0 + self.Outputs=WireLib.CreateOutputs(self,outputs) + self.IsInteractive = true +end + + +function ENT:ReceiveData() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end + +function ENT:UpdateOverlay() -- required by interactiveprop functions + +end + + +function ENT:Prompt( ply ) + if not self.IsInteractive then return end + if ply then + if CurTime() < self.NextPrompt then return end -- anti spam + self.NextPrompt = CurTime() + 0.1 + + if IsValid( self.User ) then + WireLib.AddNotify(ply,"That interactive prop is in use by another player!",NOTIFY_ERROR,5,6) + return + end + + self.User = ply + + net.Start( "wire_interactiveprop_show" ) + net.WriteEntity( self ) + net.Send( ply ) + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +function ENT:Use(ply) + if not IsValid( ply ) then return end + self:Prompt( ply ) +end + +function ENT:Unprompt() + if not self.IsInteractive then return end + self.User = nil +end + + function ENT:Initialize() - self:PhysicsInit(SOLID_VPHYSICS) - self:SetMoveType(MOVETYPE_VPHYSICS) - self:SetSolid(SOLID_VPHYSICS) - - self.Inputs = WireLib.CreateInputs(self, { "CharX", "CharY", "Char (ASCII/Unicode)", "CharParam (RGBrgb; White=999)", "Clk", "Reset" }) - self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) - - self.Memory = {} - - for i = 0, 2047 do - self.Memory[i] = 0 - end - - self.CharX = 0 - self.CharY = 0 - self.Char = 0 - self.CharParam = 0 - - self.Memory[2020] = 0 - self.Memory[2021] = 0 - - self.Memory[2022] = 3/4 - self.Memory[2023] = 0 - self.Memory[2024] = 0 - self.Memory[2025] = 1 - self.Memory[2026] = 1 - self.Memory[2027] = 1 - self.Memory[2028] = 1 - self.Memory[2029] = 1 - self.Memory[2030] = 1 - self.Memory[2031] = 0 - self.Memory[2032] = 29 - self.Memory[2033] = 0 - self.Memory[2034] = 17 - self.Memory[2035] = 0 - self.Memory[2036] = 0 - - self.Memory[2042] = 000 - self.Memory[2043] = 0.5 - self.Memory[2044] = 0.25 - self.Memory[2045] = 0 - self.Memory[2046] = 0 - self.Memory[2047] = 1 -- CLK - - self.Cache = GPUCacheManager(self,true) + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + + self.Inputs = WireLib.CreateInputs(self, { "CharX", "CharY", "Char (ASCII/Unicode)", "CharParam (RGBrgb; White=999)", "Clk", "Reset" }) + if WireLib.IsValidInteractiveModel(self:GetModel()) then + self:InitInteractive() + else + self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) + end + + + self.Memory = {} + + for i = 0, 2047 do + self.Memory[i] = 0 + end + + self.CharX = 0 + self.CharY = 0 + self.Char = 0 + self.CharParam = 0 + + self.Memory[2020] = 0 + self.Memory[2021] = 0 + + self.Memory[2022] = 3/4 + self.Memory[2023] = 0 + self.Memory[2024] = 0 + self.Memory[2025] = 1 + self.Memory[2026] = 1 + self.Memory[2027] = 1 + self.Memory[2028] = 1 + self.Memory[2029] = 1 + self.Memory[2030] = 1 + self.Memory[2031] = 0 + self.Memory[2032] = 29 + self.Memory[2033] = 0 + self.Memory[2034] = 17 + self.Memory[2035] = 0 + self.Memory[2036] = 0 + + self.Memory[2042] = 000 + self.Memory[2043] = 0.5 + self.Memory[2044] = 0.25 + self.Memory[2045] = 0 + self.Memory[2046] = 0 + self.Memory[2047] = 1 -- CLK + + self.Cache = GPUCacheManager(self,true) end function ENT:SendPixel() - if (self.Memory[2047] ~= 0) and (self.CharX >= 0) and (self.CharX < 30) and - (self.CharY >= 0) and (self.CharY < 18) then - local pixelno = math.floor(self.CharY)*30+math.floor(self.CharX) + if (self.Memory[2047] ~= 0) and (self.CharX >= 0) and (self.CharX < 30) and + (self.CharY >= 0) and (self.CharY < 18) then + local pixelno = math.floor(self.CharY)*30+math.floor(self.CharX) - self:WriteCell(pixelno*2, self.Char) - self:WriteCell(pixelno*2+1, self.CharParam) - end + self:WriteCell(pixelno*2, self.Char) + self:WriteCell(pixelno*2+1, self.CharParam) + end end function ENT:ReadCell(Address) - Address = math.floor(Address) - if Address < 0 then return nil end - if Address >= 2048 then return nil end - if Address == 2022 then return WireGPU_Monitors[self:GetModel()].RatioX end + Address = math.floor(Address) + if Address < 0 then return nil end + if Address >= 2048 then return nil end + if Address == 2022 then return WireGPU_Monitors[self:GetModel()].RatioX end - return self.Memory[Address] + return self.Memory[Address] end function ENT:WriteCell(Address, value) - Address = math.floor(Address) - if Address < 0 then return false end - if Address >= 2048 then return false end - if Address < 2000 then -- text/attribute data - if self.Memory[Address] == value then return true end - else --- self.Memory[Address] = value - self:ClientWriteCell(Address, value) --- self.Cache:WriteNow(Address, value) --- return true - end - - self.Memory[Address] = value - self.Cache:Write(Address,value) - return true + Address = math.floor(Address) + if Address < 0 then return false end + if Address >= 2048 then return false end + if Address < 2000 then -- text/attribute data + if self.Memory[Address] == value then return true end + else +-- self.Memory[Address] = value + self:ClientWriteCell(Address, value) +-- self.Cache:WriteNow(Address, value) +-- return true + end + + self.Memory[Address] = value + self.Cache:Write(Address,value) + return true end function ENT:Think() - self.Cache:Flush() - self:NextThink(CurTime()+0.1) - return true + self.Cache:Flush() + self:NextThink(CurTime()+0.1) + return true end function ENT:Retransmit(ply) @@ -104,115 +168,115 @@ function ENT:Retransmit(ply) end function ENT:TriggerInput(iname, value) - if iname == "CharX" then - self.CharX = value - self:SendPixel() - elseif iname == "CharY" then - self.CharY = value - self:SendPixel() - elseif iname == "Char" then - self.Char = value - self:SendPixel() - elseif iname == "CharParam" then - self.CharParam = value - self:SendPixel() - elseif iname == "Clk" then - self:WriteCell(2047, value) - self:SendPixel() - elseif iname == "Reset" then - self:WriteCell(2041,0) - self:WriteCell(2046,0) - self:WriteCell(2042,0) - end + if iname == "CharX" then + self.CharX = value + self:SendPixel() + elseif iname == "CharY" then + self.CharY = value + self:SendPixel() + elseif iname == "Char" then + self.Char = value + self:SendPixel() + elseif iname == "CharParam" then + self.CharParam = value + self:SendPixel() + elseif iname == "Clk" then + self:WriteCell(2047, value) + self:SendPixel() + elseif iname == "Reset" then + self:WriteCell(2041,0) + self:WriteCell(2046,0) + self:WriteCell(2042,0) + end end function ENT:ClientWriteCell(Address, value) - if Address == 2019 then -- Hardware Clear Viewport - local low = math.floor(math.Clamp(self.Memory[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) - for j = low, high do - for i = 2*lowc, 2*highc+1 do - self.Memory[i*60+value] = 0 - end - end - elseif Address == 2037 then -- Shift cells (number of cells, >0 right, <0 left) - local delta = math.floor(math.Clamp(math.abs(value),-30,30)) - local low = math.floor(math.Clamp(self.Memory[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) - if value > 0 then - for j = low,high do - for i = highc,lowc+delta,-1 do - self.Memory[j*60+i*2] = self.Memory[j*60+i*2-delta*2] - self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1-delta*2] - end - end - for j = low,high do - for i = lowc, lowc+delta-1 do - self.Memory[j*60+i*2] = 0 - self.Memory[j*60+i*2+1] = 0 - end - end - else - for j = low,high do - for i = lowc,highc-delta do - self.Memory[j*60+i*2] = self.Memory[j*60+i*2+delta*2] - self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1+delta*2] - end - end - for j = low,high do - for i = highc-delta+1,highc do - self.Memory[j*60+i*2] = 0 - self.Memory[j*60+i*2+1] = 0 - end - end - end - elseif Address == 2038 then -- Shift rows (number of rows, >0 shift down, <0 shift up) - local delta = math.floor(math.Clamp(math.abs(value),-30,30)) - local low = math.floor(math.Clamp(self.Memory[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) - if value > 0 then - for j = low, high-delta do - for i = 2*lowc, 2*highc+1 do - self.Memory[j*60+i] = self.Memory[(j+delta)*60+i] - end - end - for j = high-delta+1,high do - for i = 2*lowc, 2*highc+1 do - self.Memory[j*60+i] = 0 - end - end - else - for j = high,low+delta,-1 do - for i = 2*lowc, 2*highc+1 do - self.Memory[j*60+i] = self.Memory[(j-delta)*60+i] - end - end - for j = low,low+delta-1 do - for i = 2*lowc, 2*highc+1 do - self.Memory[j*60+i] = 0 - end - end - end - elseif Address == 2039 then -- Hardware Clear Row (Writing clears row) - for i = 0, 59 do - self.Memory[value*60+i] = 0 - end - elseif Address == 2040 then -- Hardware Clear Column (Writing clears column) - for i = 0, 17 do - self.Memory[i*60+value] = 0 - end - elseif Address == 2041 then -- Hardware Clear Screen - for i = 0, 18*30*2-1 do - self.Memory[i] = 0 - end - self.Cache:Reset() - end + if Address == 2019 then -- Hardware Clear Viewport + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + for j = low, high do + for i = 2*lowc, 2*highc+1 do + self.Memory[i*60+value] = 0 + end + end + elseif Address == 2037 then -- Shift cells (number of cells, >0 right, <0 left) + local delta = math.floor(math.Clamp(math.abs(value),-30,30)) + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + if value > 0 then + for j = low,high do + for i = highc,lowc+delta,-1 do + self.Memory[j*60+i*2] = self.Memory[j*60+i*2-delta*2] + self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1-delta*2] + end + end + for j = low,high do + for i = lowc, lowc+delta-1 do + self.Memory[j*60+i*2] = 0 + self.Memory[j*60+i*2+1] = 0 + end + end + else + for j = low,high do + for i = lowc,highc-delta do + self.Memory[j*60+i*2] = self.Memory[j*60+i*2+delta*2] + self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1+delta*2] + end + end + for j = low,high do + for i = highc-delta+1,highc do + self.Memory[j*60+i*2] = 0 + self.Memory[j*60+i*2+1] = 0 + end + end + end + elseif Address == 2038 then -- Shift rows (number of rows, >0 shift down, <0 shift up) + local delta = math.floor(math.Clamp(math.abs(value),-30,30)) + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + if value > 0 then + for j = low, high-delta do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = self.Memory[(j+delta)*60+i] + end + end + for j = high-delta+1,high do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = 0 + end + end + else + for j = high,low+delta,-1 do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = self.Memory[(j-delta)*60+i] + end + end + for j = low,low+delta-1 do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = 0 + end + end + end + elseif Address == 2039 then -- Hardware Clear Row (Writing clears row) + for i = 0, 59 do + self.Memory[value*60+i] = 0 + end + elseif Address == 2040 then -- Hardware Clear Column (Writing clears column) + for i = 0, 17 do + self.Memory[i*60+value] = 0 + end + elseif Address == 2041 then -- Hardware Clear Screen + for i = 0, 18*30*2-1 do + self.Memory[i] = 0 + end + self.Cache:Reset() + end end duplicator.RegisterEntityClass("gmod_wire_consolescreen", WireLib.MakeWireEnt, "Data") diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 749c56bc94..2210df6f47 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -203,11 +203,38 @@ function WireLib.IsValidInteractiveModel( model ) return InteractiveModels[model] ~= nil end +function WireLib.GetInteractiveModel( model ) + return InteractiveModels[model] +end + +function WireLib.GetInteractiveWidgetBody( ent, data ) + local body = vgui.Create("DFrame") + body:SetTitle(data.title) + body:SetSize(data.width, data.height) + body:SetVisible(true) + body.Paint = function( self, w, h ) -- 'function Frame:Paint( w, h )' works too + -- surface.SetDrawColor(255,255,255) + -- surface.DrawOutlinedRect(0, 0, w, h) + -- surface.SetDrawColor(0,0,0) + -- surface.DrawOutlinedRect(1, 1, w-2, h-2) + draw.RoundedBox( 4, 0, 0, w, h, Color( 255, 255, 255 ) ) + draw.RoundedBox( 4, 1, 1, w-2, h-2, Color( 64, 64, 64 ) ) + end + body:SetDraggable(false) + body:Center() + body:ShowCloseButton(true) + body:MakePopup() + for id, widget in ipairs( data.widgets ) do + WidgetBuilders[widget.type](ent, widget, body, id) + end + return body +end + InteractiveModels["models/props_c17/furnituresink001a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Furniture Sink" ) InteractiveModels["models/props_interiors/sinkkitchen01a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Kitchen Sink" ) InteractiveModels["models/props_wasteland/prison_sink001a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Prison Sink" ) -local WidgetBuilders = { +WidgetBuilders = { DCheckBox = function(self, data, body, index) local checkbox = vgui.Create("DCheckBox", body) @@ -252,6 +279,14 @@ local WidgetBuilders = { button:SetPos(data.x, data.y) button:SetText(data.text or "") button:SetSize(data.width or 20, data.height or 20) + button.OnDepressed = function(scratch) + self.InteractiveData[index] = 1 + self:SendData() + end + button.OnReleased = function(scratch) + self.InteractiveData[index] = 0 + self:SendData() + end self:AddButton(index,button) end @@ -259,26 +294,7 @@ local WidgetBuilders = { function ENT:GetPanel() local data = InteractiveModels[ self:GetModel() ] - local body = vgui.Create("DFrame") - body:SetTitle(data.title) - body:SetSize(data.width, data.height) - body:SetVisible(true) - body.Paint = function( self, w, h ) -- 'function Frame:Paint( w, h )' works too - -- surface.SetDrawColor(255,255,255) - -- surface.DrawOutlinedRect(0, 0, w, h) - -- surface.SetDrawColor(0,0,0) - -- surface.DrawOutlinedRect(1, 1, w-2, h-2) - draw.RoundedBox( 4, 0, 0, w, h, Color( 255, 255, 255 ) ) - draw.RoundedBox( 4, 1, 1, w-2, h-2, Color( 64, 64, 64 ) ) - end - body:SetDraggable(false) - body:Center() - body:ShowCloseButton(true) - body:MakePopup() - for id, widget in ipairs( data.widgets ) do - WidgetBuilders[widget.type](self, widget, body, id) - end - return body + return WireLib.GetInteractiveWidgetBody(self, data) end @@ -312,22 +328,6 @@ if CLIENT then end end - function ENT:Think() - if IsValid( panel ) and #self.Buttons ~= 0 then - local needToUpdate = false - for k,v in pairs(self.Buttons) do - self.LastButtons[k] = self.InteractiveData[k] - self.InteractiveData[k] = v:IsDown() and 1 or 0 - if self.InteractiveData[k] ~= self.LastButtons[k] then - needToUpdate = true - end - end - if needToUpdate then - self:SendData() - end - end - end - net.Receive("wire_interactiveprop_show",function() local self = net.ReadEntity() if not IsValid(self) then return end @@ -419,7 +419,10 @@ end util.AddNetworkString("wire_interactiveprop_action") net.Receive("wire_interactiveprop_action",function(len,ply) local ent = net.ReadEntity() - if not ent:IsValid() or ent:GetClass() ~= "gmod_wire_interactiveprop" or ply ~= ent.User then return end + if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and + ent:GetClass() ~= "gmod_wire_characterlcd" and + ent:GetClass() ~= "gmod_wire_consolescreen" + ) or ply ~= ent.User then return end ent:ReceiveData() ent:UpdateOverlay() @@ -455,7 +458,10 @@ end util.AddNetworkString("wire_interactiveprop_close") net.Receive("wire_interactiveprop_close",function(len,ply) local ent = net.ReadEntity() - if not ent:IsValid() or ent:GetClass() ~= "gmod_wire_interactiveprop" or ply ~= ent.User then return end + if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and + ent:GetClass() ~= "gmod_wire_characterlcd" and + ent:GetClass() ~= "gmod_wire_consolescreen" + ) or ply ~= ent.User then return end ent:Unprompt() end) diff --git a/lua/wire/stools/interactiveprop.lua b/lua/wire/stools/interactiveprop.lua index 6d5815bd6c..44ea69b21f 100644 --- a/lua/wire/stools/interactiveprop.lua +++ b/lua/wire/stools/interactiveprop.lua @@ -16,8 +16,8 @@ TOOL.ClientConVar = { if SERVER then function TOOL:GetDataTables() - return {} - end + return {} + end end WireToolSetup.BaseLang() From 579e40761d0496f37e85b7615b59b0785f90748f Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 27 Aug 2025 22:30:14 +0200 Subject: [PATCH 02/50] digi --- .../gmod_wire_consolescreen/cl_init.lua | 47 +++++++------- .../gmod_wire_digitalscreen/cl_init.lua | 37 +++++++++++ lua/entities/gmod_wire_digitalscreen/init.lua | 65 ++++++++++++++++++- lua/entities/gmod_wire_interactiveprop.lua | 16 ++++- 4 files changed, 138 insertions(+), 27 deletions(-) diff --git a/lua/entities/gmod_wire_consolescreen/cl_init.lua b/lua/entities/gmod_wire_consolescreen/cl_init.lua index 7de763341b..5ccb5a1477 100644 --- a/lua/entities/gmod_wire_consolescreen/cl_init.lua +++ b/lua/entities/gmod_wire_consolescreen/cl_init.lua @@ -1,6 +1,29 @@ include("shared.lua") +local panel + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end +function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) +end + + +function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button +end function ENT:Initialize() self.Memory1 = {} @@ -711,27 +734,3 @@ end -local panel - -function ENT:SendData() - net.Start("wire_interactiveprop_action") - - local data = WireLib.GetInteractiveModel(self:GetModel()).widgets - net.WriteEntity(self) - for i=1, #data do - net.WriteFloat(self.InteractiveData[i]) - end - net.SendToServer() -end - -function ENT:GetPanel() - if not self.IsInteractive then return end - local data = WireLib.GetInteractiveModel(self:GetModel()) - return WireLib.GetInteractiveWidgetBody(self, data) -end - - -function ENT:AddButton(id,button) - if not self.IsInteractive then return end - self.Buttons[id] = button -end \ No newline at end of file diff --git a/lua/entities/gmod_wire_digitalscreen/cl_init.lua b/lua/entities/gmod_wire_digitalscreen/cl_init.lua index 2295fdf215..b14c1bd788 100644 --- a/lua/entities/gmod_wire_digitalscreen/cl_init.lua +++ b/lua/entities/gmod_wire_digitalscreen/cl_init.lua @@ -1,9 +1,46 @@ include('shared.lua') +local panel + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end + +function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) +end + + +function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button +end + function ENT:Initialize() self.Memory1 = {} self.Memory2 = {} + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + local interactive_model = WireLib.GetInteractiveModel(self:GetModel()) + self.IsInteractive = false + if interactive_model then + self.IsInteractive = true + for i=1, #WireLib.GetInteractiveModel(self:GetModel()).widgets do + self.InteractiveData[i] = 0 + end + end + self.LastClk = true self.NewClk = true self.Memory1[1048575] = 1 diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua index 3fc448cbd4..f882e00956 100644 --- a/lua/entities/gmod_wire_digitalscreen/init.lua +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -5,6 +5,65 @@ DEFINE_BASECLASS( "base_wire_entity" ) ENT.WireDebugName = "DigitalScreen" + +function ENT:InitInteractive() + local model = self:GetModel() + local outputs = {"Memory"} + local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + for i=1, #interactivemodel.widgets do + outputs[i+1] = interactivemodel.widgets[i].name + end + self.BlockInput = false + self.NextPrompt = 0 + self.Outputs=WireLib.CreateOutputs(self,outputs) + self.IsInteractive = true +end + + +function ENT:ReceiveData() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end + +function ENT:UpdateOverlay() -- required by interactiveprop functions + +end + + +function ENT:Prompt( ply ) + if not self.IsInteractive then return end + if ply then + if CurTime() < self.NextPrompt then return end -- anti spam + self.NextPrompt = CurTime() + 0.1 + + if IsValid( self.User ) then + WireLib.AddNotify(ply,"That interactive prop is in use by another player!",NOTIFY_ERROR,5,6) + return + end + + self.User = ply + + net.Start( "wire_interactiveprop_show" ) + net.WriteEntity( self ) + net.Send( ply ) + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +function ENT:Use(ply) + if not IsValid( ply ) then return end + self:Prompt( ply ) +end + +function ENT:Unprompt() + if not self.IsInteractive then return end + self.User = nil +end + function ENT:Initialize() self:PhysicsInit(SOLID_VPHYSICS) @@ -12,7 +71,11 @@ function ENT:Initialize() self:SetSolid(SOLID_VPHYSICS) self.Inputs = Wire_CreateInputs(self, { "PixelX", "PixelY", "PixelG", "Clk", "FillColor", "ClearRow", "ClearCol" }) - self.Outputs = Wire_CreateOutputs(self, { "Memory" }) + if WireLib.IsValidInteractiveModel(self:GetModel()) then + self:InitInteractive() + else + self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) + end self.Memory = {} diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 2210df6f47..4c8b4a3653 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -194,6 +194,16 @@ InteractiveModels = { {type="DButton", x=135, y=60, name="Button11"}, {type="DButton", x=160, y=60, name="Button12"}, } + }, + ["models/props_lab/monitor01b.mdl"] = { + width = 40, + height = 128, + title = "plotter", + widgets={ + {type="DNumberScratch", x = 10, y = 32, name="Knob1"}, + {type="DNumberScratch", x = 10, y = 64, name="Knob2"}, + {type="DNumberScratch", x = 10, y = 96, name="Knob3"}, + } } } @@ -421,7 +431,8 @@ net.Receive("wire_interactiveprop_action",function(len,ply) local ent = net.ReadEntity() if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and ent:GetClass() ~= "gmod_wire_characterlcd" and - ent:GetClass() ~= "gmod_wire_consolescreen" + ent:GetClass() ~= "gmod_wire_consolescreen" and + ent:GetClass() ~= "gmod_wire_digitalscreen" ) or ply ~= ent.User then return end ent:ReceiveData() @@ -460,7 +471,8 @@ net.Receive("wire_interactiveprop_close",function(len,ply) local ent = net.ReadEntity() if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and ent:GetClass() ~= "gmod_wire_characterlcd" and - ent:GetClass() ~= "gmod_wire_consolescreen" + ent:GetClass() ~= "gmod_wire_consolescreen" and + ent:GetClass() ~= "gmod_wire_digitalscreen" ) or ply ~= ent.User then return end ent:Unprompt() end) From 44049cd932d4a7890cfea7d710587005b04889e9 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 27 Aug 2025 22:44:25 +0200 Subject: [PATCH 03/50] add oscilloscope --- lua/entities/gmod_wire_interactiveprop.lua | 12 +-- lua/entities/gmod_wire_oscilloscope.lua | 103 +++++++++++++++++++++ lua/wire/stools/characterlcd.lua | 34 +++---- 3 files changed, 122 insertions(+), 27 deletions(-) diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 4c8b4a3653..395e5ae217 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -429,11 +429,7 @@ end util.AddNetworkString("wire_interactiveprop_action") net.Receive("wire_interactiveprop_action",function(len,ply) local ent = net.ReadEntity() - if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and - ent:GetClass() ~= "gmod_wire_characterlcd" and - ent:GetClass() ~= "gmod_wire_consolescreen" and - ent:GetClass() ~= "gmod_wire_digitalscreen" - ) or ply ~= ent.User then return end + if not ent:IsValid() or ply ~= ent.User then return end ent:ReceiveData() ent:UpdateOverlay() @@ -469,11 +465,7 @@ end util.AddNetworkString("wire_interactiveprop_close") net.Receive("wire_interactiveprop_close",function(len,ply) local ent = net.ReadEntity() - if not ent:IsValid() or (ent:GetClass() ~= "gmod_wire_interactiveprop" and - ent:GetClass() ~= "gmod_wire_characterlcd" and - ent:GetClass() ~= "gmod_wire_consolescreen" and - ent:GetClass() ~= "gmod_wire_digitalscreen" - ) or ply ~= ent.User then return end + if not ent:IsValid() or ply ~= ent.User then return end ent:Unprompt() end) diff --git a/lua/entities/gmod_wire_oscilloscope.lua b/lua/entities/gmod_wire_oscilloscope.lua index ee44bf7d82..ae41a7ac86 100644 --- a/lua/entities/gmod_wire_oscilloscope.lua +++ b/lua/entities/gmod_wire_oscilloscope.lua @@ -4,8 +4,47 @@ ENT.PrintName = "Wire Oscilloscope" ENT.WireDebugName = "Oscilloscope" if CLIENT then + + local panel + + function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() + end + + function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) + end + + + function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button + end + function ENT:Initialize() self.GPU = WireGPU(self) + + + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + local interactive_model = WireLib.GetInteractiveModel(self:GetModel()) + self.IsInteractive = false + if interactive_model then + self.IsInteractive = true + for i=1, #WireLib.GetInteractiveModel(self:GetModel()).widgets do + self.InteractiveData[i] = 0 + end + end self.Nodes = {} end @@ -109,12 +148,76 @@ function ENT:SetNextNode(x, y) net.SendPVS( self:GetPos() ) end + +function ENT:InitInteractive() + local model = self:GetModel() + local outputs = {} + local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + for i=1, #interactivemodel.widgets do + outputs[i+1] = interactivemodel.widgets[i].name + end + self.BlockInput = false + self.NextPrompt = 0 + self.Outputs=WireLib.CreateOutputs(self,outputs) + self.IsInteractive = true +end + + +function ENT:ReceiveData() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end + +function ENT:UpdateOverlay() -- required by interactiveprop functions + +end + + +function ENT:Prompt( ply ) + if not self.IsInteractive then return end + if ply then + if CurTime() < self.NextPrompt then return end -- anti spam + self.NextPrompt = CurTime() + 0.1 + + if IsValid( self.User ) then + WireLib.AddNotify(ply,"That interactive prop is in use by another player!",NOTIFY_ERROR,5,6) + return + end + + self.User = ply + + net.Start( "wire_interactiveprop_show" ) + net.WriteEntity( self ) + net.Send( ply ) + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +function ENT:Use(ply) + if not IsValid( ply ) then return end + self:Prompt( ply ) +end + +function ENT:Unprompt() + if not self.IsInteractive then return end + self.User = nil +end + + function ENT:Initialize() self:PhysicsInit( SOLID_VPHYSICS ) self:SetMoveType( MOVETYPE_VPHYSICS ) self:SetSolid( SOLID_VPHYSICS ) self.Inputs = WireLib.CreateInputs(self, { "X", "Y", "R", "G", "B", "Pause", "Length", "Update Frequency" }) + + if WireLib.IsValidInteractiveModel(self:GetModel()) then + self:InitInteractive() + end end function ENT:Think() diff --git a/lua/wire/stools/characterlcd.lua b/lua/wire/stools/characterlcd.lua index f5d4c2b08f..cbb9565e61 100644 --- a/lua/wire/stools/characterlcd.lua +++ b/lua/wire/stools/characterlcd.lua @@ -4,8 +4,8 @@ WireToolSetup.open( "characterlcd", "Character LCD", "gmod_wire_characterlcd", n if CLIENT then language.Add( "tool.wire_characterlcd.name", "Character LCD Tool (Wire)" ) language.Add( "tool.wire_characterlcd.desc", "Spawns a Character LCD, which can be used to display text" ) - language.Add( "tool.wire_characterlcd.bgcolor", "Background color:" ) - language.Add( "tool.wire_characterlcd.fgcolor", "Text color:" ) + language.Add( "tool.wire_characterlcd.bgcolor", "Background color:" ) + language.Add( "tool.wire_characterlcd.fgcolor", "Text color:" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") @@ -16,33 +16,33 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() return self:GetClientInfo("width"), self:GetClientInfo("height"), - math.Clamp(self:GetClientNumber("bgred"), 0, 255), + math.Clamp(self:GetClientNumber("bgred"), 0, 255), math.Clamp(self:GetClientNumber("bggreen"), 0, 255), math.Clamp(self:GetClientNumber("bgblue"), 0, 255), - math.Clamp(self:GetClientNumber("fgred"), 0, 255), + math.Clamp(self:GetClientNumber("fgred"), 0, 255), math.Clamp(self:GetClientNumber("fggreen"), 0, 255), math.Clamp(self:GetClientNumber("fgblue"), 0, 255) end end TOOL.ClientConVar = { - model = "models/props_lab/monitor01b.mdl", - width = 16, - height = 2, - createflat = 0, - bgred = 148, - bggreen = 178, - bgblue = 15, - fgred = 45, - fggreen = 91, - fgblue = 45, + model = "models/props_lab/monitor01b.mdl", + width = 16, + height = 2, + createflat = 0, + bgred = 148, + bggreen = 178, + bgblue = 15, + fgred = 45, + fggreen = 91, + fgblue = 45, } function TOOL.BuildCPanel(panel) - WireToolHelpers.MakePresetControl(panel, "wire_characterlcd") + WireToolHelpers.MakePresetControl(panel, "wire_characterlcd") WireDermaExts.ModelSelect(panel, "wire_characterlcd_model", list.Get( "WireScreenModels" ), 5) - panel:AddControl("Color", { + panel:AddControl("Color", { Label = "#tool.wire_characterlcd.bgcolor", Red = "wire_characterlcd_bgred", Green = "wire_characterlcd_bggreen", @@ -52,7 +52,7 @@ function TOOL.BuildCPanel(panel) ShowRGB = "1", Multiplier = "255" }) - panel:AddControl("Color", { + panel:AddControl("Color", { Label = "#tool.wire_characterlcd.fgcolor", Red = "wire_characterlcd_fgred", Green = "wire_characterlcd_fggreen", From ca83be4bdc04f1749f812b20b0172af7aea88f78 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 27 Aug 2025 23:40:10 +0200 Subject: [PATCH 04/50] Fix linting --- lua/entities/gmod_wire_characterlcd/cl_init.lua | 9 ++++----- lua/entities/gmod_wire_characterlcd/init.lua | 6 +++--- lua/entities/gmod_wire_consolescreen/cl_init.lua | 6 ++---- lua/entities/gmod_wire_consolescreen/init.lua | 2 +- lua/entities/gmod_wire_digitalscreen/cl_init.lua | 2 +- lua/entities/gmod_wire_digitalscreen/init.lua | 2 +- lua/entities/gmod_wire_oscilloscope.lua | 12 +++++------- 7 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lua/entities/gmod_wire_characterlcd/cl_init.lua b/lua/entities/gmod_wire_characterlcd/cl_init.lua index e28f2ca533..16275b2216 100644 --- a/lua/entities/gmod_wire_characterlcd/cl_init.lua +++ b/lua/entities/gmod_wire_characterlcd/cl_init.lua @@ -19,7 +19,7 @@ function ENT:Initialize() self.InteractiveData[i] = 0 end end - + -- Screen control: -- [1003] - Background red -- [1004] - Background green @@ -87,17 +87,16 @@ function ENT:Initialize() GPULib.ClientCacheCallback(self,function(Address,Value) self:WriteCell(Address,Value) end) - - + + WireLib.netRegister(self) end -local panel function ENT:SendData() net.Start("wire_interactiveprop_action") - + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets net.WriteEntity(self) for i=1, #data do diff --git a/lua/entities/gmod_wire_characterlcd/init.lua b/lua/entities/gmod_wire_characterlcd/init.lua index e1f929a335..95d119c004 100644 --- a/lua/entities/gmod_wire_characterlcd/init.lua +++ b/lua/entities/gmod_wire_characterlcd/init.lua @@ -1,4 +1,4 @@ -AddCSLuaFile("cl_init.lua") +AddCSLuaFile("cl_init.lua") AddCSLuaFile("shared.lua") include("shared.lua") @@ -7,7 +7,7 @@ ENT.WireDebugName = "CharacterLcdScreen" function ENT:InitInteractive() local model = self:GetModel() local outputs = {"Memory"} - local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + local interactivemodel = WireLib.GetInteractiveModel(model) for i=1, #interactivemodel.widgets do outputs[i+1] = interactivemodel.widgets[i].name end @@ -23,7 +23,7 @@ function ENT:Initialize() self:SetSolid(SOLID_VPHYSICS) self.Inputs = WireLib.CreateInputs(self, { "CharAddress", "Char (ASCII/Unicode)", "Contrast", "Clk", "Reset" }) - + self.InteractiveData = {} self.IsInteractive = false if WireLib.IsValidInteractiveModel(self:GetModel()) then diff --git a/lua/entities/gmod_wire_consolescreen/cl_init.lua b/lua/entities/gmod_wire_consolescreen/cl_init.lua index 5ccb5a1477..780605c986 100644 --- a/lua/entities/gmod_wire_consolescreen/cl_init.lua +++ b/lua/entities/gmod_wire_consolescreen/cl_init.lua @@ -1,10 +1,8 @@ include("shared.lua") -local panel - function ENT:SendData() net.Start("wire_interactiveprop_action") - + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets net.WriteEntity(self) for i=1, #data do @@ -31,7 +29,7 @@ function ENT:Initialize() for i = 0, 2047 do self.Memory1[i] = 0 end - + self.InteractiveData = {} self.LastButtons = {} self.Buttons = {} diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua index ce0f0d1502..d0086b8748 100644 --- a/lua/entities/gmod_wire_consolescreen/init.lua +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -7,7 +7,7 @@ ENT.WireDebugName = "ConsoleScreen" function ENT:InitInteractive() local model = self:GetModel() local outputs = {"Memory"} - local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + local interactivemodel = WireLib.GetInteractiveModel(model) for i=1, #interactivemodel.widgets do outputs[i+1] = interactivemodel.widgets[i].name end diff --git a/lua/entities/gmod_wire_digitalscreen/cl_init.lua b/lua/entities/gmod_wire_digitalscreen/cl_init.lua index b14c1bd788..cb76842e13 100644 --- a/lua/entities/gmod_wire_digitalscreen/cl_init.lua +++ b/lua/entities/gmod_wire_digitalscreen/cl_init.lua @@ -4,7 +4,7 @@ local panel function ENT:SendData() net.Start("wire_interactiveprop_action") - + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets net.WriteEntity(self) for i=1, #data do diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua index f882e00956..5858f21d3e 100644 --- a/lua/entities/gmod_wire_digitalscreen/init.lua +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -1,6 +1,6 @@ AddCSLuaFile( "cl_init.lua" ) AddCSLuaFile( "shared.lua" ) -include('shared.lua') +include("shared.lua") DEFINE_BASECLASS( "base_wire_entity" ) ENT.WireDebugName = "DigitalScreen" diff --git a/lua/entities/gmod_wire_oscilloscope.lua b/lua/entities/gmod_wire_oscilloscope.lua index ae41a7ac86..21ffcbeca8 100644 --- a/lua/entities/gmod_wire_oscilloscope.lua +++ b/lua/entities/gmod_wire_oscilloscope.lua @@ -4,12 +4,10 @@ ENT.PrintName = "Wire Oscilloscope" ENT.WireDebugName = "Oscilloscope" if CLIENT then - - local panel function ENT:SendData() net.Start("wire_interactiveprop_action") - + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets net.WriteEntity(self) for i=1, #data do @@ -32,8 +30,8 @@ if CLIENT then function ENT:Initialize() self.GPU = WireGPU(self) - - + + self.InteractiveData = {} self.LastButtons = {} self.Buttons = {} @@ -152,7 +150,7 @@ end function ENT:InitInteractive() local model = self:GetModel() local outputs = {} - local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + local interactivemodel = WireLib.GetInteractiveModel(model) for i=1, #interactivemodel.widgets do outputs[i+1] = interactivemodel.widgets[i].name end @@ -214,7 +212,7 @@ function ENT:Initialize() self:SetSolid( SOLID_VPHYSICS ) self.Inputs = WireLib.CreateInputs(self, { "X", "Y", "R", "G", "B", "Pause", "Length", "Update Frequency" }) - + if WireLib.IsValidInteractiveModel(self:GetModel()) then self:InitInteractive() end From a0471593e6c0a190981a789763ad1a1e189b8e9f Mon Sep 17 00:00:00 2001 From: wav3 Date: Thu, 28 Aug 2025 02:54:21 +0200 Subject: [PATCH 05/50] linter should be happy now --- lua/entities/gmod_wire_consolescreen/init.lua | 2 +- .../gmod_wire_digitalscreen/cl_init.lua | 22 +++++++++---------- lua/entities/gmod_wire_digitalscreen/init.lua | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua index d0086b8748..463cf74c21 100644 --- a/lua/entities/gmod_wire_consolescreen/init.lua +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -1,6 +1,6 @@ AddCSLuaFile("cl_init.lua") AddCSLuaFile("shared.lua") -include('shared.lua') +include("shared.lua") ENT.WireDebugName = "ConsoleScreen" diff --git a/lua/entities/gmod_wire_digitalscreen/cl_init.lua b/lua/entities/gmod_wire_digitalscreen/cl_init.lua index cb76842e13..788a343127 100644 --- a/lua/entities/gmod_wire_digitalscreen/cl_init.lua +++ b/lua/entities/gmod_wire_digitalscreen/cl_init.lua @@ -1,6 +1,6 @@ -include('shared.lua') +include("shared.lua") + -local panel function ENT:SendData() net.Start("wire_interactiveprop_action") @@ -58,15 +58,15 @@ function ENT:Initialize() self.RefreshRows[i] = i-1 end - //0..786431 - RGB data + --0..786431 - RGB data - //1048569 - Color mode (0: RGBXXX; 1: R G B) - //1048570 - Clear row - //1048571 - Clear column - //1048572 - Screen Height - //1048573 - Screen Width - //1048574 - Hardware Clear Screen - //1048575 - CLK + --1048569 - Color mode (0: RGBXXX; 1: R G B) + --1048570 - Clear row + --1048571 - Clear column + --1048572 - Screen Height + --1048573 - Screen Width + --1048574 - Hardware Clear Screen + --1048575 - CLK self.GPU = WireGPU(self) @@ -264,8 +264,6 @@ transformcolor[4] = function(c) -- XXX return c, c, c end -local floor = math.floor - function ENT:RedrawPixel(a) if a >= self.ScreenWidth*self.ScreenHeight then return end diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua index 5858f21d3e..f76e0a1cfa 100644 --- a/lua/entities/gmod_wire_digitalscreen/init.lua +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -9,7 +9,7 @@ ENT.WireDebugName = "DigitalScreen" function ENT:InitInteractive() local model = self:GetModel() local outputs = {"Memory"} - local interactivemodel = WireLib.GetInteractiveModel(self:GetModel()) + local interactivemodel = WireLib.GetInteractiveModel(model) for i=1, #interactivemodel.widgets do outputs[i+1] = interactivemodel.widgets[i].name end @@ -378,7 +378,7 @@ function ENT:WriteCell(Address, value) -- reset memory self.Memory = mem - elseif Address == 1048575 then -- CLK + -- elseif Address == 1048575 then -- CLK -- not needed atm end end From 2b4f898cb6ff200fb72941ee57e4f40391b4c280 Mon Sep 17 00:00:00 2001 From: wav3 Date: Fri, 29 Aug 2025 18:19:28 +0200 Subject: [PATCH 06/50] This should be better --- lua/entities/gmod_wire_interactiveprop.lua | 47 +++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 395e5ae217..8990df01de 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -219,24 +219,23 @@ end function WireLib.GetInteractiveWidgetBody( ent, data ) local body = vgui.Create("DFrame") - body:SetTitle(data.title) - body:SetSize(data.width, data.height) - body:SetVisible(true) - body.Paint = function( self, w, h ) -- 'function Frame:Paint( w, h )' works too - -- surface.SetDrawColor(255,255,255) - -- surface.DrawOutlinedRect(0, 0, w, h) - -- surface.SetDrawColor(0,0,0) - -- surface.DrawOutlinedRect(1, 1, w-2, h-2) - draw.RoundedBox( 4, 0, 0, w, h, Color( 255, 255, 255 ) ) - draw.RoundedBox( 4, 1, 1, w-2, h-2, Color( 64, 64, 64 ) ) - end - body:SetDraggable(false) - body:Center() - body:ShowCloseButton(true) - body:MakePopup() - for id, widget in ipairs( data.widgets ) do - WidgetBuilders[widget.type](ent, widget, body, id) - end + + body:SetTitle(data.title) + body:SetSize(data.width, data.height) + body:SetVisible(true) + body:SetDraggable(false) + body:ShowCloseButton(true) + body:MakePopup() + body:Center() + + function body:Paint(w, h) + draw.RoundedBox(4, 0, 0, w, h, color_white) + draw.RoundedBox(4, 1, 1, w - 2, h - 2, Color(64, 64, 64)) + end + + for id, widget in ipairs( data.widgets ) do + WidgetBuilders[widget.type](ent, widget, body, id) + end return body end @@ -250,7 +249,7 @@ WidgetBuilders = { local checkbox = vgui.Create("DCheckBox", body) checkbox:SetPos(data.x, data.y) checkbox:SetValue(self.InteractiveData[index]) - checkbox.OnChange = function(box,value) + function checkbox.OnChange(box, value) surface.PlaySound("buttons/lightswitch2.wav") self.InteractiveData[index] = value and 1 or 0 self:SendData() @@ -265,13 +264,13 @@ WidgetBuilders = { numberscratch:SetDecimals(4) numberscratch:SetPos(data.x, data.y) numberscratch:SetValue(self.InteractiveData[index]) - numberscratch.OnValueChanged = function(scratch,value) + function numberscratch.OnValueChanged(scratch, value) self.InteractiveData[index] = value self:SendData() end numberscratch:SetImageVisible( false ) numberscratch:SetSize( 17, 17 ) - numberscratch.Paint = function( self, w, h ) + function numberscratch:Paint( w, h ) draw.RoundedBox( 8.5, 0, 0, w, h, numberscratch.color ) local value = self:GetFloatValue() surface.SetDrawColor(255, 255, 255) @@ -289,11 +288,11 @@ WidgetBuilders = { button:SetPos(data.x, data.y) button:SetText(data.text or "") button:SetSize(data.width or 20, data.height or 20) - button.OnDepressed = function(scratch) + function button.OnDepressed(btn) self.InteractiveData[index] = 1 self:SendData() end - button.OnReleased = function(scratch) + function button.OnReleased(btn) self.InteractiveData[index] = 0 self:SendData() end @@ -342,7 +341,7 @@ if CLIENT then local self = net.ReadEntity() if not IsValid(self) then return end panel = self:GetPanel() - panel.OnClose = function(panel) + function panel.OnClose(panel) net.Start("wire_interactiveprop_close") self.Buttons = {} self.LastButtons = {} From fa18df478620410d59cb34cbbf570c70e35099e1 Mon Sep 17 00:00:00 2001 From: wav3 Date: Fri, 29 Aug 2025 19:19:10 +0200 Subject: [PATCH 07/50] get rid of trailing ws --- lua/entities/gmod_wire_interactiveprop.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 8990df01de..f41b4000e5 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -219,7 +219,7 @@ end function WireLib.GetInteractiveWidgetBody( ent, data ) local body = vgui.Create("DFrame") - + body:SetTitle(data.title) body:SetSize(data.width, data.height) body:SetVisible(true) @@ -227,7 +227,7 @@ function WireLib.GetInteractiveWidgetBody( ent, data ) body:ShowCloseButton(true) body:MakePopup() body:Center() - + function body:Paint(w, h) draw.RoundedBox(4, 0, 0, w, h, color_white) draw.RoundedBox(4, 1, 1, w - 2, h - 2, Color(64, 64, 64)) From fdd6adf7daee80ec00820993421c45c6ec9c9374 Mon Sep 17 00:00:00 2001 From: wav3 Date: Fri, 29 Aug 2025 19:19:43 +0200 Subject: [PATCH 08/50] They are visible by default --- lua/entities/gmod_wire_interactiveprop.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index f41b4000e5..e3acce6dd4 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -222,9 +222,7 @@ function WireLib.GetInteractiveWidgetBody( ent, data ) body:SetTitle(data.title) body:SetSize(data.width, data.height) - body:SetVisible(true) body:SetDraggable(false) - body:ShowCloseButton(true) body:MakePopup() body:Center() From eb55f3d005030a71a8b166d923a34c2674aaa8fa Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 1 Sep 2025 01:03:10 +0200 Subject: [PATCH 09/50] Optional interactiveness --- lua/entities/gmod_wire_characterlcd/init.lua | 21 ++++++++++++++++--- lua/entities/gmod_wire_consolescreen/init.lua | 18 ++++++++++++++-- lua/entities/gmod_wire_digitalscreen/init.lua | 17 ++++++++++++--- lua/entities/gmod_wire_oscilloscope.lua | 18 +++++++++++++++- lua/wire/stools/characterlcd.lua | 6 +++++- lua/wire/stools/consolescreen.lua | 10 ++++++++- lua/wire/stools/digitalscreen.lua | 5 ++++- lua/wire/stools/oscilloscope.lua | 10 ++++++++- 8 files changed, 92 insertions(+), 13 deletions(-) diff --git a/lua/entities/gmod_wire_characterlcd/init.lua b/lua/entities/gmod_wire_characterlcd/init.lua index 95d119c004..7622d96bb9 100644 --- a/lua/entities/gmod_wire_characterlcd/init.lua +++ b/lua/entities/gmod_wire_characterlcd/init.lua @@ -15,8 +15,11 @@ function ENT:InitInteractive() self.NextPrompt = 0 self.Outputs=WireLib.CreateOutputs(self,outputs) self.IsInteractive = true + self:UpdateOverlay() end + + function ENT:Initialize() self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) @@ -61,7 +64,7 @@ function ENT:Initialize() self.Cache = GPUCacheManager(self,true) end -function ENT:Setup(ScreenWidth, ScreenHeight, bgred,bggreen,bgblue,fgred,fggreen,fgblue) +function ENT:Setup(ScreenWidth, ScreenHeight, bgred,bggreen,bgblue,fgred,fggreen,fgblue,IsInteractive) self:WriteCell(1010, tonumber(ScreenHeight) or 2) self:WriteCell(1009, tonumber(ScreenWidth) or 16) self:WriteCell(1008, tonumber(fgblue) or 45) @@ -71,6 +74,7 @@ function ENT:Setup(ScreenWidth, ScreenHeight, bgred,bggreen,bgblue,fgred,fggreen self:WriteCell(1004, tonumber(bggreen) or 178) self:WriteCell(1003, tonumber(bgred) or 148) self:WriteCell(1023,1) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) end function ENT:SendPixel() if (self.Memory[1023] ~= 0) and (self.CharAddress >= 0) and (self.CharAddress < self.ScreenWidth*self.ScreenHeight) then @@ -209,11 +213,22 @@ function ENT:ReceiveData() end end -function ENT:UpdateOverlay() -- required by interactiveprop functions +function ENT:UpdateOverlay() + if not self.IsInteractive then + return + end + + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + + self:SetOverlayText(txt) end + function ENT:Prompt( ply ) if not self.IsInteractive then return end if ply then @@ -245,4 +260,4 @@ function ENT:Unprompt() self.User = nil end -duplicator.RegisterEntityClass("gmod_wire_characterlcd", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight") +duplicator.RegisterEntityClass("gmod_wire_characterlcd", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight", "IsInteractive") diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua index 463cf74c21..ae0672e645 100644 --- a/lua/entities/gmod_wire_consolescreen/init.lua +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -15,6 +15,7 @@ function ENT:InitInteractive() self.NextPrompt = 0 self.Outputs=WireLib.CreateOutputs(self,outputs) self.IsInteractive = true + self:UpdateOverlay() end @@ -26,8 +27,17 @@ function ENT:ReceiveData() end end -function ENT:UpdateOverlay() -- required by interactiveprop functions +function ENT:UpdateOverlay() + if not self.IsInteractive then + return + end + + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + self:SetOverlayText(txt) end @@ -52,6 +62,10 @@ function ENT:Prompt( ply ) end end +function ENT:Setup(IsInteractive) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) +end + function ENT:Use(ply) if not IsValid( ply ) then return end self:Prompt( ply ) @@ -279,4 +293,4 @@ function ENT:ClientWriteCell(Address, value) end end -duplicator.RegisterEntityClass("gmod_wire_consolescreen", WireLib.MakeWireEnt, "Data") +duplicator.RegisterEntityClass("gmod_wire_consolescreen", WireLib.MakeWireEnt, "Data", "IsInteractive") diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua index f76e0a1cfa..23d49f53d2 100644 --- a/lua/entities/gmod_wire_digitalscreen/init.lua +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -17,6 +17,7 @@ function ENT:InitInteractive() self.NextPrompt = 0 self.Outputs=WireLib.CreateOutputs(self,outputs) self.IsInteractive = true + self:UpdateOverlay() end @@ -28,8 +29,17 @@ function ENT:ReceiveData() end end -function ENT:UpdateOverlay() -- required by interactiveprop functions +function ENT:UpdateOverlay() + if not self.IsInteractive then + return + end + + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + self:SetOverlayText(txt) end @@ -97,9 +107,10 @@ function ENT:Initialize() self.ChangedStep = 1 end -function ENT:Setup(ScreenWidth, ScreenHeight) +function ENT:Setup(ScreenWidth, ScreenHeight, IsInteractive) self:WriteCell(1048572, ScreenHeight or 32) self:WriteCell(1048573, ScreenWidth or 32) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) end function ENT:SendPixel() @@ -418,4 +429,4 @@ function ENT:TriggerInput(iname, value) end end -duplicator.RegisterEntityClass("gmod_wire_digitalscreen", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight") +duplicator.RegisterEntityClass("gmod_wire_digitalscreen", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight", "IsInteractive") diff --git a/lua/entities/gmod_wire_oscilloscope.lua b/lua/entities/gmod_wire_oscilloscope.lua index 21ffcbeca8..77731ff233 100644 --- a/lua/entities/gmod_wire_oscilloscope.lua +++ b/lua/entities/gmod_wire_oscilloscope.lua @@ -146,6 +146,9 @@ function ENT:SetNextNode(x, y) net.SendPVS( self:GetPos() ) end +function ENT:Setup(IsInteractive) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) +end function ENT:InitInteractive() local model = self:GetModel() @@ -158,6 +161,7 @@ function ENT:InitInteractive() self.NextPrompt = 0 self.Outputs=WireLib.CreateOutputs(self,outputs) self.IsInteractive = true + self:UpdateOverlay() end @@ -169,11 +173,22 @@ function ENT:ReceiveData() end end -function ENT:UpdateOverlay() -- required by interactiveprop functions +function ENT:UpdateOverlay() + if not self.IsInteractive then + return + end + + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + + self:SetOverlayText(txt) end + function ENT:Prompt( ply ) if not self.IsInteractive then return end if ply then @@ -197,6 +212,7 @@ end function ENT:Use(ply) if not IsValid( ply ) then return end + if not self.IsInteractive then return end self:Prompt( ply ) end diff --git a/lua/wire/stools/characterlcd.lua b/lua/wire/stools/characterlcd.lua index cbb9565e61..05b6ca756c 100644 --- a/lua/wire/stools/characterlcd.lua +++ b/lua/wire/stools/characterlcd.lua @@ -6,6 +6,7 @@ if CLIENT then language.Add( "tool.wire_characterlcd.desc", "Spawns a Character LCD, which can be used to display text" ) language.Add( "tool.wire_characterlcd.bgcolor", "Background color:" ) language.Add( "tool.wire_characterlcd.fgcolor", "Text color:" ) + language.Add( "tool.wire_characterlcd.interactive", "Interactive (if available):" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") @@ -21,7 +22,8 @@ if SERVER then math.Clamp(self:GetClientNumber("bgblue"), 0, 255), math.Clamp(self:GetClientNumber("fgred"), 0, 255), math.Clamp(self:GetClientNumber("fggreen"), 0, 255), - math.Clamp(self:GetClientNumber("fgblue"), 0, 255) + math.Clamp(self:GetClientNumber("fgblue"), 0, 255), + self:GetClientNumber("interactive") end end @@ -36,6 +38,7 @@ TOOL.ClientConVar = { fgred = 45, fggreen = 91, fgblue = 45, + interactive = 1, } @@ -64,6 +67,7 @@ function TOOL.BuildCPanel(panel) }) panel:NumSlider("Width", "wire_characterlcd_width", 1, 56, 0) panel:NumSlider("Height", "wire_characterlcd_height", 1, 16, 0) + panel:CheckBox("#tool.wire_characterlcd.interactive", "wire_characterlcd_interactive") panel:CheckBox("#Create Flat to Surface", "wire_characterlcd_createflat") end diff --git a/lua/wire/stools/consolescreen.lua b/lua/wire/stools/consolescreen.lua index 836b51e427..aa1d4bbb95 100644 --- a/lua/wire/stools/consolescreen.lua +++ b/lua/wire/stools/consolescreen.lua @@ -4,6 +4,7 @@ WireToolSetup.open( "consolescreen", "Console Screen", "gmod_wire_consolescreen" if CLIENT then language.Add( "tool.wire_consolescreen.name", "Console Screen Tool (Wire)" ) language.Add( "tool.wire_consolescreen.desc", "Spawns a console screen" ) + language.Add( "tool.wire_consolescreen.interactive", "Interactive (if available):" ) TOOL.Information = { { name = "left", text = "Create " .. TOOL.Name } } WireToolSetup.setToolMenuIcon( "icon16/application_xp_terminal.png" ) @@ -11,14 +12,21 @@ end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 20 ) -TOOL.NoLeftOnClass = true -- no update ent function needed +if SERVER then + function TOOL:GetConVars() + return self:GetClientNumber("interactive") + end +end + TOOL.ClientConVar = { model = "models/props_lab/monitor01b.mdl", createflat = 0, + interactive = 1, } function TOOL.BuildCPanel(panel) WireDermaExts.ModelSelect(panel, "wire_consolescreen_model", list.Get( "WireScreenModels" ), 5) + panel:CheckBox("#tool.wire_consolescreen.interactive", "wire_consolescreen_interactive") panel:CheckBox("#Create Flat to Surface", "wire_consolescreen_createflat") panel:Help("CharParam is LBBBFFF format: background and foreground colour of the character (one digit each for RGB), if L is nonzero the char flashes") end diff --git a/lua/wire/stools/digitalscreen.lua b/lua/wire/stools/digitalscreen.lua index 0a3b03ed27..5f984d5ecf 100644 --- a/lua/wire/stools/digitalscreen.lua +++ b/lua/wire/stools/digitalscreen.lua @@ -4,6 +4,7 @@ WireToolSetup.open( "digitalscreen", "Digital Screen", "gmod_wire_digitalscreen" if CLIENT then language.Add( "tool.wire_digitalscreen.name", "Digital Screen Tool (Wire)" ) language.Add( "tool.wire_digitalscreen.desc", "Spawns a digital screen, which can be used to draw pixel by pixel." ) + language.Add( "tool.wire_digitalscreen.interactive", "Interactive (if available):" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") @@ -13,7 +14,7 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() - return self:GetClientInfo("width"), self:GetClientInfo("height") + return self:GetClientInfo("width"), self:GetClientInfo("height"), self:GetClientNumber("interactive") end end @@ -22,11 +23,13 @@ TOOL.ClientConVar = { width = 32, height = 32, createflat = 0, + interactive = 1, } function TOOL.BuildCPanel(panel) WireDermaExts.ModelSelect(panel, "wire_digitalscreen_model", list.Get( "WireScreenModels" ), 5) panel:NumSlider("Width", "wire_digitalscreen_width", 1, 512, 0) panel:NumSlider("Height", "wire_digitalscreen_height", 1, 512, 0) + panel:CheckBox("#tool.wire_digitalscreen.interactive", "wire_digitalscreen_interactive") panel:CheckBox("#Create Flat to Surface", "wire_digitalscreen_createflat") end diff --git a/lua/wire/stools/oscilloscope.lua b/lua/wire/stools/oscilloscope.lua index 531af22dbe..ff97632400 100644 --- a/lua/wire/stools/oscilloscope.lua +++ b/lua/wire/stools/oscilloscope.lua @@ -4,6 +4,7 @@ WireToolSetup.open( "oscilloscope", "Oscilloscope", "gmod_wire_oscilloscope", ni if CLIENT then language.Add( "tool.wire_oscilloscope.name", "Oscilloscope Tool (Wire)" ) language.Add( "tool.wire_oscilloscope.desc", "Spawns an oscilloscope that displays line graphs." ) + language.Add( "tool.wire_oscilloscope.interactive", "Interactive (if available):" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/chart_line.png") @@ -11,13 +12,20 @@ end WireToolSetup.BaseLang() WireToolSetup.SetupMax( 20 ) -TOOL.NoLeftOnClass = true -- no update ent function needed +if SERVER then + function TOOL:GetConVars() + return self:GetClientNumber("interactive") + end +end + TOOL.ClientConVar = { model = "models/props_lab/monitor01b.mdl", createflat = 0, + interactive = 1, } function TOOL.BuildCPanel(panel) WireDermaExts.ModelSelect(panel, "wire_oscilloscope_model", list.Get( "WireScreenModels" ), 5) + panel:CheckBox("#tool.wire_oscilloscope.interactive", "wire_oscilloscope_interactive") panel:CheckBox("#Create Flat to Surface", "wire_oscilloscope_createflat") end From 599365b636ccd48f2efab71557eef5d28bfcefac Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 3 Sep 2025 08:52:02 +0200 Subject: [PATCH 10/50] Fix lint --- lua/entities/gmod_wire_characterlcd/init.lua | 2 +- lua/entities/gmod_wire_consolescreen/init.lua | 2 +- lua/entities/gmod_wire_digitalscreen/init.lua | 2 +- lua/entities/gmod_wire_oscilloscope.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/entities/gmod_wire_characterlcd/init.lua b/lua/entities/gmod_wire_characterlcd/init.lua index 7622d96bb9..1acba99350 100644 --- a/lua/entities/gmod_wire_characterlcd/init.lua +++ b/lua/entities/gmod_wire_characterlcd/init.lua @@ -218,7 +218,7 @@ function ENT:UpdateOverlay() if not self.IsInteractive then return end - + txt = "" if IsValid(self.User) then txt = "In use by: " .. self.User:Nick() diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua index ae0672e645..252002559a 100644 --- a/lua/entities/gmod_wire_consolescreen/init.lua +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -31,7 +31,7 @@ function ENT:UpdateOverlay() if not self.IsInteractive then return end - + txt = "" if IsValid(self.User) then txt = "In use by: " .. self.User:Nick() diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua index 23d49f53d2..3be26fc1ee 100644 --- a/lua/entities/gmod_wire_digitalscreen/init.lua +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -33,7 +33,7 @@ function ENT:UpdateOverlay() if not self.IsInteractive then return end - + txt = "" if IsValid(self.User) then txt = "In use by: " .. self.User:Nick() diff --git a/lua/entities/gmod_wire_oscilloscope.lua b/lua/entities/gmod_wire_oscilloscope.lua index 77731ff233..c7320560b3 100644 --- a/lua/entities/gmod_wire_oscilloscope.lua +++ b/lua/entities/gmod_wire_oscilloscope.lua @@ -178,7 +178,7 @@ function ENT:UpdateOverlay() if not self.IsInteractive then return end - + txt = "" if IsValid(self.User) then txt = "In use by: " .. self.User:Nick() From 72c52c7aa6b194aac0b203b78e9e1eda013ea928 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 16:14:31 +0200 Subject: [PATCH 11/50] Multisegment lcd --- lua/entities/gmod_wire_addressbus.lua | 2 +- .../gmod_wire_multisegmentlcd/cl_init.lua | 133 ++++++ .../gmod_wire_multisegmentlcd/init.lua | 146 +++++++ .../gmod_wire_multisegmentlcd/shared.lua | 16 + lua/wire/stools/multisegmentlcd.lua | 381 ++++++++++++++++++ 5 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 lua/entities/gmod_wire_multisegmentlcd/cl_init.lua create mode 100644 lua/entities/gmod_wire_multisegmentlcd/init.lua create mode 100644 lua/entities/gmod_wire_multisegmentlcd/shared.lua create mode 100644 lua/wire/stools/multisegmentlcd.lua diff --git a/lua/entities/gmod_wire_addressbus.lua b/lua/entities/gmod_wire_addressbus.lua index efadda46f5..9afb40b31d 100644 --- a/lua/entities/gmod_wire_addressbus.lua +++ b/lua/entities/gmod_wire_addressbus.lua @@ -95,7 +95,7 @@ end function ENT:TriggerInput(iname, value) for i = 1,4 do if iname == "Memory"..i then - self.Memory[i] = self.Inputs["Memory"..i].Src + self.Memory[i] = self.Inputs["Memory"..i].Src and self.Inputs["Memory"..i].Src:GetTable() or nil end end end diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua new file mode 100644 index 0000000000..f8f94bb958 --- /dev/null +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -0,0 +1,133 @@ +include("shared.lua") + + +function ENT:Initialize() + self.Memory = {} + + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + local interactive_model = WireLib.GetInteractiveModel(self:GetModel()) + self.IsInteractive = false + if interactive_model then + self.IsInteractive = true + for i=1, #WireLib.GetInteractiveModel(self:GetModel()).widgets do + self.InteractiveData[i] = 0 + end + end + + self.GPU = WireGPU(self) + + GPULib.ClientCacheCallback(self,function(Address,Value) + self:WriteCell(Address,Value) + end) + + WireLib.netRegister(self) +end + + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end + +function ENT:GetPanel() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()) + return WireLib.GetInteractiveWidgetBody(self, data) +end + + +function ENT:AddButton(id,button) + if not self.IsInteractive then return end + self.Buttons[id] = button +end + +function ENT:OnRemove() + self.GPU:Finalize() +end + +function ENT:ReadCell(Address) + return self.Memory[math.floor(Address)] +end + +function ENT:WriteCell(Address,value) + self.Memory[math.floor(Address)] = value +end + +function ENT:DrawSegment(segment) + if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then + surface.DrawRect(segment.X+self.LocalX,segment.Y+self.LocalY,segment.W,segment.H) + end + self.BitIndex = self.BitIndex+1 +end + +function ENT:DrawUnion(group) + self.LocalX = self.LocalX + (group.X or 0) + self.LocalY = self.LocalY + (group.Y or 0) + local savedindex = self.BitIndex + local biggestindex = savedindex + for k,v in ipairs(group.Children) do + if v.Type == GROUP then + self:DrawGroup(v) + elseif v.Type == UNION then + self:DrawUnion(v) + elseif v.Type == SEGMENT then + self:DrawSegment(v) + end + biggestindex = math.max(biggestindex,self.BitIndex) + self.BitIndex = savedindex + end + self.BitIndex = biggestindex + self.LocalX = self.LocalX - (group.X or 0) + self.LocalY = self.LocalY - (group.Y or 0) +end + +function ENT:DrawGroup(group) + self.LocalX = self.LocalX + (group.X or 0) + self.LocalY = self.LocalY + (group.Y or 0) + for k,v in ipairs(group.Children) do + if v.Type == GROUP then + self:DrawGroup(v) + elseif v.Type == UNION then + self:DrawUnion(v) + elseif v.Type == SEGMENT then + self:DrawSegment(v) + end + end + self.LocalX = self.LocalX - (group.X or 0) + self.LocalY = self.LocalY - (group.Y or 0) +end + +function ENT:Draw() + self:DrawModel() + self.GPU:RenderToGPU(function() + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(0,0,1024,1024) + + if self.Tree then + surface.SetDrawColor(255,255,255,255) + self.BitIndex = 0 + self.LocalX = 0 + self.LocalY = 0 + self:DrawGroup(self.Tree) + end + end) + self.GPU:Render(0,0,1024,1024,nil,0,0) + Wire_Render(self) +end + +function ENT:IsTranslucent() + return true +end + +function ENT:Receive() + local ent = net.ReadEntity() + self.Tree = net.ReadTable() +end diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua new file mode 100644 index 0000000000..2238b5a1fa --- /dev/null +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -0,0 +1,146 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +ENT.WireDebugName = "MultiSegmentLcdScreen" + + + +function ENT:InitInteractive() + local model = self:GetModel() + local outputs = {"Memory"} + local interactivemodel = WireLib.GetInteractiveModel(model) + for i=1, #interactivemodel.widgets do + outputs[i+1] = interactivemodel.widgets[i].name + end + self.BlockInput = false + self.NextPrompt = 0 + self.Outputs=WireLib.CreateOutputs(self,outputs) + self.IsInteractive = true + self:UpdateOverlay() +end + +util.AddNetworkString("wire_multisegmentlcd_init") + +function ENT:Initialize() + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + + self.InteractiveData = {} + self.IsInteractive = false + if WireLib.IsValidInteractiveModel(self:GetModel()) then + self:InitInteractive() + else + self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) + end + + self.Memory = {} + self.Cache = GPUCacheManager(self,true) +end + +function ENT:SendSerializedTree(ply) + if self.Tree == nil then return end + WireLib.netStart(self) + net.WriteEntity(self) + net.WriteTable(self.Tree) + WireLib.netEnd(ply) +end + +function ENT:Retransmit(ply) + self:SendSerializedTree(ply) + + self.Cache:Flush() + for address,value in pairs(self.Memory) do + self.Cache:Write(address,value) + end + self.Cache:Flush(ply) +end + +function ENT:Setup(IsInteractive) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) +end + +function ENT:ReadCell(Address) + Address = math.floor(Address) + if Address < 0 then return nil end + + return self.Memory[Address] +end + +function ENT:WriteCell(Address, value) + Address = math.floor(Address) + if Address < 0 then return false end + + self.Memory[Address] = value + self.Cache:Write(Address, value) + return true +end + +function ENT:TriggerInput(iname, value) + +end + +function ENT:Think() + self.Cache:Flush() + self:NextThink(CurTime()+0.01) + return true +end + + +function ENT:ReceiveData() + if not self.IsInteractive then return end + local data = WireLib.GetInteractiveModel(self:GetModel()).widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end + + +function ENT:UpdateOverlay() + if not self.IsInteractive then + return + end + + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + + self:SetOverlayText(txt) +end + + + +function ENT:Prompt( ply ) + if not self.IsInteractive then return end + if ply then + if CurTime() < self.NextPrompt then return end -- anti spam + self.NextPrompt = CurTime() + 0.1 + + if IsValid( self.User ) then + WireLib.AddNotify(ply,"That interactive prop is in use by another player!",NOTIFY_ERROR,5,6) + return + end + + self.User = ply + + net.Start( "wire_interactiveprop_show" ) + net.WriteEntity( self ) + net.Send( ply ) + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +function ENT:Use(ply) + if not IsValid( ply ) then return end + self:Prompt( ply ) +end + +function ENT:Unprompt() + if not self.IsInteractive then return end + self.User = nil +end + +duplicator.RegisterEntityClass("gmod_wire_multisegmentlcd", WireLib.MakeWireEnt, "Data", "IsInteractive") diff --git a/lua/entities/gmod_wire_multisegmentlcd/shared.lua b/lua/entities/gmod_wire_multisegmentlcd/shared.lua new file mode 100644 index 0000000000..c2a1f25af3 --- /dev/null +++ b/lua/entities/gmod_wire_multisegmentlcd/shared.lua @@ -0,0 +1,16 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Multi-segment LCD" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false + +ENT.RenderGroup = RENDERGROUP_BOTH + +GROUP = -1 +UNION = 0 +SEGMENT = 1 \ No newline at end of file diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua new file mode 100644 index 0000000000..cdf2cbe305 --- /dev/null +++ b/lua/wire/stools/multisegmentlcd.lua @@ -0,0 +1,381 @@ +WireToolSetup.setCategory( "Visuals/Screens" ) +WireToolSetup.open( "multisegmentlcd", "Multi-segment LCD", "gmod_wire_multisegmentlcd", nil, "Multi-segment LCDs" ) + +GROUP = -1 +UNION = 0 +SEGMENT = 1 + +WireLib.SegmentLCD_Tree = { + Type=GROUP, + X=0, + Y=0, + Children= + { + + } + } + + +if CLIENT then + language.Add( "tool.wire_multisegmentlcd.name", "Multi-segment LCD Tool (Wire)" ) + language.Add( "tool.wire_multisegmentlcd.desc", "Spawns a Multi-segment LCD, which can be used to display numbers and miscellaneous graphics" ) + language.Add( "tool.wire_multisegmentlcd.interactive", "Interactive (if available):" ) + TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } + + WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") + + + + net.Receive("wire_multisegmentlcd_tool_upload_request", function(len, ply) + local ent = net.ReadUInt(16) + net.Start("wire_multisegmentlcd_tool_upload") + net.WriteUInt(ent,16) + net.WriteTable(WireLib.SegmentLCD_Tree) + net.SendToServer() + --ent.Tree = table.Copy(WireLib.SegmentLCD_Tree) + end) +end +WireToolSetup.BaseLang() +WireToolSetup.SetupMax( 20 ) + +if SERVER then + + function TOOL:GetConVars() + return self:GetClientNumber("interactive") + end + + util.AddNetworkString("wire_multisegmentlcd_tool_upload_request") + util.AddNetworkString("wire_multisegmentlcd_tool_upload") + + + function TOOL:LeftClick_Update( trace ) + net.Start("wire_multisegmentlcd_tool_upload_request") + net.WriteUInt(trace.Entity:EntIndex(),16) + net.Send(self:GetOwner()) + end + + net.Receive("wire_multisegmentlcd_tool_upload", function(len, ply) + local ent = ents.GetByIndex(net.ReadUInt(16)) + ent.Tree = net.ReadTable() + ent:Retransmit() + end) + + function TOOL:MakeEnt( ply, model, Ang, trace ) + local ent = WireLib.MakeWireEnt( ply, {Class = self.WireClass, Pos=trace.HitPos, Angle=Ang, Model=model}, self:GetConVars() ) + if ent and ent.RestoreNetworkVars then ent:RestoreNetworkVars(self:GetDataTables()) end + net.Start("wire_multisegmentlcd_tool_upload_request") + net.WriteUInt(ent:EntIndex(),16) + net.Send(ply) + return ent + end +end + +TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + createflat = 0, + interactive = 1, + +} + + +function BuildNode(v,node,group) + local new = nil + if v.Type == GROUP then + new = node:AddNode( "Group", "icon16/text_list_numbers.png" ) + BuildNodes(new,v) + elseif v.Type == UNION then + new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) + BuildNodes(new,v) + else + new = node:AddNode( "Segment", "icon16/bullet_green.png" ) + end + new.group = v + new.parentgroup = group +end + +function BuildNodes(node,group) + for i,v in ipairs(group.Children) do + BuildNode(v,node,group) + end +end + +local invalid_filename_chars = { + ["*"] = "", + ["?"] = "", + [">"] = "", + ["<"] = "", + ["|"] = "", + ["\\"] = "", + ['"'] = "", + [" "] = "_", +} + +function TOOL.BuildCPanel(panel) + WireDermaExts.ModelSelect(panel, "wire_multisegmentlcd_model", list.Get( "WireScreenModels" ), 5) + panel:CheckBox("#tool.wire_multisegmentlcd.interactive", "wire_multisegmentlcd_interactive") + panel:CheckBox("#Create Flat to Surface", "wire_multisegmentlcd_createflat") + TreeDataHolder = vgui.Create("DPanel", panel) + panel:AddPanel(TreeDataHolder) + TreeDataHolder:DockMargin(0, 0, 0, 0) + TreeDataHolder:Dock(TOP) + TreeDataHolder:SetHeight(480) + DisplayData = vgui.Create("DTree", TreeDataHolder) + DisplayData:Dock(FILL) + DisplayData:DockMargin(0, 0, 0, 0) + --DisplayData.RootNode:AddNode( "Root", "icon16/monitor.png" ) + DisplayData.RootNode.group = WireLib.SegmentLCD_Tree + BuildNodes(DisplayData.RootNode,WireLib.SegmentLCD_Tree) + ButtonsHolder = TreeDataHolder:Add( "DPanel" ) + ButtonsHolder:Dock(TOP) + ButtonsHolder:DockMargin(0, 0, 0, 0) + ButtonsHolder.buttons = {} + ButtonsHolder:SetHeight(48) + + AddSegment = ButtonsHolder:Add( "DButton" ) + AddSegment:SetText( "Add Segment" ) + ButtonsHolder.buttons[1] = AddSegment + function AddSegment:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=SEGMENT, X=30,Y=0,W=10,H=20} + children[#children+1] = newgroup + local new = node:AddNode( "Segment", "icon16/bullet_green.png" ) + new.group = newgroup + new.parentgroup = group + end + + AddGroup = ButtonsHolder:Add( "DButton" ) + AddGroup:SetText( "Add Group" ) + ButtonsHolder.buttons[2] = AddGroup + function AddGroup:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=GROUP,Children={},X=0,Y=0} + children[#children+1] = newgroup + local new = node:AddNode( "Group", "icon16/text_list_numbers.png" ) + new.group = newgroup + new.parentgroup = group + end + + AddUnion = ButtonsHolder:Add( "DButton" ) + AddUnion:SetText( "Add Union" ) + ButtonsHolder.buttons[3] = AddUnion + function AddUnion:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=UNION,Children={},X=0,Y=0} + children[#children+1] = newgroup + local new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) + new.group = newgroup + new.parentgroup = group + end + + Remove = ButtonsHolder:Add( "DButton" ) + Remove:SetText( "Remove" ) + ButtonsHolder.buttons[4] = Remove + function Remove:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + return + end + local parentgroup = node.parentgroup + if parentgroup == nil then + return + end + for i,v in pairs(parentgroup.Children) do + if v == node.group then + table.remove(parentgroup.Children,i) + node:Remove() + return + end + end + end + ButtonsHolder.textboxes = {} + WangX = ButtonsHolder:Add( "DNumberWang" ) + function WangX:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil then + return + end + node.group.X = value + end + ButtonsHolder.textboxes[1] = WangX + WangY = ButtonsHolder:Add( "DNumberWang" ) + function WangY:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil then + return + end + node.group.Y = value + end + ButtonsHolder.textboxes[2] = WangY + WangW = ButtonsHolder:Add( "DNumberWang" ) + function WangW:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group.Type ~= SEGMENT then + return + end + node.group.W = value + end + ButtonsHolder.textboxes[3] = WangW + WangH = ButtonsHolder:Add( "DNumberWang" ) + function WangH:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group.Type ~= SEGMENT then + return + end + node.group.H = value + end + ButtonsHolder.textboxes[4] = WangH + function ButtonsHolder:PerformLayout(w, h) + for i,v in ipairs(self.buttons) do + v:SetPos((i-1)*w/#self.buttons,0) + v:SetSize(w/#self.buttons,h/2) + end + for i,v in ipairs(self.textboxes) do + v:SetPos((i-1)*w/#self.textboxes,h/2) + v:SetSize(w/#self.textboxes,h/2) + end + end + + + + function DisplayData:DoClick(node) + group = node.group + if group.Type == SEGMENT then + WangX:SetValue(group.X) + WangY:SetValue(group.Y) + WangW:SetValue(group.W) + WangH:SetValue(group.H) + else + WangX:SetValue(group.X) + WangY:SetValue(group.Y) + WangW:SetValue(0) + WangH:SetValue(0) + end + return true + end + + function DisplayData:DoRightClick(node) + local Menu = DermaMenu() + Menu:AddOption( "Copy" ) + Menu:AddOption( "Paste" ) + Menu:Open() + function Menu:OptionSelected(option, optionText) + if optionText == "Copy" then + SegmentLCD_Clipboard = table.Copy(node.group) + elseif optionText == "Paste" then + if node.group.Children then + local newgroup = table.Copy(SegmentLCD_Clipboard) + node.group.Children[#node.group.Children+1] = newgroup + BuildNode(newgroup,node,node.group) + else + local newgroup = table.Copy(SegmentLCD_Clipboard) + WireLib.SegmentLCD_Tree.Children[#WireLib.SegmentLCD_Tree.Children+1] = newgroup + BuildNode(newgroup,DisplayData.RootNode,WireLib.SegmentLCD_Tree) + end + end + end + return true + end + + local FileBrowser = vgui.Create("wire_expression2_browser", panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("multisegmentlcd") + FileBrowser:SetSize(w, 320) + FileBrowser:DockMargin(0, 0, 0, 0) + FileBrowser:DockPadding(0, 0, 0, 0) + FileBrowser:Dock(TOP) + FileBrowser:RemoveRightClick("New File") + + for k, v in pairs(FileBrowser.foldermenu) do + if (v[1] == "New File..") then + FileBrowser.foldermenu[k] = nil + break + end + end + + function SaveFile(curloc,path) + file.CreateDir(curloc) + file.Write(path, util.TableToJSON(WireLib.SegmentLCD_Tree)) + FileBrowser:UpdateFolders() + end + + function FileBrowser:OnFileOpen(filepath, newtab) + local newgroup = util.JSONToTable(file.Read(filepath)) + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + node.group.Children[#node.group.Children+1] = newgroup + BuildNode(newgroup,node,node.group) + end + + local Save = panel:Button("Save") + function Save:DoClick() + Derma_StringRequestNoBlur("Save to file", "", "", + function(strTextOut) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + local curlocation = "multisegmentlcd" + if FileBrowser.File then + local newcurloc = FileBrowser.File:GetFolder() + curlocation = newcurloc or curlocation + end + local save_location = curlocation .. "/" .. strTextOut .. ".txt" + if file.Exists(save_location, "DATA") then + Derma_QueryNoBlur("The file '" .. strTextOut .. "' already exists. Do you want to overwrite it?", "File already exists", + "Yes", function() SaveFile(curlocation,save_location) end, + "No", function() end) + else + SaveFile(curlocation,save_location) + end + end) + end +end From c002c2fb96833d1534fdd6160ab08a707da26b5a Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 16:45:14 +0200 Subject: [PATCH 12/50] try using RenderToWorld --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index f8f94bb958..a028243221 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -16,7 +16,7 @@ function ENT:Initialize() end end - self.GPU = WireGPU(self) + self.GPU = WireGPU(self, true) GPULib.ClientCacheCallback(self,function(Address,Value) self:WriteCell(Address,Value) @@ -107,19 +107,17 @@ end function ENT:Draw() self:DrawModel() - self.GPU:RenderToGPU(function() + self.GPU:RenderToWorld(nil, 188, function(x, y, w, h) surface.SetDrawColor(0,0,0,255) - surface.DrawRect(0,0,1024,1024) - + surface.DrawRect(x,y,w,h) if self.Tree then surface.SetDrawColor(255,255,255,255) self.BitIndex = 0 - self.LocalX = 0 - self.LocalY = 0 + self.LocalX = x + self.LocalY = y self:DrawGroup(self.Tree) end end) - self.GPU:Render(0,0,1024,1024,nil,0,0) Wire_Render(self) end From 94e019ca2115cbef926c34e7f192baefc9470b52 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 17:17:13 +0200 Subject: [PATCH 13/50] Have a ratio changer --- .../gmod_wire_multisegmentlcd/cl_init.lua | 15 ++++++++++----- lua/entities/gmod_wire_multisegmentlcd/init.lua | 10 +++++++++- lua/wire/stools/multisegmentlcd.lua | 10 ++++++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index a028243221..a6b3523a6e 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -16,7 +16,9 @@ function ENT:Initialize() end end - self.GPU = WireGPU(self, true) + self.GPU = WireGPU(self) + self.ResolutionW = 1024 + self.ResolutionH = 1024 GPULib.ClientCacheCallback(self,function(Address,Value) self:WriteCell(Address,Value) @@ -107,17 +109,18 @@ end function ENT:Draw() self:DrawModel() - self.GPU:RenderToWorld(nil, 188, function(x, y, w, h) + self.GPU:RenderToGPU(function() surface.SetDrawColor(0,0,0,255) - surface.DrawRect(x,y,w,h) + surface.DrawRect(0,0,1024,1024) if self.Tree then surface.SetDrawColor(255,255,255,255) self.BitIndex = 0 - self.LocalX = x - self.LocalY = y + self.LocalX = 0 + self.LocalY = 0 self:DrawGroup(self.Tree) end end) + self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) Wire_Render(self) end @@ -128,4 +131,6 @@ end function ENT:Receive() local ent = net.ReadEntity() self.Tree = net.ReadTable() + self.ResolutionW = net.ReadUInt(16) + self.ResolutionH = net.ReadUInt(16) end diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index 2238b5a1fa..34ac19d3cf 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -35,6 +35,9 @@ function ENT:Initialize() self.Outputs = WireLib.CreateOutputs(self, { "Memory" }) end + self.ResolutionW = 1024 + self.ResolutionH = 1024 + self.Memory = {} self.Cache = GPUCacheManager(self,true) end @@ -44,6 +47,8 @@ function ENT:SendSerializedTree(ply) WireLib.netStart(self) net.WriteEntity(self) net.WriteTable(self.Tree) + net.WriteUInt(self.ResolutionW,16) + net.WriteUInt(self.ResolutionH,16) WireLib.netEnd(ply) end @@ -57,8 +62,11 @@ function ENT:Retransmit(ply) self.Cache:Flush(ply) end -function ENT:Setup(IsInteractive) +function ENT:Setup(IsInteractive, ResolutionW, ResolutionH) self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) + self.ResolutionW = ResolutionW + self.ResolutionH = ResolutionH + self:Retransmit() end function ENT:ReadCell(Address) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index cdf2cbe305..b9fff3e6d3 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -20,6 +20,8 @@ if CLIENT then language.Add( "tool.wire_multisegmentlcd.name", "Multi-segment LCD Tool (Wire)" ) language.Add( "tool.wire_multisegmentlcd.desc", "Spawns a Multi-segment LCD, which can be used to display numbers and miscellaneous graphics" ) language.Add( "tool.wire_multisegmentlcd.interactive", "Interactive (if available):" ) + language.Add( "tool.wire_multisegmentlcd.resw", "Canvas Width:" ) + language.Add( "tool.wire_multisegmentlcd.resh", "Canvas Height:" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") @@ -41,7 +43,7 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() - return self:GetClientNumber("interactive") + return self:GetClientNumber("interactive"), math.max(0,math.min(1024,self:GetClientNumber("resw"))), math.max(0,math.min(1024,self:GetClientNumber("resh"))) end util.AddNetworkString("wire_multisegmentlcd_tool_upload_request") @@ -52,6 +54,7 @@ if SERVER then net.Start("wire_multisegmentlcd_tool_upload_request") net.WriteUInt(trace.Entity:EntIndex(),16) net.Send(self:GetOwner()) + trace.Entity:Setup(self:GetConVars()) end net.Receive("wire_multisegmentlcd_tool_upload", function(len, ply) @@ -74,7 +77,8 @@ TOOL.ClientConVar = { model = "models/props_lab/monitor01b.mdl", createflat = 0, interactive = 1, - + resw = 1024, + resh = 1024, } @@ -114,6 +118,8 @@ function TOOL.BuildCPanel(panel) WireDermaExts.ModelSelect(panel, "wire_multisegmentlcd_model", list.Get( "WireScreenModels" ), 5) panel:CheckBox("#tool.wire_multisegmentlcd.interactive", "wire_multisegmentlcd_interactive") panel:CheckBox("#Create Flat to Surface", "wire_multisegmentlcd_createflat") + panel:TextEntry("#tool.wire_multisegmentlcd.resw", "wire_multisegmentlcd_resw") + panel:TextEntry("#tool.wire_multisegmentlcd.resh", "wire_multisegmentlcd_resh") TreeDataHolder = vgui.Create("DPanel", panel) panel:AddPanel(TreeDataHolder) TreeDataHolder:DockMargin(0, 0, 0, 0) From 7860fd598187852c54249aec58f3ce3b07c11cbe Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 18:16:03 +0200 Subject: [PATCH 14/50] Do fixes --- .../gmod_wire_multisegmentlcd/cl_init.lua | 17 ++++- .../gmod_wire_multisegmentlcd/init.lua | 7 +- .../gmod_wire_multisegmentlcd/shared.lua | 4 +- lua/wire/stools/multisegmentlcd.lua | 69 +++++++++++++++++-- 4 files changed, 88 insertions(+), 9 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index a6b3523a6e..26341c1fd0 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -70,6 +70,16 @@ function ENT:DrawSegment(segment) self.BitIndex = self.BitIndex+1 end +function ENT:DrawText(text) + if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then + surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) + surface.SetFont("Default") + surface.SetTextColor(255,255,255,255) + surface.DrawText(text.Text) + end + self.BitIndex = self.BitIndex+1 +end + function ENT:DrawUnion(group) self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) @@ -82,6 +92,8 @@ function ENT:DrawUnion(group) self:DrawUnion(v) elseif v.Type == SEGMENT then self:DrawSegment(v) + elseif v.Type == TEXT then + self:DrawText(v) end biggestindex = math.max(biggestindex,self.BitIndex) self.BitIndex = savedindex @@ -101,6 +113,8 @@ function ENT:DrawGroup(group) self:DrawUnion(v) elseif v.Type == SEGMENT then self:DrawSegment(v) + elseif v.Type == TEXT then + self:DrawText(v) end end self.LocalX = self.LocalX - (group.X or 0) @@ -130,7 +144,8 @@ end function ENT:Receive() local ent = net.ReadEntity() - self.Tree = net.ReadTable() + local sz = net.ReadUInt(16) + self.Tree = WireLib.von.deserialize(net.ReadData(sz)) self.ResolutionW = net.ReadUInt(16) self.ResolutionH = net.ReadUInt(16) end diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index 34ac19d3cf..737f4669ed 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -44,9 +44,14 @@ end function ENT:SendSerializedTree(ply) if self.Tree == nil then return end + local serialized = WireLib.von.serialize(self.Tree) + if #serialized > 65535 then + return + end WireLib.netStart(self) net.WriteEntity(self) - net.WriteTable(self.Tree) + net.WriteUInt(#serialized,16) + net.WriteData(serialized) net.WriteUInt(self.ResolutionW,16) net.WriteUInt(self.ResolutionH,16) WireLib.netEnd(ply) diff --git a/lua/entities/gmod_wire_multisegmentlcd/shared.lua b/lua/entities/gmod_wire_multisegmentlcd/shared.lua index c2a1f25af3..e20f1983b7 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/shared.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/shared.lua @@ -13,4 +13,6 @@ ENT.RenderGroup = RENDERGROUP_BOTH GROUP = -1 UNION = 0 -SEGMENT = 1 \ No newline at end of file +SEGMENT = 1 +TEXT = 2 +MATRIX = 3 \ No newline at end of file diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index b9fff3e6d3..c541f292bb 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -4,6 +4,8 @@ WireToolSetup.open( "multisegmentlcd", "Multi-segment LCD", "gmod_wire_multisegm GROUP = -1 UNION = 0 SEGMENT = 1 +TEXT = 2 +MATRIX = 3 WireLib.SegmentLCD_Tree = { Type=GROUP, @@ -30,9 +32,14 @@ if CLIENT then net.Receive("wire_multisegmentlcd_tool_upload_request", function(len, ply) local ent = net.ReadUInt(16) + local serialized = WireLib.von.serialize(WireLib.SegmentLCD_Tree) + if #serialized > 65535 then + return + end net.Start("wire_multisegmentlcd_tool_upload") net.WriteUInt(ent,16) - net.WriteTable(WireLib.SegmentLCD_Tree) + net.WriteUInt(#serialized,16) + net.WriteData(serialized) net.SendToServer() --ent.Tree = table.Copy(WireLib.SegmentLCD_Tree) end) @@ -43,7 +50,7 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() - return self:GetClientNumber("interactive"), math.max(0,math.min(1024,self:GetClientNumber("resw"))), math.max(0,math.min(1024,self:GetClientNumber("resh"))) + return self:GetClientNumber("interactive"), math.Clamp(self:GetClientNumber("resw"),0,1024), math.Clamp(self:GetClientNumber("resh"),0,1024) end util.AddNetworkString("wire_multisegmentlcd_tool_upload_request") @@ -59,7 +66,8 @@ if SERVER then net.Receive("wire_multisegmentlcd_tool_upload", function(len, ply) local ent = ents.GetByIndex(net.ReadUInt(16)) - ent.Tree = net.ReadTable() + local sz = net.ReadUInt(16) + ent.Tree = WireLib.von.deserialize(net.ReadData(sz)) ent:Retransmit() end) @@ -90,6 +98,10 @@ function BuildNode(v,node,group) elseif v.Type == UNION then new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) BuildNodes(new,v) + elseif v.Type == TEXT then + new = node:AddNode( "Text", "icon16/bullet_yellow.png" ) + elseif v.Type == MATRIX then + new = node:AddNode( "Matrix", "icon16/bullet_red.png" ) else new = node:AddNode( "Segment", "icon16/bullet_green.png" ) end @@ -163,9 +175,35 @@ function TOOL.BuildCPanel(panel) new.parentgroup = group end + AddText = ButtonsHolder:Add( "DButton" ) + AddText:SetText( "Add Text" ) + ButtonsHolder.buttons[2] = AddText + function AddText:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=TEXT, X=30, Y=0, Text="Hello"} + children[#children+1] = newgroup + local new = node:AddNode( "Text", "icon16/bullet_yellow.png" ) + new.group = newgroup + new.parentgroup = group + end + AddGroup = ButtonsHolder:Add( "DButton" ) AddGroup:SetText( "Add Group" ) - ButtonsHolder.buttons[2] = AddGroup + ButtonsHolder.buttons[3] = AddGroup function AddGroup:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -190,7 +228,7 @@ function TOOL.BuildCPanel(panel) AddUnion = ButtonsHolder:Add( "DButton" ) AddUnion:SetText( "Add Union" ) - ButtonsHolder.buttons[3] = AddUnion + ButtonsHolder.buttons[4] = AddUnion function AddUnion:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -215,7 +253,7 @@ function TOOL.BuildCPanel(panel) Remove = ButtonsHolder:Add( "DButton" ) Remove:SetText( "Remove" ) - ButtonsHolder.buttons[4] = Remove + ButtonsHolder.buttons[5] = Remove function Remove:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -270,6 +308,17 @@ function TOOL.BuildCPanel(panel) node.group.H = value end ButtonsHolder.textboxes[4] = WangH + + TextSetter = ButtonsHolder:Add( "DTextEntry" ) + function TextSetter:OnValueChange(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group.Type ~= TEXT then + return + end + node.group.Text = value + end + ButtonsHolder.textboxes[5] = TextSetter + function ButtonsHolder:PerformLayout(w, h) for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) @@ -290,11 +339,19 @@ function TOOL.BuildCPanel(panel) WangY:SetValue(group.Y) WangW:SetValue(group.W) WangH:SetValue(group.H) + TextSetter:SetValue("") + elseif group.Type == TEXT then + WangX:SetValue(group.X) + WangY:SetValue(group.Y) + WangW:SetValue(0) + WangH:SetValue(0) + TextSetter:SetValue(group.Text) else WangX:SetValue(group.X) WangY:SetValue(group.Y) WangW:SetValue(0) WangH:SetValue(0) + TextSetter:SetValue("") end return true end From c62513012f7761f8fa8a5697418b6863b969e7cb Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 18:53:24 +0200 Subject: [PATCH 15/50] Matrix segment --- .../gmod_wire_multisegmentlcd/cl_init.lua | 18 +++ lua/wire/stools/multisegmentlcd.lua | 148 +++++++++++++++--- 2 files changed, 146 insertions(+), 20 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 26341c1fd0..aeffeb5b8a 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -80,6 +80,20 @@ function ENT:DrawText(text) self.BitIndex = self.BitIndex+1 end +function ENT:DrawMatrix(matrix) + for y = 0,matrix.H do + for x = 0,matrix.W do + if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then + surface.DrawRect(matrix.X+self.LocalX+x*matrix.OffsetX,matrix.Y+self.LocalY+y*matrix.OffsetY,matrix.ScaleW,matrix.ScaleH) + end + self.BitIndex = self.BitIndex+1 + end + end + + +end + + function ENT:DrawUnion(group) self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) @@ -94,6 +108,8 @@ function ENT:DrawUnion(group) self:DrawSegment(v) elseif v.Type == TEXT then self:DrawText(v) + elseif v.Type == MATRIX then + self:DrawMatrix(v) end biggestindex = math.max(biggestindex,self.BitIndex) self.BitIndex = savedindex @@ -115,6 +131,8 @@ function ENT:DrawGroup(group) self:DrawSegment(v) elseif v.Type == TEXT then self:DrawText(v) + elseif v.Type == MATRIX then + self:DrawMatrix(v) end end self.LocalX = self.LocalX - (group.X or 0) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index c541f292bb..7a75d5ae00 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -147,7 +147,7 @@ function TOOL.BuildCPanel(panel) ButtonsHolder:Dock(TOP) ButtonsHolder:DockMargin(0, 0, 0, 0) ButtonsHolder.buttons = {} - ButtonsHolder:SetHeight(48) + ButtonsHolder:SetHeight(60) AddSegment = ButtonsHolder:Add( "DButton" ) AddSegment:SetText( "Add Segment" ) @@ -201,9 +201,35 @@ function TOOL.BuildCPanel(panel) new.parentgroup = group end + AddMatrix = ButtonsHolder:Add( "DButton" ) + AddMatrix:SetText( "Add Matrix" ) + ButtonsHolder.buttons[3] = AddMatrix + function AddMatrix:DoClick() + local node = DisplayData:GetSelectedItem() + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=MATRIX, X=0, Y=0, W=6, H=8, ScaleW=5, ScaleH=5, OffsetX=6, OffsetY=6} + children[#children+1] = newgroup + local new = node:AddNode( "Matrix", "icon16/bullet_red.png" ) + new.group = newgroup + new.parentgroup = group + end + AddGroup = ButtonsHolder:Add( "DButton" ) AddGroup:SetText( "Add Group" ) - ButtonsHolder.buttons[3] = AddGroup + ButtonsHolder.buttons[4] = AddGroup function AddGroup:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -228,7 +254,7 @@ function TOOL.BuildCPanel(panel) AddUnion = ButtonsHolder:Add( "DButton" ) AddUnion:SetText( "Add Union" ) - ButtonsHolder.buttons[4] = AddUnion + ButtonsHolder.buttons[5] = AddUnion function AddUnion:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -253,7 +279,7 @@ function TOOL.BuildCPanel(panel) Remove = ButtonsHolder:Add( "DButton" ) Remove:SetText( "Remove" ) - ButtonsHolder.buttons[5] = Remove + ButtonsHolder.buttons[6] = Remove function Remove:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -275,7 +301,7 @@ function TOOL.BuildCPanel(panel) WangX = ButtonsHolder:Add( "DNumberWang" ) function WangX:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil then + if node == nil or node.group == nil then return end node.group.X = value @@ -284,7 +310,7 @@ function TOOL.BuildCPanel(panel) WangY = ButtonsHolder:Add( "DNumberWang" ) function WangY:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil then + if node == nil or node.group == nil then return end node.group.Y = value @@ -293,7 +319,7 @@ function TOOL.BuildCPanel(panel) WangW = ButtonsHolder:Add( "DNumberWang" ) function WangW:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group.Type ~= SEGMENT then + if node == nil or node.group == nil or node.group.Type ~= SEGMENT then return end node.group.W = value @@ -302,7 +328,7 @@ function TOOL.BuildCPanel(panel) WangH = ButtonsHolder:Add( "DNumberWang" ) function WangH:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group.Type ~= SEGMENT then + if node == nil or node.group == nil or node.group.Type ~= SEGMENT then return end node.group.H = value @@ -312,24 +338,75 @@ function TOOL.BuildCPanel(panel) TextSetter = ButtonsHolder:Add( "DTextEntry" ) function TextSetter:OnValueChange(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group.Type ~= TEXT then + if node == nil or node.group == nil or node.group.Type ~= TEXT then return end node.group.Text = value end - ButtonsHolder.textboxes[5] = TextSetter + + WangScaleW = ButtonsHolder:Add( "DNumberWang" ) + function WangScaleW:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil or node.group.Type ~= MATRIX then + return + end + node.group.ScaleW = value + end + + WangScaleH = ButtonsHolder:Add( "DNumberWang" ) + function WangScaleH:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil or node.group.Type ~= MATRIX then + return + end + node.group.ScaleH = value + end + + WangOffsetX = ButtonsHolder:Add( "DNumberWang" ) + function WangOffsetX:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil or node.group.Type ~= MATRIX then + return + end + node.group.OffsetX = value + end + + WangOffsetY = ButtonsHolder:Add( "DNumberWang" ) + function WangOffsetY:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil or node.group.Type ~= MATRIX then + return + end + node.group.OffsetY = value + end function ButtonsHolder:PerformLayout(w, h) for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) - v:SetSize(w/#self.buttons,h/2) + v:SetSize(w/#self.buttons,h/3) end for i,v in ipairs(self.textboxes) do - v:SetPos((i-1)*w/#self.textboxes,h/2) - v:SetSize(w/#self.textboxes,h/2) + v:SetPos((i-1)*w/#self.textboxes,h/3) + v:SetSize(w/#self.textboxes,h/3) end + TextSetter:SetPos(0,h/3*2) + TextSetter:SetSize(w,h/3) + WangScaleW:SetPos(0,h/3*2) + WangScaleW:SetSize(w/4,h/3) + WangScaleH:SetPos(w/4,h/3*2) + WangScaleH:SetSize(w/4,h/3) + WangOffsetX:SetPos(w/4*2,h/3*2) + WangOffsetX:SetSize(w/4,h/3) + WangOffsetY:SetPos(w/4*3,h/3*2) + WangOffsetY:SetSize(w/4,h/3) end - + WangW:SetVisible(false) + WangH:SetVisible(false) + TextSetter:SetVisible(false) + WangScaleW:SetVisible(false) + WangScaleH:SetVisible(false) + WangOffsetX:SetVisible(false) + WangOffsetY:SetVisible(false) function DisplayData:DoClick(node) @@ -339,19 +416,50 @@ function TOOL.BuildCPanel(panel) WangY:SetValue(group.Y) WangW:SetValue(group.W) WangH:SetValue(group.H) - TextSetter:SetValue("") + WangW:SetVisible(true) + WangH:SetVisible(true) + TextSetter:SetVisible(false) + WangScaleW:SetVisible(false) + WangScaleH:SetVisible(false) + WangOffsetX:SetVisible(false) + WangOffsetY:SetVisible(false) + elseif group.Type == MATRIX then + WangX:SetValue(group.X) + WangY:SetValue(group.Y) + WangW:SetValue(group.W) + WangH:SetValue(group.H) + TextSetter:SetVisible(false) + WangScaleW:SetVisible(true) + WangScaleH:SetVisible(true) + WangOffsetX:SetVisible(true) + WangOffsetY:SetVisible(true) + WangW:SetVisible(true) + WangH:SetVisible(true) + WangScaleW:SetValue(group.ScaleW) + WangScaleH:SetValue(group.ScaleH) + WangOffsetX:SetValue(group.OffsetX) + WangOffsetY:SetValue(group.OffsetY) elseif group.Type == TEXT then WangX:SetValue(group.X) WangY:SetValue(group.Y) - WangW:SetValue(0) - WangH:SetValue(0) + WangW:SetVisible(false) + WangH:SetVisible(false) + TextSetter:SetVisible(true) TextSetter:SetValue(group.Text) + WangScaleW:SetVisible(false) + WangScaleH:SetVisible(false) + WangOffsetX:SetVisible(false) + WangOffsetY:SetVisible(false) else WangX:SetValue(group.X) WangY:SetValue(group.Y) - WangW:SetValue(0) - WangH:SetValue(0) - TextSetter:SetValue("") + WangW:SetVisible(false) + WangH:SetVisible(false) + WangScaleW:SetVisible(false) + WangScaleH:SetVisible(false) + WangOffsetX:SetVisible(false) + WangOffsetY:SetVisible(false) + TextSetter:SetVisible(false) end return true end From a26887d7cef175e888fe84edb45ec44fb7c06723 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 19:20:56 +0200 Subject: [PATCH 16/50] It is amazing --- lua/wire/stools/multisegmentlcd.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 7a75d5ae00..346ef5e194 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -319,7 +319,7 @@ function TOOL.BuildCPanel(panel) WangW = ButtonsHolder:Add( "DNumberWang" ) function WangW:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= SEGMENT then + if node == nil or node.group == nil then return end node.group.W = value @@ -328,7 +328,7 @@ function TOOL.BuildCPanel(panel) WangH = ButtonsHolder:Add( "DNumberWang" ) function WangH:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= SEGMENT then + if node == nil or node.group == nil then return end node.group.H = value From 6fdf855ef3ea3876f7182b48d7d13585e35fcb16 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 20:03:06 +0200 Subject: [PATCH 17/50] colors --- .../gmod_wire_multisegmentlcd/cl_init.lua | 19 ++++++++++++++++--- .../gmod_wire_multisegmentlcd/init.lua | 14 +++++++++++++- lua/wire/stools/multisegmentlcd.lua | 18 +++++++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index aeffeb5b8a..0dfc176335 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -16,6 +16,13 @@ function ENT:Initialize() end end + self.Fgblue = 45 + self.Fggreen = 91 + self.Fgred = 45 + self.Bgblue = 15 + self.Bggreen = 178 + self.Bgred = 148 + self.GPU = WireGPU(self) self.ResolutionW = 1024 self.ResolutionH = 1024 @@ -74,7 +81,7 @@ function ENT:DrawText(text) if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) surface.SetFont("Default") - surface.SetTextColor(255,255,255,255) + surface.SetTextColor(self.Fgred,self.Fggreen,self.Fgblue,255) surface.DrawText(text.Text) end self.BitIndex = self.BitIndex+1 @@ -142,10 +149,10 @@ end function ENT:Draw() self:DrawModel() self.GPU:RenderToGPU(function() - surface.SetDrawColor(0,0,0,255) + surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) surface.DrawRect(0,0,1024,1024) if self.Tree then - surface.SetDrawColor(255,255,255,255) + surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) self.BitIndex = 0 self.LocalX = 0 self.LocalY = 0 @@ -166,4 +173,10 @@ function ENT:Receive() self.Tree = WireLib.von.deserialize(net.ReadData(sz)) self.ResolutionW = net.ReadUInt(16) self.ResolutionH = net.ReadUInt(16) + self.Fgblue = net.ReadUInt(8) + self.Fggreen = net.ReadUInt(8) + self.Fgred = net.ReadUInt(8) + self.Bgblue = net.ReadUInt(8) + self.Bggreen = net.ReadUInt(8) + self.Bgred = net.ReadUInt(8) end diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index 737f4669ed..9c5bb9630d 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -54,6 +54,12 @@ function ENT:SendSerializedTree(ply) net.WriteData(serialized) net.WriteUInt(self.ResolutionW,16) net.WriteUInt(self.ResolutionH,16) + net.WriteUInt(self.Fgblue,8) + net.WriteUInt(self.Fggreen,8) + net.WriteUInt(self.Fgred,8) + net.WriteUInt(self.Bgblue,8) + net.WriteUInt(self.Bggreen,8) + net.WriteUInt(self.Bgred,8) WireLib.netEnd(ply) end @@ -67,10 +73,16 @@ function ENT:Retransmit(ply) self.Cache:Flush(ply) end -function ENT:Setup(IsInteractive, ResolutionW, ResolutionH) +function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Fgred,Fggreen,Fgblue) self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) self.ResolutionW = ResolutionW self.ResolutionH = ResolutionH + self.Fgblue = Fgblue or 45 + self.Fggreen = Fggreen or 91 + self.Fgred = Fgred or 45 + self.Bgblue = Bgblue or 15 + self.Bggreen = Bggreen or 178 + self.Bgred = Bgred or 148 self:Retransmit() end diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 346ef5e194..ed95e17aee 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -50,7 +50,13 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() - return self:GetClientNumber("interactive"), math.Clamp(self:GetClientNumber("resw"),0,1024), math.Clamp(self:GetClientNumber("resh"),0,1024) + return self:GetClientNumber("interactive"), math.Clamp(self:GetClientNumber("resw"),0,1024), math.Clamp(self:GetClientNumber("resh"),0,1024), + math.Clamp(self:GetClientNumber("bgred"), 0, 255), + math.Clamp(self:GetClientNumber("bggreen"), 0, 255), + math.Clamp(self:GetClientNumber("bgblue"), 0, 255), + math.Clamp(self:GetClientNumber("fgred"), 0, 255), + math.Clamp(self:GetClientNumber("fggreen"), 0, 255), + math.Clamp(self:GetClientNumber("fgblue"), 0, 255) end util.AddNetworkString("wire_multisegmentlcd_tool_upload_request") @@ -85,8 +91,14 @@ TOOL.ClientConVar = { model = "models/props_lab/monitor01b.mdl", createflat = 0, interactive = 1, - resw = 1024, - resh = 1024, + resw = 1024, + resh = 1024, + bgred = 148, + bggreen = 178, + bgblue = 15, + fgred = 45, + fggreen = 91, + fgblue = 45 } From 0f842715408c6d692345fdc6d6be0832ca806e66 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 6 Sep 2025 22:05:47 +0200 Subject: [PATCH 18/50] Fixes --- lua/wire/stools/multisegmentlcd.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index ed95e17aee..441ad24f12 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -311,6 +311,8 @@ function TOOL.BuildCPanel(panel) end ButtonsHolder.textboxes = {} WangX = ButtonsHolder:Add( "DNumberWang" ) + WangX:SetMin(-1024) + WangX:SetMax(1024) function WangX:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil then @@ -320,6 +322,8 @@ function TOOL.BuildCPanel(panel) end ButtonsHolder.textboxes[1] = WangX WangY = ButtonsHolder:Add( "DNumberWang" ) + WangY:SetMin(-1024) + WangY:SetMax(1024) function WangY:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil then @@ -329,6 +333,7 @@ function TOOL.BuildCPanel(panel) end ButtonsHolder.textboxes[2] = WangY WangW = ButtonsHolder:Add( "DNumberWang" ) + WangW:SetMax(1024) function WangW:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil then @@ -338,6 +343,7 @@ function TOOL.BuildCPanel(panel) end ButtonsHolder.textboxes[3] = WangW WangH = ButtonsHolder:Add( "DNumberWang" ) + WangH:SetMax(1024) function WangH:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil then @@ -357,6 +363,7 @@ function TOOL.BuildCPanel(panel) end WangScaleW = ButtonsHolder:Add( "DNumberWang" ) + WangScaleW:SetMax(1024) function WangScaleW:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil or node.group.Type ~= MATRIX then @@ -366,6 +373,7 @@ function TOOL.BuildCPanel(panel) end WangScaleH = ButtonsHolder:Add( "DNumberWang" ) + WangScaleH:SetMax(1024) function WangScaleH:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil or node.group.Type ~= MATRIX then @@ -375,6 +383,7 @@ function TOOL.BuildCPanel(panel) end WangOffsetX = ButtonsHolder:Add( "DNumberWang" ) + WangOffsetX:SetMax(1024) function WangOffsetX:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil or node.group.Type ~= MATRIX then @@ -384,6 +393,7 @@ function TOOL.BuildCPanel(panel) end WangOffsetY = ButtonsHolder:Add( "DNumberWang" ) + WangOffsetY:SetMax(1024) function WangOffsetY:OnValueChanged(value) local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil or node.group.Type ~= MATRIX then From 2e0a7ce6c2d096a9fe0bdf1e9c2e14a7e4a408aa Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 7 Sep 2025 00:56:30 +0200 Subject: [PATCH 19/50] Fix --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 0dfc176335..bd967e2007 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -88,8 +88,8 @@ function ENT:DrawText(text) end function ENT:DrawMatrix(matrix) - for y = 0,matrix.H do - for x = 0,matrix.W do + for y = 0,matrix.H-1 do + for x = 0,matrix.W-1 do if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then surface.DrawRect(matrix.X+self.LocalX+x*matrix.OffsetX,matrix.Y+self.LocalY+y*matrix.OffsetY,matrix.ScaleW,matrix.ScaleH) end From bc419b1ad8753de06cb0d7cc81069a98bbbe5c88 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 7 Sep 2025 01:00:58 +0200 Subject: [PATCH 20/50] add the missing color pickers --- lua/wire/stools/multisegmentlcd.lua | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 441ad24f12..7da28f18ea 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -22,8 +22,10 @@ if CLIENT then language.Add( "tool.wire_multisegmentlcd.name", "Multi-segment LCD Tool (Wire)" ) language.Add( "tool.wire_multisegmentlcd.desc", "Spawns a Multi-segment LCD, which can be used to display numbers and miscellaneous graphics" ) language.Add( "tool.wire_multisegmentlcd.interactive", "Interactive (if available):" ) - language.Add( "tool.wire_multisegmentlcd.resw", "Canvas Width:" ) - language.Add( "tool.wire_multisegmentlcd.resh", "Canvas Height:" ) + language.Add( "tool.wire_multisegmentlcd.resw", "Canvas width:" ) + language.Add( "tool.wire_multisegmentlcd.resh", "Canvas height:" ) + language.Add( "tool.wire_multisegmentlcd.fgcolor", "Segment color:" ) + language.Add( "tool.wire_multisegmentlcd.bgcolor", "Background color:" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } WireToolSetup.setToolMenuIcon("icon16/application_xp_terminal.png") @@ -571,4 +573,25 @@ function TOOL.BuildCPanel(panel) end end) end + + panel:AddControl("Color", { + Label = "#tool.wire_multisegmentlcd.bgcolor", + Red = "wire_multisegmentlcd_bgred", + Green = "wire_multisegmentlcd_bggreen", + Blue = "wire_multisegmentlcd_bgblue", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + panel:AddControl("Color", { + Label = "#tool.wire_multisegmentlcd.fgcolor", + Red = "wire_multisegmentlcd_fgred", + Green = "wire_multisegmentlcd_fggreen", + Blue = "wire_multisegmentlcd_fgblue", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) end From 7d8770ac6f3aa3d2dd36e542bde5ad181e6cffcd Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 7 Sep 2025 11:30:29 +0200 Subject: [PATCH 21/50] make it dupeable --- .../gmod_wire_multisegmentlcd/init.lua | 18 ++++++++++++++++-- .../gmod_wire_multisegmentlcd/shared.lua | 1 + lua/wire/stools/multisegmentlcd.lua | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index 9c5bb9630d..fde06666d9 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -2,6 +2,7 @@ AddCSLuaFile("cl_init.lua") AddCSLuaFile("shared.lua") include("shared.lua") +DEFINE_BASECLASS( "base_wire_entity" ) ENT.WireDebugName = "MultiSegmentLcdScreen" @@ -74,7 +75,7 @@ function ENT:Retransmit(ply) end function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Fgred,Fggreen,Fgblue) - self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive == 1) + self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive) self.ResolutionW = ResolutionW self.ResolutionH = ResolutionH self.Fgblue = Fgblue or 45 @@ -168,4 +169,17 @@ function ENT:Unprompt() self.User = nil end -duplicator.RegisterEntityClass("gmod_wire_multisegmentlcd", WireLib.MakeWireEnt, "Data", "IsInteractive") +function ENT:BuildDupeInfo() + local info = BaseClass.BuildDupeInfo( self ) or {} + info.Tree = self.Tree + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.Tree = info.Tree + ent.Tree = info.Tree + BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + ent:Retransmit() +end + +duplicator.RegisterEntityClass("gmod_wire_multisegmentlcd", WireLib.MakeWireEnt, "Data", "IsInteractive", "ResolutionW", "ResolutionH", "Bgred", "Bggreen", "Bgblue", "Fgred", "Fggreen", "Fgblue") diff --git a/lua/entities/gmod_wire_multisegmentlcd/shared.lua b/lua/entities/gmod_wire_multisegmentlcd/shared.lua index e20f1983b7..735b21d490 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/shared.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/shared.lua @@ -1,6 +1,7 @@ ENT.Type = "anim" ENT.Base = "base_wire_entity" + ENT.PrintName = "Wire Multi-segment LCD" ENT.Author = "" ENT.Contact = "" diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 7da28f18ea..c12676cf5a 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -52,7 +52,7 @@ WireToolSetup.SetupMax( 20 ) if SERVER then function TOOL:GetConVars() - return self:GetClientNumber("interactive"), math.Clamp(self:GetClientNumber("resw"),0,1024), math.Clamp(self:GetClientNumber("resh"),0,1024), + return self:GetClientNumber("interactive") == 1, math.Clamp(self:GetClientNumber("resw"),0,1024), math.Clamp(self:GetClientNumber("resh"),0,1024), math.Clamp(self:GetClientNumber("bgred"), 0, 255), math.Clamp(self:GetClientNumber("bggreen"), 0, 255), math.Clamp(self:GetClientNumber("bgblue"), 0, 255), From 1f495c919f7c1b286dae0cd0866d1f56295a8ec0 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 08:42:46 +0200 Subject: [PATCH 22/50] Colors --- .../gmod_wire_multisegmentlcd/cl_init.lua | 31 ++++- lua/wire/stools/multisegmentlcd.lua | 117 +++++++++++++----- 2 files changed, 115 insertions(+), 33 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index bd967e2007..70ab720ef0 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -81,7 +81,7 @@ function ENT:DrawText(text) if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) surface.SetFont("Default") - surface.SetTextColor(self.Fgred,self.Fggreen,self.Fgblue,255) + surface.SetTextColor(self.Cr,self.Cg,self.Cb,255) surface.DrawText(text.Text) end self.BitIndex = self.BitIndex+1 @@ -102,6 +102,15 @@ end function ENT:DrawUnion(group) + local oCr = self.Cr + local oCg = self.Cg + local oCb = self.Cb + if group.HasColor then + self.Cr = group.R + self.Cg = group.G + self.Cb = group.B + surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + end self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) local savedindex = self.BitIndex @@ -124,9 +133,22 @@ function ENT:DrawUnion(group) self.BitIndex = biggestindex self.LocalX = self.LocalX - (group.X or 0) self.LocalY = self.LocalY - (group.Y or 0) + self.Cr = oCr + self.Cg = oCg + self.Cb = oCb + surface.SetDrawColor(self.Cr,self.Cg,self.Cb) end function ENT:DrawGroup(group) + local oCr = self.Cr + local oCg = self.Cg + local oCb = self.Cb + if group.HasColor then + self.Cr = group.R + self.Cg = group.G + self.Cb = group.B + surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + end self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) for k,v in ipairs(group.Children) do @@ -144,6 +166,10 @@ function ENT:DrawGroup(group) end self.LocalX = self.LocalX - (group.X or 0) self.LocalY = self.LocalY - (group.Y or 0) + self.Cr = oCr + self.Cg = oCg + self.Cb = oCb + surface.SetDrawColor(self.Cr,self.Cg,self.Cb) end function ENT:Draw() @@ -153,6 +179,9 @@ function ENT:Draw() surface.DrawRect(0,0,1024,1024) if self.Tree then surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) + self.Cr = self.Fgred + self.Cg = self.Fggreen + self.Cb = self.Fgblue self.BitIndex = 0 self.LocalX = 0 self.LocalY = 0 diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index c12676cf5a..b35e2352ba 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -153,6 +153,7 @@ function TOOL.BuildCPanel(panel) TreeDataHolder:SetHeight(480) DisplayData = vgui.Create("DTree", TreeDataHolder) DisplayData:Dock(FILL) + DisplayData:SetClickOnDragHover( true ) DisplayData:DockMargin(0, 0, 0, 0) --DisplayData.RootNode:AddNode( "Root", "icon16/monitor.png" ) DisplayData.RootNode.group = WireLib.SegmentLCD_Tree @@ -259,7 +260,7 @@ function TOOL.BuildCPanel(panel) children = WireLib.SegmentLCD_Tree.Children group = WireLib.SegmentLCD_Tree end - local newgroup = {Type=GROUP,Children={},X=0,Y=0} + local newgroup = {Type=GROUP,Children={},X=0,Y=0,HasColor=false,R=255,G=255,B=255} children[#children+1] = newgroup local new = node:AddNode( "Group", "icon16/text_list_numbers.png" ) new.group = newgroup @@ -284,7 +285,7 @@ function TOOL.BuildCPanel(panel) children = WireLib.SegmentLCD_Tree.Children group = WireLib.SegmentLCD_Tree end - local newgroup = {Type=UNION,Children={},X=0,Y=0} + local newgroup = {Type=UNION,Children={},X=0,Y=0,HasColor=false,R=255,G=255,B=255} children[#children+1] = newgroup local new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) new.group = newgroup @@ -404,6 +405,48 @@ function TOOL.BuildCPanel(panel) node.group.OffsetY = value end + CheckHasColor = ButtonsHolder:Add( "DCheckBox" ) + function CheckHasColor:OnChange(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.HasColor = value + end + CheckLabel = ButtonsHolder:Add( "DLabel" ) + CheckLabel:SetText("Has color") + CheckLabel:SetTextColor(Color(0,0,0,255)) + + WangColorR = ButtonsHolder:Add( "DNumberWang" ) + WangColorR:SetMax(255) + function WangColorR:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.R = value + end + + WangColorG = ButtonsHolder:Add( "DNumberWang" ) + WangColorG:SetMax(255) + function WangColorG:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.G = value + end + + WangColorB = ButtonsHolder:Add( "DNumberWang" ) + WangColorB:SetMax(255) + function WangColorB:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.B = value + end + function ButtonsHolder:PerformLayout(w, h) for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) @@ -423,6 +466,16 @@ function TOOL.BuildCPanel(panel) WangOffsetX:SetSize(w/4,h/3) WangOffsetY:SetPos(w/4*3,h/3*2) WangOffsetY:SetSize(w/4,h/3) + + CheckHasColor:SetPos(1,h/3*2+3) + CheckLabel:SetPos(18,h/3*2) + CheckLabel:SetSize(w/4-18,h/3) + WangColorR:SetPos(w/4,h/3*2) + WangColorR:SetSize(w/4,h/3) + WangColorG:SetPos(w/4*2,h/3*2) + WangColorG:SetSize(w/4,h/3) + WangColorB:SetPos(w/4*3,h/3*2) + WangColorB:SetSize(w/4,h/3) end WangW:SetVisible(false) WangH:SetVisible(false) @@ -431,28 +484,34 @@ function TOOL.BuildCPanel(panel) WangScaleH:SetVisible(false) WangOffsetX:SetVisible(false) WangOffsetY:SetVisible(false) - + WangColorR:SetVisible(false) + WangColorG:SetVisible(false) + WangColorB:SetVisible(false) + CheckHasColor:SetVisible(false) + CheckLabel:SetVisible(false) function DisplayData:DoClick(node) group = node.group + TextSetter:SetVisible(false) + WangScaleW:SetVisible(false) + WangScaleH:SetVisible(false) + WangOffsetX:SetVisible(false) + WangOffsetY:SetVisible(false) + WangW:SetVisible(false) + WangH:SetVisible(false) + WangColorR:SetVisible(false) + WangColorG:SetVisible(false) + WangColorB:SetVisible(false) + CheckHasColor:SetVisible(false) + CheckLabel:SetVisible(false) if group.Type == SEGMENT then - WangX:SetValue(group.X) - WangY:SetValue(group.Y) WangW:SetValue(group.W) WangH:SetValue(group.H) WangW:SetVisible(true) WangH:SetVisible(true) - TextSetter:SetVisible(false) - WangScaleW:SetVisible(false) - WangScaleH:SetVisible(false) - WangOffsetX:SetVisible(false) - WangOffsetY:SetVisible(false) elseif group.Type == MATRIX then - WangX:SetValue(group.X) - WangY:SetValue(group.Y) WangW:SetValue(group.W) WangH:SetValue(group.H) - TextSetter:SetVisible(false) WangScaleW:SetVisible(true) WangScaleH:SetVisible(true) WangOffsetX:SetVisible(true) @@ -464,27 +523,21 @@ function TOOL.BuildCPanel(panel) WangOffsetX:SetValue(group.OffsetX) WangOffsetY:SetValue(group.OffsetY) elseif group.Type == TEXT then - WangX:SetValue(group.X) - WangY:SetValue(group.Y) - WangW:SetVisible(false) - WangH:SetVisible(false) TextSetter:SetVisible(true) TextSetter:SetValue(group.Text) - WangScaleW:SetVisible(false) - WangScaleH:SetVisible(false) - WangOffsetX:SetVisible(false) - WangOffsetY:SetVisible(false) - else - WangX:SetValue(group.X) - WangY:SetValue(group.Y) - WangW:SetVisible(false) - WangH:SetVisible(false) - WangScaleW:SetVisible(false) - WangScaleH:SetVisible(false) - WangOffsetX:SetVisible(false) - WangOffsetY:SetVisible(false) - TextSetter:SetVisible(false) - end + elseif group.Type == GROUP or group.Type == UNION then + WangColorR:SetVisible(true) + WangColorG:SetVisible(true) + WangColorB:SetVisible(true) + CheckHasColor:SetVisible(true) + CheckLabel:SetVisible(true) + WangColorR:SetValue(group.R) + WangColorG:SetValue(group.G) + WangColorB:SetValue(group.B) + CheckHasColor:SetValue(group.HasColor) + end + WangX:SetValue(group.X) + WangY:SetValue(group.Y) return true end From 443738c3865af889a15210b2d28c86cfa6c43b52 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 10:21:12 +0200 Subject: [PATCH 23/50] dedup, and segment names --- lua/wire/stools/multisegmentlcd.lua | 156 +++++++++++++++++++--------- 1 file changed, 108 insertions(+), 48 deletions(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index b35e2352ba..12d12b6e86 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -6,6 +6,13 @@ UNION = 0 SEGMENT = 1 TEXT = 2 MATRIX = 3 +SegmentTypeNames = { +[GROUP] = "Group", +[UNION] = "Union", +[SEGMENT] = "Segment", +[TEXT] = "Text", +[MATRIX] = "Matrix", +} WireLib.SegmentLCD_Tree = { Type=GROUP, @@ -107,17 +114,17 @@ TOOL.ClientConVar = { function BuildNode(v,node,group) local new = nil if v.Type == GROUP then - new = node:AddNode( "Group", "icon16/text_list_numbers.png" ) + new = node:AddNode( v.Text or "Group", "icon16/text_list_numbers.png" ) BuildNodes(new,v) elseif v.Type == UNION then - new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) + new = node:AddNode( v.Text or "Union", "icon16/text_list_bullets.png" ) BuildNodes(new,v) elseif v.Type == TEXT then - new = node:AddNode( "Text", "icon16/bullet_yellow.png" ) + new = node:AddNode( v.Text or "Text", "icon16/bullet_yellow.png" ) elseif v.Type == MATRIX then - new = node:AddNode( "Matrix", "icon16/bullet_red.png" ) + new = node:AddNode( v.Text or "Matrix", "icon16/bullet_red.png" ) else - new = node:AddNode( "Segment", "icon16/bullet_green.png" ) + new = node:AddNode( v.Text or "Segment", "icon16/bullet_green.png" ) end new.group = v new.parentgroup = group @@ -162,13 +169,12 @@ function TOOL.BuildCPanel(panel) ButtonsHolder:Dock(TOP) ButtonsHolder:DockMargin(0, 0, 0, 0) ButtonsHolder.buttons = {} - ButtonsHolder:SetHeight(60) + ButtonsHolder:SetHeight(72) AddSegment = ButtonsHolder:Add( "DButton" ) AddSegment:SetText( "Add Segment" ) ButtonsHolder.buttons[1] = AddSegment - function AddSegment:DoClick() - local node = DisplayData:GetSelectedItem() + function AddSegmentI(node) if node == nil then node = DisplayData.RootNode end @@ -189,12 +195,15 @@ function TOOL.BuildCPanel(panel) new.group = newgroup new.parentgroup = group end + function AddSegment:DoClick() + local node = DisplayData:GetSelectedItem() + AddSegmentI(node) + end AddText = ButtonsHolder:Add( "DButton" ) AddText:SetText( "Add Text" ) ButtonsHolder.buttons[2] = AddText - function AddText:DoClick() - local node = DisplayData:GetSelectedItem() + function AddTextI(node) if node == nil then node = DisplayData.RootNode end @@ -209,18 +218,21 @@ function TOOL.BuildCPanel(panel) children = WireLib.SegmentLCD_Tree.Children group = WireLib.SegmentLCD_Tree end - local newgroup = {Type=TEXT, X=30, Y=0, Text="Hello"} + local newgroup = {Type=TEXT, X=30, Y=0, Text="Text"} children[#children+1] = newgroup local new = node:AddNode( "Text", "icon16/bullet_yellow.png" ) new.group = newgroup new.parentgroup = group end + function AddText:DoClick() + local node = DisplayData:GetSelectedItem() + AddTextI(node) + end AddMatrix = ButtonsHolder:Add( "DButton" ) AddMatrix:SetText( "Add Matrix" ) ButtonsHolder.buttons[3] = AddMatrix - function AddMatrix:DoClick() - local node = DisplayData:GetSelectedItem() + function AddMatrixI(node) if node == nil then node = DisplayData.RootNode end @@ -241,12 +253,16 @@ function TOOL.BuildCPanel(panel) new.group = newgroup new.parentgroup = group end + function AddMatrix:DoClick() + local node = DisplayData:GetSelectedItem() + AddMatrixI(node) + end AddGroup = ButtonsHolder:Add( "DButton" ) AddGroup:SetText( "Add Group" ) ButtonsHolder.buttons[4] = AddGroup - function AddGroup:DoClick() - local node = DisplayData:GetSelectedItem() + function AddGroupI(node) + if node == nil then node = DisplayData.RootNode end @@ -263,15 +279,19 @@ function TOOL.BuildCPanel(panel) local newgroup = {Type=GROUP,Children={},X=0,Y=0,HasColor=false,R=255,G=255,B=255} children[#children+1] = newgroup local new = node:AddNode( "Group", "icon16/text_list_numbers.png" ) + new:SetExpanded(true); new.group = newgroup new.parentgroup = group end + function AddGroup:DoClick() + local node = DisplayData:GetSelectedItem() + AddGroupI(node) + end AddUnion = ButtonsHolder:Add( "DButton" ) AddUnion:SetText( "Add Union" ) ButtonsHolder.buttons[5] = AddUnion - function AddUnion:DoClick() - local node = DisplayData:GetSelectedItem() + function AddUnionI(node) if node == nil then node = DisplayData.RootNode end @@ -288,13 +308,34 @@ function TOOL.BuildCPanel(panel) local newgroup = {Type=UNION,Children={},X=0,Y=0,HasColor=false,R=255,G=255,B=255} children[#children+1] = newgroup local new = node:AddNode( "Union", "icon16/text_list_bullets.png" ) + new:SetExpanded(true); new.group = newgroup new.parentgroup = group end + function AddUnion:DoClick() + local node = DisplayData:GetSelectedItem() + AddUnionI(node) + end Remove = ButtonsHolder:Add( "DButton" ) Remove:SetText( "Remove" ) ButtonsHolder.buttons[6] = Remove + function RemoveI(node) + if node == nil then + return + end + local parentgroup = node.parentgroup + if parentgroup == nil then + return + end + for i,v in pairs(parentgroup.Children) do + if v == node.group then + table.remove(parentgroup.Children,i) + node:Remove() + return + end + end + end function Remove:DoClick() local node = DisplayData:GetSelectedItem() if node == nil then @@ -359,10 +400,17 @@ function TOOL.BuildCPanel(panel) TextSetter = ButtonsHolder:Add( "DTextEntry" ) function TextSetter:OnValueChange(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= TEXT then + if node == nil or node.group == nil then return end - node.group.Text = value + if value == "" then + node:SetText(SegmentTypeNames[node.group.Type]) + node.group.Text = nil + else + node:SetText(value) + node.group.Text = value + end + end WangScaleW = ButtonsHolder:Add( "DNumberWang" ) @@ -448,38 +496,39 @@ function TOOL.BuildCPanel(panel) end function ButtonsHolder:PerformLayout(w, h) + local rowh = h/4 for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) - v:SetSize(w/#self.buttons,h/3) + v:SetSize(w/#self.buttons,rowh) end for i,v in ipairs(self.textboxes) do - v:SetPos((i-1)*w/#self.textboxes,h/3) - v:SetSize(w/#self.textboxes,h/3) - end - TextSetter:SetPos(0,h/3*2) - TextSetter:SetSize(w,h/3) - WangScaleW:SetPos(0,h/3*2) - WangScaleW:SetSize(w/4,h/3) - WangScaleH:SetPos(w/4,h/3*2) - WangScaleH:SetSize(w/4,h/3) - WangOffsetX:SetPos(w/4*2,h/3*2) - WangOffsetX:SetSize(w/4,h/3) - WangOffsetY:SetPos(w/4*3,h/3*2) - WangOffsetY:SetSize(w/4,h/3) + v:SetPos((i-1)*w/#self.textboxes,rowh) + v:SetSize(w/#self.textboxes,rowh) + end + + TextSetter:SetPos(0,rowh*3) + TextSetter:SetSize(w,rowh) + WangScaleW:SetPos(0,rowh*2) + WangScaleW:SetSize(w/4,rowh) + WangScaleH:SetPos(w/4,rowh*2) + WangScaleH:SetSize(w/4,rowh) + WangOffsetX:SetPos(w/4*2,rowh*2) + WangOffsetX:SetSize(w/4,rowh) + WangOffsetY:SetPos(w/4*3,rowh*2) + WangOffsetY:SetSize(w/4,rowh) - CheckHasColor:SetPos(1,h/3*2+3) - CheckLabel:SetPos(18,h/3*2) - CheckLabel:SetSize(w/4-18,h/3) - WangColorR:SetPos(w/4,h/3*2) - WangColorR:SetSize(w/4,h/3) - WangColorG:SetPos(w/4*2,h/3*2) - WangColorG:SetSize(w/4,h/3) - WangColorB:SetPos(w/4*3,h/3*2) - WangColorB:SetSize(w/4,h/3) + CheckHasColor:SetPos(1,rowh*2+3) + CheckLabel:SetPos(18,rowh*2) + CheckLabel:SetSize(w/4-18,rowh) + WangColorR:SetPos(w/4,rowh*2) + WangColorR:SetSize(w/4,rowh) + WangColorG:SetPos(w/4*2,rowh*2) + WangColorG:SetSize(w/4,rowh) + WangColorB:SetPos(w/4*3,rowh*2) + WangColorB:SetSize(w/4,rowh) end WangW:SetVisible(false) WangH:SetVisible(false) - TextSetter:SetVisible(false) WangScaleW:SetVisible(false) WangScaleH:SetVisible(false) WangOffsetX:SetVisible(false) @@ -492,7 +541,6 @@ function TOOL.BuildCPanel(panel) function DisplayData:DoClick(node) group = node.group - TextSetter:SetVisible(false) WangScaleW:SetVisible(false) WangScaleH:SetVisible(false) WangOffsetX:SetVisible(false) @@ -522,9 +570,6 @@ function TOOL.BuildCPanel(panel) WangScaleH:SetValue(group.ScaleH) WangOffsetX:SetValue(group.OffsetX) WangOffsetY:SetValue(group.OffsetY) - elseif group.Type == TEXT then - TextSetter:SetVisible(true) - TextSetter:SetValue(group.Text) elseif group.Type == GROUP or group.Type == UNION then WangColorR:SetVisible(true) WangColorG:SetVisible(true) @@ -538,16 +583,29 @@ function TOOL.BuildCPanel(panel) end WangX:SetValue(group.X) WangY:SetValue(group.Y) + TextSetter:SetValue(group.Text or "") return true end function DisplayData:DoRightClick(node) local Menu = DermaMenu() + Menu:AddOption( "Rename" ) Menu:AddOption( "Copy" ) Menu:AddOption( "Paste" ) + local InsertM, MMOption = Menu:AddSubMenu( "Insert" ) + InsertM:AddOption( "Union", function() AddUnionI(node) end ) + InsertM:AddOption( "Group", function() AddGroupI(node) end ) + InsertM:AddOption( "Segment", function() AddSegmentI(node) end ) + InsertM:AddOption( "Matrix", function() AddMatrixI(node) end ) + InsertM:AddOption( "Text", function() AddTextI(node) end ) + Menu:AddSpacer() + Menu:AddOption( "Remove" ) Menu:Open() + print("AAA") function Menu:OptionSelected(option, optionText) - if optionText == "Copy" then + if optionText == "Rename" then + + elseif optionText == "Copy" then SegmentLCD_Clipboard = table.Copy(node.group) elseif optionText == "Paste" then if node.group.Children then @@ -559,6 +617,8 @@ function TOOL.BuildCPanel(panel) WireLib.SegmentLCD_Tree.Children[#WireLib.SegmentLCD_Tree.Children+1] = newgroup BuildNode(newgroup,DisplayData.RootNode,WireLib.SegmentLCD_Tree) end + elseif optionText == "Remove" then + RemoveI(node) end end return true From 83b4edfd2b5f68defa4e03585ddb1518e088c631 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 10:30:34 +0200 Subject: [PATCH 24/50] no more enter --- lua/wire/stools/multisegmentlcd.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 12d12b6e86..f018700706 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -398,7 +398,8 @@ function TOOL.BuildCPanel(panel) ButtonsHolder.textboxes[4] = WangH TextSetter = ButtonsHolder:Add( "DTextEntry" ) - function TextSetter:OnValueChange(value) + function TextSetter:OnChange() + local value = TextSetter:GetText() local node = DisplayData:GetSelectedItem() if node == nil or node.group == nil then return From a8abd904b5515a8ca521a1acdaeb72ece914d035 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 19:01:38 +0200 Subject: [PATCH 25/50] Fading --- .../gmod_wire_multisegmentlcd/cl_init.lua | 54 +++++++++++++++---- lua/wire/stools/multisegmentlcd.lua | 48 +++++++++++++++++ 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 70ab720ef0..6f7173835a 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -3,6 +3,7 @@ include("shared.lua") function ENT:Initialize() self.Memory = {} + self.Fade = {} self.InteractiveData = {} self.LastButtons = {} @@ -70,29 +71,56 @@ function ENT:WriteCell(Address,value) self.Memory[math.floor(Address)] = value end +function ENT:Transform(x,y) + return { + x=x*self.LocalXX+y*self.LocalXY+self.LocalX, + y=x*self.LocalYX+y*self.LocalYY+self.LocalY + } +end + function ENT:DrawSegment(segment) + self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - surface.DrawRect(segment.X+self.LocalX,segment.Y+self.LocalY,segment.W,segment.H) + self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.8 end + surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + self.LocalX = self.LocalX + segment.X + self.LocalY = self.LocalY + segment.Y + --[[local Rect = { + self:Transform(0,segment.H), + self:Transform(0,0), + self:Transform(segment.W,0), + self:Transform(segment.W,segment.H) + } + surface.DrawPoly(Rect) + ]] + surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) + self.LocalX = self.LocalX - segment.X + self.LocalY = self.LocalY - segment.Y self.BitIndex = self.BitIndex+1 end function ENT:DrawText(text) + self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) - surface.SetFont("Default") - surface.SetTextColor(self.Cr,self.Cg,self.Cb,255) - surface.DrawText(text.Text) + self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 end + surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) + surface.SetFont("Default") + surface.SetTextColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + surface.DrawText(text.Text) self.BitIndex = self.BitIndex+1 end function ENT:DrawMatrix(matrix) for y = 0,matrix.H-1 do for x = 0,matrix.W-1 do + self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - surface.DrawRect(matrix.X+self.LocalX+x*matrix.OffsetX,matrix.Y+self.LocalY+y*matrix.OffsetY,matrix.ScaleW,matrix.ScaleH) + self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 end + surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + surface.DrawRect(matrix.X+self.LocalX+x*matrix.OffsetX,matrix.Y+self.LocalY+y*matrix.OffsetY,matrix.ScaleW,matrix.ScaleH) self.BitIndex = self.BitIndex+1 end end @@ -109,7 +137,7 @@ function ENT:DrawUnion(group) self.Cr = group.R self.Cg = group.G self.Cb = group.B - surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) @@ -136,7 +164,7 @@ function ENT:DrawUnion(group) self.Cr = oCr self.Cg = oCg self.Cb = oCb - surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end function ENT:DrawGroup(group) @@ -147,7 +175,7 @@ function ENT:DrawGroup(group) self.Cr = group.R self.Cg = group.G self.Cb = group.B - surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end self.LocalX = self.LocalX + (group.X or 0) self.LocalY = self.LocalY + (group.Y or 0) @@ -169,7 +197,7 @@ function ENT:DrawGroup(group) self.Cr = oCr self.Cg = oCg self.Cb = oCb - surface.SetDrawColor(self.Cr,self.Cg,self.Cb) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end function ENT:Draw() @@ -182,9 +210,13 @@ function ENT:Draw() self.Cr = self.Fgred self.Cg = self.Fggreen self.Cb = self.Fgblue - self.BitIndex = 0 + self.LocalXX = 1 + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = 1 self.LocalX = 0 self.LocalY = 0 + self.BitIndex = 0 self:DrawGroup(self.Tree) end end) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index f018700706..fb75fe72c3 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -496,6 +496,37 @@ function TOOL.BuildCPanel(panel) node.group.B = value end + WangRotation = ButtonsHolder:Add( "DNumberWang" ) + WangRotation:SetMax(360) + WangRotation:SetMin(-360) + function WangRotation:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.Rotation = value + end + + WangSkewX = ButtonsHolder:Add( "DNumberWang" ) + WangSkewX:SetMax(4096) + function WangSkewX:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.SkewX = value + end + + WangSkewY = ButtonsHolder:Add( "DNumberWang" ) + WangSkewY:SetMax(4096) + function WangSkewY:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.SkewY = value + end + function ButtonsHolder:PerformLayout(w, h) local rowh = h/4 for i,v in ipairs(self.buttons) do @@ -527,6 +558,13 @@ function TOOL.BuildCPanel(panel) WangColorG:SetSize(w/4,rowh) WangColorB:SetPos(w/4*3,rowh*2) WangColorB:SetSize(w/4,rowh) + + WangRotation:SetPos(0,rowh*2) + WangRotation:SetSize(w/4,rowh) + WangSkewX:SetPos(w/4,rowh*2) + WangSkewX:SetSize(w/4,rowh) + WangSkewY:SetPos(w/4*2,rowh*2) + WangSkewY:SetSize(w/4,rowh) end WangW:SetVisible(false) WangH:SetVisible(false) @@ -539,6 +577,10 @@ function TOOL.BuildCPanel(panel) WangColorB:SetVisible(false) CheckHasColor:SetVisible(false) CheckLabel:SetVisible(false) + WangRotation:SetVisible(false) + WangSkewX:SetVisible(false) + WangSkewY:SetVisible(false) + function DisplayData:DoClick(node) group = node.group @@ -553,11 +595,17 @@ function TOOL.BuildCPanel(panel) WangColorB:SetVisible(false) CheckHasColor:SetVisible(false) CheckLabel:SetVisible(false) + WangRotation:SetVisible(false) + WangSkewX:SetVisible(false) + WangSkewY:SetVisible(false) if group.Type == SEGMENT then WangW:SetValue(group.W) WangH:SetValue(group.H) WangW:SetVisible(true) WangH:SetVisible(true) + WangRotation:SetVisible(true) + WangSkewX:SetVisible(true) + WangSkewY:SetVisible(true) elseif group.Type == MATRIX then WangW:SetValue(group.W) WangH:SetValue(group.H) From 0e761557357071165dde827cff39e6c2770a95a5 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 19:42:09 +0200 Subject: [PATCH 26/50] in the bad bad code there are bad bad practices with bad bad perf --- .../gmod_wire_multisegmentlcd/cl_init.lua | 100 +++++++++++++++--- lua/wire/stools/multisegmentlcd.lua | 45 ++++---- 2 files changed, 107 insertions(+), 38 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 6f7173835a..ca5696d9b0 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -78,25 +78,61 @@ function ENT:Transform(x,y) } end +function ENT:TransformOffset(x,y) + return { + x=x*self.LocalXX+y*self.LocalXY, + y=x*self.LocalYX+y*self.LocalYY + } +end + +function ENT:PushTransform(XX,XY,YX,YY) + self.TransformStack[#self.TransformStack + 1] = {self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY} + local oXX = self.LocalXX + local oXY = self.LocalXY + local oYX = self.LocalYX + local oYY = self.LocalYY + + local nXX = oXX*XX + oXY*YX + local nXY = oXY*YY + oXX*XY + local nYX = oYX*XX + oYY*YX + local nYY = oYY*YY + oYX*XY + + self.LocalXX = nXX + self.LocalXY = nXY + self.LocalYX = nYX + self.LocalYY = nYY +end + +function ENT:PopTransform() + self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY = unpack(self.TransformStack[#self.TransformStack]) + self.TransformStack[#self.TransformStack] = nil +end + function ENT:DrawSegment(segment) self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.8 end surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) - self.LocalX = self.LocalX + segment.X - self.LocalY = self.LocalY + segment.Y - --[[local Rect = { + local transformedLocal = self:TransformOffset(segment.X or 0,segment.Y or 0) + self.LocalX = self.LocalX + transformedLocal.x + self.LocalY = self.LocalY + transformedLocal.y + local angle = math.rad(segment.Rotation or 0) + self:PushTransform(math.cos(angle), + math.sin(angle)-(segment.SkewX or 0), + -math.sin(angle)+(segment.SkewY or 0), + math.cos(angle)) + local Rect = { self:Transform(0,segment.H), self:Transform(0,0), self:Transform(segment.W,0), self:Transform(segment.W,segment.H) } surface.DrawPoly(Rect) - ]] - surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) - self.LocalX = self.LocalX - segment.X - self.LocalY = self.LocalY - segment.Y + self:PopTransform() + --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) + self.LocalX = self.LocalX - transformedLocal.x + self.LocalY = self.LocalY - transformedLocal.y self.BitIndex = self.BitIndex+1 end @@ -105,10 +141,15 @@ function ENT:DrawText(text) if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 end - surface.SetTextPos(text.X+self.LocalX,text.Y+self.LocalY) + local transformedLocal = self:TransformOffset(text.X or 0,text.Y or 0) + self.LocalX = self.LocalX + transformedLocal.x + self.LocalY = self.LocalY + transformedLocal.y + surface.SetTextPos(self.LocalX,self.LocalY) surface.SetFont("Default") surface.SetTextColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) surface.DrawText(text.Text) + self.LocalX = self.LocalX - transformedLocal.x + self.LocalY = self.LocalY - transformedLocal.y self.BitIndex = self.BitIndex+1 end @@ -119,8 +160,23 @@ function ENT:DrawMatrix(matrix) if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 end + + local transformedLocal = self:TransformOffset(matrix.X+x*matrix.OffsetX,matrix.Y+y*matrix.OffsetY) + self.LocalX = self.LocalX + transformedLocal.x + self.LocalY = self.LocalY + transformedLocal.y surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) - surface.DrawRect(matrix.X+self.LocalX+x*matrix.OffsetX,matrix.Y+self.LocalY+y*matrix.OffsetY,matrix.ScaleW,matrix.ScaleH) + local Rect = { + self:Transform(0,matrix.ScaleH), + self:Transform(0,0), + self:Transform(matrix.ScaleW,0), + self:Transform(matrix.ScaleW,matrix.ScaleH) + } + surface.DrawPoly(Rect) + + + --surface.DrawRect(self.LocalX,self.LocalY,matrix.ScaleW,matrix.ScaleH) + self.LocalX = self.LocalX - transformedLocal.x + self.LocalY = self.LocalY - transformedLocal.y self.BitIndex = self.BitIndex+1 end end @@ -139,8 +195,9 @@ function ENT:DrawUnion(group) self.Cb = group.B --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end - self.LocalX = self.LocalX + (group.X or 0) - self.LocalY = self.LocalY + (group.Y or 0) + local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) + self.LocalX = self.LocalX + transformedLocal.x + self.LocalY = self.LocalY + transformedLocal.y local savedindex = self.BitIndex local biggestindex = savedindex for k,v in ipairs(group.Children) do @@ -159,8 +216,8 @@ function ENT:DrawUnion(group) self.BitIndex = savedindex end self.BitIndex = biggestindex - self.LocalX = self.LocalX - (group.X or 0) - self.LocalY = self.LocalY - (group.Y or 0) + self.LocalX = self.LocalX - transformedLocal.x + self.LocalY = self.LocalY - transformedLocal.y self.Cr = oCr self.Cg = oCg self.Cb = oCb @@ -177,8 +234,14 @@ function ENT:DrawGroup(group) self.Cb = group.B --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end - self.LocalX = self.LocalX + (group.X or 0) - self.LocalY = self.LocalY + (group.Y or 0) + local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) + self.LocalX = self.LocalX + transformedLocal.x + self.LocalY = self.LocalY + transformedLocal.y + local angle = math.rad(group.Rotation or 0) + self:PushTransform(math.cos(angle), + math.sin(angle)-(group.SkewX or 0), + -math.sin(angle)+(group.SkewY or 0), + math.cos(angle)) for k,v in ipairs(group.Children) do if v.Type == GROUP then self:DrawGroup(v) @@ -192,8 +255,9 @@ function ENT:DrawGroup(group) self:DrawMatrix(v) end end - self.LocalX = self.LocalX - (group.X or 0) - self.LocalY = self.LocalY - (group.Y or 0) + self:PopTransform() + self.LocalX = self.LocalX - transformedLocal.x + self.LocalY = self.LocalY - transformedLocal.y self.Cr = oCr self.Cg = oCg self.Cb = oCb @@ -217,6 +281,8 @@ function ENT:Draw() self.LocalX = 0 self.LocalY = 0 self.BitIndex = 0 + draw.NoTexture() + self.TransformStack = {} self:DrawGroup(self.Tree) end end) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index fb75fe72c3..4371d12a42 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -528,6 +528,7 @@ function TOOL.BuildCPanel(panel) end function ButtonsHolder:PerformLayout(w, h) + local roww = w/7 local rowh = h/4 for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) @@ -537,34 +538,33 @@ function TOOL.BuildCPanel(panel) v:SetPos((i-1)*w/#self.textboxes,rowh) v:SetSize(w/#self.textboxes,rowh) end - TextSetter:SetPos(0,rowh*3) TextSetter:SetSize(w,rowh) WangScaleW:SetPos(0,rowh*2) - WangScaleW:SetSize(w/4,rowh) - WangScaleH:SetPos(w/4,rowh*2) - WangScaleH:SetSize(w/4,rowh) - WangOffsetX:SetPos(w/4*2,rowh*2) - WangOffsetX:SetSize(w/4,rowh) - WangOffsetY:SetPos(w/4*3,rowh*2) - WangOffsetY:SetSize(w/4,rowh) + WangScaleW:SetSize(roww,rowh) + WangScaleH:SetPos(roww,rowh*2) + WangScaleH:SetSize(roww,rowh) + WangOffsetX:SetPos(roww*2,rowh*2) + WangOffsetX:SetSize(roww,rowh) + WangOffsetY:SetPos(roww*3,rowh*2) + WangOffsetY:SetSize(roww,rowh) CheckHasColor:SetPos(1,rowh*2+3) CheckLabel:SetPos(18,rowh*2) - CheckLabel:SetSize(w/4-18,rowh) - WangColorR:SetPos(w/4,rowh*2) - WangColorR:SetSize(w/4,rowh) - WangColorG:SetPos(w/4*2,rowh*2) - WangColorG:SetSize(w/4,rowh) - WangColorB:SetPos(w/4*3,rowh*2) - WangColorB:SetSize(w/4,rowh) + CheckLabel:SetSize(roww-18,rowh) + WangColorR:SetPos(roww,rowh*2) + WangColorR:SetSize(roww,rowh) + WangColorG:SetPos(roww*2,rowh*2) + WangColorG:SetSize(roww,rowh) + WangColorB:SetPos(roww*3,rowh*2) + WangColorB:SetSize(roww,rowh) - WangRotation:SetPos(0,rowh*2) - WangRotation:SetSize(w/4,rowh) - WangSkewX:SetPos(w/4,rowh*2) - WangSkewX:SetSize(w/4,rowh) - WangSkewY:SetPos(w/4*2,rowh*2) - WangSkewY:SetSize(w/4,rowh) + WangRotation:SetPos(roww*4,rowh*2) + WangRotation:SetSize(roww,rowh) + WangSkewX:SetPos(roww*5,rowh*2) + WangSkewX:SetSize(roww,rowh) + WangSkewY:SetPos(roww*6,rowh*2) + WangSkewY:SetSize(roww,rowh) end WangW:SetVisible(false) WangH:SetVisible(false) @@ -629,6 +629,9 @@ function TOOL.BuildCPanel(panel) WangColorG:SetValue(group.G) WangColorB:SetValue(group.B) CheckHasColor:SetValue(group.HasColor) + WangRotation:SetVisible(true) + WangSkewX:SetVisible(true) + WangSkewY:SetVisible(true) end WangX:SetValue(group.X) WangY:SetValue(group.Y) From 60c1fe265fcc674777e74e8969a150fc816ea3d3 Mon Sep 17 00:00:00 2001 From: wav3 Date: Mon, 8 Sep 2025 20:45:17 +0200 Subject: [PATCH 27/50] oh my --- .../gmod_wire_multisegmentlcd/cl_init.lua | 29 +++++++++----- lua/wire/stools/multisegmentlcd.lua | 39 ++++++++++++++++++- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index ca5696d9b0..f0ab815796 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -122,11 +122,17 @@ function ENT:DrawSegment(segment) math.sin(angle)-(segment.SkewX or 0), -math.sin(angle)+(segment.SkewY or 0), math.cos(angle)) + --self:Transform(,segment.H/2+(segment.H*(segment.BevelSkew or 0))), + local bevel = math.min(segment.H,segment.W)/2*(segment.Bevel or 0) local Rect = { - self:Transform(0,segment.H), - self:Transform(0,0), - self:Transform(segment.W,0), - self:Transform(segment.W,segment.H) + self:Transform(bevel,segment.H), + self:Transform(0,segment.H-bevel), + self:Transform(0,bevel), + self:Transform(bevel,0), + self:Transform(segment.W-bevel,0), + self:Transform(segment.W,bevel), + self:Transform(segment.W,segment.H-bevel), + self:Transform(segment.W-bevel,segment.H) } surface.DrawPoly(Rect) self:PopTransform() @@ -266,10 +272,12 @@ end function ENT:Draw() self:DrawModel() - self.GPU:RenderToGPU(function() + self.GPU:RenderToWorld(nil, self.ResolutionH, function(x, y, w, h) + draw.NoTexture() surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) - surface.DrawRect(0,0,1024,1024) + surface.DrawRect(x,y,w,h) if self.Tree then + --render.SetScissorRect(x,y,w,h, true) surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) self.Cr = self.Fgred self.Cg = self.Fggreen @@ -278,15 +286,16 @@ function ENT:Draw() self.LocalXY = 0 self.LocalYX = 0 self.LocalYY = 1 - self.LocalX = 0 - self.LocalY = 0 + self.LocalX = x + self.LocalY = y self.BitIndex = 0 - draw.NoTexture() + self.TransformStack = {} self:DrawGroup(self.Tree) + --render.SetScissorRect( 0, 0, 0, 0, false ) end end) - self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) + --self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) Wire_Render(self) end diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 4371d12a42..d7e6c91db3 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -517,6 +517,27 @@ function TOOL.BuildCPanel(panel) node.group.SkewX = value end + WangBevel = ButtonsHolder:Add( "DNumberWang" ) + WangBevel:SetMax(1024) + function WangBevel:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.Bevel = value + end + + WangBevelSkew = ButtonsHolder:Add( "DNumberWang" ) + WangBevelSkew:SetMax(1024) + WangBevelSkew:SetMin(-1024) + function WangBevelSkew:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.BevelSkew = value + end + WangSkewY = ButtonsHolder:Add( "DNumberWang" ) WangSkewY:SetMax(4096) function WangSkewY:OnValueChanged(value) @@ -565,6 +586,11 @@ function TOOL.BuildCPanel(panel) WangSkewX:SetSize(roww,rowh) WangSkewY:SetPos(roww*6,rowh*2) WangSkewY:SetSize(roww,rowh) + + WangBevel:SetPos(0,rowh*2) + WangBevel:SetSize(roww,rowh) + WangBevelSkew:SetPos(roww,rowh*2) + WangBevelSkew:SetSize(roww,rowh) end WangW:SetVisible(false) WangH:SetVisible(false) @@ -580,7 +606,8 @@ function TOOL.BuildCPanel(panel) WangRotation:SetVisible(false) WangSkewX:SetVisible(false) WangSkewY:SetVisible(false) - + WangBevel:SetVisible(false) + WangBevelSkew:SetVisible(false) function DisplayData:DoClick(node) group = node.group @@ -598,6 +625,8 @@ function TOOL.BuildCPanel(panel) WangRotation:SetVisible(false) WangSkewX:SetVisible(false) WangSkewY:SetVisible(false) + WangBevel:SetVisible(false) + WangBevelSkew:SetVisible(false) if group.Type == SEGMENT then WangW:SetValue(group.W) WangH:SetValue(group.H) @@ -606,6 +635,14 @@ function TOOL.BuildCPanel(panel) WangRotation:SetVisible(true) WangSkewX:SetVisible(true) WangSkewY:SetVisible(true) + WangBevel:SetVisible(true) + WangBevelSkew:SetVisible(true) + + WangRotation:SetValue(group.Rotation or 0) + WangSkewX:SetValue(group.SkewX or 0) + WangSkewY:SetValue(group.SkewY or 0) + WangBevel:SetValue(group.Bevel or 0) + WangBevelSkew:SetValue(group.BevelSkew or 0) elseif group.Type == MATRIX then WangW:SetValue(group.W) WangH:SetValue(group.H) From bf9bed0557bbba903492f89775b35cf6121514a3 Mon Sep 17 00:00:00 2001 From: wav3 Date: Tue, 9 Sep 2025 11:32:10 +0200 Subject: [PATCH 28/50] Begin --- .../gmod_wire_multisegmentlcd/cl_init.lua | 106 ++++++++++++------ 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index f0ab815796..0897fe9278 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -31,6 +31,8 @@ function ENT:Initialize() GPULib.ClientCacheCallback(self,function(Address,Value) self:WriteCell(Address,Value) end) + + self.TreeMesh = Mesh() WireLib.netRegister(self) end @@ -73,15 +75,15 @@ end function ENT:Transform(x,y) return { - x=x*self.LocalXX+y*self.LocalXY+self.LocalX, - y=x*self.LocalYX+y*self.LocalYY+self.LocalY + x*self.LocalXX+y*self.LocalXY+self.LocalX, + x*self.LocalYX+y*self.LocalYY+self.LocalY } end function ENT:TransformOffset(x,y) return { - x=x*self.LocalXX+y*self.LocalXY, - y=x*self.LocalYX+y*self.LocalYY + x*self.LocalXX+y*self.LocalXY, + x*self.LocalYX+y*self.LocalYY } end @@ -108,15 +110,27 @@ function ENT:PopTransform() self.TransformStack[#self.TransformStack] = nil end +function ENT:AddPoly(poly) + local u = ((self.BitIndex%1024)+0.5)/1024 + local v = (math.floor(self.BitIndex/1024)+0.5)/1024 + mesh.Begin(MATERIAL_POLYGON,#poly) + for i = 1,#poly do + mesh.Position(Vector(poly[i][1],poly[i][2],0)) + mesh.TexCoord(0, u, v) + mesh.AdvanceVertex() + end + mesh.End() +end + function ENT:DrawSegment(segment) self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.8 end - surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) local transformedLocal = self:TransformOffset(segment.X or 0,segment.Y or 0) - self.LocalX = self.LocalX + transformedLocal.x - self.LocalY = self.LocalY + transformedLocal.y + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] local angle = math.rad(segment.Rotation or 0) self:PushTransform(math.cos(angle), math.sin(angle)-(segment.SkewX or 0), @@ -134,11 +148,12 @@ function ENT:DrawSegment(segment) self:Transform(segment.W,segment.H-bevel), self:Transform(segment.W-bevel,segment.H) } - surface.DrawPoly(Rect) + self:AddPoly(Rect) + --surface.DrawPoly(Rect) self:PopTransform() --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) - self.LocalX = self.LocalX - transformedLocal.x - self.LocalY = self.LocalY - transformedLocal.y + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] self.BitIndex = self.BitIndex+1 end @@ -148,14 +163,14 @@ function ENT:DrawText(text) self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 end local transformedLocal = self:TransformOffset(text.X or 0,text.Y or 0) - self.LocalX = self.LocalX + transformedLocal.x - self.LocalY = self.LocalY + transformedLocal.y - surface.SetTextPos(self.LocalX,self.LocalY) - surface.SetFont("Default") - surface.SetTextColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) - surface.DrawText(text.Text) - self.LocalX = self.LocalX - transformedLocal.x - self.LocalY = self.LocalY - transformedLocal.y + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] + --surface.SetTextPos(self.LocalX,self.LocalY) + --surface.SetFont("Default") + --surface.SetTextColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + --surface.DrawText(text.Text) + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] self.BitIndex = self.BitIndex+1 end @@ -168,21 +183,21 @@ function ENT:DrawMatrix(matrix) end local transformedLocal = self:TransformOffset(matrix.X+x*matrix.OffsetX,matrix.Y+y*matrix.OffsetY) - self.LocalX = self.LocalX + transformedLocal.x - self.LocalY = self.LocalY + transformedLocal.y - surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) local Rect = { self:Transform(0,matrix.ScaleH), self:Transform(0,0), self:Transform(matrix.ScaleW,0), self:Transform(matrix.ScaleW,matrix.ScaleH) } - surface.DrawPoly(Rect) + self:AddPoly(Rect) --surface.DrawRect(self.LocalX,self.LocalY,matrix.ScaleW,matrix.ScaleH) - self.LocalX = self.LocalX - transformedLocal.x - self.LocalY = self.LocalY - transformedLocal.y + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] self.BitIndex = self.BitIndex+1 end end @@ -202,8 +217,8 @@ function ENT:DrawUnion(group) --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) - self.LocalX = self.LocalX + transformedLocal.x - self.LocalY = self.LocalY + transformedLocal.y + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] local savedindex = self.BitIndex local biggestindex = savedindex for k,v in ipairs(group.Children) do @@ -222,8 +237,8 @@ function ENT:DrawUnion(group) self.BitIndex = savedindex end self.BitIndex = biggestindex - self.LocalX = self.LocalX - transformedLocal.x - self.LocalY = self.LocalY - transformedLocal.y + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] self.Cr = oCr self.Cg = oCg self.Cb = oCb @@ -241,8 +256,8 @@ function ENT:DrawGroup(group) --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) - self.LocalX = self.LocalX + transformedLocal.x - self.LocalY = self.LocalY + transformedLocal.y + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] local angle = math.rad(group.Rotation or 0) self:PushTransform(math.cos(angle), math.sin(angle)-(group.SkewX or 0), @@ -262,8 +277,8 @@ function ENT:DrawGroup(group) end end self:PopTransform() - self.LocalX = self.LocalX - transformedLocal.x - self.LocalY = self.LocalY - transformedLocal.y + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] self.Cr = oCr self.Cg = oCg self.Cb = oCb @@ -272,6 +287,7 @@ end function ENT:Draw() self:DrawModel() + --[[ self.GPU:RenderToWorld(nil, self.ResolutionH, function(x, y, w, h) draw.NoTexture() surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) @@ -294,7 +310,29 @@ function ENT:Draw() self:DrawGroup(self.Tree) --render.SetScissorRect( 0, 0, 0, 0, false ) end - end) + end)]] + local mat = Material( "models/debug/debugwhite" ) + render.SetMaterial( mat ) + cam.PushModelMatrix( self:GetWorldTransformMatrix() ) + if self.Tree then + --surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) + self.Cr = self.Fgred + self.Cg = self.Fggreen + self.Cb = self.Fgblue + self.LocalXX = 1 + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = 1 + self.LocalX = 0 + self.LocalY = 0 + self.BitIndex = 0 + + self.TransformStack = {} + self:DrawGroup(self.Tree) + --render.SetScissorRect( 0, 0, 0, 0, false ) + end + --self.TreeMesh:Draw() + cam.PopModelMatrix() --self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) Wire_Render(self) end @@ -315,4 +353,6 @@ function ENT:Receive() self.Bgblue = net.ReadUInt(8) self.Bggreen = net.ReadUInt(8) self.Bgred = net.ReadUInt(8) + + end From 776fa0ef680e0959b6f44dc1fc3b500601c13802 Mon Sep 17 00:00:00 2001 From: wav3 Date: Tue, 9 Sep 2025 13:30:11 +0200 Subject: [PATCH 29/50] very messy code, but it works! --- .../gmod_wire_multisegmentlcd/cl_init.lua | 124 ++++++++++++++---- 1 file changed, 100 insertions(+), 24 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 0897fe9278..45324aaf82 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -63,6 +63,7 @@ end function ENT:OnRemove() self.GPU:Finalize() + self.TreeMesh:Destroy() end function ENT:ReadCell(Address) @@ -113,13 +114,12 @@ end function ENT:AddPoly(poly) local u = ((self.BitIndex%1024)+0.5)/1024 local v = (math.floor(self.BitIndex/1024)+0.5)/1024 - mesh.Begin(MATERIAL_POLYGON,#poly) for i = 1,#poly do mesh.Position(Vector(poly[i][1],poly[i][2],0)) - mesh.TexCoord(0, u, v) + mesh.TexCoord(0, u, v, u ,v) + mesh.Color(255,255,255,255) mesh.AdvanceVertex() end - mesh.End() end function ENT:DrawSegment(segment) @@ -138,7 +138,7 @@ function ENT:DrawSegment(segment) math.cos(angle)) --self:Transform(,segment.H/2+(segment.H*(segment.BevelSkew or 0))), local bevel = math.min(segment.H,segment.W)/2*(segment.Bevel or 0) - local Rect = { + local rect = { self:Transform(bevel,segment.H), self:Transform(0,segment.H-bevel), self:Transform(0,bevel), @@ -148,7 +148,15 @@ function ENT:DrawSegment(segment) self:Transform(segment.W,segment.H-bevel), self:Transform(segment.W-bevel,segment.H) } - self:AddPoly(Rect) + local poly = { + rect[1],rect[2],rect[3], + rect[1],rect[3],rect[4], + rect[1],rect[4],rect[5], + rect[1],rect[5],rect[6], + rect[1],rect[6],rect[7], + rect[1],rect[7],rect[8] + } + self:AddPoly(poly) --surface.DrawPoly(Rect) self:PopTransform() --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) @@ -186,13 +194,17 @@ function ENT:DrawMatrix(matrix) self.LocalX = self.LocalX + transformedLocal[1] self.LocalY = self.LocalY + transformedLocal[2] --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) - local Rect = { + local rect = { self:Transform(0,matrix.ScaleH), self:Transform(0,0), self:Transform(matrix.ScaleW,0), self:Transform(matrix.ScaleW,matrix.ScaleH) } - self:AddPoly(Rect) + local poly = { + rect[1],rect[2],rect[3], + rect[1],rect[3],rect[4], + } + self:AddPoly(poly) --surface.DrawRect(self.LocalX,self.LocalY,matrix.ScaleW,matrix.ScaleH) @@ -285,6 +297,19 @@ function ENT:DrawGroup(group) --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end +function ENT:CountTris(node) + if node.Type == GROUP or node.Type == UNION then + local sum = 0 + for i=1,#node.Children do + sum = sum + self:CountTris(node.Children[i]) + end + return sum + elseif node.Type == MATRIX then + return node.W*node.H*2 + end + return 6 +end + function ENT:Draw() self:DrawModel() --[[ @@ -311,28 +336,62 @@ function ENT:Draw() --render.SetScissorRect( 0, 0, 0, 0, false ) end end)]] - local mat = Material( "models/debug/debugwhite" ) - render.SetMaterial( mat ) - cam.PushModelMatrix( self:GetWorldTransformMatrix() ) + + + + if self.Tree then + + local oldw = ScrW() + local oldh = ScrH() + + local NewRT = self.GPU.RT + local OldRT = render.GetRenderTarget() + + render.SetRenderTarget(NewRT) + render.SetViewPort(0, 0, 1024, 1024) + cam.Start2D() + for i=0,self.BitIndex do + local x = ((i%1024)+0.5) + local y = (math.floor(i/1024)+0.5) + self.Fade[i] = (self.Fade[i] or 0)*0.92 + if bit.band(self.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then + self.Fade[i] = self.Fade[i] + 0.08 + end + surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[i]*255) + surface.DrawRect( x, y, 1, 1 ) + end + cam.End2D() + render.SetViewPort(0, 0, oldw, oldh) + render.SetRenderTarget(OldRT) + + + local OldTex = WireGPU_matScreen:GetTexture("$basetexture") + WireGPU_matScreen:SetTexture("$basetexture", self.GPU.RT) + render.SetMaterial( WireGPU_matScreen ) + + local monitor, pos, ang = self.GPU:GetInfo() + local h = self.ResolutionH + local scale = monitor.RS*1024/h + local m = Matrix() + m:SetAngles( ang ) + m:SetTranslation( pos ) + m:SetScale( Vector( scale, -scale, 1 ) ) + cam.PushModelMatrix( self:GetWorldTransformMatrix() ) + cam.PushModelMatrix( m ) + --surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) - self.Cr = self.Fgred - self.Cg = self.Fggreen - self.Cb = self.Fgblue - self.LocalXX = 1 - self.LocalXY = 0 - self.LocalYX = 0 - self.LocalYY = 1 - self.LocalX = 0 - self.LocalY = 0 - self.BitIndex = 0 - self.TransformStack = {} - self:DrawGroup(self.Tree) + + self.TreeMesh:Draw() + cam.PopModelMatrix() + cam.PopModelMatrix() + + WireGPU_matScreen:SetTexture("$basetexture", OldTex) --render.SetScissorRect( 0, 0, 0, 0, false ) end - --self.TreeMesh:Draw() - cam.PopModelMatrix() + -- + --self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) Wire_Render(self) end @@ -354,5 +413,22 @@ function ENT:Receive() self.Bggreen = net.ReadUInt(8) self.Bgred = net.ReadUInt(8) + self.Cr = self.Fgred + self.Cg = self.Fggreen + self.Cb = self.Fgblue + self.LocalXX = 1 + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = 1 + self.BitIndex = 0 + self.TransformStack = {} + local monitor, pos, ang = self.GPU:GetInfo() + local h = self.ResolutionH + local w = h/monitor.RatioX + self.LocalX = -w/2 + self.LocalY = -h/2 + mesh.Begin(self.TreeMesh,MATERIAL_TRIANGLES,self:CountTris(self.Tree)) + self:DrawGroup(self.Tree) + mesh.End() end From ffe8be03e963f3d9d9486047112e4972a303ebd5 Mon Sep 17 00:00:00 2001 From: wav3 Date: Tue, 9 Sep 2025 14:06:43 +0200 Subject: [PATCH 30/50] It is working, except for text --- .../gmod_wire_multisegmentlcd/cl_init.lua | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 45324aaf82..e87deff883 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -112,21 +112,23 @@ function ENT:PopTransform() end function ENT:AddPoly(poly) - local u = ((self.BitIndex%1024)+0.5)/1024 - local v = (math.floor(self.BitIndex/1024)+0.5)/1024 + local u = (((self.BitIndex+1)%1024)+0.5)/1024 + local v = (math.floor((self.BitIndex+1)/1024)+0.5)/1024 for i = 1,#poly do - mesh.Position(Vector(poly[i][1],poly[i][2],0)) + mesh.Position(Vector(poly[i][1],poly[i][2],0.1)) mesh.TexCoord(0, u, v, u ,v) mesh.Color(255,255,255,255) mesh.AdvanceVertex() end + for i = 1,#poly do + mesh.Position(Vector(poly[i][1],poly[i][2],0)) + mesh.TexCoord(0, u, v, u ,v) + mesh.Color(127,127,127,127) + mesh.AdvanceVertex() + end end function ENT:DrawSegment(segment) - self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 - if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.8 - end --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) local transformedLocal = self:TransformOffset(segment.X or 0,segment.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] @@ -162,14 +164,11 @@ function ENT:DrawSegment(segment) --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} self.BitIndex = self.BitIndex+1 end function ENT:DrawText(text) - self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 - if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 - end local transformedLocal = self:TransformOffset(text.X or 0,text.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] self.LocalY = self.LocalY + transformedLocal[2] @@ -179,16 +178,13 @@ function ENT:DrawText(text) --surface.DrawText(text.Text) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} self.BitIndex = self.BitIndex+1 end function ENT:DrawMatrix(matrix) for y = 0,matrix.H-1 do for x = 0,matrix.W-1 do - self.Fade[self.BitIndex] = (self.Fade[self.BitIndex] or 0)*0.92 - if bit.band(self.Memory[bit.rshift(self.BitIndex,3)] or 0,bit.lshift(1,bit.band(self.BitIndex,7))) ~= 0 then - self.Fade[self.BitIndex] = self.Fade[self.BitIndex] + 0.08 - end local transformedLocal = self:TransformOffset(matrix.X+x*matrix.OffsetX,matrix.Y+y*matrix.OffsetY) self.LocalX = self.LocalX + transformedLocal[1] @@ -210,6 +206,7 @@ function ENT:DrawMatrix(matrix) --surface.DrawRect(self.LocalX,self.LocalY,matrix.ScaleW,matrix.ScaleH) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} self.BitIndex = self.BitIndex+1 end end @@ -226,7 +223,6 @@ function ENT:DrawUnion(group) self.Cr = group.R self.Cg = group.G self.Cb = group.B - --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] @@ -254,7 +250,6 @@ function ENT:DrawUnion(group) self.Cr = oCr self.Cg = oCg self.Cb = oCb - --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end function ENT:DrawGroup(group) @@ -294,7 +289,6 @@ function ENT:DrawGroup(group) self.Cr = oCr self.Cg = oCg self.Cb = oCb - --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end function ENT:CountTris(node) @@ -305,9 +299,9 @@ function ENT:CountTris(node) end return sum elseif node.Type == MATRIX then - return node.W*node.H*2 + return node.W*node.H*4 end - return 6 + return 12 end function ENT:Draw() @@ -351,14 +345,17 @@ function ENT:Draw() render.SetRenderTarget(NewRT) render.SetViewPort(0, 0, 1024, 1024) cam.Start2D() - for i=0,self.BitIndex do - local x = ((i%1024)+0.5) - local y = (math.floor(i/1024)+0.5) + surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) + surface.DrawRect( 0, 0, 1, 1 ) + for i=0,self.BitIndex-1 do + local x = (((i+1)%1024)+0.5) + local y = (math.floor((i+1)/1024)+0.5) self.Fade[i] = (self.Fade[i] or 0)*0.92 if bit.band(self.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then self.Fade[i] = self.Fade[i] + 0.08 end - surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[i]*255) + local color = self.Colors[i] + surface.SetDrawColor(color[1],color[2],color[3],self.Fade[i]*255) surface.DrawRect( x, y, 1, 1 ) end cam.End2D() @@ -422,12 +419,21 @@ function ENT:Receive() self.LocalYY = 1 self.BitIndex = 0 self.TransformStack = {} + self.Colors = {} local monitor, pos, ang = self.GPU:GetInfo() local h = self.ResolutionH local w = h/monitor.RatioX self.LocalX = -w/2 self.LocalY = -h/2 mesh.Begin(self.TreeMesh,MATERIAL_TRIANGLES,self:CountTris(self.Tree)) + + mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0)mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + + mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() self:DrawGroup(self.Tree) mesh.End() From 3825caef5ece8048e615bd4d23b61fc8e8dc8c10 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 10 Sep 2025 11:13:40 +0200 Subject: [PATCH 31/50] Change rotation and skew order --- .../gmod_wire_multisegmentlcd/cl_init.lua | 97 ++++++++++++++----- .../gmod_wire_multisegmentlcd/init.lua | 4 +- lua/wire/stools/multisegmentlcd.lua | 54 ++++++++--- 3 files changed, 115 insertions(+), 40 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index e87deff883..d944f526de 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -32,7 +32,7 @@ function ENT:Initialize() self:WriteCell(Address,Value) end) - self.TreeMesh = Mesh() + self.TreeMesh = {} WireLib.netRegister(self) end @@ -63,7 +63,9 @@ end function ENT:OnRemove() self.GPU:Finalize() - self.TreeMesh:Destroy() + for i=1,#self.TreeMesh do + self.TreeMesh[i]:Destroy() + end end function ENT:ReadCell(Address) @@ -112,19 +114,35 @@ function ENT:PopTransform() end function ENT:AddPoly(poly) - local u = (((self.BitIndex+1)%1024)+0.5)/1024 + local u = (((bit.bxor(self.BitIndex,self.XorMask)+1)%1024)+0.5)/1024 local v = (math.floor((self.BitIndex+1)/1024)+0.5)/1024 for i = 1,#poly do + if self.CurTris > 10922 then + mesh.End() + self.TreeMesh[#self.TreeMesh + 1] = Mesh() + mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) + self.Tris = self.Tris - 10922 + self.CurTris = 0 + end mesh.Position(Vector(poly[i][1],poly[i][2],0.1)) mesh.TexCoord(0, u, v, u ,v) mesh.Color(255,255,255,255) mesh.AdvanceVertex() + self.CurTris = self.CurTris + 1 end for i = 1,#poly do + if self.CurTris > 10922 then + mesh.End() + self.TreeMesh[#self.TreeMesh + 1] = Mesh() + mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) + self.Tris = self.Tris - 10922 + self.CurTris = 0 + end mesh.Position(Vector(poly[i][1],poly[i][2],0)) mesh.TexCoord(0, u, v, u ,v) mesh.Color(127,127,127,127) mesh.AdvanceVertex() + self.CurTris = self.CurTris + 1 end end @@ -164,7 +182,7 @@ function ENT:DrawSegment(segment) --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] - self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb,self.Ca} self.BitIndex = self.BitIndex+1 end @@ -178,7 +196,7 @@ function ENT:DrawText(text) --surface.DrawText(text.Text) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] - self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb,self.Ca} self.BitIndex = self.BitIndex+1 end @@ -206,7 +224,7 @@ function ENT:DrawMatrix(matrix) --surface.DrawRect(self.LocalX,self.LocalY,matrix.ScaleW,matrix.ScaleH) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] - self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb} + self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb,self.Ca} self.BitIndex = self.BitIndex+1 end end @@ -219,10 +237,12 @@ function ENT:DrawUnion(group) local oCr = self.Cr local oCg = self.Cg local oCb = self.Cb + local oCa = self.Ca if group.HasColor then self.Cr = group.R self.Cg = group.G self.Cb = group.B + self.Ca = group.A or 255 end local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] @@ -247,29 +267,37 @@ function ENT:DrawUnion(group) self.BitIndex = biggestindex self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] - self.Cr = oCr + self.Cr = oCrq self.Cg = oCg self.Cb = oCb + self.Ca = oCa end function ENT:DrawGroup(group) local oCr = self.Cr local oCg = self.Cg local oCb = self.Cb + local oCa = self.Ca if group.HasColor then self.Cr = group.R self.Cg = group.G self.Cb = group.B + self.Ca = group.A or 255 --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,255) end + + local angle = math.rad(group.Rotation or 0) local transformedLocal = self:TransformOffset(group.X or 0,group.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] self.LocalY = self.LocalY + transformedLocal[2] - local angle = math.rad(group.Rotation or 0) self:PushTransform(math.cos(angle), - math.sin(angle)-(group.SkewX or 0), - -math.sin(angle)+(group.SkewY or 0), + math.sin(angle), + -math.sin(angle), math.cos(angle)) + self:PushTransform(1, + -(group.SkewX or 0), + (group.SkewY or 0), + 1) for k,v in ipairs(group.Children) do if v.Type == GROUP then self:DrawGroup(v) @@ -289,6 +317,7 @@ function ENT:DrawGroup(group) self.Cr = oCr self.Cg = oCg self.Cb = oCb + self.Ca = oCa end function ENT:CountTris(node) @@ -299,7 +328,7 @@ function ENT:CountTris(node) end return sum elseif node.Type == MATRIX then - return node.W*node.H*4 + return node.W*node.H*16 end return 12 end @@ -345,17 +374,21 @@ function ENT:Draw() render.SetRenderTarget(NewRT) render.SetViewPort(0, 0, 1024, 1024) cam.Start2D() + render.ClearRenderTarget(self.GPU.RT, Color(0, 0, 0, 0)) surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) surface.DrawRect( 0, 0, 1, 1 ) for i=0,self.BitIndex-1 do - local x = (((i+1)%1024)+0.5) - local y = (math.floor((i+1)/1024)+0.5) - self.Fade[i] = (self.Fade[i] or 0)*0.92 + local x = (i+1)%1024 + local y = math.floor((i+1)/1024) + self.Fade[i] = (self.Fade[i] or 0)*0.92 + 0.01 if bit.band(self.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then - self.Fade[i] = self.Fade[i] + 0.08 + self.Fade[i] = self.Fade[i] + 0.07 end local color = self.Colors[i] - surface.SetDrawColor(color[1],color[2],color[3],self.Fade[i]*255) + surface.SetDrawColor(color[1]*self.Fade[i]+self.Bgred*(1-self.Fade[i]),color[2]*self.Fade[i]+self.Bggreen*(1-self.Fade[i]),color[3]*self.Fade[i]+self.Bgblue*(1-self.Fade[i]),self.Fade[i]*color[4]) + if x == 0 and y == 0 then + break + end surface.DrawRect( x, y, 1, 1 ) end cam.End2D() @@ -379,8 +412,9 @@ function ENT:Draw() --surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) - - self.TreeMesh:Draw() + for i=1,#self.TreeMesh do + self.TreeMesh[i]:Draw() + end cam.PopModelMatrix() cam.PopModelMatrix() @@ -409,10 +443,12 @@ function ENT:Receive() self.Bgblue = net.ReadUInt(8) self.Bggreen = net.ReadUInt(8) self.Bgred = net.ReadUInt(8) + self.XorMask = net.ReadUInt(8) self.Cr = self.Fgred self.Cg = self.Fggreen self.Cb = self.Fgblue + self.Ca = 255 self.LocalXX = 1 self.LocalXY = 0 self.LocalYX = 0 @@ -425,15 +461,24 @@ function ENT:Receive() local w = h/monitor.RatioX self.LocalX = -w/2 self.LocalY = -h/2 - mesh.Begin(self.TreeMesh,MATERIAL_TRIANGLES,self:CountTris(self.Tree)) - - mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() - mesh.Position(self.LocalX+w,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0)mesh.AdvanceVertex() - mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + self.TreeMesh = self.TreeMesh or {} + for i=#self.TreeMesh,1,-1 do + self.TreeMesh[i]:Destroy() + self.TreeMesh[i] = nil + end + self.TreeMesh[#self.TreeMesh + 1] = Mesh() + self.Tris = self:CountTris(self.Tree) + mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) + self.Tris = self.Tris - 10922 + + mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255)mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() - mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() - mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() - mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 0, 0, 0, 0) mesh.AdvanceVertex() + mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + self.CurTris = 6 self:DrawGroup(self.Tree) mesh.End() diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index fde06666d9..4d6c579596 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -61,6 +61,7 @@ function ENT:SendSerializedTree(ply) net.WriteUInt(self.Bgblue,8) net.WriteUInt(self.Bggreen,8) net.WriteUInt(self.Bgred,8) + net.WriteUInt(self.XorMask,8) WireLib.netEnd(ply) end @@ -74,7 +75,7 @@ function ENT:Retransmit(ply) self.Cache:Flush(ply) end -function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Fgred,Fggreen,Fgblue) +function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Fgred,Fggreen,Fgblue,XorMask) self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive) self.ResolutionW = ResolutionW self.ResolutionH = ResolutionH @@ -84,6 +85,7 @@ function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue self.Bgblue = Bgblue or 15 self.Bggreen = Bggreen or 178 self.Bgred = Bgred or 148 + self.XorMask = XorMask or 0 self:Retransmit() end diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index d7e6c91db3..7b50285838 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -31,6 +31,7 @@ if CLIENT then language.Add( "tool.wire_multisegmentlcd.interactive", "Interactive (if available):" ) language.Add( "tool.wire_multisegmentlcd.resw", "Canvas width:" ) language.Add( "tool.wire_multisegmentlcd.resh", "Canvas height:" ) + language.Add( "tool.wire_multisegmentlcd.xormask", "Xor segment order mask:" ) language.Add( "tool.wire_multisegmentlcd.fgcolor", "Segment color:" ) language.Add( "tool.wire_multisegmentlcd.bgcolor", "Background color:" ) TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } @@ -65,7 +66,8 @@ if SERVER then math.Clamp(self:GetClientNumber("bgblue"), 0, 255), math.Clamp(self:GetClientNumber("fgred"), 0, 255), math.Clamp(self:GetClientNumber("fggreen"), 0, 255), - math.Clamp(self:GetClientNumber("fgblue"), 0, 255) + math.Clamp(self:GetClientNumber("fgblue"), 0, 255), + math.Clamp(self:GetClientNumber("xormask"), 0, 255) end util.AddNetworkString("wire_multisegmentlcd_tool_upload_request") @@ -107,7 +109,8 @@ TOOL.ClientConVar = { bgblue = 15, fgred = 45, fggreen = 91, - fgblue = 45 + fgblue = 45, + xormask = 0 } @@ -153,6 +156,7 @@ function TOOL.BuildCPanel(panel) panel:CheckBox("#Create Flat to Surface", "wire_multisegmentlcd_createflat") panel:TextEntry("#tool.wire_multisegmentlcd.resw", "wire_multisegmentlcd_resw") panel:TextEntry("#tool.wire_multisegmentlcd.resh", "wire_multisegmentlcd_resh") + panel:TextEntry("#tool.wire_multisegmentlcd.xormask", "wire_multisegmentlcd_xormask") TreeDataHolder = vgui.Create("DPanel", panel) panel:AddPanel(TreeDataHolder) TreeDataHolder:DockMargin(0, 0, 0, 0) @@ -496,6 +500,16 @@ function TOOL.BuildCPanel(panel) node.group.B = value end + WangColorA = ButtonsHolder:Add( "DNumberWang" ) + WangColorA:SetMax(255) + function WangColorA:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.A = value + end + WangRotation = ButtonsHolder:Add( "DNumberWang" ) WangRotation:SetMax(360) WangRotation:SetMin(-360) @@ -508,6 +522,7 @@ function TOOL.BuildCPanel(panel) end WangSkewX = ButtonsHolder:Add( "DNumberWang" ) + WangSkewX:SetMin(-4096) WangSkewX:SetMax(4096) function WangSkewX:OnValueChanged(value) local node = DisplayData:GetSelectedItem() @@ -517,7 +532,19 @@ function TOOL.BuildCPanel(panel) node.group.SkewX = value end + WangSkewY = ButtonsHolder:Add( "DNumberWang" ) + WangSkewY:SetMin(-4096) + WangSkewY:SetMax(4096) + function WangSkewY:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.SkewY = value + end + WangBevel = ButtonsHolder:Add( "DNumberWang" ) + WangBevel:SetMin(-1024) WangBevel:SetMax(1024) function WangBevel:OnValueChanged(value) local node = DisplayData:GetSelectedItem() @@ -538,18 +565,8 @@ function TOOL.BuildCPanel(panel) node.group.BevelSkew = value end - WangSkewY = ButtonsHolder:Add( "DNumberWang" ) - WangSkewY:SetMax(4096) - function WangSkewY:OnValueChanged(value) - local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil then - return - end - node.group.SkewY = value - end - function ButtonsHolder:PerformLayout(w, h) - local roww = w/7 + local roww = w/8 local rowh = h/4 for i,v in ipairs(self.buttons) do v:SetPos((i-1)*w/#self.buttons,0) @@ -579,6 +596,8 @@ function TOOL.BuildCPanel(panel) WangColorG:SetSize(roww,rowh) WangColorB:SetPos(roww*3,rowh*2) WangColorB:SetSize(roww,rowh) + WangColorA:SetPos(roww*4,rowh*2) + WangColorA:SetSize(roww,rowh) WangRotation:SetPos(roww*4,rowh*2) WangRotation:SetSize(roww,rowh) @@ -601,6 +620,7 @@ function TOOL.BuildCPanel(panel) WangColorR:SetVisible(false) WangColorG:SetVisible(false) WangColorB:SetVisible(false) + WangColorA:SetVisible(false) CheckHasColor:SetVisible(false) CheckLabel:SetVisible(false) WangRotation:SetVisible(false) @@ -620,6 +640,7 @@ function TOOL.BuildCPanel(panel) WangColorR:SetVisible(false) WangColorG:SetVisible(false) WangColorB:SetVisible(false) + WangColorA:SetVisible(false) CheckHasColor:SetVisible(false) CheckLabel:SetVisible(false) WangRotation:SetVisible(false) @@ -660,15 +681,22 @@ function TOOL.BuildCPanel(panel) WangColorR:SetVisible(true) WangColorG:SetVisible(true) WangColorB:SetVisible(true) + WangColorA:SetVisible(true) CheckHasColor:SetVisible(true) CheckLabel:SetVisible(true) WangColorR:SetValue(group.R) WangColorG:SetValue(group.G) WangColorB:SetValue(group.B) + WangColorA:SetValue(group.A) CheckHasColor:SetValue(group.HasColor) WangRotation:SetVisible(true) WangSkewX:SetVisible(true) WangSkewY:SetVisible(true) + WangRotation:SetValue(group.Rotation or 0) + WangSkewX:SetValue(group.SkewX or 0) + WangSkewY:SetValue(group.SkewY or 0) + WangBevel:SetValue(group.Bevel or 0) + WangBevelSkew:SetValue(group.BevelSkew or 0) end WangX:SetValue(group.X) WangY:SetValue(group.Y) From 5f2244ef1f4e1b3ccf13f527841aa1d8b41d7233 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 10 Sep 2025 11:24:08 +0200 Subject: [PATCH 32/50] alpha --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 8 +++++--- lua/entities/gmod_wire_multisegmentlcd/init.lua | 6 +++++- lua/wire/stools/multisegmentlcd.lua | 10 ++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index d944f526de..96ca4071b3 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -375,7 +375,7 @@ function ENT:Draw() render.SetViewPort(0, 0, 1024, 1024) cam.Start2D() render.ClearRenderTarget(self.GPU.RT, Color(0, 0, 0, 0)) - surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) + surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,self.Bgalpha) surface.DrawRect( 0, 0, 1, 1 ) for i=0,self.BitIndex-1 do local x = (i+1)%1024 @@ -385,7 +385,7 @@ function ENT:Draw() self.Fade[i] = self.Fade[i] + 0.07 end local color = self.Colors[i] - surface.SetDrawColor(color[1]*self.Fade[i]+self.Bgred*(1-self.Fade[i]),color[2]*self.Fade[i]+self.Bggreen*(1-self.Fade[i]),color[3]*self.Fade[i]+self.Bgblue*(1-self.Fade[i]),self.Fade[i]*color[4]) + surface.SetDrawColor(color[1]*self.Fade[i]+self.Bgred*(1-self.Fade[i]),color[2]*self.Fade[i]+self.Bggreen*(1-self.Fade[i]),color[3]*self.Fade[i]+self.Bgblue*(1-self.Fade[i]),self.Fade[i]*color[4]+self.Bgalpha*(1-self.Fade[i])) if x == 0 and y == 0 then break end @@ -440,15 +440,17 @@ function ENT:Receive() self.Fgblue = net.ReadUInt(8) self.Fggreen = net.ReadUInt(8) self.Fgred = net.ReadUInt(8) + self.Fgalpha = net.ReadUInt(8) self.Bgblue = net.ReadUInt(8) self.Bggreen = net.ReadUInt(8) self.Bgred = net.ReadUInt(8) + self.Bgalpha = net.ReadUInt(8) self.XorMask = net.ReadUInt(8) self.Cr = self.Fgred self.Cg = self.Fggreen self.Cb = self.Fgblue - self.Ca = 255 + self.Ca = self.Fgalpha self.LocalXX = 1 self.LocalXY = 0 self.LocalYX = 0 diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index 4d6c579596..c0d5ea8d24 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -58,9 +58,11 @@ function ENT:SendSerializedTree(ply) net.WriteUInt(self.Fgblue,8) net.WriteUInt(self.Fggreen,8) net.WriteUInt(self.Fgred,8) + net.WriteUInt(self.Fgalpha,8) net.WriteUInt(self.Bgblue,8) net.WriteUInt(self.Bggreen,8) net.WriteUInt(self.Bgred,8) + net.WriteUInt(self.Bgalpha,8) net.WriteUInt(self.XorMask,8) WireLib.netEnd(ply) end @@ -75,16 +77,18 @@ function ENT:Retransmit(ply) self.Cache:Flush(ply) end -function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Fgred,Fggreen,Fgblue,XorMask) +function ENT:Setup(IsInteractive, ResolutionW, ResolutionH, Bgred,Bggreen,Bgblue,Bgalpha,Fgred,Fggreen,Fgblue,Fgalpha,XorMask) self.IsInteractive = WireLib.IsValidInteractiveModel(self:GetModel()) and (IsInteractive) self.ResolutionW = ResolutionW self.ResolutionH = ResolutionH self.Fgblue = Fgblue or 45 self.Fggreen = Fggreen or 91 self.Fgred = Fgred or 45 + self.Fgalpha = Fgalpha or 255 self.Bgblue = Bgblue or 15 self.Bggreen = Bggreen or 178 self.Bgred = Bgred or 148 + self.Bgalpha = Bgalpha or 255 self.XorMask = XorMask or 0 self:Retransmit() end diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 7b50285838..b5e0ac436d 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -64,9 +64,11 @@ if SERVER then math.Clamp(self:GetClientNumber("bgred"), 0, 255), math.Clamp(self:GetClientNumber("bggreen"), 0, 255), math.Clamp(self:GetClientNumber("bgblue"), 0, 255), + math.Clamp(self:GetClientNumber("bgalpha"), 0, 255), math.Clamp(self:GetClientNumber("fgred"), 0, 255), math.Clamp(self:GetClientNumber("fggreen"), 0, 255), math.Clamp(self:GetClientNumber("fgblue"), 0, 255), + math.Clamp(self:GetClientNumber("fgalpha"), 0, 255), math.Clamp(self:GetClientNumber("xormask"), 0, 255) end @@ -107,9 +109,11 @@ TOOL.ClientConVar = { bgred = 148, bggreen = 178, bgblue = 15, + bgalpha = 255, fgred = 45, fggreen = 91, fgblue = 45, + fgalpha = 255, xormask = 0 } @@ -809,7 +813,8 @@ function TOOL.BuildCPanel(panel) Red = "wire_multisegmentlcd_bgred", Green = "wire_multisegmentlcd_bggreen", Blue = "wire_multisegmentlcd_bgblue", - ShowAlpha = "0", + Alpha = "wire_multisegmentlcd_bgalpha", + ShowAlpha = "1", ShowHSV = "1", ShowRGB = "1", Multiplier = "255" @@ -819,7 +824,8 @@ function TOOL.BuildCPanel(panel) Red = "wire_multisegmentlcd_fgred", Green = "wire_multisegmentlcd_fggreen", Blue = "wire_multisegmentlcd_fgblue", - ShowAlpha = "0", + Alpha = "wire_multisegmentlcd_fgalpha", + ShowAlpha = "1", ShowHSV = "1", ShowRGB = "1", Multiplier = "255" From 2b75bcb464ef908f2af1c564550b942092d73432 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 10 Sep 2025 11:35:11 +0200 Subject: [PATCH 33/50] fix layout --- lua/wire/stools/multisegmentlcd.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index b5e0ac436d..6f0e51663a 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -603,11 +603,11 @@ function TOOL.BuildCPanel(panel) WangColorA:SetPos(roww*4,rowh*2) WangColorA:SetSize(roww,rowh) - WangRotation:SetPos(roww*4,rowh*2) + WangRotation:SetPos(roww*5,rowh*2) WangRotation:SetSize(roww,rowh) - WangSkewX:SetPos(roww*5,rowh*2) + WangSkewX:SetPos(roww*6,rowh*2) WangSkewX:SetSize(roww,rowh) - WangSkewY:SetPos(roww*6,rowh*2) + WangSkewY:SetPos(roww*7,rowh*2) WangSkewY:SetSize(roww,rowh) WangBevel:SetPos(0,rowh*2) From 40941e9f88cf91910df1df3ca98053dee3eda454 Mon Sep 17 00:00:00 2001 From: wav3 Date: Thu, 11 Sep 2025 16:36:08 +0200 Subject: [PATCH 34/50] ungroup --- lua/wire/stools/multisegmentlcd.lua | 250 +++++++++++++++++++++++++--- 1 file changed, 227 insertions(+), 23 deletions(-) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 6f0e51663a..50b7accff9 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -161,6 +161,12 @@ function TOOL.BuildCPanel(panel) panel:TextEntry("#tool.wire_multisegmentlcd.resw", "wire_multisegmentlcd_resw") panel:TextEntry("#tool.wire_multisegmentlcd.resh", "wire_multisegmentlcd_resh") panel:TextEntry("#tool.wire_multisegmentlcd.xormask", "wire_multisegmentlcd_xormask") + PreviewPanel = vgui.Create("DPanel", panel) + panel:AddPanel(PreviewPanel) + PreviewPanel:SetHeight(256) + PreviewPanel:Dock(TOP) + PreviewPanel.Paint = DrawSegmentLCDPreview + TreeDataHolder = vgui.Create("DPanel", panel) panel:AddPanel(TreeDataHolder) TreeDataHolder:DockMargin(0, 0, 0, 0) @@ -334,7 +340,7 @@ function TOOL.BuildCPanel(panel) end local parentgroup = node.parentgroup if parentgroup == nil then - return + parentgroup = WireLib.SegmentLCD_Tree end for i,v in pairs(parentgroup.Children) do if v == node.group then @@ -346,20 +352,7 @@ function TOOL.BuildCPanel(panel) end function Remove:DoClick() local node = DisplayData:GetSelectedItem() - if node == nil then - return - end - local parentgroup = node.parentgroup - if parentgroup == nil then - return - end - for i,v in pairs(parentgroup.Children) do - if v == node.group then - table.remove(parentgroup.Children,i) - node:Remove() - return - end - end + RemoveI(node) end ButtonsHolder.textboxes = {} WangX = ButtonsHolder:Add( "DNumberWang" ) @@ -426,7 +419,7 @@ function TOOL.BuildCPanel(panel) WangScaleW:SetMax(1024) function WangScaleW:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= MATRIX then + if node == nil or node.group == nil then return end node.group.ScaleW = value @@ -436,7 +429,7 @@ function TOOL.BuildCPanel(panel) WangScaleH:SetMax(1024) function WangScaleH:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= MATRIX then + if node == nil or node.group == nil then return end node.group.ScaleH = value @@ -446,7 +439,7 @@ function TOOL.BuildCPanel(panel) WangOffsetX:SetMax(1024) function WangOffsetX:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= MATRIX then + if node == nil or node.group == nil then return end node.group.OffsetX = value @@ -456,7 +449,7 @@ function TOOL.BuildCPanel(panel) WangOffsetY:SetMax(1024) function WangOffsetY:OnValueChanged(value) local node = DisplayData:GetSelectedItem() - if node == nil or node.group == nil or node.group.Type ~= MATRIX then + if node == nil or node.group == nil then return end node.group.OffsetY = value @@ -710,7 +703,7 @@ function TOOL.BuildCPanel(panel) function DisplayData:DoRightClick(node) local Menu = DermaMenu() - Menu:AddOption( "Rename" ) + Menu:AddOption( "Ungroup" ) Menu:AddOption( "Copy" ) Menu:AddOption( "Paste" ) local InsertM, MMOption = Menu:AddSubMenu( "Insert" ) @@ -722,10 +715,52 @@ function TOOL.BuildCPanel(panel) Menu:AddSpacer() Menu:AddOption( "Remove" ) Menu:Open() - print("AAA") function Menu:OptionSelected(option, optionText) - if optionText == "Rename" then - + if optionText == "Ungroup" then + if node.group.Children then + local parentgroup = node.parentgroup + if parentgroup == nil then + parentgroup = WireLib.SegmentLCD_Tree + end + local indexofnode = 0 + for i,v in pairs(parentgroup.Children) do + if v == node.group then + table.remove(parentgroup.Children,i) + indexofnode = i + break + end + end + + + local children = node:GetChildNodes() + local parent = node:GetParent() + local parentnode = node:GetParentNode() + + local root = node:GetRoot() + for i=1,#children do + children[i]:SetParent(parent) + children[i]:SetParentNode(parentnode) + children[i]:SetRoot(root) + --parentnode:Insert(children[i],insertbefore,true) + table.insert(parentgroup.Children,indexofnode+i-1,node.group.Children[i]) + end + + local parentchildren = parentnode:GetChildNodes() + for j=1,#parentgroup.Children do + for i=1,#parentchildren do + if parentgroup.Children[j] == parentchildren[i].group then + parentchildren[i]:SetParent(node) -- required for it to reparent + parentchildren[i]:SetParentNode(parentnode) + parentchildren[i]:SetParent(parent) + parentchildren[i]:SetRoot(root) + break + end + end + end + + + node:Remove() + end elseif optionText == "Copy" then SegmentLCD_Clipboard = table.Copy(node.group) elseif optionText == "Paste" then @@ -831,3 +866,172 @@ function TOOL.BuildCPanel(panel) Multiplier = "255" }) end + +function Transform(self,x,y) + return { + x=x*self.LocalXX+y*self.LocalXY+self.LocalX, + y=x*self.LocalYX+y*self.LocalYY+self.LocalY + } +end + +function TransformOffset(self,x,y) + return { + x*self.LocalXX+y*self.LocalXY, + x*self.LocalYX+y*self.LocalYY + } +end + + +function PushTransform(self,XX,XY,YX,YY) + self.TransformStack[#self.TransformStack + 1] = {self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY} + local oXX = self.LocalXX + local oXY = self.LocalXY + local oYX = self.LocalYX + local oYY = self.LocalYY + + local nXX = oXX*XX + oXY*YX + local nXY = oXY*YY + oXX*XY + local nYX = oYX*XX + oYY*YX + local nYY = oYY*YY + oYX*XY + + self.LocalXX = nXX + self.LocalXY = nXY + self.LocalYX = nYX + self.LocalYY = nYY +end + +function PopTransform(self) + self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY = unpack(self.TransformStack[#self.TransformStack]) + self.TransformStack[#self.TransformStack] = nil +end + +function DrawSegment(self,segment) + local transformedLocal = TransformOffset(self,segment.X or 0,segment.Y or 0) + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] + local angle = math.rad(segment.Rotation or 0) + PushTransform(self,math.cos(angle), + math.sin(angle)-(segment.SkewX or 0), + -math.sin(angle)+(segment.SkewY or 0), + math.cos(angle)) + --self:Transform(,segment.H/2+(segment.H*(segment.BevelSkew or 0))), + local bevel = math.min(segment.H,segment.W)/2*(segment.Bevel or 0) + local rect = { + Transform(self,bevel,segment.H), + Transform(self,0,segment.H-bevel), + Transform(self,0,bevel), + Transform(self,bevel,0), + Transform(self,segment.W-bevel,0), + Transform(self,segment.W,bevel), + Transform(self,segment.W,segment.H-bevel), + Transform(self,segment.W-bevel,segment.H) + } + surface.DrawPoly(rect) + PopTransform(self) + --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] +end + +function DrawText(self,text) + +end + +function DrawMatrix(self,matrix) + +end + +function DrawUnion(self,union) + for k,v in ipairs(union.Children) do + if v.Type == GROUP then + DrawGroup(self,v) + elseif v.Type == UNION then + DrawUnion(self,v) + elseif v.Type == SEGMENT then + DrawSegment(self,v) + elseif v.Type == TEXT then + DrawText(self,v) + elseif v.Type == MATRIX then + DrawMatrix(self,v) + end + end +end + +function DrawGroup(self,group) + local oCr = self.Cr + local oCg = self.Cg + local oCb = self.Cb + local oCa = self.Ca + if group.HasColor then + self.Cr = group.R or 255 + self.Cg = group.G or 255 + self.Cb = group.B or 255 + self.Ca = group.A or 255 + surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Ca) + end + + local angle = math.rad(group.Rotation or 0) + local transformedLocal = TransformOffset(self,group.X or 0,group.Y or 0) + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] + PushTransform(self,math.cos(angle), + math.sin(angle), + -math.sin(angle), + math.cos(angle)) + PushTransform(self,1, + -(group.SkewX or 0), + (group.SkewY or 0), + 1) + for k,v in ipairs(group.Children) do + if v.Type == GROUP then + DrawGroup(self,v) + elseif v.Type == UNION then + DrawUnion(self,v) + elseif v.Type == SEGMENT then + DrawSegment(self,v) + elseif v.Type == TEXT then + DrawText(self,v) + elseif v.Type == MATRIX then + DrawMatrix(self,v) + end + end + PopTransform(self) + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] + self.Cr = oCr + self.Cg = oCg + self.Cb = oCb + self.Ca = oCa +end + +function DrawSegmentLCDPreview(self,width, height) + surface.SetDrawColor(0, 0, 0, 255) + draw.NoTexture() + surface.DrawRect(0, 0, width, height) + + resw = GetConVar( "wire_multisegmentlcd_resw" ):GetInt() + resh = GetConVar( "wire_multisegmentlcd_resh" ):GetInt() + + + local maxres = math.min(width/resw,height/resh) + + self.Cr = 255 + self.Cg = 255 + self.Cb = 255 + self.LocalXX = maxres + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = maxres + self.LocalX = 0 + self.LocalY = 0 + self.BitIndex = 0 + + surface.SetDrawColor(255, 255, 255, 255) + self.TransformStack = {} + + DrawGroup(self,WireLib.SegmentLCD_Tree) +end + +function TOOL:DrawToolScreen(width, height) + DrawSegmentLCDPreview(self,width,height) +end \ No newline at end of file From 8037ddd5bc75d3b97a880b4e6e29e74616379adb Mon Sep 17 00:00:00 2001 From: wav3 Date: Fri, 12 Sep 2025 16:58:42 +0200 Subject: [PATCH 35/50] Fix dupe --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 10 ++++++---- lua/entities/gmod_wire_multisegmentlcd/init.lua | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 96ca4071b3..eab463ff45 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -124,9 +124,9 @@ function ENT:AddPoly(poly) self.Tris = self.Tris - 10922 self.CurTris = 0 end - mesh.Position(Vector(poly[i][1],poly[i][2],0.1)) + mesh.Position(Vector(poly[i][1],poly[i][2],0)) mesh.TexCoord(0, u, v, u ,v) - mesh.Color(255,255,255,255) + mesh.Color(255,255,255,127) mesh.AdvanceVertex() self.CurTris = self.CurTris + 1 end @@ -138,9 +138,9 @@ function ENT:AddPoly(poly) self.Tris = self.Tris - 10922 self.CurTris = 0 end - mesh.Position(Vector(poly[i][1],poly[i][2],0)) + mesh.Position(Vector(poly[i][1],poly[i][2],self.ZOffset)) mesh.TexCoord(0, u, v, u ,v) - mesh.Color(127,127,127,127) + mesh.Color(255,255,255,255) mesh.AdvanceVertex() self.CurTris = self.CurTris + 1 end @@ -461,6 +461,8 @@ function ENT:Receive() local monitor, pos, ang = self.GPU:GetInfo() local h = self.ResolutionH local w = h/monitor.RatioX + + self.ZOffset = monitor.RS*1024/h self.LocalX = -w/2 self.LocalY = -h/2 self.TreeMesh = self.TreeMesh or {} diff --git a/lua/entities/gmod_wire_multisegmentlcd/init.lua b/lua/entities/gmod_wire_multisegmentlcd/init.lua index c0d5ea8d24..9761d0ec6b 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/init.lua @@ -188,4 +188,4 @@ function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) ent:Retransmit() end -duplicator.RegisterEntityClass("gmod_wire_multisegmentlcd", WireLib.MakeWireEnt, "Data", "IsInteractive", "ResolutionW", "ResolutionH", "Bgred", "Bggreen", "Bgblue", "Fgred", "Fggreen", "Fgblue") +duplicator.RegisterEntityClass("gmod_wire_multisegmentlcd", WireLib.MakeWireEnt, "Data", "IsInteractive", "ResolutionW", "ResolutionH", "Bgred", "Bggreen", "Bgblue", "Bgalpha", "Fgred", "Fggreen", "Fgblue", "Fgalpha", "XorMask") From da7e46a51ae44847575d32dc0b59b100cef531cb Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 13 Sep 2025 14:18:57 +0200 Subject: [PATCH 36/50] Fix Z and optimize rendering --- .../gmod_wire_multisegmentlcd/cl_init.lua | 71 +++++++------------ lua/wire/gpulib.lua | 10 ++- 2 files changed, 32 insertions(+), 49 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index eab463ff45..24e5b90292 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -335,73 +335,53 @@ end function ENT:Draw() self:DrawModel() - --[[ - self.GPU:RenderToWorld(nil, self.ResolutionH, function(x, y, w, h) - draw.NoTexture() - surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,255) - surface.DrawRect(x,y,w,h) - if self.Tree then - --render.SetScissorRect(x,y,w,h, true) - surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) - self.Cr = self.Fgred - self.Cg = self.Fggreen - self.Cb = self.Fgblue - self.LocalXX = 1 - self.LocalXY = 0 - self.LocalYX = 0 - self.LocalYY = 1 - self.LocalX = x - self.LocalY = y - self.BitIndex = 0 - - self.TransformStack = {} - self:DrawGroup(self.Tree) - --render.SetScissorRect( 0, 0, 0, 0, false ) - end - end)]] + local self2 = self:GetTable() - if self.Tree then + if self2.Tree then local oldw = ScrW() local oldh = ScrH() - local NewRT = self.GPU.RT + local NewRT = self2.GPU.RT local OldRT = render.GetRenderTarget() render.SetRenderTarget(NewRT) render.SetViewPort(0, 0, 1024, 1024) cam.Start2D() - render.ClearRenderTarget(self.GPU.RT, Color(0, 0, 0, 0)) - surface.SetDrawColor(self.Bgred,self.Bggreen,self.Bgblue,self.Bgalpha) + render.OverrideBlend( true, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD ) + surface.SetDrawColor(self2.Bgred,self2.Bggreen,self2.Bgblue,self2.Bgalpha) surface.DrawRect( 0, 0, 1, 1 ) - for i=0,self.BitIndex-1 do + for i=0,self2.BitIndex-1 do local x = (i+1)%1024 local y = math.floor((i+1)/1024) - self.Fade[i] = (self.Fade[i] or 0)*0.92 + 0.01 - if bit.band(self.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then - self.Fade[i] = self.Fade[i] + 0.07 + self2.Fade[i] = (self2.Fade[i] or 0)*0.92 + 0.01 + if bit.band(self2.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then + self2.Fade[i] = self2.Fade[i] + 0.07 end - local color = self.Colors[i] - surface.SetDrawColor(color[1]*self.Fade[i]+self.Bgred*(1-self.Fade[i]),color[2]*self.Fade[i]+self.Bggreen*(1-self.Fade[i]),color[3]*self.Fade[i]+self.Bgblue*(1-self.Fade[i]),self.Fade[i]*color[4]+self.Bgalpha*(1-self.Fade[i])) - if x == 0 and y == 0 then - break + if self2.Fade[i] > 0.05 and self2.Fade[i] < 0.98 then + local color = self2.Colors[i] + surface.SetDrawColor(color[1]*self2.Fade[i]+self2.Bgred*(1-self2.Fade[i]),color[2]*self2.Fade[i]+self2.Bggreen*(1-self2.Fade[i]),color[3]*self2.Fade[i]+self2.Bgblue*(1-self2.Fade[i]),self2.Fade[i]*color[4]+self2.Bgalpha*(1-self2.Fade[i])*0.15) + if x == 0 and y == 0 then + break + end + surface.DrawRect( x, y, 1, 1 ) end - surface.DrawRect( x, y, 1, 1 ) end + render.OverrideBlend( false ) cam.End2D() render.SetViewPort(0, 0, oldw, oldh) render.SetRenderTarget(OldRT) - local OldTex = WireGPU_matScreen:GetTexture("$basetexture") - WireGPU_matScreen:SetTexture("$basetexture", self.GPU.RT) - render.SetMaterial( WireGPU_matScreen ) + local OldTex = WireGPU_matSegment:GetTexture("$basetexture") + WireGPU_matSegment:SetTexture("$basetexture", self2.GPU.RT) + render.SetMaterial( WireGPU_matSegment ) - local monitor, pos, ang = self.GPU:GetInfo() - local h = self.ResolutionH + local monitor, pos, ang = self2.GPU:GetInfo() + local h = self2.ResolutionH local scale = monitor.RS*1024/h local m = Matrix() m:SetAngles( ang ) @@ -413,17 +393,14 @@ function ENT:Draw() --surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) for i=1,#self.TreeMesh do - self.TreeMesh[i]:Draw() + self2.TreeMesh[i]:Draw() end cam.PopModelMatrix() cam.PopModelMatrix() - WireGPU_matScreen:SetTexture("$basetexture", OldTex) - --render.SetScissorRect( 0, 0, 0, 0, false ) + WireGPU_matSegment:SetTexture("$basetexture", OldTex) end - -- - --self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ResolutionW)/1024,-(1024-self.ResolutionH)/1024) Wire_Render(self) end diff --git a/lua/wire/gpulib.lua b/lua/wire/gpulib.lua index 62d625cf86..1b44e1d2e5 100644 --- a/lua/wire/gpulib.lua +++ b/lua/wire/gpulib.lua @@ -154,17 +154,23 @@ if CLIENT then WireGPU_matScreen = CreateMaterial("sprites/GPURT","UnlitGeneric",{ ["$vertexcolor"] = 1, ["$vertexalpha"] = 1, - ["$translucent"] = 1, + ["$translucent"] = 1, ["$ignorez"] = 1, ["$nolod"] = 1, }) WireGPU_matBuffer = CreateMaterial("sprites/GPUBUF","UnlitGeneric",{ ["$vertexcolor"] = 1, ["$vertexalpha"] = 1, - ["$translucent"] = 1, + ["$translucent"] = 1, ["$ignorez"] = 1, ["$nolod"] = 1, }) + WireGPU_matSegment = CreateMaterial("sprites/SEGRT","UnlitGeneric",{ + ["$vertexcolor"] = 1, + ["$vertexalpha"] = 1, + ["$translucent"] = 1, + ["$nolod"] = 1, + }) function GPU:Initialize(no_rendertarget) From ec6f6db8ecad02ea207f1caa3cac0239017ca462 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 13 Sep 2025 14:21:28 +0200 Subject: [PATCH 37/50] change minimum --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 24e5b90292..bb0ee1b4e9 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -361,7 +361,7 @@ function ENT:Draw() if bit.band(self2.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then self2.Fade[i] = self2.Fade[i] + 0.07 end - if self2.Fade[i] > 0.05 and self2.Fade[i] < 0.98 then + if self2.Fade[i] > 0.15 and self2.Fade[i] < 0.9 then local color = self2.Colors[i] surface.SetDrawColor(color[1]*self2.Fade[i]+self2.Bgred*(1-self2.Fade[i]),color[2]*self2.Fade[i]+self2.Bggreen*(1-self2.Fade[i]),color[3]*self2.Fade[i]+self2.Bgblue*(1-self2.Fade[i]),self2.Fade[i]*color[4]+self2.Bgalpha*(1-self2.Fade[i])*0.15) if x == 0 and y == 0 then From 3060dfe80e1d86a378cb9d208f010f7ff0896b55 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 13 Sep 2025 15:08:09 +0200 Subject: [PATCH 38/50] Optimizations --- .../gmod_wire_multisegmentlcd/cl_init.lua | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index bb0ee1b4e9..a0732f1a01 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -335,6 +335,7 @@ end function ENT:Draw() self:DrawModel() + local self2 = self:GetTable() @@ -350,28 +351,32 @@ function ENT:Draw() render.SetRenderTarget(NewRT) render.SetViewPort(0, 0, 1024, 1024) - cam.Start2D() - render.OverrideBlend( true, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD ) - surface.SetDrawColor(self2.Bgred,self2.Bggreen,self2.Bgblue,self2.Bgalpha) - surface.DrawRect( 0, 0, 1, 1 ) - for i=0,self2.BitIndex-1 do - local x = (i+1)%1024 - local y = math.floor((i+1)/1024) - self2.Fade[i] = (self2.Fade[i] or 0)*0.92 + 0.01 - if bit.band(self2.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then - self2.Fade[i] = self2.Fade[i] + 0.07 - end - if self2.Fade[i] > 0.15 and self2.Fade[i] < 0.9 then - local color = self2.Colors[i] - surface.SetDrawColor(color[1]*self2.Fade[i]+self2.Bgred*(1-self2.Fade[i]),color[2]*self2.Fade[i]+self2.Bggreen*(1-self2.Fade[i]),color[3]*self2.Fade[i]+self2.Bgblue*(1-self2.Fade[i]),self2.Fade[i]*color[4]+self2.Bgalpha*(1-self2.Fade[i])*0.15) - if x == 0 and y == 0 then - break + if self:GetPos():DistToSqr(EyePos()) < 262144 then + local fade = self2.Fade + cam.Start2D() + render.OverrideBlend( true, BLEND_ONE, BLEND_ZERO, BLENDFUNC_ADD ) + surface.SetDrawColor(self2.Bgred,self2.Bggreen,self2.Bgblue,self2.Bgalpha) + surface.DrawRect( 0, 0, 1, 1 ) + for i=0,self2.BitIndex-1 do + local x = (i+1)%1024 + local y = math.floor((i+1)/1024) + fade[i] = (fade[i] or 0)*0.92 + 0.01 + if bit.band(self2.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then + fade[i] = fade[i] + 0.07 + end + + if fade[i] > 0.14 and fade[i] < 0.95 then + local color = self2.Colors[i] + surface.SetDrawColor(color[1]*fade[i]+self2.Bgred*(1-fade[i]),color[2]*fade[i]+self2.Bggreen*(1-fade[i]),color[3]*fade[i]+self2.Bgblue*(1-fade[i]),fade[i]*color[4]+self2.Bgalpha*(1-fade[i])*0.15) + if x == 0 and y == 0 then + break + end + surface.DrawRect( x, y, 1, 1 ) end - surface.DrawRect( x, y, 1, 1 ) end - end - render.OverrideBlend( false ) - cam.End2D() + render.OverrideBlend( false ) + cam.End2D() + end render.SetViewPort(0, 0, oldw, oldh) render.SetRenderTarget(OldRT) From 174155625b8c14884cab27cdae9a525c5a0c2c2b Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 13 Sep 2025 19:07:25 +0200 Subject: [PATCH 39/50] text --- .../gmod_wire_multisegmentlcd/cl_init.lua | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index a0732f1a01..71cf4bc0dd 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -33,6 +33,7 @@ function ENT:Initialize() end) self.TreeMesh = {} + self.Texts = {} WireLib.netRegister(self) end @@ -90,6 +91,15 @@ function ENT:TransformOffset(x,y) } end +function ENT:GetTransformMatrix(x,y) + return Matrix({ + {x*self.LocalXX,y*self.LocalXY,0,self.LocalX}, + {x*self.LocalYX,y*self.LocalYY,0,self.LocalY}, + {0,0,1,0}, + {0,0,0,1} + }) +end + function ENT:PushTransform(XX,XY,YX,YY) self.TransformStack[#self.TransformStack + 1] = {self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY} local oXX = self.LocalXX @@ -186,14 +196,19 @@ function ENT:DrawSegment(segment) self.BitIndex = self.BitIndex+1 end +_TEXT_MATRIX = 0 +_TEXT_TEXT = 1 +_TEXT_BITINDEX = 2 + function ENT:DrawText(text) local transformedLocal = self:TransformOffset(text.X or 0,text.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] self.LocalY = self.LocalY + transformedLocal[2] - --surface.SetTextPos(self.LocalX,self.LocalY) - --surface.SetFont("Default") - --surface.SetTextColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) - --surface.DrawText(text.Text) + self.Texts[#self.Texts+1] = { + [_TEXT_MATRIX] = self:GetTransformMatrix(text.W or 1,text.H or 1), + [_TEXT_TEXT] = text.Text, + [_TEXT_BITINDEX] = self.BitIndex + } self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb,self.Ca} @@ -365,7 +380,7 @@ function ENT:Draw() fade[i] = fade[i] + 0.07 end - if fade[i] > 0.14 and fade[i] < 0.95 then + if fade[i] < 0.1 or fade[i] > 0.14 and fade[i] < 0.95 then local color = self2.Colors[i] surface.SetDrawColor(color[1]*fade[i]+self2.Bgred*(1-fade[i]),color[2]*fade[i]+self2.Bggreen*(1-fade[i]),color[3]*fade[i]+self2.Bgblue*(1-fade[i]),fade[i]*color[4]+self2.Bgalpha*(1-fade[i])*0.15) if x == 0 and y == 0 then @@ -392,16 +407,29 @@ function ENT:Draw() m:SetAngles( ang ) m:SetTranslation( pos ) m:SetScale( Vector( scale, -scale, 1 ) ) - cam.PushModelMatrix( self:GetWorldTransformMatrix() ) + --cam.PushModelMatrix( self:GetWorldTransformMatrix() ) cam.PushModelMatrix( m ) - - --surface.SetDrawColor(self.Fgred,self.Fggreen,self.Fgblue,255) + - for i=1,#self.TreeMesh do + for i=1,#self2.TreeMesh do self2.TreeMesh[i]:Draw() end cam.PopModelMatrix() - cam.PopModelMatrix() + + for i=1,#self2.Texts do + local text = self2.Texts[i] + local newm = text[_TEXT_MATRIX] + + surface.SetFont( "Default" ) + cam.Start3D2D(pos, ang, scale) + cam.PushModelMatrix( newm, true ) + draw.DrawText( text[_TEXT_TEXT], "Default" ) + cam.PopModelMatrix() + cam.End3D2D() + end + + + --cam.PopModelMatrix() WireGPU_matSegment:SetTexture("$basetexture", OldTex) end @@ -448,6 +476,7 @@ function ENT:Receive() self.LocalX = -w/2 self.LocalY = -h/2 self.TreeMesh = self.TreeMesh or {} + self.Texts = {} for i=#self.TreeMesh,1,-1 do self.TreeMesh[i]:Destroy() self.TreeMesh[i] = nil From 56e38df219d8c87311dd2b6310c2516c393c571b Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 14 Sep 2025 14:50:42 +0200 Subject: [PATCH 40/50] Fix segment begin count --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 71cf4bc0dd..f31f1089af 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -127,7 +127,7 @@ function ENT:AddPoly(poly) local u = (((bit.bxor(self.BitIndex,self.XorMask)+1)%1024)+0.5)/1024 local v = (math.floor((self.BitIndex+1)/1024)+0.5)/1024 for i = 1,#poly do - if self.CurTris > 10922 then + if self.CurTris > 10922*3 then mesh.End() self.TreeMesh[#self.TreeMesh + 1] = Mesh() mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) @@ -141,7 +141,7 @@ function ENT:AddPoly(poly) self.CurTris = self.CurTris + 1 end for i = 1,#poly do - if self.CurTris > 10922 then + if self.CurTris > 10922*3 then mesh.End() self.TreeMesh[#self.TreeMesh + 1] = Mesh() mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) @@ -483,6 +483,7 @@ function ENT:Receive() end self.TreeMesh[#self.TreeMesh + 1] = Mesh() self.Tris = self:CountTris(self.Tree) + print(self.Tris) mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) self.Tris = self.Tris - 10922 @@ -493,7 +494,7 @@ function ENT:Receive() mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() - self.CurTris = 6 + self.CurTris = 2 self:DrawGroup(self.Tree) mesh.End() From 9339203ebcfd5fdd2ccafd91c4950810da8a8f10 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 14 Sep 2025 15:37:48 +0200 Subject: [PATCH 41/50] remove print --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index f31f1089af..ff2974120c 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -483,7 +483,6 @@ function ENT:Receive() end self.TreeMesh[#self.TreeMesh + 1] = Mesh() self.Tris = self:CountTris(self.Tree) - print(self.Tris) mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) self.Tris = self.Tris - 10922 From d8cc8088a8c153386fbf6644d67a876542e7fcb7 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 17 Sep 2025 15:46:17 +0200 Subject: [PATCH 42/50] fix segment rendering --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index ff2974120c..340e9bc29b 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -127,7 +127,7 @@ function ENT:AddPoly(poly) local u = (((bit.bxor(self.BitIndex,self.XorMask)+1)%1024)+0.5)/1024 local v = (math.floor((self.BitIndex+1)/1024)+0.5)/1024 for i = 1,#poly do - if self.CurTris > 10922*3 then + if self.CurTris >= 10922*3 then mesh.End() self.TreeMesh[#self.TreeMesh + 1] = Mesh() mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) @@ -141,7 +141,7 @@ function ENT:AddPoly(poly) self.CurTris = self.CurTris + 1 end for i = 1,#poly do - if self.CurTris > 10922*3 then + if self.CurTris >= 10922*3 then mesh.End() self.TreeMesh[#self.TreeMesh + 1] = Mesh() mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) @@ -343,7 +343,7 @@ function ENT:CountTris(node) end return sum elseif node.Type == MATRIX then - return node.W*node.H*16 + return node.W*node.H*4 end return 12 end @@ -493,7 +493,7 @@ function ENT:Receive() mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() - self.CurTris = 2 + self.CurTris = 0 self:DrawGroup(self.Tree) mesh.End() From 04ab2c3aee9323221d06ce52663a54d0fa738857 Mon Sep 17 00:00:00 2001 From: wav3 Date: Wed, 24 Sep 2025 11:01:16 +0200 Subject: [PATCH 43/50] Offset and Align --- .../gmod_wire_multisegmentlcd/cl_init.lua | 12 ++- .../gmod_wire_multisegmentlcd/shared.lua | 2 + lua/wire/stools/multisegmentlcd.lua | 96 ++++++++++++++++++- 3 files changed, 106 insertions(+), 4 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 340e9bc29b..2966659883 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -275,6 +275,10 @@ function ENT:DrawUnion(group) self:DrawText(v) elseif v.Type == MATRIX then self:DrawMatrix(v) + elseif v.Type == ALIGN then + self.BitIndex = math.ceil(self.BitIndex/v.Size)*v.Size + elseif v.Type == OFFSET then + self.BitIndex = self.BitIndex + v.Size end biggestindex = math.max(biggestindex,self.BitIndex) self.BitIndex = savedindex @@ -324,6 +328,10 @@ function ENT:DrawGroup(group) self:DrawText(v) elseif v.Type == MATRIX then self:DrawMatrix(v) + elseif v.Type == ALIGN then + self.BitIndex = math.ceil(self.BitIndex/v.Size)*v.Size + elseif v.Type == OFFSET then + self.BitIndex = self.BitIndex + v.Size end end self:PopTransform() @@ -412,7 +420,9 @@ function ENT:Draw() for i=1,#self2.TreeMesh do - self2.TreeMesh[i]:Draw() + if self2.TreeMesh[i] then + self2.TreeMesh[i]:Draw() + end end cam.PopModelMatrix() diff --git a/lua/entities/gmod_wire_multisegmentlcd/shared.lua b/lua/entities/gmod_wire_multisegmentlcd/shared.lua index 735b21d490..971661f7d0 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/shared.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/shared.lua @@ -12,6 +12,8 @@ ENT.Spawnable = false ENT.RenderGroup = RENDERGROUP_BOTH +OFFSET = -3 +ALIGN = -2 GROUP = -1 UNION = 0 SEGMENT = 1 diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 50b7accff9..943c348326 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -1,6 +1,8 @@ WireToolSetup.setCategory( "Visuals/Screens" ) WireToolSetup.open( "multisegmentlcd", "Multi-segment LCD", "gmod_wire_multisegmentlcd", nil, "Multi-segment LCDs" ) +OFFSET = -3 +ALIGN = -2 GROUP = -1 UNION = 0 SEGMENT = 1 @@ -12,6 +14,8 @@ SegmentTypeNames = { [SEGMENT] = "Segment", [TEXT] = "Text", [MATRIX] = "Matrix", +[ALIGN] = "Align", +[OFFSET] = "Offset", } WireLib.SegmentLCD_Tree = { @@ -130,6 +134,10 @@ function BuildNode(v,node,group) new = node:AddNode( v.Text or "Text", "icon16/bullet_yellow.png" ) elseif v.Type == MATRIX then new = node:AddNode( v.Text or "Matrix", "icon16/bullet_red.png" ) + elseif v.Type == ALIGN then + new = node:AddNode( v.Text or "Text", "icon16/bullet_pink.png" ) + elseif v.Type == OFFSET then + new = node:AddNode( v.Text or "Text", "icon16/bullet_white.png" ) else new = node:AddNode( v.Text or "Segment", "icon16/bullet_green.png" ) end @@ -562,6 +570,61 @@ function TOOL.BuildCPanel(panel) node.group.BevelSkew = value end + WangSize = ButtonsHolder:Add( "DNumberWang" ) + WangSize:SetMax(1024) + WangSize:SetMin(1) + function WangSize:OnValueChanged(value) + local node = DisplayData:GetSelectedItem() + if node == nil or node.group == nil then + return + end + node.group.Size = value + end + + function AddOffsetI(node) + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=OFFSET, Size=1} + children[#children+1] = newgroup + local new = node:AddNode( "Offset", "icon16/bullet_white.png" ) + new.group = newgroup + new.parentgroup = group + end + + function AddAlignI(node) + if node == nil then + node = DisplayData.RootNode + end + local group = node.group + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + node = DisplayData.RootNode + children = WireLib.SegmentLCD_Tree.Children + group = WireLib.SegmentLCD_Tree + end + local newgroup = {Type=ALIGN, Size=8} + children[#children+1] = newgroup + local new = node:AddNode( "Align", "icon16/bullet_pink.png" ) + new.group = newgroup + new.parentgroup = group + end + function ButtonsHolder:PerformLayout(w, h) local roww = w/8 local rowh = h/4 @@ -584,6 +647,9 @@ function TOOL.BuildCPanel(panel) WangOffsetY:SetPos(roww*3,rowh*2) WangOffsetY:SetSize(roww,rowh) + WangSize:SetPos(0,rowh) + WangSize:SetSize(w,rowh) + CheckHasColor:SetPos(1,rowh*2+3) CheckLabel:SetPos(18,rowh*2) CheckLabel:SetSize(roww-18,rowh) @@ -625,6 +691,9 @@ function TOOL.BuildCPanel(panel) WangSkewY:SetVisible(false) WangBevel:SetVisible(false) WangBevelSkew:SetVisible(false) + WangSize:SetVisible(false) + WangX:SetVisible(false) + WangY:SetVisible(false) function DisplayData:DoClick(node) group = node.group @@ -645,6 +714,9 @@ function TOOL.BuildCPanel(panel) WangSkewY:SetVisible(false) WangBevel:SetVisible(false) WangBevelSkew:SetVisible(false) + WangSize:SetVisible(false) + WangX:SetVisible(false) + WangY:SetVisible(false) if group.Type == SEGMENT then WangW:SetValue(group.W) WangH:SetValue(group.H) @@ -655,12 +727,16 @@ function TOOL.BuildCPanel(panel) WangSkewY:SetVisible(true) WangBevel:SetVisible(true) WangBevelSkew:SetVisible(true) - + WangX:SetVisible(true) + WangY:SetVisible(true) + WangRotation:SetValue(group.Rotation or 0) WangSkewX:SetValue(group.SkewX or 0) WangSkewY:SetValue(group.SkewY or 0) WangBevel:SetValue(group.Bevel or 0) WangBevelSkew:SetValue(group.BevelSkew or 0) + WangX:SetValue(group.X) + WangY:SetValue(group.Y) elseif group.Type == MATRIX then WangW:SetValue(group.W) WangH:SetValue(group.H) @@ -670,10 +746,15 @@ function TOOL.BuildCPanel(panel) WangOffsetY:SetVisible(true) WangW:SetVisible(true) WangH:SetVisible(true) + WangX:SetVisible(true) + WangY:SetVisible(true) + WangScaleW:SetValue(group.ScaleW) WangScaleH:SetValue(group.ScaleH) WangOffsetX:SetValue(group.OffsetX) WangOffsetY:SetValue(group.OffsetY) + WangX:SetValue(group.X) + WangY:SetValue(group.Y) elseif group.Type == GROUP or group.Type == UNION then WangColorR:SetVisible(true) WangColorG:SetVisible(true) @@ -681,6 +762,9 @@ function TOOL.BuildCPanel(panel) WangColorA:SetVisible(true) CheckHasColor:SetVisible(true) CheckLabel:SetVisible(true) + WangX:SetVisible(true) + WangY:SetVisible(true) + WangColorR:SetValue(group.R) WangColorG:SetValue(group.G) WangColorB:SetValue(group.B) @@ -694,9 +778,13 @@ function TOOL.BuildCPanel(panel) WangSkewY:SetValue(group.SkewY or 0) WangBevel:SetValue(group.Bevel or 0) WangBevelSkew:SetValue(group.BevelSkew or 0) + WangX:SetValue(group.X) + WangY:SetValue(group.Y) + elseif group.Type == ALIGN or group.Type == OFFSET then + WangSize:SetVisible(true) + WangSize:SetValue(group.Size) end - WangX:SetValue(group.X) - WangY:SetValue(group.Y) + TextSetter:SetValue(group.Text or "") return true end @@ -712,6 +800,8 @@ function TOOL.BuildCPanel(panel) InsertM:AddOption( "Segment", function() AddSegmentI(node) end ) InsertM:AddOption( "Matrix", function() AddMatrixI(node) end ) InsertM:AddOption( "Text", function() AddTextI(node) end ) + InsertM:AddOption( "Align", function() AddAlignI(node) end ) + InsertM:AddOption( "Offset", function() AddOffsetI(node) end ) Menu:AddSpacer() Menu:AddOption( "Remove" ) Menu:Open() From e307d7dc0baa82d124e97a7ecccc3b75265d9777 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 9 May 2026 04:26:09 +0200 Subject: [PATCH 44/50] Fix rendering bugs --- .../gmod_wire_multisegmentlcd/cl_init.lua | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 2966659883..5ce7f6c364 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -20,10 +20,13 @@ function ENT:Initialize() self.Fgblue = 45 self.Fggreen = 91 self.Fgred = 45 + self.Fgalpha = 255 self.Bgblue = 15 self.Bggreen = 178 self.Bgred = 148 - + self.Bgalpha = 255 + self.Colors = {} + self.GPU = WireGPU(self) self.ResolutionW = 1024 self.ResolutionH = 1024 @@ -65,7 +68,9 @@ end function ENT:OnRemove() self.GPU:Finalize() for i=1,#self.TreeMesh do - self.TreeMesh[i]:Destroy() + if self.TreeMesh[i] and self.TreeMesh[i]:IsValid() then + self.TreeMesh[i]:Destroy() + end end end @@ -125,7 +130,7 @@ end function ENT:AddPoly(poly) local u = (((bit.bxor(self.BitIndex,self.XorMask)+1)%1024)+0.5)/1024 - local v = (math.floor((self.BitIndex+1)/1024)+0.5)/1024 + local v = (math.floor((bit.bxor(self.BitIndex,self.XorMask)+1)/1024)+0.5)/1024 for i = 1,#poly do if self.CurTris >= 10922*3 then mesh.End() @@ -388,10 +393,11 @@ function ENT:Draw() fade[i] = fade[i] + 0.07 end - if fade[i] < 0.1 or fade[i] > 0.14 and fade[i] < 0.95 then - local color = self2.Colors[i] + if fade[i] < 0.1 or (fade[i] > 0.14 and fade[i] < 0.95) then + local color = self2.Colors[i] or {self2.Fgred,self2.Fggreen,self2.Fgblue,self2.Fgalpha} surface.SetDrawColor(color[1]*fade[i]+self2.Bgred*(1-fade[i]),color[2]*fade[i]+self2.Bggreen*(1-fade[i]),color[3]*fade[i]+self2.Bgblue*(1-fade[i]),fade[i]*color[4]+self2.Bgalpha*(1-fade[i])*0.15) if x == 0 and y == 0 then + print("x,y = 0,0") break end surface.DrawRect( x, y, 1, 1 ) @@ -420,7 +426,7 @@ function ENT:Draw() for i=1,#self2.TreeMesh do - if self2.TreeMesh[i] then + if self2.TreeMesh[i] and self2.TreeMesh[i]:IsValid() then self2.TreeMesh[i]:Draw() end end @@ -488,23 +494,23 @@ function ENT:Receive() self.TreeMesh = self.TreeMesh or {} self.Texts = {} for i=#self.TreeMesh,1,-1 do - self.TreeMesh[i]:Destroy() + if self.TreeMesh[i] and self.TreeMesh[i]:IsValid() then + self.TreeMesh[i]:Destroy() + end self.TreeMesh[i] = nil end self.TreeMesh[#self.TreeMesh + 1] = Mesh() - self.Tris = self:CountTris(self.Tree) + self.Tris = self:CountTris(self.Tree) + 2 mesh.Begin(self.TreeMesh[#self.TreeMesh],MATERIAL_TRIANGLES,math.min(10922,self.Tris)) self.Tris = self.Tris - 10922 - mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX+w,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() - mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255)mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() + mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX,self.LocalY,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX+w,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() mesh.Position(self.LocalX,self.LocalY+h,0) mesh.Color(255,255,255,255) mesh.TexCoord(0, 1/2048, 1/2048, 1/2048, 1/2048) mesh.AdvanceVertex() - self.CurTris = 0 - + self.CurTris = 6 self:DrawGroup(self.Tree) mesh.End() end From d46cf464e810514ca1d855aaa41c195afa62116a Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 30 May 2026 18:35:57 +0200 Subject: [PATCH 45/50] New editor --- lua/autorun/wire_load.lua | 6 + .../gmod_wire_multisegmentlcd/cl_init.lua | 63 +- .../client/segment_editor/mslcdeditor.lua | 769 +++++ .../segment_editor/mslcdeditor.lua.unused | 2757 +++++++++++++++++ .../segment_editor/wire_mslcd_editor.lua | 1338 ++++++++ lua/wire/client/wire_expression2_browser.lua | 1 + lua/wire/stools/multisegmentlcd.lua | 94 +- 7 files changed, 4987 insertions(+), 41 deletions(-) create mode 100644 lua/wire/client/segment_editor/mslcdeditor.lua create mode 100644 lua/wire/client/segment_editor/mslcdeditor.lua.unused create mode 100644 lua/wire/client/segment_editor/wire_mslcd_editor.lua diff --git a/lua/autorun/wire_load.lua b/lua/autorun/wire_load.lua index 404705a750..b8ade49b5e 100644 --- a/lua/autorun/wire_load.lua +++ b/lua/autorun/wire_load.lua @@ -69,6 +69,10 @@ if SERVER then -- node editor AddCSLuaFile("wire/client/node_editor/nodeeditor.lua") AddCSLuaFile("wire/client/node_editor/wire_fpga_editor.lua") + + -- node editor + AddCSLuaFile("wire/client/segment_editor/mslcdeditor.lua") + AddCSLuaFile("wire/client/segment_editor/wire_mslcd_editor.lua") -- hl-zasm AddCSLuaFile("wire/client/hlzasm/hc_compiler.lua") @@ -150,6 +154,8 @@ if CLIENT then include("wire/client/customspawnmenu.lua") include("wire/client/node_editor/nodeeditor.lua") include("wire/client/node_editor/wire_fpga_editor.lua") + include("wire/client/segment_editor/mslcdeditor.lua") + include("wire/client/segment_editor/wire_mslcd_editor.lua") include("wire/client/hlzasm/hc_compiler.lua") end diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 5ce7f6c364..9a2ff84c52 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -36,7 +36,6 @@ function ENT:Initialize() end) self.TreeMesh = {} - self.Texts = {} WireLib.netRegister(self) end @@ -139,9 +138,9 @@ function ENT:AddPoly(poly) self.Tris = self.Tris - 10922 self.CurTris = 0 end - mesh.Position(Vector(poly[i][1],poly[i][2],0)) + mesh.Position(Vector(poly[i][1],poly[i][2],self.ZOffset)) mesh.TexCoord(0, u, v, u ,v) - mesh.Color(255,255,255,127) + mesh.Color(255,255,255,255) mesh.AdvanceVertex() self.CurTris = self.CurTris + 1 end @@ -153,12 +152,13 @@ function ENT:AddPoly(poly) self.Tris = self.Tris - 10922 self.CurTris = 0 end - mesh.Position(Vector(poly[i][1],poly[i][2],self.ZOffset)) + mesh.Position(Vector(poly[i][1],poly[i][2],0)) mesh.TexCoord(0, u, v, u ,v) - mesh.Color(255,255,255,255) + mesh.Color(255,255,255,127) mesh.AdvanceVertex() self.CurTris = self.CurTris + 1 end + end function ENT:DrawSegment(segment) @@ -201,19 +201,18 @@ function ENT:DrawSegment(segment) self.BitIndex = self.BitIndex+1 end -_TEXT_MATRIX = 0 -_TEXT_TEXT = 1 -_TEXT_BITINDEX = 2 - -function ENT:DrawText(text) - local transformedLocal = self:TransformOffset(text.X or 0,text.Y or 0) +function ENT:DrawPoly(poly) + --surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Fade[self.BitIndex]*255) + local transformedLocal = self:TransformOffset(poly.X or 0,poly.Y or 0) self.LocalX = self.LocalX + transformedLocal[1] self.LocalY = self.LocalY + transformedLocal[2] - self.Texts[#self.Texts+1] = { - [_TEXT_MATRIX] = self:GetTransformMatrix(text.W or 1,text.H or 1), - [_TEXT_TEXT] = text.Text, - [_TEXT_BITINDEX] = self.BitIndex - } + local angle = math.rad(poly.Rotation or 0) + local tp = {} + tp = LoopToTris(poly.Poly) + for i=1,#tp do + tp[i] = self:Transform(tp[i].x,tp[i].y) + end + self:AddPoly(tp) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] self.Colors[self.BitIndex] = {self.Cr,self.Cg,self.Cb,self.Ca} @@ -276,8 +275,8 @@ function ENT:DrawUnion(group) self:DrawUnion(v) elseif v.Type == SEGMENT then self:DrawSegment(v) - elseif v.Type == TEXT then - self:DrawText(v) + elseif v.Type == POLY then + self:DrawPoly(v) elseif v.Type == MATRIX then self:DrawMatrix(v) elseif v.Type == ALIGN then @@ -329,8 +328,8 @@ function ENT:DrawGroup(group) self:DrawUnion(v) elseif v.Type == SEGMENT then self:DrawSegment(v) - elseif v.Type == TEXT then - self:DrawText(v) + elseif v.Type == POLY then + self:DrawPoly(v) elseif v.Type == MATRIX then self:DrawMatrix(v) elseif v.Type == ALIGN then @@ -357,6 +356,9 @@ function ENT:CountTris(node) return sum elseif node.Type == MATRIX then return node.W*node.H*4 + elseif node.Type == POLY then + print(#node.Poly) + return (#node.Poly-2)*3 end return 12 end @@ -386,14 +388,14 @@ function ENT:Draw() surface.SetDrawColor(self2.Bgred,self2.Bggreen,self2.Bgblue,self2.Bgalpha) surface.DrawRect( 0, 0, 1, 1 ) for i=0,self2.BitIndex-1 do - local x = (i+1)%1024 - local y = math.floor((i+1)/1024) + local x = (bit.bxor(i,self.XorMask)+1)%1024 + local y = math.floor((bit.bxor(i,self.XorMask)+1)/1024) fade[i] = (fade[i] or 0)*0.92 + 0.01 if bit.band(self2.Memory[bit.rshift(i,3)] or 0,bit.lshift(1,bit.band(i,7))) ~= 0 then fade[i] = fade[i] + 0.07 end - if fade[i] < 0.1 or (fade[i] > 0.14 and fade[i] < 0.95) then + --if fade[i] < 0.1 or (fade[i] > 0.14 and fade[i] < 0.95) then local color = self2.Colors[i] or {self2.Fgred,self2.Fggreen,self2.Fgblue,self2.Fgalpha} surface.SetDrawColor(color[1]*fade[i]+self2.Bgred*(1-fade[i]),color[2]*fade[i]+self2.Bggreen*(1-fade[i]),color[3]*fade[i]+self2.Bgblue*(1-fade[i]),fade[i]*color[4]+self2.Bgalpha*(1-fade[i])*0.15) if x == 0 and y == 0 then @@ -401,7 +403,7 @@ function ENT:Draw() break end surface.DrawRect( x, y, 1, 1 ) - end + --end end render.OverrideBlend( false ) cam.End2D() @@ -432,18 +434,6 @@ function ENT:Draw() end cam.PopModelMatrix() - for i=1,#self2.Texts do - local text = self2.Texts[i] - local newm = text[_TEXT_MATRIX] - - surface.SetFont( "Default" ) - cam.Start3D2D(pos, ang, scale) - cam.PushModelMatrix( newm, true ) - draw.DrawText( text[_TEXT_TEXT], "Default" ) - cam.PopModelMatrix() - cam.End3D2D() - end - --cam.PopModelMatrix() @@ -492,7 +482,6 @@ function ENT:Receive() self.LocalX = -w/2 self.LocalY = -h/2 self.TreeMesh = self.TreeMesh or {} - self.Texts = {} for i=#self.TreeMesh,1,-1 do if self.TreeMesh[i] and self.TreeMesh[i]:IsValid() then self.TreeMesh[i]:Destroy() diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua b/lua/wire/client/segment_editor/mslcdeditor.lua new file mode 100644 index 0000000000..b124bdfbaf --- /dev/null +++ b/lua/wire/client/segment_editor/mslcdeditor.lua @@ -0,0 +1,769 @@ +local Editor = {} + +OFFSET = -3 +ALIGN = -2 +GROUP = -1 +UNION = 0 +SEGMENT = 1 +TEXT = 2 +POLY = 2 +MATRIX = 3 +SegmentTypeNames = { +[GROUP] = "Group", +[UNION] = "Union", +[SEGMENT] = "Segment", +[POLY] = "Poly", +[MATRIX] = "Matrix", +[ALIGN] = "Align", +[OFFSET] = "Offset", +} + +function Editor:Init() + self.SegmentTree = { + Type=GROUP, + X=0, + Y=0, + Children= + { + + } + } + + self.DraggingWorld = false + self.DraggingNode = nil + self.DraggingOffset = { 0, 0 } + self.DraggingPolyVert = nil + + self.SelectedSegments = nil + self.SelectedSegment = nil + self.SelectedVert = nil + + self.LastMousePos = { 0, 0 } + self.MouseDown = false + + self.GateSize = FPGANodeSize + self.GridSize = self.GateSize * 2 + self.GridEnabled = true + + self.IOSize = 2 + + self.BackgroundColor = Color(40, 40, 40, 255) + self.GridColor = Color(50, 50, 50, 255) + self.SelectionColor = Color(220, 220, 100, 255) + + self.ZoomHideThreshold = 2 + self.ZoomThreshold = 7 + + self.LastFrameTime = SysTime() + + self.Mode = SEGMENT +end + +function Editor:SetMode(mode) + self.Mode = mode +end + +function Transform(self,x,y) + return x*self.LocalXX+y*self.LocalXY+self.LocalX, x*self.LocalYX+y*self.LocalYY+self.LocalY +end + +function TransformOffset(self,x,y) + return { + x*self.LocalXX+y*self.LocalXY, + x*self.LocalYX+y*self.LocalYY + } +end + + +function PushTransform(self,XX,XY,YX,YY) + self.TransformStack[#self.TransformStack + 1] = {self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY} + local oXX = self.LocalXX + local oXY = self.LocalXY + local oYX = self.LocalYX + local oYY = self.LocalYY + + local nXX = oXX*XX + oXY*YX + local nXY = oXY*YY + oXX*XY + local nYX = oYX*XX + oYY*YX + local nYY = oYY*YY + oYX*XY + + self.LocalXX = nXX + self.LocalXY = nXY + self.LocalYX = nYX + self.LocalYY = nYY +end + +function PopTransform(self) + self.LocalXX,self.LocalXY,self.LocalYX,self.LocalYY = unpack(self.TransformStack[#self.TransformStack]) + self.TransformStack[#self.TransformStack] = nil +end + +function PolyDimensions(self,poly,tlocal) + self.LocalX = self.LocalX + tlocal[1] + self.LocalY = self.LocalY + tlocal[2] + local minx, miny = Transform(self, poly[1].x, poly[1].y) + local maxx, maxy = minx, miny + for i, v in ipairs(poly) do + x, y = Transform(self, v.x, v.y) + minx, miny = math.min(minx, x), math.min(miny, y) + maxx, maxy = math.max(maxx, x), math.max(maxy, y) + end + self.LocalX = self.LocalX - tlocal[1] + self.LocalY = self.LocalY - tlocal[2] + return minx, miny, maxx, maxy +end + + +function DrawSegment(self,segment) + local transformedLocal = TransformOffset(self,segment.X or 0,segment.Y or 0) + local angle = math.rad(segment.Rotation or 0) + PushTransform(self,math.cos(angle), + math.sin(angle)-(segment.SkewX or 0), + -math.sin(angle)+(segment.SkewY or 0), + math.cos(angle)) + --self:Transform(,segment.H/2+(segment.H*(segment.BevelSkew or 0))), + local bevel = math.min(segment.H,segment.W)/2*(segment.Bevel or 0) + + local rect = { + {x=bevel,y=segment.H}, + {x=0,y=segment.H-bevel}, + {x=0,y=bevel}, + {x=bevel,y=0}, + {x=segment.W-bevel,y=0}, + {x=segment.W,y=bevel}, + {x=segment.W,y=segment.H-bevel}, + {x=segment.W-bevel,y=segment.H} + } + local x, y = self:LocalToScreen(0,0) + local m = Matrix() + m:Translate(Vector(x,y,0)) + m:Mul(Matrix({ + {self.LocalXX,self.LocalXY,0,self.LocalX + transformedLocal[1]}, + {self.LocalYX,self.LocalYY,0,self.LocalY + transformedLocal[2]}, + {0,0,1,0}, + {0,0,0,1} + })) + m:Translate(Vector(-x,-y,0)) + cam.PushModelMatrix(m) + surface.DrawPoly(rect) + cam.PopModelMatrix() + PopTransform(self) + --surface.DrawRect(self.LocalX,self.LocalY,segment.W,segment.H) + + return PolyDimensions(self,rect,transformedLocal) +end + + +function LoopToTris(poly) + poly = table.Copy(poly) + if #poly == 3 then + return poly + end + local tries = 0 + local tris = {} + local i = 0 + while #poly > 3 do + tries = tries + 1 + i = i%#poly+1 + local a = poly[i] + local b = poly[i%#poly+1] + local c = poly[(i+1)%#poly+1] + local lax = a.x-b.x + local lay = a.y-b.y + local la = a.y*lax-a.x*lay + + local lbx = b.x-c.x + local lby = b.y-c.y + local lb = b.y*lbx-b.x*lby + + local lcx = c.x-a.x + local lcy = c.y-a.y + local lc = c.y*lcx-c.x*lcy + + if (c.y*lax - c.x*lay) > la then + goto fail + end + + + for j,p in ipairs(poly) do + if j == i or j == (i%#poly+1) or j == ((i+1)%#poly+1) then + goto skip + end + local lpa = p.y*lax - p.x*lay + local lpb = p.y*lbx - p.x*lby + local lpc = p.y*lcx - p.x*lcy + if lpa < la and lpb < lb and lpc < lc then + goto fail + end + ::skip:: + end + + tris[#tris+1] = a + tris[#tris+1] = b + tris[#tris+1] = c + table.remove(poly,i%#poly+1) + tries = 0 + --i = (i-2)%#poly+1 + --i = 0 + + ::fail:: + if tries > #poly then + break + end + end + + tris[#tris+1] = poly[1] + tris[#tris+1] = poly[2] + tris[#tris+1] = poly[3] + return tris +end + + +function DrawPoly(self,poly) + local selected = poly == self.SelectedSegment + local transformedLocal = TransformOffset(self,poly.X or 0,poly.Y or 0) + + local x, y = self:LocalToScreen(0,0) + local m = Matrix() + m:Translate(Vector(x,y,0)) + m:Mul(Matrix({ + {self.LocalXX,self.LocalXY,0,self.LocalX + transformedLocal[1] - self.LocalXX/2}, + {self.LocalYX,self.LocalYY,0,self.LocalY + transformedLocal[2] - self.LocalYY/2}, + {0,0,1,0}, + {0,0,0,1} + })) + m:Translate(Vector(-x,-y,0)) + cam.PushModelMatrix(m) + --surface.DrawPoly(poly.Poly) + if selected then + surface.SetDrawColor(255,192,192,255) + else + surface.SetDrawColor(255,255,255,255) + end + + for i,p in ipairs(poly.Poly) do + local op = poly.Poly[i%#poly.Poly+1] + surface.DrawLine(p.x,p.y,op.x,op.y) + end + + surface.SetDrawColor(255,255,0,255) + for i,p in ipairs(poly.Poly) do + local selectedvert = i == self.SelectedVert and selected + local m = Matrix() + m:Translate(Vector(x+p.x+0.5,y+p.y+0.5,0)) + m:Scale(Vector(1/self.Zoom,1/self.Zoom,0)) + m:Translate(Vector(-x,-y,0)) + cam.PushModelMatrix(m, true) + if selectedvert then + surface.SetDrawColor(255,0,0,255) + surface.DrawRect(-4,-4,8,8) + surface.SetDrawColor(255,255,0,255) + else + surface.DrawRect(-4,-4,8,8) + end + cam.PopModelMatrix() + end + if selected then + surface.SetDrawColor(0,255,255,255) + else + surface.SetDrawColor(0,255,0,255) + end + m = Matrix() + m:Translate(Vector(x-4.0/self.Zoom,y-4.0/self.Zoom,0)) + m:Scale(Vector(1/self.Zoom,1/self.Zoom,0)) + m:Translate(Vector(-x,-y,0)) + cam.PushModelMatrix(m, true) + surface.DrawRect(0,0,8,8) + cam.PopModelMatrix() + surface.SetDrawColor(255,255,255,255) + cam.PopModelMatrix() + + return PolyDimensions(self,poly.Poly,transformedLocal) +end + +function DrawMatrix(self,matrix) + +end + +function DrawUnion(self,union) + for k,v in ipairs(union.Children) do + if v.Type == GROUP then + DrawGroup(self,v) + elseif v.Type == UNION then + DrawUnion(self,v) + elseif v.Type == SEGMENT then + DrawSegment(self,v) + elseif v.Type == POLY then + DrawPoly(self,v) + elseif v.Type == MATRIX then + DrawMatrix(self,v) + end + end +end + +function DrawGroup(self,group) + if #group.Children == 0 then + return + end + local oCr = self.Cr + local oCg = self.Cg + local oCb = self.Cb + local oCa = self.Ca + if group.HasColor then + self.Cr = group.R or 255 + self.Cg = group.G or 255 + self.Cb = group.B or 255 + self.Ca = group.A or 255 + surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Ca) + end + + local angle = math.rad(group.Rotation or 0) + local transformedLocal = TransformOffset(self,group.X or 0,group.Y or 0) + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] + PushTransform(self,math.cos(angle), + math.sin(angle), + -math.sin(angle), + math.cos(angle)) + PushTransform(self,1, + -(group.SkewX or 0), + (group.SkewY or 0), + 1) + local minx, miny = Transform(self, group.Children[1].X, group.Children[1].Y) + local maxx, maxy = minx, miny + + for k,v in ipairs(group.Children) do + local nminx, nminy, nmaxx, nmaxy + if v.Type == GROUP then + nminx, nminy, nmaxx, nmaxy = DrawGroup(self,v) + elseif v.Type == UNION then + nminx, nminy, nmaxx, nmaxy = DrawGroup(self,v) + elseif v.Type == SEGMENT then + nminx, nminy, nmaxx, nmaxy = DrawSegment(self,v) + elseif v.Type == POLY then + nminx, nminy, nmaxx, nmaxy = DrawPoly(self,v) + elseif v.Type == MATRIX then + DrawMatrix(self,v) + end + minx, miny = math.min(nminx, minx), math.min(nminy, miny) + maxx, maxy = math.max(nmaxx, maxx), math.max(nmaxy, maxy) + end + --m:Translate(Vector(x+p.x-4.0/self.Zoom,y+p.y-4.0/self.Zoom,0)) + --m:Scale(Vector(1/self.Zoom,1/self.Zoom,0)) + --m:Translate(Vector(-x,-y,0)) + local x, y = self:LocalToScreen(0,0) + local m = Matrix() + m:Translate(Vector(x,y,0)) + m:Mul(Matrix({ + {self.LocalXX/self.Zoom,self.LocalXY/self.Zoom,0,self.LocalX + transformedLocal[1]}, + {self.LocalYX/self.Zoom,self.LocalYY/self.Zoom,0,self.LocalY + transformedLocal[2]}, + {0,0,1,0}, + {0,0,0,1} + })) + m:Translate(Vector(-x,-y,0)) + --cam.PushModelMatrix(m,true) + surface.DrawOutlinedRect(minx,miny,(maxx-minx),(maxy-miny)) + --cam.PopModelMatrix() + PopTransform(self) + self.LocalX = self.LocalX - transformedLocal[1] + self.LocalY = self.LocalY - transformedLocal[2] + self.Cr = oCr + self.Cg = oCg + self.Cb = oCb + self.Ca = oCa + + return minx,miny,maxx,maxy +end + +function Editor:Paint() + local width = self:GetWide() + local height = self:GetTall() + local snapincrement = GetConVar("wire_multisegmentlcd_snapinc"):GetFloat() + -- Update animation frame time + self.LastFrameTime = SysTime() + + surface.SetDrawColor(self.BackgroundColor) + draw.NoTexture() + surface.DrawRect(0, 0, width, height) + + -- detects if mouse is let go outside of the window + if not input.IsMouseDown(MOUSE_RIGHT) then + self.DraggingWorld = nil + end + if not input.IsMouseDown(MOUSE_LEFT) then + self.DraggingNode = nil + self.DrawingConnection = nil + self.DrawingSelection = nil + end + + local x, y = self:CursorPos() + local dx, dy = self.LastMousePos[1] - x, self.LastMousePos[2] - y + -- moving the plane + if self.DraggingWorld then + self.Position = { self.Position[1] + dx * (1 / self.Zoom), self.Position[2] + dy * (1 / self.Zoom) } + end + + + if self.DraggingPolyVert then + local wx, wy = self:ScrToPos(x, y) + if self.DraggingPolyVert[2] == 0 then + local poly = self.DraggingPolyVert[1] + poly.X = wx + poly.Y = wy + if snapincrement > 0.001 then + poly.X = math.floor(poly.X/snapincrement + 0.5)*snapincrement + poly.Y = math.floor(poly.Y/snapincrement + 0.5)*snapincrement + end + else + local vert = self.DraggingPolyVert[1].Poly[self.DraggingPolyVert[2]] + vert.x = wx-self.DraggingPolyVert[1].X + vert.y = wy-self.DraggingPolyVert[1].Y + if snapincrement > 0.001 then + vert.x = math.floor(vert.x/snapincrement + 0.5)*snapincrement + vert.y = math.floor(vert.y/snapincrement + 0.5)*snapincrement + end + end + end + + self:PaintGrid() + + + self.Cr = 255 + self.Cg = 255 + self.Cb = 255 + self.Ca = 255 + self.LocalXX = self.Zoom + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = self.Zoom + self.LocalX = self:GetWide() / 2 - self.Position[1]*self.Zoom + self.LocalY = self:GetTall() / 2 - self.Position[2]*self.Zoom + self.BitIndex = 0 + + surface.SetDrawColor(255, 255, 255, 255) + self.TransformStack = {} + DisableClipping(true) + self.SegmentTree.X = 0 + self.SegmentTree.Y = 0 + self.minx,self.miny,self.maxx,self.maxy = DrawGroup(self,self.SegmentTree) + self.SegmentTree.X = -((self.minx or 0)-self.LocalX)/self.Zoom + self.SegmentTree.Y = -((self.miny or 0)-self.LocalY)/self.Zoom + DisableClipping(false) + local x, y = self:CursorPos() + self.LastMousePos = { x, y } + + if self.SelectedSegment then + self.ParentPanel.C.Prop_X:SetValue(self.SelectedSegment.X) + self.ParentPanel.C.Prop_Y:SetValue(self.SelectedSegment.Y) + if self.SelectedVert ~= 0 then + self.ParentPanel.C.Vert_X:SetValue(self.SelectedSegment.Poly[self.SelectedVert].x) + self.ParentPanel.C.Vert_Y:SetValue(self.SelectedSegment.Poly[self.SelectedVert].y) + end + end +end + +function Editor:SetData(data) + local ok, data = pcall(WireLib.von.deserialize, data) + if not ok then + self:ClearData() + self.C.Name:SetValue("corrupt") + return + end + + if data.Position then self.Position = data.Position else self.Position = { 0, 0 } end + if data.Zoom then self.Zoom = data.Zoom else self.Zoom = 5 end + if data.SegmentTree then self.SegmentTree = data.SegmentTree end +end + +function Editor:GetData() + return WireLib.von.serialize({ + SegmentTree = self.SegmentTree, + Position = self.Position, + Zoom = self.Zoom + }, false) +end + +function Editor:ClearData() + self.Position = { 0, 0 } + self.Zoom = 5 + self.SegmentTree = { + Type=GROUP, + X=0, + Y=0, + Children= + { + + } + } +end + +function Editor:PaintGrid() + if not self.GridEnabled then return end + + local gridSize = self.GridSize * self.Zoom + if gridSize < 5 then return end + + local screenW = self:GetWide() + local screenH = self:GetTall() + + local startX = math.floor((self.Position[1] - screenW / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local endX = math.ceil((self.Position[1] + screenW / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local startY = math.floor((self.Position[2] - screenH / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local endY = math.ceil((self.Position[2] + screenH / (2 * self.Zoom)) / self.GridSize) * self.GridSize + + surface.SetDrawColor(self.GridColor) + + for x = startX, endX, self.GridSize do + local sx, sy1 = self:PosToScr(x, startY) + local _, sy2 = self:PosToScr(x, endY) + surface.DrawLine(sx, sy1, sx, sy2) + end + + for y = startY, endY, self.GridSize do + local sx1, sy = self:PosToScr(startX, y) + local sx2, _ = self:PosToScr(endX, y) + surface.DrawLine(sx1, sy, sx2, sy) + end +end + +-- EDITING + +function Editor:CreateSegment(x, y) + local group = nil + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + children = self.SegmentTree.Children + group = self.SegmentTree + end + local n = {Type=SEGMENT, X=x,Y=y,W=70,H=70,Bevel = 0.1} + children[#children+1] = n +end + +function Editor:CreatePoly(x, y) + local group = nil + local children = nil + if group ~= nil then + children = group.Children + end + + if children == nil then + children = self.SegmentTree.Children + group = self.SegmentTree + end + local n = {Type=POLY, X=x,Y=y, Poly={{x=0,y=0},{x=10,y=0},{x=0,y=10}}} + children[#children+1] = n +end + +-- KEYBOARD + +function Editor:OnKeyCodePressed(code) + local x, y = self:CursorPos() + local gx, gy = self:ScrToPos(x, y) + local snapincrement = GetConVar("wire_multisegmentlcd_snapinc"):GetFloat() + if snapincrement > 0.001 then + gx = math.floor(gx/snapincrement + 0.5)*snapincrement + gy = math.floor(gy/snapincrement + 0.5)*snapincrement + end + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + local shift = input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) + if control then + if code == KEY_C then + self.Clipboard = table.Copy(self.SelectedSegment) + elseif code == KEY_V then + if self.Clipboard ~= nil then + self.Clipboard.X = gx + self.Clipboard.Y = gy + self.SegmentTree.Children[#self.SegmentTree.Children+1] = table.Copy(self.Clipboard) + end + end + elseif code == KEY_C then + --Create + if self.Mode == SEGMENT then + self:CreateSegment(gx, gy) + elseif self.Mode == POLY then + self:CreatePoly(gx, gy) + end + end +end + +-- MOUSE + +function Editor:OnMouseWheeled(delta) + local sx, sy = self:CursorPos() + + self.Zoom = self.Zoom + delta * 0.1 * self.Zoom + if self.Zoom < 0.1 then self.Zoom = 0.1 end + if self.Zoom > 100 then self.Zoom = 100 end +end + + +function Editor:GetPolyEdgeAtPoly(x, y, poly) + --local x,y = x/self.Zoom, y/self.Zoom + for i,v in ipairs(poly.Poly) do + local o = poly.Poly[i%#poly.Poly+1] + local lx = o.x-v.x + local ly = o.y-v.y + local d = math.sqrt(lx*lx+ly*ly) + local ls = (v.x*ly - v.y*lx)/d + local lf = (v.x*lx + v.y*ly)/d + local f = (x*lx + y*ly)/d - lf + local s = (x*ly - y*lx)/d - ls + if f > 0 and f < d and s >= -25/self.Zoom and s <= 25/self.Zoom then + return poly, i + end + end + return nil, nil +end + +function Editor:GetPolyEdgeAtGroup(x, y, group) + for i,v in ipairs(group.Children) do + if v.Type == GROUP then + ri, rv, ex, ey = self:GetPolyEdgeAtGroup(x-v.X, y-v.Y, v) + if ri then + return ri, rv, ex, ey + end + elseif v.Type == POLY then + ri, rv = self:GetPolyEdgeAtPoly(x-v.X, y-v.Y, v) + if ri then + return ri, rv, x-v.X, y-v.Y + end + end + + end + return nil, nil +end + +function Editor:GetPolyEdgeAt(x, y) + return self:GetPolyEdgeAtGroup(x, y, self.SegmentTree) +end + + +function Editor:GetPolyVertAtPoly(x, y, poly) + local x,y = x*self.Zoom, y*self.Zoom + for i,v in ipairs(poly.Poly) do + local vx = v.x*self.Zoom + local vy = v.y*self.Zoom + if math.abs(vx-x) < 4 and math.abs(vy-y) < 4 then + return poly, i + end + + end + if math.abs(x) < 4 and math.abs(y) < 4 then + return poly, 0 + end + return nil, nil +end + +function Editor:GetPolyVertAtGroup(x, y, group) + for i,v in ipairs(group.Children) do + if v.Type == GROUP then + ri, rv, g, gi = self:GetPolyVertAtGroup(x-v.X, y-v.Y, v) + if ri then + return ri, rv, g, gi + end + elseif v.Type == POLY then + ri, rv = self:GetPolyVertAtPoly(x-v.X, y-v.Y, v) + if ri then + return ri, rv, group, i + end + end + + end + return nil, nil, nil, nil +end + +function Editor:GetPolyVertAt(x, y) + return self:GetPolyVertAtGroup(x, y, self.SegmentTree) +end + + + + + +-- MOUSE + +function Editor:OnMousePressed(code) + self:RequestFocus() --Fix for weird bug, remove once resolved + + if code == MOUSE_LEFT then + self.MouseDown = true + + --double click detection + local doubleClick + if self.LastClick then + doubleClick = SysTime() - self.LastClick < 0.3 + else doubleClick = false end + self.LastClick = SysTime() + + local x, y = self:ScrToPos(self:CursorPos()) + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + + local pvKey, pvIndex, pvGroup, pvGroupIndex = self:GetPolyVertAt(x, y) + + if pvKey then + if control then + if pvIndex ~= 0 then + table.remove(pvKey.Poly,pvIndex) + if #pvKey.Poly < 3 then + table.remove(pvGroup.Children,pvGroupIndex) + end + end + else + self.DraggingPolyVert = {pvKey,pvIndex} + end + elseif not control then + local peKey, peIndex, ex, ey = self:GetPolyEdgeAt(x, y) + + if peKey then + table.insert(peKey.Poly,peIndex+1,{x=ex,y=ey}) + self.DraggingPolyVert = {peKey,peIndex+1} + end + end + self.SelectedSegment = nil + self.SelectedVert = nil + if self.DraggingPolyVert then + self.SelectedSegment = self.DraggingPolyVert[1] + self.SelectedVert = self.DraggingPolyVert[2] + end + elseif code == MOUSE_RIGHT then + -- PLANE DRAGGING + self.DraggingWorld = true + end +end + +function Editor:OnMouseReleased(code) + local x, y = self:CursorPos() + + if code == MOUSE_LEFT then + self.MouseDown = false + self.DraggingNode = nil + self.DraggingPolyVert = nil + elseif code == MOUSE_RIGHT then + self.DraggingWorld = false + end + +end + +-- UTILITY + +function Editor:PosToScr(x, y) + return (self:GetWide()) / 2 - (self.Position[1] - x) * self.Zoom, self:GetTall() / 2 - (self.Position[2] - y) * self.Zoom +end + +function Editor:ScrToPos(x, y) + return self.Position[1] - ((self:GetWide()) / 2 - x) / self.Zoom, self.Position[2] - (self:GetTall() / 2 - y) / self.Zoom +end + +function Editor:AlignPosToGrid(x, y) + return math.Round(x / self.GateSize) * self.GateSize, math.Round(y / self.GateSize) * self.GateSize +end + +function Editor:DragHoverClick(hoverTime) + print(hoverTime) +end + +vgui.Register("MSLCDEditor", Editor, "Panel"); diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua.unused b/lua/wire/client/segment_editor/mslcdeditor.lua.unused new file mode 100644 index 0000000000..05fdfc3624 --- /dev/null +++ b/lua/wire/client/segment_editor/mslcdeditor.lua.unused @@ -0,0 +1,2757 @@ +local Editor = {} + +FPGATypeColor = { + NORMAL = Color(190, 190, 255, 255), --Very light blue nearing white + VECTOR2 = Color(150, 255, 255, 255), --Light blue + VECTOR = Color(70, 160, 255, 255), --Blue + VECTOR4 = Color(0, 50, 255, 255), --Dark blue + ANGLE = Color(100, 200, 100, 255), --Light green + STRING = Color(250, 160, 90, 255), --Orange + ARRAY = Color(20, 110, 20, 255), --Dark green + ENTITY = Color(255, 100, 100, 255), --Dark red + RANGER = Color(130, 100, 60, 255), --Brown + WIRELINK = Color(200, 80, 200, 255), --Deep purple +} + +--GATE HELPERS +local function getGate(node) + if node.type == "wire" then + return GateActions[node.gate] + elseif node.type == "fpga" then + return FPGAGateActions[node.gate] + elseif node.type == "cpu" then + return CPUGateActions[node.gate] + end +end + +local function getInputType(gate, inputNum) + if gate.inputtypes then + return gate.inputtypes[inputNum] or "NORMAL" + else + return "NORMAL" + end +end + +local function getOutputType(gate, outputNum) + if gate.outputtypes then + return gate.outputtypes[outputNum] or "NORMAL" + else + return "NORMAL" + end +end + +function Editor:Init() + self.Nodes = {} + + self.AlignToGrid = false + + self.DraggingWorld = false + self.DraggingNode = nil + self.DraggingOffset = { 0, 0 } + + self.DraggingWaypoint = nil + self.HoveringWaypoint = nil + + self.DrawingConnection = false + self.DrawingFromInput = false + self.DrawingFromOutput = false + self.DrawingConnectionFrom = nil + + self.DrawingSelection = nil + self.SelectedNodes = {} + self.SelectedNodeCount = 0 + + self.SelectedWaypoints = {} -- Format: {connectionKey_waypointIndex = true} + self.SelectedWaypointCount = 0 + + self.LastMousePos = { 0, 0 } + self.MouseDown = false + + self.SelectedInMenu = nil + + self.GateSize = FPGANodeSize + self.GridSize = self.GateSize * 2 + self.GridEnabled = true + + self.IOSize = 2 + + self.BackgroundColor = Color(40, 40, 40, 255) + self.GridColor = Color(50, 50, 50, 255) + self.SelectionColor = Color(220, 220, 100, 255) + self.WaypointColor = Color(255, 198, 109, 255) + self.WaypointSelectedColor = Color(0, 122, 204, 255) + self.WaypointHoverColor = Color(255, 198, 109, 200) + + self.NodeColor = Color(100, 100, 100, 255) + self.InputNodeColor = Color(80, 90, 80, 255) + self.OutputNodeColor = Color(80, 80, 90, 255) + self.TimedNodeColor = Color(110, 70, 70, 255) + self.SelectedNodeColor = Color(0, 122, 204, 255) + + self.NodeOutlineColor = Color(80, 80, 80, 255) -- Subtle outline + self.NodeShadowColor = Color(0, 0, 0, 100) + + self.ZoomHideThreshold = 2 + self.ZoomThreshold = 7 + + self.UndoStack = {} + self.RedoStack = {} + self.MaxUndoSteps = 50 + self.UndoEnabled = true + + self.MinimapConVar = CreateClientConVar("wire_fpga_editor_minimap", "1", true, false) + self.MinimapSize = 200 + self.MinimapPadding = 10 + self.MinimapAlpha = 200 + self.MinimapConnectionAlpha = 50 + self.MinimapBackgroundColor = Color(20, 20, 20, self.MinimapAlpha) + self.MinimapBorderColor = Color(60, 60, 60, self.MinimapAlpha) + + self.Animations = {} + self.LastFrameTime = SysTime() + + self.C = {} + self:InitComponents() +end + +function Editor:GetParent() + return self.ParentPanel +end + +surface.CreateFont( "FPGAText", { + font = "Tahoma", + extended = false, + size = 16, + weight = 500, + blursize = 0, + scanlines = 0, + antialias = true, + underline = false, + italic = false, + strikeout = false, + symbol = false, + rotary = false, + shadow = false, + additive = false, + outline = false, +}) +surface.CreateFont( "FPGAIO", { + font = "Tahoma", + extended = false, + size = 12, + weight = 500, + blursize = 0, + scanlines = 0, + antialias = true, + underline = false, + italic = false, + strikeout = false, + symbol = false, + rotary = false, + shadow = false, + additive = false, + outline = false, +}) +surface.CreateFont( "FPGATextBig", { + font = "Tahoma", + extended = false, + size = 20, + weight = 1000, + blursize = 0, + scanlines = 0, + antialias = true, + underline = false, + italic = false, + strikeout = false, + symbol = false, + rotary = false, + shadow = false, + additive = false, + outline = false, +}) +surface.CreateFont( "FPGAIOBig", { + font = "Tahoma", + extended = false, + size = 18, + weight = 200, + blursize = 0, + scanlines = 0, + antialias = true, + underline = false, + italic = false, + strikeout = false, + symbol = false, + rotary = false, + shadow = false, + additive = false, + outline = false, +}) +surface.CreateFont( "FPGALabel", { + font = "Bahnschrift", + extended = false, + size = 25, + weight = 1000, + blursize = 0, + scanlines = 0, + antialias = true, + underline = false, + italic = true, + strikeout = false, + symbol = false, + rotary = false, + shadow = false, + additive = false, + outline = false, +}) + +-------------------------------------------------------- +--COMPONENTS +-------------------------------------------------------- +function Editor:InitComponents() + local this = self + + self.C = {} + + self.C.TopBar = vgui.Create("DPanel", self) + self.C.TopBar:Dock(TOP) + self.C.TopBar:SetHeight(36) + self.C.TopBar:DockPadding(5, 18, 5, 4) + self.C.TopBar:SetBackgroundColor(Color(176.5, 180, 185, 255)) + + local x = 7 + self.C.NameLabel = vgui.Create("DLabel", self.C.TopBar) + self.C.NameLabel:SetText("Chip Name") + self.C.NameLabel:SizeToContents() + self.C.NameLabel:SetTextColor(Color(255, 255, 255, 255)) + self.C.NameLabel:SetPos(x, 4) + self.C.Name = vgui.Create("DTextEntry", self.C.TopBar) + self.C.Name:SetEditable(true) + self.C.Name:SetSize(140, 15) + self.C.Name:SetPos(x - 2, 18) + + self.C.Name.OnLoseFocus = function (pnl) + if string.len(pnl:GetValue()) == 0 then + pnl:SetText("gate") + end + this:RequestFocus() + end + x = x + 160 + + self.C.ExecutionIntervalLabel = vgui.Create("DLabel", self.C.TopBar) + self.C.ExecutionIntervalLabel:SetText("Execution Interval") + self.C.ExecutionIntervalLabel:SizeToContents() + self.C.ExecutionIntervalLabel:SetTextColor(Color(255, 255, 255, 255)) + self.C.ExecutionIntervalLabel:SetPos(x, 4) + self.C.ExecutionIntervalLabel2 = vgui.Create("DLabel", self.C.TopBar) + self.C.ExecutionIntervalLabel2:SetText("every s") + self.C.ExecutionIntervalLabel2:SizeToContents() + self.C.ExecutionIntervalLabel2:SetTextColor(Color(255, 255, 255, 255)) + self.C.ExecutionIntervalLabel2:SetPos(x, 18) + self.C.ExecutionInterval = vgui.Create("DNumberWang", self.C.TopBar) + self.C.ExecutionInterval:SetInterval(0.01) + self.C.ExecutionInterval:SetDecimals(3) + self.C.ExecutionInterval:SetMax(1) + self.C.ExecutionInterval:SetMin(0.001) + self.C.ExecutionInterval:SetValue(0.1) + self.C.ExecutionInterval:SetSize(40, 15) + self.C.ExecutionInterval:SetPos(x + 31, 18) + + self.C.ExecutionInterval.OnLoseFocus = function (pnl) + this:RequestFocus() + end + x = x + 110 + + self.C.ExecuteOnLabel = vgui.Create("DLabel", self.C.TopBar) + self.C.ExecuteOnLabel:SetText("Execute on") + self.C.ExecuteOnLabel:SizeToContents() + self.C.ExecuteOnLabel:SetTextColor(Color(255, 255, 255, 255)) + self.C.ExecuteOnLabel:SetPos(x, 4) + self.C.ExecuteOnInputs = vgui.Create("DCheckBoxLabel", self.C.TopBar) + self.C.ExecuteOnInputs:SetPos(x, 18) + self.C.ExecuteOnInputs:SetText("Inputs") + self.C.ExecuteOnInputs:SetTextColor(Color(240, 240, 240, 255)) + self.C.ExecuteOnInputs:SetValue(true) + self.C.ExecuteOnInputs:SizeToContents() + self.C.ExecuteOnTimed = vgui.Create("DCheckBoxLabel", self.C.TopBar) + self.C.ExecuteOnTimed:SetPos(x + 60, 18) + self.C.ExecuteOnTimed:SetText("Timed") + self.C.ExecuteOnTimed:SetTextColor(Color(240, 240, 240, 255)) + self.C.ExecuteOnTimed:SetValue(true) + self.C.ExecuteOnTimed:SizeToContents() + self.C.ExecuteOnTrigger = vgui.Create("DCheckBoxLabel", self.C.TopBar) + self.C.ExecuteOnTrigger:SetPos(x + 120, 18) + self.C.ExecuteOnTrigger:SetText("Trigger In") + self.C.ExecuteOnTrigger:SetTextColor(Color(240, 240, 240, 255)) + self.C.ExecuteOnTrigger:SetValue(false) + self.C.ExecuteOnTrigger:SizeToContents() + + --Gate spawning + self.C.Holder = vgui.Create("DPanel", self) + self.C.Holder:SetWidth(300) + self.C.Holder:Dock(RIGHT) + self.C.Holder:SetBackgroundColor(Color(170, 174, 179, 255)) + + self.C.Tree = vgui.Create("DTree", self.C.Holder) + self.C.Tree:Dock(FILL) + self.C.Tree:DockMargin(2, 0, 2, 2) + + + --Gate searching + self.C.Search = vgui.Create("DTextEntry", self.C.Holder) + self.C.Search:Dock(TOP) + self.C.Search:DockMargin(2, 0, 2, 0) + self.C.Search:SetValue("Search...") + + local oldOnGetFocus = self.C.Search.OnGetFocus + function self.C.Search:OnGetFocus() + if self:GetValue() == "Search..." then -- If "Search...", erase it + self:SetValue("") + end + oldOnGetFocus(self) + end + + -- On lose focus + local oldOnLoseFocus = self.C.Search.OnLoseFocus + function self.C.Search:OnLoseFocus() + if self:GetValue() == "" then -- if empty, reset "Search..." text + timer.Simple(0, function() self:SetValue("Search...") end) + end + oldOnLoseFocus(self) + this:RequestFocus() + end + + self.C.SearchList = vgui.Create("DListView", self.C.Holder) + self.C.SearchList:AddColumn("Gate Name") + self.C.SearchList:AddColumn("Type"):SetWidth(10) + self.C.SearchList:AddColumn("Category"):SetWidth(35) + + -- Searching algorithm + local function Search(text) + text = string.lower(text) + + local results = {} + for action, gate in pairs(FPGAGateActions) do + local name = gate.name + local lowname = string.lower(name) + if string.find(lowname, text, 1, true) then -- If it has ANY match at all + results[#results + 1] = { name = gate.name, group = gate.group, type = "fpga", action = action, dist = WireLib.levenshtein(text, lowname) } + end + end + for action, gate in pairs(CPUGateActions) do + local name = gate.name + local lowname = string.lower(name) + if string.find(lowname, text, 1, true) then -- If it has ANY match at all + results[#results + 1] = { name = gate.name, group = gate.group, type = "cpu", action = action, dist = WireLib.levenshtein(text, lowname) } + end + end + for action, gate in pairs(GateActions) do + local name = gate.name + local lowname = string.lower(name) + if string.find(lowname, text, 1, true) then -- If it has ANY match at all + results[#results + 1] = { name = gate.name, group = gate.group, type = "wire", action = action, dist = WireLib.levenshtein(text, lowname) } + end + end + + table.SortByMember(results, "dist", true) + + return results + end + + -- Main searching + local searching + function self.C.Search:OnTextChanged() + local text = self:GetValue() + if text ~= "" then + if not searching then + searching = true + local x, y = this.C.Tree:GetPos() + local w, h = this.C.Tree:GetSize() + this.C.SearchList:SetPos(x + w, y) + this.C.SearchList:MoveTo(x, y, 0.1, 0, 1) + this.C.SearchList:SetSize(w, h) + this.C.SearchList:SetVisible(true) + end + local results = Search(text) + this.C.SearchList:Clear() + for i = 1, #results do + local result = results[i] + + local type + if result.type == "wire" then type = "Wire" + elseif result.type == "fpga" then type = "FPGA" + elseif result.type == "cpu" then type = "CPU" + end + local line = this.C.SearchList:AddLine(result.name, type, result.group) + + if this.SelectedInMenu then + if this.SelectedInMenu.type == result.type and this.SelectedInMenu.gate == result.action then + line:SetSelected(true) + end + end + + line.action = result.action + line.type = result.type + end + else + if searching then + searching = false + local x, y = this.C.Tree:GetPos() + local w, h = this.C.Tree:GetSize() + this.C.SearchList:SetPos(x, y) + this.C.SearchList:MoveTo(x + w, y, 0.1, 0, 1) + this.C.SearchList:SetSize(w, h) + timer.Create("fpga_customspawnmenu_hidesearchlist", 0.1, 1, function() + if IsValid(this.C.SearchList) then + this.C.SearchList:SetVisible(false) + end + end) + end + this.C.SearchList:Clear() + end + end + + function self.C.SearchList:OnClickLine(line) + -- Deselect old + local t = self:GetSelected() + if t and next(t) then + t[1]:SetSelected(false) + end + + line:SetSelected(true) -- Select new + this.SelectedInMenu = { type = line.type, gate = line.action } + this:RequestFocus() + end + + function self.C.Search:OnEnter() + if #this.C.SearchList:GetLines() > 0 then + this.C.SearchList:OnClickLine(this.C.SearchList:GetLine(1)) + end + this:RequestFocus() + end + + -- Set sizes & other settings + self.C.SearchList:SetVisible(false) + self.C.SearchList:SetMultiSelect(false) + + --utility + local function FillSubTree(editor, tree, node, temp, type, sortByName) + node.Icon:SetImage("icon16/folder.png") + + local subtree = {} + for k, v in pairs(temp) do + subtree[#subtree + 1] = { action = k, gate = v, name = v.name, order = v.order } + end + + if sortByName then + table.SortByMember(subtree, "name", true) + else + table.SortByMember(subtree, "order", true) + end + + for index = 1, #subtree do + local action, gate = subtree[index].action, subtree[index].gate + local node2 = node:AddNode(gate.name or "No name found :(") + node2.name = gate.name + node2.action = action + if gate.description then + node2:SetTooltip(gate.description) + end + function node2:DoClick() + editor.SelectedInMenu = { type = type, gate = self.action } + end + node2.Icon:SetImage("icon16/newspaper.png") + end + tree:InvalidateLayout() + end + + local function addGates(editor, gates, name, key, icon) + local CategoriesSorted = {} + + for gatetype, gatefuncs in pairs(gates) do + local allowed_gates = {} + local any_allowed = false + for k, v in pairs(gatefuncs) do + if not v.is_banned then + allowed_gates[k] = v + any_allowed = true + end + end + if any_allowed then + CategoriesSorted[#CategoriesSorted + 1] = { gatetype = gatetype, gatefuncs = allowed_gates } + end + end + + table.sort(CategoriesSorted, function(a, b) return a.gatetype < b.gatetype end) + + local parentNode = self.C.Tree:AddNode(name, icon) + function parentNode:DoClick() + self:SetExpanded(not self.m_bExpanded) + end + + for i = 1, #CategoriesSorted do + local gatetype = CategoriesSorted[i].gatetype + local gatefuncs = CategoriesSorted[i].gatefuncs + + local node = parentNode:AddNode(gatetype) + node.Icon:SetImage("icon16/folder.png") + FillSubTree(self, self.C.Tree, node, gatefuncs, key, name == "Wire") + function node:DoClick() + self:SetExpanded(not self.m_bExpanded) + end + end + end + + --EDITOR extras + local labelNode = self.C.Tree:AddNode("Label", "icon16/text_allcaps.png") + function labelNode:DoClick() + this.SelectedInMenu = { type = "editor", visual = "label", gate = nil } + end + local commentNode = self.C.Tree:AddNode("Comment", "icon16/comment.png") + function commentNode:DoClick() + this.SelectedInMenu = { type = "editor", visual = "comment", gate = nil } + end + + --FPGA gates + addGates(self, FPGAGatesSorted, "FPGA", "fpga", "icon16/bricks.png") + + --CPU gates + addGates(self, CPUGatesSorted, "CPU", "cpu", "icon16/computer.png") + + --WIREMOD gates + addGates(self, WireGatesSorted, "Wire", "wire", "icon16/connect.png") +end + + +-------------------------------------------------------- +--INTERACTION +-------------------------------------------------------- +function Editor:GetData() + return WireLib.von.serialize({ + Name = self.C.Name:GetValue(), + Nodes = self.Nodes, + Position = self.Position, + Zoom = self.Zoom, + ExecutionInterval = self.C.ExecutionInterval:GetValue(), + ExecuteOn = { + Inputs = self.C.ExecuteOnInputs:GetChecked(), + Timed = self.C.ExecuteOnTimed:GetChecked(), + Trigger = self.C.ExecuteOnTrigger:GetChecked() + } + }, false) +end + +function Editor:SetData(data) + local ok, data = pcall(WireLib.von.deserialize, data) + if not ok then + self:ClearData() + self.C.Name:SetValue("corrupt") + return + end + + if data.Nodes then self.Nodes = data.Nodes else self.Nodes = {} end + + if data.Name then self.C.Name:SetValue(data.Name) else self.C.Name:SetValue("gate") end + + if data.ExecutionInterval then + self.C.ExecutionInterval:SetValue(data.ExecutionInterval) + else + self.C.ExecutionInterval:SetValue(0.01) + end + + if data.ExecuteOn then + self.C.ExecuteOnInputs:SetValue(data.ExecuteOn.Inputs) + self.C.ExecuteOnTimed:SetValue(data.ExecuteOn.Timed) + self.C.ExecuteOnTrigger:SetValue(data.ExecuteOn.Trigger) + else + self.C.ExecuteOnInputs:SetValue(true) + self.C.ExecuteOnTimed:SetValue(true) + self.C.ExecuteOnTrigger:SetValue(false) + end + + if data.Position then self.Position = data.Position else self.Position = { 0, 0 } end + if data.Zoom then self.Zoom = data.Zoom else self.Zoom = 5 end + + self.InputNameCounter = 0 + self.OutputNameCounter = 0 + for nodeId, node in pairs(self.Nodes) do + local gate = getGate(node) + if not node.visual then + if not gate then self:DeleteNode(nodeId) + elseif gate.isInput then self.InputNameCounter = self.InputNameCounter + 1 + elseif gate.isOutput then self.OutputNameCounter = self.OutputNameCounter + 1 end + end + end +end + +function Editor:ClearData() + self.C.Name:SetValue("gate") + self.Nodes = {} + self.Position = { 0, 0 } + self.Zoom = 5 + self.InputNameCounter = 0 + self.OutputNameCounter = 0 +end + +function Editor:GetName() + return self.C.Name:GetValue() +end + +function Editor:HasNodes() + return next(self.Nodes) ~= nil +end + +-------------------------------------------------------- +--NODE INFO +-------------------------------------------------------- +--EDITOR NODE +function Editor:GetVisual(node) + if node.type == "editor" then + if node.visual == "label" then + return { method = "text", font = "FPGALabel", default = "Label" } + elseif node.visual == "comment" then + return { method = "text", font = "auto", default = "Comment" } + end + end + return nil +end + +--GATES (further up) + +-------------------------------------------------------- +--UTILITY +-------------------------------------------------------- +function Editor:PosToScr(x, y) + return (self:GetWide() - 300) / 2 - (self.Position[1] - x) * self.Zoom, self:GetTall() / 2 - (self.Position[2] - y) * self.Zoom +end + +function Editor:ScrToPos(x, y) + return self.Position[1] - ((self:GetWide() - 300) / 2 - x) / self.Zoom, self.Position[2] - (self:GetTall() / 2 - y) / self.Zoom +end + +function Editor:AlignPosToGrid(x, y) + return math.Round(x / self.GateSize) * self.GateSize, math.Round(y / self.GateSize) * self.GateSize +end + +function Editor:NodeInputPos(node, input) + return node.x - self.GateSize / 2 - self.IOSize / 2, node.y + (input - 1) * self.GateSize +end + +function Editor:NodeOutputPos(node, output) + return node.x + self.GateSize / 2 + self.IOSize / 2, node.y + (output - 1) * self.GateSize +end + +function Editor:GetConnectionKey(nodeId, inputNum) + return tostring(nodeId) .. "_" .. tostring(inputNum) +end + +function Editor:GetWaypointSelectionKey(connectionKey, waypointIndex) + return connectionKey .. "_wp" .. tostring(waypointIndex) +end + +function Editor:DrawBezierCurve(x1, y1, x2, y2, color, segments, flipStart, flipEnd) + segments = segments or 20 + + -- Calculate control points for smooth curve + local distance = math.sqrt((x2 - x1)^2 + (y2 - y1)^2) + local offsetX = math.min(distance * 0.5, 100) + + local cx1 = x1 + (flipStart and -offsetX or offsetX) + local cy1 = y1 + local cx2 = x2 - (flipEnd and -offsetX or offsetX) + local cy2 = y2 + + surface.SetDrawColor(color) + + local prevX, prevY = x1, y1 + for i = 1, segments do + local t = i / segments + local it = 1 - t + + -- Cubic Bezier formula + local x = it^3 * x1 + 3 * it^2 * t * cx1 + 3 * it * t^2 * cx2 + t^3 * x2 + local y = it^3 * y1 + 3 * it^2 * t * cy1 + 3 * it * t^2 * cy2 + t^3 * y2 + + surface.DrawLine(prevX, prevY, x, y) + prevX, prevY = x, y + end +end + +function Editor:DrawCircle(x, y, radius, segments) + segments = segments or 16 + local circle = {} + + for i = 0, segments do + local angle = (i / segments) * math.pi * 2 + table.insert(circle, { + x = x + math.cos(angle) * radius, + y = y + math.sin(angle) * radius + }) + end + + draw.NoTexture() + surface.DrawPoly(circle) +end + +function getInputAmountForNode(node) + local gate = getGate(node) + local amountOfInputs = 0 + if gate.compact_inputs then + inputLimit = gate.compact_inputs + for inputIdx, _ in pairs(node.connections) do + inputLimit = math.max(inputLimit, inputIdx + 1) + end + amountOfInputs = math.min(#gate.inputs, inputLimit) + else + amountOfInputs = #gate.inputs + end + return amountOfInputs +end + +-------------------------------------------------------- +--UNDO/REDO SYSTEM +-------------------------------------------------------- +function Editor:SaveState(description) + if not self.UndoEnabled then return end + + -- Deep copy current state + local state = { + Nodes = table.Copy(self.Nodes), + Position = {self.Position[1], self.Position[2]}, + Zoom = self.Zoom, + InputNameCounter = self.InputNameCounter, + OutputNameCounter = self.OutputNameCounter, + Description = description or "Unknown Action" + } + + -- Deep copy connections with waypoints + for nodeId, node in pairs(state.Nodes) do + if node.connections then + local newConnections = {} + for inputNum, connection in pairs(node.connections) do + newConnections[inputNum] = { + connection[1], + connection[2], + waypoints = connection.waypoints and table.Copy(connection.waypoints) or nil + } + end + state.Nodes[nodeId].connections = newConnections + end + end + + table.insert(self.UndoStack, state) + + -- Limit undo stack size + while #self.UndoStack > self.MaxUndoSteps do + table.remove(self.UndoStack, 1) + end + + -- Clear redo stack when new action is performed + self.RedoStack = {} +end + +function Editor:Undo() + if #self.UndoStack == 0 then return end + + -- Save current state to redo stack + local currentState = { + Nodes = table.Copy(self.Nodes), + Position = {self.Position[1], self.Position[2]}, + Zoom = self.Zoom, + InputNameCounter = self.InputNameCounter, + OutputNameCounter = self.OutputNameCounter + } + + -- Deep copy connections with waypoints for redo + for nodeId, node in pairs(currentState.Nodes) do + if node.connections then + local newConnections = {} + for inputNum, connection in pairs(node.connections) do + newConnections[inputNum] = { + connection[1], + connection[2], + waypoints = connection.waypoints and table.Copy(connection.waypoints) or nil + } + end + currentState.Nodes[nodeId].connections = newConnections + end + end + + table.insert(self.RedoStack, currentState) + + -- Restore previous state + local state = table.remove(self.UndoStack) + self:RestoreState(state) +end + +function Editor:Redo() + if #self.RedoStack == 0 then return end + + -- Save current state to undo stack + local currentState = { + Nodes = table.Copy(self.Nodes), + Position = {self.Position[1], self.Position[2]}, + Zoom = self.Zoom, + InputNameCounter = self.InputNameCounter, + OutputNameCounter = self.OutputNameCounter + } + + -- Deep copy connections with waypoints for undo + for nodeId, node in pairs(currentState.Nodes) do + if node.connections then + local newConnections = {} + for inputNum, connection in pairs(node.connections) do + newConnections[inputNum] = { + connection[1], + connection[2], + waypoints = connection.waypoints and table.Copy(connection.waypoints) or nil + } + end + currentState.Nodes[nodeId].connections = newConnections + end + end + + table.insert(self.UndoStack, currentState) + + -- Restore next state + local state = table.remove(self.RedoStack) + self:RestoreState(state) +end + +function Editor:RestoreState(state) + self.Nodes = table.Copy(state.Nodes) + self.Position = {state.Position[1], state.Position[2]} + self.Zoom = state.Zoom + self.InputNameCounter = state.InputNameCounter or 0 + self.OutputNameCounter = state.OutputNameCounter or 0 + + -- Deep copy connections with waypoints + for nodeId, node in pairs(self.Nodes) do + if node.connections then + local newConnections = {} + for inputNum, connection in pairs(node.connections) do + newConnections[inputNum] = { + connection[1], + connection[2], + waypoints = connection.waypoints and table.Copy(connection.waypoints) or nil + } + end + self.Nodes[nodeId].connections = newConnections + end + end + + -- Clear selections + self.SelectedNodes = {} + self.SelectedNodeCount = 0 +end + +-------------------------------------------------------- +--DETECTION +-------------------------------------------------------- +function Editor:GetNodeAt(x, y) + local gx, gy = self:ScrToPos(x, y) + + for k, node in pairs(self.Nodes) do + local gate = getGate(node) + + if gate then + --gates + local amountOfInputs = 0 + if gate.inputs then + amountOfInputs = getInputAmountForNode(node) + end + local amountOfOutputs = 1 + if gate.outputs then + amountOfOutputs = #gate.outputs + end + + local height = math.max(amountOfInputs, amountOfOutputs) + + if gx < node.x - self.GateSize / 2 then continue end + if gx > node.x + self.GateSize / 2 then continue end + if gy < node.y - self.GateSize / 2 then continue end + if gy > node.y - self.GateSize / 2 + self.GateSize * height then continue end + end + + local visual = self:GetVisual(node) + if visual then + --editor nodes + + if visual.method == "text" then + if visual.font == "auto" then + if (self.Zoom > self.ZoomThreshold) then + surface.SetFont("FPGATextBig") + elseif (self.Zoom <= self.ZoomHideThreshold) then + continue + else + surface.SetFont("FPGAText") + end + else + surface.SetFont(visual.font) + end + local tx, ty = surface.GetTextSize(node.value) + + if gx < node.x - (tx / 2) / self.Zoom then continue end + if gx > node.x + (tx / 2) / self.Zoom then continue end + if gy < node.y - (ty / 2) / self.Zoom then continue end + if gy > node.y + (ty / 2) / self.Zoom then continue end + else + continue + end + end + + return k + end + + return nil +end + +function Editor:GetNodeInputAt(x, y) + local gx, gy = self:ScrToPos(x, y) + + for k, node in pairs(self.Nodes) do + local gate = getGate(node) + + if not gate then continue end + + local amountOfInputs = getInputAmountForNode(node) + + if gx < node.x - self.GateSize / 2 - self.IOSize then continue end + if gx > node.x + self.GateSize / 2 + self.IOSize then continue end + if gy < node.y - self.GateSize / 2 then continue end + if gy > node.y - self.GateSize / 2 + self.GateSize * amountOfInputs then continue end + + for inputNum = 1, amountOfInputs do + local ix, iy = self:NodeInputPos(node, inputNum) + + if gx < ix - self.IOSize / 2 then continue end + if gx > ix + self.IOSize / 2 then continue end + if gy < iy - self.IOSize / 2 then continue end + if gy > iy + self.IOSize / 2 then continue end + + return k, inputNum + end + end + + return nil +end + +function Editor:GetNodeOutputAt(x, y) + local gx, gy = self:ScrToPos(x, y) + + for k, node in pairs(self.Nodes) do + local gate = getGate(node) + + if not gate then continue end + + if gx < node.x - self.GateSize / 2 - self.IOSize then continue end + if gx > node.x + self.GateSize / 2 + self.IOSize then continue end + if gy < node.y - self.GateSize / 2 then continue end + if gate.outputs then + if gy > node.y - self.GateSize / 2 + self.GateSize * #gate.outputs then continue end + else + if gy > node.y + self.GateSize / 2 then continue end + end + + if gate.outputs then + for outputNum, _ in pairs(gate.outputs) do + local ix, iy = self:NodeOutputPos(node, outputNum) + + if gx < ix - self.IOSize / 2 then continue end + if gx > ix + self.IOSize / 2 then continue end + if gy < iy - self.IOSize / 2 then continue end + if gy > iy + self.IOSize / 2 then continue end + + return k, outputNum + end + else + local ix, iy = self:NodeOutputPos(node, 1) + + if gx < ix - self.IOSize / 2 then continue end + if gx > ix + self.IOSize / 2 then continue end + if gy < iy - self.IOSize / 2 then continue end + if gy > iy + self.IOSize / 2 then continue end + + return k, 1 + end + end + + return nil +end + +function Editor:GetWaypointAt(x, y) + local radius = self.Zoom * self.IOSize / 2 + + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local waypoints = connectedTo.waypoints + if waypoints then + for i, wp in ipairs(waypoints) do + local sx, sy = self:PosToScr(wp[1], wp[2]) + local dx = x - sx + local dy = y - sy + local dist = math.sqrt(dx * dx + dy * dy) + + if dist <= radius then + local key = self:GetConnectionKey(nodeId, inputNum) + return key, i + end + end + end + end + end + + return nil, nil +end + +function Editor:ClosestPointOnBezier(gx, gy, x1, y1, x2, y2, flipStart, flipEnd, samples) + samples = samples or 100 + + local distance = math.sqrt((x2 - x1)^2 + (y2 - y1)^2) + local offsetX = math.min(distance * 0.5, 100 / self.Zoom) + + local cx1 = x1 + (flipStart and -offsetX or offsetX) + local cy1 = y1 + local cx2 = x2 - (flipEnd and -offsetX or offsetX) + local cy2 = y2 + + local bestDist = math.huge + local bestX, bestY = x1, y1 + + for i = 0, samples do + local t = i / samples + local it = 1 - t + + local bx = it^3 * x1 + 3 * it^2 * t * cx1 + 3 * it * t^2 * cx2 + t^3 * x2 + local by = it^3 * y1 + 3 * it^2 * t * cy1 + 3 * it * t^2 * cy2 + t^3 * y2 + + local dx = gx - bx + local dy = gy - by + local dist = dx * dx + dy * dy -- with no sqrt, just to compare + + if dist < bestDist then + bestDist = dist + bestX, bestY = bx, by + end + end + + return bestX, bestY, math.sqrt(bestDist) +end + +function Editor:GetConnectionSegmentAt(x, y) + local gx, gy = self:ScrToPos(x, y) + local threshold = 8 / self.Zoom + + for nodeId, node in pairs(self.Nodes) do + local gate = getGate(node) + if not gate then continue end + + for inputNum, connectedTo in pairs(node.connections) do + local outputNodeId = connectedTo[1] + local outputNum = connectedTo[2] + local outputNode = self.Nodes[outputNodeId] + if not outputNode then continue end + + local points = {} + points[1] = {self:NodeOutputPos(outputNode, outputNum)} + + if connectedTo.waypoints then + for _, wp in ipairs(connectedTo.waypoints) do + points[#points + 1] = {wp[1], wp[2]} + end + end + + points[#points + 1] = {self:NodeInputPos(node, inputNum)} + + local hasWaypoints = #points > 2 + local lastFlipped + + for i = 1, #points - 1 do + local flipStart, flipEnd + + if hasWaypoints then + local goingLeft = points[i+1][1] < points[i][1] + + if i == 1 then + flipStart = false + flipEnd = goingLeft + else + flipStart = lastFlipped + flipEnd = (i < #points - 1) and goingLeft + end + lastFlipped = goingLeft + end + + local px, py, dist = self:ClosestPointOnBezier( + gx, gy, + points[i][1], points[i][2], + points[i+1][1], points[i+1][2], + flipStart, flipEnd + ) + + if dist <= threshold then + local key = self:GetConnectionKey(nodeId, inputNum) + return key, i, px, py + end + end + end + end + + return nil, nil, nil, nil +end + +-------------------------------------------------------- +--DRAWING +-------------------------------------------------------- +function Editor:PaintConnection(nodeFrom, output, nodeTo, input, type) + local connectedTo = nodeTo.connections[input] + local x1, y1 = self:NodeOutputPos(nodeFrom, output) + local sx1, sy1 = self:PosToScr(x1, y1) + local points = {{sx1, sy1}} + + if connectedTo and connectedTo.waypoints then + for _, wp in ipairs(connectedTo.waypoints) do + points[#points + 1] = {self:PosToScr(wp[1], wp[2])} + end + end + + local x2, y2 = self:NodeInputPos(nodeTo, input) + points[#points + 1] = {self:PosToScr(x2, y2)} + + local color = FPGATypeColor[type] + local hasWaypoints = #points > 2 + local lastFlipped + + -- Draw smooth Bezier curves between points + for i = 1, #points - 1 do + local flipStart, flipEnd + + if hasWaypoints then + local goingLeft = points[i+1][1] < points[i][1] + + if i == 1 then + flipStart = false + flipEnd = goingLeft + else + flipStart = lastFlipped + flipEnd = (i < #points - 1) and goingLeft + end + lastFlipped = goingLeft + end + + self:DrawBezierCurve( + points[i][1], points[i][2], + points[i+1][1], points[i+1][2], + color, nil, flipStart, flipEnd + ) + end + + if connectedTo and connectedTo.waypoints then + local key = self:GetConnectionKey(nodeTo.id or input, input) + local r = self.Zoom * self.IOSize / 2 + + for i, wp in ipairs(connectedTo.waypoints) do + local sx, sy = self:PosToScr(wp[1], wp[2]) + local wpKey = self:GetWaypointSelectionKey(key, i) + + if self.SelectedWaypoints[wpKey] then + surface.SetDrawColor(self.WaypointSelectedColor) + self:DrawCircle(sx, sy, r + 2) + elseif (self.DraggingWaypoint and self.DraggingWaypoint[1] == key and self.DraggingWaypoint[2] == i) + or (self.HoveringWaypoint and self.HoveringWaypoint[1] == key and self.HoveringWaypoint[2] == i) then + surface.SetDrawColor(self.WaypointHoverColor) + self:DrawCircle(sx, sy, r + 1) + end + + surface.SetDrawColor(self.WaypointColor) + self:DrawCircle(sx, sy, r) + end + end +end + +function Editor:PaintConnections() + for nodeId, node in pairs(self.Nodes) do + node.id = nodeId -- Store ID for waypoint tracking + local gate = getGate(node) + if not gate then continue end + for inputNum, connectedTo in pairs(node.connections) do + self:PaintConnection(self.Nodes[connectedTo[1]], connectedTo[2], node, inputNum, getInputType(gate, inputNum)) + end + end +end + +function Editor:PaintInput(x, y, type, name, ioSize) + surface.SetDrawColor(FPGATypeColor[type]) + surface.DrawRect(x, y, ioSize * 2, ioSize) + + if (self.Zoom > self.ZoomHideThreshold) then + local tx, ty = surface.GetTextSize(name) + surface.SetTextPos(x - tx - ioSize * 0.3, y + ioSize / 2 - ty / 2) + surface.DrawText(name) + end +end + +function Editor:PaintOutput(x, y, type, name, ioSize) + surface.SetDrawColor(FPGATypeColor[type]) + surface.DrawRect(x, y, ioSize * 2, ioSize) + + if (self.Zoom > self.ZoomHideThreshold) then + local _, ty = surface.GetTextSize(name) + surface.SetTextPos(x + ioSize * 2.3, y + ioSize / 2 - ty / 2) + surface.DrawText(name) + end +end + +function Editor:PaintGate(nodeId, node, gate) + local amountOfInputs = 0 + if gate.inputs then + amountOfInputs = getInputAmountForNode(node) + end + local amountOfOutputs = 1 + if gate.outputs then + amountOfOutputs = #gate.outputs + end + + local x, y = self:PosToScr(node.x, node.y) + + local size = self.Zoom * self.GateSize + local ioSize = self.Zoom * self.IOSize + + -- Inputs + if (self.Zoom > self.ZoomThreshold) then + surface.SetFont("FPGAIOBig") + else + surface.SetFont("FPGAIO") + end + surface.SetTextColor(255, 255, 255) + + + if gate.inputs then + for inputNum, inputName in pairs(gate.inputs) do + if inputNum > amountOfInputs then break end + local nx = x - size / 2 - ioSize + local ny = y - ioSize / 2 + (inputNum-1) * size + + self:PaintInput(nx, ny, getInputType(gate, inputNum), inputName, ioSize) + end + end + + -- Output + if gate.outputs then + for outputNum, outputName in pairs(gate.outputs) do + local nx = x + size / 2 - ioSize + local ny = y - ioSize / 2 + (outputNum - 1) * size + + self:PaintOutput(nx, ny, getOutputType(gate, outputNum), outputName, ioSize) + end + else + local nx = x + size / 2 - ioSize + local ny = y - ioSize / 2 + + self:PaintOutput(nx, ny, getOutputType(gate, 1), "Out", ioSize) + end + + -- Body + local height = math.max(amountOfInputs, amountOfOutputs, 1) + local cornerRadius = math.min(size * 0.15, 8) -- Adaptive corner radius + + local bodyColor + if self.SelectedNodes[nodeId] then + bodyColor = self.SelectedNodeColor + else + if gate.isInput then + bodyColor = self.InputNodeColor + elseif gate.isOutput then + bodyColor = self.OutputNodeColor + elseif gate.timed then + bodyColor = self.TimedNodeColor + else + bodyColor = self.NodeColor + end + end + + -- Animated glow for selected nodes (draw FIRST, behind everything) + if self.SelectedNodes[nodeId] then + local glowAnim = self:Animate("node_glow_" .. nodeId, 1, 5) + local glowAlpha = 100 + math.sin(SysTime() * 3) * 50 * glowAnim + local glowSize = 2 + glowAnim * 2 + + local glowColor = Color(0, 122, 204, glowAlpha) + draw.RoundedBox(cornerRadius + glowSize, x - size / 2 - glowSize, y - size / 2 - glowSize, size + glowSize * 2, size * height + glowSize * 2, glowColor) + else + -- Fade out glow + self:Animate("node_glow_" .. nodeId, 0, 8) + end + + -- Draw shadow (offset) + draw.RoundedBox(cornerRadius, x - size / 2 + 3, y - size / 2 + 3, size, size * height, self.NodeShadowColor) + + -- Draw main node body + draw.RoundedBox(cornerRadius, x - size / 2, y - size / 2, size, size * height, bodyColor) + + -- Draw subtle outline + surface.SetDrawColor(self.NodeOutlineColor) + -- Top + surface.DrawLine(x - size / 2 + cornerRadius, y - size / 2, x + size / 2 - cornerRadius, y - size / 2) + -- Bottom + surface.DrawLine(x - size / 2 + cornerRadius, y - size / 2 + size * height, x + size / 2 - cornerRadius, y - size / 2 + size * height) + -- Left + surface.DrawLine(x - size / 2, y - size / 2 + cornerRadius, x - size / 2, y - size / 2 + size * height - cornerRadius) + -- Right + surface.DrawLine(x + size / 2, y - size / 2 + cornerRadius, x + size / 2, y - size / 2 + size * height - cornerRadius) + + -- Name + if (self.Zoom > self.ZoomThreshold) then + surface.SetFont("FPGATextBig") + else + surface.SetFont("FPGAText") + end + surface.SetTextColor(255, 255, 255) + if (self.Zoom > self.ZoomHideThreshold) then + local tx, ty = surface.GetTextSize(gate.name) + surface.SetTextPos(x - tx / 2, y - ty / 2 - size / 1.2) + surface.DrawText(gate.name) + + surface.SetTextColor(200, 200, 200) + -- Input + if node.ioName then + local tx, ty = surface.GetTextSize(node.ioName) + surface.SetTextPos(x - tx / 2, y - ty / 2 + size / 1.2) + surface.DrawText(node.ioName) + -- Constant + elseif node.value then + local s = util.TypeToString(node.value) + local tx, ty = surface.GetTextSize(s) + surface.SetTextPos(x - tx / 2, y - ty / 2 + size / 1.2) + surface.DrawText(s) + end + end +end + +function Editor:PaintEditorNode(nodeId, node, visual) + local x, y = self:PosToScr(node.x, node.y) + + if visual.method == "text" then + if visual.font == "auto" then + if (self.Zoom > self.ZoomThreshold) then + surface.SetFont("FPGATextBig") + elseif (self.Zoom <= self.ZoomHideThreshold) then + return + else + surface.SetFont("FPGAText") + end + else + surface.SetFont(visual.font) + end + + local tx, ty = surface.GetTextSize(node.value) + surface.SetTextPos(x - tx / 2, y - ty / 2) + + surface.DrawText(node.value) + end +end + +function Editor:PaintNodes() + for nodeId, node in pairs(self.Nodes) do + local gate = getGate(node) + if gate then + self:PaintGate(nodeId, node, gate) + continue + end + + local visual = self:GetVisual(node) + if visual then + self:PaintEditorNode(nodeId, node, visual) + end + end +end + +function Editor:PaintGrid() + if not self.GridEnabled then return end + + local gridSize = self.GridSize * self.Zoom + if gridSize < 5 then return end + + local screenW = self:GetWide() - 300 + local screenH = self:GetTall() - 36 + + local startX = math.floor((self.Position[1] - screenW / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local endX = math.ceil((self.Position[1] + screenW / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local startY = math.floor((self.Position[2] - screenH / (2 * self.Zoom)) / self.GridSize) * self.GridSize + local endY = math.ceil((self.Position[2] + screenH / (2 * self.Zoom)) / self.GridSize) * self.GridSize + + surface.SetDrawColor(self.GridColor) + + for x = startX, endX, self.GridSize do + local sx, sy1 = self:PosToScr(x, startY) + local _, sy2 = self:PosToScr(x, endY) + surface.DrawLine(sx, sy1, sx, sy2) + end + + for y = startY, endY, self.GridSize do + local sx1, sy = self:PosToScr(startX, y) + local sx2, _ = self:PosToScr(endX, y) + surface.DrawLine(sx1, sy, sx2, sy) + end +end + +function Editor:PaintHelp() + local x, y = self:PosToScr(0, 0) + + surface.SetFont("FPGAText") + surface.SetTextColor(255, 255, 255) + + local helpText = [[Drag gates and draw selections with the left mouse button, + and drag around the plane with the right mouse button. + Connect inputs and outputs by left clicking on either, and dragging to the other. + By double clicking on an input or output, you can draw multiple connections at once. + + 'C' creates a gate at the cursor position (select which gate on the right menu) + 'X' deletes the gate or the waypoint under the cursor (or with a selection, deletes all selected gates) + 'E' edits the gate under the cursor (input/output names, constant values) + 'G' toggles align to grid + 'W' adds waypoint to connection line at cursor + + 'Ctrl + C' copies the selected gates (relative to mouse position) + 'Ctrl + V' pastes the copied gates (relative to mouse position) + 'Ctrl + Z' undo last action + 'Ctrl + Y' redo last undone action + + + To create inputs and outputs for the FPGA chip, use the gates found in 'FPGA/Input & Output' + ]] + + for line in helpText:gmatch("([^\n]*)\n?") do + local tx, ty = surface.GetTextSize(line) + surface.SetTextPos(x - tx / 2, y - ty / 2) + surface.DrawText(line) + y = y + ty + end +end + +function Editor:PaintMinimap() + if not self.MinimapConVar:GetBool() or not self:HasNodes() then return end + + local size = self.MinimapSize + local padding = self.MinimapPadding + local x = self:GetWide() - 300 - size - padding + local y = self:GetTall() - size - padding + + -- Background + draw.RoundedBox(4, x, y, size, size, self.MinimapBackgroundColor) + + -- Border + surface.SetDrawColor(self.MinimapBorderColor) + surface.DrawOutlinedRect(x, y, size, size) + + -- Calculate bounds of all nodes + if #self.Nodes < 1 then return end + local minX, maxX = math.huge, -math.huge + local minY, maxY = math.huge, -math.huge + + for _, node in pairs(self.Nodes) do + if node.visual == "comment" then continue end + if node.x then + minX = math.min(minX, node.x) + maxX = math.max(maxX, node.x) + minY = math.min(minY, node.y) + maxY = math.max(maxY, node.y) + end + end + + local worldW = maxX - minX + 200 + local worldH = maxY - minY + 200 + local scale = math.min((size - 20) / worldW, (size - 20) / worldH) + + -- Draw nodes on minimap + for nodeId, node in pairs(self.Nodes) do + if not node.x or node.visual == "comment" then continue end + + local nx = x + 10 + (node.x - minX + 100) * scale + local ny = y + 10 + (node.y - minY + 100) * scale + local nodeSize = 3 + + for inputNum, connectedTo in pairs(node.connections) do + local outputNodeId = connectedTo[1] + local outputNode = self.Nodes[outputNodeId] + if not outputNode then continue end + + local color = ColorAlpha(FPGATypeColor[getInputType(getGate(node), inputNum)], self.MinimapConnectionAlpha) + + -- Draw connection with waypoints + local points = {} + points[1] = {x + 10 + (outputNode.x - minX + 100) * scale, y + 10 + (outputNode.y - minY + 100) * scale} + + if connectedTo.waypoints then + for _, wp in ipairs(connectedTo.waypoints) do + points[#points + 1] = {x + 10 + (wp[1] - minX + 100) * scale, y + 10 + (wp[2] - minY + 100) * scale} + end + end + + points[#points + 1] = {x + 10 + (node.x - minX + 100) * scale, y + 10 + (node.y - minY + 100) * scale} + + local hasWaypoints = #points > 2 + local lastFlipped + + for i = 1, #points - 1 do + local flipStart, flipEnd + + if hasWaypoints then + local goingLeft = points[i+1][1] < points[i][1] + + if i == 1 then + flipStart = false + flipEnd = goingLeft + else + flipStart = lastFlipped + flipEnd = (i < #points - 1) and goingLeft + end + lastFlipped = goingLeft + end + + self:DrawBezierCurve( + points[i][1], points[i][2], + points[i+1][1], points[i+1][2], + color, 8, flipStart, flipEnd + ) + end + end + + if self.SelectedNodes[nodeId] then + surface.SetDrawColor(self.SelectedNodeColor) + surface.SetTextColor(self.SelectedNodeColor) + else + surface.SetDrawColor(self.NodeColor) + surface.SetTextColor(255, 255, 255) + end + + if node.visual == "label" then + surface.SetFont("FPGAIO") + local szx, szy = surface.GetTextSize(node.value) + surface.SetTextPos(nx - szx/2, ny - szy) + surface.DrawText(node.value) + else + surface.DrawRect(nx - nodeSize/2, ny - nodeSize/2, nodeSize, nodeSize) + end + end + + -- Draw viewport rectangle with clipping + local viewW = (self:GetWide() - 300) / self.Zoom + local viewH = self:GetTall() / self.Zoom + local vx = x + 10 + (self.Position[1] - minX + 100 - viewW/2) * scale + local vy = y + 10 + (self.Position[2] - minY + 100 - viewH/2) * scale + local vw = viewW * scale + local vh = viewH * scale + + local minimapLeft = x + 10 + local minimapTop = y + 10 + local minimapRight = x + size - 10 + local minimapBottom = y + size - 10 + + local viewLeft = vx + local viewTop = vy + local viewRight = vx + vw + local viewBottom = vy + vh + + local hasIntersection = not (viewRight < minimapLeft or + viewLeft > minimapRight or + viewBottom < minimapTop or + viewTop > minimapBottom) + + if hasIntersection then + local clippedLeft = math.max(viewLeft, minimapLeft) + local clippedTop = math.max(viewTop, minimapTop) + local clippedRight = math.min(viewRight, minimapRight) + local clippedBottom = math.min(viewBottom, minimapBottom) + + local clippedVx = clippedLeft + local clippedVy = clippedTop + local clippedVw = clippedRight - clippedLeft + local clippedVh = clippedBottom - clippedTop + + -- Draw clipped viewport rectangle + if clippedVw > 0 and clippedVh > 0 then + surface.SetDrawColor(self.SelectedNodeColor) + surface.DrawOutlinedRect(clippedVx, clippedVy, clippedVw, clippedVh) + if clippedVw > 2 and clippedVh > 2 then + surface.DrawOutlinedRect(clippedVx + 1, clippedVy + 1, clippedVw - 2, clippedVh - 2) + end + end + else + -- Camera is outside minimap bounds - show direction indicator + local viewCenterX = vx + vw / 2 + local viewCenterY = vy + vh / 2 + local circleX = math.Clamp(viewCenterX, minimapLeft, minimapRight) + local circleY = math.Clamp(viewCenterY, minimapTop, minimapBottom) + + surface.SetDrawColor(self.SelectedNodeColor) + self:DrawCircle(circleX, circleY, 5, 16) + end +end + +-------------------------------------------------------- +--ANIMATION SYSTEM +-------------------------------------------------------- +function Editor:Animate(key, targetValue, speed) + speed = speed or 10 + + if not self.Animations[key] then + self.Animations[key] = {value = 0, target = targetValue} + end + + local anim = self.Animations[key] + anim.target = targetValue + + -- Smooth lerp + local delta = self.LastFrameTime and (SysTime() - self.LastFrameTime) or 0 + local step = delta * speed + + if math.abs(anim.target - anim.value) > 0.01 then + anim.value = anim.value + (anim.target - anim.value) * step + else + anim.value = anim.target + end + + return anim.value +end + +function Editor:GetAnimValue(key) + return self.Animations[key] and self.Animations[key].value or 0 +end + +function Editor:Paint() + -- Update animation frame time + self.LastFrameTime = SysTime() + + surface.SetDrawColor(self.BackgroundColor) + surface.DrawRect(0, 36, self:GetWide() - 300, self:GetTall() - 36) + + self:PaintGrid() + self:PaintNodes() + self:PaintConnections() + + if not self:HasNodes() then + self:PaintHelp() + end + + -- Update hovering waypoint + local x, y = self:CursorPos() + if not self.DraggingWaypoint and not self.DraggingNode and not self.DrawingConnection then + self.HoveringWaypoint = nil + local key, wpIndex = self:GetWaypointAt(x, y) + if key then + self.HoveringWaypoint = {key, wpIndex} + end + end + + -- detects if mouse is let go outside of the window + if not input.IsMouseDown(MOUSE_RIGHT) then + self.DraggingWorld = nil + end + if not input.IsMouseDown(MOUSE_LEFT) then + self.DraggingNode = nil + self.DrawingConnection = nil + self.DrawingSelection = nil + self.DraggingWaypoint = nil + end + + -- moving the plane + if self.DraggingWorld then + local x, y = self:CursorPos() + local dx, dy = self.LastMousePos[1] - x, self.LastMousePos[2] - y + self.Position = { self.Position[1] + dx * (1 / self.Zoom), self.Position[2] + dy * (1 / self.Zoom) } + end + -- moving a node + if self.DraggingNode then + local x, y = self:CursorPos() + local gx, gy = self:ScrToPos(x, y) + gx = gx + self.DraggingOffset[1] + gy = gy + self.DraggingOffset[2] + + if self.AlignToGrid then + gx, gy = self:AlignPosToGrid(gx, gy) + end + + local cx, cy = self.Nodes[self.DraggingNode].x, self.Nodes[self.DraggingNode].y + local dx, dy = gx - cx, gy - cy -- Calculate movement delta + + if self.SelectedNodes[self.DraggingNode] and self.SelectedNodeCount > 0 then + -- Track which nodes are being moved for waypoint updates + local movedNodes = {} + + for selectedNodeId, selectedNode in pairs(self.SelectedNodes) do + local sox, soy = self.Nodes[selectedNodeId].x - cx, self.Nodes[selectedNodeId].y - cy + self.Nodes[selectedNodeId].x = gx + sox + self.Nodes[selectedNodeId].y = gy + soy + movedNodes[selectedNodeId] = true + end + + -- Update waypoints for connections involving moved nodes + for nodeId, node in pairs(self.Nodes) do + for inputNum, connection in pairs(node.connections) do + local outputNodeId = connection[1] + + -- Check if either end of connection is being moved + local inputMoved = movedNodes[nodeId] + local outputMoved = movedNodes[outputNodeId] + + if connection.waypoints then + for i, wp in ipairs(connection.waypoints) do + local wpKey = self:GetConnectionKey(nodeId, inputNum) + local wpSelKey = self:GetWaypointSelectionKey(wpKey, i) + + -- Move waypoint if it's selected OR if both ends are moving + if self.SelectedWaypoints[wpSelKey] then + -- Selected waypoint: move it by delta + connection.waypoints[i] = {wp[1] + dx, wp[2] + dy} + elseif inputMoved and outputMoved then + -- Both ends moving but waypoint not selected: move it too + connection.waypoints[i] = {wp[1] + dx, wp[2] + dy} + end + end + end + end + end + else + self.SelectedNodes = {} + self.Nodes[self.DraggingNode].x = gx + self.Nodes[self.DraggingNode].y = gy + end + end + -- NEW: moving waypoint(s) + if self.DraggingWaypoint then + local x, y = self:CursorPos() + local gx, gy = self:ScrToPos(x, y) + + if self.AlignToGrid then + gx, gy = self:AlignPosToGrid(gx, gy) + end + + local key = self.DraggingWaypoint[1] + local wpIndex = self.DraggingWaypoint[2] + + -- Calculate delta from the dragged waypoint's original position + local originalPos = nil + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == key and connectedTo.waypoints and connectedTo.waypoints[wpIndex] then + originalPos = {connectedTo.waypoints[wpIndex][1], connectedTo.waypoints[wpIndex][2]} + break + end + end + if originalPos then break end + end + + if originalPos then + local dx = gx - originalPos[1] + local dy = gy - originalPos[2] + + -- Move all selected waypoints by the same delta + for wpSelKey, wpData in pairs(self.SelectedWaypoints) do + local wpKey = wpData.key + local wpIdx = wpData.index + + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == wpKey and connectedTo.waypoints and connectedTo.waypoints[wpIdx] then + connectedTo.waypoints[wpIdx] = { + connectedTo.waypoints[wpIdx][1] + dx, + connectedTo.waypoints[wpIdx][2] + dy + } + end + end + end + end + end + end + -- drawing a connection + if self.DrawingConnection then + local nodeId = self.DrawingConnectionFrom[1] + local node = self.Nodes[nodeId] + local gate = getGate(node) + + local drawingConnectionFrom = { self.DrawingConnectionFrom[2] } + local selectedPort = self.DrawingConnectionFrom[2] + if self.DrawingConnectionAll then + drawingConnectionFrom = {} + local ports + local amountOfInputs = getInputAmountForNode(node) + if self.DrawingFromInput then ports = gate.inputs + elseif self.DrawingFromOutput then ports = gate.outputs or { "Out" } end + for portNum, portName in pairs(ports) do + if self.DrawingFromInput and portNum > amountOfInputs then break end + drawingConnectionFrom[portNum] = portNum + end + end + + local x, y = 0, 0 + for _, inputNum in pairs(drawingConnectionFrom) do + local type = "NORMAL" + if self.DrawingFromInput then + x, y = self:NodeInputPos(node, inputNum) + type = getInputType(gate, inputNum) + elseif self.DrawingFromOutput then + x, y = self:NodeOutputPos(node, inputNum) + type = getOutputType(gate, inputNum) + end + local sx, sy = self:PosToScr(x, y) + local mx, my = self:CursorPos() + self:DrawBezierCurve(sx, sy, mx, my + (inputNum - selectedPort) * self.GateSize * self.Zoom, FPGATypeColor[type], nil, self.DrawingFromInput, not self.DrawingFromOutput) + end + end + -- selecting + if self.DrawingSelection then + local sx, sy = self:PosToScr(self.DrawingSelection[1], self.DrawingSelection[2]) + local mx, my = self:CursorPos() + + local x, y = math.min(sx, mx), math.min(sy, my) + local w, h = math.abs(sx - mx), math.abs(sy - my) + + surface.SetDrawColor(self.SelectionColor) + surface.DrawOutlinedRect(x, y, w, h) + end + + self:PaintMinimap() + self:PaintOverlay() + + local x, y = self:CursorPos() + self.LastMousePos = { x, y } +end + +function Editor:PaintDebug() + surface.SetFont("Default") + surface.SetTextColor(255, 255, 255) + surface.SetTextPos(10, 50) + surface.DrawText(self.Position[1] .. ", " .. self.Position[2]) + surface.SetTextPos(10, 70) + surface.DrawText(self.Zoom) +end + +function Editor:PaintOverlay() + surface.SetFont("FPGAText") + local y = 43 + local xOffset = self:GetWide() - 310 + + if self.AlignToGrid then + surface.SetTextColor(100, 180, 255) + local tx, _ = surface.GetTextSize("Align to grid") + surface.SetTextPos(xOffset - tx, y) + surface.DrawText("Align to grid") + y = y + 20 + end + + if self.SelectedNodeCount > 0 then + surface.SetTextColor(255, 255, 120) + local text = self.SelectedNodeCount + if self.SelectedNodeCount == 1 then + text = text .. " node selected" + else + text = text .. " nodes selected" + end + local tx, _ = surface.GetTextSize(text) + surface.SetTextPos(xOffset - tx, y) + surface.DrawText(text) + y = y + 20 + end + + -- Display selected waypoints count + if self.SelectedWaypointCount > 0 then + surface.SetTextColor(255, 200, 100) + local text = self.SelectedWaypointCount + if self.SelectedWaypointCount == 1 then + text = text .. " waypoint selected" + else + text = text .. " waypoints selected" + end + local tx, _ = surface.GetTextSize(text) + surface.SetTextPos(xOffset - tx, y) + surface.DrawText(text) + y = y + 20 + end + + local copyDataSize = self:GetParent():GetCopyDataSize() + if copyDataSize > 0 then + surface.SetTextColor(120, 255, 120) + local text = copyDataSize + if copyDataSize == 1 then + text = text .. " node in paste buffer" + else + text = text .. " nodes in paste buffer" + end + local tx, _ = surface.GetTextSize(text) + surface.SetTextPos(xOffset - tx, y) + surface.DrawText(text) + y = y + 20 + end + + -- Display Undo/Redo stack info + if #self.UndoStack > 0 or #self.RedoStack > 0 then + surface.SetTextColor(180, 180, 180) + local text = "Undo: " .. #self.UndoStack .. " | Redo: " .. #self.RedoStack + local tx, _ = surface.GetTextSize(text) + surface.SetTextPos(xOffset - tx, y) + surface.DrawText(text) + y = y + 20 + end +end + + +-------------------------------------------------------- +--ACTIONS +-------------------------------------------------------- +function Editor:GetInputName() + self.InputNameCounter = self.InputNameCounter + 1 + return "In" .. self.InputNameCounter +end + +function Editor:GetOutputName() + self.OutputNameCounter = self.OutputNameCounter + 1 + return "Out" .. self.OutputNameCounter +end + +function Editor:CreateNode(selectedInMenu, x, y) + -- Save state before creating node + self:SaveState("Create Node") + + node = { + type = selectedInMenu.type, + gate = selectedInMenu.gate, + visual = selectedInMenu.visual, + x = x, + y = y, + connections = {} + } + + if self.AlignToGrid then + node.x, node.y = self:AlignPosToGrid(node.x, node.y) + end + + if selectedInMenu.gate then + local gateInfo = getGate(node) + + if gateInfo.isInput then + node.ioName = self:GetInputName() + elseif gateInfo.isOutput then + node.ioName = self:GetOutputName() + elseif gateInfo.isConstant then + local type = getOutputType(gateInfo, 1) + node.value = FPGADefaultValueForType[type] + end + elseif selectedInMenu.visual then + node.value = self:GetVisual(node).default + end + + --print("Created " .. table.ToString(node, "node", false)) + + table.insert(self.Nodes, node) +end + +function Editor:DeleteNode(nodeId) + --print("Deleted " .. nodeId) + + --remove all connections to this node + for k1, node in pairs(self.Nodes) do + for inputNum, connection in pairs(node.connections) do + if connection[1] == nodeId then + node.connections[inputNum] = nil + end + end + end + + --finally remove node + self.Nodes[nodeId] = nil +end + +function Editor:CopyNodes(nodeIds) + local nodeIdLookup = {} + local i = 1 + for nodeId, _ in pairs(nodeIds) do + nodeIdLookup[nodeId] = i + i = i + 1 + end + + local nodeAmount = table.Count(nodeIds) + local copyBuffer = {} + local copyOffset = { 0, 0 } + for nodeId, _ in pairs(nodeIds) do + local node = self.Nodes[nodeId] + local gate = getGate(node) + + local nodeCopy = { + type = node.type, + gate = node.gate, + x = node.x, + y = node.y, + connections = {} + } + + if gate then + if gate.isInput then + nodeCopy.ioName = node.ioName + elseif gate.isOutput then + nodeCopy.ioName = node.ioName + elseif gate.isConstant then + nodeCopy.value = node.value + nodeCopy.valueAsString = node.valueAsString + end + elseif node.visual then + nodeCopy.visual = node.visual + if node.visual == "label" or node.visual == "comment" then + nodeCopy.value = node.value + end + end + + for inputNum, connection in pairs(node.connections) do + if nodeIds[connection[1]] then + nodeCopy.connections[inputNum] = { + nodeIdLookup[connection[1]], + connection[2], + waypoints = connection.waypoints and table.Copy(connection.waypoints) or nil + } + end + end + + table.insert(copyBuffer, nodeCopy) + + copyOffset = { copyOffset[1] + node.x / nodeAmount, copyOffset[2] + node.y / nodeAmount } + end + + self:GetParent():SetCopyData(copyBuffer, copyOffset) +end + +function Editor:PasteNodes(x, y) + local copyData = self:GetParent():GetCopyData() + local copyBuffer = copyData[1] + local copyOffset = copyData[2] + + if not copyBuffer then return end + + local nodeIdLookup = {} + self.SelectedNodes = {} + self.SelectedNodeCount = 0 + local i = #self.Nodes + 1 + for copyNodeId, _ in pairs(copyBuffer) do + while self.Nodes[i] do + i = i + 1 + end + + nodeIdLookup[copyNodeId] = i + self.SelectedNodes[i] = true + self.SelectedNodeCount = self.SelectedNodeCount + 1 + i = i + 1 + end + + for copyNodeId, copyNode in pairs(copyBuffer) do + local nodeCopy = { + type = copyNode.type, + gate = copyNode.gate, + connections = {} + } + + local gate = getGate(copyNode) + if gate then + if gate.isInput then + nodeCopy.ioName = copyNode.ioName + elseif gate.isOutput then + nodeCopy.ioName = copyNode.ioName + elseif gate.isConstant then + nodeCopy.value = copyNode.value + nodeCopy.valueAsString = copyNode.valueAsString + end + elseif copyNode.visual then + nodeCopy.visual = copyNode.visual + if copyNode.visual == "label" or copyNode.visual == "comment" then + nodeCopy.value = copyNode.value + end + end + + for inputNum, connection in pairs(copyNode.connections) do + local waypointsCopy = nil + if connection.waypoints then + waypointsCopy = {} + local offsetX = x - copyOffset[1] + local offsetY = y - copyOffset[2] + + for i, wp in ipairs(connection.waypoints) do + waypointsCopy[i] = {wp[1] + offsetX, wp[2] + offsetY} + end + end + + nodeCopy.connections[inputNum] = { + nodeIdLookup[connection[1]], + connection[2], + waypoints = waypointsCopy + } + end + + nodeCopy.x = (copyNode.x - copyOffset[1]) + x + nodeCopy.y = (copyNode.y - copyOffset[2]) + y + + self.Nodes[nodeIdLookup[copyNodeId]] = nodeCopy + end +end + +-------------------------------------------------------- +--Local functions for editor (I'm not an egyptian) +-------------------------------------------------------- +local function deleteWaypointsByKey(self, key, indices) + table.sort(indices, function(a, b) return a > b end) + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == key and connectedTo.waypoints then + for _, idx in ipairs(indices) do + table.remove(connectedTo.waypoints, idx) + end + if #connectedTo.waypoints == 0 then + connectedTo.waypoints = nil + end + break + end + end + end +end + +local function deleteSelectedWaypoints(self) + local waypointsToDelete = {} + for wpSelKey, wpData in pairs(self.SelectedWaypoints) do + local key = wpData.key + local index = wpData.index + if not waypointsToDelete[key] then + waypointsToDelete[key] = {} + end + table.insert(waypointsToDelete[key], index) + end + + for key, indices in pairs(waypointsToDelete) do + deleteWaypointsByKey(self, key, indices) + end + + self.SelectedWaypoints = {} + self.SelectedWaypointCount = 0 +end + +local function deleteSelectedNodes(self) + for selectedNodeId in pairs(self.SelectedNodes) do + self:DeleteNode(selectedNodeId) + end + self.SelectedNodes = {} + self.SelectedNodeCount = 0 +end + +local function deleteWaypointAt(self, x, y) + local wpKey, wpIndex = self:GetWaypointAt(x, y) + if not wpKey then return false end + + self:SaveState("Delete Waypoint") + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == wpKey and connectedTo.waypoints then + table.remove(connectedTo.waypoints, wpIndex) + if #connectedTo.waypoints == 0 then + connectedTo.waypoints = nil + end + break + end + end + end + return true +end + +-------------------------------------------------------- +--EVENTS +-------------------------------------------------------- +--KEYBOARD +function Editor:OnKeyCodePressed(code) + local x, y = self:CursorPos() + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + local shift = input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) + + if control then + self:OnShortcut(code) + + if code == KEY_Z then + --Undo + self:Undo() + return + elseif code == KEY_Y then + --Redo + self:Redo() + return + elseif code == KEY_C then + --Copy + if self.SelectedNodeCount > 0 then + self:CopyNodes(self.SelectedNodes) + else + self:GetParent():ClearCopyData() + end + elseif code == KEY_V then + --Paste + self:SaveState("Paste Nodes") + local gx, gy = self:ScrToPos(x, y) + self:PasteNodes(gx, gy) + end + elseif code == KEY_X then + if self.SelectedWaypointCount > 0 or self.SelectedNodeCount > 0 then + self:SaveState("Delete Selection") + if self.SelectedWaypointCount > 0 then + deleteSelectedWaypoints(self) + end + if self.SelectedNodeCount > 0 then + deleteSelectedNodes(self) + end + else + local deleted = deleteWaypointAt(self, x, y) + if not deleted then + local nodeId = self:GetNodeAt(x, y) + if nodeId then + self:SaveState("Delete Node") + self:DeleteNode(nodeId) + end + end + end + elseif code == KEY_C then + --Create + if self.SelectedInMenu then + local gx, gy = self:ScrToPos(x, y) + self:CreateNode(self.SelectedInMenu, gx, gy) + end + elseif code == KEY_W then + if shift then + -- Remove waypoint + local key, wpIndex = self:GetWaypointAt(x, y) + if key then + self:SaveState("Remove Waypoint") + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == key and connectedTo.waypoints then + table.remove(connectedTo.waypoints, wpIndex) + if #connectedTo.waypoints == 0 then + connectedTo.waypoints = nil + end + break + end + end + end + end + else + -- Add waypoint + local key, segmentIndex, px, py = self:GetConnectionSegmentAt(x, y) + if key then + self:SaveState("Add Waypoint") + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + local testKey = self:GetConnectionKey(nodeId, inputNum) + if testKey == key then + if not connectedTo.waypoints then + connectedTo.waypoints = {} + end + table.insert(connectedTo.waypoints, segmentIndex, {px, py}) + break + end + end + end + end + end + elseif code == KEY_E and not self.EditingNode then + --Edit + local nodeId = self:GetNodeAt(x, y) + if nodeId then + local node = self.Nodes[nodeId] + local gate = getGate(node) + + if gate then + if gate.isInput or gate.isOutput then + self.EditingNode = true + self:OpenNamingWindow(node, x, y) + elseif gate.isConstant then + self.EditingNode = true + self:OpenConstantSetWindow(node, x, y, gate.outputtypes[1]) + end + return + end + + local visual = self:GetVisual(node) + if visual then + if visual.method == "text" then + self.EditingNode = true + self:OpenNamingWindow(node, x, y) + end + return + end + end + elseif code == KEY_G then + self.AlignToGrid = not self.AlignToGrid + end +end + +--MOUSE +function Editor:OnMouseWheeled(delta) + local sx, sy = self:CursorPos() + + if sx > 0 and sy > 36 and sx < self:GetWide() - 300 and sy < self:GetTall() - 36 then + self.Zoom = self.Zoom + delta * 0.1 * self.Zoom + if self.Zoom < 0.1 then self.Zoom = 0.1 end + if self.Zoom > 10 then self.Zoom = 10 end + end +end + +function Editor:OnMousePressed(code) + self:RequestFocus() --Fix for weird bug, remove once resolved + + if code == MOUSE_LEFT then + self.MouseDown = true + + --double click detection + local doubleClick + if self.LastClick then + doubleClick = SysTime() - self.LastClick < 0.3 + else doubleClick = false end + self.LastClick = SysTime() + + local x, y = self:CursorPos() + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + + local wpKey, wpIndex = self:GetWaypointAt(x, y) + if wpKey then + local wpSelectionKey = self:GetWaypointSelectionKey(wpKey, wpIndex) + + -- Handle selection + if control then + -- Ctrl+Click: Toggle selection + if self.SelectedWaypoints[wpSelectionKey] then + self.SelectedWaypoints[wpSelectionKey] = nil + self.SelectedWaypointCount = self.SelectedWaypointCount - 1 + else + self.SelectedWaypoints[wpSelectionKey] = {key = wpKey, index = wpIndex} + self.SelectedWaypointCount = self.SelectedWaypointCount + 1 + end + return + elseif not self.SelectedWaypoints[wpSelectionKey] then + -- Click on unselected waypoint: clear selection and select this one + self.SelectedWaypoints = {} + self.SelectedWaypointCount = 0 + self.SelectedWaypoints[wpSelectionKey] = {key = wpKey, index = wpIndex} + self.SelectedWaypointCount = 1 + end + + -- Start dragging selected waypoint(s) + self:SaveState("Move Waypoint(s)") + self.DraggingWaypoint = {wpKey, wpIndex} + return + end + + -- Check for node BEFORE clearing waypoints + local nodeId = self:GetNodeAt(x, y) + + -- Clear waypoint selection only if NOT clicking on a node and NOT holding Ctrl + if not nodeId and not control then + self.SelectedWaypoints = {} + self.SelectedWaypointCount = 0 + end + + --NODE DRAGGING + if nodeId then + self:SaveState("Move Node") -- Save BEFORE dragging starts + self.DraggingNode = nodeId + local gx, gy = self:ScrToPos(x, y) + self.DraggingOffset = { self.Nodes[nodeId].x - gx, self.Nodes[nodeId].y - gy } + + else + --CONNECTION DRAWING + local nodeId, inputNum = self:GetNodeInputAt(x, y) + if nodeId then + self:BeginDrawingConnection(nodeId, inputNum, nil, doubleClick) + else + local nodeId, outputNum = self:GetNodeOutputAt(x, y) + if nodeId then + self:BeginDrawingConnection(nodeId, nil, outputNum, doubleClick) + else + --SELECTION DRAWING + local gx, gy = self:ScrToPos(x, y) + self.DrawingSelection = { gx, gy } + end + end + + end + elseif code == MOUSE_RIGHT then + -- PLANE DRAGGING + self.DraggingWorld = true + end +end + +function Editor:OnMouseReleased(code) + local x, y = self:CursorPos() + + if code == MOUSE_LEFT then + self.MouseDown = false + self.DraggingNode = nil + self.DraggingWaypoint = nil + + if self.DrawingConnection then + self:OnDrawConnectionFinished(x, y) + elseif self.DrawingSelection then + self:OnDrawSelectionFinished(x, y) + end + elseif code == MOUSE_RIGHT then + self.DraggingWorld = false + end + +end + +--EDITOR EVENTS +function Editor:BeginDrawingConnection(nodeId, inputNum, outputNum, doubleClick) + self.DrawingConnectionAll = doubleClick + + if inputNum then + --check if something is connected to this input + node = self.Nodes[nodeId] + Input = node.connections[inputNum] + + --Input already connected + if Input then + self:SaveState("Disconnect Connection") + local connectedNode, connectedOutput = Input[1], Input[2] + node.connections[inputNum] = nil + self.DrawingConnectionFrom = { connectedNode, connectedOutput } + self.DrawingFromOutput = true + self.DrawingConnectionAll = false + else + --input not connected + self.DrawingConnectionFrom = { nodeId, inputNum } + self.DrawingFromInput = true + end + + self.DrawingConnection = true + end + + if outputNum then + self.DrawingConnection = true + self.DrawingFromOutput = true + self.DrawingConnectionFrom = { nodeId, outputNum } + end +end + +function Editor:OnDrawSelectionFinished(x, y) + local gx, gy = self.DrawingSelection[1], self.DrawingSelection[2] + local mx, my = self:CursorPos() + local mgx, mgy = self:ScrToPos(mx, my) + + local lx, ly = math.min(gx, mgx), math.min(gy, mgy) + local ux, uy = math.max(gx, mgx), math.max(gy, mgy) + + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + + -- Don't clear selections if holding Ctrl + if not control then + self.SelectedNodes = {} + self.SelectedNodeCount = 0 + self.SelectedWaypoints = {} + self.SelectedWaypointCount = 0 + end + + -- Select nodes in rectangle + for nodeId, node in pairs(self.Nodes) do + if node.x < lx then continue end + if node.x > ux then continue end + if node.y < ly then continue end + if node.y > uy then continue end + + self.SelectedNodes[nodeId] = true + self.SelectedNodeCount = self.SelectedNodeCount + 1 + end + + -- Select waypoints in rectangle + for nodeId, node in pairs(self.Nodes) do + for inputNum, connectedTo in pairs(node.connections) do + if connectedTo.waypoints then + local key = self:GetConnectionKey(nodeId, inputNum) + for i, wp in ipairs(connectedTo.waypoints) do + if wp[1] >= lx and wp[1] <= ux and wp[2] >= ly and wp[2] <= uy then + local wpSelKey = self:GetWaypointSelectionKey(key, i) + if not self.SelectedWaypoints[wpSelKey] then + self.SelectedWaypoints[wpSelKey] = {key = key, index = i} + self.SelectedWaypointCount = self.SelectedWaypointCount + 1 + end + end + end + end + end + end + + self.DrawingSelection = nil +end + +function Editor:OnDrawConnectionFinished(x, y) + local fromNodeId = self.DrawingConnectionFrom[1] + local fromNode = self.Nodes[fromNodeId] + local fromGate = getGate(fromNode) + + local drawingConnectionFrom = { self.DrawingConnectionFrom[2] } + local selectedPort = self.DrawingConnectionFrom[2] + if self.DrawingConnectionAll then + drawingConnectionFrom = {} + local ports + local amountOfInputs = getInputAmountForNode(fromNode) + if self.DrawingFromInput then ports = fromGate.inputs + elseif self.DrawingFromOutput then ports = fromGate.outputs or { "Out" } end + for portNum, _ in pairs(ports) do + if self.DrawingFromInput and portNum > amountOfInputs then break end + drawingConnectionFrom[portNum] = portNum + end + end + + local inputNode = fromNode + local outputNodeId = fromNodeId + local outputNode = fromNode + local connectionMade = false + + for _, portNum in pairs(drawingConnectionFrom) do + local nodeId, inputNum, outputNum + if self.DrawingFromOutput then + nodeId, inputNum = self:GetNodeInputAt(x, y + (portNum - selectedPort) * self.GateSize * self.Zoom) + outputNum = portNum + elseif self.DrawingFromInput then + nodeId, outputNum = self:GetNodeOutputAt(x, y + (portNum - selectedPort) * self.GateSize * self.Zoom) + inputNum = portNum + end + + if nodeId then + if self.DrawingFromOutput then + inputNode = self.Nodes[nodeId] + elseif self.DrawingFromInput then + outputNode = self.Nodes[nodeId] + outputNodeId = nodeId + end + + --check type + local inputType, outputType + if self.DrawingFromOutput then + inputType = getInputType(getGate(inputNode), inputNum) + outputType = getOutputType(fromGate, outputNum) + elseif self.DrawingFromInput then + inputType = getInputType(fromGate, inputNum) + outputType = getOutputType(getGate(outputNode), outputNum) + end + + if inputType == outputType and inputNode ~= outputNode then + if not connectionMade then + self:SaveState("Create Connection") + connectionMade = true + end + --connect up + inputNode.connections[inputNum] = { outputNodeId, outputNum } + end + end + end + + self.DrawingConnection = false + self.DrawingFromInput = false + self.DrawingFromOutput = false +end + +-------------------------------------------------------- +--EXTRA WINDOWS +-------------------------------------------------------- +function Editor:CreateNamingWindow() + self.NamingWindow = vgui.Create("DFrame", self) + local pnl = self.NamingWindow + pnl:SetSize(300, 55) + pnl:ShowCloseButton(true) + pnl:SetDeleteOnClose(false) + pnl:MakePopup() + pnl:SetVisible(false) + pnl:SetTitle("Edit") + pnl:SetScreenLock(true) + do + local old = pnl.Close + function pnl.Close() + self.ForceDrawCursor = false + self.EditingNode = false + old(pnl) + end + end + + self.NamingNameEntry = vgui.Create("DTextEntry", pnl) + self.NamingNameEntry:Dock(BOTTOM) + self.NamingNameEntry:SetSize(175, 20) + self.NamingNameEntry:RequestFocus() +end + +function Editor:OpenNamingWindow(node, x, y) + if not self.NamingWindow then self:CreateNamingWindow() end + + if node.gate then + self.NamingNameEntry:SetText(node.ioName) + self.NamingNameEntry.OnEnter = function(pnl) + node.ioName = pnl:GetValue() + pnl:RequestFocus() + pnl:GetParent():Close() + end + elseif node.visual then + self.NamingNameEntry:SetText(node.value) + self.NamingNameEntry.OnEnter = function(pnl) + node.value = pnl:GetValue() + pnl:RequestFocus() + pnl:GetParent():Close() + end + else + return + end + + self.NamingWindow:SetVisible(true) + self.NamingWindow:MakePopup() -- This will move it above the E2 editor frame if it is behind it. + self.ForceDrawCursor = true + + local px, py = self:GetParent():GetPos() + self.NamingWindow:SetPos(px + x + 80, py + y + 30) + + local inputField = self.NamingNameEntry + local this = self + inputField.OnLoseFocus = function (pnl) + timer.Simple(0, function () if not pnl:GetParent():HasFocus() and this.EditingNode then pnl:OnEnter() end end) + pnl:GetParent():MoveToFront() + end + + self.NamingWindow.OnFocusChanged = function (pnl, gained) + if not gained then + timer.Simple(0, function () if not inputField:HasFocus() and this.EditingNode then inputField:OnEnter() end end) + pnl:MoveToFront() + end + end +end + +function Editor:CreateConstantSetWindow() + self.ConstantSetWindow = vgui.Create("DFrame", self) + local pnl = self.ConstantSetWindow + pnl:SetSize(200, 55) + pnl:ShowCloseButton(true) + pnl:SetDeleteOnClose(false) + pnl:MakePopup() + pnl:SetVisible(false) + pnl:SetTitle("Set constant value") + pnl:SetScreenLock(true) + + self.ConstantSetNormal = vgui.Create("DNumberWang", pnl) + self.ConstantSetNormal:Dock(BOTTOM) + self.ConstantSetNormal:SetSize(175, 20) + self.ConstantSetNormal:SetMinMax(-10 ^ 100, 10 ^ 100) + self.ConstantSetNormal:SetDecimals(6) + self.ConstantSetNormal:SetVisible(false) + self.ConstantSetString = vgui.Create("DTextEntry", pnl) + self.ConstantSetString:Dock(BOTTOM) + self.ConstantSetString:SetSize(175, 20) + self.ConstantSetString:SetVisible(false) + + do + local old = pnl.Close + function pnl.Close() + self.ForceDrawCursor = false + self.EditingNode = false + old(pnl) + end + end +end + +local function validateVector(string) + local x,y,z = string.match(string, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$") + return tonumber(x) ~= nil and tonumber(y) ~= nil and tonumber(z) ~= nil, x, y, z +end + +function Editor:OpenConstantSetWindow(node, x, y, type) + if not self.ConstantSetWindow then self:CreateConstantSetWindow() end + self.ConstantSetNormal:SetVisible(false) + self.ConstantSetNormal.OnEnter = function () end + self.ConstantSetString:SetVisible(false) + self.ConstantSetString.OnEnter = function () end + self.ConstantSetString.OnChange = function () end + self.ConstantSetString:SetValue("") + self.ConstantSetWindow:SetVisible(true) + self.ConstantSetWindow:MakePopup() -- This will move it above the FPGA editor if it is behind it. + self.ForceDrawCursor = true + + local invalidColor = Color(255, 0, 50, 255) + self.ConstantSetString:SetTextColor(color_black) + + local px, py = self:GetParent():GetPos() + self.ConstantSetWindow:SetPos(px + x + 80, py + y + 30) + + if type == "NORMAL" then + self.ConstantSetNormal:SetVisible(true) + self.ConstantSetNormal:SetValue(node.value) + self.ConstantSetNormal:RequestFocus() + self.ConstantSetNormal.OnEnter = function(pnl) + node.value = pnl:GetValue() + pnl:SetVisible(false) + pnl:GetParent():Close() + end + elseif type == "STRING" then + self.ConstantSetString:SetVisible(true) + self.ConstantSetString:SetText(node.value) + self.ConstantSetString:RequestFocus() + self.ConstantSetString.OnEnter = function(pnl) + node.value = pnl:GetValue() + pnl:SetVisible(false) + pnl:GetParent():Close() + end + elseif type == "VECTOR" then + self.ConstantSetString:SetVisible(true) + self.ConstantSetString:SetText(node.valueAsString or (node.value.x .. ", " .. node.value.y .. ", " .. node.value.z)) + self.ConstantSetString:RequestFocus() + self.ConstantSetString.OnEnter = function(pnl) + valid, x, y, z = validateVector(pnl:GetValue()) + if valid then + node.value = Vector(x, y, z) + node.valueAsString = pnl:GetValue() + pnl:SetVisible(false) + pnl:GetParent():Close() + end + end + self.ConstantSetString.OnChange = function(pnl) + valid, _, _, _ = validateVector(pnl:GetValue()) + if valid then pnl:SetTextColor(color_black) + else pnl:SetTextColor(invalidColor) end + end + elseif type == "ANGLE" then + self.ConstantSetString:SetVisible(true) + self.ConstantSetString:SetText(node.valueAsString or (node.value.x .. ", " .. node.value.y .. ", " .. node.value.z)) + self.ConstantSetString:RequestFocus() + self.ConstantSetString.OnEnter = function(pnl) + valid, p, y, r = validateVector(pnl:GetValue()) + if valid then + node.value = Angle(p, y, r) + node.valueAsString = pnl:GetValue() + pnl:SetVisible(false) + pnl:GetParent():Close() + end + end + self.ConstantSetString.OnChange = function(pnl) + valid, _, _, _ = validateVector(pnl:GetValue()) + if valid then pnl:SetTextColor(color_black) + else pnl:SetTextColor(invalidColor) end + end + end + + local inputField = self.ConstantSetString + if type == "NORMAL" then + inputField = self.ConstantSetNormal + end + + local this = self + inputField.OnLoseFocus = function (pnl) + timer.Simple(0, function () if not pnl:GetParent():HasFocus() and this.EditingNode then pnl:OnEnter() end end) + pnl:GetParent():MoveToFront() + end + + self.ConstantSetWindow.OnFocusChanged = function (pnl, gained) + if not gained then + timer.Simple(0, function () if not inputField:HasFocus() and this.EditingNode then inputField:OnEnter() end end) + pnl:MoveToFront() + end + end +end + +vgui.Register("MSLCDEditor", Editor, "Panel"); \ No newline at end of file diff --git a/lua/wire/client/segment_editor/wire_mslcd_editor.lua b/lua/wire/client/segment_editor/wire_mslcd_editor.lua new file mode 100644 index 0000000000..1d6d0720f0 --- /dev/null +++ b/lua/wire/client/segment_editor/wire_mslcd_editor.lua @@ -0,0 +1,1338 @@ +local Editor = {} + +Editor.NewTabOnOpen = CreateClientConVar("wire_mslcd_new_tab_on_open", "1", true, false) + +surface.CreateFont("DefaultBold", { + font = "Tahoma", + size = 12, + weight = 700, + antialias = true, + additive = false, +}) + +------------------------------------------------------------------------ + +local invalid_filename_chars = { + ["*"] = "", + ["?"] = "", + [">"] = "", + ["<"] = "", + ["|"] = "", + ["\\"] = "", + ['"'] = "", + [" "] = "_", +} + +-- overwritten commands +function Editor:Init() + -- don't use any of the default DFrame UI components + for _, v in pairs(self:GetChildren()) do v:Remove() end + self.Title = "" + self.subTitle = "" + self.LastClick = 0 + self.GuiClick = 0 + self.SimpleGUI = false + self.Location = "" + + self.C = {} + self.Components = {} + + -- Load border colors, position, & size + self:LoadEditorSettings() + + local fontTable = { + font = "default", + size = 11, + weight = 300, + antialias = false, + additive = false, + } + surface.CreateFont("E2SmallFont", fontTable) + self.logo = surface.GetTextureID("vgui/e2logo") + + self:InitComponents() + + -- This turns off the engine drawing + self:SetPaintBackgroundEnabled(false) + self:SetPaintBorderEnabled(false) + + self:SetV(false) + + self:InitShutdownHook() +end + +local size = CreateClientConVar("wire_mslcd_editor_size", "800_600", true, false) +local pos = CreateClientConVar("wire_mslcd_editor_pos", "-1_-1", true, false) + +function Editor:LoadEditorSettings() + + -- Position & Size + local w, h = size:GetString():match("(%d+)_(%d+)") + w = tonumber(w) + h = tonumber(h) + + self:SetSize(w, h) + + local x, y = pos:GetString():match("(%-?%d+)_(%-?%d+)") + x = tonumber(x) + y = tonumber(y) + + if x == -1 and y == -1 then + self:Center() + else + self:SetPos(x, y) + end + + if x < 0 or y < 0 or x + w > ScrW() or y + h > ScrH() then -- If the editor is outside the screen, reset it + local width, height = math.min(ScrW() - 200, 800), math.min(ScrH() - 200, 620) + self:SetPos((ScrW() - width) / 2, (ScrH() - height) / 2) + self:SetSize(width, height) + + self:SaveEditorSettings() + end +end + +function Editor:SaveEditorSettings() + + -- Position & Size + local w, h = self:GetSize() + RunConsoleCommand("wire_mslcd_editor_size", w .. "_" .. h) + + local x, y = self:GetPos() + RunConsoleCommand("wire_mslcd_editor_pos", x .. "_" .. y) +end + + +function Editor:PaintOver() + surface.SetFont("DefaultBold") + surface.SetTextColor(255, 255, 255, 255) + surface.SetTextPos(10, 6) + surface.DrawText(self.Title .. self.subTitle) + surface.SetDrawColor(255, 255, 255, 255) + surface.SetTextPos(0, 0) + surface.SetFont("Default") + return true +end + +function Editor:PerformLayout() + local w, h = self:GetSize() + + for i = 1, #self.Components do + local c = self.Components[i] + local c_x, c_y, c_w, c_h = c.Bounds.x, c.Bounds.y, c.Bounds.w, c.Bounds.h + if (c_x < 0) then c_x = w + c_x end + if (c_y < 0) then c_y = h + c_y end + if (c_w < 0) then c_w = w + c_w - c_x end + if (c_h < 0) then c_h = h + c_h - c_y end + c:SetPos(c_x, c_y) + c:SetSize(c_w, c_h) + end + + self.C.Tree:SetHeight(self.C.Holder:GetTall()/2) + self.C.Properties:SetHeight(self.C.Holder:GetTall()/2) +end + +function Editor:OnMousePressed(mousecode) + if mousecode ~= 107 then return end -- do nothing if mouseclick is other than left-click + if not self.pressed then + self.pressed = true + self.p_x, self.p_y = self:GetPos() + self.p_w, self.p_h = self:GetSize() + self.p_mx = gui.MouseX() + self.p_my = gui.MouseY() + self.p_mode = self:getMode() + if self.p_mode == "drag" then + if self.GuiClick > CurTime() - 0.2 then + self:fullscreen() + self.pressed = false + self.GuiClick = 0 + else + self.GuiClick = CurTime() + end + end + end +end + +function Editor:OnMouseReleased(mousecode) + if mousecode ~= 107 then return end -- do nothing if mouseclick is other than left-click + self.pressed = false +end + +function Editor:Think() + if self.fs then return end + if self.pressed then + if not input.IsMouseDown(MOUSE_LEFT) then -- needs this if you let go of the mouse outside the panel + self.pressed = false + end + local movedX = gui.MouseX() - self.p_mx + local movedY = gui.MouseY() - self.p_my + if self.p_mode == "drag" then + local x = self.p_x + movedX + local y = self.p_y + movedY + if (x < 10 and x > -10) then x = 0 end + if (y < 10 and y > -10) then y = 0 end + if (x + self.p_w < ScrW() + 10 and x + self.p_w > ScrW() - 10) then x = ScrW() - self.p_w end + if (y + self.p_h < ScrH() + 10 and y + self.p_h > ScrH() - 10) then y = ScrH() - self.p_h end + self:SetPos(x, y) + end + if self.p_mode == "sizeBR" then + local w = self.p_w + movedX + local h = self.p_h + movedY + if (self.p_x + w < ScrW() + 10 and self.p_x + w > ScrW() - 10) then w = ScrW() - self.p_x end + if (self.p_y + h < ScrH() + 10 and self.p_y + h > ScrH() - 10) then h = ScrH() - self.p_y end + if (w < 300) then w = 300 end + if (h < 200) then h = 200 end + self:SetSize(w, h) + end + if self.p_mode == "sizeR" then + local w = self.p_w + movedX + if (w < 300) then w = 300 end + self:SetWide(w) + end + if self.p_mode == "sizeB" then + local h = self.p_h + movedY + if (h < 200) then h = 200 end + self:SetTall(h) + end + end + if not self.pressed then + local cursor = "arrow" + local mode = self:getMode() + if (mode == "sizeBR") then cursor = "sizenwse" + elseif (mode == "sizeR") then cursor = "sizewe" + elseif (mode == "sizeB") then cursor = "sizens" + end + if cursor ~= self.cursor then + self.cursor = cursor + self:SetCursor(self.cursor) + end + end + + local x, y = self:GetPos() + local w, h = self:GetSize() + + if w < 518 then w = 518 end + if h < 200 then h = 200 end + if x < 0 then x = 0 end + if y < 0 then y = 0 end + if x + w > ScrW() then x = ScrW() - w end + if y + h > ScrH() then y = ScrH() - h end + if y < 0 then y = 0 end + if x < 0 then x = 0 end + if w > ScrW() then w = ScrW() end + if h > ScrH() then h = ScrH() end + + self:SetPos(x, y) + self:SetSize(w, h) +end + +-- special functions + +function Editor:fullscreen() + if self.fs then + self:SetPos(self.preX, self.preY) + self:SetSize(self.preW, self.preH) + self.fs = false + else + self.preX, self.preY = self:GetPos() + self.preW, self.preH = self:GetSize() + self:SetPos(0, 0) + self:SetSize(ScrW(), ScrH()) + self.fs = true + end +end + +function Editor:getMode() + local x, y = self:GetPos() + local w, h = self:GetSize() + local ix = gui.MouseX() - x + local iy = gui.MouseY() - y + + if (ix < 0 or ix > w or iy < 0 or iy > h) then return end -- if the mouse is outside the box + if (iy < 22) then + return "drag" + end + if (iy > h - 10) then + if (ix > w - 20) then return "sizeBR" end + return "sizeB" + end + if (ix > w - 10) then + if (iy > h - 20) then return "sizeBR" end + return "sizeR" + end +end + +function Editor:addComponent(panel, x, y, w, h) + assert(not panel.Bounds) + panel.Bounds = { x = x, y = y, w = w, h = h } + self.Components[#self.Components + 1] = panel + return panel +end + +function Editor:GetLastTab() return self.LastTab end + +function Editor:SetLastTab(Tab) self.LastTab = Tab end + +function Editor:GetActiveTab() return self.C.TabHolder:GetActiveTab() end + +function Editor:GetNumTabs() return #self.C.TabHolder.Items end + +function Editor:SetActiveTab(val) + if self:GetActiveTab() == val then + val:GetPanel():RequestFocus() + return + end + self:SetLastTab(self:GetActiveTab()) + if isnumber(val) then + self.C.TabHolder:SetActiveTab(self.C.TabHolder.Items[val].Tab) + self:GetCurrentEditor():RequestFocus() + elseif val and val:IsValid() then + self.C.TabHolder:SetActiveTab(val) + val:GetPanel():RequestFocus() + end + + self:UpdateActiveTabTitle() +end + +function Editor:ExtractNameFromEditor() + return self:GetCurrentEditor():GetName() +end + +function Editor:UpdateActiveTabTitle() + local title = self:GetChosenFile() + local tabtext = self:ExtractNameFromEditor() + + if title then self:SubTitle("Editing: " .. title) else self:SubTitle() end + if tabtext then + if self:GetActiveTab():GetText() ~= tabtext then + self:GetActiveTab():SetText(tabtext) + self.C.TabHolder.tabScroller:InvalidateLayout() + end + end +end + +function Editor:GetActiveTabIndex() + local tab = self:GetActiveTab() + for k, v in pairs(self.C.TabHolder.Items) do + if tab == v.Tab then + return k + end + end + return -1 +end + + +function Editor:SetActiveTabIndex(index) + local tab = self.C.TabHolder.Items[index].Tab + + if not tab then return end + + self:SetActiveTab(tab) +end + +local old +function Editor:FixTabFadeTime() + if old ~= nil then return end -- It's already being fixed + old = self.C.TabHolder:GetFadeTime() + self.C.TabHolder:SetFadeTime(0) + timer.Simple(old, function() self.C.TabHolder:SetFadeTime(old) old = nil end) +end + +function Editor:CreateTab(chosenfile) + local editor = vgui.Create("MSLCDEditor") + --editor:SetDropTarget(0,0,200,200) + editor.ParentPanel = self + + local sheet = self.C.TabHolder:AddSheet(chosenfile, editor) + editor.chosenfile = chosenfile + + sheet.Tab.OnMousePressed = function(pnl, keycode, ...) + + if keycode == MOUSE_MIDDLE then + self:CloseTab(pnl) + return + elseif keycode == MOUSE_RIGHT then + local menu = DermaMenu() + menu:AddOption("Close", function() + self:CloseTab(pnl) + end) + menu:AddOption("Close all others", function() + self:FixTabFadeTime() + self:SetActiveTab(pnl) + for i = self:GetNumTabs(), 1, -1 do + if self.C.TabHolder.Items[i] ~= sheet then + self:CloseTab(i) + end + end + end) + menu:AddSpacer() + menu:AddOption("Save", function() + self:FixTabFadeTime() + local old = self:GetLastTab() + local currentTab = self:GetActiveTab() + self:SetActiveTab(pnl) + self:SaveFile(self:GetChosenFile(), false) + self:SetActiveTab(currentTab) + self:SetLastTab(old) + end) + menu:AddOption("Save As", function() + self:FixTabFadeTime() + self:SetActiveTab(pnl) + self:SaveFile(self:GetChosenFile(), false, true) + end) + menu:AddOption("Reload", function() + self:FixTabFadeTime() + local old = self:GetLastTab() + self:SetActiveTab(pnl) + self:LoadFile(editor.chosenfile, false) + self:SetActiveTab(self:GetLastTab()) + self:SetLastTab(old) + self:UpdateActiveTabTitle() + end) + menu:AddSpacer() + menu:AddOption("Copy file path to clipboard", function() + if editor.chosenfile and editor.chosenfile ~= "" then + SetClipboardText(editor.chosenfile) + end + end) + menu:AddOption("Copy all file paths to clipboard", function() + local str = "" + for i = 1, self:GetNumTabs() do + local chosenfile = self:GetEditor(i).chosenfile + if chosenfile and chosenfile ~= "" then + str = str .. chosenfile .. ";" + end + end + str = str:sub(1, -2) + SetClipboardText(str) + end) + menu:Open() + return + end + + self:SetActiveTab(pnl) + end + + editor.OnTextChanged = function(panel) + timer.Create("mslcdautosave", 5, 1, function() + self:AutoSave() + end) + end + editor.OnShortcut = function(_, code) + if code == KEY_S then + self:SaveFile(self:GetChosenFile()) + end + end + editor:RequestFocus() + + self:OnTabCreated(sheet) -- Call a function that you can override to do custom stuff to each tab. + + return sheet +end + +function Editor:OnTabCreated(sheet) end + +-- This function is made to be overwritten + +function Editor:GetNextAvailableTab() + local activetab = self:GetActiveTab() + for _, v in pairs(self.C.TabHolder.Items) do + if v.Tab and v.Tab:IsValid() and v.Tab ~= activetab then + return v.Tab + end + end +end + +function Editor:NewTab() + local sheet = self:CreateTab("gate") + self:SetActiveTab(sheet.Tab) + + self:NewChip(true) +end + +function Editor:CloseTab(_tab) + local activetab, sheetindex + if _tab then + if isnumber(_tab) then + local temp = self.C.TabHolder.Items[_tab] + if temp then + activetab = temp.Tab + sheetindex = _tab + else + return + end + else + activetab = _tab + -- Find the sheet index + for k, v in pairs(self.C.TabHolder.Items) do + if activetab == v.Tab then + sheetindex = k + break + end + end + end + else + activetab = self:GetActiveTab() + -- Find the sheet index + for k, v in pairs(self.C.TabHolder.Items) do + if activetab == v.Tab then + sheetindex = k + break + end + end + end + + self:AutoSave() + + -- There's only one tab open, no need to actually close any tabs + if self:GetNumTabs() == 1 then + activetab:SetText("gate") + self.C.TabHolder:InvalidateLayout() + self:NewChip(true) + return + end + + -- Find the panel (for the scroller) + local tabscroller_sheetindex + for k, v in pairs(self.C.TabHolder.tabScroller.Panels) do + if v == activetab then + tabscroller_sheetindex = k + break + end + end + + self:FixTabFadeTime() + + if activetab == self:GetActiveTab() then -- We're about to close the current tab + if self:GetLastTab() and self:GetLastTab():IsValid() then -- If the previous tab was saved + if activetab == self:GetLastTab() then -- If the previous tab is equal to the current tab + local othertab = self:GetNextAvailableTab() -- Find another tab + if othertab and othertab:IsValid() then -- If that other tab is valid, use it + self:SetActiveTab(othertab) + self:SetLastTab() + else -- Reset the current tab (backup) + self:GetActiveTab():SetText("gate") + self.C.TabHolder:InvalidateLayout() + self:NewChip(true) + return + end + else -- Change to the previous tab + self:SetActiveTab(self:GetLastTab()) + self:SetLastTab() + end + else -- If the previous tab wasn't saved + local othertab = self:GetNextAvailableTab() -- Find another tab + if othertab and othertab:IsValid() then -- If that other tab is valid, use it + self:SetActiveTab(othertab) + else -- Reset the current tab (backup) + self:GetActiveTab():SetText("gate") + self.C.TabHolder:InvalidateLayout() + self:NewChip(true) + return + end + end + end + + self:OnTabClosed(activetab) -- Call a function that you can override to do custom stuff to each tab. + + activetab:GetPanel():Remove() + activetab:Remove() + table.remove(self.C.TabHolder.Items, sheetindex) + table.remove(self.C.TabHolder.tabScroller.Panels, tabscroller_sheetindex) + + self.C.TabHolder.tabScroller:InvalidateLayout() + local w, h = self.C.TabHolder:GetSize() + self.C.TabHolder:SetSize(w + 1, h) -- +1 so it updates +end + +function Editor:BuildNode(v,node,group) + local new = nil + if v.Type == GROUP then + new = node:AddNode( v.Text or "Group", "icon16/text_list_numbers.png" ) + self:BuildNodes(new,v) + elseif v.Type == UNION then + new = node:AddNode( v.Text or "Union", "icon16/text_list_bullets.png" ) + self:BuildNodes(new,v) + elseif v.Type == TEXT then + new = node:AddNode( v.Text or "Text", "icon16/bullet_yellow.png" ) + elseif v.Type == MATRIX then + new = node:AddNode( v.Text or "Matrix", "icon16/bullet_red.png" ) + elseif v.Type == ALIGN then + new = node:AddNode( v.Text or "Text", "icon16/bullet_pink.png" ) + elseif v.Type == OFFSET then + new = node:AddNode( v.Text or "Text", "icon16/bullet_white.png" ) + else + new = node:AddNode( v.Text or "Segment", "icon16/bullet_green.png" ) + end + new.group = v + new.parentgroup = group +end + +function Editor:BuildNodes(node,group) + for i,v in ipairs(group.Children) do + self:BuildNode(v,node,group) + end +end + +function Editor:OnTabClosed(sheet) end + +-- This function is made to be overwritten + +-- initialization commands +function Editor:InitComponents() + self.Components = {} + self.C = {} + + local function PaintFlatButton(panel, w, h) + if not (panel:IsHovered() or panel:IsDown()) then return end + derma.SkinHook("Paint", "Button", panel, w, h) + end + + local DMenuButton = vgui.RegisterTable({ + Init = function(panel) + panel:SetText("") + panel:SetSize(24, 20) + panel:Dock(LEFT) + end, + Paint = PaintFlatButton, + DoClick = function(panel) + local name = panel:GetName() + local f = name and name ~= "" and self[name] or nil + if f then f(self) end + end + }, "DButton") + + -- addComponent( panel, x, y, w, h ) + -- if x, y, w, h is minus, it will stay relative to right or buttom border + self.C.Close = self:addComponent(vgui.Create("DButton", self), -45-4, 0, 45, 22) -- Close button + self.C.Inf = self:addComponent(vgui.CreateFromTable(DMenuButton, self), -45-4-26, 0, 24, 22) -- Info button + self.C.ConBut = self:addComponent(vgui.CreateFromTable(DMenuButton, self), -45-4-24-26, 0, 24, 22) -- Control panel open/close + + self.C.Divider = vgui.Create("DHorizontalDivider", self) + + self.C.Browser = vgui.Create("wire_expression2_browser", self.C.Divider) -- Expression 2 file browser + do + local pnl = self.C.Browser.SearchBox + local old = pnl.OnLoseFocus + + function pnl.OnLoseFocus() + old(pnl) + self:GetCurrentEditor():RequestFocus() + end + end + self.C.Browser.Folders:SetClickOnDragHover(true) + + + self.C.MainPane = vgui.Create("DPanel", self.C.Divider) + self.C.Menu = vgui.Create("DPanel", self.C.MainPane) + self.C.TabHolder = vgui.Create("DPropertySheet", self.C.MainPane) + + self.C.Btoggle = vgui.CreateFromTable(DMenuButton, self.C.Menu) -- Toggle Browser being shown + self.C.Sav = vgui.CreateFromTable(DMenuButton, self.C.Menu) -- Save button + self.C.NewTab = vgui.CreateFromTable(DMenuButton, self.C.Menu, "NewTab") -- New tab button + self.C.CloseTab = vgui.CreateFromTable(DMenuButton, self.C.Menu, "CloseTab") -- Close tab button + self.C.Reload = vgui.CreateFromTable(DMenuButton, self.C.Menu) -- Reload tab button + self.C.Segment = vgui.Create("DButton", self.C.Menu) + self.C.Poly = vgui.Create("DButton", self.C.Menu) + --self.C.Matrix = vgui.Create("DButton", self.C.Menu) + --self.C.Group = vgui.Create("DButton", self.C.Menu) + --self.C.Align = vgui.Create("DButton", self.C.Menu) + self.C.SaE = vgui.Create("DButton", self.C.Menu) -- Save & Exit button + self.C.SavAs = vgui.Create("DButton", self.C.Menu) -- Save As button + + self.C.Control = self:addComponent(vgui.Create("Panel", self), -350, 52, 342, -32) -- Control Panel + self.C.Credit = self:addComponent(vgui.Create("DTextEntry", self), -160, 52, 150, 190) -- Credit box + self.C.Credit:SetEditable(false) + + self.C.Holder = vgui.Create("DPanel", self) + self.C.Holder:SetWidth(300) + self.C.Holder:Dock(RIGHT) + self.C.Holder:SetBackgroundColor(Color(255, 255, 255, 255)) + + self.C.Tree = vgui.Create("DTree", self.C.Holder) + self.C.Tree:Dock(TOP) + self.C.Tree:DockMargin(2, 0, 2, 2) + + self.C.PropList = vgui.Create("DCategoryList", self.C.Holder) + self.C.PropList:Dock(FILL) + self.C.PropList:DockMargin(2, 0, 2, 2) + + self.C.EditorProps = vgui.Create("DForm", self.C.PropList) + self.C.EditorProps:Dock(FILL) + self.C.EditorProps:DockMargin(2, 0, 2, 2) + self.C.EditorProps:NumSlider("Snap increment", "wire_multisegmentlcd_snapinc", 0, 128) + self.C.EditorProps:SetLabel("Editor") + + self.C.Properties = vgui.Create("DForm", self.C.PropList) + self.C.Properties:Dock(FILL) + self.C.Properties:DockMargin(2, 0, 2, 2) + self.C.Prop_X = self.C.Properties:NumberWang("X",nil,-65536,65536) + function self.C.Prop_X.OnValueChanged(wang, val) + local editor = self:GetCurrentEditor() + if editor.SelectedSegment == nil then return end + editor.SelectedSegment.X = val + end + self.C.Prop_Y = self.C.Properties:NumberWang("Y",nil,-65536,65536) + function self.C.Prop_Y.OnValueChanged(wang, val) + local editor = self:GetCurrentEditor() + if editor.SelectedSegment == nil then return end + editor.SelectedSegment.Y = val + end + self.C.Properties:SetLabel("Properties") + + + self.C.VertProps = vgui.Create("DForm", self.C.PropList) + self.C.VertProps:Dock(FILL) + self.C.VertProps:DockMargin(2, 0, 2, 2) + self.C.VertProps:SetLabel("Vertex Properties") + + self.C.Vert_X = self.C.VertProps:NumberWang("X",nil,-65536,65536) + function self.C.Vert_X.OnValueChanged(wang, val) + local editor = self:GetCurrentEditor() + if editor.SelectedSegment == nil then return end + if editor.SelectedVert == 0 then return end + editor.SelectedSegment.Poly[editor.SelectedVert].x = val + end + self.C.Vert_Y = self.C.VertProps:NumberWang("Y",nil,-65536,65536) + function self.C.Vert_Y.OnValueChanged(wang, val) + local editor = self:GetCurrentEditor() + if editor.SelectedSegment == nil then return end + if editor.SelectedVert == 0 then return end + editor.SelectedSegment.Poly[editor.SelectedVert].y = val + end + + self.C.PropList:AddItem(self.C.EditorProps) + self.C.PropList:AddItem(self.C.Properties) + self.C.PropList:AddItem(self.C.VertProps) + -- extra component options + + self.C.Divider:SetLeft(self.C.Browser) + self.C.Divider:SetRight(self.C.MainPane) + self.C.Divider:Dock(FILL) + self.C.Divider:SetDividerWidth(4) + self.C.Divider:SetCookieName("wire_mslcd_editor_divider") + self.C.Divider:SetLeftMin(0) + + local DoNothing = function() end + self.C.MainPane.Paint = DoNothing + + self.C.Menu:Dock(TOP) + self.C.TabHolder:Dock(FILL) + + self.C.TabHolder:SetPadding(1) + + self.C.Menu:SetHeight(24) + self.C.Menu:DockPadding(2,2,2,2) + + self.C.SaE:SetSize(80, 20) + self.C.SaE:Dock(RIGHT) + self.C.SavAs:SetSize(51, 20) + self.C.SavAs:Dock(RIGHT) + + self.C.Inf:Dock(NODOCK) + self.C.ConBut:Dock(NODOCK) + + self.C.Close:SetText("r") + self.C.Close:SetFont("Marlett") + self.C.Close.DoClick = function(btn) self:Close() end + + self.C.ConBut:SetImage("icon16/wrench.png") + self.C.ConBut:SetText("") + self.C.ConBut.Paint = PaintFlatButton + self.C.ConBut.DoClick = function() self.C.Control:SetVisible(not self.C.Control:IsVisible()) end + + self.C.Inf:SetImage("icon16/information.png") + self.C.Inf.Paint = PaintFlatButton + self.C.Inf.DoClick = function(btn) + self.C.Credit:SetVisible(not self.C.Credit:IsVisible()) + end + + + self.C.Sav:SetImage("icon16/disk.png") + self.C.Sav.DoClick = function(button) self:SaveFile(self:GetChosenFile()) end + self.C.Sav:SetTooltip( "Save" ) + + self.C.NewTab:SetImage("icon16/page_white_add.png") + self.C.NewTab.DoClick = function(button) self:NewTab() end + self.C.NewTab:SetTooltip( "New tab" ) + + self.C.CloseTab:SetImage("icon16/page_white_delete.png") + self.C.CloseTab.DoClick = function(button) self:CloseTab() end + self.C.CloseTab:SetTooltip( "Close tab" ) + + self.C.Reload:SetImage("icon16/page_refresh.png") + self.C.Reload:SetTooltip( "Refresh file" ) + self.C.Reload.DoClick = function(button) + self:LoadFile(self:GetChosenFile(), false) + self:UpdateActiveTabTitle() + end + + self.C.Segment:SetText("Segment") + self.C.Segment:SetTooltip( "Segment Tool" ) + self.C.Segment:Dock(LEFT) + self.C.Segment.DoClick = function(button) + self:GetCurrentEditor():SetMode(SEGMENT) + end + + self.C.Poly:SetText("Poly") + self.C.Poly:SetTooltip( "Poly Tool" ) + self.C.Poly:Dock(LEFT) + self.C.Poly.DoClick = function(button) + self:GetCurrentEditor():SetMode(POLY) + end + + + + self.C.SaE:SetText("Save and Exit") + self.C.SaE.DoClick = function(button) self:SaveFile(self:GetChosenFile(), true) end + + self.C.SavAs:SetText("Save As") + self.C.SavAs.DoClick = function(button) self:SaveFile(self:GetChosenFile(), false, true) end + + self.C.Browser:AddRightClick(self.C.Browser.filemenu, 4, "Save to", function() + Derma_Query("Overwrite this file?", "Save To", + "Overwrite", function() + self:SaveFile(self.C.Browser.File.FileDir) + end, + "Cancel") + end) + self.C.Browser.OnFileOpen = function(_, filepath, newtab) + self:Open(filepath, nil, newtab) + end + + self.C.Btoggle:SetImage("icon16/application_side_contract.png") + function self.C.Btoggle.DoClick(button) + if button.hide then + self.C.Divider:LoadCookies() + else + self.C.Divider:SetLeftWidth(0) + end + self.C.Divider:InvalidateLayout() + button:InvalidateLayout() + end + + local oldBtoggleLayout = self.C.Btoggle.PerformLayout + function self.C.Btoggle.PerformLayout(button) + oldBtoggleLayout(button) + if self.C.Divider:GetLeftWidth() > 0 then + button.hide = false + button:SetImage("icon16/application_side_contract.png") + else + button.hide = true + button:SetImage("icon16/application_side_expand.png") + end + end + + self.C.Credit:SetTextColor(Color(0, 0, 0, 255)) + self.C.Credit:SetText("\t\tCREDITS\n\n\tEditor by: \tSyranide and Shandolum\n\n\tTabs (and more) added by Divran.\n\n\tFixed for GMod13 By Ninja101 \n\n\tRewritten into a node editor by Lysdal\n\n\tRewritten again into a segment editor by wav3") -- woohoo! + self.C.Credit:SetMultiline(true) + self.C.Credit:SetVisible(false) + + self:InitControlPanel(self.C.Control) -- making it seperate for better overview + self.C.Control:SetVisible(false) + + self:CreateTab("gate") +end + +function Editor:AutoSave() + local buffer = self:GetData() + if self.savebuffer == buffer or buffer == "" then return end + self.savebuffer = buffer + file.CreateDir(self.Location) + file.Write(self.Location .. "/_autosave_.txt", buffer) +end + +function Editor:AddControlPanelTab(label, icon, tooltip) + local frame = self.C.Control + local panel = vgui.Create("DPanel") + local ret = frame.TabHolder:AddSheet(label, panel, icon, false, false, tooltip) + local old = ret.Tab.OnMousePressed + function ret.Tab.OnMousePressed(...) + timer.Simple(0.1,function() frame:ResizeAll() end) -- timers solve everything + old(...) + end + + ret.Panel:SetBackgroundColor(Color(96, 96, 96, 255)) + + return ret +end + +function Editor:InitControlPanel(frame) + -- Add a property sheet to hold the tabs + local tabholder = vgui.Create("DPropertySheet", frame) + tabholder:SetPos(2, 4) + frame.TabHolder = tabholder + + -- They need to be resized one at a time... dirty fix incoming (If you know of a nicer way to do this, don't hesitate to fix it.) + local function callNext(t, n) + local obj = t[n] + local pnl = obj[1] + if pnl and pnl:IsValid() then + local x, y = obj[2], obj[3] + pnl:SetPos(x, y) + local w, h = pnl:GetParent():GetSize() + local wofs, hofs = w - x * 2, h - y * 2 + pnl:SetSize(wofs, hofs) + end + n = n + 1 + if n <= #t then + timer.Simple(0, function() callNext(t, n) end) + end + end + + function frame:ResizeAll() + timer.Simple(0, function() + callNext(self.ResizeObjects, 1) + end) + end + + -- Resize them at the right times + local oldFrameSetSize = frame.SetSize + function frame:SetSize(...) + self:ResizeAll() + oldFrameSetSize(self, ...) + end + + local oldFrameSetVisible = frame.SetVisible + function frame:SetVisible(...) + self:ResizeAll() + oldFrameSetVisible(self, ...) + end + + -- Function to add more objects to resize automatically + frame.ResizeObjects = {} + function frame:AddResizeObject(...) + self.ResizeObjects[#self.ResizeObjects + 1] = { ... } + end + + -- Our first object to auto resize is the tabholder. This sets it to position 2,4 and with a width and height offset of w-4, h-8. + frame:AddResizeObject(tabholder, 2, 4) + + -- ------------------------------------------- EDITOR TAB + local sheet = self:AddControlPanelTab("Editor", "icon16/wrench.png", "Options for the editor itself.") + + -- WINDOW BORDER COLORS + + local dlist = vgui.Create("DPanelList", sheet.Panel) + dlist.Paint = function() end + frame:AddResizeObject(dlist, 4, 4) + dlist:EnableVerticalScrollbar(true) + + local NewTabOnOpen = vgui.Create("DCheckBoxLabel") + dlist:AddItem(NewTabOnOpen) + NewTabOnOpen:SetConVar("wire_mslcd_new_tab_on_open") + NewTabOnOpen:SetText("New tab on open") + NewTabOnOpen:SizeToContents() + NewTabOnOpen:SetTooltip("Enable/disable loaded files opening in a new tab.\nIf disabled, loaded files will be opened in the current tab.") + + local SaveTabsOnClose = vgui.Create("DCheckBoxLabel") + dlist:AddItem(SaveTabsOnClose) + SaveTabsOnClose:SetConVar("wire_mslcd_editor_savetabs") + SaveTabsOnClose:SetText("Save tabs on close") + SaveTabsOnClose:SizeToContents() + SaveTabsOnClose:SetTooltip("Save the currently opened tab file paths on shutdown.\nOnly saves tabs whose files are saved.") + + local OpenOldTabs = vgui.Create("DCheckBoxLabel") + dlist:AddItem(OpenOldTabs) + OpenOldTabs:SetConVar("wire_mslcd_editor_openoldtabs") + OpenOldTabs:SetText("Open old tabs on load") + OpenOldTabs:SizeToContents() + OpenOldTabs:SetTooltip("Open the tabs from the last session on load.\nOnly tabs whose files were saved before disconnecting from the server are stored.") + + local WorldClicker = vgui.Create("DCheckBoxLabel") + dlist:AddItem(WorldClicker) + WorldClicker:SetConVar("wire_mslcd_editor_worldclicker") + WorldClicker:SetText("Enable Clicking Outside Editor") + WorldClicker:SizeToContents() + function WorldClicker.OnChange(pnl, bVal) + self:GetParent():SetWorldClicker(bVal) + end + + local Minimap = vgui.Create("DCheckBoxLabel") + dlist:AddItem(Minimap) + Minimap:SetConVar("wire_mslcd_editor_minimap") + Minimap:SetText("Show minimap") + Minimap:SizeToContents() + Minimap:SetTooltip("Enable or disable the minimap in the editor.") + --------------------------------------------- MSLCD TAB + sheet = self:AddControlPanelTab("MSLCD", "icon16/computer.png", "Options for MSLCD.") + + dlist = vgui.Create("DPanelList", sheet.Panel) + dlist.Paint = function() end + frame:AddResizeObject(dlist, 4, 4) + dlist:EnableVerticalScrollbar(true) + + dlist:InvalidateLayout() +end + +------------------------------------- + + +function Editor:NewChip(incurrent) + if not incurrent and self.NewTabOnOpen:GetBool() then + self:NewTab() + else + self:AutoSave() + self:ChosenFile() + + -- Set title + self:GetActiveTab():SetText("gate") + + self.C.TabHolder:InvalidateLayout() + self:ClearData() + end +end + +local wire_mslcd_editor_savetabs = CreateClientConVar("wire_mslcd_editor_savetabs", "1", true, false) + +local id = 0 +function Editor:InitShutdownHook() + id = id + 1 + + -- save code when shutting down + hook.Add("ShutDown", "wire_mslcd_ShutDown" .. id, function() + local buffer = self:GetData() + if not self:GetCurrentEditor():HasNodes() then return end + + file.CreateDir(self.Location) + file.Write(self.Location .. "/_shutdown_.txt", buffer) + + if wire_mslcd_editor_savetabs:GetBool() then + self:SaveTabs() + end + end) +end + +function Editor:SaveTabs() + local strtabs = "" + local tabs = {} + for i=1, self:GetNumTabs() do + local chosenfile = self:GetEditor(i).chosenfile + if chosenfile and chosenfile ~= "" and not tabs[chosenfile] then + strtabs = strtabs .. chosenfile .. ";" + tabs[chosenfile] = true -- Prevent duplicates + end + end + + strtabs = strtabs:sub(1, -2) + + file.CreateDir(self.Location) + file.Write(self.Location .. "/_tabs_.txt", strtabs) +end + +local wire_mslcd_editor_openoldtabs = CreateClientConVar("wire_mslcd_editor_openoldtabs", "1", true, false) + +function Editor:OpenOldTabs() + if not file.Exists(self.Location .. "/_tabs_.txt", "DATA") then return end + + -- Read file + local tabs = file.Read(self.Location .. "/_tabs_.txt") + if not tabs or tabs == "" then return end + + -- Explode around ; + tabs = string.Explode(";", tabs) + if not tabs or #tabs == 0 then return end + + -- Temporarily remove fade time + self:FixTabFadeTime() + + local is_first = true + for _, v in pairs(tabs) do + if v and v ~= "" then + if (file.Exists(v, "DATA")) then + -- Open it in a new tab + self:LoadFile(v, true) + + -- If this is the first loop, close the initial tab. + if (is_first) then + timer.Simple(0, function() + self:CloseTab(1) + end) + is_first = false + end + end + end + end +end + +function Editor:SubTitle(sub) + if not sub then self.subTitle = "" + else self.subTitle = " - " .. sub + end +end + +local wire_mslcd_editor_worldclicker = CreateClientConVar("wire_mslcd_editor_worldclicker", "0", true, false) +function Editor:SetV(bool) + if bool then + self:MakePopup() + self:InvalidateLayout(true) + end + self:SetVisible(bool) + self:SetKeyboardInputEnabled(bool) + self:GetParent():SetWorldClicker(wire_mslcd_editor_worldclicker:GetBool() and bool) -- Enable this on the background so we can update MSLCD's without closing the editor +end + +function Editor:GetChosenFile() + return self:GetCurrentEditor().chosenfile +end + +function Editor:ChosenFile(Line) + self:GetCurrentEditor().chosenfile = Line + if Line then + self:SubTitle("Editing: " .. Line) + else + self:SubTitle() + end +end + +function Editor:FindOpenFile(FilePath) + for i = 1, self:GetNumTabs() do + local ed = self:GetEditor(i) + if ed.chosenfile == FilePath then + return ed + end + end +end + +function Editor:ExtractName() + self.savefilefn = self:ExtractNameFromEditor() + return +end + +function Editor:ClearCopyData() + self.copyBuffer = nil + self.copyBufferSize = 0 + self.copyOffset = nil +end + +function Editor:SetCopyData(buffer, offset) + self.copyBuffer = buffer + self.copyBufferSize = table.Count(buffer) + self.copyOffset = offset +end + +function Editor:GetCopyData() + if self.copyBuffer then + return {self.copyBuffer, self.copyOffset} + else + return {nil, nil} + end +end + +function Editor:GetCopyDataSize() + if self.copyBufferSize then + return self.copyBufferSize + end + return 0 +end + + +function Editor:SetData(data) + self:GetCurrentEditor():SetData(data) + local childs = self.C.Tree.RootNode:GetChildren() + if childs[4] ~= nil then + childs[4]:Remove() + end + self:BuildNodes(self.C.Tree,self:GetCurrentEditor().SegmentTree) + self.savebuffer = self:GetData() + self:ExtractName() +end + +function Editor:SetDataFromEnt(data) + self:GetCurrentEditor().SegmentTree = table.Copy(data) + local childs = self.C.Tree.RootNode:GetChildren() + if childs[4] ~= nil then + childs[4]:Remove() + end + self:BuildNodes(self.C.Tree,self:GetCurrentEditor().SegmentTree) + self.savebuffer = self:GetData() + self:ExtractName() +end + +function Editor:ClearData() + self:GetCurrentEditor():ClearData() + self.savebuffer = self:GetData() +end + +function Editor:GetEditor(n) + if self.C.TabHolder.Items[n] then + return self.C.TabHolder.Items[n].Panel + end +end + +function Editor:GetData() + local data = self:GetCurrentEditor():GetData() + + local last_data = "" + if #data < 64 then + last_data = data + else + last_data = data:sub(-64 + #data % 8) + end + + --MSLCDSetToolInfo(self:ExtractNameFromEditor(), #data, last_data) + return data +end + +function Editor:GetCurrentEditor() + return self:GetActiveTab():GetPanel() +end + +function Editor:Open(Line, data, forcenewtab) + if self:IsVisible() and not Line and not data then self:Close() end + hook.Run("WireMSLCDEditorOpen", self, Line, data, forcenewtab) + self:SetV(true) + self.C.SaE:SetText("Save and Exit") + if data then + if not forcenewtab then + for i = 1, self:GetNumTabs() do + if self:GetEditor(i).chosenfile == Line then + self:SetActiveTab(i) + self:SetData(data) + return + elseif self:GetEditor(i):GetValue() == data then + self:SetActiveTab(i) + return + end + end + end + + local tab + if self.NewTabOnOpen:GetBool() or forcenewtab then + tab = self:CreateTab("Download").Tab + else + tab = self:GetActiveTab() + end + self:SetActiveTab(tab) + + self:ChosenFile() + self:SetData(data) + + self:UpdateActiveTabTitle() + + if Line then self:SubTitle("Editing: " .. Line) end + return + end + if Line then self:LoadFile(Line, forcenewtab) return end +end + +function Editor:SaveFile(Line, close, SaveAs) + self:ExtractName() + + if close and self.chip then + self:Close() + return + end + if not Line or SaveAs or Line == self.Location .. "/" .. ".txt" then + local str + if self.C.Browser.File then + str = self.C.Browser.File.FileDir -- Get FileDir + if str and str ~= "" then -- Check if not nil + + -- Remove "expression2/" or "cpuchip/" etc + local n, _ = str:find("/", 1, true) + str = str:sub(n + 1, -1) + + if str and str ~= "" then -- Check if not nil + if str:Right(4) == ".txt" then -- If it's a file + str = string.GetPathFromFilename(str):Left(-2) -- Get the file path instead + if not str or str == "" then + str = nil + end + end + else + str = nil + end + else + str = nil + end + end + Derma_StringRequestNoBlur("Save to New File", "", (str ~= nil and str .. "/" or "") .. self.savefilefn, + function(strTextOut) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars):lower() + self:SaveFile(self.Location .. "/" .. strTextOut .. ".txt", close) + end) + return + end + + file.CreateDir(string.GetPathFromFilename(Line)) + file.Write(Line, self:GetData()) + + surface.PlaySound("ambient/water/drip3.wav") + + if not self.chip then self:ChosenFile(Line) end + + self:UpdateActiveTabTitle() + + if close then + GAMEMODE:AddNotify("MSLCD saved as " .. Line .. ".", NOTIFY_GENERIC, 7) + self:Close() + end +end + +function Editor:LoadFile(Line, forcenewtab) + if not Line or file.IsDir(Line, "DATA") then return end + + local f = file.Open(Line, "r", "DATA") + if not f then + ErrorNoHalt("Erroring opening file: " .. Line) + else + local str = f:Read(f:Size()) or "" + f:Close() + self:AutoSave() + if not forcenewtab then + for i = 1, self:GetNumTabs() do + if self:GetEditor(i).chosenfile == Line then + self:SetActiveTab(i) + if forcenewtab ~= nil then self:SetData(str) end + return + elseif self:GetEditor(i):GetData() == str then + self:SetActiveTab(i) + return + end + end + end + + local tab + if self.NewTabOnOpen:GetBool() or forcenewtab then + tab = self:CreateTab("").Tab + else + tab = self:GetActiveTab() + end + self:SetActiveTab(tab) + self:ChosenFile(Line) + self:SetData(str) + self:UpdateActiveTabTitle() + end +end + +function Editor:Close() + timer.Stop("mslcdautosave") + self:AutoSave() + + self:ExtractName() + self:SetV(false) + self.chip = false + + self:SaveEditorSettings() + + hook.Run("WireMSLCDEditorClose", self) +end + + +function Editor:Setup(nTitle, nLocation) + self.Title = nTitle + self.Location = nLocation + self.C.Browser:Setup(nLocation) + + self:NewChip(true) -- Opens initial tab, in case OpenOldTabs is disabled or fails. + + if wire_mslcd_editor_openoldtabs:GetBool() then + self:OpenOldTabs() + end + + self:InvalidateLayout() +end + +vgui.Register("MSLCDEditorFrame", Editor, "DFrame") +if SERVER then MsgC(Color(0, 100, 255), "MSLCD Editor loaded!\n") end \ No newline at end of file diff --git a/lua/wire/client/wire_expression2_browser.lua b/lua/wire/client/wire_expression2_browser.lua index 3b355fc31d..e7d9391d72 100644 --- a/lua/wire/client/wire_expression2_browser.lua +++ b/lua/wire/client/wire_expression2_browser.lua @@ -36,6 +36,7 @@ function PANEL:Search( str, foldername, fullpath, parentfullpath, first_recursio if string_find( string_lower( files[i] ), str, 1, true ) ~= nil then local filenode = node:AddNode( files[i], "icon16/page_white.png" ) filenode:SetFileName( fullpath .. "/" .. files[i] ) + filenode:SetDraggableName( fullpath .. "/" .. files[i] ) myresults = myresults + 1 end diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 943c348326..707efe74fd 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -46,7 +46,7 @@ if CLIENT then net.Receive("wire_multisegmentlcd_tool_upload_request", function(len, ply) local ent = net.ReadUInt(16) - local serialized = WireLib.von.serialize(WireLib.SegmentLCD_Tree) + local serialized = util.Compress(MSLCD_Editor:GetData()) if #serialized > 65535 then return end @@ -90,7 +90,12 @@ if SERVER then net.Receive("wire_multisegmentlcd_tool_upload", function(len, ply) local ent = ents.GetByIndex(net.ReadUInt(16)) local sz = net.ReadUInt(16) - ent.Tree = WireLib.von.deserialize(net.ReadData(sz)) + local data = net.ReadData(sz) + local ok, data = pcall(util.Decompress, data) + if not ok then return end + + ok, data = pcall(WireLib.von.deserialize, data) + ent.Tree = data.SegmentTree ent:Retransmit() end) @@ -118,7 +123,8 @@ TOOL.ClientConVar = { fggreen = 91, fgblue = 45, fgalpha = 255, - xormask = 0 + xormask = 0, + snapinc = 0 } @@ -162,6 +168,81 @@ local invalid_filename_chars = { [" "] = "_", } +if CLIENT then + function TOOL:RightClick( trace ) + if not IsValid( trace.Entity ) then return end + local ent = trace.Entity + if ent:GetClass() ~= "gmod_wire_multisegmentlcd" then return end + MSLCD_Editor:SetDataFromEnt(ent.Tree) + end + ------------------------------------------------------------------------------ + -- Open Multi-segment editor + ------------------------------------------------------------------------------ + function MSLCD_OpenEditor() + if MSLCD_Editor == nil then + MSLCD_Editor = vgui.Create("MSLCDEditorFrame") + MSLCD_Editor:Setup("MSLCD Editor", "multisegmentlcd") + end + MSLCD_Editor:Open() + end + + function MSLCD_NewEditor() + MSLCD_Editor = vgui.Create("MSLCDEditorFrame") + MSLCD_Editor:Setup("MSLCD Editor", "multisegmentlcd") + MSLCD_Editor:Open() + end + + ------------------------------------------------------------------------------ + -- Build tool control panel + ------------------------------------------------------------------------------ + function TOOL.BuildCPanel(panel) + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("multisegmentlcd") + FileBrowser:SetSize(235,400) + function FileBrowser:OnFileOpen(filepath, newtab) + if MSLCD_Editor == nil then + MSLCD_Editor = vgui.Create("MSLCDEditorFrame") + MSLCD_Editor:Setup("MSLCD Editor", "multisegmentlcd") + end + MSLCD_Editor:Open(filepath, nil, newtab) + end + + + ---------------------------------------------------------------------------- + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + MSLCD_OpenEditor() + MSLCD_Editor:AutoSave() + MSLCD_Editor:NewChip(false) + end + panel:AddControl("Label", {Text = ""}) + + ---------------------------------------------------------------------------- + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Open Editor") + OpenEditor.DoClick = MSLCD_OpenEditor + + + local NewEditor = vgui.Create("DButton", panel) + panel:AddPanel(NewEditor) + NewEditor:SetText("New Editor") + NewEditor.DoClick = MSLCD_NewEditor + + ---------------------------------------------------------------------------- + panel:AddControl("Label", {Text = ""}) + panel:AddControl("Label", {Text = "MS-LCD settings:"}) + + + ---------------------------------------------------------------------------- + WireDermaExts.ModelSelect(panel, "wire_multisegmentlcd_model", list.Get("WireScreenModels"), 5) + panel:AddControl("Label", {Text = ""}) + end +end +--[[ function TOOL.BuildCPanel(panel) WireDermaExts.ModelSelect(panel, "wire_multisegmentlcd_model", list.Get( "WireScreenModels" ), 5) panel:CheckBox("#tool.wire_multisegmentlcd.interactive", "wire_multisegmentlcd_interactive") @@ -169,6 +250,10 @@ function TOOL.BuildCPanel(panel) panel:TextEntry("#tool.wire_multisegmentlcd.resw", "wire_multisegmentlcd_resw") panel:TextEntry("#tool.wire_multisegmentlcd.resh", "wire_multisegmentlcd_resh") panel:TextEntry("#tool.wire_multisegmentlcd.xormask", "wire_multisegmentlcd_xormask") + + + net.Receive("MSLCD_OpenEditor", MSLCD_OpenEditor) + PreviewPanel = vgui.Create("DPanel", panel) panel:AddPanel(PreviewPanel) PreviewPanel:SetHeight(256) @@ -1124,4 +1209,5 @@ end function TOOL:DrawToolScreen(width, height) DrawSegmentLCDPreview(self,width,height) -end \ No newline at end of file +end +]] \ No newline at end of file From 0a348722976360abb42153a44b13125801c5a711 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 30 May 2026 23:23:26 +0200 Subject: [PATCH 46/50] Selection --- .../client/segment_editor/mslcdeditor.lua | 72 +++++++++++++------ 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua b/lua/wire/client/segment_editor/mslcdeditor.lua index b124bdfbaf..d5059a7f43 100644 --- a/lua/wire/client/segment_editor/mslcdeditor.lua +++ b/lua/wire/client/segment_editor/mslcdeditor.lua @@ -37,6 +37,7 @@ function Editor:Init() self.SelectedSegments = nil self.SelectedSegment = nil self.SelectedVert = nil + self.Selecting = nil self.LastMousePos = { 0, 0 } self.MouseDown = false @@ -305,22 +306,12 @@ function DrawGroup(self,group) if #group.Children == 0 then return end - local oCr = self.Cr - local oCg = self.Cg - local oCb = self.Cb - local oCa = self.Ca - if group.HasColor then - self.Cr = group.R or 255 - self.Cg = group.G or 255 - self.Cb = group.B or 255 - self.Ca = group.A or 255 - surface.SetDrawColor(self.Cr,self.Cg,self.Cb,self.Ca) - end + local angle = math.rad(group.Rotation or 0) local transformedLocal = TransformOffset(self,group.X or 0,group.Y or 0) - self.LocalX = self.LocalX + transformedLocal[1] - self.LocalY = self.LocalY + transformedLocal[2] + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalXX/2 + self.LocalY = self.LocalY + transformedLocal[2] + self.LocalYY/2 PushTransform(self,math.cos(angle), math.sin(angle), -math.sin(angle), @@ -362,15 +353,15 @@ function DrawGroup(self,group) })) m:Translate(Vector(-x,-y,0)) --cam.PushModelMatrix(m,true) + if group.HasColor then + surface.SetDrawColor(group.R or 255,group.G or 255,group.B or 255,group.A or 255) + end surface.DrawOutlinedRect(minx,miny,(maxx-minx),(maxy-miny)) --cam.PopModelMatrix() PopTransform(self) self.LocalX = self.LocalX - transformedLocal[1] self.LocalY = self.LocalY - transformedLocal[2] - self.Cr = oCr - self.Cg = oCg - self.Cb = oCb - self.Ca = oCa + return minx,miny,maxx,maxy end @@ -428,17 +419,12 @@ function Editor:Paint() self:PaintGrid() - self.Cr = 255 - self.Cg = 255 - self.Cb = 255 - self.Ca = 255 self.LocalXX = self.Zoom self.LocalXY = 0 self.LocalYX = 0 self.LocalYY = self.Zoom self.LocalX = self:GetWide() / 2 - self.Position[1]*self.Zoom self.LocalY = self:GetTall() / 2 - self.Position[2]*self.Zoom - self.BitIndex = 0 surface.SetDrawColor(255, 255, 255, 255) self.TransformStack = {} @@ -446,9 +432,18 @@ function Editor:Paint() self.SegmentTree.X = 0 self.SegmentTree.Y = 0 self.minx,self.miny,self.maxx,self.maxy = DrawGroup(self,self.SegmentTree) + --[[ + if self.SelectedSegments then + self.SelectedSegments.HasColor = true + self.SelectedSegments.G = 0 + self.SelectedSegments.X = -0.5 + self.SelectedSegments.Y = -0.5 + DrawGroup(self,self.SelectedSegments) + end + ]]-- + DisableClipping(false) self.SegmentTree.X = -((self.minx or 0)-self.LocalX)/self.Zoom self.SegmentTree.Y = -((self.miny or 0)-self.LocalY)/self.Zoom - DisableClipping(false) local x, y = self:CursorPos() self.LastMousePos = { x, y } @@ -652,6 +647,7 @@ function Editor:GetPolyVertAtPoly(x, y, poly) end end + print(x,y) if math.abs(x) < 4 and math.abs(y) < 4 then return poly, 0 end @@ -681,7 +677,31 @@ function Editor:GetPolyVertAt(x, y) end +function Editor:SelectSegmentsAtGroup(x1, y1, x2, y2, group) + local sel = { + X = group.X, + Y = group.Y, + Type = GROUP, + Children = {} + } + for i,v in ipairs(group.Children) do + if v.Type == GROUP then + sel.Children[#sel.Children+1] = self:SelectSegmentsAtGroup(x1-v.X, y1-v.Y, x2-v.X, y2-v.Y, v) + elseif v.Type == POLY then + if v.X >= x1 and v.Y >= y1 and v.X <= x2 and v.Y <= y2 then + sel.Children[#sel.Children+1] = v + end + end + end + if #sel.Children > 0 then + return sel + end + return nil +end +function Editor:SelectSegments(x1, y1, x2, y2) + self.SelectedSegments = self:SelectSegmentsAtGroup(x1, y1, x2, y2, self.SegmentTree) +end -- MOUSE @@ -728,6 +748,8 @@ function Editor:OnMousePressed(code) if self.DraggingPolyVert then self.SelectedSegment = self.DraggingPolyVert[1] self.SelectedVert = self.DraggingPolyVert[2] + else + self.Selecting = {x=x,y=y} end elseif code == MOUSE_RIGHT then -- PLANE DRAGGING @@ -742,6 +764,10 @@ function Editor:OnMouseReleased(code) self.MouseDown = false self.DraggingNode = nil self.DraggingPolyVert = nil + if self.Selecting ~= nil then + self:SelectSegments(self.Selecting.x,self.Selecting.y,x,y) + end + self.Selecting = nil elseif code == MOUSE_RIGHT then self.DraggingWorld = false end From d293f3eaa5586330fe1001c19e9aea472ed38039 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sat, 30 May 2026 23:31:31 +0200 Subject: [PATCH 47/50] decrease sensitivity --- lua/wire/client/segment_editor/mslcdeditor.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua b/lua/wire/client/segment_editor/mslcdeditor.lua index d5059a7f43..8c6509fcc2 100644 --- a/lua/wire/client/segment_editor/mslcdeditor.lua +++ b/lua/wire/client/segment_editor/mslcdeditor.lua @@ -607,7 +607,7 @@ function Editor:GetPolyEdgeAtPoly(x, y, poly) local lf = (v.x*lx + v.y*ly)/d local f = (x*lx + y*ly)/d - lf local s = (x*ly - y*lx)/d - ls - if f > 0 and f < d and s >= -25/self.Zoom and s <= 25/self.Zoom then + if f > 0 and f < d and s >= -10/self.Zoom and s <= 10/self.Zoom then return poly, i end end From 36ac2738a92eea56d9a5f57c430fd2952e968c02 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 31 May 2026 18:27:15 +0200 Subject: [PATCH 48/50] Multiselect, bugfixes --- .../client/segment_editor/mslcdeditor.lua | 140 +++++++++++++----- .../segment_editor/wire_mslcd_editor.lua | 31 ++-- 2 files changed, 119 insertions(+), 52 deletions(-) diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua b/lua/wire/client/segment_editor/mslcdeditor.lua index 8c6509fcc2..5e39d779ee 100644 --- a/lua/wire/client/segment_editor/mslcdeditor.lua +++ b/lua/wire/client/segment_editor/mslcdeditor.lua @@ -34,6 +34,8 @@ function Editor:Init() self.DraggingOffset = { 0, 0 } self.DraggingPolyVert = nil + + self.SelectedSegments = nil self.SelectedSegment = nil self.SelectedVert = nil @@ -57,7 +59,16 @@ function Editor:Init() self.LastFrameTime = SysTime() - self.Mode = SEGMENT + self.Mode = POLY + self.Zoom = 5 + self.Position = {0,0} + + self.LocalXX = self.Zoom + self.LocalXY = 0 + self.LocalYX = 0 + self.LocalYY = self.Zoom + self.LocalX = self:GetWide() / 2 - self.Position[1]*self.Zoom + self.LocalY = self:GetTall() / 2 - self.Position[2]*self.Zoom end function Editor:SetMode(mode) @@ -228,8 +239,8 @@ function DrawPoly(self,poly) local m = Matrix() m:Translate(Vector(x,y,0)) m:Mul(Matrix({ - {self.LocalXX,self.LocalXY,0,self.LocalX + transformedLocal[1] - self.LocalXX/2}, - {self.LocalYX,self.LocalYY,0,self.LocalY + transformedLocal[2] - self.LocalYY/2}, + {self.LocalXX,self.LocalXY,0,self.LocalX + transformedLocal[1]}, + {self.LocalYX,self.LocalYY,0,self.LocalY + transformedLocal[2]}, {0,0,1,0}, {0,0,0,1} })) @@ -244,14 +255,14 @@ function DrawPoly(self,poly) for i,p in ipairs(poly.Poly) do local op = poly.Poly[i%#poly.Poly+1] - surface.DrawLine(p.x,p.y,op.x,op.y) + surface.DrawLine(p.x-0.5,p.y-0.5,op.x-0.5,op.y-0.5) end surface.SetDrawColor(255,255,0,255) for i,p in ipairs(poly.Poly) do local selectedvert = i == self.SelectedVert and selected local m = Matrix() - m:Translate(Vector(x+p.x+0.5,y+p.y+0.5,0)) + m:Translate(Vector(x+p.x,y+p.y,0)) m:Scale(Vector(1/self.Zoom,1/self.Zoom,0)) m:Translate(Vector(-x,-y,0)) cam.PushModelMatrix(m, true) @@ -310,8 +321,8 @@ function DrawGroup(self,group) local angle = math.rad(group.Rotation or 0) local transformedLocal = TransformOffset(self,group.X or 0,group.Y or 0) - self.LocalX = self.LocalX + transformedLocal[1] + self.LocalXX/2 - self.LocalY = self.LocalY + transformedLocal[2] + self.LocalYY/2 + self.LocalX = self.LocalX + transformedLocal[1] + self.LocalY = self.LocalY + transformedLocal[2] PushTransform(self,math.cos(angle), math.sin(angle), -math.sin(angle), @@ -336,6 +347,9 @@ function DrawGroup(self,group) elseif v.Type == MATRIX then DrawMatrix(self,v) end + if nminx == nil then + PrintTable(group) + end minx, miny = math.min(nminx, minx), math.min(nminy, miny) maxx, maxy = math.max(nmaxx, maxx), math.max(nmaxy, maxy) end @@ -388,27 +402,28 @@ function Editor:Paint() end local x, y = self:CursorPos() + local dx, dy = self.LastMousePos[1] - x, self.LastMousePos[2] - y -- moving the plane if self.DraggingWorld then self.Position = { self.Position[1] + dx * (1 / self.Zoom), self.Position[2] + dy * (1 / self.Zoom) } end - + local wx, wy = self:ScrToPos(x, y) if self.DraggingPolyVert then - local wx, wy = self:ScrToPos(x, y) + if self.DraggingPolyVert[2] == 0 then local poly = self.DraggingPolyVert[1] - poly.X = wx - poly.Y = wy + poly.X = wx-self.DraggingPolyVert[3] + poly.Y = wy-self.DraggingPolyVert[4] if snapincrement > 0.001 then poly.X = math.floor(poly.X/snapincrement + 0.5)*snapincrement poly.Y = math.floor(poly.Y/snapincrement + 0.5)*snapincrement end else local vert = self.DraggingPolyVert[1].Poly[self.DraggingPolyVert[2]] - vert.x = wx-self.DraggingPolyVert[1].X - vert.y = wy-self.DraggingPolyVert[1].Y + vert.x = wx-self.DraggingPolyVert[3] + vert.y = wy-self.DraggingPolyVert[4] if snapincrement > 0.001 then vert.x = math.floor(vert.x/snapincrement + 0.5)*snapincrement vert.y = math.floor(vert.y/snapincrement + 0.5)*snapincrement @@ -417,7 +432,7 @@ function Editor:Paint() end self:PaintGrid() - + self.LocalXX = self.Zoom self.LocalXY = 0 @@ -432,18 +447,26 @@ function Editor:Paint() self.SegmentTree.X = 0 self.SegmentTree.Y = 0 self.minx,self.miny,self.maxx,self.maxy = DrawGroup(self,self.SegmentTree) - --[[ + if self.SelectedSegments then self.SelectedSegments.HasColor = true self.SelectedSegments.G = 0 - self.SelectedSegments.X = -0.5 - self.SelectedSegments.Y = -0.5 DrawGroup(self,self.SelectedSegments) + self.SelectedSegments.HasColor = nil + self.SelectedSegments.G = nil + end + if self.Selecting ~= nil then + local sminx = math.min(self.Selecting.x,wx) + local sminy = math.min(self.Selecting.y,wy) + local smaxx = math.max(self.Selecting.x,wx) + local smaxy = math.max(self.Selecting.y,wy) + sminx,sminy = self:PosToScr(sminx,sminy) + smaxx,smaxy = self:PosToScr(smaxx,smaxy) + surface.DrawOutlinedRect(sminx,sminy,smaxx-sminx,smaxy-sminy) end - ]]-- + DisableClipping(false) - self.SegmentTree.X = -((self.minx or 0)-self.LocalX)/self.Zoom - self.SegmentTree.Y = -((self.miny or 0)-self.LocalY)/self.Zoom + --self:GetPolyVertAt(wx,wy) local x, y = self:CursorPos() self.LastMousePos = { x, y } @@ -455,6 +478,7 @@ function Editor:Paint() self.ParentPanel.C.Vert_Y:SetValue(self.SelectedSegment.Poly[self.SelectedVert].y) end end + end function Editor:SetData(data) @@ -471,6 +495,8 @@ function Editor:SetData(data) end function Editor:GetData() + self.SegmentTree.X = -((self.minx or 0)-self.LocalX)/self.Zoom + self.SegmentTree.Y = -((self.miny or 0)-self.LocalY)/self.Zoom return WireLib.von.serialize({ SegmentTree = self.SegmentTree, Position = self.Position, @@ -567,12 +593,16 @@ function Editor:OnKeyCodePressed(code) local shift = input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) if control then if code == KEY_C then - self.Clipboard = table.Copy(self.SelectedSegment) + if self.SelectedSegments then + self.ParentPanel.Clipboard = table.Copy(self.SelectedSegments) + else + self.ParentPanel.Clipboard = table.Copy(self.SelectedSegment) + end elseif code == KEY_V then - if self.Clipboard ~= nil then - self.Clipboard.X = gx - self.Clipboard.Y = gy - self.SegmentTree.Children[#self.SegmentTree.Children+1] = table.Copy(self.Clipboard) + if self.ParentPanel.Clipboard ~= nil then + self.ParentPanel.Clipboard.X = gx + self.ParentPanel.Clipboard.Y = gy + self.SegmentTree.Children[#self.SegmentTree.Children+1] = table.Copy(self.ParentPanel.Clipboard) end end elseif code == KEY_C then @@ -615,9 +645,11 @@ function Editor:GetPolyEdgeAtPoly(x, y, poly) end function Editor:GetPolyEdgeAtGroup(x, y, group) + local x = x-group.X + local y = y-group.Y for i,v in ipairs(group.Children) do if v.Type == GROUP then - ri, rv, ex, ey = self:GetPolyEdgeAtGroup(x-v.X, y-v.Y, v) + ri, rv, ex, ey = self:GetPolyEdgeAtGroup(x, y, v) if ri then return ri, rv, ex, ey end @@ -638,16 +670,16 @@ end function Editor:GetPolyVertAtPoly(x, y, poly) - local x,y = x*self.Zoom, y*self.Zoom + local x,y = (x)*self.Zoom, (y)*self.Zoom for i,v in ipairs(poly.Poly) do local vx = v.x*self.Zoom local vy = v.y*self.Zoom + surface.DrawRect(vx-x-4,vy-y-4,8,8) if math.abs(vx-x) < 4 and math.abs(vy-y) < 4 then return poly, i end end - print(x,y) if math.abs(x) < 4 and math.abs(y) < 4 then return poly, 0 end @@ -684,9 +716,13 @@ function Editor:SelectSegmentsAtGroup(x1, y1, x2, y2, group) Type = GROUP, Children = {} } + local x1 = x1-group.X + local y1 = y1-group.Y + local x2 = x2-group.X + local y2 = y2-group.Y for i,v in ipairs(group.Children) do if v.Type == GROUP then - sel.Children[#sel.Children+1] = self:SelectSegmentsAtGroup(x1-v.X, y1-v.Y, x2-v.X, y2-v.Y, v) + sel.Children[#sel.Children+1] = self:SelectSegmentsAtGroup(x1, y1, x2, y2, v) elseif v.Type == POLY then if v.X >= x1 and v.Y >= y1 and v.X <= x2 and v.Y <= y2 then sel.Children[#sel.Children+1] = v @@ -696,13 +732,44 @@ function Editor:SelectSegmentsAtGroup(x1, y1, x2, y2, group) if #sel.Children > 0 then return sel end + --if #sel.Children == 1 then + -- return sel.Children[1] + --end return nil end function Editor:SelectSegments(x1, y1, x2, y2) - self.SelectedSegments = self:SelectSegmentsAtGroup(x1, y1, x2, y2, self.SegmentTree) + local minx = math.min(x1,x2) + local miny = math.min(y1,y2) + local maxx = math.max(x1,x2) + local maxy = math.max(y1,y2) + local grp = self:SelectSegmentsAtGroup(minx, miny, maxx, maxy, self.SegmentTree) + if grp == nil then + self.SelectedSegments = nil + elseif grp.Type ~= GROUP then + self.SelectedSegments = { + X = 0, + Y = 0, + Type = GROUP, + Children = {grp} + } + else + self.SelectedSegments = grp + end end +function Editor:PruneGroups(children) + for i=#children,1,-1 do + local v = children[i] + if v.Type == GROUP then + if #v.Children == 0 then + table.remove(children,i) + else + self:PruneGroups(v.Children) + end + end + end +end -- MOUSE @@ -730,17 +797,24 @@ function Editor:OnMousePressed(code) table.remove(pvKey.Poly,pvIndex) if #pvKey.Poly < 3 then table.remove(pvGroup.Children,pvGroupIndex) + self:PruneGroups(self.SegmentTree.Children) end end else - self.DraggingPolyVert = {pvKey,pvIndex} + if pvIndex == 0 then + self.DraggingPolyVert = {pvKey,pvIndex,x-pvKey.X,y-pvKey.Y} + else + local point = pvKey.Poly[pvIndex] + self.DraggingPolyVert = {pvKey,pvIndex,x - point.x, y - point.y} + end + end elseif not control then local peKey, peIndex, ex, ey = self:GetPolyEdgeAt(x, y) if peKey then table.insert(peKey.Poly,peIndex+1,{x=ex,y=ey}) - self.DraggingPolyVert = {peKey,peIndex+1} + self.DraggingPolyVert = {peKey,peIndex+1,x-ex,y-ey} end end self.SelectedSegment = nil @@ -758,7 +832,7 @@ function Editor:OnMousePressed(code) end function Editor:OnMouseReleased(code) - local x, y = self:CursorPos() + local x, y = self:ScrToPos(self:CursorPos()) if code == MOUSE_LEFT then self.MouseDown = false diff --git a/lua/wire/client/segment_editor/wire_mslcd_editor.lua b/lua/wire/client/segment_editor/wire_mslcd_editor.lua index 1d6d0720f0..d11983d82f 100644 --- a/lua/wire/client/segment_editor/wire_mslcd_editor.lua +++ b/lua/wire/client/segment_editor/wire_mslcd_editor.lua @@ -342,10 +342,10 @@ function Editor:CreateTab(chosenfile) local editor = vgui.Create("MSLCDEditor") --editor:SetDropTarget(0,0,200,200) editor.ParentPanel = self - local sheet = self.C.TabHolder:AddSheet(chosenfile, editor) editor.chosenfile = chosenfile - + sheet.Tab:SetText(chosenfile) + editor:SetName(chosenfile) sheet.Tab.OnMousePressed = function(pnl, keycode, ...) if keycode == MOUSE_MIDDLE then @@ -444,7 +444,7 @@ function Editor:GetNextAvailableTab() end function Editor:NewTab() - local sheet = self:CreateTab("gate") + local sheet = self:CreateTab("screen") self:SetActiveTab(sheet.Tab) self:NewChip(true) @@ -486,7 +486,7 @@ function Editor:CloseTab(_tab) -- There's only one tab open, no need to actually close any tabs if self:GetNumTabs() == 1 then - activetab:SetText("gate") + activetab:SetText("screen") self.C.TabHolder:InvalidateLayout() self:NewChip(true) return @@ -511,7 +511,7 @@ function Editor:CloseTab(_tab) self:SetActiveTab(othertab) self:SetLastTab() else -- Reset the current tab (backup) - self:GetActiveTab():SetText("gate") + self:GetActiveTab():SetText("screen") self.C.TabHolder:InvalidateLayout() self:NewChip(true) return @@ -525,7 +525,7 @@ function Editor:CloseTab(_tab) if othertab and othertab:IsValid() then -- If that other tab is valid, use it self:SetActiveTab(othertab) else -- Reset the current tab (backup) - self:GetActiveTab():SetText("gate") + self:GetActiveTab():SetText("screen") self.C.TabHolder:InvalidateLayout() self:NewChip(true) return @@ -553,8 +553,8 @@ function Editor:BuildNode(v,node,group) elseif v.Type == UNION then new = node:AddNode( v.Text or "Union", "icon16/text_list_bullets.png" ) self:BuildNodes(new,v) - elseif v.Type == TEXT then - new = node:AddNode( v.Text or "Text", "icon16/bullet_yellow.png" ) + elseif v.Type == POLY then + new = node:AddNode( v.Text or "Poly", "icon16/bullet_green.png" ) elseif v.Type == MATRIX then new = node:AddNode( v.Text or "Matrix", "icon16/bullet_red.png" ) elseif v.Type == ALIGN then @@ -632,7 +632,7 @@ function Editor:InitComponents() self.C.NewTab = vgui.CreateFromTable(DMenuButton, self.C.Menu, "NewTab") -- New tab button self.C.CloseTab = vgui.CreateFromTable(DMenuButton, self.C.Menu, "CloseTab") -- Close tab button self.C.Reload = vgui.CreateFromTable(DMenuButton, self.C.Menu) -- Reload tab button - self.C.Segment = vgui.Create("DButton", self.C.Menu) + --self.C.Segment = vgui.Create("DButton", self.C.Menu) self.C.Poly = vgui.Create("DButton", self.C.Menu) --self.C.Matrix = vgui.Create("DButton", self.C.Menu) --self.C.Group = vgui.Create("DButton", self.C.Menu) @@ -767,13 +767,6 @@ function Editor:InitComponents() self:UpdateActiveTabTitle() end - self.C.Segment:SetText("Segment") - self.C.Segment:SetTooltip( "Segment Tool" ) - self.C.Segment:Dock(LEFT) - self.C.Segment.DoClick = function(button) - self:GetCurrentEditor():SetMode(SEGMENT) - end - self.C.Poly:SetText("Poly") self.C.Poly:SetTooltip( "Poly Tool" ) self.C.Poly:Dock(LEFT) @@ -831,7 +824,7 @@ function Editor:InitComponents() self:InitControlPanel(self.C.Control) -- making it seperate for better overview self.C.Control:SetVisible(false) - self:CreateTab("gate") + self:CreateTab("screen") end function Editor:AutoSave() @@ -976,7 +969,7 @@ function Editor:NewChip(incurrent) self:ChosenFile() -- Set title - self:GetActiveTab():SetText("gate") + self:GetActiveTab():SetText("screen") self.C.TabHolder:InvalidateLayout() self:ClearData() @@ -1295,7 +1288,7 @@ function Editor:LoadFile(Line, forcenewtab) local tab if self.NewTabOnOpen:GetBool() or forcenewtab then - tab = self:CreateTab("").Tab + tab = self:CreateTab(Line).Tab else tab = self:GetActiveTab() end From 06b0eb4ac899b65cf0f39611d2abc5848976e2ac Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 31 May 2026 18:51:47 +0200 Subject: [PATCH 49/50] Get rid of useless prints --- lua/entities/gmod_wire_multisegmentlcd/cl_init.lua | 4 ++-- lua/wire/client/segment_editor/mslcdeditor.lua | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua index 9a2ff84c52..7e6b83b793 100644 --- a/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua +++ b/lua/entities/gmod_wire_multisegmentlcd/cl_init.lua @@ -357,7 +357,7 @@ function ENT:CountTris(node) elseif node.Type == MATRIX then return node.W*node.H*4 elseif node.Type == POLY then - print(#node.Poly) + --print(#node.Poly) return (#node.Poly-2)*3 end return 12 @@ -399,7 +399,7 @@ function ENT:Draw() local color = self2.Colors[i] or {self2.Fgred,self2.Fggreen,self2.Fgblue,self2.Fgalpha} surface.SetDrawColor(color[1]*fade[i]+self2.Bgred*(1-fade[i]),color[2]*fade[i]+self2.Bggreen*(1-fade[i]),color[3]*fade[i]+self2.Bgblue*(1-fade[i]),fade[i]*color[4]+self2.Bgalpha*(1-fade[i])*0.15) if x == 0 and y == 0 then - print("x,y = 0,0") + --print("x,y = 0,0") break end surface.DrawRect( x, y, 1, 1 ) diff --git a/lua/wire/client/segment_editor/mslcdeditor.lua b/lua/wire/client/segment_editor/mslcdeditor.lua index 5e39d779ee..50278bbf16 100644 --- a/lua/wire/client/segment_editor/mslcdeditor.lua +++ b/lua/wire/client/segment_editor/mslcdeditor.lua @@ -347,9 +347,9 @@ function DrawGroup(self,group) elseif v.Type == MATRIX then DrawMatrix(self,v) end - if nminx == nil then - PrintTable(group) - end + --if nminx == nil then + --PrintTable(group) + --end minx, miny = math.min(nminx, minx), math.min(nminy, miny) maxx, maxy = math.max(nmaxx, maxx), math.max(nmaxy, maxy) end @@ -863,7 +863,7 @@ function Editor:AlignPosToGrid(x, y) end function Editor:DragHoverClick(hoverTime) - print(hoverTime) + --print(hoverTime) end vgui.Register("MSLCDEditor", Editor, "Panel"); From 645ea764c181f12990153960bda81734b919d593 Mon Sep 17 00:00:00 2001 From: wav3 Date: Sun, 31 May 2026 19:05:25 +0200 Subject: [PATCH 50/50] Setup MSLCD editor when uploading if nil --- lua/wire/stools/multisegmentlcd.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lua/wire/stools/multisegmentlcd.lua b/lua/wire/stools/multisegmentlcd.lua index 707efe74fd..9146dd6cf5 100644 --- a/lua/wire/stools/multisegmentlcd.lua +++ b/lua/wire/stools/multisegmentlcd.lua @@ -46,6 +46,10 @@ if CLIENT then net.Receive("wire_multisegmentlcd_tool_upload_request", function(len, ply) local ent = net.ReadUInt(16) + if MSLCD_Editor == nil then + MSLCD_Editor = vgui.Create("MSLCDEditorFrame") + MSLCD_Editor:Setup("MSLCD Editor", "multisegmentlcd") + end local serialized = util.Compress(MSLCD_Editor:GetData()) if #serialized > 65535 then return