mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
429 lines
12 KiB
Lua
429 lines
12 KiB
Lua
--[[
|
|
| This file was obtained through the combined efforts
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
|
|
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
|
|
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
|
--]]
|
|
|
|
|
|
local PLUGIN = PLUGIN
|
|
|
|
PLUGIN.name = "3D Panels"
|
|
PLUGIN.author = "Chessnut"
|
|
PLUGIN.description = "Adds web panels that can be placed on the map."
|
|
|
|
-- List of available panel dislays.
|
|
PLUGIN.list = PLUGIN.list or {}
|
|
|
|
if (SERVER) then
|
|
util.AddNetworkString("ixPanelList")
|
|
util.AddNetworkString("ixPanelAdd")
|
|
util.AddNetworkString("ixPanelRemove")
|
|
|
|
-- Called when the player is sending client info.
|
|
function PLUGIN:PlayerInitialSpawn(client)
|
|
-- Send the list of panel displays.
|
|
timer.Simple(1, function()
|
|
if (IsValid(client)) then
|
|
local json = util.TableToJSON(self.list)
|
|
local compressed = util.Compress(json)
|
|
local length = compressed:len()
|
|
|
|
net.Start("ixPanelList")
|
|
net.WriteUInt(length, 32)
|
|
net.WriteData(compressed, length)
|
|
net.Send(client)
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- Adds a panel to the list, sends it to the players, and saves data.
|
|
function PLUGIN:AddPanel(position, angles, url, scale, brightness)
|
|
scale = math.Clamp((scale or 1) * 0.1, 0.001, 5)
|
|
brightness = math.Clamp(math.Round((brightness or 100) * 2.55), 1, 255)
|
|
|
|
-- Find an ID for this panel within the list.
|
|
local index = #self.list + 1
|
|
|
|
-- Add the panel to the list so it can be sent and saved.
|
|
self.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}
|
|
|
|
-- Send the panel information to the players.
|
|
net.Start("ixPanelAdd")
|
|
net.WriteUInt(index, 32)
|
|
net.WriteVector(position)
|
|
net.WriteAngle(angles)
|
|
net.WriteFloat(scale)
|
|
net.WriteString(url)
|
|
net.WriteUInt(brightness, 8)
|
|
net.Broadcast()
|
|
|
|
-- Save the plugin data.
|
|
self:SavePanels()
|
|
end
|
|
|
|
-- Removes a panel that are within the radius of a position.
|
|
function PLUGIN:RemovePanel(position, radius)
|
|
-- Default the radius to 100.
|
|
radius = radius or 100
|
|
|
|
local panelsDeleted = {}
|
|
|
|
-- Loop through all of the panels.
|
|
for k, v in pairs(self.list) do
|
|
if (k == 0) then
|
|
continue
|
|
end
|
|
|
|
-- Check if the distance from our specified position to the panel is less than the radius.
|
|
if (v[1]:Distance(position) <= radius) then
|
|
panelsDeleted[#panelsDeleted + 1] = k
|
|
end
|
|
end
|
|
|
|
-- Save the plugin data if we actually changed anything.
|
|
if (#panelsDeleted > 0) then
|
|
-- Invert index table to delete from highest -> lowest
|
|
panelsDeleted = table.Reverse(panelsDeleted)
|
|
|
|
for _, v in ipairs(panelsDeleted) do
|
|
-- Remove the panel from the list of panels.
|
|
table.remove(self.list, v)
|
|
|
|
-- Tell the players to stop showing the panel.
|
|
net.Start("ixPanelRemove")
|
|
net.WriteUInt(v, 32)
|
|
net.Broadcast()
|
|
end
|
|
|
|
self:SavePanels()
|
|
end
|
|
|
|
-- Return the number of deleted panels.
|
|
return #panelsDeleted
|
|
end
|
|
|
|
-- Called after entities have been loaded on the map.
|
|
function PLUGIN:LoadData()
|
|
self.list = self:GetData() or {}
|
|
|
|
-- Formats table to sequential to support legacy panels.
|
|
self.list = table.ClearKeys(self.list)
|
|
end
|
|
|
|
-- Called when the plugin needs to save information.
|
|
function PLUGIN:SavePanels()
|
|
self:SetData(self.list)
|
|
end
|
|
else
|
|
local function IsUsingPanelAddTool()
|
|
if !IsValid(LocalPlayer()) then return false end
|
|
if !IsValid(LocalPlayer():GetActiveWeapon()) then return false end
|
|
if !LocalPlayer():GetActiveWeapon():GetClass() then return false end
|
|
if !LocalPlayer():GetTool() then return false end
|
|
if !LocalPlayer():GetTool().Mode then return false end
|
|
|
|
if (LocalPlayer():GetActiveWeapon():GetClass() == "gmod_tool" and LocalPlayer():GetTool().Mode == "sh_paneladd") then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Pre-define the zero index in client before the net receives
|
|
PLUGIN.list[0] = PLUGIN.list[0] or 0
|
|
|
|
-- Holds the current cached material and filename.
|
|
local cachedPreview = {}
|
|
|
|
local function CacheMaterial(index)
|
|
if (index < 1) then
|
|
return
|
|
end
|
|
|
|
local info = PLUGIN.list[index]
|
|
local url = !IsUsingPanelAddTool() and info[6] or GetConVar("sh_paneladd_url"):GetString() or ""
|
|
if url == "" then return end
|
|
|
|
local exploded = string.Explode("/", url)
|
|
local filename = exploded[#exploded]
|
|
local path = "helix/"..Schema.folder.."/"..PLUGIN.uniqueID.."/"
|
|
|
|
if (file.Exists(path..filename, "DATA")) then
|
|
local material = Material("../data/"..path..filename, "noclamp smooth")
|
|
|
|
if (!material:IsError()) then
|
|
info[7] = material
|
|
|
|
-- Set width and height
|
|
info[3] = material:GetInt("$realwidth")
|
|
info[4] = material:GetInt("$realheight")
|
|
end
|
|
else
|
|
file.CreateDir(path)
|
|
|
|
http.Fetch(url, function(body)
|
|
file.Write(path..filename, body)
|
|
|
|
local material = Material("../data/"..path..filename, "noclamp smooth")
|
|
|
|
if (!material:IsError()) then
|
|
info[7] = material
|
|
|
|
-- Set width and height
|
|
info[3] = material:GetInt("$realwidth")
|
|
info[4] = material:GetInt("$realheight")
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
local function UpdateCachedPreview(url)
|
|
local path = "helix/"..Schema.folder.."/"..PLUGIN.uniqueID.."/"
|
|
|
|
-- Gets the file name
|
|
local exploded = string.Explode("/", url)
|
|
local filename = exploded[#exploded]
|
|
|
|
if (file.Exists(path..filename, "DATA")) then
|
|
local preview = Material("../data/"..path..filename, "noclamp smooth")
|
|
|
|
-- Update the cached preview if success
|
|
if (!preview:IsError()) then
|
|
cachedPreview = {url, preview}
|
|
else
|
|
cachedPreview = {}
|
|
end
|
|
else
|
|
file.CreateDir(path)
|
|
|
|
http.Fetch(url, function(body)
|
|
file.Write(path..filename, body)
|
|
|
|
local preview = Material("../data/"..path..filename, "noclamp smooth")
|
|
|
|
-- Update the cached preview if success
|
|
if (!preview:IsError()) then
|
|
cachedPreview = {url, preview}
|
|
else
|
|
cachedPreview = {}
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
|
|
-- Receives new panel objects that need to be drawn.
|
|
net.Receive("ixPanelAdd", function()
|
|
local index = net.ReadUInt(32)
|
|
local position = net.ReadVector()
|
|
local angles = net.ReadAngle()
|
|
local scale = net.ReadFloat()
|
|
local url = net.ReadString()
|
|
local brightness = net.ReadUInt(8)
|
|
|
|
if (url != "") then
|
|
PLUGIN.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}
|
|
|
|
CacheMaterial(index)
|
|
|
|
PLUGIN.list[0] = #PLUGIN.list
|
|
end
|
|
end)
|
|
|
|
net.Receive("ixPanelRemove", function()
|
|
local index = net.ReadUInt(32)
|
|
|
|
table.remove(PLUGIN.list, index)
|
|
|
|
PLUGIN.list[0] = #PLUGIN.list
|
|
end)
|
|
|
|
-- Receives a full update on ALL panels.
|
|
net.Receive("ixPanelList", function()
|
|
local length = net.ReadUInt(32)
|
|
local data = net.ReadData(length)
|
|
local uncompressed = util.Decompress(data)
|
|
|
|
if (!uncompressed) then
|
|
ErrorNoHalt("[Helix] Unable to decompress panel data!\n")
|
|
return
|
|
end
|
|
|
|
-- Set the list of panels to the ones provided by the server.
|
|
PLUGIN.list = util.JSONToTable(uncompressed)
|
|
|
|
-- Will be saved, but refresh just to make sure.
|
|
PLUGIN.list[0] = #PLUGIN.list
|
|
|
|
local CacheQueue = {}
|
|
|
|
-- Loop through the list of panels.
|
|
for k, _ in pairs(PLUGIN.list) do
|
|
if (k == 0) then
|
|
continue
|
|
end
|
|
|
|
CacheQueue[#CacheQueue + 1] = k
|
|
end
|
|
|
|
if (#CacheQueue == 0) then
|
|
return
|
|
end
|
|
|
|
timer.Create("ixCache3DPanels", 1, #CacheQueue, function()
|
|
if (#CacheQueue > 0) then
|
|
CacheMaterial(CacheQueue[1])
|
|
|
|
table.remove(CacheQueue, 1)
|
|
else
|
|
timer.Remove("ixCache3DPanels")
|
|
end
|
|
end)
|
|
end)
|
|
|
|
-- Called after all translucent objects are drawn.
|
|
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
|
|
if (bDrawingDepth or bDrawingSkybox) then
|
|
return
|
|
end
|
|
|
|
local toolUsage = IsUsingPanelAddTool()
|
|
|
|
if toolUsage then UpdateCachedPreview(GetConVar("sh_paneladd_url"):GetString() or "") end
|
|
-- Panel preview
|
|
if (ix.chat.currentCommand == "paneladd" or toolUsage) then
|
|
self:PreviewPanel()
|
|
end
|
|
|
|
-- Store the position of the player to be more optimized.
|
|
local ourPosition = LocalPlayer():GetPos()
|
|
|
|
local panel = self.list
|
|
|
|
for i = 1, panel[0] do
|
|
local position = panel[i][1]
|
|
local image = panel[i][7]
|
|
|
|
-- Older panels do not have a brightness index
|
|
local brightness = panel[i][8] or 255
|
|
|
|
if (panel[i][7] and ourPosition:DistToSqr(position) <= 2796203) then
|
|
local width, height = panel[i][3] or image:Width(), panel[i][4] or image:Height()
|
|
cam.Start3D2D(position, panel[i][2], panel[i][5] or 0.1)
|
|
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
|
|
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
|
|
surface.SetDrawColor(brightness, brightness, brightness)
|
|
surface.SetMaterial(image)
|
|
surface.DrawTexturedRect(-width * 0.5, -height * 0.5, width, height)
|
|
render.PopFilterMag()
|
|
render.PopFilterMin()
|
|
cam.End3D2D()
|
|
end
|
|
end
|
|
end
|
|
|
|
function PLUGIN:ChatTextChanged(text)
|
|
if (ix.chat.currentCommand == "paneladd") then
|
|
-- Allow time for ix.chat.currentArguments to update
|
|
timer.Simple(0, function()
|
|
local arguments = ix.chat.currentArguments
|
|
|
|
if (!arguments[1]) then
|
|
return
|
|
end
|
|
|
|
UpdateCachedPreview(arguments[1])
|
|
end)
|
|
end
|
|
end
|
|
|
|
function PLUGIN:PreviewPanel()
|
|
local arguments = ix.chat.currentArguments
|
|
local arguments2 = GetConVar("sh_paneladd_url"):GetString()
|
|
-- if there's no URL, then no preview.
|
|
if IsUsingPanelAddTool() then
|
|
arguments = {}
|
|
end
|
|
|
|
if (!arguments[1] and !arguments2 or IsUsingPanelAddTool() and arguments2 and arguments2 == "") then
|
|
return
|
|
end
|
|
|
|
-- If the material is valid, preview the panel
|
|
if (cachedPreview[2] and !cachedPreview[2]:IsError()) then
|
|
local trace = LocalPlayer():GetEyeTrace()
|
|
local angles = trace.HitNormal:Angle()
|
|
angles:RotateAroundAxis(angles:Up(), 90)
|
|
angles:RotateAroundAxis(angles:Forward(), 90)
|
|
|
|
-- validate argument types
|
|
local cBrightness = IsUsingPanelAddTool() and GetConVar("sh_paneladd_brightness"):GetInt() or 100
|
|
local cScale = IsUsingPanelAddTool() and GetConVar("sh_paneladd_scale"):GetInt() or 1
|
|
|
|
local scale = math.Clamp((tonumber(arguments[2]) or cScale) * 0.1, 0.001, 5)
|
|
local brightness = math.Clamp(math.Round((tonumber(arguments[3]) or cBrightness) * 2.55), 1, 255)
|
|
|
|
-- Attempt to collect the dimensions from the Material
|
|
local width, height = cachedPreview[2]:GetInt("$realwidth"), cachedPreview[2]:GetInt("$realheight")
|
|
|
|
local position = (trace.HitPos + angles:Up() * 1)
|
|
local ourPosition = LocalPlayer():GetPos()
|
|
|
|
if (ourPosition:DistToSqr(position) <= 4194304) then
|
|
cam.Start3D2D(position, angles, scale or 0.1)
|
|
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
|
|
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
|
|
surface.SetDrawColor(brightness, brightness, brightness)
|
|
surface.SetMaterial(cachedPreview[2])
|
|
surface.DrawTexturedRect(-width * 0.5, -height * 0.5, width or cachedPreview[2]:Width(),
|
|
height or cachedPreview[2]:Height())
|
|
render.PopFilterMag()
|
|
render.PopFilterMin()
|
|
cam.End3D2D()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
ix.command.Add("PanelAdd", {
|
|
description = "@cmdPanelAdd",
|
|
privilege = "Manage Panels",
|
|
adminOnly = true,
|
|
arguments = {
|
|
ix.type.string,
|
|
bit.bor(ix.type.number, ix.type.optional),
|
|
bit.bor(ix.type.number, ix.type.optional)
|
|
},
|
|
OnRun = function(self, client, url, scale, brightness)
|
|
-- Get the position and angles of the panel.
|
|
local trace = client:GetEyeTrace()
|
|
local position = trace.HitPos
|
|
local angles = trace.HitNormal:Angle()
|
|
angles:RotateAroundAxis(angles:Up(), 90)
|
|
angles:RotateAroundAxis(angles:Forward(), 90)
|
|
|
|
-- Add the panel.
|
|
PLUGIN:AddPanel(position + angles:Up() * 1, angles, url, scale, brightness)
|
|
return "@panelAdded"
|
|
end
|
|
})
|
|
|
|
ix.command.Add("PanelRemove", {
|
|
description = "@cmdPanelRemove",
|
|
privilege = "Manage Panels",
|
|
adminOnly = true,
|
|
arguments = bit.bor(ix.type.number, ix.type.optional),
|
|
OnRun = function(self, client, radius)
|
|
-- Get the origin to remove panel.
|
|
local trace = client:GetEyeTrace()
|
|
local position = trace.HitPos
|
|
-- Remove the panel(s) and get the amount removed.
|
|
local amount = PLUGIN:RemovePanel(position, radius)
|
|
|
|
return "@panelRemoved", amount
|
|
end
|
|
})
|