mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
Upload
This commit is contained in:
433
gamemodes/helix/plugins/3dpanel.lua
Normal file
433
gamemodes/helix/plugins/3dpanel.lua
Normal file
@@ -0,0 +1,433 @@
|
||||
--[[
|
||||
| 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 {}
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Panels",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
425
gamemodes/helix/plugins/3dtext.lua
Normal file
425
gamemodes/helix/plugins/3dtext.lua
Normal file
@@ -0,0 +1,425 @@
|
||||
--[[
|
||||
| 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 Text"
|
||||
PLUGIN.author = "Chessnut & Votton"
|
||||
PLUGIN.description = "Adds text that can be placed on the map."
|
||||
|
||||
-- List of available text panels
|
||||
PLUGIN.list = PLUGIN.list or {}
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixTextList")
|
||||
util.AddNetworkString("ixTextAdd")
|
||||
util.AddNetworkString("ixTextRemove")
|
||||
|
||||
ix.log.AddType("undo3dText", function(client)
|
||||
return string.format("%s has removed their last 3D text.", client:GetName())
|
||||
end)
|
||||
|
||||
-- Called when the player is sending client info.
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
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("ixTextList")
|
||||
net.WriteUInt(length, 32)
|
||||
net.WriteData(compressed, length)
|
||||
net.Send(client)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Adds a text to the list, sends it to the players, and saves data.
|
||||
function PLUGIN:AddText(position, angles, text, scale)
|
||||
local index = #self.list + 1
|
||||
scale = math.Clamp((scale or 1) * 0.1, 0.001, 5)
|
||||
|
||||
self.list[index] = {position, angles, text, scale}
|
||||
|
||||
net.Start("ixTextAdd")
|
||||
net.WriteUInt(index, 32)
|
||||
net.WriteVector(position)
|
||||
net.WriteAngle(angles)
|
||||
net.WriteString(text)
|
||||
net.WriteFloat(scale)
|
||||
net.Broadcast()
|
||||
|
||||
self:SaveText()
|
||||
return index
|
||||
end
|
||||
|
||||
-- Removes a text that are within the radius of a position.
|
||||
function PLUGIN:RemoveText(position, radius)
|
||||
radius = radius or 100
|
||||
|
||||
local textDeleted = {}
|
||||
|
||||
for k, v in pairs(self.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
if (v[1]:Distance(position) <= radius) then
|
||||
textDeleted[#textDeleted + 1] = k
|
||||
end
|
||||
end
|
||||
|
||||
if (#textDeleted > 0) then
|
||||
-- Invert index table to delete from highest -> lowest
|
||||
textDeleted = table.Reverse(textDeleted)
|
||||
|
||||
for _, v in ipairs(textDeleted) do
|
||||
table.remove(self.list, v)
|
||||
|
||||
net.Start("ixTextRemove")
|
||||
net.WriteUInt(v, 32)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
self:SaveText()
|
||||
end
|
||||
|
||||
return #textDeleted
|
||||
end
|
||||
|
||||
function PLUGIN:RemoveTextByID(id)
|
||||
local info = self.list[id]
|
||||
|
||||
if (!info) then
|
||||
return false
|
||||
end
|
||||
|
||||
net.Start("ixTextRemove")
|
||||
net.WriteUInt(id, 32)
|
||||
net.Broadcast()
|
||||
|
||||
table.remove(self.list, id)
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:EditText(position, text, scale, radius)
|
||||
radius = radius or 100
|
||||
|
||||
local textEdited = false
|
||||
|
||||
for k, v in pairs(self.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
if (v[1]:Distance(position) <= radius) then
|
||||
v[3] = text
|
||||
v[4] = scale
|
||||
textEdited = true
|
||||
|
||||
net.Start("ixTextAdd")
|
||||
net.WriteUInt(k, 32)
|
||||
net.WriteVector(v[1])
|
||||
net.WriteAngle(v[2])
|
||||
net.WriteString(text)
|
||||
net.WriteFloat(scale)
|
||||
net.Broadcast()
|
||||
|
||||
self:SaveText()
|
||||
end
|
||||
end
|
||||
|
||||
return textEdited
|
||||
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:SaveText()
|
||||
self:SetData(self.list)
|
||||
end
|
||||
else
|
||||
local function IsUsingTextAddTool()
|
||||
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_textadd") 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
|
||||
|
||||
language.Add("Undone_ix3dText", "Removed 3D Text")
|
||||
|
||||
function PLUGIN:GenerateMarkup(text)
|
||||
local object = ix.markup.Parse("<font=ix3D2DFont>"..text:gsub("\\n", "\n"))
|
||||
|
||||
object.onDrawText = function(surfaceText, font, x, y, color, alignX, alignY, alpha)
|
||||
local _, textH = surface.GetTextSize( surfaceText )
|
||||
|
||||
-- shadow
|
||||
surface.SetTextPos(x + 1, y + 1 - textH * 0.5)
|
||||
surface.SetTextColor(0, 0, 0, alpha)
|
||||
surface.SetFont(font)
|
||||
surface.DrawText(surfaceText)
|
||||
|
||||
surface.SetTextPos(x, y - (textH * 0.5))
|
||||
surface.SetTextColor(color.r or 255, color.g or 255, color.b or 255, alpha)
|
||||
surface.SetFont(font)
|
||||
surface.DrawText(surfaceText)
|
||||
end
|
||||
|
||||
return object
|
||||
end
|
||||
|
||||
-- Receives new text objects that need to be drawn.
|
||||
net.Receive("ixTextAdd", function()
|
||||
local index = net.ReadUInt(32)
|
||||
local position = net.ReadVector()
|
||||
local angles = net.ReadAngle()
|
||||
local text = net.ReadString()
|
||||
local scale = net.ReadFloat()
|
||||
|
||||
if (text != "") then
|
||||
PLUGIN.list[index] = {
|
||||
position,
|
||||
angles,
|
||||
PLUGIN:GenerateMarkup(text),
|
||||
scale
|
||||
}
|
||||
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixTextRemove", function()
|
||||
local index = net.ReadUInt(32)
|
||||
|
||||
table.remove(PLUGIN.list, index)
|
||||
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
end)
|
||||
|
||||
-- Receives a full update on ALL texts.
|
||||
net.Receive("ixTextList", function()
|
||||
local length = net.ReadUInt(32)
|
||||
local data = net.ReadData(length)
|
||||
local uncompressed = util.Decompress(data)
|
||||
|
||||
if (!uncompressed) then
|
||||
ErrorNoHalt("[Helix] Unable to decompress text data!\n")
|
||||
return
|
||||
end
|
||||
|
||||
PLUGIN.list = util.JSONToTable(uncompressed)
|
||||
|
||||
-- Will be saved, but refresh just to make sure.
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
|
||||
for k, v in pairs(PLUGIN.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
local object = ix.markup.Parse("<font=ix3D2DFont>"..v[3]:gsub("\\n", "\n"))
|
||||
|
||||
object.onDrawText = function(text, font, x, y, color, alignX, alignY, alpha)
|
||||
draw.TextShadow({
|
||||
pos = {x, y},
|
||||
color = ColorAlpha(color, alpha),
|
||||
text = text,
|
||||
xalign = 0,
|
||||
yalign = alignY,
|
||||
font = font
|
||||
}, 1, alpha)
|
||||
end
|
||||
|
||||
v[3] = object
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:StartChat()
|
||||
self.preview = nil
|
||||
end
|
||||
|
||||
function PLUGIN:FinishChat()
|
||||
self.preview = nil
|
||||
end
|
||||
|
||||
function PLUGIN:HUDPaint()
|
||||
if (ix.chat.currentCommand != "textremove") then
|
||||
return
|
||||
end
|
||||
|
||||
local radius = tonumber(ix.chat.currentArguments[1]) or 100
|
||||
|
||||
surface.SetDrawColor(200, 30, 30)
|
||||
surface.SetTextColor(200, 30, 30)
|
||||
surface.SetFont("ixMenuButtonFont")
|
||||
|
||||
local i = 0
|
||||
|
||||
for k, v in pairs(self.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
if (v[1]:Distance(LocalPlayer():GetEyeTraceNoCursor().HitPos) <= radius) then
|
||||
local screen = v[1]:ToScreen()
|
||||
surface.DrawLine(
|
||||
ScrW() * 0.5,
|
||||
ScrH() * 0.5,
|
||||
math.Clamp(screen.x, 0, ScrW()),
|
||||
math.Clamp(screen.y, 0, ScrH())
|
||||
)
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if (i > 0) then
|
||||
local textWidth, textHeight = surface.GetTextSize(i)
|
||||
surface.SetTextPos(ScrW() * 0.5 - textWidth * 0.5, ScrH() * 0.5 + textHeight + 8)
|
||||
surface.DrawText(i)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
|
||||
if (bDrawingDepth or bDrawingSkybox) then
|
||||
return
|
||||
end
|
||||
|
||||
local toolUsage = IsUsingTextAddTool()
|
||||
|
||||
-- preview for textadd command
|
||||
if (ix.chat.currentCommand == "textadd" or toolUsage) then
|
||||
local arguments = ix.chat.currentArguments
|
||||
local text = toolUsage and GetConVar("sh_textadd_text"):GetString() or tostring(arguments[1] or "")
|
||||
local scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)
|
||||
if toolUsage then
|
||||
scale = math.Clamp((tonumber(GetConVar("sh_textadd_scale"):GetInt()) or 1) * 0.1, 0.001, 5)
|
||||
end
|
||||
|
||||
local trace = LocalPlayer():GetEyeTraceNoCursor()
|
||||
local position = trace.HitPos
|
||||
local angles = trace.HitNormal:Angle()
|
||||
local markup
|
||||
|
||||
angles:RotateAroundAxis(angles:Up(), 90)
|
||||
angles:RotateAroundAxis(angles:Forward(), 90)
|
||||
|
||||
-- markup will error with invalid fonts
|
||||
pcall(function()
|
||||
markup = PLUGIN:GenerateMarkup(text)
|
||||
end)
|
||||
|
||||
if (markup) then
|
||||
cam.Start3D2D(position, angles, scale)
|
||||
markup:draw(0, 0, 1, 1, 255)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
|
||||
local position = LocalPlayer():GetPos()
|
||||
local texts = self.list
|
||||
|
||||
for i = 1, texts[0] do
|
||||
local distance = texts[i][1]:DistToSqr(position)
|
||||
|
||||
if (distance > 1048576) then
|
||||
continue
|
||||
end
|
||||
|
||||
cam.Start3D2D(texts[i][1], texts[i][2], texts[i][4] or 0.1)
|
||||
local alpha = (1 - ((distance - 65536) / 768432)) * 255
|
||||
texts[i][3]:draw(0, 0, 1, 1, alpha)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("TextAdd", {
|
||||
description = "@cmdTextAdd",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, text, scale)
|
||||
local trace = client:GetEyeTrace()
|
||||
local position = trace.HitPos
|
||||
local angles = trace.HitNormal:Angle()
|
||||
angles:RotateAroundAxis(angles:Up(), 90)
|
||||
angles:RotateAroundAxis(angles:Forward(), 90)
|
||||
|
||||
local index = PLUGIN:AddText(position + angles:Up() * 0.1, angles, text, scale)
|
||||
|
||||
undo.Create("ix3dText")
|
||||
undo.SetPlayer(client)
|
||||
undo.AddFunction(function()
|
||||
if (PLUGIN:RemoveTextByID(index)) then
|
||||
ix.log.Add(client, "undo3dText")
|
||||
end
|
||||
end)
|
||||
undo.Finish()
|
||||
|
||||
return "@textAdded"
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("TextRemove", {
|
||||
description = "@cmdTextRemove",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.number, ix.type.optional),
|
||||
OnRun = function(self, client, radius)
|
||||
local trace = client:GetEyeTrace()
|
||||
local position = trace.HitPos + trace.HitNormal * 2
|
||||
local amount = PLUGIN:RemoveText(position, radius)
|
||||
|
||||
return "@textRemoved", amount
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("TextEdit", {
|
||||
description = "@cmdTextEdit",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
bit.bor(ix.type.string, ix.type.optional),
|
||||
bit.bor(ix.type.number, ix.type.optional),
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, text, scale, radius)
|
||||
local trace = client:GetEyeTrace()
|
||||
local position = trace.HitPos + trace.HitNormal * 2
|
||||
|
||||
local edited = PLUGIN:EditText(position, text, scale, radius)
|
||||
|
||||
if (edited) then
|
||||
return "@textEdited"
|
||||
else
|
||||
return "@textNotFound"
|
||||
end
|
||||
end
|
||||
})
|
||||
123
gamemodes/helix/plugins/act/cl_hooks.lua
Normal file
123
gamemodes/helix/plugins/act/cl_hooks.lua
Normal file
@@ -0,0 +1,123 @@
|
||||
--[[
|
||||
| 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.cameraWheelOffset = 1
|
||||
PLUGIN.cameraWheelDelta = PLUGIN.cameraWheelOffset
|
||||
|
||||
PLUGIN.cameraInOutOffset = 0
|
||||
PLUGIN.cameraInOutDelta = PLUGIN.cameraInOutOffset
|
||||
|
||||
local function GetHeadBone(client)
|
||||
local head
|
||||
|
||||
for i = 1, client:GetBoneCount() do
|
||||
if (string.find(client:GetBoneName(i):lower(), "head")) then
|
||||
head = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return head
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerBindPress(client, bind, bPressed)
|
||||
if (!client:GetNetVar("actEnterAngle")) then
|
||||
return
|
||||
end
|
||||
|
||||
if (bind:find("+jump") and bPressed) then
|
||||
ix.command.Send("ExitAct")
|
||||
|
||||
PLUGIN.cameraInOutOffset = ix.option.Get("thirdpersonEnabled") and 1 or 0
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InputMouseApply(cmd)
|
||||
if !LocalPlayer():GetNetVar("actEnterAngle") then
|
||||
return
|
||||
end
|
||||
|
||||
local mouseWheel = cmd:GetMouseWheel()
|
||||
|
||||
if mouseWheel != 0 then
|
||||
self.cameraWheelOffset = math.Clamp(self.cameraWheelOffset + (-mouseWheel * 0.5), 1.0, 1.5)
|
||||
end
|
||||
end
|
||||
|
||||
local traceMin = Vector(-4, -4, -4)
|
||||
local traceMax = Vector(4, 4, 4)
|
||||
|
||||
function PLUGIN:CalcView(client, origin)
|
||||
if !client:GetNetVar("actEnterAngle") then
|
||||
return
|
||||
end
|
||||
|
||||
local frameTime = FrameTime()
|
||||
|
||||
self.cameraWheelDelta = math.Approach(self.cameraWheelDelta, self.cameraWheelOffset, frameTime * 6)
|
||||
self.cameraInOutDelta = math.Approach(self.cameraInOutDelta, self.cameraInOutOffset, frameTime * 4)
|
||||
|
||||
local view = {}
|
||||
view.drawviewer = self.cameraInOutDelta >= 0.225 * (self.cameraWheelOffset * 0.1)
|
||||
|
||||
local head = GetHeadBone(client)
|
||||
local headOffset = Vector(0, 0, 64)
|
||||
|
||||
if head then
|
||||
headOffset = client:GetBonePosition(head)
|
||||
end
|
||||
|
||||
local currentAngle = client.camAng or angle_zero
|
||||
|
||||
local tracePosition = {}
|
||||
tracePosition.start = headOffset + client:GetNetVar("actEnterAngle"):Forward() * 10
|
||||
tracePosition.endpos = tracePosition.start - currentAngle:Forward() * (70 * self.cameraInOutDelta) * self.cameraWheelDelta
|
||||
tracePosition.filter = client
|
||||
tracePosition.ignoreworld = false
|
||||
tracePosition.mins = traceMin
|
||||
tracePosition.maxs = traceMax
|
||||
|
||||
view.origin = util.TraceHull(tracePosition).HitPos
|
||||
view.angles = currentAngle + client:GetViewPunchAngles()
|
||||
|
||||
local aimOrigin = view.origin
|
||||
|
||||
local traceAngle = {}
|
||||
traceAngle.start = aimOrigin
|
||||
traceAngle.endpos = aimOrigin + currentAngle:Forward() * 65535
|
||||
traceAngle.filter = client
|
||||
traceAngle.ignoreworld = (client:GetMoveType() == MOVETYPE_NOCLIP)
|
||||
|
||||
client:SetEyeAngles((util.TraceLine(traceAngle).HitPos - client:GetShootPos()):Angle())
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
net.Receive("ixActEnter", function()
|
||||
if ix.option.Get("thirdpersonEnabled") then
|
||||
PLUGIN.cameraInOutOffset = 1
|
||||
PLUGIN.cameraInOutDelta = PLUGIN.cameraInOutOffset
|
||||
end
|
||||
|
||||
PLUGIN.cameraInOutOffset = 1
|
||||
end)
|
||||
|
||||
net.Receive("ixActLeave", function()
|
||||
PLUGIN.cameraWheelOffset = 1
|
||||
PLUGIN.cameraWheelDelta = PLUGIN.cameraWheelOffset
|
||||
|
||||
PLUGIN.cameraInOutOffset = 0
|
||||
PLUGIN.cameraInOutDelta = PLUGIN.cameraInOutOffset
|
||||
end)
|
||||
377
gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
377
gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
@@ -0,0 +1,377 @@
|
||||
--[[
|
||||
| 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 function FacingWall(client)
|
||||
local data = {}
|
||||
data.start = client:EyePos()
|
||||
data.endpos = data.start + client:GetForward() * 20
|
||||
data.filter = client
|
||||
|
||||
if (!util.TraceLine(data).Hit) then
|
||||
return "@faceWall"
|
||||
end
|
||||
end
|
||||
|
||||
local function FacingWallBack(client)
|
||||
local data = {}
|
||||
data.start = client:LocalToWorld(client:OBBCenter())
|
||||
data.endpos = data.start - client:GetForward() * 20
|
||||
data.filter = client
|
||||
|
||||
if (!util.TraceLine(data).Hit) then
|
||||
return "@faceWallBack"
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:SetupActs()
|
||||
-- sit
|
||||
ix.act.Register("Sit", "metrocop", {
|
||||
sequence = {"n7_male_sit_ground", "n7_male_sit01", "n7_male_sit02", "n7_male_sit03", "n7_male_sit04", "n7_male_sit05", "n7_male_sit06", "n7_male_sit07"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Sit", "citizen_male", {
|
||||
sequence = {"willard_male_male_sit_ground", "willard_male_male_sit01", "willard_male_male_sit02", "willard_male_male_sit03", "willard_male_male_sit04", "willard_male_male_sit05", "willard_male_male_sit06", "willard_male_male_sit07"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Sit", "citizen_female", {
|
||||
sequence = {"willard_female_sit_ground", "willard_female_sit01", "willard_female_sit02", "willard_female_sit03", "willard_female_sit04", "willard_female_sit05", "willard_female_sit06", "willard_female_sit07"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Sit", "metrocop_female", {
|
||||
sequence = {"n7_female_sit_ground", "n7_female_sit01", "n7_female_sit02", "n7_female_sit03", "n7_female_sit04", "n7_female_sit05", "n7_female_sit06", "n7_female_sit07"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- sitwall
|
||||
ix.act.Register("SitWall", "metrocop", {
|
||||
sequence = {
|
||||
{"n7_male_sitwall", check = FacingWallBack},
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("SitWall", "citizen_male", {
|
||||
sequence = {
|
||||
{"willard_male_male_sitwall", check = FacingWallBack},
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("SitWall", "metrocop_female", {
|
||||
sequence = {
|
||||
{"n7_female_sitwall", check = FacingWallBack},
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("SitWall", "citizen_female", {
|
||||
sequence = {
|
||||
{"willard_female_sitwall", check = FacingWallBack},
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
--sitlean
|
||||
ix.act.Register("SitLean", {"citizen_male", "citizen_female"}, {
|
||||
sequence = {"sitccouchtv1", "sitchair1", "sitchairtable1", "sitcouch1", "sitcouchknees1"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("SitChair", "citizen_male", {
|
||||
start = {"idle_to_sit_chair"},
|
||||
sequence = "sit_chair",
|
||||
finish = {"sit_chair_to_idle", duration = 2.1},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- Idle
|
||||
ix.act.Register("Idle", "vortigaunt", {
|
||||
sequence = "idle_nectar",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- Kneel
|
||||
ix.act.Register("Kneel", "vortigaunt", {
|
||||
sequence = "rescue_idle",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- Sit
|
||||
ix.act.Register("Sit", "vortigaunt", {
|
||||
sequence = {"chess_wait", {"sit_rollercoaster", offset = function(client)
|
||||
return client:GetUp() * 25 + client:GetForward() * -30
|
||||
end}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- stand
|
||||
ix.act.Register("Stand","metrocop", {
|
||||
sequence = {"n7_male_stand01", "n7_male_stand02", "n7_male_stand03", "n7_male_stand04", "n7_male_stand05"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Stand", "citizen_male", {
|
||||
sequence = {"willard_male_male_stand01", "willard_male_male_stand02", "willard_male_male_stand03", "willard_male_male_stand04", "willard_male_male_stand05"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Stand", "metrocop_female", {
|
||||
sequence = {"n7_female_stand01", "n7_female_stand02", "n7_female_stand03", "n7_female_stand04", "n7_female_stand05"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Stand", "citizen_female", {
|
||||
sequence = {"willard_female_stand01", "willard_female_stand02", "willard_female_stand03", "willard_female_stand04", "willard_female_stand05"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- type
|
||||
ix.act.Register("Type", "overwatch", {
|
||||
sequence = "console_type_loop",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- cheer
|
||||
ix.act.Register("Cheer", {"citizen_male", "metrocop"}, {
|
||||
sequence = {{"cheer1", duration = 1.6}, "cheer2", "wave_smg1"}
|
||||
})
|
||||
|
||||
ix.act.Register("Cheer", {"citizen_female", "metrocop_female"}, {
|
||||
sequence = {"cheer1", "wave_smg1"}
|
||||
})
|
||||
|
||||
-- lean
|
||||
ix.act.Register("Lean", {"citizen_male", "citizen_female"}, {
|
||||
start = {"idle_to_lean_back", "", ""},
|
||||
sequence = {
|
||||
{"lean_back", check = FacingWallBack},
|
||||
{"plazaidle1", check = FacingWallBack},
|
||||
{"plazaidle2", check = FacingWallBack}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Lean", {"metrocop"}, {
|
||||
sequence = {
|
||||
{"n7_male_lean01", check = FacingWallBack},
|
||||
{"n7_male_lean02", check = FacingWallBack},
|
||||
{"idle_baton", check = FacingWallBack},
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Lean", "citizen_male", {
|
||||
sequence = {
|
||||
{"willard_male_male_lean01", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 4
|
||||
end},
|
||||
{"willard_male_male_lean02", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 3
|
||||
end}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Lean", "metrocop_female", {
|
||||
sequence = {
|
||||
{"n7_female_lean01", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 6
|
||||
end},
|
||||
{"n7_female_lean02", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 4
|
||||
end}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Lean", "citizen_female", {
|
||||
sequence = {
|
||||
{"willard_female_lean01", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 6
|
||||
end},
|
||||
{"willard_female_lean02", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 4
|
||||
end}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- injured
|
||||
ix.act.Register("Injured", "metrocop", {
|
||||
sequence = {"n7_male_injured"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Injured", "citizen_male", {
|
||||
sequence = {"willard_male_male_injured"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Injured", "metrocop_female", {
|
||||
sequence = "n7_female_injured",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Injured", "citizen_female", {
|
||||
sequence = "willard_female_injured",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- arrest
|
||||
ix.act.Register("Arrest", "metrocop", {
|
||||
sequence = {"n7_male_arrest", "n7_male_arrest_sit01", "n7_male_handsup"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Arrest", "citizen_male", {
|
||||
sequence = {"willard_male_male_arrest", "willard_male_male_arrest_sit01", "willard_male_male_handsup"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Arrest", "metrocop_female", {
|
||||
sequence = {"n7_female_arrest", "n7_female_arrest_sit01", "n7_female_handsup"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Arrest", "citizen_female", {
|
||||
sequence = {"willard_female_arrest", "willard_female_arrest_sit01", "willard_female_handsup"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- threat
|
||||
ix.act.Register("Threat", "metrocop", {
|
||||
sequence = {"plazathreat1", "plazathreat2"}
|
||||
})
|
||||
|
||||
-- search
|
||||
ix.act.Register("Search", "metrocop", {
|
||||
sequence = "spreadwall",
|
||||
})
|
||||
|
||||
-- deny
|
||||
ix.act.Register("Deny", "metrocop", {
|
||||
sequence = {"harassfront2", "harassfront1"}
|
||||
})
|
||||
|
||||
-- motion
|
||||
ix.act.Register("Motion", "metrocop", {
|
||||
sequence = {"motionleft", "motionright", "luggagewarn"}
|
||||
})
|
||||
|
||||
-- wave
|
||||
ix.act.Register("Wave", {"citizen_male", "citizen_female"}, {
|
||||
sequence = {{"wave", duration = 2.75}, {"wave_close", duration = 1.75}}
|
||||
})
|
||||
|
||||
-- pant
|
||||
ix.act.Register("Pant", {"citizen_male", "citizen_female"}, {
|
||||
start = {"d2_coast03_postbattle_idle02_entry", "d2_coast03_postbattle_idle01_entry"},
|
||||
sequence = {"d2_coast03_postbattle_idle02", {"d2_coast03_postbattle_idle01", check = FacingWall}},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- window
|
||||
ix.act.Register("Window", {"citizen_male", "metrocop"}, {
|
||||
sequence = "d1_t03_tenements_look_out_window_idle",
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Window", {"citizen_female", "metrocop_female"}, {
|
||||
sequence = "d1_t03_lookoutwindow",
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- down
|
||||
ix.act.Register("Down", "metrocop", {
|
||||
sequence = {"n7_male_down01", "n7_male_down02", "n7_male_down03"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Down", "citizen_male", {
|
||||
sequence = {"willard_male_male_down01", "willard_male_male_down02", "willard_male_male_down03"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Down", "metrocop_female", {
|
||||
sequence = {"n7_female_down01", "n7_female_down02", "n7_female_down03"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Down", "citizen_female", {
|
||||
sequence = {"willard_female_down01", "willard_female_down02", "willard_female_down03"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
|
||||
-- Check
|
||||
ix.act.Register("Check", "metrocop", {
|
||||
sequence = {"n7_male_check"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Check", "citizen_male", {
|
||||
sequence = {"willard_male_male_check"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Check", "metrocop_female", {
|
||||
sequence = {"n7_female_check"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Check", "citizen_female", {
|
||||
sequence = {"willard_female_check"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
|
||||
-- Knock
|
||||
ix.act.Register("KnockIdle", "metrocop", {
|
||||
sequence = {"adoorcidle"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Knock", "metrocop", {
|
||||
sequence = {"adoorknock"},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Knock", "citizen_male", {
|
||||
sequence = {"d1_town05_Leon_Door_Knock"}
|
||||
})
|
||||
end
|
||||
285
gamemodes/helix/plugins/act/sh_plugin.lua
Normal file
285
gamemodes/helix/plugins/act/sh_plugin.lua
Normal file
@@ -0,0 +1,285 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[--
|
||||
Provides players the ability to perform animations.
|
||||
|
||||
]]
|
||||
-- @module ix.act
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Player Acts"
|
||||
PLUGIN.description = "Adds animations that can be performed by certain models."
|
||||
PLUGIN.author = "`impulse"
|
||||
|
||||
ix.act = ix.act or {}
|
||||
ix.act.stored = ix.act.stored or {}
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Player Acts",
|
||||
MinAccess = "user"
|
||||
})
|
||||
|
||||
--- Registers a sequence as a performable animation.
|
||||
-- @realm shared
|
||||
-- @string name Name of the animation (in CamelCase)
|
||||
-- @string modelClass Model class to add this animation to
|
||||
-- @tab data An `ActInfoStructure` table describing the animation
|
||||
function ix.act.Register(name, modelClass, data)
|
||||
ix.act.stored[name] = ix.act.stored[name] or {} -- might be adding onto an existing act
|
||||
|
||||
if (!data.sequence) then
|
||||
return ErrorNoHalt(string.format(
|
||||
"Act '%s' for '%s' tried to register without a provided sequence\n", name, modelClass
|
||||
))
|
||||
end
|
||||
|
||||
if (!istable(data.sequence)) then
|
||||
data.sequence = {data.sequence}
|
||||
end
|
||||
|
||||
if (data.start and istable(data.start) and #data.start != #data.sequence) then
|
||||
return ErrorNoHalt(string.format(
|
||||
"Act '%s' tried to register without matching number of enter sequences\n", name
|
||||
))
|
||||
end
|
||||
|
||||
if (data.finish and istable(data.finish) and #data.finish != #data.sequence) then
|
||||
return ErrorNoHalt(string.format(
|
||||
"Act '%s' tried to register without matching number of exit sequences\n", name
|
||||
))
|
||||
end
|
||||
|
||||
if (istable(modelClass)) then
|
||||
for _, v in ipairs(modelClass) do
|
||||
ix.act.stored[name][v] = data
|
||||
end
|
||||
else
|
||||
ix.act.stored[name][modelClass] = data
|
||||
end
|
||||
end
|
||||
|
||||
--- Removes a sequence from being performable if it has been previously registered.
|
||||
-- @realm shared
|
||||
-- @string name Name of the animation
|
||||
function ix.act.Remove(name)
|
||||
ix.act.stored[name] = nil
|
||||
ix.command.list["Act" .. name] = nil
|
||||
end
|
||||
|
||||
ix.util.Include("sh_definitions.lua")
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
|
||||
function PLUGIN:InitializedPlugins()
|
||||
hook.Run("SetupActs")
|
||||
hook.Run("PostSetupActs")
|
||||
end
|
||||
|
||||
function PLUGIN:ExitAct(client)
|
||||
client.ixUntimedSequence = nil
|
||||
client:SetNetVar("actEnterAngle")
|
||||
|
||||
hook.Run("OnPlayerExitAct", client)
|
||||
|
||||
net.Start("ixActLeave")
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
function PLUGIN:PostSetupActs()
|
||||
-- create chat commands for all stored acts
|
||||
for act, classes in pairs(ix.act.stored) do
|
||||
local variants = 1
|
||||
local COMMAND = {
|
||||
privilege = "Player Acts"
|
||||
}
|
||||
|
||||
-- check if this act has any variants (i.e /ActSit 2)
|
||||
for _, v in pairs(classes) do
|
||||
if (#v.sequence > 1) then
|
||||
variants = math.max(variants, #v.sequence)
|
||||
end
|
||||
end
|
||||
|
||||
-- setup command arguments if there are variants for this act
|
||||
if (variants > 1) then
|
||||
COMMAND.arguments = bit.bor(ix.type.number, ix.type.optional)
|
||||
COMMAND.argumentNames = {"variant (1-" .. variants .. ")"}
|
||||
end
|
||||
|
||||
COMMAND.alias = "Anim" .. act -- Old habits die hard.
|
||||
|
||||
COMMAND.GetDescription = function(command)
|
||||
return L("cmdAct", act)
|
||||
end
|
||||
|
||||
local privilege = "Helix - " .. COMMAND.privilege
|
||||
|
||||
-- we'll perform a model class check in OnCheckAccess to prevent the command from showing up on the client at all
|
||||
COMMAND.OnCheckAccess = function(command, client)
|
||||
local bHasAccess, _ = CAMI.PlayerHasAccess(client, privilege, nil)
|
||||
|
||||
if (!bHasAccess) then
|
||||
return false
|
||||
end
|
||||
|
||||
local modelClass = ix.anim.GetModelClass(client:GetModel())
|
||||
|
||||
if (!classes[modelClass]) then
|
||||
return false, "modelNoSeq"
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
COMMAND.OnRun = function(command, client, variant)
|
||||
-- Anti-Exploit measure. Just silently fail if near a door.
|
||||
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), 50)) do
|
||||
if (entity:GetClass() == "func_door" or entity:GetClass() == "func_door_rotating" or entity:GetClass() == "prop_door_rotating") then return end
|
||||
end
|
||||
|
||||
variant = math.Clamp(tonumber(variant) or 1, 1, variants)
|
||||
|
||||
if (client:GetNetVar("actEnterAngle")) then
|
||||
return "@notNow"
|
||||
end
|
||||
|
||||
local modelClass = ix.anim.GetModelClass(client:GetModel())
|
||||
local bCanEnter, error = hook.Run("CanPlayerEnterAct", client, modelClass, variant, classes)
|
||||
|
||||
if (!bCanEnter) then
|
||||
return error
|
||||
end
|
||||
|
||||
local data = classes[modelClass]
|
||||
local mainSequence = data.sequence[variant]
|
||||
local mainDuration
|
||||
|
||||
-- check if the main sequence has any extra info
|
||||
if (istable(mainSequence)) then
|
||||
-- any validity checks to perform (i.e facing a wall)
|
||||
if (mainSequence.check) then
|
||||
local result = mainSequence.check(client)
|
||||
|
||||
if (result) then
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
-- position offset
|
||||
if (mainSequence.offset) then
|
||||
client.ixOldPosition = client:GetPos()
|
||||
client:SetPos(client:GetPos() + mainSequence.offset(client))
|
||||
end
|
||||
|
||||
mainDuration = mainSequence.duration
|
||||
mainSequence = mainSequence[1]
|
||||
end
|
||||
|
||||
local startSequence = data.start and data.start[variant] or ""
|
||||
local startDuration
|
||||
|
||||
if (istable(startSequence)) then
|
||||
startDuration = startSequence.duration
|
||||
startSequence = startSequence[1]
|
||||
end
|
||||
|
||||
client:SetNetVar("actEnterAngle", client:GetAngles())
|
||||
|
||||
client:ForceSequence(startSequence, function()
|
||||
-- we've finished the start sequence
|
||||
client.ixUntimedSequence = data.untimed -- client can exit after the start sequence finishes playing
|
||||
|
||||
local duration = client:ForceSequence(mainSequence, function()
|
||||
-- we've stopped playing the main sequence (either duration expired or user cancelled the act)
|
||||
if (data.finish) then
|
||||
local finishSequence = data.finish[variant]
|
||||
local finishDuration
|
||||
|
||||
if (istable(finishSequence)) then
|
||||
finishDuration = finishSequence.duration
|
||||
finishSequence = finishSequence[1]
|
||||
end
|
||||
|
||||
client:ForceSequence(finishSequence, function()
|
||||
-- client has finished the end sequence and is no longer playing any animations
|
||||
self:ExitAct(client)
|
||||
end, finishDuration)
|
||||
else
|
||||
-- there's no end sequence so we can exit right away
|
||||
self:ExitAct(client)
|
||||
end
|
||||
end, data.untimed and 0 or (mainDuration or nil))
|
||||
|
||||
if (!duration) then
|
||||
-- the model doesn't support this variant
|
||||
self:ExitAct(client)
|
||||
client:NotifyLocalized("modelNoSeq")
|
||||
|
||||
return
|
||||
end
|
||||
end, startDuration, nil)
|
||||
|
||||
net.Start("ixActEnter")
|
||||
net.WriteBool(data.idle or false)
|
||||
net.Send(client)
|
||||
|
||||
client.ixNextAct = CurTime() + 4
|
||||
end
|
||||
|
||||
ix.command.Add("Act" .. act, COMMAND)
|
||||
end
|
||||
|
||||
-- setup exit act command
|
||||
local COMMAND = {
|
||||
privilege = "Player Acts",
|
||||
OnRun = function(command, client)
|
||||
-- Anti-Exploit measure. Just silently fail if near a door.
|
||||
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), 50)) do
|
||||
if (entity:GetClass() == "func_door" or entity:GetClass() == "func_door_rotating" or entity:GetClass() == "prop_door_rotating") then return end
|
||||
end
|
||||
|
||||
if (client.ixUntimedSequence) then
|
||||
client:LeaveSequence()
|
||||
|
||||
hook.Run("OnPlayerExitAct", client)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
if (CLIENT) then
|
||||
-- hide this command from the command list
|
||||
COMMAND.OnCheckAccess = function(client)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("ExitAct", COMMAND)
|
||||
end
|
||||
|
||||
function PLUGIN:UpdateAnimation(client, moveData)
|
||||
local angle = client:GetNetVar("actEnterAngle")
|
||||
|
||||
if (angle) then
|
||||
client:SetRenderAngles(angle)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local keyBlacklist = IN_ATTACK + IN_ATTACK2
|
||||
|
||||
function PLUGIN:StartCommand(client, command)
|
||||
if (client:GetNetVar("actEnterAngle")) then
|
||||
command:RemoveKey(keyBlacklist)
|
||||
end
|
||||
end
|
||||
end
|
||||
65
gamemodes/helix/plugins/act/sv_hooks.lua
Normal file
65
gamemodes/helix/plugins/act/sv_hooks.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
util.AddNetworkString("ixActEnter")
|
||||
util.AddNetworkString("ixActLeave")
|
||||
|
||||
function PLUGIN:CanPlayerEnterAct(client, modelClass, variant, act)
|
||||
if (!client:Alive() or client:GetLocalVar("ragdoll") or client:WaterLevel() > 0 or !client:IsOnGround()) then
|
||||
return false, L("notNow", client)
|
||||
end
|
||||
|
||||
-- check if player's model class has an entry in this act table
|
||||
modelClass = modelClass or ix.anim.GetModelClass(client:GetModel())
|
||||
local data = act[modelClass]
|
||||
|
||||
if (!data) then
|
||||
return false, L("modelNoSeq", client)
|
||||
end
|
||||
|
||||
-- some models don't support certain variants
|
||||
local sequence = data.sequence[variant]
|
||||
|
||||
if (!sequence) then
|
||||
hook.Run("OnPlayerExitAct", client)
|
||||
return false, L("modelNoSeq", client)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDeath(client)
|
||||
if (client.ixUntimedSequence) then
|
||||
client:SetNetVar("actEnterAngle")
|
||||
client:LeaveSequence()
|
||||
client.ixUntimedSequence = nil
|
||||
hook.Run("OnPlayerExitAct", client)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSpawn(client)
|
||||
if (client.ixUntimedSequence) then
|
||||
client:SetNetVar("actEnterAngle")
|
||||
client:LeaveSequence()
|
||||
client.ixUntimedSequence = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterFallover(client)
|
||||
if (client.ixUntimedSequence) then
|
||||
client:SetNetVar("actEnterAngle")
|
||||
client:LeaveSequence()
|
||||
client.ixUntimedSequence = nil
|
||||
hook.Run("OnPlayerExitAct", client)
|
||||
end
|
||||
end
|
||||
99
gamemodes/helix/plugins/ammosave.lua
Normal file
99
gamemodes/helix/plugins/ammosave.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
--[[
|
||||
| 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 = "Ammo Saver"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "Saves the ammo of a character."
|
||||
PLUGIN.ammoList = {}
|
||||
|
||||
ix.ammo = ix.ammo or {}
|
||||
|
||||
function ix.ammo.Register(name)
|
||||
name = name:lower()
|
||||
|
||||
if (!table.HasValue(PLUGIN.ammoList, name)) then
|
||||
PLUGIN.ammoList[#PLUGIN.ammoList + 1] = name
|
||||
end
|
||||
end
|
||||
|
||||
-- Register Default HL2 Ammunition.
|
||||
ix.ammo.Register("ar2")
|
||||
ix.ammo.Register("pistol")
|
||||
ix.ammo.Register("357")
|
||||
ix.ammo.Register("smg1")
|
||||
ix.ammo.Register("xbowbolt")
|
||||
ix.ammo.Register("buckshot")
|
||||
ix.ammo.Register("rpg_round")
|
||||
ix.ammo.Register("smg1_grenade")
|
||||
ix.ammo.Register("grenade")
|
||||
ix.ammo.Register("ar2altfire")
|
||||
ix.ammo.Register("slam")
|
||||
|
||||
-- Register Cut HL2 Ammunition.
|
||||
ix.ammo.Register("alyxgun")
|
||||
ix.ammo.Register("sniperround")
|
||||
ix.ammo.Register("sniperpenetratedround")
|
||||
ix.ammo.Register("thumper")
|
||||
ix.ammo.Register("gravity")
|
||||
ix.ammo.Register("battery")
|
||||
ix.ammo.Register("gaussenergy")
|
||||
ix.ammo.Register("combinecannon")
|
||||
ix.ammo.Register("airboatgun")
|
||||
ix.ammo.Register("striderminigun")
|
||||
ix.ammo.Register("helicoptergun")
|
||||
|
||||
-- Called right before the character has its information save.
|
||||
function PLUGIN:CharacterPreSave(character)
|
||||
-- Get the player from the character.
|
||||
local client = character:GetPlayer()
|
||||
|
||||
-- Check to see if we can get the player's ammo.
|
||||
if (IsValid(client)) then
|
||||
local ammoTable = {}
|
||||
|
||||
for _, v in ipairs(self.ammoList) do
|
||||
local ammo = client:GetAmmoCount(v)
|
||||
|
||||
if (ammo > 0) then
|
||||
ammoTable[v] = ammo
|
||||
end
|
||||
end
|
||||
|
||||
character:SetData("ammo", ammoTable)
|
||||
end
|
||||
end
|
||||
|
||||
-- Called after the player's loadout has been set.
|
||||
function PLUGIN:PlayerLoadedCharacter(client)
|
||||
timer.Simple(0.25, function()
|
||||
if (!IsValid(client)) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the saved ammo table from the character data.
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (!character) then
|
||||
return
|
||||
end
|
||||
|
||||
local ammoTable = character:GetData("ammo")
|
||||
|
||||
-- Check if the ammotable is exists.
|
||||
if (ammoTable) then
|
||||
for k, v in pairs(ammoTable) do
|
||||
client:SetAmmo(v, tostring(k))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
270
gamemodes/helix/plugins/area/cl_hooks.lua
Normal file
270
gamemodes/helix/plugins/area/cl_hooks.lua
Normal file
@@ -0,0 +1,270 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
local function DrawTextBackground(x, y, text, font, backgroundColor, padding)
|
||||
font = font or "ixSubTitleFont"
|
||||
padding = padding or 8
|
||||
backgroundColor = backgroundColor or Color(88, 88, 88, 255)
|
||||
|
||||
surface.SetFont(font)
|
||||
local textWidth, textHeight = surface.GetTextSize(text)
|
||||
local width, height = textWidth + padding * 2, textHeight + padding * 2
|
||||
|
||||
ix.util.DrawBlurAt(x, y, width, height)
|
||||
surface.SetDrawColor(0, 0, 0, 40)
|
||||
surface.DrawRect(x, y, width, height)
|
||||
|
||||
derma.SkinFunc("DrawImportantBackground", x, y, width, height, backgroundColor)
|
||||
|
||||
surface.SetTextColor(color_white)
|
||||
surface.SetTextPos(x + padding, y + padding)
|
||||
surface.DrawText(text)
|
||||
|
||||
return height
|
||||
end
|
||||
|
||||
function PLUGIN:InitPostEntity()
|
||||
hook.Run("SetupAreaProperties")
|
||||
end
|
||||
|
||||
function PLUGIN:ChatboxCreated()
|
||||
if (IsValid(self.panel)) then
|
||||
self.panel:Remove()
|
||||
end
|
||||
|
||||
self.panel = vgui.Create("ixArea")
|
||||
end
|
||||
|
||||
function PLUGIN:ChatboxPositionChanged(x, y, width, height)
|
||||
if (!IsValid(self.panel)) then
|
||||
return
|
||||
end
|
||||
|
||||
self.panel:SetSize(width, y)
|
||||
self.panel:SetPos(32, 0)
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDrawCrosshair()
|
||||
if (ix.area.bEditing) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerBindPress(client, bind, bPressed)
|
||||
if (!ix.area.bEditing) then
|
||||
return
|
||||
end
|
||||
|
||||
if ((bind:find("invnext") or bind:find("invprev")) and bPressed) then
|
||||
return true
|
||||
elseif (bind:find("attack2") and bPressed) then
|
||||
self:EditRightClick()
|
||||
return true
|
||||
elseif (bind:find("attack") and bPressed) then
|
||||
self:EditClick()
|
||||
return true
|
||||
elseif (bind:find("reload") and bPressed) then
|
||||
self:EditReload()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:HUDPaint()
|
||||
if (!ix.area.bEditing) then
|
||||
return
|
||||
end
|
||||
|
||||
local id = LocalPlayer():GetArea()
|
||||
local area = ix.area.stored[id]
|
||||
local height = ScrH()
|
||||
|
||||
local y = 64
|
||||
y = y + DrawTextBackground(64, y, L("areaEditMode"), nil, ix.config.Get("color"))
|
||||
|
||||
if (!self.editStart) then
|
||||
y = y + DrawTextBackground(64, y, L("areaEditTip"), "ixSmallTitleFont")
|
||||
DrawTextBackground(64, y, L("areaRemoveTip"), "ixSmallTitleFont")
|
||||
else
|
||||
DrawTextBackground(64, y, L("areaFinishTip"), "ixSmallTitleFont")
|
||||
end
|
||||
|
||||
if (area) then
|
||||
DrawTextBackground(64, height - 64 - ScreenScale(12), id, "ixSmallTitleFont", area.properties.color)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDepth, bSkybox)
|
||||
if (bSkybox or !ix.area.bEditing) then
|
||||
return
|
||||
end
|
||||
|
||||
-- draw all areas
|
||||
for k, v in pairs(ix.area.stored) do
|
||||
local center, min, max = self:GetLocalAreaPosition(v.startPosition, v.endPosition)
|
||||
local color = ColorAlpha(v.properties.color or ix.config.Get("color"), 255)
|
||||
|
||||
render.DrawWireframeBox(center, angle_zero, min, max, color)
|
||||
|
||||
cam.Start2D()
|
||||
local centerScreen = center:ToScreen()
|
||||
local _, textHeight = draw.SimpleText(
|
||||
k, "BudgetLabel", centerScreen.x, centerScreen.y, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if (v.type != "area") then
|
||||
draw.SimpleText(
|
||||
"(" .. L(v.type) .. ")", "BudgetLabel",
|
||||
centerScreen.x, centerScreen.y + textHeight, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER
|
||||
)
|
||||
end
|
||||
cam.End2D()
|
||||
end
|
||||
|
||||
-- draw currently edited area
|
||||
if (self.editStart) then
|
||||
local center, min, max = self:GetLocalAreaPosition(self.editStart, self:GetPlayerAreaTrace().HitPos)
|
||||
local color = Color(255, 255, 255, 25 + (1 + math.sin(SysTime() * 6)) * 115)
|
||||
|
||||
render.DrawWireframeBox(center, angle_zero, min, max, color)
|
||||
|
||||
cam.Start2D()
|
||||
local centerScreen = center:ToScreen()
|
||||
|
||||
draw.SimpleText(L("areaNew"), "BudgetLabel",
|
||||
centerScreen.x, centerScreen.y, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
cam.End2D()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:EditRightClick()
|
||||
if (self.editStart) then
|
||||
self.editStart = nil
|
||||
else
|
||||
self:StopEditing()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:EditClick()
|
||||
if (!self.editStart) then
|
||||
self.editStart = LocalPlayer():GetEyeTraceNoCursor().HitPos
|
||||
elseif (self.editStart and !self.editProperties) then
|
||||
self.editProperties = true
|
||||
|
||||
local panel = vgui.Create("ixAreaEdit")
|
||||
panel:MakePopup()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:EditReload()
|
||||
if (self.editStart) then
|
||||
return
|
||||
end
|
||||
|
||||
local id = LocalPlayer():GetArea()
|
||||
local area = ix.area.stored[id]
|
||||
|
||||
if (!area) then
|
||||
return
|
||||
end
|
||||
|
||||
Derma_Query(L("areaDeleteConfirm", id), L("areaDelete"),
|
||||
L("no"), nil,
|
||||
L("yes"), function()
|
||||
net.Start("ixAreaRemove")
|
||||
net.WriteString(id)
|
||||
net.SendToServer()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDisplayArea(id)
|
||||
if (ix.area.bEditing) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnAreaChanged(oldID, newID)
|
||||
local client = LocalPlayer()
|
||||
client.ixArea = newID
|
||||
|
||||
local area = ix.area.stored[newID]
|
||||
|
||||
if (!area) then
|
||||
client.ixInArea = false
|
||||
return
|
||||
end
|
||||
|
||||
client.ixInArea = true
|
||||
|
||||
if (hook.Run("ShouldDisplayArea", newID) == false or !area.properties.display) then
|
||||
return
|
||||
end
|
||||
|
||||
local format = newID .. (ix.option.Get("24hourTime", false) and ", %H:%M." or ", %I:%M %p.")
|
||||
format = ix.date.GetFormatted(format)
|
||||
|
||||
if (ix.option.Get("showAreaNotices", true)) then
|
||||
self.panel:AddEntry(format, area.properties.color)
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixAreaEditStart", function()
|
||||
PLUGIN:StartEditing()
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaEditEnd", function()
|
||||
PLUGIN:StopEditing()
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaAdd", function()
|
||||
local name = net.ReadString()
|
||||
local type = net.ReadString()
|
||||
local startPosition, endPosition = net.ReadVector(), net.ReadVector()
|
||||
local properties = net.ReadTable()
|
||||
|
||||
if (name != "") then
|
||||
ix.area.stored[name] = {
|
||||
type = type,
|
||||
startPosition = startPosition,
|
||||
endPosition = endPosition,
|
||||
properties = properties
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaRemove", function()
|
||||
local name = net.ReadString()
|
||||
|
||||
if (ix.area.stored[name]) then
|
||||
ix.area.stored[name] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaSync", function()
|
||||
local length = net.ReadUInt(32)
|
||||
local data = net.ReadData(length)
|
||||
local uncompressed = util.Decompress(data)
|
||||
|
||||
if (!uncompressed) then
|
||||
ErrorNoHalt("[Helix] Unable to decompress area data!\n")
|
||||
return
|
||||
end
|
||||
|
||||
-- Set the list of texts to the ones provided by the server.
|
||||
ix.area.stored = util.JSONToTable(uncompressed)
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaChanged", function()
|
||||
local oldID, newID = net.ReadString(), net.ReadString()
|
||||
|
||||
hook.Run("OnAreaChanged", oldID, newID)
|
||||
end)
|
||||
36
gamemodes/helix/plugins/area/cl_plugin.lua
Normal file
36
gamemodes/helix/plugins/area/cl_plugin.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function PLUGIN:GetPlayerAreaTrace()
|
||||
local client = LocalPlayer()
|
||||
|
||||
return util.TraceLine({
|
||||
start = client:GetShootPos(),
|
||||
endpos = client:GetShootPos() + client:GetForward() * 96,
|
||||
filter = client
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:StartEditing()
|
||||
ix.area.bEditing = true
|
||||
self.editStart = nil
|
||||
self.editProperties = nil
|
||||
end
|
||||
|
||||
function PLUGIN:StopEditing()
|
||||
ix.area.bEditing = false
|
||||
|
||||
if (IsValid(ix.gui.areaEdit)) then
|
||||
ix.gui.areaEdit:Remove()
|
||||
end
|
||||
end
|
||||
192
gamemodes/helix/plugins/area/derma/cl_area.lua
Normal file
192
gamemodes/helix/plugins/area/derma/cl_area.lua
Normal file
@@ -0,0 +1,192 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- area entry
|
||||
DEFINE_BASECLASS("Panel")
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "text", "Text", FORCE_STRING)
|
||||
AccessorFunc(PANEL, "backgroundColor", "BackgroundColor")
|
||||
AccessorFunc(PANEL, "tickSound", "TickSound", FORCE_STRING)
|
||||
AccessorFunc(PANEL, "tickSoundRange", "TickSoundRange")
|
||||
AccessorFunc(PANEL, "backgroundAlpha", "BackgroundAlpha", FORCE_NUMBER)
|
||||
AccessorFunc(PANEL, "expireTime", "ExpireTime", FORCE_NUMBER)
|
||||
AccessorFunc(PANEL, "animationTime", "AnimationTime", FORCE_NUMBER)
|
||||
|
||||
function PANEL:Init()
|
||||
self:DockPadding(4, 4, 4, 4)
|
||||
self:SetSize(self:GetParent():GetWide(), 0)
|
||||
|
||||
self.label = self:Add("DLabel")
|
||||
self.label:Dock(FILL)
|
||||
self.label:SetFont("ixMediumLightFont")
|
||||
self.label:SetTextColor(color_white)
|
||||
self.label:SetExpensiveShadow(1, color_black)
|
||||
self.label:SetText("Area")
|
||||
|
||||
self.text = ""
|
||||
self.tickSound = "ui/buttonrollover.wav"
|
||||
self.tickSoundRange = {190, 200}
|
||||
self.backgroundAlpha = 255
|
||||
self.expireTime = 8
|
||||
self.animationTime = 2
|
||||
|
||||
self.character = 1
|
||||
self.createTime = RealTime()
|
||||
self.currentAlpha = 255
|
||||
self.currentHeight = 0
|
||||
self.nextThink = RealTime()
|
||||
end
|
||||
|
||||
function PANEL:Show()
|
||||
self:CreateAnimation(0.5, {
|
||||
index = -1,
|
||||
target = {currentHeight = self.label:GetTall() + 8},
|
||||
easing = "outQuint",
|
||||
|
||||
Think = function(animation, panel)
|
||||
panel:SetTall(panel.currentHeight)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function PANEL:SetFont(font)
|
||||
self.label:SetFont(font)
|
||||
end
|
||||
|
||||
function PANEL:SetText(text)
|
||||
if (text:sub(1, 1) == "@") then
|
||||
text = L(text:sub(2))
|
||||
end
|
||||
|
||||
self.label:SetText(text)
|
||||
self.text = text
|
||||
self.character = 1
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
local time = RealTime()
|
||||
|
||||
if (time >= self.nextThink) then
|
||||
if (self.character < self.text:utf8len()) then
|
||||
self.character = self.character + 1
|
||||
self.label:SetText(string.utf8sub(self.text, 1, self.character))
|
||||
|
||||
LocalPlayer():EmitSound(self.tickSound, 100, math.random(self.tickSoundRange[1], self.tickSoundRange[2]))
|
||||
end
|
||||
|
||||
if (time >= self.createTime + self.expireTime and !self.bRemoving) then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.nextThink = time + 0.05
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SizeToContents()
|
||||
self:SetWide(self:GetParent():GetWide())
|
||||
|
||||
self.label:SetWide(self:GetWide())
|
||||
self.label:SizeToContentsY()
|
||||
end
|
||||
|
||||
function PANEL:Paint(width, height)
|
||||
self.backgroundAlpha = math.max(self.backgroundAlpha - 200 * FrameTime(), 0)
|
||||
|
||||
derma.SkinFunc("PaintAreaEntry", self, width, height)
|
||||
end
|
||||
|
||||
function PANEL:Remove()
|
||||
if (self.bRemoving) then
|
||||
return
|
||||
end
|
||||
|
||||
self:CreateAnimation(self.animationTime, {
|
||||
target = {currentAlpha = 0},
|
||||
|
||||
Think = function(animation, panel)
|
||||
panel:SetAlpha(panel.currentAlpha)
|
||||
end,
|
||||
|
||||
OnComplete = function(animation, panel)
|
||||
panel:CreateAnimation(0.5, {
|
||||
index = -1,
|
||||
target = {currentHeight = 0},
|
||||
easing = "outQuint",
|
||||
|
||||
Think = function(_, sizePanel)
|
||||
sizePanel:SetTall(sizePanel.currentHeight)
|
||||
end,
|
||||
|
||||
OnComplete = function(_, sizePanel)
|
||||
sizePanel:OnRemove()
|
||||
BaseClass.Remove(sizePanel)
|
||||
end
|
||||
})
|
||||
end
|
||||
})
|
||||
|
||||
self.bRemoving = true
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
end
|
||||
|
||||
vgui.Register("ixAreaEntry", PANEL, "Panel")
|
||||
|
||||
-- main panel
|
||||
PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
local chatWidth, _ = chat.GetChatBoxSize()
|
||||
local _, chatY = chat.GetChatBoxPos()
|
||||
|
||||
self:SetSize(chatWidth, chatY)
|
||||
self:SetPos(32, 0)
|
||||
self:ParentToHUD()
|
||||
|
||||
self.entries = {}
|
||||
ix.gui.area = self
|
||||
end
|
||||
|
||||
function PANEL:AddEntry(entry, color)
|
||||
color = color or ix.config.Get("color")
|
||||
|
||||
local id = #self.entries + 1
|
||||
local panel = entry
|
||||
|
||||
if (isstring(entry)) then
|
||||
panel = self:Add("ixAreaEntry")
|
||||
panel:SetText(entry)
|
||||
end
|
||||
|
||||
panel:SetBackgroundColor(color)
|
||||
panel:SizeToContents()
|
||||
panel:Dock(BOTTOM)
|
||||
panel:Show()
|
||||
panel.OnRemove = function()
|
||||
for k, v in pairs(self.entries) do
|
||||
if (v == panel) then
|
||||
table.remove(self.entries, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.entries[id] = panel
|
||||
return id
|
||||
end
|
||||
|
||||
function PANEL:GetEntries()
|
||||
return self.entries
|
||||
end
|
||||
|
||||
vgui.Register("ixArea", PANEL, "Panel")
|
||||
203
gamemodes/helix/plugins/area/derma/cl_areaedit.lua
Normal file
203
gamemodes/helix/plugins/area/derma/cl_areaedit.lua
Normal file
@@ -0,0 +1,203 @@
|
||||
--[[
|
||||
| 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
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
if (IsValid(ix.gui.areaEdit)) then
|
||||
ix.gui.areaEdit:Remove()
|
||||
end
|
||||
|
||||
ix.gui.areaEdit = self
|
||||
self.list = {}
|
||||
self.properties = {}
|
||||
|
||||
self:SetDeleteOnClose(true)
|
||||
self:SetSizable(true)
|
||||
self:SetTitle(L("areaNew"))
|
||||
|
||||
-- scroll panel
|
||||
self.canvas = self:Add("DScrollPanel")
|
||||
self.canvas:Dock(FILL)
|
||||
|
||||
-- name entry
|
||||
self.nameEntry = vgui.Create("ixTextEntry")
|
||||
self.nameEntry:SetFont("ixMenuButtonFont")
|
||||
self.nameEntry:SetText(L("areaNew"))
|
||||
|
||||
local listRow = self.canvas:Add("ixListRow")
|
||||
listRow:SetList(self.list)
|
||||
listRow:SetLabelText(L("name"))
|
||||
listRow:SetRightPanel(self.nameEntry)
|
||||
listRow:Dock(TOP)
|
||||
listRow:SizeToContents()
|
||||
|
||||
-- type entry
|
||||
self.typeEntry = self.canvas:Add("DComboBox")
|
||||
self.typeEntry:Dock(RIGHT)
|
||||
self.typeEntry:SetFont("ixMenuButtonFont")
|
||||
self.typeEntry:SetTextColor(color_white)
|
||||
self.typeEntry.OnSelect = function(panel)
|
||||
panel:SizeToContents()
|
||||
panel:SetWide(panel:GetWide() + 12) -- padding for arrow (nice)
|
||||
end
|
||||
|
||||
for id, name in pairs(ix.area.types) do
|
||||
self.typeEntry:AddChoice(L(name), id, id == "area")
|
||||
end
|
||||
|
||||
listRow = self.canvas:Add("ixListRow")
|
||||
listRow:SetList(self.list)
|
||||
listRow:SetLabelText(L("type"))
|
||||
listRow:SetRightPanel(self.typeEntry)
|
||||
listRow:Dock(TOP)
|
||||
listRow:SizeToContents()
|
||||
|
||||
-- properties
|
||||
for k, v in pairs(ix.area.properties) do
|
||||
local panel
|
||||
|
||||
if (v.type == ix.type.string or v.type == ix.type.number) then
|
||||
panel = vgui.Create("ixTextEntry")
|
||||
panel:SetFont("ixMenuButtonFont")
|
||||
panel:SetText(tostring(v.default))
|
||||
|
||||
if (v.type == ix.type.number) then
|
||||
panel.realGetValue = panel.GetValue
|
||||
panel.GetValue = function()
|
||||
return tonumber(panel:realGetValue()) or v.default
|
||||
end
|
||||
end
|
||||
elseif (v.type == ix.type.bool) then
|
||||
panel = vgui.Create("ixCheckBox")
|
||||
panel:SetChecked(v.default, true)
|
||||
elseif (v.type == ix.type.color) then
|
||||
panel = vgui.Create("DButton")
|
||||
panel.value = v.default
|
||||
panel:SetText("")
|
||||
panel:SetSize(64, 64)
|
||||
|
||||
panel.picker = vgui.Create("DColorCombo")
|
||||
panel.picker:SetColor(panel.value)
|
||||
panel.picker:SetVisible(false)
|
||||
panel.picker.OnValueChanged = function(_, newColor)
|
||||
panel.value = newColor
|
||||
end
|
||||
|
||||
panel.Paint = function(_, width, height)
|
||||
surface.SetDrawColor(0, 0, 0, 255)
|
||||
surface.DrawOutlinedRect(0, 0, width, height)
|
||||
|
||||
surface.SetDrawColor(panel.value)
|
||||
surface.DrawRect(4, 4, width - 8, height - 8)
|
||||
end
|
||||
|
||||
panel.DoClick = function()
|
||||
if (!panel.picker:IsVisible()) then
|
||||
local x, y = panel:LocalToScreen(0, 0)
|
||||
|
||||
panel.picker:SetPos(x, y + 32)
|
||||
panel.picker:SetColor(panel.value)
|
||||
panel.picker:SetVisible(true)
|
||||
panel.picker:MakePopup()
|
||||
else
|
||||
panel.picker:SetVisible(false)
|
||||
end
|
||||
end
|
||||
|
||||
panel.OnRemove = function()
|
||||
panel.picker:Remove()
|
||||
end
|
||||
|
||||
panel.GetValue = function()
|
||||
return panel.picker:GetColor()
|
||||
end
|
||||
end
|
||||
|
||||
if (IsValid(panel)) then
|
||||
local row = self.canvas:Add("ixListRow")
|
||||
row:SetList(self.list)
|
||||
row:SetLabelText(L(k))
|
||||
row:SetRightPanel(panel)
|
||||
row:Dock(TOP)
|
||||
row:SizeToContents()
|
||||
end
|
||||
|
||||
self.properties[k] = function()
|
||||
return panel:GetValue()
|
||||
end
|
||||
end
|
||||
|
||||
-- save button
|
||||
self.saveButton = self:Add("DButton")
|
||||
self.saveButton:SetText(L("save"))
|
||||
self.saveButton:SizeToContents()
|
||||
self.saveButton:Dock(BOTTOM)
|
||||
self.saveButton.DoClick = function()
|
||||
self:Submit()
|
||||
end
|
||||
|
||||
self:SizeToContents()
|
||||
self:SetPos(64, 0)
|
||||
self:CenterVertical()
|
||||
end
|
||||
|
||||
function PANEL:SizeToContents()
|
||||
local width = 64
|
||||
local height = 37
|
||||
|
||||
for _, v in ipairs(self.canvas:GetCanvas():GetChildren()) do
|
||||
width = math.max(width, v:GetLabelWidth())
|
||||
height = height + v:GetTall()
|
||||
end
|
||||
|
||||
self:SetWide(width + 200)
|
||||
self:SetTall(height + self.saveButton:GetTall())
|
||||
end
|
||||
|
||||
function PANEL:Submit()
|
||||
local name = self.nameEntry:GetValue()
|
||||
|
||||
if (ix.area.stored[name]) then
|
||||
ix.util.NotifyLocalized("areaAlreadyExists")
|
||||
return
|
||||
end
|
||||
|
||||
local properties = {}
|
||||
|
||||
for k, v in pairs(self.properties) do
|
||||
properties[k] = v()
|
||||
end
|
||||
|
||||
local _, type = self.typeEntry:GetSelected()
|
||||
|
||||
net.Start("ixAreaAdd")
|
||||
net.WriteString(name)
|
||||
net.WriteString(type)
|
||||
net.WriteVector(PLUGIN.editStart)
|
||||
net.WriteVector(PLUGIN:GetPlayerAreaTrace().HitPos)
|
||||
net.WriteTable(properties)
|
||||
net.SendToServer()
|
||||
|
||||
PLUGIN.editStart = nil
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
PLUGIN.editProperties = nil
|
||||
end
|
||||
|
||||
vgui.Register("ixAreaEdit", PANEL, "DFrame")
|
||||
|
||||
if (IsValid(ix.gui.areaEdit)) then
|
||||
ix.gui.areaEdit:Remove()
|
||||
end
|
||||
29
gamemodes/helix/plugins/area/languages/sh_english.lua
Normal file
29
gamemodes/helix/plugins/area/languages/sh_english.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
LANGUAGE = {
|
||||
area = "Area",
|
||||
areas = "Areas",
|
||||
areaEditMode = "Area Edit Mode",
|
||||
areaNew = "New Area",
|
||||
areaAlreadyExists = "An area with this name already exists!",
|
||||
areaDoesntExist = "An area with that name doesn't exist!",
|
||||
areaInvalidType = "You have specified an invalid area type!",
|
||||
areaEditTip = "Click to start creating an area. Right click to exit.",
|
||||
areaFinishTip = "Click again to finish drawing the area. Right click to go back.",
|
||||
areaRemoveTip = "Press reload to remove the area you're currently in.",
|
||||
areaDeleteConfirm = "Are you sure you want to delete the area \"%s\"?",
|
||||
areaDelete = "Delete Area",
|
||||
|
||||
cmdAreaEdit = "Enters area edit mode.",
|
||||
|
||||
optShowAreaNotices = "Show Area Notices",
|
||||
optdShowAreaNotices = "Whether a notification should appear on the screen when you enter a new area."
|
||||
}
|
||||
26
gamemodes/helix/plugins/area/languages/sh_russian.lua
Normal file
26
gamemodes/helix/plugins/area/languages/sh_russian.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
LANGUAGE = {
|
||||
area = "Зона",
|
||||
areas = "Зоны",
|
||||
areaEditMode = "Редактирование зоны",
|
||||
areaNew = "Новая зона",
|
||||
areaAlreadyExists = "Зона с таким названием уже существует!",
|
||||
areaDoesntExist = "Зона с таким названием не существует!",
|
||||
areaInvalidType = "Вы указали неверный тип зоны!",
|
||||
areaEditTip = "Нажмите, чтобы начать создание зоны. Щелкните правой кнопкой мыши, чтобы выйти.",
|
||||
areaFinishTip = "Нажмите еще раз, чтобы закончить создание зоны. Щелкните правой кнопкой мыши, чтобы вернуться.",
|
||||
areaRemoveTip = "Нажмите перезарядку, чтобы удалить зону, в которой вы находитесь.",
|
||||
areaDeleteConfirm = "Вы уверены, что хотите удалить зону \"%s\"?",
|
||||
areaDelete = "Удалить зону",
|
||||
|
||||
cmdAreaEdit = "Вход в режим редактирования зоны."
|
||||
}
|
||||
104
gamemodes/helix/plugins/area/sh_plugin.lua
Normal file
104
gamemodes/helix/plugins/area/sh_plugin.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
--[[
|
||||
| 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 = "Areas"
|
||||
PLUGIN.author = "`impulse"
|
||||
PLUGIN.description = "Provides customizable area definitions."
|
||||
|
||||
ix.area = ix.area or {}
|
||||
ix.area.types = ix.area.types or {}
|
||||
ix.area.properties = ix.area.properties or {}
|
||||
ix.area.stored = ix.area.stored or {}
|
||||
|
||||
ix.config.Add("areaTickTime", 1, "How many seconds between each time a character's current area is calculated.",
|
||||
function(oldValue, newValue)
|
||||
if (SERVER) then
|
||||
timer.Remove("ixAreaThink")
|
||||
timer.Create("ixAreaThink", newValue, 0, function()
|
||||
PLUGIN:AreaThink()
|
||||
end)
|
||||
end
|
||||
end,
|
||||
{
|
||||
data = {min = 0.1, max = 4},
|
||||
category = "areas"
|
||||
}
|
||||
)
|
||||
|
||||
ix.option.Add("showAreaNotices", ix.type.bool, true, {
|
||||
category = "appearance",
|
||||
})
|
||||
|
||||
function ix.area.AddProperty(name, type, default, data)
|
||||
ix.area.properties[name] = {
|
||||
type = type,
|
||||
default = default
|
||||
}
|
||||
end
|
||||
|
||||
function ix.area.AddType(type, name)
|
||||
name = name or type
|
||||
|
||||
-- only store localized strings on the client
|
||||
ix.area.types[type] = CLIENT and name or true
|
||||
end
|
||||
|
||||
function PLUGIN:SetupAreaProperties()
|
||||
ix.area.AddType("area")
|
||||
|
||||
ix.area.AddProperty("color", ix.type.color, ix.config.Get("color"))
|
||||
ix.area.AddProperty("display", ix.type.bool, true)
|
||||
end
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
|
||||
-- return world center, local min, and local max from world start/end positions
|
||||
function PLUGIN:GetLocalAreaPosition(startPosition, endPosition)
|
||||
local center = LerpVector(0.5, startPosition, endPosition)
|
||||
local min = WorldToLocal(startPosition, angle_zero, center, angle_zero)
|
||||
local max = WorldToLocal(endPosition, angle_zero, center, angle_zero)
|
||||
|
||||
return center, min, max
|
||||
end
|
||||
|
||||
do
|
||||
local COMMAND = {}
|
||||
COMMAND.description = "@cmdAreaEdit"
|
||||
COMMAND.adminOnly = true
|
||||
|
||||
function COMMAND:OnRun(client)
|
||||
client:SetWepRaised(false)
|
||||
|
||||
net.Start("ixAreaEditStart")
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
ix.command.Add("AreaEdit", COMMAND)
|
||||
end
|
||||
|
||||
do
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
|
||||
-- returns the current area the player is in, or the last valid one if the player is not in an area
|
||||
function PLAYER:GetArea()
|
||||
return self.ixArea
|
||||
end
|
||||
|
||||
-- returns true if the player is in any area, this does not use the last valid area like GetArea does
|
||||
function PLAYER:IsInArea()
|
||||
return self.ixInArea
|
||||
end
|
||||
end
|
||||
136
gamemodes/helix/plugins/area/sv_hooks.lua
Normal file
136
gamemodes/helix/plugins/area/sv_hooks.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
hook.Run("SetupAreaProperties")
|
||||
ix.area.stored = self:GetData() or {}
|
||||
|
||||
timer.Create("ixAreaThink", ix.config.Get("areaTickTime"), 0, function()
|
||||
self:AreaThink()
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
self:SetData(ix.area.stored)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
timer.Simple(1, function()
|
||||
if (IsValid(client)) then
|
||||
local json = util.TableToJSON(ix.area.stored)
|
||||
local compressed = util.Compress(json)
|
||||
local length = compressed:len()
|
||||
|
||||
net.Start("ixAreaSync")
|
||||
net.WriteUInt(length, 32)
|
||||
net.WriteData(compressed, length)
|
||||
net.Send(client)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client)
|
||||
client.ixArea = ""
|
||||
client.ixInArea = nil
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSpawn(client)
|
||||
client.ixArea = ""
|
||||
client.ixInArea = nil
|
||||
end
|
||||
|
||||
function PLUGIN:AreaThink()
|
||||
for _, client in ipairs(player.GetAll()) do
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (!client:Alive() or !character) then
|
||||
continue
|
||||
end
|
||||
|
||||
local overlappingBoxes = {}
|
||||
local position = client:GetPos() + client:OBBCenter()
|
||||
|
||||
for id, info in pairs(ix.area.stored) do
|
||||
if (position:WithinAABox(info.startPosition, info.endPosition)) then
|
||||
overlappingBoxes[#overlappingBoxes + 1] = id
|
||||
end
|
||||
end
|
||||
|
||||
if (#overlappingBoxes > 0) then
|
||||
local oldID = client:GetArea()
|
||||
local id = overlappingBoxes[1]
|
||||
|
||||
if (oldID != id) then
|
||||
hook.Run("OnPlayerAreaChanged", client, client.ixArea, id)
|
||||
client.ixArea = id
|
||||
end
|
||||
|
||||
client.ixInArea = true
|
||||
else
|
||||
client.ixInArea = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnPlayerAreaChanged(client, oldID, newID)
|
||||
net.Start("ixAreaChanged")
|
||||
net.WriteString(oldID)
|
||||
net.WriteString(newID)
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
net.Receive("ixAreaAdd", function(length, client)
|
||||
if (!client:Alive() or !CAMI.PlayerHasAccess(client, "Helix - AreaEdit", nil)) then
|
||||
return
|
||||
end
|
||||
|
||||
local id = net.ReadString()
|
||||
local type = net.ReadString()
|
||||
local startPosition, endPosition = net.ReadVector(), net.ReadVector()
|
||||
local properties = net.ReadTable()
|
||||
|
||||
if (!ix.area.types[type]) then
|
||||
client:NotifyLocalized("areaInvalidType")
|
||||
return
|
||||
end
|
||||
|
||||
if (ix.area.stored[id]) then
|
||||
client:NotifyLocalized("areaAlreadyExists")
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(properties) do
|
||||
if (!isstring(k) or !ix.area.properties[k]) then
|
||||
continue
|
||||
end
|
||||
|
||||
properties[k] = ix.util.SanitizeType(ix.area.properties[k].type, v)
|
||||
end
|
||||
|
||||
ix.area.Create(id, type, startPosition, endPosition, nil, properties)
|
||||
ix.log.Add(client, "areaAdd", id)
|
||||
end)
|
||||
|
||||
net.Receive("ixAreaRemove", function(length, client)
|
||||
if (!client:Alive() or !CAMI.PlayerHasAccess(client, "Helix - AreaEdit", nil)) then
|
||||
return
|
||||
end
|
||||
|
||||
local id = net.ReadString()
|
||||
|
||||
if (!ix.area.stored[id]) then
|
||||
client:NotifyLocalized("areaDoesntExist")
|
||||
return
|
||||
end
|
||||
|
||||
ix.area.Remove(id)
|
||||
ix.log.Add(client, "areaRemove", id)
|
||||
end)
|
||||
65
gamemodes/helix/plugins/area/sv_plugin.lua
Normal file
65
gamemodes/helix/plugins/area/sv_plugin.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
util.AddNetworkString("ixAreaSync")
|
||||
util.AddNetworkString("ixAreaAdd")
|
||||
util.AddNetworkString("ixAreaRemove")
|
||||
util.AddNetworkString("ixAreaChanged")
|
||||
|
||||
util.AddNetworkString("ixAreaEditStart")
|
||||
util.AddNetworkString("ixAreaEditEnd")
|
||||
|
||||
ix.log.AddType("areaAdd", function(client, name)
|
||||
return string.format("%s has added area \"%s\".", client:Name(), tostring(name))
|
||||
end)
|
||||
|
||||
ix.log.AddType("areaRemove", function(client, name)
|
||||
return string.format("%s has removed area \"%s\".", client:Name(), tostring(name))
|
||||
end)
|
||||
|
||||
local function SortVector(first, second)
|
||||
return Vector(math.min(first.x, second.x), math.min(first.y, second.y), math.min(first.z, second.z)),
|
||||
Vector(math.max(first.x, second.x), math.max(first.y, second.y), math.max(first.z, second.z))
|
||||
end
|
||||
|
||||
function ix.area.Create(name, type, startPosition, endPosition, bNoReplicate, properties)
|
||||
local min, max = SortVector(startPosition, endPosition)
|
||||
|
||||
ix.area.stored[name] = {
|
||||
type = type or "area",
|
||||
startPosition = min,
|
||||
endPosition = max,
|
||||
bNoReplicate = bNoReplicate,
|
||||
properties = properties
|
||||
}
|
||||
|
||||
-- network to clients if needed
|
||||
if (!bNoReplicate) then
|
||||
net.Start("ixAreaAdd")
|
||||
net.WriteString(name)
|
||||
net.WriteString(type)
|
||||
net.WriteVector(startPosition)
|
||||
net.WriteVector(endPosition)
|
||||
net.WriteTable(properties)
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
|
||||
function ix.area.Remove(name, bNoReplicate)
|
||||
ix.area.stored[name] = nil
|
||||
|
||||
-- network to clients if needed
|
||||
if (!bNoReplicate) then
|
||||
net.Start("ixAreaRemove")
|
||||
net.WriteString(name)
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
75
gamemodes/helix/plugins/autocleandb.lua
Normal file
75
gamemodes/helix/plugins/autocleandb.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
--[[
|
||||
| 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
|
||||
local ix = ix
|
||||
|
||||
PLUGIN.name = "Database Cleaner"
|
||||
PLUGIN.description = "Cleans up old logs and other database entries."
|
||||
PLUGIN.author = "Votton"
|
||||
|
||||
ix.config.Add("cleanupInterval", 3, "The number of hours between database cleanups.", nil, {
|
||||
data = {min = 1, max = 24},
|
||||
category = "Database Cleanup"
|
||||
})
|
||||
|
||||
ix.config.Add("logCleanupDays", 122, "The number of days to keep logs for.", nil, {
|
||||
data = {min = 1, max = 365},
|
||||
category = "Database Cleanup"
|
||||
})
|
||||
|
||||
ix.config.Add("deletedEntityCleanupDays", 30, "The number of days to keep deleted entities for.", nil, {
|
||||
data = {min = 1, max = 365},
|
||||
category = "Database Cleanup"
|
||||
})
|
||||
|
||||
-- Cleans up logs that are older than the specified number of days.
|
||||
function PLUGIN:cleanLogs()
|
||||
local cleanupDays = ix.config.Get("logCleanupDays")
|
||||
local cleanupTimestamp = os.time() - (cleanupDays * 24 * 60 * 60)
|
||||
|
||||
local query = mysql:Delete("ix_logs")
|
||||
query:WhereLT("datetime", cleanupTimestamp)
|
||||
query:Execute(function(result)
|
||||
print("[Database Cleaner] is cleaning up logs older than " .. cleanupDays .. " days.")
|
||||
if result and result.affected > 0 then
|
||||
print("[Database Cleaner] Deleted " .. result.affected .. " logs.")
|
||||
else
|
||||
print("[Database Cleaner] No logs to clean up.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Cleans up deleted entities that are older than the specified number of days.
|
||||
function PLUGIN:cleanDeletedEntities()
|
||||
local cleanupDays = ix.config.Get("deletedEntityCleanupDays")
|
||||
local cleanupTimestamp = os.time() - (cleanupDays * 24 * 60 * 60)
|
||||
|
||||
local query = mysql:Delete("ix_saveents")
|
||||
query:WhereLT("deleted", cleanupTimestamp)
|
||||
query:WhereNotEqual("deleted", 0)
|
||||
query:Execute(function(result)
|
||||
print("[Database Cleaner] is cleaning up deleted entities older than " .. cleanupDays .. " days.")
|
||||
if result and result.affected > 0 then
|
||||
print("[Database Cleaner] Deleted " .. result.affected .. " entities.")
|
||||
else
|
||||
print("[Database Cleaner] No deleted entities to clean up.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Initialise the cleanup timer when the database is connected.
|
||||
function PLUGIN:DatabaseConnected()
|
||||
local cleanupInterval = ix.config.Get("cleanupInterval")
|
||||
timer.Create("ixDatabaseCleaner", cleanupInterval * 60 * 60, 0, function()
|
||||
self:cleanLogs()
|
||||
self:cleanDeletedEntities()
|
||||
end)
|
||||
end
|
||||
80
gamemodes/helix/plugins/autowalk.lua
Normal file
80
gamemodes/helix/plugins/autowalk.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
--[[
|
||||
| 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
|
||||
local ix = ix
|
||||
local pairs = pairs
|
||||
|
||||
PLUGIN.name = "Autowalk"
|
||||
PLUGIN.author = "AleXXX_007"
|
||||
PLUGIN.description = "Allows players to enable autowalk."
|
||||
|
||||
ix.option.Add("enableAutoWalkBind", ix.type.bool, true, {
|
||||
bNetworked = true,
|
||||
category = "General",
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optEnableAutoWalkBind = "Włącz bind automatycznego chodzenia",
|
||||
optdEnableAutoWalkBind = "Włącza funkcję automatycznego chodzenia do przodu za pomocą przycisku 'n'.",
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
optEnableAutoWalkBind = "Włącz bind automatycznego chodzenia",
|
||||
optdEnableAutoWalkBind = "Włącza funkcję automatycznego chodzenia do przodu za pomocą przycisku 'n'.",
|
||||
})
|
||||
|
||||
PLUGIN.bind = KEY_N
|
||||
|
||||
local exitKeys = {
|
||||
IN_FORWARD,
|
||||
IN_BACK,
|
||||
IN_MOVELEFT,
|
||||
IN_MOVERIGHT
|
||||
}
|
||||
|
||||
function PLUGIN:GetOption(client)
|
||||
if (CLIENT) then
|
||||
return ix.option.Get("enableAutoWalkBind")
|
||||
else
|
||||
return ix.option.Get(client, "enableAutoWalkBind")
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:SetupMove(client, moveData, cmdData)
|
||||
if (!client.autowalk) then return end
|
||||
|
||||
if (ix.fights and client:GetFight()) then
|
||||
client.autowalk = nil
|
||||
return
|
||||
end
|
||||
|
||||
moveData:SetForwardSpeed(moveData:GetMaxSpeed())
|
||||
|
||||
for _, v in pairs(exitKeys) do
|
||||
if (cmdData:KeyDown(v)) then
|
||||
client.autowalk = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerButtonDown(client, button)
|
||||
if (!self:GetOption(client)) then
|
||||
client.autowalk = false
|
||||
return
|
||||
end
|
||||
local curTime = CurTime()
|
||||
|
||||
if (button == self.bind and (!client.nextBind or client.nextBind <= curTime)) then
|
||||
client.autowalk = !client.autowalk
|
||||
client.nextBind = curTime + 0.1
|
||||
end
|
||||
end
|
||||
39
gamemodes/helix/plugins/awarn.lua
Normal file
39
gamemodes/helix/plugins/awarn.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
PLUGIN.name = "AWarn3 Compat"
|
||||
PLUGIN.author = "Gr4Ss"
|
||||
PLUGIN.description = "Adds AWarn 3 integration."
|
||||
|
||||
if (!AWarn) then return end
|
||||
|
||||
if (SERVER) then
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character, lastChar)
|
||||
client:SetNetVar("AWarnWarnings", client:GetActiveWarnings())
|
||||
end
|
||||
|
||||
function PLUGIN:AWarnPlayerWarned(client, aID, reason)
|
||||
client:SetNetVar("AWarnWarnings", client:GetActiveWarnings())
|
||||
end
|
||||
function PLUGIN:AWarnPlayerIDWarn(clientID, aID, reason)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:SteamID64() == clientID) then
|
||||
return self:AwarnPlayerWarned(v, aID, reason)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
function PLUGIN:GetPlayerESPText(client, toDraw, distance, alphaFar, alphaMid, alphaClose)
|
||||
if (client:GetNetVar("AWarnWarnings", 0) > 0) then
|
||||
toDraw[#toDraw + 1] = {alpha = alphaClose, priority = 29.5, text = "Warnings: "..client:GetNetVar("AWarnWarnings", 0)}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,295 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.base = "base_bags"
|
||||
ITEM.allowNesting = false -- Whether to allow this bag to be stored inside other bags.
|
||||
ITEM.restriction = {} -- List of item IDs allowed to be put inside this bag. Supports item bases.
|
||||
ITEM.noOpen = false -- Whether to disallow this bag from being opened through right click or not.
|
||||
ITEM.noEquip = false -- Whether to disallow this bag from being 'equipped' by the player.
|
||||
|
||||
ITEM.functions.View = {
|
||||
icon = "icon16/briefcase.png",
|
||||
OnClick = function(item)
|
||||
local index = item:GetData("id", "")
|
||||
|
||||
if (index) then
|
||||
local panel = ix.gui["inv"..index]
|
||||
local inventory = ix.item.inventories[index]
|
||||
local parent
|
||||
local iconSize = SScaleMin(90 / 3)
|
||||
|
||||
if ix.gui.menuInventoryParent then
|
||||
if IsValid(ix.gui.menuInventoryParent.backpacks) then
|
||||
parent = IsValid(ix.gui.menuInventoryParent.backpacks) and ix.gui.menuInventoryParent.backpacks
|
||||
else
|
||||
parent = ix.gui.openedStorage
|
||||
end
|
||||
else
|
||||
parent = ix.gui.openedStorage
|
||||
end
|
||||
|
||||
if (IsValid(panel)) then
|
||||
panel:Remove()
|
||||
end
|
||||
|
||||
if (inventory and inventory.slots) then
|
||||
panel = vgui.Create("ixInventory", IsValid(parent) and parent or nil)
|
||||
panel:SetInventory(inventory)
|
||||
panel:Dock(LEFT)
|
||||
panel:DockMargin(0, SScaleMin(30 / 3), 0, 0)
|
||||
|
||||
if (parent == ix.gui.openedStorage or !item.noOpen) then
|
||||
if (panel) then
|
||||
panel:Remove()
|
||||
end
|
||||
|
||||
local DFrame = vgui.Create("DFrame", IsValid(parent) and parent or nil)
|
||||
-- 4 and 30 are accounting for the size of DFrame borders here
|
||||
DFrame:SetSize(
|
||||
item.invWidth * (iconSize + 2) + SScaleMin(4 / 3),
|
||||
item.invHeight * (iconSize + SScaleMin(2 / 3)) + SScaleMin(30 / 3)
|
||||
)
|
||||
DFrame:SetTitle(item.GetName and item:GetName() or L(item.name))
|
||||
DFrame:SetDraggable(true)
|
||||
DFrame:MoveToFront()
|
||||
|
||||
if (!item.noOpen and (ix.gui.menu and parent != ix.gui.openedStorage)) then
|
||||
DFrame:SetParent(ix.gui.menuInventoryParent)
|
||||
else
|
||||
DFrame:MakePopup()
|
||||
end
|
||||
|
||||
DFrameFixer(DFrame, true, true)
|
||||
|
||||
parent.bagFrame = DFrame
|
||||
|
||||
panel = vgui.Create("ixInventory", DFrame)
|
||||
panel:Dock(TOP)
|
||||
panel:SetInventory(inventory)
|
||||
|
||||
DFrame:SetPos(input.GetCursorPos())
|
||||
else
|
||||
panel:MoveToFront()
|
||||
end
|
||||
|
||||
ix.gui["inv"..index] = panel
|
||||
else
|
||||
ErrorNoHalt("[Helix] Attempt to view an uninitialized inventory '"..index.."'\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item)
|
||||
if (CLIENT) and item.noOpen and
|
||||
(!ix.gui.openedStorage or ix.gui.openedStorage and !IsValid(ix.gui.openedStorage)) then return false end
|
||||
return !IsValid(item.entity) and item:GetData("id") and !IsValid(ix.gui["inv" .. item:GetData("id", "")])
|
||||
end
|
||||
}
|
||||
|
||||
-- Allows bags to be opened from the world.
|
||||
ITEM.functions.ViewAlt = {
|
||||
name = "Wyświetl",
|
||||
OnRun = function(item)
|
||||
local inventory = item:GetInventory()
|
||||
|
||||
if (inventory) then
|
||||
ix.storage.Open(item.player, inventory, {
|
||||
name = item.name,
|
||||
entity = item.entity,
|
||||
searchTime = 0
|
||||
})
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item)
|
||||
return IsValid(item.entity)
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.Equip = {
|
||||
OnRun = function(item, data)
|
||||
local owner = item:GetOwner()
|
||||
|
||||
if (owner) then
|
||||
local index = owner:FindBodygroupByName(item.uniqueID == "largebag" and owner:GetActiveCombineSuit() and "cp_Bag" or item.bodygroup)
|
||||
local groups = owner:GetCharacter():GetData("groups", {})
|
||||
|
||||
if (index > -1) then
|
||||
groups[index] = 1
|
||||
owner:GetCharacter():SetData("groups", groups)
|
||||
owner:SetBodygroup(index, 1)
|
||||
|
||||
netstream.Start(owner, "ItemEquipBodygroups", index, 1)
|
||||
end
|
||||
|
||||
item:SetData("equip", true)
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item, creationClient)
|
||||
local client = item.player or creationClient
|
||||
return !IsValid(item.entity) and IsValid(client) and item:GetData("equip") != true and hook.Run("CanPlayerEquipItem", client, item) != false and !item.noEquip
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.EquipUn = { -- sorry, for name order.
|
||||
name = "Zdejmij",
|
||||
tip = "unequipTip",
|
||||
icon = "icon16/cross.png",
|
||||
OnRun = function(item, creationClient)
|
||||
local client = item.player or creationClient
|
||||
|
||||
if (client) then
|
||||
local index = client:FindBodygroupByName(item.uniqueID == "largebag" and client:GetActiveCombineSuit() and "cp_Bag" or item.bodygroup)
|
||||
local groups = client:GetCharacter():GetData("groups", {})
|
||||
|
||||
if (index > -1) then
|
||||
groups[index] = 0
|
||||
client:GetCharacter():SetData("groups", groups)
|
||||
client:SetBodygroup(index, 0)
|
||||
|
||||
netstream.Start(client, "ItemEquipBodygroups", index, 0)
|
||||
end
|
||||
|
||||
item:SetData("equip", false)
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(item, creationClient)
|
||||
local client = item.player or creationClient
|
||||
|
||||
return !IsValid(item.entity) and IsValid(client) and item:GetData("equip") == true and
|
||||
hook.Run("CanPlayerUnequipItem", client, item) != false
|
||||
end
|
||||
}
|
||||
|
||||
-- Called when the item should tell whether or not it can be transfered between inventories.
|
||||
-- Allows bags to be put inside containers.
|
||||
function ITEM:CanTransfer(oldInventory, newInventory)
|
||||
local index = self:GetData("id")
|
||||
|
||||
if (newInventory) then
|
||||
if (newInventory.vars and newInventory.vars.isBag and !newInventory.vars.isContainer and !self.allowNesting) then
|
||||
return false
|
||||
end
|
||||
|
||||
local index2 = newInventory:GetID()
|
||||
|
||||
if (index == index2) then
|
||||
return false
|
||||
end
|
||||
|
||||
local curInv = self.GetInventory and self:GetInventory()
|
||||
local curInvItems = curInv and curInv.GetItems and curInv:GetItems()
|
||||
if curInvItems then
|
||||
for _, v in pairs(curInvItems) do
|
||||
if (v:GetData("id") == index2) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return !newInventory or newInventory:GetID() != oldInventory:GetID() or newInventory.vars.isBag
|
||||
end
|
||||
|
||||
-- Called when a new instance of this item has been made.
|
||||
function ITEM:OnInstanced(invID, x, y)
|
||||
if (self.invBeingMade) then return end
|
||||
self.invBeingMade = true
|
||||
|
||||
local inventory = ix.item.inventories[invID]
|
||||
|
||||
ix.inventory.New(inventory and inventory.owner or 0, self.uniqueID, function(inv)
|
||||
local client = inv:GetOwner()
|
||||
|
||||
inv.vars.isBag = self.uniqueID
|
||||
inv.vars.allowNesting = self.allowNesting
|
||||
inv.vars.restriction = self.restriction
|
||||
inv.vars.noEquipInv = true
|
||||
self:SetData("id", inv:GetID())
|
||||
|
||||
if (IsValid(client)) then
|
||||
inv:AddReceiver(client)
|
||||
end
|
||||
|
||||
if (self.OnBagInitialized) then
|
||||
self:OnBagInitialized(inv)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Called when the item first appears for a client.
|
||||
function ITEM:OnSendData()
|
||||
local index = self:GetData("id")
|
||||
|
||||
if (index) then
|
||||
local inventory = ix.item.inventories[index]
|
||||
|
||||
if (inventory) then
|
||||
inventory.vars.isBag = self.uniqueID
|
||||
inventory.vars.allowNesting = self.allowNesting
|
||||
inventory.vars.restriction = self.restriction
|
||||
inventory.vars.noEquipInv = true
|
||||
|
||||
inventory:Sync(self.player)
|
||||
inventory:AddReceiver(self.player)
|
||||
else
|
||||
local owner = self.player:GetCharacter():GetID()
|
||||
|
||||
ix.inventory.Restore(self:GetData("id"), self.invWidth, self.invHeight, function(inv)
|
||||
inv.vars.isBag = self.uniqueID
|
||||
inv.vars.allowNesting = self.allowNesting
|
||||
inv.vars.restriction = self.restriction
|
||||
inv.vars.noEquipInv = true
|
||||
|
||||
inv:SetOwner(owner, true)
|
||||
|
||||
if (!inv.owner) then
|
||||
return
|
||||
end
|
||||
|
||||
for client, character in ix.util.GetCharacters() do
|
||||
if (character:GetID() == inv.owner) then
|
||||
inv:AddReceiver(client)
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
if (self.invBeingMade) then return end
|
||||
self.invBeingMade = true
|
||||
|
||||
ix.inventory.New(self.player:GetCharacter():GetID(), self.uniqueID, function(inv)
|
||||
local client = inv:GetOwner()
|
||||
|
||||
inv.vars.isBag = self.uniqueID
|
||||
inv.vars.allowNesting = self.allowNesting
|
||||
inv.vars.restriction = self.restriction
|
||||
inv.vars.noEquipInv = true
|
||||
self:SetData("id", inv:GetID())
|
||||
|
||||
if (IsValid(client)) then
|
||||
inv:AddReceiver(client)
|
||||
end
|
||||
|
||||
if (self.OnBagInitialized) then
|
||||
self:OnBagInitialized(inv)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Ładownica na amunicję"
|
||||
ITEM.description = "Torba, która może pomieścić sporą ilość pocisków."
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/backpack.mdl")
|
||||
ITEM.noOpen = false
|
||||
ITEM.noEquip = true
|
||||
ITEM.invWidth = 4
|
||||
ITEM.invHeight = 2
|
||||
ITEM.width = 2
|
||||
ITEM.height = 1
|
||||
ITEM.restriction = { -- ammo is base_stackable, and thats kinda too broad for this so gotta list it manually
|
||||
"bullets_357",
|
||||
"bullets_assaultrifle",
|
||||
"bullets_buckshot",
|
||||
"bullets_heavypulse",
|
||||
"bullets_pistol",
|
||||
"bullets_pulse",
|
||||
"bullets_smg1",
|
||||
"bullets_sniper"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Duża torba"
|
||||
ITEM.description = "Plecak z insygniami Kombinatu."
|
||||
ITEM.invWidth = 4
|
||||
ITEM.invHeight = 4
|
||||
ITEM.bodygroup = "bag"
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/backpack.mdl")
|
||||
ITEM.outfitCategory = "Bag"
|
||||
ITEM.noOpen = true
|
||||
|
||||
ITEM.hooks.View = function(item, data)
|
||||
local client = item.player
|
||||
local inventory = client:GetCharacter():GetInventory()
|
||||
local items = inventory:GetItemsByUniqueID(item.uniqueID)
|
||||
if (#items > 1) then
|
||||
table.SortByMember(items, "id", true)
|
||||
if (items[1].id != item.id) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Ładownica na magazynki"
|
||||
ITEM.description = "Torba, do której można schować magazynki."
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
|
||||
ITEM.noOpen = false
|
||||
ITEM.noEquip = true
|
||||
ITEM.invWidth = 4
|
||||
ITEM.invHeight = 1
|
||||
ITEM.restriction = {"base_arccwmag"}
|
||||
@@ -0,0 +1,26 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Torba medyczna"
|
||||
ITEM.description = "Torba, w której można schować wszystkie przybory medyczne"
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
|
||||
ITEM.noOpen = false
|
||||
ITEM.noEquip = true
|
||||
ITEM.invWidth = 2
|
||||
ITEM.invHeight = 6
|
||||
ITEM.restriction = {
|
||||
"base_medical",
|
||||
"drink_saline",
|
||||
"comp_antidote",
|
||||
"comp_bloodsyringe",
|
||||
"comp_syringe",
|
||||
"drug_amph_a",
|
||||
"drug_amph_b",
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Mała torba"
|
||||
ITEM.description = "Mała saszetka, która spoczywa na biodrze."
|
||||
ITEM.invWidth = 3
|
||||
ITEM.invHeight = 4
|
||||
ITEM.bodygroup = "satchel"
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
|
||||
ITEM.outfitCategory = "Satchel"
|
||||
ITEM.noOpen = true
|
||||
78
gamemodes/helix/plugins/bagsystem/sh_hooks.lua
Normal file
78
gamemodes/helix/plugins/bagsystem/sh_hooks.lua
Normal file
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Called to check if an item can be transferred.
|
||||
-- Allows inventories to be nested inside containers. NOTE: Also needs custom bag base.
|
||||
function PLUGIN:CanTransferItem(itemObject, curInv, inventory)
|
||||
if (SERVER) then
|
||||
local client = itemObject.GetOwner and itemObject:GetOwner() or nil
|
||||
|
||||
if (IsValid(client) and curInv.GetReceivers) then
|
||||
local bAuthorized = false
|
||||
|
||||
for _, v in ipairs(curInv:GetReceivers()) do
|
||||
if (client == v) then
|
||||
bAuthorized = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if (!bAuthorized) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- don't allow bags to be put inside bags
|
||||
if (inventory.id != 0 and curInv.id != inventory.id) then
|
||||
if (inventory.vars and inventory.vars.isBag and !inventory.vars.isContainer and !itemObject.allowNesting and itemObject.isBag) then
|
||||
local owner = itemObject:GetOwner()
|
||||
|
||||
if (IsValid(owner)) then
|
||||
owner:NotifyLocalized("nestedBags")
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if (inventory.vars and inventory.vars.restriction and #inventory.vars.restriction > 0) then
|
||||
if (!table.HasValue(inventory.vars.restriction, itemObject.uniqueID) and !table.HasValue(inventory.vars.restriction, itemObject.base)) then
|
||||
local owner = itemObject:GetOwner()
|
||||
|
||||
if (IsValid(owner)) then
|
||||
owner:NotifyLocalized("restrictedBag")
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
elseif (inventory.id != 0 and curInv.id == inventory.id) then
|
||||
-- we are simply moving items around if we're transferring to the same inventory
|
||||
return
|
||||
end
|
||||
|
||||
inventory = ix.item.inventories[itemObject:GetData("id")]
|
||||
|
||||
-- don't allow transferring items that are in use
|
||||
if (inventory) then
|
||||
for _, v in pairs(inventory:GetItems()) do
|
||||
if (v:GetData("equip") == true) then
|
||||
local owner = itemObject:GetOwner()
|
||||
|
||||
if (owner and IsValid(owner)) then
|
||||
owner:NotifyLocalized("equippedBag")
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
gamemodes/helix/plugins/bagsystem/sh_plugin.lua
Normal file
25
gamemodes/helix/plugins/bagsystem/sh_plugin.lua
Normal file
@@ -0,0 +1,25 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
PLUGIN.name = "Bag System"
|
||||
PLUGIN.author = "Fruity & Aspect™"
|
||||
PLUGIN.description = "A simple bag system."
|
||||
|
||||
ix.util.Include("sh_hooks.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
restrictedBag = "Ten ekwipunek nie może pomieścić tego typu przedmiotów!"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
restrictedBag = "Ten ekwipunek nie może pomieścić tego typu przedmiotów!"
|
||||
})
|
||||
21
gamemodes/helix/plugins/bagsystem/sv_plugin.lua
Normal file
21
gamemodes/helix/plugins/bagsystem/sv_plugin.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function PLUGIN:CanPlayerTradeWithVendor(client, entity, uniqueID, isSellingToVendor)
|
||||
if (isSellingToVendor) then return end
|
||||
|
||||
if (uniqueID == "smallbag" or uniqueID == "largebag") then
|
||||
if (client:GetCharacter():GetInventory():HasItem(uniqueID)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
1
gamemodes/helix/plugins/bastion/apikey_proxy.txt
Normal file
1
gamemodes/helix/plugins/bastion/apikey_proxy.txt
Normal file
@@ -0,0 +1 @@
|
||||
4x1374-j382fs-599g87-31j7h3
|
||||
1
gamemodes/helix/plugins/bastion/apikey_proxy.txt.example
Normal file
1
gamemodes/helix/plugins/bastion/apikey_proxy.txt.example
Normal file
@@ -0,0 +1 @@
|
||||
<put https://proxycheck.io proxy key here and remove .example from file name>
|
||||
1
gamemodes/helix/plugins/bastion/apiwebhook_discord.txt
Normal file
1
gamemodes/helix/plugins/bastion/apiwebhook_discord.txt
Normal file
@@ -0,0 +1 @@
|
||||
https://discord.com/api/webhooks/832651486860541973/iYpexHJmA5E6pD6WwUT1GyvLtQ9_8ly-qB0xloIsPu7KmaU0mkQVRE_Kg0qVwGRlwhs_
|
||||
@@ -0,0 +1 @@
|
||||
<put discord webhook URL here and remove .example from filename>
|
||||
153
gamemodes/helix/plugins/bastion/cl_plugin.lua
Normal file
153
gamemodes/helix/plugins/bastion/cl_plugin.lua
Normal file
@@ -0,0 +1,153 @@
|
||||
--[[
|
||||
| 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 CAMI = CAMI
|
||||
local LocalPlayer = LocalPlayer
|
||||
local L = L
|
||||
local SetClipboardText = SetClipboardText
|
||||
local chat = chat
|
||||
local net = net
|
||||
local table = table
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
local render = render
|
||||
local Color = Color
|
||||
local gui = gui
|
||||
local hook = hook
|
||||
local surface = surface
|
||||
local netstream = netstream
|
||||
local ipairs = ipairs
|
||||
local MsgN = MsgN
|
||||
local ix = ix
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.infoIcon = ix.util.GetMaterial("icon16/information.png")
|
||||
|
||||
ix.option.Add("staffChat", ix.type.bool, true, {
|
||||
category = "administration",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Hear Staff Chat", nil)
|
||||
end
|
||||
})
|
||||
|
||||
function PLUGIN:PopulateScoreboardPlayerMenu(client, menu)
|
||||
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")) then
|
||||
menu:AddOption(L("bastionCopySteamName"), function()
|
||||
SetClipboardText(client:SteamName())
|
||||
LocalPlayer():NotifyLocalized("bastionCopiedSteamName")
|
||||
end)
|
||||
|
||||
menu:AddOption(L("bastionCopyCharName"), function()
|
||||
SetClipboardText(client:Name())
|
||||
LocalPlayer():NotifyLocalized("bastionCopiedCharName")
|
||||
end)
|
||||
end
|
||||
|
||||
if (sam and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands") and !LocalPlayer():InVehicle() and client != LocalPlayer()) then
|
||||
menu:AddOption(L("bastionGoto"), function()
|
||||
if (LocalPlayer():GetMoveType() != MOVETYPE_NOCLIP) then
|
||||
LocalPlayer():ConCommand("noclip")
|
||||
end
|
||||
LocalPlayer():ConCommand("say !goto "..client:Name())
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PrintTarget(target)
|
||||
if (ix.option.Get("pgi")) then
|
||||
SetClipboardText(target:SteamID())
|
||||
end
|
||||
|
||||
chat.AddText(self.infoIcon, target:Name(), " (", target:SteamName(), "; ", target:SteamID(),
|
||||
") | HP: ", target:Health(), " | Armor: ", target:Armor())
|
||||
end
|
||||
|
||||
function PLUGIN:PrintStaffList(amount)
|
||||
for _ = 1, amount do
|
||||
local group = net.ReadString()
|
||||
local members = net.ReadUInt(8)
|
||||
local memberList = {}
|
||||
for _ = 1, members do
|
||||
memberList[#memberList + 1] = net.ReadEntity():SteamName()
|
||||
end
|
||||
|
||||
table.sort(memberList)
|
||||
chat.AddText(self.infoIcon, "[", string.utf8upper(group), "]: ", table.concat(memberList, ", "))
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDisplayArea(id)
|
||||
if (LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP and !LocalPlayer():InVehicle()) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local commands = {
|
||||
["playsound"] = 2,
|
||||
["localevent"] = 2,
|
||||
["showentsinradius"] = 1,
|
||||
["localbroadcast"] = 2,
|
||||
["localbroadcastme"] = 2,
|
||||
["localbroadcastit"] = 2,
|
||||
["playsong"] = 3,
|
||||
["screenshake"] = 4,
|
||||
["moviebars"] = 1,
|
||||
["removepersistedprops"] = 1,
|
||||
["removeclientprops"] = 1
|
||||
}
|
||||
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
|
||||
local command = string.utf8lower(ix.chat.currentCommand)
|
||||
|
||||
if (commands[command]) then
|
||||
local range = tonumber(ix.chat.currentArguments[commands[command]])
|
||||
|
||||
if (range) then
|
||||
render.SetColorMaterial()
|
||||
render.DrawSphere(LocalPlayer():GetPos(), 0 - range, 50, 50, Color(255, 150, 0, 100))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixOpenURL", function(len)
|
||||
gui.OpenURL(net.ReadString())
|
||||
end)
|
||||
|
||||
net.Receive("ixPlayerInfo", function(len)
|
||||
PLUGIN:PrintTarget(net.ReadEntity())
|
||||
end)
|
||||
|
||||
net.Receive("ixStaffList", function(len)
|
||||
PLUGIN:PrintStaffList(net.ReadUInt(8))
|
||||
end)
|
||||
|
||||
net.Receive("ixPlaySound", function(len)
|
||||
local sound = net.ReadString()
|
||||
local isGlobal = net.ReadBool()
|
||||
|
||||
if (hook.Run("PrePlaySound", sound, isGlobal) != false) then
|
||||
surface.PlaySound(sound)
|
||||
|
||||
hook.Run("PostPlaySound", sound, isGlobal)
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("PrintInfoList", function(list)
|
||||
for _, v in ipairs(list) do
|
||||
MsgN(v)
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:OnPlayerChat()
|
||||
if (ix.config.Get("suppressOnPlayerChat", true)) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
137
gamemodes/helix/plugins/bastion/modules/sh_bindchecker.lua
Normal file
137
gamemodes/helix/plugins/bastion/modules/sh_bindchecker.lua
Normal file
@@ -0,0 +1,137 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixBindGrab")
|
||||
util.AddNetworkString("ixBindGrabList")
|
||||
end
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Check Bind",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
ix.command.Add("PlyGetBinds", {
|
||||
description = "Get a list of all of someone's binds.",
|
||||
privilege = "Check Bind",
|
||||
arguments = {ix.type.player, bit.bor(ix.type.optional, ix.type.bool)},
|
||||
OnRun = function(self, client, target, all)
|
||||
if (IsValid(target.ixBindGrab) and target.ixBindGrabTime and target.ixBindGrabTime > CurTime()) then
|
||||
return "Someone else is checking this player's binds already. Please wait a few seconds!"
|
||||
end
|
||||
|
||||
target.ixBindGrab = client
|
||||
target.ixBindGrabTime = CurTime() + 5
|
||||
target.ixBindGrabAll = all
|
||||
net.Start("ixBindGrab")
|
||||
net.Send(target)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
if (CLIENT) then
|
||||
net.Receive("ixBindGrab", function()
|
||||
net.Start("ixBindGrab")
|
||||
for i = 1, BUTTON_CODE_LAST do
|
||||
net.WriteString(string.Left(input.LookupKeyBinding(i) or "", 255))
|
||||
end
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
local blacklist = {
|
||||
["slot0"] = true,
|
||||
["slot1"] = true,
|
||||
["slot2"] = true,
|
||||
["slot3"] = true,
|
||||
["slot4"] = true,
|
||||
["slot5"] = true,
|
||||
["slot6"] = true,
|
||||
["slot7"] = true,
|
||||
["slot8"] = true,
|
||||
["slot9"] = true,
|
||||
["+zoom"] = true,
|
||||
|
||||
["+forward"] = true,
|
||||
["+back"] = true,
|
||||
["+moveleft"] = true,
|
||||
["+moveright"] = true,
|
||||
["+jump"] = true,
|
||||
["+speed"] = true,
|
||||
["+walk"] = true,
|
||||
["+duck"] = true,
|
||||
|
||||
["+lookup"] = true,
|
||||
["+left"] = true,
|
||||
["+lookdown"] = true,
|
||||
["+right"] = true,
|
||||
|
||||
["+attack"] = true,
|
||||
["+attack2"] = true,
|
||||
["+reload"] = true,
|
||||
["+use"] = true,
|
||||
["invprev"] = true,
|
||||
["invnext"] = true,
|
||||
|
||||
["+menu"] = true,
|
||||
["+menu_context"] = true,
|
||||
["gmod_undo"] = true,
|
||||
|
||||
["+showscores"] = true,
|
||||
["gm_showhelp"] = true,
|
||||
["gm_showteam"] = true,
|
||||
["gm_showspare1"] = true,
|
||||
["gm_showspare2"] = true,
|
||||
|
||||
["noclip"] = true,
|
||||
["messagemode"] = true,
|
||||
|
||||
["toggleconsole"] = true,
|
||||
["cancelselect"] = true,
|
||||
["pause"] = true,
|
||||
["save quick"] = true,
|
||||
["load quick"] = true,
|
||||
|
||||
["impulse 100"] = true,
|
||||
["+voicerecord"] = true,
|
||||
["jpeg"] = true,
|
||||
}
|
||||
net.Receive("ixBindGrabList", function()
|
||||
local all = net.ReadBool()
|
||||
MsgN(net.ReadString().."'s binds ("..net.ReadString()..")")
|
||||
for i = 1, BUTTON_CODE_LAST do
|
||||
local bind = net.ReadString()
|
||||
if (!all and blacklist[bind]) then continue end
|
||||
|
||||
if (bind and bind != "") then
|
||||
if (#bind == 255) then
|
||||
bind = bind.."..."
|
||||
end
|
||||
MsgN((input.GetKeyName(i) or i)..": ", bind)
|
||||
end
|
||||
end
|
||||
|
||||
LocalPlayer():Notify("Binds were printed in console!")
|
||||
end)
|
||||
else
|
||||
net.Receive("ixBindGrab", function(len, client)
|
||||
if (!IsValid(client.ixBindGrab) or !CAMI.PlayerHasAccess(client.ixBindGrab, "Helix - Check Bind")) then return end
|
||||
net.Start("ixBindGrabList")
|
||||
net.WriteBool(client.ixBindGrabAll)
|
||||
net.WriteString(client:SteamName())
|
||||
net.WriteString(client:SteamID())
|
||||
for i = 1, BUTTON_CODE_LAST do
|
||||
net.WriteString(string.Left(net.ReadString(), 255))
|
||||
end
|
||||
net.Send(client.ixBindGrab)
|
||||
|
||||
client.ixBindGrab = nil
|
||||
end)
|
||||
end
|
||||
511
gamemodes/helix/plugins/bastion/modules/sv_antialt.lua
Normal file
511
gamemodes/helix/plugins/bastion/modules/sv_antialt.lua
Normal file
@@ -0,0 +1,511 @@
|
||||
--[[
|
||||
| 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 pcall = pcall
|
||||
local util = util
|
||||
local ix = ix
|
||||
local string = string
|
||||
local net = net
|
||||
local ipairs = ipairs
|
||||
local IsValid = IsValid
|
||||
local os = os
|
||||
local L = L
|
||||
local math = math
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local print = print
|
||||
local CHTTP = CHTTP
|
||||
local player = player
|
||||
local CAMI = CAMI
|
||||
|
||||
if (!CHTTP) then
|
||||
pcall(require, "chttp")
|
||||
end
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
PLUGIN.TYPE_UNKNOWN = 1
|
||||
PLUGIN.TYPE_KNOWN = 2
|
||||
|
||||
local COOKIE_KEY = "qvFT4QSSST8K4tZF"
|
||||
|
||||
local fileChecks = {
|
||||
{"hl2_misc_001.vpk", "hl2"},
|
||||
{"ep2_pak_008.vpk", "ep2"},
|
||||
{"cstrike_pak_003.vpk", "css", "cstrike"},
|
||||
{"bin/client_panorama.dll", "csgo"},
|
||||
{"detail.vbsp", "gmod", "garrysmod"}
|
||||
}
|
||||
|
||||
util.AddNetworkString("RecieveDupe")
|
||||
|
||||
ix.util.Include("sv_antialt_db.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
altSignupProxy = "Próbujesz dołączyć na Veles jako nowy gracz, korzystając z proxy lub VPN. Aby przeciwdziałać multikontom, nie zezwalamy na to.\n\n Prosimy o wyłączenie proxy/VPN i ponowne dołączenie na serwer. Po pomyślnym dołączeniu na serwer możesz ponownie włączyć proxy/VPN dla przyszłych sesji.\n\nJeśli nie korzystasz z proxy lub VPN, skontaktuj się z zarządem społeczności na discordzie."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
altSignupProxy = "Próbujesz dołączyć na Veles jako nowy gracz, korzystając z proxy lub VPN. Aby przeciwdziałać multikontom, nie zezwalamy na to.\n\n Prosimy o wyłączenie proxy/VPN i ponowne dołączenie na serwer. Po pomyślnym dołączeniu na serwer możesz ponownie włączyć proxy/VPN dla przyszłych sesji.\n\nJeśli nie korzystasz z proxy lub VPN, skontaktuj się z zarządem społeczności na discordzie."
|
||||
})
|
||||
|
||||
ix.log.AddType("altNewClient", function(client)
|
||||
return string.format("[AltChecker] %s joined for the first time, generating new cookie.", client:SteamName())
|
||||
end)
|
||||
ix.log.AddType("altKnownNewCookie", function(client)
|
||||
return string.format("[AltChecker] %s joined with unknown installation, generating new cookie.", client:SteamName())
|
||||
end)
|
||||
ix.log.AddType("altKnownCookieMatched", function(client, matched, total)
|
||||
return string.format("[AltChecker] %s joined without a cookie, but matched installation with existing cookie. Certainty %d/%d", client:SteamName(), matched, total)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "bastionAntiAlt", function(client)
|
||||
if (client:IsBot()) then return end
|
||||
|
||||
client.ixAltData = {
|
||||
checks = 4 + #fileChecks,
|
||||
altLogging = 0,
|
||||
error = false,
|
||||
received = false,
|
||||
checkComplete = false,
|
||||
newAltFound = false,
|
||||
|
||||
mode = 0,
|
||||
localCookie = "",
|
||||
cookies = false,
|
||||
ip = false,
|
||||
timestamps = false,
|
||||
|
||||
otherCookies = {},
|
||||
otherIPs = {},
|
||||
otherTimestamps = {},
|
||||
otherTimestampsNZ = {},
|
||||
otherTimeMatches = {},
|
||||
|
||||
discordAlert = {
|
||||
cookies = {},
|
||||
timestamps = {},
|
||||
ips = {},
|
||||
altIDs = {}
|
||||
}
|
||||
}
|
||||
|
||||
-- Lookup user data
|
||||
PLUGIN:AltLoadUserData(client)
|
||||
|
||||
if (PLUGIN.API_KEY) then
|
||||
-- Lookup user IP
|
||||
PLUGIN:AltLookupIP(client)
|
||||
else
|
||||
client.ixAltData.checkes = client.ixAltData.checks - 1
|
||||
end
|
||||
|
||||
-- Check for IP matches
|
||||
PLUGIN:AltLookupIPMatches(client)
|
||||
|
||||
-- Request cookie and install timestamps
|
||||
PLUGIN:RequestClientData(client)
|
||||
end)
|
||||
|
||||
function PLUGIN:RequestClientData(client)
|
||||
net.Start("RecieveDupe")
|
||||
net.WriteUInt(1, 3)
|
||||
net.WriteString(COOKIE_KEY)
|
||||
for _, v in ipairs(fileChecks) do
|
||||
net.WriteString(v[1])
|
||||
net.WriteString(v[3] or v[2])
|
||||
end
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
net.Receive("RecieveDupe", function(len, client)
|
||||
if (!IsValid(client) or !client.ixAltData or client.ixAltData.received) then return end
|
||||
|
||||
local data = client.ixAltData
|
||||
data.received = true
|
||||
-- set cookie
|
||||
data.localCookie = net.ReadString()
|
||||
-- set file timestamps
|
||||
data.timestamps = {}
|
||||
for i = 1, #fileChecks do
|
||||
data.timestamps[i] = net.ReadUInt(32)
|
||||
end
|
||||
|
||||
-- Check for cookie matches
|
||||
if (data.localCookie != "") then
|
||||
PLUGIN:AltLookupCookieMatches(client, data)
|
||||
else
|
||||
PLUGIN:AltPreFinalChecking(client)
|
||||
end
|
||||
|
||||
-- Check for install timestamp matches
|
||||
data.otherTimestamps = {}
|
||||
data.otherTimestampsNZ = {}
|
||||
for i = 1, #fileChecks do
|
||||
PLUGIN:AltLookupTimestampMatches(client, data, fileChecks[i][2], data.timestamps[i])
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:AltPreFinalChecking(client)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
local data = client.ixAltData
|
||||
-- if we errored, don't do anything
|
||||
-- check will be reexecuted when the client rejoins
|
||||
if (data.error) then return end
|
||||
|
||||
-- check if all queries finished
|
||||
data.checks = data.checks - 1
|
||||
if (data.checks != 0) then return end
|
||||
|
||||
-- cookie matches (STRONG)
|
||||
if (#data.otherCookies > 0) then
|
||||
for _, v in ipairs(data.otherCookies) do
|
||||
self:AltFound(client, v[2], "cookie", "cookie")
|
||||
data.discordAlert.cookies[#data.discordAlert.cookies + 1] = (v[2] or "").." ("..(v[1] or "")..")"
|
||||
data.discordAlert.altIDs[v[2]] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- IP (WEAK)
|
||||
if (#data.otherIPs > 0) then
|
||||
for _, v in ipairs(data.otherIPs) do
|
||||
data.discordAlert.ips[#data.discordAlert.ips + 1] = v[2]
|
||||
data.discordAlert.altIDs[v[2]] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- time matches (STRONG-MEDIUM)
|
||||
self:AggregateTimestampMatches(data)
|
||||
|
||||
-- If no local cookie and player is known, check if a known cookie was time-matched
|
||||
if (data.localCookie == "" and data.mode == self.TYPE_KNOWN) then
|
||||
self:FindMatchingCookie(client, data)
|
||||
end
|
||||
|
||||
if (#data.otherTimeMatches > 0) then
|
||||
-- go looking for the other clients that own our matched timestamps
|
||||
self:AltLookupCookieForTimestamps(client, data, #fileChecks)
|
||||
else
|
||||
-- else we don't need to wait for the lookup above
|
||||
data.checkComplete = true
|
||||
self:AltFinalChecking(client)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:AltFinalChecking(client)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
local data = client.ixAltData
|
||||
if (!data.checkComplete or data.altLogging != 0) then return end
|
||||
|
||||
self:DiscordAlert(client)
|
||||
|
||||
-- update IP list
|
||||
local steamID = client:SteamID64()
|
||||
local ip = self.GetIPAddress(client)
|
||||
local query = mysql:Select("bastion_antialt_userips")
|
||||
query:Where("steamid", steamID)
|
||||
query:Where("ip", ip)
|
||||
query:Callback(function(result)
|
||||
if (!result or #result == 0) then
|
||||
local query2 = mysql:Insert("bastion_antialt_userips")
|
||||
query2:Insert("steamid", steamID)
|
||||
query2:Insert("ip", ip)
|
||||
query2:Insert("last_seen", os.time())
|
||||
query2:Execute()
|
||||
else
|
||||
local query2 = mysql:Update("bastion_antialt_userips")
|
||||
query2:Where("id", result[1].id)
|
||||
query2:Update("last_seen", os.time())
|
||||
query2:Execute()
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
|
||||
-- Kick if new player on proxy/vpn
|
||||
if (data.mode == self.TYPE_UNKNOWN) then
|
||||
if (self.API_KEY and (data.ip.proxy == "yes" or (data.ip.risk or 0) > 60)) then
|
||||
if (ix.config.Get("VPNKick")) then
|
||||
self:ProxyAlert(client)
|
||||
client:Kick(L("altSignupProxy", client))
|
||||
else
|
||||
if (!self:NotifyProxyJoin(client) or ix.config.Get("ProxyAlwaysAlert")) then
|
||||
self:ProxyAlert(client)
|
||||
end
|
||||
end
|
||||
elseif (data.localCookie == "") then
|
||||
ix.log.Add(client, "altNewClient")
|
||||
self:GenerateCookie(client, data)
|
||||
else
|
||||
-- Update the cookie's timestamps
|
||||
self:StoreCookieInfo(data, fileChecks)
|
||||
-- Add this cookie to client as well so it can be time matched/timestamps updated
|
||||
self:StoreClientCookie(client, data)
|
||||
end
|
||||
elseif (data.localCookie == "") then
|
||||
ix.log.Add(client, "altKnownNewCookie")
|
||||
self:GenerateCookie(client, data)
|
||||
else
|
||||
-- Update the cookie's timestamps
|
||||
self:StoreCookieInfo(data, fileChecks)
|
||||
-- Add this cookie to client as well so it can be time matched/timestamps updated
|
||||
self:StoreClientCookie(client, data)
|
||||
end
|
||||
|
||||
if (sam) then
|
||||
timer.Simple(3, function()
|
||||
if (!IsValid(client)) then return end
|
||||
local query1 = mysql:Select("bastion_antialt_alts")
|
||||
query1:Where("steamid", client:SteamID64())
|
||||
query1:Select("alt_id")
|
||||
query1:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
if (!result or #result == 0) then return end
|
||||
local query2 = mysql:Select("bastion_antialt_alts")
|
||||
query2:Where("alt_id", result[1].alt_id)
|
||||
query2:WhereNotEqual("steamid", client:SteamID64())
|
||||
query2:Select("steamid")
|
||||
query2:Callback(function(result2)
|
||||
if (!IsValid(client)) then return end
|
||||
if (!result2 or #result2 == 0) then return end
|
||||
|
||||
for k, v in ipairs(result2) do
|
||||
sam.player.is_banned(util.SteamIDFrom64(v.steamid), function(banned)
|
||||
if (!banned or !IsValid(client)) then return end
|
||||
client:Kick("You have a banned alt account.")
|
||||
return
|
||||
end)
|
||||
end
|
||||
end)
|
||||
query2:Execute()
|
||||
end)
|
||||
query1:Execute()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
COOKIE STUFF
|
||||
]]
|
||||
function PLUGIN:WhitelistPlayer(client)
|
||||
local data = client.ixAltData
|
||||
if (!data) then return end
|
||||
|
||||
if (data.localCookie) then
|
||||
ix.log.Add(client, "altNewClient")
|
||||
self:GenerateCookie(client, data)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN.RandomString()
|
||||
local result = {} -- The empty table we start with
|
||||
while (#result != 64) do
|
||||
local char = string.char(math.random(32, 126))
|
||||
if (string.find(char, "%w")) then
|
||||
result[#result + 1] = char
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(result)
|
||||
end
|
||||
|
||||
function PLUGIN:GenerateCookie(client, data)
|
||||
local cookie = self.RandomString()
|
||||
self:UpdateLocalCookie(client, data, cookie)
|
||||
|
||||
local query = mysql:Insert("bastion_antialt_users")
|
||||
query:Insert("steamid", client:SteamID64())
|
||||
query:Insert("steam_name", client:SteamName())
|
||||
query:Insert("cookie", cookie)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:UpdateLocalCookie(client, data, cookie)
|
||||
data.localCookie = cookie
|
||||
|
||||
net.Start("RecieveDupe")
|
||||
net.WriteUInt(2, 3)
|
||||
net.WriteString(COOKIE_KEY)
|
||||
net.WriteString(cookie)
|
||||
net.Send(client)
|
||||
|
||||
self:StoreCookieInfo(data, fileChecks)
|
||||
end
|
||||
|
||||
function PLUGIN:FindMatchingCookie(client, data)
|
||||
for _, v in ipairs(data.otherTimeMatches) do
|
||||
for _, v1 in ipairs(data.cookies) do
|
||||
if (v1.cookie == v[1]) then
|
||||
-- found a timestamp match belonging to the client, restore cookie
|
||||
-- in case of e.g. gmod reinstall
|
||||
ix.log.Add(client, "altKnownCookieMatched", v[2], #fileChecks)
|
||||
self:UpdateLocalCookie(client, data, v[1])
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
LOGGING AND ALERTING
|
||||
]]
|
||||
local ALT_OFFSET = 0
|
||||
function PLUGIN:AltFound(client, steamid, type, info)
|
||||
local ids = {client:SteamID64(), steamid}
|
||||
local data = client.ixAltData
|
||||
|
||||
data.altLogging = data.altLogging + 1
|
||||
|
||||
local query = mysql:Select("bastion_antialt_alts")
|
||||
query:WhereIn("steamid", ids)
|
||||
query:Callback(function(result)
|
||||
if (!result or #result == 0) then
|
||||
local alt_id = os.time() + ALT_OFFSET
|
||||
ALT_OFFSET = ALT_OFFSET + 1
|
||||
|
||||
local text = table.concat(ids,"+")..": "..info.." (alt-id: "..alt_id..")"
|
||||
self:InsertNewAlt(ids[1], alt_id, type, text)
|
||||
self:InsertNewAlt(steamid, alt_id, type, text)
|
||||
|
||||
data.newAltFound = true
|
||||
elseif (#result == 1) then
|
||||
self:InsertNewAlt(
|
||||
result[1].steamid == steamid and ids[1] or steamid,
|
||||
result[1].alt_id,
|
||||
type,
|
||||
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
|
||||
)
|
||||
|
||||
data.newAltFound = true
|
||||
elseif (result[2].alt_id != result[1].alt_id) then
|
||||
self:MergeAlts(
|
||||
result[2].alt_id,
|
||||
result[1].alt_id,
|
||||
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
|
||||
)
|
||||
|
||||
data.newAltFound = true
|
||||
end
|
||||
|
||||
data.altLogging = data.altLogging - 1
|
||||
self:AltFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:DiscordAlert(client)
|
||||
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
|
||||
|
||||
local data = client.ixAltData
|
||||
if (!data.newAltFound) then return end
|
||||
|
||||
local tbl = {
|
||||
embeds = {{
|
||||
title = "Alt found for "..client:SteamName(),
|
||||
description = "An alt account match was found for **"..client:SteamName().."** (*"..client:SteamID64().."*)\n\n__COOKIE__: 99.99% certainty via installed cookie\nShown as 'SteamID64 (SteamName)'\n__TIMESTAMP__: check via installation date/time or absense of mounted games (hl2,ep2,css,csgo,gmod)\nMore matches = more certainty, especially if all/most are installed\nShown as 'SteamID64 (SteamName; date/time matches - installed matches)'\n__IP__: users that connected from the same IP address at any point\nShown as 'SteamID64'",
|
||||
color = 13632027,
|
||||
timestamp = os.date("%Y-%m-%d %X%z"),
|
||||
footer = {
|
||||
text = "Bastion Alt Checker for GMod by Gr4Ss"
|
||||
},
|
||||
author = {
|
||||
name = "Bastion Alt Checker"
|
||||
},
|
||||
fields = {
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
if (data.discordAlert.cookies and #data.discordAlert.cookies > 0) then
|
||||
table.insert(tbl.embeds[1].fields, {name = "COOKIE", value = table.concat(data.discordAlert.cookies, "\n")})
|
||||
end
|
||||
if (data.discordAlert.timestamps and #data.discordAlert.timestamps > 0) then
|
||||
table.insert(tbl.embeds[1].fields, {name = "TIMESTAMP", value = table.concat(data.discordAlert.timestamps, "\n")})
|
||||
end
|
||||
if (data.discordAlert.ips and #data.discordAlert.ips > 0) then
|
||||
table.insert(tbl.embeds[1].fields, {
|
||||
name = "IP",
|
||||
value = string.format("Address: [%s](https://proxycheck.io/threats/%s)\n", data.ipAddress, data.ipAddress)..
|
||||
table.concat(data.discordAlert.ips, "\n")
|
||||
})
|
||||
end
|
||||
local ipLinks = {"["..client:SteamID64().."](https://steamidfinder.com/lookup/"..client:SteamID64().."/)"}
|
||||
for k in pairs(data.discordAlert.altIDs) do
|
||||
ipLinks[#ipLinks + 1] = "["..k.."](https://steamidfinder.com/lookup/"..k.."/)"
|
||||
end
|
||||
table.insert(tbl.embeds[1].fields, {
|
||||
name = "SteamID Finder Links",
|
||||
value = table.concat(ipLinks,"\n")
|
||||
})
|
||||
|
||||
for _, field in ipairs(tbl.embeds[1].fields) do
|
||||
if (string.len(field.value) > 1024) then
|
||||
field.value = string.sub(field.value, 1, 1024)
|
||||
end
|
||||
end
|
||||
|
||||
local request = {
|
||||
failed = function(error) print("discord error", error) end,
|
||||
success = function(code, body, headers)
|
||||
if (code != 200) then print("discord error", code, body) end
|
||||
end,
|
||||
method = "post",
|
||||
url = self.DISCORD_WEBHOOK_ALTS,
|
||||
body = util.TableToJSON(tbl),
|
||||
type = "application/json; charset=utf-8"
|
||||
}
|
||||
|
||||
CHTTP(request)
|
||||
end
|
||||
|
||||
function PLUGIN:ProxyAlert(client)
|
||||
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
|
||||
|
||||
local ip, steamID = client.ixAltData.ipAddress, client:SteamID64()
|
||||
local tbl = {
|
||||
embeds = {{
|
||||
title = "New player using VPN - "..client:SteamName(),
|
||||
description = client:SteamName().." joined WN for the first time, but was using a proxy/VPN. They have been kicked.\n\n"..
|
||||
string.format("More info: [%s](https://proxycheck.io/threats/%s) & [%s](https://steamidfinder.com/lookup/%s/)", ip, ip, steamID, steamID),
|
||||
color = 16312092,
|
||||
timestamp = os.date("%Y-%m-%d %X%z"),
|
||||
footer = {
|
||||
text = "Bastion Alt Checker for GMod by Gr4Ss"
|
||||
},
|
||||
author = {
|
||||
name = "Bastion Alt Checker"
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
local request = {
|
||||
failed = function(error) print("discord error", error) end,
|
||||
method = "post",
|
||||
url = self.DISCORD_WEBHOOK_ALTS,
|
||||
body = util.TableToJSON(tbl),
|
||||
type = "application/json; charset=utf-8"
|
||||
}
|
||||
|
||||
CHTTP(request)
|
||||
end
|
||||
|
||||
function PLUGIN:NotifyProxyJoin(client)
|
||||
local bSend = false
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (CAMI.PlayerHasAccess(v, "Helix - Proxy Notify")) then
|
||||
bSend = true
|
||||
v:NotifyLocalized("bastionProxyNotify", client:SteamName())
|
||||
end
|
||||
end
|
||||
|
||||
return bSend
|
||||
end
|
||||
461
gamemodes/helix/plugins/bastion/modules/sv_antialt_db.lua
Normal file
461
gamemodes/helix/plugins/bastion/modules/sv_antialt_db.lua
Normal file
@@ -0,0 +1,461 @@
|
||||
--[[
|
||||
| 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 IsValid = IsValid
|
||||
local string = string
|
||||
local os = os
|
||||
local print = print
|
||||
local util = util
|
||||
local CHTTP = CHTTP
|
||||
local ipairs = ipairs
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local netstream = netstream
|
||||
local SortedPairsByMemberValue = SortedPairsByMemberValue
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
local MAX_CACHE_AGE = 7 * 24 * 3600 -- 7 days
|
||||
PLUGIN.ipCache = {}
|
||||
|
||||
hook.Add("DatabaseConnected", "bastionAntiAlt", function()
|
||||
local query = mysql:Create("bastion_antialt_users")
|
||||
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("steam_name", "VARCHAR(128) NOT NULL")
|
||||
query:Create("cookie", "VARCHAR(64) NOT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Create("bastion_antialt_cookies")
|
||||
query:Create("cookie", "VARCHAR(64) NOT NULL")
|
||||
query:Create("ts_hl2", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:Create("ts_ep2", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:Create("ts_css", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:Create("ts_csgo", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:Create("ts_gmod", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:PrimaryKey("cookie")
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Create("bastion_antialt_userips")
|
||||
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("ip", "VARCHAR(15) NOT NULL")
|
||||
query:Create("last_seen", "INT(11) UNSIGNED NOT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Create("bastion_antialt_ip")
|
||||
query:Create("ip", "VARCHAR(15) NOT NULL")
|
||||
query:Create("updated", "INT(11) UNSIGNED NOT NULL")
|
||||
query:Create("asn", "VARCHAR(12) NOT NULL")
|
||||
query:Create("provider", "VARCHAR(128) NOT NULL")
|
||||
query:Create("isocode", "VARCHAR(2) NOT NULL")
|
||||
query:Create("proxy", "TINYINT(1) UNSIGNED DEFAULT 0")
|
||||
query:Create("type", "VARCHAR(32) NOT NULL")
|
||||
query:Create("risk", "INT UNSIGNED NOT NULL")
|
||||
query:Create("attack_count", "INT UNSIGNED DEFAULT NULL")
|
||||
query:Create("attack_history", "TEXT DEFAULT NULL")
|
||||
query:PrimaryKey("ip")
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Create("bastion_antialt_alts")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("alt_id", "INT UNSIGNED NOT NULL")
|
||||
query:Create("type", "VARCHAR(10) NOT NULL")
|
||||
query:Create("info", "TEXT NOT NULL")
|
||||
query:PrimaryKey("steamid")
|
||||
query:Execute()
|
||||
end)
|
||||
|
||||
function PLUGIN:AltLoadUserData(client)
|
||||
local query = mysql:Select("bastion_antialt_users")
|
||||
query:Where("steamid", client:SteamID64())
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
if (!result or #result == 0) then
|
||||
client.ixAltData.mode = self.TYPE_UNKNOWN
|
||||
else
|
||||
client.ixAltData.mode = self.TYPE_KNOWN
|
||||
client.ixAltData.cookies = result
|
||||
end
|
||||
|
||||
self:AltPreFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN.GetIPAddress(client)
|
||||
local ip = client:IPAddress()
|
||||
|
||||
return string.gsub(ip, ":%d+$", "", 1)
|
||||
end
|
||||
|
||||
function PLUGIN:AltLookupIP(client)
|
||||
local ip = self.GetIPAddress(client)
|
||||
|
||||
client.ixAltData.ipAddress = ip
|
||||
|
||||
if (self.ipCache[ip]) then
|
||||
-- ip address still in cache
|
||||
client.ixAltData.ip = self.ipCache[ip]
|
||||
self:AltPreFinalChecking(client)
|
||||
else
|
||||
-- lookup address
|
||||
local query = mysql:Select("bastion_antialt_ip")
|
||||
query:Where("ip", ip)
|
||||
query:Callback(function(result)
|
||||
if (!result or #result == 0 or (result[1].updated < (os.time() - MAX_CACHE_AGE))) then
|
||||
self:AltFetchIP(client, ip, !result or #result == 0)
|
||||
else
|
||||
-- load in data from the DB
|
||||
if (result[1].proxy == 1) then
|
||||
result[1].proxy = "yes"
|
||||
else
|
||||
result[1].proxy = "no"
|
||||
end
|
||||
client.ixAltData.ip = result[1]
|
||||
self:AltPreFinalChecking(client)
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:AltFetchIP(client, ip, bCreateNew)
|
||||
-- address not found or record too old, look it up
|
||||
local url = string.format("https://proxycheck.io/v2/%s?key=%s&vpn=1&asn=1&risk=1", ip, self.API_KEY)
|
||||
local request = {
|
||||
failed = function(error)
|
||||
-- error stop matching
|
||||
print("[BASTION] Alt check IP API call failed with error: "..error)
|
||||
print("[BASTION] Client: "..client:SteamName().."; ip: "..ip)
|
||||
client.ixAltData.error = true
|
||||
end,
|
||||
success = function(code, body, headers)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
local httpResult = util.JSONToTable(body)
|
||||
if (!httpResult) then
|
||||
-- error stop matching
|
||||
print("[BASTION] Alt check IP API call failed to parse httpResult.")
|
||||
client.ixAltData.error = true
|
||||
return
|
||||
end
|
||||
|
||||
local status = httpResult.status
|
||||
if (status == "denied" or status == "error" or status == "warning") then
|
||||
print("[BASTION] Alt check IP API call failed. Status: "..status.."; Message: "..(httpResult.message or "no message").."\n")
|
||||
if (status != "warning") then
|
||||
-- error stop matching
|
||||
client.ixAltData.error = true
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- we got the data
|
||||
local lookup = httpResult[ip]
|
||||
self:StoreIPLookup(ip, lookup, bCreateNew)
|
||||
|
||||
client.ixAltData.ip = lookup
|
||||
self:AltPreFinalChecking(client)
|
||||
end,
|
||||
url = url,
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
CHTTP(request)
|
||||
end
|
||||
|
||||
function PLUGIN:AltLookupCookieMatches(client, data)
|
||||
local query = mysql:Select("bastion_antialt_users")
|
||||
query:Where("cookie", data.localCookie)
|
||||
query:WhereNotEqual("steamid", client:SteamID64())
|
||||
query:Callback(function(result)
|
||||
-- we found other cookies
|
||||
if (result and #result > 0) then
|
||||
for k, v in ipairs(result) do
|
||||
data.otherCookies[k] = {v.steam_name, v.steamid}
|
||||
end
|
||||
end
|
||||
|
||||
self:AltPreFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:AltLookupIPMatches(client)
|
||||
local query = mysql:Select("bastion_antialt_userips")
|
||||
query:Where("ip", PLUGIN.GetIPAddress(client))
|
||||
query:WhereNotEqual("steamid", client:SteamID64())
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
if (result and #result > 0) then
|
||||
for k, v in ipairs(result) do
|
||||
client.ixAltData.otherIPs[k] = {v.ip, v.steamid}
|
||||
end
|
||||
end
|
||||
|
||||
self:AltPreFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:AltLookupTimestampMatches(client, data, game, timestamp)
|
||||
local query = mysql:Select("bastion_antialt_cookies")
|
||||
query:Where("ts_"..game, timestamp)
|
||||
if (data.localCookie != "") then
|
||||
-- not interested in timestamps that we know for this client
|
||||
query:WhereNotEqual("cookie", data.localCookie)
|
||||
end
|
||||
query:Callback(function(result)
|
||||
if (result and #result > 0) then
|
||||
for _, v in ipairs(result) do
|
||||
data.otherTimestamps[v.cookie] = data.otherTimestamps[v.cookie] or {}
|
||||
table.insert(data.otherTimestamps[v.cookie], game)
|
||||
-- track timestamp matches
|
||||
-- non-zero timestamps are worth a lot more, so tracked separately too
|
||||
if (timestamp != 0) then
|
||||
data.otherTimestampsNZ[v.cookie] = data.otherTimestampsNZ[v.cookie] or {}
|
||||
data.otherTimestampsNZ[v.cookie][game] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:AltPreFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:AggregateTimestampMatches(data)
|
||||
data.otherTimeMatches = {}
|
||||
for cookie, matches in pairs(data.otherTimestamps) do
|
||||
if (#matches == 1) then continue end
|
||||
|
||||
data.otherTimeMatches[#data.otherTimeMatches + 1] = {cookie, #matches}
|
||||
end
|
||||
table.SortByMember(data.otherTimeMatches, 2, false)
|
||||
end
|
||||
|
||||
function PLUGIN:AltLookupCookieForTimestamps(client, data, maxMatches)
|
||||
local cookies = {}
|
||||
for cookie, matches in pairs(data.otherTimestamps) do
|
||||
-- only find cookies if at least 2 matches of which one is non-zero
|
||||
if (#matches >= 2 and data.otherTimestampsNZ[cookie]) then
|
||||
cookies[#cookies + 1] = cookie
|
||||
end
|
||||
end
|
||||
|
||||
if (#cookies == 0) then
|
||||
data.checkComplete = true
|
||||
self:AltFinalChecking(client)
|
||||
return
|
||||
end
|
||||
|
||||
local query = mysql:Select("bastion_antialt_users")
|
||||
query:WhereNotEqual("steamid", client:SteamID64())
|
||||
query:WhereIn("cookie", cookies)
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
if (result and #result > 0) then
|
||||
for _, v in ipairs(result) do
|
||||
local installed = table.GetKeys(data.otherTimestampsNZ[v.cookie])
|
||||
local text = string.format("%d/%d matches - installed: %s)",
|
||||
#data.otherTimestamps[v.cookie],
|
||||
maxMatches,
|
||||
table.concat(installed, "+")
|
||||
)
|
||||
self:AltFound(client, v.steamid, "timestamp", text)
|
||||
|
||||
data.discordAlert.timestamps[# data.discordAlert.timestamps + 1] = v.steamid.." ("..v.steam_name.."; "..text..")"
|
||||
data.discordAlert.altIDs[v.steamid] = true
|
||||
end
|
||||
end
|
||||
|
||||
data.checkComplete = true
|
||||
self:AltFinalChecking(client)
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:StoreCookieInfo(data, fileChecks)
|
||||
local query = mysql:Select("bastion_antialt_cookies")
|
||||
query:Where("cookie", data.localCookie)
|
||||
query:Callback(function(result)
|
||||
if (!result or #result == 0) then
|
||||
local queryInsert = mysql:Insert("bastion_antialt_cookies")
|
||||
queryInsert:Insert("cookie", data.localCookie)
|
||||
for k, v in ipairs(fileChecks) do
|
||||
queryInsert:Insert("ts_"..v[2], data.timestamps[k])
|
||||
end
|
||||
queryInsert:Execute()
|
||||
else
|
||||
local queryUpdate = mysql:Update("bastion_antialt_cookies")
|
||||
queryUpdate:Where("cookie", data.localCookie)
|
||||
for k, v in ipairs(fileChecks) do
|
||||
queryUpdate:Update("ts_"..v[2], data.timestamps[k])
|
||||
end
|
||||
queryUpdate:Execute()
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:StoreClientCookie(client, data)
|
||||
local query = mysql:Select("bastion_antialt_users")
|
||||
query:Where("cookie", data.localCookie)
|
||||
query:Where("steamid", client:SteamID64())
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
if (!result or #result == 0) then
|
||||
local queryInsert = mysql:Insert("bastion_antialt_users")
|
||||
queryInsert:Insert("steamid", client:SteamID64())
|
||||
queryInsert:Insert("steam_name", client:SteamName())
|
||||
queryInsert:Insert("cookie", data.localCookie)
|
||||
queryInsert:Execute()
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:StoreIPLookup(ip, httpResult, bNewEntry)
|
||||
if (!bNewEntry) then
|
||||
local query = mysql:Update("bastion_antialt_ip")
|
||||
query:Where("ip", ip)
|
||||
query:Update("updated", os.time())
|
||||
query:Update("asn", httpResult.asn)
|
||||
query:Update("provider", httpResult.provider)
|
||||
query:Update("isocode", httpResult.isocode)
|
||||
query:Update("proxy", httpResult.proxy == "yes" and 1 or 0)
|
||||
query:Update("type", httpResult.type)
|
||||
query:Update("risk", httpResult.risk or 0)
|
||||
if (httpResult["attack history"]) then
|
||||
query:Update("attack_count", httpResult["attack history"].Total)
|
||||
query:Update("attack_history", util.TableToJSON(httpResult["attack history"]))
|
||||
end
|
||||
query:Execute()
|
||||
else
|
||||
local query = mysql:Insert("bastion_antialt_ip")
|
||||
query:Insert("ip", ip)
|
||||
query:Insert("updated", os.time())
|
||||
query:Insert("asn", httpResult.asn)
|
||||
query:Insert("provider", httpResult.provider)
|
||||
query:Insert("isocode", httpResult.isocode)
|
||||
query:Insert("proxy", httpResult.proxy == "yes" and 1 or 0)
|
||||
query:Insert("type", httpResult.type)
|
||||
query:Insert("risk", httpResult.risk or 0)
|
||||
if (httpResult["attack history"]) then
|
||||
query:Insert("attack_count", httpResult["attack history"].Total)
|
||||
query:Insert("attack_history", util.TableToJSON(httpResult["attack history"]))
|
||||
end
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InsertNewAlt(steamid, alt_id, type, text)
|
||||
local query = mysql:Insert("bastion_antialt_alts")
|
||||
query:Insert("steamid", steamid)
|
||||
query:Insert("alt_id", alt_id)
|
||||
query:Insert("type", type)
|
||||
query:Insert("info", text)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:MergeAlts(old_id, new_id, text)
|
||||
local query = mysql:Select("bastion_antialt_alts")
|
||||
query:Where("alt_id", old_id)
|
||||
query:Callback(function(result2)
|
||||
for _, v in ipairs(result2) do
|
||||
local queryUpdate = mysql:Update("bastion_antialt_alts")
|
||||
queryUpdate:Where("steamid", v.steamid)
|
||||
queryUpdate:Update("alt_id", new_id)
|
||||
queryUpdate:Update("info", v.info.." - "..text)
|
||||
queryUpdate:Execute()
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:LookupSteamID(client, steamid)
|
||||
if (string.find(steamid, "^STEAM_")) then
|
||||
steamid = util.SteamIDTo64()
|
||||
end
|
||||
|
||||
local query = mysql:Select("bastion_antialt_alts")
|
||||
query:Where("steamid", steamid)
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
if (!result or #result == 0) then
|
||||
client:NotifyLocalized("bastionNoRecordFound", steamid)
|
||||
return
|
||||
end
|
||||
|
||||
local querySelect = mysql:Select("bastion_antialt_alts")
|
||||
querySelect:Where("alt_id", result[1].alt_id)
|
||||
querySelect:Callback(function(finalResult)
|
||||
if (!IsValid(client)) then return end
|
||||
|
||||
local toReturn = {"Alts for "..steamid..":", "(SteamID64) - (detection trigger type) - (info)"}
|
||||
for _, v in ipairs(finalResult) do
|
||||
toReturn[#toReturn + 1] = v.steamid.." - "..v.type.." - info: "..v.info
|
||||
end
|
||||
|
||||
netstream.Start(client, "PrintInfoList", toReturn)
|
||||
client:NotifyLocalized("bastionResultsPrinted")
|
||||
end)
|
||||
querySelect:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:LookupIPUsers(client, steamid)
|
||||
if (string.find(steamid, "^STEAM_")) then
|
||||
steamid = util.SteamIDTo64(steamid)
|
||||
end
|
||||
|
||||
local query = mysql:query("bastion_antialt_userips")
|
||||
query:Where("steamid", steamid)
|
||||
query:Callback(function(result)
|
||||
if (!IsValid(client)) then return end
|
||||
if (!result or #result == 0) then
|
||||
client:NotifyLocalized("bastionNoRecordFound", steamid)
|
||||
return
|
||||
end
|
||||
|
||||
local list = {}
|
||||
for k, v in ipairs(result) do
|
||||
list[k] = v.ip
|
||||
end
|
||||
|
||||
local querySelect = mysql:query("bastion_antialt_userips")
|
||||
querySelect:WhereIn("ip", list)
|
||||
querySelect:WhereNotEqual("steamid", steamid)
|
||||
querySelect:Callback(function(finalResult)
|
||||
if (!IsValid(client)) then return end
|
||||
if (!result or #result == 0) then
|
||||
client:NotifyLocalized("bastionNoRecordFound", steamid)
|
||||
return
|
||||
end
|
||||
|
||||
local toReturn = {"Other users on same IP as "..steamid, "(SteamID64) - (ip) - (last seen on this ip)"}
|
||||
for _, v in SortedPairsByMemberValue(finalResult, "steamid") do
|
||||
toReturn[#toReturn + 1] = v.steamid.." - "..v.ip.." - "..os.date("%Y-%m-%d", v.last_seen)
|
||||
end
|
||||
netstream.Start(client, "PrintInfoList", toReturn)
|
||||
client:NotifyLocalized("bastionResultsPrinted")
|
||||
end)
|
||||
querySelect:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
96
gamemodes/helix/plugins/bastion/modules/sv_banlist.lua
Normal file
96
gamemodes/helix/plugins/bastion/modules/sv_banlist.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
--[[
|
||||
| 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 hook = hook
|
||||
local ix = ix
|
||||
local util = util
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.permaBan = {
|
||||
-- Crazyman (~Atle/Gr4Ss), code stealing
|
||||
["STEAM_0:1:120921609"] = true,
|
||||
["2.73.226.221"] = true,
|
||||
["2.72.176.41"] = true,
|
||||
["2.132.96.235"] = true,
|
||||
["2.132.103.122"] = true,
|
||||
["2.132.147.172"] = true,
|
||||
["2.132.150.162"] = true,
|
||||
["95.57.132.81"] = true,
|
||||
-- carlsmei (~Atle/Gr4Ss), code stealing
|
||||
["STEAM_0:0:216726444"] = true,
|
||||
-- Schwarz Kruppzo (~Atle/Gr4Ss), code stealing
|
||||
["STEAM_0:1:44629398"] = true,
|
||||
-- KoC (~Atle/Gr4Ss), toxic, ban evasion
|
||||
["76561198017957016"] = true,
|
||||
["76561199123547331"] = true,
|
||||
["73.121.218.83"] = true,
|
||||
["136.144.43.116"] = true,
|
||||
["136.144.43.63"] = true,
|
||||
-- Kalingi (Staff vote, Hiros/Gr4Ss), toxic, threatening hacks & blackmail
|
||||
["76561198066620287"] = true,
|
||||
["STEAM_0:1:53177279"] = true,
|
||||
["24.197.171.2"] = true,
|
||||
-- Brando (~Atle/Gr4Ss), pedo
|
||||
["STEAM_0:1:54660756"] = true,
|
||||
-- Walter (~Atle/Gr4Ss), none
|
||||
["STEAM_0:1:43085888"] = true,
|
||||
-- PrplSckz/The Enemy (~Rad/Gr4Ss), forum DDoS
|
||||
["STEAM_0:1:68538156"] = true,
|
||||
-- Hackers (~Gr4Ss)
|
||||
["STEAM_0:1:13809165"] = true,
|
||||
["STEAM_0:1:4916602"] = true,
|
||||
["STEAM_0:1:517232907"] = true,
|
||||
["STEAM_0:1:17046093"] = true,
|
||||
-- Exploiters
|
||||
["STEAM_0:0:549109050"] = true,
|
||||
["76561199131288084"] = true,
|
||||
["76561199087140341"] = true,
|
||||
["76561199206105794"] = true,
|
||||
["76561198874018211"] = true,
|
||||
["109.252.109.68"] = true,
|
||||
["76561199121843993"] = true,
|
||||
-- Zeroday / Newport Gaming - Sketchy dude + some hacking & exploiting (~M!NT/RAD)
|
||||
["172.82.32.147"] = true,
|
||||
["76561199441966033"] = true,
|
||||
-- Cazo
|
||||
["82.0.106.136"] = true,
|
||||
["76561199150421701"] = true,
|
||||
-- lqut (Translating ISIS Propaganda) (~M!NT/RAD)
|
||||
["STEAM_0:0:173208852"] = true,
|
||||
["5.30.219.71"] = true,
|
||||
["76561198306683432"] = true,
|
||||
-- madbluntz (doxxing, minging, ddosing, etc etc) (~M!NT)
|
||||
["176.117.229.107"] = true,
|
||||
["176.117.229.107"] = true,
|
||||
["178.214.250.178"] = true,
|
||||
["46.191.232.69"] = true,
|
||||
["178.168.94.11"] = true,
|
||||
["163.182.82.195"] = true,
|
||||
["104.231.185.125"] = true,
|
||||
["STEAM_0:0:763201893"] = true,
|
||||
["STEAM_0:0:629741416"] = true,
|
||||
["STEAM_0:1:764405213"] = true,
|
||||
["STEAM_0:1:817531224"] = true,
|
||||
["STEAM_0:0:785033797"] = true,
|
||||
["STEAM_0:1:421783352"] = true,
|
||||
["STEAM_0:1:78544439"] = true,
|
||||
["STEAM_0:1:178702634"] = true,
|
||||
["STEAM_0:0:627119036"] = true,
|
||||
["STEAM_0:0:585787645"] = true,
|
||||
["STEAM_0:1:43085888"] = true,
|
||||
}
|
||||
|
||||
hook.Add("CheckPassword", "bastionBanList", function(steamid, networkid)
|
||||
if (PLUGIN.permaBan[steamid] or PLUGIN.permaBan[util.SteamIDFrom64(steamid)] or PLUGIN.permaBan[networkid]) then
|
||||
ix.log.AddRaw("[BANS] "..steamid.." ("..networkid..") tried to connect but is hard-banned.")
|
||||
return false
|
||||
end
|
||||
end)
|
||||
211
gamemodes/helix/plugins/bastion/modules/sv_netmonitor.lua
Normal file
211
gamemodes/helix/plugins/bastion/modules/sv_netmonitor.lua
Normal file
@@ -0,0 +1,211 @@
|
||||
--[[
|
||||
| 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 hook_Add = hook.Add
|
||||
local timer_Create = timer.Create
|
||||
local os_time = os.time
|
||||
local pairs = pairs
|
||||
local net_ReadHeader = net.ReadHeader
|
||||
local util_NetworkIDToString = util.NetworkIDToString
|
||||
local ix = ix
|
||||
local IsValid = IsValid
|
||||
local string_len = string.len
|
||||
local string_sub = string.sub
|
||||
|
||||
-- Code inspired by:
|
||||
-- Gmod Net Library Debug
|
||||
-- https://github.com/HexaneNetworks/gmod-netlibrary-debug
|
||||
-- v2.3
|
||||
-- October 2020
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
hook_Add("DatabaseConnected", "bastionNetDB", function()
|
||||
local query = mysql:Create("bastion_netlog")
|
||||
query:Create("id", "BIGINT UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("name", "VARCHAR(50) NOT NULL")
|
||||
query:Create("length", "INT UNSIGNED NOT NULL")
|
||||
query:Create("count", "INT UNSIGNED NOT NULL")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("args", "VARCHAR(200) DEFAULT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Create("bastion_netspam")
|
||||
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("name", "VARCHAR(50) NOT NULL")
|
||||
query:Create("type", "BOOL NOT NULL")
|
||||
query:Create("count", "INT UNSIGNED NOT NULL")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("steam_name", "VARCHAR(50) NOT NULL")
|
||||
query:Create("ip", "VARCHAR(25) NOT NULL")
|
||||
query:Create("time", "INT NOT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Execute()
|
||||
end)
|
||||
|
||||
local netSpamCount = {}
|
||||
local netFlagged = {}
|
||||
|
||||
local comSpamCount = {}
|
||||
local comFlagged = {}
|
||||
|
||||
local threshold = 20
|
||||
|
||||
timer_Create("ixBastionNetSpam.Clear", 1.1, 0, function()
|
||||
local time = os_time()
|
||||
for steamID, data in pairs(netFlagged) do
|
||||
for name in pairs(data.names) do
|
||||
local query = mysql:Insert("bastion_netspam")
|
||||
query:Insert("name", name)
|
||||
query:Insert("type", 0)
|
||||
query:Insert("count", netSpamCount[steamID][name] or 0)
|
||||
query:Insert("steamid", steamID)
|
||||
query:Insert("steam_name", data.steamName)
|
||||
query:Insert("ip", data.ip)
|
||||
query:Insert("time", time)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
netSpamCount = {}
|
||||
netFlagged = {}
|
||||
|
||||
for steamID, data in pairs(comFlagged) do
|
||||
for name in pairs(data.commands) do
|
||||
local query = mysql:Insert("bastion_netspam")
|
||||
query:Insert("name", name)
|
||||
query:Insert("type", 1)
|
||||
query:Insert("count", comSpamCount[steamID][name] or 0)
|
||||
query:Insert("steamid", steamID)
|
||||
query:Insert("steam_name", data.steamName)
|
||||
query:Insert("ip", data.ip)
|
||||
query:Insert("time", time)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
comSpamCount = {}
|
||||
comFlagged = {}
|
||||
end)
|
||||
|
||||
|
||||
local netIgnoreList = {
|
||||
["76561198002319953"] = 2
|
||||
}
|
||||
|
||||
local netSpamAmount = {
|
||||
["NetStreamDS"] = 20,
|
||||
["76561198002319953"] = 30
|
||||
}
|
||||
|
||||
function net.Incoming(len, client)
|
||||
local i = net_ReadHeader()
|
||||
local name = util_NetworkIDToString(i)
|
||||
if (!name) then return end
|
||||
|
||||
local func = net.Receivers[name:lower()]
|
||||
if (!func) then return end
|
||||
|
||||
--
|
||||
-- len includes the 16 bit int which told us the message name
|
||||
--
|
||||
len = len - 16
|
||||
|
||||
if (ix.config.Get("netAntiSpam")) then
|
||||
local steamID = IsValid(client) and client:SteamID64() or "UNKNOWN"
|
||||
netSpamCount[steamID] = netSpamCount[steamID] or {}
|
||||
netSpamCount[steamID][name] = (netSpamCount[steamID][name] or 0) + 1
|
||||
if (netSpamCount[steamID][name] > (netSpamAmount[name] or threshold)) then
|
||||
if (!netFlagged[steamID]) then
|
||||
netFlagged[steamID] = {
|
||||
names = {},
|
||||
steamID = steamID,
|
||||
steamName = IsValid(client) and (client.SteamName and client:SteamName() or client:Name()) or "UNKNOWN PLAYER NAME",
|
||||
ip = IsValid(client) and client:IPAddress() or "UNKNOWN IP"
|
||||
}
|
||||
|
||||
if (!ix.config.Get("netLoggingEnabled")) then
|
||||
local query = mysql:Insert("bastion_netlog")
|
||||
query:Insert("name", name)
|
||||
query:Insert("length", len)
|
||||
query:Insert("count", netSpamCount[steamID][name])
|
||||
query:Insert("steamid", steamID)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
netFlagged[steamID].names[name] = true
|
||||
end
|
||||
|
||||
|
||||
|
||||
if (ix.config.Get("netLoggingEnabled") and (!netIgnoreList[name] or netIgnoreList[name] < netSpamCount[steamID][name])) then
|
||||
local query = mysql:Insert("bastion_netlog")
|
||||
query:Insert("name", name)
|
||||
query:Insert("length", len)
|
||||
query:Insert("count", netSpamCount[steamID][name])
|
||||
query:Insert("steamid", steamID)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
func(len, client)
|
||||
end
|
||||
|
||||
local conSpamAmount = {
|
||||
|
||||
}
|
||||
local conIgnoreList = {
|
||||
|
||||
}
|
||||
|
||||
PLUGIN.oldRun = PLUGIN.oldRun or concommand.Run
|
||||
function concommand.Run(client, command, args, argString)
|
||||
if (IsValid(client) and command and ix.config.Get("netAntiSpam")) then
|
||||
local steamID = IsValid(client) and client:SteamID64() or "UNKNOWN"
|
||||
comSpamCount[steamID] = comSpamCount[steamID] or {}
|
||||
comSpamCount[steamID][command] = (comSpamCount[steamID][command] or 0) + 1
|
||||
if (comSpamCount[steamID][command] > (conSpamAmount[command] or threshold)) then
|
||||
if (!comFlagged[steamID]) then
|
||||
comFlagged[steamID] = {
|
||||
commands = {},
|
||||
steamID = steamID,
|
||||
steamName = IsValid(client) and (client.SteamName and client:SteamName() or client:Name()) or "UNKNOWN PLAYER NAME",
|
||||
ip = IsValid(client) and client:IPAddress() or "UNKNOWN IP"
|
||||
}
|
||||
|
||||
if (!ix.config.Get("netLoggingEnabled")) then
|
||||
local query = mysql:Insert("bastion_netlog")
|
||||
query:Insert("name", command)
|
||||
query:Insert("length", #argString)
|
||||
query:Insert("count", netSpamCount[steamID][command])
|
||||
query:Insert("steamid", steamID)
|
||||
query:Insert("args", string_len(argString or "") > 200 and string_sub(argString, 1, 200) or argString or "")
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
comFlagged[steamID].commands[command] = true
|
||||
end
|
||||
|
||||
if (ix.config.Get("netLoggingEnabled") and (!conIgnoreList[command] or conIgnoreList[command] < comSpamCount[steamID][command])) then
|
||||
local query = mysql:Insert("bastion_netlog")
|
||||
query:Insert("name", command)
|
||||
query:Insert("length", #argString)
|
||||
query:Insert("count", comSpamCount[steamID][command])
|
||||
query:Insert("steamid", steamID)
|
||||
query:Insert("args", string_len(argString or "") > 200 and string_sub(argString, 1, 200) or argString or "")
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
return PLUGIN.oldRun(client, command, args, argString)
|
||||
end
|
||||
100
gamemodes/helix/plugins/bastion/modules/sv_netsizelog.lua
Normal file
100
gamemodes/helix/plugins/bastion/modules/sv_netsizelog.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
--Made by Liquid
|
||||
PLUGIN.MIN_PLAYER_COUNT = 30
|
||||
PLUGIN.STOP_AFTER_CONNECTED_FOR = 600
|
||||
PLUGIN.MIN_SIZE = 100
|
||||
PLUGIN.TRACE_SIZE = 1000
|
||||
|
||||
ORIGINAL_NET = ORIGINAL_NET or table.Copy(net)
|
||||
|
||||
local _net = ORIGINAL_NET
|
||||
local IsValid = IsValid
|
||||
|
||||
local currentMessageName
|
||||
hook.Add("DatabaseConnected", "bastionNetSizeLog", function()
|
||||
local query = mysql:Create("bastion_netsizelog")
|
||||
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("steamid", "VARCHAR(20) NOT NULL")
|
||||
query:Create("timestamp", "INT(11) UNSIGNED NOT NULL")
|
||||
query:Create("realtime", "FLOAT NOT NULL")
|
||||
query:Create("message_name", "VARCHAR(128) NOT NULL")
|
||||
query:Create("size", "INT(11) UNSIGNED NOT NULL")
|
||||
query:Create("stack_trace", "TEXT DEFAULT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Callback(function()
|
||||
local delete = mysql:Delete("bastion_netsizelog")
|
||||
delete:WhereLT("timestamp", os.time() - 3 * 24 * 3600)
|
||||
delete:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end)
|
||||
|
||||
local function netLog(players)
|
||||
local count = player.GetCount()
|
||||
if (count > 1 and count <= PLUGIN.MIN_PLAYER_COUNT) then return end
|
||||
|
||||
if (type(players) == "Player") then
|
||||
players = {players}
|
||||
elseif (type(players) == "CRecipientFilter") then
|
||||
players = players:GetPlayers()
|
||||
end
|
||||
|
||||
local size = _net.BytesWritten()
|
||||
if (size <= PLUGIN.MIN_SIZE) then return end
|
||||
|
||||
for k, v in ipairs(players) do
|
||||
if (!IsValid(v)) then continue end
|
||||
|
||||
if (v.ixStopNetLog or v:TimeConnected() > PLUGIN.STOP_AFTER_CONNECTED_FOR) then
|
||||
v.ixStopNetLog = true
|
||||
continue
|
||||
end
|
||||
|
||||
local query = mysql:Insert("bastion_netsizelog")
|
||||
query:Insert("steamid", v:SteamID64())
|
||||
query:Insert("timestamp", os.time())
|
||||
query:Insert("realtime", RealTime())
|
||||
query:Insert("message_name", currentMessageName)
|
||||
query:Insert("size", size)
|
||||
if (size >= PLUGIN.TRACE_SIZE or currentMessageName == "NetStreamDS") then
|
||||
query:Insert("stack_trace", debug.traceback())
|
||||
end
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
net.Start = function(name, unreliable)
|
||||
currentMessageName = name
|
||||
|
||||
return _net.Start(name, unreliable)
|
||||
end
|
||||
|
||||
net.Send = function(players)
|
||||
netLog(players)
|
||||
currentMessageName = nil
|
||||
return _net.Send(players)
|
||||
end
|
||||
|
||||
net.SendOmit = function(players)
|
||||
netLog(players)
|
||||
currentMessageName = nil
|
||||
return _net.SendOmit(players)
|
||||
end
|
||||
|
||||
net.Broadcast = function(pos)
|
||||
netLog(player.GetAll())
|
||||
currentMessageName = nil
|
||||
return _net.Broadcast()
|
||||
end
|
||||
201
gamemodes/helix/plugins/bastion/sh_classes.lua
Normal file
201
gamemodes/helix/plugins/bastion/sh_classes.lua
Normal file
@@ -0,0 +1,201 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local Color = Color
|
||||
local chat = chat
|
||||
local string = string
|
||||
local IsValid = IsValid
|
||||
local LocalPlayer = LocalPlayer
|
||||
local MsgC = MsgC
|
||||
local CAMI = CAMI
|
||||
local surface = surface
|
||||
local team = team
|
||||
|
||||
-- luacheck: globals FACTION_SERVERADMIN
|
||||
|
||||
|
||||
-- Roll information in chat.
|
||||
ix.chat.Register("gmroll", {
|
||||
format = "** You have gm-rolled %s out of %s.",
|
||||
color = Color(155, 111, 176),
|
||||
CanHear = function(self, speaker, listener)
|
||||
return speaker == listener
|
||||
end,
|
||||
deadCanChat = true,
|
||||
OnChatAdd = function(self, speaker, text, bAnonymous, data)
|
||||
chat.AddText(self.color, string.format(self.format, text, data.max or 20))
|
||||
end
|
||||
})
|
||||
|
||||
ix.chat.Register("localevent", {
|
||||
CanHear = (ix.config.Get("chatRange", 280) * 2),
|
||||
OnChatAdd = function(self, speaker, text)
|
||||
chat.AddText(Color(255, 150, 0), text)
|
||||
end,
|
||||
})
|
||||
|
||||
local broadcastIcon = ix.util.GetMaterial("willardnetworks/chat/broadcast_icon.png")
|
||||
|
||||
ix.chat.Register("localbroadcast", {
|
||||
CanHear = (ix.config.Get("chatRange", 280) * 2),
|
||||
CanSay = function(self, speaker, text)
|
||||
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
|
||||
speaker:NotifyLocalized("notAllowed")
|
||||
|
||||
return false
|
||||
end
|
||||
end,
|
||||
OnChatAdd = function(self, speaker, text)
|
||||
if (ix.option.Get("standardIconsEnabled")) then
|
||||
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
|
||||
else
|
||||
chat.AddText(Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.chat.Register("localbroadcastme", {
|
||||
CanHear = (ix.config.Get("chatRange", 280) * 2),
|
||||
CanSay = function(self, speaker, text)
|
||||
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
|
||||
speaker:NotifyLocalized("notAllowed")
|
||||
|
||||
return false
|
||||
end
|
||||
end,
|
||||
OnChatAdd = function(self, speaker, text)
|
||||
if (ix.option.Get("standardIconsEnabled")) then
|
||||
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
|
||||
else
|
||||
chat.AddText(Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.chat.Register("localbroadcastit", {
|
||||
CanHear = (ix.config.Get("chatRange", 280) * 2),
|
||||
CanSay = function(self, speaker, text)
|
||||
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
|
||||
speaker:NotifyLocalized("notAllowed")
|
||||
|
||||
return false
|
||||
end
|
||||
end,
|
||||
OnChatAdd = function(self, speaker, text)
|
||||
if (ix.option.Get("standardIconsEnabled")) then
|
||||
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("***' %s", text))
|
||||
else
|
||||
chat.AddText(Color(151, 161, 255), string.format("***' %s", text))
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.chat.Register("announcement", {
|
||||
OnChatAdd = function(self, speaker, text)
|
||||
chat.AddText(Color(254, 238, 60), "[ADMIN] ", text)
|
||||
end,
|
||||
CanSay = function(self, speaker, text)
|
||||
return true
|
||||
end
|
||||
})
|
||||
|
||||
-- STAFF CHAT
|
||||
do
|
||||
local CLASS = {}
|
||||
local icon = ix.util.GetMaterial("icon16/medal_gold_3.png")
|
||||
|
||||
if (CLIENT) then
|
||||
function CLASS:OnChatAdd(speaker, text, anonymous, data)
|
||||
if (!IsValid(speaker)) then return end
|
||||
|
||||
if (speaker != LocalPlayer() and !ix.option.Get("staffChat")) then
|
||||
local character = LocalPlayer():GetCharacter()
|
||||
if (character and character:GetFaction() != FACTION_SERVERADMIN) then
|
||||
MsgC(Color(255,215,0), "[Staff] ",
|
||||
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
|
||||
Color(255, 255, 255), text.."\n")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
chat.AddText(icon, Color(255,215,0), "[Staff] ",
|
||||
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
|
||||
Color(255, 255, 255), text)
|
||||
end
|
||||
else
|
||||
function CLASS:CanHear(speaker, listener)
|
||||
return CAMI.PlayerHasAccess(listener, "Helix - Hear Staff Chat")
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Register("staff_chat", CLASS)
|
||||
end
|
||||
|
||||
-- GM CHAT
|
||||
do
|
||||
local CLASS = {}
|
||||
local icon = ix.util.GetMaterial("icon16/rosette.png")
|
||||
|
||||
if (CLIENT) then
|
||||
function CLASS:OnChatAdd(speaker, text, anonymous, data)
|
||||
if (!IsValid(speaker)) then return end
|
||||
|
||||
chat.AddText(icon, Color(142, 28, 255), "[GM] ", Color(255, 215, 0, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
|
||||
end
|
||||
else
|
||||
function CLASS:CanHear(speaker, listener)
|
||||
return CAMI.PlayerHasAccess(listener, "Helix - Hear GM Chat")
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Register("gm_chat", CLASS)
|
||||
end
|
||||
|
||||
-- MENTOR CHAT
|
||||
do
|
||||
local CLASS = {}
|
||||
local icon = ix.util.GetMaterial("icon16/user_suit.png")
|
||||
|
||||
if (CLIENT) then
|
||||
function CLASS:OnChatAdd(speaker, text, anonymous, data)
|
||||
if (!IsValid(speaker)) then return end
|
||||
|
||||
chat.AddText(icon, Color(66, 135, 245), "[Mentor] ", Color(66, 245, 191, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
|
||||
end
|
||||
else
|
||||
function CLASS:CanHear(speaker, listener)
|
||||
return CAMI.PlayerHasAccess(listener, "Helix - Hear Mentor Chat")
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Register("mentor_chat", CLASS)
|
||||
end
|
||||
|
||||
-- ACHIEVEMENT
|
||||
do
|
||||
local CLASS = {}
|
||||
|
||||
if (CLIENT) then
|
||||
function CLASS:OnChatAdd(speaker, text, anonymous, data)
|
||||
if (!IsValid(data[1])) then return end
|
||||
|
||||
if (data[2]) then
|
||||
surface.PlaySound(data[2])
|
||||
end
|
||||
|
||||
local target = data[1]
|
||||
chat.AddText(team.GetColor(target:Team()), target:SteamName(), Color(255, 255, 255), " earned the achievement ",
|
||||
Color( 255, 201, 0, 255 ), text)
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Register("achievement_get", CLASS)
|
||||
end
|
||||
662
gamemodes/helix/plugins/bastion/sh_commands.lua
Normal file
662
gamemodes/helix/plugins/bastion/sh_commands.lua
Normal file
@@ -0,0 +1,662 @@
|
||||
--[[
|
||||
| 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 bit = bit
|
||||
local IsValid = IsValid
|
||||
local pairs = pairs
|
||||
local table = table
|
||||
local L = L
|
||||
local netstream = netstream
|
||||
local Entity = Entity
|
||||
local print = print
|
||||
local math = math
|
||||
local tostring = tostring
|
||||
local CAMI = CAMI
|
||||
local Vector = Vector
|
||||
local game = game
|
||||
local RunConsoleCommand = RunConsoleCommand
|
||||
local net = net
|
||||
local player = player
|
||||
local ipairs = ipairs
|
||||
local ents = ents
|
||||
local string = string
|
||||
local timer = timer
|
||||
local ix = ix
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
--Links
|
||||
ix.command.Add("Discord", {
|
||||
description = "Get a link to the Discord Server",
|
||||
privilege = "Basic Commands",
|
||||
OnRun = function(self, client)
|
||||
net.Start("ixOpenURL")
|
||||
net.WriteString(ix.config.Get("DiscordLink"))
|
||||
net.Send(client)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
ix.command.Add("Content", {
|
||||
description = "Get a link to theWorkshop Content Pack",
|
||||
privilege = "Basic Commands",
|
||||
OnRun = function(self, client)
|
||||
net.Start("ixOpenURL")
|
||||
net.WriteString(ix.config.Get("ContentLink"))
|
||||
net.Send(client)
|
||||
end
|
||||
})
|
||||
ix.command.Add("Forum", {
|
||||
description = "Get a link to the Forums",
|
||||
privilege = "Basic Commands",
|
||||
OnRun = function(self, client)
|
||||
net.Start("ixOpenURL")
|
||||
net.WriteString(ix.config.Get("ForumLink"))
|
||||
net.Send(client)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
--Basic Admin
|
||||
ix.command.Add("PGI", {
|
||||
description = "Get someone's basic information and copy their SteamID.",
|
||||
arguments = {
|
||||
bit.bor(ix.type.player, ix.type.optional)
|
||||
},
|
||||
--alias = "PlyGetInfo",
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, target)
|
||||
if (!target) then
|
||||
target = client:GetEyeTraceNoCursor().Entity
|
||||
end
|
||||
|
||||
if (!IsValid(target) or !target:IsPlayer()) then
|
||||
client:NotifyLocalized("bastionPGIInvalidTarget")
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("ixPlayerInfo")
|
||||
net.WriteEntity(target)
|
||||
net.Send(client)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("PrintStaffList", {
|
||||
alias = "PSL",
|
||||
description = "Print a list of staff members online.",
|
||||
OnRun = function(self, client)
|
||||
local staff = {}
|
||||
local bFound = false
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
local incognitoSetting = ix.option.Get(v, "iconIncognitoMode", false)
|
||||
local iconIncognitoMode = !client:IsAdmin() and !client:IsSuperAdmin() and incognitoSetting
|
||||
if (v:IsAdmin() or v:IsSuperAdmin()) and !iconIncognitoMode then
|
||||
local userGroup = v:GetUserGroup()
|
||||
staff[userGroup] = staff[userGroup] or {}
|
||||
staff[userGroup][#staff[userGroup] + 1] = v
|
||||
bFound = true
|
||||
end
|
||||
end
|
||||
|
||||
local toSend = {}
|
||||
for k, v in pairs(staff) do
|
||||
toSend[#toSend + 1] = {name = k, players = v}
|
||||
end
|
||||
table.SortByMember(toSend, "name", true)
|
||||
|
||||
if (bFound) then
|
||||
net.Start("ixStaffList")
|
||||
net.WriteUInt(#toSend, 8)
|
||||
for _, v in ipairs(toSend) do
|
||||
net.WriteString(v.name)
|
||||
net.WriteUInt(#v.players, 8)
|
||||
for i = 1, #v.players do
|
||||
net.WriteEntity(v.players[i])
|
||||
end
|
||||
end
|
||||
net.Send(client)
|
||||
else
|
||||
client:Notify("There are no staff online currently.")
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("PrintFactionList", {
|
||||
alias = "PFL",
|
||||
description = "Print a list of members of a faction currently online (including on another character).",
|
||||
arguments = {
|
||||
ix.type.string
|
||||
},
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, name)
|
||||
if (name == "") then
|
||||
return "@invalidArg", 2
|
||||
end
|
||||
|
||||
local faction = ix.faction.teams[name]
|
||||
|
||||
if (!faction) then
|
||||
for _, v in ipairs(ix.faction.indices) do
|
||||
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
|
||||
faction = v
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (faction) then
|
||||
local players = {}
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:HasWhitelist(faction.index)) then
|
||||
players[#players + 1] = v
|
||||
end
|
||||
end
|
||||
net.Start("ixStaffList")
|
||||
net.WriteUInt(1, 8)
|
||||
net.WriteString(faction.name)
|
||||
net.WriteUInt(#players, 8)
|
||||
for i = 1, #players do
|
||||
net.WriteEntity(players[i])
|
||||
end
|
||||
net.Send(client)
|
||||
else
|
||||
return "@invalidFaction"
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("PlaySound", {
|
||||
description = "Play a sound for all players (when no range is given) or those near you.",
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, sound, range)
|
||||
local targets = range and {} or player.GetAll()
|
||||
if (range) then
|
||||
range = range * range
|
||||
local clientPos = client:EyePos()
|
||||
for _, target in ipairs(player.GetAll()) do
|
||||
if (target:EyePos():DistToSqr(clientPos) < range) then
|
||||
targets[#targets + 1] = target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Start("ixPlaySound")
|
||||
net.WriteString(PLUGIN.soundAlias[sound] or sound)
|
||||
net.Send(targets)
|
||||
end,
|
||||
indicator = "chatPerforming"
|
||||
})
|
||||
|
||||
ix.command.Add("ScreenShake", {
|
||||
description = "Shakes the screen of everyone in the specified range. Specify the amplitude, the frequency and the radius for it to work.",
|
||||
arguments = {
|
||||
ix.type.number,
|
||||
ix.type.number,
|
||||
ix.type.number,
|
||||
ix.type.number
|
||||
},
|
||||
argumentNames = {"time(seconds)", "amplitude", "frequency", "radius"},
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, seconds, strength, frequency, radius)
|
||||
local vector = client:GetPos()
|
||||
util.ScreenShake(vector, strength or 5, frequency or 5, seconds, radius, true)
|
||||
end,
|
||||
indicator = "chatPerforming"
|
||||
})
|
||||
|
||||
ix.command.Add("PlaySoundGlobal", {
|
||||
description = "Play a sound for all players.",
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
},
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, sound)
|
||||
net.Start("ixPlaySound")
|
||||
net.WriteString(PLUGIN.soundAlias[sound] or sound)
|
||||
net.WriteBool(true)
|
||||
net.Send(player.GetAll())
|
||||
end,
|
||||
indicator = "chatPerforming"
|
||||
})
|
||||
|
||||
ix.command.Add("ShowEdicts", {
|
||||
description = "Returns the amount of networked entities currently on the server.",
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client)
|
||||
local edictsCount = ents.GetEdictCount()
|
||||
local edictsLeft = 8192 - edictsCount
|
||||
|
||||
return string.format("There are currently %s edicts on the server. You can have up to %s more.", edictsCount, edictsLeft)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("ShowEntsInRadius", {
|
||||
description = "Shows a list of entities within a given radius.",
|
||||
privilege = "Basic Admin Commands",
|
||||
arguments = {ix.type.number},
|
||||
OnRun = function(self, client, radius)
|
||||
local entities = {}
|
||||
local pos = client:GetPos()
|
||||
for _, v in pairs(ents.FindInSphere(pos, radius)) do
|
||||
if (!IsValid(v)) then continue end
|
||||
entities[#entities + 1] = table.concat({v:EntIndex(), v:GetClass(), v:GetModel() or "no model", v:GetPos():Distance(pos), v:MapCreationID()}, ", ")
|
||||
end
|
||||
netstream.Start(client, "ixShowEntsInRadius", table.concat(entities,"\n"))
|
||||
client:NotifyLocalized("entsPrintedInConsole")
|
||||
end,
|
||||
bNoIndicator = true,
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveEntityByID", {
|
||||
description = "Shows a list of entities within a given radius.",
|
||||
superAdminOnly = true,
|
||||
arguments = {ix.type.number},
|
||||
OnRun = function(self, client, number)
|
||||
local entity = Entity(number)
|
||||
if (IsValid(entity)) then
|
||||
client:NotifyLocalized("entityRemoved", number, entity:GetClass())
|
||||
entity:Remove()
|
||||
else
|
||||
client:NotifyLocalized("entityNotFound", number)
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true,
|
||||
})
|
||||
|
||||
if (CLIENT) then
|
||||
netstream.Hook("ixShowEntsInRadius", function(text)
|
||||
print(text)
|
||||
end)
|
||||
end
|
||||
|
||||
ix.command.Add("LocalEvent", {
|
||||
description = "@cmdLocalEvent",
|
||||
privilege = "Event",
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, event, range)
|
||||
local _range = range or (ix.config.Get("chatRange", 280) * 2)
|
||||
|
||||
ix.chat.classes.localevent.range = _range * _range
|
||||
|
||||
local doubleCommand = false
|
||||
|
||||
-- This could probably be done a bit smarter but I'm sure it'll do.
|
||||
if (string.Left(string.lower(event), 11) == "/localevent") then
|
||||
doubleCommand = true
|
||||
|
||||
event = string.Right(event, #event - 12)
|
||||
elseif (string.Left(string.lower(event), 10) == "localevent") then
|
||||
doubleCommand = true
|
||||
|
||||
event = string.Right(event, #event - 11)
|
||||
end
|
||||
|
||||
if (doubleCommand) then
|
||||
client:NotifyLocalized("textDoubleCommand", "/LocalEvent")
|
||||
end
|
||||
|
||||
ix.chat.Send(client, "localevent", event)
|
||||
end,
|
||||
indicator = "chatPerforming"
|
||||
})
|
||||
|
||||
ix.command.Add("RemovePersistedProps", {
|
||||
description = "@cmdRemovePersistedProps",
|
||||
superAdminOnly = true,
|
||||
arguments = {
|
||||
ix.type.number
|
||||
},
|
||||
OnRun = function(self, client, radius)
|
||||
if (radius < 0) then
|
||||
client:Notify("Radius must be a positive number!")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), radius)) do
|
||||
if (!entity:GetNetVar("Persistent")) then continue end
|
||||
|
||||
entity:Remove()
|
||||
end
|
||||
|
||||
client:Notify("Removed all persisted props in a radius of " .. radius .. " units.")
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("Announce", {
|
||||
description = "@cmdAnnounce",
|
||||
arguments = {
|
||||
ix.type.text
|
||||
},
|
||||
OnRun = function(self, client, event)
|
||||
ix.chat.Send(client, "announcement", event)
|
||||
end,
|
||||
OnCheckAccess = function(self, client)
|
||||
return !client or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands")
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
ix.command.Add("GmRoll", {
|
||||
description = "Do a roll that only you can see.",
|
||||
arguments = {
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client, maximum)
|
||||
maximum = math.Clamp(maximum or 20, 0, 1000000)
|
||||
|
||||
local value = math.random(0, maximum)
|
||||
|
||||
ix.chat.Send(client, "gmroll", tostring(value), nil, nil, {
|
||||
max = maximum
|
||||
})
|
||||
|
||||
ix.log.Add(client, "roll", value, maximum)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
-- Help
|
||||
ix.command.Add("Help", {
|
||||
description = "@cmdStaffHelp",
|
||||
arguments = ix.type.text,
|
||||
alias = {"GMHelp", "ModHelp"},
|
||||
OnRun = function(self, client, text)
|
||||
client:Say("@ " .. text)
|
||||
end,
|
||||
OnCheckAccess = function(self, client)
|
||||
return !CAMI.PlayerHasAccess(client, "Helix - Hear Staff Chat")
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
-- Admin Chat
|
||||
ix.command.Add("Staff", {
|
||||
description = "Chat with other staff members.",
|
||||
arguments = ix.type.text,
|
||||
privilege = "Hear Staff Chat",
|
||||
alias = {"Op", "Mod", "ModChat"},
|
||||
OnRun = function(self, client, text)
|
||||
ix.chat.Send(client, "staff_chat", text)
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
-- Gamemaster Chat
|
||||
ix.command.Add("GameMaster", {
|
||||
description = "Chat with other Gamemasters.",
|
||||
arguments = ix.type.text,
|
||||
privilege = "Hear GM Chat",
|
||||
alias = {"GMChat", "GM"},
|
||||
OnRun = function(self, client, text)
|
||||
ix.chat.Send(client, "gm_chat", text)
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
-- Mentor Chat
|
||||
ix.command.Add("Mentor", {
|
||||
description = "Chat with other Mentors.",
|
||||
arguments = ix.type.text,
|
||||
privilege = "Hear Mentor Chat",
|
||||
alias = {"MChat", "M"},
|
||||
OnRun = function(self, client, text)
|
||||
ix.chat.Send(client, "mentor_chat", text)
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
-- Fun Stuff
|
||||
ix.command.Add("Achievement", {
|
||||
description = "Someone has earned a special achievement!",
|
||||
arguments = {
|
||||
ix.type.player,
|
||||
ix.type.text
|
||||
},
|
||||
privilege = "Fun Stuff",
|
||||
OnRun = function(self, client, target, text)
|
||||
ix.chat.Send(client, "achievement_get", text, false, nil,
|
||||
{target, "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav"})
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
ix.command.Add("DarwinAward", {
|
||||
description = "Someone has earned an achievement: he has made the ultimate sacrifice to increase humanity's average IQ.",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
privilege = "Fun Stuff",
|
||||
OnRun = function(self, client, target)
|
||||
if (!target:Alive()) then
|
||||
local pos = target:GetPos()
|
||||
target:Spawn()
|
||||
|
||||
target:SetPos(pos)
|
||||
end
|
||||
target:SetMoveType(MOVETYPE_WALK)
|
||||
target:SetVelocity(Vector(0, 0, 4000))
|
||||
|
||||
timer.Simple(1, function() PLUGIN:Explode(target) end)
|
||||
ix.chat.Send(client, "achievement_get", "DARWIN AWARD", false, nil,
|
||||
{target, "ambient/alarms/razortrain_horn1.wav"})
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
ix.command.Add("PlyRocket", {
|
||||
description = "To infinity, and beyond!.",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
privilege = "Fun Stuff",
|
||||
OnRun = function(self, client, target)
|
||||
if (!target:Alive()) then
|
||||
local pos = target:GetPos()
|
||||
target:Spawn()
|
||||
|
||||
target:SetPos(pos)
|
||||
end
|
||||
target:SetMoveType(MOVETYPE_WALK)
|
||||
target:SetVelocity(Vector(0, 0, 4000))
|
||||
|
||||
timer.Simple(1, function() PLUGIN:Explode(target) end)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("SetTimeScale", {
|
||||
description = "@cmdTimeScale",
|
||||
arguments = {
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
privilege = "Fun Stuff",
|
||||
OnRun = function(self, client, number)
|
||||
local scale = math.Clamp(number or 1, 0.001, 5)
|
||||
game.SetTimeScale(scale)
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (self:OnCheckAccess(v)) then
|
||||
v:NotifyLocalized("bastionTimeScale", client:Name(), scale)
|
||||
end
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("SetGravity", {
|
||||
description = "@cmdGravity",
|
||||
arguments = {
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
privilege = "Fun Stuff",
|
||||
OnRun = function(self, client, number)
|
||||
RunConsoleCommand("sv_gravity", number)
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (self:OnCheckAccess(v)) then
|
||||
v:NotifyLocalized("bastionGravity", client:Name(), number)
|
||||
end
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
-- lookup commands
|
||||
ix.command.Add("LookupSteamID", {
|
||||
description = "Lookup a SteamID in the Bastion user database",
|
||||
arguments = {
|
||||
ix.type.text
|
||||
},
|
||||
privilege = "Bastion Lookup",
|
||||
OnRun = function(self, client, target)
|
||||
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
|
||||
PLUGIN:LookupSteamID(client, target)
|
||||
return
|
||||
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
|
||||
PLUGIN:LookupSteamID(client, target)
|
||||
return
|
||||
end
|
||||
|
||||
target = ix.util.FindPlayer(target, false)
|
||||
client:NotifyLocalized("bastionTargetSelected", target:Name())
|
||||
|
||||
PLUGIN:LookupSteamID(client, target:SteamID64())
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("LookupIPUsers", {
|
||||
description = "Lookup a SteamID in the Bastion IP database",
|
||||
arguments = {
|
||||
ix.type.text
|
||||
},
|
||||
privilege = "Bastion Lookup",
|
||||
OnRun = function(self, client, target)
|
||||
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
|
||||
PLUGIN:LookupIPUsers(client, target)
|
||||
return
|
||||
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
|
||||
PLUGIN:LookupIPUsers(client, target)
|
||||
return
|
||||
end
|
||||
|
||||
target = ix.util.FindPlayer(target, false)
|
||||
client:NotifyLocalized("bastionTargetSelected", target:Name())
|
||||
|
||||
PLUGIN:LookupIPUsers(client, target:SteamID64())
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("ProxyWhitelist", {
|
||||
description = "Whitelist a player as trusted to disable future proxy checks.",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
privilege = "Bastion Whitelist",
|
||||
OnRun = function(self, client, target)
|
||||
if (PLUGIN:WhitelistPlayer(target)) then
|
||||
client:NotifyLocalized("whitelistDone")
|
||||
else
|
||||
client:NotifyLocalized("whitelistError")
|
||||
end
|
||||
end,
|
||||
indicator = "chatTyping"
|
||||
})
|
||||
|
||||
ix.command.Add("PlyGetCharacters", {
|
||||
description = "Get a list of a player's characters.",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client, target)
|
||||
client:ChatNotify(target:SteamName() .. "'s characters:")
|
||||
client:ChatNotify("====================")
|
||||
|
||||
for _, character in pairs(target.ixCharList) do
|
||||
client:ChatNotify(ix.char.loaded[character].vars.name)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
ix.command.Add("LTARP", {
|
||||
description = "Automatically ban a player for leaving to avoid RP after a grace period.",
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
ix.type.number,
|
||||
bit.bor(ix.type.text, ix.type.optional),
|
||||
},
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client, steamID, days, reason)
|
||||
local target = player.GetBySteamID64(steamID) or player.GetBySteamID(steamID)
|
||||
if (target) then
|
||||
client:Notify(target:SteamName().." is still on the server as '"..target:Name().."!")
|
||||
return
|
||||
end
|
||||
|
||||
if (days > 100) then
|
||||
client:Notify("Invalid duration entered. Max ban length is 100 days.")
|
||||
return
|
||||
end
|
||||
|
||||
if (string.find(string.upper(steamID), "^STEAM")) then
|
||||
steamID = util.SteamIDTo64(string.upper(steamID))
|
||||
end
|
||||
|
||||
if (!PLUGIN.disconnects[steamID]) then
|
||||
client:Notify("Could not find a disconnect for "..steamID..", manually ban them if needed.")
|
||||
return
|
||||
end
|
||||
|
||||
local info = PLUGIN.disconnects[steamID]
|
||||
if (info.timer) then
|
||||
client:Notify(steamID.." is already tagged for LTARP by "..info.bannedByName..".")
|
||||
return
|
||||
end
|
||||
|
||||
local reconnectTime, maxBanDelay = 30, 40
|
||||
if (info.time + reconnectTime * 60 < os.time()) then
|
||||
if (info.time + maxBanDelay * 60 < os.time()) then
|
||||
client:Notify(steamID.." disconnected more than "..maxBanDelay.." minutes ago already. Manually ban them if needed.")
|
||||
return
|
||||
end
|
||||
|
||||
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..client:SteamName())
|
||||
return
|
||||
end
|
||||
|
||||
local uniqueID = "ixLTARP"..steamID
|
||||
local bannedBy = client and client:SteamName() or "console"
|
||||
timer.Create(uniqueID, info.time - os.time() + reconnectTime * 60, 1, function()
|
||||
if (IsValid(client)) then
|
||||
client:Notify(steamID.." did not reconnect and has been banned.")
|
||||
end
|
||||
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..bannedBy)
|
||||
end)
|
||||
|
||||
info.timer = uniqueID
|
||||
info.bannedBy = client
|
||||
info.bannedByName = client and client:SteamName() or "console"
|
||||
client:Notify("Timer set! "..steamID.." will be banned in "..math.ceil((info.time - os.time() + reconnectTime * 60) / 60).." minutes.")
|
||||
end
|
||||
})
|
||||
324
gamemodes/helix/plugins/bastion/sh_context.lua
Normal file
324
gamemodes/helix/plugins/bastion/sh_context.lua
Normal file
@@ -0,0 +1,324 @@
|
||||
--[[
|
||||
| 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 properties = properties
|
||||
local IsValid = IsValid
|
||||
local RunConsoleCommand = RunConsoleCommand
|
||||
local tostring = tostring
|
||||
|
||||
local Derma_StringRequest = Derma_StringRequest
|
||||
local CAMI = CAMI
|
||||
local hook = hook
|
||||
local SetClipboardText = SetClipboardText
|
||||
local LocalPlayer = LocalPlayer
|
||||
local net = net
|
||||
local ix = ix
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
properties.Add("ixCopyCharName", {
|
||||
MenuLabel = "#Copy Character Name",
|
||||
Order = 0,
|
||||
MenuIcon = "icon16/user.png",
|
||||
|
||||
Filter = function(self, target)
|
||||
return target:IsPlayer()
|
||||
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
|
||||
and hook.Run("CanProperty", LocalPlayer(), "ixCopyCharName", target) != false
|
||||
end,
|
||||
|
||||
Action = function(self, target)
|
||||
SetClipboardText(target:Name())
|
||||
LocalPlayer():NotifyLocalized("bastionCopiedCharName")
|
||||
end,
|
||||
})
|
||||
|
||||
properties.Add("ixCopySteamName", {
|
||||
MenuLabel = "#Copy Steam Name",
|
||||
Order = 1,
|
||||
MenuIcon = "icon16/user.png",
|
||||
|
||||
Filter = function(self, target)
|
||||
return target:IsPlayer()
|
||||
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
|
||||
and hook.Run("CanProperty", LocalPlayer(), "ixCopySteamName", target) != false
|
||||
end,
|
||||
|
||||
Action = function(self, target)
|
||||
SetClipboardText(target:SteamName())
|
||||
LocalPlayer():NotifyLocalized("bastionCopiedSteamName")
|
||||
end,
|
||||
})
|
||||
|
||||
properties.Add("ixCopySteamID", {
|
||||
MenuLabel = "#Copy Steam ID",
|
||||
Order = 2,
|
||||
MenuIcon = "icon16/user.png",
|
||||
|
||||
Filter = function(self, target)
|
||||
return target:IsPlayer()
|
||||
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
|
||||
and hook.Run("CanProperty", LocalPlayer(), "ixCopySteamID", target) != false
|
||||
end,
|
||||
|
||||
Action = function(self, target)
|
||||
SetClipboardText(target:SteamID())
|
||||
LocalPlayer():NotifyLocalized("bastionCopiedSteamID")
|
||||
end,
|
||||
})
|
||||
|
||||
properties.Add("ixViewInventory", {
|
||||
MenuLabel = "#View Inventory",
|
||||
Order = 10,
|
||||
MenuIcon = "icon16/eye.png",
|
||||
PrependSpacer = true,
|
||||
|
||||
Filter = function(self, target, client)
|
||||
client = client or LocalPlayer()
|
||||
return target:IsPlayer()
|
||||
and CAMI.PlayerHasAccess(client, "Helix - View Inventory")
|
||||
and hook.Run("CanProperty", client, "ixViewInventory", target) != false
|
||||
end,
|
||||
|
||||
Action = function(self, target)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(target)
|
||||
self:MsgEnd()
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local target = net.ReadEntity()
|
||||
|
||||
if (!IsValid(target)) then return end
|
||||
if (!self:Filter(target, client)) then return end
|
||||
|
||||
PLUGIN:OpenInventory(client, target)
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("ixSetHealth", {
|
||||
MenuLabel = "#Health",
|
||||
Order = 100,
|
||||
PrependSpacer = true,
|
||||
MenuIcon = "icon16/heart.png",
|
||||
|
||||
Filter = function(self, target, client)
|
||||
client = client or LocalPlayer()
|
||||
return target:IsPlayer()
|
||||
and (sam and (client:HasPermission("hp") or client:HasPermission("slay"))
|
||||
or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands"))
|
||||
and hook.Run("CanProperty", client, "ixSetHealth", target) != false
|
||||
end,
|
||||
|
||||
MenuOpen = function(self, option, target)
|
||||
local submenu = option:AddSubMenu()
|
||||
local maxHealth = target:GetMaxHealth()
|
||||
local step = maxHealth > 100 and -50 or -25
|
||||
|
||||
if (sam) then
|
||||
if (LocalPlayer():HasPermission("hp")) then
|
||||
for i = maxHealth, 1, step do
|
||||
submenu:AddOption(i, function() RunConsoleCommand("sam", "hp", target:SteamID(), tostring(i)) end)
|
||||
end
|
||||
submenu:AddOption("1", function() RunConsoleCommand("sam", "hp", target:SteamID(), "1") end)
|
||||
end
|
||||
|
||||
if (LocalPlayer():HasPermission("slay")) then
|
||||
submenu:AddOption("Kill", function() RunConsoleCommand("sam", "slay", target:SteamID()) end)
|
||||
end
|
||||
else
|
||||
for i = maxHealth, 1, step do
|
||||
submenu:AddOption(i, function() self:SetHealth(target, i) end)
|
||||
end
|
||||
|
||||
submenu:AddOption("1", function() self:SetHealth(target, 1) end)
|
||||
submenu:AddOption("Kill", function() self:SetHealth(target, 0) end)
|
||||
end
|
||||
end,
|
||||
|
||||
SetHealth = function(self, target, health)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(target)
|
||||
net.WriteUInt(health, 16)
|
||||
self:MsgEnd()
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local target = net.ReadEntity()
|
||||
local health = net.ReadUInt(16)
|
||||
|
||||
if (!IsValid(target)) then return end
|
||||
if (!self:Filter(target, client)) then return end
|
||||
|
||||
if (health > 0) then
|
||||
target:SetHealth(health)
|
||||
ix.log.Add(client, "bastionSetHealth", target)
|
||||
else
|
||||
target:Kill()
|
||||
ix.log.Add(client, "bastionSlay", target)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("ixSetArmor", {
|
||||
MenuLabel = "#Armor",
|
||||
Order = 110,
|
||||
MenuIcon = "icon16/heart.png",
|
||||
|
||||
Filter = function(self, target, client)
|
||||
client = client or LocalPlayer()
|
||||
return target:IsPlayer()
|
||||
and (sam and client:HasPermission("armor")
|
||||
or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands"))
|
||||
and hook.Run("CanProperty", client, "ixSetArmor", target) != false
|
||||
end,
|
||||
|
||||
MenuOpen = function(self, option, target)
|
||||
local submenu = option:AddSubMenu()
|
||||
local maxArmor = 100
|
||||
local step = maxArmor > 100 and -50 or -10
|
||||
|
||||
if (sam) then
|
||||
for i = maxArmor, 1, step do
|
||||
submenu:AddOption(i, function() RunConsoleCommand("sam", "armor", target:SteamID(), tostring(i)) end)
|
||||
end
|
||||
submenu:AddOption("Remove", function() RunConsoleCommand("sam", "armor", target:SteamID(), "0") end)
|
||||
else
|
||||
for i = maxArmor, 1, step do
|
||||
submenu:AddOption(i, function() self:SetArmor(target, i) end)
|
||||
end
|
||||
|
||||
submenu:AddOption("Remove", function() self:SetArmor(target, 0) end)
|
||||
end
|
||||
end,
|
||||
|
||||
SetArmor = function(self, target, armor)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(target)
|
||||
net.WriteUInt(armor, 16)
|
||||
self:MsgEnd()
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local target = net.ReadEntity()
|
||||
local armor = net.ReadUInt(16)
|
||||
|
||||
if (!IsValid(target)) then return end
|
||||
if (!self:Filter(target, client)) then return end
|
||||
|
||||
target:SetArmor(armor)
|
||||
ix.log.Add(client, "bastionSetArmor", target)
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("ixSetCharName", {
|
||||
MenuLabel = "#Set Name",
|
||||
Order = 120,
|
||||
MenuIcon = "icon16/book_edit.png",
|
||||
PrependSpacer = true,
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
return CAMI.PlayerHasAccess(client, "Helix - CharSetName", nil) and entity:IsPlayer() and entity:GetCharacter()
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
Derma_StringRequest("Set Name", "Set the character's name", entity:Name(), function(text)
|
||||
if (text == "") then return end
|
||||
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
net.WriteString(text)
|
||||
self:MsgEnd()
|
||||
end)
|
||||
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
if (CAMI.PlayerHasAccess(client, "Helix - CharSetName", nil)) then
|
||||
local entity = net.ReadEntity()
|
||||
local text = net.ReadString()
|
||||
|
||||
if (IsValid(entity) and entity:IsPlayer() and entity:GetCharacter()) then
|
||||
local oldName = entity:GetCharacter():GetName()
|
||||
entity:GetCharacter():SetName(text)
|
||||
|
||||
ix.log.Add(client, "bastionSetName", entity:GetCharacter(), oldName)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("ixSetCharDescription", {
|
||||
MenuLabel = "#Set Description",
|
||||
Order = 121,
|
||||
MenuIcon = "icon16/book_edit.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
return CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil) and entity:IsPlayer() and entity:GetCharacter()
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
Derma_StringRequest("Set Description", "Set the character's description", entity:GetCharacter():GetDescription(), function(text)
|
||||
if (text == "") then return end
|
||||
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
net.WriteString(text)
|
||||
self:MsgEnd()
|
||||
end)
|
||||
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
if (CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil)) then
|
||||
local entity = net.ReadEntity()
|
||||
local text = net.ReadString()
|
||||
|
||||
if (IsValid(entity) and entity:IsPlayer() and entity:GetCharacter()) then
|
||||
entity:GetCharacter():SetDescription(text)
|
||||
|
||||
ix.log.Add(client, "bastionSetDesc", entity:GetCharacter())
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("ixPropViewOwner", {
|
||||
MenuLabel = "View Owner",
|
||||
Order = 405,
|
||||
MenuIcon = "icon16/magnifier.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (entity:GetClass() == "prop_physics" and CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil)) then return true end
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
self:MsgEnd()
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity)) then return end
|
||||
if (!self:Filter(entity, client)) then return end
|
||||
|
||||
local ownerCharacter = entity.ownerCharacter
|
||||
local ownerName = entity.ownerName
|
||||
local ownerSteamID = entity.ownerSteamID
|
||||
|
||||
if (ownerCharacter and ownerName and ownerSteamID) then
|
||||
client:ChatNotifyLocalized("bastionPropOwnerInformation", ownerCharacter, ownerName, ownerSteamID)
|
||||
else
|
||||
client:ChatNotifyLocalized("bastionPropOwnerUnknown")
|
||||
end
|
||||
end
|
||||
})
|
||||
54
gamemodes/helix/plugins/bastion/sh_hooks.lua
Normal file
54
gamemodes/helix/plugins/bastion/sh_hooks.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- Remake the connect & disconnect chat classes to stop the default ones.
|
||||
function PLUGIN:InitializedChatClasses()
|
||||
ix.chat.classes["connect"] = nil
|
||||
ix.chat.classes["disconnect"] = nil
|
||||
|
||||
ix.chat.Register("new_connect", {
|
||||
CanSay = function(_, speaker, text)
|
||||
return !IsValid(speaker)
|
||||
end,
|
||||
OnChatAdd = function(_, speaker, text)
|
||||
local icon = ix.util.GetMaterial("willardnetworks/chat/connected_icon.png")
|
||||
|
||||
chat.AddText(icon, Color(151, 153, 152), L("playerConnected", text))
|
||||
end,
|
||||
noSpaceAfter = true
|
||||
})
|
||||
|
||||
ix.chat.Register("new_disconnect", {
|
||||
CanSay = function(_, speaker, text)
|
||||
return !IsValid(speaker)
|
||||
end,
|
||||
OnChatAdd = function(_, speaker, text)
|
||||
local icon = ix.util.GetMaterial("willardnetworks/chat/disconnected_icon.png")
|
||||
|
||||
chat.AddText(icon, Color(151, 153, 152), L("playerDisconnected", text))
|
||||
end,
|
||||
noSpaceAfter = true
|
||||
})
|
||||
|
||||
ix.chat.Register("bastionPlayerDeath", {
|
||||
CanSay = function(_, speaker, text)
|
||||
return true
|
||||
end,
|
||||
OnChatAdd = function(_, speaker, text)
|
||||
local icon = ix.util.GetMaterial("icon16/user_red.png")
|
||||
|
||||
chat.AddText(icon, Color(255, 0, 0), text)
|
||||
end,
|
||||
CanHear = function(_, speaker, listener)
|
||||
return listener:IsAdmin() and ix.option.Get(listener, "playerDeathNotification")
|
||||
end
|
||||
})
|
||||
end
|
||||
331
gamemodes/helix/plugins/bastion/sh_plugin.lua
Normal file
331
gamemodes/helix/plugins/bastion/sh_plugin.lua
Normal file
@@ -0,0 +1,331 @@
|
||||
--[[
|
||||
| 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 IsValid = IsValid
|
||||
local ix = ix
|
||||
|
||||
PLUGIN.name = "Bastion"
|
||||
PLUGIN.author = "Gr4Ss"
|
||||
PLUGIN.description = "Some admin extensions for Helix."
|
||||
|
||||
local CAMI = CAMI
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Hear Staff Chat",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Hear GM Chat",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Hear Mentor Chat",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Fun Stuff",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Basic Commands",
|
||||
MinAccess = "user"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Basic Admin Commands",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Full Remover Tool",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - HL2 Tools",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Bastion Whitelist",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - View Inventory",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Increase Character Limit",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Bastion Lookup",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Container Password",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Proxy Notify",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
ix.option.Add("pgi", ix.type.bool, true, {category = "administration"})
|
||||
|
||||
ix.option.Add("playerDeathNotification", ix.type.bool, true, {category = "administration", bNetworked = true})
|
||||
|
||||
ix.config.Add("netLoggingEnabled", false, "Enable or disable net logging into the database (WARNING: PERFORMANCE IMPACT)", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("netAntiSpam", true, "Enable or disable net anti-spam (WARNING: PERFORMANCE IMPACT)", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("suppressOnPlayerChat", true, "Suppress the default OnPlayerChat hook (should not be used by helix)", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("maxCharactersIncreased", 5, "The maximum number of characters a player can have if they have the increased character limit permission.", nil, {
|
||||
data = {min = 1, max = 50},
|
||||
category = "characters"
|
||||
})
|
||||
ix.config.Add("charCreateInterval", 5, "How many minutes there should be between 2 successful character creations of one player (to avoid character spam).", nil, {
|
||||
data = {min = 1, max = 50},
|
||||
category = "characters"
|
||||
})
|
||||
ix.config.Add("AllowContainerSpawn", false, "Allow anyone to directly spawn containers by spawning in their prop. Disallowing this will require admins to create containers from a prop using the context menu.", nil, {
|
||||
category = "containers"
|
||||
})
|
||||
ix.config.Add("VPNKick", false, "Kick new users if they use a VPN.", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("ProxyAlwaysAlert", true, "Always Discord Alert for new joiners with a VPN.", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("showConnectMessages", true, "Whether or not to show notifications when players connect to the server. When off, only Staff will be notified.", nil, {
|
||||
category = "server"
|
||||
})
|
||||
ix.config.Add("showDisconnectMessages", true, "Whether or not to show notifications when players disconnect from the server. When off, only Staff will be notified.", nil, {
|
||||
category = "server"
|
||||
})
|
||||
ix.config.Add("DiscordLink", "https://discord.gg/HbDjUQd", "Invite link to the discord.", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("ContentLink", "https://steamcommunity.com/sharedfiles/filedetails/?id=2145501003", "Link to the workshop collection.", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("ForumLink", "https://willard.network", "Link to the forums", nil, {
|
||||
category = "Bastion"
|
||||
})
|
||||
ix.config.Add("EdictWarningLimit", 1024, "How many edicts can be left before warning messages start to appear.", nil, {
|
||||
data = {min = 100, max = 1024},
|
||||
category = "Bastion"
|
||||
})
|
||||
|
||||
ix.flag.Add("a", "Access to the advanced duplicator.")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
getPlayerInfo = "Uzyskaj informacje o graczu",
|
||||
optStaffChat = "Wyświetla czatu administracji na wszystkich postaciach",
|
||||
optdStaffChat = "Włącza/wyłącza czat administracji na wszystkich postaciach. Gdy jest wyłączony, czat administracji będzie wyświetlany tylko w trybie obserwatora lub na postaci administratora.",
|
||||
optPgi = "Skopiuj Steam ID do schowka w 'PGI'/'Wyświetl Gracza'",
|
||||
optdPgi = "Umożliwia włączenie/wyłączenie automatycznego kopiowania SteamID gracza do schowka podczas korzystania z komendy PGI lub opcji menu kontekstowego 'Wyświetl Gracza'.",
|
||||
cmdStaffHelp = "Poproś o pomoc wszystkich administratorów.\n",
|
||||
cmdAnnounce = "Ogłoszenie administracyjne OOC na całym serwerze.",
|
||||
cmdLocalEvent = "Utwórz wydarzenie administracyjne IC, które będzie słyszalne tylko w określonym promieniu.",
|
||||
bastionPGIInvalidTarget = "Musisz wejść do prawidłowego celu lub patrzeć na niego.",
|
||||
bastionTakingItemsTooQuickly = "Zbyt szybko zabierasz przedmioty! Zwolnij tempo.",
|
||||
bastionItemDropSpamKick = "%s został zkickowany za exploitowanie item dropów.",
|
||||
bastionItemDropSpamWarn = "%s został ostrzeżony za exploitowanie item dropów.",
|
||||
bastionItemDropTooQuick = "Zbyt szybko upuszczasz przedmioty! Zwolnij tempo.",
|
||||
bastionItemTakeWarn = "%s został ostrzeżony za zbyt szybkie zabieranie przedmiotów.",
|
||||
bastionItemTakeKick = "%s został zkickowany za zbyt szybkie upuszczanie przedmiotów.",
|
||||
charCreateTooFast = "Zbyt szybko tworzysz postacie. Odczekaj co najmniej %d minut z kolejną próbą.",
|
||||
bastionCopiedCharName = "Skopiowano imię postaci",
|
||||
bastionCopiedSteamName = "Skopiowano nazwę Steam",
|
||||
bastionCopiedSteamID = "Skopiowano Steam ID",
|
||||
bastionGoto = "Przejdź do gracza",
|
||||
bastionCopyCharName = "Kopiuj imię postaci",
|
||||
bastionCopySteamName = "Kopiuj nazwę Steam",
|
||||
bastionNoRecordFound = "Nie znaleziono żadnych rekordów dla %s.",
|
||||
bastionResultsPrinted = "Wyniki zostały wydrukowane w konsoli.",
|
||||
bastionProxyNotify = "%s połączył się jako nowy gracz korzystając z VPN/Proxy.",
|
||||
bastionPropOwnerInformation = "Właścicielem tego propa jest %s (%s - %s).",
|
||||
bastionPropOwnerUnknown = "Właściciel tego propa nie został zarejestrowany!",
|
||||
whitelistDone = "Gracz został zwhitelistowany.",
|
||||
whitelistError = "Coś poszło przy nadaniu whitelisty graczowi. Skontaktuj się z deweloperem.",
|
||||
cmdTimeScale = "Zmień skalę czasu (min 0.001, max 5).",
|
||||
bastionTimeScale = "%s ustawił skalę czasu na %d.",
|
||||
cmdGravity = "Zmień grawitację.",
|
||||
bastionGravity = "%s ustawił grawitacje na %d.",
|
||||
edictWarning = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192!",
|
||||
edictCritical = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192! Koniecznie trzeba zrobić awaryjny Cleanup!",
|
||||
entsPrintedInConsole = "Lista Entity została wydrukowana w konsoli.",
|
||||
entityRemoved = "Entity %d (%s) zostało usunięte!",
|
||||
entityNotFound = "Entity %d nie zostało znalezione/jest nieprawidłowe.",
|
||||
optPlayerDeathNotification = "Powiadomienie o śmierci gracza",
|
||||
optdPlayerDeathNotification = "Określa, czy wysłać wiadomość na czacie, gdy gracz zginie.",
|
||||
cmdRemovePersistedProps = "Usuwa wszystkie utrwalone propy w promieniu wokół ciebie."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
bastionCopySteamName = "Copiar nombre de Steam",
|
||||
bastionPGIInvalidTarget = "Debes especificar o estar mirando a un objetivo válido.",
|
||||
cmdLocalEvent = "Haz un evento IC de administrador que sólo pueda ser escuchado dentro de un radio determinado.",
|
||||
bastionNoRecordFound = "No se ha podido encontrar ningún registro para %s.",
|
||||
cmdAnnounce = "Haz un anuncio OOC de administrador a todo el servidor.",
|
||||
cmdGravity = "Cambia la gravedad.",
|
||||
bastionCopiedSteamID = "Steam ID copiada",
|
||||
bastionProxyNotify = "%s se ha conectado como nuevo jugador mientras usa una VPN/Proxy.",
|
||||
bastionTakingItemsTooQuickly = "¡Estás agarrando objetos demasiado rápido! Por favor, reduce la velocidad.",
|
||||
optdStaffChat = "Activa/desactiva el Chat de Staff en todos los personajes. Cuando está desactivado, sólo se mostrará el Chat de Staff mientras se esté en observer o en un personaje administrativo.",
|
||||
entityNotFound = "La entidad %d no fue encontrada/no es válida.",
|
||||
whitelistDone = "El jugador estaba en la Whitelist.",
|
||||
bastionCopiedCharName = "Nombre del personaje copiado",
|
||||
getPlayerInfo = "Obtener información del jugador",
|
||||
bastionCopyCharName = "Copiar el nombre del personaje",
|
||||
charCreateTooFast = "Estás creando personajes demasiado rápido. Por favor, espera al menos %d minutos entre intentos.",
|
||||
entsPrintedInConsole = "La lista de entidades se ha impreso en la consola.",
|
||||
bastionItemDropTooQuick = "¡Estás soltando objetos demasiado rápido! Por favor, baja la velocidad.",
|
||||
bastionGravity = "%s ha fijado la gravedad en %d.",
|
||||
entityRemoved = "¡La entidad %d (%s) ha sido eliminada!",
|
||||
cmdStaffHelp = "Pide ayuda a todo el Staff.\n",
|
||||
bastionCopiedSteamName = "Nombre de Steam copiado",
|
||||
bastionItemTakeWarn = "%s fue advertido por agarrar objetos demasiado rápido.",
|
||||
bastionResultsPrinted = "Los resultados se imprimieron en la consola.",
|
||||
optStaffChat = "Mostrar el Chat de Staff en todos los personajes",
|
||||
bastionGoto = "Ir al jugador",
|
||||
cmdTimeScale = "Cambie la escala de tiempo (mínimo 0,001, máximo 5).",
|
||||
whitelistError = "Algo fue mal cuando se intentó meter al jugador en la whitelist. Contacta con un desarrollador.",
|
||||
bastionTimeScale = "%s ha fijado la escala de tiempo en %d.",
|
||||
bastionItemDropSpamKick = "%s ha sido expulsado por exploits relacionados con item-dropping.",
|
||||
optdPgi = "Te permite activar/desactivar la copia automática al portapapeles del SteamID de un jugador cuando usa el comando PGI o la opción del menú contextual 'Ver Jugador'.",
|
||||
bastionItemDropSpamWarn = "%s ha sido advertido del uso de exploits relacionados con item-dropping.",
|
||||
bastionItemTakeKick = "%s ha sido expulsado por soltar objetos demasiado rápido.",
|
||||
optPgi = "Copiar Steam ID al portapapeles en el 'PGI'/'Ver Jugador'",
|
||||
edictWarning = "¡Solamente quedan disponibles %d edictos! ¡El conteo total de edictos es de: %d/8192!"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
getPlayerInfo = "Uzyskaj informacje o graczu",
|
||||
optStaffChat = "Wyświetla czatu administracji na wszystkich postaciach",
|
||||
optdStaffChat = "Włącza/wyłącza czat administracji na wszystkich postaciach. Gdy jest wyłączony, czat administracji będzie wyświetlany tylko w trybie obserwatora lub na postaci administratora.",
|
||||
optPgi = "Skopiuj Steam ID do schowka w 'PGI'/'Wyświetl Gracza'",
|
||||
optdPgi = "Umożliwia włączenie/wyłączenie automatycznego kopiowania SteamID gracza do schowka podczas korzystania z komendy PGI lub opcji menu kontekstowego 'Wyświetl Gracza'.",
|
||||
cmdStaffHelp = "Poproś o pomoc wszystkich administratorów.\n",
|
||||
cmdAnnounce = "Ogłoszenie administracyjne OOC na całym serwerze.",
|
||||
cmdLocalEvent = "Utwórz wydarzenie administracyjne IC, które będzie słyszalne tylko w określonym promieniu.",
|
||||
bastionPGIInvalidTarget = "Musisz wejść do prawidłowego celu lub patrzeć na niego.",
|
||||
bastionTakingItemsTooQuickly = "Zbyt szybko zabierasz przedmioty! Zwolnij tempo.",
|
||||
bastionItemDropSpamKick = "%s został zkickowany za exploitowanie item dropów.",
|
||||
bastionItemDropSpamWarn = "%s został ostrzeżony za exploitowanie item dropów.",
|
||||
bastionItemDropTooQuick = "Zbyt szybko upuszczasz przedmioty! Zwolnij tempo.",
|
||||
bastionItemTakeWarn = "%s został ostrzeżony za zbyt szybkie zabieranie przedmiotów.",
|
||||
bastionItemTakeKick = "%s został zkickowany za zbyt szybkie upuszczanie przedmiotów.",
|
||||
charCreateTooFast = "Zbyt szybko tworzysz postacie. Odczekaj co najmniej %d minut z kolejną próbą.",
|
||||
bastionCopiedCharName = "Skopiowano imię postaci",
|
||||
bastionCopiedSteamName = "Skopiowano nazwę Steam",
|
||||
bastionCopiedSteamID = "Skopiowano Steam ID",
|
||||
bastionGoto = "Przejdź do gracza",
|
||||
bastionCopyCharName = "Kopiuj imię postaci",
|
||||
bastionCopySteamName = "Kopiuj nazwę Steam",
|
||||
bastionNoRecordFound = "Nie znaleziono żadnych rekordów dla %s.",
|
||||
bastionResultsPrinted = "Wyniki zostały wydrukowane w konsoli.",
|
||||
bastionProxyNotify = "%s połączył się jako nowy gracz korzystając z VPN/Proxy.",
|
||||
bastionPropOwnerInformation = "Właścicielem tego propa jest %s (%s - %s).",
|
||||
bastionPropOwnerUnknown = "Właściciel tego propa nie został zarejestrowany!",
|
||||
whitelistDone = "Gracz został zwhitelistowany.",
|
||||
whitelistError = "Coś poszło przy nadaniu whitelisty graczowi. Skontaktuj się z deweloperem.",
|
||||
cmdTimeScale = "Zmień skalę czasu (min 0.001, max 5).",
|
||||
bastionTimeScale = "%s ustawił skalę czasu na %d.",
|
||||
cmdGravity = "Zmień grawitację.",
|
||||
bastionGravity = "%s ustawił grawitacje na %d.",
|
||||
edictWarning = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192!",
|
||||
entsPrintedInConsole = "Lista Entity została wydrukowana w konsoli.",
|
||||
entityRemoved = "Entity %d (%s) zostało usunięte!",
|
||||
entityNotFound = "Entity %d nie zostało znalezione/jest nieprawidłowe.",
|
||||
optPlayerDeathNotification = "Powiadomienie o śmierci gracza",
|
||||
optdPlayerDeathNotification = "Określa, czy wysłać wiadomość na czacie, gdy gracz zginie.",
|
||||
})
|
||||
|
||||
PLUGIN.soundAlias = {
|
||||
["restricted_block_deploy"] = "voices/dispatch/restrictedblock_deployment.wav",
|
||||
["restricted_block"] = "voices/dispatch/restrictedblock_warning.wav",
|
||||
["access_restricted"] = "voices/dispatch/access_restricted.wav",
|
||||
["anticivil_evading"] = "voices/dispatch/anticivil_evading.wav",
|
||||
["civil_insurrection"] = "voices/dispatch/civil_insurrection.wav",
|
||||
["victory_music"] = "music/scary_tense/victory_music.mp3",
|
||||
}
|
||||
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sh_classes.lua")
|
||||
ix.util.Include("sh_commands.lua")
|
||||
ix.util.Include("sh_context.lua")
|
||||
ix.util.Include("sh_hooks.lua")
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.util.Include("modules/sh_bindchecker.lua")
|
||||
|
||||
function PLUGIN:GetMaxPlayerCharacter(client)
|
||||
if (CAMI.PlayerHasAccess(client, "Helix - Increase Character Limit")) then
|
||||
return ix.config.Get("maxCharactersIncreased", 8)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanProperty(client, property, entity)
|
||||
if (property == "container_setpassword" and !CAMI.PlayerHasAccess(client, "Helix - Container Password")) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanPlayerSpawnContainer()
|
||||
if (!ix.config.Get("AllowContainerSpawn")) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanPlayerAccessDoor(client)
|
||||
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then return true end
|
||||
|
||||
if (ix.faction.Get(client:Team()).lockAllDoors) then return true end
|
||||
end
|
||||
|
||||
PLUGIN.removeWhitelist = {
|
||||
["prop_physics"] = true,
|
||||
["prop_ragdoll"] = true
|
||||
}
|
||||
|
||||
local hl2Tools = {
|
||||
["env_headcrabcanister"] = true,
|
||||
["item_ammo_crate"] = true,
|
||||
["item_item_crate"] = true,
|
||||
["prop_door"] = true,
|
||||
["prop_thumper"] = true
|
||||
}
|
||||
|
||||
function PLUGIN:CanTool(client, trace, tool)
|
||||
if (tool == "remover" and !CAMI.PlayerHasAccess(client, "Helix - Full Remover Tool")) then
|
||||
if (IsValid(trace.Entity) and !self.removeWhitelist[trace.Entity:GetClass()]) then
|
||||
return false
|
||||
end
|
||||
elseif (tool == "advdupe2" and !client:GetCharacter():HasFlags("a")) then
|
||||
return false
|
||||
elseif (hl2Tools[tool] and !CAMI.PlayerHasAccess(client, "Helix - HL2 Tools")) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
193
gamemodes/helix/plugins/bastion/sv_hooks.lua
Normal file
193
gamemodes/helix/plugins/bastion/sv_hooks.lua
Normal file
@@ -0,0 +1,193 @@
|
||||
--[[
|
||||
| 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 timer = timer
|
||||
local pairs = pairs
|
||||
local hook = hook
|
||||
local CurTime = CurTime
|
||||
local player = player
|
||||
local ipairs = ipairs
|
||||
local ix = ix
|
||||
local IsValid = IsValid
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.disconnects = PLUGIN.disconnects or {}
|
||||
|
||||
PLUGIN.takeCounter = {}
|
||||
timer.Create("ixBastionAntiTakeSpam", 1, 0, function()
|
||||
for client, amount in pairs(PLUGIN.takeCounter) do
|
||||
if (amount < 10) then continue end
|
||||
if (!IsValid(client)) then continue end
|
||||
|
||||
for _, admin in ipairs(player.GetAll()) do
|
||||
if (admin:IsSuperAdmin()) then
|
||||
admin:NotifyLocalized("bastionItemTakeKick", client:Name())
|
||||
end
|
||||
end
|
||||
|
||||
client:Kick("Item take spam")
|
||||
end
|
||||
|
||||
PLUGIN.takeCounter = {}
|
||||
end)
|
||||
|
||||
function PLUGIN:CanPlayerInteractItem(client, action, item, data)
|
||||
if (action == "take") then
|
||||
if (self.takeCounter[client] and self.takeCounter[client] >= 5) then
|
||||
if (self.takeCounter[client] == 5) then
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:IsSuperAdmin()) then
|
||||
v:NotifyLocalized("bastionItemTakeWarn", client:Name())
|
||||
end
|
||||
end
|
||||
client:NotifyLocalized("bastionTakingItemsTooQuickly")
|
||||
end
|
||||
|
||||
self.takeCounter[client] = self.takeCounter[client] + 1
|
||||
return false
|
||||
end
|
||||
elseif (action == "drop" and client.ixAntiItemSpam and client.ixAntiItemSpam > CurTime()) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
function PLUGIN:PlayerInteractItem(client, action, item)
|
||||
if (action == "take") then
|
||||
self.takeCounter[client] = (self.takeCounter[client] or 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
PLUGIN.itemSpawns = {}
|
||||
function PLUGIN:OnItemSpawned(entity)
|
||||
if (IsValid(self.itemSpawns[entity.ixItemID])) then
|
||||
if (self.itemSpawns[entity.ixItemID].ixItemID != entity.ixItemID) then
|
||||
return -- just in case
|
||||
end
|
||||
|
||||
--Now we are trying to spawn an item which already has an entity!
|
||||
--Check if it is the same person, in case of weird behaviour
|
||||
if (entity.ixSteamID == self.itemSpawns[entity.ixItemID]) then
|
||||
local client = player.GetBySteamID(entity.ixSteamID)
|
||||
if ((client.ixAntiItemSpam or 0) > CurTime()) then
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:IsSuperAdmin()) then
|
||||
v:NotifyLocalized("bastionItemDropSpamKick", client:Name())
|
||||
end
|
||||
end
|
||||
|
||||
client:Kick("Item drop spam")
|
||||
else
|
||||
client.ixAntiItemSpam = CurTime() + 10
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v:IsSuperAdmin()) then
|
||||
v:NotifyLocalized("bastionItemDropSpamWarn", client:Name())
|
||||
end
|
||||
end
|
||||
|
||||
client:NotifyLocalized("bastionItemDropTooQuick")
|
||||
end
|
||||
end
|
||||
|
||||
self.itemSpawns[entity.ixItemID]:Remove()
|
||||
self.itemSpawns[entity.ixItemID] = entity
|
||||
else
|
||||
self.itemSpawns[entity.ixItemID] = entity
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanPlayerCreateCharacter(client)
|
||||
if (client.ixNextCharCreate and (client.ixNextCharCreate + ix.config.Get("charCreateInterval") * 60) > CurTime()) then
|
||||
return false, "charCreateTooFast", ix.config.Get("charCreateInterval")
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterCreated(client)
|
||||
if (!client:IsAdmin()) then
|
||||
client.ixNextCharCreate = CurTime()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSpawnedProp(client, model, entity)
|
||||
entity.ownerCharacter = client:GetName()
|
||||
entity.ownerName = client:SteamName()
|
||||
entity.ownerSteamID = client:SteamID()
|
||||
end
|
||||
|
||||
function PLUGIN:OnPlayerHitGround(client, inWater, onFloater, speed)
|
||||
local currentVelocity = client:GetVelocity()
|
||||
|
||||
client:SetVelocity(-Vector(currentVelocity.x, currentVelocity.y, 0))
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
local receivers
|
||||
|
||||
if (!ix.config.Get("showConnectMessages", true)) then
|
||||
receivers = {}
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if (ply:IsAdmin()) then
|
||||
receivers[#receivers + 1] = ply
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Give some time for the player's data to be loaded, just in case.
|
||||
timer.Simple(1, function()
|
||||
ix.chat.Send(nil, "new_connect", client:SteamName(), false, receivers)
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDisconnected(client)
|
||||
local receivers
|
||||
|
||||
self.disconnects[client:SteamID64()] = {time = os.time(), charID = client:GetCharacter() and client:GetCharacter():GetID()}
|
||||
|
||||
if (!ix.config.Get("showDisconnectMessages", true)) then
|
||||
receivers = {}
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if (ply:IsAdmin()) then
|
||||
receivers[#receivers + 1] = ply
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Send(nil, "new_disconnect", client:SteamName(), false, receivers)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character, lastChar)
|
||||
if (self.disconnects[client:SteamID64()]) then
|
||||
local info = self.disconnects[client:SteamID64()]
|
||||
if (info.timer) then
|
||||
timer.Remove(info.timer)
|
||||
if (IsValid(info.bannedBy)) then
|
||||
if (info.charID) then
|
||||
if (character:GetID() == info.charID) then
|
||||
info.bannedBy:Notify(client:SteamName().." has reconnected and is back on their character "..character:GetName()..".")
|
||||
end
|
||||
else
|
||||
info.bannedBy:Notify(client:SteamName().." has reconnected on a different character '"..character:GetName().."'.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDeath(client, inflictor, attacker)
|
||||
if (!client:GetCharacter()) then return end
|
||||
|
||||
ix.chat.Send(client, "bastionPlayerDeath", client:GetName() .. " (" .. client:SteamName() .. ") has died at " .. (client.ixArea and client.ixArea != "" and client.ixArea or "an Uncategorized Location") .. ".")
|
||||
end
|
||||
|
||||
hook.Add("SAM.RanCommand", "BastionSamCommandLogs", function(client, cmd_name, args, cmd)
|
||||
ix.log.Add(client, "bastionSamCommand", cmd_name, args, cmd)
|
||||
end)
|
||||
139
gamemodes/helix/plugins/bastion/sv_plugin.lua
Normal file
139
gamemodes/helix/plugins/bastion/sv_plugin.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--[[
|
||||
| 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 util = util
|
||||
local file = file
|
||||
local table = table
|
||||
local timer = timer
|
||||
local ipairs = ipairs
|
||||
local player = player
|
||||
local CAMI = CAMI
|
||||
local string = string
|
||||
local ents = ents
|
||||
local IsValid = IsValid
|
||||
local ix = ix
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
util.AddNetworkString("ixOpenURL")
|
||||
util.AddNetworkString("ixPlayerInfo")
|
||||
util.AddNetworkString("ixStaffList")
|
||||
util.AddNetworkString("ixPlaySound")
|
||||
|
||||
function PLUGIN:InitializedPlugins()
|
||||
if (!CHTTP) then return end
|
||||
self.API_KEY = file.Read("gamemodes/helix/plugins/bastion/apikey_proxy.txt", "GAME")
|
||||
if (self.API_KEY) then
|
||||
self.API_KEY = string.gsub(self.API_KEY, "[^%w%-]", "")
|
||||
end
|
||||
self.DISCORD_WEBHOOK_ALTS = file.Read("gamemodes/helix/plugins/bastion/apiwebhook_disocrd.txt", "GAME")
|
||||
end
|
||||
|
||||
function PLUGIN:InitializedConfig()
|
||||
ix.config.Set("EdictWarningLimit", 1024)
|
||||
end
|
||||
|
||||
ix.util.Include("modules/sv_banlist.lua")
|
||||
ix.util.Include("modules/sv_antialt.lua")
|
||||
ix.util.Include("modules/sv_netsizelog.lua")
|
||||
--ix.util.Include("modules/sv_netmonitor.lua") --high performance impact!
|
||||
|
||||
ix.log.AddType("bastionCheckInfo", function(client, target)
|
||||
return string.format("%s has checked %s's info.", client:GetName(), target:GetName())
|
||||
end)
|
||||
ix.log.AddType("bastionSetHealth", function(client, target)
|
||||
return string.format("%s has set %s's health to %d.", client:GetName(), target:GetName(), target:Health())
|
||||
end)
|
||||
ix.log.AddType("bastionSetArmor", function(client, target)
|
||||
return string.format("%s has set %s's armor to %d.", client:GetName(), target:GetName(), target:Armor())
|
||||
end)
|
||||
ix.log.AddType("bastionSetName", function(client, target, oldName)
|
||||
return string.format("%s has set %s's name to %s.", client:GetName(), oldName, target:GetName())
|
||||
end)
|
||||
ix.log.AddType("bastionSetDesc", function(client, target)
|
||||
return string.format("%s has set %s's description to %s.", client:GetName(), target:GetName(), target:GetDescription())
|
||||
end)
|
||||
ix.log.AddType("bastionSlay", function(client, target)
|
||||
return string.format("%s has slayed %s.", client:GetName(), target:GetName())
|
||||
end, FLAG_DANGER)
|
||||
ix.log.AddType("bastionInvSearch", function(client, name)
|
||||
return string.format("%s is admin-searching %s.", client:GetName(), name)
|
||||
end)
|
||||
ix.log.AddType("bastionInvClose", function(client, name)
|
||||
return string.format("%s has closed %s.", client:GetName(), name)
|
||||
end)
|
||||
ix.log.AddType("netstreamHoneypot", function(client, hook, bNoAccess)
|
||||
return string.format("[BANME] %s has just hit the %s HONEYPOT!!! Please ban SteamID: %s (trigger reason: %s)", client:SteamName(), hook, client:SteamID(), bNoAccess and "no access" or "invalid arg")
|
||||
end, FLAG_DANGER)
|
||||
ix.log.AddType("luaHack", function(client, name)
|
||||
return string.format("[BANME] %s just tried to %s through lua-injection!!! Please ban SteamID: %s", client:SteamName(), name, client:SteamID())
|
||||
end)
|
||||
ix.log.AddType("bastionSamCommand", function(client, cmd, args)
|
||||
return string.format("%s ran SAM command '%s' with arguments: '%s'", client:Name(), cmd, table.concat(args, "' '"))
|
||||
end)
|
||||
ix.log.AddType("samReportClaimed", function(client, reporter)
|
||||
return string.format("%s claimed a report by %s (%s).", client:Name(), reporter:Name(), reporter:SteamName())
|
||||
end)
|
||||
|
||||
timer.Create("ixBastionEdictCheck", 60, 0, function()
|
||||
local edictsCount = ents.GetEdictCount()
|
||||
local edictsLeft = 8192 - edictsCount
|
||||
if (edictsLeft < ix.config.Get("EdictWarningLimit")) then
|
||||
if (edictsLeft < 600) then
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (CAMI.PlayerHasAccess(v, "Helix - Basic Admin Commands")) then
|
||||
v:NotifyLocalized("edictCritical", edictsLeft, edictsCount)
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (CAMI.PlayerHasAccess(v, "Helix - Basic Admin Commands")) then
|
||||
v:NotifyLocalized("edictWarning", edictsLeft, edictsCount)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:Explode(target)
|
||||
local explosive = ents.Create("env_explosion")
|
||||
explosive:SetPos(target:GetPos())
|
||||
explosive:SetOwner(target)
|
||||
explosive:Spawn()
|
||||
explosive:SetKeyValue("iMagnitude", "1")
|
||||
explosive:Fire("Explode", 0, 0)
|
||||
explosive:EmitSound("ambient/explosions/explode_4.wav", 500, 500)
|
||||
|
||||
target:StopParticles()
|
||||
target:Kill()
|
||||
end
|
||||
|
||||
function PLUGIN:OpenInventory(client, target)
|
||||
if (!IsValid(client) or !IsValid(target)) then return end
|
||||
if (!target:IsPlayer()) then return end
|
||||
|
||||
local character = target:GetCharacter()
|
||||
if (!character) then return end
|
||||
|
||||
local inventory = character:GetInventory()
|
||||
if (inventory) then
|
||||
local name = target:Name().."'s inventory"
|
||||
|
||||
ix.storage.Open(client, inventory, {
|
||||
entity = target,
|
||||
name = name,
|
||||
OnPlayerClose = function()
|
||||
ix.log.Add(client, "bastionInvClose", name)
|
||||
end
|
||||
})
|
||||
|
||||
ix.log.Add(client, "bastionInvSearch", name)
|
||||
end
|
||||
end
|
||||
78
gamemodes/helix/plugins/betterlogs/cl_hooks.lua
Normal file
78
gamemodes/helix/plugins/betterlogs/cl_hooks.lua
Normal file
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
| 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 CAMI = CAMI
|
||||
local LocalPlayer = LocalPlayer
|
||||
local CurTime = CurTime
|
||||
local table = table
|
||||
local os = os
|
||||
local pairs = pairs
|
||||
local draw = draw
|
||||
local Color = Color
|
||||
local netstream = netstream
|
||||
local IsValid = IsValid
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.marks = {}
|
||||
|
||||
function PLUGIN:CreateMenuButtons(tabs)
|
||||
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage Logs", nil)) then
|
||||
tabs["Logs"] = {
|
||||
RowNumber = 8,
|
||||
Width = 23,
|
||||
Height = 17,
|
||||
Right = true,
|
||||
Icon = "willardnetworks/tabmenu/charmenu/licenses.png",
|
||||
Create = function(info, container)
|
||||
local panel = container:Add("ixLogs")
|
||||
ix.gui.logs = panel
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:LogMark(pos, record)
|
||||
local curTime = CurTime()
|
||||
table.insert(self.marks, {
|
||||
fadeTime = curTime + 120,
|
||||
text = "["..os.date("%d/%m/%y %X", record.datetime).."] "..record.text,
|
||||
pos = pos
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:HUDPaint()
|
||||
if (self.marks) then
|
||||
local curTime = CurTime()
|
||||
|
||||
for _, v in pairs(self.marks) do
|
||||
if (v.fadeTime > curTime) then
|
||||
local pos = v.pos:ToScreen()
|
||||
draw.SimpleTextOutlined(v.text, "DermaDefault", pos.x, pos.y, Color(255, 255, 255), TEXT_ALIGN_CENTER, nil, 1, Color(0, 0, 0))
|
||||
else
|
||||
table.remove(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("ixSendLogTypes", function(logTypes)
|
||||
if (IsValid(ix.gui.logs)) then
|
||||
ix.gui.logs.requestedLogTypes = logTypes
|
||||
ix.gui.logs:Rebuild()
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("ixSendLogs", function(logs)
|
||||
if (IsValid(ix.gui.logs)) then
|
||||
ix.gui.logs.requestedLogs = logs
|
||||
ix.gui.logs:FillLogs(true)
|
||||
end
|
||||
end)
|
||||
585
gamemodes/helix/plugins/betterlogs/derma/cl_logs.lua
Normal file
585
gamemodes/helix/plugins/betterlogs/derma/cl_logs.lua
Normal file
@@ -0,0 +1,585 @@
|
||||
--[[
|
||||
| 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
|
||||
local PANEL = {}
|
||||
|
||||
local boxPattern = Material("willardnetworks/tabmenu/crafting/box_pattern.png", "noclamp")
|
||||
|
||||
-- Shared frame painting function between two VGUI registrations
|
||||
local function PaintFrames(self, w, h, bCustom)
|
||||
local value = 100
|
||||
if bCustom then value = 200 end
|
||||
if isnumber(bCustom) then value = bCustom end
|
||||
local color
|
||||
if !isnumber(bCustom) then
|
||||
color = Color(0, 0, 0, value)
|
||||
else
|
||||
color = Color(25, 25, 25, value)
|
||||
end
|
||||
|
||||
surface.SetDrawColor(color)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
if bCustom then
|
||||
surface.SetDrawColor(Color(255, 255, 255, 1))
|
||||
surface.SetMaterial(boxPattern)
|
||||
surface.DrawTexturedRectUV(0, 0, w, h, 0, 0, w / SScaleMin(414 / 3), h / SScaleMin(677 / 3))
|
||||
end
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, 255 / 100 * 30))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
ix.gui.logs = self
|
||||
|
||||
local titlePushDown = SScaleMin(30 / 3)
|
||||
local topPushDown = SScaleMin(150 / 3)
|
||||
local scale780 = SScaleMin(780 / 3)
|
||||
local scale120 = SScaleMin(120 / 3)
|
||||
|
||||
self:SetWide(ScrW() - (topPushDown * 2))
|
||||
|
||||
local sizeXtitle, sizeYtitle = self:GetWide(), scale120
|
||||
local sizeXcontent, sizeYcontent = self:GetWide(), scale780
|
||||
|
||||
self.titlePanel = self:Add("Panel")
|
||||
self.titlePanel:SetSize(sizeXtitle, sizeYtitle)
|
||||
self.titlePanel:SetPos(self:GetWide() * 0.5 - self.titlePanel:GetWide() * 0.5)
|
||||
self.titlePanel.noRemove = true
|
||||
|
||||
self:CreateTitleText()
|
||||
|
||||
self.contentFrame = self:Add("Panel")
|
||||
self.contentFrame:SetSize(sizeXcontent, sizeYcontent)
|
||||
self.contentFrame:SetPos(self:GetWide() * 0.5 - self.contentFrame:GetWide() * 0.5, titlePushDown)
|
||||
self.contentFrame.noRemove = true
|
||||
self.contentFrame.Paint = function(panel, w, h)
|
||||
PaintFrames(panel, w, h, true)
|
||||
end
|
||||
|
||||
self:SetTall(scale120 + scale780 + titlePushDown)
|
||||
self:Center()
|
||||
self.columnWidth = SScaleMin(150 / 3)
|
||||
|
||||
self.requestedLogTypes = {}
|
||||
self.requestedLogs = {}
|
||||
|
||||
self.page = 1
|
||||
|
||||
netstream.Start("ixRequestLogTypes")
|
||||
|
||||
self:Rebuild()
|
||||
end
|
||||
|
||||
function PANEL:CreateTitleText()
|
||||
local logsTitleIcon = self.titlePanel:Add("DImage")
|
||||
logsTitleIcon:SetImage("willardnetworks/tabmenu/charmenu/licenses.png")
|
||||
logsTitleIcon:SetSize(SScaleMin(23 / 3), SScaleMin(17 / 3))
|
||||
|
||||
self.logsTitle = self.titlePanel:Add("DLabel")
|
||||
self.logsTitle:SetFont("TitlesFontNoClamp")
|
||||
self.logsTitle:SetText("Logs")
|
||||
self.logsTitle:SizeToContents()
|
||||
self.logsTitle:SetPos(SScaleMin(33 / 3), SScaleMin(17 / 3) * 0.5 - self.logsTitle:GetTall() * 0.5)
|
||||
end
|
||||
|
||||
function PANEL:Rebuild()
|
||||
self.contentFrame:Clear()
|
||||
self.buttonsList = {}
|
||||
|
||||
self.leftSide = self.contentFrame:Add("Panel")
|
||||
self.leftSide:Dock(LEFT)
|
||||
self.leftSide:SetWide(self.contentFrame:GetWide() * 0.20)
|
||||
|
||||
self.rightSide = self.contentFrame:Add("Panel")
|
||||
self.rightSide:Dock(RIGHT)
|
||||
self.rightSide:SetWide(self.contentFrame:GetWide() * 0.80)
|
||||
self.rightSide.Paint = function() end
|
||||
|
||||
self.logs = self.rightSide:Add("DListView")
|
||||
self.logs:Dock(FILL)
|
||||
self.logs:DockMargin(4, 4, 4, 4)
|
||||
self.logs:AddColumn("ID")
|
||||
self.logs:AddColumn("Time")
|
||||
self.logs:AddColumn("Steam ID")
|
||||
self.logs:AddColumn("Text")
|
||||
self.logs:SetHeaderHeight(SScaleMin(16 / 3))
|
||||
self.logs:SetDataHeight(SScaleMin(17 / 3))
|
||||
|
||||
self.logs.Paint = function() end
|
||||
|
||||
for _, v in pairs(self.logs:GetChildren()) do
|
||||
if v:GetName() != "DListView_Column" then continue end
|
||||
for _, v2 in pairs(v:GetChildren()) do
|
||||
if v2:GetName() != "DButton" then continue end
|
||||
local text = v2:GetText()
|
||||
if (text == "Steam ID" or text == "Time" or text == "ID") then
|
||||
v:SetFixedWidth(self.columnWidth)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.logs.OnRowRightClick = function(list, lineId, line)
|
||||
local dmenu = DermaMenu()
|
||||
dmenu:MakePopup()
|
||||
dmenu:SetPos(input.GetCursorPos())
|
||||
|
||||
local record = line.record
|
||||
|
||||
if (record) then
|
||||
dmenu:AddOption("Copy to clipboard", function()
|
||||
SetClipboardText("["..os.date("%Y-%m-%d %X", record.datetime).."] "..record.text)
|
||||
end)
|
||||
|
||||
if (record.steamid) then
|
||||
dmenu:AddOption("Copy SteamID to clipboard", function()
|
||||
SetClipboardText(util.SteamIDFrom64(record.steamid))
|
||||
end)
|
||||
|
||||
dmenu:AddOption("Copy SteamID64 to clipboard", function()
|
||||
SetClipboardText(record.steamid)
|
||||
end)
|
||||
end
|
||||
|
||||
if (record.pos_x) then
|
||||
local pos = Vector(record.pos_x, record.pos_y, record.pos_z)
|
||||
|
||||
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Tp", nil)) then
|
||||
dmenu:AddOption("Teleport", function()
|
||||
netstream.Start("ixLogTeleport", pos)
|
||||
end)
|
||||
end
|
||||
|
||||
dmenu:AddOption("Mark", function()
|
||||
PLUGIN:LogMark(pos, record)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
for _, v in pairs(dmenu:GetChildren()[1]:GetChildren()) do
|
||||
v:SetFont("MenuFontNoClamp")
|
||||
end
|
||||
end
|
||||
|
||||
self:FillLogs()
|
||||
|
||||
self:CreatePagination()
|
||||
self:CreateLeftSideButtons()
|
||||
end
|
||||
|
||||
function PANEL:FillLogs(bPage)
|
||||
self.logs:Clear()
|
||||
|
||||
local logsData = self:GetCache(self.page) or self.requestedLogs
|
||||
|
||||
if (logsData) then
|
||||
if (istable(logsData)and !table.IsEmpty(logsData)) then
|
||||
for _, v in pairs(logsData) do
|
||||
local e = "UNKNOWN"
|
||||
v.id, v.datetime, v.steamid, v.text = v.id or e, v.datetime or e, v.steamid or e, v.text or e
|
||||
|
||||
local line = self.logs:AddLine(v.id, os.date("%Y-%m-%d %X", v.datetime), util.SteamIDFrom64(v.steamid), v.text)
|
||||
line.record = v
|
||||
line.type = v.log_type
|
||||
end
|
||||
|
||||
self:SetCache(self.page, table.Copy(logsData))
|
||||
elseif (isnumber(logsData)) then
|
||||
self.logs:AddLine("You have to wait "..logsData.." second(s) before next search.")
|
||||
end
|
||||
else
|
||||
self.logs:AddLine("Logs not found!")
|
||||
end
|
||||
|
||||
if (bPage) then
|
||||
self:UpdatePage(true)
|
||||
end
|
||||
|
||||
for k, v2 in pairs(self.logs:GetLines()) do
|
||||
for k2, v3 in pairs(v2:GetChildren()) do
|
||||
v3:SetFont("MenuFontNoClamp")
|
||||
end
|
||||
end
|
||||
|
||||
for k3, v4 in pairs(self.logs.Columns) do
|
||||
for k4, v5 in pairs(v4:GetChildren()) do
|
||||
v5:SetFont("MenuFontNoClamp")
|
||||
end
|
||||
end
|
||||
|
||||
for _, v in ipairs(self.logs.Lines) do
|
||||
v.Paint = function(panel, width, height)
|
||||
if (panel:IsSelected()) then
|
||||
SKIN.tex.Input.ListBox.Hovered(0, 0, width, height)
|
||||
elseif (panel.Hovered) then
|
||||
surface.SetDrawColor(34, 58, 112)
|
||||
surface.DrawRect(0, 0, width, height)
|
||||
elseif (panel.m_bAlt) then
|
||||
SKIN.tex.Input.ListBox.EvenLine(0, 0, width, height)
|
||||
end
|
||||
|
||||
if self.highlightDeathsKnockouts:GetChecked() then
|
||||
if v.type and v.type != "chat" and v:GetColumnText( 4 ):find("knocked") then
|
||||
surface.SetDrawColor(255, 218, 185, 10)
|
||||
surface.DrawRect(0, 0, width, height)
|
||||
end
|
||||
|
||||
if v:GetColumnText( 4 ):find("died") or v:GetColumnText( 4 ):find("death") or v:GetColumnText( 4 ):find("killed") then
|
||||
surface.SetDrawColor(255, 0, 0, 10)
|
||||
surface.DrawRect(0, 0, width, height)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, v2 in ipairs(v.Columns) do
|
||||
v2:SetTextColor(Color(255, 255, 255))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper paint function for outlined rectangles
|
||||
local function PaintStandard(self, w, h, alpha)
|
||||
surface.SetDrawColor(Color(0, 0, 0, alpha))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, 255 / 100 * 30))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
function PANEL:CreateLeftSideButtons()
|
||||
local function search(pnl)
|
||||
local curTime = CurTime()
|
||||
|
||||
if (pnl.nextClick and pnl.nextClick >= curTime) then
|
||||
LocalPlayer():Notify("Please wait before searching again!")
|
||||
return
|
||||
end
|
||||
|
||||
self:SetCache()
|
||||
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
self:RequestLogs()
|
||||
|
||||
pnl.nextClick = curTime + 3
|
||||
end
|
||||
self.afterTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.afterTextEntry, true, 32, false, false, false, "Time after")
|
||||
self.afterTextEntry:SetTooltip("Events after x long ago. Time example: 5y2d7w = 5 years, 2 days, and 7 weeks")
|
||||
self.afterTextEntry:SetEnterAllowed(true)
|
||||
self.afterTextEntry.OnEnter = search
|
||||
|
||||
self.afterTextEntry:SetValue(ix.option.Get("logDefaultTime"))
|
||||
|
||||
self.beforeTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.beforeTextEntry, true, 32, false, false, false, "Time before")
|
||||
self.beforeTextEntry:SetTooltip("Events before x long ago. Time example: 5y2d7w = 5 years, 2 days, and 7 weeks")
|
||||
self.beforeTextEntry:SetEnterAllowed(true)
|
||||
self.beforeTextEntry.OnEnter = search
|
||||
|
||||
self.descCheckBox = self.leftSide:Add("DCheckBoxLabel")
|
||||
self.descCheckBox:SetText("Reversed time order")
|
||||
self.descCheckBox:Dock(TOP)
|
||||
self.descCheckBox:SetFont("MenuFontNoClamp")
|
||||
self.descCheckBox:SetChecked(true)
|
||||
self.descCheckBox:DockMargin(2, 2, 2, 2)
|
||||
|
||||
self.descCheckBox.PerformLayout = function(panel)
|
||||
local x = panel.m_iIndent or 0
|
||||
|
||||
panel.Button:SetSize(SScaleMin(15 / 3), SScaleMin(15 / 3))
|
||||
panel.Button:SetPos(x, math.floor((panel:GetTall() - panel.Button:GetTall()) / 2))
|
||||
|
||||
panel.Label:SizeToContents()
|
||||
panel.Label:SetPos(x + panel.Button:GetWide() + SScaleMin(9 / 3), 0)
|
||||
end
|
||||
|
||||
self.highlightDeathsKnockouts = self.leftSide:Add("DCheckBoxLabel")
|
||||
self.highlightDeathsKnockouts:SetText("Highlight deaths/knockouts")
|
||||
self.highlightDeathsKnockouts:Dock(TOP)
|
||||
self.highlightDeathsKnockouts:SetFont("MenuFontNoClamp")
|
||||
self.highlightDeathsKnockouts:SetChecked(true)
|
||||
self.highlightDeathsKnockouts:DockMargin(2, 2, 2, 2)
|
||||
|
||||
self.highlightDeathsKnockouts.PerformLayout = function(panel)
|
||||
local x = panel.m_iIndent or 0
|
||||
|
||||
panel.Button:SetSize(SScaleMin(15 / 3), SScaleMin(15 / 3))
|
||||
panel.Button:SetPos(x, math.floor((panel:GetTall() - panel.Button:GetTall()) / 2))
|
||||
|
||||
panel.Label:SizeToContents()
|
||||
panel.Label:SetPos(x + panel.Button:GetWide() + SScaleMin(9 / 3), 0)
|
||||
end
|
||||
|
||||
self.steamidTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.steamidTextEntry, true, 32, false, false, false, "SteamID")
|
||||
self.steamidTextEntry:SetTooltip("Events commited by the player with this SteamID")
|
||||
self.steamidTextEntry:SetEnterAllowed(true)
|
||||
self.steamidTextEntry.OnEnter = search
|
||||
|
||||
self.distanceTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.distanceTextEntry, true, 32, false, false, false, "Distance")
|
||||
self.distanceTextEntry:SetTooltip("Events on the x distance from your current position. Leave empty for global")
|
||||
self.distanceTextEntry:SetEnterAllowed(true)
|
||||
self.distanceTextEntry.OnEnter = search
|
||||
|
||||
self.textTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.textTextEntry, true, 32, false, false, false, "Text")
|
||||
self.textTextEntry:SetTooltip("Part of the log text")
|
||||
self.textTextEntry:SetEnterAllowed(true)
|
||||
self.textTextEntry.OnEnter = search
|
||||
|
||||
self.mapTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.mapTextEntry, true, 32, false, false, false, "Map")
|
||||
self.mapTextEntry:SetTooltip("Event on the specific map. Leave empty for current")
|
||||
self.mapTextEntry:SetEnterAllowed(true)
|
||||
self.mapTextEntry.OnEnter = search
|
||||
|
||||
if (!table.IsEmpty(self.requestedLogTypes)) then
|
||||
self.logTypeCombo = self.leftSide:Add("DComboBox")
|
||||
self.logTypeCombo:SetValue("Log type")
|
||||
self.logTypeCombo:SetTall(SScaleMin(32 / 3))
|
||||
self.logTypeCombo:Dock(TOP)
|
||||
self.logTypeCombo:SetFont("MenuFontNoClamp")
|
||||
|
||||
self.logTypeCombo.Think = function(comboBox)
|
||||
if (comboBox:IsMenuOpen()) then
|
||||
comboBox.Menu:SetMaxHeight(ScrH() * 0.4)
|
||||
end
|
||||
end
|
||||
|
||||
local logTypes = self.requestedLogTypes
|
||||
|
||||
self.logTypeCombo:AddChoice("raw")
|
||||
for _, v in pairs(logTypes) do
|
||||
self.logTypeCombo:AddChoice(v)
|
||||
end
|
||||
end
|
||||
|
||||
local searchButton = self.leftSide:Add("DButton")
|
||||
searchButton:Dock(BOTTOM)
|
||||
searchButton:SetText("Search")
|
||||
searchButton:SetFont("MenuFontNoClamp")
|
||||
searchButton:SetTall(SScaleMin(32 / 3))
|
||||
searchButton:DockMargin(4, 4, 4, 4)
|
||||
searchButton:SetContentAlignment(5)
|
||||
|
||||
searchButton.Paint = function(panel, w, h)
|
||||
PaintStandard(panel, w, h, 100)
|
||||
end
|
||||
|
||||
searchButton.DoClick = search
|
||||
end
|
||||
|
||||
function PANEL:RequestLogs()
|
||||
local data = {
|
||||
before = ix.util.GetStringTime(self.beforeTextEntry:GetValue()),
|
||||
after = ix.util.GetStringTime(self.afterTextEntry:GetValue()),
|
||||
steamid = self.steamidTextEntry:GetValue(),
|
||||
distance = tonumber(self.distanceTextEntry:GetValue()),
|
||||
text = self.textTextEntry:GetValue(),
|
||||
logType = self.logTypeCombo:GetSelected(),
|
||||
map = self.mapTextEntry:GetValue(),
|
||||
logsPerPage = 25,
|
||||
currentPage = self.page,
|
||||
desc = self.descCheckBox:GetChecked()
|
||||
}
|
||||
|
||||
netstream.Start("ixRequestLogs", data)
|
||||
end
|
||||
|
||||
function PANEL:CreatePagination()
|
||||
local paginationBg = self.rightSide:Add("DPanel")
|
||||
paginationBg:Dock(BOTTOM)
|
||||
paginationBg:DockMargin(4, 4, 4, 4)
|
||||
paginationBg:SetTall(SScaleMin(32 / 3))
|
||||
|
||||
self.firstPage = paginationBg:Add("DButton")
|
||||
self.firstPage:Dock(LEFT)
|
||||
self.firstPage:SetText("RETURN TO FIRST PAGE")
|
||||
self.firstPage:SetTextColor(color_white)
|
||||
self.firstPage:SetFont("MenuFontNoClamp")
|
||||
self.firstPage:SizeToContents()
|
||||
self.firstPage:SetWide(self.firstPage:GetWide() + SScaleMin(20 / 3))
|
||||
self.firstPage:DockMargin(4, 4, 4, 4)
|
||||
self.firstPage.DoClick = function(btn)
|
||||
local curTime = CurTime()
|
||||
|
||||
if (btn.nextClick and btn.nextClick >= curTime) then return end
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
self.page = 1
|
||||
|
||||
self:UpdatePage()
|
||||
|
||||
btn.nextClick = curTime + 1
|
||||
end
|
||||
|
||||
self.pagePrev = paginationBg:Add("DButton")
|
||||
self.pagePrev:Dock(LEFT)
|
||||
self.pagePrev:SetText("<")
|
||||
self.pagePrev:SetTextColor(color_white)
|
||||
self.pagePrev:SetFont("MenuFontNoClamp")
|
||||
self.pagePrev:DockMargin(4, 4, 4, 4)
|
||||
self.pagePrev.DoClick = function(btn)
|
||||
local curTime = CurTime()
|
||||
|
||||
if (btn.nextClick and btn.nextClick >= curTime) then return end
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
self.page = self.page - 1
|
||||
|
||||
self:UpdatePage()
|
||||
|
||||
btn.nextClick = curTime + 1
|
||||
end
|
||||
|
||||
self.pageLabel = paginationBg:Add("DLabel")
|
||||
self.pageLabel:SetText("Page #"..self.page)
|
||||
self.pageLabel:SetFont("MenuFontNoClamp")
|
||||
self.pageLabel:SetContentAlignment(5)
|
||||
self.pageLabel:Dock(FILL)
|
||||
|
||||
self.pageNext = paginationBg:Add("DButton")
|
||||
self.pageNext:Dock(RIGHT)
|
||||
self.pageNext:SetText(">")
|
||||
self.pageNext:SetFont("MenuFontNoClamp")
|
||||
self.pageNext:SetTextColor(color_white)
|
||||
self.pageNext:DockMargin(4, 4, 4, 4)
|
||||
self.pageNext.DoClick = function(btn)
|
||||
local curTime = CurTime()
|
||||
|
||||
if (btn.nextClick and btn.nextClick >= curTime) then return end
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
self.page = self.page + 1
|
||||
|
||||
self:UpdatePage()
|
||||
|
||||
btn.nextClick = curTime + 1
|
||||
end
|
||||
|
||||
self:UpdatePage(true)
|
||||
|
||||
-- Page field
|
||||
self.pageTextEntry = paginationBg:Add("DTextEntry")
|
||||
self.pageTextEntry:Dock(RIGHT)
|
||||
self.pageTextEntry:SetFont("MenuFontNoClamp")
|
||||
self.pageTextEntry:SetNumeric(true)
|
||||
self.pageTextEntry:SetTall(SScaleMin(32 / 3))
|
||||
self.pageTextEntry:DockMargin(4, 4, 4, 4)
|
||||
self.pageTextEntry:SetWide(SScaleMin(100 / 3))
|
||||
|
||||
-- Goto page x button
|
||||
local gotoPage = paginationBg:Add("DButton")
|
||||
gotoPage:Dock(RIGHT)
|
||||
gotoPage:SetText("GOTO PAGE")
|
||||
gotoPage:SetFont("MenuFontNoClamp")
|
||||
gotoPage:SetTextColor(color_white)
|
||||
gotoPage:DockMargin(4, 4, 4, 4)
|
||||
gotoPage:SetWide(SScaleMin(100 / 3))
|
||||
gotoPage.DoClick = function(btn)
|
||||
local curTime = CurTime()
|
||||
|
||||
if (btn.nextClick and btn.nextClick >= curTime) then return end
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
local page = tonumber(self.pageTextEntry:GetValue())
|
||||
|
||||
if (page and page > 0) then
|
||||
self.page = page
|
||||
|
||||
self:UpdatePage()
|
||||
end
|
||||
|
||||
btn.nextClick = curTime + 1
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:UpdatePage(bNoRequest)
|
||||
self.pagePrev:SetDisabled(self.page == 1)
|
||||
self.pageNext:SetDisabled(table.Count(self.logs:GetLines()) < 25)
|
||||
self.pageLabel:SetText("Page #"..self.page)
|
||||
|
||||
if (!bNoRequest) then
|
||||
if (self:GetCache(self.page)) then
|
||||
self:FillLogs(true)
|
||||
else
|
||||
self:RequestLogs()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateTextEntry(parent, bDockTop, height, bMultiline, bScrollbar, bEnter, placeholderText)
|
||||
parent:SetPlaceholderText(placeholderText)
|
||||
parent:Dock(TOP)
|
||||
parent:SetTall(SScaleMin(height / 3))
|
||||
parent:SetMultiline(bMultiline)
|
||||
parent:SetVerticalScrollbarEnabled(bScrollbar)
|
||||
parent:SetEnterAllowed(bEnter)
|
||||
parent:SetTextColor(Color(200, 200, 200, 255))
|
||||
parent:SetCursorColor(Color(200, 200, 200, 255))
|
||||
parent:SetFont("MenuFontNoClamp")
|
||||
parent:SetPlaceholderColor(Color(200, 200, 200, 200))
|
||||
|
||||
if bDockTop then
|
||||
parent:DockMargin(2, 2, 2, 2)
|
||||
end
|
||||
|
||||
parent.Paint = function(panel, w, h)
|
||||
if bMultiline then
|
||||
surface.SetDrawColor(Color(25, 25, 25, 255))
|
||||
else
|
||||
surface.SetDrawColor(Color(0, 0, 0, 100))
|
||||
end
|
||||
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, 255 / 100 * 30))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
|
||||
if (panel.GetPlaceholderText and panel.GetPlaceholderColor and panel:GetPlaceholderText() and panel:GetPlaceholderText():Trim() != "" and panel:GetPlaceholderColor() and (!panel:GetText() or panel:GetText() == "")) then
|
||||
local oldText = panel:GetText()
|
||||
local str = panel:GetPlaceholderText()
|
||||
if (str:StartWith("#")) then str = str:utf8sub(2) end
|
||||
str = language.GetPhrase(str)
|
||||
|
||||
panel:SetText(str)
|
||||
panel:DrawTextEntryText(panel:GetPlaceholderColor(), panel:GetHighlightColor(), panel:GetCursorColor())
|
||||
panel:SetText(oldText)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
panel:DrawTextEntryText(panel:GetTextColor(), panel:GetHighlightColor(), panel:GetCursorColor())
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:GetCache(page)
|
||||
local client = LocalPlayer()
|
||||
client.logCache = client.logCache or {}
|
||||
return client.logCache[page]
|
||||
end
|
||||
|
||||
function PANEL:SetCache(page, cache)
|
||||
local client = LocalPlayer()
|
||||
client.logCache = client.logCache or {}
|
||||
|
||||
if (page) then
|
||||
client.logCache[page] = cache
|
||||
else
|
||||
client.logCache = {}
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("ixLogs", PANEL, "EditablePanel")
|
||||
55
gamemodes/helix/plugins/betterlogs/sh_plugin.lua
Normal file
55
gamemodes/helix/plugins/betterlogs/sh_plugin.lua
Normal file
@@ -0,0 +1,55 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local CAMI = CAMI
|
||||
local LocalPlayer = LocalPlayer
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Better Logs"
|
||||
PLUGIN.author = "AleXXX_007"
|
||||
PLUGIN.description = "Saves logs in a database and allows permitted staff to look them up."
|
||||
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Logs",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Tp",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optLogDefaultTime = "Domyślny czas wyszukiwania logów",
|
||||
optdLogDefaultTime = "Domyślny czas wyszukiwania w narzędziu wyszukiwania logów."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
optdLogDefaultTime = "La cantidad de tiempo por defecto para buscar en la herramienta de búsqueda de registros.",
|
||||
optLogDefaultTime = "Tiempo de búsqueda de registros por defecto"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
optLogDefaultTime = "Domyślny czas wyszukiwania logów",
|
||||
optdLogDefaultTime = "Domyślny czas wyszukiwania w narzędziu wyszukiwania logów."
|
||||
})
|
||||
|
||||
ix.option.Add("logDefaultTime", ix.type.string, "1d", {
|
||||
bNetworked = true,
|
||||
category = "administration",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage Logs", nil)
|
||||
end
|
||||
})
|
||||
182
gamemodes/helix/plugins/betterlogs/sv_hooks.lua
Normal file
182
gamemodes/helix/plugins/betterlogs/sv_hooks.lua
Normal file
@@ -0,0 +1,182 @@
|
||||
--[[
|
||||
| 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 table = table
|
||||
local IsValid = IsValid
|
||||
local game = game
|
||||
local os = os
|
||||
local math = math
|
||||
local ix = ix
|
||||
local file = file
|
||||
local netstream = netstream
|
||||
local CAMI = CAMI
|
||||
local CurTime = CurTime
|
||||
local pairs = pairs
|
||||
|
||||
do
|
||||
local HANDLER = {}
|
||||
|
||||
function HANDLER.Load()
|
||||
local query = mysql:Create("ix_logs")
|
||||
query:Create("id", "INT(11) UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("steamid", "VARCHAR(20) DEFAULT NULL")
|
||||
query:Create("char_id", "INT(11) DEFAULT NULL")
|
||||
query:Create("log_type", "TEXT DEFAULT NULL")
|
||||
query:Create("pos_x", "FLOAT(17) DEFAULT NULL")
|
||||
query:Create("pos_y", "FLOAT(17) DEFAULT NULL")
|
||||
query:Create("pos_z", "FLOAT(17) DEFAULT NULL")
|
||||
query:Create("map", "TEXT DEFAULT NULL")
|
||||
query:Create("datetime", "INT(11) UNSIGNED DEFAULT NULL")
|
||||
query:Create("text", "TEXT DEFAULT NULL")
|
||||
query:Create("lookup1", "TEXT DEFAULT NULL")
|
||||
query:Create("lookup2", "TEXT DEFAULT NULL")
|
||||
query:Create("lookup3", "TEXT DEFAULT NULL")
|
||||
query:PrimaryKey("id")
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function HANDLER.Write(client, message, flag, logType, args)
|
||||
local character = client and client.GetCharacter and client:GetCharacter()
|
||||
local count = args and table.Count(args) or 0
|
||||
|
||||
local query = mysql:Insert("ix_logs")
|
||||
if (IsValid(client)) then
|
||||
if (client:IsPlayer()) then
|
||||
query:Insert("steamid", client:SteamID64())
|
||||
end
|
||||
|
||||
local pos = client:GetPos()
|
||||
query:Insert("pos_x", pos.x)
|
||||
query:Insert("pos_y", pos.y)
|
||||
query:Insert("pos_z", pos.z)
|
||||
end
|
||||
|
||||
if (character) then
|
||||
query:Insert("char_id", character:GetID())
|
||||
end
|
||||
|
||||
query:Insert("log_type", logType or "raw")
|
||||
|
||||
query:Insert("map", game.GetMap())
|
||||
query:Insert("datetime", os.time())
|
||||
query:Insert("text", message)
|
||||
|
||||
if (count > 0) then
|
||||
for i = 1, math.min(3, count) do
|
||||
query:Insert("lookup"..i, args[i])
|
||||
end
|
||||
end
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
ix.log.RegisterHandler("Database", HANDLER)
|
||||
end
|
||||
|
||||
do
|
||||
local HANDLER = {}
|
||||
|
||||
function HANDLER.Load()
|
||||
file.CreateDir("helix/logs")
|
||||
end
|
||||
|
||||
function HANDLER.Write(client, message)
|
||||
file.Append("helix/logs/"..os.date("%Y-%m-%d")..".txt", os.date("[%X\t]")..message.."\r\n")
|
||||
end
|
||||
|
||||
ix.log.RegisterHandler("File", HANDLER)
|
||||
end
|
||||
|
||||
netstream.Hook("ixRequestLogTypes", function(client)
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Logs")) then return end
|
||||
netstream.Start(client, "ixSendLogTypes", table.GetKeys(ix.log.types))
|
||||
end)
|
||||
|
||||
netstream.Hook("ixRequestLogs", function(client, data)
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Logs")) then return end
|
||||
|
||||
local curTime = CurTime()
|
||||
|
||||
if (data) then
|
||||
local currentPage = data.currentPage or 1
|
||||
local logsPerPage = math.min(data.logsPerPage or 25, 25)
|
||||
local query = mysql:Select("ix_logs")
|
||||
query:Limit(logsPerPage)
|
||||
query:Offset((currentPage - 1) * logsPerPage)
|
||||
if (data.steamid and data.steamid != "") then
|
||||
if (string.find(data.steamid, ",", 1, true)) then
|
||||
local ids = string.Explode(",", string.gsub(data.steamid, "%s", ""), false)
|
||||
for k, v in ipairs(ids) do
|
||||
if (string.find(v, "^STEAM")) then
|
||||
ids[k] = util.SteamIDTo64(v)
|
||||
end
|
||||
end
|
||||
query:WhereIn("steamid", ids)
|
||||
else
|
||||
query:Where("steamid", string.find(data.steamid, "^STEAM") and util.SteamIDTo64(data.steamid) or data.steamid)
|
||||
end
|
||||
end
|
||||
|
||||
if (data.logType and data.logType != "" and ix.log.types[data.logType]) then
|
||||
query:Where("log_type", data.logType)
|
||||
end
|
||||
|
||||
if (data.distance and data.distance != 0) then
|
||||
local pos = client:GetPos()
|
||||
local x, y, z = pos.x, pos.y, pos.z
|
||||
local dist = data.distance * 0.5
|
||||
|
||||
query:Where("map", game.GetMap())
|
||||
query:WhereGTE("pos_x", x - dist)
|
||||
query:WhereGTE("pos_y", y - dist)
|
||||
query:WhereGTE("pos_z", z - dist)
|
||||
|
||||
query:WhereLTE("pos_x", x + dist)
|
||||
query:WhereLTE("pos_y", y + dist)
|
||||
query:WhereLTE("pos_z", z + dist)
|
||||
elseif (data.map != "") then
|
||||
query:Where("map", data.map)
|
||||
end
|
||||
|
||||
if (data.before and data.before != 0) then
|
||||
query:WhereLTE("datetime", os.time() - data.before)
|
||||
end
|
||||
|
||||
if (data.after and data.after != 0) then
|
||||
query:WhereGTE("datetime", os.time() - data.after)
|
||||
end
|
||||
|
||||
if (data.text and data.text != "") then
|
||||
query:WhereLike("text", data.text:utf8lower())
|
||||
end
|
||||
|
||||
if (data.desc) then
|
||||
query:OrderByDesc("datetime")
|
||||
end
|
||||
|
||||
query:Callback(function(result)
|
||||
if (result and table.Count(result) > 0) then
|
||||
netstream.Start(client, "ixSendLogs", result)
|
||||
else
|
||||
netstream.Start(client, "ixSendLogs", false)
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
|
||||
client.nextQuery = curTime + 5
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("ixLogTeleport", function(client, pos)
|
||||
if (CAMI.PlayerHasAccess(client, "Helix - Tp", nil)) then
|
||||
client:SetPos(pos)
|
||||
else
|
||||
client:NotifyLocalized("notAllowed")
|
||||
end
|
||||
end)
|
||||
204
gamemodes/helix/plugins/betterobserver/cl_hooks.lua
Normal file
204
gamemodes/helix/plugins/betterobserver/cl_hooks.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
--[[
|
||||
| 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 IsValid = IsValid
|
||||
local LocalPlayer = LocalPlayer
|
||||
local DynamicLight = DynamicLight
|
||||
local math = math
|
||||
local CurTime = CurTime
|
||||
local ix = ix
|
||||
local hook = hook
|
||||
local CAMI = CAMI
|
||||
local ScrW = ScrW
|
||||
local ScrH = ScrH
|
||||
local pairs = pairs
|
||||
local ents = ents
|
||||
local string = string
|
||||
local ipairs = ipairs
|
||||
local surface = surface
|
||||
local ColorAlpha = ColorAlpha
|
||||
local table = table
|
||||
local Color = Color
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.dimDistance = 1024
|
||||
|
||||
net.Receive("ixObserverFlashlight", function(len, ply)
|
||||
LocalPlayer():EmitSound("buttons/lightswitch2.wav")
|
||||
end)
|
||||
|
||||
function PLUGIN:ShouldPopulateEntityInfo(entity)
|
||||
if (IsValid(entity) and
|
||||
(entity:IsPlayer() or IsValid(entity:GetNetVar("player"))) and
|
||||
(entity:GetMoveType() == MOVETYPE_NOCLIP and !entity:InVehicle())) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:DrawPhysgunBeam(client, physgun, enabled, target, bone, hitPos)
|
||||
if (client != LocalPlayer() and client:GetMoveType() == MOVETYPE_NOCLIP) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PrePlayerDraw(client)
|
||||
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:Think()
|
||||
if (!LocalPlayer():GetLocalVar("observerLight") or ix.option.Get("observerFullBright", false)) then return end
|
||||
|
||||
local dlight = DynamicLight(LocalPlayer():EntIndex())
|
||||
if (dlight) then
|
||||
local trace = LocalPlayer():GetEyeTraceNoCursor()
|
||||
dlight.pos = LocalPlayer():GetShootPos() + LocalPlayer():EyeAngles():Forward() * -100
|
||||
dlight.r = 255
|
||||
dlight.g = 255
|
||||
dlight.b = 255
|
||||
dlight.brightness = math.Remap(math.Clamp(trace.HitPos:DistToSqr(LocalPlayer():EyePos()), 100, 10000), 100, 10000, 0.01, 1)
|
||||
dlight.Decay = 20000
|
||||
dlight.Size = 2000
|
||||
dlight.DieTime = CurTime() + 0.1
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ThirdPersonToggled(oldValue, value)
|
||||
if (value and LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP and !LocalPlayer():InVehicle()) then
|
||||
ix.option.Set("thirdpersonEnabled", false)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:HUDPaint()
|
||||
local client = LocalPlayer()
|
||||
|
||||
local drawESP = hook.Run("ShouldDrawAdminESP")
|
||||
if (drawESP == nil) then
|
||||
drawESP = ix.option.Get("observerESP", true) and client:GetMoveType() == MOVETYPE_NOCLIP and
|
||||
!client:InVehicle() and CAMI.PlayerHasAccess(client, "Helix - Observer", nil)
|
||||
end
|
||||
|
||||
if (drawESP) then
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
local marginX, marginY = scrH * .1, scrH * .1
|
||||
self:DrawPlayerESP(client, scrW, scrH)
|
||||
|
||||
if (ix.observer:ShouldRenderAnyTypes() and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer Extra ESP", nil)) then
|
||||
for _, ent in pairs(ents.GetAll()) do
|
||||
if (!IsValid(ent)) then continue end
|
||||
|
||||
local class = string.lower(ent:GetClass())
|
||||
if (ix.observer.types[class] and ix.option.Get(ix.observer.types[class][1])) then
|
||||
local screenPosition = ent:GetPos():ToScreen()
|
||||
local x, y = math.Clamp(screenPosition.x, marginX, scrW - marginX), math.Clamp(screenPosition.y, marginY, scrH - marginY)
|
||||
if ((x != screenPosition.x or screenPosition.y != y) and !ix.observer.types[class][3]) then
|
||||
continue
|
||||
end
|
||||
|
||||
local distance = client:GetPos():Distance(ent:GetPos())
|
||||
local factor = 1 - math.Clamp(distance / self.dimDistance, 0, 1)
|
||||
ix.observer.types[class][2](client, ent, x, y, factor, distance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local points = {}
|
||||
hook.Run("DrawPointESP", points)
|
||||
for _, v in ipairs(points) do
|
||||
local screenPosition = v[1]:ToScreen()
|
||||
local x, y = math.Clamp(screenPosition.x, marginX, scrW - marginX), math.Clamp(screenPosition.y, marginY, scrH - marginY)
|
||||
|
||||
local distance = client:GetPos():Distance(v[1])
|
||||
local alpha = math.Remap(math.Clamp(distance, v[4] or 1500, v[5] or 2000), v[4] or 1500, v[4] or 2000, 255, v[6] or 0)
|
||||
local size = math.Remap(math.Clamp(distance, 0, v[5] or 2000), v[4] or 1500, v[4] or 2000, 10, 2)
|
||||
local drawColor = v[3] or color_white
|
||||
|
||||
surface.SetDrawColor(drawColor.r, drawColor.g, drawColor.b, alpha)
|
||||
surface.SetFont("ixGenericFont")
|
||||
surface.DrawRect(x - size / 2, y - size / 2, size, size)
|
||||
ix.util.DrawText(v[2], x, y - (size + 5), ColorAlpha(drawColor, alpha), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:DrawPointESP(points)
|
||||
if (ix.option.Get("mapscenesESP")) then
|
||||
local scenes = hook.Run("GetMapscenes")
|
||||
|
||||
if (scenes and !table.IsEmpty(scenes)) then
|
||||
for k, v in pairs(scenes) do
|
||||
points[#points + 1] = {v[1], "Mapscene #"..k..", "..v[3], Color(50, 191, 179)}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local blacklist = {
|
||||
["ix_hands"] = true,
|
||||
["ix_keys"] = true,
|
||||
["gmod_tool"] = true,
|
||||
["weapon_physgun"] = true,
|
||||
}
|
||||
|
||||
function PLUGIN:GetPlayerESPText(client, toDraw, distance, alphaFar, alphaMid, alphaClose)
|
||||
toDraw[#toDraw + 1] = {alpha = alphaMid, priority = 11, text = client:SteamName()}
|
||||
|
||||
local weapon = client:GetActiveWeapon()
|
||||
if (IsValid(weapon) and !blacklist[weapon:GetClass()]) then
|
||||
toDraw[#toDraw + 1] = {alpha = alphaMid, priority = 15, text = "Weapon: "..weapon:GetClass()}
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PreRender()
|
||||
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then
|
||||
render.SetLightingMode(1)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PreDrawHUD()
|
||||
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then
|
||||
render.SetLightingMode(0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Overriding the SF2 PostDraw2DSkyBox hook otherwise fullbright doesn't work.
|
||||
hook.Add("PostDraw2DSkyBox", "StormFox2.SkyBoxRender", function()
|
||||
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then return end
|
||||
|
||||
if (!StormFox2 or !StormFox2.Loaded or !StormFox2.Setting.SFEnabled()) then return end
|
||||
if (!StormFox2.util or !StormFox2.Sun or !StormFox2.Moon or !StormFox2.Moon.GetAngle) then return end
|
||||
|
||||
local c_pos = StormFox2.util.RenderPos()
|
||||
local sky = StormFox2.Setting.GetCache("enable_skybox", true)
|
||||
local use_2d = StormFox2.Setting.GetCache("use_2dskybox",false)
|
||||
cam.Start3D(Vector(0, 0, 0), EyeAngles(), nil, nil, nil, nil, nil, 1, 32000) -- 2d maps fix
|
||||
render.OverrideDepthEnable(false,false)
|
||||
render.SuppressEngineLighting(true)
|
||||
render.SetLightingMode(2)
|
||||
if (!use_2d or !sky) then
|
||||
hook.Run("StormFox2.2DSkybox.StarRender", c_pos)
|
||||
|
||||
-- hook.Run("StormFox2.2DSkybox.BlockStarRender",c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.SunRender", c_pos) -- No need to block, shrink the sun.
|
||||
|
||||
hook.Run("StormFox2.2DSkybox.Moon", c_pos)
|
||||
end
|
||||
hook.Run("StormFox2.2DSkybox.CloudBox", c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.CloudLayer", c_pos)
|
||||
hook.Run("StormFox2.2DSkybox.FogLayer", c_pos)
|
||||
render.SuppressEngineLighting(false)
|
||||
render.SetLightingMode(0)
|
||||
render.OverrideDepthEnable( false, false )
|
||||
cam.End3D()
|
||||
|
||||
render.SetColorMaterial()
|
||||
end)
|
||||
224
gamemodes/helix/plugins/betterobserver/cl_plugin.lua
Normal file
224
gamemodes/helix/plugins/betterobserver/cl_plugin.lua
Normal file
@@ -0,0 +1,224 @@
|
||||
--[[
|
||||
| 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 Color = Color
|
||||
local CreateMaterial = CreateMaterial
|
||||
local net = net
|
||||
local os = os
|
||||
local cam = cam
|
||||
local hook = hook
|
||||
local player = player
|
||||
local team = team
|
||||
local Vector = Vector
|
||||
local surface = surface
|
||||
local table = table
|
||||
local render = render
|
||||
local util = util
|
||||
local ix = ix
|
||||
local math = math
|
||||
local ipairs = ipairs
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.traceFilter = {nil, nil}
|
||||
|
||||
local extraColor = Color(200, 200, 200, 255)
|
||||
local mat1 = CreateMaterial("GA0249aSFJ3","VertexLitGeneric",{
|
||||
["$basetexture"] = "models/debug/debugwhite",
|
||||
["$model"] = 1,
|
||||
["$translucent"] = 1,
|
||||
["$alpha"] = 1,
|
||||
["$nocull"] = 1,
|
||||
["$ignorez"] = 1
|
||||
})
|
||||
|
||||
net.Receive("ixObserverDisableTP", function(len)
|
||||
if (ix.option.Get("thirdpersonEnabled")) then
|
||||
net.Start("ixObserverDisableTP")
|
||||
net.SendToServer()
|
||||
end
|
||||
ix.option.Set("thirdpersonEnabled", net.ReadBool())
|
||||
end)
|
||||
|
||||
do
|
||||
local npcColor = Color(255, 0, 128)
|
||||
local espColor = Color(255,255,255,255)
|
||||
local espColors = {
|
||||
["Weapons"] = Color(255,78,69),
|
||||
["Ammunition"] = Color(255,78,69),
|
||||
["Medical"] = Color(138,200,97),
|
||||
["Crafting"] = Color(255,204,0)
|
||||
}
|
||||
local minAlpha = {
|
||||
["Writing"] = 0,
|
||||
["Workbenches"] = 0,
|
||||
}
|
||||
local function itemESP(client, entity, x, y, factor, distance)
|
||||
local itemTable = entity:GetItemTable()
|
||||
if (!itemTable) then return end
|
||||
local color = espColors[itemTable.category] or espColor
|
||||
local alpha = math.Remap(math.Clamp(distance, 1500, 2000), 1500, 2000, 255, minAlpha[itemTable.category] or 45)
|
||||
if (alpha == 0) then return end
|
||||
|
||||
color.a = alpha
|
||||
|
||||
ix.util.DrawText(itemTable.name .. " (#" .. entity:GetNetVar("itemID", "nil ID") .. ")", x, y + math.max(10, 32 * factor), color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
|
||||
|
||||
local owner = entity:GetNetVar("ownerName")
|
||||
if (owner) then
|
||||
local time = (entity:GetNetVar("spawnTime") and " - "..math.ceil((os.time() - entity:GetNetVar("spawnTime")) / 60).."m") or ""
|
||||
alpha = math.Remap(math.Clamp(distance, 400, 700), 400, 700, 255, 0)
|
||||
espColor.a = alpha
|
||||
ix.util.DrawText(owner..time, x, y + math.max(10, 32 * factor) + 20, espColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
|
||||
end
|
||||
end
|
||||
ix.observer:RegisterESPType("ix_item", itemESP, "item")
|
||||
|
||||
local function npcESP(client, entity, x, y, factor)
|
||||
ix.util.DrawText(entity:GetClass(), x, y - math.max(10, 32 * factor), npcColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, math.max(255 * factor, 80))
|
||||
end
|
||||
ix.observer:RegisterESPType("ix_npc", npcESP, "npc", "Show NPC ESP", nil, true)
|
||||
|
||||
if (CLIENT) then
|
||||
local function containerESP(client, entity, x, y, factor, distance)
|
||||
local color = espColor
|
||||
local alpha = math.Remap(math.Clamp(distance, 500, 1000), 500, 1000, 255, 30)
|
||||
color.a = alpha
|
||||
|
||||
ix.util.DrawText("Container - "..entity:GetDisplayName().." #"..entity:EntIndex(), x, y - math.max(10, 32 * factor), color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
|
||||
end
|
||||
ix.observer:RegisterESPType("ix_container", containerESP, "container")
|
||||
end
|
||||
|
||||
local function staticESP(client, entity, x, y, factor, distance)
|
||||
if (distance > 2500) then return end
|
||||
|
||||
local alpha = math.Remap(math.Clamp(distance, 500, 2500), 500, 2500, 255, 45)
|
||||
espColor.a = alpha
|
||||
if (IsValid(entity) and entity:GetNetVar("Persistent", false)) then
|
||||
ix.util.DrawText(entity:GetModel(), x, y - math.max(10, 32 * factor), espColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
|
||||
end
|
||||
end
|
||||
ix.observer:RegisterESPType("prop_physics", staticESP, "static", "Show Static Prop ESP")
|
||||
end
|
||||
|
||||
local function sortFunc(a, b)
|
||||
if (a.alpha != b.alpha) then
|
||||
return a.alpha > b.alpha
|
||||
elseif (a.priority != b.priority) then
|
||||
return a.priority < b.priority
|
||||
else
|
||||
return a.text < b.text
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:DrawPlayerESP(client, scrW, scrH)
|
||||
local pos = client:EyePos()
|
||||
local marginX, marginY = scrW * .1, scrH * .1
|
||||
self.traceFilter[1] = client
|
||||
|
||||
local names = {}
|
||||
cam.Start3D()
|
||||
local targets = hook.Run("GetAdminESPTargets") or player.GetAll()
|
||||
for _, v in ipairs(targets) do
|
||||
if (v == client or !v:GetCharacter() or client:GetAimVector():Dot((v:GetPos() - pos):GetNormal()) < 0.65) then
|
||||
continue
|
||||
end
|
||||
|
||||
local bObserver = v:GetMoveType() == MOVETYPE_NOCLIP and !v:InVehicle()
|
||||
local teamColor = bObserver and Color(255, 85, 20, 255) or team.GetColor(v:Team())
|
||||
local vEyePos = v:EyePos()
|
||||
local distance = pos:Distance(vEyePos)
|
||||
|
||||
if ix.option.Get("observerPlayerHighlight") then
|
||||
self:RenderAdminESP(client, v, teamColor, pos, vEyePos, distance)
|
||||
end
|
||||
|
||||
names[#names + 1] = {v, teamColor, distance}
|
||||
end
|
||||
cam.End3D()
|
||||
|
||||
local right = client:GetRight() * 25
|
||||
for _, info in ipairs(names) do
|
||||
local ply, teamColor, distance = info[1], info[2], info[3]
|
||||
local plyPos = ply:GetPos()
|
||||
|
||||
local min, max = ply:GetModelRenderBounds()
|
||||
min = min + plyPos + right
|
||||
max = max + plyPos + right
|
||||
|
||||
local barMin = Vector((min.x + max.x) / 2, (min.y + max.y) / 2, min.z):ToScreen()
|
||||
local barMax = Vector((min.x + max.x) / 2, (min.y + max.y) / 2, max.z):ToScreen()
|
||||
local eyePos = ply:EyePos():ToScreen()
|
||||
local rightS = math.min(math.max(barMin.x, barMax.x), eyePos.x + 150)
|
||||
|
||||
local barWidth = math.Remap(math.Clamp(distance, 200, 2000), 500, 2000, 120, 75)
|
||||
local barHeight = math.abs(barMax.y - barMin.y)
|
||||
local barX, barY = math.Clamp(rightS, marginX, scrW - marginX - barWidth), math.Clamp(barMin.y - barHeight + 18, marginY, scrH - marginY)
|
||||
|
||||
local alphaFar = math.Remap(math.Clamp(distance, 1500, 2000), 1500, 2000, 255, 0)
|
||||
local alphaMid = math.Remap(math.Clamp(distance, 400, 700), 400, 700, 255, 0)
|
||||
local alphaClose = math.Remap(math.Clamp(distance, 200, 500), 200, 500, 255, 0)
|
||||
|
||||
local bArmor = ply:Armor() > 0
|
||||
surface.SetDrawColor(40, 40, 40, 200 * alphaFar / 255)
|
||||
surface.DrawRect(barX - 1, barY - 1, barWidth + 2, 5)
|
||||
if (bArmor) then surface.DrawRect(barX - 1, barY + 9, barWidth + 2, 5) end
|
||||
|
||||
surface.SetDrawColor(teamColor.r * 1.6, teamColor.g * 1.6, teamColor.b * 1.6, alphaFar)
|
||||
surface.DrawRect(barX, barY, barWidth * math.Clamp(ply:Health() / ply:GetMaxHealth(), 0, 1), 3)
|
||||
|
||||
local extraHeight = 0
|
||||
if (bArmor) then
|
||||
extraHeight = 10
|
||||
surface.SetDrawColor(255, 255, 255, alphaFar)
|
||||
surface.DrawRect(barX, barY + 10, barWidth * math.Clamp(ply:Armor() / 50, 0, 1), 3)
|
||||
end
|
||||
|
||||
surface.SetFont("WNBackFontNoClamp")
|
||||
ix.util.DrawText(ply:Name(), barX, barY - 13, teamColor, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, nil, 255)
|
||||
|
||||
if (ix.option.Get("steamESP")) then
|
||||
surface.SetFont("WNMenuFontNoClamp")
|
||||
local y = barY + extraHeight + 13
|
||||
local toDraw = {}
|
||||
hook.Run("GetPlayerESPText", ply, toDraw, distance, alphaFar, alphaMid, alphaClose)
|
||||
table.sort(toDraw, sortFunc)
|
||||
|
||||
for _, v in ipairs(toDraw) do
|
||||
if (v.alpha <= 0) then continue end
|
||||
|
||||
extraColor.a = v.alpha
|
||||
ix.util.DrawText(v.text, barX, y, v.color or extraColor, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, nil, v.alpha)
|
||||
|
||||
local _, txtHeight = surface.GetTextSize(v.text)
|
||||
y = y + txtHeight
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:RenderAdminESP(client, target, color, clientPos, targetEyePos, distance)
|
||||
render.SuppressEngineLighting(true)
|
||||
render.SetColorModulation(color.r / 255, color.g / 255, color.b / 255)
|
||||
|
||||
self.traceFilter[2] = target
|
||||
if (ix.option.Get("cheapBlur", false) or util.QuickTrace(clientPos, targetEyePos - clientPos, self.traceFilter).Fraction < 0.95) then
|
||||
render.SetBlend(1)
|
||||
else
|
||||
render.SetBlend(math.Remap(math.Clamp(distance, 200, 4000), 200, 8000, 0.05, 1))
|
||||
end
|
||||
render.MaterialOverride(mat1)
|
||||
target:DrawModel()
|
||||
|
||||
render.MaterialOverride()
|
||||
|
||||
render.SuppressEngineLighting(false)
|
||||
end
|
||||
48
gamemodes/helix/plugins/betterobserver/libs/sh_observer.lua
Normal file
48
gamemodes/helix/plugins/betterobserver/libs/sh_observer.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
|
||||
ix.observer = ix.observer or {}
|
||||
ix.observer.types = ix.observer.types or {}
|
||||
|
||||
function ix.observer:RegisterESPType(type, func, optionName, optionNiceName, optionDesc, bDrawClamped)
|
||||
optionName = string.lower(optionName)
|
||||
local editCapital = string.utf8sub(optionName, 1, 1)
|
||||
local capitalName = string.utf8upper(editCapital)..string.utf8sub(optionName, 2)
|
||||
|
||||
ix.option.Add(optionName.."ESP", ix.type.bool, false, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer Extra ESP")
|
||||
end
|
||||
})
|
||||
ix.lang.AddTable("english", {
|
||||
["opt"..capitalName.."ESP"] = optionNiceName or "Pokaż ESP "..capitalName,
|
||||
["optd"..capitalName.."ESP"] = optionDesc or "Włącz/wyłącz ESP "..optionName
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
["opt"..capitalName.."ESP"] = optionNiceName or "Pokaż ESP "..capitalName,
|
||||
["optd"..capitalName.."ESP"] = optionDesc or "Włącz/wyłącz ESP "..optionName
|
||||
})
|
||||
|
||||
ix.observer.types[string.lower(type)] = {optionName.."ESP", func, bDrawClamped}
|
||||
end
|
||||
|
||||
function ix.observer:ShouldRenderAnyTypes()
|
||||
for _, v in pairs(ix.observer.types) do
|
||||
if (ix.option.Get(v[1])) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
118
gamemodes/helix/plugins/betterobserver/sh_plugin.lua
Normal file
118
gamemodes/helix/plugins/betterobserver/sh_plugin.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local CAMI = CAMI
|
||||
local LocalPlayer = LocalPlayer
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Better Observer"
|
||||
PLUGIN.author = "Chessnut & Gr4Ss"
|
||||
PLUGIN.description = "Adds on to the no-clip mode to prevent intrusion. Edited for WN by Gr4Ss."
|
||||
|
||||
ix.plugin.SetUnloaded("observer", true)
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Observer",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Observer Extra ESP",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
ix.option.Add("observerTeleportBack", ix.type.bool, true, {
|
||||
bNetworked = true,
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("observerESP", ix.type.bool, true, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("steamESP", ix.type.bool, true, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("mapscenesESP", ix.type.bool, false, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("alwaysObserverLight", ix.type.bool, true, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
|
||||
end,
|
||||
bNetworked = true
|
||||
})
|
||||
ix.option.Add("observerFullBright", ix.type.bool, false, {
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
|
||||
end,
|
||||
bNetworked = true
|
||||
})
|
||||
|
||||
ix.option.Add("observerPlayerHighlight", ix.type.bool, true, {
|
||||
bNetworked = true,
|
||||
category = "observer",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optSteamESP = "Wyświetla dodatkowe informacje w Admin ESP",
|
||||
optdSteamESP = "Wyświetla SteamID gracza oraz jego Zdrowie i Pancerz na Admin ESP",
|
||||
optMapscenesESP = "Wyświetla Sceny Mapy ESP",
|
||||
optdMapscenesESP = "Wyświetla lokalizacje Scen Mapy w Admin ESP.",
|
||||
optAlwaysObserverLight = "Zawsze włączaj światło Obserwatora",
|
||||
optdAlwaysObserverLight = "Włącza światło Obserwatora automatycznie przejściu do trybu Obserwatora. W przeciwnym razie będzie podążać za twoją latarką. Światło nadal można wyłączyć ręcznie.",
|
||||
optObserverFullBright = "Full Bright jako światło Obserwatora",
|
||||
optdObserverFullBright = "Oświetlenie całej mapy po włączeniu światła Obserwatora.",
|
||||
optObserverPlayerHighlight = "Podświetl gracza",
|
||||
optdObserverPlayerHighlight = "Czy gracze dostają aura podświetlającą ich modele"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
optdSteamESP = "Muestra el SteamID de un jugador, su salud/armadura en el admin ESP",
|
||||
optdAlwaysObserverLight = "Enciende la luz del observer automáticamente al entrar en él. De lo contrario seguirá tu linterna. Se puede apagar manualmente.",
|
||||
optAlwaysObserverLight = "Encender siempre la luz del observer",
|
||||
optSteamESP = "Muestra la información extra del Admin ESP",
|
||||
optdMapscenesESP = "Mostrar las localizaciones de Escenarios del Mapa en el Admin-ESP.",
|
||||
optMapscenesESP = "Mostrar el ESP del Escenario"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
optSteamESP = "Wyświetla dodatkowe informacje w Admin ESP",
|
||||
optdSteamESP = "Wyświetla SteamID gracza oraz jego Zdrowie i Pancerz na Admin ESP",
|
||||
optMapscenesESP = "Wyświetla Sceny Mapy ESP",
|
||||
optdMapscenesESP = "Wyświetla lokalizacje Scen Mapy w Admin ESP.",
|
||||
optAlwaysObserverLight = "Zawsze włączaj światło Obserwatora",
|
||||
optdAlwaysObserverLight = "Włącza światło Obserwatora automatycznie przejściu do trybu Obserwatora. W przeciwnym razie będzie podążać za twoją latarką. Światło nadal można wyłączyć ręcznie.",
|
||||
optObserverFullBright = "Full Bright jako światło Obserwatora",
|
||||
optdObserverFullBright = "Oświetlenie całej mapy po włączeniu światła Obserwatora.",
|
||||
optObserverPlayerHighlight = "Podświetl gracza",
|
||||
optdObserverPlayerHighlight = "Czy gracze dostają aura podświetlającą ich modele"
|
||||
})
|
||||
173
gamemodes/helix/plugins/betterobserver/sv_plugin.lua
Normal file
173
gamemodes/helix/plugins/betterobserver/sv_plugin.lua
Normal file
@@ -0,0 +1,173 @@
|
||||
--[[
|
||||
| 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 util = util
|
||||
local string = string
|
||||
local CAMI = CAMI
|
||||
local timer = timer
|
||||
local Vector = Vector
|
||||
local net = net
|
||||
local os = os
|
||||
local ipairs = ipairs
|
||||
local ix = ix
|
||||
local hook = hook
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
util.AddNetworkString("ixObserverDisableTP")
|
||||
util.AddNetworkString("ixObserverFlashlight")
|
||||
|
||||
ix.log.AddType("observerEnter", function(client, ...)
|
||||
return string.format("%s entered observer.", client:Name())
|
||||
end)
|
||||
|
||||
ix.log.AddType("observerExit", function(client, ...)
|
||||
if (ix.option.Get(client, "observerTeleportBack", true)) then
|
||||
return string.format("%s exited observer.", client:Name())
|
||||
else
|
||||
return string.format("%s exited observer at their location.", client:Name())
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:CanPlayerEnterObserver(client)
|
||||
if (CAMI.PlayerHasAccess(client, "Helix - Observer", nil)) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanPlayerEnterVehicle(client, vehicle, role)
|
||||
if (client:GetMoveType() == MOVETYPE_NOCLIP) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerNoClip(client, state)
|
||||
if (hook.Run("CanPlayerEnterObserver", client) or (!state and client.ixObsData)) then
|
||||
if (state) then
|
||||
client.ixObsData = {client:GetPos(), client:EyeAngles()}
|
||||
|
||||
-- Hide them so they are not visible.
|
||||
client:SetNoDraw(true)
|
||||
client:SetNotSolid(true)
|
||||
client:DrawWorldModel(false)
|
||||
client:DrawShadow(false)
|
||||
client:GodEnable()
|
||||
client:SetNoTarget(true)
|
||||
|
||||
hook.Run("OnPlayerObserve", client, state)
|
||||
else
|
||||
if (client.ixObsData) then
|
||||
-- Move they player back if they want.
|
||||
if (ix.option.Get(client, "observerTeleportBack", true)) then
|
||||
local position, angles = client.ixObsData[1], client.ixObsData[2]
|
||||
|
||||
-- Do it the next frame since the player can not be moved right now.
|
||||
timer.Simple(0, function()
|
||||
client:SetPos(position)
|
||||
client:SetEyeAngles(angles)
|
||||
client:SetVelocity(Vector(0, 0, 0))
|
||||
end)
|
||||
end
|
||||
|
||||
client.ixObsData = nil
|
||||
end
|
||||
|
||||
-- Make the player visible again.
|
||||
client:SetNoDraw(false)
|
||||
client:SetNotSolid(false)
|
||||
client:DrawWorldModel(true)
|
||||
client:DrawShadow(true)
|
||||
client:GodDisable()
|
||||
client:SetNoTarget(false)
|
||||
|
||||
hook.Run("OnPlayerObserve", client, state)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnPlayerObserve(client, state)
|
||||
local flashlightOn = client:FlashlightIsOn()
|
||||
|
||||
if (state) then
|
||||
if (flashlightOn) then
|
||||
client:Flashlight(false)
|
||||
end
|
||||
client.ixObserverFlashlightReset = flashlightOn
|
||||
client.ixObserverRestoreTP = nil
|
||||
|
||||
if (ix.config.Get("thirdperson")) then
|
||||
net.Start("ixObserverDisableTP")
|
||||
net.WriteBool(false)
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
ix.log.Add(client, "observerEnter")
|
||||
else
|
||||
local flashlightState = client.ixObserverFlashlightReset
|
||||
client.ixObserverFlashlightReset = nil
|
||||
if (flashlightOn != flashlightState) then
|
||||
client:Flashlight(flashlightState)
|
||||
end
|
||||
|
||||
if (ix.config.Get("thirdperson") and client.ixObserverRestoreTP) then
|
||||
net.Start("ixObserverDisableTP")
|
||||
net.WriteBool(true)
|
||||
net.Send(client)
|
||||
end
|
||||
client.ixObserverRestoreTP = nil
|
||||
|
||||
ix.log.Add(client, "observerExit")
|
||||
end
|
||||
|
||||
client:SetLocalVar("observerLight", state and (ix.option.Get(client, "alwaysObserverLight") or flashlightOn))
|
||||
end
|
||||
|
||||
net.Receive("ixObserverDisableTP", function(len, client)
|
||||
if (ix.config.Get("thirdperson")) then
|
||||
client.ixObserverRestoreTP = true
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:PlayerSwitchFlashlight(client, state)
|
||||
if (!client.ixObsData or client.ixObserverFlashlightReset == nil) then return end
|
||||
|
||||
client:SetLocalVar("observerLight", !client:GetLocalVar("observerLight"))
|
||||
net.Start("ixObserverFlashlight")
|
||||
net.Send(client)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PLUGIN:OnItemSpawned(entity, bOnLoad)
|
||||
entity:SetNetVar("spawnTime", os.time())
|
||||
|
||||
local owner = entity:GetNetVar("owner")
|
||||
|
||||
if (owner and ix.char.loaded[owner]) then
|
||||
entity:SetNetVar("ownerName", ix.char.loaded[owner]:GetName())
|
||||
else
|
||||
entity:SetNetVar("ownerName", bOnLoad and "mapload" or "spawned")
|
||||
end
|
||||
|
||||
entity:SetNetVar("itemID", entity.ixItemID)
|
||||
end
|
||||
|
||||
function PLUGIN:OnSavedItemLoaded(items, entities)
|
||||
for _, v in ipairs(entities) do
|
||||
self:OnItemSpawned(v, true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character)
|
||||
client.ixObsData = nil
|
||||
client:SetLocalVar("observerLight", false)
|
||||
end
|
||||
174
gamemodes/helix/plugins/blind.lua
Normal file
174
gamemodes/helix/plugins/blind.lua
Normal file
@@ -0,0 +1,174 @@
|
||||
--[[
|
||||
| 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 = "Blind Commands"
|
||||
PLUGIN.author = "AleXXX_007"
|
||||
PLUGIN.description = "Adds commands to blind the players by making their screen completely black."
|
||||
|
||||
--luacheck: globals BLIND_NONE BLIND_TARGET BLIND_ALL
|
||||
BLIND_NONE = 0
|
||||
BLIND_TARGET = 1
|
||||
BLIND_ALL = 2
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Blind",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
ix.command.Add("PlyBlind", {
|
||||
description = "Blinds the specified player.",
|
||||
privilege = "Manage Blind",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
OnRun = function(self, client, target)
|
||||
if (IsValid(target)) then
|
||||
target:SetBlind(BLIND_TARGET)
|
||||
else
|
||||
client:NotifyLocalized("plyNotValid")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("PlyUnBlind", {
|
||||
description = "Unblinds the specified player.",
|
||||
privilege = "Manage Blind",
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
OnRun = function(self, client, target)
|
||||
if (IsValid(target)) then
|
||||
target:SetBlind(BLIND_NONE)
|
||||
else
|
||||
client:NotifyLocalized("plyNotValid")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
PLUGIN.blindAll = false
|
||||
|
||||
ix.command.Add("PlyBlindAll", {
|
||||
description = "Blinds all players on the server.",
|
||||
privilege = "Manage Blind",
|
||||
OnRun = function(self, client)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
v:SetBlind(BLIND_ALL)
|
||||
end
|
||||
|
||||
PLUGIN.blindAll = true
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("PlyUnBlindAll", {
|
||||
description = "Unblinds all players on the server.",
|
||||
privilege = "Manage Blind",
|
||||
OnRun = function(self, client)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
v:SetBlind(BLIND_NONE)
|
||||
end
|
||||
|
||||
PLUGIN.blindAll = false
|
||||
end
|
||||
})
|
||||
|
||||
ix.char.RegisterVar("blind", {
|
||||
field = "blind",
|
||||
fieldType = ix.type.number,
|
||||
default = BLIND_NONE,
|
||||
isLocal = true,
|
||||
bNoDisplay = true
|
||||
})
|
||||
|
||||
if (CLIENT) then
|
||||
local wasBlind = false
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
|
||||
function PLUGIN:HUDPaintBackground()
|
||||
if (self.blind) then
|
||||
local curTime = CurTime()
|
||||
local textTime = 5
|
||||
|
||||
local client = LocalPlayer()
|
||||
local reduceBlindness = client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()
|
||||
draw.RoundedBox(0, 0, 0, scrW, scrH, Color(0, 0, 0, reduceBlindness and 200 or 255))
|
||||
|
||||
if (!wasBlind) then
|
||||
wasBlind = curTime + textTime
|
||||
elseif (isnumber(wasBlind) and curTime < wasBlind) then
|
||||
local timeLeft = wasBlind - curTime
|
||||
local text = "You have been blinded by the administration."
|
||||
local font = "WNBleedingText"
|
||||
|
||||
surface.SetFont(font)
|
||||
local w, h = surface.GetTextSize(text)
|
||||
local x, y = scrW * 0.5, scrH * 0.75
|
||||
|
||||
draw.SimpleTextOutlined(text, font, x - w * 0.5, y - h * 0.5, Color(255, 255, 255, 510 * timeLeft * 0.4), nil, nil, 1, Color(0, 0, 0, 510 * timeLeft * 0.4))
|
||||
else
|
||||
wasBlind = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CharacterLoaded(character)
|
||||
local blind = character:GetBlind()
|
||||
|
||||
if (blind == BLIND_TARGET or PLUGIN.blindAll) then
|
||||
PLUGIN.blind = true
|
||||
elseif (blind == BLIND_ALL and !PLUGIN.blindAll) then
|
||||
PLUGIN.blind = false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDrawCrosshair()
|
||||
if (self.blind) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("ixBlindPlayer", function(blind)
|
||||
local delay = 1
|
||||
|
||||
blind = blind != BLIND_NONE
|
||||
|
||||
LocalPlayer():ScreenFade(blind and SCREENFADE.OUT or SCREENFADE.IN, Color(0, 0, 0, 255), delay, 0)
|
||||
|
||||
if (blind) then
|
||||
wasBlind = false
|
||||
|
||||
timer.Simple(delay, function()
|
||||
PLUGIN.blind = blind
|
||||
end)
|
||||
else
|
||||
PLUGIN.blind = blind
|
||||
end
|
||||
end)
|
||||
else
|
||||
local playerMeta = FindMetaTable("Player")
|
||||
|
||||
function playerMeta:SetBlind(blind)
|
||||
if (self:GetCharacter()) then
|
||||
self:GetCharacter():SetBlind(blind)
|
||||
end
|
||||
|
||||
netstream.Start(self, "ixBlindPlayer", blind)
|
||||
end
|
||||
|
||||
function playerMeta:GetBlind()
|
||||
if (self:GetCharacter()) then
|
||||
return self:GetCharacter():GetBlind()
|
||||
end
|
||||
|
||||
return BLIND_NONE
|
||||
end
|
||||
end
|
||||
21
gamemodes/helix/plugins/bodygroupmanager/LICENSE
Normal file
21
gamemodes/helix/plugins/bodygroupmanager/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Gary Tate
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
gamemodes/helix/plugins/bodygroupmanager/README.md
Normal file
11
gamemodes/helix/plugins/bodygroupmanager/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Bodygroup Manager
|
||||
|
||||
## Installation
|
||||
- Download the folder as a .zip
|
||||
- Place inside of your schema/plugins folder
|
||||
|
||||
## Preview
|
||||

|
||||
|
||||
## Support
|
||||
- Gary#1170
|
||||
20
gamemodes/helix/plugins/bodygroupmanager/cl_hooks.lua
Normal file
20
gamemodes/helix/plugins/bodygroupmanager/cl_hooks.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
--[[
|
||||
| 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 net = net
|
||||
local vgui = vgui
|
||||
|
||||
net.Receive("ixBodygroupView", function()
|
||||
local target = net.ReadEntity()
|
||||
local proxyColors = net.ReadTable()
|
||||
local panel = vgui.Create("ixBodygroupView")
|
||||
panel:SetViewModel(target:GetModel())
|
||||
panel:SetTarget(target, proxyColors)
|
||||
end)
|
||||
191
gamemodes/helix/plugins/bodygroupmanager/derma/cl_viewer.lua
Normal file
191
gamemodes/helix/plugins/bodygroupmanager/derma/cl_viewer.lua
Normal file
@@ -0,0 +1,191 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
--self:SetText("Bodygroup Manager")
|
||||
self:SetSize(SScaleMin(800 / 3), SScaleMin(800 / 3))
|
||||
self:Center()
|
||||
self:SetTitle("Bodygroup Manager")
|
||||
DFrameFixer(self)
|
||||
|
||||
self.clipboard = self:Add("DButton")
|
||||
self.clipboard:Dock(BOTTOM)
|
||||
self.clipboard:DockMargin(0, SScaleMin(4 / 3), 0, 0)
|
||||
self.clipboard:SetText("Save Changes")
|
||||
self.clipboard:SetFont("MenuFontBoldNoClamp")
|
||||
self.clipboard:SetTall(SScaleMin(50 / 3))
|
||||
self.clipboard.DoClick = function()
|
||||
LocalPlayer():NotifyLocalized("You have set this character's bodygroups.")
|
||||
local bodygroups = {}
|
||||
for _, v in pairs(self.bodygroupIndex) do
|
||||
table.insert(bodygroups, v.index, v.value)
|
||||
end
|
||||
|
||||
local color = self.colorPicker:GetValue()
|
||||
|
||||
net.Start("ixBodygroupTableSet")
|
||||
net.WriteEntity(self.target)
|
||||
net.WriteTable(bodygroups)
|
||||
net.WriteTable(color)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
self.model = self:Add("ixModelPanel")
|
||||
self.model.rotating = true
|
||||
self.model:Dock(FILL)
|
||||
self.model:SetFOV(35)
|
||||
self.model:SetModel(Model("models/props_junk/watermelon01.mdl"))
|
||||
|
||||
self.bodygroupPanel = self:Add("DScrollPanel")
|
||||
self.bodygroupPanel:Dock(RIGHT)
|
||||
self.bodygroupPanel:DockMargin(0, SScaleMin(7 / 3), 0, 0)
|
||||
self.bodygroupPanel:SetWide(self:GetWide() - SScaleMin(300 / 3))
|
||||
self.bodygroups = {}
|
||||
|
||||
PLUGIN.viewer = self
|
||||
end
|
||||
|
||||
function PANEL:SetTarget(target, proxyColors)
|
||||
self.target = target
|
||||
self:PopulateBodygroupOptions(proxyColors)
|
||||
self:SetTitle(target:GetName())
|
||||
|
||||
self.model.Entity.overrideProxyColors = proxyColors
|
||||
end
|
||||
|
||||
function PANEL:PopulateBodygroupOptions(proxyColors)
|
||||
self.bodygroupBox = {}
|
||||
self.bodygroupName = {}
|
||||
self.bodygroupPrevious = {}
|
||||
self.bodygroupNext = {}
|
||||
self.bodygroupIndex = {}
|
||||
|
||||
for k, v in pairs(self.target:GetBodyGroups()) do
|
||||
-- Disregard the model bodygroup.
|
||||
if !(v.id == 0) then
|
||||
local index = v.id
|
||||
|
||||
self.bodygroupBox[v.id] = self.bodygroupPanel:Add("DPanel")
|
||||
self.bodygroupBox[v.id]:Dock(TOP)
|
||||
self.bodygroupBox[v.id]:DockMargin(0, SScaleMin(25 / 3), SScaleMin(25 / 3), 0)
|
||||
self.bodygroupBox[v.id]:SetHeight(SScaleMin(50 / 3))
|
||||
self.bodygroupBox[v.id].Paint = function(self, w, h)
|
||||
surface.SetDrawColor(Color(40, 40, 40, 100))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
local hairBg = self.model.Entity:FindBodygroupByName("hair")
|
||||
|
||||
self.bodygroupName[v.id] = self.bodygroupBox[v.id]:Add("DLabel")
|
||||
self.bodygroupName[v.id].index = v.id
|
||||
self.bodygroupName[v.id]:SetText(v.name:gsub("^%l", string.utf8upper))
|
||||
self.bodygroupName[v.id]:SetFont("TitlesFontNoClamp")
|
||||
self.bodygroupName[v.id]:Dock(LEFT)
|
||||
self.bodygroupName[v.id]:DockMargin(SScaleMin(30 / 3), 0, 0, 0)
|
||||
self.bodygroupName[v.id]:SetWidth(SScaleMin(200 / 3))
|
||||
|
||||
self.bodygroupNext[v.id] = self.bodygroupBox[v.id]:Add("DButton")
|
||||
self.bodygroupNext[v.id].index = v.id
|
||||
self.bodygroupNext[v.id]:Dock(RIGHT)
|
||||
self.bodygroupNext[v.id]:SetFont("MenuFontNoClamp")
|
||||
self.bodygroupNext[v.id]:SetText("Next")
|
||||
self.bodygroupNext[v.id]:SetWide(SScaleMin(100 / 3))
|
||||
self.bodygroupNext[v.id].DoClick = function()
|
||||
local index = v.id
|
||||
if (self.model.Entity:GetBodygroupCount(index) - 1) <= self.bodygroupIndex[index].value then
|
||||
return
|
||||
end
|
||||
|
||||
self.bodygroupIndex[index].value = self.bodygroupIndex[index].value + 1
|
||||
self.bodygroupIndex[index]:SetText(self.bodygroupIndex[index].value)
|
||||
self.model.Entity:SetBodygroup(index, self.bodygroupIndex[index].value)
|
||||
|
||||
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
|
||||
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
|
||||
end
|
||||
|
||||
self.bodygroupIndex[v.id] = self.bodygroupBox[v.id]:Add("DLabel")
|
||||
self.bodygroupIndex[v.id].index = v.id
|
||||
self.bodygroupIndex[v.id].value = self.target:GetBodygroup(index)
|
||||
self.bodygroupIndex[v.id]:SetText(self.bodygroupIndex[v.id].value)
|
||||
self.bodygroupIndex[v.id]:SetFont("TitlesFontNoClamp")
|
||||
self.bodygroupIndex[v.id]:Dock(RIGHT)
|
||||
self.bodygroupIndex[v.id]:SetContentAlignment(5)
|
||||
|
||||
self.bodygroupPrevious[v.id] = self.bodygroupBox[v.id]:Add("DButton")
|
||||
self.bodygroupPrevious[v.id].index = v.id
|
||||
self.bodygroupPrevious[v.id]:Dock(RIGHT)
|
||||
self.bodygroupPrevious[v.id]:SetFont("MenuFontNoClamp")
|
||||
self.bodygroupPrevious[v.id]:SetText("Previous")
|
||||
self.bodygroupPrevious[v.id]:SetWide(SScaleMin(100 / 3))
|
||||
self.bodygroupPrevious[v.id].DoClick = function()
|
||||
local index = v.id
|
||||
if 0 == self.bodygroupIndex[index].value then
|
||||
return
|
||||
end
|
||||
|
||||
self.bodygroupIndex[index].value = self.bodygroupIndex[index].value - 1
|
||||
self.bodygroupIndex[index]:SetText(self.bodygroupIndex[index].value)
|
||||
self.model.Entity:SetBodygroup(index, self.bodygroupIndex[index].value)
|
||||
|
||||
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
|
||||
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
|
||||
end
|
||||
|
||||
self.model.Entity:SetBodygroup(index, self.target:GetBodygroup(index))
|
||||
|
||||
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
|
||||
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
|
||||
end
|
||||
end
|
||||
|
||||
local hairColor = self.bodygroupPanel:Add("DPanel")
|
||||
hairColor:Dock(TOP)
|
||||
hairColor:DockMargin(0, SScaleMin(25 / 3), SScaleMin(25 / 3), 0)
|
||||
hairColor:SetHeight(SScaleMin(50 / 3))
|
||||
hairColor.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(40, 40, 40, 100))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
self.colorPicker = hairColor:Add("ixSettingsRowColor")
|
||||
self.colorPicker:Dock(FILL)
|
||||
self.colorPicker:SetText("Hair Color")
|
||||
if proxyColors["HairColor"] then
|
||||
self.colorPicker:SetValue(proxyColors["HairColor"])
|
||||
end
|
||||
|
||||
self.colorPicker.OnValueChanged = function(this, newColor)
|
||||
proxyColors["HairColor"] = Color(newColor.r, newColor.g, newColor.b)
|
||||
|
||||
self.model.Entity.GetProxyColors = function()
|
||||
return proxyColors
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetViewModel(model)
|
||||
self.playerModel = model
|
||||
if model then
|
||||
self.model:SetModel(Model(model))
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("ixBodygroupView", PANEL, "DFrame")
|
||||
48
gamemodes/helix/plugins/bodygroupmanager/sh_plugin.lua
Normal file
48
gamemodes/helix/plugins/bodygroupmanager/sh_plugin.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local bit = bit
|
||||
local net = net
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Bodygroup Manager"
|
||||
PLUGIN.author = "Gary Tate"
|
||||
PLUGIN.description = "Allows players and administration to have an easier time customising bodygroups."
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
cmdEditBodygroup = "Dostosuj bodygroupy celu."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
cmdEditBodygroup = "Personalizar los bodygroups de un objetivo."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
cmdEditBodygroup = "Dostosuj bodygroupy celu."
|
||||
})
|
||||
|
||||
ix.command.Add("CharEditBodygroup", {
|
||||
description = "cmdEditBodygroup",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
bit.bor(ix.type.player, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, target)
|
||||
net.Start("ixBodygroupView")
|
||||
net.WriteEntity(target or client)
|
||||
net.WriteTable(target:GetCharacter():GetProxyColors() or {})
|
||||
net.Send(client)
|
||||
end
|
||||
})
|
||||
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
79
gamemodes/helix/plugins/bodygroupmanager/sv_hooks.lua
Normal file
79
gamemodes/helix/plugins/bodygroupmanager/sv_hooks.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
--[[
|
||||
| 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 util = util
|
||||
local ix = ix
|
||||
local string = string
|
||||
local net = net
|
||||
local IsValid = IsValid
|
||||
local pairs = pairs
|
||||
local tonumber = tonumber
|
||||
|
||||
util.AddNetworkString("ixBodygroupView")
|
||||
util.AddNetworkString("ixBodygroupTableSet")
|
||||
|
||||
ix.log.AddType("bodygroupEditor", function(client, target)
|
||||
return string.format("%s has changed %s's bodygroups.", client:GetName(), target:GetName())
|
||||
end)
|
||||
|
||||
net.Receive("ixBodygroupTableSet", function(length, client)
|
||||
if (!ix.command.HasAccess(client, "CharEditBodygroup") and !client:IsCombine()) then return end
|
||||
|
||||
local target = net.ReadEntity()
|
||||
|
||||
if (client:IsCombine() and !ix.command.HasAccess(client, "CharEditBodygroup") and target != client) then return end
|
||||
|
||||
if (!IsValid(target) or !target:IsPlayer() or !target:GetCharacter()) then
|
||||
return
|
||||
end
|
||||
|
||||
local bodygroups = net.ReadTable()
|
||||
|
||||
local groups = {}
|
||||
|
||||
for k, v in pairs(bodygroups) do
|
||||
target:SetBodygroup(tonumber(k) or 0, tonumber(v) or 0)
|
||||
groups[tonumber(k) or 0] = tonumber(v) or 0
|
||||
|
||||
local hairBG = client:FindBodygroupByName( "hair" )
|
||||
if k != hairBG then continue end
|
||||
if !client:GetModel():find("models/willardnetworks/citizens/") then continue end
|
||||
|
||||
local curHeadwearBG = client:GetBodygroup(client:FindBodygroupByName( "headwear" ))
|
||||
local curHairBG = client:GetBodygroup(hairBG)
|
||||
|
||||
local hairBgLength = 0
|
||||
for _, v2 in pairs(client:GetBodyGroups()) do
|
||||
if v2.name != "hair" then continue end
|
||||
if !v2.submodels then continue end
|
||||
if !istable(v2.submodels) then continue end
|
||||
|
||||
hairBgLength = #v2.submodels
|
||||
break
|
||||
end
|
||||
|
||||
if (curHeadwearBG != 0) then
|
||||
if curHairBG != 0 then
|
||||
client:SetBodygroup(hairBG, hairBgLength)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
target:GetCharacter():SetData("groups", groups)
|
||||
|
||||
local hairColor = net.ReadTable()
|
||||
local charProxies = target:GetCharacter():GetProxyColors() or {}
|
||||
|
||||
charProxies["HairColor"] = Color(hairColor.r, hairColor.g, hairColor.b)
|
||||
|
||||
target:GetCharacter():SetProxyColors(charProxies)
|
||||
|
||||
ix.log.Add(client, "bodygroupEditor", target)
|
||||
end)
|
||||
58
gamemodes/helix/plugins/charcreation/cl_plugin.lua
Normal file
58
gamemodes/helix/plugins/charcreation/cl_plugin.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
-- Glasses stuff
|
||||
ix.option.Add("useImmersiveGlasses", ix.type.bool, true, {
|
||||
category = "appearance",
|
||||
hidden = function()
|
||||
return ix.config.Get("forceImmersiveGlasses")
|
||||
end
|
||||
})
|
||||
|
||||
netstream.Hook("OpenBeardStyling", function(target)
|
||||
if (IsValid(ix.gui.menu)) then
|
||||
ix.gui.menu:Remove()
|
||||
end
|
||||
|
||||
if IsValid(beardStyling) then
|
||||
beardStyling:Remove()
|
||||
end
|
||||
|
||||
local beardStyling = vgui.Create("BeardStyling")
|
||||
beardStyling:CustomInit(target)
|
||||
end)
|
||||
|
||||
netstream.Hook("GetStylingConfirmation", function(attempter)
|
||||
Derma_Query("Ktoś chce stylizować twoje włosy/brodę! Czy chcesz na to pozwolić?", "Stylizacja włosów/brody", "Pozwól", function()
|
||||
netstream.Start("AcceptStyling", attempter)
|
||||
end, "Disallow")
|
||||
end)
|
||||
|
||||
-- Called when blurry screen space effects should be rendered.
|
||||
function PLUGIN:RenderScreenspaceEffects()
|
||||
if (!ix.config.Get("forceImmersiveGlasses")) then
|
||||
if (!ix.option.Get("useImmersiveGlasses", true)) then return end
|
||||
end
|
||||
|
||||
local client = LocalPlayer()
|
||||
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then return end
|
||||
|
||||
local character = client:GetCharacter()
|
||||
if (!character) then return end
|
||||
|
||||
local needsGlasses = character:GetGlasses()
|
||||
local hasGlasses = character:HasGlasses()
|
||||
if ((needsGlasses and !hasGlasses) or (!needsGlasses and hasGlasses)) then
|
||||
DrawToyTown(28,ScrH())
|
||||
end
|
||||
end
|
||||
463
gamemodes/helix/plugins/charcreation/derma/cl_beardstyling.lua
Normal file
463
gamemodes/helix/plugins/charcreation/derma/cl_beardstyling.lua
Normal file
@@ -0,0 +1,463 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
local padding = SScaleMin(10 / 3)
|
||||
|
||||
local function Paint(self, w, h)
|
||||
surface.SetDrawColor(Color(0, 0, 0, 100))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
function PANEL:CustomInit(target)
|
||||
self:SetSize(ScrW(), ScrH())
|
||||
|
||||
local background = self:Add("Panel")
|
||||
background:SetSize(self:GetSize())
|
||||
background.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(63, 58, 115, 220))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
Derma_DrawBackgroundBlur( this, 1 )
|
||||
end
|
||||
|
||||
self.innerContent = background:Add("EditablePanel")
|
||||
self.innerContent:SetSize(SScaleMin(1300 / 3), SScaleMin(900 / 3))
|
||||
self.innerContent:Center()
|
||||
self.innerContent:MakePopup()
|
||||
self.innerContent.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(0, 0, 0, 130)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
Schema:AllowMessage(self.innerContent)
|
||||
|
||||
self:DrawTopBar()
|
||||
self:DrawModel()
|
||||
self:DrawLeftSide()
|
||||
end
|
||||
|
||||
function PANEL:DrawTopBar()
|
||||
local topbar = self.innerContent:Add("Panel")
|
||||
topbar:SetSize(self.innerContent:GetWide(), SScaleMin(50 / 3))
|
||||
topbar:Dock(TOP)
|
||||
topbar.Paint = function( this, w, h )
|
||||
surface.SetDrawColor(0, 0, 0, 130)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
local titleText = topbar:Add("DLabel")
|
||||
titleText:SetFont("CharCreationBoldTitleNoClamp")
|
||||
titleText:Dock(LEFT)
|
||||
titleText:SetText("Stylizacja włosów/brody")
|
||||
titleText:DockMargin(SScaleMin(10 / 3), 0, 0, 0)
|
||||
titleText:SetContentAlignment(4)
|
||||
titleText:SizeToContents()
|
||||
|
||||
local exit = topbar:Add("DImageButton")
|
||||
exit:SetImage("willardnetworks/tabmenu/navicons/exit.png")
|
||||
exit:SetSize(SScaleMin(20 / 3), SScaleMin(20 / 3))
|
||||
exit:DockMargin(0, SScaleMin(15 / 3), SScaleMin(10 / 3), SScaleMin(15 / 3))
|
||||
exit:Dock(RIGHT)
|
||||
exit.DoClick = function()
|
||||
self:Remove()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DrawModel()
|
||||
local target = self.target or LocalPlayer()
|
||||
local characterModelList = self.innerContent:Add("Panel")
|
||||
characterModelList:Dock(RIGHT)
|
||||
characterModelList:SetWide(self.innerContent:GetWide() * 0.5)
|
||||
characterModelList.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(255, 255, 255, 30));
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
local imgBackground = characterModelList:Add("DImage")
|
||||
imgBackground:SetImage(ix.faction.indices[target:Team()].inventoryImage or
|
||||
"materials/willardnetworks/tabmenu/inventory/backgrounds/street.png")
|
||||
imgBackground:SetKeepAspect(true)
|
||||
imgBackground:Dock(FILL)
|
||||
imgBackground:DockMargin(1, 1, 1, 1)
|
||||
|
||||
self.characterModel = imgBackground:Add("ixModelPanel")
|
||||
self.characterModel:Dock(FILL)
|
||||
|
||||
local styleButton = characterModelList:Add("DButton")
|
||||
styleButton:Dock(BOTTOM)
|
||||
styleButton:SetFont("MenuFontLargerBoldNoFix")
|
||||
styleButton:SetText("Stylizuj")
|
||||
styleButton:SetTall(SScaleMin(50 / 3))
|
||||
styleButton.DoClick = function()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
local char = target.GetCharacter and target:GetCharacter()
|
||||
|
||||
local hairBG = target:FindBodygroupByName("hair")
|
||||
local beard = target:FindBodygroupByName("beard")
|
||||
|
||||
local curHair = target:GetBodygroup(hairBG)
|
||||
local curBeard = target:GetBodygroup(beard)
|
||||
local newHair = self.characterModel.Entity:GetBodygroup(hairBG)
|
||||
local newBeard = self.characterModel.Entity:GetBodygroup(beard)
|
||||
local curHairColor = char and char.GetHair and char:GetHair() and char:GetHair().color or color_white
|
||||
local newHairColor = self.hairColor
|
||||
|
||||
if (curHair != newHair) or (curBeard != newBeard) or (curHairColor and newHairColor and curHairColor != newHairColor) then
|
||||
netstream.Start("SetHairBeardBodygroup", newHair, newBeard, newHairColor, target)
|
||||
target:NotifyLocalized("Pomyślnie wystylizowałeś swoją brodę/włosy.")
|
||||
self:Remove()
|
||||
else
|
||||
target:NotifyLocalized("Nie możesz wybrać tych samych włosów/brody, jakie już masz!")
|
||||
end
|
||||
end
|
||||
|
||||
styleButton.Paint = function(this, w, h)
|
||||
Paint(this, w, h)
|
||||
end
|
||||
|
||||
self:SetCharacter()
|
||||
|
||||
local bone = self.characterModel.Entity:LookupBone("ValveBiped.Bip01_Head1")
|
||||
if bone then
|
||||
local eyepos = self.characterModel.Entity:GetBonePosition( bone )
|
||||
|
||||
self.characterModel:SetLookAt(eyepos)
|
||||
self.characterModel:SetCamPos(eyepos-Vector(-12, -12, 0)) -- Move cam in front of eyes
|
||||
self.characterModel:SetFOV(60)
|
||||
self.characterModel.PaintModel = self.characterModel.Paint
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetCharacter()
|
||||
local target = self.target or LocalPlayer()
|
||||
local model = target:GetModel()
|
||||
self.characterModel:SetModel(model, target:GetSkin(), true)
|
||||
|
||||
local isCP = model:find("wn7new")
|
||||
local indexName = isCP and "cp_Head" or "headwear"
|
||||
local index = target:FindBodygroupByName(indexName)
|
||||
|
||||
if index != -1 then
|
||||
if self.characterModel.Entity:GetBodygroup(index) > 0 then
|
||||
self.characterModel.Entity:SetBodygroup(index, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local curHair = target:GetCharacter():GetHair()
|
||||
local hairValue = curHair and curHair.hair
|
||||
if hairValue then
|
||||
local hairIndex = target:FindBodygroupByName("hair")
|
||||
self.characterModel.Entity:SetBodygroup(hairIndex, hairValue)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DrawLeftSide()
|
||||
self.leftSide = self.innerContent:Add("Panel")
|
||||
self.leftSide:Dock(LEFT)
|
||||
self.leftSide:SetWide(self.innerContent:GetWide() * 0.5)
|
||||
self.leftSide.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(255, 255, 255, 10));
|
||||
surface.DrawRect(0, 0, w, h )
|
||||
|
||||
surface.SetDrawColor(Color(255, 255, 255, 30));
|
||||
surface.DrawOutlinedRect(0, 0, w + 1, h)
|
||||
end
|
||||
|
||||
self:DrawShaveTrimButtons()
|
||||
|
||||
local beardBodygroup = self.characterModel.Entity:GetBodygroup(11)
|
||||
|
||||
local beardPart = self.leftSide:Add("DScrollPanel")
|
||||
beardPart:Dock(BOTTOM)
|
||||
beardPart:SetTall((beardBodygroup != 5 and beardBodygroup != 8 and SScaleMin(100 / 3)) or (self.innerContent:GetTall() - SScaleMin(50 / 3)) * 0.5)
|
||||
beardPart.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.DrawRect(0, 0, w, 1)
|
||||
end
|
||||
beardPart:DockMargin(0, padding * 3 - 1, 0, 0)
|
||||
|
||||
local hairParts = self.leftSide:Add("Panel")
|
||||
hairParts:Dock(FILL)
|
||||
|
||||
local hairPart = hairParts:Add("DScrollPanel")
|
||||
hairPart:Dock(LEFT)
|
||||
hairPart:SetWide(self.innerContent:GetWide() * 0.25)
|
||||
|
||||
local hairColorPart = hairParts:Add("DScrollPanel")
|
||||
hairColorPart:Dock(FILL)
|
||||
|
||||
local titleText = hairPart:Add("DLabel")
|
||||
titleText:SetFont("CharCreationBoldTitleNoClamp")
|
||||
titleText:Dock(TOP)
|
||||
titleText:SetText("Włosy")
|
||||
titleText:DockMargin(0, padding * 3 - 1, 0, 0)
|
||||
titleText:SetContentAlignment(5)
|
||||
titleText:SizeToContents()
|
||||
|
||||
local titleText2 = hairColorPart:Add("DLabel")
|
||||
titleText2:SetFont("CharCreationBoldTitleNoClamp")
|
||||
titleText2:Dock(TOP)
|
||||
titleText2:SetText("Kolor włosów")
|
||||
titleText2:DockMargin(0, padding * 3 - 1, 0, 0)
|
||||
titleText2:SetContentAlignment(5)
|
||||
titleText2:SizeToContents()
|
||||
|
||||
self:DrawHairButtons(hairPart, hairColorPart)
|
||||
|
||||
if beardBodygroup == 5 or beardBodygroup == 8 then
|
||||
self:DrawBeardButtons(beardPart)
|
||||
else
|
||||
local notEnough = beardPart:Add("DLabel")
|
||||
notEnough:SetText("Masz zbyt krótki zarost do stylizacji...")
|
||||
notEnough:SetFont("MenuFontLargerBoldNoFix")
|
||||
notEnough:SetContentAlignment(5)
|
||||
notEnough:Dock(TOP)
|
||||
notEnough:DockMargin(0, padding * 3 - 1, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DrawBeardButtons(beardPart)
|
||||
local target = self.target or LocalPlayer()
|
||||
for i = 1, 6 do
|
||||
local beardButton = beardPart:Add("DButton")
|
||||
local sideMargins = self.leftSide:GetWide() * 0.1
|
||||
beardButton:Dock(TOP)
|
||||
beardButton:SetTall(SScaleMin(50 / 3))
|
||||
beardButton:SetFont("MenuFontLargerBoldNoFix")
|
||||
beardButton:SetText("Styl "..i)
|
||||
beardButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
|
||||
|
||||
beardButton.DoClick = function()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
local beard = target:FindBodygroupByName("beard")
|
||||
if i == 5 then
|
||||
self.characterModel.Entity:SetBodygroup(beard, 6)
|
||||
elseif i == 6 then
|
||||
self.characterModel.Entity:SetBodygroup(beard, 7)
|
||||
else
|
||||
self.characterModel.Entity:SetBodygroup(beard, i)
|
||||
end
|
||||
end
|
||||
|
||||
beardButton.OnCursorEntered = function()
|
||||
surface.PlaySound("helix/ui/rollover.wav")
|
||||
end
|
||||
|
||||
beardButton.Paint = function(this, w, h)
|
||||
Paint(this, w, h)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DrawHairButtons(hairPart, hairColorPart)
|
||||
local target = self.target or LocalPlayer()
|
||||
local gender = target.GetCharacter and target:GetCharacter():GetGender() or "female"
|
||||
local hairs = {}
|
||||
for i = 0, gender == "female" and 14 or 12 do hairs[#hairs + 1] = i end
|
||||
|
||||
local curStyle = 0
|
||||
for _, hairID in pairs(hairs) do
|
||||
curStyle = curStyle + 1
|
||||
local hairButton = hairPart:Add("DButton")
|
||||
local sideMargins = self.leftSide:GetWide() * 0.1
|
||||
hairButton:Dock(TOP)
|
||||
hairButton:SetTall(SScaleMin(50 / 3))
|
||||
hairButton:SetFont("MenuFontLargerBoldNoFix")
|
||||
hairButton:SetText("Styl "..curStyle)
|
||||
hairButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
|
||||
|
||||
hairButton.DoClick = function()
|
||||
local hairBG = target:FindBodygroupByName("hair")
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
self.characterModel.Entity:SetBodygroup(hairBG, hairID)
|
||||
end
|
||||
|
||||
hairButton.OnCursorEntered = function()
|
||||
surface.PlaySound("helix/ui/rollover.wav")
|
||||
end
|
||||
|
||||
hairButton.Paint = function(this, w, h)
|
||||
Paint(this, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
for _, colorTable in pairs(ix.allowedHairColors) do
|
||||
for _, color in pairs(colorTable) do
|
||||
local hairButton = hairColorPart:Add("DButton")
|
||||
local sideMargins = self.leftSide:GetWide() * 0.1
|
||||
hairButton:Dock(TOP)
|
||||
hairButton:SetTall(SScaleMin(50 / 3))
|
||||
hairButton:SetFont("MenuFontLargerBoldNoFix")
|
||||
hairButton:SetText("")
|
||||
hairButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
|
||||
|
||||
hairButton.DoClick = function()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
|
||||
if self.characterModel and IsValid(self.characterModel) then
|
||||
self.characterModel.overrideProxyColors = {HairColor = Vector(color.r / 255, color.g / 255, color.b / 255)}
|
||||
end
|
||||
|
||||
self.hairColor = color
|
||||
end
|
||||
|
||||
hairButton.OnCursorEntered = function()
|
||||
surface.PlaySound("helix/ui/rollover.wav")
|
||||
end
|
||||
|
||||
hairButton.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(color)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:DrawShaveTrimButtons()
|
||||
local target = self.target or LocalPlayer()
|
||||
local buttonPanel = self.leftSide:Add("Panel")
|
||||
buttonPanel:Dock(BOTTOM)
|
||||
buttonPanel:SetTall(SScaleMin(50 / 3))
|
||||
|
||||
local shaveButton = buttonPanel:Add("DButton")
|
||||
shaveButton:Dock(FILL)
|
||||
shaveButton:SetText("Ogól")
|
||||
shaveButton:SetFont("MenuFontLargerBoldNoFix")
|
||||
shaveButton:SetWide(self.leftSide:GetWide() * 0.5)
|
||||
shaveButton.DoClick = function()
|
||||
surface.PlaySound("willardnetworks/charactercreation/boop1.wav")
|
||||
self:CreateWarningPanel()
|
||||
end
|
||||
|
||||
local gender = target.GetCharacter and target:GetCharacter():GetGender() or "female"
|
||||
if gender == "female" then shaveButton:SetDisabled(true) end
|
||||
|
||||
shaveButton.Paint = function(this, w, h)
|
||||
if gender == "female" then
|
||||
surface.SetDrawColor(255, 255, 255, 5)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
return
|
||||
end
|
||||
|
||||
Paint(this, w, h)
|
||||
end
|
||||
|
||||
local beardBodygroup = self.characterModel.Entity:GetBodygroup(11)
|
||||
|
||||
if beardBodygroup == 0 then
|
||||
shaveButton:SetDisabled(true)
|
||||
shaveButton.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(255, 255, 255, 5)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
shaveButton.OnCursorEntered = function() end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateWarningPanel()
|
||||
local warningPanel = vgui.Create("Panel")
|
||||
warningPanel:SetAlpha(0)
|
||||
warningPanel:MakePopup()
|
||||
warningPanel:SetSize(ScrW(), ScrH())
|
||||
warningPanel:AlphaTo(255, 0.5, 0)
|
||||
warningPanel.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(0, 0, 0, 230)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
local warningContent = warningPanel:Add("Panel")
|
||||
warningContent:SetSize(ScrW() * 0.4, SScaleMin(95 / 3))
|
||||
warningContent:Center()
|
||||
|
||||
local label = warningContent:Add("DLabel")
|
||||
label:SetFont("CharCreationBoldTitleNoClamp")
|
||||
label:SetText("Ta akcja usunie twój zarost, czy jesteś pewien?")
|
||||
label:SetContentAlignment(5)
|
||||
label:Dock(TOP)
|
||||
label:SizeToContents()
|
||||
|
||||
local warningButtons = warningContent:Add("Panel")
|
||||
warningButtons:Dock(TOP)
|
||||
warningButtons:DockMargin(0, padding, 0, 0)
|
||||
warningButtons:SetTall(SScaleMin(50 / 3))
|
||||
|
||||
local yes = warningButtons:Add("DButton")
|
||||
yes:Dock(LEFT)
|
||||
yes:SetWide(warningContent:GetWide() * 0.5)
|
||||
yes:SetText("TAK")
|
||||
yes:SetFont("CharCreationBoldTitleNoClamp")
|
||||
yes:SetContentAlignment(6)
|
||||
yes:SetTextColor(Color(200, 200, 200, 255))
|
||||
yes:SetTextInset(padding * 2, 0)
|
||||
yes.Paint = function(this, w, h)
|
||||
if this:IsHovered() then
|
||||
this:SetTextColor(Color(255, 255, 255, 255))
|
||||
else
|
||||
this:SetTextColor(Color(200, 200, 200, 255))
|
||||
end
|
||||
end
|
||||
|
||||
local target = self.target or LocalPlayer()
|
||||
|
||||
yes.DoClick = function()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
surface.PlaySound("npc/antlion/idle1.wav")
|
||||
warningPanel:AlphaTo(0, 0.5, 0, function()
|
||||
local beard = target:FindBodygroupByName("beard")
|
||||
warningPanel:Remove()
|
||||
self:Remove()
|
||||
|
||||
netstream.Start("RemoveBeardBodygroup", target)
|
||||
self.characterModel.Entity:SetBodygroup(beard, 0)
|
||||
end)
|
||||
end
|
||||
|
||||
local no = warningButtons:Add("DButton")
|
||||
no:Dock(RIGHT)
|
||||
no:SetWide(warningContent:GetWide() * 0.5)
|
||||
no:SetText("NIE")
|
||||
no:SetFont("CharCreationBoldTitleNoClamp")
|
||||
no:SetTextColor(Color(200, 200, 200, 255))
|
||||
no:SetContentAlignment(4)
|
||||
no:SetTextInset(padding * 2, 0)
|
||||
no.Paint = function(this, w, h)
|
||||
if this:IsHovered() then
|
||||
this:SetTextColor(Color(255, 255, 255, 255))
|
||||
else
|
||||
this:SetTextColor(Color(200, 200, 200, 255))
|
||||
end
|
||||
end
|
||||
no.DoClick = function()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
warningPanel:AlphaTo(0, 0.5, 0, function()
|
||||
warningPanel:Remove()
|
||||
end)
|
||||
end
|
||||
|
||||
yes.OnCursorEntered = function()
|
||||
surface.PlaySound("helix/ui/rollover.wav")
|
||||
end
|
||||
|
||||
no.OnCursorEntered = function()
|
||||
surface.PlaySound("helix/ui/rollover.wav")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetTarget(target)
|
||||
self.target = target
|
||||
end
|
||||
|
||||
vgui.Register("BeardStyling", PANEL, "EditablePanel")
|
||||
@@ -0,0 +1,30 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
ITEM.name = "[Odtwarzacz Audio] Naucz się czytać"
|
||||
ITEM.uniqueID = "audiobook_reading"
|
||||
ITEM.model = "models/props_lab/reciever01d.mdl"
|
||||
ITEM.width = 1
|
||||
ITEM.height = 1
|
||||
ITEM.description = "Słuchając tego prostego urządzenia poprawisz swoją zdolność czytania."
|
||||
ITEM.category = "Audiobooks"
|
||||
|
||||
ITEM.functions.Listen = {
|
||||
OnRun = function(itemTable)
|
||||
local client = itemTable.player
|
||||
local character = client:GetCharacter()
|
||||
|
||||
character:SetCanread(true)
|
||||
|
||||
client:NotifyLocalized("Czuję się teraz znacznie lepszy w czytaniu.")
|
||||
end
|
||||
}
|
||||
89
gamemodes/helix/plugins/charcreation/items/sh_razor.lua
Normal file
89
gamemodes/helix/plugins/charcreation/items/sh_razor.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Narzędzia fryzjera"
|
||||
ITEM.uniqueID = "beard_razor"
|
||||
ITEM.model = "models/props_junk/cardboard_box004a.mdl"
|
||||
ITEM.width = 1
|
||||
ITEM.height = 1
|
||||
ITEM.description = "Narzędzia dla twórczych umysłów w tym przygnębiającym świecie."
|
||||
ITEM.category = "Tools"
|
||||
|
||||
ITEM.functions.Style = {
|
||||
icon = "icon16/paintbrush.png",
|
||||
OnRun = function(itemTable)
|
||||
local client = itemTable.player
|
||||
if !client or client and !IsValid(client) then return end
|
||||
|
||||
local model = client:GetModel()
|
||||
if model:find("willardnetworks/citizens/") or model:find("wn7new") then
|
||||
if !client.CantPlace then
|
||||
client.CantPlace = true
|
||||
netstream.Start(client, "OpenBeardStyling")
|
||||
|
||||
timer.Simple(3, function()
|
||||
if client then
|
||||
client.CantPlace = false
|
||||
end
|
||||
end)
|
||||
else
|
||||
client:NotifyLocalized("Musisz poczekać, zanim będziesz mógł to zrobić!")
|
||||
return false
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Nie masz włosów/brody do stylizacji!")
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.Style2 = {
|
||||
icon = "icon16/paintbrush.png",
|
||||
name = "Stylizuj kogoś",
|
||||
OnRun = function(itemTable)
|
||||
local client = itemTable.player
|
||||
if !client or client and !IsValid(client) then return end
|
||||
|
||||
local target = client:GetEyeTraceNoCursor().Entity
|
||||
if !target or target and !IsValid(target) then
|
||||
client:Notify("Nieprawidłowy cel!")
|
||||
return false
|
||||
end
|
||||
|
||||
if target:GetModel():find("willardnetworks/citizens/") then
|
||||
if !client.CantPlace then
|
||||
client.CantPlace = true
|
||||
|
||||
if target:IsBot() then
|
||||
netstream.Start(client, "OpenBeardStyling", target)
|
||||
else
|
||||
netstream.Start(target, "GetStylingConfirmation", client)
|
||||
end
|
||||
|
||||
timer.Simple(3, function()
|
||||
if client then
|
||||
client.CantPlace = false
|
||||
end
|
||||
end)
|
||||
else
|
||||
client:NotifyLocalized("Musisz poczekać, zanim będziesz mógł to zrobić!")
|
||||
return false
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Cel nie ma włosów/brody do stylizacji!")
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
}
|
||||
180
gamemodes/helix/plugins/charcreation/libs/sh_faction.lua
Normal file
180
gamemodes/helix/plugins/charcreation/libs/sh_faction.lua
Normal file
@@ -0,0 +1,180 @@
|
||||
--[[
|
||||
| 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 CITIZEN_MODELS = {
|
||||
"models/humans/group01/male_01.mdl",
|
||||
"models/humans/group01/male_02.mdl",
|
||||
"models/humans/group01/male_04.mdl",
|
||||
"models/humans/group01/male_05.mdl",
|
||||
"models/humans/group01/male_06.mdl",
|
||||
"models/humans/group01/male_07.mdl",
|
||||
"models/humans/group01/male_08.mdl",
|
||||
"models/humans/group01/male_09.mdl",
|
||||
"models/humans/group02/male_01.mdl",
|
||||
"models/humans/group02/male_03.mdl",
|
||||
"models/humans/group02/male_05.mdl",
|
||||
"models/humans/group02/male_07.mdl",
|
||||
"models/humans/group02/male_09.mdl",
|
||||
"models/humans/group01/female_01.mdl",
|
||||
"models/humans/group01/female_02.mdl",
|
||||
"models/humans/group01/female_03.mdl",
|
||||
"models/humans/group01/female_06.mdl",
|
||||
"models/humans/group01/female_07.mdl",
|
||||
"models/humans/group02/female_01.mdl",
|
||||
"models/humans/group02/female_03.mdl",
|
||||
"models/humans/group02/female_06.mdl",
|
||||
"models/humans/group01/female_04.mdl"
|
||||
}
|
||||
|
||||
--- Loads factions from a directory.
|
||||
-- @realm shared
|
||||
-- @string directory The path to the factions files.
|
||||
function ix.faction.LoadFromDir(directory)
|
||||
for _, v in ipairs(file.Find(directory.."/*.lua", "LUA")) do
|
||||
local niceName = v:sub(4, -5)
|
||||
|
||||
FACTION = ix.faction.teams[niceName] or {index = table.Count(ix.faction.teams) + 1, isDefault = false}
|
||||
if (PLUGIN) then
|
||||
FACTION.plugin = PLUGIN.uniqueID
|
||||
end
|
||||
|
||||
ix.util.Include(directory.."/"..v, "shared")
|
||||
|
||||
if (!FACTION.name) then
|
||||
FACTION.name = "Unknown"
|
||||
ErrorNoHalt("Faction '"..niceName.."' is missing a name. You need to add a FACTION.name = \"Name\"\n")
|
||||
end
|
||||
|
||||
if (!FACTION.description) then
|
||||
FACTION.description = "noDesc"
|
||||
ErrorNoHalt("Faction '"..niceName.."' is missing a description. You need to add a FACTION.description = \"Description\"\n")
|
||||
end
|
||||
|
||||
if (!FACTION.color) then
|
||||
FACTION.color = Color(150, 150, 150)
|
||||
ErrorNoHalt("Faction '"..niceName.."' is missing a color. You need to add FACTION.color = Color(1, 2, 3)\n")
|
||||
end
|
||||
|
||||
team.SetUp(FACTION.index, FACTION.name or "Unknown", FACTION.color or Color(125, 125, 125))
|
||||
|
||||
FACTION.models = FACTION.models or CITIZEN_MODELS
|
||||
FACTION.uniqueID = FACTION.uniqueID or niceName
|
||||
|
||||
for _, v2 in pairs(FACTION.models) do
|
||||
if (isstring(v2)) then
|
||||
util.PrecacheModel(v2)
|
||||
elseif (istable(v2)) then
|
||||
util.PrecacheModel(v2[1])
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetModels) then
|
||||
function FACTION:GetModels(client)
|
||||
return self.models
|
||||
end
|
||||
end
|
||||
|
||||
-- GENDERS
|
||||
if (!FACTION.GetModelsMale) then
|
||||
function FACTION:GetModelsMale(client)
|
||||
return self.models.male
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetModelsFemale) then
|
||||
function FACTION:GetModelsFemale(client)
|
||||
return self.models.female
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetNoGender) then
|
||||
function FACTION:GetNoGender(client)
|
||||
return self.noGender
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetNoGenetics) then
|
||||
function FACTION:GetNoGenetics(client)
|
||||
return self.noGenetics
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetNoAppearances) then
|
||||
function FACTION:GetNoAppearances(client)
|
||||
return self.noAppearances
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetReadOptionDisabled) then
|
||||
function FACTION:GetReadOptionDisabled(client)
|
||||
return self.ReadOptionDisabled
|
||||
end
|
||||
end
|
||||
|
||||
if (!FACTION.GetNoBackground) then
|
||||
function FACTION:GetNoBackground(client)
|
||||
return self.noBackground
|
||||
end
|
||||
end
|
||||
|
||||
ix.faction.indices[FACTION.index] = FACTION
|
||||
ix.faction.teams[niceName] = FACTION
|
||||
FACTION = nil
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("PlyWhitelist", {
|
||||
description = "@cmdPlyWhitelist",
|
||||
privilege = "Manage Character Whitelist",
|
||||
superAdminOnly = true,
|
||||
arguments = {
|
||||
ix.type.player,
|
||||
ix.type.text
|
||||
},
|
||||
OnRun = function(self, client, target, name)
|
||||
if (name == "") then
|
||||
return "@invalidArg", 2
|
||||
end
|
||||
|
||||
local faction = ix.faction.teams[name]
|
||||
|
||||
if (!faction) then
|
||||
for _, v in ipairs(ix.faction.indices) do
|
||||
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
|
||||
faction = v
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (faction) then
|
||||
local result, text = hook.Run("CanWhitelistPlayer", target, faction)
|
||||
if (result == false) then
|
||||
return "@"..(text or "invalidFaction")
|
||||
end
|
||||
|
||||
if (target:SetWhitelisted(faction.index, true)) then
|
||||
if faction.OnWhitelist then
|
||||
faction:OnWhitelist(target)
|
||||
end
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (self:OnCheckAccess(v) or v == target) then
|
||||
v:NotifyLocalized("whitelist", client:GetName(), target:GetName(), L(faction.name, v))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
return "@invalidFaction"
|
||||
end
|
||||
end
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
do
|
||||
for _, proxy in pairs(PLUGIN.proxyList) do
|
||||
matproxy.Add( {
|
||||
name = proxy,
|
||||
|
||||
init = function( self, _, values )
|
||||
-- Store the name of the variable we want to set
|
||||
self.ResultTo = values.resultvar
|
||||
end,
|
||||
|
||||
bind = function( self, mat, ent )
|
||||
if ( !IsValid( ent ) ) then return end
|
||||
|
||||
-- If entity is a ragdoll try to convert it into the player
|
||||
-- ( this applies to their corpses )
|
||||
if ( ent:IsRagdoll() ) then
|
||||
local owner = ent:GetRagdollOwner()
|
||||
if ( IsValid( owner ) ) then ent = owner end
|
||||
end
|
||||
|
||||
-- SHOULD return a Vector with the items stored-/character's stored hair colour.
|
||||
local clrFallback = Vector( 255 / 255, 255 / 255, 255 / 255 )
|
||||
|
||||
local entColor = ent.GetProxyColors and ent:GetProxyColors() or false
|
||||
|
||||
local ragdollNWVector = ent:GetNWVector(proxy, false)
|
||||
|
||||
local character = ent.GetCharacter and ent:GetCharacter() or false
|
||||
local charProxyColors = character and character.GetProxyColors and character:GetProxyColors() or {}
|
||||
|
||||
local color = entColor and entColor[proxy] or ragdollNWVector or charProxyColors[proxy] or clrFallback
|
||||
|
||||
if istable(color) and color.r then
|
||||
color = Vector(color.r / 255, color.g / 255, color.b / 255)
|
||||
end
|
||||
|
||||
mat:SetVector(self.ResultTo, color)
|
||||
end
|
||||
} )
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
--[[
|
||||
| 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.proxyList = {
|
||||
"HeadGearColor",
|
||||
"HairColor",
|
||||
"TorsoColor",
|
||||
"ShirtColor",
|
||||
"PantsColor",
|
||||
"ShoesColor"
|
||||
}
|
||||
|
||||
ix.char.RegisterVar("proxyColors", {
|
||||
field = "proxyColors",
|
||||
fieldType = ix.type.text,
|
||||
default = {},
|
||||
bNoDisplay = true
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
-- could just call ixPlayer on the ragdoll but then the colors will change on the ragdoll according to what the player is doing
|
||||
-- in their inventory (even if they are dead)
|
||||
|
||||
function PLUGIN:OnCharacterFallover(client, entity, bFallenOver)
|
||||
if (entity == nil or !IsValid(entity)) then return end
|
||||
|
||||
local character = client.GetCharacter and client:GetCharacter()
|
||||
if (!character) then return end
|
||||
|
||||
local colorProxies = character:GetProxyColors() or {}
|
||||
for proxy, vector in pairs(colorProxies) do
|
||||
local color = vector
|
||||
if !isvector(color) and istable(color) and color.r then
|
||||
color = Vector(color.r / 255, color.g / 255, color.b / 255)
|
||||
end
|
||||
|
||||
entity:SetNWVector(proxy, color)
|
||||
end
|
||||
end
|
||||
329
gamemodes/helix/plugins/charcreation/sh_plugin.lua
Normal file
329
gamemodes/helix/plugins/charcreation/sh_plugin.lua
Normal file
@@ -0,0 +1,329 @@
|
||||
--[[
|
||||
| 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 = "Char Creation Necessities"
|
||||
PLUGIN.author = "Fruity"
|
||||
PLUGIN.description = "Required stuff for the char creation such as gender etc."
|
||||
PLUGIN.TIMER_DELAY = PLUGIN.TIMER_DELAY or 60
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("matproxies/sv_matproxies.lua")
|
||||
ix.util.Include("matproxies/sh_matproxies.lua")
|
||||
ix.util.Include("matproxies/cl_matproxies.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optUseImmersiveGlasses = "Użyj immersyjnych okularów",
|
||||
optdUseImmersiveGlasses = "Używaj okularów immersyjnych. Jeśli postać potrzebuje okularów, a ich nie używa, to obraz staje się rozmyty."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
optdUseImmersiveGlasses = "Utiliza las gafas de inmersión, difuminando la vista de tu personaje si necesita gafas y no las lleva puestas.",
|
||||
optUseImmersiveGlasses = "Utilizar gafas inmersivas"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
optUseImmersiveGlasses = "Użyj immersyjnych okularów",
|
||||
optdUseImmersiveGlasses = "Używaj okularów immersyjnych. Jeśli postać potrzebuje okularów, a ich nie używa, to obraz staje się rozmyty."
|
||||
})
|
||||
|
||||
ix.char.RegisterVar("glasses", {
|
||||
field = "glasses",
|
||||
fieldType = ix.type.bool,
|
||||
default = false,
|
||||
isLocal = true,
|
||||
bNoDisplay = true
|
||||
})
|
||||
|
||||
ix.char.RegisterVar("canread", {
|
||||
field = "canread",
|
||||
fieldType = ix.type.bool,
|
||||
default = true,
|
||||
isLocal = true,
|
||||
bNoDisplay = true
|
||||
})
|
||||
|
||||
ix.char.RegisterVar("beardProgress", {
|
||||
field = "beard",
|
||||
fieldType = ix.type.number,
|
||||
default = 0,
|
||||
bNoNetworking = true,
|
||||
bNoDisplay = true
|
||||
})
|
||||
|
||||
ix.allowedHairColors = {
|
||||
szare = {
|
||||
Color(244,233,230),
|
||||
Color(221,202,195),
|
||||
Color(182,170,165),
|
||||
Color(151,132,126),
|
||||
Color(111,101,98),
|
||||
Color(126,122,121),
|
||||
Color(89,89,89)
|
||||
},
|
||||
brazowe = {
|
||||
Color(95,52,39),
|
||||
Color(101,66,56),
|
||||
Color(62,50,47),
|
||||
Color(80,69,66),
|
||||
Color(138,106,96),
|
||||
Color(164,149,137),
|
||||
Color(85,72,56),
|
||||
Color(83,61,50)
|
||||
},
|
||||
jasne = {
|
||||
Color(223,186,155),
|
||||
Color(172,129,94),
|
||||
Color(145,124,109),
|
||||
Color(229,200,170),
|
||||
Color(203,191,177),
|
||||
Color(184,151,120),
|
||||
Color(230,206,168),
|
||||
Color(255,216,149)
|
||||
},
|
||||
["miekki niebieski"] = {
|
||||
Color(161,165,167),
|
||||
Color(125,132,135)
|
||||
}
|
||||
}
|
||||
|
||||
ix.char.RegisterVar("hair", {
|
||||
field = "hair",
|
||||
fieldType = ix.type.table,
|
||||
default = {},
|
||||
isLocal = true,
|
||||
bNoDisplay = true,
|
||||
OnValidate = function(self, data, payload, client)
|
||||
if !payload.hair or payload.hair and !istable(payload.hair) then
|
||||
return false, "Nie wybrano włosów/koloru włosów!"
|
||||
end
|
||||
|
||||
if !istable(payload.hair) then
|
||||
return false, "Coś poszło nie tak przy wyborze włosów!"
|
||||
end
|
||||
|
||||
if !payload.hair.hair then
|
||||
return false, "Nie wybrano włosów!"
|
||||
end
|
||||
|
||||
if !payload.hair.color then
|
||||
return false, "Nie wybrano koloru włosów!"
|
||||
end
|
||||
|
||||
local found = false
|
||||
for _, v in pairs(ix.allowedHairColors) do
|
||||
if !table.HasValue(v, payload.hair.color) then continue end
|
||||
|
||||
found = true
|
||||
break
|
||||
end
|
||||
|
||||
if !found then
|
||||
return false, "Nie wybrano dozwolonego koloru włosów!"
|
||||
end
|
||||
|
||||
if !isnumber(payload.hair.hair) then
|
||||
return false, "Nie wybrano dozwolonych włosów!"
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
OnGet = function(self, default)
|
||||
local hair = self.vars.hair
|
||||
|
||||
return hair or {}
|
||||
end,
|
||||
OnAdjust = function(self, client, data, value, newData)
|
||||
newData.hair = value
|
||||
end
|
||||
})
|
||||
|
||||
ix.config.Add("forceImmeseriveGlasses", true, "Wymusza rysowanie efektu okularów, nawet jeśli klient wyłączy dla nich tę opcję.", nil, {
|
||||
category = "characters"
|
||||
})
|
||||
|
||||
do
|
||||
local CHAR = ix.meta.character
|
||||
function CHAR:HasGlasses()
|
||||
for _, v in pairs(self:GetInventory():GetItems()) do
|
||||
if (v.glasses and v:GetData("equip")) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
ix.char.vars["model"].OnDisplay = function(self, container, payload) end
|
||||
ix.char.vars["model"].OnValidate = function(self, value, payload, client)
|
||||
local faction = ix.faction.indices[payload.faction]
|
||||
|
||||
if (faction) then
|
||||
local gender = payload.gender
|
||||
local models
|
||||
if gender == "male" and faction:GetModelsMale(client) then
|
||||
models = faction:GetModelsMale(client)
|
||||
elseif gender == "female" and faction:GetModelsFemale(client) then
|
||||
models = faction:GetModelsFemale(client)
|
||||
else
|
||||
models = faction:GetModels(client)
|
||||
end
|
||||
|
||||
if (!payload.model or !models[payload.model]) then
|
||||
return false, "Nie wybrano modelu!"
|
||||
end
|
||||
else
|
||||
return false, "Nie wybrano modelu!"
|
||||
end
|
||||
end
|
||||
|
||||
ix.char.vars["model"].OnAdjust = function(self, client, data, value, newData)
|
||||
local faction = ix.faction.indices[data.faction]
|
||||
|
||||
if (faction) then
|
||||
local gender = data.gender
|
||||
local model
|
||||
if gender == "male" and faction:GetModelsMale(client) then
|
||||
model = faction:GetModelsMale(client)[value]
|
||||
elseif gender == "female" and faction:GetModelsFemale(client) then
|
||||
model = faction:GetModelsFemale(client)[value]
|
||||
else
|
||||
model = faction:GetModels(client)[value]
|
||||
end
|
||||
|
||||
if (isstring(model)) then
|
||||
newData.model = model
|
||||
elseif (istable(model)) then
|
||||
newData.model = model[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ix.char.vars["model"].ShouldDisplay = function(self, container, payload)
|
||||
local faction = ix.faction.indices[payload.faction]
|
||||
|
||||
if faction then
|
||||
local gender = payload.gender
|
||||
if gender == "male" and faction:GetModelsMale(LocalPlayer()) then
|
||||
return #faction:GetModelsMale(LocalPlayer()) > 1
|
||||
elseif gender == "female" and faction:GetModelsFemale(LocalPlayer()) then
|
||||
return #faction:GetModelsFemale(LocalPlayer()) > 1
|
||||
else
|
||||
return #faction:GetModels(LocalPlayer()) > 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Registers the var "Gender"
|
||||
ix.char.RegisterVar("gender", {
|
||||
field = "gender",
|
||||
fieldType = ix.type.string,
|
||||
default = "male",
|
||||
bNoDisplay = true,
|
||||
OnSet = function(self, value)
|
||||
local client = self:GetPlayer()
|
||||
|
||||
if (IsValid(client)) then
|
||||
self.vars.gender = value
|
||||
|
||||
-- @todo refactor networking of character vars so this doesn't need to be repeated on every OnSet override
|
||||
net.Start("ixCharacterVarChanged")
|
||||
net.WriteUInt(self:GetID(), 32)
|
||||
net.WriteString("gender")
|
||||
net.WriteType(self.vars.gender)
|
||||
net.Broadcast()
|
||||
end
|
||||
end,
|
||||
OnGet = function(self, default)
|
||||
local gender = self.vars.gender
|
||||
|
||||
return gender or 0
|
||||
end,
|
||||
OnValidate = function(self, data, payload, client)
|
||||
local faction = ix.faction.indices[payload.faction]
|
||||
if (payload.gender == "female" or payload.gender == "male") then
|
||||
return true
|
||||
end
|
||||
|
||||
if faction then
|
||||
if faction:GetNoGender(client) == true then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false, "Nie wybrano płci!"
|
||||
end,
|
||||
OnAdjust = function(self, client, data, value, newData)
|
||||
newData.gender = value
|
||||
end
|
||||
})
|
||||
|
||||
ix.char.vars["data"].OnValidate = function(self, datas, payload, client)
|
||||
local faction = ix.faction.indices[payload.faction]
|
||||
|
||||
if faction then
|
||||
if (!payload.data["background"] or payload.data["background"] == "") and faction:GetNoBackground(client) != true then
|
||||
return false, "Nie wybrano tła!"
|
||||
end
|
||||
|
||||
if faction:GetNoGenetics(client) then
|
||||
return true
|
||||
end
|
||||
|
||||
if !payload.data.age or payload.data["age"] == "" then
|
||||
return false, "Nie wybrano wieku!"
|
||||
end
|
||||
|
||||
if !payload.data.height or payload.data["height"] == "" then
|
||||
return false, "Nie wybrano wzrostu!"
|
||||
end
|
||||
|
||||
if faction.name != "Vortigaunt" then
|
||||
if !payload.data["eye color"] or payload.data["eye color"] == "" then
|
||||
return false, "Nie wybrano koloru oczu!"
|
||||
end
|
||||
end
|
||||
|
||||
if payload.data.skin < 0 then
|
||||
return false, "Nie wybrano prawidłowej skóry!"
|
||||
end
|
||||
|
||||
if payload.data.groups then
|
||||
if payload.data.groups["2"]then
|
||||
if payload.data.groups["2"] < 0 then
|
||||
return false, "Nie wybrano prawidłowego tułowia!"
|
||||
end
|
||||
end
|
||||
|
||||
if payload.data.groups["3"] then
|
||||
if payload.data.groups["3"] < 0 then
|
||||
return false, "Nie wybrano prawidłowych spodni!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if faction:GetNoAppearances(client) then
|
||||
return true
|
||||
end
|
||||
|
||||
if faction:GetReadOptionDisabled(client) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
ix.char.vars["data"].OnAdjust = function(self, client, datas, value, newData)
|
||||
newData.data = value
|
||||
end
|
||||
218
gamemodes/helix/plugins/charcreation/sv_plugin.lua
Normal file
218
gamemodes/helix/plugins/charcreation/sv_plugin.lua
Normal file
@@ -0,0 +1,218 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character)
|
||||
if (character:GetGlasses() and !character:HasGlasses()) then
|
||||
timer.Simple(3, function()
|
||||
if (IsValid(client) and !character:HasGlasses()) then
|
||||
client:NotifyLocalized("Potrzebuję swoich okularów, by dobrze widzieć...")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Beard stuff
|
||||
local uniqueID = "ixBeard" .. client:SteamID64()
|
||||
local gender = character:GetGender()
|
||||
if (gender == "male" and !ix.faction.Get(client:Team()).noBeard) then
|
||||
timer.Create(uniqueID, PLUGIN.TIMER_DELAY, 0, function()
|
||||
if (IsValid(client)) then
|
||||
PLUGIN:BeardPlayerTick(client)
|
||||
else
|
||||
timer.Remove(uniqueID)
|
||||
end
|
||||
end)
|
||||
else
|
||||
timer.Remove(uniqueID)
|
||||
end
|
||||
|
||||
local hairTable = character.GetHair and character:GetHair() or {}
|
||||
if !table.IsEmpty(hairTable) then
|
||||
local color = hairTable.color
|
||||
local charProxies = character:GetProxyColors() or {}
|
||||
|
||||
charProxies["HairColor"] = color
|
||||
|
||||
character:SetProxyColors(charProxies)
|
||||
else
|
||||
client:Notify("Dodano nowe włosy i kolory włosów, które można wybrać z menu.")
|
||||
netstream.Start(client, "OpenBeardStyling")
|
||||
end
|
||||
|
||||
local curModel = client:GetModel()
|
||||
if curModel:find("willardnetworks/citizens/male") and !curModel:find("willardnetworks/citizens/male_") then
|
||||
local beardIndex = client:FindBodygroupByName("beard")
|
||||
local curBeard = client:GetBodygroup(beardIndex)
|
||||
character:SetModel(string.gsub(curModel, "male", "male_"))
|
||||
|
||||
for i = 0, client:GetNumBodyGroups() do
|
||||
client:SetBodygroup(i, 0)
|
||||
end
|
||||
|
||||
timer.Simple(2, function()
|
||||
if !client or client and !IsValid(client) then return end
|
||||
if !character then return end
|
||||
|
||||
local groups = {}
|
||||
if curBeard then
|
||||
groups[beardIndex] = curBeard
|
||||
end
|
||||
|
||||
local hairIndex = client:FindBodygroupByName("hair")
|
||||
groups[hairIndex] = hairTable.hair
|
||||
character:SetData("groups", groups)
|
||||
|
||||
if hairTable.hair then
|
||||
client:SetBodygroup(hairIndex, hairTable.hair)
|
||||
end
|
||||
|
||||
if curBeard then
|
||||
client:SetBodygroup(beardIndex, curBeard)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:AdjustCreationPayload(client, payload, newPayload)
|
||||
if (newPayload.data.glasses != nil) then
|
||||
newPayload.glasses = newPayload.data.glasses
|
||||
newPayload.data.glasses = nil
|
||||
end
|
||||
|
||||
if (newPayload.data.canread != nil) then
|
||||
newPayload.canread = newPayload.data.canread
|
||||
newPayload.data.canread = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:BeardPlayerTick(client)
|
||||
local character = client:GetCharacter()
|
||||
if (character) then
|
||||
if (!client:Alive()) then return end
|
||||
|
||||
local beardProgress = character:GetBeardProgress() + 1
|
||||
character:SetBeardProgress(beardProgress)
|
||||
|
||||
if (beardProgress == 180 * 8) then
|
||||
local index = client:FindBodygroupByName("beard")
|
||||
local groups = character:GetData("groups", {})
|
||||
groups[index] = 8
|
||||
client:SetBodygroup(index, 8)
|
||||
character:SetData("groups", groups)
|
||||
client:NotifyLocalized("Powinienem przyciąć już brodę.")
|
||||
elseif (beardProgress == 180 * 16) then
|
||||
local index = client:FindBodygroupByName("beard")
|
||||
local groups = character:GetData("groups", {})
|
||||
groups[index] = 5
|
||||
client:SetBodygroup(index, 5)
|
||||
character:SetData("groups", groups)
|
||||
client:NotifyLocalized("Moja broda robi się już naprawdę długa.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("RemoveBeardBodygroup", function(client, target)
|
||||
if target and IsValid(target) then
|
||||
if client != target then
|
||||
client = target
|
||||
end
|
||||
end
|
||||
|
||||
local character = client:GetCharacter()
|
||||
local index = client:FindBodygroupByName("beard")
|
||||
if client:GetBodygroup( index ) <= 0 then
|
||||
client:NotifyLocalized("Nie mam brody!")
|
||||
return false
|
||||
end
|
||||
|
||||
local groups = character:GetData("groups", {})
|
||||
groups[index] = 0
|
||||
client:SetBodygroup(index, 0)
|
||||
character:SetData("groups", groups)
|
||||
character:SetBeardProgress(0)
|
||||
end)
|
||||
|
||||
netstream.Hook("SetHairBeardBodygroup", function(client, hairID, beardID, hairColor, target)
|
||||
if target and IsValid(target) then
|
||||
if client != target then
|
||||
client = target
|
||||
end
|
||||
end
|
||||
|
||||
local character = client:GetCharacter()
|
||||
local gender = character:GetGender()
|
||||
|
||||
local beardBGIndex = client:FindBodygroupByName("beard")
|
||||
local hairBGIndex = client:FindBodygroupByName("hair")
|
||||
|
||||
local curHair = client:GetBodygroup(hairBGIndex)
|
||||
local curBeard = beardBGIndex != -1 and client:GetBodygroup(beardBGIndex) or false
|
||||
|
||||
local groups = character:GetData("groups", {})
|
||||
|
||||
if gender != "female" and curBeard and beardID and curBeard != beardID then
|
||||
local beardProgress = character:GetBeardProgress()
|
||||
|
||||
if (beardProgress < (180 * 8)) and client:GetBodygroup(beardBGIndex) != 5 and client:GetBodygroup(beardBGIndex) != 8 then
|
||||
return false
|
||||
end
|
||||
|
||||
character:SetBeardProgress(0)
|
||||
|
||||
groups[beardBGIndex] = beardID
|
||||
client:SetBodygroup(beardBGIndex, beardID)
|
||||
end
|
||||
|
||||
if hairID and curHair != hairID or hairColor then
|
||||
local hairData = character:GetHair()
|
||||
|
||||
if hairID then
|
||||
groups[hairBGIndex] = hairID
|
||||
local bgName = client:GetModel():find("wn7new") and "cp_Head" or "headwear"
|
||||
if client:GetBodygroup(client:FindBodygroupByName(bgName)) <= 0 then
|
||||
client:SetBodygroup(hairBGIndex, hairID)
|
||||
end
|
||||
|
||||
hairData.hair = hairID
|
||||
end
|
||||
|
||||
if hairColor then
|
||||
hairData.color = hairColor
|
||||
|
||||
local charProxies = character:GetProxyColors() or {}
|
||||
|
||||
charProxies["HairColor"] = hairColor
|
||||
|
||||
character:SetProxyColors(charProxies)
|
||||
end
|
||||
|
||||
character:SetHair(hairData)
|
||||
end
|
||||
|
||||
character:SetData("groups", groups)
|
||||
end)
|
||||
|
||||
function PLUGIN:InitializedPlugins()
|
||||
Schema.charCreationItems = {}
|
||||
for _, v in pairs(ix.item.list) do
|
||||
if (v.charCreation or v.adminCreation) and v.bodyGroups then
|
||||
Schema.charCreationItems[v.uniqueID] = {bodygroups = v.bodyGroups, proxy = v.proxy or false}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("AcceptStyling", function(target, attempter)
|
||||
if !target or !attempter then return end
|
||||
if !IsValid(target) or !IsValid(attempter) then return end
|
||||
|
||||
netstream.Start(attempter, "OpenBeardStyling", target)
|
||||
end)
|
||||
1373
gamemodes/helix/plugins/chatbox/derma/cl_chatbox.lua
Normal file
1373
gamemodes/helix/plugins/chatbox/derma/cl_chatbox.lua
Normal file
File diff suppressed because it is too large
Load Diff
135
gamemodes/helix/plugins/chatbox/derma/cl_chatboxcustomize.lua
Normal file
135
gamemodes/helix/plugins/chatbox/derma/cl_chatboxcustomize.lua
Normal file
@@ -0,0 +1,135 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
ix.gui.chatTabCustomize = self
|
||||
|
||||
self:SetTitle(L("chatNewTab"))
|
||||
self:SetSize(ScrW() * 0.5, ScrH() * 0.5)
|
||||
DFrameFixer(self)
|
||||
|
||||
self.settings = self:Add("ixSettings")
|
||||
self.settings:Dock(FILL)
|
||||
self.settings:SetSearchEnabled(true)
|
||||
self.settings:AddCategory(L("chatAllowedClasses"))
|
||||
|
||||
-- controls
|
||||
local controlsPanel = self:Add("Panel")
|
||||
controlsPanel:Dock(BOTTOM)
|
||||
controlsPanel:DockMargin(0, SScaleMin(4 / 3), 0, 0)
|
||||
controlsPanel:SetTall(SScaleMin(32 / 3))
|
||||
self.settings.contentFrame:SetSize(self:GetWide(), self:GetTall() - SScaleMin(25 / 3) - controlsPanel:GetTall())
|
||||
|
||||
self.create = controlsPanel:Add("DButton")
|
||||
self.create:SetText(L("create"))
|
||||
self.create:SizeToContents()
|
||||
self.create:Dock(FILL)
|
||||
self.create:DockMargin(0, 0, SScaleMin(4 / 3), 0)
|
||||
self.create:SetFont("MenuFontNoClamp")
|
||||
self.create.DoClick = ix.util.Bind(self, self.CreateClicked)
|
||||
|
||||
local uncheckAll = controlsPanel:Add("DButton")
|
||||
uncheckAll:SetText(L("uncheckAll"))
|
||||
uncheckAll:SetFont("MenuFontNoClamp")
|
||||
uncheckAll:SizeToContents()
|
||||
uncheckAll:Dock(RIGHT)
|
||||
uncheckAll.DoClick = function()
|
||||
self:SetAllValues(false)
|
||||
end
|
||||
|
||||
local checkAll = controlsPanel:Add("DButton")
|
||||
checkAll:SetText(L("checkAll"))
|
||||
checkAll:SetFont("MenuFontNoClamp")
|
||||
checkAll:SizeToContents()
|
||||
checkAll:Dock(RIGHT)
|
||||
checkAll:DockMargin(0, 0, SScaleMin(4 / 3), 0)
|
||||
checkAll.DoClick = function()
|
||||
self:SetAllValues(true)
|
||||
end
|
||||
|
||||
-- chat class settings
|
||||
self.name = self.settings:AddRow(ix.type.string)
|
||||
self.name:SetText(L("chatTabName"))
|
||||
self.name:SetValue(L("chatNewTabTitle"))
|
||||
self.name:SetZPos(-1)
|
||||
|
||||
for k, _ in SortedPairs(ix.chat.classes) do
|
||||
local panel = self.settings:AddRow(ix.type.bool, L("chatAllowedClasses"))
|
||||
panel:SetText(k)
|
||||
panel:SetValue(true, true)
|
||||
end
|
||||
|
||||
self.settings:SizeToContents()
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
end
|
||||
|
||||
function PANEL:PopulateFromTab(name, filter)
|
||||
self.tab = name
|
||||
|
||||
self:SetTitle(L("chatCustomize"))
|
||||
self.create:SetText(L("update"))
|
||||
self.name:SetValue(name)
|
||||
|
||||
for _, v in ipairs(self.settings:GetRows()) do
|
||||
if (filter[v:GetText()]) then
|
||||
v:SetValue(false, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetAllValues(bValue)
|
||||
for _, v in ipairs(self.settings:GetRows()) do
|
||||
if (v == self.name) then
|
||||
continue
|
||||
end
|
||||
|
||||
v:SetValue(tobool(bValue), true)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateClicked()
|
||||
local name = self.tab and self.tab or self.name:GetValue()
|
||||
|
||||
if (self.tab != self.name:GetValue() and PLUGIN:TabExists(name)) then
|
||||
ix.util.Notify(L("chatTabExists"))
|
||||
return
|
||||
end
|
||||
|
||||
local filter = {}
|
||||
|
||||
for _, v in ipairs(self.settings:GetRows()) do
|
||||
-- we only want to add entries for classes we don't want shown
|
||||
if (!v:GetValue()) then
|
||||
filter[v:GetText()] = true
|
||||
end
|
||||
end
|
||||
|
||||
if (self.tab) then
|
||||
self:OnTabUpdated(name, filter, self.name:GetValue())
|
||||
else
|
||||
self:OnTabCreated(name, filter)
|
||||
end
|
||||
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
function PANEL:OnTabCreated(id, filter)
|
||||
end
|
||||
|
||||
function PANEL:OnTabUpdated(id, filter, newID)
|
||||
end
|
||||
|
||||
vgui.Register("ixChatboxTabCustomize", PANEL, "DFrame")
|
||||
167
gamemodes/helix/plugins/chatbox/sh_plugin.lua
Normal file
167
gamemodes/helix/plugins/chatbox/sh_plugin.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
--[[
|
||||
| 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 = "Chatbox"
|
||||
PLUGIN.author = "`impulse"
|
||||
PLUGIN.description = "Replaces the chatbox to enable customization, autocomplete, and useful info."
|
||||
|
||||
if (CLIENT) then
|
||||
ix.chat.history = ix.chat.history or {} -- array of strings the player has entered into the chatbox
|
||||
ix.chat.currentCommand = ""
|
||||
ix.chat.currentArguments = {}
|
||||
|
||||
ix.option.Add("chatNotices", ix.type.bool, false, {
|
||||
category = "chat"
|
||||
})
|
||||
|
||||
ix.option.Add("chatTimestamps", ix.type.bool, false, {
|
||||
category = "chat"
|
||||
})
|
||||
|
||||
ix.option.Add("chatFontScale", ix.type.number, 1, {
|
||||
category = "chat", min = 0.1, max = 2, decimals = 2,
|
||||
OnChanged = function()
|
||||
hook.Run("LoadFonts", ix.config.Get("font"), ix.config.Get("genericFont"))
|
||||
PLUGIN:CreateChat()
|
||||
end
|
||||
})
|
||||
|
||||
ix.option.Add("chatOutline", ix.type.bool, false, {
|
||||
category = "chat"
|
||||
})
|
||||
|
||||
-- tabs and their respective filters
|
||||
ix.option.Add("chatTabs", ix.type.string, "", {
|
||||
category = "chat",
|
||||
hidden = function()
|
||||
return true
|
||||
end
|
||||
})
|
||||
|
||||
-- chatbox size and position
|
||||
ix.option.Add("chatPosition", ix.type.string, "", {
|
||||
category = "chat",
|
||||
hidden = function()
|
||||
return true
|
||||
end
|
||||
})
|
||||
|
||||
function PLUGIN:CreateChat()
|
||||
if (IsValid(self.panel)) then
|
||||
self.panel:Remove()
|
||||
end
|
||||
|
||||
self.panel = vgui.Create("ixChatbox")
|
||||
self.panel:SetupTabs(util.JSONToTable(ix.option.Get("chatTabs", "")))
|
||||
self.panel:SetupPosition(util.JSONToTable(ix.option.Get("chatPosition", "")))
|
||||
|
||||
hook.Run("ChatboxCreated")
|
||||
end
|
||||
|
||||
function PLUGIN:TabExists(id)
|
||||
if (!IsValid(self.panel)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return self.panel.tabs:GetTabs()[id] != nil
|
||||
end
|
||||
|
||||
function PLUGIN:SaveTabs()
|
||||
local tabs = {}
|
||||
|
||||
for id, panel in pairs(self.panel.tabs:GetTabs()) do
|
||||
tabs[id] = panel:GetFilter()
|
||||
end
|
||||
|
||||
ix.option.Set("chatTabs", util.TableToJSON(tabs))
|
||||
end
|
||||
|
||||
function PLUGIN:SavePosition()
|
||||
local x, y = self.panel:GetPos()
|
||||
local width, height = self.panel:GetSize()
|
||||
|
||||
ix.option.Set("chatPosition", util.TableToJSON({x, y, width, height}))
|
||||
end
|
||||
|
||||
function PLUGIN:InitPostEntity()
|
||||
self:CreateChat()
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerBindPress(client, bind, pressed)
|
||||
bind = bind:lower()
|
||||
|
||||
if (bind:find("messagemode") and pressed) then
|
||||
if (hook.Run("ShouldChatOpen") != false) then
|
||||
self.panel:SetActive(true)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:HUDShouldDraw(element)
|
||||
if (element == "CHudChat") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ScreenResolutionChanged(oldWidth, oldHeight)
|
||||
self:CreateChat()
|
||||
end
|
||||
|
||||
function PLUGIN:ChatText(index, name, text, messageType)
|
||||
if (messageType == "none" and IsValid(self.panel)) then
|
||||
self.panel:AddMessage(text)
|
||||
end
|
||||
end
|
||||
|
||||
-- luacheck: globals chat
|
||||
chat.ixAddText = chat.ixAddText or chat.AddText
|
||||
|
||||
function chat.AddText(...)
|
||||
if (IsValid(PLUGIN.panel)) then
|
||||
PLUGIN.panel:AddMessage(...)
|
||||
end
|
||||
|
||||
-- log chat message to console
|
||||
local text = {}
|
||||
|
||||
for _, v in ipairs({...}) do
|
||||
if (istable(v) or isstring(v)) then
|
||||
text[#text + 1] = v
|
||||
elseif (isentity(v) and v:IsPlayer()) then
|
||||
text[#text + 1] = team.GetColor(v:Team())
|
||||
text[#text + 1] = v:Name()
|
||||
elseif (type(v) != "IMaterial") then
|
||||
text[#text + 1] = tostring(v)
|
||||
end
|
||||
end
|
||||
|
||||
text[#text + 1] = "\n"
|
||||
MsgC(unpack(text))
|
||||
end
|
||||
else
|
||||
util.AddNetworkString("ixChatMessage")
|
||||
|
||||
net.Receive("ixChatMessage", function(length, client)
|
||||
local text = net.ReadString()
|
||||
if (string.len(text) > 2048) then
|
||||
return
|
||||
end
|
||||
|
||||
if ((client.ixNextChat or 0) < CurTime() and isstring(text) and text:find("%S")) then
|
||||
hook.Run("PlayerSay", client, text)
|
||||
client.ixNextChat = CurTime() + 0.5
|
||||
end
|
||||
end)
|
||||
end
|
||||
46
gamemodes/helix/plugins/checkstaff.lua
Normal file
46
gamemodes/helix/plugins/checkstaff.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
if (!sam) then return end
|
||||
|
||||
PLUGIN.name = "Check Staff"
|
||||
PLUGIN.author = "AleXXX_007"
|
||||
PLUGIN.description = "Allows superadmins to check various info about server staff."
|
||||
|
||||
ix.command.Add("CheckStaff", {
|
||||
description = "Get admins list with their last play time.",
|
||||
superAdminOnly = true,
|
||||
OnRun = function(self, client)
|
||||
local query = mysql:Select("sam_players")
|
||||
query:Select("steamid")
|
||||
query:Select("name")
|
||||
query:Select("rank")
|
||||
query:Select("last_join")
|
||||
query:Select("play_time")
|
||||
query:WhereNotLike("rank", "user")
|
||||
query:Callback(function(result)
|
||||
if (!result or !istable(result) or #result == 0) then
|
||||
client:NotifyLocalized("No staff found!")
|
||||
return
|
||||
end
|
||||
|
||||
for _, v in pairs(result) do
|
||||
local playTime = math.Round(v.play_time / 3600, 1)
|
||||
if (player.GetBySteamID(v.steamid)) then
|
||||
client:ChatPrint(v.name .. " (" .. v.steamid .. "), " .. v.rank .. " is currently online. Total play time: " .. playTime .. " hours.")
|
||||
else
|
||||
client:ChatPrint(v.name .. " (" .. v.steamid .. "), " .. v.rank .. " was online " .. os.date("%x %X", v.last_join) .. ". Total play time: " .. playTime .. " hours.")
|
||||
end
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
})
|
||||
155
gamemodes/helix/plugins/clientsideprops/cl_hooks.lua
Normal file
155
gamemodes/helix/plugins/clientsideprops/cl_hooks.lua
Normal file
@@ -0,0 +1,155 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function PLUGIN:KeyPress(client, key)
|
||||
if (!IsFirstTimePredicted()) then return end
|
||||
if (key != IN_ATTACK) then return end
|
||||
|
||||
local weapon = client:GetActiveWeapon()
|
||||
if (!IsValid(weapon) or weapon:GetClass() != "weapon_physgun") then return end
|
||||
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Clientside Props")) then return end
|
||||
|
||||
-- Get the clientside entity that the player is looking at
|
||||
local traceLength = 0
|
||||
local targetEntity
|
||||
local aimVector = client:GetAimVector()
|
||||
local trace = {
|
||||
start = client:GetShootPos(),
|
||||
endpos = client:GetShootPos(),
|
||||
filter = client
|
||||
}
|
||||
|
||||
while (traceLength < 250) do -- Don't want it to go forever
|
||||
if (IsValid(targetEntity)) then break end
|
||||
|
||||
trace.endpos = trace.start + aimVector * traceLength
|
||||
|
||||
for _, csent in ipairs(self.activeClientProps) do
|
||||
if (csent:GetPos():DistToSqr(trace.endpos) > 62500) then continue end
|
||||
|
||||
local vMin, vMax = csent:GetRenderBounds()
|
||||
local vPos = csent:WorldToLocal(trace.endpos)
|
||||
|
||||
if (!vPos:WithinAABox(vMax, vMin)) then continue end
|
||||
|
||||
targetEntity = csent
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
traceLength = traceLength + 1
|
||||
end
|
||||
|
||||
if (!IsValid(targetEntity)) then return end
|
||||
|
||||
net.Start("ixClientProps.RecreateProp")
|
||||
net.WriteVector(targetEntity:GetPos())
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local frameInterval = 5
|
||||
|
||||
function PLUGIN:Think()
|
||||
self.coroutine = self.coroutine and coroutine.status(self.coroutine) != "dead" and self.coroutine or coroutine.create(function()
|
||||
while (true) do
|
||||
local maxPerFrame = ix.option.Get("csentRenderSpeed", 50)
|
||||
local i = 0
|
||||
|
||||
for _, data in ipairs(self.clientProps) do
|
||||
self:ManageClientsideProp(data)
|
||||
|
||||
i = i + 1
|
||||
|
||||
if (i == maxPerFrame) then
|
||||
i = 0
|
||||
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
coroutine.yield()
|
||||
end
|
||||
end)
|
||||
|
||||
if (FrameNumber() % frameInterval != 0) then return end
|
||||
local succ, err = coroutine.resume(self.coroutine)
|
||||
|
||||
if (succ) then return end
|
||||
|
||||
ErrorNoHalt(err)
|
||||
end
|
||||
|
||||
function PLUGIN:InitPostEntity()
|
||||
net.Start("ixClientProps.RequestProps")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
net.Receive("ixClientProps.NetworkProp", function()
|
||||
local propData = net.ReadTable()
|
||||
|
||||
PLUGIN.clientProps[#PLUGIN.clientProps + 1] = propData
|
||||
end)
|
||||
|
||||
net.Receive("ixClientProps.RecreateProp", function()
|
||||
local position = net.ReadVector()
|
||||
|
||||
for k, data in ipairs(PLUGIN.clientProps) do
|
||||
if (!data.position:IsEqualTol(position, 0.1)) then continue end
|
||||
|
||||
table.remove(PLUGIN.clientProps, k)
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
for k, csent in ipairs(PLUGIN.activeClientProps) do
|
||||
if (!csent:GetPos():IsEqualTol(position, 0.1)) then continue end
|
||||
|
||||
csent:Remove()
|
||||
table.remove(PLUGIN.activeClientProps, k)
|
||||
|
||||
break
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixClientProps.MassRemoveProps", function()
|
||||
local position = net.ReadVector()
|
||||
local radius = net.ReadUInt(16)
|
||||
|
||||
local newTable = {}
|
||||
|
||||
for k, data in ipairs(PLUGIN.clientProps) do
|
||||
if (data.position:Distance(position) <= radius) then continue end
|
||||
|
||||
newTable[#newTable + 1] = data
|
||||
end
|
||||
|
||||
PLUGIN.clientProps = newTable
|
||||
|
||||
local newActiveTable = {}
|
||||
|
||||
for k, csent in ipairs(PLUGIN.activeClientProps) do
|
||||
if (csent:GetPos():Distance(position) <= radius) then
|
||||
csent:Remove()
|
||||
else
|
||||
newActiveTable[#newActiveTable + 1] = csent
|
||||
end
|
||||
end
|
||||
|
||||
PLUGIN.activeClientProps = newActiveTable
|
||||
end)
|
||||
|
||||
express.Receive("ixClientProps.RequestProps", function(props)
|
||||
PLUGIN.clientProps = props
|
||||
end)
|
||||
|
||||
42
gamemodes/helix/plugins/clientsideprops/cl_plugin.lua
Normal file
42
gamemodes/helix/plugins/clientsideprops/cl_plugin.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
PLUGIN.activeClientProps = PLUGIN.activeClientProps or {}
|
||||
|
||||
function PLUGIN:ManageClientsideProp(csentData)
|
||||
if (NikNaks.PVS.IsPositionVisible(csentData.position, LocalPlayer():EyePos())) then
|
||||
for _, activeProp in ipairs(self.activeClientProps) do
|
||||
if (csentData.position:IsEqualTol(activeProp:GetPos(), 0.1)) then return end -- Ensure we don't have a duplicate
|
||||
end
|
||||
|
||||
local clientProp = ClientsideModel(csentData.model)
|
||||
clientProp:SetPos(csentData.position)
|
||||
clientProp:SetAngles(csentData.angles)
|
||||
clientProp:SetSkin(csentData.skin)
|
||||
clientProp:SetColor(csentData.color)
|
||||
clientProp:SetRenderMode(RENDERMODE_TRANSCOLOR)
|
||||
clientProp:SetMaterial(csentData.material)
|
||||
|
||||
clientProp:Spawn()
|
||||
|
||||
self.activeClientProps[#self.activeClientProps + 1] = clientProp
|
||||
else
|
||||
for k, activeProp in ipairs(self.activeClientProps) do
|
||||
if (!csentData.position:IsEqualTol(activeProp:GetPos(), 0.1)) then continue end
|
||||
|
||||
activeProp:Remove()
|
||||
|
||||
table.remove(self.activeClientProps, k)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
139
gamemodes/helix/plugins/clientsideprops/sh_plugin.lua
Normal file
139
gamemodes/helix/plugins/clientsideprops/sh_plugin.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
require("niknaks")
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Clientside Props"
|
||||
PLUGIN.description = "Adds a way to convert server props to clientside props for performance reasons."
|
||||
PLUGIN.author = "Aspect™"
|
||||
|
||||
PLUGIN.clientProps = PLUGIN.clientProps or {}
|
||||
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sv_hooks.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Clientside Props",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
ix.option.Add("csentRenderSpeed", ix.type.number, 50, {
|
||||
category = "performance",
|
||||
min = 1,
|
||||
max = 500
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optCsentRenderSpeed = "Prędkość renderowana Propów Clientside",
|
||||
optdCsentRenderSpeed = "Ile propów clientside powinno być kalkulowanych co klatkę. Niższe wartości = więcej FPS, ale wolniej renderuje. Wyższe wartości = mniej FPS, ale szybciej renderuje.",
|
||||
cmdRemoveClientProps = "Usuń wszystkie propy clientside w promieniu wokół ciebie."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
optCsentRenderSpeed = "Prędkość renderowana Propów Clientside",
|
||||
optdCsentRenderSpeed = "Ile propów clientside powinno być kalkulowanych co klatkę. Niższe wartości = więcej FPS, ale wolniej renderuje. Wyższe wartości = mniej FPS, ale szybciej renderuje.",
|
||||
cmdRemoveClientProps = "Usuń wszystkie propy clientside w promieniu wokół ciebie."
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveClientProps", {
|
||||
description = "@cmdRemoveClientProps",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.number
|
||||
},
|
||||
OnRun = function(self, client, radius)
|
||||
if (radius < 0) then
|
||||
client:Notify("Radius must be a positive number!")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local newTable = {}
|
||||
|
||||
for k, propData in ipairs(PLUGIN.clientProps) do
|
||||
if (propData.position:Distance(client:GetPos()) <= radius) then continue end
|
||||
|
||||
newTable[#newTable + 1] = propData
|
||||
end
|
||||
|
||||
PLUGIN.clientProps = newTable
|
||||
|
||||
net.Start("ixClientProps.MassRemoveProps")
|
||||
net.WriteVector(client:GetPos())
|
||||
net.WriteUInt(radius, 16)
|
||||
net.Broadcast()
|
||||
|
||||
client:Notify("Removed all clientside props in a radius of " .. radius .. " units.")
|
||||
end
|
||||
})
|
||||
|
||||
local PERSISTENCE = ix.plugin.Get("persistence")
|
||||
|
||||
properties.Add("clientprop", {
|
||||
MenuLabel = "Convert to Client Prop",
|
||||
Order = 400,
|
||||
MenuIcon = "icon16/contrast_low.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
return entity:GetClass() == "prop_physics" and CAMI.PlayerHasAccess(client, "Helix - Manage Clientside Props")
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
self:MsgEnd()
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity)) then return end
|
||||
if (!self:Filter(entity, client)) then return end
|
||||
|
||||
if (!entity:TestPVS(client)) then
|
||||
client:Notify("That prop cannot be converted because its origin is outside the world!")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Unpersist it if it's persisted
|
||||
if (PERSISTENCE) then
|
||||
for k, v in ipairs(PERSISTENCE.stored) do
|
||||
if (v == entity) then
|
||||
table.remove(PERSISTENCE.stored, k)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
entity:SetNetVar("Persistent", false)
|
||||
end
|
||||
|
||||
local propData = {
|
||||
position = entity:GetPos(),
|
||||
angles = entity:GetAngles(),
|
||||
model = entity:GetModel(),
|
||||
skin = entity:GetSkin(),
|
||||
color = entity:GetColor(),
|
||||
material = entity:GetMaterial()
|
||||
}
|
||||
|
||||
entity:Remove()
|
||||
|
||||
PLUGIN.clientProps[#PLUGIN.clientProps + 1] = propData
|
||||
|
||||
PLUGIN:NetworkProp(propData)
|
||||
end
|
||||
})
|
||||
59
gamemodes/helix/plugins/clientsideprops/sv_hooks.lua
Normal file
59
gamemodes/helix/plugins/clientsideprops/sv_hooks.lua
Normal file
@@ -0,0 +1,59 @@
|
||||
--[[
|
||||
| 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
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
self.clientProps = self:GetData() or {}
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
self:SetData(self.clientProps)
|
||||
end
|
||||
|
||||
net.Receive("ixClientProps.RecreateProp", function(_, client)
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Clientside Props")) then return end
|
||||
|
||||
local position = net.ReadVector()
|
||||
|
||||
for k, propData in ipairs(PLUGIN.clientProps) do
|
||||
if (!propData.position:IsEqualTol(position, 0.1)) then continue end
|
||||
|
||||
local entity = ents.Create("prop_physics")
|
||||
entity:SetModel(propData.model)
|
||||
entity:SetPos(position)
|
||||
entity:SetAngles(propData.angles)
|
||||
entity:SetSkin(propData.skin)
|
||||
entity:SetColor(propData.color)
|
||||
entity:SetRenderMode(RENDERMODE_TRANSCOLOR)
|
||||
entity:SetMaterial(propData.material)
|
||||
|
||||
entity:Spawn()
|
||||
|
||||
local physicsObject = entity:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physicsObject)) then
|
||||
physicsObject:EnableMotion(false)
|
||||
end
|
||||
|
||||
table.remove(PLUGIN.clientProps, k)
|
||||
|
||||
net.Start("ixClientProps.RecreateProp")
|
||||
net.WriteVector(position)
|
||||
net.Broadcast()
|
||||
|
||||
break
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixClientProps.RequestProps", function(_, client)
|
||||
express.Send("ixClientProps.RequestProps", PLUGIN.clientProps, client)
|
||||
end)
|
||||
21
gamemodes/helix/plugins/clientsideprops/sv_plugin.lua
Normal file
21
gamemodes/helix/plugins/clientsideprops/sv_plugin.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
util.AddNetworkString("ixClientProps.NetworkProp")
|
||||
util.AddNetworkString("ixClientProps.RecreateProp")
|
||||
util.AddNetworkString("ixClientProps.RequestProps")
|
||||
util.AddNetworkString("ixClientProps.MassRemoveProps")
|
||||
|
||||
function PLUGIN:NetworkProp(propData)
|
||||
net.Start("ixClientProps.NetworkProp")
|
||||
net.WriteTable(propData)
|
||||
net.Broadcast()
|
||||
end
|
||||
@@ -0,0 +1,188 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Container"
|
||||
ENT.Category = "Helix"
|
||||
ENT.Spawnable = false
|
||||
ENT.bNoPersist = true
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("Int", 0, "ID")
|
||||
self:NetworkVar("Bool", 0, "Locked")
|
||||
self:NetworkVar("String", 0, "DisplayName")
|
||||
self:NetworkVar("String", 1, "Password")
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
function ENT:Initialize()
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self.receivers = {}
|
||||
|
||||
local definition = ix.container.stored[self:GetModel():lower()]
|
||||
|
||||
if (definition) then
|
||||
self:SetDisplayName(definition.name)
|
||||
end
|
||||
|
||||
local physObj = self:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physObj)) then
|
||||
physObj:EnableMotion(true)
|
||||
physObj:Wake()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetInventory(inventory)
|
||||
if (inventory) then
|
||||
self:SetID(inventory:GetID())
|
||||
if (ix.saveEnts) then
|
||||
ix.saveEnts:SaveEntity(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetMoney(amount)
|
||||
self.money = math.max(0, math.Round(tonumber(amount) or 0))
|
||||
if (ix.saveEnts) then
|
||||
ix.saveEnts:SaveEntity(self)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetMoney()
|
||||
return self.money or 0
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
local index = self:GetID()
|
||||
|
||||
if (!ix.shuttingDown and !self.ixIsSafe and ix.entityDataLoaded and index) then
|
||||
local inventory = index != 0 and ix.item.inventories[index]
|
||||
|
||||
if (inventory) then
|
||||
ix.item.inventories[index] = nil
|
||||
|
||||
local query = mysql:Delete("ix_items")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Delete("ix_inventories")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
|
||||
hook.Run("ContainerRemoved", self, inventory)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OpenInventory(activator)
|
||||
local inventory = self:GetInventory()
|
||||
|
||||
if (inventory) then
|
||||
local name = self:GetDisplayName()
|
||||
|
||||
ix.storage.Open(activator, inventory, {
|
||||
name = name,
|
||||
entity = self,
|
||||
bMultipleUsers = true,
|
||||
searchTime = ix.config.Get("containerOpenTime", 0.7),
|
||||
data = {money = self:GetMoney()},
|
||||
OnPlayerClose = function()
|
||||
ix.log.Add(activator, "closeContainer", name, inventory:GetID())
|
||||
end
|
||||
})
|
||||
|
||||
ix.log.Add(activator, "openContainer", name, inventory:GetID())
|
||||
|
||||
if (ix.plugin.list.willardcontainers and ix.config.Get("notifyOldcontainer") and self:GetClass() == "ix_container") then
|
||||
activator:ChatNotifyLocalized("containerUseOld")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Use(activator)
|
||||
local inventory = self:GetInventory()
|
||||
|
||||
if (inventory and (activator.ixNextOpen or 0) < CurTime()) then
|
||||
local character = activator:GetCharacter()
|
||||
|
||||
if (character) then
|
||||
local def = ix.container.stored[self:GetModel():lower()]
|
||||
|
||||
if (self:GetLocked() and !self.Sessions[character:GetID()] and !self:GetNetVar("isOneWay", false)) then
|
||||
self:EmitSound(def.locksound or "doors/default_locked.wav")
|
||||
|
||||
if (!self.keypad) then
|
||||
net.Start("ixContainerPassword")
|
||||
net.WriteEntity(self)
|
||||
net.Send(activator)
|
||||
end
|
||||
else
|
||||
self:OpenInventory(activator)
|
||||
end
|
||||
end
|
||||
|
||||
activator.ixNextOpen = CurTime() + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
ENT.PopulateEntityInfo = true
|
||||
|
||||
local COLOR_LOCKED = Color(200, 38, 19, 200)
|
||||
local COLOR_UNLOCKED = Color(135, 211, 124, 200)
|
||||
|
||||
function ENT:OnPopulateEntityInfo(tooltip)
|
||||
local definition = ix.container.stored[self:GetModel():lower()]
|
||||
local bLocked = self:GetLocked()
|
||||
|
||||
surface.SetFont("ixIconsSmall")
|
||||
|
||||
local iconText = bLocked and "P" or "Q"
|
||||
local iconWidth, iconHeight = surface.GetTextSize(iconText)
|
||||
|
||||
-- minimal tooltips have centered text so we'll draw the icon above the name instead
|
||||
if (tooltip:IsMinimal()) then
|
||||
local icon = tooltip:AddRow("icon")
|
||||
icon:SetFont("ixIconsSmall")
|
||||
icon:SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
|
||||
icon:SetText(iconText)
|
||||
icon:SizeToContents()
|
||||
end
|
||||
|
||||
local title = tooltip:AddRow("name")
|
||||
title:SetImportant()
|
||||
title:SetText(self:GetDisplayName())
|
||||
title:SetBackgroundColor(ix.config.Get("color"))
|
||||
title:SetTextInset(iconWidth + 8, 0)
|
||||
title:SizeToContents()
|
||||
|
||||
if (!tooltip:IsMinimal()) then
|
||||
title.Paint = function(panel, width, height)
|
||||
panel:PaintBackground(width, height)
|
||||
|
||||
surface.SetFont("ixIconsSmall")
|
||||
surface.SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
|
||||
surface.SetTextPos(4, height * 0.5 - iconHeight * 0.5)
|
||||
surface.DrawText(iconText)
|
||||
end
|
||||
end
|
||||
|
||||
local description = tooltip:AddRow("description")
|
||||
description:SetText(definition.description)
|
||||
description:SizeToContents()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetInventory()
|
||||
return ix.item.inventories[self:GetID()]
|
||||
end
|
||||
136
gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
136
gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
ix.container.Register(model, {
|
||||
name = "Crate",
|
||||
description = "A simple wooden create.",
|
||||
width = 4,
|
||||
height = 4,
|
||||
locksound = "",
|
||||
opensound = ""
|
||||
})
|
||||
]]--
|
||||
|
||||
ix.container.Register("models/props_junk/wood_crate001a.mdl", {
|
||||
name = "Crate",
|
||||
description = "A simple wooden crate.",
|
||||
width = 4,
|
||||
height = 4,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_c17/lockers001a.mdl", {
|
||||
name = "Locker",
|
||||
description = "A white locker.",
|
||||
width = 3,
|
||||
height = 5,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_storagecloset001a.mdl", {
|
||||
name = "Metal Cabinet",
|
||||
description = "A green metal cabinet.",
|
||||
width = 4,
|
||||
height = 5,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_storagecloset001b.mdl", {
|
||||
name = "Metal Cabinet",
|
||||
description = "A green metal cabinet.",
|
||||
width = 4,
|
||||
height = 5,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_filecabinet001a.mdl", {
|
||||
name = "File Cabinet",
|
||||
description = "A metal filing cabinet.",
|
||||
width = 5,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_filecabinet002a.mdl", {
|
||||
name = "File Cabinet",
|
||||
description = "A metal filing cabinet.",
|
||||
width = 3,
|
||||
height = 6,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_lab/filecabinet02.mdl", {
|
||||
name = "File Cabinet",
|
||||
description = "A metal filing cabinet.",
|
||||
width = 5,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_c17/furniturefridge001a.mdl", {
|
||||
name = "Refrigerator",
|
||||
description = "A metal box for keeping food in.",
|
||||
width = 2,
|
||||
height = 3,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/kitchen_fridge001a.mdl", {
|
||||
name = "Large Refrigerator",
|
||||
description = "A large metal box for storing even more food in.",
|
||||
width = 4,
|
||||
height = 5,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/trashbin01a.mdl", {
|
||||
name = "Trash Bin",
|
||||
description = "What do you expect to find in here?",
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/trashdumpster01a.mdl", {
|
||||
name = "Dumpster",
|
||||
description = "A dumpster meant to stow away trash. It emanates an unpleasant smell.",
|
||||
width = 6,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/items/ammocrate_smg1.mdl", {
|
||||
name = "Ammo Crate",
|
||||
description = "A heavy crate that stores ammo.",
|
||||
width = 5,
|
||||
height = 3,
|
||||
OnOpen = function(entity, activator)
|
||||
local closeSeq = entity:LookupSequence("Close")
|
||||
entity:ResetSequence(closeSeq)
|
||||
|
||||
timer.Simple(2, function()
|
||||
if (entity and IsValid(entity)) then
|
||||
local openSeq = entity:LookupSequence("Open")
|
||||
entity:ResetSequence(openSeq)
|
||||
end
|
||||
end)
|
||||
end
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_forest/footlocker01_closed.mdl", {
|
||||
name = "Footlocker",
|
||||
description = "A small chest to store belongings in.",
|
||||
width = 5,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/Items/item_item_crate.mdl", {
|
||||
name = "Item Crate",
|
||||
description = "A crate to store some belongings in.",
|
||||
width = 5,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_c17/cashregister01a.mdl", {
|
||||
name = "Cash Register",
|
||||
description = "A register with some buttons and a drawer.",
|
||||
width = 2,
|
||||
height = 1
|
||||
})
|
||||
487
gamemodes/helix/plugins/containers/sh_plugin.lua
Normal file
487
gamemodes/helix/plugins/containers/sh_plugin.lua
Normal file
@@ -0,0 +1,487 @@
|
||||
--[[
|
||||
| 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 = "Containers"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Provides the ability to store items."
|
||||
|
||||
ix.container = ix.container or {}
|
||||
ix.container.stored = ix.container.stored or {}
|
||||
|
||||
ix.config.Add("containerSave", true, "Whether or not containers will save after a server restart.", nil, {
|
||||
category = "Containers"
|
||||
})
|
||||
|
||||
ix.config.Add("containerOpenTime", 0.7, "How long it takes to open a container.", nil, {
|
||||
data = {min = 0, max = 50},
|
||||
category = "Containers"
|
||||
})
|
||||
|
||||
function ix.container.Register(model, data)
|
||||
ix.container.stored[model:lower()] = data
|
||||
end
|
||||
|
||||
ix.util.Include("sh_definitions.lua")
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixContainerPassword")
|
||||
|
||||
function PLUGIN:PlayerSpawnedProp(client, model, entity)
|
||||
model = tostring(model):lower()
|
||||
local data = ix.container.stored[model:lower()]
|
||||
|
||||
if (data) then
|
||||
if (hook.Run("CanPlayerSpawnContainer", client, model, entity) == false) then return end
|
||||
|
||||
local container = ents.Create("ix_container")
|
||||
container:SetPos(entity:GetPos())
|
||||
container:SetAngles(entity:GetAngles())
|
||||
container:SetModel(model)
|
||||
container:Spawn()
|
||||
|
||||
ix.inventory.New(0, "container:" .. model:lower(), function(inventory)
|
||||
-- we'll technically call this a bag since we don't want other bags to go inside
|
||||
inventory.vars.isBag = true
|
||||
inventory.vars.isContainer = true
|
||||
|
||||
if (IsValid(container)) then
|
||||
container:SetInventory(inventory)
|
||||
self:SaveContainer()
|
||||
if (ix.saveEnts) then
|
||||
ix.saveEnts:SaveEntity(container)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
entity:Remove()
|
||||
|
||||
hook.Run("PlayerSpawnedContainer", client, container)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:RegisterSaveEnts()
|
||||
ix.saveEnts:RegisterEntity("ix_container", true, true, true, {
|
||||
OnSave = function(entity, data) --OnSave
|
||||
data.motion = false
|
||||
local inventory = entity:GetInventory()
|
||||
local lockentity = entity:GetChildren()[1]
|
||||
data.invID = inventory:GetID()
|
||||
data.model = entity:GetModel()
|
||||
data.pass = entity.password
|
||||
data.name = entity:GetDisplayName()
|
||||
data.money = entity:GetMoney()
|
||||
data.lockpos = lockentity and lockentity:GetPos()
|
||||
data.lockangs = lockentity and lockentity:GetAngles()
|
||||
data.lockowner = lockentity and lockentity:GetNetVar("owner")
|
||||
data.group = entity.group
|
||||
data.oneway = entity:GetNetVar("isOneWay", false)
|
||||
end,
|
||||
OnRestore = function(entity, data) --OnRestore
|
||||
local data2 = ix.container.stored[data.model:lower()] -- Model name
|
||||
if (data2) then
|
||||
local inventoryID = tonumber(data.invID) -- invID
|
||||
|
||||
if (!inventoryID or inventoryID < 1) then
|
||||
ErrorNoHalt(string.format(
|
||||
"[Helix] Attempted to restore container inventory with invalid inventory ID '%s' (%s, %s)\n",
|
||||
tostring(inventoryID), data.name or "no name", data.model or "no model"))
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
entity:SetModel(data.model) -- Model name
|
||||
entity:SetSolid(SOLID_VPHYSICS)
|
||||
entity:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
|
||||
|
||||
if (data.pass) then -- Password
|
||||
entity.password = data.pass
|
||||
entity:SetLocked(true)
|
||||
entity:SetPassword(data.pass)
|
||||
entity.Sessions = {}
|
||||
end
|
||||
|
||||
if (data.name) then -- Display name
|
||||
entity:SetDisplayName(data.name)
|
||||
end
|
||||
|
||||
if (data.money) then -- Money
|
||||
entity:SetMoney(data.money)
|
||||
end
|
||||
|
||||
if (data.lockpos) then -- Lock Pos
|
||||
local lockentity = ents.Create("ix_containerlock")
|
||||
lockentity:SetPos(data.lockpos) -- Lock Pos
|
||||
lockentity:SetAngles(data.lockangs) -- Lock Angles
|
||||
lockentity:SetParent(entity)
|
||||
lockentity:SetNetVar("owner", data.lockowner) -- Lock Owner
|
||||
lockentity:Spawn()
|
||||
end
|
||||
|
||||
if (data.group) then
|
||||
entity.group = data.group -- Group
|
||||
end
|
||||
|
||||
entity:SetNetVar("isOneWay", data.oneway)
|
||||
|
||||
ix.inventory.Restore(inventoryID, data2.width, data2.height, function(inventory)
|
||||
inventory.vars.isBag = true
|
||||
inventory.vars.isContainer = true
|
||||
|
||||
if (IsValid(entity)) then
|
||||
entity:SetInventory(inventory)
|
||||
end
|
||||
end)
|
||||
|
||||
local physObject = entity:GetPhysicsObject()
|
||||
if (IsValid(physObject)) then
|
||||
physObject:EnableMotion()
|
||||
end
|
||||
end
|
||||
end,
|
||||
ShouldSave = function(entity)
|
||||
local inventory = entity:GetInventory()
|
||||
return entity:GetModel() != "models/error.mdl" and inventory:GetID() != 0 //avoid bad save that somehow happened
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:CanSaveContainer(entity, inventory)
|
||||
return ix.config.Get("containerSave", true)
|
||||
end
|
||||
|
||||
function PLUGIN:SaveContainer()
|
||||
local data = {}
|
||||
|
||||
for _, v in ipairs(ents.FindByClass("ix_container")) do
|
||||
if (hook.Run("CanSaveContainer", v, v:GetInventory()) != false) then
|
||||
local inventory = v:GetInventory()
|
||||
if (!inventory) then continue end
|
||||
|
||||
local lockentity = v:GetChildren()[1]
|
||||
|
||||
data[#data + 1] = {
|
||||
v:GetPos(),
|
||||
v:GetAngles(),
|
||||
inventory:GetID(),
|
||||
v:GetModel(),
|
||||
v.password,
|
||||
v:GetDisplayName(),
|
||||
v:GetMoney(),
|
||||
lockentity and lockentity:GetPos(),
|
||||
lockentity and lockentity:GetAngles(),
|
||||
lockentity and lockentity:GetNetVar("owner"),
|
||||
v.group,
|
||||
v:GetNetVar("isOneWay", false)
|
||||
}
|
||||
else
|
||||
local index = v:GetID()
|
||||
|
||||
local query = mysql:Delete("ix_items")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Delete("ix_inventories")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
self:SetData(data)
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
if (!ix.shuttingDown) then
|
||||
self:SaveContainer()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ContainerRemoved(entity, inventory)
|
||||
self:SaveContainer()
|
||||
end
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
if (ix.saveEnts and !ix.config.Get("SaveEntsOldLoadingEnabled")) then return end
|
||||
local data = self:GetData()
|
||||
|
||||
if (data) then
|
||||
for _, v in ipairs(data) do
|
||||
if (!v[4]) then continue end -- Model name
|
||||
|
||||
local data2 = ix.container.stored[v[4]:lower()] -- Model name
|
||||
|
||||
if (data2) then
|
||||
local inventoryID = tonumber(v[3]) -- invID
|
||||
|
||||
if (!inventoryID or inventoryID < 1) then
|
||||
ErrorNoHalt(string.format(
|
||||
"[Helix] Attempted to restore container inventory with invalid inventory ID '%s' (%s, %s)\n",
|
||||
tostring(inventoryID), v[6] or "no name", v[4] or "no model"))
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
local entity = ents.Create("ix_container")
|
||||
entity:SetPos(v[1]) -- Pos
|
||||
entity:SetAngles(v[2]) -- Angles
|
||||
entity:Spawn()
|
||||
entity:SetModel(v[4]) -- Model name
|
||||
entity:SetSolid(SOLID_VPHYSICS)
|
||||
entity:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
if (v[5]) then -- Password
|
||||
entity.password = v[5]
|
||||
entity:SetLocked(true)
|
||||
entity:SetPassword(v[5])
|
||||
entity.Sessions = {}
|
||||
end
|
||||
|
||||
if (v[6]) then -- Display name
|
||||
entity:SetDisplayName(v[6])
|
||||
end
|
||||
|
||||
if (v[7]) then -- Money
|
||||
entity:SetMoney(v[7])
|
||||
end
|
||||
|
||||
if (v[8]) then -- Lock Pos
|
||||
local lockentity = ents.Create("ix_containerlock")
|
||||
lockentity:SetPos(v[8]) -- Lock Pos
|
||||
lockentity:SetAngles(v[9]) -- Lock Angles
|
||||
lockentity:SetParent(entity)
|
||||
lockentity:SetNetVar("owner", v[10]) -- Lock Owner
|
||||
lockentity:Spawn()
|
||||
end
|
||||
|
||||
if (v[11]) then
|
||||
entity.group = v[11] -- Group
|
||||
end
|
||||
|
||||
entity:SetNetVar("isOneWay", v[12])
|
||||
|
||||
ix.inventory.Restore(inventoryID, data2.width, data2.height, function(inventory)
|
||||
inventory.vars.isBag = true
|
||||
inventory.vars.isContainer = true
|
||||
|
||||
if (IsValid(entity)) then
|
||||
entity:SetInventory(inventory)
|
||||
end
|
||||
end)
|
||||
|
||||
local physObject = entity:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physObject)) then
|
||||
physObject:EnableMotion()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixContainerPassword", function(length, client)
|
||||
if ((client.ixNextContainerPassword or 0) > RealTime()) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = net.ReadEntity()
|
||||
local password = net.ReadString()
|
||||
local dist = entity:GetPos():DistToSqr(client:GetPos())
|
||||
|
||||
if (dist < 16384 and password) then
|
||||
if (entity.password and entity.password == password) then
|
||||
if (!entity:GetNetVar("isOneWay", false)) then
|
||||
entity:OpenInventory(client)
|
||||
end
|
||||
entity.Sessions[client:GetCharacter():GetID()] = true
|
||||
else
|
||||
client:NotifyLocalized("wrongPassword")
|
||||
end
|
||||
end
|
||||
|
||||
client.ixNextContainerPassword = RealTime() + 0.5
|
||||
end)
|
||||
|
||||
ix.log.AddType("containerPassword", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s has %s the password for '%s'.", client:Name(), arg[3] and "set" or "removed", arg[1], arg[2])
|
||||
end)
|
||||
|
||||
ix.log.AddType("containerName", function(client, ...)
|
||||
local arg = {...}
|
||||
|
||||
if (arg[3]) then
|
||||
return string.format("%s has set container %d name to '%s'.", client:Name(), arg[2], arg[1])
|
||||
else
|
||||
return string.format("%s has removed container %d name.", client:Name(), arg[2])
|
||||
end
|
||||
end)
|
||||
|
||||
ix.log.AddType("openContainer", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s opened the '%s' #%d container.", client:Name(), arg[1], arg[2])
|
||||
end, FLAG_NORMAL)
|
||||
|
||||
ix.log.AddType("closeContainer", function(client, ...)
|
||||
local name
|
||||
if !client or client and !IsValid(client) or IsValid(client) and !client.Name then
|
||||
name = "N/A"
|
||||
else
|
||||
name = client.Name and client:Name()
|
||||
end
|
||||
|
||||
local arg = {...}
|
||||
return string.format("%s closed the '%s' #%d container.", name, arg[1], arg[2])
|
||||
end, FLAG_NORMAL)
|
||||
else
|
||||
net.Receive("ixContainerPassword", function(length)
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
Derma_StringRequest(
|
||||
L("containerPasswordWrite"),
|
||||
L("containerPasswordWrite"),
|
||||
"",
|
||||
function(val)
|
||||
net.Start("ixContainerPassword")
|
||||
net.WriteEntity(entity)
|
||||
net.WriteString(val)
|
||||
net.SendToServer()
|
||||
end
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:InitializedPlugins()
|
||||
for k, v in pairs(ix.container.stored) do
|
||||
if (v.name and v.width and v.height) then
|
||||
ix.inventory.Register("container:" .. k:lower(), v.width, v.height)
|
||||
else
|
||||
ErrorNoHalt("[Helix] Container for '"..k.."' is missing all inventory information!\n")
|
||||
ix.container.stored[k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- properties
|
||||
properties.Add("container_setpassword", {
|
||||
MenuLabel = "Set Password",
|
||||
Order = 400,
|
||||
MenuIcon = "icon16/lock_edit.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (entity:GetClass() != "ix_container") then return false end
|
||||
if (!gamemode.Call("CanProperty", client, "container_setpassword", entity)) then return false end
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
Derma_StringRequest(L("containerPasswordWrite"), "", "", function(text)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
net.WriteString(text)
|
||||
self:MsgEnd()
|
||||
end)
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity)) then return end
|
||||
if (!self:Filter(entity, client)) then return end
|
||||
|
||||
local password = net.ReadString()
|
||||
|
||||
entity.Sessions = {}
|
||||
|
||||
if (password:len() != 0) then
|
||||
entity:SetLocked(true)
|
||||
entity:SetPassword(password)
|
||||
entity.password = password
|
||||
|
||||
client:NotifyLocalized("containerPassword", password)
|
||||
else
|
||||
entity:SetLocked(false)
|
||||
entity:SetPassword(nil)
|
||||
entity.password = nil
|
||||
|
||||
client:NotifyLocalized("containerPasswordRemove")
|
||||
end
|
||||
|
||||
local name = entity:GetDisplayName()
|
||||
local inventory = entity:GetInventory()
|
||||
|
||||
local definition = ix.container.stored[entity:GetModel():lower()]
|
||||
|
||||
if (definition) then
|
||||
entity:SetDisplayName(definition.name)
|
||||
end
|
||||
|
||||
if (ix.saveEnts) then
|
||||
ix.saveEnts:SaveEntity(self)
|
||||
end
|
||||
|
||||
ix.log.Add(client, "containerPassword", name, inventory:GetID(), password:len() != 0)
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("container_setname", {
|
||||
MenuLabel = "Set Name",
|
||||
Order = 400,
|
||||
MenuIcon = "icon16/tag_blue_edit.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (entity:GetClass() != "ix_container") then return false end
|
||||
if (!gamemode.Call("CanProperty", client, "container_setname", entity)) then return false end
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
Action = function(self, entity)
|
||||
Derma_StringRequest(L("containerNameWrite"), "", "", function(text)
|
||||
self:MsgStart()
|
||||
net.WriteEntity(entity)
|
||||
net.WriteString(text)
|
||||
self:MsgEnd()
|
||||
end)
|
||||
end,
|
||||
|
||||
Receive = function(self, length, client)
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity)) then return end
|
||||
if (!self:Filter(entity, client)) then return end
|
||||
|
||||
local name = net.ReadString()
|
||||
|
||||
if (name:len() != 0) then
|
||||
entity:SetDisplayName(name)
|
||||
|
||||
client:NotifyLocalized("containerName", name)
|
||||
else
|
||||
local definition = ix.container.stored[entity:GetModel():lower()]
|
||||
|
||||
entity:SetDisplayName(definition.name)
|
||||
|
||||
client:NotifyLocalized("containerNameRemove")
|
||||
end
|
||||
|
||||
if (ix.saveEnts) then
|
||||
ix.saveEnts:SaveEntity(self)
|
||||
end
|
||||
|
||||
local inventory = entity:GetInventory()
|
||||
|
||||
ix.log.Add(client, "containerName", name, inventory:GetID(), name:len() != 0)
|
||||
end
|
||||
})
|
||||
120
gamemodes/helix/plugins/crosshair.lua
Normal file
120
gamemodes/helix/plugins/crosshair.lua
Normal file
@@ -0,0 +1,120 @@
|
||||
--[[
|
||||
| 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 = "Crosshair"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "A Crosshair."
|
||||
|
||||
if (CLIENT) then
|
||||
local function drawdot( pos, size, col )
|
||||
local color = col[2]
|
||||
surface.SetDrawColor(color.r, color.g, color.b, color.a)
|
||||
surface.DrawRect(pos[1] - size/2, pos[2] - size/2, size, size)
|
||||
|
||||
color = col[1]
|
||||
surface.SetDrawColor(color.r, color.g, color.b, color.a)
|
||||
surface.DrawOutlinedRect(pos[1] - size/2, pos[2] - size/2 , size, size)
|
||||
end
|
||||
|
||||
local aimVector, punchAngle, ft, screen, scaleFraction, distance
|
||||
local math_round = math.Round
|
||||
local curGap = 0
|
||||
local curAlpha = 0
|
||||
local maxDistance = 1000 ^ 2
|
||||
local crossSize = 4
|
||||
local crossGap = 0
|
||||
local colors = {color_black}
|
||||
local filter = {}
|
||||
|
||||
function PLUGIN:DrawCrosshair(x, y, trace)
|
||||
local entity = trace.Entity
|
||||
distance = trace.StartPos:DistToSqr(trace.HitPos)
|
||||
scaleFraction = 1 - math.Clamp(distance / maxDistance, 0, .5)
|
||||
crossSize = 4
|
||||
crossGap = 25 * (scaleFraction - (LocalPlayer():IsWepRaised() and 0 or .1))
|
||||
|
||||
if (IsValid(entity) and entity:GetClass() == "ix_item" and
|
||||
entity:GetPos():DistToSqr(trace.StartPos) <= 16384) then
|
||||
crossGap = 0
|
||||
crossSize = 5
|
||||
end
|
||||
|
||||
curGap = Lerp(ft * 2, curGap, crossGap)
|
||||
curAlpha = Lerp(ft * 2, curAlpha, !LocalPlayer():IsWepRaised() and 255 or 150)
|
||||
curAlpha = hook.Run("GetCrosshairAlpha", curAlpha) or curAlpha
|
||||
colors[2] = Color(255, curAlpha, curAlpha, curAlpha)
|
||||
|
||||
if (curAlpha > 1) then
|
||||
drawdot( {math_round(screen.x), math_round(screen.y)}, crossSize, colors)
|
||||
drawdot( {math_round(screen.x + curGap), math_round(screen.y)}, crossSize, colors)
|
||||
drawdot( {math_round(screen.x - curGap), math_round(screen.y)}, crossSize, colors)
|
||||
drawdot( {math_round(screen.x), math_round(screen.y + curGap * .8)}, crossSize, colors)
|
||||
drawdot( {math_round(screen.x), math_round(screen.y - curGap * .8)}, crossSize, colors)
|
||||
end
|
||||
end
|
||||
|
||||
-- luacheck: globals g_ContextMenu
|
||||
function PLUGIN:PostDrawHUD()
|
||||
local client = LocalPlayer()
|
||||
if (!client:GetCharacter() or !client:Alive()) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = Entity(client:GetLocalVar("ragdoll", 0))
|
||||
|
||||
if (entity:IsValid()) then
|
||||
return
|
||||
end
|
||||
|
||||
local wep = client:GetActiveWeapon()
|
||||
local bShouldDraw = hook.Run("ShouldDrawCrosshair", client, wep)
|
||||
|
||||
if (bShouldDraw == false or !IsValid(wep) or wep.DrawCrosshair == false) then
|
||||
return
|
||||
end
|
||||
|
||||
if (bShouldDraw == false or g_ContextMenu:IsVisible() or
|
||||
(IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing())) then
|
||||
return
|
||||
end
|
||||
|
||||
aimVector = client:EyeAngles()
|
||||
punchAngle = client:GetViewPunchAngles()
|
||||
ft = FrameTime()
|
||||
filter = {client}
|
||||
|
||||
local vehicle = client:GetVehicle()
|
||||
if (vehicle and IsValid(vehicle)) then
|
||||
aimVector = aimVector + vehicle:GetAngles()
|
||||
table.insert(filter, vehicle)
|
||||
end
|
||||
|
||||
local data = {}
|
||||
data.start = client:GetShootPos()
|
||||
data.endpos = data.start + (aimVector + punchAngle):Forward() * 65535
|
||||
data.filter = filter
|
||||
local trace = util.TraceLine(data)
|
||||
|
||||
local drawTarget = self
|
||||
local drawFunction = self.DrawCrosshair
|
||||
|
||||
-- we'll manually call this since CHudCrosshair is never drawn; checks are already performed
|
||||
if (wep.DoDrawCrosshair) then
|
||||
drawTarget = wep
|
||||
drawFunction = wep.DoDrawCrosshair
|
||||
end
|
||||
|
||||
screen = trace.HitPos:ToScreen()
|
||||
drawFunction(drawTarget, screen.x, screen.y, trace)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
|
||||
function ITEM:GetName()
|
||||
return self:GetData("name", "Custom Item")
|
||||
end
|
||||
|
||||
function ITEM:GetDescription()
|
||||
return self:GetData("description", "Custom item description.")
|
||||
end
|
||||
|
||||
function ITEM:GetModel()
|
||||
return self:GetData("model", "models/Gibs/HGIBS.mdl")
|
||||
end
|
||||
|
||||
function ITEM:GetMaterial()
|
||||
return self:GetData("material", nil)
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
|
||||
ITEM.width = 3
|
||||
ITEM.height = 3
|
||||
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
|
||||
ITEM.width = 2
|
||||
ITEM.height = 2
|
||||
@@ -0,0 +1,13 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
|
||||
ITEM.width = 4
|
||||
ITEM.height = 4
|
||||
14
gamemodes/helix/plugins/customitemold/sh_plugin.lua
Normal file
14
gamemodes/helix/plugins/customitemold/sh_plugin.lua
Normal file
@@ -0,0 +1,14 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
PLUGIN.name = "[OLD] Custom Scripts"
|
||||
PLUGIN.author = "Gary Tate"
|
||||
PLUGIN.description = "Enables staff members to create custom scripts. Kept for backwards compatibility."
|
||||
131
gamemodes/helix/plugins/deathdrop/entities/entities/ix_drop.lua
Normal file
131
gamemodes/helix/plugins/deathdrop/entities/entities/ix_drop.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
--[[
|
||||
| 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 DEFINE_BASECLASS = DEFINE_BASECLASS
|
||||
local IsValid = IsValid
|
||||
local hook = hook
|
||||
local CurTime = CurTime
|
||||
local net = net
|
||||
local Color = Color
|
||||
local surface = surface
|
||||
local ix = ix
|
||||
|
||||
|
||||
DEFINE_BASECLASS("ix_container")
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Dropped Items"
|
||||
ENT.Category = "Helix"
|
||||
ENT.Spawnable = false
|
||||
|
||||
if (SERVER) then
|
||||
function ENT:Initialize()
|
||||
self:PhysicsInit(SOLID_VPHYSICS)
|
||||
self:SetSolid(SOLID_VPHYSICS)
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self.receivers = {}
|
||||
|
||||
self:SetDisplayName("Dropped Items")
|
||||
|
||||
local physObj = self:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physObj)) then
|
||||
physObj:EnableMotion(true)
|
||||
physObj:Wake()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
local index = self:GetID()
|
||||
|
||||
if (!ix.shuttingDown and !self.ixIsSafe and ix.entityDataLoaded and index) then
|
||||
local inventory = index != 0 and ix.item.inventories[index]
|
||||
|
||||
if (inventory) then
|
||||
ix.item.inventories[index] = nil
|
||||
|
||||
local query = mysql:Delete("ix_items")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Delete("ix_inventories")
|
||||
query:Where("inventory_id", index)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
hook.Run("DropRemoved", self, inventory)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Use(activator)
|
||||
local inventory = self:GetInventory()
|
||||
|
||||
if (inventory and (activator.ixNextOpen or 0) < CurTime()) then
|
||||
local character = activator:GetCharacter()
|
||||
|
||||
if (character) then
|
||||
if (self:GetLocked() and !self.Sessions[character:GetID()]) then
|
||||
self:EmitSound("doors/default_locked.wav")
|
||||
|
||||
if (!self.keypad) then
|
||||
net.Start("ixContainerPassword")
|
||||
net.WriteEntity(self)
|
||||
net.Send(activator)
|
||||
end
|
||||
else
|
||||
self:OpenInventory(activator)
|
||||
end
|
||||
end
|
||||
|
||||
activator.ixNextOpen = CurTime() + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
ENT.PopulateEntityInfo = true
|
||||
|
||||
local COLOR_LOCKED = Color(200, 38, 19, 200)
|
||||
local COLOR_UNLOCKED = Color(135, 211, 124, 200)
|
||||
|
||||
function ENT:OnPopulateEntityInfo(tooltip)
|
||||
local bLocked = self:GetLocked()
|
||||
|
||||
surface.SetFont("ixIconsSmall")
|
||||
|
||||
local iconText = bLocked and "P" or "Q"
|
||||
local iconWidth, iconHeight = surface.GetTextSize(iconText)
|
||||
|
||||
-- minimal tooltips have centered text so we'll draw the icon above the name instead
|
||||
if (tooltip:IsMinimal()) then
|
||||
local icon = tooltip:AddRow("icon")
|
||||
icon:SetFont("ixIconsSmall")
|
||||
icon:SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
|
||||
icon:SetText(iconText)
|
||||
icon:SizeToContents()
|
||||
end
|
||||
|
||||
local title = tooltip:AddRow("name")
|
||||
title:SetImportant()
|
||||
title:SetText(self:GetDisplayName())
|
||||
title:SetBackgroundColor(ix.config.Get("color"))
|
||||
title:SetTextInset(iconWidth + 8, 0)
|
||||
title:SizeToContents()
|
||||
|
||||
if (!tooltip:IsMinimal()) then
|
||||
title.Paint = function(panel, width, height)
|
||||
panel:PaintBackground(width, height)
|
||||
|
||||
surface.SetFont("ixIconsSmall")
|
||||
surface.SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
|
||||
surface.SetTextPos(4, height * 0.5 - iconHeight * 0.5)
|
||||
surface.DrawText(iconText)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
16
gamemodes/helix/plugins/deathdrop/languages/sh_english.lua
Normal file
16
gamemodes/helix/plugins/deathdrop/languages/sh_english.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
LANGUAGE = {
|
||||
plyNoRefunding = "This character does not have any lost items.",
|
||||
plyRefund = "Items were successfully refunded.",
|
||||
plyRefundTemp = "Items were successfully refunded. Excess ones were put in a container in front.",
|
||||
plyRefundLost = "%s items were irretrievably lost in the refund!"
|
||||
}
|
||||
16
gamemodes/helix/plugins/deathdrop/languages/sh_polish.lua
Normal file
16
gamemodes/helix/plugins/deathdrop/languages/sh_polish.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
LANGUAGE = {
|
||||
plyNoRefunding = "Ta postać nie ma żadnych utraconych przedmiotów.",
|
||||
plyRefund = "Przedmioty zostały pomyślnie zwrócone.",
|
||||
plyRefundTemp = "Przedmioty zostały pomyślnie zwrócone. Nadmiar został umieszczony w pojemniku z przodu",
|
||||
plyRefundLost = "%s przedmiotów zostało bezpowrotnie utracone podczas zwrotu!"
|
||||
}
|
||||
16
gamemodes/helix/plugins/deathdrop/languages/sh_spanish.lua
Normal file
16
gamemodes/helix/plugins/deathdrop/languages/sh_spanish.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
LANGUAGE = {
|
||||
plyNoRefunding = "Este personaje no tiene objetos perdidos.",
|
||||
plyRefundTemp = "Los objetos se devolvieron con éxito. Los objetos sobrantes se colocaron en un contenedor en frente.",
|
||||
plyRefund = "Los objetos han sido devueltos con éxito.",
|
||||
plyRefundLost = "%s objetos han sido irremediablemente perdidos en la devolución!"
|
||||
}
|
||||
230
gamemodes/helix/plugins/deathdrop/sh_plugin.lua
Normal file
230
gamemodes/helix/plugins/deathdrop/sh_plugin.lua
Normal file
@@ -0,0 +1,230 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local table = table
|
||||
local pairs = pairs
|
||||
local ents = ents
|
||||
local IsValid = IsValid
|
||||
local Color = Color
|
||||
local math = math
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Drop Items on Death"
|
||||
PLUGIN.author = "AleXXX_007"
|
||||
PLUGIN.description = "After death, the character will drop they belongings."
|
||||
|
||||
ix.config.Add("dropItems", false, "Whether or not characters drop their items after death.", nil, {
|
||||
category = "Drop Items on Death"
|
||||
})
|
||||
|
||||
ix.config.Add("dropContainerModel", "models/props_c17/BriefCase001a.mdl", "A model path of a container with dropped items.", nil, {
|
||||
category = "Drop Items on Death"
|
||||
})
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
if (ix.allowedHoldableClasses) then
|
||||
ix.allowedHoldableClasses["ix_drop"] = true
|
||||
end
|
||||
|
||||
ix.char.RegisterVar("refundItems", {
|
||||
field = "refundItems",
|
||||
default = {},
|
||||
bNoNetworking = true,
|
||||
bNoDisplay = true
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
deathDropRemoved = "Usunięto łącznie %d death dropów."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
deathDropRemoved = "Borrados un total de %d death drops."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("polish", {
|
||||
deathDropRemoved = "Usunięto łącznie %d death dropów."
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveDeathDrops", {
|
||||
description = "Removes all death drops on the map or in the given radius.",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.number
|
||||
},
|
||||
OnRun = function(self, client, radius)
|
||||
radius = radius and radius * radius or math.huge
|
||||
local pos = client:GetPos()
|
||||
|
||||
local count = 0
|
||||
for k, v in pairs(ents.GetAll()) do
|
||||
if (!IsValid(v)) then continue end
|
||||
|
||||
if (v:GetClass() != "ix_drop") then continue end
|
||||
if (v:GetPos():DistToSqr(pos) > radius) then continue end
|
||||
|
||||
v:Remove()
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
return "@deathDropRemoved", count
|
||||
end
|
||||
})
|
||||
|
||||
if (CLIENT) then
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
|
||||
local command = string.utf8lower(ix.chat.currentCommand)
|
||||
if (command == "removedeathdrops") then
|
||||
local range = tonumber(ix.chat.currentArguments[1])
|
||||
if (range) then
|
||||
render.SetColorMaterial()
|
||||
render.DrawSphere(LocalPlayer():GetPos(), 0 - range, 50, 50, Color(255,150,0,100))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("RefundKill", {
|
||||
description = "Returns all items player lost after they last death.",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.player
|
||||
},
|
||||
OnRun = function(self, client, target)
|
||||
local inventory = target:GetCharacter():GetInventory()
|
||||
local itemIds = target:GetCharacter():GetRefundItems()
|
||||
|
||||
if (!itemIds) then
|
||||
client:NotifyLocalized("plyNoRefunding")
|
||||
end
|
||||
local leftItems = table.Copy(itemIds)
|
||||
local lost = 0
|
||||
local temp = false
|
||||
|
||||
for _, v in pairs(itemIds) do
|
||||
local itemTable = ix.item.instances[v]
|
||||
if (!itemTable) then
|
||||
lost = lost + 1
|
||||
continue
|
||||
end
|
||||
|
||||
local curInv = ix.item.inventories[self.invID or 0]
|
||||
if (!curInv) then
|
||||
ErrorNoHalt("[DEATHDROP] Failed to transfer item "..itemTable.uniqueID.." (#"..v.."), no curInv!")
|
||||
continue
|
||||
end
|
||||
|
||||
local w, h = itemTable.width, itemTable.height
|
||||
if (inventory:FindEmptySlot(w, h)) then
|
||||
PLUGIN:TransferItem(target, itemTable, curInv, inventory)
|
||||
table.RemoveByValue(leftItems, v)
|
||||
else
|
||||
temp = true
|
||||
|
||||
local container = ents.Create("ix_drop")
|
||||
container:SetPos(target:GetPos() + target:GetAngles():Forward() * 5)
|
||||
container:SetModel(ix.config.Get("dropContainerModel", "models/props_c17/BriefCase001a.mdl"))
|
||||
container:Spawn()
|
||||
|
||||
ix.inventory.New(0, "droppedItems", function(newInv)
|
||||
newInv.vars.isBag = false
|
||||
newInv.vars.isDrop = true
|
||||
|
||||
function newInv.OnAuthorizeTransfer(_, _, _, item)
|
||||
if (item.authorized) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if (!IsValid(container)) then
|
||||
return
|
||||
end
|
||||
|
||||
container:SetInventory(newInv)
|
||||
newInv.vars.entity = container
|
||||
|
||||
for _, v1 in pairs(leftItems) do
|
||||
local item = ix.item.instances[v1]
|
||||
|
||||
if (!item) then continue end
|
||||
local owner = item:GetOwner()
|
||||
local curInv1 = IsValid(owner) and owner:GetCharacter():GetInventory() or false
|
||||
|
||||
PLUGIN:TransferItem(owner, item, curInv1, newInv)
|
||||
end
|
||||
|
||||
ix.saveEnts:SaveEntity(container)
|
||||
end)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local notify = temp and "plyRefundTemp" or "plyRefund"
|
||||
client:NotifyLocalized(notify)
|
||||
|
||||
if (lost > 0) then
|
||||
client:NotifyLocalized("plyRefundLost", lost)
|
||||
end
|
||||
|
||||
if (client != target) then
|
||||
target:NotifyLocalized(notify)
|
||||
|
||||
if (lost > 0) then
|
||||
target:NotifyLocalized("plyRefundLost", lost)
|
||||
end
|
||||
end
|
||||
|
||||
target:GetCharacter():SetRefundItems(nil)
|
||||
end
|
||||
})
|
||||
|
||||
function PLUGIN:InitializedConfig()
|
||||
ix.inventory.Register("droppedItems", 9, 9)
|
||||
end
|
||||
|
||||
function PLUGIN:CanTransferItem(itemTable, curInv, inventory)
|
||||
if (!itemTable.authorized) then
|
||||
if (curInv:GetID() == 0) then return end
|
||||
|
||||
if (curInv.vars and curInv.vars.isDrop and inventory:GetID() == curInv:GetID()) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (inventory.vars and inventory.vars.isDrop) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnItemTransferred(itemTable, curInv, inventory)
|
||||
if (curInv.vars and curInv.vars.isDrop and table.IsEmpty(curInv:GetItems())) then
|
||||
local container = curInv.vars.entity
|
||||
|
||||
if (IsValid(container)) then
|
||||
container:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (CLIENT) then
|
||||
local color = Color(255, 0, 128)
|
||||
function PLUGIN:InitializedPlugins()
|
||||
local function drawDropESP(client, entity, x, y, factor)
|
||||
ix.util.DrawText("Death Drop", x, y - math.max(10, 32 * factor), color,
|
||||
TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, math.max(255 * factor, 80))
|
||||
end
|
||||
|
||||
ix.observer:RegisterESPType("ix_drop", drawDropESP, "drop")
|
||||
end
|
||||
end
|
||||
220
gamemodes/helix/plugins/deathdrop/sv_plugin.lua
Normal file
220
gamemodes/helix/plugins/deathdrop/sv_plugin.lua
Normal file
@@ -0,0 +1,220 @@
|
||||
--[[
|
||||
| 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 ix = ix
|
||||
local IsValid = IsValid
|
||||
local pairs = pairs
|
||||
local isfunction = isfunction
|
||||
local table = table
|
||||
local ents = ents
|
||||
local hook = hook
|
||||
local ipairs = ipairs
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
ix.log.AddType("droppedItems", function(client, text)
|
||||
return text
|
||||
end)
|
||||
|
||||
function PLUGIN:TransferItem(client, itemTable, curInv, inventory)
|
||||
itemTable.player = client
|
||||
if (itemTable:GetData("equip") == true and itemTable.functions.EquipUn) then
|
||||
itemTable.functions.EquipUn.OnRun(itemTable)
|
||||
end
|
||||
|
||||
if (itemTable.OnTransferred) then
|
||||
itemTable:OnTransferred(curInv, inventory)
|
||||
end
|
||||
|
||||
if (itemTable.OnDrop) then
|
||||
itemTable:OnDrop()
|
||||
end
|
||||
|
||||
local x, y = itemTable.gridX, itemTable.gridY
|
||||
local w, h = itemTable.width, itemTable.height
|
||||
|
||||
if (!x or !y or !inventory:CanItemFit(x, y, w, h)) then
|
||||
x, y = inventory:FindEmptySlot(w, h)
|
||||
end
|
||||
|
||||
itemTable.authorized = true
|
||||
itemTable:Transfer(inventory:GetID(), x, y)
|
||||
|
||||
local entity = itemTable:GetEntity()
|
||||
|
||||
if (IsValid(entity)) then
|
||||
itemTable:GetEntity():Remove()
|
||||
end
|
||||
|
||||
itemTable.player = nil
|
||||
itemTable.authorized = nil
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldCharacterDropItems(client)
|
||||
if (!ix.config.Get("dropItems")) then
|
||||
return false
|
||||
end
|
||||
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (character) then
|
||||
local faction = character:GetFaction()
|
||||
|
||||
if (faction) then
|
||||
local factionTable = ix.faction.Get(faction)
|
||||
|
||||
if (factionTable.saveItemsAfterDeath) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDeath(client, inflictor, attacker)
|
||||
if (hook.Run("ShouldCharacterDropItems", client) != false) then
|
||||
local clientInv = client:GetCharacter():GetInventory()
|
||||
local droppedItems = {}
|
||||
local items = {}
|
||||
|
||||
for _, v in pairs(clientInv:GetItems()) do
|
||||
if (v.isBag) then continue end
|
||||
|
||||
if (v.OnDeathDrop) then
|
||||
v:OnDeathDrop(client, items, droppedItems)
|
||||
elseif ((isfunction(v.KeepOnDeath) and v:KeepOnDeath(client) or v.KeepOnDeath) != true) then
|
||||
table.insert(items, v)
|
||||
table.insert(droppedItems, v:GetID())
|
||||
if (v.OnDoDeathDrop) then
|
||||
v:OnDoDeathDrop(client, items, droppedItems)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (!table.IsEmpty(items)) then
|
||||
local container = ents.Create("ix_drop")
|
||||
container:SetPos(client:GetPos())
|
||||
container:SetModel(ix.config.Get("dropContainerModel", "models/props_c17/BriefCase001a.mdl"))
|
||||
container:Spawn()
|
||||
|
||||
hook.Run("PreDropCharacterInventory", client, items, container)
|
||||
|
||||
ix.inventory.New(0, "droppedItems", function(inventory)
|
||||
inventory.vars.isBag = false
|
||||
inventory.vars.isDrop = true
|
||||
|
||||
function inventory.OnAuthorizeTransfer(_, _, _, itemTable)
|
||||
if (itemTable.authorized) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if (IsValid(container)) then
|
||||
container:SetInventory(inventory)
|
||||
inventory.vars.entity = container
|
||||
|
||||
for _, v in pairs(items) do
|
||||
self:TransferItem(client, v, clientInv, inventory)
|
||||
end
|
||||
|
||||
ix.saveEnts:SaveEntity(container)
|
||||
hook.Run("PostDropCharacterInventory", client, items, container, inventory)
|
||||
else
|
||||
ix.item.inventories[inventory.id] = nil
|
||||
|
||||
local query = mysql:Delete("ix_items")
|
||||
query:Where("inventory_id", inventory.id)
|
||||
query:Execute()
|
||||
|
||||
query = mysql:Delete("ix_inventories")
|
||||
query:Where("inventory_id", inventory.id)
|
||||
query:Execute()
|
||||
end
|
||||
end)
|
||||
|
||||
client:GetCharacter():SetRefundItems(droppedItems)
|
||||
|
||||
local logString = client:Name().." has dropped:"
|
||||
for _, v in ipairs(items) do
|
||||
if (!v.maxStackSize or v.maxStackSize == 1) then
|
||||
logString = logString.." "..v:GetName().." (#"..v:GetID()..");"
|
||||
else
|
||||
logString = logString.." "..v:GetStackSize().."x "..v:GetName().." (#"..v:GetID()..");"
|
||||
end
|
||||
end
|
||||
ix.log.Add(client, "droppedItems", logString)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PostDropCharacterInventory(client, items, container, inventory)
|
||||
for k, v in pairs(inventory:GetItems()) do
|
||||
if (v.replaceOnDeath) then
|
||||
local newItem = v.replaceOnDeath
|
||||
v:Remove()
|
||||
|
||||
if (ix.item.list[v.replaceOnDeath]) then
|
||||
inventory:Add(newItem)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:RegisterSaveEnts()
|
||||
ix.saveEnts:RegisterEntity("ix_drop", true, true, true, {
|
||||
OnSave = function(entity, data) --OnSave
|
||||
local inventory = entity:GetInventory()
|
||||
data.invID = inventory:GetID()
|
||||
data.model = entity:GetModel()
|
||||
data.name = entity.name
|
||||
data.password = entity.password
|
||||
data.money = entity:GetMoney()
|
||||
end,
|
||||
OnRestore = function(entity, data) --OnRestore
|
||||
entity:SetModel(data.model)
|
||||
entity:SetSolid(SOLID_VPHYSICS)
|
||||
entity:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
if (data.password) then
|
||||
entity.password = data.password
|
||||
entity:SetLocked(true)
|
||||
entity.Sessions = {}
|
||||
end
|
||||
|
||||
if (data.name) then
|
||||
entity.name = data.name
|
||||
entity:SetDisplayName(data.name)
|
||||
end
|
||||
|
||||
if (data.money) then
|
||||
entity:SetMoney(data.money)
|
||||
end
|
||||
|
||||
ix.inventory.Restore(data.invID, ix.config.Get("inventoryWidth"), ix.config.Get("inventoryHeight"), function(inventory)
|
||||
inventory.vars.isBag = false
|
||||
inventory.vars.isDrop = true
|
||||
|
||||
if (IsValid(entity)) then
|
||||
entity:SetInventory(inventory)
|
||||
inventory.vars.entity = entity
|
||||
end
|
||||
end)
|
||||
end,
|
||||
ShouldSave = function(entity) --ShouldSave
|
||||
return entity:GetInventory() and entity:GetInventory() != 0
|
||||
end,
|
||||
ShouldRestore = function(data) --ShouldRestore
|
||||
return data.invID >= 1
|
||||
end
|
||||
})
|
||||
end
|
||||
213
gamemodes/helix/plugins/doors/cl_plugin.lua
Normal file
213
gamemodes/helix/plugins/doors/cl_plugin.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
-- luacheck: globals ACCESS_LABELS
|
||||
ACCESS_LABELS = {}
|
||||
ACCESS_LABELS[DOOR_OWNER] = "owner"
|
||||
ACCESS_LABELS[DOOR_TENANT] = "tenant"
|
||||
ACCESS_LABELS[DOOR_GUEST] = "guest"
|
||||
ACCESS_LABELS[DOOR_NONE] = "none"
|
||||
|
||||
function PLUGIN:GetDefaultDoorInfo(door)
|
||||
local owner = IsValid(door:GetDTEntity(0)) and door:GetDTEntity(0) or nil
|
||||
local name = door:GetNetVar("title", door:GetNetVar("name", IsValid(owner) and L"dTitleOwned" or L"dTitle"))
|
||||
local description = door:GetNetVar("ownable") and L("dIsOwnable") or L("dIsNotOwnable")
|
||||
local color = ix.config.Get("color")
|
||||
local faction = door:GetNetVar("faction")
|
||||
local class = door:GetNetVar("class")
|
||||
|
||||
if (class) then
|
||||
local classData = ix.class.list[class]
|
||||
|
||||
if (classData) then
|
||||
if (classData.color) then
|
||||
color = classData.color
|
||||
end
|
||||
|
||||
if (!owner) then
|
||||
description = L("dOwnedBy", L2(classData.name) or classData.name)
|
||||
end
|
||||
end
|
||||
elseif (faction) then
|
||||
local info = ix.faction.indices[faction]
|
||||
color = team.GetColor(faction)
|
||||
|
||||
if (info and !owner) then
|
||||
description = L("dOwnedBy", L2(info.name) or info.name)
|
||||
end
|
||||
end
|
||||
|
||||
if (owner) then
|
||||
description = L("dOwnedBy", owner:GetName())
|
||||
end
|
||||
|
||||
return {
|
||||
name = name,
|
||||
description = description,
|
||||
color = color
|
||||
}
|
||||
end
|
||||
|
||||
function PLUGIN:DrawDoorInfo(door, width, position, angles, scale, clientPosition)
|
||||
local alpha = math.max((1 - clientPosition:DistToSqr(door:GetPos()) / 65536) * 255, 0)
|
||||
|
||||
if (alpha < 1) then
|
||||
return
|
||||
end
|
||||
|
||||
local info = hook.Run("GetDoorInfo", door) or self:GetDefaultDoorInfo(door)
|
||||
|
||||
if (!istable(info) or table.IsEmpty(info)) then
|
||||
return
|
||||
end
|
||||
|
||||
-- title + background
|
||||
surface.SetFont("ix3D2DMediumFont")
|
||||
local nameWidth, nameHeight = surface.GetTextSize(info.name)
|
||||
|
||||
derma.SkinFunc("DrawImportantBackground", -width * 0.5, -nameHeight * 0.5,
|
||||
width, nameHeight, ColorAlpha(info.color, alpha * 0.5))
|
||||
|
||||
surface.SetTextColor(ColorAlpha(color_white, alpha))
|
||||
surface.SetTextPos(-nameWidth * 0.5, -nameHeight * 0.5)
|
||||
surface.DrawText(info.name)
|
||||
|
||||
-- description
|
||||
local lines = ix.util.WrapText(info.description, width, "ix3D2DSmallFont")
|
||||
local y = nameHeight * 0.5 + 4
|
||||
|
||||
for i = 1, #lines do
|
||||
local line = lines[i]
|
||||
local textWidth, textHeight = surface.GetTextSize(line)
|
||||
|
||||
surface.SetTextPos(-textWidth * 0.5, y)
|
||||
surface.DrawText(line)
|
||||
|
||||
y = y + textHeight
|
||||
end
|
||||
|
||||
-- background blur
|
||||
ix.util.PushBlur(function()
|
||||
cam.Start3D2D(position, angles, scale)
|
||||
surface.SetDrawColor(11, 11, 11, math.max(alpha - 100, 0))
|
||||
surface.DrawRect(-width * 0.5, -nameHeight * 0.5, width, y + nameHeight * 0.5 + 4)
|
||||
cam.End3D2D()
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDepth, bSkybox)
|
||||
if (bDepth or bSkybox or !LocalPlayer():GetCharacter()) then
|
||||
return
|
||||
end
|
||||
|
||||
local entities = ents.FindInSphere(EyePos(), 256)
|
||||
local clientPosition = LocalPlayer():GetPos()
|
||||
|
||||
for _, v in ipairs(entities) do
|
||||
if (!IsValid(v) or !v:IsDoor() or !v:GetNetVar("visible")) then
|
||||
continue
|
||||
end
|
||||
|
||||
local color = v:GetColor()
|
||||
|
||||
if (v:IsEffectActive(EF_NODRAW) or color.a <= 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
local position = v:LocalToWorld(v:OBBCenter())
|
||||
local mins, maxs = v:GetCollisionBounds()
|
||||
local width = 0
|
||||
local size = maxs - mins
|
||||
local trace = {
|
||||
collisiongroup = COLLISION_GROUP_WORLD,
|
||||
ignoreworld = true,
|
||||
endpos = position
|
||||
}
|
||||
|
||||
-- trace from shortest side to center to get correct position for rendering
|
||||
if (size.z < size.x and size.z < size.y) then
|
||||
trace.start = position - v:GetUp() * size.z
|
||||
width = size.y
|
||||
elseif (size.x < size.y) then
|
||||
trace.start = position - v:GetForward() * size.x
|
||||
width = size.y
|
||||
elseif (size.y < size.x) then
|
||||
trace.start = position - v:GetRight() * size.y
|
||||
width = size.x
|
||||
end
|
||||
|
||||
width = math.max(width, 12)
|
||||
trace = util.TraceLine(trace)
|
||||
|
||||
local angles = trace.HitNormal:Angle()
|
||||
local anglesOpposite = trace.HitNormal:Angle()
|
||||
|
||||
angles:RotateAroundAxis(angles:Forward(), 90)
|
||||
angles:RotateAroundAxis(angles:Right(), 90)
|
||||
anglesOpposite:RotateAroundAxis(anglesOpposite:Forward(), 90)
|
||||
anglesOpposite:RotateAroundAxis(anglesOpposite:Right(), -90)
|
||||
|
||||
local positionFront = trace.HitPos - (((position - trace.HitPos):Length() * 2) + 1) * trace.HitNormal
|
||||
local positionOpposite = trace.HitPos + (trace.HitNormal * 2)
|
||||
|
||||
if (trace.HitNormal:Dot((clientPosition - position):GetNormalized()) < 0) then
|
||||
-- draw front
|
||||
cam.Start3D2D(positionFront, angles, 0.1)
|
||||
self:DrawDoorInfo(v, width * 8, positionFront, angles, 0.1, clientPosition)
|
||||
cam.End3D2D()
|
||||
else
|
||||
-- draw back
|
||||
cam.Start3D2D(positionOpposite, anglesOpposite, 0.1)
|
||||
self:DrawDoorInfo(v, width * 8, positionOpposite, anglesOpposite, 0.1, clientPosition)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixDoorMenu", function()
|
||||
if (IsValid(ix.gui.door)) then
|
||||
return ix.gui.door:Remove()
|
||||
end
|
||||
|
||||
local door = net.ReadEntity()
|
||||
local access = net.ReadTable()
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (IsValid(door)) then
|
||||
ix.gui.door = vgui.Create("ixDoorMenu")
|
||||
ix.gui.door:SetDoor(door, access, entity)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixDoorPermission", function()
|
||||
local door = net.ReadEntity()
|
||||
|
||||
if (!IsValid(door)) then
|
||||
return
|
||||
end
|
||||
|
||||
local target = net.ReadEntity()
|
||||
local access = net.ReadUInt(4)
|
||||
|
||||
local panel = door.ixPanel
|
||||
|
||||
if (IsValid(panel) and IsValid(target)) then
|
||||
panel.access[target] = access
|
||||
|
||||
for _, v in ipairs(panel.access:GetLines()) do
|
||||
if (v.player == target) then
|
||||
v:SetColumnText(2, L(ACCESS_LABELS[access or 0]))
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
116
gamemodes/helix/plugins/doors/derma/cl_door.lua
Normal file
116
gamemodes/helix/plugins/doors/derma/cl_door.lua
Normal file
@@ -0,0 +1,116 @@
|
||||
--[[
|
||||
| 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 PANEL = {}
|
||||
|
||||
local function DoorSetPermission(door, target, permission)
|
||||
net.Start("ixDoorPermission")
|
||||
net.WriteEntity(door)
|
||||
net.WriteEntity(target)
|
||||
net.WriteUInt(permission, 4)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(280, 240)
|
||||
self:SetTitle(L"doorSettings")
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
|
||||
self.access = self:Add("DListView")
|
||||
self.access:Dock(FILL)
|
||||
self.access:AddColumn(L"name").Header:SetTextColor(Color(25, 25, 25))
|
||||
self.access:AddColumn(L"access").Header:SetTextColor(Color(25, 25, 25))
|
||||
self.access.OnClickLine = function(this, line, selected)
|
||||
if (IsValid(line.player)) then
|
||||
local menu = DermaMenu()
|
||||
menu:AddOption(L"tenant", function()
|
||||
if (self.accessData and self.accessData[line.player] != DOOR_TENANT) then
|
||||
DoorSetPermission(self.door, line.player, DOOR_TENANT)
|
||||
end
|
||||
end):SetImage("icon16/user_add.png")
|
||||
menu:AddOption(L"guest", function()
|
||||
if (self.accessData and self.accessData[line.player] != DOOR_GUEST) then
|
||||
DoorSetPermission(self.door, line.player, DOOR_GUEST)
|
||||
end
|
||||
end):SetImage("icon16/user_green.png")
|
||||
menu:AddOption(L"none", function()
|
||||
if (self.accessData and self.accessData[line.player] != DOOR_NONE) then
|
||||
DoorSetPermission(self.door, line.player, DOOR_NONE)
|
||||
end
|
||||
end):SetImage("icon16/user_red.png")
|
||||
menu:Open()
|
||||
|
||||
for _, v in pairs(menu:GetChildren()[1]:GetChildren()) do
|
||||
v:SetFont("MenuFontNoClamp")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetDoor(door, access, door2)
|
||||
door.ixPanel = self
|
||||
|
||||
self.accessData = access
|
||||
self.door = door
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if (v != LocalPlayer() and v:GetCharacter()) then
|
||||
self.access:AddLine(v:Name(), L(ACCESS_LABELS[access[v] or 0])).player = v
|
||||
end
|
||||
end
|
||||
|
||||
if (self:CheckAccess(DOOR_OWNER)) then
|
||||
self.sell = self:Add("DButton")
|
||||
self.sell:Dock(BOTTOM)
|
||||
self.sell:SetText(L"sell")
|
||||
self.sell:SetTextColor(color_white)
|
||||
self.sell:DockMargin(0, 5, 0, 0)
|
||||
self.sell.DoClick = function(this)
|
||||
self:Remove()
|
||||
ix.command.Send("doorsell")
|
||||
end
|
||||
end
|
||||
|
||||
if (self:CheckAccess(DOOR_TENANT)) then
|
||||
self.name = self:Add("DTextEntry")
|
||||
self.name:Dock(TOP)
|
||||
self.name:DockMargin(0, 0, 0, 5)
|
||||
self.name.Think = function(this)
|
||||
if (!this:IsEditing()) then
|
||||
local entity = IsValid(door2) and door2 or door
|
||||
|
||||
self.name:SetText(entity:GetNetVar("title", L"dTitleOwned"))
|
||||
end
|
||||
end
|
||||
self.name.OnEnter = function(this)
|
||||
ix.command.Send("doorsettitle", this:GetText())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CheckAccess(access)
|
||||
access = access or DOOR_GUEST
|
||||
|
||||
if ((self.accessData[LocalPlayer()] or 0) >= access) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
if (self.accessData and !IsValid(self.door) and self:CheckAccess()) then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("ixDoorMenu", PANEL, "DFrame")
|
||||
211
gamemodes/helix/plugins/doors/entities/weapons/ix_keys.lua
Normal file
211
gamemodes/helix/plugins/doors/entities/weapons/ix_keys.lua
Normal file
@@ -0,0 +1,211 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
AddCSLuaFile()
|
||||
|
||||
if (CLIENT) then
|
||||
SWEP.PrintName = "Keys"
|
||||
SWEP.Slot = 0
|
||||
SWEP.SlotPos = 2
|
||||
SWEP.DrawAmmo = false
|
||||
SWEP.DrawCrosshair = false
|
||||
end
|
||||
|
||||
SWEP.Author = "Chessnut"
|
||||
SWEP.Instructions = "Primary Fire: Lock\nSecondary Fire: Unlock"
|
||||
SWEP.Purpose = "Hitting things and knocking on doors."
|
||||
SWEP.Drop = false
|
||||
|
||||
SWEP.ViewModelFOV = 45
|
||||
SWEP.ViewModelFlip = false
|
||||
SWEP.AnimPrefix = "rpg"
|
||||
|
||||
SWEP.ViewTranslation = 4
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = ""
|
||||
SWEP.Primary.Damage = 5
|
||||
SWEP.Primary.Delay = 0.75
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = 0
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = ""
|
||||
|
||||
SWEP.ViewModel = Model("models/weapons/c_arms_animations.mdl")
|
||||
SWEP.WorldModel = ""
|
||||
|
||||
SWEP.UseHands = false
|
||||
SWEP.LowerAngles = Angle(0, 5, -14)
|
||||
SWEP.LowerAngles2 = Angle(0, 5, -22)
|
||||
|
||||
SWEP.IsAlwaysLowered = true
|
||||
SWEP.FireWhenLowered = true
|
||||
SWEP.HoldType = "passive"
|
||||
|
||||
-- luacheck: globals ACT_VM_FISTS_DRAW ACT_VM_FISTS_HOLSTER
|
||||
ACT_VM_FISTS_DRAW = 2
|
||||
ACT_VM_FISTS_HOLSTER = 1
|
||||
|
||||
function SWEP:Holster()
|
||||
if (!IsValid(self.Owner)) then
|
||||
return
|
||||
end
|
||||
|
||||
local viewModel = self.Owner:GetViewModel()
|
||||
|
||||
if (IsValid(viewModel)) then
|
||||
viewModel:SetPlaybackRate(1)
|
||||
viewModel:ResetSequence(ACT_VM_FISTS_HOLSTER)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Precache()
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
self:SetHoldType(self.HoldType)
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
local time = ix.config.Get("doorLockTime", 1)
|
||||
local time2 = math.max(time, 1)
|
||||
|
||||
self:SetNextPrimaryFire(CurTime() + time2)
|
||||
self:SetNextSecondaryFire(CurTime() + time2)
|
||||
|
||||
if (!IsFirstTimePredicted()) then
|
||||
return
|
||||
end
|
||||
|
||||
if (CLIENT) then
|
||||
return
|
||||
end
|
||||
|
||||
local data = {}
|
||||
data.start = self.Owner:GetShootPos()
|
||||
data.endpos = data.start + self.Owner:GetAimVector()*96
|
||||
data.filter = self.Owner
|
||||
local entity = util.TraceLine(data).Entity
|
||||
|
||||
--[[
|
||||
Locks the entity if the contiditon fits:
|
||||
1. The entity is door and client has access to the door.
|
||||
2. The entity is vehicle and the "owner" variable is same as client's character ID.
|
||||
--]]
|
||||
if (IsValid(entity) and
|
||||
(
|
||||
(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or
|
||||
(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)
|
||||
)
|
||||
) then
|
||||
self.Owner:SetAction("@locking", time, function()
|
||||
self:ToggleLock(entity, true)
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:ToggleLock(door, state)
|
||||
if (IsValid(self.Owner) and self.Owner:GetPos():Distance(door:GetPos()) > 96) then
|
||||
return
|
||||
end
|
||||
|
||||
if (door:IsDoor()) then
|
||||
local partner = door:GetDoorPartner()
|
||||
|
||||
if (state) then
|
||||
if (IsValid(partner)) then
|
||||
partner:Fire("lock")
|
||||
end
|
||||
|
||||
door:Fire("lock")
|
||||
self.Owner:EmitSound("doors/door_latch3.wav")
|
||||
|
||||
hook.Run("PlayerLockedDoor", self.Owner, door, partner)
|
||||
else
|
||||
if (IsValid(partner)) then
|
||||
partner:Fire("unlock")
|
||||
end
|
||||
|
||||
door:Fire("unlock")
|
||||
self.Owner:EmitSound("doors/door_latch1.wav")
|
||||
|
||||
hook.Run("PlayerUnlockedDoor", self.Owner, door, partner)
|
||||
end
|
||||
elseif (door:IsVehicle()) then
|
||||
if (state) then
|
||||
door:Fire("lock")
|
||||
|
||||
if (door.IsSimfphyscar) then
|
||||
door.IsLocked = true
|
||||
end
|
||||
|
||||
self.Owner:EmitSound("doors/door_latch3.wav")
|
||||
hook.Run("PlayerLockedVehicle", self.Owner, door)
|
||||
else
|
||||
door:Fire("unlock")
|
||||
|
||||
if (door.IsSimfphyscar) then
|
||||
door.IsLocked = nil
|
||||
end
|
||||
|
||||
self.Owner:EmitSound("doors/door_latch1.wav")
|
||||
hook.Run("PlayerUnlockedVehicle", self.Owner, door)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
local time = ix.config.Get("doorLockTime", 1)
|
||||
local time2 = math.max(time, 1)
|
||||
|
||||
self:SetNextPrimaryFire(CurTime() + time2)
|
||||
self:SetNextSecondaryFire(CurTime() + time2)
|
||||
|
||||
if (!IsFirstTimePredicted()) then
|
||||
return
|
||||
end
|
||||
|
||||
if (CLIENT) then
|
||||
return
|
||||
end
|
||||
|
||||
local data = {}
|
||||
data.start = self.Owner:GetShootPos()
|
||||
data.endpos = data.start + self.Owner:GetAimVector()*96
|
||||
data.filter = self.Owner
|
||||
local entity = util.TraceLine(data).Entity
|
||||
|
||||
|
||||
--[[
|
||||
Unlocks the entity if the contiditon fits:
|
||||
1. The entity is door and client has access to the door.
|
||||
2. The entity is vehicle and the "owner" variable is same as client's character ID.
|
||||
]]--
|
||||
if (IsValid(entity) and
|
||||
(
|
||||
(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or
|
||||
(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)
|
||||
)
|
||||
) then
|
||||
self.Owner:SetAction("@unlocking", time, function()
|
||||
self:ToggleLock(entity, false)
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user