This commit is contained in:
lifestorm
2024-08-04 23:12:27 +03:00
parent 0e770b2b49
commit ba1fc01b16
7084 changed files with 2173495 additions and 14 deletions

View File

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

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 has removed their last 3D text.", client:GetName())
end)
-- Called when the player is sending client info.
function PLUGIN:PlayerInitialSpawn(client)
timer.Simple(1, function()
if (IsValid(client)) then
local json = util.TableToJSON(self.list)
local compressed = util.Compress(json)
local length = compressed:len()
net.Start("ixTextList")
net.WriteUInt(length, 32)
net.WriteData(compressed, length)
net.Send(client)
end
end)
end
-- Adds a text to the list, sends it to the players, and saves data.
function PLUGIN:AddText(position, angles, text, scale)
local index = #self.list + 1
scale = math.Clamp((scale or 1) * 0.1, 0.001, 5)
self.list[index] = {position, angles, text, scale}
net.Start("ixTextAdd")
net.WriteUInt(index, 32)
net.WriteVector(position)
net.WriteAngle(angles)
net.WriteString(text)
net.WriteFloat(scale)
net.Broadcast()
self:SaveText()
return index
end
-- Removes a text that are within the radius of a position.
function PLUGIN:RemoveText(position, radius)
radius = radius or 100
local textDeleted = {}
for k, v in pairs(self.list) do
if (k == 0) then
continue
end
if (v[1]:Distance(position) <= radius) then
textDeleted[#textDeleted + 1] = k
end
end
if (#textDeleted > 0) then
-- Invert index table to delete from highest -> lowest
textDeleted = table.Reverse(textDeleted)
for _, v in ipairs(textDeleted) do
table.remove(self.list, v)
net.Start("ixTextRemove")
net.WriteUInt(v, 32)
net.Broadcast()
end
self:SaveText()
end
return #textDeleted
end
function PLUGIN:RemoveTextByID(id)
local info = self.list[id]
if (!info) then
return false
end
net.Start("ixTextRemove")
net.WriteUInt(id, 32)
net.Broadcast()
table.remove(self.list, id)
return true
end
function PLUGIN:EditText(position, text, scale, radius)
radius = radius or 100
local textEdited = false
for k, v in pairs(self.list) do
if (k == 0) then
continue
end
if (v[1]:Distance(position) <= radius) then
v[3] = text
v[4] = scale
textEdited = true
net.Start("ixTextAdd")
net.WriteUInt(k, 32)
net.WriteVector(v[1])
net.WriteAngle(v[2])
net.WriteString(text)
net.WriteFloat(scale)
net.Broadcast()
self:SaveText()
end
end
return textEdited
end
-- Called after entities have been loaded on the map.
function PLUGIN:LoadData()
self.list = self:GetData() or {}
-- Formats table to sequential to support legacy panels.
self.list = table.ClearKeys(self.list)
end
-- Called when the plugin needs to save information.
function PLUGIN:SaveText()
self:SetData(self.list)
end
else
local function IsUsingTextAddTool()
if !IsValid(LocalPlayer()) then return false end
if !IsValid(LocalPlayer():GetActiveWeapon()) then return false end
if !LocalPlayer():GetActiveWeapon():GetClass() then return false end
if !LocalPlayer():GetTool() then return false end
if !LocalPlayer():GetTool().Mode then return false end
if (LocalPlayer():GetActiveWeapon():GetClass() == "gmod_tool" and LocalPlayer():GetTool().Mode == "sh_textadd") then
return true
end
return false
end
-- Pre-define the zero index in client before the net receives
PLUGIN.list[0] = PLUGIN.list[0] or 0
language.Add("Undone_ix3dText", "Removed 3D Text")
function PLUGIN:GenerateMarkup(text)
local object = ix.markup.Parse("<font=ix3D2DFont>"..text:gsub("\\n", "\n"))
object.onDrawText = function(surfaceText, font, x, y, color, alignX, alignY, alpha)
local _, textH = surface.GetTextSize( surfaceText )
-- shadow
surface.SetTextPos(x + 1, y + 1 - textH * 0.5)
surface.SetTextColor(0, 0, 0, alpha)
surface.SetFont(font)
surface.DrawText(surfaceText)
surface.SetTextPos(x, y - (textH * 0.5))
surface.SetTextColor(color.r or 255, color.g or 255, color.b or 255, alpha)
surface.SetFont(font)
surface.DrawText(surfaceText)
end
return object
end
-- Receives new text objects that need to be drawn.
net.Receive("ixTextAdd", function()
local index = net.ReadUInt(32)
local position = net.ReadVector()
local angles = net.ReadAngle()
local text = net.ReadString()
local scale = net.ReadFloat()
if (text != "") then
PLUGIN.list[index] = {
position,
angles,
PLUGIN:GenerateMarkup(text),
scale
}
PLUGIN.list[0] = #PLUGIN.list
end
end)
net.Receive("ixTextRemove", function()
local index = net.ReadUInt(32)
table.remove(PLUGIN.list, index)
PLUGIN.list[0] = #PLUGIN.list
end)
-- Receives a full update on ALL texts.
net.Receive("ixTextList", function()
local length = net.ReadUInt(32)
local data = net.ReadData(length)
local uncompressed = util.Decompress(data)
if (!uncompressed) then
ErrorNoHalt("[Helix] Unable to decompress text data!\n")
return
end
PLUGIN.list = util.JSONToTable(uncompressed)
-- Will be saved, but refresh just to make sure.
PLUGIN.list[0] = #PLUGIN.list
for k, v in pairs(PLUGIN.list) do
if (k == 0) then
continue
end
local object = ix.markup.Parse("<font=ix3D2DFont>"..v[3]:gsub("\\n", "\n"))
object.onDrawText = function(text, font, x, y, color, alignX, alignY, alpha)
draw.TextShadow({
pos = {x, y},
color = ColorAlpha(color, alpha),
text = text,
xalign = 0,
yalign = alignY,
font = font
}, 1, alpha)
end
v[3] = object
end
end)
function PLUGIN:StartChat()
self.preview = nil
end
function PLUGIN:FinishChat()
self.preview = nil
end
function PLUGIN:HUDPaint()
if (ix.chat.currentCommand != "textremove") then
return
end
local radius = tonumber(ix.chat.currentArguments[1]) or 100
surface.SetDrawColor(200, 30, 30)
surface.SetTextColor(200, 30, 30)
surface.SetFont("ixMenuButtonFont")
local i = 0
for k, v in pairs(self.list) do
if (k == 0) then
continue
end
if (v[1]:Distance(LocalPlayer():GetEyeTraceNoCursor().HitPos) <= radius) then
local screen = v[1]:ToScreen()
surface.DrawLine(
ScrW() * 0.5,
ScrH() * 0.5,
math.Clamp(screen.x, 0, ScrW()),
math.Clamp(screen.y, 0, ScrH())
)
i = i + 1
end
end
if (i > 0) then
local textWidth, textHeight = surface.GetTextSize(i)
surface.SetTextPos(ScrW() * 0.5 - textWidth * 0.5, ScrH() * 0.5 + textHeight + 8)
surface.DrawText(i)
end
end
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
if (bDrawingDepth or bDrawingSkybox) then
return
end
local toolUsage = IsUsingTextAddTool()
-- preview for textadd command
if (ix.chat.currentCommand == "textadd" or toolUsage) then
local arguments = ix.chat.currentArguments
local text = toolUsage and GetConVar("sh_textadd_text"):GetString() or tostring(arguments[1] or "")
local scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)
if toolUsage then
scale = math.Clamp((tonumber(GetConVar("sh_textadd_scale"):GetInt()) or 1) * 0.1, 0.001, 5)
end
local trace = LocalPlayer():GetEyeTraceNoCursor()
local position = trace.HitPos
local angles = trace.HitNormal:Angle()
local markup
angles:RotateAroundAxis(angles:Up(), 90)
angles:RotateAroundAxis(angles:Forward(), 90)
-- markup will error with invalid fonts
pcall(function()
markup = PLUGIN:GenerateMarkup(text)
end)
if (markup) then
cam.Start3D2D(position, angles, scale)
markup:draw(0, 0, 1, 1, 255)
cam.End3D2D()
end
end
local position = LocalPlayer():GetPos()
local texts = self.list
for i = 1, texts[0] do
local distance = texts[i][1]:DistToSqr(position)
if (distance > 1048576) then
continue
end
cam.Start3D2D(texts[i][1], texts[i][2], texts[i][4] or 0.1)
local alpha = (1 - ((distance - 65536) / 768432)) * 255
texts[i][3]:draw(0, 0, 1, 1, alpha)
cam.End3D2D()
end
end
end
ix.command.Add("TextAdd", {
description = "@cmdTextAdd",
adminOnly = true,
arguments = {
ix.type.string,
bit.bor(ix.type.number, ix.type.optional)
},
OnRun = function(self, client, text, scale)
local trace = client:GetEyeTrace()
local position = trace.HitPos
local angles = trace.HitNormal:Angle()
angles:RotateAroundAxis(angles:Up(), 90)
angles:RotateAroundAxis(angles:Forward(), 90)
local index = PLUGIN:AddText(position + angles:Up() * 0.1, angles, text, scale)
undo.Create("ix3dText")
undo.SetPlayer(client)
undo.AddFunction(function()
if (PLUGIN:RemoveTextByID(index)) then
ix.log.Add(client, "undo3dText")
end
end)
undo.Finish()
return "@textAdded"
end
})
ix.command.Add("TextRemove", {
description = "@cmdTextRemove",
adminOnly = true,
arguments = bit.bor(ix.type.number, ix.type.optional),
OnRun = function(self, client, radius)
local trace = client:GetEyeTrace()
local position = trace.HitPos + trace.HitNormal * 2
local amount = PLUGIN:RemoveText(position, radius)
return "@textRemoved", amount
end
})
ix.command.Add("TextEdit", {
description = "@cmdTextEdit",
adminOnly = true,
arguments = {
bit.bor(ix.type.string, ix.type.optional),
bit.bor(ix.type.number, ix.type.optional),
bit.bor(ix.type.number, ix.type.optional)
},
OnRun = function(self, client, text, scale, radius)
local trace = client:GetEyeTrace()
local position = trace.HitPos + trace.HitNormal * 2
local edited = PLUGIN:EditText(position, text, scale, radius)
if (edited) then
return "@textEdited"
else
return "@textNotFound"
end
end
})

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,377 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local function FacingWall(client)
local data = {}
data.start = client:EyePos()
data.endpos = data.start + client:GetForward() * 20
data.filter = client
if (!util.TraceLine(data).Hit) then
return "@faceWall"
end
end
local function FacingWallBack(client)
local data = {}
data.start = client:LocalToWorld(client:OBBCenter())
data.endpos = data.start - client:GetForward() * 20
data.filter = client
if (!util.TraceLine(data).Hit) then
return "@faceWallBack"
end
end
function PLUGIN:SetupActs()
-- sit
ix.act.Register("Sit", "metrocop", {
sequence = {"n7_male_sit_ground", "n7_male_sit01", "n7_male_sit02", "n7_male_sit03", "n7_male_sit04", "n7_male_sit05", "n7_male_sit06", "n7_male_sit07"},
untimed = true
})
ix.act.Register("Sit", "citizen_male", {
sequence = {"willard_male_male_sit_ground", "willard_male_male_sit01", "willard_male_male_sit02", "willard_male_male_sit03", "willard_male_male_sit04", "willard_male_male_sit05", "willard_male_male_sit06", "willard_male_male_sit07"},
untimed = true
})
ix.act.Register("Sit", "citizen_female", {
sequence = {"willard_female_sit_ground", "willard_female_sit01", "willard_female_sit02", "willard_female_sit03", "willard_female_sit04", "willard_female_sit05", "willard_female_sit06", "willard_female_sit07"},
untimed = true
})
ix.act.Register("Sit", "metrocop_female", {
sequence = {"n7_female_sit_ground", "n7_female_sit01", "n7_female_sit02", "n7_female_sit03", "n7_female_sit04", "n7_female_sit05", "n7_female_sit06", "n7_female_sit07"},
untimed = true
})
-- sitwall
ix.act.Register("SitWall", "metrocop", {
sequence = {
{"n7_male_sitwall", check = FacingWallBack},
},
untimed = true,
idle = true
})
ix.act.Register("SitWall", "citizen_male", {
sequence = {
{"willard_male_male_sitwall", check = FacingWallBack},
},
untimed = true,
idle = true
})
ix.act.Register("SitWall", "metrocop_female", {
sequence = {
{"n7_female_sitwall", check = FacingWallBack},
},
untimed = true,
idle = true
})
ix.act.Register("SitWall", "citizen_female", {
sequence = {
{"willard_female_sitwall", check = FacingWallBack},
},
untimed = true,
idle = true
})
--sitlean
ix.act.Register("SitLean", {"citizen_male", "citizen_female"}, {
sequence = {"sitccouchtv1", "sitchair1", "sitchairtable1", "sitcouch1", "sitcouchknees1"},
untimed = true,
idle = true
})
ix.act.Register("SitChair", "citizen_male", {
start = {"idle_to_sit_chair"},
sequence = "sit_chair",
finish = {"sit_chair_to_idle", duration = 2.1},
untimed = true,
idle = true
})
-- Idle
ix.act.Register("Idle", "vortigaunt", {
sequence = "idle_nectar",
untimed = true,
idle = true
})
-- Kneel
ix.act.Register("Kneel", "vortigaunt", {
sequence = "rescue_idle",
untimed = true,
idle = true
})
-- Sit
ix.act.Register("Sit", "vortigaunt", {
sequence = {"chess_wait", {"sit_rollercoaster", offset = function(client)
return client:GetUp() * 25 + client:GetForward() * -30
end}
},
untimed = true,
idle = true
})
-- stand
ix.act.Register("Stand","metrocop", {
sequence = {"n7_male_stand01", "n7_male_stand02", "n7_male_stand03", "n7_male_stand04", "n7_male_stand05"},
untimed = true
})
ix.act.Register("Stand", "citizen_male", {
sequence = {"willard_male_male_stand01", "willard_male_male_stand02", "willard_male_male_stand03", "willard_male_male_stand04", "willard_male_male_stand05"},
untimed = true
})
ix.act.Register("Stand", "metrocop_female", {
sequence = {"n7_female_stand01", "n7_female_stand02", "n7_female_stand03", "n7_female_stand04", "n7_female_stand05"},
untimed = true
})
ix.act.Register("Stand", "citizen_female", {
sequence = {"willard_female_stand01", "willard_female_stand02", "willard_female_stand03", "willard_female_stand04", "willard_female_stand05"},
untimed = true
})
-- type
ix.act.Register("Type", "overwatch", {
sequence = "console_type_loop",
untimed = true,
idle = true
})
-- cheer
ix.act.Register("Cheer", {"citizen_male", "metrocop"}, {
sequence = {{"cheer1", duration = 1.6}, "cheer2", "wave_smg1"}
})
ix.act.Register("Cheer", {"citizen_female", "metrocop_female"}, {
sequence = {"cheer1", "wave_smg1"}
})
-- lean
ix.act.Register("Lean", {"citizen_male", "citizen_female"}, {
start = {"idle_to_lean_back", "", ""},
sequence = {
{"lean_back", check = FacingWallBack},
{"plazaidle1", check = FacingWallBack},
{"plazaidle2", check = FacingWallBack}
},
untimed = true,
idle = true
})
ix.act.Register("Lean", {"metrocop"}, {
sequence = {
{"n7_male_lean01", check = FacingWallBack},
{"n7_male_lean02", check = FacingWallBack},
{"idle_baton", check = FacingWallBack},
},
untimed = true,
idle = true
})
ix.act.Register("Lean", "citizen_male", {
sequence = {
{"willard_male_male_lean01", check = FacingWallBack, offset = function(client)
return client:GetForward() * 4
end},
{"willard_male_male_lean02", check = FacingWallBack, offset = function(client)
return client:GetForward() * 3
end}
},
untimed = true,
idle = true
})
ix.act.Register("Lean", "metrocop_female", {
sequence = {
{"n7_female_lean01", check = FacingWallBack, offset = function(client)
return client:GetForward() * 6
end},
{"n7_female_lean02", check = FacingWallBack, offset = function(client)
return client:GetForward() * 4
end}
},
untimed = true,
idle = true
})
ix.act.Register("Lean", "citizen_female", {
sequence = {
{"willard_female_lean01", check = FacingWallBack, offset = function(client)
return client:GetForward() * 6
end},
{"willard_female_lean02", check = FacingWallBack, offset = function(client)
return client:GetForward() * 4
end}
},
untimed = true,
idle = true
})
-- injured
ix.act.Register("Injured", "metrocop", {
sequence = {"n7_male_injured"},
untimed = true,
idle = true
})
ix.act.Register("Injured", "citizen_male", {
sequence = {"willard_male_male_injured"},
untimed = true,
idle = true
})
ix.act.Register("Injured", "metrocop_female", {
sequence = "n7_female_injured",
untimed = true,
idle = true
})
ix.act.Register("Injured", "citizen_female", {
sequence = "willard_female_injured",
untimed = true,
idle = true
})
-- arrest
ix.act.Register("Arrest", "metrocop", {
sequence = {"n7_male_arrest", "n7_male_arrest_sit01", "n7_male_handsup"},
untimed = true
})
ix.act.Register("Arrest", "citizen_male", {
sequence = {"willard_male_male_arrest", "willard_male_male_arrest_sit01", "willard_male_male_handsup"},
untimed = true
})
ix.act.Register("Arrest", "metrocop_female", {
sequence = {"n7_female_arrest", "n7_female_arrest_sit01", "n7_female_handsup"},
untimed = true
})
ix.act.Register("Arrest", "citizen_female", {
sequence = {"willard_female_arrest", "willard_female_arrest_sit01", "willard_female_handsup"},
untimed = true
})
-- threat
ix.act.Register("Threat", "metrocop", {
sequence = {"plazathreat1", "plazathreat2"}
})
-- search
ix.act.Register("Search", "metrocop", {
sequence = "spreadwall",
})
-- deny
ix.act.Register("Deny", "metrocop", {
sequence = {"harassfront2", "harassfront1"}
})
-- motion
ix.act.Register("Motion", "metrocop", {
sequence = {"motionleft", "motionright", "luggagewarn"}
})
-- wave
ix.act.Register("Wave", {"citizen_male", "citizen_female"}, {
sequence = {{"wave", duration = 2.75}, {"wave_close", duration = 1.75}}
})
-- pant
ix.act.Register("Pant", {"citizen_male", "citizen_female"}, {
start = {"d2_coast03_postbattle_idle02_entry", "d2_coast03_postbattle_idle01_entry"},
sequence = {"d2_coast03_postbattle_idle02", {"d2_coast03_postbattle_idle01", check = FacingWall}},
untimed = true
})
-- window
ix.act.Register("Window", {"citizen_male", "metrocop"}, {
sequence = "d1_t03_tenements_look_out_window_idle",
untimed = true
})
ix.act.Register("Window", {"citizen_female", "metrocop_female"}, {
sequence = "d1_t03_lookoutwindow",
untimed = true
})
-- down
ix.act.Register("Down", "metrocop", {
sequence = {"n7_male_down01", "n7_male_down02", "n7_male_down03"},
untimed = true,
idle = true
})
ix.act.Register("Down", "citizen_male", {
sequence = {"willard_male_male_down01", "willard_male_male_down02", "willard_male_male_down03"},
untimed = true,
idle = true
})
ix.act.Register("Down", "metrocop_female", {
sequence = {"n7_female_down01", "n7_female_down02", "n7_female_down03"},
untimed = true,
idle = true
})
ix.act.Register("Down", "citizen_female", {
sequence = {"willard_female_down01", "willard_female_down02", "willard_female_down03"},
untimed = true,
idle = true
})
-- Check
ix.act.Register("Check", "metrocop", {
sequence = {"n7_male_check"},
untimed = true
})
ix.act.Register("Check", "citizen_male", {
sequence = {"willard_male_male_check"},
untimed = true
})
ix.act.Register("Check", "metrocop_female", {
sequence = {"n7_female_check"},
untimed = true
})
ix.act.Register("Check", "citizen_female", {
sequence = {"willard_female_check"},
untimed = true
})
-- Knock
ix.act.Register("KnockIdle", "metrocop", {
sequence = {"adoorcidle"},
untimed = true
})
ix.act.Register("Knock", "metrocop", {
sequence = {"adoorknock"},
untimed = true
})
ix.act.Register("Knock", "citizen_male", {
sequence = {"d1_town05_Leon_Door_Knock"}
})
end

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 = "Ammo Saver"
PLUGIN.author = "Black Tea"
PLUGIN.description = "Saves the ammo of a character."
PLUGIN.ammoList = {}
ix.ammo = ix.ammo or {}
function ix.ammo.Register(name)
name = name:lower()
if (!table.HasValue(PLUGIN.ammoList, name)) then
PLUGIN.ammoList[#PLUGIN.ammoList + 1] = name
end
end
-- Register Default HL2 Ammunition.
ix.ammo.Register("ar2")
ix.ammo.Register("pistol")
ix.ammo.Register("357")
ix.ammo.Register("smg1")
ix.ammo.Register("xbowbolt")
ix.ammo.Register("buckshot")
ix.ammo.Register("rpg_round")
ix.ammo.Register("smg1_grenade")
ix.ammo.Register("grenade")
ix.ammo.Register("ar2altfire")
ix.ammo.Register("slam")
-- Register Cut HL2 Ammunition.
ix.ammo.Register("alyxgun")
ix.ammo.Register("sniperround")
ix.ammo.Register("sniperpenetratedround")
ix.ammo.Register("thumper")
ix.ammo.Register("gravity")
ix.ammo.Register("battery")
ix.ammo.Register("gaussenergy")
ix.ammo.Register("combinecannon")
ix.ammo.Register("airboatgun")
ix.ammo.Register("striderminigun")
ix.ammo.Register("helicoptergun")
-- Called right before the character has its information save.
function PLUGIN:CharacterPreSave(character)
-- Get the player from the character.
local client = character:GetPlayer()
-- Check to see if we can get the player's ammo.
if (IsValid(client)) then
local ammoTable = {}
for _, v in ipairs(self.ammoList) do
local ammo = client:GetAmmoCount(v)
if (ammo > 0) then
ammoTable[v] = ammo
end
end
character:SetData("ammo", ammoTable)
end
end
-- Called after the player's loadout has been set.
function PLUGIN:PlayerLoadedCharacter(client)
timer.Simple(0.25, function()
if (!IsValid(client)) then
return
end
-- Get the saved ammo table from the character data.
local character = client:GetCharacter()
if (!character) then
return
end
local ammoTable = character:GetData("ammo")
-- Check if the ammotable is exists.
if (ammoTable) then
for k, v in pairs(ammoTable) do
client:SetAmmo(v, tostring(k))
end
end
end)
end

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("Area")
self.text = ""
self.tickSound = "ui/buttonrollover.wav"
self.tickSoundRange = {190, 200}
self.backgroundAlpha = 255
self.expireTime = 8
self.animationTime = 2
self.character = 1
self.createTime = RealTime()
self.currentAlpha = 255
self.currentHeight = 0
self.nextThink = RealTime()
end
function PANEL:Show()
self:CreateAnimation(0.5, {
index = -1,
target = {currentHeight = self.label:GetTall() + 8},
easing = "outQuint",
Think = function(animation, panel)
panel:SetTall(panel.currentHeight)
end
})
end
function PANEL:SetFont(font)
self.label:SetFont(font)
end
function PANEL:SetText(text)
if (text:sub(1, 1) == "@") then
text = L(text:sub(2))
end
self.label:SetText(text)
self.text = text
self.character = 1
end
function PANEL:Think()
local time = RealTime()
if (time >= self.nextThink) then
if (self.character < self.text:utf8len()) then
self.character = self.character + 1
self.label:SetText(string.utf8sub(self.text, 1, self.character))
LocalPlayer():EmitSound(self.tickSound, 100, math.random(self.tickSoundRange[1], self.tickSoundRange[2]))
end
if (time >= self.createTime + self.expireTime and !self.bRemoving) then
self:Remove()
end
self.nextThink = time + 0.05
end
end
function PANEL:SizeToContents()
self:SetWide(self:GetParent():GetWide())
self.label:SetWide(self:GetWide())
self.label:SizeToContentsY()
end
function PANEL:Paint(width, height)
self.backgroundAlpha = math.max(self.backgroundAlpha - 200 * FrameTime(), 0)
derma.SkinFunc("PaintAreaEntry", self, width, height)
end
function PANEL:Remove()
if (self.bRemoving) then
return
end
self:CreateAnimation(self.animationTime, {
target = {currentAlpha = 0},
Think = function(animation, panel)
panel:SetAlpha(panel.currentAlpha)
end,
OnComplete = function(animation, panel)
panel:CreateAnimation(0.5, {
index = -1,
target = {currentHeight = 0},
easing = "outQuint",
Think = function(_, sizePanel)
sizePanel:SetTall(sizePanel.currentHeight)
end,
OnComplete = function(_, sizePanel)
sizePanel:OnRemove()
BaseClass.Remove(sizePanel)
end
})
end
})
self.bRemoving = true
end
function PANEL:OnRemove()
end
vgui.Register("ixAreaEntry", PANEL, "Panel")
-- main panel
PANEL = {}
function PANEL:Init()
local chatWidth, _ = chat.GetChatBoxSize()
local _, chatY = chat.GetChatBoxPos()
self:SetSize(chatWidth, chatY)
self:SetPos(32, 0)
self:ParentToHUD()
self.entries = {}
ix.gui.area = self
end
function PANEL:AddEntry(entry, color)
color = color or ix.config.Get("color")
local id = #self.entries + 1
local panel = entry
if (isstring(entry)) then
panel = self:Add("ixAreaEntry")
panel:SetText(entry)
end
panel:SetBackgroundColor(color)
panel:SizeToContents()
panel:Dock(BOTTOM)
panel:Show()
panel.OnRemove = function()
for k, v in pairs(self.entries) do
if (v == panel) then
table.remove(self.entries, k)
break
end
end
end
self.entries[id] = panel
return id
end
function PANEL:GetEntries()
return self.entries
end
vgui.Register("ixArea", PANEL, "Panel")

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 = "Area",
areas = "Areas",
areaEditMode = "Area Edit Mode",
areaNew = "New Area",
areaAlreadyExists = "An area with this name already exists!",
areaDoesntExist = "An area with that name doesn't exist!",
areaInvalidType = "You have specified an invalid area type!",
areaEditTip = "Click to start creating an area. Right click to exit.",
areaFinishTip = "Click again to finish drawing the area. Right click to go back.",
areaRemoveTip = "Press reload to remove the area you're currently in.",
areaDeleteConfirm = "Are you sure you want to delete the area \"%s\"?",
areaDelete = "Delete Area",
cmdAreaEdit = "Enters area edit mode.",
optShowAreaNotices = "Show Area Notices",
optdShowAreaNotices = "Whether a notification should appear on the screen when you enter a new area."
}

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, "How many seconds between each time a character's current area is calculated.",
function(oldValue, newValue)
if (SERVER) then
timer.Remove("ixAreaThink")
timer.Create("ixAreaThink", newValue, 0, function()
PLUGIN:AreaThink()
end)
end
end,
{
data = {min = 0.1, max = 4},
category = "areas"
}
)
ix.option.Add("showAreaNotices", ix.type.bool, true, {
category = "appearance",
})
function ix.area.AddProperty(name, type, default, data)
ix.area.properties[name] = {
type = type,
default = default
}
end
function ix.area.AddType(type, name)
name = name or type
-- only store localized strings on the client
ix.area.types[type] = CLIENT and name or true
end
function PLUGIN:SetupAreaProperties()
ix.area.AddType("area")
ix.area.AddProperty("color", ix.type.color, ix.config.Get("color"))
ix.area.AddProperty("display", ix.type.bool, true)
end
ix.util.Include("sv_plugin.lua")
ix.util.Include("cl_plugin.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("cl_hooks.lua")
-- return world center, local min, and local max from world start/end positions
function PLUGIN:GetLocalAreaPosition(startPosition, endPosition)
local center = LerpVector(0.5, startPosition, endPosition)
local min = WorldToLocal(startPosition, angle_zero, center, angle_zero)
local max = WorldToLocal(endPosition, angle_zero, center, angle_zero)
return center, min, max
end
do
local COMMAND = {}
COMMAND.description = "@cmdAreaEdit"
COMMAND.adminOnly = true
function COMMAND:OnRun(client)
client:SetWepRaised(false)
net.Start("ixAreaEditStart")
net.Send(client)
end
ix.command.Add("AreaEdit", COMMAND)
end
do
local PLAYER = FindMetaTable("Player")
-- returns the current area the player is in, or the last valid one if the player is not in an area
function PLAYER:GetArea()
return self.ixArea
end
-- returns true if the player is in any area, this does not use the last valid area like GetArea does
function PLAYER:IsInArea()
return self.ixInArea
end
end

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 has added area \"%s\".", client:Name(), tostring(name))
end)
ix.log.AddType("areaRemove", function(client, name)
return string.format("%s has removed area \"%s\".", client:Name(), tostring(name))
end)
local function SortVector(first, second)
return Vector(math.min(first.x, second.x), math.min(first.y, second.y), math.min(first.z, second.z)),
Vector(math.max(first.x, second.x), math.max(first.y, second.y), math.max(first.z, second.z))
end
function ix.area.Create(name, type, startPosition, endPosition, bNoReplicate, properties)
local min, max = SortVector(startPosition, endPosition)
ix.area.stored[name] = {
type = type or "area",
startPosition = min,
endPosition = max,
bNoReplicate = bNoReplicate,
properties = properties
}
-- network to clients if needed
if (!bNoReplicate) then
net.Start("ixAreaAdd")
net.WriteString(name)
net.WriteString(type)
net.WriteVector(startPosition)
net.WriteVector(endPosition)
net.WriteTable(properties)
net.Broadcast()
end
end
function ix.area.Remove(name, bNoReplicate)
ix.area.stored[name] = nil
-- network to clients if needed
if (!bNoReplicate) then
net.Start("ixAreaRemove")
net.WriteString(name)
net.Broadcast()
end
end

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 = "Allows players to enable autowalk."
ix.option.Add("enableAutoWalkBind", ix.type.bool, true, {
bNetworked = true,
category = "General",
})
ix.lang.AddTable("english", {
optEnableAutoWalkBind = "Włącz bind automatycznego chodzenia",
optdEnableAutoWalkBind = "Włącza funkcję automatycznego chodzenia do przodu za pomocą przycisku 'n'.",
})
ix.lang.AddTable("polish", {
optEnableAutoWalkBind = "Włącz bind automatycznego chodzenia",
optdEnableAutoWalkBind = "Włącza funkcję automatycznego chodzenia do przodu za pomocą przycisku 'n'.",
})
PLUGIN.bind = KEY_N
local exitKeys = {
IN_FORWARD,
IN_BACK,
IN_MOVELEFT,
IN_MOVERIGHT
}
function PLUGIN:GetOption(client)
if (CLIENT) then
return ix.option.Get("enableAutoWalkBind")
else
return ix.option.Get(client, "enableAutoWalkBind")
end
end
function PLUGIN:SetupMove(client, moveData, cmdData)
if (!client.autowalk) then return end
if (ix.fights and client:GetFight()) then
client.autowalk = nil
return
end
moveData:SetForwardSpeed(moveData:GetMaxSpeed())
for _, v in pairs(exitKeys) do
if (cmdData:KeyDown(v)) then
client.autowalk = nil
end
end
end
function PLUGIN:PlayerButtonDown(client, button)
if (!self:GetOption(client)) then
client.autowalk = false
return
end
local curTime = CurTime()
if (button == self.bind and (!client.nextBind or client.nextBind <= curTime)) then
client.autowalk = !client.autowalk
client.nextBind = curTime + 0.1
end
end

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 = "Adds AWarn 3 integration."
if (!AWarn) then return end
if (SERVER) then
function PLUGIN:PlayerLoadedCharacter(client, character, lastChar)
client:SetNetVar("AWarnWarnings", client:GetActiveWarnings())
end
function PLUGIN:AWarnPlayerWarned(client, aID, reason)
client:SetNetVar("AWarnWarnings", client:GetActiveWarnings())
end
function PLUGIN:AWarnPlayerIDWarn(clientID, aID, reason)
for _, v in ipairs(player.GetAll()) do
if (v:SteamID64() == clientID) then
return self:AwarnPlayerWarned(v, aID, reason)
end
end
end
else
function PLUGIN:GetPlayerESPText(client, toDraw, distance, alphaFar, alphaMid, alphaClose)
if (client:GetNetVar("AWarnWarnings", 0) > 0) then
toDraw[#toDraw + 1] = {alpha = alphaClose, priority = 29.5, text = "Warnings: "..client:GetNetVar("AWarnWarnings", 0)}
end
end
end

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] Attempt to view an uninitialized inventory '"..index.."'\n")
end
end
return false
end,
OnCanRun = function(item)
if (CLIENT) and item.noOpen and
(!ix.gui.openedStorage or ix.gui.openedStorage and !IsValid(ix.gui.openedStorage)) then return false end
return !IsValid(item.entity) and item:GetData("id") and !IsValid(ix.gui["inv" .. item:GetData("id", "")])
end
}
-- Allows bags to be opened from the world.
ITEM.functions.ViewAlt = {
name = "Wyświetl",
OnRun = function(item)
local inventory = item:GetInventory()
if (inventory) then
ix.storage.Open(item.player, inventory, {
name = item.name,
entity = item.entity,
searchTime = 0
})
end
return false
end,
OnCanRun = function(item)
return IsValid(item.entity)
end
}
ITEM.functions.Equip = {
OnRun = function(item, data)
local owner = item:GetOwner()
if (owner) then
local index = owner:FindBodygroupByName(item.uniqueID == "largebag" and owner:GetActiveCombineSuit() and "cp_Bag" or item.bodygroup)
local groups = owner:GetCharacter():GetData("groups", {})
if (index > -1) then
groups[index] = 1
owner:GetCharacter():SetData("groups", groups)
owner:SetBodygroup(index, 1)
netstream.Start(owner, "ItemEquipBodygroups", index, 1)
end
item:SetData("equip", true)
end
return false
end,
OnCanRun = function(item, creationClient)
local client = item.player or creationClient
return !IsValid(item.entity) and IsValid(client) and item:GetData("equip") != true and hook.Run("CanPlayerEquipItem", client, item) != false and !item.noEquip
end
}
ITEM.functions.EquipUn = { -- sorry, for name order.
name = "Zdejmij",
tip = "unequipTip",
icon = "icon16/cross.png",
OnRun = function(item, creationClient)
local client = item.player or creationClient
if (client) then
local index = client:FindBodygroupByName(item.uniqueID == "largebag" and client:GetActiveCombineSuit() and "cp_Bag" or item.bodygroup)
local groups = client:GetCharacter():GetData("groups", {})
if (index > -1) then
groups[index] = 0
client:GetCharacter():SetData("groups", groups)
client:SetBodygroup(index, 0)
netstream.Start(client, "ItemEquipBodygroups", index, 0)
end
item:SetData("equip", false)
end
return false
end,
OnCanRun = function(item, creationClient)
local client = item.player or creationClient
return !IsValid(item.entity) and IsValid(client) and item:GetData("equip") == true and
hook.Run("CanPlayerUnequipItem", client, item) != false
end
}
-- Called when the item should tell whether or not it can be transfered between inventories.
-- Allows bags to be put inside containers.
function ITEM:CanTransfer(oldInventory, newInventory)
local index = self:GetData("id")
if (newInventory) then
if (newInventory.vars and newInventory.vars.isBag and !newInventory.vars.isContainer and !self.allowNesting) then
return false
end
local index2 = newInventory:GetID()
if (index == index2) then
return false
end
local curInv = self.GetInventory and self:GetInventory()
local curInvItems = curInv and curInv.GetItems and curInv:GetItems()
if curInvItems then
for _, v in pairs(curInvItems) do
if (v:GetData("id") == index2) then
return false
end
end
end
end
return !newInventory or newInventory:GetID() != oldInventory:GetID() or newInventory.vars.isBag
end
-- Called when a new instance of this item has been made.
function ITEM:OnInstanced(invID, x, y)
if (self.invBeingMade) then return end
self.invBeingMade = true
local inventory = ix.item.inventories[invID]
ix.inventory.New(inventory and inventory.owner or 0, self.uniqueID, function(inv)
local client = inv:GetOwner()
inv.vars.isBag = self.uniqueID
inv.vars.allowNesting = self.allowNesting
inv.vars.restriction = self.restriction
inv.vars.noEquipInv = true
self:SetData("id", inv:GetID())
if (IsValid(client)) then
inv:AddReceiver(client)
end
if (self.OnBagInitialized) then
self:OnBagInitialized(inv)
end
end)
end
-- Called when the item first appears for a client.
function ITEM:OnSendData()
local index = self:GetData("id")
if (index) then
local inventory = ix.item.inventories[index]
if (inventory) then
inventory.vars.isBag = self.uniqueID
inventory.vars.allowNesting = self.allowNesting
inventory.vars.restriction = self.restriction
inventory.vars.noEquipInv = true
inventory:Sync(self.player)
inventory:AddReceiver(self.player)
else
local owner = self.player:GetCharacter():GetID()
ix.inventory.Restore(self:GetData("id"), self.invWidth, self.invHeight, function(inv)
inv.vars.isBag = self.uniqueID
inv.vars.allowNesting = self.allowNesting
inv.vars.restriction = self.restriction
inv.vars.noEquipInv = true
inv:SetOwner(owner, true)
if (!inv.owner) then
return
end
for client, character in ix.util.GetCharacters() do
if (character:GetID() == inv.owner) then
inv:AddReceiver(client)
break
end
end
end)
end
else
if (self.invBeingMade) then return end
self.invBeingMade = true
ix.inventory.New(self.player:GetCharacter():GetID(), self.uniqueID, function(inv)
local client = inv:GetOwner()
inv.vars.isBag = self.uniqueID
inv.vars.allowNesting = self.allowNesting
inv.vars.restriction = self.restriction
inv.vars.noEquipInv = true
self:SetData("id", inv:GetID())
if (IsValid(client)) then
inv:AddReceiver(client)
end
if (self.OnBagInitialized) then
self:OnBagInitialized(inv)
end
end)
end
end

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 = "Ładownica na amunicję"
ITEM.description = "Torba, która może pomieścić sporą ilość pocisków."
ITEM.model = Model("models/willardnetworks/clothingitems/backpack.mdl")
ITEM.noOpen = false
ITEM.noEquip = true
ITEM.invWidth = 4
ITEM.invHeight = 2
ITEM.width = 2
ITEM.height = 1
ITEM.restriction = { -- ammo is base_stackable, and thats kinda too broad for this so gotta list it manually
"bullets_357",
"bullets_assaultrifle",
"bullets_buckshot",
"bullets_heavypulse",
"bullets_pistol",
"bullets_pulse",
"bullets_smg1",
"bullets_sniper"
}

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/
--]]
ITEM.name = "Duża torba"
ITEM.description = "Plecak z insygniami Kombinatu."
ITEM.invWidth = 4
ITEM.invHeight = 4
ITEM.bodygroup = "bag"
ITEM.model = Model("models/willardnetworks/clothingitems/backpack.mdl")
ITEM.outfitCategory = "Bag"
ITEM.noOpen = true
ITEM.hooks.View = function(item, data)
local client = item.player
local inventory = client:GetCharacter():GetInventory()
local items = inventory:GetItemsByUniqueID(item.uniqueID)
if (#items > 1) then
table.SortByMember(items, "id", true)
if (items[1].id != item.id) then
return false
end
end
end

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 = "Ładownica na magazynki"
ITEM.description = "Torba, do której można schować magazynki."
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
ITEM.noOpen = false
ITEM.noEquip = true
ITEM.invWidth = 4
ITEM.invHeight = 1
ITEM.restriction = {"base_arccwmag"}

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/
--]]
ITEM.name = "Torba medyczna"
ITEM.description = "Torba, w której można schować wszystkie przybory medyczne"
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
ITEM.noOpen = false
ITEM.noEquip = true
ITEM.invWidth = 2
ITEM.invHeight = 6
ITEM.restriction = {
"base_medical",
"drink_saline",
"comp_antidote",
"comp_bloodsyringe",
"comp_syringe",
"drug_amph_a",
"drug_amph_b",
}

View File

@@ -0,0 +1,19 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
ITEM.name = "Mała torba"
ITEM.description = "Mała saszetka, która spoczywa na biodrze."
ITEM.invWidth = 3
ITEM.invHeight = 4
ITEM.bodygroup = "satchel"
ITEM.model = Model("models/willardnetworks/clothingitems/satchel.mdl")
ITEM.outfitCategory = "Satchel"
ITEM.noOpen = true

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,25 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
PLUGIN.name = "Bag System"
PLUGIN.author = "Fruity & Aspect™"
PLUGIN.description = "A simple bag system."
ix.util.Include("sh_hooks.lua")
ix.util.Include("sv_plugin.lua")
ix.lang.AddTable("english", {
restrictedBag = "Ten ekwipunek nie może pomieścić tego typu przedmiotów!"
})
ix.lang.AddTable("polish", {
restrictedBag = "Ten ekwipunek nie może pomieścić tego typu przedmiotów!"
})

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,153 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local CAMI = CAMI
local LocalPlayer = LocalPlayer
local L = L
local SetClipboardText = SetClipboardText
local chat = chat
local net = net
local table = table
local string = string
local tonumber = tonumber
local render = render
local Color = Color
local gui = gui
local hook = hook
local surface = surface
local netstream = netstream
local ipairs = ipairs
local MsgN = MsgN
local ix = ix
local PLUGIN = PLUGIN
PLUGIN.infoIcon = ix.util.GetMaterial("icon16/information.png")
ix.option.Add("staffChat", ix.type.bool, true, {
category = "administration",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Hear Staff Chat", nil)
end
})
function PLUGIN:PopulateScoreboardPlayerMenu(client, menu)
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")) then
menu:AddOption(L("bastionCopySteamName"), function()
SetClipboardText(client:SteamName())
LocalPlayer():NotifyLocalized("bastionCopiedSteamName")
end)
menu:AddOption(L("bastionCopyCharName"), function()
SetClipboardText(client:Name())
LocalPlayer():NotifyLocalized("bastionCopiedCharName")
end)
end
if (sam and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands") and !LocalPlayer():InVehicle() and client != LocalPlayer()) then
menu:AddOption(L("bastionGoto"), function()
if (LocalPlayer():GetMoveType() != MOVETYPE_NOCLIP) then
LocalPlayer():ConCommand("noclip")
end
LocalPlayer():ConCommand("say !goto "..client:Name())
end)
end
end
function PLUGIN:PrintTarget(target)
if (ix.option.Get("pgi")) then
SetClipboardText(target:SteamID())
end
chat.AddText(self.infoIcon, target:Name(), " (", target:SteamName(), "; ", target:SteamID(),
") | HP: ", target:Health(), " | Armor: ", target:Armor())
end
function PLUGIN:PrintStaffList(amount)
for _ = 1, amount do
local group = net.ReadString()
local members = net.ReadUInt(8)
local memberList = {}
for _ = 1, members do
memberList[#memberList + 1] = net.ReadEntity():SteamName()
end
table.sort(memberList)
chat.AddText(self.infoIcon, "[", string.utf8upper(group), "]: ", table.concat(memberList, ", "))
end
end
function PLUGIN:ShouldDisplayArea(id)
if (LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP and !LocalPlayer():InVehicle()) then
return false
end
end
local commands = {
["playsound"] = 2,
["localevent"] = 2,
["showentsinradius"] = 1,
["localbroadcast"] = 2,
["localbroadcastme"] = 2,
["localbroadcastit"] = 2,
["playsong"] = 3,
["screenshake"] = 4,
["moviebars"] = 1,
["removepersistedprops"] = 1,
["removeclientprops"] = 1
}
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
local command = string.utf8lower(ix.chat.currentCommand)
if (commands[command]) then
local range = tonumber(ix.chat.currentArguments[commands[command]])
if (range) then
render.SetColorMaterial()
render.DrawSphere(LocalPlayer():GetPos(), 0 - range, 50, 50, Color(255, 150, 0, 100))
end
end
end
net.Receive("ixOpenURL", function(len)
gui.OpenURL(net.ReadString())
end)
net.Receive("ixPlayerInfo", function(len)
PLUGIN:PrintTarget(net.ReadEntity())
end)
net.Receive("ixStaffList", function(len)
PLUGIN:PrintStaffList(net.ReadUInt(8))
end)
net.Receive("ixPlaySound", function(len)
local sound = net.ReadString()
local isGlobal = net.ReadBool()
if (hook.Run("PrePlaySound", sound, isGlobal) != false) then
surface.PlaySound(sound)
hook.Run("PostPlaySound", sound, isGlobal)
end
end)
netstream.Hook("PrintInfoList", function(list)
for _, v in ipairs(list) do
MsgN(v)
end
end)
function PLUGIN:OnPlayerChat()
if (ix.config.Get("suppressOnPlayerChat", true)) then
return true
end
end

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,511 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local pcall = pcall
local util = util
local ix = ix
local string = string
local net = net
local ipairs = ipairs
local IsValid = IsValid
local os = os
local L = L
local math = math
local table = table
local pairs = pairs
local print = print
local CHTTP = CHTTP
local player = player
local CAMI = CAMI
if (!CHTTP) then
pcall(require, "chttp")
end
local PLUGIN = PLUGIN
PLUGIN.TYPE_UNKNOWN = 1
PLUGIN.TYPE_KNOWN = 2
local COOKIE_KEY = "qvFT4QSSST8K4tZF"
local fileChecks = {
{"hl2_misc_001.vpk", "hl2"},
{"ep2_pak_008.vpk", "ep2"},
{"cstrike_pak_003.vpk", "css", "cstrike"},
{"bin/client_panorama.dll", "csgo"},
{"detail.vbsp", "gmod", "garrysmod"}
}
util.AddNetworkString("RecieveDupe")
ix.util.Include("sv_antialt_db.lua")
ix.lang.AddTable("english", {
altSignupProxy = "Próbujesz dołączyć na Veles jako nowy gracz, korzystając z proxy lub VPN. Aby przeciwdziałać multikontom, nie zezwalamy na to.\n\n Prosimy o wyłączenie proxy/VPN i ponowne dołączenie na serwer. Po pomyślnym dołączeniu na serwer możesz ponownie włączyć proxy/VPN dla przyszłych sesji.\n\nJeśli nie korzystasz z proxy lub VPN, skontaktuj się z zarządem społeczności na discordzie."
})
ix.lang.AddTable("polish", {
altSignupProxy = "Próbujesz dołączyć na Veles jako nowy gracz, korzystając z proxy lub VPN. Aby przeciwdziałać multikontom, nie zezwalamy na to.\n\n Prosimy o wyłączenie proxy/VPN i ponowne dołączenie na serwer. Po pomyślnym dołączeniu na serwer możesz ponownie włączyć proxy/VPN dla przyszłych sesji.\n\nJeśli nie korzystasz z proxy lub VPN, skontaktuj się z zarządem społeczności na discordzie."
})
ix.log.AddType("altNewClient", function(client)
return string.format("[AltChecker] %s joined for the first time, generating new cookie.", client:SteamName())
end)
ix.log.AddType("altKnownNewCookie", function(client)
return string.format("[AltChecker] %s joined with unknown installation, generating new cookie.", client:SteamName())
end)
ix.log.AddType("altKnownCookieMatched", function(client, matched, total)
return string.format("[AltChecker] %s joined without a cookie, but matched installation with existing cookie. Certainty %d/%d", client:SteamName(), matched, total)
end)
hook.Add("PlayerInitialSpawn", "bastionAntiAlt", function(client)
if (client:IsBot()) then return end
client.ixAltData = {
checks = 4 + #fileChecks,
altLogging = 0,
error = false,
received = false,
checkComplete = false,
newAltFound = false,
mode = 0,
localCookie = "",
cookies = false,
ip = false,
timestamps = false,
otherCookies = {},
otherIPs = {},
otherTimestamps = {},
otherTimestampsNZ = {},
otherTimeMatches = {},
discordAlert = {
cookies = {},
timestamps = {},
ips = {},
altIDs = {}
}
}
-- Lookup user data
PLUGIN:AltLoadUserData(client)
if (PLUGIN.API_KEY) then
-- Lookup user IP
PLUGIN:AltLookupIP(client)
else
client.ixAltData.checkes = client.ixAltData.checks - 1
end
-- Check for IP matches
PLUGIN:AltLookupIPMatches(client)
-- Request cookie and install timestamps
PLUGIN:RequestClientData(client)
end)
function PLUGIN:RequestClientData(client)
net.Start("RecieveDupe")
net.WriteUInt(1, 3)
net.WriteString(COOKIE_KEY)
for _, v in ipairs(fileChecks) do
net.WriteString(v[1])
net.WriteString(v[3] or v[2])
end
net.Send(client)
end
net.Receive("RecieveDupe", function(len, client)
if (!IsValid(client) or !client.ixAltData or client.ixAltData.received) then return end
local data = client.ixAltData
data.received = true
-- set cookie
data.localCookie = net.ReadString()
-- set file timestamps
data.timestamps = {}
for i = 1, #fileChecks do
data.timestamps[i] = net.ReadUInt(32)
end
-- Check for cookie matches
if (data.localCookie != "") then
PLUGIN:AltLookupCookieMatches(client, data)
else
PLUGIN:AltPreFinalChecking(client)
end
-- Check for install timestamp matches
data.otherTimestamps = {}
data.otherTimestampsNZ = {}
for i = 1, #fileChecks do
PLUGIN:AltLookupTimestampMatches(client, data, fileChecks[i][2], data.timestamps[i])
end
end)
function PLUGIN:AltPreFinalChecking(client)
if (!IsValid(client)) then return end
local data = client.ixAltData
-- if we errored, don't do anything
-- check will be reexecuted when the client rejoins
if (data.error) then return end
-- check if all queries finished
data.checks = data.checks - 1
if (data.checks != 0) then return end
-- cookie matches (STRONG)
if (#data.otherCookies > 0) then
for _, v in ipairs(data.otherCookies) do
self:AltFound(client, v[2], "cookie", "cookie")
data.discordAlert.cookies[#data.discordAlert.cookies + 1] = (v[2] or "").." ("..(v[1] or "")..")"
data.discordAlert.altIDs[v[2]] = true
end
end
-- IP (WEAK)
if (#data.otherIPs > 0) then
for _, v in ipairs(data.otherIPs) do
data.discordAlert.ips[#data.discordAlert.ips + 1] = v[2]
data.discordAlert.altIDs[v[2]] = true
end
end
-- time matches (STRONG-MEDIUM)
self:AggregateTimestampMatches(data)
-- If no local cookie and player is known, check if a known cookie was time-matched
if (data.localCookie == "" and data.mode == self.TYPE_KNOWN) then
self:FindMatchingCookie(client, data)
end
if (#data.otherTimeMatches > 0) then
-- go looking for the other clients that own our matched timestamps
self:AltLookupCookieForTimestamps(client, data, #fileChecks)
else
-- else we don't need to wait for the lookup above
data.checkComplete = true
self:AltFinalChecking(client)
end
end
function PLUGIN:AltFinalChecking(client)
if (!IsValid(client)) then return end
local data = client.ixAltData
if (!data.checkComplete or data.altLogging != 0) then return end
self:DiscordAlert(client)
-- update IP list
local steamID = client:SteamID64()
local ip = self.GetIPAddress(client)
local query = mysql:Select("bastion_antialt_userips")
query:Where("steamid", steamID)
query:Where("ip", ip)
query:Callback(function(result)
if (!result or #result == 0) then
local query2 = mysql:Insert("bastion_antialt_userips")
query2:Insert("steamid", steamID)
query2:Insert("ip", ip)
query2:Insert("last_seen", os.time())
query2:Execute()
else
local query2 = mysql:Update("bastion_antialt_userips")
query2:Where("id", result[1].id)
query2:Update("last_seen", os.time())
query2:Execute()
end
end)
query:Execute()
-- Kick if new player on proxy/vpn
if (data.mode == self.TYPE_UNKNOWN) then
if (self.API_KEY and (data.ip.proxy == "yes" or (data.ip.risk or 0) > 60)) then
if (ix.config.Get("VPNKick")) then
self:ProxyAlert(client)
client:Kick(L("altSignupProxy", client))
else
if (!self:NotifyProxyJoin(client) or ix.config.Get("ProxyAlwaysAlert")) then
self:ProxyAlert(client)
end
end
elseif (data.localCookie == "") then
ix.log.Add(client, "altNewClient")
self:GenerateCookie(client, data)
else
-- Update the cookie's timestamps
self:StoreCookieInfo(data, fileChecks)
-- Add this cookie to client as well so it can be time matched/timestamps updated
self:StoreClientCookie(client, data)
end
elseif (data.localCookie == "") then
ix.log.Add(client, "altKnownNewCookie")
self:GenerateCookie(client, data)
else
-- Update the cookie's timestamps
self:StoreCookieInfo(data, fileChecks)
-- Add this cookie to client as well so it can be time matched/timestamps updated
self:StoreClientCookie(client, data)
end
if (sam) then
timer.Simple(3, function()
if (!IsValid(client)) then return end
local query1 = mysql:Select("bastion_antialt_alts")
query1:Where("steamid", client:SteamID64())
query1:Select("alt_id")
query1:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then return end
local query2 = mysql:Select("bastion_antialt_alts")
query2:Where("alt_id", result[1].alt_id)
query2:WhereNotEqual("steamid", client:SteamID64())
query2:Select("steamid")
query2:Callback(function(result2)
if (!IsValid(client)) then return end
if (!result2 or #result2 == 0) then return end
for k, v in ipairs(result2) do
sam.player.is_banned(util.SteamIDFrom64(v.steamid), function(banned)
if (!banned or !IsValid(client)) then return end
client:Kick("You have a banned alt account.")
return
end)
end
end)
query2:Execute()
end)
query1:Execute()
end)
end
end
--[[
COOKIE STUFF
]]
function PLUGIN:WhitelistPlayer(client)
local data = client.ixAltData
if (!data) then return end
if (data.localCookie) then
ix.log.Add(client, "altNewClient")
self:GenerateCookie(client, data)
return true
end
end
function PLUGIN.RandomString()
local result = {} -- The empty table we start with
while (#result != 64) do
local char = string.char(math.random(32, 126))
if (string.find(char, "%w")) then
result[#result + 1] = char
end
end
return table.concat(result)
end
function PLUGIN:GenerateCookie(client, data)
local cookie = self.RandomString()
self:UpdateLocalCookie(client, data, cookie)
local query = mysql:Insert("bastion_antialt_users")
query:Insert("steamid", client:SteamID64())
query:Insert("steam_name", client:SteamName())
query:Insert("cookie", cookie)
query:Execute()
end
function PLUGIN:UpdateLocalCookie(client, data, cookie)
data.localCookie = cookie
net.Start("RecieveDupe")
net.WriteUInt(2, 3)
net.WriteString(COOKIE_KEY)
net.WriteString(cookie)
net.Send(client)
self:StoreCookieInfo(data, fileChecks)
end
function PLUGIN:FindMatchingCookie(client, data)
for _, v in ipairs(data.otherTimeMatches) do
for _, v1 in ipairs(data.cookies) do
if (v1.cookie == v[1]) then
-- found a timestamp match belonging to the client, restore cookie
-- in case of e.g. gmod reinstall
ix.log.Add(client, "altKnownCookieMatched", v[2], #fileChecks)
self:UpdateLocalCookie(client, data, v[1])
return
end
end
end
end
--[[
LOGGING AND ALERTING
]]
local ALT_OFFSET = 0
function PLUGIN:AltFound(client, steamid, type, info)
local ids = {client:SteamID64(), steamid}
local data = client.ixAltData
data.altLogging = data.altLogging + 1
local query = mysql:Select("bastion_antialt_alts")
query:WhereIn("steamid", ids)
query:Callback(function(result)
if (!result or #result == 0) then
local alt_id = os.time() + ALT_OFFSET
ALT_OFFSET = ALT_OFFSET + 1
local text = table.concat(ids,"+")..": "..info.." (alt-id: "..alt_id..")"
self:InsertNewAlt(ids[1], alt_id, type, text)
self:InsertNewAlt(steamid, alt_id, type, text)
data.newAltFound = true
elseif (#result == 1) then
self:InsertNewAlt(
result[1].steamid == steamid and ids[1] or steamid,
result[1].alt_id,
type,
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
)
data.newAltFound = true
elseif (result[2].alt_id != result[1].alt_id) then
self:MergeAlts(
result[2].alt_id,
result[1].alt_id,
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
)
data.newAltFound = true
end
data.altLogging = data.altLogging - 1
self:AltFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:DiscordAlert(client)
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
local data = client.ixAltData
if (!data.newAltFound) then return end
local tbl = {
embeds = {{
title = "Alt found for "..client:SteamName(),
description = "An alt account match was found for **"..client:SteamName().."** (*"..client:SteamID64().."*)\n\n__COOKIE__: 99.99% certainty via installed cookie\nShown as 'SteamID64 (SteamName)'\n__TIMESTAMP__: check via installation date/time or absense of mounted games (hl2,ep2,css,csgo,gmod)\nMore matches = more certainty, especially if all/most are installed\nShown as 'SteamID64 (SteamName; date/time matches - installed matches)'\n__IP__: users that connected from the same IP address at any point\nShown as 'SteamID64'",
color = 13632027,
timestamp = os.date("%Y-%m-%d %X%z"),
footer = {
text = "Bastion Alt Checker for GMod by Gr4Ss"
},
author = {
name = "Bastion Alt Checker"
},
fields = {
}
}}
}
if (data.discordAlert.cookies and #data.discordAlert.cookies > 0) then
table.insert(tbl.embeds[1].fields, {name = "COOKIE", value = table.concat(data.discordAlert.cookies, "\n")})
end
if (data.discordAlert.timestamps and #data.discordAlert.timestamps > 0) then
table.insert(tbl.embeds[1].fields, {name = "TIMESTAMP", value = table.concat(data.discordAlert.timestamps, "\n")})
end
if (data.discordAlert.ips and #data.discordAlert.ips > 0) then
table.insert(tbl.embeds[1].fields, {
name = "IP",
value = string.format("Address: [%s](https://proxycheck.io/threats/%s)\n", data.ipAddress, data.ipAddress)..
table.concat(data.discordAlert.ips, "\n")
})
end
local ipLinks = {"["..client:SteamID64().."](https://steamidfinder.com/lookup/"..client:SteamID64().."/)"}
for k in pairs(data.discordAlert.altIDs) do
ipLinks[#ipLinks + 1] = "["..k.."](https://steamidfinder.com/lookup/"..k.."/)"
end
table.insert(tbl.embeds[1].fields, {
name = "SteamID Finder Links",
value = table.concat(ipLinks,"\n")
})
for _, field in ipairs(tbl.embeds[1].fields) do
if (string.len(field.value) > 1024) then
field.value = string.sub(field.value, 1, 1024)
end
end
local request = {
failed = function(error) print("discord error", error) end,
success = function(code, body, headers)
if (code != 200) then print("discord error", code, body) end
end,
method = "post",
url = self.DISCORD_WEBHOOK_ALTS,
body = util.TableToJSON(tbl),
type = "application/json; charset=utf-8"
}
CHTTP(request)
end
function PLUGIN:ProxyAlert(client)
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
local ip, steamID = client.ixAltData.ipAddress, client:SteamID64()
local tbl = {
embeds = {{
title = "New player using VPN - "..client:SteamName(),
description = client:SteamName().." joined WN for the first time, but was using a proxy/VPN. They have been kicked.\n\n"..
string.format("More info: [%s](https://proxycheck.io/threats/%s) & [%s](https://steamidfinder.com/lookup/%s/)", ip, ip, steamID, steamID),
color = 16312092,
timestamp = os.date("%Y-%m-%d %X%z"),
footer = {
text = "Bastion Alt Checker for GMod by Gr4Ss"
},
author = {
name = "Bastion Alt Checker"
}
}}
}
local request = {
failed = function(error) print("discord error", error) end,
method = "post",
url = self.DISCORD_WEBHOOK_ALTS,
body = util.TableToJSON(tbl),
type = "application/json; charset=utf-8"
}
CHTTP(request)
end
function PLUGIN:NotifyProxyJoin(client)
local bSend = false
for _, v in ipairs(player.GetAll()) do
if (CAMI.PlayerHasAccess(v, "Helix - Proxy Notify")) then
bSend = true
v:NotifyLocalized("bastionProxyNotify", client:SteamName())
end
end
return bSend
end

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..") tried to connect but is hard-banned.")
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,201 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ix = ix
local Color = Color
local chat = chat
local string = string
local IsValid = IsValid
local LocalPlayer = LocalPlayer
local MsgC = MsgC
local CAMI = CAMI
local surface = surface
local team = team
-- luacheck: globals FACTION_SERVERADMIN
-- Roll information in chat.
ix.chat.Register("gmroll", {
format = "** You have gm-rolled %s out of %s.",
color = Color(155, 111, 176),
CanHear = function(self, speaker, listener)
return speaker == listener
end,
deadCanChat = true,
OnChatAdd = function(self, speaker, text, bAnonymous, data)
chat.AddText(self.color, string.format(self.format, text, data.max or 20))
end
})
ix.chat.Register("localevent", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
OnChatAdd = function(self, speaker, text)
chat.AddText(Color(255, 150, 0), text)
end,
})
local broadcastIcon = ix.util.GetMaterial("willardnetworks/chat/broadcast_icon.png")
ix.chat.Register("localbroadcast", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
else
chat.AddText(Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
end
end
})
ix.chat.Register("localbroadcastme", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
else
chat.AddText(Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
end
end
})
ix.chat.Register("localbroadcastit", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("***' %s", text))
else
chat.AddText(Color(151, 161, 255), string.format("***' %s", text))
end
end
})
ix.chat.Register("announcement", {
OnChatAdd = function(self, speaker, text)
chat.AddText(Color(254, 238, 60), "[ADMIN] ", text)
end,
CanSay = function(self, speaker, text)
return true
end
})
-- STAFF CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/medal_gold_3.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
if (speaker != LocalPlayer() and !ix.option.Get("staffChat")) then
local character = LocalPlayer():GetCharacter()
if (character and character:GetFaction() != FACTION_SERVERADMIN) then
MsgC(Color(255,215,0), "[Staff] ",
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
Color(255, 255, 255), text.."\n")
return
end
end
chat.AddText(icon, Color(255,215,0), "[Staff] ",
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear Staff Chat")
end
end
ix.chat.Register("staff_chat", CLASS)
end
-- GM CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/rosette.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
chat.AddText(icon, Color(142, 28, 255), "[GM] ", Color(255, 215, 0, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear GM Chat")
end
end
ix.chat.Register("gm_chat", CLASS)
end
-- MENTOR CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/user_suit.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
chat.AddText(icon, Color(66, 135, 245), "[Mentor] ", Color(66, 245, 191, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear Mentor Chat")
end
end
ix.chat.Register("mentor_chat", CLASS)
end
-- ACHIEVEMENT
do
local CLASS = {}
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(data[1])) then return end
if (data[2]) then
surface.PlaySound(data[2])
end
local target = data[1]
chat.AddText(team.GetColor(target:Team()), target:SteamName(), Color(255, 255, 255), " earned the achievement ",
Color( 255, 201, 0, 255 ), text)
end
end
ix.chat.Register("achievement_get", CLASS)
end

View File

@@ -0,0 +1,662 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local bit = bit
local IsValid = IsValid
local pairs = pairs
local table = table
local L = L
local netstream = netstream
local Entity = Entity
local print = print
local math = math
local tostring = tostring
local CAMI = CAMI
local Vector = Vector
local game = game
local RunConsoleCommand = RunConsoleCommand
local net = net
local player = player
local ipairs = ipairs
local ents = ents
local string = string
local timer = timer
local ix = ix
local PLUGIN = PLUGIN
--Links
ix.command.Add("Discord", {
description = "Get a link to the Discord Server",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("DiscordLink"))
net.Send(client)
end,
bNoIndicator = true
})
ix.command.Add("Content", {
description = "Get a link to theWorkshop Content Pack",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("ContentLink"))
net.Send(client)
end
})
ix.command.Add("Forum", {
description = "Get a link to the Forums",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("ForumLink"))
net.Send(client)
end,
bNoIndicator = true
})
--Basic Admin
ix.command.Add("PGI", {
description = "Get someone's basic information and copy their SteamID.",
arguments = {
bit.bor(ix.type.player, ix.type.optional)
},
--alias = "PlyGetInfo",
privilege = "Basic Admin Commands",
OnRun = function(self, client, target)
if (!target) then
target = client:GetEyeTraceNoCursor().Entity
end
if (!IsValid(target) or !target:IsPlayer()) then
client:NotifyLocalized("bastionPGIInvalidTarget")
return
end
net.Start("ixPlayerInfo")
net.WriteEntity(target)
net.Send(client)
end,
bNoIndicator = true
})
ix.command.Add("PrintStaffList", {
alias = "PSL",
description = "Print a list of staff members online.",
OnRun = function(self, client)
local staff = {}
local bFound = false
for _, v in ipairs(player.GetAll()) do
local incognitoSetting = ix.option.Get(v, "iconIncognitoMode", false)
local iconIncognitoMode = !client:IsAdmin() and !client:IsSuperAdmin() and incognitoSetting
if (v:IsAdmin() or v:IsSuperAdmin()) and !iconIncognitoMode then
local userGroup = v:GetUserGroup()
staff[userGroup] = staff[userGroup] or {}
staff[userGroup][#staff[userGroup] + 1] = v
bFound = true
end
end
local toSend = {}
for k, v in pairs(staff) do
toSend[#toSend + 1] = {name = k, players = v}
end
table.SortByMember(toSend, "name", true)
if (bFound) then
net.Start("ixStaffList")
net.WriteUInt(#toSend, 8)
for _, v in ipairs(toSend) do
net.WriteString(v.name)
net.WriteUInt(#v.players, 8)
for i = 1, #v.players do
net.WriteEntity(v.players[i])
end
end
net.Send(client)
else
client:Notify("There are no staff online currently.")
end
end,
bNoIndicator = true
})
ix.command.Add("PrintFactionList", {
alias = "PFL",
description = "Print a list of members of a faction currently online (including on another character).",
arguments = {
ix.type.string
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, name)
if (name == "") then
return "@invalidArg", 2
end
local faction = ix.faction.teams[name]
if (!faction) then
for _, v in ipairs(ix.faction.indices) do
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
faction = v
break
end
end
end
if (faction) then
local players = {}
for _, v in ipairs(player.GetAll()) do
if (v:HasWhitelist(faction.index)) then
players[#players + 1] = v
end
end
net.Start("ixStaffList")
net.WriteUInt(1, 8)
net.WriteString(faction.name)
net.WriteUInt(#players, 8)
for i = 1, #players do
net.WriteEntity(players[i])
end
net.Send(client)
else
return "@invalidFaction"
end
end,
bNoIndicator = true
})
ix.command.Add("PlaySound", {
description = "Play a sound for all players (when no range is given) or those near you.",
arguments = {
ix.type.string,
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, sound, range)
local targets = range and {} or player.GetAll()
if (range) then
range = range * range
local clientPos = client:EyePos()
for _, target in ipairs(player.GetAll()) do
if (target:EyePos():DistToSqr(clientPos) < range) then
targets[#targets + 1] = target
end
end
end
net.Start("ixPlaySound")
net.WriteString(PLUGIN.soundAlias[sound] or sound)
net.Send(targets)
end,
indicator = "chatPerforming"
})
ix.command.Add("ScreenShake", {
description = "Shakes the screen of everyone in the specified range. Specify the amplitude, the frequency and the radius for it to work.",
arguments = {
ix.type.number,
ix.type.number,
ix.type.number,
ix.type.number
},
argumentNames = {"time(seconds)", "amplitude", "frequency", "radius"},
privilege = "Basic Admin Commands",
OnRun = function(self, client, seconds, strength, frequency, radius)
local vector = client:GetPos()
util.ScreenShake(vector, strength or 5, frequency or 5, seconds, radius, true)
end,
indicator = "chatPerforming"
})
ix.command.Add("PlaySoundGlobal", {
description = "Play a sound for all players.",
arguments = {
ix.type.string,
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, sound)
net.Start("ixPlaySound")
net.WriteString(PLUGIN.soundAlias[sound] or sound)
net.WriteBool(true)
net.Send(player.GetAll())
end,
indicator = "chatPerforming"
})
ix.command.Add("ShowEdicts", {
description = "Returns the amount of networked entities currently on the server.",
privilege = "Basic Admin Commands",
OnRun = function(self, client)
local edictsCount = ents.GetEdictCount()
local edictsLeft = 8192 - edictsCount
return string.format("There are currently %s edicts on the server. You can have up to %s more.", edictsCount, edictsLeft)
end,
bNoIndicator = true
})
ix.command.Add("ShowEntsInRadius", {
description = "Shows a list of entities within a given radius.",
privilege = "Basic Admin Commands",
arguments = {ix.type.number},
OnRun = function(self, client, radius)
local entities = {}
local pos = client:GetPos()
for _, v in pairs(ents.FindInSphere(pos, radius)) do
if (!IsValid(v)) then continue end
entities[#entities + 1] = table.concat({v:EntIndex(), v:GetClass(), v:GetModel() or "no model", v:GetPos():Distance(pos), v:MapCreationID()}, ", ")
end
netstream.Start(client, "ixShowEntsInRadius", table.concat(entities,"\n"))
client:NotifyLocalized("entsPrintedInConsole")
end,
bNoIndicator = true,
})
ix.command.Add("RemoveEntityByID", {
description = "Shows a list of entities within a given radius.",
superAdminOnly = true,
arguments = {ix.type.number},
OnRun = function(self, client, number)
local entity = Entity(number)
if (IsValid(entity)) then
client:NotifyLocalized("entityRemoved", number, entity:GetClass())
entity:Remove()
else
client:NotifyLocalized("entityNotFound", number)
end
end,
bNoIndicator = true,
})
if (CLIENT) then
netstream.Hook("ixShowEntsInRadius", function(text)
print(text)
end)
end
ix.command.Add("LocalEvent", {
description = "@cmdLocalEvent",
privilege = "Event",
arguments = {
ix.type.string,
bit.bor(ix.type.number, ix.type.optional)
},
OnRun = function(self, client, event, range)
local _range = range or (ix.config.Get("chatRange", 280) * 2)
ix.chat.classes.localevent.range = _range * _range
local doubleCommand = false
-- This could probably be done a bit smarter but I'm sure it'll do.
if (string.Left(string.lower(event), 11) == "/localevent") then
doubleCommand = true
event = string.Right(event, #event - 12)
elseif (string.Left(string.lower(event), 10) == "localevent") then
doubleCommand = true
event = string.Right(event, #event - 11)
end
if (doubleCommand) then
client:NotifyLocalized("textDoubleCommand", "/LocalEvent")
end
ix.chat.Send(client, "localevent", event)
end,
indicator = "chatPerforming"
})
ix.command.Add("RemovePersistedProps", {
description = "@cmdRemovePersistedProps",
superAdminOnly = true,
arguments = {
ix.type.number
},
OnRun = function(self, client, radius)
if (radius < 0) then
client:Notify("Radius must be a positive number!")
return
end
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), radius)) do
if (!entity:GetNetVar("Persistent")) then continue end
entity:Remove()
end
client:Notify("Removed all persisted props in a radius of " .. radius .. " units.")
end
})
ix.command.Add("Announce", {
description = "@cmdAnnounce",
arguments = {
ix.type.text
},
OnRun = function(self, client, event)
ix.chat.Send(client, "announcement", event)
end,
OnCheckAccess = function(self, client)
return !client or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands")
end,
indicator = "chatTyping"
})
ix.command.Add("GmRoll", {
description = "Do a roll that only you can see.",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, maximum)
maximum = math.Clamp(maximum or 20, 0, 1000000)
local value = math.random(0, maximum)
ix.chat.Send(client, "gmroll", tostring(value), nil, nil, {
max = maximum
})
ix.log.Add(client, "roll", value, maximum)
end,
bNoIndicator = true
})
-- Help
ix.command.Add("Help", {
description = "@cmdStaffHelp",
arguments = ix.type.text,
alias = {"GMHelp", "ModHelp"},
OnRun = function(self, client, text)
client:Say("@ " .. text)
end,
OnCheckAccess = function(self, client)
return !CAMI.PlayerHasAccess(client, "Helix - Hear Staff Chat")
end,
indicator = "chatTyping"
})
-- Admin Chat
ix.command.Add("Staff", {
description = "Chat with other staff members.",
arguments = ix.type.text,
privilege = "Hear Staff Chat",
alias = {"Op", "Mod", "ModChat"},
OnRun = function(self, client, text)
ix.chat.Send(client, "staff_chat", text)
end,
indicator = "chatTyping"
})
-- Gamemaster Chat
ix.command.Add("GameMaster", {
description = "Chat with other Gamemasters.",
arguments = ix.type.text,
privilege = "Hear GM Chat",
alias = {"GMChat", "GM"},
OnRun = function(self, client, text)
ix.chat.Send(client, "gm_chat", text)
end,
indicator = "chatTyping"
})
-- Mentor Chat
ix.command.Add("Mentor", {
description = "Chat with other Mentors.",
arguments = ix.type.text,
privilege = "Hear Mentor Chat",
alias = {"MChat", "M"},
OnRun = function(self, client, text)
ix.chat.Send(client, "mentor_chat", text)
end,
indicator = "chatTyping"
})
-- Fun Stuff
ix.command.Add("Achievement", {
description = "Someone has earned a special achievement!",
arguments = {
ix.type.player,
ix.type.text
},
privilege = "Fun Stuff",
OnRun = function(self, client, target, text)
ix.chat.Send(client, "achievement_get", text, false, nil,
{target, "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav"})
end,
indicator = "chatTyping"
})
ix.command.Add("DarwinAward", {
description = "Someone has earned an achievement: he has made the ultimate sacrifice to increase humanity's average IQ.",
arguments = {
ix.type.player
},
privilege = "Fun Stuff",
OnRun = function(self, client, target)
if (!target:Alive()) then
local pos = target:GetPos()
target:Spawn()
target:SetPos(pos)
end
target:SetMoveType(MOVETYPE_WALK)
target:SetVelocity(Vector(0, 0, 4000))
timer.Simple(1, function() PLUGIN:Explode(target) end)
ix.chat.Send(client, "achievement_get", "DARWIN AWARD", false, nil,
{target, "ambient/alarms/razortrain_horn1.wav"})
end,
indicator = "chatTyping"
})
ix.command.Add("PlyRocket", {
description = "To infinity, and beyond!.",
arguments = {
ix.type.player
},
privilege = "Fun Stuff",
OnRun = function(self, client, target)
if (!target:Alive()) then
local pos = target:GetPos()
target:Spawn()
target:SetPos(pos)
end
target:SetMoveType(MOVETYPE_WALK)
target:SetVelocity(Vector(0, 0, 4000))
timer.Simple(1, function() PLUGIN:Explode(target) end)
end,
bNoIndicator = true
})
ix.command.Add("SetTimeScale", {
description = "@cmdTimeScale",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Fun Stuff",
OnRun = function(self, client, number)
local scale = math.Clamp(number or 1, 0.001, 5)
game.SetTimeScale(scale)
for _, v in ipairs(player.GetAll()) do
if (self:OnCheckAccess(v)) then
v:NotifyLocalized("bastionTimeScale", client:Name(), scale)
end
end
end,
bNoIndicator = true
})
ix.command.Add("SetGravity", {
description = "@cmdGravity",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Fun Stuff",
OnRun = function(self, client, number)
RunConsoleCommand("sv_gravity", number)
for _, v in ipairs(player.GetAll()) do
if (self:OnCheckAccess(v)) then
v:NotifyLocalized("bastionGravity", client:Name(), number)
end
end
end,
bNoIndicator = true
})
-- lookup commands
ix.command.Add("LookupSteamID", {
description = "Lookup a SteamID in the Bastion user database",
arguments = {
ix.type.text
},
privilege = "Bastion Lookup",
OnRun = function(self, client, target)
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
PLUGIN:LookupSteamID(client, target)
return
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
PLUGIN:LookupSteamID(client, target)
return
end
target = ix.util.FindPlayer(target, false)
client:NotifyLocalized("bastionTargetSelected", target:Name())
PLUGIN:LookupSteamID(client, target:SteamID64())
end,
bNoIndicator = true
})
ix.command.Add("LookupIPUsers", {
description = "Lookup a SteamID in the Bastion IP database",
arguments = {
ix.type.text
},
privilege = "Bastion Lookup",
OnRun = function(self, client, target)
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
PLUGIN:LookupIPUsers(client, target)
return
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
PLUGIN:LookupIPUsers(client, target)
return
end
target = ix.util.FindPlayer(target, false)
client:NotifyLocalized("bastionTargetSelected", target:Name())
PLUGIN:LookupIPUsers(client, target:SteamID64())
end,
bNoIndicator = true
})
ix.command.Add("ProxyWhitelist", {
description = "Whitelist a player as trusted to disable future proxy checks.",
arguments = {
ix.type.player
},
privilege = "Bastion Whitelist",
OnRun = function(self, client, target)
if (PLUGIN:WhitelistPlayer(target)) then
client:NotifyLocalized("whitelistDone")
else
client:NotifyLocalized("whitelistError")
end
end,
indicator = "chatTyping"
})
ix.command.Add("PlyGetCharacters", {
description = "Get a list of a player's characters.",
arguments = {
ix.type.player
},
adminOnly = true,
OnRun = function(self, client, target)
client:ChatNotify(target:SteamName() .. "'s characters:")
client:ChatNotify("====================")
for _, character in pairs(target.ixCharList) do
client:ChatNotify(ix.char.loaded[character].vars.name)
end
end
})
ix.command.Add("LTARP", {
description = "Automatically ban a player for leaving to avoid RP after a grace period.",
arguments = {
ix.type.string,
ix.type.number,
bit.bor(ix.type.text, ix.type.optional),
},
adminOnly = true,
OnRun = function(self, client, steamID, days, reason)
local target = player.GetBySteamID64(steamID) or player.GetBySteamID(steamID)
if (target) then
client:Notify(target:SteamName().." is still on the server as '"..target:Name().."!")
return
end
if (days > 100) then
client:Notify("Invalid duration entered. Max ban length is 100 days.")
return
end
if (string.find(string.upper(steamID), "^STEAM")) then
steamID = util.SteamIDTo64(string.upper(steamID))
end
if (!PLUGIN.disconnects[steamID]) then
client:Notify("Could not find a disconnect for "..steamID..", manually ban them if needed.")
return
end
local info = PLUGIN.disconnects[steamID]
if (info.timer) then
client:Notify(steamID.." is already tagged for LTARP by "..info.bannedByName..".")
return
end
local reconnectTime, maxBanDelay = 30, 40
if (info.time + reconnectTime * 60 < os.time()) then
if (info.time + maxBanDelay * 60 < os.time()) then
client:Notify(steamID.." disconnected more than "..maxBanDelay.." minutes ago already. Manually ban them if needed.")
return
end
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..client:SteamName())
return
end
local uniqueID = "ixLTARP"..steamID
local bannedBy = client and client:SteamName() or "console"
timer.Create(uniqueID, info.time - os.time() + reconnectTime * 60, 1, function()
if (IsValid(client)) then
client:Notify(steamID.." did not reconnect and has been banned.")
end
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..bannedBy)
end)
info.timer = uniqueID
info.bannedBy = client
info.bannedByName = client and client:SteamName() or "console"
client:Notify("Timer set! "..steamID.." will be banned in "..math.ceil((info.time - os.time() + reconnectTime * 60) / 60).." minutes.")
end
})

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 = "View Owner",
Order = 405,
MenuIcon = "icon16/magnifier.png",
Filter = function(self, entity, client)
if (entity:GetClass() == "prop_physics" and CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil)) then return true end
end,
Action = function(self, entity)
self:MsgStart()
net.WriteEntity(entity)
self:MsgEnd()
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
local ownerCharacter = entity.ownerCharacter
local ownerName = entity.ownerName
local ownerSteamID = entity.ownerSteamID
if (ownerCharacter and ownerName and ownerSteamID) then
client:ChatNotifyLocalized("bastionPropOwnerInformation", ownerCharacter, ownerName, ownerSteamID)
else
client:ChatNotifyLocalized("bastionPropOwnerUnknown")
end
end
})

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,331 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local IsValid = IsValid
local ix = ix
PLUGIN.name = "Bastion"
PLUGIN.author = "Gr4Ss"
PLUGIN.description = "Some admin extensions for Helix."
local CAMI = CAMI
CAMI.RegisterPrivilege({
Name = "Helix - Hear Staff Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Hear GM Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Hear Mentor Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Fun Stuff",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Basic Commands",
MinAccess = "user"
})
CAMI.RegisterPrivilege({
Name = "Helix - Basic Admin Commands",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Full Remover Tool",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - HL2 Tools",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Bastion Whitelist",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - View Inventory",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Increase Character Limit",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Bastion Lookup",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Container Password",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Proxy Notify",
MinAccess = "superadmin"
})
ix.option.Add("pgi", ix.type.bool, true, {category = "administration"})
ix.option.Add("playerDeathNotification", ix.type.bool, true, {category = "administration", bNetworked = true})
ix.config.Add("netLoggingEnabled", false, "Enable or disable net logging into the database (WARNING: PERFORMANCE IMPACT)", nil, {
category = "Bastion"
})
ix.config.Add("netAntiSpam", true, "Enable or disable net anti-spam (WARNING: PERFORMANCE IMPACT)", nil, {
category = "Bastion"
})
ix.config.Add("suppressOnPlayerChat", true, "Suppress the default OnPlayerChat hook (should not be used by helix)", nil, {
category = "Bastion"
})
ix.config.Add("maxCharactersIncreased", 5, "The maximum number of characters a player can have if they have the increased character limit permission.", nil, {
data = {min = 1, max = 50},
category = "characters"
})
ix.config.Add("charCreateInterval", 5, "How many minutes there should be between 2 successful character creations of one player (to avoid character spam).", nil, {
data = {min = 1, max = 50},
category = "characters"
})
ix.config.Add("AllowContainerSpawn", false, "Allow anyone to directly spawn containers by spawning in their prop. Disallowing this will require admins to create containers from a prop using the context menu.", nil, {
category = "containers"
})
ix.config.Add("VPNKick", false, "Kick new users if they use a VPN.", nil, {
category = "Bastion"
})
ix.config.Add("ProxyAlwaysAlert", true, "Always Discord Alert for new joiners with a VPN.", nil, {
category = "Bastion"
})
ix.config.Add("showConnectMessages", true, "Whether or not to show notifications when players connect to the server. When off, only Staff will be notified.", nil, {
category = "server"
})
ix.config.Add("showDisconnectMessages", true, "Whether or not to show notifications when players disconnect from the server. When off, only Staff will be notified.", nil, {
category = "server"
})
ix.config.Add("DiscordLink", "https://discord.gg/HbDjUQd", "Invite link to the discord.", nil, {
category = "Bastion"
})
ix.config.Add("ContentLink", "https://steamcommunity.com/sharedfiles/filedetails/?id=2145501003", "Link to the workshop collection.", nil, {
category = "Bastion"
})
ix.config.Add("ForumLink", "https://willard.network", "Link to the forums", nil, {
category = "Bastion"
})
ix.config.Add("EdictWarningLimit", 1024, "How many edicts can be left before warning messages start to appear.", nil, {
data = {min = 100, max = 1024},
category = "Bastion"
})
ix.flag.Add("a", "Access to the advanced duplicator.")
ix.lang.AddTable("english", {
getPlayerInfo = "Uzyskaj informacje o graczu",
optStaffChat = "Wyświetla czatu administracji na wszystkich postaciach",
optdStaffChat = "Włącza/wyłącza czat administracji na wszystkich postaciach. Gdy jest wyłączony, czat administracji będzie wyświetlany tylko w trybie obserwatora lub na postaci administratora.",
optPgi = "Skopiuj Steam ID do schowka w 'PGI'/'Wyświetl Gracza'",
optdPgi = "Umożliwia włączenie/wyłączenie automatycznego kopiowania SteamID gracza do schowka podczas korzystania z komendy PGI lub opcji menu kontekstowego 'Wyświetl Gracza'.",
cmdStaffHelp = "Poproś o pomoc wszystkich administratorów.\n",
cmdAnnounce = "Ogłoszenie administracyjne OOC na całym serwerze.",
cmdLocalEvent = "Utwórz wydarzenie administracyjne IC, które będzie słyszalne tylko w określonym promieniu.",
bastionPGIInvalidTarget = "Musisz wejść do prawidłowego celu lub patrzeć na niego.",
bastionTakingItemsTooQuickly = "Zbyt szybko zabierasz przedmioty! Zwolnij tempo.",
bastionItemDropSpamKick = "%s został zkickowany za exploitowanie item dropów.",
bastionItemDropSpamWarn = "%s został ostrzeżony za exploitowanie item dropów.",
bastionItemDropTooQuick = "Zbyt szybko upuszczasz przedmioty! Zwolnij tempo.",
bastionItemTakeWarn = "%s został ostrzeżony za zbyt szybkie zabieranie przedmiotów.",
bastionItemTakeKick = "%s został zkickowany za zbyt szybkie upuszczanie przedmiotów.",
charCreateTooFast = "Zbyt szybko tworzysz postacie. Odczekaj co najmniej %d minut z kolejną próbą.",
bastionCopiedCharName = "Skopiowano imię postaci",
bastionCopiedSteamName = "Skopiowano nazwę Steam",
bastionCopiedSteamID = "Skopiowano Steam ID",
bastionGoto = "Przejdź do gracza",
bastionCopyCharName = "Kopiuj imię postaci",
bastionCopySteamName = "Kopiuj nazwę Steam",
bastionNoRecordFound = "Nie znaleziono żadnych rekordów dla %s.",
bastionResultsPrinted = "Wyniki zostały wydrukowane w konsoli.",
bastionProxyNotify = "%s połączył się jako nowy gracz korzystając z VPN/Proxy.",
bastionPropOwnerInformation = "Właścicielem tego propa jest %s (%s - %s).",
bastionPropOwnerUnknown = "Właściciel tego propa nie został zarejestrowany!",
whitelistDone = "Gracz został zwhitelistowany.",
whitelistError = "Coś poszło przy nadaniu whitelisty graczowi. Skontaktuj się z deweloperem.",
cmdTimeScale = "Zmień skalę czasu (min 0.001, max 5).",
bastionTimeScale = "%s ustawił skalę czasu na %d.",
cmdGravity = "Zmień grawitację.",
bastionGravity = "%s ustawił grawitacje na %d.",
edictWarning = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192!",
edictCritical = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192! Koniecznie trzeba zrobić awaryjny Cleanup!",
entsPrintedInConsole = "Lista Entity została wydrukowana w konsoli.",
entityRemoved = "Entity %d (%s) zostało usunięte!",
entityNotFound = "Entity %d nie zostało znalezione/jest nieprawidłowe.",
optPlayerDeathNotification = "Powiadomienie o śmierci gracza",
optdPlayerDeathNotification = "Określa, czy wysłać wiadomość na czacie, gdy gracz zginie.",
cmdRemovePersistedProps = "Usuwa wszystkie utrwalone propy w promieniu wokół ciebie."
})
ix.lang.AddTable("spanish", {
bastionCopySteamName = "Copiar nombre de Steam",
bastionPGIInvalidTarget = "Debes especificar o estar mirando a un objetivo válido.",
cmdLocalEvent = "Haz un evento IC de administrador que sólo pueda ser escuchado dentro de un radio determinado.",
bastionNoRecordFound = "No se ha podido encontrar ningún registro para %s.",
cmdAnnounce = "Haz un anuncio OOC de administrador a todo el servidor.",
cmdGravity = "Cambia la gravedad.",
bastionCopiedSteamID = "Steam ID copiada",
bastionProxyNotify = "%s se ha conectado como nuevo jugador mientras usa una VPN/Proxy.",
bastionTakingItemsTooQuickly = "¡Estás agarrando objetos demasiado rápido! Por favor, reduce la velocidad.",
optdStaffChat = "Activa/desactiva el Chat de Staff en todos los personajes. Cuando está desactivado, sólo se mostrará el Chat de Staff mientras se esté en observer o en un personaje administrativo.",
entityNotFound = "La entidad %d no fue encontrada/no es válida.",
whitelistDone = "El jugador estaba en la Whitelist.",
bastionCopiedCharName = "Nombre del personaje copiado",
getPlayerInfo = "Obtener información del jugador",
bastionCopyCharName = "Copiar el nombre del personaje",
charCreateTooFast = "Estás creando personajes demasiado rápido. Por favor, espera al menos %d minutos entre intentos.",
entsPrintedInConsole = "La lista de entidades se ha impreso en la consola.",
bastionItemDropTooQuick = "¡Estás soltando objetos demasiado rápido! Por favor, baja la velocidad.",
bastionGravity = "%s ha fijado la gravedad en %d.",
entityRemoved = "¡La entidad %d (%s) ha sido eliminada!",
cmdStaffHelp = "Pide ayuda a todo el Staff.\n",
bastionCopiedSteamName = "Nombre de Steam copiado",
bastionItemTakeWarn = "%s fue advertido por agarrar objetos demasiado rápido.",
bastionResultsPrinted = "Los resultados se imprimieron en la consola.",
optStaffChat = "Mostrar el Chat de Staff en todos los personajes",
bastionGoto = "Ir al jugador",
cmdTimeScale = "Cambie la escala de tiempo (mínimo 0,001, máximo 5).",
whitelistError = "Algo fue mal cuando se intentó meter al jugador en la whitelist. Contacta con un desarrollador.",
bastionTimeScale = "%s ha fijado la escala de tiempo en %d.",
bastionItemDropSpamKick = "%s ha sido expulsado por exploits relacionados con item-dropping.",
optdPgi = "Te permite activar/desactivar la copia automática al portapapeles del SteamID de un jugador cuando usa el comando PGI o la opción del menú contextual 'Ver Jugador'.",
bastionItemDropSpamWarn = "%s ha sido advertido del uso de exploits relacionados con item-dropping.",
bastionItemTakeKick = "%s ha sido expulsado por soltar objetos demasiado rápido.",
optPgi = "Copiar Steam ID al portapapeles en el 'PGI'/'Ver Jugador'",
edictWarning = "¡Solamente quedan disponibles %d edictos! ¡El conteo total de edictos es de: %d/8192!"
})
ix.lang.AddTable("polish", {
getPlayerInfo = "Uzyskaj informacje o graczu",
optStaffChat = "Wyświetla czatu administracji na wszystkich postaciach",
optdStaffChat = "Włącza/wyłącza czat administracji na wszystkich postaciach. Gdy jest wyłączony, czat administracji będzie wyświetlany tylko w trybie obserwatora lub na postaci administratora.",
optPgi = "Skopiuj Steam ID do schowka w 'PGI'/'Wyświetl Gracza'",
optdPgi = "Umożliwia włączenie/wyłączenie automatycznego kopiowania SteamID gracza do schowka podczas korzystania z komendy PGI lub opcji menu kontekstowego 'Wyświetl Gracza'.",
cmdStaffHelp = "Poproś o pomoc wszystkich administratorów.\n",
cmdAnnounce = "Ogłoszenie administracyjne OOC na całym serwerze.",
cmdLocalEvent = "Utwórz wydarzenie administracyjne IC, które będzie słyszalne tylko w określonym promieniu.",
bastionPGIInvalidTarget = "Musisz wejść do prawidłowego celu lub patrzeć na niego.",
bastionTakingItemsTooQuickly = "Zbyt szybko zabierasz przedmioty! Zwolnij tempo.",
bastionItemDropSpamKick = "%s został zkickowany za exploitowanie item dropów.",
bastionItemDropSpamWarn = "%s został ostrzeżony za exploitowanie item dropów.",
bastionItemDropTooQuick = "Zbyt szybko upuszczasz przedmioty! Zwolnij tempo.",
bastionItemTakeWarn = "%s został ostrzeżony za zbyt szybkie zabieranie przedmiotów.",
bastionItemTakeKick = "%s został zkickowany za zbyt szybkie upuszczanie przedmiotów.",
charCreateTooFast = "Zbyt szybko tworzysz postacie. Odczekaj co najmniej %d minut z kolejną próbą.",
bastionCopiedCharName = "Skopiowano imię postaci",
bastionCopiedSteamName = "Skopiowano nazwę Steam",
bastionCopiedSteamID = "Skopiowano Steam ID",
bastionGoto = "Przejdź do gracza",
bastionCopyCharName = "Kopiuj imię postaci",
bastionCopySteamName = "Kopiuj nazwę Steam",
bastionNoRecordFound = "Nie znaleziono żadnych rekordów dla %s.",
bastionResultsPrinted = "Wyniki zostały wydrukowane w konsoli.",
bastionProxyNotify = "%s połączył się jako nowy gracz korzystając z VPN/Proxy.",
bastionPropOwnerInformation = "Właścicielem tego propa jest %s (%s - %s).",
bastionPropOwnerUnknown = "Właściciel tego propa nie został zarejestrowany!",
whitelistDone = "Gracz został zwhitelistowany.",
whitelistError = "Coś poszło przy nadaniu whitelisty graczowi. Skontaktuj się z deweloperem.",
cmdTimeScale = "Zmień skalę czasu (min 0.001, max 5).",
bastionTimeScale = "%s ustawił skalę czasu na %d.",
cmdGravity = "Zmień grawitację.",
bastionGravity = "%s ustawił grawitacje na %d.",
edictWarning = "Pozostało tylko %d edyktów! Całkowita liczba edyktów wynosi obecnie: %d/8192!",
entsPrintedInConsole = "Lista Entity została wydrukowana w konsoli.",
entityRemoved = "Entity %d (%s) zostało usunięte!",
entityNotFound = "Entity %d nie zostało znalezione/jest nieprawidłowe.",
optPlayerDeathNotification = "Powiadomienie o śmierci gracza",
optdPlayerDeathNotification = "Określa, czy wysłać wiadomość na czacie, gdy gracz zginie.",
})
PLUGIN.soundAlias = {
["restricted_block_deploy"] = "voices/dispatch/restrictedblock_deployment.wav",
["restricted_block"] = "voices/dispatch/restrictedblock_warning.wav",
["access_restricted"] = "voices/dispatch/access_restricted.wav",
["anticivil_evading"] = "voices/dispatch/anticivil_evading.wav",
["civil_insurrection"] = "voices/dispatch/civil_insurrection.wav",
["victory_music"] = "music/scary_tense/victory_music.mp3",
}
ix.util.Include("cl_plugin.lua")
ix.util.Include("sh_classes.lua")
ix.util.Include("sh_commands.lua")
ix.util.Include("sh_context.lua")
ix.util.Include("sh_hooks.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("sv_plugin.lua")
ix.util.Include("modules/sh_bindchecker.lua")
function PLUGIN:GetMaxPlayerCharacter(client)
if (CAMI.PlayerHasAccess(client, "Helix - Increase Character Limit")) then
return ix.config.Get("maxCharactersIncreased", 8)
end
end
function PLUGIN:CanProperty(client, property, entity)
if (property == "container_setpassword" and !CAMI.PlayerHasAccess(client, "Helix - Container Password")) then
return false
end
end
function PLUGIN:CanPlayerSpawnContainer()
if (!ix.config.Get("AllowContainerSpawn")) then
return false
end
end
function PLUGIN:CanPlayerAccessDoor(client)
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then return true end
if (ix.faction.Get(client:Team()).lockAllDoors) then return true end
end
PLUGIN.removeWhitelist = {
["prop_physics"] = true,
["prop_ragdoll"] = true
}
local hl2Tools = {
["env_headcrabcanister"] = true,
["item_ammo_crate"] = true,
["item_item_crate"] = true,
["prop_door"] = true,
["prop_thumper"] = true
}
function PLUGIN:CanTool(client, trace, tool)
if (tool == "remover" and !CAMI.PlayerHasAccess(client, "Helix - Full Remover Tool")) then
if (IsValid(trace.Entity) and !self.removeWhitelist[trace.Entity:GetClass()]) then
return false
end
elseif (tool == "advdupe2" and !client:GetCharacter():HasFlags("a")) then
return false
elseif (hl2Tools[tool] and !CAMI.PlayerHasAccess(client, "Helix - HL2 Tools")) then
return false
end
end

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("Time")
self.logs:AddColumn("Steam ID")
self.logs:AddColumn("Text")
self.logs:SetHeaderHeight(SScaleMin(16 / 3))
self.logs:SetDataHeight(SScaleMin(17 / 3))
self.logs.Paint = function() end
for _, v in pairs(self.logs:GetChildren()) do
if v:GetName() != "DListView_Column" then continue end
for _, v2 in pairs(v:GetChildren()) do
if v2:GetName() != "DButton" then continue end
local text = v2:GetText()
if (text == "Steam ID" or text == "Time" or text == "ID") then
v:SetFixedWidth(self.columnWidth)
end
end
end
self.logs.OnRowRightClick = function(list, lineId, line)
local dmenu = DermaMenu()
dmenu:MakePopup()
dmenu:SetPos(input.GetCursorPos())
local record = line.record
if (record) then
dmenu:AddOption("Copy to clipboard", function()
SetClipboardText("["..os.date("%Y-%m-%d %X", record.datetime).."] "..record.text)
end)
if (record.steamid) then
dmenu:AddOption("Copy SteamID to clipboard", function()
SetClipboardText(util.SteamIDFrom64(record.steamid))
end)
dmenu:AddOption("Copy SteamID64 to clipboard", function()
SetClipboardText(record.steamid)
end)
end
if (record.pos_x) then
local pos = Vector(record.pos_x, record.pos_y, record.pos_z)
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Tp", nil)) then
dmenu:AddOption("Teleport", function()
netstream.Start("ixLogTeleport", pos)
end)
end
dmenu:AddOption("Mark", function()
PLUGIN:LogMark(pos, record)
end)
end
end
for _, v in pairs(dmenu:GetChildren()[1]:GetChildren()) do
v:SetFont("MenuFontNoClamp")
end
end
self:FillLogs()
self:CreatePagination()
self:CreateLeftSideButtons()
end
function PANEL:FillLogs(bPage)
self.logs:Clear()
local logsData = self:GetCache(self.page) or self.requestedLogs
if (logsData) then
if (istable(logsData)and !table.IsEmpty(logsData)) then
for _, v in pairs(logsData) do
local e = "UNKNOWN"
v.id, v.datetime, v.steamid, v.text = v.id or e, v.datetime or e, v.steamid or e, v.text or e
local line = self.logs:AddLine(v.id, os.date("%Y-%m-%d %X", v.datetime), util.SteamIDFrom64(v.steamid), v.text)
line.record = v
line.type = v.log_type
end
self:SetCache(self.page, table.Copy(logsData))
elseif (isnumber(logsData)) then
self.logs:AddLine("You have to wait "..logsData.." second(s) before next search.")
end
else
self.logs:AddLine("Logs not found!")
end
if (bPage) then
self:UpdatePage(true)
end
for k, v2 in pairs(self.logs:GetLines()) do
for k2, v3 in pairs(v2:GetChildren()) do
v3:SetFont("MenuFontNoClamp")
end
end
for k3, v4 in pairs(self.logs.Columns) do
for k4, v5 in pairs(v4:GetChildren()) do
v5:SetFont("MenuFontNoClamp")
end
end
for _, v in ipairs(self.logs.Lines) do
v.Paint = function(panel, width, height)
if (panel:IsSelected()) then
SKIN.tex.Input.ListBox.Hovered(0, 0, width, height)
elseif (panel.Hovered) then
surface.SetDrawColor(34, 58, 112)
surface.DrawRect(0, 0, width, height)
elseif (panel.m_bAlt) then
SKIN.tex.Input.ListBox.EvenLine(0, 0, width, height)
end
if self.highlightDeathsKnockouts:GetChecked() then
if v.type and v.type != "chat" and v:GetColumnText( 4 ):find("knocked") then
surface.SetDrawColor(255, 218, 185, 10)
surface.DrawRect(0, 0, width, height)
end
if v:GetColumnText( 4 ):find("died") or v:GetColumnText( 4 ):find("death") or v:GetColumnText( 4 ):find("killed") then
surface.SetDrawColor(255, 0, 0, 10)
surface.DrawRect(0, 0, width, height)
end
end
end
for _, v2 in ipairs(v.Columns) do
v2:SetTextColor(Color(255, 255, 255))
end
end
end
-- Helper paint function for outlined rectangles
local function PaintStandard(self, w, h, alpha)
surface.SetDrawColor(Color(0, 0, 0, alpha))
surface.DrawRect(0, 0, w, h)
surface.SetDrawColor(Color(111, 111, 136, 255 / 100 * 30))
surface.DrawOutlinedRect(0, 0, w, h)
end
function PANEL:CreateLeftSideButtons()
local function search(pnl)
local curTime = CurTime()
if (pnl.nextClick and pnl.nextClick >= curTime) then
LocalPlayer():Notify("Please wait before searching again!")
return
end
self:SetCache()
surface.PlaySound("helix/ui/press.wav")
self:RequestLogs()
pnl.nextClick = curTime + 3
end
self.afterTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.afterTextEntry, true, 32, false, false, false, "Time after")
self.afterTextEntry:SetTooltip("Events after x long ago. Time example: 5y2d7w = 5 years, 2 days, and 7 weeks")
self.afterTextEntry:SetEnterAllowed(true)
self.afterTextEntry.OnEnter = search
self.afterTextEntry:SetValue(ix.option.Get("logDefaultTime"))
self.beforeTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.beforeTextEntry, true, 32, false, false, false, "Time before")
self.beforeTextEntry:SetTooltip("Events before x long ago. Time example: 5y2d7w = 5 years, 2 days, and 7 weeks")
self.beforeTextEntry:SetEnterAllowed(true)
self.beforeTextEntry.OnEnter = search
self.descCheckBox = self.leftSide:Add("DCheckBoxLabel")
self.descCheckBox:SetText("Reversed time order")
self.descCheckBox:Dock(TOP)
self.descCheckBox:SetFont("MenuFontNoClamp")
self.descCheckBox:SetChecked(true)
self.descCheckBox:DockMargin(2, 2, 2, 2)
self.descCheckBox.PerformLayout = function(panel)
local x = panel.m_iIndent or 0
panel.Button:SetSize(SScaleMin(15 / 3), SScaleMin(15 / 3))
panel.Button:SetPos(x, math.floor((panel:GetTall() - panel.Button:GetTall()) / 2))
panel.Label:SizeToContents()
panel.Label:SetPos(x + panel.Button:GetWide() + SScaleMin(9 / 3), 0)
end
self.highlightDeathsKnockouts = self.leftSide:Add("DCheckBoxLabel")
self.highlightDeathsKnockouts:SetText("Highlight deaths/knockouts")
self.highlightDeathsKnockouts:Dock(TOP)
self.highlightDeathsKnockouts:SetFont("MenuFontNoClamp")
self.highlightDeathsKnockouts:SetChecked(true)
self.highlightDeathsKnockouts:DockMargin(2, 2, 2, 2)
self.highlightDeathsKnockouts.PerformLayout = function(panel)
local x = panel.m_iIndent or 0
panel.Button:SetSize(SScaleMin(15 / 3), SScaleMin(15 / 3))
panel.Button:SetPos(x, math.floor((panel:GetTall() - panel.Button:GetTall()) / 2))
panel.Label:SizeToContents()
panel.Label:SetPos(x + panel.Button:GetWide() + SScaleMin(9 / 3), 0)
end
self.steamidTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.steamidTextEntry, true, 32, false, false, false, "SteamID")
self.steamidTextEntry:SetTooltip("Events commited by the player with this SteamID")
self.steamidTextEntry:SetEnterAllowed(true)
self.steamidTextEntry.OnEnter = search
self.distanceTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.distanceTextEntry, true, 32, false, false, false, "Distance")
self.distanceTextEntry:SetTooltip("Events on the x distance from your current position. Leave empty for global")
self.distanceTextEntry:SetEnterAllowed(true)
self.distanceTextEntry.OnEnter = search
self.textTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.textTextEntry, true, 32, false, false, false, "Text")
self.textTextEntry:SetTooltip("Part of the log text")
self.textTextEntry:SetEnterAllowed(true)
self.textTextEntry.OnEnter = search
self.mapTextEntry = self.leftSide:Add("DTextEntry")
self:CreateTextEntry(self.mapTextEntry, true, 32, false, false, false, "Map")
self.mapTextEntry:SetTooltip("Event on the specific map. Leave empty for current")
self.mapTextEntry:SetEnterAllowed(true)
self.mapTextEntry.OnEnter = search
if (!table.IsEmpty(self.requestedLogTypes)) then
self.logTypeCombo = self.leftSide:Add("DComboBox")
self.logTypeCombo:SetValue("Log type")
self.logTypeCombo:SetTall(SScaleMin(32 / 3))
self.logTypeCombo:Dock(TOP)
self.logTypeCombo:SetFont("MenuFontNoClamp")
self.logTypeCombo.Think = function(comboBox)
if (comboBox:IsMenuOpen()) then
comboBox.Menu:SetMaxHeight(ScrH() * 0.4)
end
end
local logTypes = self.requestedLogTypes
self.logTypeCombo:AddChoice("raw")
for _, v in pairs(logTypes) do
self.logTypeCombo:AddChoice(v)
end
end
local searchButton = self.leftSide:Add("DButton")
searchButton:Dock(BOTTOM)
searchButton:SetText("Search")
searchButton:SetFont("MenuFontNoClamp")
searchButton:SetTall(SScaleMin(32 / 3))
searchButton:DockMargin(4, 4, 4, 4)
searchButton:SetContentAlignment(5)
searchButton.Paint = function(panel, w, h)
PaintStandard(panel, w, h, 100)
end
searchButton.DoClick = search
end
function PANEL:RequestLogs()
local data = {
before = ix.util.GetStringTime(self.beforeTextEntry:GetValue()),
after = ix.util.GetStringTime(self.afterTextEntry:GetValue()),
steamid = self.steamidTextEntry:GetValue(),
distance = tonumber(self.distanceTextEntry:GetValue()),
text = self.textTextEntry:GetValue(),
logType = self.logTypeCombo:GetSelected(),
map = self.mapTextEntry:GetValue(),
logsPerPage = 25,
currentPage = self.page,
desc = self.descCheckBox:GetChecked()
}
netstream.Start("ixRequestLogs", data)
end
function PANEL:CreatePagination()
local paginationBg = self.rightSide:Add("DPanel")
paginationBg:Dock(BOTTOM)
paginationBg:DockMargin(4, 4, 4, 4)
paginationBg:SetTall(SScaleMin(32 / 3))
self.firstPage = paginationBg:Add("DButton")
self.firstPage:Dock(LEFT)
self.firstPage:SetText("RETURN TO FIRST PAGE")
self.firstPage:SetTextColor(color_white)
self.firstPage:SetFont("MenuFontNoClamp")
self.firstPage:SizeToContents()
self.firstPage:SetWide(self.firstPage:GetWide() + SScaleMin(20 / 3))
self.firstPage:DockMargin(4, 4, 4, 4)
self.firstPage.DoClick = function(btn)
local curTime = CurTime()
if (btn.nextClick and btn.nextClick >= curTime) then return end
surface.PlaySound("helix/ui/press.wav")
self.page = 1
self:UpdatePage()
btn.nextClick = curTime + 1
end
self.pagePrev = paginationBg:Add("DButton")
self.pagePrev:Dock(LEFT)
self.pagePrev:SetText("<")
self.pagePrev:SetTextColor(color_white)
self.pagePrev:SetFont("MenuFontNoClamp")
self.pagePrev:DockMargin(4, 4, 4, 4)
self.pagePrev.DoClick = function(btn)
local curTime = CurTime()
if (btn.nextClick and btn.nextClick >= curTime) then return end
surface.PlaySound("helix/ui/press.wav")
self.page = self.page - 1
self:UpdatePage()
btn.nextClick = curTime + 1
end
self.pageLabel = paginationBg:Add("DLabel")
self.pageLabel:SetText("Page #"..self.page)
self.pageLabel:SetFont("MenuFontNoClamp")
self.pageLabel:SetContentAlignment(5)
self.pageLabel:Dock(FILL)
self.pageNext = paginationBg:Add("DButton")
self.pageNext:Dock(RIGHT)
self.pageNext:SetText(">")
self.pageNext:SetFont("MenuFontNoClamp")
self.pageNext:SetTextColor(color_white)
self.pageNext:DockMargin(4, 4, 4, 4)
self.pageNext.DoClick = function(btn)
local curTime = CurTime()
if (btn.nextClick and btn.nextClick >= curTime) then return end
surface.PlaySound("helix/ui/press.wav")
self.page = self.page + 1
self:UpdatePage()
btn.nextClick = curTime + 1
end
self:UpdatePage(true)
-- Page field
self.pageTextEntry = paginationBg:Add("DTextEntry")
self.pageTextEntry:Dock(RIGHT)
self.pageTextEntry:SetFont("MenuFontNoClamp")
self.pageTextEntry:SetNumeric(true)
self.pageTextEntry:SetTall(SScaleMin(32 / 3))
self.pageTextEntry:DockMargin(4, 4, 4, 4)
self.pageTextEntry:SetWide(SScaleMin(100 / 3))
-- Goto page x button
local gotoPage = paginationBg:Add("DButton")
gotoPage:Dock(RIGHT)
gotoPage:SetText("GOTO PAGE")
gotoPage:SetFont("MenuFontNoClamp")
gotoPage:SetTextColor(color_white)
gotoPage:DockMargin(4, 4, 4, 4)
gotoPage:SetWide(SScaleMin(100 / 3))
gotoPage.DoClick = function(btn)
local curTime = CurTime()
if (btn.nextClick and btn.nextClick >= curTime) then return end
surface.PlaySound("helix/ui/press.wav")
local page = tonumber(self.pageTextEntry:GetValue())
if (page and page > 0) then
self.page = page
self:UpdatePage()
end
btn.nextClick = curTime + 1
end
end
function PANEL:UpdatePage(bNoRequest)
self.pagePrev:SetDisabled(self.page == 1)
self.pageNext:SetDisabled(table.Count(self.logs:GetLines()) < 25)
self.pageLabel:SetText("Page #"..self.page)
if (!bNoRequest) then
if (self:GetCache(self.page)) then
self:FillLogs(true)
else
self:RequestLogs()
end
end
end
function PANEL:CreateTextEntry(parent, bDockTop, height, bMultiline, bScrollbar, bEnter, placeholderText)
parent:SetPlaceholderText(placeholderText)
parent:Dock(TOP)
parent:SetTall(SScaleMin(height / 3))
parent:SetMultiline(bMultiline)
parent:SetVerticalScrollbarEnabled(bScrollbar)
parent:SetEnterAllowed(bEnter)
parent:SetTextColor(Color(200, 200, 200, 255))
parent:SetCursorColor(Color(200, 200, 200, 255))
parent:SetFont("MenuFontNoClamp")
parent:SetPlaceholderColor(Color(200, 200, 200, 200))
if bDockTop then
parent:DockMargin(2, 2, 2, 2)
end
parent.Paint = function(panel, w, h)
if bMultiline then
surface.SetDrawColor(Color(25, 25, 25, 255))
else
surface.SetDrawColor(Color(0, 0, 0, 100))
end
surface.DrawRect(0, 0, w, h)
surface.SetDrawColor(Color(111, 111, 136, 255 / 100 * 30))
surface.DrawOutlinedRect(0, 0, w, h)
if (panel.GetPlaceholderText and panel.GetPlaceholderColor and panel:GetPlaceholderText() and panel:GetPlaceholderText():Trim() != "" and panel:GetPlaceholderColor() and (!panel:GetText() or panel:GetText() == "")) then
local oldText = panel:GetText()
local str = panel:GetPlaceholderText()
if (str:StartWith("#")) then str = str:utf8sub(2) end
str = language.GetPhrase(str)
panel:SetText(str)
panel:DrawTextEntryText(panel:GetPlaceholderColor(), panel:GetHighlightColor(), panel:GetCursorColor())
panel:SetText(oldText)
return
end
panel:DrawTextEntryText(panel:GetTextColor(), panel:GetHighlightColor(), panel:GetCursorColor())
end
end
function PANEL:GetCache(page)
local client = LocalPlayer()
client.logCache = client.logCache or {}
return client.logCache[page]
end
function PANEL:SetCache(page, cache)
local client = LocalPlayer()
client.logCache = client.logCache or {}
if (page) then
client.logCache[page] = cache
else
client.logCache = {}
end
end
vgui.Register("ixLogs", PANEL, "EditablePanel")

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("english", {
optLogDefaultTime = "Domyślny czas wyszukiwania logów",
optdLogDefaultTime = "Domyślny czas wyszukiwania w narzędziu wyszukiwania logów."
})
ix.lang.AddTable("spanish", {
optdLogDefaultTime = "La cantidad de tiempo por defecto para buscar en la herramienta de búsqueda de registros.",
optLogDefaultTime = "Tiempo de búsqueda de registros por defecto"
})
ix.lang.AddTable("polish", {
optLogDefaultTime = "Domyślny czas wyszukiwania logów",
optdLogDefaultTime = "Domyślny czas wyszukiwania w narzędziu wyszukiwania logów."
})
ix.option.Add("logDefaultTime", ix.type.string, "1d", {
bNetworked = true,
category = "administration",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage Logs", nil)
end
})

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 = "Weapon: "..weapon:GetClass()}
end
end
function PLUGIN:PreRender()
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then
render.SetLightingMode(1)
end
end
function PLUGIN:PreDrawHUD()
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then
render.SetLightingMode(0)
end
end
-- Overriding the SF2 PostDraw2DSkyBox hook otherwise fullbright doesn't work.
hook.Add("PostDraw2DSkyBox", "StormFox2.SkyBoxRender", function()
if (LocalPlayer():GetLocalVar("observerLight") and ix.option.Get("observerFullBright", false)) then return end
if (!StormFox2 or !StormFox2.Loaded or !StormFox2.Setting.SFEnabled()) then return end
if (!StormFox2.util or !StormFox2.Sun or !StormFox2.Moon or !StormFox2.Moon.GetAngle) then return end
local c_pos = StormFox2.util.RenderPos()
local sky = StormFox2.Setting.GetCache("enable_skybox", true)
local use_2d = StormFox2.Setting.GetCache("use_2dskybox",false)
cam.Start3D(Vector(0, 0, 0), EyeAngles(), nil, nil, nil, nil, nil, 1, 32000) -- 2d maps fix
render.OverrideDepthEnable(false,false)
render.SuppressEngineLighting(true)
render.SetLightingMode(2)
if (!use_2d or !sky) then
hook.Run("StormFox2.2DSkybox.StarRender", c_pos)
-- hook.Run("StormFox2.2DSkybox.BlockStarRender",c_pos)
hook.Run("StormFox2.2DSkybox.SunRender", c_pos) -- No need to block, shrink the sun.
hook.Run("StormFox2.2DSkybox.Moon", c_pos)
end
hook.Run("StormFox2.2DSkybox.CloudBox", c_pos)
hook.Run("StormFox2.2DSkybox.CloudLayer", c_pos)
hook.Run("StormFox2.2DSkybox.FogLayer", c_pos)
render.SuppressEngineLighting(false)
render.SetLightingMode(0)
render.OverrideDepthEnable( false, false )
cam.End3D()
render.SetColorMaterial()
end)

View File

@@ -0,0 +1,224 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local Color = Color
local CreateMaterial = CreateMaterial
local net = net
local os = os
local cam = cam
local hook = hook
local player = player
local team = team
local Vector = Vector
local surface = surface
local table = table
local render = render
local util = util
local ix = ix
local math = math
local ipairs = ipairs
local PLUGIN = PLUGIN
PLUGIN.traceFilter = {nil, nil}
local extraColor = Color(200, 200, 200, 255)
local mat1 = CreateMaterial("GA0249aSFJ3","VertexLitGeneric",{
["$basetexture"] = "models/debug/debugwhite",
["$model"] = 1,
["$translucent"] = 1,
["$alpha"] = 1,
["$nocull"] = 1,
["$ignorez"] = 1
})
net.Receive("ixObserverDisableTP", function(len)
if (ix.option.Get("thirdpersonEnabled")) then
net.Start("ixObserverDisableTP")
net.SendToServer()
end
ix.option.Set("thirdpersonEnabled", net.ReadBool())
end)
do
local npcColor = Color(255, 0, 128)
local espColor = Color(255,255,255,255)
local espColors = {
["Weapons"] = Color(255,78,69),
["Ammunition"] = Color(255,78,69),
["Medical"] = Color(138,200,97),
["Crafting"] = Color(255,204,0)
}
local minAlpha = {
["Writing"] = 0,
["Workbenches"] = 0,
}
local function itemESP(client, entity, x, y, factor, distance)
local itemTable = entity:GetItemTable()
if (!itemTable) then return end
local color = espColors[itemTable.category] or espColor
local alpha = math.Remap(math.Clamp(distance, 1500, 2000), 1500, 2000, 255, minAlpha[itemTable.category] or 45)
if (alpha == 0) then return end
color.a = alpha
ix.util.DrawText(itemTable.name .. " (#" .. entity:GetNetVar("itemID", "nil ID") .. ")", x, y + math.max(10, 32 * factor), color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
local owner = entity:GetNetVar("ownerName")
if (owner) then
local time = (entity:GetNetVar("spawnTime") and " - "..math.ceil((os.time() - entity:GetNetVar("spawnTime")) / 60).."m") or ""
alpha = math.Remap(math.Clamp(distance, 400, 700), 400, 700, 255, 0)
espColor.a = alpha
ix.util.DrawText(owner..time, x, y + math.max(10, 32 * factor) + 20, espColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
end
end
ix.observer:RegisterESPType("ix_item", itemESP, "item")
local function npcESP(client, entity, x, y, factor)
ix.util.DrawText(entity:GetClass(), x, y - math.max(10, 32 * factor), npcColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, math.max(255 * factor, 80))
end
ix.observer:RegisterESPType("ix_npc", npcESP, "npc", "Show NPC ESP", nil, true)
if (CLIENT) then
local function containerESP(client, entity, x, y, factor, distance)
local color = espColor
local alpha = math.Remap(math.Clamp(distance, 500, 1000), 500, 1000, 255, 30)
color.a = alpha
ix.util.DrawText("Container - "..entity:GetDisplayName().." #"..entity:EntIndex(), x, y - math.max(10, 32 * factor), color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
end
ix.observer:RegisterESPType("ix_container", containerESP, "container")
end
local function staticESP(client, entity, x, y, factor, distance)
if (distance > 2500) then return end
local alpha = math.Remap(math.Clamp(distance, 500, 2500), 500, 2500, 255, 45)
espColor.a = alpha
if (IsValid(entity) and entity:GetNetVar("Persistent", false)) then
ix.util.DrawText(entity:GetModel(), x, y - math.max(10, 32 * factor), espColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, alpha)
end
end
ix.observer:RegisterESPType("prop_physics", staticESP, "static", "Show Static Prop ESP")
end
local function sortFunc(a, b)
if (a.alpha != b.alpha) then
return a.alpha > b.alpha
elseif (a.priority != b.priority) then
return a.priority < b.priority
else
return a.text < b.text
end
end
function PLUGIN:DrawPlayerESP(client, scrW, scrH)
local pos = client:EyePos()
local marginX, marginY = scrW * .1, scrH * .1
self.traceFilter[1] = client
local names = {}
cam.Start3D()
local targets = hook.Run("GetAdminESPTargets") or player.GetAll()
for _, v in ipairs(targets) do
if (v == client or !v:GetCharacter() or client:GetAimVector():Dot((v:GetPos() - pos):GetNormal()) < 0.65) then
continue
end
local bObserver = v:GetMoveType() == MOVETYPE_NOCLIP and !v:InVehicle()
local teamColor = bObserver and Color(255, 85, 20, 255) or team.GetColor(v:Team())
local vEyePos = v:EyePos()
local distance = pos:Distance(vEyePos)
if ix.option.Get("observerPlayerHighlight") then
self:RenderAdminESP(client, v, teamColor, pos, vEyePos, distance)
end
names[#names + 1] = {v, teamColor, distance}
end
cam.End3D()
local right = client:GetRight() * 25
for _, info in ipairs(names) do
local ply, teamColor, distance = info[1], info[2], info[3]
local plyPos = ply:GetPos()
local min, max = ply:GetModelRenderBounds()
min = min + plyPos + right
max = max + plyPos + right
local barMin = Vector((min.x + max.x) / 2, (min.y + max.y) / 2, min.z):ToScreen()
local barMax = Vector((min.x + max.x) / 2, (min.y + max.y) / 2, max.z):ToScreen()
local eyePos = ply:EyePos():ToScreen()
local rightS = math.min(math.max(barMin.x, barMax.x), eyePos.x + 150)
local barWidth = math.Remap(math.Clamp(distance, 200, 2000), 500, 2000, 120, 75)
local barHeight = math.abs(barMax.y - barMin.y)
local barX, barY = math.Clamp(rightS, marginX, scrW - marginX - barWidth), math.Clamp(barMin.y - barHeight + 18, marginY, scrH - marginY)
local alphaFar = math.Remap(math.Clamp(distance, 1500, 2000), 1500, 2000, 255, 0)
local alphaMid = math.Remap(math.Clamp(distance, 400, 700), 400, 700, 255, 0)
local alphaClose = math.Remap(math.Clamp(distance, 200, 500), 200, 500, 255, 0)
local bArmor = ply:Armor() > 0
surface.SetDrawColor(40, 40, 40, 200 * alphaFar / 255)
surface.DrawRect(barX - 1, barY - 1, barWidth + 2, 5)
if (bArmor) then surface.DrawRect(barX - 1, barY + 9, barWidth + 2, 5) end
surface.SetDrawColor(teamColor.r * 1.6, teamColor.g * 1.6, teamColor.b * 1.6, alphaFar)
surface.DrawRect(barX, barY, barWidth * math.Clamp(ply:Health() / ply:GetMaxHealth(), 0, 1), 3)
local extraHeight = 0
if (bArmor) then
extraHeight = 10
surface.SetDrawColor(255, 255, 255, alphaFar)
surface.DrawRect(barX, barY + 10, barWidth * math.Clamp(ply:Armor() / 50, 0, 1), 3)
end
surface.SetFont("WNBackFontNoClamp")
ix.util.DrawText(ply:Name(), barX, barY - 13, teamColor, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, nil, 255)
if (ix.option.Get("steamESP")) then
surface.SetFont("WNMenuFontNoClamp")
local y = barY + extraHeight + 13
local toDraw = {}
hook.Run("GetPlayerESPText", ply, toDraw, distance, alphaFar, alphaMid, alphaClose)
table.sort(toDraw, sortFunc)
for _, v in ipairs(toDraw) do
if (v.alpha <= 0) then continue end
extraColor.a = v.alpha
ix.util.DrawText(v.text, barX, y, v.color or extraColor, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, nil, v.alpha)
local _, txtHeight = surface.GetTextSize(v.text)
y = y + txtHeight
end
end
end
end
function PLUGIN:RenderAdminESP(client, target, color, clientPos, targetEyePos, distance)
render.SuppressEngineLighting(true)
render.SetColorModulation(color.r / 255, color.g / 255, color.b / 255)
self.traceFilter[2] = target
if (ix.option.Get("cheapBlur", false) or util.QuickTrace(clientPos, targetEyePos - clientPos, self.traceFilter).Fraction < 0.95) then
render.SetBlend(1)
else
render.SetBlend(math.Remap(math.Clamp(distance, 200, 4000), 200, 8000, 0.05, 1))
end
render.MaterialOverride(mat1)
target:DrawModel()
render.MaterialOverride()
render.SuppressEngineLighting(false)
end

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
ix.observer = ix.observer or {}
ix.observer.types = ix.observer.types or {}
function ix.observer:RegisterESPType(type, func, optionName, optionNiceName, optionDesc, bDrawClamped)
optionName = string.lower(optionName)
local editCapital = string.utf8sub(optionName, 1, 1)
local capitalName = string.utf8upper(editCapital)..string.utf8sub(optionName, 2)
ix.option.Add(optionName.."ESP", ix.type.bool, false, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer Extra ESP")
end
})
ix.lang.AddTable("english", {
["opt"..capitalName.."ESP"] = optionNiceName or "Pokaż ESP "..capitalName,
["optd"..capitalName.."ESP"] = optionDesc or "Włącz/wyłącz ESP "..optionName
})
ix.lang.AddTable("polish", {
["opt"..capitalName.."ESP"] = optionNiceName or "Pokaż ESP "..capitalName,
["optd"..capitalName.."ESP"] = optionDesc or "Włącz/wyłącz ESP "..optionName
})
ix.observer.types[string.lower(type)] = {optionName.."ESP", func, bDrawClamped}
end
function ix.observer:ShouldRenderAnyTypes()
for _, v in pairs(ix.observer.types) do
if (ix.option.Get(v[1])) then
return true
end
end
return false
end

View File

@@ -0,0 +1,118 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ix = ix
local CAMI = CAMI
local LocalPlayer = LocalPlayer
local PLUGIN = PLUGIN
PLUGIN.name = "Better Observer"
PLUGIN.author = "Chessnut & Gr4Ss"
PLUGIN.description = "Adds on to the no-clip mode to prevent intrusion. Edited for WN by Gr4Ss."
ix.plugin.SetUnloaded("observer", true)
CAMI.RegisterPrivilege({
Name = "Helix - Observer",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Observer Extra ESP",
MinAccess = "superadmin"
})
ix.option.Add("observerTeleportBack", ix.type.bool, true, {
bNetworked = true,
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
end
})
ix.option.Add("observerESP", ix.type.bool, true, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
end
})
ix.option.Add("steamESP", ix.type.bool, true, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
end
})
ix.option.Add("mapscenesESP", ix.type.bool, false, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
end
})
ix.option.Add("alwaysObserverLight", ix.type.bool, true, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
end,
bNetworked = true
})
ix.option.Add("observerFullBright", ix.type.bool, false, {
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer")
end,
bNetworked = true
})
ix.option.Add("observerPlayerHighlight", ix.type.bool, true, {
bNetworked = true,
category = "observer",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Observer", nil)
end
})
ix.util.Include("cl_hooks.lua")
ix.util.Include("cl_plugin.lua")
ix.util.Include("sv_plugin.lua")
ix.lang.AddTable("english", {
optSteamESP = "Wyświetla dodatkowe informacje w Admin ESP",
optdSteamESP = "Wyświetla SteamID gracza oraz jego Zdrowie i Pancerz na Admin ESP",
optMapscenesESP = "Wyświetla Sceny Mapy ESP",
optdMapscenesESP = "Wyświetla lokalizacje Scen Mapy w Admin ESP.",
optAlwaysObserverLight = "Zawsze włączaj światło Obserwatora",
optdAlwaysObserverLight = "Włącza światło Obserwatora automatycznie przejściu do trybu Obserwatora. W przeciwnym razie będzie podążać za twoją latarką. Światło nadal można wyłączyć ręcznie.",
optObserverFullBright = "Full Bright jako światło Obserwatora",
optdObserverFullBright = "Oświetlenie całej mapy po włączeniu światła Obserwatora.",
optObserverPlayerHighlight = "Podświetl gracza",
optdObserverPlayerHighlight = "Czy gracze dostają aura podświetlającą ich modele"
})
ix.lang.AddTable("spanish", {
optdSteamESP = "Muestra el SteamID de un jugador, su salud/armadura en el admin ESP",
optdAlwaysObserverLight = "Enciende la luz del observer automáticamente al entrar en él. De lo contrario seguirá tu linterna. Se puede apagar manualmente.",
optAlwaysObserverLight = "Encender siempre la luz del observer",
optSteamESP = "Muestra la información extra del Admin ESP",
optdMapscenesESP = "Mostrar las localizaciones de Escenarios del Mapa en el Admin-ESP.",
optMapscenesESP = "Mostrar el ESP del Escenario"
})
ix.lang.AddTable("polish", {
optSteamESP = "Wyświetla dodatkowe informacje w Admin ESP",
optdSteamESP = "Wyświetla SteamID gracza oraz jego Zdrowie i Pancerz na Admin ESP",
optMapscenesESP = "Wyświetla Sceny Mapy ESP",
optdMapscenesESP = "Wyświetla lokalizacje Scen Mapy w Admin ESP.",
optAlwaysObserverLight = "Zawsze włączaj światło Obserwatora",
optdAlwaysObserverLight = "Włącza światło Obserwatora automatycznie przejściu do trybu Obserwatora. W przeciwnym razie będzie podążać za twoją latarką. Światło nadal można wyłączyć ręcznie.",
optObserverFullBright = "Full Bright jako światło Obserwatora",
optdObserverFullBright = "Oświetlenie całej mapy po włączeniu światła Obserwatora.",
optObserverPlayerHighlight = "Podświetl gracza",
optdObserverPlayerHighlight = "Czy gracze dostają aura podświetlającą ich modele"
})

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 entered observer.", client:Name())
end)
ix.log.AddType("observerExit", function(client, ...)
if (ix.option.Get(client, "observerTeleportBack", true)) then
return string.format("%s exited observer.", client:Name())
else
return string.format("%s exited observer at their location.", client:Name())
end
end)
function PLUGIN:CanPlayerEnterObserver(client)
if (CAMI.PlayerHasAccess(client, "Helix - Observer", nil)) then
return true
end
end
function PLUGIN:CanPlayerEnterVehicle(client, vehicle, role)
if (client:GetMoveType() == MOVETYPE_NOCLIP) then
return false
end
end
function PLUGIN:PlayerNoClip(client, state)
if (hook.Run("CanPlayerEnterObserver", client) or (!state and client.ixObsData)) then
if (state) then
client.ixObsData = {client:GetPos(), client:EyeAngles()}
-- Hide them so they are not visible.
client:SetNoDraw(true)
client:SetNotSolid(true)
client:DrawWorldModel(false)
client:DrawShadow(false)
client:GodEnable()
client:SetNoTarget(true)
hook.Run("OnPlayerObserve", client, state)
else
if (client.ixObsData) then
-- Move they player back if they want.
if (ix.option.Get(client, "observerTeleportBack", true)) then
local position, angles = client.ixObsData[1], client.ixObsData[2]
-- Do it the next frame since the player can not be moved right now.
timer.Simple(0, function()
client:SetPos(position)
client:SetEyeAngles(angles)
client:SetVelocity(Vector(0, 0, 0))
end)
end
client.ixObsData = nil
end
-- Make the player visible again.
client:SetNoDraw(false)
client:SetNotSolid(false)
client:DrawWorldModel(true)
client:DrawShadow(true)
client:GodDisable()
client:SetNoTarget(false)
hook.Run("OnPlayerObserve", client, state)
end
return true
end
end
function PLUGIN:OnPlayerObserve(client, state)
local flashlightOn = client:FlashlightIsOn()
if (state) then
if (flashlightOn) then
client:Flashlight(false)
end
client.ixObserverFlashlightReset = flashlightOn
client.ixObserverRestoreTP = nil
if (ix.config.Get("thirdperson")) then
net.Start("ixObserverDisableTP")
net.WriteBool(false)
net.Send(client)
end
ix.log.Add(client, "observerEnter")
else
local flashlightState = client.ixObserverFlashlightReset
client.ixObserverFlashlightReset = nil
if (flashlightOn != flashlightState) then
client:Flashlight(flashlightState)
end
if (ix.config.Get("thirdperson") and client.ixObserverRestoreTP) then
net.Start("ixObserverDisableTP")
net.WriteBool(true)
net.Send(client)
end
client.ixObserverRestoreTP = nil
ix.log.Add(client, "observerExit")
end
client:SetLocalVar("observerLight", state and (ix.option.Get(client, "alwaysObserverLight") or flashlightOn))
end
net.Receive("ixObserverDisableTP", function(len, client)
if (ix.config.Get("thirdperson")) then
client.ixObserverRestoreTP = true
end
end)
function PLUGIN:PlayerSwitchFlashlight(client, state)
if (!client.ixObsData or client.ixObserverFlashlightReset == nil) then return end
client:SetLocalVar("observerLight", !client:GetLocalVar("observerLight"))
net.Start("ixObserverFlashlight")
net.Send(client)
return false
end
function PLUGIN:OnItemSpawned(entity, bOnLoad)
entity:SetNetVar("spawnTime", os.time())
local owner = entity:GetNetVar("owner")
if (owner and ix.char.loaded[owner]) then
entity:SetNetVar("ownerName", ix.char.loaded[owner]:GetName())
else
entity:SetNetVar("ownerName", bOnLoad and "mapload" or "spawned")
end
entity:SetNetVar("itemID", entity.ixItemID)
end
function PLUGIN:OnSavedItemLoaded(items, entities)
for _, v in ipairs(entities) do
self:OnItemSpawned(v, true)
end
end
function PLUGIN:PlayerLoadedCharacter(client, character)
client.ixObsData = nil
client:SetLocalVar("observerLight", false)
end

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 = "Adds commands to blind the players by making their screen completely black."
--luacheck: globals BLIND_NONE BLIND_TARGET BLIND_ALL
BLIND_NONE = 0
BLIND_TARGET = 1
BLIND_ALL = 2
CAMI.RegisterPrivilege({
Name = "Helix - Manage Blind",
MinAccess = "admin"
})
ix.command.Add("PlyBlind", {
description = "Blinds the specified player.",
privilege = "Manage Blind",
arguments = {
ix.type.player
},
OnRun = function(self, client, target)
if (IsValid(target)) then
target:SetBlind(BLIND_TARGET)
else
client:NotifyLocalized("plyNotValid")
end
end
})
ix.command.Add("PlyUnBlind", {
description = "Unblinds the specified player.",
privilege = "Manage Blind",
arguments = {
ix.type.player
},
OnRun = function(self, client, target)
if (IsValid(target)) then
target:SetBlind(BLIND_NONE)
else
client:NotifyLocalized("plyNotValid")
end
end
})
PLUGIN.blindAll = false
ix.command.Add("PlyBlindAll", {
description = "Blinds all players on the server.",
privilege = "Manage Blind",
OnRun = function(self, client)
for _, v in ipairs(player.GetAll()) do
v:SetBlind(BLIND_ALL)
end
PLUGIN.blindAll = true
end
})
ix.command.Add("PlyUnBlindAll", {
description = "Unblinds all players on the server.",
privilege = "Manage Blind",
OnRun = function(self, client)
for _, v in ipairs(player.GetAll()) do
v:SetBlind(BLIND_NONE)
end
PLUGIN.blindAll = false
end
})
ix.char.RegisterVar("blind", {
field = "blind",
fieldType = ix.type.number,
default = BLIND_NONE,
isLocal = true,
bNoDisplay = true
})
if (CLIENT) then
local wasBlind = false
local scrW, scrH = ScrW(), ScrH()
function PLUGIN:HUDPaintBackground()
if (self.blind) then
local curTime = CurTime()
local textTime = 5
local client = LocalPlayer()
local reduceBlindness = client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()
draw.RoundedBox(0, 0, 0, scrW, scrH, Color(0, 0, 0, reduceBlindness and 200 or 255))
if (!wasBlind) then
wasBlind = curTime + textTime
elseif (isnumber(wasBlind) and curTime < wasBlind) then
local timeLeft = wasBlind - curTime
local text = "You have been blinded by the administration."
local font = "WNBleedingText"
surface.SetFont(font)
local w, h = surface.GetTextSize(text)
local x, y = scrW * 0.5, scrH * 0.75
draw.SimpleTextOutlined(text, font, x - w * 0.5, y - h * 0.5, Color(255, 255, 255, 510 * timeLeft * 0.4), nil, nil, 1, Color(0, 0, 0, 510 * timeLeft * 0.4))
else
wasBlind = true
end
end
end
function PLUGIN:CharacterLoaded(character)
local blind = character:GetBlind()
if (blind == BLIND_TARGET or PLUGIN.blindAll) then
PLUGIN.blind = true
elseif (blind == BLIND_ALL and !PLUGIN.blindAll) then
PLUGIN.blind = false
end
end
function PLUGIN:ShouldDrawCrosshair()
if (self.blind) then
return false
end
end
netstream.Hook("ixBlindPlayer", function(blind)
local delay = 1
blind = blind != BLIND_NONE
LocalPlayer():ScreenFade(blind and SCREENFADE.OUT or SCREENFADE.IN, Color(0, 0, 0, 255), delay, 0)
if (blind) then
wasBlind = false
timer.Simple(delay, function()
PLUGIN.blind = blind
end)
else
PLUGIN.blind = blind
end
end)
else
local playerMeta = FindMetaTable("Player")
function playerMeta:SetBlind(blind)
if (self:GetCharacter()) then
self:GetCharacter():SetBlind(blind)
end
netstream.Start(self, "ixBlindPlayer", blind)
end
function playerMeta:GetBlind()
if (self:GetCharacter()) then
return self:GetCharacter():GetBlind()
end
return BLIND_NONE
end
end

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 Manager")
DFrameFixer(self)
self.clipboard = self:Add("DButton")
self.clipboard:Dock(BOTTOM)
self.clipboard:DockMargin(0, SScaleMin(4 / 3), 0, 0)
self.clipboard:SetText("Save Changes")
self.clipboard:SetFont("MenuFontBoldNoClamp")
self.clipboard:SetTall(SScaleMin(50 / 3))
self.clipboard.DoClick = function()
LocalPlayer():NotifyLocalized("You have set this character's bodygroups.")
local bodygroups = {}
for _, v in pairs(self.bodygroupIndex) do
table.insert(bodygroups, v.index, v.value)
end
local color = self.colorPicker:GetValue()
net.Start("ixBodygroupTableSet")
net.WriteEntity(self.target)
net.WriteTable(bodygroups)
net.WriteTable(color)
net.SendToServer()
end
self.model = self:Add("ixModelPanel")
self.model.rotating = true
self.model:Dock(FILL)
self.model:SetFOV(35)
self.model:SetModel(Model("models/props_junk/watermelon01.mdl"))
self.bodygroupPanel = self:Add("DScrollPanel")
self.bodygroupPanel:Dock(RIGHT)
self.bodygroupPanel:DockMargin(0, SScaleMin(7 / 3), 0, 0)
self.bodygroupPanel:SetWide(self:GetWide() - SScaleMin(300 / 3))
self.bodygroups = {}
PLUGIN.viewer = self
end
function PANEL:SetTarget(target, proxyColors)
self.target = target
self:PopulateBodygroupOptions(proxyColors)
self:SetTitle(target:GetName())
self.model.Entity.overrideProxyColors = proxyColors
end
function PANEL:PopulateBodygroupOptions(proxyColors)
self.bodygroupBox = {}
self.bodygroupName = {}
self.bodygroupPrevious = {}
self.bodygroupNext = {}
self.bodygroupIndex = {}
for k, v in pairs(self.target:GetBodyGroups()) do
-- Disregard the model bodygroup.
if !(v.id == 0) then
local index = v.id
self.bodygroupBox[v.id] = self.bodygroupPanel:Add("DPanel")
self.bodygroupBox[v.id]:Dock(TOP)
self.bodygroupBox[v.id]:DockMargin(0, SScaleMin(25 / 3), SScaleMin(25 / 3), 0)
self.bodygroupBox[v.id]:SetHeight(SScaleMin(50 / 3))
self.bodygroupBox[v.id].Paint = function(self, w, h)
surface.SetDrawColor(Color(40, 40, 40, 100))
surface.DrawRect(0, 0, w, h)
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
surface.DrawOutlinedRect(0, 0, w, h)
end
local hairBg = self.model.Entity:FindBodygroupByName("hair")
self.bodygroupName[v.id] = self.bodygroupBox[v.id]:Add("DLabel")
self.bodygroupName[v.id].index = v.id
self.bodygroupName[v.id]:SetText(v.name:gsub("^%l", string.utf8upper))
self.bodygroupName[v.id]:SetFont("TitlesFontNoClamp")
self.bodygroupName[v.id]:Dock(LEFT)
self.bodygroupName[v.id]:DockMargin(SScaleMin(30 / 3), 0, 0, 0)
self.bodygroupName[v.id]:SetWidth(SScaleMin(200 / 3))
self.bodygroupNext[v.id] = self.bodygroupBox[v.id]:Add("DButton")
self.bodygroupNext[v.id].index = v.id
self.bodygroupNext[v.id]:Dock(RIGHT)
self.bodygroupNext[v.id]:SetFont("MenuFontNoClamp")
self.bodygroupNext[v.id]:SetText("Next")
self.bodygroupNext[v.id]:SetWide(SScaleMin(100 / 3))
self.bodygroupNext[v.id].DoClick = function()
local index = v.id
if (self.model.Entity:GetBodygroupCount(index) - 1) <= self.bodygroupIndex[index].value then
return
end
self.bodygroupIndex[index].value = self.bodygroupIndex[index].value + 1
self.bodygroupIndex[index]:SetText(self.bodygroupIndex[index].value)
self.model.Entity:SetBodygroup(index, self.bodygroupIndex[index].value)
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
end
self.bodygroupIndex[v.id] = self.bodygroupBox[v.id]:Add("DLabel")
self.bodygroupIndex[v.id].index = v.id
self.bodygroupIndex[v.id].value = self.target:GetBodygroup(index)
self.bodygroupIndex[v.id]:SetText(self.bodygroupIndex[v.id].value)
self.bodygroupIndex[v.id]:SetFont("TitlesFontNoClamp")
self.bodygroupIndex[v.id]:Dock(RIGHT)
self.bodygroupIndex[v.id]:SetContentAlignment(5)
self.bodygroupPrevious[v.id] = self.bodygroupBox[v.id]:Add("DButton")
self.bodygroupPrevious[v.id].index = v.id
self.bodygroupPrevious[v.id]:Dock(RIGHT)
self.bodygroupPrevious[v.id]:SetFont("MenuFontNoClamp")
self.bodygroupPrevious[v.id]:SetText("Previous")
self.bodygroupPrevious[v.id]:SetWide(SScaleMin(100 / 3))
self.bodygroupPrevious[v.id].DoClick = function()
local index = v.id
if 0 == self.bodygroupIndex[index].value then
return
end
self.bodygroupIndex[index].value = self.bodygroupIndex[index].value - 1
self.bodygroupIndex[index]:SetText(self.bodygroupIndex[index].value)
self.model.Entity:SetBodygroup(index, self.bodygroupIndex[index].value)
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
end
self.model.Entity:SetBodygroup(index, self.target:GetBodygroup(index))
local hairValue = self.bodygroupIndex[hairBg] and self.bodygroupIndex[hairBg].value
self.model:SetCorrectHair(v.name == "headwear" and hairValue)
end
end
local hairColor = self.bodygroupPanel:Add("DPanel")
hairColor:Dock(TOP)
hairColor:DockMargin(0, SScaleMin(25 / 3), SScaleMin(25 / 3), 0)
hairColor:SetHeight(SScaleMin(50 / 3))
hairColor.Paint = function(this, w, h)
surface.SetDrawColor(Color(40, 40, 40, 100))
surface.DrawRect(0, 0, w, h)
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
surface.DrawOutlinedRect(0, 0, w, h)
end
self.colorPicker = hairColor:Add("ixSettingsRowColor")
self.colorPicker:Dock(FILL)
self.colorPicker:SetText("Hair Color")
if proxyColors["HairColor"] then
self.colorPicker:SetValue(proxyColors["HairColor"])
end
self.colorPicker.OnValueChanged = function(this, newColor)
proxyColors["HairColor"] = Color(newColor.r, newColor.g, newColor.b)
self.model.Entity.GetProxyColors = function()
return proxyColors
end
end
end
function PANEL:SetViewModel(model)
self.playerModel = model
if model then
self.model:SetModel(Model(model))
end
end
vgui.Register("ixBodygroupView", PANEL, "DFrame")

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 = "Dostosuj bodygroupy celu."
})
ix.lang.AddTable("spanish", {
cmdEditBodygroup = "Personalizar los bodygroups de un objetivo."
})
ix.lang.AddTable("polish", {
cmdEditBodygroup = "Dostosuj bodygroupy celu."
})
ix.command.Add("CharEditBodygroup", {
description = "cmdEditBodygroup",
adminOnly = true,
arguments = {
bit.bor(ix.type.player, ix.type.optional)
},
OnRun = function(self, client, target)
net.Start("ixBodygroupView")
net.WriteEntity(target or client)
net.WriteTable(target:GetCharacter():GetProxyColors() or {})
net.Send(client)
end
})
ix.util.Include("sv_hooks.lua")
ix.util.Include("cl_hooks.lua")

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 has changed %s's bodygroups.", client:GetName(), target:GetName())
end)
net.Receive("ixBodygroupTableSet", function(length, client)
if (!ix.command.HasAccess(client, "CharEditBodygroup") and !client:IsCombine()) then return end
local target = net.ReadEntity()
if (client:IsCombine() and !ix.command.HasAccess(client, "CharEditBodygroup") and target != client) then return end
if (!IsValid(target) or !target:IsPlayer() or !target:GetCharacter()) then
return
end
local bodygroups = net.ReadTable()
local groups = {}
for k, v in pairs(bodygroups) do
target:SetBodygroup(tonumber(k) or 0, tonumber(v) or 0)
groups[tonumber(k) or 0] = tonumber(v) or 0
local hairBG = client:FindBodygroupByName( "hair" )
if k != hairBG then continue end
if !client:GetModel():find("models/willardnetworks/citizens/") then continue end
local curHeadwearBG = client:GetBodygroup(client:FindBodygroupByName( "headwear" ))
local curHairBG = client:GetBodygroup(hairBG)
local hairBgLength = 0
for _, v2 in pairs(client:GetBodyGroups()) do
if v2.name != "hair" then continue end
if !v2.submodels then continue end
if !istable(v2.submodels) then continue end
hairBgLength = #v2.submodels
break
end
if (curHeadwearBG != 0) then
if curHairBG != 0 then
client:SetBodygroup(hairBG, hairBgLength)
end
end
end
target:GetCharacter():SetData("groups", groups)
local hairColor = net.ReadTable()
local charProxies = target:GetCharacter():GetProxyColors() or {}
charProxies["HairColor"] = Color(hairColor.r, hairColor.g, hairColor.b)
target:GetCharacter():SetProxyColors(charProxies)
ix.log.Add(client, "bodygroupEditor", target)
end)

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 = "appearance",
hidden = function()
return ix.config.Get("forceImmersiveGlasses")
end
})
netstream.Hook("OpenBeardStyling", function(target)
if (IsValid(ix.gui.menu)) then
ix.gui.menu:Remove()
end
if IsValid(beardStyling) then
beardStyling:Remove()
end
local beardStyling = vgui.Create("BeardStyling")
beardStyling:CustomInit(target)
end)
netstream.Hook("GetStylingConfirmation", function(attempter)
Derma_Query("Ktoś chce stylizować twoje włosy/brodę! Czy chcesz na to pozwolić?", "Stylizacja włosów/brody", "Pozwól", function()
netstream.Start("AcceptStyling", attempter)
end, "Disallow")
end)
-- Called when blurry screen space effects should be rendered.
function PLUGIN:RenderScreenspaceEffects()
if (!ix.config.Get("forceImmersiveGlasses")) then
if (!ix.option.Get("useImmersiveGlasses", true)) then return end
end
local client = LocalPlayer()
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then return end
local character = client:GetCharacter()
if (!character) then return end
local needsGlasses = character:GetGlasses()
local hasGlasses = character:HasGlasses()
if ((needsGlasses and !hasGlasses) or (!needsGlasses and hasGlasses)) then
DrawToyTown(28,ScrH())
end
end

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("Stylizacja włosów/brody")
titleText:DockMargin(SScaleMin(10 / 3), 0, 0, 0)
titleText:SetContentAlignment(4)
titleText:SizeToContents()
local exit = topbar:Add("DImageButton")
exit:SetImage("willardnetworks/tabmenu/navicons/exit.png")
exit:SetSize(SScaleMin(20 / 3), SScaleMin(20 / 3))
exit:DockMargin(0, SScaleMin(15 / 3), SScaleMin(10 / 3), SScaleMin(15 / 3))
exit:Dock(RIGHT)
exit.DoClick = function()
self:Remove()
surface.PlaySound("helix/ui/press.wav")
end
end
function PANEL:DrawModel()
local target = self.target or LocalPlayer()
local characterModelList = self.innerContent:Add("Panel")
characterModelList:Dock(RIGHT)
characterModelList:SetWide(self.innerContent:GetWide() * 0.5)
characterModelList.Paint = function(this, w, h)
surface.SetDrawColor(Color(255, 255, 255, 30));
surface.DrawOutlinedRect(0, 0, w, h)
end
local imgBackground = characterModelList:Add("DImage")
imgBackground:SetImage(ix.faction.indices[target:Team()].inventoryImage or
"materials/willardnetworks/tabmenu/inventory/backgrounds/street.png")
imgBackground:SetKeepAspect(true)
imgBackground:Dock(FILL)
imgBackground:DockMargin(1, 1, 1, 1)
self.characterModel = imgBackground:Add("ixModelPanel")
self.characterModel:Dock(FILL)
local styleButton = characterModelList:Add("DButton")
styleButton:Dock(BOTTOM)
styleButton:SetFont("MenuFontLargerBoldNoFix")
styleButton:SetText("Stylizuj")
styleButton:SetTall(SScaleMin(50 / 3))
styleButton.DoClick = function()
surface.PlaySound("helix/ui/press.wav")
local char = target.GetCharacter and target:GetCharacter()
local hairBG = target:FindBodygroupByName("hair")
local beard = target:FindBodygroupByName("beard")
local curHair = target:GetBodygroup(hairBG)
local curBeard = target:GetBodygroup(beard)
local newHair = self.characterModel.Entity:GetBodygroup(hairBG)
local newBeard = self.characterModel.Entity:GetBodygroup(beard)
local curHairColor = char and char.GetHair and char:GetHair() and char:GetHair().color or color_white
local newHairColor = self.hairColor
if (curHair != newHair) or (curBeard != newBeard) or (curHairColor and newHairColor and curHairColor != newHairColor) then
netstream.Start("SetHairBeardBodygroup", newHair, newBeard, newHairColor, target)
target:NotifyLocalized("Pomyślnie wystylizowałeś swoją brodę/włosy.")
self:Remove()
else
target:NotifyLocalized("Nie możesz wybrać tych samych włosów/brody, jakie już masz!")
end
end
styleButton.Paint = function(this, w, h)
Paint(this, w, h)
end
self:SetCharacter()
local bone = self.characterModel.Entity:LookupBone("ValveBiped.Bip01_Head1")
if bone then
local eyepos = self.characterModel.Entity:GetBonePosition( bone )
self.characterModel:SetLookAt(eyepos)
self.characterModel:SetCamPos(eyepos-Vector(-12, -12, 0)) -- Move cam in front of eyes
self.characterModel:SetFOV(60)
self.characterModel.PaintModel = self.characterModel.Paint
end
end
function PANEL:SetCharacter()
local target = self.target or LocalPlayer()
local model = target:GetModel()
self.characterModel:SetModel(model, target:GetSkin(), true)
local isCP = model:find("wn7new")
local indexName = isCP and "cp_Head" or "headwear"
local index = target:FindBodygroupByName(indexName)
if index != -1 then
if self.characterModel.Entity:GetBodygroup(index) > 0 then
self.characterModel.Entity:SetBodygroup(index, 0)
end
end
local curHair = target:GetCharacter():GetHair()
local hairValue = curHair and curHair.hair
if hairValue then
local hairIndex = target:FindBodygroupByName("hair")
self.characterModel.Entity:SetBodygroup(hairIndex, hairValue)
end
end
function PANEL:DrawLeftSide()
self.leftSide = self.innerContent:Add("Panel")
self.leftSide:Dock(LEFT)
self.leftSide:SetWide(self.innerContent:GetWide() * 0.5)
self.leftSide.Paint = function(this, w, h)
surface.SetDrawColor(Color(255, 255, 255, 10));
surface.DrawRect(0, 0, w, h )
surface.SetDrawColor(Color(255, 255, 255, 30));
surface.DrawOutlinedRect(0, 0, w + 1, h)
end
self:DrawShaveTrimButtons()
local beardBodygroup = self.characterModel.Entity:GetBodygroup(11)
local beardPart = self.leftSide:Add("DScrollPanel")
beardPart:Dock(BOTTOM)
beardPart:SetTall((beardBodygroup != 5 and beardBodygroup != 8 and SScaleMin(100 / 3)) or (self.innerContent:GetTall() - SScaleMin(50 / 3)) * 0.5)
beardPart.Paint = function(this, w, h)
surface.SetDrawColor(color_white)
surface.DrawRect(0, 0, w, 1)
end
beardPart:DockMargin(0, padding * 3 - 1, 0, 0)
local hairParts = self.leftSide:Add("Panel")
hairParts:Dock(FILL)
local hairPart = hairParts:Add("DScrollPanel")
hairPart:Dock(LEFT)
hairPart:SetWide(self.innerContent:GetWide() * 0.25)
local hairColorPart = hairParts:Add("DScrollPanel")
hairColorPart:Dock(FILL)
local titleText = hairPart:Add("DLabel")
titleText:SetFont("CharCreationBoldTitleNoClamp")
titleText:Dock(TOP)
titleText:SetText("Włosy")
titleText:DockMargin(0, padding * 3 - 1, 0, 0)
titleText:SetContentAlignment(5)
titleText:SizeToContents()
local titleText2 = hairColorPart:Add("DLabel")
titleText2:SetFont("CharCreationBoldTitleNoClamp")
titleText2:Dock(TOP)
titleText2:SetText("Kolor włosów")
titleText2:DockMargin(0, padding * 3 - 1, 0, 0)
titleText2:SetContentAlignment(5)
titleText2:SizeToContents()
self:DrawHairButtons(hairPart, hairColorPart)
if beardBodygroup == 5 or beardBodygroup == 8 then
self:DrawBeardButtons(beardPart)
else
local notEnough = beardPart:Add("DLabel")
notEnough:SetText("Masz zbyt krótki zarost do stylizacji...")
notEnough:SetFont("MenuFontLargerBoldNoFix")
notEnough:SetContentAlignment(5)
notEnough:Dock(TOP)
notEnough:DockMargin(0, padding * 3 - 1, 0, 0)
end
end
function PANEL:DrawBeardButtons(beardPart)
local target = self.target or LocalPlayer()
for i = 1, 6 do
local beardButton = beardPart:Add("DButton")
local sideMargins = self.leftSide:GetWide() * 0.1
beardButton:Dock(TOP)
beardButton:SetTall(SScaleMin(50 / 3))
beardButton:SetFont("MenuFontLargerBoldNoFix")
beardButton:SetText("Styl "..i)
beardButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
beardButton.DoClick = function()
surface.PlaySound("helix/ui/press.wav")
local beard = target:FindBodygroupByName("beard")
if i == 5 then
self.characterModel.Entity:SetBodygroup(beard, 6)
elseif i == 6 then
self.characterModel.Entity:SetBodygroup(beard, 7)
else
self.characterModel.Entity:SetBodygroup(beard, i)
end
end
beardButton.OnCursorEntered = function()
surface.PlaySound("helix/ui/rollover.wav")
end
beardButton.Paint = function(this, w, h)
Paint(this, w, h)
end
end
end
function PANEL:DrawHairButtons(hairPart, hairColorPart)
local target = self.target or LocalPlayer()
local gender = target.GetCharacter and target:GetCharacter():GetGender() or "female"
local hairs = {}
for i = 0, gender == "female" and 14 or 12 do hairs[#hairs + 1] = i end
local curStyle = 0
for _, hairID in pairs(hairs) do
curStyle = curStyle + 1
local hairButton = hairPart:Add("DButton")
local sideMargins = self.leftSide:GetWide() * 0.1
hairButton:Dock(TOP)
hairButton:SetTall(SScaleMin(50 / 3))
hairButton:SetFont("MenuFontLargerBoldNoFix")
hairButton:SetText("Styl "..curStyle)
hairButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
hairButton.DoClick = function()
local hairBG = target:FindBodygroupByName("hair")
surface.PlaySound("helix/ui/press.wav")
self.characterModel.Entity:SetBodygroup(hairBG, hairID)
end
hairButton.OnCursorEntered = function()
surface.PlaySound("helix/ui/rollover.wav")
end
hairButton.Paint = function(this, w, h)
Paint(this, w, h)
end
end
for _, colorTable in pairs(ix.allowedHairColors) do
for _, color in pairs(colorTable) do
local hairButton = hairColorPart:Add("DButton")
local sideMargins = self.leftSide:GetWide() * 0.1
hairButton:Dock(TOP)
hairButton:SetTall(SScaleMin(50 / 3))
hairButton:SetFont("MenuFontLargerBoldNoFix")
hairButton:SetText("")
hairButton:DockMargin(sideMargins, padding * 3 - 1, sideMargins, 0)
hairButton.DoClick = function()
surface.PlaySound("helix/ui/press.wav")
if self.characterModel and IsValid(self.characterModel) then
self.characterModel.overrideProxyColors = {HairColor = Vector(color.r / 255, color.g / 255, color.b / 255)}
end
self.hairColor = color
end
hairButton.OnCursorEntered = function()
surface.PlaySound("helix/ui/rollover.wav")
end
hairButton.Paint = function(this, w, h)
surface.SetDrawColor(color)
surface.DrawRect(0, 0, w, h)
end
end
end
end
function PANEL:DrawShaveTrimButtons()
local target = self.target or LocalPlayer()
local buttonPanel = self.leftSide:Add("Panel")
buttonPanel:Dock(BOTTOM)
buttonPanel:SetTall(SScaleMin(50 / 3))
local shaveButton = buttonPanel:Add("DButton")
shaveButton:Dock(FILL)
shaveButton:SetText("Ogól")
shaveButton:SetFont("MenuFontLargerBoldNoFix")
shaveButton:SetWide(self.leftSide:GetWide() * 0.5)
shaveButton.DoClick = function()
surface.PlaySound("willardnetworks/charactercreation/boop1.wav")
self:CreateWarningPanel()
end
local gender = target.GetCharacter and target:GetCharacter():GetGender() or "female"
if gender == "female" then shaveButton:SetDisabled(true) end
shaveButton.Paint = function(this, w, h)
if gender == "female" then
surface.SetDrawColor(255, 255, 255, 5)
surface.DrawRect(0, 0, w, h)
return
end
Paint(this, w, h)
end
local beardBodygroup = self.characterModel.Entity:GetBodygroup(11)
if beardBodygroup == 0 then
shaveButton:SetDisabled(true)
shaveButton.Paint = function(this, w, h)
surface.SetDrawColor(255, 255, 255, 5)
surface.DrawRect(0, 0, w, h)
end
shaveButton.OnCursorEntered = function() end
end
end
function PANEL:CreateWarningPanel()
local warningPanel = vgui.Create("Panel")
warningPanel:SetAlpha(0)
warningPanel:MakePopup()
warningPanel:SetSize(ScrW(), ScrH())
warningPanel:AlphaTo(255, 0.5, 0)
warningPanel.Paint = function(this, w, h)
surface.SetDrawColor(0, 0, 0, 230)
surface.DrawRect(0, 0, w, h)
end
local warningContent = warningPanel:Add("Panel")
warningContent:SetSize(ScrW() * 0.4, SScaleMin(95 / 3))
warningContent:Center()
local label = warningContent:Add("DLabel")
label:SetFont("CharCreationBoldTitleNoClamp")
label:SetText("Ta akcja usunie twój zarost, czy jesteś pewien?")
label:SetContentAlignment(5)
label:Dock(TOP)
label:SizeToContents()
local warningButtons = warningContent:Add("Panel")
warningButtons:Dock(TOP)
warningButtons:DockMargin(0, padding, 0, 0)
warningButtons:SetTall(SScaleMin(50 / 3))
local yes = warningButtons:Add("DButton")
yes:Dock(LEFT)
yes:SetWide(warningContent:GetWide() * 0.5)
yes:SetText("TAK")
yes:SetFont("CharCreationBoldTitleNoClamp")
yes:SetContentAlignment(6)
yes:SetTextColor(Color(200, 200, 200, 255))
yes:SetTextInset(padding * 2, 0)
yes.Paint = function(this, w, h)
if this:IsHovered() then
this:SetTextColor(Color(255, 255, 255, 255))
else
this:SetTextColor(Color(200, 200, 200, 255))
end
end
local target = self.target or LocalPlayer()
yes.DoClick = function()
surface.PlaySound("helix/ui/press.wav")
surface.PlaySound("npc/antlion/idle1.wav")
warningPanel:AlphaTo(0, 0.5, 0, function()
local beard = target:FindBodygroupByName("beard")
warningPanel:Remove()
self:Remove()
netstream.Start("RemoveBeardBodygroup", target)
self.characterModel.Entity:SetBodygroup(beard, 0)
end)
end
local no = warningButtons:Add("DButton")
no:Dock(RIGHT)
no:SetWide(warningContent:GetWide() * 0.5)
no:SetText("NIE")
no:SetFont("CharCreationBoldTitleNoClamp")
no:SetTextColor(Color(200, 200, 200, 255))
no:SetContentAlignment(4)
no:SetTextInset(padding * 2, 0)
no.Paint = function(this, w, h)
if this:IsHovered() then
this:SetTextColor(Color(255, 255, 255, 255))
else
this:SetTextColor(Color(200, 200, 200, 255))
end
end
no.DoClick = function()
surface.PlaySound("helix/ui/press.wav")
warningPanel:AlphaTo(0, 0.5, 0, function()
warningPanel:Remove()
end)
end
yes.OnCursorEntered = function()
surface.PlaySound("helix/ui/rollover.wav")
end
no.OnCursorEntered = function()
surface.PlaySound("helix/ui/rollover.wav")
end
end
function PANEL:SetTarget(target)
self.target = target
end
vgui.Register("BeardStyling", PANEL, "EditablePanel")

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/
--]]
local PLUGIN = PLUGIN
ITEM.name = "[Odtwarzacz Audio] Naucz się czytać"
ITEM.uniqueID = "audiobook_reading"
ITEM.model = "models/props_lab/reciever01d.mdl"
ITEM.width = 1
ITEM.height = 1
ITEM.description = "Słuchając tego prostego urządzenia poprawisz swoją zdolność czytania."
ITEM.category = "Audiobooks"
ITEM.functions.Listen = {
OnRun = function(itemTable)
local client = itemTable.player
local character = client:GetCharacter()
character:SetCanread(true)
client:NotifyLocalized("Czuję się teraz znacznie lepszy w czytaniu.")
end
}

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 = "Narzędzia fryzjera"
ITEM.uniqueID = "beard_razor"
ITEM.model = "models/props_junk/cardboard_box004a.mdl"
ITEM.width = 1
ITEM.height = 1
ITEM.description = "Narzędzia dla twórczych umysłów w tym przygnębiającym świecie."
ITEM.category = "Tools"
ITEM.functions.Style = {
icon = "icon16/paintbrush.png",
OnRun = function(itemTable)
local client = itemTable.player
if !client or client and !IsValid(client) then return end
local model = client:GetModel()
if model:find("willardnetworks/citizens/") or model:find("wn7new") then
if !client.CantPlace then
client.CantPlace = true
netstream.Start(client, "OpenBeardStyling")
timer.Simple(3, function()
if client then
client.CantPlace = false
end
end)
else
client:NotifyLocalized("Musisz poczekać, zanim będziesz mógł to zrobić!")
return false
end
else
client:NotifyLocalized("Nie masz włosów/brody do stylizacji!")
return false
end
return false
end
}
ITEM.functions.Style2 = {
icon = "icon16/paintbrush.png",
name = "Stylizuj kogoś",
OnRun = function(itemTable)
local client = itemTable.player
if !client or client and !IsValid(client) then return end
local target = client:GetEyeTraceNoCursor().Entity
if !target or target and !IsValid(target) then
client:Notify("Nieprawidłowy cel!")
return false
end
if target:GetModel():find("willardnetworks/citizens/") then
if !client.CantPlace then
client.CantPlace = true
if target:IsBot() then
netstream.Start(client, "OpenBeardStyling", target)
else
netstream.Start(target, "GetStylingConfirmation", client)
end
timer.Simple(3, function()
if client then
client.CantPlace = false
end
end)
else
client:NotifyLocalized("Musisz poczekać, zanim będziesz mógł to zrobić!")
return false
end
else
client:NotifyLocalized("Cel nie ma włosów/brody do stylizacji!")
return false
end
return false
end
}

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.."' is missing a name. You need to add a FACTION.name = \"Name\"\n")
end
if (!FACTION.description) then
FACTION.description = "noDesc"
ErrorNoHalt("Faction '"..niceName.."' is missing a description. You need to add a FACTION.description = \"Description\"\n")
end
if (!FACTION.color) then
FACTION.color = Color(150, 150, 150)
ErrorNoHalt("Faction '"..niceName.."' is missing a color. You need to add FACTION.color = Color(1, 2, 3)\n")
end
team.SetUp(FACTION.index, FACTION.name or "Unknown", FACTION.color or Color(125, 125, 125))
FACTION.models = FACTION.models or CITIZEN_MODELS
FACTION.uniqueID = FACTION.uniqueID or niceName
for _, v2 in pairs(FACTION.models) do
if (isstring(v2)) then
util.PrecacheModel(v2)
elseif (istable(v2)) then
util.PrecacheModel(v2[1])
end
end
if (!FACTION.GetModels) then
function FACTION:GetModels(client)
return self.models
end
end
-- GENDERS
if (!FACTION.GetModelsMale) then
function FACTION:GetModelsMale(client)
return self.models.male
end
end
if (!FACTION.GetModelsFemale) then
function FACTION:GetModelsFemale(client)
return self.models.female
end
end
if (!FACTION.GetNoGender) then
function FACTION:GetNoGender(client)
return self.noGender
end
end
if (!FACTION.GetNoGenetics) then
function FACTION:GetNoGenetics(client)
return self.noGenetics
end
end
if (!FACTION.GetNoAppearances) then
function FACTION:GetNoAppearances(client)
return self.noAppearances
end
end
if (!FACTION.GetReadOptionDisabled) then
function FACTION:GetReadOptionDisabled(client)
return self.ReadOptionDisabled
end
end
if (!FACTION.GetNoBackground) then
function FACTION:GetNoBackground(client)
return self.noBackground
end
end
ix.faction.indices[FACTION.index] = FACTION
ix.faction.teams[niceName] = FACTION
FACTION = nil
end
end
ix.command.Add("PlyWhitelist", {
description = "@cmdPlyWhitelist",
privilege = "Manage Character Whitelist",
superAdminOnly = true,
arguments = {
ix.type.player,
ix.type.text
},
OnRun = function(self, client, target, name)
if (name == "") then
return "@invalidArg", 2
end
local faction = ix.faction.teams[name]
if (!faction) then
for _, v in ipairs(ix.faction.indices) do
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
faction = v
break
end
end
end
if (faction) then
local result, text = hook.Run("CanWhitelistPlayer", target, faction)
if (result == false) then
return "@"..(text or "invalidFaction")
end
if (target:SetWhitelisted(faction.index, true)) then
if faction.OnWhitelist then
faction:OnWhitelist(target)
end
for _, v in ipairs(player.GetAll()) do
if (self:OnCheckAccess(v) or v == target) then
v:NotifyLocalized("whitelist", client:GetName(), target:GetName(), L(faction.name, v))
end
end
end
else
return "@invalidFaction"
end
end
})

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 = "Użyj immersyjnych okularów",
optdUseImmersiveGlasses = "Używaj okularów immersyjnych. Jeśli postać potrzebuje okularów, a ich nie używa, to obraz staje się rozmyty."
})
ix.lang.AddTable("spanish", {
optdUseImmersiveGlasses = "Utiliza las gafas de inmersión, difuminando la vista de tu personaje si necesita gafas y no las lleva puestas.",
optUseImmersiveGlasses = "Utilizar gafas inmersivas"
})
ix.lang.AddTable("polish", {
optUseImmersiveGlasses = "Użyj immersyjnych okularów",
optdUseImmersiveGlasses = "Używaj okularów immersyjnych. Jeśli postać potrzebuje okularów, a ich nie używa, to obraz staje się rozmyty."
})
ix.char.RegisterVar("glasses", {
field = "glasses",
fieldType = ix.type.bool,
default = false,
isLocal = true,
bNoDisplay = true
})
ix.char.RegisterVar("canread", {
field = "canread",
fieldType = ix.type.bool,
default = true,
isLocal = true,
bNoDisplay = true
})
ix.char.RegisterVar("beardProgress", {
field = "beard",
fieldType = ix.type.number,
default = 0,
bNoNetworking = true,
bNoDisplay = true
})
ix.allowedHairColors = {
szare = {
Color(244,233,230),
Color(221,202,195),
Color(182,170,165),
Color(151,132,126),
Color(111,101,98),
Color(126,122,121),
Color(89,89,89)
},
brazowe = {
Color(95,52,39),
Color(101,66,56),
Color(62,50,47),
Color(80,69,66),
Color(138,106,96),
Color(164,149,137),
Color(85,72,56),
Color(83,61,50)
},
jasne = {
Color(223,186,155),
Color(172,129,94),
Color(145,124,109),
Color(229,200,170),
Color(203,191,177),
Color(184,151,120),
Color(230,206,168),
Color(255,216,149)
},
["miekki niebieski"] = {
Color(161,165,167),
Color(125,132,135)
}
}
ix.char.RegisterVar("hair", {
field = "hair",
fieldType = ix.type.table,
default = {},
isLocal = true,
bNoDisplay = true,
OnValidate = function(self, data, payload, client)
if !payload.hair or payload.hair and !istable(payload.hair) then
return false, "Nie wybrano włosów/koloru włosów!"
end
if !istable(payload.hair) then
return false, "Coś poszło nie tak przy wyborze włosów!"
end
if !payload.hair.hair then
return false, "Nie wybrano włosów!"
end
if !payload.hair.color then
return false, "Nie wybrano koloru włosów!"
end
local found = false
for _, v in pairs(ix.allowedHairColors) do
if !table.HasValue(v, payload.hair.color) then continue end
found = true
break
end
if !found then
return false, "Nie wybrano dozwolonego koloru włosów!"
end
if !isnumber(payload.hair.hair) then
return false, "Nie wybrano dozwolonych włosów!"
end
return true
end,
OnGet = function(self, default)
local hair = self.vars.hair
return hair or {}
end,
OnAdjust = function(self, client, data, value, newData)
newData.hair = value
end
})
ix.config.Add("forceImmeseriveGlasses", true, "Wymusza rysowanie efektu okularów, nawet jeśli klient wyłączy dla nich tę opcję.", nil, {
category = "characters"
})
do
local CHAR = ix.meta.character
function CHAR:HasGlasses()
for _, v in pairs(self:GetInventory():GetItems()) do
if (v.glasses and v:GetData("equip")) then
return true
end
end
return false
end
end
ix.char.vars["model"].OnDisplay = function(self, container, payload) end
ix.char.vars["model"].OnValidate = function(self, value, payload, client)
local faction = ix.faction.indices[payload.faction]
if (faction) then
local gender = payload.gender
local models
if gender == "male" and faction:GetModelsMale(client) then
models = faction:GetModelsMale(client)
elseif gender == "female" and faction:GetModelsFemale(client) then
models = faction:GetModelsFemale(client)
else
models = faction:GetModels(client)
end
if (!payload.model or !models[payload.model]) then
return false, "Nie wybrano modelu!"
end
else
return false, "Nie wybrano modelu!"
end
end
ix.char.vars["model"].OnAdjust = function(self, client, data, value, newData)
local faction = ix.faction.indices[data.faction]
if (faction) then
local gender = data.gender
local model
if gender == "male" and faction:GetModelsMale(client) then
model = faction:GetModelsMale(client)[value]
elseif gender == "female" and faction:GetModelsFemale(client) then
model = faction:GetModelsFemale(client)[value]
else
model = faction:GetModels(client)[value]
end
if (isstring(model)) then
newData.model = model
elseif (istable(model)) then
newData.model = model[1]
end
end
end
ix.char.vars["model"].ShouldDisplay = function(self, container, payload)
local faction = ix.faction.indices[payload.faction]
if faction then
local gender = payload.gender
if gender == "male" and faction:GetModelsMale(LocalPlayer()) then
return #faction:GetModelsMale(LocalPlayer()) > 1
elseif gender == "female" and faction:GetModelsFemale(LocalPlayer()) then
return #faction:GetModelsFemale(LocalPlayer()) > 1
else
return #faction:GetModels(LocalPlayer()) > 1
end
end
end
-- Registers the var "Gender"
ix.char.RegisterVar("gender", {
field = "gender",
fieldType = ix.type.string,
default = "male",
bNoDisplay = true,
OnSet = function(self, value)
local client = self:GetPlayer()
if (IsValid(client)) then
self.vars.gender = value
-- @todo refactor networking of character vars so this doesn't need to be repeated on every OnSet override
net.Start("ixCharacterVarChanged")
net.WriteUInt(self:GetID(), 32)
net.WriteString("gender")
net.WriteType(self.vars.gender)
net.Broadcast()
end
end,
OnGet = function(self, default)
local gender = self.vars.gender
return gender or 0
end,
OnValidate = function(self, data, payload, client)
local faction = ix.faction.indices[payload.faction]
if (payload.gender == "female" or payload.gender == "male") then
return true
end
if faction then
if faction:GetNoGender(client) == true then
return true
end
end
return false, "Nie wybrano płci!"
end,
OnAdjust = function(self, client, data, value, newData)
newData.gender = value
end
})
ix.char.vars["data"].OnValidate = function(self, datas, payload, client)
local faction = ix.faction.indices[payload.faction]
if faction then
if (!payload.data["background"] or payload.data["background"] == "") and faction:GetNoBackground(client) != true then
return false, "Nie wybrano tła!"
end
if faction:GetNoGenetics(client) then
return true
end
if !payload.data.age or payload.data["age"] == "" then
return false, "Nie wybrano wieku!"
end
if !payload.data.height or payload.data["height"] == "" then
return false, "Nie wybrano wzrostu!"
end
if faction.name != "Vortigaunt" then
if !payload.data["eye color"] or payload.data["eye color"] == "" then
return false, "Nie wybrano koloru oczu!"
end
end
if payload.data.skin < 0 then
return false, "Nie wybrano prawidłowej skóry!"
end
if payload.data.groups then
if payload.data.groups["2"]then
if payload.data.groups["2"] < 0 then
return false, "Nie wybrano prawidłowego tułowia!"
end
end
if payload.data.groups["3"] then
if payload.data.groups["3"] < 0 then
return false, "Nie wybrano prawidłowych spodni!"
end
end
end
if faction:GetNoAppearances(client) then
return true
end
if faction:GetReadOptionDisabled(client) then
return true
end
end
return true
end
ix.char.vars["data"].OnAdjust = function(self, client, datas, value, newData)
newData.data = value
end

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("Potrzebuję swoich okularów, by dobrze widzieć...")
end
end)
end
-- Beard stuff
local uniqueID = "ixBeard" .. client:SteamID64()
local gender = character:GetGender()
if (gender == "male" and !ix.faction.Get(client:Team()).noBeard) then
timer.Create(uniqueID, PLUGIN.TIMER_DELAY, 0, function()
if (IsValid(client)) then
PLUGIN:BeardPlayerTick(client)
else
timer.Remove(uniqueID)
end
end)
else
timer.Remove(uniqueID)
end
local hairTable = character.GetHair and character:GetHair() or {}
if !table.IsEmpty(hairTable) then
local color = hairTable.color
local charProxies = character:GetProxyColors() or {}
charProxies["HairColor"] = color
character:SetProxyColors(charProxies)
else
client:Notify("Dodano nowe włosy i kolory włosów, które można wybrać z menu.")
netstream.Start(client, "OpenBeardStyling")
end
local curModel = client:GetModel()
if curModel:find("willardnetworks/citizens/male") and !curModel:find("willardnetworks/citizens/male_") then
local beardIndex = client:FindBodygroupByName("beard")
local curBeard = client:GetBodygroup(beardIndex)
character:SetModel(string.gsub(curModel, "male", "male_"))
for i = 0, client:GetNumBodyGroups() do
client:SetBodygroup(i, 0)
end
timer.Simple(2, function()
if !client or client and !IsValid(client) then return end
if !character then return end
local groups = {}
if curBeard then
groups[beardIndex] = curBeard
end
local hairIndex = client:FindBodygroupByName("hair")
groups[hairIndex] = hairTable.hair
character:SetData("groups", groups)
if hairTable.hair then
client:SetBodygroup(hairIndex, hairTable.hair)
end
if curBeard then
client:SetBodygroup(beardIndex, curBeard)
end
end)
end
end
function PLUGIN:AdjustCreationPayload(client, payload, newPayload)
if (newPayload.data.glasses != nil) then
newPayload.glasses = newPayload.data.glasses
newPayload.data.glasses = nil
end
if (newPayload.data.canread != nil) then
newPayload.canread = newPayload.data.canread
newPayload.data.canread = nil
end
end
function PLUGIN:BeardPlayerTick(client)
local character = client:GetCharacter()
if (character) then
if (!client:Alive()) then return end
local beardProgress = character:GetBeardProgress() + 1
character:SetBeardProgress(beardProgress)
if (beardProgress == 180 * 8) then
local index = client:FindBodygroupByName("beard")
local groups = character:GetData("groups", {})
groups[index] = 8
client:SetBodygroup(index, 8)
character:SetData("groups", groups)
client:NotifyLocalized("Powinienem przyciąć już brodę.")
elseif (beardProgress == 180 * 16) then
local index = client:FindBodygroupByName("beard")
local groups = character:GetData("groups", {})
groups[index] = 5
client:SetBodygroup(index, 5)
character:SetData("groups", groups)
client:NotifyLocalized("Moja broda robi się już naprawdę długa.")
end
end
end
netstream.Hook("RemoveBeardBodygroup", function(client, target)
if target and IsValid(target) then
if client != target then
client = target
end
end
local character = client:GetCharacter()
local index = client:FindBodygroupByName("beard")
if client:GetBodygroup( index ) <= 0 then
client:NotifyLocalized("Nie mam brody!")
return false
end
local groups = character:GetData("groups", {})
groups[index] = 0
client:SetBodygroup(index, 0)
character:SetData("groups", groups)
character:SetBeardProgress(0)
end)
netstream.Hook("SetHairBeardBodygroup", function(client, hairID, beardID, hairColor, target)
if target and IsValid(target) then
if client != target then
client = target
end
end
local character = client:GetCharacter()
local gender = character:GetGender()
local beardBGIndex = client:FindBodygroupByName("beard")
local hairBGIndex = client:FindBodygroupByName("hair")
local curHair = client:GetBodygroup(hairBGIndex)
local curBeard = beardBGIndex != -1 and client:GetBodygroup(beardBGIndex) or false
local groups = character:GetData("groups", {})
if gender != "female" and curBeard and beardID and curBeard != beardID then
local beardProgress = character:GetBeardProgress()
if (beardProgress < (180 * 8)) and client:GetBodygroup(beardBGIndex) != 5 and client:GetBodygroup(beardBGIndex) != 8 then
return false
end
character:SetBeardProgress(0)
groups[beardBGIndex] = beardID
client:SetBodygroup(beardBGIndex, beardID)
end
if hairID and curHair != hairID or hairColor then
local hairData = character:GetHair()
if hairID then
groups[hairBGIndex] = hairID
local bgName = client:GetModel():find("wn7new") and "cp_Head" or "headwear"
if client:GetBodygroup(client:FindBodygroupByName(bgName)) <= 0 then
client:SetBodygroup(hairBGIndex, hairID)
end
hairData.hair = hairID
end
if hairColor then
hairData.color = hairColor
local charProxies = character:GetProxyColors() or {}
charProxies["HairColor"] = hairColor
character:SetProxyColors(charProxies)
end
character:SetHair(hairData)
end
character:SetData("groups", groups)
end)
function PLUGIN:InitializedPlugins()
Schema.charCreationItems = {}
for _, v in pairs(ix.item.list) do
if (v.charCreation or v.adminCreation) and v.bodyGroups then
Schema.charCreationItems[v.uniqueID] = {bodygroups = v.bodyGroups, proxy = v.proxy or false}
end
end
end
netstream.Hook("AcceptStyling", function(target, attempter)
if !target or !attempter then return end
if !IsValid(target) or !IsValid(attempter) then return end
netstream.Start(attempter, "OpenBeardStyling", target)
end)

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 = "Get admins list with their last play time.",
superAdminOnly = true,
OnRun = function(self, client)
local query = mysql:Select("sam_players")
query:Select("steamid")
query:Select("name")
query:Select("rank")
query:Select("last_join")
query:Select("play_time")
query:WhereNotLike("rank", "user")
query:Callback(function(result)
if (!result or !istable(result) or #result == 0) then
client:NotifyLocalized("No staff found!")
return
end
for _, v in pairs(result) do
local playTime = math.Round(v.play_time / 3600, 1)
if (player.GetBySteamID(v.steamid)) then
client:ChatPrint(v.name .. " (" .. v.steamid .. "), " .. v.rank .. " is currently online. Total play time: " .. playTime .. " hours.")
else
client:ChatPrint(v.name .. " (" .. v.steamid .. "), " .. v.rank .. " was online " .. os.date("%x %X", v.last_join) .. ". Total play time: " .. playTime .. " hours.")
end
end
end)
query:Execute()
end
})

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,139 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
require("niknaks")
local PLUGIN = PLUGIN
PLUGIN.name = "Clientside Props"
PLUGIN.description = "Adds a way to convert server props to clientside props for performance reasons."
PLUGIN.author = "Aspect™"
PLUGIN.clientProps = PLUGIN.clientProps or {}
ix.util.Include("cl_hooks.lua")
ix.util.Include("cl_plugin.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("sv_plugin.lua")
CAMI.RegisterPrivilege({
Name = "Helix - Manage Clientside Props",
MinAccess = "admin"
})
ix.option.Add("csentRenderSpeed", ix.type.number, 50, {
category = "performance",
min = 1,
max = 500
})
ix.lang.AddTable("english", {
optCsentRenderSpeed = "Prędkość renderowana Propów Clientside",
optdCsentRenderSpeed = "Ile propów clientside powinno być kalkulowanych co klatkę. Niższe wartości = więcej FPS, ale wolniej renderuje. Wyższe wartości = mniej FPS, ale szybciej renderuje.",
cmdRemoveClientProps = "Usuń wszystkie propy clientside w promieniu wokół ciebie."
})
ix.lang.AddTable("polish", {
optCsentRenderSpeed = "Prędkość renderowana Propów Clientside",
optdCsentRenderSpeed = "Ile propów clientside powinno być kalkulowanych co klatkę. Niższe wartości = więcej FPS, ale wolniej renderuje. Wyższe wartości = mniej FPS, ale szybciej renderuje.",
cmdRemoveClientProps = "Usuń wszystkie propy clientside w promieniu wokół ciebie."
})
ix.command.Add("RemoveClientProps", {
description = "@cmdRemoveClientProps",
adminOnly = true,
arguments = {
ix.type.number
},
OnRun = function(self, client, radius)
if (radius < 0) then
client:Notify("Radius must be a positive number!")
return
end
local newTable = {}
for k, propData in ipairs(PLUGIN.clientProps) do
if (propData.position:Distance(client:GetPos()) <= radius) then continue end
newTable[#newTable + 1] = propData
end
PLUGIN.clientProps = newTable
net.Start("ixClientProps.MassRemoveProps")
net.WriteVector(client:GetPos())
net.WriteUInt(radius, 16)
net.Broadcast()
client:Notify("Removed all clientside props in a radius of " .. radius .. " units.")
end
})
local PERSISTENCE = ix.plugin.Get("persistence")
properties.Add("clientprop", {
MenuLabel = "Convert to Client Prop",
Order = 400,
MenuIcon = "icon16/contrast_low.png",
Filter = function(self, entity, client)
return entity:GetClass() == "prop_physics" and CAMI.PlayerHasAccess(client, "Helix - Manage Clientside Props")
end,
Action = function(self, entity)
self:MsgStart()
net.WriteEntity(entity)
self:MsgEnd()
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
if (!entity:TestPVS(client)) then
client:Notify("That prop cannot be converted because its origin is outside the world!")
return
end
-- Unpersist it if it's persisted
if (PERSISTENCE) then
for k, v in ipairs(PERSISTENCE.stored) do
if (v == entity) then
table.remove(PERSISTENCE.stored, k)
break
end
end
entity:SetNetVar("Persistent", false)
end
local propData = {
position = entity:GetPos(),
angles = entity:GetAngles(),
model = entity:GetModel(),
skin = entity:GetSkin(),
color = entity:GetColor(),
material = entity:GetMaterial()
}
entity:Remove()
PLUGIN.clientProps[#PLUGIN.clientProps + 1] = propData
PLUGIN:NetworkProp(propData)
end
})

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 = "Container"
ENT.Category = "Helix"
ENT.Spawnable = false
ENT.bNoPersist = true
function ENT:SetupDataTables()
self:NetworkVar("Int", 0, "ID")
self:NetworkVar("Bool", 0, "Locked")
self:NetworkVar("String", 0, "DisplayName")
self:NetworkVar("String", 1, "Password")
end
if (SERVER) then
function ENT:Initialize()
self:PhysicsInit(SOLID_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetUseType(SIMPLE_USE)
self.receivers = {}
local definition = ix.container.stored[self:GetModel():lower()]
if (definition) then
self:SetDisplayName(definition.name)
end
local physObj = self:GetPhysicsObject()
if (IsValid(physObj)) then
physObj:EnableMotion(true)
physObj:Wake()
end
end
function ENT:SetInventory(inventory)
if (inventory) then
self:SetID(inventory:GetID())
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(self)
end
end
end
function ENT:SetMoney(amount)
self.money = math.max(0, math.Round(tonumber(amount) or 0))
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(self)
end
end
function ENT:GetMoney()
return self.money or 0
end
function ENT:OnRemove()
local index = self:GetID()
if (!ix.shuttingDown and !self.ixIsSafe and ix.entityDataLoaded and index) then
local inventory = index != 0 and ix.item.inventories[index]
if (inventory) then
ix.item.inventories[index] = nil
local query = mysql:Delete("ix_items")
query:Where("inventory_id", index)
query:Execute()
query = mysql:Delete("ix_inventories")
query:Where("inventory_id", index)
query:Execute()
hook.Run("ContainerRemoved", self, inventory)
end
end
end
function ENT:OpenInventory(activator)
local inventory = self:GetInventory()
if (inventory) then
local name = self:GetDisplayName()
ix.storage.Open(activator, inventory, {
name = name,
entity = self,
bMultipleUsers = true,
searchTime = ix.config.Get("containerOpenTime", 0.7),
data = {money = self:GetMoney()},
OnPlayerClose = function()
ix.log.Add(activator, "closeContainer", name, inventory:GetID())
end
})
ix.log.Add(activator, "openContainer", name, inventory:GetID())
if (ix.plugin.list.willardcontainers and ix.config.Get("notifyOldcontainer") and self:GetClass() == "ix_container") then
activator:ChatNotifyLocalized("containerUseOld")
end
end
end
function ENT:Use(activator)
local inventory = self:GetInventory()
if (inventory and (activator.ixNextOpen or 0) < CurTime()) then
local character = activator:GetCharacter()
if (character) then
local def = ix.container.stored[self:GetModel():lower()]
if (self:GetLocked() and !self.Sessions[character:GetID()] and !self:GetNetVar("isOneWay", false)) then
self:EmitSound(def.locksound or "doors/default_locked.wav")
if (!self.keypad) then
net.Start("ixContainerPassword")
net.WriteEntity(self)
net.Send(activator)
end
else
self:OpenInventory(activator)
end
end
activator.ixNextOpen = CurTime() + 1
end
end
else
ENT.PopulateEntityInfo = true
local COLOR_LOCKED = Color(200, 38, 19, 200)
local COLOR_UNLOCKED = Color(135, 211, 124, 200)
function ENT:OnPopulateEntityInfo(tooltip)
local definition = ix.container.stored[self:GetModel():lower()]
local bLocked = self:GetLocked()
surface.SetFont("ixIconsSmall")
local iconText = bLocked and "P" or "Q"
local iconWidth, iconHeight = surface.GetTextSize(iconText)
-- minimal tooltips have centered text so we'll draw the icon above the name instead
if (tooltip:IsMinimal()) then
local icon = tooltip:AddRow("icon")
icon:SetFont("ixIconsSmall")
icon:SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
icon:SetText(iconText)
icon:SizeToContents()
end
local title = tooltip:AddRow("name")
title:SetImportant()
title:SetText(self:GetDisplayName())
title:SetBackgroundColor(ix.config.Get("color"))
title:SetTextInset(iconWidth + 8, 0)
title:SizeToContents()
if (!tooltip:IsMinimal()) then
title.Paint = function(panel, width, height)
panel:PaintBackground(width, height)
surface.SetFont("ixIconsSmall")
surface.SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
surface.SetTextPos(4, height * 0.5 - iconHeight * 0.5)
surface.DrawText(iconText)
end
end
local description = tooltip:AddRow("description")
description:SetText(definition.description)
description:SizeToContents()
end
end
function ENT:GetInventory()
return ix.item.inventories[self:GetID()]
end

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/
--]]
--[[
ix.container.Register(model, {
name = "Crate",
description = "A simple wooden create.",
width = 4,
height = 4,
locksound = "",
opensound = ""
})
]]--
ix.container.Register("models/props_junk/wood_crate001a.mdl", {
name = "Crate",
description = "A simple wooden crate.",
width = 4,
height = 4,
})
ix.container.Register("models/props_c17/lockers001a.mdl", {
name = "Locker",
description = "A white locker.",
width = 3,
height = 5,
})
ix.container.Register("models/props_wasteland/controlroom_storagecloset001a.mdl", {
name = "Metal Cabinet",
description = "A green metal cabinet.",
width = 4,
height = 5,
})
ix.container.Register("models/props_wasteland/controlroom_storagecloset001b.mdl", {
name = "Metal Cabinet",
description = "A green metal cabinet.",
width = 4,
height = 5,
})
ix.container.Register("models/props_wasteland/controlroom_filecabinet001a.mdl", {
name = "File Cabinet",
description = "A metal filing cabinet.",
width = 5,
height = 3
})
ix.container.Register("models/props_wasteland/controlroom_filecabinet002a.mdl", {
name = "File Cabinet",
description = "A metal filing cabinet.",
width = 3,
height = 6,
})
ix.container.Register("models/props_lab/filecabinet02.mdl", {
name = "File Cabinet",
description = "A metal filing cabinet.",
width = 5,
height = 3
})
ix.container.Register("models/props_c17/furniturefridge001a.mdl", {
name = "Refrigerator",
description = "A metal box for keeping food in.",
width = 2,
height = 3,
})
ix.container.Register("models/props_wasteland/kitchen_fridge001a.mdl", {
name = "Large Refrigerator",
description = "A large metal box for storing even more food in.",
width = 4,
height = 5,
})
ix.container.Register("models/props_junk/trashbin01a.mdl", {
name = "Trash Bin",
description = "What do you expect to find in here?",
width = 2,
height = 2,
})
ix.container.Register("models/props_junk/trashdumpster01a.mdl", {
name = "Dumpster",
description = "A dumpster meant to stow away trash. It emanates an unpleasant smell.",
width = 6,
height = 3
})
ix.container.Register("models/items/ammocrate_smg1.mdl", {
name = "Ammo Crate",
description = "A heavy crate that stores ammo.",
width = 5,
height = 3,
OnOpen = function(entity, activator)
local closeSeq = entity:LookupSequence("Close")
entity:ResetSequence(closeSeq)
timer.Simple(2, function()
if (entity and IsValid(entity)) then
local openSeq = entity:LookupSequence("Open")
entity:ResetSequence(openSeq)
end
end)
end
})
ix.container.Register("models/props_forest/footlocker01_closed.mdl", {
name = "Footlocker",
description = "A small chest to store belongings in.",
width = 5,
height = 3
})
ix.container.Register("models/Items/item_item_crate.mdl", {
name = "Item Crate",
description = "A crate to store some belongings in.",
width = 5,
height = 3
})
ix.container.Register("models/props_c17/cashregister01a.mdl", {
name = "Cash Register",
description = "A register with some buttons and a drawer.",
width = 2,
height = 1
})

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, "Whether or not containers will save after a server restart.", nil, {
category = "Containers"
})
ix.config.Add("containerOpenTime", 0.7, "How long it takes to open a container.", nil, {
data = {min = 0, max = 50},
category = "Containers"
})
function ix.container.Register(model, data)
ix.container.stored[model:lower()] = data
end
ix.util.Include("sh_definitions.lua")
if (SERVER) then
util.AddNetworkString("ixContainerPassword")
function PLUGIN:PlayerSpawnedProp(client, model, entity)
model = tostring(model):lower()
local data = ix.container.stored[model:lower()]
if (data) then
if (hook.Run("CanPlayerSpawnContainer", client, model, entity) == false) then return end
local container = ents.Create("ix_container")
container:SetPos(entity:GetPos())
container:SetAngles(entity:GetAngles())
container:SetModel(model)
container:Spawn()
ix.inventory.New(0, "container:" .. model:lower(), function(inventory)
-- we'll technically call this a bag since we don't want other bags to go inside
inventory.vars.isBag = true
inventory.vars.isContainer = true
if (IsValid(container)) then
container:SetInventory(inventory)
self:SaveContainer()
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(container)
end
end
end)
entity:Remove()
hook.Run("PlayerSpawnedContainer", client, container)
end
end
function PLUGIN:RegisterSaveEnts()
ix.saveEnts:RegisterEntity("ix_container", true, true, true, {
OnSave = function(entity, data) --OnSave
data.motion = false
local inventory = entity:GetInventory()
local lockentity = entity:GetChildren()[1]
data.invID = inventory:GetID()
data.model = entity:GetModel()
data.pass = entity.password
data.name = entity:GetDisplayName()
data.money = entity:GetMoney()
data.lockpos = lockentity and lockentity:GetPos()
data.lockangs = lockentity and lockentity:GetAngles()
data.lockowner = lockentity and lockentity:GetNetVar("owner")
data.group = entity.group
data.oneway = entity:GetNetVar("isOneWay", false)
end,
OnRestore = function(entity, data) --OnRestore
local data2 = ix.container.stored[data.model:lower()] -- Model name
if (data2) then
local inventoryID = tonumber(data.invID) -- invID
if (!inventoryID or inventoryID < 1) then
ErrorNoHalt(string.format(
"[Helix] Attempted to restore container inventory with invalid inventory ID '%s' (%s, %s)\n",
tostring(inventoryID), data.name or "no name", data.model or "no model"))
return
end
entity:SetModel(data.model) -- Model name
entity:SetSolid(SOLID_VPHYSICS)
entity:PhysicsInit(SOLID_VPHYSICS)
if (data.pass) then -- Password
entity.password = data.pass
entity:SetLocked(true)
entity:SetPassword(data.pass)
entity.Sessions = {}
end
if (data.name) then -- Display name
entity:SetDisplayName(data.name)
end
if (data.money) then -- Money
entity:SetMoney(data.money)
end
if (data.lockpos) then -- Lock Pos
local lockentity = ents.Create("ix_containerlock")
lockentity:SetPos(data.lockpos) -- Lock Pos
lockentity:SetAngles(data.lockangs) -- Lock Angles
lockentity:SetParent(entity)
lockentity:SetNetVar("owner", data.lockowner) -- Lock Owner
lockentity:Spawn()
end
if (data.group) then
entity.group = data.group -- Group
end
entity:SetNetVar("isOneWay", data.oneway)
ix.inventory.Restore(inventoryID, data2.width, data2.height, function(inventory)
inventory.vars.isBag = true
inventory.vars.isContainer = true
if (IsValid(entity)) then
entity:SetInventory(inventory)
end
end)
local physObject = entity:GetPhysicsObject()
if (IsValid(physObject)) then
physObject:EnableMotion()
end
end
end,
ShouldSave = function(entity)
local inventory = entity:GetInventory()
return entity:GetModel() != "models/error.mdl" and inventory:GetID() != 0 //avoid bad save that somehow happened
end
})
end
function PLUGIN:CanSaveContainer(entity, inventory)
return ix.config.Get("containerSave", true)
end
function PLUGIN:SaveContainer()
local data = {}
for _, v in ipairs(ents.FindByClass("ix_container")) do
if (hook.Run("CanSaveContainer", v, v:GetInventory()) != false) then
local inventory = v:GetInventory()
if (!inventory) then continue end
local lockentity = v:GetChildren()[1]
data[#data + 1] = {
v:GetPos(),
v:GetAngles(),
inventory:GetID(),
v:GetModel(),
v.password,
v:GetDisplayName(),
v:GetMoney(),
lockentity and lockentity:GetPos(),
lockentity and lockentity:GetAngles(),
lockentity and lockentity:GetNetVar("owner"),
v.group,
v:GetNetVar("isOneWay", false)
}
else
local index = v:GetID()
local query = mysql:Delete("ix_items")
query:Where("inventory_id", index)
query:Execute()
query = mysql:Delete("ix_inventories")
query:Where("inventory_id", index)
query:Execute()
end
end
self:SetData(data)
end
function PLUGIN:SaveData()
if (!ix.shuttingDown) then
self:SaveContainer()
end
end
function PLUGIN:ContainerRemoved(entity, inventory)
self:SaveContainer()
end
function PLUGIN:LoadData()
if (ix.saveEnts and !ix.config.Get("SaveEntsOldLoadingEnabled")) then return end
local data = self:GetData()
if (data) then
for _, v in ipairs(data) do
if (!v[4]) then continue end -- Model name
local data2 = ix.container.stored[v[4]:lower()] -- Model name
if (data2) then
local inventoryID = tonumber(v[3]) -- invID
if (!inventoryID or inventoryID < 1) then
ErrorNoHalt(string.format(
"[Helix] Attempted to restore container inventory with invalid inventory ID '%s' (%s, %s)\n",
tostring(inventoryID), v[6] or "no name", v[4] or "no model"))
continue
end
local entity = ents.Create("ix_container")
entity:SetPos(v[1]) -- Pos
entity:SetAngles(v[2]) -- Angles
entity:Spawn()
entity:SetModel(v[4]) -- Model name
entity:SetSolid(SOLID_VPHYSICS)
entity:PhysicsInit(SOLID_VPHYSICS)
if (v[5]) then -- Password
entity.password = v[5]
entity:SetLocked(true)
entity:SetPassword(v[5])
entity.Sessions = {}
end
if (v[6]) then -- Display name
entity:SetDisplayName(v[6])
end
if (v[7]) then -- Money
entity:SetMoney(v[7])
end
if (v[8]) then -- Lock Pos
local lockentity = ents.Create("ix_containerlock")
lockentity:SetPos(v[8]) -- Lock Pos
lockentity:SetAngles(v[9]) -- Lock Angles
lockentity:SetParent(entity)
lockentity:SetNetVar("owner", v[10]) -- Lock Owner
lockentity:Spawn()
end
if (v[11]) then
entity.group = v[11] -- Group
end
entity:SetNetVar("isOneWay", v[12])
ix.inventory.Restore(inventoryID, data2.width, data2.height, function(inventory)
inventory.vars.isBag = true
inventory.vars.isContainer = true
if (IsValid(entity)) then
entity:SetInventory(inventory)
end
end)
local physObject = entity:GetPhysicsObject()
if (IsValid(physObject)) then
physObject:EnableMotion()
end
end
end
end
end
net.Receive("ixContainerPassword", function(length, client)
if ((client.ixNextContainerPassword or 0) > RealTime()) then
return
end
local entity = net.ReadEntity()
local password = net.ReadString()
local dist = entity:GetPos():DistToSqr(client:GetPos())
if (dist < 16384 and password) then
if (entity.password and entity.password == password) then
if (!entity:GetNetVar("isOneWay", false)) then
entity:OpenInventory(client)
end
entity.Sessions[client:GetCharacter():GetID()] = true
else
client:NotifyLocalized("wrongPassword")
end
end
client.ixNextContainerPassword = RealTime() + 0.5
end)
ix.log.AddType("containerPassword", function(client, ...)
local arg = {...}
return string.format("%s has %s the password for '%s'.", client:Name(), arg[3] and "set" or "removed", arg[1], arg[2])
end)
ix.log.AddType("containerName", function(client, ...)
local arg = {...}
if (arg[3]) then
return string.format("%s has set container %d name to '%s'.", client:Name(), arg[2], arg[1])
else
return string.format("%s has removed container %d name.", client:Name(), arg[2])
end
end)
ix.log.AddType("openContainer", function(client, ...)
local arg = {...}
return string.format("%s opened the '%s' #%d container.", client:Name(), arg[1], arg[2])
end, FLAG_NORMAL)
ix.log.AddType("closeContainer", function(client, ...)
local name
if !client or client and !IsValid(client) or IsValid(client) and !client.Name then
name = "N/A"
else
name = client.Name and client:Name()
end
local arg = {...}
return string.format("%s closed the '%s' #%d container.", name, arg[1], arg[2])
end, FLAG_NORMAL)
else
net.Receive("ixContainerPassword", function(length)
local entity = net.ReadEntity()
Derma_StringRequest(
L("containerPasswordWrite"),
L("containerPasswordWrite"),
"",
function(val)
net.Start("ixContainerPassword")
net.WriteEntity(entity)
net.WriteString(val)
net.SendToServer()
end
)
end)
end
function PLUGIN:InitializedPlugins()
for k, v in pairs(ix.container.stored) do
if (v.name and v.width and v.height) then
ix.inventory.Register("container:" .. k:lower(), v.width, v.height)
else
ErrorNoHalt("[Helix] Container for '"..k.."' is missing all inventory information!\n")
ix.container.stored[k] = nil
end
end
end
-- properties
properties.Add("container_setpassword", {
MenuLabel = "Set Password",
Order = 400,
MenuIcon = "icon16/lock_edit.png",
Filter = function(self, entity, client)
if (entity:GetClass() != "ix_container") then return false end
if (!gamemode.Call("CanProperty", client, "container_setpassword", entity)) then return false end
return true
end,
Action = function(self, entity)
Derma_StringRequest(L("containerPasswordWrite"), "", "", function(text)
self:MsgStart()
net.WriteEntity(entity)
net.WriteString(text)
self:MsgEnd()
end)
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
local password = net.ReadString()
entity.Sessions = {}
if (password:len() != 0) then
entity:SetLocked(true)
entity:SetPassword(password)
entity.password = password
client:NotifyLocalized("containerPassword", password)
else
entity:SetLocked(false)
entity:SetPassword(nil)
entity.password = nil
client:NotifyLocalized("containerPasswordRemove")
end
local name = entity:GetDisplayName()
local inventory = entity:GetInventory()
local definition = ix.container.stored[entity:GetModel():lower()]
if (definition) then
entity:SetDisplayName(definition.name)
end
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(self)
end
ix.log.Add(client, "containerPassword", name, inventory:GetID(), password:len() != 0)
end
})
properties.Add("container_setname", {
MenuLabel = "Set Name",
Order = 400,
MenuIcon = "icon16/tag_blue_edit.png",
Filter = function(self, entity, client)
if (entity:GetClass() != "ix_container") then return false end
if (!gamemode.Call("CanProperty", client, "container_setname", entity)) then return false end
return true
end,
Action = function(self, entity)
Derma_StringRequest(L("containerNameWrite"), "", "", function(text)
self:MsgStart()
net.WriteEntity(entity)
net.WriteString(text)
self:MsgEnd()
end)
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
local name = net.ReadString()
if (name:len() != 0) then
entity:SetDisplayName(name)
client:NotifyLocalized("containerName", name)
else
local definition = ix.container.stored[entity:GetModel():lower()]
entity:SetDisplayName(definition.name)
client:NotifyLocalized("containerNameRemove")
end
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(self)
end
local inventory = entity:GetInventory()
ix.log.Add(client, "containerName", name, inventory:GetID(), name:len() != 0)
end
})

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 = "A Crosshair."
if (CLIENT) then
local function drawdot( pos, size, col )
local color = col[2]
surface.SetDrawColor(color.r, color.g, color.b, color.a)
surface.DrawRect(pos[1] - size/2, pos[2] - size/2, size, size)
color = col[1]
surface.SetDrawColor(color.r, color.g, color.b, color.a)
surface.DrawOutlinedRect(pos[1] - size/2, pos[2] - size/2 , size, size)
end
local aimVector, punchAngle, ft, screen, scaleFraction, distance
local math_round = math.Round
local curGap = 0
local curAlpha = 0
local maxDistance = 1000 ^ 2
local crossSize = 4
local crossGap = 0
local colors = {color_black}
local filter = {}
function PLUGIN:DrawCrosshair(x, y, trace)
local entity = trace.Entity
distance = trace.StartPos:DistToSqr(trace.HitPos)
scaleFraction = 1 - math.Clamp(distance / maxDistance, 0, .5)
crossSize = 4
crossGap = 25 * (scaleFraction - (LocalPlayer():IsWepRaised() and 0 or .1))
if (IsValid(entity) and entity:GetClass() == "ix_item" and
entity:GetPos():DistToSqr(trace.StartPos) <= 16384) then
crossGap = 0
crossSize = 5
end
curGap = Lerp(ft * 2, curGap, crossGap)
curAlpha = Lerp(ft * 2, curAlpha, !LocalPlayer():IsWepRaised() and 255 or 150)
curAlpha = hook.Run("GetCrosshairAlpha", curAlpha) or curAlpha
colors[2] = Color(255, curAlpha, curAlpha, curAlpha)
if (curAlpha > 1) then
drawdot( {math_round(screen.x), math_round(screen.y)}, crossSize, colors)
drawdot( {math_round(screen.x + curGap), math_round(screen.y)}, crossSize, colors)
drawdot( {math_round(screen.x - curGap), math_round(screen.y)}, crossSize, colors)
drawdot( {math_round(screen.x), math_round(screen.y + curGap * .8)}, crossSize, colors)
drawdot( {math_round(screen.x), math_round(screen.y - curGap * .8)}, crossSize, colors)
end
end
-- luacheck: globals g_ContextMenu
function PLUGIN:PostDrawHUD()
local client = LocalPlayer()
if (!client:GetCharacter() or !client:Alive()) then
return
end
local entity = Entity(client:GetLocalVar("ragdoll", 0))
if (entity:IsValid()) then
return
end
local wep = client:GetActiveWeapon()
local bShouldDraw = hook.Run("ShouldDrawCrosshair", client, wep)
if (bShouldDraw == false or !IsValid(wep) or wep.DrawCrosshair == false) then
return
end
if (bShouldDraw == false or g_ContextMenu:IsVisible() or
(IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing())) then
return
end
aimVector = client:EyeAngles()
punchAngle = client:GetViewPunchAngles()
ft = FrameTime()
filter = {client}
local vehicle = client:GetVehicle()
if (vehicle and IsValid(vehicle)) then
aimVector = aimVector + vehicle:GetAngles()
table.insert(filter, vehicle)
end
local data = {}
data.start = client:GetShootPos()
data.endpos = data.start + (aimVector + punchAngle):Forward() * 65535
data.filter = filter
local trace = util.TraceLine(data)
local drawTarget = self
local drawFunction = self.DrawCrosshair
-- we'll manually call this since CHudCrosshair is never drawn; checks are already performed
if (wep.DoDrawCrosshair) then
drawTarget = wep
drawFunction = wep.DoDrawCrosshair
end
screen = trace.HitPos:ToScreen()
drawFunction(drawTarget, screen.x, screen.y, trace)
end
end

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 = "Generic Item"
ITEM.description = "Generic Description"
ITEM.model = Model("models/maxofs2d/hover_rings.mdl")
function ITEM:GetName()
return self:GetData("name", "Custom Item")
end
function ITEM:GetDescription()
return self:GetData("description", "Custom item description.")
end
function ITEM:GetModel()
return self:GetData("model", "models/Gibs/HGIBS.mdl")
end
function ITEM:GetMaterial()
return self:GetData("material", nil)
end

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 = "Dropped Items"
ENT.Category = "Helix"
ENT.Spawnable = false
if (SERVER) then
function ENT:Initialize()
self:PhysicsInit(SOLID_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetUseType(SIMPLE_USE)
self.receivers = {}
self:SetDisplayName("Dropped Items")
local physObj = self:GetPhysicsObject()
if (IsValid(physObj)) then
physObj:EnableMotion(true)
physObj:Wake()
end
end
function ENT:OnRemove()
local index = self:GetID()
if (!ix.shuttingDown and !self.ixIsSafe and ix.entityDataLoaded and index) then
local inventory = index != 0 and ix.item.inventories[index]
if (inventory) then
ix.item.inventories[index] = nil
local query = mysql:Delete("ix_items")
query:Where("inventory_id", index)
query:Execute()
query = mysql:Delete("ix_inventories")
query:Where("inventory_id", index)
query:Execute()
end
hook.Run("DropRemoved", self, inventory)
end
end
function ENT:Use(activator)
local inventory = self:GetInventory()
if (inventory and (activator.ixNextOpen or 0) < CurTime()) then
local character = activator:GetCharacter()
if (character) then
if (self:GetLocked() and !self.Sessions[character:GetID()]) then
self:EmitSound("doors/default_locked.wav")
if (!self.keypad) then
net.Start("ixContainerPassword")
net.WriteEntity(self)
net.Send(activator)
end
else
self:OpenInventory(activator)
end
end
activator.ixNextOpen = CurTime() + 1
end
end
else
ENT.PopulateEntityInfo = true
local COLOR_LOCKED = Color(200, 38, 19, 200)
local COLOR_UNLOCKED = Color(135, 211, 124, 200)
function ENT:OnPopulateEntityInfo(tooltip)
local bLocked = self:GetLocked()
surface.SetFont("ixIconsSmall")
local iconText = bLocked and "P" or "Q"
local iconWidth, iconHeight = surface.GetTextSize(iconText)
-- minimal tooltips have centered text so we'll draw the icon above the name instead
if (tooltip:IsMinimal()) then
local icon = tooltip:AddRow("icon")
icon:SetFont("ixIconsSmall")
icon:SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
icon:SetText(iconText)
icon:SizeToContents()
end
local title = tooltip:AddRow("name")
title:SetImportant()
title:SetText(self:GetDisplayName())
title:SetBackgroundColor(ix.config.Get("color"))
title:SetTextInset(iconWidth + 8, 0)
title:SizeToContents()
if (!tooltip:IsMinimal()) then
title.Paint = function(panel, width, height)
panel:PaintBackground(width, height)
surface.SetFont("ixIconsSmall")
surface.SetTextColor(bLocked and COLOR_LOCKED or COLOR_UNLOCKED)
surface.SetTextPos(4, height * 0.5 - iconHeight * 0.5)
surface.DrawText(iconText)
end
end
end
end

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 = "This character does not have any lost items.",
plyRefund = "Items were successfully refunded.",
plyRefundTemp = "Items were successfully refunded. Excess ones were put in a container in front.",
plyRefundLost = "%s items were irretrievably lost in the refund!"
}

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 = "Ta postać nie ma żadnych utraconych przedmiotów.",
plyRefund = "Przedmioty zostały pomyślnie zwrócone.",
plyRefundTemp = "Przedmioty zostały pomyślnie zwrócone. Nadmiar został umieszczony w pojemniku z przodu",
plyRefundLost = "%s przedmiotów zostało bezpowrotnie utracone podczas zwrotu!"
}

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

View File

@@ -0,0 +1,230 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ix = ix
local table = table
local pairs = pairs
local ents = ents
local IsValid = IsValid
local Color = Color
local math = math
local PLUGIN = PLUGIN
PLUGIN.name = "Drop Items on Death"
PLUGIN.author = "AleXXX_007"
PLUGIN.description = "After death, the character will drop they belongings."
ix.config.Add("dropItems", false, "Whether or not characters drop their items after death.", nil, {
category = "Drop Items on Death"
})
ix.config.Add("dropContainerModel", "models/props_c17/BriefCase001a.mdl", "A model path of a container with dropped items.", nil, {
category = "Drop Items on Death"
})
ix.util.Include("sv_plugin.lua")
if (ix.allowedHoldableClasses) then
ix.allowedHoldableClasses["ix_drop"] = true
end
ix.char.RegisterVar("refundItems", {
field = "refundItems",
default = {},
bNoNetworking = true,
bNoDisplay = true
})
ix.lang.AddTable("english", {
deathDropRemoved = "Usunięto łącznie %d death dropów."
})
ix.lang.AddTable("spanish", {
deathDropRemoved = "Borrados un total de %d death drops."
})
ix.lang.AddTable("polish", {
deathDropRemoved = "Usunięto łącznie %d death dropów."
})
ix.command.Add("RemoveDeathDrops", {
description = "Removes all death drops on the map or in the given radius.",
adminOnly = true,
arguments = {
ix.type.number
},
OnRun = function(self, client, radius)
radius = radius and radius * radius or math.huge
local pos = client:GetPos()
local count = 0
for k, v in pairs(ents.GetAll()) do
if (!IsValid(v)) then continue end
if (v:GetClass() != "ix_drop") then continue end
if (v:GetPos():DistToSqr(pos) > radius) then continue end
v:Remove()
count = count + 1
end
return "@deathDropRemoved", count
end
})
if (CLIENT) then
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
local command = string.utf8lower(ix.chat.currentCommand)
if (command == "removedeathdrops") then
local range = tonumber(ix.chat.currentArguments[1])
if (range) then
render.SetColorMaterial()
render.DrawSphere(LocalPlayer():GetPos(), 0 - range, 50, 50, Color(255,150,0,100))
end
end
end
end
ix.command.Add("RefundKill", {
description = "Returns all items player lost after they last death.",
adminOnly = true,
arguments = {
ix.type.player
},
OnRun = function(self, client, target)
local inventory = target:GetCharacter():GetInventory()
local itemIds = target:GetCharacter():GetRefundItems()
if (!itemIds) then
client:NotifyLocalized("plyNoRefunding")
end
local leftItems = table.Copy(itemIds)
local lost = 0
local temp = false
for _, v in pairs(itemIds) do
local itemTable = ix.item.instances[v]
if (!itemTable) then
lost = lost + 1
continue
end
local curInv = ix.item.inventories[self.invID or 0]
if (!curInv) then
ErrorNoHalt("[DEATHDROP] Failed to transfer item "..itemTable.uniqueID.." (#"..v.."), no curInv!")
continue
end
local w, h = itemTable.width, itemTable.height
if (inventory:FindEmptySlot(w, h)) then
PLUGIN:TransferItem(target, itemTable, curInv, inventory)
table.RemoveByValue(leftItems, v)
else
temp = true
local container = ents.Create("ix_drop")
container:SetPos(target:GetPos() + target:GetAngles():Forward() * 5)
container:SetModel(ix.config.Get("dropContainerModel", "models/props_c17/BriefCase001a.mdl"))
container:Spawn()
ix.inventory.New(0, "droppedItems", function(newInv)
newInv.vars.isBag = false
newInv.vars.isDrop = true
function newInv.OnAuthorizeTransfer(_, _, _, item)
if (item.authorized) then
return true
end
end
if (!IsValid(container)) then
return
end
container:SetInventory(newInv)
newInv.vars.entity = container
for _, v1 in pairs(leftItems) do
local item = ix.item.instances[v1]
if (!item) then continue end
local owner = item:GetOwner()
local curInv1 = IsValid(owner) and owner:GetCharacter():GetInventory() or false
PLUGIN:TransferItem(owner, item, curInv1, newInv)
end
ix.saveEnts:SaveEntity(container)
end)
break
end
end
local notify = temp and "plyRefundTemp" or "plyRefund"
client:NotifyLocalized(notify)
if (lost > 0) then
client:NotifyLocalized("plyRefundLost", lost)
end
if (client != target) then
target:NotifyLocalized(notify)
if (lost > 0) then
target:NotifyLocalized("plyRefundLost", lost)
end
end
target:GetCharacter():SetRefundItems(nil)
end
})
function PLUGIN:InitializedConfig()
ix.inventory.Register("droppedItems", 9, 9)
end
function PLUGIN:CanTransferItem(itemTable, curInv, inventory)
if (!itemTable.authorized) then
if (curInv:GetID() == 0) then return end
if (curInv.vars and curInv.vars.isDrop and inventory:GetID() == curInv:GetID()) then
return false
end
if (inventory.vars and inventory.vars.isDrop) then
return false
end
end
end
function PLUGIN:OnItemTransferred(itemTable, curInv, inventory)
if (curInv.vars and curInv.vars.isDrop and table.IsEmpty(curInv:GetItems())) then
local container = curInv.vars.entity
if (IsValid(container)) then
container:Remove()
end
end
end
if (CLIENT) then
local color = Color(255, 0, 128)
function PLUGIN:InitializedPlugins()
local function drawDropESP(client, entity, x, y, factor)
ix.util.DrawText("Death Drop", x, y - math.max(10, 32 * factor), color,
TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, nil, math.max(255 * factor, 80))
end
ix.observer:RegisterESPType("ix_drop", drawDropESP, "drop")
end
end

View File

@@ -0,0 +1,220 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ix = ix
local IsValid = IsValid
local pairs = pairs
local isfunction = isfunction
local table = table
local ents = ents
local hook = hook
local ipairs = ipairs
local PLUGIN = PLUGIN
ix.log.AddType("droppedItems", function(client, text)
return text
end)
function PLUGIN:TransferItem(client, itemTable, curInv, inventory)
itemTable.player = client
if (itemTable:GetData("equip") == true and itemTable.functions.EquipUn) then
itemTable.functions.EquipUn.OnRun(itemTable)
end
if (itemTable.OnTransferred) then
itemTable:OnTransferred(curInv, inventory)
end
if (itemTable.OnDrop) then
itemTable:OnDrop()
end
local x, y = itemTable.gridX, itemTable.gridY
local w, h = itemTable.width, itemTable.height
if (!x or !y or !inventory:CanItemFit(x, y, w, h)) then
x, y = inventory:FindEmptySlot(w, h)
end
itemTable.authorized = true
itemTable:Transfer(inventory:GetID(), x, y)
local entity = itemTable:GetEntity()
if (IsValid(entity)) then
itemTable:GetEntity():Remove()
end
itemTable.player = nil
itemTable.authorized = nil
end
function PLUGIN:ShouldCharacterDropItems(client)
if (!ix.config.Get("dropItems")) then
return false
end
local character = client:GetCharacter()
if (character) then
local faction = character:GetFaction()
if (faction) then
local factionTable = ix.faction.Get(faction)
if (factionTable.saveItemsAfterDeath) then
return false
end
end
else
return false
end
end
function PLUGIN:PlayerDeath(client, inflictor, attacker)
if (hook.Run("ShouldCharacterDropItems", client) != false) then
local clientInv = client:GetCharacter():GetInventory()
local droppedItems = {}
local items = {}
for _, v in pairs(clientInv:GetItems()) do
if (v.isBag) then continue end
if (v.OnDeathDrop) then
v:OnDeathDrop(client, items, droppedItems)
elseif ((isfunction(v.KeepOnDeath) and v:KeepOnDeath(client) or v.KeepOnDeath) != true) then
table.insert(items, v)
table.insert(droppedItems, v:GetID())
if (v.OnDoDeathDrop) then
v:OnDoDeathDrop(client, items, droppedItems)
end
end
end
if (!table.IsEmpty(items)) then
local container = ents.Create("ix_drop")
container:SetPos(client:GetPos())
container:SetModel(ix.config.Get("dropContainerModel", "models/props_c17/BriefCase001a.mdl"))
container:Spawn()
hook.Run("PreDropCharacterInventory", client, items, container)
ix.inventory.New(0, "droppedItems", function(inventory)
inventory.vars.isBag = false
inventory.vars.isDrop = true
function inventory.OnAuthorizeTransfer(_, _, _, itemTable)
if (itemTable.authorized) then
return true
end
end
if (IsValid(container)) then
container:SetInventory(inventory)
inventory.vars.entity = container
for _, v in pairs(items) do
self:TransferItem(client, v, clientInv, inventory)
end
ix.saveEnts:SaveEntity(container)
hook.Run("PostDropCharacterInventory", client, items, container, inventory)
else
ix.item.inventories[inventory.id] = nil
local query = mysql:Delete("ix_items")
query:Where("inventory_id", inventory.id)
query:Execute()
query = mysql:Delete("ix_inventories")
query:Where("inventory_id", inventory.id)
query:Execute()
end
end)
client:GetCharacter():SetRefundItems(droppedItems)
local logString = client:Name().." has dropped:"
for _, v in ipairs(items) do
if (!v.maxStackSize or v.maxStackSize == 1) then
logString = logString.." "..v:GetName().." (#"..v:GetID()..");"
else
logString = logString.." "..v:GetStackSize().."x "..v:GetName().." (#"..v:GetID()..");"
end
end
ix.log.Add(client, "droppedItems", logString)
end
end
end
function PLUGIN:PostDropCharacterInventory(client, items, container, inventory)
for k, v in pairs(inventory:GetItems()) do
if (v.replaceOnDeath) then
local newItem = v.replaceOnDeath
v:Remove()
if (ix.item.list[v.replaceOnDeath]) then
inventory:Add(newItem)
end
end
end
end
function PLUGIN:RegisterSaveEnts()
ix.saveEnts:RegisterEntity("ix_drop", true, true, true, {
OnSave = function(entity, data) --OnSave
local inventory = entity:GetInventory()
data.invID = inventory:GetID()
data.model = entity:GetModel()
data.name = entity.name
data.password = entity.password
data.money = entity:GetMoney()
end,
OnRestore = function(entity, data) --OnRestore
entity:SetModel(data.model)
entity:SetSolid(SOLID_VPHYSICS)
entity:PhysicsInit(SOLID_VPHYSICS)
if (data.password) then
entity.password = data.password
entity:SetLocked(true)
entity.Sessions = {}
end
if (data.name) then
entity.name = data.name
entity:SetDisplayName(data.name)
end
if (data.money) then
entity:SetMoney(data.money)
end
ix.inventory.Restore(data.invID, ix.config.Get("inventoryWidth"), ix.config.Get("inventoryHeight"), function(inventory)
inventory.vars.isBag = false
inventory.vars.isDrop = true
if (IsValid(entity)) then
entity:SetInventory(inventory)
inventory.vars.entity = entity
end
end)
end,
ShouldSave = function(entity) --ShouldSave
return entity:GetInventory() and entity:GetInventory() != 0
end,
ShouldRestore = function(data) --ShouldRestore
return data.invID >= 1
end
})
end

View File

@@ -0,0 +1,213 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- luacheck: globals ACCESS_LABELS
ACCESS_LABELS = {}
ACCESS_LABELS[DOOR_OWNER] = "owner"
ACCESS_LABELS[DOOR_TENANT] = "tenant"
ACCESS_LABELS[DOOR_GUEST] = "guest"
ACCESS_LABELS[DOOR_NONE] = "none"
function PLUGIN:GetDefaultDoorInfo(door)
local owner = IsValid(door:GetDTEntity(0)) and door:GetDTEntity(0) or nil
local name = door:GetNetVar("title", door:GetNetVar("name", IsValid(owner) and L"dTitleOwned" or L"dTitle"))
local description = door:GetNetVar("ownable") and L("dIsOwnable") or L("dIsNotOwnable")
local color = ix.config.Get("color")
local faction = door:GetNetVar("faction")
local class = door:GetNetVar("class")
if (class) then
local classData = ix.class.list[class]
if (classData) then
if (classData.color) then
color = classData.color
end
if (!owner) then
description = L("dOwnedBy", L2(classData.name) or classData.name)
end
end
elseif (faction) then
local info = ix.faction.indices[faction]
color = team.GetColor(faction)
if (info and !owner) then
description = L("dOwnedBy", L2(info.name) or info.name)
end
end
if (owner) then
description = L("dOwnedBy", owner:GetName())
end
return {
name = name,
description = description,
color = color
}
end
function PLUGIN:DrawDoorInfo(door, width, position, angles, scale, clientPosition)
local alpha = math.max((1 - clientPosition:DistToSqr(door:GetPos()) / 65536) * 255, 0)
if (alpha < 1) then
return
end
local info = hook.Run("GetDoorInfo", door) or self:GetDefaultDoorInfo(door)
if (!istable(info) or table.IsEmpty(info)) then
return
end
-- title + background
surface.SetFont("ix3D2DMediumFont")
local nameWidth, nameHeight = surface.GetTextSize(info.name)
derma.SkinFunc("DrawImportantBackground", -width * 0.5, -nameHeight * 0.5,
width, nameHeight, ColorAlpha(info.color, alpha * 0.5))
surface.SetTextColor(ColorAlpha(color_white, alpha))
surface.SetTextPos(-nameWidth * 0.5, -nameHeight * 0.5)
surface.DrawText(info.name)
-- description
local lines = ix.util.WrapText(info.description, width, "ix3D2DSmallFont")
local y = nameHeight * 0.5 + 4
for i = 1, #lines do
local line = lines[i]
local textWidth, textHeight = surface.GetTextSize(line)
surface.SetTextPos(-textWidth * 0.5, y)
surface.DrawText(line)
y = y + textHeight
end
-- background blur
ix.util.PushBlur(function()
cam.Start3D2D(position, angles, scale)
surface.SetDrawColor(11, 11, 11, math.max(alpha - 100, 0))
surface.DrawRect(-width * 0.5, -nameHeight * 0.5, width, y + nameHeight * 0.5 + 4)
cam.End3D2D()
end)
end
function PLUGIN:PostDrawTranslucentRenderables(bDepth, bSkybox)
if (bDepth or bSkybox or !LocalPlayer():GetCharacter()) then
return
end
local entities = ents.FindInSphere(EyePos(), 256)
local clientPosition = LocalPlayer():GetPos()
for _, v in ipairs(entities) do
if (!IsValid(v) or !v:IsDoor() or !v:GetNetVar("visible")) then
continue
end
local color = v:GetColor()
if (v:IsEffectActive(EF_NODRAW) or color.a <= 0) then
continue
end
local position = v:LocalToWorld(v:OBBCenter())
local mins, maxs = v:GetCollisionBounds()
local width = 0
local size = maxs - mins
local trace = {
collisiongroup = COLLISION_GROUP_WORLD,
ignoreworld = true,
endpos = position
}
-- trace from shortest side to center to get correct position for rendering
if (size.z < size.x and size.z < size.y) then
trace.start = position - v:GetUp() * size.z
width = size.y
elseif (size.x < size.y) then
trace.start = position - v:GetForward() * size.x
width = size.y
elseif (size.y < size.x) then
trace.start = position - v:GetRight() * size.y
width = size.x
end
width = math.max(width, 12)
trace = util.TraceLine(trace)
local angles = trace.HitNormal:Angle()
local anglesOpposite = trace.HitNormal:Angle()
angles:RotateAroundAxis(angles:Forward(), 90)
angles:RotateAroundAxis(angles:Right(), 90)
anglesOpposite:RotateAroundAxis(anglesOpposite:Forward(), 90)
anglesOpposite:RotateAroundAxis(anglesOpposite:Right(), -90)
local positionFront = trace.HitPos - (((position - trace.HitPos):Length() * 2) + 1) * trace.HitNormal
local positionOpposite = trace.HitPos + (trace.HitNormal * 2)
if (trace.HitNormal:Dot((clientPosition - position):GetNormalized()) < 0) then
-- draw front
cam.Start3D2D(positionFront, angles, 0.1)
self:DrawDoorInfo(v, width * 8, positionFront, angles, 0.1, clientPosition)
cam.End3D2D()
else
-- draw back
cam.Start3D2D(positionOpposite, anglesOpposite, 0.1)
self:DrawDoorInfo(v, width * 8, positionOpposite, anglesOpposite, 0.1, clientPosition)
cam.End3D2D()
end
end
end
net.Receive("ixDoorMenu", function()
if (IsValid(ix.gui.door)) then
return ix.gui.door:Remove()
end
local door = net.ReadEntity()
local access = net.ReadTable()
local entity = net.ReadEntity()
if (IsValid(door)) then
ix.gui.door = vgui.Create("ixDoorMenu")
ix.gui.door:SetDoor(door, access, entity)
end
end)
net.Receive("ixDoorPermission", function()
local door = net.ReadEntity()
if (!IsValid(door)) then
return
end
local target = net.ReadEntity()
local access = net.ReadUInt(4)
local panel = door.ixPanel
if (IsValid(panel) and IsValid(target)) then
panel.access[target] = access
for _, v in ipairs(panel.access:GetLines()) do
if (v.player == target) then
v:SetColumnText(2, L(ACCESS_LABELS[access or 0]))
return
end
end
end
end)

View File

@@ -0,0 +1,116 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local PANEL = {}
local function DoorSetPermission(door, target, permission)
net.Start("ixDoorPermission")
net.WriteEntity(door)
net.WriteEntity(target)
net.WriteUInt(permission, 4)
net.SendToServer()
end
function PANEL:Init()
self:SetSize(280, 240)
self:SetTitle(L"doorSettings")
self:Center()
self:MakePopup()
self.access = self:Add("DListView")
self.access:Dock(FILL)
self.access:AddColumn(L"name").Header:SetTextColor(Color(25, 25, 25))
self.access:AddColumn(L"access").Header:SetTextColor(Color(25, 25, 25))
self.access.OnClickLine = function(this, line, selected)
if (IsValid(line.player)) then
local menu = DermaMenu()
menu:AddOption(L"tenant", function()
if (self.accessData and self.accessData[line.player] != DOOR_TENANT) then
DoorSetPermission(self.door, line.player, DOOR_TENANT)
end
end):SetImage("icon16/user_add.png")
menu:AddOption(L"guest", function()
if (self.accessData and self.accessData[line.player] != DOOR_GUEST) then
DoorSetPermission(self.door, line.player, DOOR_GUEST)
end
end):SetImage("icon16/user_green.png")
menu:AddOption(L"none", function()
if (self.accessData and self.accessData[line.player] != DOOR_NONE) then
DoorSetPermission(self.door, line.player, DOOR_NONE)
end
end):SetImage("icon16/user_red.png")
menu:Open()
for _, v in pairs(menu:GetChildren()[1]:GetChildren()) do
v:SetFont("MenuFontNoClamp")
end
end
end
end
function PANEL:SetDoor(door, access, door2)
door.ixPanel = self
self.accessData = access
self.door = door
for _, v in ipairs(player.GetAll()) do
if (v != LocalPlayer() and v:GetCharacter()) then
self.access:AddLine(v:Name(), L(ACCESS_LABELS[access[v] or 0])).player = v
end
end
if (self:CheckAccess(DOOR_OWNER)) then
self.sell = self:Add("DButton")
self.sell:Dock(BOTTOM)
self.sell:SetText(L"sell")
self.sell:SetTextColor(color_white)
self.sell:DockMargin(0, 5, 0, 0)
self.sell.DoClick = function(this)
self:Remove()
ix.command.Send("doorsell")
end
end
if (self:CheckAccess(DOOR_TENANT)) then
self.name = self:Add("DTextEntry")
self.name:Dock(TOP)
self.name:DockMargin(0, 0, 0, 5)
self.name.Think = function(this)
if (!this:IsEditing()) then
local entity = IsValid(door2) and door2 or door
self.name:SetText(entity:GetNetVar("title", L"dTitleOwned"))
end
end
self.name.OnEnter = function(this)
ix.command.Send("doorsettitle", this:GetText())
end
end
end
function PANEL:CheckAccess(access)
access = access or DOOR_GUEST
if ((self.accessData[LocalPlayer()] or 0) >= access) then
return true
end
return false
end
function PANEL:Think()
if (self.accessData and !IsValid(self.door) and self:CheckAccess()) then
self:Remove()
end
end
vgui.Register("ixDoorMenu", PANEL, "DFrame")

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/
--]]
AddCSLuaFile()
if (CLIENT) then
SWEP.PrintName = "Keys"
SWEP.Slot = 0
SWEP.SlotPos = 2
SWEP.DrawAmmo = false
SWEP.DrawCrosshair = false
end
SWEP.Author = "Chessnut"
SWEP.Instructions = "Primary Fire: Lock\nSecondary Fire: Unlock"
SWEP.Purpose = "Hitting things and knocking on doors."
SWEP.Drop = false
SWEP.ViewModelFOV = 45
SWEP.ViewModelFlip = false
SWEP.AnimPrefix = "rpg"
SWEP.ViewTranslation = 4
SWEP.Primary.ClipSize = -1
SWEP.Primary.DefaultClip = -1
SWEP.Primary.Automatic = false
SWEP.Primary.Ammo = ""
SWEP.Primary.Damage = 5
SWEP.Primary.Delay = 0.75
SWEP.Secondary.ClipSize = -1
SWEP.Secondary.DefaultClip = 0
SWEP.Secondary.Automatic = false
SWEP.Secondary.Ammo = ""
SWEP.ViewModel = Model("models/weapons/c_arms_animations.mdl")
SWEP.WorldModel = ""
SWEP.UseHands = false
SWEP.LowerAngles = Angle(0, 5, -14)
SWEP.LowerAngles2 = Angle(0, 5, -22)
SWEP.IsAlwaysLowered = true
SWEP.FireWhenLowered = true
SWEP.HoldType = "passive"
-- luacheck: globals ACT_VM_FISTS_DRAW ACT_VM_FISTS_HOLSTER
ACT_VM_FISTS_DRAW = 2
ACT_VM_FISTS_HOLSTER = 1
function SWEP:Holster()
if (!IsValid(self.Owner)) then
return
end
local viewModel = self.Owner:GetViewModel()
if (IsValid(viewModel)) then
viewModel:SetPlaybackRate(1)
viewModel:ResetSequence(ACT_VM_FISTS_HOLSTER)
end
return true
end
function SWEP:Precache()
end
function SWEP:Initialize()
self:SetHoldType(self.HoldType)
end
function SWEP:PrimaryAttack()
local time = ix.config.Get("doorLockTime", 1)
local time2 = math.max(time, 1)
self:SetNextPrimaryFire(CurTime() + time2)
self:SetNextSecondaryFire(CurTime() + time2)
if (!IsFirstTimePredicted()) then
return
end
if (CLIENT) then
return
end
local data = {}
data.start = self.Owner:GetShootPos()
data.endpos = data.start + self.Owner:GetAimVector()*96
data.filter = self.Owner
local entity = util.TraceLine(data).Entity
--[[
Locks the entity if the contiditon fits:
1. The entity is door and client has access to the door.
2. The entity is vehicle and the "owner" variable is same as client's character ID.
--]]
if (IsValid(entity) and
(
(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or
(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)
)
) then
self.Owner:SetAction("@locking", time, function()
self:ToggleLock(entity, true)
end)
return
end
end
function SWEP:ToggleLock(door, state)
if (IsValid(self.Owner) and self.Owner:GetPos():Distance(door:GetPos()) > 96) then
return
end
if (door:IsDoor()) then
local partner = door:GetDoorPartner()
if (state) then
if (IsValid(partner)) then
partner:Fire("lock")
end
door:Fire("lock")
self.Owner:EmitSound("doors/door_latch3.wav")
hook.Run("PlayerLockedDoor", self.Owner, door, partner)
else
if (IsValid(partner)) then
partner:Fire("unlock")
end
door:Fire("unlock")
self.Owner:EmitSound("doors/door_latch1.wav")
hook.Run("PlayerUnlockedDoor", self.Owner, door, partner)
end
elseif (door:IsVehicle()) then
if (state) then
door:Fire("lock")
if (door.IsSimfphyscar) then
door.IsLocked = true
end
self.Owner:EmitSound("doors/door_latch3.wav")
hook.Run("PlayerLockedVehicle", self.Owner, door)
else
door:Fire("unlock")
if (door.IsSimfphyscar) then
door.IsLocked = nil
end
self.Owner:EmitSound("doors/door_latch1.wav")
hook.Run("PlayerUnlockedVehicle", self.Owner, door)
end
end
end
function SWEP:SecondaryAttack()
local time = ix.config.Get("doorLockTime", 1)
local time2 = math.max(time, 1)
self:SetNextPrimaryFire(CurTime() + time2)
self:SetNextSecondaryFire(CurTime() + time2)
if (!IsFirstTimePredicted()) then
return
end
if (CLIENT) then
return
end
local data = {}
data.start = self.Owner:GetShootPos()
data.endpos = data.start + self.Owner:GetAimVector()*96
data.filter = self.Owner
local entity = util.TraceLine(data).Entity
--[[
Unlocks the entity if the contiditon fits:
1. The entity is door and client has access to the door.
2. The entity is vehicle and the "owner" variable is same as client's character ID.
]]--
if (IsValid(entity) and
(
(entity:IsDoor() and entity:CheckDoorAccess(self.Owner)) or
(entity:IsVehicle() and entity.CPPIGetOwner and entity:CPPIGetOwner() == self.Owner)
)
) then
self.Owner:SetAction("@unlocking", time, function()
self:ToggleLock(entity, false)
end)
return
end
end

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