From f7a97066274de1e64414edab64a0fabbf053f6db Mon Sep 17 00:00:00 2001 From: relt-1 <60782515+relt-1@users.noreply.github.com> Date: Tue, 30 May 2023 16:51:15 +0200 Subject: [PATCH] Added more screens. (#2551) * Add files via upload * Add files via upload * Lots more monitors * Add files via upload * Add character lcd * Update init.lua * More console/lcd special characters * New font, and hi res screens. Github dif broke changed files: textscreen cl_init consolescreen cl_init digitalscreen cl_init oscilloscope entity egp cl_init wiremonitors.lua gpulib increased RT size to 1024 added a font from https://mdileo.tripod.com/utilities.html This may break some things. * LCD models by fasteroid ! ! * More lcd models 16x4 40x4 * Use matrices instead of looping through an array * One more because i felt like it 20x4 added * Update cl_init.lua * Update cl_init.lua * Interactive props * Update cl_init.lua * Make digital screen better * Fix tabs Changed tabs into double spaces. * FIx tabs (again) * Refactor * Fix potential issue * Make only certain values floor * Made cursor drawing independent of rest of drawing * Some fixes --------- Co-authored-by: thegrb93 --- lua/autorun/wire_load.lua | 3 + .../gmod_wire_characterlcd/cl_init.lua | 548 +++++++++ lua/entities/gmod_wire_characterlcd/init.lua | 180 +++ .../gmod_wire_characterlcd/shared.lua | 12 + .../gmod_wire_consolescreen/cl_init.lua | 1028 ++++++++++------- .../gmod_wire_digitalscreen/cl_init.lua | 14 +- lua/entities/gmod_wire_egp/cl_init.lua | 2 +- lua/entities/gmod_wire_gpu/cl_init.lua | 20 +- lua/entities/gmod_wire_interactiveprop.lua | 476 ++++++++ lua/entities/gmod_wire_oscilloscope.lua | 24 +- lua/entities/gmod_wire_textscreen.lua | 6 +- lua/wire/client/cl_modelplug.lua | 76 ++ lua/wire/gpulib.lua | 40 +- lua/wire/stools/characterlcd.lua | 67 ++ lua/wire/stools/interactiveprop.lua | 29 + lua/wire/wiremonitors.lua | 46 +- models/fasteroid/bull/lcd1.dx80.vtx | Bin 0 -> 2145 bytes models/fasteroid/bull/lcd1.dx90.vtx | Bin 0 -> 2145 bytes models/fasteroid/bull/lcd1.mdl | Bin 0 -> 2516 bytes models/fasteroid/bull/lcd1.phy | Bin 0 -> 695 bytes models/fasteroid/bull/lcd1.sw.vtx | Bin 0 -> 2113 bytes models/fasteroid/bull/lcd1.vvd | Bin 0 -> 8960 bytes models/fasteroid/bull/lcd2.dx80.vtx | Bin 0 -> 2361 bytes models/fasteroid/bull/lcd2.dx90.vtx | Bin 0 -> 2361 bytes models/fasteroid/bull/lcd2.mdl | Bin 0 -> 2516 bytes models/fasteroid/bull/lcd2.phy | Bin 0 -> 695 bytes models/fasteroid/bull/lcd2.sw.vtx | Bin 0 -> 2329 bytes models/fasteroid/bull/lcd2.vvd | Bin 0 -> 10496 bytes models/fasteroid/bull/lcd3.dx80.vtx | Bin 0 -> 2502 bytes models/fasteroid/bull/lcd3.dx90.vtx | Bin 0 -> 2502 bytes models/fasteroid/bull/lcd3.mdl | Bin 0 -> 2516 bytes models/fasteroid/bull/lcd3.phy | Bin 0 -> 683 bytes models/fasteroid/bull/lcd3.sw.vtx | Bin 0 -> 2470 bytes models/fasteroid/bull/lcd3.vvd | Bin 0 -> 11328 bytes models/fasteroid/bull/lcd4.dx80.vtx | Bin 0 -> 2547 bytes models/fasteroid/bull/lcd4.dx90.vtx | Bin 0 -> 2547 bytes models/fasteroid/bull/lcd4.mdl | Bin 0 -> 2516 bytes models/fasteroid/bull/lcd4.phy | Bin 0 -> 684 bytes models/fasteroid/bull/lcd4.sw.vtx | Bin 0 -> 2515 bytes models/fasteroid/bull/lcd4.vvd | Bin 0 -> 11648 bytes models/fasteroid/bull/lcd5.dx80.vtx | Bin 0 -> 2502 bytes models/fasteroid/bull/lcd5.dx90.vtx | Bin 0 -> 2502 bytes models/fasteroid/bull/lcd5.mdl | Bin 0 -> 2516 bytes models/fasteroid/bull/lcd5.phy | Bin 0 -> 684 bytes models/fasteroid/bull/lcd5.sw.vtx | Bin 0 -> 2470 bytes models/fasteroid/bull/lcd5.vvd | Bin 0 -> 11328 bytes resource/fonts/alphalcd.ttf | Bin 0 -> 21456 bytes 47 files changed, 2115 insertions(+), 456 deletions(-) create mode 100644 lua/entities/gmod_wire_characterlcd/cl_init.lua create mode 100644 lua/entities/gmod_wire_characterlcd/init.lua create mode 100644 lua/entities/gmod_wire_characterlcd/shared.lua create mode 100644 lua/entities/gmod_wire_interactiveprop.lua create mode 100644 lua/wire/stools/characterlcd.lua create mode 100644 lua/wire/stools/interactiveprop.lua create mode 100644 models/fasteroid/bull/lcd1.dx80.vtx create mode 100644 models/fasteroid/bull/lcd1.dx90.vtx create mode 100644 models/fasteroid/bull/lcd1.mdl create mode 100644 models/fasteroid/bull/lcd1.phy create mode 100644 models/fasteroid/bull/lcd1.sw.vtx create mode 100644 models/fasteroid/bull/lcd1.vvd create mode 100644 models/fasteroid/bull/lcd2.dx80.vtx create mode 100644 models/fasteroid/bull/lcd2.dx90.vtx create mode 100644 models/fasteroid/bull/lcd2.mdl create mode 100644 models/fasteroid/bull/lcd2.phy create mode 100644 models/fasteroid/bull/lcd2.sw.vtx create mode 100644 models/fasteroid/bull/lcd2.vvd create mode 100644 models/fasteroid/bull/lcd3.dx80.vtx create mode 100644 models/fasteroid/bull/lcd3.dx90.vtx create mode 100644 models/fasteroid/bull/lcd3.mdl create mode 100644 models/fasteroid/bull/lcd3.phy create mode 100644 models/fasteroid/bull/lcd3.sw.vtx create mode 100644 models/fasteroid/bull/lcd3.vvd create mode 100644 models/fasteroid/bull/lcd4.dx80.vtx create mode 100644 models/fasteroid/bull/lcd4.dx90.vtx create mode 100644 models/fasteroid/bull/lcd4.mdl create mode 100644 models/fasteroid/bull/lcd4.phy create mode 100644 models/fasteroid/bull/lcd4.sw.vtx create mode 100644 models/fasteroid/bull/lcd4.vvd create mode 100644 models/fasteroid/bull/lcd5.dx80.vtx create mode 100644 models/fasteroid/bull/lcd5.dx90.vtx create mode 100644 models/fasteroid/bull/lcd5.mdl create mode 100644 models/fasteroid/bull/lcd5.phy create mode 100644 models/fasteroid/bull/lcd5.sw.vtx create mode 100644 models/fasteroid/bull/lcd5.vvd create mode 100644 resource/fonts/alphalcd.ttf diff --git a/lua/autorun/wire_load.lua b/lua/autorun/wire_load.lua index 82dd80f2..6fbca622 100644 --- a/lua/autorun/wire_load.lua +++ b/lua/autorun/wire_load.lua @@ -84,6 +84,8 @@ if SERVER then if CreateConVar("wire_force_workshop", 1, {FCVAR_ARCHIVE}, "Should Wire force all clients to download the Workshop edition of Wire, for models? (requires restart to disable)"):GetBool() then resource.AddWorkshop("160250458") end + resource.AddFile("resource/fonts/alphalcd.ttf") + end -- shared includes @@ -129,6 +131,7 @@ if CLIENT then include("wire/client/rendertarget_fix.lua") include("wire/client/hlzasm/hc_compiler.lua") include("wire/client/customspawnmenu.lua") + end diff --git a/lua/entities/gmod_wire_characterlcd/cl_init.lua b/lua/entities/gmod_wire_characterlcd/cl_init.lua new file mode 100644 index 00000000..7c642b99 --- /dev/null +++ b/lua/entities/gmod_wire_characterlcd/cl_init.lua @@ -0,0 +1,548 @@ +include("shared.lua") + +function ENT:Initialize() + local mem = {} + self.Memory = mem + for i = 0, 1023 do + mem[i] = 0 + end + + -- Screen control: + -- [1003] - Background red + -- [1004] - Background green + -- [1005] - Background blue + -- [1006] - Text red + -- [1007] - Text green + -- [1008] - Text blue + -- [1009] - Width + -- [1010] - Height + + -- Character control: + -- [1011] - Write char at cursor (Writing puts character and shifts) + -- + -- Caching control: + -- [1012] - Force cache refresh + -- [1013] - Cached blocks size (up to 28, 0 if disabled) + -- + -- + -- Shifting control: + -- [1014] - Shift cursor 1:backwards 0:forwards + -- [1015] - Shift screen with cursor + -- + -- Character output control: + -- [1016] - Contrast + -- + -- Control registers: + -- [1017] - Hardware Clear Row (Writing clears row) + -- [1018] - Hardware Clear Screen + -- + -- Cursor control: + -- [1019] - Cursor Blink Rate (0.50) + -- [1020] - Cursor Size (0.25) + -- [1021] - Cursor Address + -- [1022] - Cursor Enabled + -- + -- [1023] - Clk + mem[1003] = 148 + mem[1004] = 178 + mem[1005] = 15 + mem[1006] = 45 + mem[1007] = 91 + mem[1008] = 45 + mem[1012] = 0 + mem[1013] = 0 + mem[1014] = 0 + mem[1015] = 0 + mem[1016] = 1 + mem[1017] = 0 + mem[1018] = 0 + mem[1019] = 0.5 + mem[1020] = 0.25 + mem[1021] = 0 + mem[1022] = 1 + mem[1023] = 1 + + self.IntTimer = 0 + self.Flash = false + + self.GPU = WireGPU(self) + mem[1009] = 16 + mem[1010] = 2 + + -- Setup caching + GPULib.ClientCacheCallback(self,function(Address,Value) + self:WriteCell(Address,Value) + end) + + WireLib.netRegister(self) +end + +function ENT:OnRemove() + self.GPU:Finalize() +end + +function ENT:ReadCell(Address,value) + return self.Memory[math.floor(Address)] +end + +function ENT:ShiftScreenRight() + local mem = self.Memory + for y=0,mem[1010]-1 do + for x=mem[1009]-1,1,-1 do + mem[x+y*mem[1009]] = mem[x+y*mem[1009]-1] + end + mem[y*mem[1009]] = 0 + end +end + +function ENT:ShiftScreenLeft() + local mem = self.Memory + for y=0,mem[1010]-1 do + for x=0,mem[1009]-2 do + mem[x+y*mem[1009]] = mem[x+y*mem[1009]+1] + end + mem[y*mem[1009]+mem[1009]-1] = 0 + end +end + +function ENT:WriteCell(Address,value) + Address = math.floor(Address) + if Address < 0 or Address >= 1024 then return false end + if value ~= value then return false end + + local mem = self.Memory + + if Address == 1009 then -- Screen width + value = math.floor(value) + if (value*mem[1010] > 1003 or value*18 > 1024) then return false end + elseif Address == 1010 then -- Screen height + value = math.floor(value) + if (value*mem[1009] > 1003 or value*24 > 1024) then return false end + elseif Address == 1011 then -- Write char at cursor + value = math.floor(value) + if mem[1015] >= 1 then + if mem[1014] >= 1 then + self:ShiftScreenRight() + else + self:ShiftScreenLeft() + end + mem[mem[1021]%(mem[1010]*mem[1009])] = value + else + mem[mem[1021]%(mem[1010]*mem[1009])] = value + if mem[1014] >= 1 then + mem[1021] = (mem[1021] - 1)%(mem[1010]*mem[1009]) + else + mem[1021] = (mem[1021] + 1)%(mem[1010]*mem[1009]) + end + end + elseif Address == 1017 then + value = math.floor(value) + if value<0 or value >= mem[1010] then return false end + for i = 0, mem[1009]-1 do + mem[value*mem[1009]+i] = 0 + end + elseif Address == 1018 then + for i = 0, mem[1009]*mem[1010]-1 do + mem[i] = 0 + end + elseif Address == 1021 then + value = math.floor(value)%(mem[1010]*mem[1009]) + end + + mem[Address] = value + + return true +end + +local specialCharacters = { + [128] = { + { x = 0, y = 1 }, + { x = 1, y = 1 }, + { x = 1, y = 0 }, + }, + [129] = { + { x = 0, y = 1 }, + { x = 0, y = 0 }, + { x = 1, y = 1 }, + }, + [130] = { + { x = 0, y = 1 }, + { x = 1, y = 0 }, + { x = 0, y = 0 }, + }, + [131] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + }, + [132] = { + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5 }, + }, + [133] = { + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + }, + [134] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0, y = 0.5 }, + }, + [135] = { + { x = 0, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + + + + + + [136] = { + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + [137] = { + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + { x = 0, y = 0.5 }, + }, + [138] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + [139] = { + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + }, + [140] = { + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5 }, + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + }, + [141] = { + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + }, + [142] = { + + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5}, + { x = 0, y = 0 }, + }, + + [143] = { + { x = 0, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0, y = 1 }, + }, + [144] = { + { x = 0, y = 1 }, + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + }, + [145] = { + { x = 1, y = 1 }, + { x = 0, y = 1 }, + { x = 0, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + }, + [146] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0, y = 1 }, + }, + [147] = { + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + }, + [148] = { + { x = 0.33, y = 0}, + { x = 0.66, y = 0}, + { x = 0.66, y = 1}, + { x = 0.33, y = 1}, + }, + [149] = { + { x = 0.66, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [150] = { + { x = 0, y = 0.33}, + { x = 1, y = 0.33}, + { x = 1, y = 0.66}, + { x = 0, y = 0.66}, + }, + [151] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [152] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [153] = { + { x = 0.66, y = 0.66 }, + { x = 1, y = 0.66 }, + { x = 1, y = 0.33 }, + { x = 0, y = 0.33 }, + { x = 0, y = 0.66 }, + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 1 }, + { x = 0.66, y = 1 }, + }, + [154] = { + { x = 0.33, y = 0.33 }, + { x = 0, y = 0.33 }, + { x = 0, y = 0.66 }, + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 1 }, + { x = 0.66, y = 1 }, + { x = 0.66, y = 0 }, + { x = 0.33, y = 0 }, + }, + [155] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + { x = 0.33, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [156] = { + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + { x = 0.66, y = 0.33 }, + { x = 0.33, y = 0.33 }, + }, + [157] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + }, + [158] = { + { x = 0.33, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + }, + [159] = { + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0.66 }, + { x = 0, y = 0.66 }, + }, + [160] = { + { x = 0.33, y = 0.33 }, + { x = 0.66, y = 0.33 }, + { x = 0.66, y = 0.66 }, + { x = 0.33, y = 0.66 }, + } +} + +function ENT:DrawSpecialCharacter(c,x,y,w,h,r,g,b) + surface.SetDrawColor(r,g,b,255) + surface.SetTexture(0) + + local vertices = specialCharacters[c] + if vertices then + local tf = Matrix() tf:SetScale(Vector(w, h, 1)) tf:SetTranslation(Vector(x, y, 0)) + cam.PushModelMatrix(tf, true) + surface.DrawPoly(vertices) + cam.PopModelMatrix() + end +end + +function ENT:Draw() + self:DrawModel() + + local szx = 18 + local szy = 24 + local mem = self.Memory + + if mem[1023] >= 1 then + mem[1023] = 0 + + self.GPU:RenderToGPU(function() + -- Draw terminal here + -- W/H = 16 + + local bc = math.min(1,math.max(0,mem[1016]-1.8)) + local br = (1-bc)*mem[1003]+bc*mem[1006] + local bg = (1-bc)*mem[1004]+bc*mem[1007] + local bb = (1-bc)*mem[1005]+bc*mem[1008] + + local sqc = math.min(1,math.max(0,mem[1016]-0.9)) + local sqr = (1-sqc)*mem[1003]+sqc*mem[1006] + local sqg = (1-sqc)*mem[1004]+sqc*mem[1007] + local sqb = (1-sqc)*mem[1005]+sqc*mem[1008] + + local fc = math.min(1,math.max(sqc,mem[1016])) + local fr = (1-fc)*mem[1003]+fc*mem[1006] + local fg = (1-fc)*mem[1004]+fc*mem[1007] + local fb = (1-fc)*mem[1005]+fc*mem[1008] + surface.SetDrawColor(br,bg,bb,255) + surface.DrawRect(0,0,1024,1024) + + for ty = 0, mem[1010]-1 do + for tx = 0, mem[1009]-1 do + local a = tx + ty*mem[1009] + + --if (self.Flash == true) then + -- fb,bb = bb,fb + -- fg,bg = bg,fg + -- fr,br = br,fr + --end + local c1 = mem[a] + + if c1 >= 2097152 then c1 = 0 end + if c1 < 0 then c1 = 0 end + + surface.SetDrawColor(sqr,sqg,sqb,255) + surface.DrawRect((tx)*szx+1,(ty)*szy+1,szx-2,szy-2) + surface.SetDrawColor(sqr,sqg,sqb,127) + surface.DrawRect((tx)*szx+2,(ty)*szy+2,szx-2,szy-2) + + if (c1 ~= 0) then + -- Note: the source engine does not handle unicode characters above 65535 properly. + local utf8 = "" + if c1 <= 127 then + utf8 = string.char (c1) + elseif c1 < 2048 then + utf8 = string.format("%c%c", 192 + math.floor (c1 / 64), 128 + (c1 % 64)) + elseif c1 < 65536 then + utf8 = string.format("%c%c%c", 224 + math.floor (c1 / 4096), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) + elseif c1 < 2097152 then + utf8 = string.format("%c%c%c%c", 240 + math.floor (c1 / 262144), 128 + (math.floor (c1 / 4096) % 64), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) + end + + if specialCharacters[c1] then + self:DrawSpecialCharacter( + c1, (tx)*szx+1, (ty)*szy+1, szx-1, szy-1, + fr,fg,fb + ) + else + draw.DrawText( + utf8, + "LCDFontBlur", + tx * szx + 2, ty * szy, + Color(fr,fg,fb,255),0 + ) + draw.DrawText( + utf8, + "LCDFont", + tx * szx + 1, ty * szy -1 , + Color(fr,fg,fb,255),0 + ) + end + end + end + end + end) + end + + self.GPU:Render(0,0,1024,1024,nil,-(1024-mem[1009]*szx)/1024,-(1024-mem[1010]*szy)/1024) + + if mem[1022] >= 1 then + self.IntTimer = self.IntTimer + FrameTime() + if self.IntTimer >= mem[1019] then + if self.IntTimer >= mem[1019]*2 then + self.IntTimer = (self.IntTimer - mem[1019]*2) % math.max(mem[1019]*2,0.01) + else + self.GPU:RenderToWorld(mem[1009]*szx, mem[1010]*szy, function() + local a = math.floor(mem[1021]) + + local tx = a - math.floor(a / mem[1009])*mem[1009] + local ty = math.floor(a / mem[1009]) + + local sqc = math.min(1,math.max(0,mem[1016]-0.9)) + local fc = math.min(1,math.max(sqc,mem[1016])) + local fr = (1-fc)*mem[1003]+fc*mem[1006] + local fg = (1-fc)*mem[1004]+fc*mem[1007] + local fb = (1-fc)*mem[1005]+fc*mem[1008] + + surface.SetDrawColor( + fr, + fg, + fb, + 255 + ) + surface.DrawRect( + (tx)*szx+1, + (ty)*szy+szy*(1-mem[1020]), + szx-2, + szy*mem[1020] + ) + end, nil, true) + end + end + end + + Wire_Render(self) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_characterlcd/init.lua b/lua/entities/gmod_wire_characterlcd/init.lua new file mode 100644 index 00000000..6f30cfdd --- /dev/null +++ b/lua/entities/gmod_wire_characterlcd/init.lua @@ -0,0 +1,180 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include('shared.lua') + +ENT.WireDebugName = "CharacterLcdScreen" + +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.Memory = {} + + for i = 0, 1023 do + self.Memory[i] = 0 + end + + self.CharAddress = 0 + self.Char = 0 + self.CharParam = 0 + self.Memory[1009] = 16 + self.Memory[1010] = 2 + self.Memory[1012] = 0 + self.Memory[1013] = 0 + self.Memory[1014] = 0 + self.Memory[1015] = 0 + self.Memory[1016] = 1 + self.Memory[1017] = 0 + self.Memory[1018] = 0 + self.Memory[1019] = 0.5 + self.Memory[1020] = 0.25 + self.Memory[1021] = 0 + self.Memory[1022] = 1 + + self.ScreenWidth = 16 + self.ScreenHeight = 2 + + self.Cache = GPUCacheManager(self,true) +end +function ENT:Setup(ScreenWidth, ScreenHeight, bgred,bggreen,bgblue,fgred,fggreen,fgblue) + self:WriteCell(1010, tonumber(ScreenHeight) or 2) + self:WriteCell(1009, tonumber(ScreenWidth) or 16) + self:WriteCell(1008, tonumber(fgblue) or 45) + self:WriteCell(1007, tonumber(fggreen) or 91) + self:WriteCell(1006, tonumber(fgred) or 45) + self:WriteCell(1005, tonumber(bgblue) or 15) + self:WriteCell(1004, tonumber(bggreen) or 178) + self:WriteCell(1003, tonumber(bgred) or 148) +end +function ENT:SendPixel() + if (self.Memory[1023] ~= 0) and (self.CharAddress >= 0) and (self.CharAddress < self.ScreenWidth*self.ScreenHeight) then + local pixelno = math.floor(self.CharAddress) + + self:WriteCell(pixelno, self.Char) + + end +end + +function ENT:ReadCell(Address) + Address = math.floor(Address) + if Address < 0 then return nil end + if Address >= 1024 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 + if Address >= 1024 then return false end + if Address < 1003 then -- text/attribute data + if self.Memory[Address] == value then return true end + else + if Address == 1009 and value*self.ScreenHeight < 1003 and value*18 <= 1024 then + self.ScreenWidth = value + end + if Address == 1010 and value*self.ScreenWidth < 1003 and value*24 <= 1024 then + self.ScreenHeight = value + end +-- 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 +end + +function ENT:Retransmit(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:TriggerInput(iname, value) + if iname == "CharAddress" then + self.CharAddress = value + self:WriteCell(1021, value) + elseif iname == "Char" then + self.Char = value + self:WriteCell(1011,value) + elseif iname == "Contrast" then + self.Contrast = value + self:WriteCell(1016, self.Contrast) + elseif iname == "Clk" then + self:WriteCell(1023, value) + self:SendPixel() + elseif iname == "Reset" then + self:WriteCell(1018,0) + end +end + +function ENT:ShiftScreenRight() + for y=0,self.ScreenHeight-1 do + for x=self.ScreenWidth-1,1 do + self.Memory[x+y*self.ScreenWidth] = self.Memory[x+y*self.ScreenWidth-1] + end + self.Memory[y*self.ScreenWidth] = 0 + end +end + +function ENT:ShiftScreenLeft() + for y=0,self.ScreenHeight-1 do + for x=0,self.ScreenWidth-2 do + self.Memory[x+y*self.ScreenWidth] = self.Memory[x+y*self.ScreenWidth+1] + end + self.Memory[y*self.ScreenWidth+self.ScreenWidth-1] = 0 + end +end + +function ENT:ClientWriteCell(Address, value) + if Address == 1009 and (value*self.Memory[1010] > 1003 or value*18 > 1024) then return false end + if Address == 1010 and (value*self.Memory[1009] > 1003 or value*24 > 1024) then return false end + if Address == 1011 then + + if self.Memory[1015] >= 1 then + if self.Memory[1014] >= 1 then + self:ShiftScreenRight() + else + self:ShiftScreenLeft() + end + self.Memory[self.Memory[1021]] = value + else + self.Memory[self.Memory[1021]] = value + if self.Memory[1014] >= 1 then + self.Memory[1021] = math.max(0,self.Memory[1021] - 1) + else + self.Memory[1021] = math.min(1023,self.Memory[1021] + 1) + end + end + + end + if Address == 1017 then + for i = 0, self.ScreenWidth-1 do + self.Memory[value*self.ScreenWidth+i] = 0 + end + self.NeedRefresh = true + end + if Address == 1018 then + for i = 0, self.ScreenWidth*self.ScreenHeight-1 do + self.Memory[i] = 0 + end + self.NeedRefresh = true + end +end + +duplicator.RegisterEntityClass("gmod_wire_characterlcd", WireLib.MakeWireEnt, "Data", "ScreenWidth", "ScreenHeight") diff --git a/lua/entities/gmod_wire_characterlcd/shared.lua b/lua/entities/gmod_wire_characterlcd/shared.lua new file mode 100644 index 00000000..c291b146 --- /dev/null +++ b/lua/entities/gmod_wire_characterlcd/shared.lua @@ -0,0 +1,12 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Character LCD" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_consolescreen/cl_init.lua b/lua/entities/gmod_wire_consolescreen/cl_init.lua index b4769bcc..9fe34175 100644 --- a/lua/entities/gmod_wire_consolescreen/cl_init.lua +++ b/lua/entities/gmod_wire_consolescreen/cl_init.lua @@ -1,470 +1,692 @@ include("shared.lua") function ENT:Initialize() - self.Memory1 = {} - self.Memory2 = {} - for i = 0, 2047 do - self.Memory1[i] = 0 - end + self.Memory1 = {} + self.Memory2 = {} + for i = 0, 2047 do + self.Memory1[i] = 0 + end - -- Caching control: - -- [2020] - Force cache refresh - -- [2021] - Cached blocks size (up to 28, 0 if disabled) - -- - -- Hardware image control: - -- [2019] - Clear viewport defined by 2031-2034 - -- [2022] - Screen ratio (read only) - -- [2023] - Hardware scale - -- [2024] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) - -- [2025] - Brightness White - -- [2026] - Brightness B - -- [2027] - Brightness G - -- [2028] - Brightness R - -- [2029] - Vertical scale (1) - -- [2030] - Horizontal scale (1) - -- - -- Shifting control: - -- [2031] - Low shift column - -- [2032] - High shift column - -- [2033] - Low shift row - -- [2034] - High shift row - -- - -- Character output control: - -- [2035] - Charset, always 0 - -- [2036] - Brightness (additive) - -- - -- Control registers: - -- [2037] - Shift cells (number of cells, >0 right, <0 left) - -- [2038] - Shift rows (number of rows, >0 shift up, <0 shift down) - -- [2039] - Hardware Clear Row (Writing clears row) - -- [2040] - Hardware Clear Column (Writing clears column) - -- [2041] - Hardware Clear Screen - -- [2042] - Hardware Background Color (000) - -- - -- Cursor control: - -- [2043] - Cursor Blink Rate (0.50) - -- [2044] - Cursor Size (0.25) - -- [2045] - Cursor Address - -- [2046] - Cursor Enabled - -- - -- [2047] - Clk + -- Caching control: + -- [2020] - Force cache refresh + -- [2021] - Cached blocks size (up to 28, 0 if disabled) + -- + -- Hardware image control: + -- [2019] - Clear viewport defined by 2031-2034 + -- [2022] - Screen ratio (read only) + -- [2023] - Hardware scale + -- [2024] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) + -- [2025] - Brightness White + -- [2026] - Brightness B + -- [2027] - Brightness G + -- [2028] - Brightness R + -- [2029] - Vertical scale (1) + -- [2030] - Horizontal scale (1) + -- + -- Shifting control: + -- [2031] - Low shift column + -- [2032] - High shift column + -- [2033] - Low shift row + -- [2034] - High shift row + -- + -- Character output control: + -- [2035] - Charset, always 0 + -- [2036] - Brightness (additive) + -- + -- Control registers: + -- [2037] - Shift cells (number of cells, >0 right, <0 left) + -- [2038] - Shift rows (number of rows, >0 shift up, <0 shift down) + -- [2039] - Hardware Clear Row (Writing clears row) + -- [2040] - Hardware Clear Column (Writing clears column) + -- [2041] - Hardware Clear Screen + -- [2042] - Hardware Background Color (000) + -- + -- Cursor control: + -- [2043] - Cursor Blink Rate (0.50) + -- [2044] - Cursor Size (0.25) + -- [2045] - Cursor Address + -- [2046] - Cursor Enabled + -- + -- [2047] - Clk - self.Memory1[2022] = 3/4 - self.Memory1[2023] = 0 - self.Memory1[2024] = 0 - self.Memory1[2025] = 1 - self.Memory1[2026] = 1 - self.Memory1[2027] = 1 - self.Memory1[2028] = 1 - self.Memory1[2029] = 1 - self.Memory1[2030] = 1 - self.Memory1[2031] = 0 - self.Memory1[2032] = 29 - self.Memory1[2033] = 0 - self.Memory1[2034] = 17 - self.Memory1[2035] = 0 - self.Memory1[2036] = 0 + self.Memory1[2022] = 3/4 + self.Memory1[2023] = 0 + self.Memory1[2024] = 0 + self.Memory1[2025] = 1 + self.Memory1[2026] = 1 + self.Memory1[2027] = 1 + self.Memory1[2028] = 1 + self.Memory1[2029] = 1 + self.Memory1[2030] = 1 + self.Memory1[2031] = 0 + self.Memory1[2032] = 29 + self.Memory1[2033] = 0 + self.Memory1[2034] = 17 + self.Memory1[2035] = 0 + self.Memory1[2036] = 0 - self.Memory1[2042] = 0 - self.Memory1[2043] = 0.5 - self.Memory1[2044] = 0.25 - self.Memory1[2045] = 0 - self.Memory1[2046] = 0 + self.Memory1[2042] = 0 + self.Memory1[2043] = 0.5 + self.Memory1[2044] = 0.25 + self.Memory1[2045] = 0 + self.Memory1[2046] = 0 - for i = 0, 2047 do - self.Memory2[i] = self.Memory1[i] - end + for i = 0, 2047 do + self.Memory2[i] = self.Memory1[i] + end - self.LastClk = false + self.LastClk = false - self.PrevTime = CurTime() - self.IntTimer = 0 + self.PrevTime = CurTime() + self.IntTimer = 0 - self.NeedRefresh = true - self.Flash = false - self.FrameNeedsFlash = false + self.NeedRefresh = true + self.Flash = false + self.FrameNeedsFlash = false - self.FramesSinceRedraw = 0 - self.NewClk = true + self.FramesSinceRedraw = 0 + self.NewClk = true - self.GPU = WireGPU(self) + self.GPU = WireGPU(self) - -- Setup caching - GPULib.ClientCacheCallback(self,function(Address,Value) - self:WriteCell(Address,Value) - end) + -- Setup caching + GPULib.ClientCacheCallback(self,function(Address,Value) + self:WriteCell(Address,Value) + end) - WireLib.netRegister(self) + WireLib.netRegister(self) end - function ENT:OnRemove() - self.GPU:Finalize() - self.NeedRefresh = true + self.GPU:Finalize() + self.NeedRefresh = true end function ENT:ReadCell(Address,value) - Address = math.floor(Address) - if Address < 0 then return nil end - if Address >= 2048 then return nil end + Address = math.floor(Address) + if Address < 0 then return nil end + if Address >= 2048 then return nil end - return self.Memory2[Address] + return self.Memory2[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 + Address = math.floor(Address) + if Address < 0 then return false end + if Address >= 2048 then return false end - if Address == 2047 then self.NewClk = value ~= 0 end + if Address == 2047 then self.NewClk = value ~= 0 end - if self.NewClk then - self.Memory1[Address] = value -- Vis mem - self.NeedRefresh = true - end - self.Memory2[Address] = value -- Invis mem + if self.NewClk then + self.Memory1[Address] = value -- Vis mem + self.NeedRefresh = true + end + self.Memory2[Address] = value -- Invis mem - -- 2038 - Shift rows (number of rows, >0 shift down, <0 shift up) - -- 2039 - Hardware Clear Row (Writing clears row) - -- 2040 - Hardware Clear Column (Writing clears column) - -- 2041 - Hardware Clear Screen + -- 2038 - Shift rows (number of rows, >0 shift down, <0 shift up) + -- 2039 - Hardware Clear Row (Writing clears row) + -- 2040 - Hardware Clear Column (Writing clears column) + -- 2041 - Hardware Clear Screen - if (Address == 2025) or - (Address == 2026) or - (Address == 2027) or - (Address == 2028) or - (Address == 2036) then - self.NeedRefresh = true - end + if (Address == 2025) or + (Address == 2026) or + (Address == 2027) or + (Address == 2028) or + (Address == 2036) then + self.NeedRefresh = true + end - if Address == 2019 then - local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) - for j = low, high do - for i = 2*lowc, 2*highc+1 do - self.Memory1[60*j+i] = 0 - self.Memory2[60*j+i] = 0 - end - end - self.NeedRefresh = true - end - if Address == 2037 then - local delta = math.floor(math.Clamp(math.abs(value),-30,30)) - local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) - if (value > 0) then - for j = low,high do - for i = highc,lowc+delta,-1 do - if (self.NewClk) then - self.Memory1[j*60+i*2] = self.Memory1[j*60+(i-delta)*2] - self.Memory1[j*60+i*2+1] = self.Memory1[j*60+(i-delta)*2+1] - end - self.Memory2[j*60+i*2] = self.Memory2[j*60+(i-delta)*2] - self.Memory2[j*60+i*2+1] = self.Memory2[j*60+(i-delta)*2+1] - end - end - for j = low,high do - for i = lowc, lowc+delta-1 do - if (self.NewClk) then - self.Memory1[j*60+i*2] = 0 - self.Memory1[j*60+i*2+1] = 0 - end - self.Memory2[j*60+i*2] = 0 - self.Memory2[j*60+i*2+1] = 0 - end - end - else - for j = low,high do - for i = lowc,highc-delta do - if (self.NewClk) then - self.Memory1[j*60+i*2] = self.Memory1[j*60+i*2+delta*2] - self.Memory1[j*60+i*2+1] = self.Memory1[j*60+i*2+1+delta*2] - end - self.Memory2[j*60+i*2] = self.Memory2[j*60+i*2+delta*2] - self.Memory2[j*60+i*2+1] = self.Memory2[j*60+i*2+1+delta*2] - end - end - for j = low,high do - for i = highc-delta+1,highc do - if (self.NewClk) then - self.Memory1[j*60+i*2] = 0 - self.Memory1[j*60+i*2+1] = 0 - end - self.Memory2[j*60+i*2] = 0 - self.Memory2[j*60+i*2+1] = 0 - end - end - end - end - if Address == 2038 then - local delta = math.floor(math.Clamp(math.abs(value),-30,30)) - local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) - local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) - local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) - local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) - if (value > 0) then - for j = low, high-delta do - for i = 2*lowc,2*highc+1 do - if (self.NewClk) then - self.Memory1[j*60+i] = self.Memory1[(j+delta)*60+i] - end - self.Memory2[j*60+i] = self.Memory2[(j+delta)*60+i] - end - end - for j = high-delta+1,high do - for i = 2*lowc, 2*highc+1 do - if (self.NewClk) then - self.Memory1[j*60+i] = 0 - end - self.Memory2[j*60+i] = 0 - end - end - else - for j = high,low+delta,-1 do - for i = 2*lowc, 2*highc+1 do - if (self.NewClk) then - self.Memory1[j*60+i] = self.Memory1[(j-delta)*60+i] - end - self.Memory2[j*60+i] = self.Memory2[(j-delta)*60+i] - end - end - for j = low,low+delta-1 do - for i = 2*lowc, 2*highc+1 do - if (self.NewClk) then - self.Memory1[j*60+i] = 0 - end - self.Memory2[j*60+i] = 0 - end - end - end - end - if Address == 2039 then - for i = 0, 59 do - self.Memory1[value*60+i] = 0 - self.Memory2[value*60+i] = 0 - end - self.NeedRefresh = true - end - if Address == 2040 then - for i = 0, 17 do - self.Memory1[i*60+value] = 0 - self.Memory2[i*60+value] = 0 - end - self.NeedRefresh = true - end - if Address == 2041 then - for i = 0, 18*30*2-1 do - self.Memory1[i] = 0 - self.Memory2[i] = 0 - end - self.NeedRefresh = true - end + if Address == 2019 then + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + for j = low, high do + for i = 2*lowc, 2*highc+1 do + self.Memory1[60*j+i] = 0 + self.Memory2[60*j+i] = 0 + end + end + self.NeedRefresh = true + end + if Address == 2037 then + local delta = math.floor(math.Clamp(math.abs(value),-30,30)) + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + if (value > 0) then + for j = low,high do + for i = highc,lowc+delta,-1 do + if (self.NewClk) then + self.Memory1[j*60+i*2] = self.Memory1[j*60+(i-delta)*2] + self.Memory1[j*60+i*2+1] = self.Memory1[j*60+(i-delta)*2+1] + end + self.Memory2[j*60+i*2] = self.Memory2[j*60+(i-delta)*2] + self.Memory2[j*60+i*2+1] = self.Memory2[j*60+(i-delta)*2+1] + end + end + for j = low,high do + for i = lowc, lowc+delta-1 do + if (self.NewClk) then + self.Memory1[j*60+i*2] = 0 + self.Memory1[j*60+i*2+1] = 0 + end + self.Memory2[j*60+i*2] = 0 + self.Memory2[j*60+i*2+1] = 0 + end + end + else + for j = low,high do + for i = lowc,highc-delta do + if (self.NewClk) then + self.Memory1[j*60+i*2] = self.Memory1[j*60+i*2+delta*2] + self.Memory1[j*60+i*2+1] = self.Memory1[j*60+i*2+1+delta*2] + end + self.Memory2[j*60+i*2] = self.Memory2[j*60+i*2+delta*2] + self.Memory2[j*60+i*2+1] = self.Memory2[j*60+i*2+1+delta*2] + end + end + for j = low,high do + for i = highc-delta+1,highc do + if (self.NewClk) then + self.Memory1[j*60+i*2] = 0 + self.Memory1[j*60+i*2+1] = 0 + end + self.Memory2[j*60+i*2] = 0 + self.Memory2[j*60+i*2+1] = 0 + end + end + end + end + if Address == 2038 then + local delta = math.floor(math.Clamp(math.abs(value),-30,30)) + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + if (value > 0) then + for j = low, high-delta do + for i = 2*lowc,2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = self.Memory1[(j+delta)*60+i] + end + self.Memory2[j*60+i] = self.Memory2[(j+delta)*60+i] + end + end + for j = high-delta+1,high do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = 0 + end + self.Memory2[j*60+i] = 0 + end + end + else + for j = high,low+delta,-1 do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = self.Memory1[(j-delta)*60+i] + end + self.Memory2[j*60+i] = self.Memory2[(j-delta)*60+i] + end + end + for j = low,low+delta-1 do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = 0 + end + self.Memory2[j*60+i] = 0 + end + end + end + end + if Address == 2039 then + for i = 0, 59 do + self.Memory1[value*60+i] = 0 + self.Memory2[value*60+i] = 0 + end + self.NeedRefresh = true + end + if Address == 2040 then + for i = 0, 17 do + self.Memory1[i*60+value] = 0 + self.Memory2[i*60+value] = 0 + end + self.NeedRefresh = true + end + if Address == 2041 then + for i = 0, 18*30*2-1 do + self.Memory1[i] = 0 + self.Memory2[i] = 0 + end + self.NeedRefresh = true + end - if self.LastClk ~= self.NewClk then - self.LastClk = self.NewClk - self.Memory1 = table.Copy(self.Memory2) -- swap the memory if clock changes - self.NeedRefresh = true - end - return true + if self.LastClk ~= self.NewClk then + self.LastClk = self.NewClk + self.Memory1 = table.Copy(self.Memory2) -- swap the memory if clock changes + self.NeedRefresh = true + end + return true end local specialCharacters = { - [128] = { - { x = 0, y = 1 }, - { x = 1, y = 1 }, - { x = 1, y = 0 }, - }, - [129] = { - { x = 0, y = 1 }, - { x = 0, y = 0 }, - { x = 1, y = 1 }, - }, - [130] = { - { x = 0, y = 1 }, - { x = 1, y = 0 }, - { x = 0, y = 0 }, - }, - [131] = { - { x = 0, y = 0 }, - { x = 1, y = 0 }, - { x = 1, y = 1 }, - }, + [128] = { + { x = 0, y = 1 }, + { x = 1, y = 1 }, + { x = 1, y = 0 }, + }, + [129] = { + { x = 0, y = 1 }, + { x = 0, y = 0 }, + { x = 1, y = 1 }, + }, + [130] = { + { x = 0, y = 1 }, + { x = 1, y = 0 }, + { x = 0, y = 0 }, + }, + [131] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + }, + [132] = { + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5 }, + }, + [133] = { + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + }, + [134] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0, y = 0.5 }, + }, + [135] = { + { x = 0, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + + + + + + [136] = { + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + [137] = { + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + { x = 0, y = 0.5 }, + }, + [138] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 1 }, + { x = 0, y = 1 }, + }, + [139] = { + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + }, + [140] = { + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5 }, + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + }, + [141] = { + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + }, + [142] = { + + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0.5, y = 1 }, + { x = 0.5, y = 0.5 }, + { x = 0, y = 0.5}, + { x = 0, y = 0 }, + }, + + [143] = { + { x = 0, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + { x = 0, y = 1 }, + }, + [144] = { + { x = 0, y = 1 }, + { x = 0, y = 0 }, + { x = 0.5, y = 0 }, + { x = 0.5, y = 0.5 }, + { x = 1, y = 0.5 }, + { x = 1, y = 1 }, + }, + [145] = { + { x = 1, y = 1 }, + { x = 0, y = 1 }, + { x = 0, y = 0.5 }, + { x = 0.5, y = 0.5 }, + { x = 0.5, y = 0 }, + { x = 1, y = 0 }, + }, + [146] = { + { x = 0, y = 0 }, + { x = 1, y = 0 }, + { x = 1, y = 1 }, + { x = 0, y = 1 }, + }, + [147] = { + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + }, + [148] = { + { x = 0.33, y = 0}, + { x = 0.66, y = 0}, + { x = 0.66, y = 1}, + { x = 0.33, y = 1}, + }, + [149] = { + { x = 0.66, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [150] = { + { x = 0, y = 0.33}, + { x = 1, y = 0.33}, + { x = 1, y = 0.66}, + { x = 0, y = 0.66}, + }, + [151] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [152] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [153] = { + { x = 0.66, y = 0.66 }, + { x = 1, y = 0.66 }, + { x = 1, y = 0.33 }, + { x = 0, y = 0.33 }, + { x = 0, y = 0.66 }, + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 1 }, + { x = 0.66, y = 1 }, + }, + [154] = { + { x = 0.33, y = 0.33 }, + { x = 0, y = 0.33 }, + { x = 0, y = 0.66 }, + { x = 0.33, y = 0.66 }, + { x = 0.33, y = 1 }, + { x = 0.66, y = 1 }, + { x = 0.66, y = 0 }, + { x = 0.33, y = 0 }, + }, + [155] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + { x = 0.33, y = 0.66 }, + { x = 0, y = 0.66 }, + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + }, + [156] = { + { x = 0.33, y = 0 }, + { x = 0.66, y = 0 }, + { x = 0.66, y = 0.33 }, + { x = 0.33, y = 0.33 }, + }, + [157] = { + { x = 0.66, y = 0.33 }, + { x = 1, y = 0.33 }, + { x = 1, y = 0.66 }, + { x = 0.66, y = 0.66 }, + }, + [158] = { + { x = 0.33, y = 0.66 }, + { x = 0.66, y = 0.66 }, + { x = 0.66, y = 1 }, + { x = 0.33, y = 1 }, + }, + [159] = { + { x = 0, y = 0.33 }, + { x = 0.33, y = 0.33 }, + { x = 0.33, y = 0.66 }, + { x = 0, y = 0.66 }, + }, + [160] = { + { x = 0.33, y = 0.33 }, + { x = 0.66, y = 0.33 }, + { x = 0.66, y = 0.66 }, + { x = 0.33, y = 0.66 }, + } } function ENT:DrawSpecialCharacter(c,x,y,w,h,r,g,b) - surface.SetDrawColor(r,g,b,255) - surface.SetTexture(0) + surface.SetDrawColor(r,g,b,255) + surface.SetTexture(0) - local vertices = specialCharacters[c] - if vertices then - local vertexData = { - { x = vertices[1].x*w+x, y = vertices[1].y*h+y }, - { x = vertices[2].x*w+x, y = vertices[2].y*h+y }, - { x = vertices[3].x*w+x, y = vertices[3].y*h+y }, - } - surface.DrawPoly(vertexData) - end + local vertices = specialCharacters[c] + if vertices then + local tf = Matrix() tf:SetScale(Vector(w, h, 1)) tf:SetTranslation(Vector(x, y, 0)) + cam.PushModelMatrix(tf, true) + surface.DrawPoly(vertices) + cam.PopModelMatrix() + end end function ENT:Draw() - self:DrawModel() + self:DrawModel() - local curtime = CurTime() - local DeltaTime = curtime - self.PrevTime - self.PrevTime = curtime - self.IntTimer = self.IntTimer + DeltaTime - self.FramesSinceRedraw = self.FramesSinceRedraw + 1 + local curtime = CurTime() + local DeltaTime = curtime - self.PrevTime + self.PrevTime = curtime + self.IntTimer = self.IntTimer + DeltaTime + self.FramesSinceRedraw = self.FramesSinceRedraw + 1 - if self.NeedRefresh == true then - self.FramesSinceRedraw = 0 - self.NeedRefresh = false - self.FrameNeedsFlash = false + if self.NeedRefresh == true then + self.FramesSinceRedraw = 0 + self.NeedRefresh = false + self.FrameNeedsFlash = false - if self.Memory1[2046] >= 1 then self.FrameNeedsFlash = true end + if self.Memory1[2046] >= 1 then self.FrameNeedsFlash = true end - self.GPU:RenderToGPU(function() - -- Draw terminal here - -- W/H = 16 - local szx = 512/31 - local szy = 512/19 + self.GPU:RenderToGPU(function() + -- Draw terminal here + -- W/H = 16 + local szx = 1024/31 + local szy = 1024/19 - local ch = self.Memory1[2042] + local ch = self.Memory1[2042] - local hb = 28*math.fmod(ch, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036] - local hg = 28*math.fmod(math.floor(ch / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036] - local hr = 28*math.fmod(math.floor(ch / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036] - surface.SetDrawColor(hr,hg,hb,255) - surface.DrawRect(0,0,512,512) + local hb = 28*math.fmod(ch, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036] + local hg = 28*math.fmod(math.floor(ch / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036] + local hr = 28*math.fmod(math.floor(ch / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036] + surface.SetDrawColor(hr,hg,hb,255) + surface.DrawRect(0,0,1024,1024) - for ty = 0, 17 do - for tx = 0, 29 do - local a = tx + ty*30 - local c1 = self.Memory1[2*a] - local c2 = self.Memory1[2*a+1] + for ty = 0, 17 do + for tx = 0, 29 do + local a = tx + ty*30 + local c1 = self.Memory1[2*a] + local c2 = self.Memory1[2*a+1] - local cback = math.floor(c2 / 1000) - local cfrnt = c2 - math.floor(c2 / 1000)*1000 + local cback = math.floor(c2 / 1000) + local cfrnt = c2 - math.floor(c2 / 1000)*1000 - local fb = math.Clamp(28*math.fmod(cfrnt, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036],0,255) - local fg = math.Clamp(28*math.fmod(math.floor(cfrnt / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036],0,255) - local fr = math.Clamp(28*math.fmod(math.floor(cfrnt / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036],0,255) - local bb = math.Clamp(28*math.fmod(cback, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036],0,255) - local bg = math.Clamp(28*math.fmod(math.floor(cback / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036],0,255) - local br = math.Clamp(28*math.fmod(math.floor(cback / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036],0,255) + local fb = math.Clamp(28*math.fmod(cfrnt, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036],0,255) + local fg = math.Clamp(28*math.fmod(math.floor(cfrnt / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036],0,255) + local fr = math.Clamp(28*math.fmod(math.floor(cfrnt / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036],0,255) + local bb = math.Clamp(28*math.fmod(cback, 10)*self.Memory1[2026]*self.Memory1[2025] + self.Memory1[2036],0,255) + local bg = math.Clamp(28*math.fmod(math.floor(cback / 10), 10)*self.Memory1[2027]*self.Memory1[2025] + self.Memory1[2036],0,255) + local br = math.Clamp(28*math.fmod(math.floor(cback / 100),10)*self.Memory1[2028]*self.Memory1[2025] + self.Memory1[2036],0,255) - if (self.Flash == true) and (cback > 999) then - fb,bb = bb,fb - fg,bg = bg,fg - fr,br = br,fr - end + if (self.Flash == true) and (cback > 999) then + fb,bb = bb,fb + fg,bg = bg,fg + fr,br = br,fr + end - if cback > 999 then - self.FrameNeedsFlash = true - end + if cback > 999 then + self.FrameNeedsFlash = true + end - if c1 >= 2097152 then c1 = 0 end - if c1 < 0 then c1 = 0 end + if c1 >= 2097152 then c1 = 0 end + if c1 < 0 then c1 = 0 end - if cback ~= 0 then - surface.SetDrawColor(br,bg,bb,255) - surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) - else - surface.SetDrawColor(hr,hg,hb,255) - surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) - end + if cback ~= 0 then + surface.SetDrawColor(br,bg,bb,255) + surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) + else + surface.SetDrawColor(hr,hg,hb,255) + surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) + end - if (c1 ~= 0) and (cfrnt ~= 0) then - -- Note: the source engine does not handle unicode characters above 65535 properly. - local utf8 = "" - if c1 <= 127 then - utf8 = string.char (c1) - elseif c1 < 2048 then - utf8 = string.format("%c%c", 192 + math.floor (c1 / 64), 128 + (c1 % 64)) - elseif c1 < 65536 then - utf8 = string.format("%c%c%c", 224 + math.floor (c1 / 4096), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) - elseif c1 < 2097152 then - utf8 = string.format("%c%c%c%c", 240 + math.floor (c1 / 262144), 128 + (math.floor (c1 / 4096) % 64), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) - end + if (c1 ~= 0) and (cfrnt ~= 0) then + -- Note: the source engine does not handle unicode characters above 65535 properly. + local utf8 = "" + if c1 <= 127 then + utf8 = string.char (c1) + elseif c1 < 2048 then + utf8 = string.format("%c%c", 192 + math.floor (c1 / 64), 128 + (c1 % 64)) + elseif c1 < 65536 then + utf8 = string.format("%c%c%c", 224 + math.floor (c1 / 4096), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) + elseif c1 < 2097152 then + utf8 = string.format("%c%c%c%c", 240 + math.floor (c1 / 262144), 128 + (math.floor (c1 / 4096) % 64), 128 + (math.floor (c1 / 64) % 64), 128 + (c1 % 64)) + end - if specialCharacters[c1] then - self:DrawSpecialCharacter( - c1, (tx+0.5)*szx, (ty+0.5)*szy, szx, szy, - fr,fg,fb - ) - else - draw.DrawText( - utf8, - "WireGPU_ConsoleFont", - (tx + 0.625) * szx, (ty + 0.75) * szy, - Color(fr,fg,fb,255),0 - ) - end - end - end - end + if specialCharacters[c1] then + self:DrawSpecialCharacter( + c1, (tx+0.5)*szx, (ty+0.5)*szy, szx, szy, + fr,fg,fb + ) + else + draw.DrawText( + utf8, + "WireGPU_ConsoleFont", + (tx + 0.625) * szx, (ty + 0.75) * szy, + Color(fr,fg,fb,255),0 + ) + end + end + end + end - if self.Memory1[2045] > 1080 then self.Memory1[2045] = 1080 end - if self.Memory1[2045] < 0 then self.Memory1[2045] = 0 end - if self.Memory1[2044] > 1 then self.Memory1[2044] = 1 end - if self.Memory1[2044] < 0 then self.Memory1[2044] = 0 end + if self.Memory1[2045] > 1080 then self.Memory1[2045] = 1080 end + if self.Memory1[2045] < 0 then self.Memory1[2045] = 0 end + if self.Memory1[2044] > 1 then self.Memory1[2044] = 1 end + if self.Memory1[2044] < 0 then self.Memory1[2044] = 0 end - if self.Memory1[2046] >= 1 then - if self.Flash == true then - local a = math.floor(self.Memory1[2045] / 2) + if self.Memory1[2046] >= 1 then + if self.Flash == true then + local a = math.floor(self.Memory1[2045] / 2) - local tx = a - math.floor(a / 30)*30 - local ty = math.floor(a / 30) + local tx = a - math.floor(a / 30)*30 + local ty = math.floor(a / 30) - local c = self.Memory1[2*a+1] - local cback = 999-math.floor(c / 1000) - local bb = 28*math.fmod(cback,10) - local bg = 28*math.fmod(math.floor(cback / 10),10) - local br = 28*math.fmod(math.floor(cback / 100),10) + local c = self.Memory1[2*a+1] + local cback = 999-math.floor(c / 1000) + local bb = 28*math.fmod(cback,10) + local bg = 28*math.fmod(math.floor(cback / 10),10) + local br = 28*math.fmod(math.floor(cback / 100),10) - surface.SetDrawColor( - math.Clamp(br*self.Memory1[2028]*self.Memory1[2025],0,255), - math.Clamp(bg*self.Memory1[2027]*self.Memory1[2025],0,255), - math.Clamp(bb*self.Memory1[2026]*self.Memory1[2025],0,255), - 255 - ) - surface.DrawRect( - tx*szx+szx/2, - ty*szy+szy/2+szy*1.2*(1-self.Memory1[2044]), - szx*1.2, - szy*1.2*self.Memory1[2044] - ) - end - end - end) - end + surface.SetDrawColor( + math.Clamp(br*self.Memory1[2028]*self.Memory1[2025],0,255), + math.Clamp(bg*self.Memory1[2027]*self.Memory1[2025],0,255), + math.Clamp(bb*self.Memory1[2026]*self.Memory1[2025],0,255), + 255 + ) + surface.DrawRect( + tx*szx+szx/2, + ty*szy+szy/2+szy*1.2*(1-self.Memory1[2044]), + szx*1.2, + szy*1.2*self.Memory1[2044] + ) + end + end + end) + end - if self.FrameNeedsFlash == true then - if self.IntTimer < self.Memory1[2043] then - if (self.Flash == false) then - self.NeedRefresh = true - end - self.Flash = true - end + if self.FrameNeedsFlash == true then + if self.IntTimer < self.Memory1[2043] then + if (self.Flash == false) then + self.NeedRefresh = true + end + self.Flash = true + end - if self.IntTimer >= self.Memory1[2043] then - if self.Flash == true then - self.NeedRefresh = true - end - self.Flash = false - end + if self.IntTimer >= self.Memory1[2043] then + if self.Flash == true then + self.NeedRefresh = true + end + self.Flash = false + end - if self.IntTimer >= self.Memory1[2043]*2 then - self.IntTimer = 0 - end - end + if self.IntTimer >= self.Memory1[2043]*2 then + self.IntTimer = 0 + end + end - self.GPU:Render(self.Memory1[2024],self.Memory1[2023]) - Wire_Render(self) + self.GPU:Render(self.Memory1[2024],self.Memory1[2023]) + Wire_Render(self) end function ENT:IsTranslucent() - return true + return true end diff --git a/lua/entities/gmod_wire_digitalscreen/cl_init.lua b/lua/entities/gmod_wire_digitalscreen/cl_init.lua index bca93cea..36a6f941 100644 --- a/lua/entities/gmod_wire_digitalscreen/cl_init.lua +++ b/lua/entities/gmod_wire_digitalscreen/cl_init.lua @@ -248,17 +248,12 @@ function ENT:RedrawPixel(a) cr, cg, cb = (transformcolor[colormode] or transformcolor[0])(c) end - local xstep = (512/self.ScreenWidth) - local ystep = (512/self.ScreenHeight) surface.SetDrawColor(cr,cg,cb,255) - local tx, ty = floor(x*xstep), floor(y*ystep) - surface.DrawRect( tx, ty, floor((x+1)*xstep-tx), floor((y+1)*ystep-ty) ) + surface.DrawRect( x, y, 1, 1 ) end function ENT:RedrawRow(y) - local xstep = (512/self.ScreenWidth) - local ystep = (512/self.ScreenHeight) if y >= self.ScreenHeight then return end local a = y*self.ScreenWidth @@ -277,8 +272,7 @@ function ENT:RedrawRow(y) end surface.SetDrawColor(cr,cg,cb,255) - local tx, ty = floor(x*xstep), floor(y*ystep) - surface.DrawRect( tx, ty, floor((x+1)*xstep-tx), floor((y+1)*ystep-ty) ) + surface.DrawRect( x, y, 1, 1 ) end end @@ -294,7 +288,7 @@ function ENT:Draw() if self.ClearQueued then surface.SetDrawColor(0,0,0,255) - surface.DrawRect(0,0, 512,512) + surface.DrawRect(0,0, 1024,1024) self.ClearQueued = false return end @@ -320,7 +314,7 @@ function ENT:Draw() end) end - self.GPU:Render() + self.GPU:Render(0,0,1024,1024,nil,-(1024-self.ScreenWidth)/1024,-(1024-self.ScreenHeight)/1024) Wire_Render(self) end diff --git a/lua/entities/gmod_wire_egp/cl_init.lua b/lua/entities/gmod_wire_egp/cl_init.lua index 4b960b49..66a6ee1d 100644 --- a/lua/entities/gmod_wire_egp/cl_init.lua +++ b/lua/entities/gmod_wire_egp/cl_init.lua @@ -83,7 +83,7 @@ function ENT:Draw() self:_EGP_Update() end - self.GPU:Render() + self.GPU:Render(0,0,1024,1024,nil,-0.5,-0.5) end function ENT:OnRemove() diff --git a/lua/entities/gmod_wire_gpu/cl_init.lua b/lua/entities/gmod_wire_gpu/cl_init.lua index 7749fdf9..4b54b492 100644 --- a/lua/entities/gmod_wire_gpu/cl_init.lua +++ b/lua/entities/gmod_wire_gpu/cl_init.lua @@ -292,8 +292,8 @@ function ENT:RenderMisc(pos, ang, resolution, aspect, monitor) if (dist < 256) then local pos = WorldToLocal( trace.HitPos, Angle(), pos, ang ) - local x = 0.5+pos.x/(monitor.RS*(512/monitor.RatioX)) - local y = 0.5-pos.y/(monitor.RS*512) + local x = 0.5+pos.x/(monitor.RS*(1024/monitor.RatioX)) + local y = 0.5-pos.y/(monitor.RS*1024) local cursorOffset = 0 if self.VM:ReadCell(65532) == 1 then -- Check for vertex mode to counter the faulty offset @@ -308,7 +308,7 @@ function ENT:RenderMisc(pos, ang, resolution, aspect, monitor) surface.SetTexture(surface.GetTextureID("gui/arrow")) x = math.Clamp(x,0 + cursorOffset, 1 + cursorOffset) y = math.Clamp(y,0 + cursorOffset, 1 + cursorOffset) - surface.DrawTexturedRectRotated(-256*aspect+x*512*aspect+10,-256+y*512+12,32,32,45) + surface.DrawTexturedRectRotated(-512*aspect+x*1024*aspect+10,-512+y*1024+12,32,32,45) end end end @@ -376,10 +376,10 @@ function ENT:Draw() if self.VM.Memory[65532] == 0 then self.GPU:Render( self.VM:ReadCell(65522), self.VM:ReadCell(65523)-self.VM:ReadCell(65518)/512, -- rotation, scale - 512*math.Clamp(self.VM:ReadCell(65525),0,1), 512*math.Clamp(self.VM:ReadCell(65524),0,1), -- width, height + 1024*math.Clamp(self.VM:ReadCell(65525),0,1), 1024*math.Clamp(self.VM:ReadCell(65524),0,1), -- width, height function(pos, ang, resolution, aspect, monitor) -- postrenderfunction self:RenderMisc(pos, ang, resolution, aspect, monitor) - end + end,-0.5,-0.5 ) else -- Custom render to world @@ -389,20 +389,20 @@ function ENT:Draw() pos = pos - ang:Right()*(monitor.y2-monitor.y1)/2 pos = pos - ang:Forward()*(monitor.x2-monitor.x1)/2 - local width,height = 512*math.Clamp(self.VM.Memory[65525],0,1), - 512*math.Clamp(self.VM.Memory[65524],0,1) + local width,height = 1024*math.Clamp(self.VM.Memory[65525],0,1), + 1024*math.Clamp(self.VM.Memory[65524],0,1) - local h = width and width*monitor.RatioX or height or 512 + local h = width and width*monitor.RatioX or height or 1024 local w = width or h/monitor.RatioX local x = -w/2 local y = -h/2 - local res = monitor.RS*512/h + local res = monitor.RS*1024/h self.VertexCamSettings = { pos, ang, res } cam.Start3D2D(pos, ang, res) self.In3D2D = true local ok, err = xpcall(function() - self:RenderVertex(512,512*monitor.RatioX) + self:RenderVertex(1024,1024*monitor.RatioX) self:RenderMisc(pos, ang, res, 1/monitor.RatioX, monitor) end, debug.traceback) if not ok then WireLib.ErrorNoHalt(err) end diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua new file mode 100644 index 00000000..89658e7a --- /dev/null +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -0,0 +1,476 @@ +AddCSLuaFile() +DEFINE_BASECLASS( "base_wire_entity" ) +ENT.PrintName = "Interactive Prop (Wire)" +ENT.WireDebugName = "Interactive Prop" + +local InteractiveModels + +local function copyPropUI(prop, newName) + local new = table.Copy( InteractiveModels[prop] ) + new.title = newName + return new +end + +InteractiveModels = { + + ["models/props_lab/reciever01a.mdl"] = { + width=220, + height=100, + title="Reciever01a", + widgets={ + {type="DCheckBox", x=20, y=50,name="Switch1"}, + {type="DCheckBox", x=40, y=50,name="Switch2"}, + {type="DNumberScratch", x=60, y=40,name="Knob1" }, + {type="DNumberScratch", x=80, y=40,name="Knob2" }, + {type="DNumberScratch", x=100, y=40,name="Knob3" }, + {type="DNumberScratch", x=120, y=40,name="Knob4" }, + {type="DNumberScratch", x=140, y=40,name="Knob5" }, + {type="DNumberScratch", x=160, y=40,name="Knob6" }, + {type="DNumberScratch", x=180, y=40,name="Knob7" }, + {type="DNumberScratch", x=60, y=60,name="Knob8" }, + {type="DNumberScratch", x=80, y=60,name="Knob9" }, + {type="DNumberScratch", x=100, y=60,name="Knob10" }, + {type="DNumberScratch", x=120, y=60,name="Knob11" }, + {type="DNumberScratch", x=140, y=60,name="Knob12" }, + {type="DNumberScratch", x=160, y=60,name="Knob13" }, + {type="DNumberScratch", x=180, y=60,name="Knob14" } + } + }, + + ["models/props_lab/reciever01b.mdl"] = { + width=190, + height=100, + title="Reciever01b", + widgets={ + {type="DButton", x=28, y=40,name="Button1"}, + {type="DButton", x=58, y=40,name="Button2"}, + {type="DButton", x=88, y=40,name="Button3"}, + {type="DButton", x=118, y=40,name="Button4"}, + {type="DNumberScratch", x=30, y=70,name="Knob1" }, + {type="DNumberScratch", x=60, y=70,name="Knob2" }, + {type="DNumberScratch", x=90, y=70,name="Knob3" }, + {type="DNumberScratch", x=120, y=70,name="Knob4" }, + {type="DNumberScratch", x=150, y=43,name="Knob5" }, + {type="DNumberScratch", x=150, y=67,name="Knob6" }, + } + }, + + ["models/props_lab/keypad.mdl"] = { + width=100, + height=120, + title="Keypad", + widgets={ + {type="DButton", x=10, y=30, text="1", name="1"}, + {type="DButton", x=40, y=30, text="2", name="2"}, + {type="DButton", x=70, y=30, text="3", name="3"}, + {type="DButton", x=10, y=60, text="4", name="4"}, + {type="DButton", x=40, y=60, text="5", name="5"}, + {type="DButton", x=70, y=60, text="6", name="6"}, + {type="DButton", x=10, y=90, text="7", name="7"}, + {type="DButton", x=40, y=90, text="8", name="8"}, + {type="DButton", x=70, y=90, text="9", name="9"}, + } + }, + + ["models/beer/wiremod/numpad.mdl"] = { + width=130, + height=180, + title="Numpad", + widgets={ + {type="DButton", x=10, y=150, text="0", name="0", width = 50}, + {type="DButton", x=10, y=120, text="1", name="1"}, + {type="DButton", x=40, y=120, text="2", name="2"}, + {type="DButton", x=70, y=120, text="3", name="3"}, + {type="DButton", x=10, y=90, text="4", name="4"}, + {type="DButton", x=40, y=90, text="5", name="5"}, + {type="DButton", x=70, y=90, text="6", name="6"}, + {type="DButton", x=10, y=60, text="7", name="7"}, + {type="DButton", x=40, y=60, text="8", name="8"}, + {type="DButton", x=70, y=60, text="9", name="9"}, + {type="DButton", x=100, y=120, text="E", name="Enter", height = 50}, + {type="DButton", x=100, y=60, text="+", name="+", height = 50}, + {type="DButton", x=100, y=30, text="-", name="-"}, + {type="DButton", x=70, y=30, text="*", name="*"}, + {type="DButton", x=40, y=30, text="/", name="/"}, + {type="DButton", x=70, y=150, text=".", name="."}, + {type="DButton", x=10, y=30, text="N", name="Numlock"}, + } + }, + + ["models/props_interiors/bathtub01a.mdl"] = { + width=100, + height=60, + title="BathTub01a", + widgets={ + {type="DNumberScratch", x=10, y=32, name="Hot", color=Color(237, 59, 59)}, + {type="DNumberScratch", x=74, y=32, name="Cold", color=Color(59, 79, 235)}, + } + }, + ["models/props_lab/citizenradio.mdl"] = { + width=160, + height=90, + title="citizenradio", + widgets={ + {type="DNumberScratch", x=10,y=30, name="Knob1"}, + {type="DNumberScratch", x=80,y=30, name="Knob2"}, + {type="DNumberScratch", x=120,y=30, name="Knob3"}, + {type="DNumberScratch", x=10,y=60, name="Knob4"}, + {type="DNumberScratch", x=40,y=60, name="Knob5"}, + {type="DNumberScratch", x=65,y=60, name="Knob6"}, + {type="DNumberScratch", x=90,y=60, name="Knob7"}, + {type="DNumberScratch", x=130,y=60, name="Knob8"}, + } + }, + ["models/props_lab/reciever01c.mdl"] = { + width = 112, + height = 80, + title="reciever01c", + widgets={ + {type="DNumberScratch", x = 10, y = 30, name="Knob1", color=Color(128,64,64)}, + {type="DNumberScratch", x = 35, y = 30, name="Knob2", color=Color(128,64,64)}, + {type="DNumberScratch", x = 10, y = 55, name="Knob3"}, + {type="DNumberScratch", x = 35, y = 55, name="Knob4"}, + {type="DNumberScratch", x = 60, y = 55, name="Knob5"}, + {type="DNumberScratch", x = 85, y = 55, name="Knob6"}, + } + }, + ["models/props_interiors/vendingmachinesoda01a.mdl"] = { + width = 60, + height = 200, + title = "vendingmachinesoda01a", + widgets = { + {type="DButton", x = 10, y = 30, name="1", width = 40, text = "1"}, + {type="DButton", x = 10, y = 50, name="2", width = 40, text = "2"}, + {type="DButton", x = 10, y = 70, name="3", width = 40, text = "3"}, + {type="DButton", x = 10, y = 90, name="4", width = 40, text = "4"}, + {type="DButton", x = 10, y = 110, name="5", width = 40, text = "5"}, + {type="DButton", x = 10, y = 130, name="6", width = 40, text = "6"}, + {type="DButton", x = 10, y = 150, name="7", width = 40, text = "7"}, + {type="DButton", x = 10, y = 170, name="8", width = 40, text = "8"}, + } + }, + ["models/props_c17/furniturewashingmachine001a.mdl"] = { + width = 36, + height = 66, + title="washingmachine001a", + widgets = { + {type="DNumberScratch", x=10,y=30,name="Knob"} + } + }, + ["models/props_trainstation/payphone001a.mdl"] = { + width = 100, + height = 150, + title="payphone001a", + widgets={ + {type="DButton", x=10, y=30, text="1", name="1"}, + {type="DButton", x=40, y=30, text="2", name="2"}, + {type="DButton", x=70, y=30, text="3", name="3"}, + {type="DButton", x=10, y=60, text="4", name="4"}, + {type="DButton", x=40, y=60, text="5", name="5"}, + {type="DButton", x=70, y=60, text="6", name="6"}, + {type="DButton", x=10, y=90, text="7", name="7"}, + {type="DButton", x=40, y=90, text="8", name="8"}, + {type="DButton", x=70, y=90, text="9", name="9"}, + {type="DButton", x=10, y=120, text="*", name="*"}, + {type="DButton", x=40, y=120, text="0", name="0"}, + {type="DButton", x=70, y=120, text="##", name="#"}, + } + }, + ["models/props_lab/plotter.mdl"] = { + width = 190, + height = 90, + title = "plotter", + widgets={ + {type="DButton", x=10, y=30, name="Button1"}, + {type="DButton", x=35, y=30, name="Button2"}, + {type="DButton", x=60, y=30, name="Button3"}, + {type="DButton", x=85, y=30, name="Button4"}, + {type="DButton", x=110, y=30, name="Button5"}, + {type="DButton", x=10, y=60, name="Button6"}, + {type="DButton", x=35, y=60, name="Button7"}, + {type="DButton", x=60, y=60, name="Button8"}, + {type="DButton", x=85, y=60, name="Button9"}, + {type="DButton", x=110, y=60, name="Button10"}, + {type="DButton", x=135, y=60, name="Button11"}, + {type="DButton", x=160, y=60, name="Button12"}, + } + } + +} + +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 = { + + DCheckBox = function(self, data, body, index) + local checkbox = vgui.Create("DCheckBox", body) + checkbox:SetPos(data.x, data.y) + checkbox:SetValue(self.InteractiveData[index]) + checkbox.OnChange = function(box,value) + surface.PlaySound("buttons/lightswitch2.wav") + self.InteractiveData[index] = value and 1 or 0 + self:SendData() + end + end, + + DNumberScratch = function(self, data, body, index) + local numberscratch = vgui.Create("DNumberScratch", body) + numberscratch.color = data.color or Color( 128, 128, 128 ) + numberscratch:SetMin(-1) + numberscratch:SetMax(1) + numberscratch:SetDecimals(4) + numberscratch:SetPos(data.x, data.y) + numberscratch:SetValue(self.InteractiveData[index]) + numberscratch.OnValueChanged = function(scratch,value) + self.InteractiveData[index] = value + self:SendData() + end + numberscratch:SetImageVisible( false ) + numberscratch:SetSize( 17, 17 ) + numberscratch.Paint = function( self, w, h ) + draw.RoundedBox( 8.5, 0, 0, w, h, numberscratch.color ) + local value = self:GetFloatValue() + surface.SetDrawColor(255, 255, 255) + surface.DrawLine( + w/2, + h/2, + math.sin(value * math.pi*0.75)*w/2+w/2, + -math.cos(value * math.pi*0.75)*h/2+h/2 + ) + end + end, + + DButton = function(self, data, body, index) + local button = vgui.Create("DButton", body) + button:SetPos(data.x, data.y) + button:SetText(data.text or "") + button:SetSize(data.width or 20, data.height or 20) + self:AddButton(index,button) + end + +} + +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 +end + + +function ENT:AddButton(id,button) + self.Buttons[id] = button +end + +function ENT:SendData() + net.Start("wire_interactiveprop_action") + local data = InteractiveModels[self:GetModel()].widgets + net.WriteEntity(self) + for i=1, #data do + net.WriteFloat(self.InteractiveData[i]) + end + net.SendToServer() +end + +if CLIENT then + + local panel + + ---------------------------------------------------- + -- Show the prompt + ---------------------------------------------------- + function ENT:Initialize() + self.InteractiveData = {} + self.LastButtons = {} + self.Buttons = {} + for i=1, #InteractiveModels[ self:GetModel() ].widgets do + self.InteractiveData[i] = 0 + 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 + panel = self:GetPanel() + panel.OnClose = function(panel) + net.Start("wire_interactiveprop_close") + self.Buttons = {} + self.LastButtons = {} + net.WriteEntity(self) + net.SendToServer() + end + end) + + net.Receive( "wire_interactiveprop_kick", function() + self.Buttons = {} + self.LastButtons = {} + if IsValid( panel ) then + panel:Remove() + end + end) + + return + +end + +function ENT:InitData() + local model = self:GetModel() + local outputs = {} + for i=1, #InteractiveModels[model].widgets do + outputs[i] = InteractiveModels[model].widgets[i].name + end + self.Outputs=WireLib.CreateOutputs(self,outputs) +end + + +---------------------------------------------------- +-- UpdateOverlay +---------------------------------------------------- +function ENT:UpdateOverlay() + txt = "" + if IsValid(self.User) then + txt = "In use by: " .. self.User:Nick() + end + + self:SetOverlayText(txt) +end + + + + + + + +---------------------------------------------------- +-- Initialize +---------------------------------------------------- +function ENT:Initialize() + self:PhysicsInit(SOLID_VPHYSICS) + self:SetUseType(SIMPLE_USE) + + self.InteractiveData = {} + + self:InitData() + + + self.BlockInput=false + self.NextPrompt = 0 + + self:UpdateOverlay() + + +end + + + +function ENT:OnRemove() + self:Unprompt( true ) +end + + +function ENT:ReceiveData() + local data = InteractiveModels[self:GetModel()].widgets + for i = 1, #data do + WireLib.TriggerOutput(self, data[i].name, net.ReadFloat()) + end +end +---------------------------------------------------- +-- Receiving data from client +---------------------------------------------------- +util.AddNetworkString("wire_interactiveprop_action") +net.Receive("wire_interactiveprop_action",function(len,ply) + self = net.ReadEntity() + if not IsValid( self ) or not IsValid( ply ) or ply ~= self.User then return end + + self:ReceiveData() + + + self:UpdateOverlay() +end) + +---------------------------------------------------- +-- Prompt +-- Sends prompt to user etc +---------------------------------------------------- +util.AddNetworkString("wire_interactiveprop_show") +function ENT:Prompt( ply ) + 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 ) + + self:UpdateOverlay() + else + self:Prompt( self:GetPlayer() ) -- prompt for owner + end +end + +util.AddNetworkString("wire_interactiveprop_close") +net.Receive("wire_interactiveprop_close",function(len,ply) +local self = net.ReadEntity() +self:Unprompt() +end) + +util.AddNetworkString("wire_interactiveprop_kick") +function ENT:Unprompt( ) + + self.User = nil + self:UpdateOverlay() +end + + +---------------------------------------------------- +-- Use +---------------------------------------------------- +function ENT:Use(ply) + if not IsValid( ply ) then return end + + self:Prompt( ply ) +end + +duplicator.RegisterEntityClass("gmod_wire_interactiveprop",WireLib.MakeWireEnt,"Data") diff --git a/lua/entities/gmod_wire_oscilloscope.lua b/lua/entities/gmod_wire_oscilloscope.lua index 17b28864..ee44bf7d 100644 --- a/lua/entities/gmod_wire_oscilloscope.lua +++ b/lua/entities/gmod_wire_oscilloscope.lua @@ -50,19 +50,19 @@ if CLIENT then self.GPU:RenderToGPU(function() surface.SetDrawColor(10,20,5,255) - surface.DrawRect(0,0,512,512) + surface.DrawRect(0,0,1024,1024) local nodes = self:GetNodeList() for i=1,length do local i_next = i+1 if not nodes[i_next] then continue end - local nx1 = nodes[i].X*256+256 - local ny1 = -nodes[i].Y*256+256 - local nx2 = nodes[i_next].X*256+256 - local ny2 = -nodes[i_next].Y*256+256 + local nx1 = nodes[i].X*512+512 + local ny1 = -nodes[i].Y*512+512 + local nx2 = nodes[i_next].X*512+512 + local ny2 = -nodes[i_next].Y*512+512 - if ((nx1-nx2)*(nx1-nx2) + (ny1-ny2)*(ny1-ny2) < 256*256) then + if ((nx1-nx2)*(nx1-nx2) + (ny1-ny2)*(ny1-ny2) < 512*512) then local a = math.max(1, 3.75-(3*i)/length)^1.33 local a2 = math.max(1, a/2) @@ -79,14 +79,14 @@ if CLIENT then end surface.SetDrawColor(30, 120, 10, 255) - surface.DrawLine(0, 128, 512, 128) - surface.DrawLine(0, 384, 512, 384) - surface.DrawLine(128, 0, 128, 512) - surface.DrawLine(384, 0, 384, 512) + surface.DrawLine(0, 256, 1024, 256) + surface.DrawLine(0, 768, 1024, 768) + surface.DrawLine(256, 0, 256, 1024) + surface.DrawLine(768, 0, 768, 1024) surface.SetDrawColor(180, 200, 10, 255) - surface.DrawLine(0, 256, 512, 256) - surface.DrawLine(256, 0, 256, 512) + surface.DrawLine(0, 512, 1024, 512) + surface.DrawLine(512, 0, 512, 1024) end) self.GPU:Render() diff --git a/lua/entities/gmod_wire_textscreen.lua b/lua/entities/gmod_wire_textscreen.lua index 8ead52b2..857e7066 100644 --- a/lua/entities/gmod_wire_textscreen.lua +++ b/lua/entities/gmod_wire_textscreen.lua @@ -142,8 +142,8 @@ if CLIENT then if self.NeedRefresh then self.NeedRefresh = nil self.GPU:RenderToGPU(function() - local w = 512 - local h = 512 + local w = 1024 + local h = 1024 surface.SetDrawColor(self.bgcolor.r, self.bgcolor.g, self.bgcolor.b, 255) surface.DrawRect(0, 0, w, h) @@ -197,7 +197,7 @@ if CLIENT then local fontData = { font = font, - size = 380 / chrPerLine, + size = 760 / chrPerLine, weight = 400, antialias = true, additive = false diff --git a/lua/wire/client/cl_modelplug.lua b/lua/wire/client/cl_modelplug.lua index c838f716..d5581c52 100644 --- a/lua/wire/client/cl_modelplug.lua +++ b/lua/wire/client/cl_modelplug.lua @@ -49,17 +49,62 @@ list.Set( "WireScreenModels", "models/props/cs_office/computer_monitor.mdl", tru list.Set( "WireScreenModels", "models/kobilica/wiremonitorbig.mdl", true ) list.Set( "WireScreenModels", "models/kobilica/wiremonitorsmall.mdl", true ) list.Set( "WireScreenModels", "models/props/cs_assault/Billboard.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb/pcb0.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb/pcb1.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb/pcb2.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb/pcb3.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb/pcb4.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb/pcb6.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb/pcb5.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb/pcb7.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb/pcb8.mdl", true ) list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb8.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb1.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb2.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb3.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb4.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb5.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb6.mdl", true ) +list.Set( "WireScreenModels", "models/cheeze/pcb2/pcb7.mdl", true ) list.Set( "WireScreenModels", "models/props_lab/monitor01a.mdl", true ) list.Set( "WireScreenModels", "models/props_lab/monitor02.mdl", true ) list.Set( "WireScreenModels", "models/props/cs_militia/reload_bullet_tray.mdl", true ) list.Set( "WireScreenModels", "models/props_lab/workspace002.mdl", true ) list.Set( "WireScreenModels", "models/props_lab/reciever01b.mdl", true ) +list.Set( "WireScreenModels","models/props_c17/consolebox05a.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/reciever01c.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/reciever01d.mdl", true ) +list.Set( "WireScreenModels","models/props_c17/consolebox01a.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_interface001.mdl", true ) +list.Set( "WireScreenModels","models/props_c17/cashregister01a.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_monitorbay.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/workspace001.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/citizenradio.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/securitybank.mdl", true ) +list.Set( "WireScreenModels","models/beer/wiremod/gate_e2.mdl", true ) +list.Set( "WireScreenModels","models/beer/wiremod/targetfinder.mdl", true ) +list.Set( "WireScreenModels","models/bull/gates/microcontroller1.mdl", true ) +list.Set( "WireScreenModels","models/bull/gates/microcontroller2.mdl", true ) +list.Set( "WireScreenModels","models/jaanus/wiretool/wiretool_gate.mdl", true ) +list.Set( "WireScreenModels","models/jaanus/wiretool/wiretool_controlchip.mdl",true ) +list.Set( "WireScreenModels","models/props_lab/keypad.mdl", true ) +list.Set( "WireScreenModels","models/weapons/w_c4_planted.mdl", true ) +list.Set( "WireScreenModels","models/weapons/w_toolgun.mdl", true ) +list.Set( "WireScreenModels","models/xqm/panel1x1.mdl", true ) +list.Set( "WireScreenModels","models/xqm/panel1x2.mdl", true ) +list.Set( "WireScreenModels","models/xqm/box5s.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/miniteleport.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/plotter.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_interface002.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_interface003.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_intmonitor003.mdl", true ) +list.Set( "WireScreenModels","models/props_combine/combine_intmonitor001.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/workspace003.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/workspace004.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/servers.mdl", true ) +list.Set( "WireScreenModels","models/props_phx/rt_screen.mdl", true ) + + --list.Set( "WireScreenModels", "models/blacknecro/ledboard60.mdl", true ) --broken list.Set( "WireScreenModels", "models/props_wasteland/controlroom_monitor001b.mdl", true ) --TF2 Billboards @@ -67,12 +112,28 @@ list.Set( "WireScreenModels", "models/props_mining/billboard001.mdl", true ) list.Set( "WireScreenModels", "models/props_mining/billboard002.mdl", true ) --PHX3 +list.Set( "WireScreenModels", "models/hunter/plates/plate025.mdl", true ) +list.Set( "WireScreenModels", "models/hunter/plates/plate025x025.mdl", true ) +list.Set( "WireScreenModels", "models/hunter/plates/plate025x05.mdl", true ) +list.Set( "WireScreenModels", "models/hunter/plates/plate05x075.mdl", true ) +list.Set( "WireScreenModels", "models/hunter/plates/plate05x1.mdl", true ) list.Set( "WireScreenModels", "models/hunter/plates/plate1x1.mdl", true ) + + list.Set( "WireScreenModels", "models/hunter/plates/plate2x2.mdl", true ) list.Set( "WireScreenModels", "models/hunter/plates/plate4x4.mdl", true ) list.Set( "WireScreenModels", "models/hunter/plates/plate8x8.mdl", true ) list.Set( "WireScreenModels", "models/hunter/plates/plate05x05.mdl", true ) list.Set( "WireScreenModels", "models/hunter/blocks/cube1x1x1.mdl", true ) +list.Set( "WireScreenModels","models/props_lab/reciever01b.mdl", true ) +list.Set( "WireScreenModels","models/fasteroid/bull/lcd1.mdl", true ) +list.Set( "WireScreenModels","models/fasteroid/bull/lcd2.mdl", true ) +list.Set( "WireScreenModels","models/fasteroid/bull/lcd3.mdl", true ) +list.Set( "WireScreenModels","models/fasteroid/bull/lcd4.mdl", true ) +list.Set( "WireScreenModels","models/fasteroid/bull/lcd5.mdl", true ) + + + --screens that are transparent list.Set( "WireScreenModels", "models/props_phx/construct/windows/window1x1.mdl", true ) @@ -405,6 +466,21 @@ list.Set( "Wire_Keyboard_Models", "models/jaanus/wiretool/wiretool_input.mdl", t list.Set( "Wire_Keyboard_Models", "models/props/kb_mouse/keyboard.mdl", true ) list.Set( "Wire_Keyboard_Models", "models/props_c17/computer01_keyboard.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/reciever01a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/reciever01b.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/keypad.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/beer/wiremod/numpad.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_interiors/bathtub01a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_c17/furnituresink001a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_interiors/sinkkitchen01a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_wasteland/prison_sink001a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/citizenradio.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_c17/furniturewashingmachine001a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/plotter.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_interiors/vendingmachinesoda01a.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_lab/reciever01c.mdl", true ) +list.Set( "Wire_InteractiveProp_Models", "models/props_trainstation/payphone001a.mdl", true ) + --Hydraulic list.Set( "Wire_Hydraulic_Models", "models/beer/wiremod/hydraulic.mdl", true ) list.Set( "Wire_Hydraulic_Models", "models/jaanus/wiretool/wiretool_siren.mdl", true ) diff --git a/lua/wire/gpulib.lua b/lua/wire/gpulib.lua index ce851dc5..b3f4eaa8 100644 --- a/lua/wire/gpulib.lua +++ b/lua/wire/gpulib.lua @@ -84,7 +84,6 @@ if CLIENT then -- Returns a render target from the cache pool and marks it as used local function GetRT() - for i, RT in pairs( RenderTargetCache ) do if not RT[1] then -- not used @@ -101,7 +100,7 @@ if CLIENT then for i, RT in pairs( RenderTargetCache ) do if not RT[1] and RT[2] == false then -- not used and doesn't exist, let's create the render target. - local rendertarget = GetRenderTarget("WireGPU_RT_"..i, 512, 512) + local rendertarget = GetRenderTarget("WireGPU_RT_"..i, 1024, 1024) if rendertarget then RT[1] = true -- Mark as used @@ -141,12 +140,23 @@ if CLIENT then local fontData = { font="lucida console", - size=20, + size=40, weight=800, antialias= true, additive = false, } surface.CreateFont("WireGPU_ConsoleFont", fontData) + surface.CreateFont("LCDFontBlur", { + font = "Alphanumeric LCD", + size = 26, + antialias = false, + blursize = 1 + }) + surface.CreateFont("LCDFont", { + font = "Alphanumeric LCD", + size = 26, + antialias = false + }) // // Create screen textures and materials // @@ -245,7 +255,7 @@ if CLIENT then }, } -- helper function for GPU:Render - function GPU.DrawScreen(x, y, w, h, rotation, scale) + function GPU.DrawScreen(x, y, w, h, rotation, scale, uvclipx, uvclipy) -- generate vertex data local vertices = { --[[ @@ -267,12 +277,12 @@ if CLIENT then if tex.u == 0 then vertex.u = tex.u-scale else - vertex.u = tex.u+scale + vertex.u = tex.u+scale+uvclipx end if tex.v == 0 then vertex.v = tex.v-scale else - vertex.v = tex.v+scale + vertex.v = tex.v+scale+uvclipy end end @@ -295,7 +305,7 @@ if CLIENT then local OldRT = render.GetRenderTarget() render.SetRenderTarget(NewRT) - render.SetViewPort(0, 0, 512, 512) + render.SetViewPort(0, 0, 1024, 1024) cam.Start2D() local ok, err = xpcall(renderfunction, debug.traceback) if not ok then WireLib.ErrorNoHalt(err) end @@ -317,19 +327,19 @@ if CLIENT then pos = pos - ang:Forward()*(monitor.x2-monitor.x1)/2 end - local h = width and width*monitor.RatioX or height or 512 + local h = width and width*monitor.RatioX or height or 1024 local w = width or h/monitor.RatioX local x = -w/2 local y = -h/2 - local res = monitor.RS*512/h + local res = monitor.RS*1024/h cam.Start3D2D(pos, ang, res) local ok, err = xpcall(renderfunction, debug.traceback, x, y, w, h, monitor, pos, ang, res) if not ok then WireLib.ErrorNoHalt(err) end cam.End3D2D() end - function GPU:Render(rotation, scale, width, height, postrenderfunction) + function GPU:Render(rotation, scale, width, height, postrenderfunction, uvclipx, uvclipy) if not self.RT then return end local monitor, pos, ang = self:GetInfo() @@ -341,8 +351,8 @@ if CLIENT then cam.Start3D2D(pos, ang, res) local ok, err = xpcall(function() local aspect = 1/monitor.RatioX - local w = (width or 512)*aspect - local h = (height or 512) + local w = (width or 1024)*aspect + local h = (height or 1024) local x = -w/2 local y = -h/2 @@ -354,7 +364,7 @@ if CLIENT then if not translucent then surface.SetDrawColor(0,0,0,255) - surface.DrawRect(-256*aspect,-256,512*aspect,512) + surface.DrawRect(-512*aspect,-512,1024*aspect,1024) end surface.SetDrawColor(255,255,255,255) @@ -363,7 +373,7 @@ if CLIENT then render.PushFilterMag(self.texture_filtering or TEXFILTER.POINT) render.PushFilterMin(self.texture_filtering or TEXFILTER.POINT) - self.DrawScreen(x, y, w, h, rotation or 0, scale or 0) + self.DrawScreen(x, y, w, h, rotation or 0, scale or 0, uvclipx or 0, uvclipy or 0) render.PopFilterMin() render.PopFilterMag() @@ -423,7 +433,7 @@ if CLIENT then local model = ent:GetModel() local monitor = WireGPU_Monitors[model] - local h = 512*monitor.RS + local h = 1024*monitor.RS local w = h/monitor.RatioX local x = -w/2 local y = -h/2 diff --git a/lua/wire/stools/characterlcd.lua b/lua/wire/stools/characterlcd.lua new file mode 100644 index 00000000..f49f71b1 --- /dev/null +++ b/lua/wire/stools/characterlcd.lua @@ -0,0 +1,67 @@ +WireToolSetup.setCategory( "Visuals/Screens" ) +WireToolSetup.open( "characterlcd", "Character LCD", "gmod_wire_characterlcd", nil, "Character LCDs" ) + +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:" ) + TOOL.Information = { { name = "left", text = "Create/Update " .. TOOL.Name } } +end +WireToolSetup.BaseLang() +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("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 +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, + +} + +function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_characterlcd") + WireDermaExts.ModelSelect(panel, "wire_characterlcd_model", list.Get( "WireScreenModels" ), 5) + panel:AddControl("Color", { + Label = "#tool.wire_characterlcd.bgcolor", + Red = "wire_characterlcd_bgred", + Green = "wire_characterlcd_bggreen", + Blue = "wire_characterlcd_bgblue", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + panel:AddControl("Color", { + Label = "#tool.wire_characterlcd.fgcolor", + Red = "wire_characterlcd_fgred", + Green = "wire_characterlcd_fggreen", + Blue = "wire_characterlcd_fgblue", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + panel:NumSlider("Width", "wire_characterlcd_width", 1, 56, 0) + panel:NumSlider("Height", "wire_characterlcd_height", 1, 16, 0) + panel:CheckBox("#Create Flat to Surface", "wire_characterlcd_createflat") + +end diff --git a/lua/wire/stools/interactiveprop.lua b/lua/wire/stools/interactiveprop.lua new file mode 100644 index 00000000..2702954f --- /dev/null +++ b/lua/wire/stools/interactiveprop.lua @@ -0,0 +1,29 @@ +-- Author: mitterdoo (with help from Divran) + +WireToolSetup.setCategory("Input, Output") +WireToolSetup.open("interactiveprop","Interactive Prop","gmod_wire_interactiveprop",nil,"Interactive Props") +if CLIENT then + language.Add( "Tool.wire_interactiveprop.name", "Wire Interactive Prop" ) + language.Add( "Tool.wire_interactiveprop.desc", "Opens a UI panel which controls outputs for use with wire system." ) +end + + + +TOOL.ClientConVar = { + model = "models/props_lab/receiver01a.mdl" +} + +if SERVER then + function TOOL:GetDataTables() + return {} + end +end + +WireToolSetup.BaseLang() +WireToolSetup.SetupMax(20) + + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header",{Description="Opens a UI panel which controls outputs for use with the wire system."}) + ModelPlug_AddToCPanel(panel, "InteractiveProp", "wire_interactiveprop", true) +end diff --git a/lua/wire/wiremonitors.lua b/lua/wire/wiremonitors.lua index 7ea64913..c779c200 100644 --- a/lua/wire/wiremonitors.lua +++ b/lua/wire/wiremonitors.lua @@ -11,7 +11,7 @@ function WireGPU_AddMonitor(name,model,tof,tou,tor,trs,x1,x2,y1,y2,rot,transluce local monitor = { Name = name, offset = Vector(tof, -tor, tou), - RS = trs or (y2 - y1) / 512, + RS = (trs or (y2 - y1) / 512)/2, RatioX = RatioX, x1 = x1, @@ -116,7 +116,7 @@ function WireGPU_FromBox_Helper(name, model, boxmin, boxmax, rot, translucent) local monitor = { Name = name, offset = offset, - RS = (y2-y1)/512, + RS = (y2-y1)/1024, RatioX = (y2-y1)/(x2-x1), x1 = x1, @@ -181,6 +181,47 @@ WireGPU_AddMonitor("Panel 0.5x0.5", "models/hunter/plates/plate05x05.mdl", WireGPU_AddMonitor("Tray", "models/props/cs_militia/reload_bullet_tray.mdl", 0 , 0.8 , 0 , nil , 0 , 7.68 , 0 , 4.608 , true) WireGPU_FromBox_Helper("Wall-mounted TV", "models/props_wasteland/controlroom_monitor001b.mdl", Vector(-10.2,-12.6,-3), Vector(10.7,4.7,15.38), Angle(0, 90, 103.2)) WireGPU_FromBox_Helper("Oscilloscope", "models/props_lab/reciever01b.mdl", Vector(-5.93,-2,-3), Vector(-1.74,2.1,6.225), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Oscilloscope 2", "models/props_c17/consolebox03a.mdl", Vector(4,2,-10), Vector(10.6,7.1,10), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Oscilloscope 3", "models/props_c17/consolebox05a.mdl", Vector(-6,0,-10), Vector(0.9,5.1,11), Angle(0, 90, 87)) +WireGPU_FromBox_Helper("Receiver", "models/props_lab/reciever01c.mdl", Vector(-5.2,-1.7,-3), Vector(-0.2,0.8,5.5), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Receiver 2", "models/props_lab/reciever01d.mdl", Vector(-5.2,-1.7,-3), Vector(-0.2,0.8,5.5), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Oscilloscope 4", "models/props_c17/consolebox01a.mdl", Vector(8.5,7.2,-10), Vector(15,9.4,16.4), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Combine Console", "models/props_combine/combine_interface001.mdl", Vector(-9.9,25.6,-10), Vector(5.7,33.5,34.2), Angle(0, 90, 41.5)) +WireGPU_FromBox_Helper("Cash Register", "models/props_c17/cashregister01a.mdl", Vector(-9.2,8.5,-10), Vector(4.4,11.6,-5.9), Angle(0, 180, 90)) +WireGPU_FromBox_Helper("Combine Monitor", "models/props_combine/combine_monitorbay.mdl", Vector(-30.7,-26,-10), Vector(38,34.7,-4.31), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Workspace 001", "models/props_lab/workspace001.mdl", Vector(4,37,0), Vector(21.2,52,11.1), Angle(0, 15, 83)) +WireGPU_FromBox_Helper("Radio", "models/props_lab/citizenradio.mdl", Vector(-5.8,11.7,-3), Vector(11.3,15.3,8.2), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Security Bank", "models/props_lab/securitybank.mdl", Vector(-4.6,66,-3), Vector(25,86.5,12), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("GPS", "models/beer/wiremod/gps.mdl", Vector(-2.9,-2.1,-3), Vector(2.9,2.9,1.18), Angle(0, 90, 0)) + +WireGPU_FromBox_Helper("E2", "models/beer/wiremod/gate_e2.mdl", Vector(-2.8,-2.8,-3), Vector(2.8,2.8,0.55), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("Target Finder", "models/beer/wiremod/targetfinder.mdl", Vector(-3.2,-2.3,-3), Vector(3.2,1.2,1.5), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("4-pin DIP", "models/bull/gates/microcontroller1.mdl", Vector(-2.3,-1.2,-3), Vector(2.3,1.2,0.96), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("8-pin DIP", "models/bull/gates/microcontroller2.mdl", Vector(-4.3,-1.2,-3), Vector(4.3,1.2,0.96), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("Gate", "models/jaanus/wiretool/wiretool_gate.mdl", Vector(-2.9,-2.9,-3), Vector(2.9,2.9,0.82), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("Controller", "models/jaanus/wiretool/wiretool_controlchip.mdl",Vector(-3.4,-1.5,-3), Vector(3.4,1.5,0.82), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("Keypad", "models/props_lab/keypad.mdl", Vector(-1.7,2,-3), Vector(1.7,4,0.68), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("C4", "models/weapons/w_c4_planted.mdl", Vector(1.5,1.35,-3), Vector(7.8,4.6,8.65), Angle(0, -90, 0)) +WireGPU_FromBox_Helper("Toolgun", "models/weapons/w_toolgun.mdl", Vector(-1.4,4.7,-3), Vector(1.05,7.16,-0.14), Angle(0, -90, 45)) +WireGPU_FromBox_Helper("Blue Panel 1x1", "models/xqm/panel1x1.mdl", Vector(-9.2,-9.2,-3), Vector(9.2,9.2,-0.3), Angle(0, -90, 0)) +WireGPU_FromBox_Helper("Blue Panel 1x2", "models/xqm/panel1x2.mdl", Vector(-9.2,-31.2,-3), Vector(9.2,9.2,-0.3), Angle(0, -90, 0)) +WireGPU_FromBox_Helper("Blue Box", "models/xqm/box5s.mdl", Vector(-9.2,-9.2,-3), Vector(9.2,9.2,9.3), Angle(0, -90, 90)) +WireGPU_FromBox_Helper("Teleporter", "models/props_lab/miniteleport.mdl", Vector(20.8,-8.1,-3), Vector(30.2,-3.7,17.8), Angle(0, 90, 55)) +WireGPU_FromBox_Helper("Printer", "models/props_lab/plotter.mdl", Vector(-10.2,-19.5,-3), Vector(-6.7,-18.3,39.6), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("Combine Console 2", "models/props_combine/combine_interface002.mdl", Vector(-14.15,27.5,-10), Vector(11.5,32.3,34.2), Angle(0, 90, 41.5)) +WireGPU_FromBox_Helper("Combine Console 3", "models/props_combine/combine_interface003.mdl", Vector(-20.3,48.7,-10), Vector(19.9,50.9,13), Angle(0.5, 91, 70)) +WireGPU_FromBox_Helper("Combine Monitor 2", "models/props_combine/combine_intmonitor003.mdl", Vector(-17,0,-10), Vector(15,48.5,22.8), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Combine Monitor 3", "models/props_combine/combine_intmonitor001.mdl", Vector(-16,3,-10), Vector(10,48.5,-4.1), Angle(0, 90, 90)) +WireGPU_FromBox_Helper("Workspace 003", "models/props_lab/workspace003.mdl", Vector(110,73.3,-3), Vector(149,96,-1), Angle(0, 90, 101)) +WireGPU_FromBox_Helper("Workspace 004", "models/props_lab/workspace004.mdl", Vector(4.2,37,0), Vector(21.4,52,11.1), Angle(0, 15, 83)) +WireGPU_FromBox_Helper("Servers", "models/props_lab/servers.mdl", Vector(-18.2,7.8,0), Vector(-4.7,19.1,12.1), Angle(0, 90, 82)) +WireGPU_AddMonitor("Plasma TV (16:10) 2", "models/props_phx/rt_screen.mdl", 6.1 , 18.93, 0 , 0.065 , -28.5 , 28.5 , 2 , 36 ) +WireGPU_FromBox_Helper("8x2 LCD", "models/fasteroid/bull/lcd1.mdl", Vector(-4.91,-1.02,-3), Vector(1.31,1.02,0.8), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("16x2 LCD", "models/fasteroid/bull/lcd2.mdl", Vector(-4.91,-1.02,-3), Vector(7.52,1.02,0.8), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("16x4 LCD", "models/fasteroid/bull/lcd3.mdl", Vector(-4.91,-3.11,-3), Vector(7.52,1.02,0.8), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("40x4 LCD", "models/fasteroid/bull/lcd4.mdl", Vector(-4.91,-3.11,-3), Vector(26.22,1.02,0.8), Angle(0, 90, 0)) +WireGPU_FromBox_Helper("20x4 LCD", "models/fasteroid/bull/lcd5.mdl", Vector(-4.91,-3.11,-3), Vector(10.65,1.02,0.8), Angle(0, 90, 0)) + -- Offset front, offset up, offset right, resolution/scale OF OU OR SCALE LOWX HIGHX LOWY HIGHY ROTATE90 --WireGPU_AddMonitor("LED Board (1:1)", "models/blacknecro/ledboard60.mdl", 6.1, 18.5 , 11 , 0.065 , -60 , 60 , -60 , 60 ) -- broken @@ -226,3 +267,4 @@ local function fallback(self, model) end setmetatable(WireGPU_Monitors, { __index = fallback }) + diff --git a/models/fasteroid/bull/lcd1.dx80.vtx b/models/fasteroid/bull/lcd1.dx80.vtx new file mode 100644 index 0000000000000000000000000000000000000000..ffbed2609ad5c687a9121892c567343420e3b56c GIT binary patch literal 2145 zcmb7_*G`*35QUE&z*N%=gx-7ay_;%-=@>BNCW@lmKfB|d`PT^mZ)tU?z#IB+-wUlV`iFAB`~b~7|8!qMuBO@0GXWM9NegZa-qC*1 ze$sxFM*s7k9IvA!xtb-gl87cg*n|bXa$m& zGn0Y|L7N~tEk4tsNV}j(V08)WTnjD)pTX?hig0J)OG<-qN$N$|E^W8lk-`!L7-8~<#m$#PR0De$@ zIrzQd_m7{LUskW*LH}O9X_O)FJA*iX6iQIa+pvt9;HQ*71qoUODpAFsfl68plBnhV znxxgE0gXseQ)ognT6noO(^}Dnc63lX(1|W|qlemqUi6`#7jYkLkTw)_n0^=|K}YGw zFpdf81ST;>Z#s!-%+Q-oV-|Cm#{zW$i&&yJUBoh0=uMZgiZ!fbgSvrDY|)!;VjDa3 zrn~fe*hiY0#sLm#Y3dRE5&bbgC!}MX(w`zj%^(}}j6RDqoKw${qvp6umUe&(T;hs$ zO}(Pt&~Ag?(cj^odP~hy^LXGVk4HSwn-=H`^v~31*sbz+bxK$tRi(UG8LLWlsLcO9 WA62ogSV^m5Dpc~%b)v@kxBdo*zE55N literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd1.dx90.vtx b/models/fasteroid/bull/lcd1.dx90.vtx new file mode 100644 index 0000000000000000000000000000000000000000..9fb21d2eb5abc7a254ad200c4798dac4f1871c6c GIT binary patch literal 2145 zcmb7_*KU(R5QUFT5~n)NNl5R#_uidqCr*!(IN=5%#2qid9nXUV4+4+Gnf-l6h+H7n znlqng*Sq7L{r67*cuQME1>Vqq`(9{`(La;}<_Boz`PF?1xteB8V*)HFk`~+;yrcc1 z{iOXUjehf<9IvA!Ih!T0l88D!Sce6^vSWZgSM2y`9gHE(Daz=-6gxgyhs|xx*0L5u z%u@TzF*cbsTg_Ur>xEr5zcmdhc4gRQD_FA?trfd0O~m4{mM9pH#RbtE>}pn|Q_v;o z5%dcB1l@uLLGZR#N|8pvyuiv5@>vor3g!fsaTpWIV-wCQG9`%qExTqEnHG!*xb18l zGbxx5v}e-$Q>qeOvt4c=BEX{}+DPe8SiHcp5&T=i@*3#C&zn$5;1MeRa>r>*YtwZvfvY zza0GD@cYMi%rC3g&!GP=Uo^^)_nkqUFNG46@-{4^Cip4ks~|zEKqad98mOezAcEJdZekld z^rpM?d)P;sn#KVRX=&;a{So~!KPRMPoYJ2nL(L!?^o%}>Gn`Y;k)!4~OO|$k3tZxg zc1^va-q3D?-qGLTo_b5oQ}cM>Cyz%w(VG_N3-r&_XV|UscXdiwA62EiSQ)EIb*Rk$ Xj*qHXSFEH}F%>HL=RDED`M3TCy{b=x literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd1.mdl b/models/fasteroid/bull/lcd1.mdl new file mode 100644 index 0000000000000000000000000000000000000000..1b0b979fc5dc55e44d4b7b20e1e12076adfddf88 GIT binary patch literal 2516 zcmds3KWGzS6o0Y*B3MZWp^AfpbE$$TR?LYG4JZiec2F+4uOaYVzi@X!3PElV99%j$ zM0W|eR7F7-lc9^CTL%Y+f>7w@Bx=R;{c`uw6@B4RGxOVIIA`xxA zG>@-Zo-bTgvCU#lN>i5XGYgI_**Y3+A5QuiuXpY&wX&4UUv2KS?k#V&iGp|MntSIkA~7B^;y5R&6wphKQ#8M#hQ@q-CDK+rYT zj%}li1$o0vK~BjO&|@Ogk>GCJs-z;wf;YkZ+Y>;6dL^Q zLGPZ*1WOdNGgmkd7ITFQybrZsz`4LoJy--qSP&}*L%d%!@Phm8S~?B?HSTg8{El>4 z>dVdciRSwjpzLndU^$Enm%m%&MVkdE45O{n!*D?`U<9&hkF z>E8H_$*GiFRZ?rdtE3d}*#*zBho x7E4uEt(Cn9y<2gGRAm)uR#muu&5iw(Yps<9b^g$h%rmJdgli(kQMSq5{s9(@{tN&B literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd1.phy b/models/fasteroid/bull/lcd1.phy new file mode 100644 index 0000000000000000000000000000000000000000..d7df85a5c5be6e8d4279101b916a1dabddf4545c GIT binary patch literal 695 zcmZuu&ui2`6n@-Vy|w4yU)-uu4yy>A|wG3u=$gqN-Br(Ypi zYF@rZh|id$^upmswB@{LovkdlYnPt3AIA^pchimePI+{0W4*J`K|`AL^gORDH5Whh zQYxGxDs%pbk73xKpd9v4%6MPrXoB)oq1+~+wyO!KKnCTX6XG85C>3d(3K7@4x`29) zzUtylLfiu`Lw%BrCi@vwq7mr7>(WKWJ33Ft!FBcU7=bsif1u~HK89+Me(F-jcD30c z=nQy%(FXn2BUQ%V(EkxLc6D{Tw!n^Cze;a8+S|1^2ypYPnJ2K4`P%95yvP`O*XOxCSFmuh3!U4MkqwSh!f8S?}#As z!aHnxC{{B0FYkq}N<8tm^F8UWWW$`Bnw*`PK0h;SnQNx&cu6Fb2x1;xNtyIV#+`{w Lg#XWoYbN~x@x!mY literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd1.sw.vtx b/models/fasteroid/bull/lcd1.sw.vtx new file mode 100644 index 0000000000000000000000000000000000000000..182267a86d40674c90718683f2447942a9a93639 GIT binary patch literal 2113 zcmZ{l=~CJ;6vYpyb;kt+!41WI!(H43QE|b2-}GWG)px^pDooOGWeItAF59dy2 zY{P{7$W3nUNp3ql(SM07Y9K2`! z&H9V=XN{lpfs9tx2)%W(&DDCtM`v&y-)Iid)N- zP!JHlaEx>2id)T3W9fHrTs z6eI-Pac|6g&ww1kHkV!L#5+a3Z)DtO#xd%y%FBwSc$L_e2F} zg1BH?&?V>*qy>Ef-XHI9STH7-6l4Xnf>tSl^vYzvt27YI7rot#0vH7`D2_V`*cDVw+%3ik*(dY%hsz%iG--@7pX5UNqbm#~)4e?1z|$Y;M95w!3vZ9_Yvh@k^995#%6b=~E91c0+2*=3NPmT%-C{t$`Csb5}Q!1*% z85K3+oQfj2z$F!3@zY9eE!1{RburvfT>`gUAw}<9)OJU8Y1~s?1`kv>j7O>)!xQy% z;+g6u8N&|1MnVQk^lez literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd1.vvd b/models/fasteroid/bull/lcd1.vvd new file mode 100644 index 0000000000000000000000000000000000000000..b3a55052f3519c3cf5c971679d619bf21c1af1db GIT binary patch literal 8960 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 H&g1_BGEx+? literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd2.dx80.vtx b/models/fasteroid/bull/lcd2.dx80.vtx new file mode 100644 index 0000000000000000000000000000000000000000..285a9f7e9e69104c4700db0331e49bf7babf7f36 GIT binary patch literal 2361 zcmb7_XFRWKrmB020o1csp%hnWYirVo(Ds)iu(!zef5|0VKrUyuWpv#STgv= z{*(O&`}d-7>Lxi_Mft_mf(iz&7xV$yGH1`6^{6nysMQii(t(nTFbkt=t3kKX{HIlq6E+?XcE*35`z1J zhk{YTHNlzSonTk6DtIes5S$9W2;zdAU`?ryj6!Z&%LNr&HMZ!I~h6N7<#ey-x zh~SaHvL3`r3&sVL0-hK)#(FH65M%@~!4tuh;HjWY@LcdrFf9m=A7jmGWJWM2s1RfY zF9fdy!TGd3FKA?5@KTTx1lA_s(8zVcxu8+-Rd69V6eI;Ff+K;Q<=`2HhnBI9HL@@G zET|Q13$_HG1T}(9!AHTmpjxmY_#jvjTot?*yb&x4Dh00v%L1?GVBn9+^fLAu^DD)x z#Y@y{%nzDZf|reN!w;Ho-b=z4^!<2rKQbQ6_v2spjd^t6k4N|Y_}$|B@vF%<=4tW= z<;nHz_$}&B%X{)F)Y7B8y)5cLlYZ^x_KXs%!+Hz_+68v_8whY&3>ybnQKS?z5lSGP|!gbt0 z6Pl^bxQQ0D@`cpGej9CQM+db7o#?_HbW^+0gS+TOAGHtt7{DNgs6!aW2<~B&I*R*v zfQJ~Pj^Po;F@ZESjY&-5F*4K)p5Q5-VVXLP89c`e%u;7Dhb&&=73Qf~EYN1Lh$Spj zm$8D^c!O2yD&FE9-XllNVGSR!jt%MtKH?KLu|?hDn|vEP*ro1b5BoU4A@vYPIK~&8 zP)~4*Go0gsdI7sJ{?1N8J~Nh;5_Zc>1#?l(ZpHI|^DSqVxoT$0@|B<_n4in*@SlHb FzX4&ZSa1LU literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd2.dx90.vtx b/models/fasteroid/bull/lcd2.dx90.vtx new file mode 100644 index 0000000000000000000000000000000000000000..3dbcca6a337d8f8fbf9c640eb4801f68cebc6414 GIT binary patch literal 2361 zcmb7_>0VPY5XDa`SSUp(OQlfR1r(5dQ9-JdRlpS#6a)nUWnc6md^mjypTjfv_RLTH zq-k^J-z3fCW+u5Y05z;bD8?1qs>&=Pw0*@0Y%AG*7yV`n$O%>})@Ne^7!>3LFAcu3 z{$Tyi`t52sbekM4r~Kq>wt#Yau*p7Jhc&DhhBLS4JNB%DgOTAB{9+~F@yR-DZY$=? zN(2#`ZH{AXGAriIO1^8DU9Dk@56O46u**DHF^^XAUA9a_qLD_L0L)7OErJF?wIC(9 zE4VKh6+#5bur&{g^x&=LgUO|_jNe~r; zJIe{1)pAo{X%_6*EkT>0T@da%|9)F79fE!V^Mi$B1_XVAAQ7!Aj3Qw~T|}X${OD^zEpviJL8)L# z@L2Fv5S&k2^K-R46Fd=Q1c9}|^=i2xxDeC{z6dS_hk~@=RB$A)vmESUcxdVCSS_Cg z9|cu{UBQmvgP>BdEqE{36jTVd1n&eZf-=Ed!E3<_!8O4v!Lq>fIcWGJGd+zx$NWn1 zZ1EKJ9P@+bnc!*T%kYEdi}#f934J~8-H(j>^7Z)NePQn1*W=!OJ$|?Ndi-kgg?X5K zqdd4C9lu3=$NbLpo7R`%&*e_Tr{FzqW1@W76`+u}{8eg<8sjUjh+2#|O89zxUDPgg;|_YzOYKD;`Z0h(>L6dFiV}q9Om&D3)BTHVhK<16wjzjcuu>77kG(f z>M~aF3a_zBUBw#S;4Rjv>)60MY+{SLh4=V?ZR}8Y_$J@Q9`>pG_=L|mz#;VzM>xg_ zPN}Cj!#OT+Nxg*K7=K5nARifvN)f9?rj)Truv+r`-*}6eMXrL8vUsJaDaPmbdHCa> F$}hFgSd{<( literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd2.mdl b/models/fasteroid/bull/lcd2.mdl new file mode 100644 index 0000000000000000000000000000000000000000..291cfc1188f348ecc60d296f5579a12302faa194 GIT binary patch literal 2516 zcmds3KWGzC7=N+ZDNd{C>IjY;s91k!*hO-S>UJ_x-!~?=Exg=B-)A z*oosQwq#aZ%aKK%%~gfSiaftKV{<{H!_n4sm)|H&xwW^x&y$0;%gHr0aO?`AFr}_Wvy3(&G6M z)c@VR&OH_LE;GgUvCN#}dB*)aeeAuEh+=%iO2qz~S<6(kt6c4J2 z>pDR-v0hKY$;s|@ox?YZ2i5!EnUk?L@?yn5BZnT7PIrR>`uQWjR^I*pg?d+drg{ B{*?d# literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd2.phy b/models/fasteroid/bull/lcd2.phy new file mode 100644 index 0000000000000000000000000000000000000000..719ddbc48f2d35ebfd7cc0d6dde43bf02b127bb0 GIT binary patch literal 695 zcmZuuJ#W)c6upU^xOEFEO?(QX7V5*VD^fS*zWC(`(6` zA{yuZ8E+$qHz-Y$lxh-cN2h2C%mqrf2-*)#Kqj3f<2j*rfHPQmG9&h9v2wsab>=?f zFFH-F6ZFkbHKx4u1HV*PqwA7DOzar(plz|VQZ znEi|g=h;7uIkWZqB8z~tQtbbU4v;KPP zyB?E~#rl`C6*%S!Zn9I>;K5F!pSd<>IClniMhU0j7i%%Yr8Bs>U2$u<5*JK{N%nEG zyW&=JC1z@&smedGE-_PxCbxnsZbesOrhiF}*0f-OhB7ARSL=l+;MNB z2Og>gd<*_by`WY=U%aV7&?;yVvFVkAWbkOm=G`o{e&}uSwXsBPOvCoMEX$+f@ML5U|p~# z$P{b~b_Cgid%>e%Q}9(#B-j!#D!o6~f?dJ6;6bo2$PpX}jszjWvEWn?7Muxk1s8%I zLB4?bJ?+phi$9XcROHngs2FZox-EzhFQxBp4S= z3Z@0~f+fMKU_-DcI24=+t^~J&CxK!P%%pY^Z7Bzotl&x<5m>U+x=8xrt1*C0)#i1QC=Cehe?T_t-sW7FM1s2BUnCUWq zyd0k6@x15~kjN80iPu^RQjx~%EFBrh(2u*WbKut*PqG(u$y{EY@$hsa}8^^Llw@jzX3c%-5QYG0<3 z@6@t^Cu)gs^d^2Lj6fs9;FbO_-=-Uz6O0W7W5dDN++ct5f<0l# M1@pL`jFwpW4+y88Y5)KL literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd2.vvd b/models/fasteroid/bull/lcd2.vvd new file mode 100644 index 0000000000000000000000000000000000000000..4204ebc74dbb5c0970349bf756aa0cac7058ff40 GIT binary patch literal 10496 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 literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd3.dx80.vtx b/models/fasteroid/bull/lcd3.dx80.vtx new file mode 100644 index 0000000000000000000000000000000000000000..f8e78f31c703db26a9b0e6101f91863163ea14da GIT binary patch literal 2502 zcmb8x$x;+S6a~=RZ3{Mt(v1v_icAg&3WyAf0y2sy2#Bbli~@?xDvpdsxHMe3@*n&H zH~xStH!l4V@2jetp<#q#y{OC+nbnzDU9Tc589)xtbVRX>_1n7A4-=?mgOxhRY(!| zBzf>x_Iy3?ANAqiB)fL2#jEY&zucEwk6Zko>v2=;WIb;2ZW(T2UP{?|YP7=F!b@RI zs1>$^7s9r%DI6Dyg`+}=a7-8!?g+PqVd1VYB#a2t!ac!~)_WEYgjwN{FeTg# z3JpS|&>~zCnuS)OLueD)h08*ha7E}8t_nTEHKAL$F7yk1La*R$50;|0)G{F46rKvt zg;n91P$$$2=Y;dZj4&_E2`j=A;jyqFEDKA*qTpUDufcA|y*vJk#?2$l2yusvU^lmx zB$mnCQ=>>>M3KtCXWEPJYvzWs?7O==z yz!H|Rg2#*%Ji$|};u&KV&+!5;@d~f8hIMRU6I+Z;YzI%7pETb0n4ePrDt`i)ylZX% literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd3.dx90.vtx b/models/fasteroid/bull/lcd3.dx90.vtx new file mode 100644 index 0000000000000000000000000000000000000000..96045d06038f0d1f11de4a0753b424d3d7b8713f GIT binary patch literal 2502 zcmb8x$x;+S6a~=RZ3{Mt(v1v_icAg&3WyAf0y2sy2#Bbli~@?xDvpdsxHMe3@*n&H zH~xStH!l4V@2jetp<#q#zNpL-nbnzDU9X}l89)w?YDBS%?c2K24kz2igAMo^1JtHsH-o*i=?R2-(;-o}(^X%}Oxzg+n&K6O4)vDw8PiJOJPl@ z6}E&I!nUv}92bg(qe6*rOc)gI2)Bh{;jScf|CEOP#gh^ps z7!yVXul?;-y3BOB`bsBn2X`x)G5UPbL;f!!WXc8_8 z4ML;PB3u%hg;t?MXcO9n%R-lMMd%c+3O&L#pDl2oIw@NGOGBEQiEF5F=}xR z=TVOfjCx!|0~*o9Xv8HnqXn&u7PO%q9k|TsKqs!C3s)IkxQ1@@;5wrRz34+fZZP^W zfSVY^Eyf^j;|_)}%oxI5+`|Y)86z0OI3_U3n81BZVHz`xX*}R($SfW)W-*6(EMSqb yfF&$r1&E=+Hoff{p>X^sXUz*9(`#63lQy(Mjka zG`dR$BSsL>LA{}ipj!tK2Zt)S#YsW&e81fHncUg?Q?mKs-TU`@?|uL7yUSg?d}Wp~ zc6=|9rC>oEp_j_?Oja!m(GoAVF9FmT;zrJoRKDI9~ z>s2>@BlB(NM0SdGC2z*SN{|X}?Q?yVG1nGyi`$v;K~9p?jY-CklA^J|hMp!JwT@2E zg9tT1u!Ya&u{bhE^=60W>gHGydVnwnkiZDr=6&7h;3;~-(-K8U7x@BtkJlCU|14k7 z<~bo0Ja@}`rV`dU##uj{o8vss*k7DQwZKFkpX+jc7B>chpBLeHLgO9UI^q6DexW?z z4h%(_i*5T+{+j)AF8Qptb3A*b{YIK%8`4u>kD(Ngdj6N=-0x$1Sp?#Y1GxBaHqe}A zAXAJjWOZovPsce)y?gvU#FoJi+9nQq?=i;r01-h4i|vNbPw(F57r=ghkd5A8LFn~S z7?ILF2bXA0Vr(>H*1r&|x)PZl0l-q-CGG~`6uPvQK0lmZN_M6LARf0y3zznI!VO@< zxHWmF8CC&n^5eJTU`=-Gi@2YF4dbYuxX0J2=Q_z#{^DdK?k8ZwII1VE>lF3Ges%oC z$=>Zc2R4kOdVcaPUh~0btTP~n*BLSY`iS|N5%Uj@nEyd61ebeVzvuK_-|Bzc6LhA= zXfJWGj*~v&>OntA6^;I$%FE0&j2cta6}hg}Sf;FKD(gS=M$u3jtR&aes}<8&Emd8; ysMS@j$yKhcl}%MMN`~ta3}Z&+Nz+Gsx;uox%t3IJCB)n&pG#;cbONb#?yiA{qVrYEfll) ziI;?ZlS!(K-M&PtpAND;iKXqYzuu1IdY@byzE3gg16Pw#kHG(xQFJSG zB4e)z`z3HUT)m{`xnG653!dLn%P~Hb5sHJW$<8=|SF!%Ub0qIUjnSFZkf%b|Q|!L1 zV}*`oSF(|>(J3*{q%!l3S7rFldHO6mrU!Oku*|+_vK>%uKFjarR1k3({C|I8FdJsHE1fg93Z;GO4mliqdw%K>X|MGUp@z+-hQj%0#IjB|5*n<& literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd3.sw.vtx b/models/fasteroid/bull/lcd3.sw.vtx new file mode 100644 index 0000000000000000000000000000000000000000..64948611b732651331abfba6cb73079b33672e58 GIT binary patch literal 2470 zcmb8x$5K>57zN-n1A>DBf`Xuu6v;tR2_hLmf<%!VhcFCC5(5%V<r ze{oLc@j+HQHhVLaDV|UYKX{KWnWvc&PJLs_jBkp0o+;tfSEkGpOfgS1C7gQC6g*z@ z+~Z>d<}El~3+61ipB7w!1qZb_C2%-<<%|#~@D11$_u7Ihut*T%g+$@JkSrt#7leyK zs*oZ?v@SP!w;Z=Xx8(m>mz(^_b)B4#+g`Wf5$&_v%#-`4^C>4r@BB4aMDU=E2 z!VTe$a7(BV_V*cOMQ9S5g;t?mxG%H`4}>nE zQ|J)7g;|g)w1Nm=Gp~aba3m5~hTpFeA(gbAn%( z7Z!wNVNqBWR)m1CCOi?=g)L!I*btry&xCE^xv(R=5MByzgjd2_VOQ7_j)X(uwcrsT zLs z$BFd*zdbh%>A1v5$7NhW1~M5L$U-)9aFvmRYdn9iBadw!@=?G(AB8AFF{212C`B2{ z8D+SEo2bAoMg?x;4k}TFYStRuMJ?_zYEg%JG@y~ufF?Af1+9!0+(#SQ@qp2e4s@am z-Ha|gL=Sq=$LK{r1~7<6j6n?HF@`b17{(~ZFpdevI3`)A*iK=Z?F?oy2S3{x#ysU` zyMRSj+b>~>e+AeEu*`OuKRJSISFp-{1y9(n^7BE;&vq7TSjPr7v4y9MEo|c%p5p~x jVh67nJJ`h@_Hls2(E5+?8gF>z0B^%@+59MrqHXyN;(E|O literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd3.vvd b/models/fasteroid/bull/lcd3.vvd new file mode 100644 index 0000000000000000000000000000000000000000..78c9e07b515ced50ce85dc9d208cf7ef3a21f90b GIT binary patch literal 11328 zcmb7K33OG}75!=)+G?vHT?iu3T3R$9spJzd$@`HpGA6i~u`vvRB&7|O00HaAV2nbP zK~b=AK@3u)!6E#_lqByJaay2saG=prtt^UF5paNtO3%J8=jHtS?)%oa)(6hN`|Y>S zx#ygF@B3sAn>Z;c6bjv)cI`vw(BDse!-s}L5&ras6}9kZ`*luE%CXFmM`k#8eQ+X^ zzx?{_9ahVr3&Nq0W9#@;gEl*5SIl;{Y53;~ZlL>nMRa@x;fE5xj;CiuzdYO1hX?=I zwm(=pK1dJrLyxZif_c#6*AIMsWVfZ`gY;iVc<9mbiS+LveqDc%9{79NwwJBl8(&uO zN9>6^RJCbap#E-z2L>18hktT4edmuq`~`lK#$TlP{dm^XGN>X|@=sFy=6K5Z&wsU( z{qv?AE0Mo-V~QO1m+^`8$b4gb(0svP@C$z_hZDzB;+a1v{)i9oB0h>gDE^FRe`&rH zKFD9z56!q39~6J^%X}mKR-*Xp`M~%@dStyYJ}Ca2KkP5#gW@@NR*$SFUdwg5&bXWM zHxwGZyx0z3wl{Nm!#JmG(PoNOe7{;gYMWF3#SG`r{$z!pK=>;N|Niz>ktOH9>cbZe zuW_o0KSkp|fBM~#M@#+u#}D_+YCgBl+Cltx9xYbSc<;PDnQOmO;H3McgMJ$xGCo_BO1m}gh z)wZsujN3szK5SvWBz`^dSB>z;2YMI}U(=TtTgxaP&ZGJ5JbQ{So^pPHr+l-J zZ*y`dI}P?mA70LviHGT*uEp);bY+T;zXE`arv2f2tLlTx0Jw zNzW< zT)*)5b3;Fo>z(AEklM%k-Dl7F_~m+?)2=6<^xR7L?RIKJ*8}~W&yv3n>F>27#fQiG za^%P;Z+)SuQfHEET3_-et@i2B*CWOUtw&u3pNN?FvA&r2F<;*Xuh%>4395I!pD;dX zy+i%p_SQ;o{bD@ocjAu+SbF@C&*g;Ye!}?ocAbov@ImnbzbQU?KVkl$euDV4(fsOq z-Xc9Fe!U+ue^5W<`4a0R#UvCOdRd#3RMjn_$0sQM&>z&#rGD;Li06H43w-?SujoHY z5Abbk{Ced4f_u}%>@?`F9Q{l`8Bh7sZ)t@MJbDB1KMVd@ z#Q)BKoK{nOkpGA$@WA4FzGP;ka#VLm$LsmV_Z%QS6}f+*`{Vl+<3)VbGk{NeBGt#w z@e%W@@W`KI^}%=%9}jQR!}EpwKh*mPofmAJ7kUpUH}0?G`X%+$Y`7~B&+9etd>*MIylKCO z`I7sqdfLBDB0X1-e$)O6{+jsvl717vT;HW0oPSOHe7@1DG2wrPE5JoZbJ zHy(cUw2~GRp7(D$K67eGOYR*X(EaiKf$_SYC8WoM4?6EuU1C0e>F0N@Nu0k-`@Nv^ z#m%dC*d{#li+*JxoH7emk;v1FY)=Ce`Vzw_N#J#Nc&Hv2m5E}|EB$? zmJ|H%zo6nA}ao)5qcXWRAPrN_W@u^vd?N$RG{RHpp zcs*tQ?uWNV>H_fCADZx8>kjzhFY6K9xS;>k(Wi|3a(#5sF1`opIXf*YqWjDJMArlU z&chb?@cQ`<&ww8I+tq-_eo4Ncq5Y5A*Yu-!W*G38FXX2l|5G$y#fJFk{kdI_71W=3 z|EBXpPw(me`0#qF<3|vFh(QnParIj(Z5{s!={NDq`h|9m5AY)l@sa0K;Blc3C0g%H z=aIUUa^rbK?q^(fm%obYG5Jw{e~{~SbH_U}E9kr#q(9O5TJHbZU+6LQ2hM-}ytAGB zHJx{)e(7(}ena2Su%4j(j9xFO72JdO8>aUm&~JL50KDma0@ts2f1>#DekKu~>z(xg zZ_>m0ukRMI zN9=Krj+gP2cwkNQ6~tdOyn8d<@8g&8m;Au43a{|tWqc&w#rpY!;sgGut0{E0y@tql zyYlN^jq̹IA&WRosjz{ij8+}96yZ+z#zx>zFHX%(JmA&S}rq;Bab(vWg&axOU$KO5kr`C$z>zkVo;kVkT zeZ!v{Tl*e-to6p*@|1mw?ynuYuGPM9meQ}|cO7VKo%;4;nVS~osqr@A+nK*Uz5Wn> zJB`}94ZYo-To9Xi;s>7Imz?Wje?9Xz`u`HHKNqKskEMV7U!#pUkN1S#>VDX} z&ip3))jJD4zCM-Z;ikEFdUn>{t+D-+YZadfKWkXGhS!dFRd{Ha+UKL#Cs%%;@Vf2h zK`2z)+%}N)at^|FKcUd$&C}w>^YfFHt&g+4_2|?%{KCd^&wl-$FH)QGPkVasJjUbx z@6Wp^97?~?^Y^+{ox@v3bn@)N@7*5Tcc{#}U*ZvuroJ!4PA}T)joXuJVznnX8*TQV zb6U6AAAP+3;@a!oF}DWv@wD03#>MA>!mkv5B%NI@fAjARh3YKR5nmq1*=Ekl-3{(p zBx>W`{H~Ph26|U-;d%PFN$_eXI*?CBb=j*R^jKltX z6Wg%(a_@%~Jnmx2FFea~{|-eTDUR+Ec4*2i&4Z+!@{0_5kL%&RaegxSq4?Ma*8RBO z@HqSHekw7PtQ}XNUdmoRKLmw-|nI<9v|wn^2sm5?r&voy_Ce(dijs| zUtcvR&aODV*sGuPTNm5^YAvVt__(6>!B_6Ew}*SRz<^zTgw~sq!`MqHd*2Hs_T22> ztN$yAaq#EO?~i;@) zN>6Xsr8nAn%lcp*)%_!T{o3BY{OQ(DA6P}0j`^fO zzS4Kr)^|ssKd5ot*7bf{8_y)}_RFu&Fm5qejzKrH;k@AHHc+Txbw%BOdp3RP*0+w= j?G7&RK&!_APM^K#t0C5~gZ=4yTv5A}?BT-xrt$v)My%i| literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd4.dx80.vtx b/models/fasteroid/bull/lcd4.dx80.vtx new file mode 100644 index 0000000000000000000000000000000000000000..125169c73e1b84ab7eea1c249634493c0e297ac5 GIT binary patch literal 2547 zcmcK6Usp{*9LDkIoYWDb5K0IMQAk1*k`O`?l8_`J6d^>BKUdcBg1i7%?k%stjhEn= zz0Y?qZp4N8tv$cbn%QS&_TJ~rJ{d{Nd=x5QInJGpA9GU9-}9xAD}01J`2;+b85yq;{#GmQ6(}k`+c!Y35Pqt$qbi8+!mzKUdk7hdtLr0kUQ=U7W z3$sfc3LRnWP{L55q?8Iv%keohh1o*McKz@rh4DiP^M#V_`sS`MLnvX!P_kX0qZ^S9 zV~56cD(*MD+_)v~bIW2{mVzaq7%Tvbz(P<2;(P4@<0~-c(^UY%vZD3O1#^IJfr~#C zo^J%-(HQYXx+|BKD9>u6aID#10-2d;wa;0EXi-QWV42Is*P=mM|71Mmnu1Y=+T zgr8pYjk*a2!6k47Tn4@13^)g({USVqv$V8>4sZ&b2FJija2%Wfhd~=S3XXt-pcS-$ zL!b#Xg9Bhc*aP-~Mz9y`1Px#p*bTOVI#3I?gUw(Y*aB+6MoHtOcvV8c+tx z!Ah_KBzxU*T1r6)NcOsTYxd(#_QPa{O!k&!7ftrVWQR;PqCaem87BRkjd3<7=Yl-Y z1MY&0;2yXGZiD;a7D$08|F$isvrS4nQhpuJQm)@ad9E{l0q1LuBVTj<@-5JOM}Zdj z#a*O@jv_76VijwNqgdf7r2c7~`22XzoM_&7&dguWyVP&m63<+sQZ08bRhd?3rH?YL za+Imub-7k+jn?|lz&hs&RXSH_z3WOxg*IrTsbrOm2VjkY*yv{l4eF+bbjvZM z+q$D+-E|D>p6+WzqmB_h&_j*skz-7c^+e-(>KNBEJ=cU@I41N`lbX^iy>>n6dfIu) a^&7{u-a4lBF8h@I|0nwM7Jq+cs(%8Vl627k literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd4.dx90.vtx b/models/fasteroid/bull/lcd4.dx90.vtx new file mode 100644 index 0000000000000000000000000000000000000000..c9bf5b466b7b0ee5f1ce3cb12dcac2cd2c8d721e GIT binary patch literal 2547 zcmcK6UsnxL9LDkIoYWDb5K0IMQAk1*k`O`?l8_`J6d^>BKUdcBg1i7%?k%stjhEor zGv_;t8*yR(*6iPB?LFOYS^4#fM zm|fyf=m=wn5{3#TrBqm2j?bYf%oa+v>xVBXj2}vvFO+Q8H+O{@LJ2d5lI{8&-H3D; zJ2a+Kalhf^#w~H5TNcx@6f6P7U;$VJ7J?!W-)j#TUx6{7t^yF26|HA3m;-zZT>Mwz z`9|;^jS*j@yK-rX@(kCNtS4Fg|69*63mXL^;4OFo-hnsZ71#kL!AnpNqCCU3Kjq{J z@C-Z$kHI*20ycp7_v$;pj+XVH5?lj);3~KdZh(H!4K9Fba2`y7F7O&W0FS^!Fa`!d z_}7bmQ8&RLxCE|%%b*vW0p~!pUxY_+mX>zV0ZxI_;21awj)N26FlYlu!4Yr}w1O6J z2sD9aZ~*KFd%!->2=;=VpaJXxyTMja2Wr7~uo-LvTR;ui2&%y*Pz6?jwO}<^1Ij=- zSP52uWUpIJODQM;$zB(4&3@d;ewgf#$=;IeqRD=k?2ySu^oNZx!=!(+G0x`XT#yHP zz+G?=+yi&OZEzpl0x1yX-?rs+wn=G6%CF;D%Jq9F&vnKx;C#(-qGW&8k+7wm53ERohgn?T%X2`E#XS zJ011frQK@K9!G=rs!>ha=V;P?9Z<6lI-1p@Lu%DwN2`wLsM>VQ(Wc`%p>~~gwCj{k zt3ziT9qQCsb?Ka=OXqb#-MZ-LR*x>JSC<{Vx}vM<(=|t*`gL6cy5Shmpl)hNw;V&d ztvedlUB|HQ>AprZ>KM@jJ=B;UImYx@Pc*Klj&VKHb4}=lV?r-AsVTkEYuA&mr=6!< Zzi~|Ktz%m6vQOFHKheiq{QH@y{s|1HboKxM literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd4.mdl b/models/fasteroid/bull/lcd4.mdl new file mode 100644 index 0000000000000000000000000000000000000000..179015db6cc545d84b5466e2a6f0c67b90ec3c33 GIT binary patch literal 2516 zcmds3KWGzS6#v?$wu;rb73tuh;3NeFg=#MWhX&%*EM?GJ4%(I2uJS~KJH zAVLigY~gcwEQ!ofz1e|x^>SFt~RJUHkNv(XzY2)#ZE zBUXC*-~zpq7#qDY>s^SJuEeIt05DZ|h@XYv6uPup-}EVaUu7x;;&W>>ann9ev;k}s zwiP*`SIIzF(*6qMLbBrMsZwE+~YT>=Pi=QgT={4JV?Msaa>Pa*J-i}#dCi9x6YT*pyhlUkUl}q#Gi3gOA@e_oiQsa#^Y?zl+l`J=SL zDTa$RMf$j>hy5f~HTruhFEi6HYD`gA9~z2T1y)p5vw$4kPzp5KKl<4EF#rGn literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd4.phy b/models/fasteroid/bull/lcd4.phy new file mode 100644 index 0000000000000000000000000000000000000000..90d81bf7c2524fa48822597a23feea334702fe0f GIT binary patch literal 684 zcmZuu-zx-B6h6B{DVJa|wV?CygM`^M&Fi9e))LuCt5{SzK#t*~-_@NC1i|PZIza#g+{nLaSkZD2g zjR%s{d5?ZR2!aT~IAA3IdoJJvH8SY|{2sX%u6S*j@G}phe*pW%5S>K&J91CF4hUaz zKiqQCr{hP;%uajc@FjSN62x!X532EGSzqOScy9nBWxax_zO(smztT^B)tLGTSL&(1 z8k6Vl=hqj#!)f^)qImZmwJ+e9hOUN}MM=-7+M*PeWJ#1VEUjT`q}0fiHMis-m1d5E zRCkwLT*x?CdxoVn%Qh_(EQ2|&oWy~CdLojT6E#(LEoQT{L-X3E>6YijLtWEl#-AB;B76hQvaHeo literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd4.sw.vtx b/models/fasteroid/bull/lcd4.sw.vtx new file mode 100644 index 0000000000000000000000000000000000000000..f6380bf63055703b18e9d987d554190d7af771c5 GIT binary patch literal 2515 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 literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd5.phy b/models/fasteroid/bull/lcd5.phy new file mode 100644 index 0000000000000000000000000000000000000000..462c3779c5c9839dbd6189e9aa33e418bbc321dc GIT binary patch literal 684 zcmZuuJ4?e*6h3{W8U;($ZbFIZB9uxIRH}AS6dWpo;2?TwVvVFtNK&nc-2McGE`o#5 z&B@V0D|Ap02WS6)AS!N(?{m_M5xtO{^F7X&Lv9G_$AhJ3y6g1_o4NGlJYd};5=vt~ z4?uDEI_Zt99-lv5j%35-B;P3~XUetVi(+x~1qVvbOg(dIEQk_aqp+Ad9dTpQ`y#i;ZrzO+8vfe)!6?m7H1gb-{NI@TZcHy zK>vQc?GY?pQ|0KUD5-f_DN0c(E{Rf}Osq9Uud a!AzF3XkSy;?Z%$?QCqi}_GiZ}5xxM@kg|&a literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd5.sw.vtx b/models/fasteroid/bull/lcd5.sw.vtx new file mode 100644 index 0000000000000000000000000000000000000000..2a0736b6a036624fcc1f195609f2e9d342dfa7b3 GIT binary patch literal 2470 zcmb8x*HTq67zN;Ur~;y*fPhqy-mB89^p5o2J1C;`uGfw);P@WCgje2p>0@}IE99(< zGmMVppCljq-zPiCW=>8ZSbR(kf-k$2j<}y5_Md_P(?4M~{jEQGKl(PSDZ1wh#k$fc zyfBDj{mN>_LZJQ3yzxEO4;;bVkmpS-n#4QS0M6<=`zCK<*(Cf}KL#+#{s-q|9>2o@JL7$;)OUNK}Z&ogvUaf@I*)vGK5qiUC0r#giIk@C=hamJRx5w5sHLD zf!`N)6H0|+;Y4^UJQFH}GND|k6l#Skp<1XB8iYEbUT74Wh37((&?2-8Z9=QiA#@9! zLYL4Z^b37LufVTSyHUf!fG{Ww38TV@FfL38W5SfMBuomc!n80W%nEbDys#jw2#dnH zuqG@Eo5F^$C2R|?gqOl=;f=5(ycPC^U13kS5DtV(;ZQgd&V*CpT<{1GdgGtNJBdNG-MzXS&U3%BL}(2W8@+q1t>%j zqY%X?K`EZ%8EYBJQGrTE1*%Yu8q_jsP=|Unppnsl=V(GRS{Th}MH|}D!DvS(y3mat zMmKuVhkm?Z^kV>n7{V}P2qPH97{(c6m|&e`JBcZ_)0n|5=Gaa%=BYWh3s_{e{Sub= z*D~8>tgv0-PmWc#YglK$h7Gps{QN34$94vr*uqP^!Zuzrwy}dZc#B=^VIK#KeH`Kl b$2h_1)%wqHjtgEn!KL>tn;&Jqv@O2@E)~#6 literal 0 HcmV?d00001 diff --git a/models/fasteroid/bull/lcd5.vvd b/models/fasteroid/bull/lcd5.vvd new file mode 100644 index 0000000000000000000000000000000000000000..4fbefdb3fdc861fd492e02568762ea60950396e8 GIT binary patch literal 11328 zcmb7K3vgA{6@6%gwlmBK_~Bz9_(2LuNElusG337R4*?{Bff5KsenChL0v;zPr}0Dro|3L5xt_-c`r*qAot@a{ff z^2Zy6#*yTfNj{%ts`%B(Tdk!Xrdc}_{IR?T>He62iZ3Pn0OD8i^sM0bH@dj+;2+WO zXG6t%>4AReQT4Z+2|aH8z#j-4HdMTq{$7NK9u?o5{@ui{>i5zEf7|SM-8lT*>mvS$ zJ#qWWH}CM&-;waZ;9~rsFYl%A-0_FMz;9OgXA6Eep7k6_F7?a&V+FrHo^t#jIn~ze zeJIOl&fn_cvn}?Q@y+Rx^NsOd^96swFZoM3+?b>a%j zU!O0tAm2=Cb3Cn}W~8-r!B&b@WW6dFy2C2|cA|CejaY&2PxwiM|9I!>z~Wm@x$v_G zRauq9pQ!NPGGRsF`Gs!&9j|1i*WFZYY$pCEujGqoT)g>c+PYunS<8DYAUjgeON4)h z^Z+mGA3kQBl@jIF(}n!~qGpo;e`Wq{!$(=i24%bW<@hWkJyvpQsCCrQw2Tp>tP?XT zO;yiQiqE^GAMqi68u$62ze~ybKy1(E^~-wt{VLDee`b%1Uydi~5&Y1zv0GW7^GvrM zdA?LVGaB^#H`Q)3Fkdo$J@J@D8@_Q>=5bB_ z%lQC)#v>ne>m{!D$n>}+Bdvemc#3@U`A!mlD)CQ>%d~F&cmvH}WIq3v^!%5wj1Q*v zG0MVv77ZKViVyVPC-Ji$=%1$HSL4b2$bVftdy)RnG7N5Az*3b2a^|$_Rx2e;UH>Xn62WkHj{Bbv8({*|n&-Ir0 z9lS1|A0d9Z-Y%kitDyX;yLM0dMoqo_p~&`E(l7W++ibFQ{OWpP{kscv&&qV?e?H;wqWqWe!rz++KlwlU^(E)$PU6oE z>(S+>x*k~%^y}6m=1Z=JjIX8m>*^uTubL0ftz8w+<%3$^I3KXT=;n*@DnIZ#e#8^| zyM#9}56I79n)S%~)%=8hogT0CQk1(%yMA@`0eXJ_PyKpC|HJt+yQr_wK6G}15tU*X zPcJ@9c4WOpzRCRz*Aw{rji#Q+`<=``%HPe{^7Sznzr0^(h4oA!Jr5Ipr|A!_7@6`IFn@1{xW&&8kZh*KVrPsesp`^YXR_M4*(XI$T_O# zI)2QTP7mt84zI3v=J#6fYCU1R*M5ifJOAu*XFp{;*6-+h63C~>d_X=I6Q1h{<6|z} zovy>H@xk7O_<&#MuUb!-->aS=J`EIq*2DarNRN(Rt%uC-RS$W-r2SFM^SDtRUX3d6 z7*OM*#-H^-zgIn%^>e*KJlC$vbMdpkQvDHnfN!X9>yhUd+?z*dJOH1|`fdp1-`&E+ z&;D|M#d_dxsNAh*66H_al2Q|R!kWbY7w}Id{__c0^}6^V{}E5%fyMROyxc(9&`y?$ zSM!bUsUEAOYw2=Iz2pJ$WM+X{GIc= zXzTs%?)Wnv`el6QIkU8Qj*p7Jyl|G)Xt?8vepR>LPgB3DTffY&>Q7l1?a43q!|)Y; zG}jNy{XFAQuXOzb&lksE#_ROU`wOt#U*SBcJ6|LIa(~G9=x>Sxy8ckDCv&KOdzSj8 zt_j5fU4JF-U$UO6jU}FV-miftegpi~gxB?Zm@m1%IzafbgzrH5b^R6m)$#wD^y~QL z{ax0B^RJGd&)4Dn6l(?dt(-j2)a~z#N58~t9r(1m&tv`RM=XYMiIDhH-J+Je{m^Hgi9iI86dSxI!UgtF#4=vb_fJc0E z@nk&CJ2D>qn-0(CN5+F27x21x%ID=RMSX?#+o|8H{KHsdu%`cH{_uVgq2G(2>zm4d zzH+ZCALR4CjGwY~u92POwKdE08{-WQ8zn`>=4XF7kzdy+d6grU4IcFb@9VH$z>N$1oi1$;RD0mj zAL{V!t53V)FRw?N-R8f3Wv`ZB-XCRrH0imKlO9n0<$9v(0e`!Jb6j}!e1~VSzwI@6 z^h@&l8R~yTUlT|1?5DwFzL1}4{IAn|c}X|EBUoPuB_V`0#$J;s+Bx zMWYAnan0G~ri%ZP^y~QL^$YDBAK(XT;v=6=fyae9)ZBijJC9T+76)|wfZWg6>^6Tn zt;g8s-St7fk_dW#rb?*~^*S$~R^((zU5&U>R(;S}HJL>^nr-$=j?I)M|Z#D5z z>xqm9H|?jQE_$7(>sLKb^`rBY?tH|0upZf0#DDr@x!Utxym;U4 zoYx#Z$hRvi?>AI`<@YDx=kuM{{w4E62QK)1q~qVjN9C8#k2c!IBlfsQ#mn)O@xbcl z%Zq>ZpiXskzl&dvzswKp>ZBqUUXG89x3O-1ulRsJXlwG_2(KaX-7NbtrZ&>vS|i(d zCfTG57x0nedrrmy?~Ly`SsiN5ws{1n2|l*%+-kDzF}3N3o{0JMrKg-XTZRj|kTIBD3p%eZ4lAsCu&1I|nAGEsP zv4_21;xB!?tFS9RIaL2(*3)U}t)?1`m&ZT+?$!FQIsLBsmVgtM@m~9lvWp?n4pd#`PY;9zth}+ur+E%`9=&Xj(%&juz}%%J)Ij7RE=&`5e!+eU}|Q~hyluW{%B!)M#P>kr1SZL{kSKflSws1sl3 zc^pf7v}qjgK2l0AB+u_$L)kim+Se40hI6>cfl7i}l6y9^b~F`Fo+c zGb!c>ZpQt^mkZ4f^Dd#6686ax^UN6;e-i&zkooIBx)=E(#_?V2it)X>cj&njf45C9 zFx!;HgI|oR`SI7-C8qhoEa&;Sr$u(q(Rs@=o|9%ywq!0ECV{g7e|I1^9b;|iKT*Zv=w CX30+g literal 0 HcmV?d00001 diff --git a/resource/fonts/alphalcd.ttf b/resource/fonts/alphalcd.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d800c63c6bc653596ff7a68d16285c1308da1a76 GIT binary patch literal 21456 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 JnZ4-F{{g1Vq=End literal 0 HcmV?d00001