mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
Upload
This commit is contained in:
428
gamemodes/helix/plugins/3dpanel.lua
Normal file
428
gamemodes/helix/plugins/3dpanel.lua
Normal file
@@ -0,0 +1,428 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "3D Panels"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds web panels that can be placed on the map."
|
||||
|
||||
-- List of available panel dislays.
|
||||
PLUGIN.list = PLUGIN.list or {}
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixPanelList")
|
||||
util.AddNetworkString("ixPanelAdd")
|
||||
util.AddNetworkString("ixPanelRemove")
|
||||
|
||||
-- Called when the player is sending client info.
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
-- Send the list of panel displays.
|
||||
timer.Simple(1, function()
|
||||
if (IsValid(client)) then
|
||||
local json = util.TableToJSON(self.list)
|
||||
local compressed = util.Compress(json)
|
||||
local length = compressed:len()
|
||||
|
||||
net.Start("ixPanelList")
|
||||
net.WriteUInt(length, 32)
|
||||
net.WriteData(compressed, length)
|
||||
net.Send(client)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Adds a panel to the list, sends it to the players, and saves data.
|
||||
function PLUGIN:AddPanel(position, angles, url, scale, brightness)
|
||||
scale = math.Clamp((scale or 1) * 0.1, 0.001, 5)
|
||||
brightness = math.Clamp(math.Round((brightness or 100) * 2.55), 1, 255)
|
||||
|
||||
-- Find an ID for this panel within the list.
|
||||
local index = #self.list + 1
|
||||
|
||||
-- Add the panel to the list so it can be sent and saved.
|
||||
self.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}
|
||||
|
||||
-- Send the panel information to the players.
|
||||
net.Start("ixPanelAdd")
|
||||
net.WriteUInt(index, 32)
|
||||
net.WriteVector(position)
|
||||
net.WriteAngle(angles)
|
||||
net.WriteFloat(scale)
|
||||
net.WriteString(url)
|
||||
net.WriteUInt(brightness, 8)
|
||||
net.Broadcast()
|
||||
|
||||
-- Save the plugin data.
|
||||
self:SavePanels()
|
||||
end
|
||||
|
||||
-- Removes a panel that are within the radius of a position.
|
||||
function PLUGIN:RemovePanel(position, radius)
|
||||
-- Default the radius to 100.
|
||||
radius = radius or 100
|
||||
|
||||
local panelsDeleted = {}
|
||||
|
||||
-- Loop through all of the panels.
|
||||
for k, v in pairs(self.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Check if the distance from our specified position to the panel is less than the radius.
|
||||
if (v[1]:Distance(position) <= radius) then
|
||||
panelsDeleted[#panelsDeleted + 1] = k
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the plugin data if we actually changed anything.
|
||||
if (#panelsDeleted > 0) then
|
||||
-- Invert index table to delete from highest -> lowest
|
||||
panelsDeleted = table.Reverse(panelsDeleted)
|
||||
|
||||
for _, v in ipairs(panelsDeleted) do
|
||||
-- Remove the panel from the list of panels.
|
||||
table.remove(self.list, v)
|
||||
|
||||
-- Tell the players to stop showing the panel.
|
||||
net.Start("ixPanelRemove")
|
||||
net.WriteUInt(v, 32)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
self:SavePanels()
|
||||
end
|
||||
|
||||
-- Return the number of deleted panels.
|
||||
return #panelsDeleted
|
||||
end
|
||||
|
||||
-- Called after entities have been loaded on the map.
|
||||
function PLUGIN:LoadData()
|
||||
self.list = self:GetData() or {}
|
||||
|
||||
-- Formats table to sequential to support legacy panels.
|
||||
self.list = table.ClearKeys(self.list)
|
||||
end
|
||||
|
||||
-- Called when the plugin needs to save information.
|
||||
function PLUGIN:SavePanels()
|
||||
self:SetData(self.list)
|
||||
end
|
||||
else
|
||||
local function IsUsingPanelAddTool()
|
||||
if !IsValid(LocalPlayer()) then return false end
|
||||
if !IsValid(LocalPlayer():GetActiveWeapon()) then return false end
|
||||
if !LocalPlayer():GetActiveWeapon():GetClass() then return false end
|
||||
if !LocalPlayer():GetTool() then return false end
|
||||
if !LocalPlayer():GetTool().Mode then return false end
|
||||
|
||||
if (LocalPlayer():GetActiveWeapon():GetClass() == "gmod_tool" and LocalPlayer():GetTool().Mode == "sh_paneladd") then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Pre-define the zero index in client before the net receives
|
||||
PLUGIN.list[0] = PLUGIN.list[0] or 0
|
||||
|
||||
-- Holds the current cached material and filename.
|
||||
local cachedPreview = {}
|
||||
|
||||
local function CacheMaterial(index)
|
||||
if (index < 1) then
|
||||
return
|
||||
end
|
||||
|
||||
local info = PLUGIN.list[index]
|
||||
local url = !IsUsingPanelAddTool() and info[6] or GetConVar("sh_paneladd_url"):GetString() or ""
|
||||
if url == "" then return end
|
||||
|
||||
local exploded = string.Explode("/", url)
|
||||
local filename = exploded[#exploded]
|
||||
local path = "helix/"..Schema.folder.."/"..PLUGIN.uniqueID.."/"
|
||||
|
||||
if (file.Exists(path..filename, "DATA")) then
|
||||
local material = Material("../data/"..path..filename, "noclamp smooth")
|
||||
|
||||
if (!material:IsError()) then
|
||||
info[7] = material
|
||||
|
||||
-- Set width and height
|
||||
info[3] = material:GetInt("$realwidth")
|
||||
info[4] = material:GetInt("$realheight")
|
||||
end
|
||||
else
|
||||
file.CreateDir(path)
|
||||
|
||||
http.Fetch(url, function(body)
|
||||
file.Write(path..filename, body)
|
||||
|
||||
local material = Material("../data/"..path..filename, "noclamp smooth")
|
||||
|
||||
if (!material:IsError()) then
|
||||
info[7] = material
|
||||
|
||||
-- Set width and height
|
||||
info[3] = material:GetInt("$realwidth")
|
||||
info[4] = material:GetInt("$realheight")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateCachedPreview(url)
|
||||
local path = "helix/"..Schema.folder.."/"..PLUGIN.uniqueID.."/"
|
||||
|
||||
-- Gets the file name
|
||||
local exploded = string.Explode("/", url)
|
||||
local filename = exploded[#exploded]
|
||||
|
||||
if (file.Exists(path..filename, "DATA")) then
|
||||
local preview = Material("../data/"..path..filename, "noclamp smooth")
|
||||
|
||||
-- Update the cached preview if success
|
||||
if (!preview:IsError()) then
|
||||
cachedPreview = {url, preview}
|
||||
else
|
||||
cachedPreview = {}
|
||||
end
|
||||
else
|
||||
file.CreateDir(path)
|
||||
|
||||
http.Fetch(url, function(body)
|
||||
file.Write(path..filename, body)
|
||||
|
||||
local preview = Material("../data/"..path..filename, "noclamp smooth")
|
||||
|
||||
-- Update the cached preview if success
|
||||
if (!preview:IsError()) then
|
||||
cachedPreview = {url, preview}
|
||||
else
|
||||
cachedPreview = {}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Receives new panel objects that need to be drawn.
|
||||
net.Receive("ixPanelAdd", function()
|
||||
local index = net.ReadUInt(32)
|
||||
local position = net.ReadVector()
|
||||
local angles = net.ReadAngle()
|
||||
local scale = net.ReadFloat()
|
||||
local url = net.ReadString()
|
||||
local brightness = net.ReadUInt(8)
|
||||
|
||||
if (url != "") then
|
||||
PLUGIN.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}
|
||||
|
||||
CacheMaterial(index)
|
||||
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixPanelRemove", function()
|
||||
local index = net.ReadUInt(32)
|
||||
|
||||
table.remove(PLUGIN.list, index)
|
||||
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
end)
|
||||
|
||||
-- Receives a full update on ALL panels.
|
||||
net.Receive("ixPanelList", function()
|
||||
local length = net.ReadUInt(32)
|
||||
local data = net.ReadData(length)
|
||||
local uncompressed = util.Decompress(data)
|
||||
|
||||
if (!uncompressed) then
|
||||
ErrorNoHalt("[Helix] Unable to decompress panel data!\n")
|
||||
return
|
||||
end
|
||||
|
||||
-- Set the list of panels to the ones provided by the server.
|
||||
PLUGIN.list = util.JSONToTable(uncompressed)
|
||||
|
||||
-- Will be saved, but refresh just to make sure.
|
||||
PLUGIN.list[0] = #PLUGIN.list
|
||||
|
||||
local CacheQueue = {}
|
||||
|
||||
-- Loop through the list of panels.
|
||||
for k, _ in pairs(PLUGIN.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
CacheQueue[#CacheQueue + 1] = k
|
||||
end
|
||||
|
||||
if (#CacheQueue == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
timer.Create("ixCache3DPanels", 1, #CacheQueue, function()
|
||||
if (#CacheQueue > 0) then
|
||||
CacheMaterial(CacheQueue[1])
|
||||
|
||||
table.remove(CacheQueue, 1)
|
||||
else
|
||||
timer.Remove("ixCache3DPanels")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Called after all translucent objects are drawn.
|
||||
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
|
||||
if (bDrawingDepth or bDrawingSkybox) then
|
||||
return
|
||||
end
|
||||
|
||||
local toolUsage = IsUsingPanelAddTool()
|
||||
|
||||
if toolUsage then UpdateCachedPreview(GetConVar("sh_paneladd_url"):GetString() or "") end
|
||||
-- Panel preview
|
||||
if (ix.chat.currentCommand == "paneladd" or toolUsage) then
|
||||
self:PreviewPanel()
|
||||
end
|
||||
|
||||
-- Store the position of the player to be more optimized.
|
||||
local ourPosition = LocalPlayer():GetPos()
|
||||
|
||||
local panel = self.list
|
||||
|
||||
for i = 1, panel[0] do
|
||||
local position = panel[i][1]
|
||||
local image = panel[i][7]
|
||||
|
||||
-- Older panels do not have a brightness index
|
||||
local brightness = panel[i][8] or 255
|
||||
|
||||
if (panel[i][7] and ourPosition:DistToSqr(position) <= 2796203) then
|
||||
local width, height = panel[i][3] or image:Width(), panel[i][4] or image:Height()
|
||||
cam.Start3D2D(position, panel[i][2], panel[i][5] or 0.1)
|
||||
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
|
||||
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
|
||||
surface.SetDrawColor(brightness, brightness, brightness)
|
||||
surface.SetMaterial(image)
|
||||
surface.DrawTexturedRect(-width * 0.5, -height * 0.5, width, height)
|
||||
render.PopFilterMag()
|
||||
render.PopFilterMin()
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ChatTextChanged(text)
|
||||
if (ix.chat.currentCommand == "paneladd") then
|
||||
-- Allow time for ix.chat.currentArguments to update
|
||||
timer.Simple(0, function()
|
||||
local arguments = ix.chat.currentArguments
|
||||
|
||||
if (!arguments[1]) then
|
||||
return
|
||||
end
|
||||
|
||||
UpdateCachedPreview(arguments[1])
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PreviewPanel()
|
||||
local arguments = ix.chat.currentArguments
|
||||
local arguments2 = GetConVar("sh_paneladd_url"):GetString()
|
||||
-- if there's no URL, then no preview.
|
||||
if IsUsingPanelAddTool() then
|
||||
arguments = {}
|
||||
end
|
||||
|
||||
if (!arguments[1] and !arguments2 or IsUsingPanelAddTool() and arguments2 and arguments2 == "") then
|
||||
return
|
||||
end
|
||||
|
||||
-- If the material is valid, preview the panel
|
||||
if (cachedPreview[2] and !cachedPreview[2]:IsError()) then
|
||||
local trace = LocalPlayer():GetEyeTrace()
|
||||
local angles = trace.HitNormal:Angle()
|
||||
angles:RotateAroundAxis(angles:Up(), 90)
|
||||
angles:RotateAroundAxis(angles:Forward(), 90)
|
||||
|
||||
-- validate argument types
|
||||
local cBrightness = IsUsingPanelAddTool() and GetConVar("sh_paneladd_brightness"):GetInt() or 100
|
||||
local cScale = IsUsingPanelAddTool() and GetConVar("sh_paneladd_scale"):GetInt() or 1
|
||||
|
||||
local scale = math.Clamp((tonumber(arguments[2]) or cScale) * 0.1, 0.001, 5)
|
||||
local brightness = math.Clamp(math.Round((tonumber(arguments[3]) or cBrightness) * 2.55), 1, 255)
|
||||
|
||||
-- Attempt to collect the dimensions from the Material
|
||||
local width, height = cachedPreview[2]:GetInt("$realwidth"), cachedPreview[2]:GetInt("$realheight")
|
||||
|
||||
local position = (trace.HitPos + angles:Up() * 1)
|
||||
local ourPosition = LocalPlayer():GetPos()
|
||||
|
||||
if (ourPosition:DistToSqr(position) <= 4194304) then
|
||||
cam.Start3D2D(position, angles, scale or 0.1)
|
||||
render.PushFilterMin(TEXFILTER.ANISOTROPIC)
|
||||
render.PushFilterMag(TEXFILTER.ANISOTROPIC)
|
||||
surface.SetDrawColor(brightness, brightness, brightness)
|
||||
surface.SetMaterial(cachedPreview[2])
|
||||
surface.DrawTexturedRect(-width * 0.5, -height * 0.5, width or cachedPreview[2]:Width(),
|
||||
height or cachedPreview[2]:Height())
|
||||
render.PopFilterMag()
|
||||
render.PopFilterMin()
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("PanelAdd", {
|
||||
description = "@cmdPanelAdd",
|
||||
privilege = "Manage Panels",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
bit.bor(ix.type.number, ix.type.optional),
|
||||
bit.bor(ix.type.number, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, url, scale, brightness)
|
||||
-- Get the position and angles of the panel.
|
||||
local trace = client:GetEyeTrace()
|
||||
local position = trace.HitPos
|
||||
local angles = trace.HitNormal:Angle()
|
||||
angles:RotateAroundAxis(angles:Up(), 90)
|
||||
angles:RotateAroundAxis(angles:Forward(), 90)
|
||||
|
||||
-- Add the panel.
|
||||
PLUGIN:AddPanel(position + angles:Up() * 1, angles, url, scale, brightness)
|
||||
return "@panelAdded"
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("PanelRemove", {
|
||||
description = "@cmdPanelRemove",
|
||||
privilege = "Manage Panels",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.number, ix.type.optional),
|
||||
OnRun = function(self, client, radius)
|
||||
-- Get the origin to remove panel.
|
||||
local trace = client:GetEyeTrace()
|
||||
local position = trace.HitPos
|
||||
-- Remove the panel(s) and get the amount removed.
|
||||
local amount = PLUGIN:RemovePanel(position, radius)
|
||||
|
||||
return "@panelRemoved", amount
|
||||
end
|
||||
})
|
||||
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 a supprimé son dernier texte 3D.", 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] Impossible de décompresser les données textuelles\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)
|
||||
371
gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
371
gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
@@ -0,0 +1,371 @@
|
||||
--[[
|
||||
| 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},
|
||||
},
|
||||
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",
|
||||
})
|
||||
|
||||
-- deny
|
||||
ix.act.Register("Deny", "metrocop", {
|
||||
sequence = "harassfront2",
|
||||
})
|
||||
|
||||
-- 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 = "Économiseur de munitions"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "Économise les munitions d'un personnage."
|
||||
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("Zone")
|
||||
|
||||
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 = "Zone",
|
||||
areas = "Zones",
|
||||
areaEditMode = "Mode d'édition de zone",
|
||||
areaNew = "Nouvelle zone",
|
||||
areaAlreadyExists = "Une zone portant ce nom existe déjà !",
|
||||
areaDoesntExist = "Une zone portant ce nom n'existe pas !",
|
||||
areaInvalidType = "Vous avez spécifié un type de zone non valide !",
|
||||
areaEditTip = "Cliquez pour commencer à créer une zone. Cliquez avec le bouton droit de la souris pour quitter.",
|
||||
areaFinishTip = "Cliquez à nouveau pour terminer le dessin de la zone. Cliquez avec le bouton droit de la souris pour revenir en arrière.",
|
||||
areaRemoveTip = "Appuyez sur reload pour supprimer la zone dans laquelle vous vous trouvez actuellement.",
|
||||
areaDeleteConfirm = "Etes-vous sûr de vouloir supprimer la zone \"%s\" ?",
|
||||
areaDelete = "Supprimer la zone",
|
||||
|
||||
cmdAreaEdit = "Entre dans le mode d'édition de zone",
|
||||
|
||||
optShowAreaNotices = "Afficher les avis de zone",
|
||||
optdShowAreaNotices = "Indique si une notification doit apparaître à l'écran lorsque vous entrez dans une nouvelle zone"
|
||||
}
|
||||
29
gamemodes/helix/plugins/area/languages/sh_french.lua
Normal file
29
gamemodes/helix/plugins/area/languages/sh_french.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 = "Zone",
|
||||
areas = "Zones",
|
||||
areaEditMode = "Mode d'édition de zone",
|
||||
areaNew = "Nouvelle zone",
|
||||
areaAlreadyExists = "Une zone portant ce nom existe déjà !",
|
||||
areaDoesntExist = "Une zone portant ce nom n'existe pas !",
|
||||
areaInvalidType = "Vous avez spécifié un type de zone non valide !",
|
||||
areaEditTip = "Cliquez pour commencer à créer une zone. Cliquez avec le bouton droit de la souris pour quitter.",
|
||||
areaFinishTip = "Cliquez à nouveau pour terminer le dessin de la zone. Cliquez avec le bouton droit de la souris pour revenir en arrière.",
|
||||
areaRemoveTip = "Appuyez sur reload pour supprimer la zone dans laquelle vous vous trouvez actuellement.",
|
||||
areaDeleteConfirm = "Etes-vous sûr de vouloir supprimer la zone \"%s\" ?",
|
||||
areaDelete = "Supprimer la zone",
|
||||
|
||||
cmdAreaEdit = "Entre dans le mode d'édition de zone",
|
||||
|
||||
optShowAreaNotices = "Afficher les avis de zone",
|
||||
optdShowAreaNotices = "Indique si une notification doit apparaître à l'écran lorsque vous entrez dans une nouvelle zone"
|
||||
}
|
||||
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, "Combien de secondes entre chaque calcul de la zone actuelle d'un personnage.",
|
||||
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 = "Zones"
|
||||
}
|
||||
)
|
||||
|
||||
ix.option.Add("showAreaNotices", ix.type.bool, true, {
|
||||
category = "Apparence",
|
||||
})
|
||||
|
||||
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 a ajouté la zone \"%s\".", client:Name(), tostring(name))
|
||||
end)
|
||||
|
||||
ix.log.AddType("areaRemove", function(client, name)
|
||||
return string.format("%s a retiré la zone \"%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 = "Permet aux joueurs d'activer la marche automatique."
|
||||
|
||||
ix.option.Add("enableAutoWalkBind", ix.type.bool, true, {
|
||||
bNetworked = true,
|
||||
category = "Général",
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optEnableAutoWalkBind = "Activer de marche automatique",
|
||||
optdEnableAutoWalkBind = "Activez la marche avant automatique en appuyant sur la touche 'n'.",
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
optEnableAutoWalkBind = "Activer de marche automatique",
|
||||
optdEnableAutoWalkBind = "Activez la marche avant automatique en appuyant sur la touche '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 = "Ajoute l'intégration AWarn 3."
|
||||
|
||||
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 = "Avertissements : "..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] Tentative d'affichage d'un inventaire non initialisé '"..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 = "Voir",
|
||||
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 = "Déséquiper",
|
||||
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 = "Poche à Munitions"
|
||||
ITEM.description = "Un petit sac permettant de mettre des munitions."
|
||||
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,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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Sac"
|
||||
ITEM.description = "Un gros sac avec l'insigne de l'Union dessus."
|
||||
ITEM.category = "Stockage"
|
||||
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 = "Poche à Chargeurs"
|
||||
ITEM.description = "Un petit sac permettant de mettre des chargeurs."
|
||||
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,33 @@
|
||||
--[[
|
||||
| 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 = "Trousse de Secours"
|
||||
ITEM.description = "Un petit sac permettant de mettre des objets de soins."
|
||||
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
|
||||
ITEM.noOpen = false
|
||||
ITEM.noEquip = true
|
||||
ITEM.invWidth = 2
|
||||
ITEM.invHeight = 6
|
||||
ITEM.restriction = {
|
||||
"surgerykit",
|
||||
"morphine",
|
||||
"makeshift_bandage",
|
||||
"health_vial",
|
||||
"health_syringe",
|
||||
"health_kit",
|
||||
"firstaid",
|
||||
"disinfected_bandage",
|
||||
"bandage",
|
||||
"adrenaline",
|
||||
"disinfectant_bottle",
|
||||
"bloodstabilizer",
|
||||
"painkillers",
|
||||
"bloodbag",
|
||||
}
|
||||
@@ -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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Petite sacoche"
|
||||
ITEM.description = "Une petite sacoche qui repose sur votre hanche."
|
||||
ITEM.category = "Stockage"
|
||||
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
|
||||
26
gamemodes/helix/plugins/bagsystem/sh_plugin.lua
Normal file
26
gamemodes/helix/plugins/bagsystem/sh_plugin.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/
|
||||
--]]
|
||||
|
||||
|
||||
PLUGIN.name = "Bag System"
|
||||
PLUGIN.author = "Fruity & Aspect™"
|
||||
PLUGIN.description = "Un système de sac simple."
|
||||
|
||||
ix.util.Include("sh_hooks.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
restrictedBag = "Cet inventaire ne peut pas contenir ce type d'objet !"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
restrictedBag = "Cet inventaire ne peut pas contenir ce type d'objet !"
|
||||
})
|
||||
|
||||
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.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>
|
||||
152
gamemodes/helix/plugins/bastion/cl_plugin.lua
Normal file
152
gamemodes/helix/plugins/bastion/cl_plugin.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
| 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(),
|
||||
") | PV : ", target:Health(), " | Armure : ", 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,
|
||||
["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
|
||||
507
gamemodes/helix/plugins/bastion/modules/sv_antialt.lua
Normal file
507
gamemodes/helix/plugins/bastion/modules/sv_antialt.lua
Normal file
@@ -0,0 +1,507 @@
|
||||
--[[
|
||||
| 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 = "Vous essayez de rejoindre Willard Networks en tant que nouvel utilisateur tout en utilisant un proxy ou un VPN. Pour lutter contre les autres comptes, nous ne l'autorisons pas.\n\nVeuillez désactiver votre proxy/VPN et rejoindre le serveur. Après avoir rejoint le serveur avec succès une fois, vous pouvez réactiver votre proxy/VPN pour de futures visites.\n\nSi vous n'utilisez pas de proxy ou de VPN, veuillez contacter un membre de l'équipe du STAFF sur nos forums ou discord"
|
||||
})
|
||||
|
||||
ix.log.AddType("altNewClient", function(client)
|
||||
return string.format("[AltChecker] %s rejoint pour la première fois.", client:SteamName())
|
||||
end)
|
||||
ix.log.AddType("altKnownNewCookie", function(client)
|
||||
return string.format("[AltChecker] %s rejoint avec une installation inconnue.", client:SteamName())
|
||||
end)
|
||||
ix.log.AddType("altKnownCookieMatched", function(client, matched, total)
|
||||
return string.format("[AltChecker] %s rejoint sans cookie, mais l'installation correspond au cookie existant. %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..") a essayé de se connecter mais est strictement interdit.")
|
||||
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
|
||||
190
gamemodes/helix/plugins/bastion/sh_classes.lua
Normal file
190
gamemodes/helix/plugins/bastion/sh_classes.lua
Normal file
@@ -0,0 +1,190 @@
|
||||
--[[
|
||||
| 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("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), " a gagné le succès : ",
|
||||
Color( 255, 201, 0, 255 ), text)
|
||||
end
|
||||
end
|
||||
|
||||
ix.chat.Register("achievement_get", CLASS)
|
||||
end
|
||||
643
gamemodes/helix/plugins/bastion/sh_commands.lua
Normal file
643
gamemodes/helix/plugins/bastion/sh_commands.lua
Normal file
@@ -0,0 +1,643 @@
|
||||
--[[
|
||||
| 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 = "Obtenir un lien vers le serveur Discord",
|
||||
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 = "Obtenir un lien vers le Workshop",
|
||||
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 = "Obtenir un lien vers les 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 = "Obtenez les informations de base de quelqu'un et copiez son SteamID.",
|
||||
arguments = {
|
||||
bit.bor(ix.type.player, ix.type.optional)
|
||||
},
|
||||
--alias = "PlyGetInfo",
|
||||
privilege = "Commandes d'administration de base",
|
||||
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 = "Imprimez une liste des membres du personnel en ligne.",
|
||||
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("Il n'y a pas de STAFF en ligne actuellement.")
|
||||
end
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("PrintFactionList", {
|
||||
alias = "PFL",
|
||||
description = "Imprimer une liste des membres d'une faction actuellement en ligne (y compris sur un autre personnage).",
|
||||
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 = "Jouez un son pour tous les joueurs (lorsqu'aucune portée n'est donnée) ou ceux qui sont proches de vous.",
|
||||
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)
|
||||
end,
|
||||
indicator = "chatPerforming"
|
||||
})
|
||||
|
||||
ix.command.Add("PlaySoundGlobal", {
|
||||
description = "Jouez un son pour tous les joueurs.",
|
||||
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 = "Renvoie le nombre d'entités en réseau actuellement sur le serveur.",
|
||||
privilege = "Basic Admin Commands",
|
||||
OnRun = function(self, client)
|
||||
local edictsCount = ents.GetEdictCount()
|
||||
local edictsLeft = 8192 - edictsCount
|
||||
|
||||
return string.format("Il y a actuellement %s édits sur le serveur. Vous pouvez avoir jusqu'à %s de plus.", edictsCount, edictsLeft)
|
||||
end,
|
||||
bNoIndicator = true
|
||||
})
|
||||
|
||||
ix.command.Add("ShowEntsInRadius", {
|
||||
description = "Affiche une liste d'entités dans un rayon donné.",
|
||||
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 = "Affiche une liste d'entités dans un rayon donné.",
|
||||
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"
|
||||
})
|
||||
|
||||
|
||||
-- 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 = "Discutez avec d'autres membres du STAFF.",
|
||||
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 = "Discutez avec d'autres 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 = "Discutez avec d'autres 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 = "Quelqu'un a gagné une réalisation spéciale !",
|
||||
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 = "Quelqu'un a mérité un exploit : il a fait le sacrifice ultime pour augmenter le QI moyen de l'humanité.",
|
||||
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 = "Voir propriétaire",
|
||||
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
|
||||
289
gamemodes/helix/plugins/bastion/sh_plugin.lua
Normal file
289
gamemodes/helix/plugins/bastion/sh_plugin.lua
Normal file
@@ -0,0 +1,289 @@
|
||||
--[[
|
||||
| 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 = "Personnages"
|
||||
})
|
||||
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 = "Personnages"
|
||||
})
|
||||
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 = "Conteneurs"
|
||||
})
|
||||
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 = "Serveur"
|
||||
})
|
||||
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 = "Serveur"
|
||||
})
|
||||
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=2934933494", "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", "Accès au duplicateur.")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
getPlayerInfo = "Get Player Info",
|
||||
optStaffChat = "Show Staff Chat on all characters",
|
||||
optdStaffChat = "Turns on/off staff chat on all characters. When off, will only show staff chat while in observer or on an admin character.",
|
||||
optPgi = "Copy Steam ID to clipboard on 'PGI'/'View Player'",
|
||||
optdPgi = "Allows you to turn on/off the automatic copy-to-clipboard of a player's SteamID when using the PGI command or 'View Player' context menu option.",
|
||||
cmdStaffHelp = "Call for help from all the staff.\n",
|
||||
cmdAnnounce = "Make an OOC admin announcement to the entire server.",
|
||||
cmdLocalEvent = "Make an IC admin event that can only be heard within a given radius.",
|
||||
bastionPGIInvalidTarget = "You must enter or be looking at a valid target.",
|
||||
bastionTakingItemsTooQuickly = "You are taking items too quickly! Please slow down.",
|
||||
bastionItemDropSpamKick = "%s was kicked for item drop exploiting.",
|
||||
bastionItemDropSpamWarn = "%s was warned for item drop exploiting.",
|
||||
bastionItemDropTooQuick = "You are dropping items too quickly! Please slow down.",
|
||||
bastionItemTakeWarn = "%s was warned for taking items too quickly.",
|
||||
bastionItemTakeKick = "%s was kicked for dropping items too quickly.",
|
||||
charCreateTooFast = "You are creating characters too fast. Please wait at least %d minutes between attempts.",
|
||||
bastionCopiedCharName = "Character name copied",
|
||||
bastionCopiedSteamName = "Steam name copied",
|
||||
bastionCopiedSteamID = "Steam ID copied",
|
||||
bastionGoto = "Go to player",
|
||||
bastionCopyCharName = "Copy character name",
|
||||
bastionCopySteamName = "Copy steam name",
|
||||
bastionNoRecordFound = "Could not find any records for %s.",
|
||||
bastionResultsPrinted = "Results were printed in console.",
|
||||
bastionProxyNotify = "%s connected as new player while using a VPN/Proxy.",
|
||||
bastionPropOwnerInformation = "The owner of this prop is %s (%s - %s).",
|
||||
bastionPropOwnerUnknown = "The owner of this prop has not been registered!",
|
||||
whitelistDone = "Player was whitelisted.",
|
||||
whitelistError = "Something went wrong whitelisting the player. Ask a dev.",
|
||||
cmdTimeScale = "Change the timescale (min 0.001, max 5).",
|
||||
bastionTimeScale = "%s has set the timescale to %d.",
|
||||
cmdGravity = "Change the gravity.",
|
||||
bastionGravity = "%s has set the gravity to %d.",
|
||||
edictWarning = "Only %d edicts are left! Total edict count is currently: %d/8192!",
|
||||
edictCritical = "Only %d edicts are left! Total edict count is currently: %d/8192! Emergency Cleanup Required!",
|
||||
entsPrintedInConsole = "Entity list has been printed in console.",
|
||||
entityRemoved = "Entity %d (%s) was removed!",
|
||||
entityNotFound = "Entity %d was not found/is not valid.",
|
||||
optPlayerDeathNotification = "Player Death Notification",
|
||||
optdPlayerDeathNotification = "Whether to send a chat message when a player dies.",
|
||||
cmdRemovePersistedProps = "Remove all persisted props in a radius around you."
|
||||
})
|
||||
|
||||
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!"
|
||||
})
|
||||
|
||||
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("Heure")
|
||||
self.logs:AddColumn("Steam ID")
|
||||
self.logs:AddColumn("Texte")
|
||||
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 == "Heure" 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("Copier dans le presse-papiers", function()
|
||||
SetClipboardText("["..os.date("%Y-%m-%d %X", record.datetime).."] "..record.text)
|
||||
end)
|
||||
|
||||
if (record.steamid) then
|
||||
dmenu:AddOption("Copier SteamID dans le presse-papiers", function()
|
||||
SetClipboardText(util.SteamIDFrom64(record.steamid))
|
||||
end)
|
||||
|
||||
dmenu:AddOption("Copier SteamID64 dans le presse-papiers", 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("Téléporter", function()
|
||||
netstream.Start("ixLogTeleport", pos)
|
||||
end)
|
||||
end
|
||||
|
||||
dmenu:AddOption("Marquer", 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("Vous devez attendre "..logsData.." seconde(s) avant la prochaine recherche.")
|
||||
end
|
||||
else
|
||||
self.logs:AddLine("Aucun logs trouvés !")
|
||||
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("Veuillez patienter avant de lancer une nouvelle recherche !")
|
||||
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, "Temps après")
|
||||
self.afterTextEntry:SetTooltip("Les événements se sont déroulés après x temps. Exemple de temps : 5y2d7w = 5 ans, 2 jours et 7 semaines")
|
||||
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, "Temps avant")
|
||||
self.beforeTextEntry:SetTooltip("Les événements qui se sont produits avant x longtemps auparavant. Exemple de temps : 5y2d7w = 5 ans, 2 jours et 7 semaines")
|
||||
self.beforeTextEntry:SetEnterAllowed(true)
|
||||
self.beforeTextEntry.OnEnter = search
|
||||
|
||||
self.descCheckBox = self.leftSide:Add("DCheckBoxLabel")
|
||||
self.descCheckBox:SetText("Inversion de l'ordre chronologique")
|
||||
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("Mettre en évidence les morts / ko")
|
||||
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("Événements commis par le joueur avec ce 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("Événements sur la distance x de votre position actuelle. Laissez vide pour le global")
|
||||
self.distanceTextEntry:SetEnterAllowed(true)
|
||||
self.distanceTextEntry.OnEnter = search
|
||||
|
||||
self.textTextEntry = self.leftSide:Add("DTextEntry")
|
||||
self:CreateTextEntry(self.textTextEntry, true, 32, false, false, false, "Nom")
|
||||
self.textTextEntry:SetTooltip("Recherche avec le nom")
|
||||
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("Événement sur la carte spécifique. Laissez vide pour l'actuelle")
|
||||
self.mapTextEntry:SetEnterAllowed(true)
|
||||
self.mapTextEntry.OnEnter = search
|
||||
|
||||
if (!table.IsEmpty(self.requestedLogTypes)) then
|
||||
self.logTypeCombo = self.leftSide:Add("DComboBox")
|
||||
self.logTypeCombo:SetValue("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("Rechercher")
|
||||
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("RETOURNER A LA 1ERE 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("french", {
|
||||
optLogDefaultTime = "Durée de recherche des logs par défaut",
|
||||
optdLogDefaultTime = "La durée par défaut de la recherche dans l'outil de recherche des logs."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optLogDefaultTime = "Durée de recherche des logs par défaut",
|
||||
optdLogDefaultTime = "La durée par défaut de la recherche dans l'outil de recherche des logs."
|
||||
})
|
||||
|
||||
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.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 = "Arme : "..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)
|
||||
222
gamemodes/helix/plugins/betterobserver/cl_plugin.lua
Normal file
222
gamemodes/helix/plugins/betterobserver/cl_plugin.lua
Normal file
@@ -0,0 +1,222 @@
|
||||
--[[
|
||||
| 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", "Voir NPC", 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", "Voir props statiques")
|
||||
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)
|
||||
|
||||
self:RenderAdminESP(client, v, teamColor, pos, vEyePos, distance)
|
||||
|
||||
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
|
||||
43
gamemodes/helix/plugins/betterobserver/libs/sh_observer.lua
Normal file
43
gamemodes/helix/plugins/betterobserver/libs/sh_observer.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
--[[
|
||||
| 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 = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer Extra ESP")
|
||||
end
|
||||
})
|
||||
ix.lang.AddTable("english", {
|
||||
["opt"..capitalName.."ESP"] = optionNiceName or "Montrer "..capitalName.." ESP",
|
||||
["optd"..capitalName.."ESP"] = optionDesc or "Activé / désactivé "..optionName.." ESP."
|
||||
})
|
||||
|
||||
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
|
||||
106
gamemodes/helix/plugins/betterobserver/sh_plugin.lua
Normal file
106
gamemodes/helix/plugins/betterobserver/sh_plugin.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
--[[
|
||||
| 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 = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("observerESP", ix.type.bool, true, {
|
||||
category = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("steamESP", ix.type.bool, true, {
|
||||
category = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("mapscenesESP", ix.type.bool, false, {
|
||||
category = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
|
||||
end
|
||||
})
|
||||
ix.option.Add("alwaysObserverLight", ix.type.bool, true, {
|
||||
category = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
|
||||
end,
|
||||
bNetworked = true
|
||||
})
|
||||
ix.option.Add("observerFullBright", ix.type.bool, false, {
|
||||
category = "Observateur",
|
||||
hidden = function()
|
||||
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
|
||||
end,
|
||||
bNetworked = true
|
||||
})
|
||||
|
||||
ix.util.Include("cl_hooks.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optSteamESP = "Afficher les informations supplémentaires de l'ESP admin",
|
||||
optdSteamESP = "Voir le SteamID d'un joueur et sa santé/armure sur l'ESP admin",
|
||||
optMapscenesESP = "Afficher les scènes de la carte ESP",
|
||||
optdMapscenesESP = "Montre les emplacements des scènes de carte dans l'ESP d'administration",
|
||||
optAlwaysObserverLight = "Toujours allumer la lumière de l'observateur",
|
||||
optdAlwaysObserverLight = "Allumez automatiquement votre lampe d'observateur lorsque vous entrez dans l'observateur. Sinon, elle suivra votre lampe de poche. Peut toujours être éteint manuellement",
|
||||
optObserverFullBright = "Lumière de l'observateur complète",
|
||||
optdObserverFullBright = "Eclairez toute la carte lorsque vous activez la lumière de l'observateur"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
optSteamESP = "Afficher les informations supplémentaires de l'ESP admin",
|
||||
optdSteamESP = "Voir le SteamID d'un joueur et sa santé/armure sur l'ESP admin",
|
||||
optMapscenesESP = "Afficher les scènes de la carte ESP",
|
||||
optdMapscenesESP = "Montre les emplacements des scènes de carte dans l'ESP d'administration",
|
||||
optAlwaysObserverLight = "Toujours allumer la lumière de l'observateur",
|
||||
optdAlwaysObserverLight = "Allumez automatiquement votre lampe d'observateur lorsque vous entrez dans l'observateur. Sinon, elle suivra votre lampe de poche. Peut toujours être éteint manuellement",
|
||||
optObserverFullBright = "Lumière de l'observateur Full Bright",
|
||||
optdObserverFullBright = "Eclairez toute la carte lorsque vous activez la lumière de l'observateur"
|
||||
})
|
||||
|
||||
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"
|
||||
})
|
||||
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 a activé le mode observateur.", client:Name())
|
||||
end)
|
||||
|
||||
ix.log.AddType("observerExit", function(client, ...)
|
||||
if (ix.option.Get(client, "observerTeleportBack", true)) then
|
||||
return string.format("%s a désactivé le mode observateur.", client:Name())
|
||||
else
|
||||
return string.format("%s a désactivé le mode observateur à son point d'origine.", 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 = "Ajoute des commandes pour aveugler les joueurs en rendant leur écran complètement noir."
|
||||
|
||||
--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 = "Masque le joueur spécifié.",
|
||||
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 = "Démasque le joueur spécifié.",
|
||||
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 = "Masquer tous les joueurs du serveur.",
|
||||
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 = "Démasquer tous les joueurs du serveur.",
|
||||
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 = "Vous avez été aveuglé par l'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")
|
||||
DFrameFixer(self)
|
||||
|
||||
self.clipboard = self:Add("DButton")
|
||||
self.clipboard:Dock(BOTTOM)
|
||||
self.clipboard:DockMargin(0, SScaleMin(4 / 3), 0, 0)
|
||||
self.clipboard:SetText("Sauvegarder")
|
||||
self.clipboard:SetFont("MenuFontBoldNoClamp")
|
||||
self.clipboard:SetTall(SScaleMin(50 / 3))
|
||||
self.clipboard.DoClick = function()
|
||||
LocalPlayer():NotifyLocalized("Vous avez défini les bodygroups de ce personnage.")
|
||||
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("Suivant")
|
||||
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("Précédent")
|
||||
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("Couleur des cheveux")
|
||||
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 = "Customiser les bodygroups d'une personne."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
cmdEditBodygroup = "Customiser les bodygroups d'une personne."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
cmdEditBodygroup = "Personalizar los bodygroups de un objetivo."
|
||||
})
|
||||
|
||||
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 a changé les bodygroups de : %s.", 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 = "Apparence",
|
||||
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("Someone wants to style your hair/beard! Allow?", "Hair/Beard Styling", "Allow", 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("Hair/Beard Styling")
|
||||
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("Style")
|
||||
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("You've successfully styled your beard/hair!")
|
||||
self:Remove()
|
||||
else
|
||||
target:NotifyLocalized("You cannot select the same hair/beard you already have!")
|
||||
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("Hair")
|
||||
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("Hair Color")
|
||||
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("There is not enough beard to style...")
|
||||
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("Style "..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("Style "..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("Shave")
|
||||
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("This will remove your beard, are you sure?")
|
||||
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("YES")
|
||||
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("NO")
|
||||
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,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/
|
||||
--]]
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
ITEM.name = "[Lecteur audio] Apprendre à lire"
|
||||
ITEM.uniqueID = "audiobook_reading"
|
||||
ITEM.model = "models/props_lab/reciever01d.mdl"
|
||||
ITEM.width = 1
|
||||
ITEM.height = 1
|
||||
ITEM.description = "L'écoute de cet appareil rustique améliorera votre capacité de lecture."
|
||||
ITEM.category = "Livres Audio"
|
||||
|
||||
ITEM.functions.Listen = {
|
||||
name = "Écouter",
|
||||
OnRun = function(itemTable)
|
||||
local client = itemTable.player
|
||||
local character = client:GetCharacter()
|
||||
|
||||
character:SetCanread(true)
|
||||
|
||||
client:NotifyLocalized("Je me sens plus à l'aise avec la lecture.")
|
||||
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 = "Hairdresser tools"
|
||||
ITEM.uniqueID = "beard_razor"
|
||||
ITEM.model = "models/props_junk/cardboard_box004a.mdl"
|
||||
ITEM.width = 1
|
||||
ITEM.height = 1
|
||||
ITEM.description = "A tool for creative souls in an otherwise depressing landscape."
|
||||
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("Vous devez attendre avant de pouvoir faire ça!")
|
||||
return false
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Vous n'avez pas de barbe ou de cheveux!")
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.Style2 = {
|
||||
icon = "icon16/paintbrush.png",
|
||||
name = "Style Other",
|
||||
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("Invalid target!")
|
||||
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("Vous devez attendre avant de pouvoir faire ça!")
|
||||
return false
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("la cible n'avez pas de barbe ou de cheveux!")
|
||||
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.."' manque un nom. Vous devez ajouter un FACTION.name = \"Name\"\n")
|
||||
end
|
||||
|
||||
if (!FACTION.description) then
|
||||
FACTION.description = "noDesc"
|
||||
ErrorNoHalt("Faction '"..niceName.."' manque une description. Vous devez ajouter un FACTION.description = \"Description\"\n")
|
||||
end
|
||||
|
||||
if (!FACTION.color) then
|
||||
FACTION.color = Color(150, 150, 150)
|
||||
ErrorNoHalt("Faction '"..niceName.."' manque une couleur. Vous devez ajouter FACTION.color = Color(1, 2, 3)\n")
|
||||
end
|
||||
|
||||
team.SetUp(FACTION.index, FACTION.name or "Inconnue", 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 = "Utilisez des lunettes",
|
||||
optdUseImmersiveGlasses = "Utilisez des lunettes, brouillant la vue de votre personnage s'il a besoin de lunettes et n'en porte pas."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
optUseImmersiveGlasses = "Utilisez des lunettes",
|
||||
optdUseImmersiveGlasses = "Utilisez des lunettes, brouillant la vue de votre personnage s'il a besoin de lunettes et n'en porte pas."
|
||||
})
|
||||
|
||||
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.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 = {
|
||||
gris = {
|
||||
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)
|
||||
},
|
||||
marrons = {
|
||||
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)
|
||||
},
|
||||
blonds = {
|
||||
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)
|
||||
},
|
||||
["teinte de bleu"] = {
|
||||
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, "Vous n'avez pas sélectionné les cheveux/la couleur des cheveux !"
|
||||
end
|
||||
|
||||
if !istable(payload.hair) then
|
||||
return false, "Quelque chose s'est mal passé avec la sélection de cheveux!"
|
||||
end
|
||||
|
||||
if !payload.hair.hair then
|
||||
return false, "Vous n'avez pas sélectionné les cheveux!"
|
||||
end
|
||||
|
||||
if !payload.hair.color then
|
||||
return false, "Vous n'avez pas sélectionné la couleur des cheveux!"
|
||||
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, "Vous n'avez pas sélectionné de couleur de cheveux autorisée !"
|
||||
end
|
||||
|
||||
if !isnumber(payload.hair.hair) then
|
||||
return false, "Vous n'avez pas sélectionné de poil autorisé !"
|
||||
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, "Forcer l'effet des lunettes, même si le client désactive l'option pour eux.", nil, {
|
||||
category = "Personnages"
|
||||
})
|
||||
|
||||
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, "Vous n'avez pas sélectionné de modèle !"
|
||||
end
|
||||
else
|
||||
return false, "Vous n'avez pas sélectionné de modèle!"
|
||||
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, "Vous n'avez pas sélectionné de sexe !"
|
||||
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, "Vous n'avez pas sélectionné votre background !"
|
||||
end
|
||||
|
||||
if faction:GetNoGenetics(client) then
|
||||
return true
|
||||
end
|
||||
|
||||
if !payload.data.age or payload.data["age"] == "" then
|
||||
return false, "Vous n'avez pas sélectionné votre âge !"
|
||||
end
|
||||
|
||||
if !payload.data.height or payload.data["height"] == "" then
|
||||
return false, "Vous n'avez pas sélectionné votre taille !"
|
||||
end
|
||||
|
||||
if faction.name != "Vortigaunt" then
|
||||
if !payload.data["couleur des yeux"] or payload.data["couleur des yeux"] == "" then
|
||||
return false, "Vous n'avez pas sélectionné la couleur de vos yeux !"
|
||||
end
|
||||
end
|
||||
|
||||
if payload.data.skin < 0 then
|
||||
return false, "Vous n'avez pas sélectionné de skin valide !"
|
||||
end
|
||||
|
||||
if payload.data.groups then
|
||||
if payload.data.groups["2"]then
|
||||
if payload.data.groups["2"] < 0 then
|
||||
return false, "Vous n'avez pas sélectionné de torse valide !"
|
||||
end
|
||||
end
|
||||
|
||||
if payload.data.groups["3"] then
|
||||
if payload.data.groups["3"] < 0 then
|
||||
return false, "Vous n'avez pas sélectionné de pantalon valide !"
|
||||
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("J'ai besoin de mes lunettes pour voir clair...")
|
||||
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("Nous avons ajouté de nouveaux cheveux et couleurs de cheveux, veuillez sélectionner via le 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("Je devrais tailler ma barbe...")
|
||||
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("Ma barbe devient longue...")
|
||||
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("Vous n'avez pas de barbe !")
|
||||
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 = "Obtenez la liste des administrateurs avec leur dernier temps de jeu.",
|
||||
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("Aucun membre du STAFF trouvé!")
|
||||
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 .. " est actuellement en ligne. Temps de jeu total : " .. playTime .. " heures.")
|
||||
else
|
||||
client:ChatPrint(v.name .. " (" .. v.steamid .. "), " .. v.rank .. " est en ligne " .. os.date("%x %X", v.last_join) .. ". Temps de jeu total : " .. playTime .. " heures.")
|
||||
end
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
})
|
||||
176
gamemodes/helix/plugins/cinematictext/derma/cl_splashmenu.lua
Normal file
176
gamemodes/helix/plugins/cinematictext/derma/cl_splashmenu.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
--[[
|
||||
| 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 ScrW, ScrH = ScrW(), ScrH()
|
||||
|
||||
function PANEL:Init()
|
||||
if not LocalPlayer():IsAdmin() then return end
|
||||
|
||||
if IsValid(ix.gui.cinematicSplashTextMenu) then
|
||||
ix.gui.cinematicSplashTextMenu:Remove()
|
||||
end
|
||||
ix.gui.cinematicSplashTextMenu = self
|
||||
|
||||
self.contents = {
|
||||
text = "",
|
||||
bigText = "",
|
||||
duration = 3,
|
||||
blackBars = true,
|
||||
music = true,
|
||||
color = color_white
|
||||
}
|
||||
|
||||
local textEntryTall = ScrH*0.045
|
||||
|
||||
self:SetSize(ScrW*0.6, ScrH*0.6)
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
self:SetTitle("Cinematic Splash Text Menu")
|
||||
|
||||
local textLabel = self:Add("DLabel")
|
||||
textLabel:SetText("Splash Text")
|
||||
textLabel:SetFont("cinematicSplashFontSmall")
|
||||
textLabel:SetTextColor(ix.config.Get("color", Color(75, 119, 190)))
|
||||
textLabel:Dock(TOP)
|
||||
textLabel:DockMargin( 20, 5, 20, 0 )
|
||||
textLabel:SizeToContents()
|
||||
|
||||
local textEntry = self:Add("DTextEntry")
|
||||
textEntry:SetFont("cinematicSplashFontSmall")
|
||||
textEntry:Dock(TOP)
|
||||
textEntry:DockMargin( 20, 5, 20, 0 )
|
||||
textEntry:SetUpdateOnType(true)
|
||||
textEntry.OnValueChange = function(this, value)
|
||||
self.contents.text = value
|
||||
end
|
||||
textEntry:SetTall(textEntryTall)
|
||||
|
||||
local bigTextLabel = self:Add("DLabel")
|
||||
bigTextLabel:SetText("Big Splash Text (Appears under normal text)")
|
||||
bigTextLabel:SetFont("cinematicSplashFontSmall")
|
||||
bigTextLabel:SetTextColor(ix.config.Get("color", Color(75, 119, 190)))
|
||||
bigTextLabel:Dock(TOP)
|
||||
bigTextLabel:DockMargin( 20, 5, 20, 0 )
|
||||
bigTextLabel:SizeToContents()
|
||||
|
||||
local bigTextEntry = self:Add("DTextEntry")
|
||||
bigTextEntry:SetFont("cinematicSplashFontSmall")
|
||||
bigTextEntry:Dock(TOP)
|
||||
bigTextEntry:DockMargin( 20, 5, 20, 0 )
|
||||
bigTextEntry:SetUpdateOnType(true)
|
||||
bigTextEntry.OnValueChange = function(this, value)
|
||||
self.contents.bigText = value
|
||||
end
|
||||
bigTextEntry:SetTall(textEntryTall)
|
||||
|
||||
local durationLabel = self:Add("DLabel")
|
||||
durationLabel:SetText("Splash Text Duration")
|
||||
durationLabel:SetFont("cinematicSplashFontSmall")
|
||||
durationLabel:SetTextColor(ix.config.Get("color", Color(75, 119, 190)))
|
||||
durationLabel:Dock(TOP)
|
||||
durationLabel:DockMargin( 20, 5, 20, 0 )
|
||||
durationLabel:SizeToContents()
|
||||
|
||||
local durationSlider = self:Add("DNumSlider")
|
||||
durationSlider:Dock(TOP)
|
||||
durationSlider:SetMin(1) -- Set the minimum number you can slide to
|
||||
durationSlider:SetMax(30) -- Set the maximum number you can slide to
|
||||
durationSlider:SetDecimals(0) -- Decimal places - zero for whole number
|
||||
durationSlider:SetValue(self.contents.duration)
|
||||
|
||||
durationSlider:DockMargin(10, 0, 0, 5)
|
||||
durationSlider.OnValueChanged = function(_, val)
|
||||
self.contents.duration = math.Round(val)
|
||||
end
|
||||
|
||||
local blackBarBool = self:Add("DCheckBoxLabel")
|
||||
blackBarBool:SetText("Draw Black Bars")
|
||||
blackBarBool:SetFont("cinematicSplashFontSmall")
|
||||
blackBarBool:SetValue(self.contents.blackBars)
|
||||
blackBarBool.OnChange = function(this, bValue)
|
||||
self.contents.blackBars = bValue
|
||||
end
|
||||
blackBarBool:Dock(TOP)
|
||||
blackBarBool:DockMargin( 20, 5, 20, 0 )
|
||||
blackBarBool:SizeToContents()
|
||||
|
||||
local musicBool = self:Add("DCheckBoxLabel")
|
||||
musicBool:SetText("Play audio")
|
||||
musicBool:SetFont("cinematicSplashFontSmall")
|
||||
musicBool:SetValue(self.contents.music)
|
||||
musicBool.OnChange = function(this, bValue)
|
||||
self.contents.music = bValue
|
||||
end
|
||||
musicBool:Dock(TOP)
|
||||
musicBool:DockMargin( 20, 5, 20, 0 )
|
||||
musicBool:SizeToContents()
|
||||
|
||||
local Mixer = self:Add("DColorMixer")
|
||||
Mixer:Dock(TOP) -- Make Mixer fill place of Frame
|
||||
Mixer:SetPalette(true) -- Show/hide the palette DEF:true
|
||||
Mixer:SetAlphaBar(true) -- Show/hide the alpha bar DEF:true
|
||||
Mixer:SetWangs(true) -- Show/hide the R G B A indicators DEF:true
|
||||
Mixer:SetColor(Color(30,100,160)) -- Set the default color
|
||||
Mixer:SetTall(textEntryTall*3.5)
|
||||
Mixer:DockMargin( 20, 5, 20, 0 )
|
||||
|
||||
local quitButton = self:Add("DButton")
|
||||
quitButton:Dock(BOTTOM)
|
||||
quitButton:DockMargin( 20, 5, 20, 0 )
|
||||
quitButton:SetText("CANCEL")
|
||||
quitButton:SetTextColor(Color(255,0,0))
|
||||
quitButton:SetFont("cinematicSplashFontSmall")
|
||||
quitButton:SetTall(ScrH*0.05)
|
||||
quitButton.DoClick = function()
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
local postButton = self:Add("DButton")
|
||||
postButton:Dock(BOTTOM)
|
||||
postButton:DockMargin( 20, 5, 20, 0 )
|
||||
postButton:SetText("POST")
|
||||
postButton:SetTextColor(color_white)
|
||||
postButton:SetFont("cinematicSplashFontSmall")
|
||||
postButton:SetTall(ScrH*0.05)
|
||||
postButton.DoClick = function()
|
||||
if not (self.contents and (self.contents.text or self.contents.bigText)) then ix.util.Notify("Something went horribly wrong. Try reloading this panel") return end
|
||||
if self.contents.text == "" and self.contents.bigText == "" then ix.util.Notify("Text is missing. Enter some text to display") return end
|
||||
|
||||
net.Start("triggerCinematicSplashMenu")
|
||||
net.WriteString(self.contents.text) -- the normal text
|
||||
net.WriteString(self.contents.bigText) -- the big text
|
||||
net.WriteUInt(self.contents.duration,6) -- the duration of the splash
|
||||
net.WriteBool(self.contents.blackBars) -- whether to draw blackBars
|
||||
net.WriteBool(self.contents.music) -- whether to play a sound
|
||||
net.WriteColor(self.contents.color)
|
||||
net.SendToServer()
|
||||
self:Remove()
|
||||
end
|
||||
self:SizeToContents()
|
||||
|
||||
Mixer.ValueChanged = function(this, col) -- this is here because it needs to reference panels that are defined after mixer
|
||||
local newColor = Color(col.r, col.g, col.b)
|
||||
self.contents.color = newColor --ValueChanged doesn't include the color metatable, so we just define it here. Also remove any alpha changes
|
||||
textLabel:SetTextColor(newColor)
|
||||
bigTextLabel:SetTextColor(newColor)
|
||||
durationLabel:SetTextColor(newColor)
|
||||
postButton:SetTextColor(newColor)
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("cinematicSplashTextMenu", PANEL, "DFrame")
|
||||
|
||||
net.Receive("openCinematicSplashMenu", function()
|
||||
vgui.Create("cinematicSplashTextMenu")
|
||||
end)
|
||||
150
gamemodes/helix/plugins/cinematictext/derma/cl_splashtext.lua
Normal file
150
gamemodes/helix/plugins/cinematictext/derma/cl_splashtext.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
--[[
|
||||
| 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 ScrW, ScrH = ScrW(), ScrH()
|
||||
local music
|
||||
|
||||
local contents = {
|
||||
text = "",
|
||||
bigText = "",
|
||||
color = color_white,
|
||||
duration = 6,
|
||||
music = true
|
||||
}
|
||||
|
||||
function PANEL:Init()
|
||||
if ix.gui.cinematicSplashText then
|
||||
ix.gui.cinematicSplashText:Remove()
|
||||
end
|
||||
|
||||
ix.gui.cinematicSplashText = self
|
||||
|
||||
self:SetSize(ScrW, ScrH)
|
||||
self.barSize = ScrH*(ix.config.Get("cinematicBarSize", 0.18))
|
||||
end
|
||||
|
||||
function PANEL:Paint()
|
||||
end
|
||||
|
||||
function PANEL:DrawBlackBars()
|
||||
self.topBar = self:Add("DPanel")
|
||||
self.topBar:SetSize(ScrW, self.barSize + 10) -- +10 in to make sure it covers the top
|
||||
self.topBar:SetPos(0, -self.barSize) -- set it to be outside of the screen
|
||||
self.topBar.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(0,0,0, 255)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
self.bottomBar = self:Add("DPanel")
|
||||
self.bottomBar:SetSize(ScrW, self.barSize + 10) -- +10 in to make sure it covers the bottom
|
||||
self.bottomBar:SetPos(0, ScrH) -- set it to be outside of the screen
|
||||
self.bottomBar.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(0,0,0, 255)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:TriggerBlackBars()
|
||||
if not (IsValid(self.topBar) and IsValid(self.bottomBar)) then return end -- dont do anything if the bars dont exist
|
||||
|
||||
self.topBar:MoveTo(0, 0, 2, 0, 0.5)
|
||||
self.bottomBar:MoveTo(0, ScrH - self.barSize, 2, 0, 0.5, function() self:TriggerText() end)
|
||||
end
|
||||
|
||||
function PANEL:TriggerText()
|
||||
local textPanel = self:Add("DPanel")
|
||||
textPanel.Paint = function() end
|
||||
local panelWide, panelTall = 300, 300
|
||||
textPanel:SetSize(panelWide, panelTall)
|
||||
if contents.text and contents.text ~= "" then
|
||||
textPanel.text = textPanel:Add("DLabel")
|
||||
textPanel.text:SetFont("cinematicSplashFont")
|
||||
textPanel.text:SetTextColor(contents.color or color_white)
|
||||
textPanel.text:SetText(contents.text)
|
||||
textPanel.text:SetAutoStretchVertical(true)
|
||||
textPanel.text:Dock(TOP)
|
||||
textPanel.text:SetAlpha(0)
|
||||
textPanel.text:AlphaTo(255, 2, 0, function()
|
||||
if not contents.bigText then self:TriggerCountdown() end
|
||||
end)
|
||||
|
||||
surface.SetFont("cinematicSplashFont")
|
||||
textPanel.text.textWide, textPanel.text.textTall = surface.GetTextSize(contents.text)
|
||||
panelWide = panelWide > textPanel.text.textWide and panelWide or textPanel.text.textWide
|
||||
panelTall = panelTall + textPanel.text.textTall
|
||||
textPanel:SetSize(panelWide, panelTall)
|
||||
end
|
||||
|
||||
if contents.bigText and contents.bigText ~= "" then
|
||||
textPanel.bigText = textPanel:Add("DLabel")
|
||||
textPanel.bigText:SetFont("cinematicSplashFontBig")
|
||||
textPanel.bigText:SetTextColor(contents.color or color_white)
|
||||
textPanel.bigText:SetText(contents.bigText)
|
||||
textPanel.bigText:SetAutoStretchVertical(true)
|
||||
textPanel.bigText:Dock(TOP)
|
||||
textPanel.bigText:SetAlpha(0)
|
||||
textPanel.bigText:AlphaTo(255, 2, 1, function()
|
||||
self:TriggerCountdown()
|
||||
end)
|
||||
|
||||
surface.SetFont("cinematicSplashFontBig")
|
||||
textPanel.bigText.textWide, textPanel.bigText.textTall = surface.GetTextSize(contents.bigText)
|
||||
panelWide = panelWide > textPanel.bigText.textWide and panelWide or textPanel.bigText.textWide
|
||||
panelTall = panelTall + textPanel.bigText.textTall
|
||||
textPanel:SetSize(panelWide, panelTall)
|
||||
end
|
||||
|
||||
if textPanel.text then textPanel.text:DockMargin((panelWide/2) - (textPanel.text.textWide/2), 0, 0, 20) end
|
||||
if textPanel.bigText then textPanel.bigText:DockMargin((panelWide/2) - (textPanel.bigText.textWide/2), 0, 0, 20) end
|
||||
textPanel:InvalidateLayout(true)
|
||||
|
||||
textPanel:SetPos(ScrW - textPanel:GetWide() - ScrW*0.05, ScrH*0.58)
|
||||
|
||||
if contents.music then
|
||||
music = CreateSound(LocalPlayer(), ix.config.Get("cinematicTextMusic","music/stingers/industrial_suspense2.wav"))
|
||||
music:PlayEx(0, 100)
|
||||
music:ChangeVolume(1, 2)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:TriggerCountdown()
|
||||
self:AlphaTo(0, 4, contents.duration, function()
|
||||
self:Remove()
|
||||
end)
|
||||
timer.Simple(contents.duration, function()
|
||||
if music then music:FadeOut(4) end
|
||||
end)
|
||||
end
|
||||
|
||||
vgui.Register("cinematicSplashText", PANEL, "DPanel")
|
||||
|
||||
net.Receive("triggerCinematicSplashMenu", function()
|
||||
contents.text = net.ReadString()
|
||||
contents.bigText = net.ReadString()
|
||||
contents.duration = net.ReadUInt(6)
|
||||
local blackbars = net.ReadBool()
|
||||
contents.music = net.ReadBool()
|
||||
contents.color = net.ReadColor()
|
||||
|
||||
if contents.text == "" then contents.text = nil end
|
||||
if contents.bigText == "" then contents.bigText = nil end
|
||||
|
||||
local splashText = vgui.Create("cinematicSplashText")
|
||||
if blackbars then
|
||||
splashText:DrawBlackBars()
|
||||
splashText:TriggerBlackBars()
|
||||
else
|
||||
splashText:TriggerText()
|
||||
end
|
||||
end)
|
||||
94
gamemodes/helix/plugins/cinematictext/sh_plugin.lua
Normal file
94
gamemodes/helix/plugins/cinematictext/sh_plugin.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| 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 = "Cinematic Splash Text"
|
||||
PLUGIN.description = "Cinematic looking splash text for that extra flair."
|
||||
PLUGIN.author = "76561198070441753 (TovarischPootis), ported to IX by mixed.dev"
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
|
||||
ix.config.Add("cinematicTextFont", "Arial", "The font used to display cinematic splash texts.", function()
|
||||
if (CLIENT) then
|
||||
hook.Run("LoadCinematicSplashTextFonts")
|
||||
end
|
||||
end, {category = PLUGIN.name})
|
||||
|
||||
ix.config.Add("cinematicTextSize", 18, "The font size multiplier used by cinematic splash texts.", function()
|
||||
if (CLIENT) then
|
||||
hook.Run("LoadCinematicSplashTextFonts")
|
||||
end
|
||||
end, {
|
||||
category = PLUGIN.name,
|
||||
data = {min = 10, max = 50},
|
||||
}
|
||||
)
|
||||
|
||||
ix.config.Add("cinematicTextSizeBig", 30, "The big font size multiplier used by cinematic splash texts.", function()
|
||||
if (CLIENT) then
|
||||
hook.Run("LoadCinematicSplashTextFonts")
|
||||
end
|
||||
end, {
|
||||
category = PLUGIN.name,
|
||||
data = {min = 10, max = 50},
|
||||
}
|
||||
)
|
||||
|
||||
ix.config.Add("cinematicBarSize", 0.18, "How big the black bars are during cinematic.", nil, {
|
||||
category = PLUGIN.name,
|
||||
data = {min = 0.1, max = 0.2, decimals = 2}
|
||||
})
|
||||
|
||||
ix.config.Add("cinematicTextMusic","music/stingers/industrial_suspense2.wav","The music played upon cinematic splash text appearance.",nil,
|
||||
{category = PLUGIN.name})
|
||||
|
||||
|
||||
ix.command.Add("CinematicMenu", {
|
||||
description = "Open a menu to setup the cinematic.",
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client)
|
||||
net.Start("openCinematicSplashMenu")
|
||||
net.Send(client)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
if CLIENT then
|
||||
function PLUGIN:LoadCinematicSplashTextFonts()
|
||||
local font = ix.config.Get("cinematicTextFont", "Arial")
|
||||
local fontSizeBig = ix.config.Get("cinematicTextSizeBig", 30)
|
||||
local fontSizeNormal = ix.config.Get("cinematicTextSize", 18)
|
||||
surface.CreateFont("cinematicSplashFontBig", {
|
||||
font = font,
|
||||
size = ScreenScale(fontSizeBig),
|
||||
extended = true,
|
||||
weight = 1000
|
||||
})
|
||||
|
||||
surface.CreateFont("cinematicSplashFont", {
|
||||
font = font,
|
||||
size = ScreenScale(fontSizeNormal),
|
||||
extended = true,
|
||||
weight = 800
|
||||
})
|
||||
|
||||
surface.CreateFont("cinematicSplashFontSmall", {
|
||||
font = font,
|
||||
size = ScreenScale(10),
|
||||
extended = true,
|
||||
weight = 800
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:LoadFonts()
|
||||
self:LoadCinematicSplashTextFonts() -- this will create the fonts upon initial load.
|
||||
end
|
||||
end
|
||||
27
gamemodes/helix/plugins/cinematictext/sv_plugin.lua
Normal file
27
gamemodes/helix/plugins/cinematictext/sv_plugin.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
--[[
|
||||
| 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("openCinematicSplashMenu")
|
||||
util.AddNetworkString("triggerCinematicSplashMenu")
|
||||
|
||||
net.Receive("triggerCinematicSplashMenu", function(_, client)
|
||||
if not client:IsAdmin() then return end
|
||||
|
||||
net.Start("triggerCinematicSplashMenu")
|
||||
net.WriteString(net.ReadString()) -- text
|
||||
net.WriteString(net.ReadString()) --bigtext
|
||||
net.WriteUInt(net.ReadUInt(6), 6) --duration
|
||||
net.WriteBool(net.ReadBool()) -- blackBars
|
||||
net.WriteBool(net.ReadBool()) -- music
|
||||
net.WriteColor(net.ReadColor()) -- color
|
||||
net.Broadcast()
|
||||
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
|
||||
133
gamemodes/helix/plugins/clientsideprops/sh_plugin.lua
Normal file
133
gamemodes/helix/plugins/clientsideprops/sh_plugin.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
--[[
|
||||
| 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 = "Clientside Prop Render Speed",
|
||||
optdCsentRenderSpeed = "How many clientside props should be calcualted every frame. Lower values = more FPS, but slower, higher values = less FPS, but faster.",
|
||||
cmdRemoveClientProps = "Remove all clientside props in a radius around you."
|
||||
})
|
||||
|
||||
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 = "Conteneur"
|
||||
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
|
||||
205
gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
205
gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
@@ -0,0 +1,205 @@
|
||||
--[[
|
||||
| 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 = "Caisse en bois",
|
||||
description = "Une simple caisse en bois, pouvant stocker quelques objets.",
|
||||
width = 3,
|
||||
height = 3,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/wood_crate002a.mdl", {
|
||||
name = "Grosse caisse en bois",
|
||||
description = "Une simple caisse en bois, pouvant stocker des objets.",
|
||||
width = 5,
|
||||
height = 5,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_c17/lockers001a.mdl", {
|
||||
name = "Vestaire",
|
||||
description = "Un vestiaire dans lequel vous pouvez stocker des objets.",
|
||||
width = 4,
|
||||
height = 4,
|
||||
})
|
||||
|
||||
ix.container.Register("mode ls/props_wasteland/controlroom_storagecloset001a.mdl", {
|
||||
name = "Grande Armoire",
|
||||
description = "A green metal cabinet.",
|
||||
width = 8,
|
||||
height = 3,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_filecabinet001a.mdl", {
|
||||
name = "Armoire à dossiers",
|
||||
description = "Une armoire à dossier dans laquelle vous pouvez stocker quelques objets.",
|
||||
width = 3,
|
||||
height = 2
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_lab/filecabinet02.mdl", {
|
||||
name = "Armoire à dossiers",
|
||||
description = "Une armoire à dossier dans laquelle vous pouvez stocker quelques objets.",
|
||||
width = 3,
|
||||
height = 2
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/controlroom_filecabinet002a.mdl", {
|
||||
name = "Armoire à dossiers moyenne",
|
||||
description = "Une armoire à dossier moyenne dans laquelle vous pouvez stocker quelques objets.",
|
||||
width = 2,
|
||||
height = 5
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_c17/furniturefridge001a.mdl", {
|
||||
name = "Réfrigirateur",
|
||||
description = "Un réfrigirateur dans lequel vous pouvez stocker des objets.",
|
||||
width = 3,
|
||||
height = 3,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_wasteland/kitchen_fridge001a.mdl", {
|
||||
name = "Grand réfrigirateur",
|
||||
description = "Un grand réfrigirateur dans lequel vous pouvez stocker des objets.",
|
||||
width = 4,
|
||||
height = 4,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/trashbin01a.mdl", {
|
||||
name = "Poubelle",
|
||||
description = "Une poubelle pour jeter ses objets. Je pourrais peut-être trouvé des choses dedans...",
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/trashdumpster01a.mdl", {
|
||||
name = "Benne",
|
||||
description = "Une benne de taille moyenne, dont une odeur nauséabonde se dégage.",
|
||||
width = 9,
|
||||
height = 9
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_forest/footlocker01_closed.mdl", {
|
||||
name = "Coffre",
|
||||
description = "Un coffre en bois pourri, dans lequel vous pouvez stocker des choses.",
|
||||
width = 5,
|
||||
height = 5
|
||||
})
|
||||
|
||||
ix.container.Register("models/items/item_item_crate.mdl", {
|
||||
name = "Caisse de livraison",
|
||||
description = "Une caisse originalement utilisée pour livrer des objets aux commercants. ",
|
||||
width = 3,
|
||||
height = 3
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/cardboard_box001a.mdl", {
|
||||
name = "Carton",
|
||||
description = "Un simple carton dans lequel vous pouvez stocker que peu de choses.",
|
||||
width = 2,
|
||||
height = 1
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/cardboard_box002a.mdl", {
|
||||
name = "Carton",
|
||||
description = "Un simple carton dans lequel vous pouvez stocker que peu de choses.",
|
||||
width = 2,
|
||||
height = 1
|
||||
})
|
||||
|
||||
ix.container.Register("models/props_junk/cardboard_box003a.mdl", {
|
||||
name = "Carton",
|
||||
description = "Un simple carton dans lequel vous pouvez stocker que peu de choses.",
|
||||
width = 2,
|
||||
height = 1
|
||||
})
|
||||
|
||||
ix.container.Register("models/items/ammocrate_smg1.mdl", {
|
||||
name = "Caisse de munitions",
|
||||
description = "A heavy crate that stores ammo.",
|
||||
width = 8,
|
||||
height = 6,
|
||||
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/items/ammocrate_rockets.mdl", {
|
||||
name = "Caisse de munitions",
|
||||
description = "A heavy crate that stores ammo.",
|
||||
width = 8,
|
||||
height = 6,
|
||||
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/items/ammocrate_ar2.mdl", {
|
||||
name = "Caisse de munitions",
|
||||
description = "A heavy crate that stores ammo.",
|
||||
width = 8,
|
||||
height = 6,
|
||||
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/items/ammocrate_grenade.mdl", {
|
||||
name = "Caisse de munitions",
|
||||
description = "A heavy crate that stores ammo.",
|
||||
width = 8,
|
||||
height = 6,
|
||||
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
|
||||
})
|
||||
|
||||
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, "Si oui ou non les conteneurs seront sauvegardés après un redémarrage du serveur.", nil, {
|
||||
category = "Stockages"
|
||||
})
|
||||
|
||||
ix.config.Add("containerOpenTime", 0.7, "Le temps nécessaire à l'ouverture d'un conteneur.", nil, {
|
||||
data = {min = 0, max = 50},
|
||||
category = "Stockages"
|
||||
})
|
||||
|
||||
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] Tentative de restauration d'un inventaire de conteneur avec un ID d'inventaire invalide '%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] Tentative de restauration d'un inventaire de conteneur avec un ID d'inventaire invalide '%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 a %s le mot de passe pour '%s'.", client:Name(), arg[3] and "réglé" or "retiré", arg[1], arg[2])
|
||||
end)
|
||||
|
||||
ix.log.AddType("containerName", function(client, ...)
|
||||
local arg = {...}
|
||||
|
||||
if (arg[3]) then
|
||||
return string.format("%s a établi un stockage %d au nom de '%s'.", client:Name(), arg[2], arg[1])
|
||||
else
|
||||
return string.format("%s a retiré le stockage %d.", client:Name(), arg[2])
|
||||
end
|
||||
end)
|
||||
|
||||
ix.log.AddType("openContainer", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s a ouvert le stockage '%s' #%d.", 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 a fermé le stockage '%s' #%d.", 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 = "Mettre un mot de passe",
|
||||
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 = "Mettre un nom",
|
||||
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 = "Réticule."
|
||||
|
||||
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,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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Generic Item"
|
||||
ITEM.description = "Generic Description"
|
||||
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
|
||||
ITEM.category = "Autres"
|
||||
|
||||
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 = "Objets tombés"
|
||||
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("Objets tombés")
|
||||
|
||||
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 = "Ce personnage n'a aucun objet perdu.",
|
||||
plyRefund = "Les articles ont été remboursés avec succès.",
|
||||
plyRefundTemp = "Les articles ont été remboursés avec succès. Les excédents ont été mis dans un conteneur à l'avant.",
|
||||
plyRefundLost = "%s les articles ont été irrémédiablement perdus lors du remboursement !"
|
||||
}
|
||||
16
gamemodes/helix/plugins/deathdrop/languages/sh_french.lua
Normal file
16
gamemodes/helix/plugins/deathdrop/languages/sh_french.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 = "Ce personnage n'a aucun objet perdu.",
|
||||
plyRefund = "Les articles ont été remboursés avec succès.",
|
||||
plyRefundTemp = "Les articles ont été remboursés avec succès. Les excédents ont été mis dans un conteneur à l'avant.",
|
||||
plyRefundLost = "%s les articles ont été irrémédiablement perdus lors du remboursement !"
|
||||
}
|
||||
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 = "Après la mort, le personnage laissera tomber ses affaires."
|
||||
|
||||
ix.config.Add("dropItems", false, "Que les personnages abandonnent ou non leurs objets après leur mort.", nil, {
|
||||
category = "Perte du stuff"
|
||||
})
|
||||
|
||||
ix.config.Add("dropContainerModel", "models/props_c17/BriefCase001a.mdl", "Un modèle de chemin d'accès d'un conteneur avec des éléments déposés.", nil, {
|
||||
category = "Perte du stuff"
|
||||
})
|
||||
|
||||
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 = "Suppression de %d objets tombés."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
deathDropRemoved = "Suppression de %d objets tombés."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
deathDropRemoved = "Borrados un total de %d death drops."
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveDeathDrops", {
|
||||
description = "Supprime tous les objets des morts sur la carte ou dans un rayon donné.",
|
||||
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 = "Redonne tous les objets que le joueur a perdu après sa dernière mort.",
|
||||
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] Échec du transfert de l'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("Objets tombés", 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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user