add sborka
This commit is contained in:
388
garrysmod/gamemodes/helix/plugins/3dpanel.lua
Normal file
388
garrysmod/gamemodes/helix/plugins/3dpanel.lua
Normal file
@@ -0,0 +1,388 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "3D Panels"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds web panels that can be placed on the map."
|
||||
|
||||
-- List of available panel dislays.
|
||||
PLUGIN.list = PLUGIN.list or {}
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixPanelList")
|
||||
util.AddNetworkString("ixPanelAdd")
|
||||
util.AddNetworkString("ixPanelRemove")
|
||||
|
||||
-- Called when the player is sending client info.
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
-- Send the list of panel displays.
|
||||
timer.Simple(1, function()
|
||||
if (IsValid(client)) then
|
||||
local json = util.TableToJSON(self.list)
|
||||
local compressed = util.Compress(json)
|
||||
local length = compressed:len()
|
||||
|
||||
net.Start("ixPanelList")
|
||||
net.WriteUInt(length, 32)
|
||||
net.WriteData(compressed, length)
|
||||
net.Send(client)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Adds a panel to the list, sends it to the players, and saves data.
|
||||
function PLUGIN:AddPanel(position, angles, url, scale, brightness)
|
||||
scale = math.Clamp((scale or 1) * 0.1, 0.001, 5)
|
||||
brightness = math.Clamp(math.Round((brightness or 100) * 2.55), 1, 255)
|
||||
|
||||
-- Find an ID for this panel within the list.
|
||||
local index = #self.list + 1
|
||||
|
||||
-- Add the panel to the list so it can be sent and saved.
|
||||
self.list[index] = {position, angles, nil, nil, scale, url, nil, brightness}
|
||||
|
||||
-- Send the panel information to the players.
|
||||
net.Start("ixPanelAdd")
|
||||
net.WriteUInt(index, 32)
|
||||
net.WriteVector(position)
|
||||
net.WriteAngle(angles)
|
||||
net.WriteFloat(scale)
|
||||
net.WriteString(url)
|
||||
net.WriteUInt(brightness, 8)
|
||||
net.Broadcast()
|
||||
|
||||
-- Save the plugin data.
|
||||
self:SavePanels()
|
||||
end
|
||||
|
||||
-- Removes a panel that are within the radius of a position.
|
||||
function PLUGIN:RemovePanel(position, radius)
|
||||
-- Default the radius to 100.
|
||||
radius = radius or 100
|
||||
|
||||
local panelsDeleted = {}
|
||||
|
||||
-- Loop through all of the panels.
|
||||
for k, v in pairs(self.list) do
|
||||
if (k == 0) then
|
||||
continue
|
||||
end
|
||||
|
||||
-- Check if the distance from our specified position to the panel is less than the radius.
|
||||
if (v[1]:Distance(position) <= radius) then
|
||||
panelsDeleted[#panelsDeleted + 1] = k
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the plugin data if we actually changed anything.
|
||||
if (#panelsDeleted > 0) then
|
||||
-- Invert index table to delete from highest -> lowest
|
||||
panelsDeleted = table.Reverse(panelsDeleted)
|
||||
|
||||
for _, v in ipairs(panelsDeleted) do
|
||||
-- Remove the panel from the list of panels.
|
||||
table.remove(self.list, v)
|
||||
|
||||
-- Tell the players to stop showing the panel.
|
||||
net.Start("ixPanelRemove")
|
||||
net.WriteUInt(v, 32)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
self:SavePanels()
|
||||
end
|
||||
|
||||
-- Return the number of deleted panels.
|
||||
return #panelsDeleted
|
||||
end
|
||||
|
||||
-- Called after entities have been loaded on the map.
|
||||
function PLUGIN:LoadData()
|
||||
self.list = self:GetData() or {}
|
||||
|
||||
-- Formats table to sequential to support legacy panels.
|
||||
self.list = table.ClearKeys(self.list)
|
||||
end
|
||||
|
||||
-- Called when the plugin needs to save information.
|
||||
function PLUGIN:SavePanels()
|
||||
self:SetData(self.list)
|
||||
end
|
||||
else
|
||||
-- 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 exploded = string.Explode("/", info[6])
|
||||
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(info[6], 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
|
||||
|
||||
-- Panel preview
|
||||
if (ix.chat.currentCommand == "paneladd") 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) <= 4194304) then
|
||||
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(0, 0, panel[i][3] or image:Width(), panel[i][4] or image: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
|
||||
|
||||
-- if there's no URL, then no preview.
|
||||
if (!arguments[1]) 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)
|
||||
local position = (trace.HitPos + angles:Up() * 0.1)
|
||||
local ourPosition = LocalPlayer():GetPos()
|
||||
|
||||
-- validate argument types
|
||||
local scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)
|
||||
local brightness = math.Clamp(math.Round((tonumber(arguments[3]) or 100) * 2.55), 1, 255)
|
||||
|
||||
-- Attempt to collect the dimensions from the Material
|
||||
local width, height = cachedPreview[2]:GetInt("$realwidth"), cachedPreview[2]:GetInt("$realheight")
|
||||
|
||||
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(0, 0, 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() * 0.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
|
||||
})
|
||||
341
garrysmod/gamemodes/helix/plugins/3dtext.lua
Normal file
341
garrysmod/gamemodes/helix/plugins/3dtext.lua
Normal file
@@ -0,0 +1,341 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "3D Text"
|
||||
PLUGIN.author = "Chessnut"
|
||||
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
|
||||
|
||||
-- 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
|
||||
-- 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)
|
||||
-- shadow
|
||||
surface.SetTextPos(x + 1, y + 1)
|
||||
surface.SetTextColor(0, 0, 0, alpha)
|
||||
surface.SetFont(font)
|
||||
surface.DrawText(surfaceText)
|
||||
|
||||
surface.SetTextPos(x, y)
|
||||
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
|
||||
|
||||
-- preview for textadd command
|
||||
if (ix.chat.currentCommand == "textadd") then
|
||||
local arguments = ix.chat.currentArguments
|
||||
local text = tostring(arguments[1] or "")
|
||||
local scale = math.Clamp((tonumber(arguments[2]) or 1) * 0.1, 0.001, 5)
|
||||
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
|
||||
})
|
||||
139
garrysmod/gamemodes/helix/plugins/act/cl_hooks.lua
Normal file
139
garrysmod/gamemodes/helix/plugins/act/cl_hooks.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
local animationTime = 2
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
PLUGIN.cameraFraction = 0
|
||||
|
||||
local function GetHeadBone(client)
|
||||
local head
|
||||
|
||||
for i = 1, client:GetBoneCount() do
|
||||
local name = client:GetBoneName(i)
|
||||
|
||||
if (string.find(name: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")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDrawLocalPlayer(client)
|
||||
if (client:GetNetVar("actEnterAngle") and self.cameraFraction > 0.25) then
|
||||
return true
|
||||
elseif (self.cameraFraction > 0.25) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local forwardOffset = 16
|
||||
local backwardOffset = -32
|
||||
local heightOffset = Vector(0, 0, 20)
|
||||
local idleHeightOffset = Vector(0, 0, 6)
|
||||
local traceMin = Vector(-4, -4, -4)
|
||||
local traceMax = Vector(4, 4, 4)
|
||||
|
||||
function PLUGIN:CalcView(client, origin)
|
||||
local enterAngle = client:GetNetVar("actEnterAngle")
|
||||
local fraction = self.cameraFraction
|
||||
local offset = self.bIdle and forwardOffset or backwardOffset
|
||||
local height = self.bIdle and idleHeightOffset or heightOffset
|
||||
|
||||
if (!enterAngle) then
|
||||
if (fraction > 0) then
|
||||
local view = {
|
||||
origin = LerpVector(fraction, origin, origin + self.forward * offset + height)
|
||||
}
|
||||
|
||||
if (self.cameraTween) then
|
||||
self.cameraTween:update(FrameTime())
|
||||
end
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local view = {}
|
||||
local forward = enterAngle:Forward()
|
||||
local head = GetHeadBone(client)
|
||||
|
||||
local bFirstPerson = true
|
||||
|
||||
if (ix.option.Get("thirdpersonEnabled", false)) then
|
||||
local originPosition = head and client:GetBonePosition(head) or client:GetPos()
|
||||
|
||||
-- check if the camera will hit something
|
||||
local data = util.TraceHull({
|
||||
start = originPosition,
|
||||
endpos = originPosition - client:EyeAngles():Forward() * 48,
|
||||
mins = traceMin * 0.75,
|
||||
maxs = traceMax * 0.75,
|
||||
filter = client
|
||||
})
|
||||
|
||||
bFirstPerson = data.Hit
|
||||
|
||||
if (!bFirstPerson) then
|
||||
view.origin = data.HitPos
|
||||
end
|
||||
end
|
||||
|
||||
if (bFirstPerson) then
|
||||
if (head) then
|
||||
local position = client:GetBonePosition(head) + forward * offset + height
|
||||
local data = {
|
||||
start = (client:GetBonePosition(head) or Vector(0, 0, 64)) + forward * 8,
|
||||
endpos = position + forward * offset,
|
||||
mins = traceMin,
|
||||
maxs = traceMax,
|
||||
filter = client
|
||||
}
|
||||
|
||||
data = util.TraceHull(data)
|
||||
|
||||
if (data.Hit) then
|
||||
view.origin = data.HitPos
|
||||
else
|
||||
view.origin = position
|
||||
end
|
||||
else
|
||||
view.origin = origin + forward * forwardOffset + height
|
||||
end
|
||||
end
|
||||
|
||||
view.origin = LerpVector(fraction, origin, view.origin)
|
||||
|
||||
if (self.cameraTween) then
|
||||
self.cameraTween:update(FrameTime())
|
||||
end
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
net.Receive("ixActEnter", function()
|
||||
PLUGIN.bIdle = net.ReadBool()
|
||||
PLUGIN.forward = LocalPlayer():GetNetVar("actEnterAngle"):Forward()
|
||||
PLUGIN.cameraTween = ix.tween.new(animationTime, PLUGIN, {
|
||||
cameraFraction = 1
|
||||
}, "outQuint")
|
||||
end)
|
||||
|
||||
net.Receive("ixActLeave", function()
|
||||
PLUGIN.cameraTween = ix.tween.new(animationTime * 0.5, PLUGIN, {
|
||||
cameraFraction = 0
|
||||
}, "outQuint")
|
||||
end)
|
||||
166
garrysmod/gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
166
garrysmod/gamemodes/helix/plugins/act/sh_definitions.lua
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
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", {"citizen_male", "citizen_female"}, {
|
||||
start = {"idle_to_sit_ground", "idle_to_sit_chair"},
|
||||
sequence = {"sit_ground", "sit_chair"},
|
||||
finish = {
|
||||
{"sit_ground_to_idle", duration = 2.1},
|
||||
""
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("SitWall", {"citizen_male", "citizen_female"}, {
|
||||
sequence = {
|
||||
{"plazaidle4", check = FacingWallBack},
|
||||
{"injured1", check = FacingWallBack, offset = function(client)
|
||||
return client:GetForward() * 14
|
||||
end}
|
||||
},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Sit", "vortigaunt", {
|
||||
sequence = "chess_wait",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- stand
|
||||
ix.act.Register("Stand", "citizen_male", {
|
||||
sequence = {"lineidle01", "lineidle02", "lineidle03", "lineidle04"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Stand", "citizen_female", {
|
||||
sequence = {"lineidle01", "lineidle02", "lineidle03"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Stand", "metrocop", {
|
||||
sequence = "plazathreat2"
|
||||
})
|
||||
|
||||
-- cheer
|
||||
ix.act.Register("Cheer", "citizen_male", {
|
||||
sequence = {{"cheer1", duration = 1.6}, "cheer2", "wave_smg1"}
|
||||
})
|
||||
|
||||
ix.act.Register("Cheer", "citizen_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 = {{"idle_baton", check = FacingWallBack}, "busyidle2"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- injured
|
||||
ix.act.Register("Injured", "citizen_male", {
|
||||
sequence = {"d1_town05_wounded_idle_1", "d1_town05_wounded_idle_2", "d1_town05_winston_down"},
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
ix.act.Register("Injured", "citizen_female", {
|
||||
sequence = "d1_town05_wounded_idle_1",
|
||||
untimed = true,
|
||||
idle = true
|
||||
})
|
||||
|
||||
-- arrest
|
||||
ix.act.Register("ArrestWall", "citizen_male", {
|
||||
sequence = {
|
||||
{"apcarrestidle",
|
||||
check = FacingWall,
|
||||
offset = function(client)
|
||||
return -client:GetForward() * 23
|
||||
end},
|
||||
"spreadwallidle"
|
||||
},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Arrest", "citizen_male", {
|
||||
sequence = "arrestidle",
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- threat
|
||||
ix.act.Register("Threat", "metrocop", {
|
||||
sequence = "plazathreat1",
|
||||
})
|
||||
|
||||
-- deny
|
||||
ix.act.Register("Deny", "metrocop", {
|
||||
sequence = "harassfront2",
|
||||
})
|
||||
|
||||
-- motion
|
||||
ix.act.Register("Motion", "metrocop", {
|
||||
sequence = {"motionleft", "motionright", "luggagewarn"}
|
||||
})
|
||||
|
||||
-- wave
|
||||
ix.act.Register("Wave", {"citizen_male", "citizen_female"}, {
|
||||
sequence = {{"wave", duration = 2.75}, {"wave_close", duration = 1.75}}
|
||||
})
|
||||
|
||||
-- pant
|
||||
ix.act.Register("Pant", {"citizen_male", "citizen_female"}, {
|
||||
start = {"d2_coast03_postbattle_idle02_entry", "d2_coast03_postbattle_idle01_entry"},
|
||||
sequence = {"d2_coast03_postbattle_idle02", {"d2_coast03_postbattle_idle01", check = FacingWall}},
|
||||
untimed = true
|
||||
})
|
||||
|
||||
-- window
|
||||
ix.act.Register("Window", "citizen_male", {
|
||||
sequence = "d1_t03_tenements_look_out_window_idle",
|
||||
untimed = true
|
||||
})
|
||||
|
||||
ix.act.Register("Window", "citizen_female", {
|
||||
sequence = "d1_t03_lookoutwindow",
|
||||
untimed = true
|
||||
})
|
||||
end
|
||||
259
garrysmod/gamemodes/helix/plugins/act/sh_plugin.lua
Normal file
259
garrysmod/gamemodes/helix/plugins/act/sh_plugin.lua
Normal file
@@ -0,0 +1,259 @@
|
||||
|
||||
--[[--
|
||||
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")
|
||||
|
||||
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.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)
|
||||
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 = PLUGIN: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)
|
||||
if (client.ixUntimedSequence) then
|
||||
client:LeaveSequence()
|
||||
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
|
||||
52
garrysmod/gamemodes/helix/plugins/act/sv_hooks.lua
Normal file
52
garrysmod/gamemodes/helix/plugins/act/sv_hooks.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
||||
89
garrysmod/gamemodes/helix/plugins/ammosave.lua
Normal file
89
garrysmod/gamemodes/helix/plugins/ammosave.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
|
||||
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
|
||||
258
garrysmod/gamemodes/helix/plugins/area/cl_hooks.lua
Normal file
258
garrysmod/gamemodes/helix/plugins/area/cl_hooks.lua
Normal file
@@ -0,0 +1,258 @@
|
||||
|
||||
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)
|
||||
|
||||
self.panel:AddEntry(format, area.properties.color)
|
||||
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)
|
||||
26
garrysmod/gamemodes/helix/plugins/area/cl_plugin.lua
Normal file
26
garrysmod/gamemodes/helix/plugins/area/cl_plugin.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
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
|
||||
182
garrysmod/gamemodes/helix/plugins/area/derma/cl_area.lua
Normal file
182
garrysmod/gamemodes/helix/plugins/area/derma/cl_area.lua
Normal file
@@ -0,0 +1,182 @@
|
||||
|
||||
-- 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")
|
||||
194
garrysmod/gamemodes/helix/plugins/area/derma/cl_areaedit.lua
Normal file
194
garrysmod/gamemodes/helix/plugins/area/derma/cl_areaedit.lua
Normal file
@@ -0,0 +1,194 @@
|
||||
|
||||
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("ixMediumLightFont")
|
||||
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("ixMediumLightFont")
|
||||
self.typeEntry:SetTextColor(color_black)
|
||||
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)
|
||||
panel:SetFont("ixMediumLightFont")
|
||||
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 = 600
|
||||
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() + 50)
|
||||
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
|
||||
@@ -0,0 +1,16 @@
|
||||
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."
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
LANGUAGE = {
|
||||
area = "Зона",
|
||||
areas = "Зоны",
|
||||
areaEditMode = "Редактирование зоны",
|
||||
areaNew = "Новая зона",
|
||||
areaAlreadyExists = "Зона с таким названием уже существует!",
|
||||
areaDoesntExist = "Зона с таким названием не существует!",
|
||||
areaInvalidType = "Вы указали неверный тип зоны!",
|
||||
areaEditTip = "Нажмите, чтобы начать создание зоны. Щелкните правой кнопкой мыши, чтобы выйти.",
|
||||
areaFinishTip = "Нажмите еще раз, чтобы закончить создание зоны. Щелкните правой кнопкой мыши, чтобы вернуться.",
|
||||
areaRemoveTip = "Нажмите перезарядку, чтобы удалить зону, в которой вы находитесь.",
|
||||
areaDeleteConfirm = "Вы уверены, что хотите удалить зону \"%s\"?",
|
||||
areaDelete = "Удалить зону",
|
||||
|
||||
cmdAreaEdit = "Вход в режим редактирования зоны."
|
||||
}
|
||||
90
garrysmod/gamemodes/helix/plugins/area/sh_plugin.lua
Normal file
90
garrysmod/gamemodes/helix/plugins/area/sh_plugin.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
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"
|
||||
}
|
||||
)
|
||||
|
||||
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
|
||||
126
garrysmod/gamemodes/helix/plugins/area/sv_hooks.lua
Normal file
126
garrysmod/gamemodes/helix/plugins/area/sv_hooks.lua
Normal file
@@ -0,0 +1,126 @@
|
||||
|
||||
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 player.Iterator() 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)
|
||||
55
garrysmod/gamemodes/helix/plugins/area/sv_plugin.lua
Normal file
55
garrysmod/gamemodes/helix/plugins/area/sv_plugin.lua
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
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
|
||||
@@ -0,0 +1,180 @@
|
||||
|
||||
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")
|
||||
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())
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetMoney(amount)
|
||||
self.money = math.max(0, math.Round(tonumber(amount) or 0))
|
||||
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 = 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()
|
||||
local definition = ix.container.stored[self:GetModel():lower()]
|
||||
|
||||
ix.storage.Open(activator, inventory, {
|
||||
name = name,
|
||||
entity = self,
|
||||
searchTime = ix.config.Get("containerOpenTime", 0.7),
|
||||
data = {money = self:GetMoney()},
|
||||
OnPlayerOpen = function()
|
||||
if (definition.OnOpen) then
|
||||
definition.OnOpen(self, activator)
|
||||
end
|
||||
end,
|
||||
OnPlayerClose = function()
|
||||
if (definition.OnClose) then
|
||||
definition.OnClose(self, activator)
|
||||
end
|
||||
|
||||
ix.log.Add(activator, "closeContainer", name, inventory:GetID())
|
||||
end
|
||||
})
|
||||
|
||||
if (self:GetLocked()) then
|
||||
self.Sessions[activator:GetCharacter():GetID()] = true
|
||||
end
|
||||
|
||||
ix.log.Add(activator, "openContainer", name, inventory:GetID())
|
||||
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 definition = ix.container.stored[self:GetModel():lower()]
|
||||
|
||||
if (self:GetLocked() and !self.Sessions[character:GetID()]) then
|
||||
self:EmitSound(definition.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
|
||||
126
garrysmod/gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
126
garrysmod/gamemodes/helix/plugins/containers/sh_definitions.lua
Normal file
@@ -0,0 +1,126 @@
|
||||
--[[
|
||||
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
|
||||
})
|
||||
346
garrysmod/gamemodes/helix/plugins/containers/sh_plugin.lua
Normal file
346
garrysmod/gamemodes/helix/plugins/containers/sh_plugin.lua
Normal file
@@ -0,0 +1,346 @@
|
||||
|
||||
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]
|
||||
|
||||
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()
|
||||
end
|
||||
end)
|
||||
|
||||
entity:Remove()
|
||||
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
|
||||
data[#data + 1] = {
|
||||
v:GetPos(),
|
||||
v:GetAngles(),
|
||||
inventory:GetID(),
|
||||
v:GetModel(),
|
||||
v.password,
|
||||
v:GetDisplayName(),
|
||||
v:GetMoney()
|
||||
}
|
||||
end
|
||||
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()
|
||||
local data = self:GetData()
|
||||
|
||||
if (data) then
|
||||
for _, v in ipairs(data) do
|
||||
local data2 = ix.container.stored[v[4]:lower()]
|
||||
|
||||
if (data2) then
|
||||
local inventoryID = tonumber(v[3])
|
||||
|
||||
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])
|
||||
entity:SetAngles(v[2])
|
||||
entity:Spawn()
|
||||
entity:SetModel(v[4])
|
||||
entity:SetSolid(SOLID_VPHYSICS)
|
||||
entity:PhysicsInit(SOLID_VPHYSICS)
|
||||
|
||||
if (v[5]) then
|
||||
entity.password = v[5]
|
||||
entity:SetLocked(true)
|
||||
entity.Sessions = {}
|
||||
entity.PasswordAttempts = {}
|
||||
end
|
||||
|
||||
if (v[6]) then
|
||||
entity:SetDisplayName(v[6])
|
||||
end
|
||||
|
||||
if (v[7]) then
|
||||
entity:SetMoney(v[7])
|
||||
end
|
||||
|
||||
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 steamID = client:SteamID()
|
||||
local attempts = entity.PasswordAttempts[steamID]
|
||||
|
||||
if (attempts and attempts >= 10) then
|
||||
client:NotifyLocalized("passwordAttemptLimit")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
entity:OpenInventory(client)
|
||||
else
|
||||
entity.PasswordAttempts[steamID] = attempts and attempts + 1 or 1
|
||||
|
||||
client:NotifyLocalized("wrongPassword")
|
||||
end
|
||||
end
|
||||
|
||||
client.ixNextContainerPassword = RealTime() + 1
|
||||
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 arg = {...}
|
||||
return string.format("%s closed the '%s' #%d container.", client: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 = {}
|
||||
entity.PasswordAttempts = {}
|
||||
|
||||
if (password:len() != 0) then
|
||||
entity:SetLocked(true)
|
||||
entity.password = password
|
||||
|
||||
client:NotifyLocalized("containerPassword", password)
|
||||
else
|
||||
entity:SetLocked(false)
|
||||
entity.password = nil
|
||||
|
||||
client:NotifyLocalized("containerPasswordRemove")
|
||||
end
|
||||
|
||||
local name = entity:GetDisplayName()
|
||||
local inventory = entity:GetInventory()
|
||||
|
||||
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
|
||||
|
||||
local inventory = entity:GetInventory()
|
||||
|
||||
ix.log.Add(client, "containerName", name, inventory:GetID(), name:len() != 0)
|
||||
end
|
||||
})
|
||||
110
garrysmod/gamemodes/helix/plugins/crosshair.lua
Normal file
110
garrysmod/gamemodes/helix/plugins/crosshair.lua
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
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
|
||||
204
garrysmod/gamemodes/helix/plugins/doors/cl_plugin.lua
Normal file
204
garrysmod/gamemodes/helix/plugins/doors/cl_plugin.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
|
||||
-- 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()
|
||||
|
||||
if (IsValid(door) and !table.IsEmpty(access)) then
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
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)
|
||||
102
garrysmod/gamemodes/helix/plugins/doors/derma/cl_door.lua
Normal file
102
garrysmod/gamemodes/helix/plugins/doors/derma/cl_door.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
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()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetDoor(door, access, door2)
|
||||
door.ixPanel = self
|
||||
|
||||
self.accessData = access
|
||||
self.door = door
|
||||
|
||||
for _, v in player.Iterator() 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")
|
||||
@@ -0,0 +1,201 @@
|
||||
|
||||
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
|
||||
497
garrysmod/gamemodes/helix/plugins/doors/sh_commands.lua
Normal file
497
garrysmod/gamemodes/helix/plugins/doors/sh_commands.lua
Normal file
@@ -0,0 +1,497 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
ix.command.Add("DoorSell", {
|
||||
description = "@cmdDoorSell",
|
||||
OnRun = function(self, client, arguments)
|
||||
-- Get the entity 96 units infront of the player.
|
||||
local data = {}
|
||||
data.start = client:GetShootPos()
|
||||
data.endpos = data.start + client:GetAimVector() * 96
|
||||
data.filter = client
|
||||
local trace = util.TraceLine(data)
|
||||
local entity = trace.Entity
|
||||
|
||||
-- Check if the entity is a valid door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
-- Check if the player owners the door.
|
||||
if (client == entity:GetDTEntity(0)) then
|
||||
entity = IsValid(entity.ixParent) and entity.ixParent or entity
|
||||
|
||||
-- Get the price that the door is sold for.
|
||||
local price = math.Round(entity:GetNetVar("price", ix.config.Get("doorCost")) * ix.config.Get("doorSellRatio"))
|
||||
local character = client:GetCharacter()
|
||||
|
||||
-- Remove old door information.
|
||||
entity:RemoveDoorAccessData()
|
||||
|
||||
local doors = character:GetVar("doors") or {}
|
||||
|
||||
for k, v in ipairs(doors) do
|
||||
if (v == entity) then
|
||||
table.remove(doors, k)
|
||||
end
|
||||
end
|
||||
|
||||
character:SetVar("doors", doors, true)
|
||||
|
||||
-- Take their money and notify them.
|
||||
character:GiveMoney(price)
|
||||
hook.Run("OnPlayerPurchaseDoor", client, entity, false, PLUGIN.CallOnDoorChildren)
|
||||
|
||||
ix.log.Add(client, "selldoor")
|
||||
return "@dSold", ix.currency.Get(price)
|
||||
else
|
||||
-- Otherwise tell them they can not.
|
||||
return "@notOwner"
|
||||
end
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorBuy", {
|
||||
description = "@cmdDoorBuy",
|
||||
OnRun = function(self, client, arguments)
|
||||
-- Get the entity 96 units infront of the player.
|
||||
local data = {}
|
||||
data.start = client:GetShootPos()
|
||||
data.endpos = data.start + client:GetAimVector() * 96
|
||||
data.filter = client
|
||||
local trace = util.TraceLine(data)
|
||||
local entity = trace.Entity
|
||||
|
||||
-- Check if the entity is a valid door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
if (!entity:GetNetVar("ownable") or entity:GetNetVar("faction") or entity:GetNetVar("class")) then
|
||||
return "@dNotAllowedToOwn"
|
||||
end
|
||||
|
||||
if (IsValid(entity:GetDTEntity(0))) then
|
||||
return "@dOwnedBy", entity:GetDTEntity(0):Name()
|
||||
end
|
||||
|
||||
entity = IsValid(entity.ixParent) and entity.ixParent or entity
|
||||
|
||||
-- Get the price that the door is bought for.
|
||||
local price = entity:GetNetVar("price", ix.config.Get("doorCost"))
|
||||
local character = client:GetCharacter()
|
||||
|
||||
-- Check if the player can actually afford it.
|
||||
if (character:HasMoney(price)) then
|
||||
-- Set the door to be owned by this player.
|
||||
entity:SetDTEntity(0, client)
|
||||
entity.ixAccess = {
|
||||
[client] = DOOR_OWNER
|
||||
}
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetDTEntity(0, client)
|
||||
end)
|
||||
|
||||
local doors = character:GetVar("doors") or {}
|
||||
doors[#doors + 1] = entity
|
||||
character:SetVar("doors", doors, true)
|
||||
|
||||
-- Take their money and notify them.
|
||||
character:TakeMoney(price)
|
||||
hook.Run("OnPlayerPurchaseDoor", client, entity, true, PLUGIN.CallOnDoorChildren)
|
||||
|
||||
ix.log.Add(client, "buydoor")
|
||||
return "@dPurchased", ix.currency.Get(price)
|
||||
else
|
||||
-- Otherwise tell them they can not.
|
||||
return "@canNotAfford"
|
||||
end
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetUnownable", {
|
||||
description = "@cmdDoorSetUnownable",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, name)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
-- Set it so it is unownable.
|
||||
entity:SetNetVar("ownable", nil)
|
||||
|
||||
-- Change the name of the door if needed.
|
||||
if (name:find("%S")) then
|
||||
entity:SetNetVar("name", name)
|
||||
end
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetNetVar("ownable", nil)
|
||||
|
||||
if (name:find("%S")) then
|
||||
child:SetNetVar("name", name)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Save the door information.
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dMadeUnownable"
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetOwnable", {
|
||||
description = "@cmdDoorSetOwnable",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, name)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
-- Set it so it is ownable.
|
||||
entity:SetNetVar("ownable", true)
|
||||
entity:SetNetVar("visible", true)
|
||||
|
||||
-- Update the name.
|
||||
if (name:find("%S")) then
|
||||
entity:SetNetVar("name", name)
|
||||
end
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetNetVar("ownable", true)
|
||||
child:SetNetVar("visible", true)
|
||||
|
||||
if (name:find("%S")) then
|
||||
child:SetNetVar("name", name)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Save the door information.
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dMadeOwnable"
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetFaction", {
|
||||
description = "@cmdDoorSetFaction",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.text, ix.type.optional),
|
||||
OnRun = function(self, client, name)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
if (!name or name == "") then
|
||||
entity.ixFactionID = nil
|
||||
entity:SetNetVar("faction", nil)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function()
|
||||
entity.ixFactionID = nil
|
||||
entity:SetNetVar("faction", nil)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dRemoveFaction"
|
||||
end
|
||||
|
||||
local faction
|
||||
|
||||
-- Loop through each faction, checking the uniqueID and name.
|
||||
for k, v in pairs(ix.faction.teams) do
|
||||
if (ix.util.StringMatches(k, name) or ix.util.StringMatches(L(v.name, client), name)) then
|
||||
-- This faction matches the provided string.
|
||||
faction = v
|
||||
|
||||
-- Escape the loop.
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if a faction was found.
|
||||
if (faction) then
|
||||
entity.ixFactionID = faction.uniqueID
|
||||
entity:SetNetVar("faction", faction.index)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function()
|
||||
entity.ixFactionID = faction.uniqueID
|
||||
entity:SetNetVar("faction", faction.index)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dSetFaction", L(faction.name, client)
|
||||
-- The faction was not found.
|
||||
else
|
||||
return "@invalidFaction"
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetDisabled", {
|
||||
description = "@cmdDoorSetDisabled",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = ix.type.bool,
|
||||
OnRun = function(self, client, bDisabled)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor()) then
|
||||
-- Set it so it is ownable.
|
||||
entity:SetNetVar("disabled", bDisabled)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetNetVar("disabled", bDisabled)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
|
||||
-- Tell the player they have made the door (un)disabled.
|
||||
return "@dSet" .. (bDisabled and "" or "Not") .. "Disabled"
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetTitle", {
|
||||
description = "@cmdDoorSetTitle",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, name)
|
||||
-- Get the door infront of the player.
|
||||
local data = {}
|
||||
data.start = client:GetShootPos()
|
||||
data.endpos = data.start + client:GetAimVector() * 96
|
||||
data.filter = client
|
||||
local trace = util.TraceLine(data)
|
||||
local entity = trace.Entity
|
||||
|
||||
-- Validate the door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
-- Make sure the name contains actual characters.
|
||||
if (!name:find("%S")) then
|
||||
return "@invalidArg", 1
|
||||
end
|
||||
|
||||
--[[
|
||||
NOTE: Here, we are setting two different networked names.
|
||||
The title is a temporary name, while the other name is the
|
||||
default name for the door. The reason for this is so when the
|
||||
server closes while someone owns the door, it doesn't save THEIR
|
||||
title, which could lead to unwanted things.
|
||||
--]]
|
||||
|
||||
name = name:utf8sub(1, 24)
|
||||
|
||||
-- Check if they are allowed to change the door's name.
|
||||
if (entity:CheckDoorAccess(client, DOOR_TENANT)) then
|
||||
entity:SetNetVar("title", name)
|
||||
elseif (CAMI.PlayerHasAccess(client, "Helix - Manage Doors", nil)) then
|
||||
entity:SetNetVar("name", name)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetNetVar("name", name)
|
||||
end)
|
||||
else
|
||||
-- Otherwise notify the player he/she can't.
|
||||
return "@notOwner"
|
||||
end
|
||||
else
|
||||
-- Notification of the door not being valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetParent", {
|
||||
description = "@cmdDoorSetParent",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client, arguments)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
client.ixDoorParent = entity
|
||||
return "@dSetParentDoor"
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetChild", {
|
||||
description = "@cmdDoorSetChild",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client, arguments)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
if (client.ixDoorParent == entity) then
|
||||
return "@dCanNotSetAsChild"
|
||||
end
|
||||
|
||||
-- Check if the player has set a door as a parent.
|
||||
if (IsValid(client.ixDoorParent)) then
|
||||
-- Add the door to the parent's list of children.
|
||||
client.ixDoorParent.ixChildren = client.ixDoorParent.ixChildren or {}
|
||||
client.ixDoorParent.ixChildren[entity:MapCreationID()] = true
|
||||
|
||||
-- Set the door's parent to the parent.
|
||||
entity.ixParent = client.ixDoorParent
|
||||
|
||||
-- Save the door information.
|
||||
PLUGIN:SaveDoorData()
|
||||
PLUGIN:CopyParentDoor(entity)
|
||||
|
||||
return "@dAddChildDoor"
|
||||
else
|
||||
-- Tell the player they do not have a door parent.
|
||||
return "@dNoParentDoor"
|
||||
end
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorRemoveChild", {
|
||||
description = "@cmdDoorRemoveChild",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
OnRun = function(self, client, arguments)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
if (client.ixDoorParent == entity) then
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child.ixParent = nil
|
||||
end)
|
||||
|
||||
entity.ixChildren = nil
|
||||
return "@dRemoveChildren"
|
||||
end
|
||||
|
||||
-- Check if the player has set a door as a parent.
|
||||
if (IsValid(entity.ixParent) and entity.ixParent.ixChildren) then
|
||||
-- Remove the door from the list of children.
|
||||
entity.ixParent.ixChildren[entity:MapCreationID()] = nil
|
||||
-- Remove the variable for the parent.
|
||||
entity.ixParent = nil
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dRemoveChildDoor"
|
||||
end
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetHidden", {
|
||||
description = "@cmdDoorSetHidden",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = ix.type.bool,
|
||||
OnRun = function(self, client, bHidden)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor()) then
|
||||
entity:SetNetVar("visible", !bHidden)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function(child)
|
||||
child:SetNetVar("visible", !bHidden)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
|
||||
-- Tell the player they have made the door (un)hidden.
|
||||
return "@dSet" .. (bHidden and "" or "Not") .. "Hidden"
|
||||
else
|
||||
-- Tell the player the door isn't valid.
|
||||
return "@dNotValid"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("DoorSetClass", {
|
||||
description = "@cmdDoorSetClass",
|
||||
privilege = "Manage Doors",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.text, ix.type.optional),
|
||||
OnRun = function(self, client, name)
|
||||
-- Get the door the player is looking at.
|
||||
local entity = client:GetEyeTrace().Entity
|
||||
|
||||
-- Validate it is a door.
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("disabled")) then
|
||||
if (!name or name == "") then
|
||||
entity:SetNetVar("class", nil)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function()
|
||||
entity:SetNetVar("class", nil)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dRemoveClass"
|
||||
end
|
||||
|
||||
local class, classData
|
||||
|
||||
for k, v in pairs(ix.class.list) do
|
||||
if (ix.util.StringMatches(v.name, name) or ix.util.StringMatches(L(v.name, client), name)) then
|
||||
class, classData = k, v
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if a faction was found.
|
||||
if (class) then
|
||||
entity.ixClassID = class
|
||||
entity:SetNetVar("class", class)
|
||||
|
||||
PLUGIN:CallOnDoorChildren(entity, function()
|
||||
entity.ixClassID = class
|
||||
entity:SetNetVar("class", class)
|
||||
end)
|
||||
|
||||
PLUGIN:SaveDoorData()
|
||||
return "@dSetClass", L(classData.name, client)
|
||||
else
|
||||
return "@invalidClass"
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
83
garrysmod/gamemodes/helix/plugins/doors/sh_plugin.lua
Normal file
83
garrysmod/gamemodes/helix/plugins/doors/sh_plugin.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Doors"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "A simple door system."
|
||||
|
||||
-- luacheck: globals DOOR_OWNER DOOR_TENANT DOOR_GUEST DOOR_NONE
|
||||
DOOR_OWNER = 3
|
||||
DOOR_TENANT = 2
|
||||
DOOR_GUEST = 1
|
||||
DOOR_NONE = 0
|
||||
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sh_commands.lua")
|
||||
|
||||
do
|
||||
local entityMeta = FindMetaTable("Entity")
|
||||
|
||||
function entityMeta:CheckDoorAccess(client, access)
|
||||
if (!self:IsDoor()) then
|
||||
return false
|
||||
end
|
||||
|
||||
access = access or DOOR_GUEST
|
||||
|
||||
local parent = self.ixParent
|
||||
|
||||
if (IsValid(parent)) then
|
||||
return parent:CheckDoorAccess(client, access)
|
||||
end
|
||||
|
||||
if (hook.Run("CanPlayerAccessDoor", client, self, access)) then
|
||||
return true
|
||||
end
|
||||
|
||||
if (self.ixAccess and (self.ixAccess[client] or 0) >= access) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
function entityMeta:RemoveDoorAccessData()
|
||||
local receivers = {}
|
||||
|
||||
for k, _ in pairs(self.ixAccess or {}) do
|
||||
receivers[#receivers + 1] = k
|
||||
end
|
||||
|
||||
if (#receivers > 0) then
|
||||
net.Start("ixDoorMenu")
|
||||
net.WriteEntity(self)
|
||||
net.WriteTable({})
|
||||
net.Send(receivers)
|
||||
end
|
||||
|
||||
self.ixAccess = {}
|
||||
self:SetDTEntity(0, nil)
|
||||
|
||||
-- Remove door information on child doors
|
||||
PLUGIN:CallOnDoorChildren(self, function(child)
|
||||
child:SetDTEntity(0, nil)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Configurations for door prices.
|
||||
ix.config.Add("doorCost", 10, "The price to purchase a door.", nil, {
|
||||
data = {min = 0, max = 500},
|
||||
category = "dConfigName"
|
||||
})
|
||||
ix.config.Add("doorSellRatio", 0.5, "How much of the door price is returned when selling a door.", nil, {
|
||||
data = {min = 0, max = 1.0, decimals = 1},
|
||||
category = "dConfigName"
|
||||
})
|
||||
ix.config.Add("doorLockTime", 1, "How long it takes to (un)lock a door.", nil, {
|
||||
data = {min = 0, max = 10.0, decimals = 1},
|
||||
category = "dConfigName"
|
||||
})
|
||||
285
garrysmod/gamemodes/helix/plugins/doors/sv_plugin.lua
Normal file
285
garrysmod/gamemodes/helix/plugins/doors/sv_plugin.lua
Normal file
@@ -0,0 +1,285 @@
|
||||
|
||||
util.AddNetworkString("ixDoorMenu")
|
||||
util.AddNetworkString("ixDoorPermission")
|
||||
|
||||
-- Variables for door data.
|
||||
local variables = {
|
||||
-- Whether or not the door will be disabled.
|
||||
"disabled",
|
||||
-- The name of the door.
|
||||
"name",
|
||||
-- Price of the door.
|
||||
"price",
|
||||
-- If the door is ownable.
|
||||
"ownable",
|
||||
-- The faction that owns a door.
|
||||
"faction",
|
||||
-- The class that owns a door.
|
||||
"class",
|
||||
-- Whether or not the door will be hidden.
|
||||
"visible"
|
||||
}
|
||||
|
||||
function PLUGIN:CallOnDoorChildren(entity, callback)
|
||||
local parent
|
||||
|
||||
if (entity.ixChildren) then
|
||||
parent = entity
|
||||
elseif (entity.ixParent) then
|
||||
parent = entity.ixParent
|
||||
end
|
||||
|
||||
if (IsValid(parent)) then
|
||||
callback(parent)
|
||||
|
||||
for k, _ in pairs(parent.ixChildren) do
|
||||
local child = ents.GetMapCreatedEntity(k)
|
||||
|
||||
if (IsValid(child)) then
|
||||
callback(child)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CopyParentDoor(child)
|
||||
local parent = child.ixParent
|
||||
|
||||
if (IsValid(parent)) then
|
||||
for _, v in ipairs(variables) do
|
||||
local value = parent:GetNetVar(v)
|
||||
|
||||
if (child:GetNetVar(v) != value) then
|
||||
child:SetNetVar(v, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Called after the entities have loaded.
|
||||
function PLUGIN:LoadData()
|
||||
-- Restore the saved door information.
|
||||
local data = self:GetData()
|
||||
|
||||
if (!data) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Loop through all of the saved doors.
|
||||
for k, v in pairs(data) do
|
||||
-- Get the door entity from the saved ID.
|
||||
local entity = ents.GetMapCreatedEntity(k)
|
||||
|
||||
-- Check it is a valid door in-case something went wrong.
|
||||
if (IsValid(entity) and entity:IsDoor()) then
|
||||
-- Loop through all of our door variables.
|
||||
for k2, v2 in pairs(v) do
|
||||
if (k2 == "children") then
|
||||
entity.ixChildren = v2
|
||||
|
||||
for index, _ in pairs(v2) do
|
||||
local door = ents.GetMapCreatedEntity(index)
|
||||
|
||||
if (IsValid(door)) then
|
||||
door.ixParent = entity
|
||||
end
|
||||
end
|
||||
elseif (k2 == "faction") then
|
||||
for k3, v3 in pairs(ix.faction.teams) do
|
||||
if (k3 == v2) then
|
||||
entity.ixFactionID = k3
|
||||
entity:SetNetVar("faction", v3.index)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
entity:SetNetVar(k2, v2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Called before the gamemode shuts down.
|
||||
function PLUGIN:SaveDoorData()
|
||||
-- Create an empty table to save information in.
|
||||
local data = {}
|
||||
local doors = {}
|
||||
|
||||
for _, v in ents.Iterator() do
|
||||
if (v:IsDoor()) then
|
||||
doors[v:MapCreationID()] = v
|
||||
end
|
||||
end
|
||||
|
||||
local doorData
|
||||
|
||||
-- Loop through doors with information.
|
||||
for k, v in pairs(doors) do
|
||||
-- Another empty table for actual information regarding the door.
|
||||
doorData = {}
|
||||
|
||||
-- Save all of the needed variables to the doorData table.
|
||||
for _, v2 in ipairs(variables) do
|
||||
local value = v:GetNetVar(v2)
|
||||
|
||||
if (value) then
|
||||
doorData[v2] = v:GetNetVar(v2)
|
||||
end
|
||||
end
|
||||
|
||||
if (v.ixChildren) then
|
||||
doorData.children = v.ixChildren
|
||||
end
|
||||
|
||||
if (v.ixClassID) then
|
||||
doorData.class = v.ixClassID
|
||||
end
|
||||
|
||||
if (v.ixFactionID) then
|
||||
doorData.faction = v.ixFactionID
|
||||
end
|
||||
|
||||
-- Add the door to the door information.
|
||||
if (!table.IsEmpty(doorData)) then
|
||||
data[k] = doorData
|
||||
end
|
||||
end
|
||||
-- Save all of the door information.
|
||||
self:SetData(data)
|
||||
end
|
||||
|
||||
function PLUGIN:CanPlayerUseDoor(client, entity)
|
||||
if (entity:GetNetVar("disabled")) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Whether or not a player a player has any abilities over the door, such as locking.
|
||||
function PLUGIN:CanPlayerAccessDoor(client, door, access)
|
||||
local faction = door:GetNetVar("faction")
|
||||
|
||||
-- If the door has a faction set which the client is a member of, allow access.
|
||||
if (faction and client:Team() == faction) then
|
||||
return true
|
||||
end
|
||||
|
||||
local class = door:GetNetVar("class")
|
||||
|
||||
-- If the door has a faction set which the client is a member of, allow access.
|
||||
local classData = ix.class.list[class]
|
||||
local charClass = client:GetCharacter():GetClass()
|
||||
local classData2 = ix.class.list[charClass]
|
||||
|
||||
if (class and classData and classData2) then
|
||||
if (classData.team) then
|
||||
if (classData.team != classData2.team) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
if (charClass != class) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PostPlayerLoadout(client)
|
||||
client:Give("ix_keys")
|
||||
end
|
||||
|
||||
function PLUGIN:ShowTeam(client)
|
||||
local data = {}
|
||||
data.start = client:GetShootPos()
|
||||
data.endpos = data.start + client:GetAimVector() * 96
|
||||
data.filter = client
|
||||
local trace = util.TraceLine(data)
|
||||
local entity = trace.Entity
|
||||
|
||||
if (IsValid(entity) and entity:IsDoor() and !entity:GetNetVar("faction") and !entity:GetNetVar("class")) then
|
||||
if (entity:CheckDoorAccess(client, DOOR_TENANT)) then
|
||||
local door = entity
|
||||
|
||||
if (IsValid(door.ixParent)) then
|
||||
door = door.ixParent
|
||||
end
|
||||
|
||||
net.Start("ixDoorMenu")
|
||||
net.WriteEntity(door)
|
||||
net.WriteTable(door.ixAccess)
|
||||
net.WriteEntity(entity)
|
||||
net.Send(client)
|
||||
elseif (!IsValid(entity:GetDTEntity(0))) then
|
||||
ix.command.Run(client, "doorbuy")
|
||||
else
|
||||
client:NotifyLocalized("notAllowed")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, curChar, prevChar)
|
||||
if (prevChar) then
|
||||
local doors = prevChar:GetVar("doors") or {}
|
||||
|
||||
for _, v in ipairs(doors) do
|
||||
if (IsValid(v) and v:IsDoor() and v:GetDTEntity(0) == client) then
|
||||
v:RemoveDoorAccessData()
|
||||
end
|
||||
end
|
||||
|
||||
prevChar:SetVar("doors", nil)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDisconnected(client)
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (character) then
|
||||
local doors = character:GetVar("doors") or {}
|
||||
|
||||
for _, v in ipairs(doors) do
|
||||
if (IsValid(v) and v:IsDoor() and v:GetDTEntity(0) == client) then
|
||||
v:RemoveDoorAccessData()
|
||||
end
|
||||
end
|
||||
|
||||
character:SetVar("doors", nil)
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixDoorPermission", function(length, client)
|
||||
local door = net.ReadEntity()
|
||||
local target = net.ReadEntity()
|
||||
local access = net.ReadUInt(4)
|
||||
|
||||
if (IsValid(target) and target:GetCharacter() and door.ixAccess and door:GetDTEntity(0) == client and target != client) then
|
||||
access = math.Clamp(access or 0, DOOR_NONE, DOOR_TENANT)
|
||||
|
||||
if (access == door.ixAccess[target]) then
|
||||
return
|
||||
end
|
||||
|
||||
door.ixAccess[target] = access
|
||||
|
||||
local recipient = {}
|
||||
|
||||
for k, v in pairs(door.ixAccess) do
|
||||
if (v > DOOR_GUEST) then
|
||||
recipient[#recipient + 1] = k
|
||||
end
|
||||
end
|
||||
|
||||
if (#recipient > 0) then
|
||||
net.Start("ixDoorPermission")
|
||||
net.WriteEntity(door)
|
||||
net.WriteEntity(target)
|
||||
net.WriteUInt(access, 4)
|
||||
net.Send(recipient)
|
||||
end
|
||||
end
|
||||
end)
|
||||
228
garrysmod/gamemodes/helix/plugins/logging.lua
Normal file
228
garrysmod/gamemodes/helix/plugins/logging.lua
Normal file
@@ -0,0 +1,228 @@
|
||||
|
||||
PLUGIN.name = "Logging"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "You can modfiy the logging text/lists on this plugin."
|
||||
|
||||
if (SERVER) then
|
||||
local L = Format
|
||||
|
||||
ix.log.AddType("chat", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("[%s] %s: %s", arg[1], client:Name(), arg[2])
|
||||
end)
|
||||
|
||||
ix.log.AddType("command", function(client, ...)
|
||||
local arg = {...}
|
||||
|
||||
if (arg[2] and #arg[2] > 0) then
|
||||
return L("%s used command '%s %s'.", client:Name(), arg[1], arg[2])
|
||||
else
|
||||
return L("%s used command '%s'.", client:Name(), arg[1])
|
||||
end
|
||||
end)
|
||||
|
||||
ix.log.AddType("cfgSet", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s set %s to '%s'.", client:Name(), arg[1], arg[2])
|
||||
end, FLAG_DANGER)
|
||||
|
||||
ix.log.AddType("connect", function(client, ...)
|
||||
return L("%s has connected.", client:SteamName())
|
||||
end, FLAG_NORMAL)
|
||||
|
||||
ix.log.AddType("disconnect", function(client, ...)
|
||||
if (client:IsTimingOut()) then
|
||||
return L("%s (%s) has disconnected (timed out).", client:SteamName(), client:SteamID())
|
||||
else
|
||||
return L("%s (%s) has disconnected.", client:SteamName(), client:SteamID())
|
||||
end
|
||||
end, FLAG_NORMAL)
|
||||
|
||||
ix.log.AddType("charCreate", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s created the character '%s'", client:SteamName(), arg[1])
|
||||
end, FLAG_SERVER)
|
||||
|
||||
ix.log.AddType("charLoad", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s loaded the character '%s'", client:SteamName(), arg[1])
|
||||
end, FLAG_SERVER)
|
||||
|
||||
ix.log.AddType("charDelete", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s (%s) deleted character '%s'", client:SteamName(), client:SteamID(), arg[1])
|
||||
end, FLAG_SERVER)
|
||||
|
||||
ix.log.AddType("itemAction", function(client, ...)
|
||||
local arg = {...}
|
||||
local item = arg[2]
|
||||
return L("%s ran '%s' on item '%s' (#%s)", client:Name(), arg[1], item:GetName(), item:GetID())
|
||||
end, FLAG_NORMAL)
|
||||
|
||||
ix.log.AddType("itemDestroy", function(client, itemName, itemID)
|
||||
local name = client:GetName() ~= "" and client:GetName() or client:GetClass()
|
||||
return L("%s destroyed a '%s' #%d.", name, itemName, itemID)
|
||||
end, FLAG_WARNING)
|
||||
|
||||
ix.log.AddType("shipmentTake", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s took '%s' from the shipment", client:Name(), arg[1])
|
||||
end, FLAG_WARNING)
|
||||
|
||||
ix.log.AddType("shipmentOrder", function(client, ...)
|
||||
return L("%s ordered a shipment", client:Name())
|
||||
end, FLAG_SUCCESS)
|
||||
|
||||
ix.log.AddType("buy", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s purchased '%s' from the NPC", client:Name(), arg[1])
|
||||
end, FLAG_SUCCESS)
|
||||
|
||||
ix.log.AddType("buydoor", function(client, ...)
|
||||
return L("%s has purchased a door.", client:Name())
|
||||
end, FLAG_SUCCESS)
|
||||
|
||||
ix.log.AddType("selldoor", function(client, ...)
|
||||
return L("%s has sold a door.", client:Name())
|
||||
end, FLAG_SUCCESS)
|
||||
|
||||
ix.log.AddType("playerHurt", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s has taken %d damage from %s.", client:Name(), arg[1], arg[2])
|
||||
end, FLAG_WARNING)
|
||||
|
||||
ix.log.AddType("playerDeath", function(client, ...)
|
||||
local arg = {...}
|
||||
return L("%s has killed %s%s.", arg[1], client:Name(), arg[2] and (" with " .. arg[2]) or "")
|
||||
end, FLAG_DANGER)
|
||||
|
||||
ix.log.AddType("money", function(client, amount)
|
||||
return L("%s has %s %s.", client:Name(), amount < 0 and "lost" or "gained", ix.currency.Get(math.abs(amount)))
|
||||
end, FLAG_SUCCESS)
|
||||
|
||||
ix.log.AddType("inventoryAdd", function(client, characterName, itemName, itemID)
|
||||
return L("%s has gained a '%s' #%d.", characterName, itemName, itemID)
|
||||
end, FLAG_WARNING)
|
||||
|
||||
ix.log.AddType("inventoryRemove", function(client, characterName, itemName, itemID)
|
||||
return L("%s has lost a '%s' #%d.", characterName, itemName, itemID)
|
||||
end, FLAG_WARNING)
|
||||
|
||||
ix.log.AddType("storageMoneyTake", function(client, entity, amount, total)
|
||||
local name = entity.GetDisplayName and entity:GetDisplayName() or entity:GetName()
|
||||
|
||||
return string.format("%s has taken %d %s from '%s' #%d (%d %s left).",
|
||||
client:GetName(), amount, ix.currency.plural, name,
|
||||
entity:GetInventory():GetID(), total, ix.currency.plural)
|
||||
end)
|
||||
|
||||
ix.log.AddType("storageMoneyGive", function(client, entity, amount, total)
|
||||
local name = entity.GetDisplayName and entity:GetDisplayName() or entity:GetName()
|
||||
|
||||
return string.format("%s has given %d %s to '%s' #%d (%d %s left).",
|
||||
client:GetName(), amount, ix.currency.plural, name,
|
||||
entity:GetInventory():GetID(), total, ix.currency.plural)
|
||||
end)
|
||||
|
||||
ix.log.AddType("roll", function(client, value, max)
|
||||
return string.format("%s rolled %d out of %d.", client:Name(), value, max)
|
||||
end)
|
||||
|
||||
ix.log.AddType("pluginLoaded", function(client, uniqueID)
|
||||
return string.format("%s has enabled the %s plugin for next restart.", client:GetName(), uniqueID)
|
||||
end)
|
||||
|
||||
ix.log.AddType("pluginUnloaded", function(client, uniqueID)
|
||||
return string.format("%s has disabled the %s plugin for next restart.", client:GetName(), uniqueID)
|
||||
end)
|
||||
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
ix.log.Add(client, "connect")
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerDisconnected(client)
|
||||
ix.log.Add(client, "disconnect")
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterCreated(client, character)
|
||||
ix.log.Add(client, "charCreate", character:GetName())
|
||||
end
|
||||
|
||||
function PLUGIN:CharacterLoaded(character)
|
||||
local client = character:GetPlayer()
|
||||
ix.log.Add(client, "charLoad", character:GetName())
|
||||
end
|
||||
|
||||
function PLUGIN:PreCharacterDeleted(client, character)
|
||||
ix.log.Add(client, "charDelete", character:GetName())
|
||||
end
|
||||
|
||||
function PLUGIN:ShipmentItemTaken(client, itemClass, amount)
|
||||
local itemTable = ix.item.list[itemClass]
|
||||
ix.log.Add(client, "shipmentTake", itemTable:GetName())
|
||||
end
|
||||
|
||||
function PLUGIN:CreateShipment(client, shipmentEntity)
|
||||
ix.log.Add(client, "shipmentOrder")
|
||||
end
|
||||
|
||||
function PLUGIN:CharacterVendorTraded(client, vendor, x, y, invID, price, isSell)
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerInteractItem(client, action, item)
|
||||
if (isentity(item)) then
|
||||
if (IsValid(item)) then
|
||||
local itemID = item.ixItemID
|
||||
item = ix.item.instances[itemID]
|
||||
else
|
||||
return
|
||||
end
|
||||
elseif (isnumber(item)) then
|
||||
item = ix.item.instances[item]
|
||||
end
|
||||
|
||||
if (!item) then
|
||||
return
|
||||
end
|
||||
|
||||
ix.log.Add(client, "itemAction", action, item)
|
||||
end
|
||||
|
||||
function PLUGIN:InventoryItemAdded(oldInv, inventory, item)
|
||||
if (!inventory.owner or (oldInv and oldInv.owner == inventory.owner)) then
|
||||
return
|
||||
end
|
||||
|
||||
local character = ix.char.loaded[inventory.owner]
|
||||
|
||||
ix.log.Add(character:GetPlayer(), "inventoryAdd", character:GetName(), item:GetName(), item:GetID())
|
||||
|
||||
if (item.isBag) then
|
||||
local bagInventory = item:GetInventory()
|
||||
|
||||
if (!bagInventory) then
|
||||
return
|
||||
end
|
||||
|
||||
for k, _ in bagInventory:Iter() do
|
||||
ix.log.Add(character:GetPlayer(), "inventoryAdd", character:GetName(), k:GetName(), k:GetID())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InventoryItemRemoved(inventory, item)
|
||||
if (!inventory.owner) then
|
||||
return
|
||||
end
|
||||
|
||||
local character = ix.char.loaded[inventory.owner]
|
||||
|
||||
ix.log.Add(character:GetPlayer(), "inventoryRemove", character:GetName(), item:GetName(), item:GetID())
|
||||
|
||||
if (item.isBag) then
|
||||
for k, _ in item:GetInventory():Iter() do
|
||||
ix.log.Add(character:GetPlayer(), "inventoryRemove", character:GetName(), k:GetName(), k:GetID())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
277
garrysmod/gamemodes/helix/plugins/mapscene.lua
Normal file
277
garrysmod/gamemodes/helix/plugins/mapscene.lua
Normal file
@@ -0,0 +1,277 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Map Scenes"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds areas of the map that are visible during character selection."
|
||||
PLUGIN.scenes = PLUGIN.scenes or {}
|
||||
|
||||
local x3, y3 = 0, 0
|
||||
local realOrigin = Vector(0, 0, 0)
|
||||
local realAngles = Angle(0, 0, 0)
|
||||
local view = {}
|
||||
|
||||
if (CLIENT) then
|
||||
PLUGIN.ordered = PLUGIN.ordered or {}
|
||||
|
||||
function PLUGIN:CalcView(client, origin, angles, fov)
|
||||
local scenes = self.scenes
|
||||
|
||||
if (IsValid(ix.gui.characterMenu) and !IsValid(ix.gui.menu) and !ix.gui.characterMenu:IsClosing() and
|
||||
!table.IsEmpty(scenes)) then
|
||||
local key = self.index
|
||||
local value = scenes[self.index]
|
||||
|
||||
if (!self.index or !value) then
|
||||
value, key = table.Random(scenes)
|
||||
self.index = key
|
||||
end
|
||||
|
||||
if (self.orderedIndex or value.origin or isvector(key)) then
|
||||
local curTime = CurTime()
|
||||
|
||||
self.orderedIndex = self.orderedIndex or 1
|
||||
|
||||
local ordered = self.ordered[self.orderedIndex]
|
||||
|
||||
if (ordered) then
|
||||
key = ordered[1]
|
||||
value = ordered[2]
|
||||
end
|
||||
|
||||
if (!self.startTime) then
|
||||
self.startTime = curTime
|
||||
self.finishTime = curTime + 30
|
||||
end
|
||||
|
||||
local fraction = math.min(math.TimeFraction(self.startTime, self.finishTime, CurTime()), 1)
|
||||
|
||||
if (value) then
|
||||
realOrigin = LerpVector(fraction, key, value[1])
|
||||
realAngles = LerpAngle(fraction, value[2], value[3])
|
||||
end
|
||||
|
||||
if (fraction >= 1) then
|
||||
self.startTime = curTime
|
||||
self.finishTime = curTime + 30
|
||||
|
||||
if (ordered) then
|
||||
self.orderedIndex = self.orderedIndex + 1
|
||||
|
||||
if (self.orderedIndex > #self.ordered) then
|
||||
self.orderedIndex = 1
|
||||
end
|
||||
else
|
||||
local keys = {}
|
||||
|
||||
for k, _ in pairs(scenes) do
|
||||
if (isvector(k)) then
|
||||
keys[#keys + 1] = k
|
||||
end
|
||||
end
|
||||
|
||||
self.index = table.Random(keys)
|
||||
end
|
||||
end
|
||||
elseif (value) then
|
||||
realOrigin = value[1]
|
||||
realAngles = value[2]
|
||||
end
|
||||
|
||||
local x, y = gui.MousePos()
|
||||
local x2, y2 = surface.ScreenWidth() * 0.5, surface.ScreenHeight() * 0.5
|
||||
local frameTime = FrameTime() * 0.5
|
||||
|
||||
y3 = Lerp(frameTime, y3, math.Clamp((y - y2) / y2, -1, 1) * -6)
|
||||
x3 = Lerp(frameTime, x3, math.Clamp((x - x2) / x2, -1, 1) * 6)
|
||||
|
||||
view.origin = realOrigin + realAngles:Up()*y3 + realAngles:Right()*x3
|
||||
view.angles = realAngles + Angle(y3 * -0.5, x3 * -0.5, 0)
|
||||
|
||||
return view
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PreDrawViewModel(viewModel, client, weapon)
|
||||
if (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing()) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixMapSceneAdd", function()
|
||||
local data = net.ReadTable()
|
||||
|
||||
PLUGIN.scenes[#PLUGIN.scenes + 1] = data
|
||||
end)
|
||||
|
||||
net.Receive("ixMapSceneRemove", function()
|
||||
local index = net.ReadUInt(16)
|
||||
|
||||
PLUGIN.scenes[index] = nil
|
||||
end)
|
||||
|
||||
net.Receive("ixMapSceneAddPair", function()
|
||||
local data = net.ReadTable()
|
||||
local origin = net.ReadVector()
|
||||
|
||||
PLUGIN.scenes[origin] = data
|
||||
|
||||
table.insert(PLUGIN.ordered, {origin, data})
|
||||
end)
|
||||
|
||||
net.Receive("ixMapSceneRemovePair", function()
|
||||
local key = net.ReadVector()
|
||||
|
||||
PLUGIN.scenes[key] = nil
|
||||
|
||||
for k, v in ipairs(PLUGIN.ordered) do
|
||||
if (v[1] == key) then
|
||||
table.remove(PLUGIN.ordered, k)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixMapSceneSync", function()
|
||||
local length = net.ReadUInt(32)
|
||||
local data = net.ReadData(length)
|
||||
local uncompressed = util.Decompress(data)
|
||||
|
||||
if (!uncompressed) then
|
||||
ErrorNoHalt("[Helix] Unable to decompress map scene data!\n")
|
||||
return
|
||||
end
|
||||
|
||||
-- Set the list of texts to the ones provided by the server.
|
||||
PLUGIN.scenes = util.JSONToTable(uncompressed)
|
||||
|
||||
for k, v in pairs(PLUGIN.scenes) do
|
||||
if (v.origin or isvector(k)) then
|
||||
table.insert(PLUGIN.ordered, {v.origin and v.origin or k, v})
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
util.AddNetworkString("ixMapSceneSync")
|
||||
util.AddNetworkString("ixMapSceneAdd")
|
||||
util.AddNetworkString("ixMapSceneRemove")
|
||||
|
||||
util.AddNetworkString("ixMapSceneAddPair")
|
||||
util.AddNetworkString("ixMapSceneRemovePair")
|
||||
|
||||
function PLUGIN:SaveScenes()
|
||||
self:SetData(self.scenes)
|
||||
end
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
self.scenes = self:GetData() or {}
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerInitialSpawn(client)
|
||||
local json = util.TableToJSON(self.scenes)
|
||||
local compressed = util.Compress(json)
|
||||
local length = compressed:len()
|
||||
|
||||
net.Start("ixMapSceneSync")
|
||||
net.WriteUInt(length, 32)
|
||||
net.WriteData(compressed, length)
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
function PLUGIN:AddScene(position, angles, position2, angles2)
|
||||
local data
|
||||
|
||||
if (position2) then
|
||||
data = {origin=position, position2, angles, angles2}
|
||||
self.scenes[#self.scenes + 1] = data
|
||||
|
||||
net.Start("ixMapSceneAddPair")
|
||||
net.WriteTable(data)
|
||||
net.WriteVector(position)
|
||||
net.Broadcast()
|
||||
else
|
||||
data = {position, angles}
|
||||
self.scenes[#self.scenes + 1] = data
|
||||
|
||||
net.Start("ixMapSceneAdd")
|
||||
net.WriteTable(data)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
self:SaveScenes()
|
||||
end
|
||||
end
|
||||
|
||||
ix.command.Add("MapSceneAdd", {
|
||||
description = "@cmdMapSceneAdd",
|
||||
privilege = "Manage Map Scenes",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.bool, ix.type.optional),
|
||||
OnRun = function(self, client, bIsPair)
|
||||
local position, angles = client:EyePos(), client:EyeAngles()
|
||||
|
||||
-- This scene is in a pair for moving scenes.
|
||||
if (tobool(bIsPair) and !client.ixScnPair) then
|
||||
client.ixScnPair = {position, angles}
|
||||
|
||||
return "@mapRepeat"
|
||||
else
|
||||
if (client.ixScnPair) then
|
||||
PLUGIN:AddScene(client.ixScnPair[1], client.ixScnPair[2], position, angles)
|
||||
client.ixScnPair = nil
|
||||
else
|
||||
PLUGIN:AddScene(position, angles)
|
||||
end
|
||||
|
||||
return "@mapAdd"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("MapSceneRemove", {
|
||||
description = "@cmdMapSceneRemove",
|
||||
privilege = "Manage Map Scenes",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.number, ix.type.optional),
|
||||
OnRun = function(self, client, radius)
|
||||
radius = radius or 280
|
||||
|
||||
local position = client:GetPos()
|
||||
local i = 0
|
||||
|
||||
for k, v in pairs(PLUGIN.scenes) do
|
||||
local delete = false
|
||||
|
||||
if (isvector(k)) then
|
||||
if (k:Distance(position) <= radius or v[1]:Distance(position) <= radius) then
|
||||
delete = true
|
||||
end
|
||||
elseif (v[1]:Distance(position) <= radius) then
|
||||
delete = true
|
||||
end
|
||||
|
||||
if (delete) then
|
||||
if (isvector(k)) then
|
||||
net.Start("ixMapSceneRemovePair")
|
||||
net.WriteVector(k)
|
||||
net.Broadcast()
|
||||
else
|
||||
net.Start("ixMapSceneRemove")
|
||||
net.WriteString(k)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
PLUGIN.scenes[k] = nil
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if (i > 0) then
|
||||
PLUGIN:SaveScenes()
|
||||
end
|
||||
|
||||
return "@mapDel", i
|
||||
end
|
||||
})
|
||||
357
garrysmod/gamemodes/helix/plugins/pac.lua
Normal file
357
garrysmod/gamemodes/helix/plugins/pac.lua
Normal file
@@ -0,0 +1,357 @@
|
||||
|
||||
-- luacheck: globals pac pace
|
||||
|
||||
-- This Library is just for PAC3 Integration.
|
||||
-- You must install PAC3 to make this library work.
|
||||
|
||||
PLUGIN.name = "PAC3 Integration"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "PAC3 integration for item parts."
|
||||
|
||||
if (!pace) then return end
|
||||
|
||||
ix.pac = ix.pac or {}
|
||||
ix.pac.list = ix.pac.list or {}
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage PAC",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
-- this stores pac3 part information to plugin's table'
|
||||
function ix.pac.RegisterPart(id, outfit)
|
||||
ix.pac.list[id] = outfit
|
||||
end
|
||||
|
||||
-- Fixing the PAC3's default stuffs to fit on Helix.
|
||||
if (CLIENT) then
|
||||
-- Disable the "in editor" HUD element.
|
||||
hook.Add("InitializedPlugins", "PAC3Fixer", function()
|
||||
hook.Remove("HUDPaint", "pac_in_editor")
|
||||
end)
|
||||
|
||||
-- Remove PAC3 LoadParts
|
||||
function pace.LoadParts(name, clear, override_part) end
|
||||
|
||||
-- Prohibits players from deleting their own PAC3 outfit.
|
||||
concommand.Add("pac_clear_parts", function()
|
||||
RunConsoleCommand("pac_restart")
|
||||
end)
|
||||
|
||||
-- you need the proper permission to open the editor
|
||||
function PLUGIN:PrePACEditorOpen()
|
||||
if (!CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage PAC", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:pac_CanWearParts(client)
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage PAC", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local meta = FindMetaTable("Player")
|
||||
|
||||
-- Get Player's PAC3 Parts.
|
||||
function meta:GetParts()
|
||||
if (!pac) then return end
|
||||
|
||||
return self:GetNetVar("parts", {})
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixPartWear")
|
||||
util.AddNetworkString("ixPartRemove")
|
||||
util.AddNetworkString("ixPartReset")
|
||||
|
||||
function meta:AddPart(uniqueID, item)
|
||||
if (!pac) then return end
|
||||
|
||||
local curParts = self:GetParts()
|
||||
|
||||
-- wear the parts.
|
||||
net.Start("ixPartWear")
|
||||
net.WriteEntity(self)
|
||||
net.WriteString(uniqueID)
|
||||
net.Broadcast()
|
||||
|
||||
curParts[uniqueID] = true
|
||||
|
||||
self:SetNetVar("parts", curParts)
|
||||
end
|
||||
|
||||
function meta:RemovePart(uniqueID)
|
||||
if (!pac) then return end
|
||||
|
||||
local curParts = self:GetParts()
|
||||
|
||||
-- remove the parts.
|
||||
net.Start("ixPartRemove")
|
||||
net.WriteEntity(self)
|
||||
net.WriteString(uniqueID)
|
||||
net.Broadcast()
|
||||
|
||||
curParts[uniqueID] = nil
|
||||
|
||||
self:SetNetVar("parts", curParts)
|
||||
end
|
||||
|
||||
function meta:ResetParts()
|
||||
if (!pac) then return end
|
||||
|
||||
net.Start("ixPartReset")
|
||||
net.WriteEntity(self)
|
||||
net.WriteTable(self:GetParts())
|
||||
net.Broadcast()
|
||||
|
||||
self:SetNetVar("parts", {})
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, curChar, prevChar)
|
||||
-- Reset the characters parts.
|
||||
local curParts = client:GetParts()
|
||||
|
||||
if (curParts) then
|
||||
client:ResetParts()
|
||||
end
|
||||
|
||||
-- After resetting all PAC3 outfits, wear all equipped PAC3 outfits.
|
||||
if (curChar) then
|
||||
local inv = curChar:GetInventory()
|
||||
|
||||
for k, _ in inv:Iter() do
|
||||
if (k:GetData("equip") == true and k.pacData) then
|
||||
client:AddPart(k.uniqueID, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSwitchWeapon(client, oldWeapon, newWeapon)
|
||||
local oldItem = IsValid(oldWeapon) and oldWeapon.ixItem
|
||||
local newItem = IsValid(newWeapon) and newWeapon.ixItem
|
||||
|
||||
if (oldItem and oldItem.isWeapon and oldItem:GetData("equip") and oldItem.pacData) then
|
||||
oldItem:WearPAC(client)
|
||||
end
|
||||
|
||||
if (newItem and newItem.isWeapon and newItem.pacData) then
|
||||
newItem:RemovePAC(client)
|
||||
end
|
||||
end
|
||||
|
||||
-- Hides PAC parts when a player enters observer.
|
||||
function PLUGIN:OnPlayerObserve(client, state)
|
||||
local curParts = client:GetParts()
|
||||
|
||||
-- Remove all the parts
|
||||
if (curParts) then
|
||||
client:ResetParts()
|
||||
end
|
||||
|
||||
-- If exiting of observer, re-add all parts.
|
||||
if (!state) then
|
||||
local character = client:GetCharacter()
|
||||
local inventory = character:GetInventory()
|
||||
|
||||
for k, _ in inventory:Iter() do
|
||||
if (k:GetData("equip") == true and k.pacData) then
|
||||
client:AddPart(k.uniqueID, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local function AttachPart(client, uniqueID)
|
||||
local itemTable = ix.item.list[uniqueID]
|
||||
local pacData = ix.pac.list[uniqueID]
|
||||
|
||||
if (pacData) then
|
||||
if (itemTable and itemTable.pacAdjust) then
|
||||
pacData = table.Copy(pacData)
|
||||
pacData = itemTable:pacAdjust(pacData, client)
|
||||
end
|
||||
|
||||
if (isfunction(client.AttachPACPart)) then
|
||||
client:AttachPACPart(pacData)
|
||||
else
|
||||
pac.SetupENT(client)
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
if (IsValid(client) and isfunction(client.AttachPACPart)) then
|
||||
client:AttachPACPart(pacData)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RemovePart(client, uniqueID)
|
||||
local itemTable = ix.item.list[uniqueID]
|
||||
local pacData = ix.pac.list[uniqueID]
|
||||
|
||||
if (pacData) then
|
||||
if (itemTable and itemTable.pacAdjust) then
|
||||
pacData = table.Copy(pacData)
|
||||
pacData = itemTable:pacAdjust(pacData, client)
|
||||
end
|
||||
|
||||
if (isfunction(client.RemovePACPart)) then
|
||||
client:RemovePACPart(pacData)
|
||||
else
|
||||
pac.SetupENT(client)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("Think", "ix_pacupdate", function()
|
||||
if (!pac) then
|
||||
hook.Remove("Think", "ix_pacupdate")
|
||||
return
|
||||
end
|
||||
|
||||
if (IsValid(pac.LocalPlayer)) then
|
||||
for _, v in player.Iterator() do
|
||||
local character = v:GetCharacter()
|
||||
|
||||
if (character) then
|
||||
local parts = v:GetParts()
|
||||
|
||||
for k2, _ in pairs(parts) do
|
||||
AttachPart(v, k2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Remove("Think", "ix_pacupdate")
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixPartWear", function(length)
|
||||
if (!pac) then return end
|
||||
|
||||
local wearer = net.ReadEntity()
|
||||
local uid = net.ReadString()
|
||||
|
||||
if (!wearer.pac_owner) then
|
||||
pac.SetupENT(wearer)
|
||||
end
|
||||
|
||||
AttachPart(wearer, uid)
|
||||
end)
|
||||
|
||||
net.Receive("ixPartRemove", function(length)
|
||||
if (!pac) then return end
|
||||
|
||||
local wearer = net.ReadEntity()
|
||||
local uid = net.ReadString()
|
||||
|
||||
if (!wearer.pac_owner) then
|
||||
pac.SetupENT(wearer)
|
||||
end
|
||||
|
||||
RemovePart(wearer, uid)
|
||||
end)
|
||||
|
||||
net.Receive("ixPartReset", function(length)
|
||||
if (!pac) then return end
|
||||
|
||||
local wearer = net.ReadEntity()
|
||||
local uidList = net.ReadTable()
|
||||
|
||||
if (!wearer.pac_owner) then
|
||||
pac.SetupENT(wearer)
|
||||
end
|
||||
|
||||
for k, _ in pairs(uidList) do
|
||||
RemovePart(wearer, k)
|
||||
end
|
||||
end)
|
||||
|
||||
function PLUGIN:DrawPlayerRagdoll(entity)
|
||||
local ply = entity.objCache
|
||||
|
||||
if (IsValid(ply)) then
|
||||
if (!entity.overridePAC3) then
|
||||
if ply.pac_parts then
|
||||
for _, part in pairs(ply.pac_parts) do
|
||||
if part.last_owner and part.last_owner:IsValid() then
|
||||
hook.Run("OnPAC3PartTransferred", part)
|
||||
part:SetOwner(entity)
|
||||
part.last_owner = entity
|
||||
end
|
||||
end
|
||||
end
|
||||
ply.pac_playerspawn = pac.RealTime -- used for events
|
||||
|
||||
entity.overridePAC3 = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnEntityCreated(entity)
|
||||
local class = entity:GetClass()
|
||||
|
||||
-- For safe progress, I skip one frame.
|
||||
timer.Simple(0.01, function()
|
||||
if (class == "prop_ragdoll") then
|
||||
if (entity:GetNetVar("player")) then
|
||||
entity.RenderOverride = function()
|
||||
entity.objCache = entity:GetNetVar("player")
|
||||
entity:DrawModel()
|
||||
|
||||
hook.Run("DrawPlayerRagdoll", entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (class:find("HL2MPRagdoll")) then
|
||||
for _, v in player.Iterator() do
|
||||
if (v:GetRagdollEntity() == entity) then
|
||||
entity.objCache = v
|
||||
end
|
||||
end
|
||||
|
||||
entity.RenderOverride = function()
|
||||
entity:DrawModel()
|
||||
|
||||
hook.Run("DrawPlayerRagdoll", entity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:DrawCharacterOverview()
|
||||
if (!pac) then
|
||||
return
|
||||
end
|
||||
|
||||
if (LocalPlayer().pac_outfits) then
|
||||
pac.RenderOverride(LocalPlayer(), "opaque")
|
||||
pac.RenderOverride(LocalPlayer(), "translucent", true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:DrawHelixModelView(panel, ent)
|
||||
if (!pac) then
|
||||
return
|
||||
end
|
||||
|
||||
if (LocalPlayer():GetCharacter()) then
|
||||
pac.RenderOverride(ent, "opaque")
|
||||
pac.RenderOverride(ent, "translucent", true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InitializedPlugins()
|
||||
local items = ix.item.list
|
||||
|
||||
for _, v in pairs(items) do
|
||||
if (v.pacData) then
|
||||
ix.pac.list[v.uniqueID] = v.pacData
|
||||
end
|
||||
end
|
||||
end
|
||||
37
garrysmod/gamemodes/helix/plugins/permakill.lua
Normal file
37
garrysmod/gamemodes/helix/plugins/permakill.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
PLUGIN.name = "Permakill"
|
||||
PLUGIN.author = "Thadah Denyse"
|
||||
PLUGIN.description = "Adds permanent death in the server options."
|
||||
|
||||
ix.config.Add("permakill", false, "Whether or not permakill is activated on the server.", nil, {
|
||||
category = "Permakill"
|
||||
})
|
||||
|
||||
ix.config.Add("permakillWorld", false, "Whether or not world and self damage produce permanent death.", nil, {
|
||||
category = "Permakill"
|
||||
})
|
||||
|
||||
function PLUGIN:PlayerDeath(client, inflictor, attacker)
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (ix.config.Get("permakill") and character) then
|
||||
if (hook.Run("ShouldPermakillCharacter", client, character, inflictor, attacker) == false) then
|
||||
return
|
||||
end
|
||||
|
||||
if (ix.config.Get("permakillWorld") and (client == attacker or inflictor:IsWorld())) then
|
||||
return
|
||||
end
|
||||
|
||||
character:SetData("permakilled", true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSpawn(client)
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (ix.config.Get("permakill") and character and character:GetData("permakilled")) then
|
||||
character:Ban()
|
||||
character:SetData("permakilled")
|
||||
end
|
||||
end
|
||||
195
garrysmod/gamemodes/helix/plugins/persistence.lua
Normal file
195
garrysmod/gamemodes/helix/plugins/persistence.lua
Normal file
@@ -0,0 +1,195 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Persistence"
|
||||
PLUGIN.description = "Define entities to persist through restarts."
|
||||
PLUGIN.author = "alexgrist"
|
||||
PLUGIN.stored = PLUGIN.stored or {}
|
||||
|
||||
local function GetRealModel(entity)
|
||||
return entity:GetClass() == "prop_effect" and entity.AttachedEntity:GetModel() or entity:GetModel()
|
||||
end
|
||||
|
||||
properties.Add("persist", {
|
||||
MenuLabel = "#makepersistent",
|
||||
Order = 400,
|
||||
MenuIcon = "icon16/link.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (entity:IsPlayer() or entity:IsVehicle() or entity.bNoPersist) then return false end
|
||||
if (!gamemode.Call("CanProperty", client, "persist", entity)) then return false end
|
||||
|
||||
return !entity:GetNetVar("Persistent", false)
|
||||
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
|
||||
|
||||
PLUGIN.stored[#PLUGIN.stored + 1] = entity
|
||||
|
||||
entity:SetNetVar("Persistent", true)
|
||||
|
||||
ix.log.Add(client, "persist", GetRealModel(entity), true)
|
||||
end
|
||||
})
|
||||
|
||||
properties.Add("persist_end", {
|
||||
MenuLabel = "#stoppersisting",
|
||||
Order = 400,
|
||||
MenuIcon = "icon16/link_break.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (entity:IsPlayer()) then return false end
|
||||
if (!gamemode.Call("CanProperty", client, "persist", entity)) then return false end
|
||||
|
||||
return entity:GetNetVar("Persistent", false)
|
||||
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
|
||||
|
||||
for k, v in ipairs(PLUGIN.stored) do
|
||||
if (v == entity) then
|
||||
table.remove(PLUGIN.stored, k)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
entity:SetNetVar("Persistent", false)
|
||||
|
||||
ix.log.Add(client, "persist", GetRealModel(entity), false)
|
||||
end
|
||||
})
|
||||
|
||||
function PLUGIN:PhysgunPickup(client, entity)
|
||||
if (entity:GetNetVar("Persistent", false)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
function PLUGIN:LoadData()
|
||||
local entities = self:GetData() or {}
|
||||
|
||||
for _, v in ipairs(entities) do
|
||||
local entity = ents.Create(v.Class)
|
||||
|
||||
if (IsValid(entity)) then
|
||||
entity:SetPos(v.Pos)
|
||||
entity:SetAngles(v.Angle)
|
||||
entity:SetModel(v.Model)
|
||||
entity:SetSkin(v.Skin)
|
||||
entity:SetColor(v.Color)
|
||||
entity:SetMaterial(v.Material)
|
||||
entity:Spawn()
|
||||
entity:Activate()
|
||||
|
||||
if (v.bNoCollision) then
|
||||
entity:SetCollisionGroup(COLLISION_GROUP_WORLD)
|
||||
end
|
||||
|
||||
if (istable(v.BodyGroups)) then
|
||||
for k2, v2 in pairs(v.BodyGroups) do
|
||||
entity:SetBodygroup(k2, v2)
|
||||
end
|
||||
end
|
||||
|
||||
if (istable(v.SubMaterial)) then
|
||||
for k2, v2 in pairs(v.SubMaterial) do
|
||||
if (!isnumber(k2) or !isstring(v2)) then
|
||||
continue
|
||||
end
|
||||
|
||||
entity:SetSubMaterial(k2 - 1, v2)
|
||||
end
|
||||
end
|
||||
|
||||
local physicsObject = entity:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physicsObject)) then
|
||||
physicsObject:EnableMotion(v.Movable)
|
||||
end
|
||||
|
||||
self.stored[#self.stored + 1] = entity
|
||||
|
||||
entity:SetNetVar("Persistent", true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
local entities = {}
|
||||
|
||||
for _, v in ipairs(self.stored) do
|
||||
if (IsValid(v)) then
|
||||
local data = {}
|
||||
data.Class = v.ClassOverride or v:GetClass()
|
||||
data.Pos = v:GetPos()
|
||||
data.Angle = v:GetAngles()
|
||||
data.Model = GetRealModel(v)
|
||||
data.Skin = v:GetSkin()
|
||||
data.Color = v:GetColor()
|
||||
data.Material = v:GetMaterial()
|
||||
data.bNoCollision = v:GetCollisionGroup() == COLLISION_GROUP_WORLD
|
||||
|
||||
local materials = v:GetMaterials()
|
||||
|
||||
if (istable(materials)) then
|
||||
data.SubMaterial = {}
|
||||
|
||||
for k2, _ in pairs(materials) do
|
||||
if (v:GetSubMaterial(k2 - 1) != "") then
|
||||
data.SubMaterial[k2] = v:GetSubMaterial(k2 - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local bodyGroups = v:GetBodyGroups()
|
||||
|
||||
if (istable(bodyGroups)) then
|
||||
data.BodyGroups = {}
|
||||
|
||||
for _, v2 in pairs(bodyGroups) do
|
||||
if (v:GetBodygroup(v2.id) > 0) then
|
||||
data.BodyGroups[v2.id] = v:GetBodygroup(v2.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local physicsObject = v:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physicsObject)) then
|
||||
data.Movable = physicsObject:IsMoveable()
|
||||
end
|
||||
|
||||
entities[#entities + 1] = data
|
||||
end
|
||||
end
|
||||
|
||||
self:SetData(entities)
|
||||
end
|
||||
|
||||
ix.log.AddType("persist", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s has %s persistence for '%s'.", client:Name(), arg[2] and "enabled" or "disabled", arg[1])
|
||||
end)
|
||||
end
|
||||
157
garrysmod/gamemodes/helix/plugins/propprotect.lua
Normal file
157
garrysmod/gamemodes/helix/plugins/propprotect.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
PLUGIN.name = "Basic Prop Protection"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds a simple prop protection system."
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Bypass Prop Protection",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
local PROP_BLACKLIST = {
|
||||
["models/props_combine/combinetrain02b.mdl"] = true,
|
||||
["models/props_combine/combinetrain02a.mdl"] = true,
|
||||
["models/props_combine/combinetrain01.mdl"] = true,
|
||||
["models/cranes/crane_frame.mdl"] = true,
|
||||
["models/props_junk/trashdumpster02.mdl"] = true,
|
||||
["models/props_c17/oildrum001_explosive.mdl"] = true,
|
||||
["models/props_canal/canal_bridge02.mdl"] = true,
|
||||
["models/props_canal/canal_bridge01.mdl"] = true,
|
||||
["models/props_canal/canal_bridge03a.mdl"] = true,
|
||||
["models/props_canal/canal_bridge03b.mdl"] = true,
|
||||
["models/props_wasteland/cargo_container01.mdl"] = true,
|
||||
["models/props_wasteland/cargo_container01c.mdl"] = true,
|
||||
["models/props_wasteland/cargo_container01b.mdl"] = true,
|
||||
["models/props_combine/combine_mine01.mdl"] = true,
|
||||
["models/props_junk/glassjug01.mdl"] = true,
|
||||
["models/props_c17/paper01.mdl"] = true,
|
||||
["models/props_junk/garbage_takeoutcarton001a.mdl"] = true,
|
||||
["models/props_c17/trappropeller_engine.mdl"] = true,
|
||||
["models/props/cs_office/microwave.mdl"] = true,
|
||||
["models/items/item_item_crate.mdl"] = true,
|
||||
["models/props_junk/gascan001a.mdl"] = true,
|
||||
["models/props_c17/consolebox01a.mdl"] = true,
|
||||
["models/props_buildings/building_002a.mdl"] = true,
|
||||
["models/props_phx/mk-82.mdl"] = true,
|
||||
["models/props_phx/cannonball.mdl"] = true,
|
||||
["models/props_phx/ball.mdl"] = true,
|
||||
["models/props_phx/amraam.mdl"] = true,
|
||||
["models/props_phx/misc/flakshell_big.mdl"] = true,
|
||||
["models/props_phx/ww2bomb.mdl"] = true,
|
||||
["models/props_phx/torpedo.mdl"] = true,
|
||||
["models/props/de_train/biohazardtank.mdl"] = true,
|
||||
["models/props_buildings/project_building01.mdl"] = true,
|
||||
["models/props_combine/prison01c.mdl"] = true,
|
||||
["models/props/cs_militia/silo_01.mdl"] = true,
|
||||
["models/props_phx/huge/evildisc_corp.mdl"] = true,
|
||||
["models/props_phx/misc/potato_launcher_explosive.mdl"] = true,
|
||||
["models/props_combine/combine_citadel001.mdl"] = true,
|
||||
["models/props_phx/oildrum001_explosive.mdl"] = true,
|
||||
["models/props_junk/wood_crate01_explosive.mdl"] = true,
|
||||
["models/props_junk/propane_tank001a.mdl"] = true,
|
||||
["models/props_explosive/explosive_butane_can.mdl"] = true,
|
||||
["models/props_explosive/explosive_butane_can02.mdl"] = true
|
||||
}
|
||||
|
||||
if (SERVER) then
|
||||
ix.log.AddType("spawnProp", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s has spawned '%s'.", client:Name(), arg[1])
|
||||
end)
|
||||
|
||||
ix.log.AddType("spawnEntity", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s has spawned a '%s'.", client:Name(), arg[1])
|
||||
end)
|
||||
|
||||
function PLUGIN:PlayerSpawnObject(client, model, entity)
|
||||
if ((client.ixNextSpawn or 0) < CurTime()) then
|
||||
client.ixNextSpawn = CurTime() + 0.75
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
if (!client:IsAdmin() and PROP_BLACKLIST[model:lower()]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PhysgunPickup(client, entity)
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
|
||||
if (entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnPhysgunReload(weapon, client)
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
local trace = client:GetEyeTrace()
|
||||
|
||||
if (IsValid(trace.Entity) and trace.Entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanProperty(client, property, entity)
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
|
||||
if (entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanTool(client, trace, tool)
|
||||
local entity = trace.Entity
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
|
||||
if (IsValid(entity) and entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerSpawnedProp(client, model, entity)
|
||||
ix.log.Add(client, "spawnProp", model)
|
||||
end
|
||||
|
||||
PLUGIN.PlayerSpawnedEffect = PLUGIN.PlayerSpawnedProp
|
||||
PLUGIN.PlayerSpawnedRagdoll = PLUGIN.PlayerSpawnedProp
|
||||
|
||||
function PLUGIN:PlayerSpawnedNPC(client, entity)
|
||||
ix.log.Add(client, "spawnEntity", entity)
|
||||
end
|
||||
|
||||
PLUGIN.PlayerSpawnedSWEP = PLUGIN.PlayerSpawnedNPC
|
||||
PLUGIN.PlayerSpawnedSENT = PLUGIN.PlayerSpawnedNPC
|
||||
PLUGIN.PlayerSpawnedVehicle = PLUGIN.PlayerSpawnedNPC
|
||||
else
|
||||
function PLUGIN:PhysgunPickup(client, entity)
|
||||
if (entity:GetNetVar("owner", 0) != client:GetCharacter():GetID()
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanProperty(client, property, entity)
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
|
||||
if (entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanTool(client, trace, tool)
|
||||
local entity = trace.Entity
|
||||
local characterID = client:GetCharacter():GetID()
|
||||
|
||||
if (IsValid(entity) and entity:GetNetVar("owner", 0) != characterID
|
||||
and !CAMI.PlayerHasAccess(client, "Helix - Bypass Prop Protection", nil)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
121
garrysmod/gamemodes/helix/plugins/saveitems.lua
Normal file
121
garrysmod/gamemodes/helix/plugins/saveitems.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
PLUGIN.name = "Save Items"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Saves items that were dropped."
|
||||
|
||||
--[[
|
||||
function PLUGIN:OnSavedItemLoaded(items)
|
||||
for k, v in ipairs(items) do
|
||||
-- do something
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDeleteSavedItems()
|
||||
return true
|
||||
end
|
||||
]]--
|
||||
|
||||
-- as title says.
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
local items = self:GetData()
|
||||
|
||||
if (items) then
|
||||
local idRange = {}
|
||||
local info = {}
|
||||
|
||||
for _, v in ipairs(items) do
|
||||
idRange[#idRange + 1] = v[1]
|
||||
info[v[1]] = {v[2], v[3], v[4]}
|
||||
end
|
||||
|
||||
if (#idRange > 0) then
|
||||
if (hook.Run("ShouldDeleteSavedItems") == true) then
|
||||
-- don't spawn saved item and just delete them.
|
||||
local query = mysql:Delete("ix_items")
|
||||
query:WhereIn("item_id", idRange)
|
||||
query:Execute()
|
||||
|
||||
print("Server Deleted Server Items (does not includes Logical Items)")
|
||||
else
|
||||
local query = mysql:Select("ix_items")
|
||||
query:Select("item_id")
|
||||
query:Select("unique_id")
|
||||
query:Select("data")
|
||||
query:WhereIn("item_id", idRange)
|
||||
query:Callback(function(result)
|
||||
if (istable(result)) then
|
||||
local loadedItems = {}
|
||||
local bagInventories = {}
|
||||
|
||||
for _, v in ipairs(result) do
|
||||
local itemID = tonumber(v.item_id)
|
||||
local data = util.JSONToTable(v.data or "[]")
|
||||
local uniqueID = v.unique_id
|
||||
local itemTable = ix.item.list[uniqueID]
|
||||
|
||||
if (itemTable and itemID) then
|
||||
local item = ix.item.New(uniqueID, itemID)
|
||||
item.data = data or {}
|
||||
|
||||
local itemInfo = info[itemID]
|
||||
local position, angles, bMovable = itemInfo[1], itemInfo[2], true
|
||||
|
||||
if (isbool(itemInfo[3])) then
|
||||
bMovable = itemInfo[3]
|
||||
end
|
||||
|
||||
local itemEntity = item:Spawn(position, angles)
|
||||
itemEntity.ixItemID = itemID
|
||||
|
||||
local physicsObject = itemEntity:GetPhysicsObject()
|
||||
|
||||
if (IsValid(physicsObject)) then
|
||||
physicsObject:EnableMotion(bMovable)
|
||||
end
|
||||
|
||||
item.invID = 0
|
||||
loadedItems[#loadedItems + 1] = item
|
||||
|
||||
if (item.isBag) then
|
||||
local invType = ix.item.inventoryTypes[uniqueID]
|
||||
bagInventories[item:GetData("id")] = {invType.w, invType.h}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- we need to manually restore bag inventories in the world since they don't have a current owner
|
||||
-- that it can automatically restore along with the character when it's loaded
|
||||
if (!table.IsEmpty(bagInventories)) then
|
||||
ix.inventory.Restore(bagInventories)
|
||||
end
|
||||
|
||||
hook.Run("OnSavedItemLoaded", loadedItems) -- when you have something in the dropped item.
|
||||
end
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
local items = {}
|
||||
|
||||
for _, v in ipairs(ents.FindByClass("ix_item")) do
|
||||
if (v.ixItemID and !v.bTemporary) then
|
||||
local physicsObject = v:GetPhysicsObject()
|
||||
local bMovable = nil
|
||||
|
||||
if (IsValid(physicsObject)) then
|
||||
bMovable = physicsObject:IsMoveable()
|
||||
end
|
||||
|
||||
items[#items + 1] = {
|
||||
v.ixItemID, v:GetPos(), v:GetAngles(), bMovable
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
self:SetData(items)
|
||||
end
|
||||
205
garrysmod/gamemodes/helix/plugins/spawns.lua
Normal file
205
garrysmod/gamemodes/helix/plugins/spawns.lua
Normal file
@@ -0,0 +1,205 @@
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Spawns"
|
||||
PLUGIN.description = "Spawn points for factions and classes."
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.spawns = PLUGIN.spawns or {}
|
||||
|
||||
local function IsSpawnPointClear(pos)
|
||||
local mins = Vector(-16, -16, 0)
|
||||
local maxs = Vector(16, 16, 72)
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:Alive() and ply:GetPos():Distance(pos) < 50 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local ents = ents.FindInBox(pos + mins, pos + maxs)
|
||||
for _, ent in ipairs(ents) do
|
||||
if IsValid(ent) and not ent:IsPlayer() and ent:GetSolid() ~= SOLID_NONE then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadout(client)
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (self.spawns and !table.IsEmpty(self.spawns) and character) then
|
||||
local class = character:GetClass()
|
||||
local points
|
||||
local className = "default"
|
||||
|
||||
for k, v in ipairs(ix.faction.indices) do
|
||||
if (k == client:Team()) then
|
||||
points = self.spawns[v.uniqueID] or {}
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if (points) then
|
||||
local factionTable = ix.faction.indices[client:Team()]
|
||||
local podr = character:GetPodr()
|
||||
|
||||
if (podr and factionTable and factionTable.Podr and factionTable.Podr[podr]) then
|
||||
className = factionTable.Podr[podr].name:lower()
|
||||
else
|
||||
for _, v in ipairs(ix.class.list) do
|
||||
if (class == v.index) then
|
||||
className = v.uniqueID
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
points = points[className] or points["default"]
|
||||
|
||||
if (points and !table.IsEmpty(points)) then
|
||||
local tries = 0
|
||||
local position
|
||||
|
||||
repeat
|
||||
position = table.Random(points)
|
||||
tries = tries + 1
|
||||
until IsSpawnPointClear(position) or tries > 10
|
||||
|
||||
client:SetPos(position)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
self.spawns = self:GetData() or {}
|
||||
end
|
||||
|
||||
function PLUGIN:SaveSpawns()
|
||||
self:SetData(self.spawns)
|
||||
end
|
||||
|
||||
ix.command.Add("SpawnAdd", {
|
||||
description = "@cmdSpawnAdd",
|
||||
privilege = "Manage Spawn Points",
|
||||
adminOnly = true,
|
||||
arguments = {
|
||||
ix.type.string,
|
||||
bit.bor(ix.type.text, ix.type.optional)
|
||||
},
|
||||
OnRun = function(self, client, name, class)
|
||||
local full = (name .. " " .. (class or "")):Trim()
|
||||
local words = string.Explode(" ", full)
|
||||
|
||||
local info, info2, faction, className
|
||||
|
||||
-- Перебираем возможные разбиения строки на фракцию и класс
|
||||
for i = 1, #words do
|
||||
local fName = table.concat(words, " ", 1, i)
|
||||
local cName = table.concat(words, " ", i + 1)
|
||||
|
||||
for _, v in ipairs(ix.faction.indices) do
|
||||
-- Проверяем совпадение фракции
|
||||
if (ix.util.StringMatches(v.uniqueID, fName) or ix.util.StringMatches(L(v.name, client), fName)) then
|
||||
local potentialInfo = v
|
||||
local potentialFaction = v.uniqueID
|
||||
local potentialClassName, potentialInfo2
|
||||
|
||||
if (cName == "") then
|
||||
potentialClassName = "default"
|
||||
else
|
||||
-- Ищем класс
|
||||
for _, c in ipairs(ix.class.list) do
|
||||
if (c.faction == v.index and (c.uniqueID:lower() == cName:lower() or ix.util.StringMatches(L(c.name, client), cName))) then
|
||||
potentialInfo2 = c
|
||||
potentialClassName = c.uniqueID
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Ищем подразделение (Podr)
|
||||
if (!potentialClassName and v.Podr) then
|
||||
for _, p in pairs(v.Podr) do
|
||||
local pName = p.name:lower()
|
||||
local qName = cName:lower()
|
||||
|
||||
-- Сравнение с запасом на окончания (по первым 4-5 символам) или по вхождению
|
||||
if (ix.util.StringMatches(pName, qName) or ix.util.StringMatches(qName, pName) or
|
||||
pName:find(qName, 1, true) or qName:find(pName, 1, true) or
|
||||
(pName:sub(1, 8) == qName:sub(1, 8))) then -- 8 байт = ~4 русских символа
|
||||
potentialInfo2 = p
|
||||
potentialClassName = p.name:lower()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (potentialClassName) then
|
||||
info = potentialInfo
|
||||
faction = potentialFaction
|
||||
className = potentialClassName
|
||||
info2 = potentialInfo2
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (className) then break end
|
||||
end
|
||||
|
||||
if (info) then
|
||||
if (!className) then
|
||||
return "@invalidClass"
|
||||
end
|
||||
|
||||
PLUGIN.spawns[faction] = PLUGIN.spawns[faction] or {}
|
||||
PLUGIN.spawns[faction][className] = PLUGIN.spawns[faction][className] or {}
|
||||
|
||||
table.insert(PLUGIN.spawns[faction][className], client:GetPos())
|
||||
PLUGIN:SaveSpawns()
|
||||
|
||||
local logName = L(info.name, client)
|
||||
if (info2) then
|
||||
logName = logName .. " (" .. L(info2.name, client) .. ")"
|
||||
end
|
||||
|
||||
return "@spawnAdded", logName
|
||||
else
|
||||
return "@invalidFaction"
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("SpawnRemove", {
|
||||
description = "@cmdSpawnRemove",
|
||||
privilege = "Manage Spawn Points",
|
||||
adminOnly = true,
|
||||
arguments = bit.bor(ix.type.number, ix.type.optional),
|
||||
OnRun = function(self, client, radius)
|
||||
radius = radius or 120
|
||||
|
||||
local position = client:GetPos()
|
||||
local i = 0
|
||||
|
||||
for _, v in pairs(PLUGIN.spawns) do
|
||||
for _, v2 in pairs(v) do
|
||||
for k3, v3 in pairs(v2) do
|
||||
if (v3:Distance(position) <= radius) then
|
||||
v2[k3] = nil
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (i > 0) then
|
||||
PLUGIN:SaveSpawns()
|
||||
end
|
||||
|
||||
return "@spawnDeleted", i
|
||||
end
|
||||
})
|
||||
@@ -0,0 +1,2 @@
|
||||
ATTRIBUTE.name = "Endurance"
|
||||
ATTRIBUTE.description = "Affects how long you can run for."
|
||||
@@ -0,0 +1,6 @@
|
||||
ATTRIBUTE.name = "Stamina"
|
||||
ATTRIBUTE.description = "Affects how fast you can run."
|
||||
|
||||
function ATTRIBUTE:OnSetup(client, value)
|
||||
client:SetRunSpeed(ix.config.Get("runSpeed") + value)
|
||||
end
|
||||
184
garrysmod/gamemodes/helix/plugins/stamina/sh_plugin.lua
Normal file
184
garrysmod/gamemodes/helix/plugins/stamina/sh_plugin.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
PLUGIN.name = "Stamina"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds a stamina system to limit running."
|
||||
|
||||
-- luacheck: push ignore 631
|
||||
ix.config.Add("staminaDrain", 1, "How much stamina to drain per tick (every quarter second). This is calculated before attribute reduction.", nil, {
|
||||
data = {min = 0, max = 10, decimals = 2},
|
||||
category = "characters"
|
||||
})
|
||||
|
||||
ix.config.Add("staminaRegeneration", 1.75, "How much stamina to regain per tick (every quarter second).", nil, {
|
||||
data = {min = 0, max = 10, decimals = 2},
|
||||
category = "characters"
|
||||
})
|
||||
|
||||
ix.config.Add("staminaCrouchRegeneration", 2, "How much stamina to regain per tick (every quarter second) while crouching.", nil, {
|
||||
data = {min = 0, max = 10, decimals = 2},
|
||||
category = "characters"
|
||||
})
|
||||
|
||||
ix.config.Add("punchStamina", 10, "How much stamina punches use up.", nil, {
|
||||
data = {min = 0, max = 100},
|
||||
category = "characters"
|
||||
})
|
||||
-- luacheck: pop
|
||||
local function CalcStaminaChange(client)
|
||||
local character = client:GetCharacter()
|
||||
|
||||
if (!character) then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Если в технике или в ноуклипе - сразу реген и выходим
|
||||
local inVehicle = client:InVehicle() or IsValid(client:GetVehicle())
|
||||
if (not inVehicle and client.lvsGetVehicle) then
|
||||
local v = client:lvsGetVehicle()
|
||||
inVehicle = IsValid(v)
|
||||
end
|
||||
if (not inVehicle and IsValid(client:GetParent()) and client:GetParent():IsVehicle()) then inVehicle = true end
|
||||
|
||||
local isNoClip = client:GetMoveType() == MOVETYPE_NOCLIP
|
||||
|
||||
if (inVehicle or isNoClip) then
|
||||
local offset = ix.config.Get("staminaRegeneration", 1.75)
|
||||
if (SERVER) then
|
||||
local current = client:GetLocalVar("stm", 0)
|
||||
client:SetLocalVar("stm", math.Clamp(current + offset, 0, 100))
|
||||
end
|
||||
return offset
|
||||
end
|
||||
|
||||
local walkSpeed = ix.config.Get("walkSpeed")
|
||||
local maxAttributes = ix.config.Get("maxAttributes", 100)
|
||||
local offset
|
||||
|
||||
if (client:KeyDown(IN_SPEED) and client:GetVelocity():LengthSqr() >= 5000 and client:OnGround()) then
|
||||
-- characters could have attribute values greater than max if the config was changed
|
||||
offset = -ix.config.Get("staminaDrain", 1) + math.min(character:GetAttribute("end", 0), maxAttributes) / 100
|
||||
|
||||
-- Ensure minimum drain
|
||||
if (offset > -0.2) then
|
||||
offset = -0.2
|
||||
end
|
||||
|
||||
-- Armor weight influence (built-in fix)
|
||||
local armor = client:Armor()
|
||||
if (armor > 0) then
|
||||
offset = offset * (1 + (math.min(armor, 100) / 100) * 0.7)
|
||||
end
|
||||
else
|
||||
offset = client:Crouching() and ix.config.Get("staminaCrouchRegeneration", 2) or ix.config.Get("staminaRegeneration", 1.75)
|
||||
|
||||
-- Armor slows down regeneration but doesn't cause drain while standing
|
||||
local armor = client:Armor()
|
||||
if (armor > 0 and offset > 0) then
|
||||
offset = offset * math.max(1 - (math.min(armor, 100) / 100) * 0.5, 0.1)
|
||||
end
|
||||
|
||||
-- Гарантируем, что регенерация это регенерация (не меньше нуля)
|
||||
offset = math.max(offset, 0.05)
|
||||
end
|
||||
|
||||
offset = hook.Run("AdjustStaminaOffset", client, offset) or offset
|
||||
|
||||
if (CLIENT) then
|
||||
return offset -- for the client we need to return the estimated stamina change
|
||||
else
|
||||
local current = client:GetLocalVar("stm", 0)
|
||||
local value = math.Clamp(current + offset, 0, 100)
|
||||
|
||||
if (current != value) then
|
||||
client:SetLocalVar("stm", value)
|
||||
|
||||
if (value == 0 and !client:GetNetVar("brth", false)) then
|
||||
client:SetNetVar("brth", true)
|
||||
|
||||
character:UpdateAttrib("end", 0.1)
|
||||
character:UpdateAttrib("stm", 0.01)
|
||||
|
||||
hook.Run("PlayerStaminaLost", client)
|
||||
elseif (value >= 50 and client:GetNetVar("brth", false)) then
|
||||
client:SetNetVar("brth", nil)
|
||||
|
||||
hook.Run("PlayerStaminaGained", client)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:SetupMove(client, mv, cmd)
|
||||
if (client:GetNetVar("brth", false)) then
|
||||
mv:SetMaxClientSpeed(client:GetWalkSpeed())
|
||||
end
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
function PLUGIN:PostPlayerLoadout(client)
|
||||
local uniqueID = "ixStam" .. client:SteamID()
|
||||
|
||||
timer.Create(uniqueID, 0.25, 0, function()
|
||||
if (!IsValid(client)) then
|
||||
timer.Remove(uniqueID)
|
||||
return
|
||||
end
|
||||
|
||||
CalcStaminaChange(client)
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:CharacterPreSave(character)
|
||||
local client = character:GetPlayer()
|
||||
|
||||
if (IsValid(client)) then
|
||||
character:SetData("stamina", client:GetLocalVar("stm", 0))
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerLoadedCharacter(client, character)
|
||||
timer.Simple(0.25, function()
|
||||
client:SetLocalVar("stm", character:GetData("stamina", 100))
|
||||
end)
|
||||
end
|
||||
|
||||
local playerMeta = FindMetaTable("Player")
|
||||
|
||||
function playerMeta:RestoreStamina(amount)
|
||||
local current = self:GetLocalVar("stm", 0)
|
||||
local value = math.Clamp(current + amount, 0, 100)
|
||||
|
||||
self:SetLocalVar("stm", value)
|
||||
end
|
||||
|
||||
function playerMeta:ConsumeStamina(amount)
|
||||
local current = self:GetLocalVar("stm", 0)
|
||||
local value = math.Clamp(current - amount, 0, 100)
|
||||
|
||||
self:SetLocalVar("stm", value)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
local predictedStamina = 100
|
||||
|
||||
function PLUGIN:Think()
|
||||
local offset = CalcStaminaChange(LocalPlayer())
|
||||
-- the server check it every 0.25 sec, here we check it every [FrameTime()] seconds
|
||||
offset = math.Remap(FrameTime(), 0, 0.25, 0, offset)
|
||||
|
||||
if (offset != 0) then
|
||||
predictedStamina = math.Clamp(predictedStamina + offset, 0, 100)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnLocalVarSet(key, var)
|
||||
if (key != "stm") then return end
|
||||
if (math.abs(predictedStamina - var) > 5) then
|
||||
predictedStamina = var
|
||||
end
|
||||
end
|
||||
|
||||
ix.bar.Add(function()
|
||||
return predictedStamina / 100
|
||||
end, Color(200, 200, 40), nil, "stm")
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
ATTRIBUTE.name = "Strength"
|
||||
ATTRIBUTE.description = "A measure of how strong you are."
|
||||
24
garrysmod/gamemodes/helix/plugins/strength/sh_plugin.lua
Normal file
24
garrysmod/gamemodes/helix/plugins/strength/sh_plugin.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
PLUGIN.name = "Strength"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds a strength attribute."
|
||||
|
||||
if (SERVER) then
|
||||
function PLUGIN:GetPlayerPunchDamage(client, damage, context)
|
||||
if (client:GetCharacter()) then
|
||||
-- Add to the total fist damage.
|
||||
context.damage = context.damage + (client:GetCharacter():GetAttribute("str", 0) * ix.config.Get("strengthMultiplier"))
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerThrowPunch(client, trace)
|
||||
if (client:GetCharacter() and IsValid(trace.Entity) and trace.Entity:IsPlayer()) then
|
||||
client:GetCharacter():UpdateAttrib("str", 0.001)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Configuration for the plugin
|
||||
ix.config.Add("strengthMultiplier", 0.3, "The strength multiplier scale", nil, {
|
||||
data = {min = 0, max = 1.0, decimals = 1},
|
||||
category = "Strength"
|
||||
})
|
||||
233
garrysmod/gamemodes/helix/plugins/thirdperson.lua
Normal file
233
garrysmod/gamemodes/helix/plugins/thirdperson.lua
Normal file
@@ -0,0 +1,233 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Third Person"
|
||||
PLUGIN.author = "Black Tea"
|
||||
PLUGIN.description = "Enables third person camera usage."
|
||||
|
||||
-- Localization
|
||||
ix.lang.AddTable("russian", {
|
||||
optThirdPerson = "Вид от третьего лица",
|
||||
optThirdPersonEnabled = "Включить вид от 3-го лица",
|
||||
optThirdPersonEnabledDesc = "Использовать камеру от третьего лица",
|
||||
optThirdPersonClassic = "Классический режим",
|
||||
optThirdPersonClassicDesc = "Использовать классический вид камеры",
|
||||
optThirdPersonVertical = "Вертикальное смещение",
|
||||
optThirdPersonVerticalDesc = "Высота камеры над персонажем",
|
||||
optThirdPersonHorizontal = "Горизонтальное смещение",
|
||||
optThirdPersonHorizontalDesc = "Боковое смещение камеры",
|
||||
optThirdPersonDistance = "Расстояние камеры",
|
||||
optThirdPersonDistanceDesc = "Дистанция камеры от персонажа",
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optThirdPerson = "Third Person",
|
||||
optThirdPersonEnabled = "Enable Third Person",
|
||||
optThirdPersonEnabledDesc = "Use third person camera view",
|
||||
optThirdPersonClassic = "Classic Mode",
|
||||
optThirdPersonClassicDesc = "Use classic camera view",
|
||||
optThirdPersonVertical = "Vertical Offset",
|
||||
optThirdPersonVerticalDesc = "Camera height above character",
|
||||
optThirdPersonHorizontal = "Horizontal Offset",
|
||||
optThirdPersonHorizontalDesc = "Camera side offset",
|
||||
optThirdPersonDistance = "Camera Distance",
|
||||
optThirdPersonDistanceDesc = "Distance of camera from character",
|
||||
})
|
||||
|
||||
ix.config.Add("thirdperson", false, "Allow Thirdperson in the server.", nil, {
|
||||
category = "server"
|
||||
})
|
||||
|
||||
if (CLIENT) then
|
||||
local function isHidden()
|
||||
return !ix.config.Get("thirdperson")
|
||||
end
|
||||
|
||||
ix.option.Add("thirdpersonEnabled", ix.type.bool, false, {
|
||||
category = "optThirdPerson",
|
||||
name = "optThirdPersonEnabled",
|
||||
description = "optThirdPersonEnabledDesc",
|
||||
hidden = isHidden,
|
||||
OnChanged = function(oldValue, value)
|
||||
hook.Run("ThirdPersonToggled", oldValue, value)
|
||||
end
|
||||
})
|
||||
|
||||
ix.option.Add("thirdpersonClassic", ix.type.bool, false, {
|
||||
category = "optThirdPerson",
|
||||
name = "optThirdPersonClassic",
|
||||
description = "optThirdPersonClassicDesc",
|
||||
hidden = isHidden
|
||||
})
|
||||
|
||||
ix.option.Add("thirdpersonVertical", ix.type.number, 10, {
|
||||
category = "optThirdPerson",
|
||||
name = "optThirdPersonVertical",
|
||||
description = "optThirdPersonVerticalDesc",
|
||||
min = 0, max = 30,
|
||||
hidden = isHidden
|
||||
})
|
||||
|
||||
ix.option.Add("thirdpersonHorizontal", ix.type.number, 0, {
|
||||
category = "optThirdPerson",
|
||||
name = "optThirdPersonHorizontal",
|
||||
description = "optThirdPersonHorizontalDesc",
|
||||
min = -30, max = 30,
|
||||
hidden = isHidden
|
||||
})
|
||||
|
||||
ix.option.Add("thirdpersonDistance", ix.type.number, 50, {
|
||||
category = "optThirdPerson",
|
||||
name = "optThirdPersonDistance",
|
||||
description = "optThirdPersonDistanceDesc",
|
||||
category = "thirdperson", min = 0, max = 100,
|
||||
hidden = isHidden
|
||||
})
|
||||
|
||||
|
||||
concommand.Add("ix_togglethirdperson", function()
|
||||
local bEnabled = !ix.option.Get("thirdpersonEnabled", false)
|
||||
|
||||
ix.option.Set("thirdpersonEnabled", bEnabled)
|
||||
end)
|
||||
|
||||
local function isAllowed()
|
||||
return ix.config.Get("thirdperson")
|
||||
end
|
||||
|
||||
local playerMeta = FindMetaTable("Player")
|
||||
local traceMin = Vector(-10, -10, -10)
|
||||
local traceMax = Vector(10, 10, 10)
|
||||
|
||||
function playerMeta:CanOverrideView()
|
||||
local entity = Entity(self:GetLocalVar("ragdoll", 0))
|
||||
|
||||
if (IsValid(ix.gui.characterMenu) and !ix.gui.characterMenu:IsClosing() and ix.gui.characterMenu:IsVisible()) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (IsValid(ix.gui.menu) and ix.gui.menu:GetCharacterOverview()) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (ix.option.Get("thirdpersonEnabled", false) and
|
||||
!IsValid(self:GetVehicle()) and
|
||||
isAllowed() and
|
||||
IsValid(self) and
|
||||
self:GetCharacter() and
|
||||
!self:GetNetVar("actEnterAngle") and
|
||||
!IsValid(entity) and
|
||||
LocalPlayer():Alive()
|
||||
) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local view, traceData, traceData2, aimOrigin, crouchFactor, ft, curAng, owner
|
||||
local clmp = math.Clamp
|
||||
crouchFactor = 0
|
||||
|
||||
function PLUGIN:CalcView(client, origin, angles, fov)
|
||||
ft = FrameTime()
|
||||
owner = client
|
||||
|
||||
if (client:CanOverrideView() and LocalPlayer():GetViewEntity() == LocalPlayer()) then
|
||||
local bNoclip = LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP
|
||||
|
||||
if ((client:OnGround() and client:KeyDown(IN_DUCK)) or client:Crouching()) then
|
||||
crouchFactor = Lerp(ft*5, crouchFactor, 1)
|
||||
else
|
||||
crouchFactor = Lerp(ft*5, crouchFactor, 0)
|
||||
end
|
||||
|
||||
curAng = owner.camAng or angle_zero
|
||||
view = {}
|
||||
|
||||
local headPos = client:GetPos() + client:GetViewOffset()
|
||||
local offset = curAng:Up() * ix.option.Get("thirdpersonVertical", 10) +
|
||||
curAng:Right() * ix.option.Get("thirdpersonHorizontal", 0) -
|
||||
client:GetViewOffsetDucked() * .5 * crouchFactor
|
||||
|
||||
local startTrace = util.TraceLine({
|
||||
start = headPos,
|
||||
endpos = headPos + offset,
|
||||
filter = client
|
||||
})
|
||||
|
||||
traceData = {}
|
||||
traceData.start = startTrace.HitPos
|
||||
traceData.endpos = traceData.start - curAng:Forward() * ix.option.Get("thirdpersonDistance", 50)
|
||||
traceData.filter = client
|
||||
traceData.ignoreworld = bNoclip
|
||||
traceData.mins = traceMin
|
||||
traceData.maxs = traceMax
|
||||
view.origin = util.TraceHull(traceData).HitPos
|
||||
aimOrigin = view.origin
|
||||
view.angles = curAng + client:GetViewPunchAngles()
|
||||
|
||||
traceData2 = {}
|
||||
traceData2.start = aimOrigin
|
||||
traceData2.endpos = aimOrigin + curAng:Forward() * 65535
|
||||
traceData2.filter = client
|
||||
traceData2.ignoreworld = bNoclip
|
||||
|
||||
local bClassic = ix.option.Get("thirdpersonClassic", false)
|
||||
|
||||
if (bClassic or owner:IsWepRaised() or
|
||||
(owner:KeyDown(bit.bor(IN_FORWARD, IN_BACK, IN_MOVELEFT, IN_MOVERIGHT)) and owner:GetVelocity():Length() >= 10)) then
|
||||
client:SetEyeAngles((util.TraceLine(traceData2).HitPos - client:GetShootPos()):Angle())
|
||||
else
|
||||
local currentAngles = client:EyeAngles()
|
||||
currentAngles.pitch = (util.TraceLine(traceData2).HitPos - client:GetShootPos()):Angle().pitch
|
||||
|
||||
client:SetEyeAngles(currentAngles)
|
||||
end
|
||||
|
||||
return view
|
||||
end
|
||||
end
|
||||
|
||||
local diff, fm, sm
|
||||
function PLUGIN:CreateMove(cmd)
|
||||
owner = LocalPlayer()
|
||||
|
||||
if (owner:CanOverrideView() and owner:GetMoveType() != MOVETYPE_NOCLIP and
|
||||
LocalPlayer():GetViewEntity() == LocalPlayer()) then
|
||||
local fm = cmd:GetForwardMove()
|
||||
local sm = cmd:GetSideMove()
|
||||
|
||||
local camAng = owner.camAng or owner:EyeAngles()
|
||||
local eyeAng = owner:EyeAngles()
|
||||
|
||||
local wishDir = (camAng:Forward() * fm) + (camAng:Right() * sm)
|
||||
wishDir.z = 0
|
||||
|
||||
local localMove = WorldToLocal(wishDir, angle_zero, vector_origin, Angle(0, eyeAng.y, 0))
|
||||
|
||||
cmd:SetForwardMove(localMove.x)
|
||||
cmd:SetSideMove(-localMove.y)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:InputMouseApply(cmd, x, y, ang)
|
||||
owner = LocalPlayer()
|
||||
|
||||
if (!owner.camAng) then
|
||||
owner.camAng = Angle(0, 0, 0)
|
||||
end
|
||||
|
||||
owner.camAng.p = clmp(math.NormalizeAngle(owner.camAng.p + y / 50), -85, 85)
|
||||
owner.camAng.y = math.NormalizeAngle(owner.camAng.y - x / 50)
|
||||
|
||||
if (owner:CanOverrideView() and LocalPlayer():GetViewEntity() == LocalPlayer()) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldDrawLocalPlayer()
|
||||
if (LocalPlayer():GetViewEntity() == LocalPlayer() and !IsValid(LocalPlayer():GetVehicle())) then
|
||||
return LocalPlayer():CanOverrideView()
|
||||
end
|
||||
end
|
||||
end
|
||||
244
garrysmod/gamemodes/helix/plugins/typing.lua
Normal file
244
garrysmod/gamemodes/helix/plugins/typing.lua
Normal file
@@ -0,0 +1,244 @@
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Typing Indicator"
|
||||
PLUGIN.description = "Shows an indicator when someone is typing."
|
||||
PLUGIN.author = "`impulse"
|
||||
PLUGIN.animationTime = 0.5
|
||||
|
||||
if (CLIENT) then
|
||||
local standingOffset = Vector(0, 0, 72)
|
||||
local crouchingOffset = Vector(0, 0, 38)
|
||||
local boneOffset = Vector(0, 0, 10)
|
||||
local textColor = Color(250, 250, 250)
|
||||
local shadowColor = Color(66, 66, 66)
|
||||
local currentClass
|
||||
|
||||
-- we can't rely on matching non-alphanumeric characters (i.e %W) due to patterns matching single bytes and not UTF-8 chars
|
||||
local symbolPattern = "[~`!@#$%%%^&*()_%+%-={}%[%]|;:'\",%./<>?]"
|
||||
|
||||
function PLUGIN:LoadFonts(font, genericFont)
|
||||
surface.CreateFont("ixTypingIndicator", {
|
||||
font = genericFont,
|
||||
size = 128,
|
||||
extended = true,
|
||||
weight = 1000
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:ChatTextChanged(text)
|
||||
if (!IsValid(LocalPlayer())) then
|
||||
return
|
||||
end
|
||||
|
||||
local character = LocalPlayer():GetCharacter()
|
||||
|
||||
if (!character) then
|
||||
return
|
||||
end
|
||||
|
||||
if (text == "") then
|
||||
currentClass = nil
|
||||
|
||||
net.Start("ixTypeClass")
|
||||
net.WriteString("")
|
||||
net.SendToServer()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local newClass = hook.Run("GetTypingIndicator", character, text)
|
||||
|
||||
if (newClass != currentClass) then
|
||||
currentClass = newClass
|
||||
|
||||
net.Start("ixTypeClass")
|
||||
net.WriteString(currentClass or "")
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:FinishChat()
|
||||
currentClass = nil
|
||||
|
||||
net.Start("ixTypeClass")
|
||||
net.WriteString("")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function PLUGIN:GetTypingIndicator(character, text)
|
||||
local prefix = text:utf8sub(1, 1)
|
||||
|
||||
if (!prefix:find(symbolPattern) and text:utf8len() > 1) then
|
||||
return "ic"
|
||||
else
|
||||
local chatType = ix.chat.Parse(nil, text)
|
||||
|
||||
if (chatType and chatType != "ic") then
|
||||
return !ix.chat.classes[chatType].bNoIndicator and chatType or nil
|
||||
end
|
||||
|
||||
-- some commands will have their own typing indicator, so we'll make sure we're actually typing out a command first
|
||||
local start, _, commandName = text:find("/(%S+)%s")
|
||||
|
||||
if (start == 1) then
|
||||
for uniqueID, command in pairs(ix.command.list) do
|
||||
if (command.bNoIndicator) then
|
||||
continue
|
||||
end
|
||||
|
||||
if (commandName == uniqueID) then
|
||||
return command.indicator and "@" .. command.indicator or "ooc"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:GetTypingIndicatorPosition(client)
|
||||
local head
|
||||
|
||||
for i = 1, client:GetBoneCount() do
|
||||
local name = client:GetBoneName(i)
|
||||
|
||||
if (string.find(name:lower(), "head")) then
|
||||
head = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local position = head and client:GetBonePosition(head) or (client:Crouching() and crouchingOffset or standingOffset)
|
||||
return position + boneOffset
|
||||
end
|
||||
|
||||
function PLUGIN:PostDrawTranslucentRenderables()
|
||||
local client = LocalPlayer()
|
||||
local position = client:GetPos()
|
||||
|
||||
for _, v in player.Iterator() do
|
||||
if (v == client) then
|
||||
continue
|
||||
end
|
||||
|
||||
local distance = v:GetPos():DistToSqr(position)
|
||||
local moveType = v:GetMoveType()
|
||||
|
||||
if (!IsValid(v) or !v:Alive() or
|
||||
(moveType != MOVETYPE_WALK and moveType != MOVETYPE_NONE) or
|
||||
!v.ixChatClassText or
|
||||
distance >= v.ixChatClassRange) then
|
||||
continue
|
||||
end
|
||||
|
||||
local text = v.ixChatClassText
|
||||
local range = v.ixChatClassRange
|
||||
|
||||
local bAnimation = !ix.option.Get("disableAnimations", false)
|
||||
local fraction
|
||||
|
||||
if (bAnimation) then
|
||||
local bComplete = v.ixChatClassTween:update(FrameTime())
|
||||
|
||||
if (bComplete and !v.ixChatStarted) then
|
||||
v.ixChatClassText = nil
|
||||
v.ixChatClassRange = nil
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
fraction = v.ixChatClassAnimation
|
||||
else
|
||||
fraction = 1
|
||||
end
|
||||
|
||||
local angle = EyeAngles()
|
||||
angle:RotateAroundAxis(angle:Forward(), 90)
|
||||
angle:RotateAroundAxis(angle:Right(), 90)
|
||||
|
||||
cam.Start3D2D(self:GetTypingIndicatorPosition(v), Angle(0, angle.y, 90), 0.05)
|
||||
surface.SetFont("ixTypingIndicator")
|
||||
|
||||
local _, textHeight = surface.GetTextSize(text)
|
||||
local alpha = bAnimation and ((1 - math.min(distance, range) / range) * 255 * fraction) or 255
|
||||
|
||||
draw.SimpleTextOutlined(text, "ixTypingIndicator", 0,
|
||||
-textHeight * 0.5 * fraction,
|
||||
ColorAlpha(textColor, alpha),
|
||||
TEXT_ALIGN_CENTER,
|
||||
TEXT_ALIGN_CENTER, 4,
|
||||
ColorAlpha(shadowColor, alpha)
|
||||
)
|
||||
cam.End3D2D()
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("ixTypeClass", function()
|
||||
local client = net.ReadEntity()
|
||||
|
||||
if (!IsValid(client) or client == LocalPlayer()) then
|
||||
return
|
||||
end
|
||||
|
||||
local newClass = net.ReadString()
|
||||
local chatClass = ix.chat.classes[newClass]
|
||||
local text
|
||||
local range
|
||||
|
||||
if (chatClass) then
|
||||
text = L(chatClass.indicator or "chatTyping")
|
||||
range = chatClass.range or math.pow(ix.config.Get("chatRange", 280), 2)
|
||||
elseif (newClass and newClass:sub(1, 1) == "@") then
|
||||
text = L(newClass:sub(2))
|
||||
range = math.pow(ix.config.Get("chatRange", 280), 2)
|
||||
end
|
||||
|
||||
if (ix.option.Get("disableAnimations", false)) then
|
||||
client.ixChatClassText = text
|
||||
client.ixChatClassRange = range
|
||||
else
|
||||
client.ixChatClassAnimation = tonumber(client.ixChatClassAnimation) or 0
|
||||
|
||||
if (text and !client.ixChatStarted) then
|
||||
client.ixChatClassTween = ix.tween.new(PLUGIN.animationTime, client, {ixChatClassAnimation = 1}, "outCubic")
|
||||
|
||||
client.ixChatClassText = text
|
||||
client.ixChatClassRange = range
|
||||
client.ixChatStarted = true
|
||||
elseif (!text and client.ixChatStarted) then
|
||||
client.ixChatClassTween = ix.tween.new(PLUGIN.animationTime, client, {ixChatClassAnimation = 0}, "inCubic")
|
||||
client.ixChatStarted = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
util.AddNetworkString("ixTypeClass")
|
||||
|
||||
function PLUGIN:PlayerSpawn(client)
|
||||
net.Start("ixTypeClass")
|
||||
net.WriteEntity(client)
|
||||
net.WriteString("")
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
net.Receive("ixTypeClass", function(length, client)
|
||||
if ((client.ixNextTypeClass or 0) > RealTime()) then
|
||||
return
|
||||
end
|
||||
|
||||
local newClass = net.ReadString()
|
||||
|
||||
-- send message to players in pvs only since they're the only ones who can see the indicator
|
||||
-- we'll broadcast if the type class is empty because they might move out of pvs before the ending net message is sent
|
||||
net.Start("ixTypeClass")
|
||||
net.WriteEntity(client)
|
||||
net.WriteString(newClass)
|
||||
|
||||
if (newClass == "") then
|
||||
net.Broadcast()
|
||||
else
|
||||
net.SendPVS(client:GetPos())
|
||||
end
|
||||
|
||||
client.ixNextTypeClass = RealTime() + 0.2
|
||||
end)
|
||||
end
|
||||
287
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendor.lua
vendored
Normal file
287
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendor.lua
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "bReadOnly", "ReadOnly", FORCE_BOOL)
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(ScrW() * 0.45, ScrH() * 0.65)
|
||||
self:SetTitle("")
|
||||
self:MakePopup()
|
||||
self:Center()
|
||||
|
||||
local header = self:Add("DPanel")
|
||||
header:SetTall(34)
|
||||
header:Dock(TOP)
|
||||
|
||||
self.vendorName = header:Add("DLabel")
|
||||
self.vendorName:Dock(LEFT)
|
||||
self.vendorName:SetWide(self:GetWide() * 0.5 - 7)
|
||||
self.vendorName:SetText("John Doe")
|
||||
self.vendorName:SetTextInset(4, 0)
|
||||
self.vendorName:SetTextColor(color_white)
|
||||
self.vendorName:SetFont("ixMediumFont")
|
||||
|
||||
self.ourName = header:Add("DLabel")
|
||||
self.ourName:Dock(RIGHT)
|
||||
self.ourName:SetWide(self:GetWide() * 0.5 - 7)
|
||||
self.ourName:SetText(L"you".." ("..ix.currency.Get(LocalPlayer():GetCharacter():GetMoney())..")")
|
||||
self.ourName:SetTextInset(0, 0)
|
||||
self.ourName:SetTextColor(color_white)
|
||||
self.ourName:SetFont("ixMediumFont")
|
||||
|
||||
local footer = self:Add("DPanel")
|
||||
footer:SetTall(34)
|
||||
footer:Dock(BOTTOM)
|
||||
footer:SetPaintBackground(false)
|
||||
|
||||
self.vendorSell = footer:Add("DButton")
|
||||
self.vendorSell:SetFont("ixMediumFont")
|
||||
self.vendorSell:SetWide(self.vendorName:GetWide())
|
||||
self.vendorSell:Dock(LEFT)
|
||||
self.vendorSell:SetContentAlignment(5)
|
||||
-- The text says purchase but the vendor is selling it to us.
|
||||
self.vendorSell:SetText(L"purchase")
|
||||
self.vendorSell:SetTextColor(color_white)
|
||||
|
||||
self.vendorSell.DoClick = function(this)
|
||||
if (IsValid(self.activeSell)) then
|
||||
net.Start("ixVendorTrade")
|
||||
net.WriteString(self.activeSell.item)
|
||||
net.WriteBool(false)
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
self.vendorBuy = footer:Add("DButton")
|
||||
self.vendorBuy:SetFont("ixMediumFont")
|
||||
self.vendorBuy:SetWide(self.ourName:GetWide())
|
||||
self.vendorBuy:Dock(RIGHT)
|
||||
self.vendorBuy:SetContentAlignment(5)
|
||||
self.vendorBuy:SetText(L"sell")
|
||||
self.vendorBuy:SetTextColor(color_white)
|
||||
self.vendorBuy.DoClick = function(this)
|
||||
if (IsValid(self.activeBuy)) then
|
||||
net.Start("ixVendorTrade")
|
||||
net.WriteString(self.activeBuy.item)
|
||||
net.WriteBool(true)
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
self.selling = self:Add("DScrollPanel")
|
||||
self.selling:SetWide(self:GetWide() * 0.5 - 7)
|
||||
self.selling:Dock(LEFT)
|
||||
self.selling:DockMargin(0, 4, 0, 4)
|
||||
self.selling:SetPaintBackground(true)
|
||||
|
||||
self.sellingItems = self.selling:Add("DListLayout")
|
||||
self.sellingItems:SetSize(self.selling:GetSize())
|
||||
self.sellingItems:DockPadding(0, 0, 0, 4)
|
||||
self.sellingItems:SetTall(ScrH())
|
||||
|
||||
self.buying = self:Add("DScrollPanel")
|
||||
self.buying:SetWide(self:GetWide() * 0.5 - 7)
|
||||
self.buying:Dock(RIGHT)
|
||||
self.buying:DockMargin(0, 4, 0, 4)
|
||||
self.buying:SetPaintBackground(true)
|
||||
|
||||
self.buyingItems = self.buying:Add("DListLayout")
|
||||
self.buyingItems:SetSize(self.buying:GetSize())
|
||||
self.buyingItems:DockPadding(0, 0, 0, 4)
|
||||
|
||||
self.sellingList = {}
|
||||
self.buyingList = {}
|
||||
end
|
||||
|
||||
function PANEL:addItem(uniqueID, listID)
|
||||
local entity = self.entity
|
||||
local items = entity.items
|
||||
local data = items[uniqueID]
|
||||
|
||||
if ((!listID or listID == "selling") and !IsValid(self.sellingList[uniqueID])
|
||||
and ix.item.list[uniqueID]) then
|
||||
if (data and data[VENDOR_MODE] and data[VENDOR_MODE] != VENDOR_BUYONLY) then
|
||||
local item = self.sellingItems:Add("ixVendorItem")
|
||||
item:Setup(uniqueID)
|
||||
|
||||
self.sellingList[uniqueID] = item
|
||||
self.sellingItems:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
|
||||
if ((!listID or listID == "buying") and !IsValid(self.buyingList[uniqueID])
|
||||
and LocalPlayer():GetCharacter():GetInventory():HasItem(uniqueID)) then
|
||||
if (data and data[VENDOR_MODE] and data[VENDOR_MODE] != VENDOR_SELLONLY) then
|
||||
local item = self.buyingItems:Add("ixVendorItem")
|
||||
item:Setup(uniqueID)
|
||||
item.isLocal = true
|
||||
|
||||
self.buyingList[uniqueID] = item
|
||||
self.buyingItems:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:removeItem(uniqueID, listID)
|
||||
if (!listID or listID == "selling") then
|
||||
if (IsValid(self.sellingList[uniqueID])) then
|
||||
self.sellingList[uniqueID]:Remove()
|
||||
self.sellingItems:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
|
||||
if (!listID or listID == "buying") then
|
||||
if (IsValid(self.buyingList[uniqueID])) then
|
||||
self.buyingList[uniqueID]:Remove()
|
||||
self.buyingItems:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Setup(entity)
|
||||
self.entity = entity
|
||||
self:SetTitle(entity:GetDisplayName())
|
||||
self.vendorName:SetText(entity:GetDisplayName()..(entity.money and " ("..entity.money..")" or ""))
|
||||
|
||||
self.vendorBuy:SetEnabled(!self:GetReadOnly())
|
||||
self.vendorSell:SetEnabled(!self:GetReadOnly())
|
||||
|
||||
for k, _ in SortedPairs(entity.items) do
|
||||
self:addItem(k, "selling")
|
||||
end
|
||||
|
||||
for _, v in SortedPairs(LocalPlayer():GetCharacter():GetInventory():GetItems()) do
|
||||
self:addItem(v.uniqueID, "buying")
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
net.Start("ixVendorClose")
|
||||
net.SendToServer()
|
||||
|
||||
if (IsValid(ix.gui.vendorEditor)) then
|
||||
ix.gui.vendorEditor:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
local entity = self.entity
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
self:Remove()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if ((self.nextUpdate or 0) < CurTime()) then
|
||||
self:SetTitle(self.entity:GetDisplayName())
|
||||
self.vendorName:SetText(entity:GetDisplayName()..(entity.money and " ("..ix.currency.Get(entity.money)..")" or ""))
|
||||
self.ourName:SetText(L"you".." ("..ix.currency.Get(LocalPlayer():GetCharacter():GetMoney())..")")
|
||||
|
||||
self.nextUpdate = CurTime() + 0.25
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnItemSelected(panel)
|
||||
local price = self.entity:GetPrice(panel.item, panel.isLocal)
|
||||
|
||||
if (panel.isLocal) then
|
||||
self.vendorBuy:SetText(L"sell".." ("..ix.currency.Get(price)..")")
|
||||
else
|
||||
self.vendorSell:SetText(L"purchase".." ("..ix.currency.Get(price)..")")
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("ixVendor", PANEL, "DFrame")
|
||||
|
||||
PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetTall(36)
|
||||
self:DockMargin(4, 4, 4, 0)
|
||||
|
||||
self.icon = self:Add("SpawnIcon")
|
||||
self.icon:SetPos(2, 2)
|
||||
self.icon:SetSize(32, 32)
|
||||
self.icon:SetModel("models/error.mdl")
|
||||
|
||||
self.name = self:Add("DLabel")
|
||||
self.name:Dock(FILL)
|
||||
self.name:DockMargin(42, 0, 0, 0)
|
||||
self.name:SetFont("ixChatFont")
|
||||
self.name:SetTextColor(color_white)
|
||||
self.name:SetExpensiveShadow(1, Color(0, 0, 0, 200))
|
||||
|
||||
self.click = self:Add("DButton")
|
||||
self.click:Dock(FILL)
|
||||
self.click:SetText("")
|
||||
self.click.Paint = function() end
|
||||
self.click.DoClick = function(this)
|
||||
if (self.isLocal) then
|
||||
ix.gui.vendor.activeBuy = self
|
||||
else
|
||||
ix.gui.vendor.activeSell = self
|
||||
end
|
||||
|
||||
ix.gui.vendor:OnItemSelected(self)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetCallback(callback)
|
||||
self.click.DoClick = function(this)
|
||||
callback()
|
||||
self.selected = true
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Setup(uniqueID)
|
||||
local item = ix.item.list[uniqueID]
|
||||
|
||||
if (item) then
|
||||
self.item = uniqueID
|
||||
self.icon:SetModel(item:GetModel(), item:GetSkin())
|
||||
self.name:SetText(item:GetName())
|
||||
self.itemName = item:GetName()
|
||||
|
||||
self.click:SetHelixTooltip(function(tooltip)
|
||||
ix.hud.PopulateItemTooltip(tooltip, item)
|
||||
|
||||
local entity = ix.gui.vendor.entity
|
||||
if (entity and entity.items[self.item] and entity.items[self.item][VENDOR_MAXSTOCK]) then
|
||||
local info = entity.items[self.item]
|
||||
local stock = tooltip:AddRowAfter("name", "stock")
|
||||
stock:SetText(string.format("Stock: %d/%d", info[VENDOR_STOCK], info[VENDOR_MAXSTOCK]))
|
||||
stock:SetBackgroundColor(derma.GetColor("Info", self))
|
||||
stock:SizeToContents()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
if ((self.nextUpdate or 0) < CurTime()) then
|
||||
local entity = ix.gui.vendor.entity
|
||||
|
||||
if (entity and self.isLocal) then
|
||||
local count = LocalPlayer():GetCharacter():GetInventory():GetItemCount(self.item)
|
||||
|
||||
if (count == 0) then
|
||||
self:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
self.nextUpdate = CurTime() + 0.1
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if (ix.gui.vendor.activeBuy == self or ix.gui.vendor.activeSell == self) then
|
||||
surface.SetDrawColor(ix.config.Get("color"))
|
||||
else
|
||||
surface.SetDrawColor(0, 0, 0, 100)
|
||||
end
|
||||
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
vgui.Register("ixVendorItem", PANEL, "DPanel")
|
||||
286
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendoreditor.lua
vendored
Normal file
286
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendoreditor.lua
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
local entity = ix.gui.vendor.entity
|
||||
|
||||
self:SetSize(320, 480)
|
||||
self:MoveLeftOf(ix.gui.vendor, 8)
|
||||
self:MakePopup()
|
||||
self:CenterVertical()
|
||||
self:SetTitle(L"vendorEditor")
|
||||
self.lblTitle:SetTextColor(color_white)
|
||||
|
||||
self.name = self:Add("DTextEntry")
|
||||
self.name:Dock(TOP)
|
||||
self.name:SetText(entity:GetDisplayName())
|
||||
self.name:SetPlaceholderText(L"name")
|
||||
self.name.OnEnter = function(this)
|
||||
if (entity:GetDisplayName() != this:GetText()) then
|
||||
self:updateVendor("name", this:GetText())
|
||||
end
|
||||
end
|
||||
|
||||
self.description = self:Add("DTextEntry")
|
||||
self.description:Dock(TOP)
|
||||
self.description:DockMargin(0, 4, 0, 0)
|
||||
self.description:SetText(entity:GetDescription())
|
||||
self.description:SetPlaceholderText(L"description")
|
||||
self.description.OnEnter = function(this)
|
||||
if (entity:GetDescription() != this:GetText()) then
|
||||
self:updateVendor("description", this:GetText())
|
||||
end
|
||||
end
|
||||
|
||||
self.model = self:Add("DTextEntry")
|
||||
self.model:Dock(TOP)
|
||||
self.model:DockMargin(0, 4, 0, 0)
|
||||
self.model:SetText(entity:GetModel())
|
||||
self.model:SetPlaceholderText(L"model")
|
||||
self.model.OnEnter = function(this)
|
||||
if (entity:GetModel():lower() != this:GetText():lower()) then
|
||||
self:updateVendor("model", this:GetText():lower())
|
||||
end
|
||||
end
|
||||
|
||||
local useMoney = tonumber(entity.money) != nil
|
||||
|
||||
self.money = self:Add("DTextEntry")
|
||||
self.money:Dock(TOP)
|
||||
self.money:DockMargin(0, 4, 0, 0)
|
||||
self.money:SetText(!useMoney and "∞" or entity.money)
|
||||
self.money:SetPlaceholderText(L"money")
|
||||
self.money:SetDisabled(!useMoney)
|
||||
self.money:SetEnabled(useMoney)
|
||||
self.money:SetNumeric(true)
|
||||
self.money.OnEnter = function(this)
|
||||
local value = tonumber(this:GetText()) or entity.money
|
||||
|
||||
if (value == entity.money) then
|
||||
return
|
||||
end
|
||||
|
||||
self:updateVendor("money", value)
|
||||
end
|
||||
|
||||
self.bubble = self:Add("DCheckBoxLabel")
|
||||
self.bubble:SetText(L"vendorNoBubble")
|
||||
self.bubble:Dock(TOP)
|
||||
self.bubble:DockMargin(0, 4, 0, 0)
|
||||
self.bubble:SetValue(entity:GetNoBubble() and 1 or 0)
|
||||
self.bubble.OnChange = function(this, value)
|
||||
if (this.noSend) then
|
||||
this.noSend = nil
|
||||
else
|
||||
self:updateVendor("bubble", value)
|
||||
end
|
||||
end
|
||||
|
||||
self.useMoney = self:Add("DCheckBoxLabel")
|
||||
self.useMoney:SetText(L"vendorUseMoney")
|
||||
self.useMoney:Dock(TOP)
|
||||
self.useMoney:DockMargin(0, 4, 0, 0)
|
||||
self.useMoney:SetChecked(useMoney)
|
||||
self.useMoney.OnChange = function(this, value)
|
||||
self:updateVendor("useMoney")
|
||||
end
|
||||
|
||||
self.sellScale = self:Add("DNumSlider")
|
||||
self.sellScale:Dock(TOP)
|
||||
self.sellScale:DockMargin(0, 4, 0, 0)
|
||||
self.sellScale:SetText(L"vendorSellScale")
|
||||
self.sellScale.Label:SetTextColor(color_white)
|
||||
self.sellScale.TextArea:SetTextColor(color_white)
|
||||
self.sellScale:SetDecimals(1)
|
||||
self.sellScale.noSend = true
|
||||
self.sellScale:SetValue(entity.scale)
|
||||
self.sellScale.OnValueChanged = function(this, value)
|
||||
if (this.noSend) then
|
||||
this.noSend = nil
|
||||
else
|
||||
timer.Create("ixVendorScale", 1, 1, function()
|
||||
if (IsValid(self) and IsValid(self.sellScale)) then
|
||||
value = self.sellScale:GetValue()
|
||||
|
||||
if (value != entity.scale) then
|
||||
self:updateVendor("scale", value)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
self.faction = self:Add("DButton")
|
||||
self.faction:SetText(L"vendorFaction")
|
||||
self.faction:Dock(TOP)
|
||||
self.faction:SetTextColor(color_white)
|
||||
self.faction:DockMargin(0, 4, 0, 0)
|
||||
self.faction.DoClick = function(this)
|
||||
if (IsValid(ix.gui.editorFaction)) then
|
||||
ix.gui.editorFaction:Remove()
|
||||
end
|
||||
|
||||
ix.gui.editorFaction = vgui.Create("ixVendorFactionEditor")
|
||||
ix.gui.editorFaction.updateVendor = self.updateVendor
|
||||
ix.gui.editorFaction.entity = entity
|
||||
ix.gui.editorFaction:Setup()
|
||||
end
|
||||
|
||||
self.searchBar = self:Add("DTextEntry")
|
||||
self.searchBar:Dock(TOP)
|
||||
self.searchBar:DockMargin(0, 4, 0, 0)
|
||||
self.searchBar:SetUpdateOnType(true)
|
||||
self.searchBar:SetPlaceholderText("Search...")
|
||||
self.searchBar.OnValueChange = function(this, value)
|
||||
self:ReloadItemList(value)
|
||||
end
|
||||
|
||||
local menu
|
||||
|
||||
self.items = self:Add("DListView")
|
||||
self.items:Dock(FILL)
|
||||
self.items:DockMargin(0, 4, 0, 0)
|
||||
self.items:AddColumn(L"name").Header:SetTextColor(color_black)
|
||||
self.items:AddColumn(L"category").Header:SetTextColor(color_black)
|
||||
self.items:AddColumn(L"mode").Header:SetTextColor(color_black)
|
||||
self.items:AddColumn(L"price").Header:SetTextColor(color_black)
|
||||
self.items:AddColumn(L"stock").Header:SetTextColor(color_black)
|
||||
self.items:SetMultiSelect(false)
|
||||
self.items.OnRowRightClick = function(this, index, line)
|
||||
if (IsValid(menu)) then
|
||||
menu:Remove()
|
||||
end
|
||||
|
||||
local uniqueID = line.item
|
||||
|
||||
menu = DermaMenu()
|
||||
-- Modes of the item.
|
||||
local mode, panel = menu:AddSubMenu(L"mode")
|
||||
panel:SetImage("icon16/key.png")
|
||||
|
||||
-- Disable buying/selling of the item.
|
||||
mode:AddOption(L"none", function()
|
||||
self:updateVendor("mode", {uniqueID, nil})
|
||||
end):SetImage("icon16/cog_error.png")
|
||||
|
||||
-- Allow the vendor to sell and buy this item.
|
||||
mode:AddOption(L"vendorBoth", function()
|
||||
self:updateVendor("mode", {uniqueID, VENDOR_SELLANDBUY})
|
||||
end):SetImage("icon16/cog.png")
|
||||
|
||||
-- Only allow the vendor to buy this item from players.
|
||||
mode:AddOption(L"vendorBuy", function()
|
||||
self:updateVendor("mode", {uniqueID, VENDOR_BUYONLY})
|
||||
end):SetImage("icon16/cog_delete.png")
|
||||
|
||||
-- Only allow the vendor to sell this item to players.
|
||||
mode:AddOption(L"vendorSell", function()
|
||||
self:updateVendor("mode", {uniqueID, VENDOR_SELLONLY})
|
||||
end):SetImage("icon16/cog_add.png")
|
||||
|
||||
local itemTable = ix.item.list[uniqueID]
|
||||
|
||||
-- Set the price of the item.
|
||||
menu:AddOption(L"price", function()
|
||||
Derma_StringRequest(
|
||||
itemTable.GetName and itemTable:GetName() or L(itemTable.name),
|
||||
L"vendorPriceReq",
|
||||
entity:GetPrice(uniqueID),
|
||||
function(text)
|
||||
text = tonumber(text)
|
||||
|
||||
if (text == itemTable.price) then
|
||||
text = nil
|
||||
end
|
||||
|
||||
self:updateVendor("price", {uniqueID, text})
|
||||
end
|
||||
)
|
||||
end):SetImage("icon16/coins.png")
|
||||
|
||||
-- Set the stock of the item or disable it.
|
||||
local stock, menuPanel = menu:AddSubMenu(L"stock")
|
||||
menuPanel:SetImage("icon16/table.png")
|
||||
|
||||
-- Disable the use of stocks for this item.
|
||||
stock:AddOption(L"disable", function()
|
||||
self:updateVendor("stockDisable", uniqueID)
|
||||
end):SetImage("icon16/table_delete.png")
|
||||
|
||||
-- Edit the maximum stock for this item.
|
||||
stock:AddOption(L"edit", function()
|
||||
local _, max = entity:GetStock(uniqueID)
|
||||
|
||||
Derma_StringRequest(
|
||||
itemTable.GetName and itemTable:GetName() or L(itemTable.name),
|
||||
L"vendorStockReq",
|
||||
max or 1,
|
||||
function(text)
|
||||
self:updateVendor("stockMax", {uniqueID, text})
|
||||
end
|
||||
)
|
||||
end):SetImage("icon16/table_edit.png")
|
||||
|
||||
-- Edit the current stock of this item.
|
||||
stock:AddOption(L"vendorEditCurStock", function()
|
||||
Derma_StringRequest(
|
||||
itemTable.GetName and itemTable:GetName() or L(itemTable.name),
|
||||
L"vendorStockCurReq",
|
||||
entity:GetStock(uniqueID) or 0,
|
||||
function(text)
|
||||
self:updateVendor("stock", {uniqueID, text})
|
||||
end
|
||||
)
|
||||
end):SetImage("icon16/table_edit.png")
|
||||
menu:Open()
|
||||
end
|
||||
|
||||
self:ReloadItemList()
|
||||
end
|
||||
|
||||
function PANEL:ReloadItemList(filter)
|
||||
local entity = ix.gui.vendor.entity
|
||||
self.lines = {}
|
||||
|
||||
self.items:Clear()
|
||||
|
||||
for k, v in SortedPairs(ix.item.list) do
|
||||
local itemName = v.GetName and v:GetName() or L(v.name)
|
||||
|
||||
if (filter and !itemName:lower():find(filter:lower(), 1, false)) then
|
||||
continue
|
||||
end
|
||||
|
||||
local mode = entity.items[k] and entity.items[k][VENDOR_MODE]
|
||||
local current, max = entity:GetStock(k)
|
||||
local panel = self.items:AddLine(
|
||||
itemName,
|
||||
v.category or L"none",
|
||||
mode and L(VENDOR_TEXT[mode]) or L"none",
|
||||
entity:GetPrice(k),
|
||||
max and current.."/"..max or "-"
|
||||
)
|
||||
|
||||
panel.item = k
|
||||
self.lines[k] = panel
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:OnRemove()
|
||||
if (IsValid(ix.gui.vendor)) then
|
||||
ix.gui.vendor:Remove()
|
||||
end
|
||||
|
||||
if (IsValid(ix.gui.editorFaction)) then
|
||||
ix.gui.editorFaction:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:updateVendor(key, value)
|
||||
net.Start("ixVendorEdit")
|
||||
net.WriteString(key)
|
||||
net.WriteType(value)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
vgui.Register("ixVendorEditor", PANEL, "DFrame")
|
||||
60
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendorfaction.lua
vendored
Normal file
60
garrysmod/gamemodes/helix/plugins/vendor/derma/cl_vendorfaction.lua
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(256, 280)
|
||||
self:Center()
|
||||
self:MakePopup()
|
||||
self:SetTitle(L"vendorFaction")
|
||||
self.scroll = self:Add("DScrollPanel")
|
||||
self.scroll:Dock(FILL)
|
||||
self.scroll:DockPadding(0, 0, 0, 4)
|
||||
|
||||
self.factions = {}
|
||||
self.classes = {}
|
||||
|
||||
for k, v in ipairs(ix.faction.indices) do
|
||||
local panel = self.scroll:Add("DPanel")
|
||||
panel:Dock(TOP)
|
||||
panel:DockPadding(4, 4, 4, 4)
|
||||
panel:DockMargin(0, 0, 0, 4)
|
||||
|
||||
local faction = panel:Add("DCheckBoxLabel")
|
||||
faction:Dock(TOP)
|
||||
faction:SetText(L(v.name))
|
||||
faction:DockMargin(0, 0, 0, 4)
|
||||
faction.OnChange = function(this, state)
|
||||
self:updateVendor("faction", v.uniqueID)
|
||||
end
|
||||
|
||||
self.factions[v.uniqueID] = faction
|
||||
|
||||
for _, v2 in ipairs(ix.class.list) do
|
||||
if (v2.faction == k) then
|
||||
local class = panel:Add("DCheckBoxLabel")
|
||||
class:Dock(TOP)
|
||||
class:DockMargin(16, 0, 0, 4)
|
||||
class:SetText(L(v2.name))
|
||||
class.OnChange = function(this, state)
|
||||
self:updateVendor("class", v2.uniqueID)
|
||||
end
|
||||
|
||||
self.classes[v2.uniqueID] = class
|
||||
|
||||
panel:SetTall(panel:GetTall() + class:GetTall() + 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Setup()
|
||||
for k, _ in pairs(self.entity.factions or {}) do
|
||||
self.factions[k]:SetChecked(true)
|
||||
end
|
||||
|
||||
for k, _ in pairs(self.entity.classes or {}) do
|
||||
self.classes[k]:SetChecked(true)
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("ixVendorFactionEditor", PANEL, "DFrame")
|
||||
349
garrysmod/gamemodes/helix/plugins/vendor/entities/entities/ix_vendor.lua
vendored
Normal file
349
garrysmod/gamemodes/helix/plugins/vendor/entities/entities/ix_vendor.lua
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
|
||||
ENT.Type = "anim"
|
||||
ENT.PrintName = "Vendor"
|
||||
ENT.Category = "Helix"
|
||||
ENT.Spawnable = true
|
||||
ENT.AdminOnly = true
|
||||
ENT.isVendor = true
|
||||
ENT.bNoPersist = true
|
||||
|
||||
function ENT:SetupDataTables()
|
||||
self:NetworkVar("Bool", 0, "NoBubble")
|
||||
self:NetworkVar("String", 0, "DisplayName")
|
||||
self:NetworkVar("String", 1, "Description")
|
||||
end
|
||||
|
||||
function ENT:Initialize()
|
||||
if (SERVER) then
|
||||
self:SetModel("models/mossman.mdl")
|
||||
self:SetUseType(SIMPLE_USE)
|
||||
self:SetMoveType(MOVETYPE_NONE)
|
||||
self:DrawShadow(true)
|
||||
self:InitPhysObj()
|
||||
|
||||
self:AddCallback("OnAngleChange", function(entity)
|
||||
local mins, maxs = entity:GetAxisAlignedBoundingBox()
|
||||
|
||||
entity:SetCollisionBounds(mins, maxs)
|
||||
end)
|
||||
|
||||
self.items = {}
|
||||
self.messages = {}
|
||||
self.factions = {}
|
||||
self.classes = {}
|
||||
|
||||
self:SetDisplayName("John Doe")
|
||||
self:SetDescription("")
|
||||
|
||||
self.receivers = {}
|
||||
end
|
||||
|
||||
timer.Simple(1, function()
|
||||
if (IsValid(self)) then
|
||||
self:SetAnim()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function ENT:InitPhysObj()
|
||||
local mins, maxs = self:GetAxisAlignedBoundingBox()
|
||||
local bPhysObjCreated = self:PhysicsInitBox(mins, maxs)
|
||||
|
||||
if (bPhysObjCreated) then
|
||||
local physObj = self:GetPhysicsObject()
|
||||
physObj:EnableMotion(false)
|
||||
physObj:Sleep()
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetAxisAlignedBoundingBox()
|
||||
local mins, maxs = self:GetModelBounds()
|
||||
mins = Vector(mins.x, mins.y, 0)
|
||||
mins, maxs = self:GetRotatedAABB(mins, maxs)
|
||||
|
||||
return mins, maxs
|
||||
end
|
||||
|
||||
function ENT:CanAccess(client)
|
||||
local bAccess = false
|
||||
local uniqueID = ix.faction.indices[client:Team()].uniqueID
|
||||
|
||||
if (self.factions and !table.IsEmpty(self.factions)) then
|
||||
if (self.factions[uniqueID]) then
|
||||
bAccess = true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if (bAccess and self.classes and !table.IsEmpty(self.classes)) then
|
||||
local class = ix.class.list[client:GetCharacter():GetClass()]
|
||||
local classID = class and class.uniqueID
|
||||
|
||||
if (classID and !self.classes[classID]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:GetStock(uniqueID)
|
||||
if (self.items[uniqueID] and self.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
return self.items[uniqueID][VENDOR_STOCK] or 0, self.items[uniqueID][VENDOR_MAXSTOCK]
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetPrice(uniqueID, selling)
|
||||
local price = ix.item.list[uniqueID] and self.items[uniqueID] and
|
||||
self.items[uniqueID][VENDOR_PRICE] or ix.item.list[uniqueID].price or 0
|
||||
|
||||
if (selling) then
|
||||
price = math.floor(price * (self.scale or 0.5))
|
||||
end
|
||||
|
||||
return price
|
||||
end
|
||||
|
||||
function ENT:CanSellToPlayer(client, uniqueID)
|
||||
local data = self.items[uniqueID]
|
||||
|
||||
if (!data or !client:GetCharacter() or !ix.item.list[uniqueID]) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (data[VENDOR_MODE] == VENDOR_BUYONLY) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!client:GetCharacter():HasMoney(self:GetPrice(uniqueID))) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (data[VENDOR_STOCK] and data[VENDOR_STOCK] < 1) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:CanBuyFromPlayer(client, uniqueID)
|
||||
local data = self.items[uniqueID]
|
||||
|
||||
if (!data or !client:GetCharacter() or !ix.item.list[uniqueID]) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (data[VENDOR_MODE] != VENDOR_SELLONLY) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!self:HasMoney(data[VENDOR_PRICE] or ix.item.list[uniqueID].price or 0)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:HasMoney(amount)
|
||||
-- Vendor not using money system so they can always afford it.
|
||||
if (!self.money) then
|
||||
return true
|
||||
end
|
||||
|
||||
return self.money >= amount
|
||||
end
|
||||
|
||||
function ENT:SetAnim()
|
||||
for k, v in ipairs(self:GetSequenceList()) do
|
||||
if (v:lower():find("idle") and v != "idlenoise") then
|
||||
return self:ResetSequence(k)
|
||||
end
|
||||
end
|
||||
|
||||
if (self:GetSequenceCount() > 1) then
|
||||
self:ResetSequence(4)
|
||||
end
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
function ENT:SpawnFunction(client, trace)
|
||||
local angles = (trace.HitPos - client:GetPos()):Angle()
|
||||
angles.r = 0
|
||||
angles.p = 0
|
||||
angles.y = angles.y + 180
|
||||
|
||||
local entity = ents.Create("ix_vendor")
|
||||
entity:SetPos(trace.HitPos)
|
||||
entity:SetAngles(angles)
|
||||
entity:Spawn()
|
||||
|
||||
PLUGIN:SaveData()
|
||||
|
||||
return entity
|
||||
end
|
||||
|
||||
function ENT:Use(activator)
|
||||
local character = activator:GetCharacter()
|
||||
|
||||
if (!self:CanAccess(activator) or hook.Run("CanPlayerUseVendor", activator, self) == false) then
|
||||
if (self.messages[VENDOR_NOTRADE]) then
|
||||
activator:ChatPrint(self:GetDisplayName()..": "..self.messages[VENDOR_NOTRADE])
|
||||
else
|
||||
activator:NotifyLocalized("vendorNoTrade")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
self.receivers[#self.receivers + 1] = activator
|
||||
|
||||
if (self.messages[VENDOR_WELCOME]) then
|
||||
activator:ChatPrint(self:GetDisplayName()..": "..self.messages[VENDOR_WELCOME])
|
||||
end
|
||||
|
||||
local items = {}
|
||||
|
||||
-- Only send what is needed.
|
||||
for k, v in pairs(self.items) do
|
||||
if (!table.IsEmpty(v) and (CAMI.PlayerHasAccess(activator, "Helix - Manage Vendors", nil) or v[VENDOR_MODE])) then
|
||||
items[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
self.scale = self.scale or 0.5
|
||||
|
||||
activator.ixVendor = self
|
||||
|
||||
-- force sync to prevent outdated inventories while buying/selling
|
||||
if (character) then
|
||||
character:GetInventory():Sync(activator, true)
|
||||
end
|
||||
|
||||
net.Start("ixVendorOpen")
|
||||
net.WriteEntity(self)
|
||||
net.WriteUInt(self.money or 0, 16)
|
||||
net.WriteTable(items)
|
||||
net.WriteFloat(self.scale or 0.5)
|
||||
net.Send(activator)
|
||||
|
||||
ix.log.Add(activator, "vendorUse", self:GetDisplayName())
|
||||
end
|
||||
|
||||
function ENT:SetMoney(value)
|
||||
self.money = value
|
||||
|
||||
net.Start("ixVendorMoney")
|
||||
net.WriteUInt(value and value or -1, 16)
|
||||
net.Send(self.receivers)
|
||||
end
|
||||
|
||||
function ENT:GiveMoney(value)
|
||||
if (self.money) then
|
||||
self:SetMoney(self:GetMoney() + value)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:TakeMoney(value)
|
||||
if (self.money) then
|
||||
self:GiveMoney(-value)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:SetStock(uniqueID, value)
|
||||
if (!self.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
return
|
||||
end
|
||||
|
||||
self.items[uniqueID] = self.items[uniqueID] or {}
|
||||
self.items[uniqueID][VENDOR_STOCK] = math.min(value, self.items[uniqueID][VENDOR_MAXSTOCK])
|
||||
|
||||
net.Start("ixVendorStock")
|
||||
net.WriteString(uniqueID)
|
||||
net.WriteUInt(value, 16)
|
||||
net.Send(self.receivers)
|
||||
end
|
||||
|
||||
function ENT:AddStock(uniqueID, value)
|
||||
if (!self.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
return
|
||||
end
|
||||
|
||||
self:SetStock(uniqueID, self:GetStock(uniqueID) + (value or 1))
|
||||
end
|
||||
|
||||
function ENT:TakeStock(uniqueID, value)
|
||||
if (!self.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
return
|
||||
end
|
||||
|
||||
self:AddStock(uniqueID, -(value or 1))
|
||||
end
|
||||
else
|
||||
function ENT:CreateBubble()
|
||||
self.bubble = ClientsideModel("models/extras/info_speech.mdl", RENDERGROUP_OPAQUE)
|
||||
self.bubble:SetPos(self:GetPos() + Vector(0, 0, 84))
|
||||
self.bubble:SetModelScale(0.6, 0)
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
local bubble = self.bubble
|
||||
|
||||
if (IsValid(bubble)) then
|
||||
local realTime = RealTime()
|
||||
|
||||
bubble:SetRenderOrigin(self:GetPos() + Vector(0, 0, 84 + math.sin(realTime * 3) * 0.05))
|
||||
bubble:SetRenderAngles(Angle(0, realTime * 100, 0))
|
||||
end
|
||||
|
||||
self:DrawModel()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
local noBubble = self:GetNoBubble()
|
||||
|
||||
if (IsValid(self.bubble) and noBubble) then
|
||||
self.bubble:Remove()
|
||||
elseif (!IsValid(self.bubble) and !noBubble) then
|
||||
self:CreateBubble()
|
||||
end
|
||||
|
||||
if ((self.nextAnimCheck or 0) < CurTime()) then
|
||||
self:SetAnim()
|
||||
self.nextAnimCheck = CurTime() + 60
|
||||
end
|
||||
|
||||
self:SetNextClientThink(CurTime() + 0.25)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if (IsValid(self.bubble)) then
|
||||
self.bubble:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
ENT.PopulateEntityInfo = true
|
||||
|
||||
function ENT:OnPopulateEntityInfo(container)
|
||||
local name = container:AddRow("name")
|
||||
name:SetImportant()
|
||||
name:SetText(self:GetDisplayName())
|
||||
name:SizeToContents()
|
||||
|
||||
local descriptionText = self:GetDescription()
|
||||
|
||||
if (descriptionText != "") then
|
||||
local description = container:AddRow("description")
|
||||
description:SetText(self:GetDescription())
|
||||
description:SizeToContents()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:GetMoney()
|
||||
return self.money
|
||||
end
|
||||
711
garrysmod/gamemodes/helix/plugins/vendor/sh_plugin.lua
vendored
Normal file
711
garrysmod/gamemodes/helix/plugins/vendor/sh_plugin.lua
vendored
Normal file
@@ -0,0 +1,711 @@
|
||||
|
||||
-- luacheck: globals VENDOR_BUY VENDOR_SELL VENDOR_BOTH VENDOR_WELCOME VENDOR_LEAVE VENDOR_NOTRADE VENDOR_PRICE
|
||||
-- luacheck: globals VENDOR_STOCK VENDOR_MODE VENDOR_MAXSTOCK VENDOR_SELLANDBUY VENDOR_SELLONLY VENDOR_BUYONLY VENDOR_TEXT
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Vendors"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "Adds NPC vendors that can sell things."
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Vendors",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
VENDOR_BUY = 1
|
||||
VENDOR_SELL = 2
|
||||
VENDOR_BOTH = 3
|
||||
|
||||
-- Keys for vendor messages.
|
||||
VENDOR_WELCOME = 1
|
||||
VENDOR_LEAVE = 2
|
||||
VENDOR_NOTRADE = 3
|
||||
|
||||
-- Keys for item information.
|
||||
VENDOR_PRICE = 1
|
||||
VENDOR_STOCK = 2
|
||||
VENDOR_MODE = 3
|
||||
VENDOR_MAXSTOCK = 4
|
||||
|
||||
-- Sell and buy the item.
|
||||
VENDOR_SELLANDBUY = 1
|
||||
-- Only sell the item to the player.
|
||||
VENDOR_SELLONLY = 2
|
||||
-- Only buy the item from the player.
|
||||
VENDOR_BUYONLY = 3
|
||||
|
||||
if (SERVER) then
|
||||
util.AddNetworkString("ixVendorOpen")
|
||||
util.AddNetworkString("ixVendorClose")
|
||||
util.AddNetworkString("ixVendorTrade")
|
||||
|
||||
util.AddNetworkString("ixVendorEdit")
|
||||
util.AddNetworkString("ixVendorEditFinish")
|
||||
util.AddNetworkString("ixVendorEditor")
|
||||
util.AddNetworkString("ixVendorMoney")
|
||||
util.AddNetworkString("ixVendorStock")
|
||||
util.AddNetworkString("ixVendorAddItem")
|
||||
|
||||
function PLUGIN:SaveData()
|
||||
local data = {}
|
||||
|
||||
for _, entity in ipairs(ents.FindByClass("ix_vendor")) do
|
||||
local bodygroups = {}
|
||||
|
||||
for _, v in ipairs(entity:GetBodyGroups() or {}) do
|
||||
bodygroups[v.id] = entity:GetBodygroup(v.id)
|
||||
end
|
||||
|
||||
data[#data + 1] = {
|
||||
name = entity:GetDisplayName(),
|
||||
description = entity:GetDescription(),
|
||||
pos = entity:GetPos(),
|
||||
angles = entity:GetAngles(),
|
||||
model = entity:GetModel(),
|
||||
skin = entity:GetSkin(),
|
||||
bodygroups = bodygroups,
|
||||
bubble = entity:GetNoBubble(),
|
||||
items = entity.items,
|
||||
factions = entity.factions,
|
||||
classes = entity.classes,
|
||||
money = entity.money,
|
||||
scale = entity.scale
|
||||
}
|
||||
end
|
||||
|
||||
self:SetData(data)
|
||||
end
|
||||
|
||||
function PLUGIN:LoadData()
|
||||
for _, v in ipairs(self:GetData() or {}) do
|
||||
local entity = ents.Create("ix_vendor")
|
||||
entity:SetPos(v.pos)
|
||||
entity:SetAngles(v.angles)
|
||||
entity:Spawn()
|
||||
|
||||
entity:SetModel(v.model)
|
||||
entity:SetSkin(v.skin or 0)
|
||||
entity:InitPhysObj()
|
||||
|
||||
entity:SetNoBubble(v.bubble)
|
||||
entity:SetDisplayName(v.name)
|
||||
entity:SetDescription(v.description)
|
||||
|
||||
for id, bodygroup in pairs(v.bodygroups or {}) do
|
||||
entity:SetBodygroup(id, bodygroup)
|
||||
end
|
||||
|
||||
local items = {}
|
||||
|
||||
for uniqueID, data in pairs(v.items) do
|
||||
items[tostring(uniqueID)] = data
|
||||
end
|
||||
|
||||
entity.items = items
|
||||
entity.factions = v.factions or {}
|
||||
entity.classes = v.classes or {}
|
||||
entity.money = v.money
|
||||
entity.scale = v.scale or 0.5
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CanVendorSellItem(client, vendor, itemID)
|
||||
local tradeData = vendor.items[itemID]
|
||||
local char = client:GetCharacter()
|
||||
|
||||
if (!tradeData or !char) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!char:HasMoney(tradeData[1] or 0)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
ix.log.AddType("vendorUse", function(client, ...)
|
||||
local arg = {...}
|
||||
return string.format("%s used the '%s' vendor.", client:Name(), arg[1])
|
||||
end)
|
||||
|
||||
ix.log.AddType("vendorBuy", function(client, ...)
|
||||
local arg = {...}
|
||||
|
||||
return string.format("%s purchased a '%s' from the '%s' vendor for %s.", client:Name(), arg[1], arg[2], arg[3])
|
||||
end)
|
||||
|
||||
ix.log.AddType("vendorSell", function(client, ...)
|
||||
local arg = {...}
|
||||
|
||||
return string.format("%s sold a '%s' to the '%s' vendor for %s.", client:Name(), arg[1], arg[2], arg[3])
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorClose", function(length, client)
|
||||
local entity = client.ixVendor
|
||||
|
||||
if (IsValid(entity)) then
|
||||
for k, v in ipairs(entity.receivers) do
|
||||
if (v == client) then
|
||||
table.remove(entity.receivers, k)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
client.ixVendor = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local function UpdateEditReceivers(receivers, key, value)
|
||||
net.Start("ixVendorEdit")
|
||||
net.WriteString(key)
|
||||
net.WriteType(value)
|
||||
net.Send(receivers)
|
||||
end
|
||||
|
||||
net.Receive("ixVendorEdit", function(length, client)
|
||||
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Vendors", nil)) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = client.ixVendor
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
local key = net.ReadString()
|
||||
local data = net.ReadType()
|
||||
local feedback = true
|
||||
|
||||
if (key == "name") then
|
||||
entity:SetDisplayName(data)
|
||||
elseif (key == "description") then
|
||||
entity:SetDescription(data)
|
||||
elseif (key == "bubble") then
|
||||
entity:SetNoBubble(data)
|
||||
elseif (key == "mode") then
|
||||
local uniqueID = data[1]
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_MODE] = data[2]
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
elseif (key == "price") then
|
||||
local uniqueID = data[1]
|
||||
data[2] = tonumber(data[2])
|
||||
|
||||
if (data[2]) then
|
||||
data[2] = math.Round(data[2])
|
||||
end
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_PRICE] = data[2]
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
|
||||
data = uniqueID
|
||||
elseif (key == "stockDisable") then
|
||||
local uniqueID = data[1]
|
||||
|
||||
entity.items[data] = entity.items[uniqueID] or {}
|
||||
entity.items[data][VENDOR_MAXSTOCK] = nil
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
elseif (key == "stockMax") then
|
||||
local uniqueID = data[1]
|
||||
data[2] = math.max(math.Round(tonumber(data[2]) or 1), 1)
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]
|
||||
entity.items[uniqueID][VENDOR_STOCK] = math.Clamp(entity.items[uniqueID][VENDOR_STOCK] or data[2], 1, data[2])
|
||||
|
||||
data[3] = entity.items[uniqueID][VENDOR_STOCK]
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
|
||||
data = uniqueID
|
||||
elseif (key == "stock") then
|
||||
local uniqueID = data[1]
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
|
||||
if (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
data[2] = math.max(math.Round(tonumber(data[2]) or 0), 0)
|
||||
entity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]
|
||||
end
|
||||
|
||||
data[2] = math.Clamp(math.Round(tonumber(data[2]) or 0), 0, entity.items[uniqueID][VENDOR_MAXSTOCK])
|
||||
entity.items[uniqueID][VENDOR_STOCK] = data[2]
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
|
||||
data = uniqueID
|
||||
elseif (key == "faction") then
|
||||
local faction = ix.faction.teams[data]
|
||||
|
||||
if (faction) then
|
||||
entity.factions[data] = !entity.factions[data]
|
||||
|
||||
if (!entity.factions[data]) then
|
||||
entity.factions[data] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local uniqueID = data
|
||||
data = {uniqueID, entity.factions[uniqueID]}
|
||||
elseif (key == "class") then
|
||||
local class
|
||||
|
||||
for _, v in ipairs(ix.class.list) do
|
||||
if (v.uniqueID == data) then
|
||||
class = v
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if (class) then
|
||||
entity.classes[data] = !entity.classes[data]
|
||||
|
||||
if (!entity.classes[data]) then
|
||||
entity.classes[data] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local uniqueID = data
|
||||
data = {uniqueID, entity.classes[uniqueID]}
|
||||
elseif (key == "model") then
|
||||
entity:SetModel(data)
|
||||
entity:InitPhysObj()
|
||||
entity:SetAnim()
|
||||
elseif (key == "useMoney") then
|
||||
if (entity.money) then
|
||||
entity:SetMoney()
|
||||
else
|
||||
entity:SetMoney(0)
|
||||
end
|
||||
elseif (key == "money") then
|
||||
data = math.Round(math.abs(tonumber(data) or 0))
|
||||
|
||||
entity:SetMoney(data)
|
||||
feedback = false
|
||||
elseif (key == "scale") then
|
||||
data = tonumber(data) or 0.5
|
||||
|
||||
entity.scale = data
|
||||
|
||||
UpdateEditReceivers(entity.receivers, key, data)
|
||||
end
|
||||
|
||||
PLUGIN:SaveData()
|
||||
|
||||
if (feedback) then
|
||||
local receivers = {}
|
||||
|
||||
for _, v in ipairs(entity.receivers) do
|
||||
if (CAMI.PlayerHasAccess(v, "Helix - Manage Vendors", nil)) then
|
||||
receivers[#receivers + 1] = v
|
||||
end
|
||||
end
|
||||
|
||||
net.Start("ixVendorEditFinish")
|
||||
net.WriteString(key)
|
||||
net.WriteType(data)
|
||||
net.Send(receivers)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorTrade", function(length, client)
|
||||
if ((client.ixVendorTry or 0) < CurTime()) then
|
||||
client.ixVendorTry = CurTime() + 0.33
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
local entity = client.ixVendor
|
||||
|
||||
if (!IsValid(entity) or client:GetPos():Distance(entity:GetPos()) > 192) then
|
||||
return
|
||||
end
|
||||
|
||||
if (!entity:CanAccess(client)) then
|
||||
return
|
||||
end
|
||||
|
||||
local uniqueID = net.ReadString()
|
||||
local isSellingToVendor = net.ReadBool()
|
||||
|
||||
if (entity.items[uniqueID] and
|
||||
hook.Run("CanPlayerTradeWithVendor", client, entity, uniqueID, isSellingToVendor) != false) then
|
||||
local price = entity:GetPrice(uniqueID, isSellingToVendor)
|
||||
|
||||
if (isSellingToVendor) then
|
||||
local found = false
|
||||
local name
|
||||
|
||||
if (!entity:HasMoney(price)) then
|
||||
return client:NotifyLocalized("vendorNoMoney")
|
||||
end
|
||||
|
||||
local stock, max = entity:GetStock(uniqueID)
|
||||
|
||||
if (stock and stock >= max) then
|
||||
return client:NotifyLocalized("vendorMaxStock")
|
||||
end
|
||||
|
||||
local invOkay = true
|
||||
|
||||
for k, _ in client:GetCharacter():GetInventory():Iter() do
|
||||
if (k.uniqueID == uniqueID and k:GetID() != 0 and ix.item.instances[k:GetID()] and k:GetData("equip", false) == false) then
|
||||
invOkay = k:Remove()
|
||||
found = true
|
||||
name = L(k.name, client)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if (!found) then
|
||||
return
|
||||
end
|
||||
|
||||
if (!invOkay) then
|
||||
client:GetCharacter():GetInventory():Sync(client, true)
|
||||
return client:NotifyLocalized("tellAdmin", "trd!iid")
|
||||
end
|
||||
|
||||
client:GetCharacter():GiveMoney(price, price == 0)
|
||||
client:NotifyLocalized("businessSell", name, ix.currency.Get(price))
|
||||
entity:TakeMoney(price)
|
||||
entity:AddStock(uniqueID)
|
||||
|
||||
ix.log.Add(client, "vendorSell", name, entity:GetDisplayName(), ix.currency.Get(price))
|
||||
else
|
||||
local stock = entity:GetStock(uniqueID)
|
||||
|
||||
if (stock and stock < 1) then
|
||||
return client:NotifyLocalized("vendorNoStock")
|
||||
end
|
||||
|
||||
if (!client:GetCharacter():HasMoney(price)) then
|
||||
return client:NotifyLocalized("canNotAfford")
|
||||
end
|
||||
|
||||
if !entity:CanSellToPlayer(client, uniqueID) then
|
||||
return false
|
||||
end
|
||||
|
||||
local name = L(ix.item.list[uniqueID].name, client)
|
||||
|
||||
client:GetCharacter():TakeMoney(price, price == 0)
|
||||
client:NotifyLocalized("businessPurchase", name, ix.currency.Get(price))
|
||||
|
||||
entity:GiveMoney(price)
|
||||
|
||||
if (!client:GetCharacter():GetInventory():Add(uniqueID)) then
|
||||
ix.item.Spawn(uniqueID, client)
|
||||
else
|
||||
net.Start("ixVendorAddItem")
|
||||
net.WriteString(uniqueID)
|
||||
net.Send(client)
|
||||
end
|
||||
|
||||
entity:TakeStock(uniqueID)
|
||||
|
||||
ix.log.Add(client, "vendorBuy", name, entity:GetDisplayName(), ix.currency.Get(price))
|
||||
end
|
||||
|
||||
PLUGIN:SaveData()
|
||||
hook.Run("CharacterVendorTraded", client, entity, uniqueID, isSellingToVendor)
|
||||
else
|
||||
client:NotifyLocalized("vendorNoTrade")
|
||||
end
|
||||
end)
|
||||
else
|
||||
VENDOR_TEXT = {}
|
||||
VENDOR_TEXT[VENDOR_SELLANDBUY] = "vendorBoth"
|
||||
VENDOR_TEXT[VENDOR_BUYONLY] = "vendorBuy"
|
||||
VENDOR_TEXT[VENDOR_SELLONLY] = "vendorSell"
|
||||
|
||||
net.Receive("ixVendorOpen", function()
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
entity.money = net.ReadUInt(16)
|
||||
entity.items = net.ReadTable()
|
||||
entity.scale = net.ReadFloat()
|
||||
|
||||
ix.gui.vendor = vgui.Create("ixVendor")
|
||||
ix.gui.vendor:SetReadOnly(false)
|
||||
ix.gui.vendor:Setup(entity)
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorEditor", function()
|
||||
local entity = net.ReadEntity()
|
||||
|
||||
if (!IsValid(entity) or !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage Vendors", nil)) then
|
||||
return
|
||||
end
|
||||
|
||||
entity.money = net.ReadUInt(16)
|
||||
entity.items = net.ReadTable()
|
||||
entity.scale = net.ReadFloat()
|
||||
entity.messages = net.ReadTable()
|
||||
entity.factions = net.ReadTable()
|
||||
entity.classes = net.ReadTable()
|
||||
|
||||
ix.gui.vendor = vgui.Create("ixVendor")
|
||||
ix.gui.vendor:SetReadOnly(true)
|
||||
ix.gui.vendor:Setup(entity)
|
||||
ix.gui.vendorEditor = vgui.Create("ixVendorEditor")
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorEdit", function()
|
||||
local panel = ix.gui.vendor
|
||||
|
||||
if (!IsValid(panel)) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = panel.entity
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
local key = net.ReadString()
|
||||
local data = net.ReadType()
|
||||
|
||||
if (key == "mode") then
|
||||
entity.items[data[1]] = entity.items[data[1]] or {}
|
||||
entity.items[data[1]][VENDOR_MODE] = data[2]
|
||||
|
||||
if (!data[2]) then
|
||||
panel:removeItem(data[1])
|
||||
elseif (data[2] == VENDOR_SELLANDBUY) then
|
||||
panel:addItem(data[1])
|
||||
else
|
||||
panel:addItem(data[1], data[2] == VENDOR_SELLONLY and "selling" or "buying")
|
||||
panel:removeItem(data[1], data[2] == VENDOR_SELLONLY and "buying" or "selling")
|
||||
end
|
||||
elseif (key == "price") then
|
||||
local uniqueID = data[1]
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_PRICE] = tonumber(data[2])
|
||||
elseif (key == "stockDisable") then
|
||||
if (entity.items[data]) then
|
||||
entity.items[data][VENDOR_MAXSTOCK] = nil
|
||||
end
|
||||
elseif (key == "stockMax") then
|
||||
local uniqueID = data[1]
|
||||
local value = data[2]
|
||||
local current = data[3]
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_MAXSTOCK] = value
|
||||
entity.items[uniqueID][VENDOR_STOCK] = current
|
||||
elseif (key == "stock") then
|
||||
local uniqueID = data[1]
|
||||
local value = data[2]
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
|
||||
if (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then
|
||||
entity.items[uniqueID][VENDOR_MAXSTOCK] = value
|
||||
end
|
||||
|
||||
entity.items[uniqueID][VENDOR_STOCK] = value
|
||||
elseif (key == "scale") then
|
||||
entity.scale = data
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorEditFinish", function()
|
||||
local panel = ix.gui.vendor
|
||||
local editor = ix.gui.vendorEditor
|
||||
|
||||
if (!IsValid(panel) or !IsValid(editor)) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = panel.entity
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
local key = net.ReadString()
|
||||
local data = net.ReadType()
|
||||
|
||||
if (key == "name") then
|
||||
editor.name:SetText(data)
|
||||
elseif (key == "description") then
|
||||
editor.description:SetText(data)
|
||||
elseif (key == "bubble") then
|
||||
editor.bubble.noSend = true
|
||||
editor.bubble:SetValue(data and 1 or 0)
|
||||
elseif (key == "mode") then
|
||||
if (data[2] == nil) then
|
||||
editor.lines[data[1]]:SetValue(3, L"none")
|
||||
else
|
||||
editor.lines[data[1]]:SetValue(3, L(VENDOR_TEXT[data[2]]))
|
||||
end
|
||||
elseif (key == "price") then
|
||||
editor.lines[data]:SetValue(4, entity:GetPrice(data))
|
||||
elseif (key == "stockDisable") then
|
||||
editor.lines[data]:SetValue(5, "-")
|
||||
elseif (key == "stockMax" or key == "stock") then
|
||||
local current, max = entity:GetStock(data)
|
||||
|
||||
editor.lines[data]:SetValue(5, current.."/"..max)
|
||||
elseif (key == "faction") then
|
||||
local uniqueID = data[1]
|
||||
local state = data[2]
|
||||
local editPanel = ix.gui.editorFaction
|
||||
|
||||
entity.factions[uniqueID] = state
|
||||
|
||||
if (IsValid(editPanel) and IsValid(editPanel.factions[uniqueID])) then
|
||||
editPanel.factions[uniqueID]:SetChecked(state == true)
|
||||
end
|
||||
elseif (key == "class") then
|
||||
local uniqueID = data[1]
|
||||
local state = data[2]
|
||||
local editPanel = ix.gui.editorFaction
|
||||
|
||||
entity.classes[uniqueID] = state
|
||||
|
||||
if (IsValid(editPanel) and IsValid(editPanel.classes[uniqueID])) then
|
||||
editPanel.classes[uniqueID]:SetChecked(state == true)
|
||||
end
|
||||
elseif (key == "model") then
|
||||
editor.model:SetText(entity:GetModel())
|
||||
elseif (key == "scale") then
|
||||
editor.sellScale.noSend = true
|
||||
editor.sellScale:SetValue(data)
|
||||
end
|
||||
|
||||
surface.PlaySound("buttons/button14.wav")
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorMoney", function()
|
||||
local panel = ix.gui.vendor
|
||||
|
||||
if (!IsValid(panel)) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = panel.entity
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
local value = net.ReadUInt(16)
|
||||
value = value != -1 and value or nil
|
||||
|
||||
entity.money = value
|
||||
|
||||
local editor = ix.gui.vendorEditor
|
||||
|
||||
if (IsValid(editor)) then
|
||||
local useMoney = tonumber(value) != nil
|
||||
|
||||
editor.money:SetDisabled(!useMoney)
|
||||
editor.money:SetEnabled(useMoney)
|
||||
editor.money:SetText(useMoney and value or "∞")
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorStock", function()
|
||||
local panel = ix.gui.vendor
|
||||
|
||||
if (!IsValid(panel)) then
|
||||
return
|
||||
end
|
||||
|
||||
local entity = panel.entity
|
||||
|
||||
if (!IsValid(entity)) then
|
||||
return
|
||||
end
|
||||
|
||||
local uniqueID = net.ReadString()
|
||||
local amount = net.ReadUInt(16)
|
||||
|
||||
entity.items[uniqueID] = entity.items[uniqueID] or {}
|
||||
entity.items[uniqueID][VENDOR_STOCK] = amount
|
||||
|
||||
local editor = ix.gui.vendorEditor
|
||||
|
||||
if (IsValid(editor)) then
|
||||
local _, max = entity:GetStock(uniqueID)
|
||||
|
||||
editor.lines[uniqueID]:SetValue(4, amount .. "/" .. max)
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("ixVendorAddItem", function()
|
||||
local uniqueID = net.ReadString()
|
||||
|
||||
if (IsValid(ix.gui.vendor)) then
|
||||
ix.gui.vendor:addItem(uniqueID, "buying")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
properties.Add("vendor_edit", {
|
||||
MenuLabel = "Edit Vendor",
|
||||
Order = 999,
|
||||
MenuIcon = "icon16/user_edit.png",
|
||||
|
||||
Filter = function(self, entity, client)
|
||||
if (!IsValid(entity)) then return false end
|
||||
if (entity:GetClass() != "ix_vendor") then return false end
|
||||
if (!gamemode.Call( "CanProperty", client, "vendor_edit", entity)) then return false end
|
||||
|
||||
return CAMI.PlayerHasAccess(client, "Helix - Manage Vendors", nil)
|
||||
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
|
||||
|
||||
entity.receivers[#entity.receivers + 1] = client
|
||||
|
||||
local itemsTable = {}
|
||||
|
||||
for k, v in pairs(entity.items) do
|
||||
if (!table.IsEmpty(v)) then
|
||||
itemsTable[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
client.ixVendor = entity
|
||||
|
||||
net.Start("ixVendorEditor")
|
||||
net.WriteEntity(entity)
|
||||
net.WriteUInt(entity.money or 0, 16)
|
||||
net.WriteTable(itemsTable)
|
||||
net.WriteFloat(entity.scale or 0.5)
|
||||
net.WriteTable(entity.messages)
|
||||
net.WriteTable(entity.factions)
|
||||
net.WriteTable(entity.classes)
|
||||
net.Send(client)
|
||||
end
|
||||
})
|
||||
209
garrysmod/gamemodes/helix/plugins/wepselect.lua
Normal file
209
garrysmod/gamemodes/helix/plugins/wepselect.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
|
||||
PLUGIN.name = "Weapon Select"
|
||||
PLUGIN.author = "Chessnut"
|
||||
PLUGIN.description = "A replacement for the default weapon selection."
|
||||
|
||||
if (CLIENT) then
|
||||
PLUGIN.index = PLUGIN.index or 1
|
||||
PLUGIN.deltaIndex = PLUGIN.deltaIndex or PLUGIN.index
|
||||
PLUGIN.infoAlpha = PLUGIN.infoAlpha or 0
|
||||
PLUGIN.alpha = PLUGIN.alpha or 0
|
||||
PLUGIN.alphaDelta = PLUGIN.alphaDelta or PLUGIN.alpha
|
||||
PLUGIN.fadeTime = PLUGIN.fadeTime or 0
|
||||
|
||||
local matrixScale = Vector(1, 1, 0)
|
||||
|
||||
function PLUGIN:LoadFonts(font, genericFont)
|
||||
surface.CreateFont("ixWeaponSelectFont", {
|
||||
font = font,
|
||||
size = ScreenScale(16),
|
||||
extended = true,
|
||||
weight = 1000
|
||||
})
|
||||
end
|
||||
|
||||
function PLUGIN:HUDShouldDraw(name)
|
||||
if (name == "CHudWeaponSelection") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:HUDPaint()
|
||||
local frameTime = FrameTime()
|
||||
|
||||
self.alphaDelta = Lerp(frameTime * 10, self.alphaDelta, self.alpha)
|
||||
|
||||
local fraction = self.alphaDelta
|
||||
|
||||
if (fraction > 0.01) then
|
||||
local x, y = ScrW() * 0.5, ScrH() * 0.5
|
||||
local spacing = math.pi * 0.85
|
||||
local radius = 240 * self.alphaDelta
|
||||
local shiftX = ScrW() * .02
|
||||
|
||||
self.deltaIndex = Lerp(frameTime * 12, self.deltaIndex, self.index)
|
||||
|
||||
local weapons = LocalPlayer():GetWeapons()
|
||||
local index = self.deltaIndex
|
||||
|
||||
if (!weapons[self.index]) then
|
||||
self.index = #weapons
|
||||
end
|
||||
|
||||
for i = 1, #weapons do
|
||||
local theta = (i - index) * 0.1
|
||||
local color = ColorAlpha(
|
||||
i == self.index and ix.config.Get("color") or color_white,
|
||||
(255 - math.abs(theta * 3) * 255) * fraction
|
||||
)
|
||||
|
||||
local lastY = 0
|
||||
|
||||
if (self.markup and (i < self.index or i == 1)) then
|
||||
if (self.index != 1) then
|
||||
local _, h = self.markup:Size()
|
||||
lastY = h * fraction
|
||||
end
|
||||
|
||||
if (i == 1 or i == self.index - 1) then
|
||||
self.infoAlpha = Lerp(frameTime * 3, self.infoAlpha, 255)
|
||||
self.markup:Draw(x + 6 + shiftX, y + 30, 0, 0, self.infoAlpha * fraction)
|
||||
end
|
||||
end
|
||||
|
||||
surface.SetFont("ixWeaponSelectFont")
|
||||
local weaponName = language.GetPhrase(weapons[i]:GetPrintName()):utf8upper()
|
||||
local _, ty = surface.GetTextSize(weaponName)
|
||||
local scale = 1 - math.abs(theta * 2)
|
||||
|
||||
local matrix = Matrix()
|
||||
matrix:Translate(Vector(
|
||||
shiftX + x + math.cos(theta * spacing + math.pi) * radius + radius,
|
||||
y + lastY + math.sin(theta * spacing + math.pi) * radius - ty / 2 ,
|
||||
1))
|
||||
matrix:Scale(matrixScale * scale)
|
||||
|
||||
cam.PushModelMatrix(matrix)
|
||||
ix.util.DrawText(weaponName, 2, ty / 2, color, 0, 1, "ixWeaponSelectFont")
|
||||
cam.PopModelMatrix()
|
||||
end
|
||||
|
||||
if (self.fadeTime < CurTime() and self.alpha > 0) then
|
||||
self.alpha = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnIndexChanged(weapon)
|
||||
self.alpha = 1
|
||||
self.fadeTime = CurTime() + 5
|
||||
self.markup = nil
|
||||
|
||||
if (IsValid(weapon)) then
|
||||
local instructions = weapon.Instructions
|
||||
local text = ""
|
||||
|
||||
if (instructions != nil and instructions:find("%S")) then
|
||||
local color = ix.config.Get("color")
|
||||
text = text .. string.format(
|
||||
"<font=ixItemBoldFont><color=%d,%d,%d>%s</font></color>\n%s\n",
|
||||
color.r, color.g, color.b, L("Instructions"), instructions
|
||||
)
|
||||
end
|
||||
|
||||
if (text != "") then
|
||||
self.markup = markup.Parse("<font=ixItemDescFont>"..text, ScrW() * 0.3)
|
||||
self.infoAlpha = 0
|
||||
end
|
||||
|
||||
local source, pitch = hook.Run("WeaponCycleSound")
|
||||
LocalPlayer():EmitSound(source or "common/talk.wav", 50, pitch or 180)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:PlayerBindPress(client, bind, pressed)
|
||||
bind = bind:lower()
|
||||
|
||||
// Блокировка селектора при изменении обвесов CW 2.0
|
||||
-- if client:Alive() then
|
||||
-- local wep = client:GetActiveWeapon()
|
||||
-- if weapons.IsBasedOn(wep:GetClass(), "cw_base") then
|
||||
-- if wep.dt.State == CW_CUSTOMIZE then return end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
|
||||
if (!pressed or !bind:find("invprev") and !bind:find("invnext")
|
||||
and !bind:find("slot") and !bind:find("attack")) then
|
||||
return
|
||||
end
|
||||
|
||||
local currentWeapon = client:GetActiveWeapon()
|
||||
local bValid = IsValid(currentWeapon)
|
||||
local bTool
|
||||
|
||||
if (client:InVehicle() or (bValid and currentWeapon:GetClass() == "weapon_physgun" and client:KeyDown(IN_ATTACK))) then
|
||||
return
|
||||
end
|
||||
|
||||
if (bValid and currentWeapon:GetClass() == "gmod_tool") then
|
||||
local tool = client:GetTool()
|
||||
bTool = tool and (tool.Scroll != nil)
|
||||
end
|
||||
|
||||
local weapons = client:GetWeapons()
|
||||
|
||||
if (bind:find("invprev") and !bTool) then
|
||||
local oldIndex = self.index
|
||||
self.index = math.min(self.index + 1, #weapons)
|
||||
|
||||
if (self.alpha == 0 or oldIndex != self.index) then
|
||||
self:OnIndexChanged(weapons[self.index])
|
||||
end
|
||||
|
||||
return true
|
||||
elseif (bind:find("invnext") and !bTool) then
|
||||
local oldIndex = self.index
|
||||
self.index = math.max(self.index - 1, 1)
|
||||
|
||||
if (self.alpha == 0 or oldIndex != self.index) then
|
||||
self:OnIndexChanged(weapons[self.index])
|
||||
end
|
||||
|
||||
return true
|
||||
elseif (bind:find("slot")) then
|
||||
self.index = math.Clamp(tonumber(bind:match("slot(%d)")) or 1, 1, #weapons)
|
||||
self:OnIndexChanged(weapons[self.index])
|
||||
|
||||
return true
|
||||
elseif (bind:find("attack") and self.alpha > 0) then
|
||||
local weapon = weapons[self.index]
|
||||
|
||||
if (IsValid(weapon)) then
|
||||
LocalPlayer():EmitSound(hook.Run("WeaponSelectSound", weapon) or "HL2Player.Use")
|
||||
|
||||
input.SelectWeapon(weapon)
|
||||
self.alpha = 0
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:Think()
|
||||
local client = LocalPlayer()
|
||||
if (!IsValid(client) or !client:Alive()) then
|
||||
self.alpha = 0
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ScoreboardShow()
|
||||
self.alpha = 0
|
||||
end
|
||||
|
||||
function PLUGIN:ShouldPopulateEntityInfo(entity)
|
||||
if (self.alpha > 0) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user