This commit is contained in:
lifestorm
2024-08-04 22:55:00 +03:00
parent 0e770b2b49
commit 94063e4369
7342 changed files with 1718932 additions and 14 deletions

View 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
})

View 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
})

View 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)

View 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

View 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

View 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

View 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

View 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)

View 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

View 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")

View 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

View 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"
}

View 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"
}

View 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 = "Вход в режим редактирования зоны."
}

View 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

View 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)

View 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

View 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

View 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

View 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

View File

@@ -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

View 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/
--]]
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"
}

View File

@@ -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

View File

@@ -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"}

View File

@@ -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",
}

View 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/
--]]
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

View 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

View 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 !"
})

View 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

View File

@@ -0,0 +1 @@
4x1374-j382fs-599g87-31j7h3

View File

@@ -0,0 +1 @@
<put https://proxycheck.io proxy key here and remove .example from file name>

View File

@@ -0,0 +1 @@
https://discord.com/api/webhooks/832651486860541973/iYpexHJmA5E6pD6WwUT1GyvLtQ9_8ly-qB0xloIsPu7KmaU0mkQVRE_Kg0qVwGRlwhs_

View File

@@ -0,0 +1 @@
<put discord webhook URL here and remove .example from filename>

View 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

View 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

View 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

View 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

View 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)

View 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

View 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

View 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

View 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
})

View 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
})

View 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

View 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

View 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)

View 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

View 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)

View 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")

View 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
})

View 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)

View 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)

View 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

View 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

View 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"
})

View 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

View 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

View 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.

View File

@@ -0,0 +1,11 @@
# Bodygroup Manager
## Installation
- Download the folder as a .zip
- Place inside of your schema/plugins folder
## Preview
![Example](https://i.imgur.com/6bqX51s.png)
## Support
- Gary#1170

View 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)

View 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")

View 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")

View 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)

View 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

View 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")

View File

@@ -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
}

View 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
}

View 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
})

View 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/
--]]
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

View File

@@ -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
})

View File

@@ -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

View 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

View 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)

File diff suppressed because it is too large Load Diff

View 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")

View 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

View 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
})

View 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)

View 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)

View 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

View 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)

View 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)

View 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

View 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
})

View 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)

View 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

View File

@@ -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

View 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
})

View 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
})

View 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

View File

@@ -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

View 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/
--]]
ITEM.name = "Generic Item"
ITEM.description = "Generic Description"
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
ITEM.width = 3
ITEM.height = 3

View 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/
--]]
ITEM.name = "Generic Item"
ITEM.description = "Generic Description"
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
ITEM.width = 2
ITEM.height = 2

View File

@@ -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")

View 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/
--]]
ITEM.name = "Generic Item"
ITEM.description = "Generic Description"
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
ITEM.width = 4
ITEM.height = 4

View 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."

View 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

View 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 !"
}

View 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 !"
}

View 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!"
}

Some files were not shown because too many files have changed in this diff Show More