Initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
Do not add, edit or remove anything from this folder.
|
||||
|
||||
Use the DarkRPMod addon instead
|
||||
https://github.com/FPtje/DarkRPModification
|
||||
17
gamemodes/darkrp/gamemode/modules/afk/cl_afk.lua
Normal file
17
gamemodes/darkrp/gamemode/modules/afk/cl_afk.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
local TextColor = Color(GetConVar("Healthforeground1"):GetFloat(), GetConVar("Healthforeground2"):GetFloat(), GetConVar("Healthforeground3"):GetFloat(), GetConVar("Healthforeground4"):GetFloat())
|
||||
|
||||
local function AFKHUDPaint()
|
||||
if not LocalPlayer():getDarkRPVar("AFK") then return end
|
||||
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("afk_mode"), "DarkRPHUD2", ScrW() / 2, (ScrH() / 2) - 100, TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("salary_frozen"), "DarkRPHUD2", ScrW() / 2, (ScrH() / 2) - 60, TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
|
||||
if not LocalPlayer():getDarkRPVar("AFKDemoted") then
|
||||
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("no_auto_demote"), "DarkRPHUD2", ScrW() / 2, (ScrH() / 2) - 20, TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
else
|
||||
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("youre_afk_demoted"), "DarkRPHUD2", ScrW() / 2, (ScrH() / 2) - 20, TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("afk_cmd_to_exit"), "DarkRPHUD2", ScrW() / 2, (ScrH() / 2) + 20, TextColor, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
hook.Add("HUDPaint", "AFK_HUD", AFKHUDPaint)
|
||||
8
gamemodes/darkrp/gamemode/modules/afk/sh_commands.lua
Normal file
8
gamemodes/darkrp/gamemode/modules/afk/sh_commands.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
DarkRP.registerDarkRPVar("AFK", net.WriteBit, fn.Compose{tobool, net.ReadBit})
|
||||
DarkRP.registerDarkRPVar("AFKDemoted", net.WriteBit, fn.Compose{tobool, net.ReadBit})
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "afk",
|
||||
description = "Go AFK",
|
||||
delay = 1.5
|
||||
}
|
||||
129
gamemodes/darkrp/gamemode/modules/afk/sv_afk.lua
Normal file
129
gamemodes/darkrp/gamemode/modules/afk/sv_afk.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
-- How to use:
|
||||
-- If a player uses /afk, they go into AFK mode, they will not be autodemoted and their salary is set to $0 (you can still be killed/vote demoted though!).
|
||||
-- If a player does not use /afk, and they don't do anything for the demote time specified, they will be automatically demoted to hobo.
|
||||
|
||||
local function AFKDemote(ply)
|
||||
local shouldDemote, demoteTeam, suppressMsg, msg = hook.Call("playerAFKDemoted", nil, ply)
|
||||
demoteTeam = demoteTeam or GAMEMODE.DefaultTeam
|
||||
|
||||
if ply:Team() ~= demoteTeam and shouldDemote ~= false then
|
||||
local rpname = ply:getDarkRPVar("rpname")
|
||||
ply:changeTeam(demoteTeam, true)
|
||||
if not suppressMsg then DarkRP.notifyAll(0, 5, msg or DarkRP.getPhrase("hes_afk_demoted", rpname)) end
|
||||
end
|
||||
ply:setSelfDarkRPVar("AFKDemoted", true)
|
||||
ply:setDarkRPVar("job", "AFK")
|
||||
end
|
||||
|
||||
local function SetAFK(ply)
|
||||
local rpname = ply:getDarkRPVar("rpname")
|
||||
ply:setSelfDarkRPVar("AFK", not ply:getDarkRPVar("AFK"))
|
||||
|
||||
ply.blackScreen = ply:getDarkRPVar("AFK")
|
||||
SendUserMessage("blackScreen", ply, ply:getDarkRPVar("AFK"))
|
||||
|
||||
if ply:getDarkRPVar("AFK") then
|
||||
DarkRP.retrieveSalary(ply, function(amount) ply.OldSalary = amount end)
|
||||
ply.OldJob = ply:getDarkRPVar("job")
|
||||
ply.lastHealth = ply:Health()
|
||||
DarkRP.notifyAll(0, 5, DarkRP.getPhrase("player_now_afk", rpname))
|
||||
|
||||
ply.AFKDemote = math.huge
|
||||
|
||||
ply:KillSilent()
|
||||
ply:Lock()
|
||||
else
|
||||
ply.AFKDemote = CurTime() + GAMEMODE.Config.afkdemotetime
|
||||
DarkRP.notifyAll(1, 5, DarkRP.getPhrase("player_no_longer_afk", rpname))
|
||||
DarkRP.notify(ply, 0, 5, DarkRP.getPhrase("salary_restored"))
|
||||
ply:Spawn()
|
||||
ply:UnLock()
|
||||
|
||||
ply:SetHealth(ply.lastHealth and ply.lastHealth > 0 and ply.lastHealth or 100)
|
||||
ply.lastHealth = nil
|
||||
end
|
||||
|
||||
if not ply.demotedWhileDead then
|
||||
ply:setDarkRPVar("job", ply:getDarkRPVar("AFK") and "AFK" or ply:getDarkRPVar("AFKDemoted") and team.GetName(ply:Team()) or ply.OldJob)
|
||||
ply:setSelfDarkRPVar("salary", ply:getDarkRPVar("AFK") and 0 or ply.OldSalary or 0)
|
||||
end
|
||||
|
||||
hook.Run("playerSetAFK", ply, ply:getDarkRPVar("AFK"))
|
||||
end
|
||||
|
||||
DarkRP.defineChatCommand("afk", function(ply)
|
||||
if ply.DarkRPLastAFK and not ply:getDarkRPVar("AFK") and ply.DarkRPLastAFK > CurTime() - GAMEMODE.Config.AFKDelay then
|
||||
DarkRP.notify(ply, 0, 5, DarkRP.getPhrase("unable_afk_spam_prevention"))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canAFK = hook.Run("canGoAFK", ply, not ply:getDarkRPVar("AFK"))
|
||||
|
||||
if canAFK == false then return "" end
|
||||
|
||||
ply.DarkRPLastAFK = CurTime()
|
||||
SetAFK(ply)
|
||||
|
||||
return ""
|
||||
end)
|
||||
|
||||
local function StartAFKOnPlayer(ply)
|
||||
ply.AFKDemote = CurTime() + GAMEMODE.Config.afkdemotetime
|
||||
end
|
||||
hook.Add("PlayerInitialSpawn", "StartAFKOnPlayer", StartAFKOnPlayer)
|
||||
|
||||
local function AFKTimer(ply, key)
|
||||
ply.AFKDemote = CurTime() + GAMEMODE.Config.afkdemotetime
|
||||
if ply:getDarkRPVar("AFKDemoted") then
|
||||
ply:setDarkRPVar("job", team.GetName(ply:Team()))
|
||||
timer.Simple(3, function() if IsValid(ply) then ply:setSelfDarkRPVar("AFKDemoted", nil) end end)
|
||||
end
|
||||
end
|
||||
hook.Add("KeyPress", "DarkRPKeyReleasedCheck", AFKTimer)
|
||||
|
||||
local function KillAFKTimer()
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply.AFKDemote and CurTime() > ply.AFKDemote and not ply:getDarkRPVar("AFK") and not ply:IsBot() then
|
||||
SetAFK(ply)
|
||||
AFKDemote(ply)
|
||||
ply.AFKDemote = math.huge
|
||||
end
|
||||
end
|
||||
end
|
||||
timer.Create("DarkRPKeyPressedCheck", 1, 0, function()
|
||||
KillAFKTimer()
|
||||
end)
|
||||
|
||||
local function BlockAFKTeamChange(ply, t, force)
|
||||
if ply:getDarkRPVar("AFK") and (not force or t ~= GAMEMODE.DefaultTeam) then
|
||||
local TEAM = RPExtraTeams[t]
|
||||
if TEAM then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", GAMEMODE.Config.chatCommandPrefix .. TEAM.command, DarkRP.getPhrase("afk_mode"))) end
|
||||
return false
|
||||
end
|
||||
end
|
||||
hook.Add("playerCanChangeTeam", "AFKCanChangeTeam", BlockAFKTeamChange)
|
||||
|
||||
-- Freeze AFK player's salary
|
||||
hook.Add("playerGetSalary", "AFKGetSalary", function(ply, amount)
|
||||
if ply:getDarkRPVar("AFK") then
|
||||
return true, "", 0
|
||||
end
|
||||
end)
|
||||
|
||||
-- For when a player's team is changed by force
|
||||
hook.Add("OnPlayerChangedTeam", "AFKCanChangeTeam", function(ply)
|
||||
if not ply:getDarkRPVar("AFK") then return end
|
||||
|
||||
ply.OldSalary = ply:getDarkRPVar("salary")
|
||||
ply.OldJob = nil
|
||||
ply:setSelfDarkRPVar("salary", 0)
|
||||
end)
|
||||
|
||||
local function unAFKPlayer(ply)
|
||||
if ply:getDarkRPVar("AFK") then
|
||||
SetAFK(ply)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("playerArrested", "DarkRP_AFK", unAFKPlayer)
|
||||
hook.Add("playerUnArrested", "DarkRP_AFK", unAFKPlayer)
|
||||
76
gamemodes/darkrp/gamemode/modules/afk/sv_interface.lua
Normal file
76
gamemodes/darkrp/gamemode/modules/afk/sv_interface.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
DarkRP.hookStub{
|
||||
name = "playerAFKDemoted",
|
||||
description = "When a player is demoted for being AFK.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player being demoted.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "shouldDemote",
|
||||
description = "Prevent the player from being actually demoted.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "team",
|
||||
description = "The team the player is to be demoted to (shouldDemote must be true.)",
|
||||
type = "number"
|
||||
},
|
||||
{
|
||||
name = "suppressMessage",
|
||||
description = "Suppress the demote message.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "demoteMessage",
|
||||
description = "Replacement of the demote message text.",
|
||||
type = "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerSetAFK",
|
||||
description = "When a player is set to AFK or returns from AFK.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "afk",
|
||||
description = "True when the player starts being AFK, false when the player stops being AFK.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canGoAFK",
|
||||
description = "When a player can MANUALLY start being AFK by entering the chat command. Note: this hook does NOT get called when a player is set to AFK automatically! That hook will not be added, because I don't want asshole server owners to make AFK rules not apply to admins.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "afk",
|
||||
description = "True when the player starts being AFK, false when the player stops being AFK.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "canGoAFK",
|
||||
description = "Whether the player is allowed to go AFK",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
152
gamemodes/darkrp/gamemode/modules/animations/sh_animations.lua
Normal file
152
gamemodes/darkrp/gamemode/modules/animations/sh_animations.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
local Anims = {}
|
||||
|
||||
-- Load animations after the languages for translation purposes
|
||||
hook.Add("loadCustomDarkRPItems", "loadAnimations", function()
|
||||
Anims[ACT_GMOD_GESTURE_BOW] = DarkRP.getPhrase("bow")
|
||||
Anims[ACT_GMOD_TAUNT_MUSCLE] = DarkRP.getPhrase("sexy_dance")
|
||||
Anims[ACT_GMOD_GESTURE_BECON] = DarkRP.getPhrase("follow_me")
|
||||
Anims[ACT_GMOD_TAUNT_LAUGH] = DarkRP.getPhrase("laugh")
|
||||
Anims[ACT_GMOD_TAUNT_PERSISTENCE] = DarkRP.getPhrase("lion_pose")
|
||||
Anims[ACT_GMOD_GESTURE_DISAGREE] = DarkRP.getPhrase("nonverbal_no")
|
||||
Anims[ACT_GMOD_GESTURE_AGREE] = DarkRP.getPhrase("thumbs_up")
|
||||
Anims[ACT_GMOD_GESTURE_WAVE] = DarkRP.getPhrase("wave")
|
||||
Anims[ACT_GMOD_TAUNT_DANCE] = DarkRP.getPhrase("dance")
|
||||
end)
|
||||
|
||||
function DarkRP.addPlayerGesture(anim, text)
|
||||
if not anim then DarkRP.error("Argument #1 of DarkRP.addPlayerGesture (animation/gesture) does not exist.", 2) end
|
||||
if not text then DarkRP.error("Argument #2 of DarkRP.addPlayerGesture (text) does not exist.", 2) end
|
||||
|
||||
Anims[anim] = text
|
||||
end
|
||||
|
||||
function DarkRP.removePlayerGesture(anim)
|
||||
if not anim then DarkRP.error("Argument #1 of DarkRP.removePlayerGesture (animation/gesture) does not exist.", 2) end
|
||||
|
||||
Anims[anim] = nil
|
||||
end
|
||||
|
||||
local function physGunCheck(ply)
|
||||
local hookName = "darkrp_anim_physgun_" .. ply:EntIndex()
|
||||
hook.Add("Think", hookName, function()
|
||||
if IsValid(ply) and
|
||||
ply:Alive() and
|
||||
ply:GetActiveWeapon():IsValid() and
|
||||
ply:GetActiveWeapon():GetClass() == "weapon_physgun" and
|
||||
ply:KeyDown(IN_ATTACK) and
|
||||
(ply:GetAllowWeaponsInVehicle() or not ply:InVehicle()) then
|
||||
local ent = ply:GetEyeTrace().Entity
|
||||
if IsValid(ent) and ent:IsPlayer() and not ply.SaidHi then
|
||||
ply.SaidHi = true
|
||||
ply:DoAnimationEvent(ACT_SIGNAL_GROUP)
|
||||
end
|
||||
else
|
||||
if IsValid(ply) then
|
||||
ply.SaidHi = nil
|
||||
end
|
||||
hook.Remove("Think", hookName)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("KeyPress", "darkrp_animations", function(ply, key)
|
||||
if key == IN_ATTACK then
|
||||
local weapon = ply:GetActiveWeapon()
|
||||
|
||||
if weapon:IsValid() then
|
||||
local class = weapon:GetClass()
|
||||
|
||||
-- Saying hi/hello to a player
|
||||
if class == "weapon_physgun" then
|
||||
physGunCheck(ply)
|
||||
|
||||
-- Hobo throwing poop!
|
||||
elseif class == "weapon_bugbait" then
|
||||
local Team = ply:Team()
|
||||
if RPExtraTeams[Team] and RPExtraTeams[Team].hobo then
|
||||
ply:DoAnimationEvent(ACT_GMOD_GESTURE_ITEM_THROW)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if SERVER then
|
||||
local function CustomAnim(ply, cmd, args)
|
||||
if ply:EntIndex() == 0 then return end
|
||||
local Gesture = tonumber(args[1] or 0)
|
||||
if not Anims[Gesture] then return end
|
||||
|
||||
local RP = RecipientFilter()
|
||||
RP:AddAllPlayers()
|
||||
|
||||
umsg.Start("_DarkRP_CustomAnim", RP)
|
||||
umsg.Entity(ply)
|
||||
umsg.Short(Gesture)
|
||||
umsg.End()
|
||||
end
|
||||
concommand.Add("_DarkRP_DoAnimation", CustomAnim)
|
||||
return
|
||||
end
|
||||
|
||||
local function KeysAnims(um)
|
||||
local ply = um:ReadEntity()
|
||||
local act = um:ReadString()
|
||||
|
||||
if not IsValid(ply) then return end
|
||||
ply:AnimRestartGesture(GESTURE_SLOT_CUSTOM, act == "usekeys" and ACT_GMOD_GESTURE_ITEM_PLACE or ACT_HL2MP_GESTURE_RANGE_ATTACK_FIST, true)
|
||||
end
|
||||
usermessage.Hook("anim_keys", KeysAnims)
|
||||
|
||||
local function CustomAnimation(um)
|
||||
local ply = um:ReadEntity()
|
||||
local act = um:ReadShort()
|
||||
|
||||
if not IsValid(ply) then return end
|
||||
ply:AnimRestartGesture(GESTURE_SLOT_CUSTOM, act, true)
|
||||
end
|
||||
usermessage.Hook("_DarkRP_CustomAnim", CustomAnimation)
|
||||
|
||||
local AnimFrame
|
||||
local function AnimationMenu()
|
||||
if AnimFrame then return end
|
||||
|
||||
local Panel = vgui.Create("Panel")
|
||||
Panel:SetPos(0,0)
|
||||
Panel:SetSize(ScrW(), ScrH())
|
||||
function Panel:OnMousePressed()
|
||||
AnimFrame:Close()
|
||||
end
|
||||
|
||||
AnimFrame = AnimFrame or vgui.Create("DFrame", Panel)
|
||||
local Height = table.Count(Anims) * 55 + 32
|
||||
AnimFrame:SetSize(130, Height)
|
||||
AnimFrame:SetPos(ScrW() / 2 + ScrW() * 0.1, ScrH() / 2 - (Height / 2))
|
||||
AnimFrame:SetTitle(DarkRP.getPhrase("custom_animation"))
|
||||
AnimFrame.btnMaxim:SetVisible(false)
|
||||
AnimFrame.btnMinim:SetVisible(false)
|
||||
AnimFrame:SetVisible(true)
|
||||
AnimFrame:MakePopup()
|
||||
AnimFrame:ParentToHUD()
|
||||
|
||||
function AnimFrame:Close()
|
||||
Panel:Remove()
|
||||
AnimFrame:Remove()
|
||||
AnimFrame = nil
|
||||
end
|
||||
|
||||
local i = 0
|
||||
for k, v in SortedPairs(Anims) do
|
||||
i = i + 1
|
||||
local button = vgui.Create("DButton", AnimFrame)
|
||||
button:SetPos(10, (i - 1) * 55 + 30)
|
||||
button:SetSize(110, 50)
|
||||
button:SetText(v)
|
||||
|
||||
button.DoClick = function()
|
||||
RunConsoleCommand("_DarkRP_DoAnimation", k)
|
||||
end
|
||||
end
|
||||
AnimFrame:SetSkin(GAMEMODE.Config.DarkRPSkin)
|
||||
end
|
||||
concommand.Add("_DarkRP_AnimationMenu", AnimationMenu)
|
||||
@@ -0,0 +1,37 @@
|
||||
DarkRP.addPlayerGesture = DarkRP.stub{
|
||||
name = "addPlayerGesture",
|
||||
description = "Add a player gesture to the DarkRP animations menu (the one that opens with the keys weapon.). Note: This function must be called BOTH serverside AND clientside!",
|
||||
parameters = {
|
||||
{
|
||||
name = "anim",
|
||||
description = "The gesture enumeration.",
|
||||
type = "number",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "text",
|
||||
description = "The textual description of the animation. This is what players see on the button in the menu.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.removePlayerGesture = DarkRP.stub{
|
||||
name = "removePlayerGesture",
|
||||
description = "Removes a player gesture from the DarkRP animations menu (the one that opens with the keys weapon.). Note: This function must be called BOTH serverside AND clientside!",
|
||||
parameters = {
|
||||
{
|
||||
name = "anim",
|
||||
description = "The gesture enumeration.",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
30
gamemodes/darkrp/gamemode/modules/base/cl_drawfunctions.lua
Normal file
30
gamemodes/darkrp/gamemode/modules/base/cl_drawfunctions.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
-- concatenate a space to avoid the text being parsed as valve string
|
||||
local function safeText(text)
|
||||
return string.match(text, "^#([a-zA-Z_]+)$") and text .. " " or text
|
||||
end
|
||||
|
||||
DarkRP.deLocalise = safeText
|
||||
|
||||
function draw.DrawNonParsedText(text, font, x, y, color, xAlign)
|
||||
return draw.DrawText(safeText(text), font, x, y, color, xAlign)
|
||||
end
|
||||
|
||||
function draw.DrawNonParsedSimpleText(text, font, x, y, color, xAlign, yAlign)
|
||||
return draw.SimpleText(safeText(text), font, x, y, color, xAlign, yAlign)
|
||||
end
|
||||
|
||||
function draw.DrawNonParsedSimpleTextOutlined(text, font, x, y, color, xAlign, yAlign, outlineWidth, outlineColor)
|
||||
return draw.SimpleTextOutlined(safeText(text), font, x, y, color, xAlign, yAlign, outlineWidth, outlineColor)
|
||||
end
|
||||
|
||||
function surface.DrawNonParsedText(text)
|
||||
return surface.DrawText(safeText(text))
|
||||
end
|
||||
|
||||
function chat.AddNonParsedText(...)
|
||||
local tbl = {...}
|
||||
for i = 2, #tbl, 2 do
|
||||
tbl[i] = safeText(tbl[i])
|
||||
end
|
||||
return chat.AddText(unpack(tbl))
|
||||
end
|
||||
139
gamemodes/darkrp/gamemode/modules/base/cl_entityvars.lua
Normal file
139
gamemodes/darkrp/gamemode/modules/base/cl_entityvars.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
DarkRP.ClientsideDarkRPVars = DarkRP.ClientsideDarkRPVars or {}
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Interface
|
||||
---------------------------------------------------------------------------]]
|
||||
local pmeta = FindMetaTable("Player")
|
||||
-- This function is made local to optimise getDarkRPVar, which is called often
|
||||
-- enough to warrant optimizing. See https://github.com/FPtje/DarkRP/pull/3212
|
||||
local get_user_id = pmeta.UserID
|
||||
function pmeta:getDarkRPVar(var, fallback)
|
||||
local user_id = get_user_id(self)
|
||||
|
||||
-- Special case: when in the EntityRemoved hook, UserID returns -1. In this
|
||||
-- case, hope that we still have a stored userID lying around somewhere.
|
||||
-- See https://github.com/FPtje/DarkRP/pull/3270
|
||||
if user_id == -1 then
|
||||
user_id = self._darkrp_stored_user_id_for_entity_removed_hook
|
||||
end
|
||||
|
||||
local vars = DarkRP.ClientsideDarkRPVars[user_id]
|
||||
if vars == nil then return fallback end
|
||||
|
||||
local results = vars[var]
|
||||
if results == nil then return fallback end
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Retrieve the information of a player var
|
||||
---------------------------------------------------------------------------]]
|
||||
local function RetrievePlayerVar(userID, var, value)
|
||||
local ply = Player(userID)
|
||||
DarkRP.ClientsideDarkRPVars[userID] = DarkRP.ClientsideDarkRPVars[userID] or {}
|
||||
|
||||
hook.Call("DarkRPVarChanged", nil, ply, var, DarkRP.ClientsideDarkRPVars[userID][var], value)
|
||||
DarkRP.ClientsideDarkRPVars[userID][var] = value
|
||||
|
||||
-- Backwards compatibility
|
||||
if IsValid(ply) then
|
||||
ply.DarkRPVars = DarkRP.ClientsideDarkRPVars[userID]
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Retrieve a player var.
|
||||
Read the usermessage and attempt to set the DarkRP var
|
||||
---------------------------------------------------------------------------]]
|
||||
local function doRetrieve()
|
||||
local userID = net.ReadUInt(16)
|
||||
local var, value = DarkRP.readNetDarkRPVar()
|
||||
|
||||
RetrievePlayerVar(userID, var, value)
|
||||
end
|
||||
net.Receive("DarkRP_PlayerVar", doRetrieve)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Retrieve the message to remove a DarkRPVar
|
||||
---------------------------------------------------------------------------]]
|
||||
local function doRetrieveRemoval()
|
||||
local userID = net.ReadUInt(16)
|
||||
local vars = DarkRP.ClientsideDarkRPVars[userID] or {}
|
||||
local var = DarkRP.readNetDarkRPVarRemoval()
|
||||
local ply = Player(userID)
|
||||
|
||||
hook.Call("DarkRPVarChanged", nil, ply, var, vars[var], nil)
|
||||
|
||||
vars[var] = nil
|
||||
end
|
||||
net.Receive("DarkRP_PlayerVarRemoval", doRetrieveRemoval)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Initialize the DarkRPVars at the start of the game
|
||||
---------------------------------------------------------------------------]]
|
||||
local function InitializeDarkRPVars(len)
|
||||
local plyCount = net.ReadUInt(8)
|
||||
|
||||
for i = 1, plyCount, 1 do
|
||||
local userID = net.ReadUInt(16)
|
||||
local varCount = net.ReadUInt(DarkRP.DARKRP_ID_BITS + 2)
|
||||
|
||||
for j = 1, varCount, 1 do
|
||||
local var, value = DarkRP.readNetDarkRPVar()
|
||||
RetrievePlayerVar(userID, var, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
net.Receive("DarkRP_InitializeVars", InitializeDarkRPVars)
|
||||
timer.Simple(0, fp{RunConsoleCommand, "_sendDarkRPvars"})
|
||||
|
||||
net.Receive("DarkRP_DarkRPVarDisconnect", function(len)
|
||||
local userID = net.ReadUInt(16)
|
||||
local ply = Player(userID)
|
||||
|
||||
-- If the player is already gone, then immediately clear the data and move on.
|
||||
if not IsValid(ply) then
|
||||
DarkRP.ClientsideDarkRPVars[userID] = nil
|
||||
return
|
||||
end
|
||||
-- Otherwise, we need to wait until the player is actually removed
|
||||
-- clientside. The net message may come in _much_ earlier than the message
|
||||
-- that the player disconnected and should therefore be removed.
|
||||
local hook_name = "darkrp_remove_darkrp_var_" .. userID
|
||||
|
||||
-- Workaround: the player's user ID is -1 in the EntityRemoved hook. This
|
||||
-- stores the user ID in a separate variable so that it is still accessible.
|
||||
-- See https://github.com/Facepunch/garrysmod-issues/issues/6117
|
||||
--
|
||||
-- This will allow getDarkRPVar to keep working
|
||||
if IsValid(ply) then
|
||||
ply._darkrp_stored_user_id_for_entity_removed_hook = userID
|
||||
end
|
||||
|
||||
hook.Add("EntityRemoved", hook_name, function(ent)
|
||||
if ent ~= ply then return end
|
||||
hook.Remove("EntityRemoved", hook_name)
|
||||
|
||||
-- Placing this in a timer allows for the rest of the hook runners to
|
||||
-- still use the DarkRPVars until the entity is _really_ gone.
|
||||
-- See https://github.com/FPtje/DarkRP/pull/3270
|
||||
timer.Simple(0, function()
|
||||
DarkRP.ClientsideDarkRPVars[userID] = nil
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Request the DarkRPVars when they haven't arrived
|
||||
---------------------------------------------------------------------------]]
|
||||
timer.Create("DarkRPCheckifitcamethrough", 15, 0, function()
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:getDarkRPVar("rpname") then continue end
|
||||
|
||||
RunConsoleCommand("_sendDarkRPvars")
|
||||
return
|
||||
end
|
||||
|
||||
timer.Remove("DarkRPCheckifitcamethrough")
|
||||
end)
|
||||
158
gamemodes/darkrp/gamemode/modules/base/cl_fonts.lua
Normal file
158
gamemodes/darkrp/gamemode/modules/base/cl_fonts.lua
Normal file
@@ -0,0 +1,158 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
The fonts that DarkRP uses
|
||||
---------------------------------------------------------------------------]]
|
||||
local function loadFonts()
|
||||
surface.CreateFont("DarkRPHUD1", {
|
||||
size = 20,
|
||||
weight = 600,
|
||||
antialias = true,
|
||||
shadow = true,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("DarkRPHUD2", {
|
||||
size = 23,
|
||||
weight = 400,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("Roboto20", {
|
||||
size = 20,
|
||||
weight = 600,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("Trebuchet18", {
|
||||
size = 18,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Trebuchet MS",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("Trebuchet20", {
|
||||
size = 20,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Trebuchet MS",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("Trebuchet24", {
|
||||
size = 24,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Trebuchet MS",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("Trebuchet48", {
|
||||
size = 48,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Trebuchet MS",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("TabLarge", {
|
||||
size = 18,
|
||||
weight = 700,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("UiBold", {
|
||||
size = 16,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Verdana",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("HUDNumber5", {
|
||||
size = 30,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Verdana",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardHeader", {
|
||||
size = 32,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardSubtitle", {
|
||||
size = 22,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardPlayerName", {
|
||||
size = 19,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardPlayerName2", {
|
||||
size = 15,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("ScoreboardPlayerNameBig", {
|
||||
size = 22,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Roboto",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("AckBarWriting", {
|
||||
size = 20,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Akbar",
|
||||
extended = true,
|
||||
})
|
||||
|
||||
surface.CreateFont("DarkRP_tipjar", {
|
||||
size = 100,
|
||||
weight = 500,
|
||||
antialias = true,
|
||||
shadow = true,
|
||||
font = "Verdana",
|
||||
extended = true,
|
||||
})
|
||||
end
|
||||
loadFonts()
|
||||
@@ -0,0 +1,84 @@
|
||||
local GUIToggled = false
|
||||
local mouseX, mouseY = ScrW() / 2, ScrH() / 2
|
||||
function GM:ShowSpare1()
|
||||
local jobTable = LocalPlayer():getJobTable()
|
||||
|
||||
-- We need to check for the existance of jobTable here, because in very rare edge cases, the player's team isn't set, when the getJobTable-function is called here.
|
||||
if jobTable and jobTable.ShowSpare1 then
|
||||
return jobTable.ShowSpare1(LocalPlayer())
|
||||
end
|
||||
|
||||
GUIToggled = not GUIToggled
|
||||
|
||||
if GUIToggled then
|
||||
gui.SetMousePos(mouseX, mouseY)
|
||||
else
|
||||
mouseX, mouseY = gui.MousePos()
|
||||
end
|
||||
gui.EnableScreenClicker(GUIToggled)
|
||||
end
|
||||
|
||||
function GM:ShowSpare2()
|
||||
local jobTable = LocalPlayer():getJobTable()
|
||||
|
||||
-- We need to check for the existance of jobTable here, because in very rare edge cases, the player's team isn't set, when the getJobTable-function is called here.
|
||||
if jobTable and jobTable.ShowSpare2 then
|
||||
return jobTable.ShowSpare2(LocalPlayer())
|
||||
end
|
||||
|
||||
-- DarkRP.toggleF4Menu()
|
||||
end
|
||||
|
||||
function GM:PlayerStartVoice(ply)
|
||||
if ply == LocalPlayer() then
|
||||
ply.DRPIsTalking = true
|
||||
return -- Not the original rectangle for yourself! ugh!
|
||||
end
|
||||
self.Sandbox.PlayerStartVoice(self, ply)
|
||||
end
|
||||
|
||||
function GM:PlayerEndVoice(ply)
|
||||
if ply == LocalPlayer() then
|
||||
ply.DRPIsTalking = false
|
||||
return
|
||||
end
|
||||
|
||||
self.Sandbox.PlayerEndVoice(self, ply)
|
||||
end
|
||||
|
||||
function GM:OnPlayerChat()
|
||||
end
|
||||
|
||||
local FKeyBinds = {
|
||||
["gm_showhelp"] = "ShowHelp",
|
||||
["gm_showteam"] = "ShowTeam",
|
||||
["gm_showspare1"] = "ShowSpare1",
|
||||
["gm_showspare2"] = "ShowSpare2"
|
||||
}
|
||||
|
||||
function GM:PlayerBindPress(ply, bind, pressed)
|
||||
self.Sandbox.PlayerBindPress(self, ply, bind, pressed)
|
||||
|
||||
local bnd = string.match(string.lower(bind), "gm_[a-z]+[12]?")
|
||||
if bnd and FKeyBinds[bnd] then
|
||||
hook.Call(FKeyBinds[bnd], GAMEMODE)
|
||||
end
|
||||
|
||||
if not self.Config.deadvoice and not ply:Alive() and string.find(string.lower(bind), "voicerecord") then return true end
|
||||
end
|
||||
|
||||
function GM:InitPostEntity()
|
||||
hook.Call("teamChanged", GAMEMODE, GAMEMODE.DefaultTeam, GAMEMODE.DefaultTeam)
|
||||
end
|
||||
|
||||
function GM:teamChanged(before, after)
|
||||
end
|
||||
|
||||
local function OnChangedTeam(um)
|
||||
local oldTeam, newTeam = um:ReadShort(), um:ReadShort()
|
||||
hook.Call("teamChanged", GAMEMODE, oldTeam, newTeam) -- backwards compatibility
|
||||
hook.Call("OnPlayerChangedTeam", GAMEMODE, LocalPlayer(), oldTeam, newTeam)
|
||||
end
|
||||
usermessage.Hook("OnChangedTeam", OnChangedTeam)
|
||||
|
||||
timer.Simple(0, function() GAMEMODE.ShowTeam = DarkRP.openKeysMenu end)
|
||||
131
gamemodes/darkrp/gamemode/modules/base/cl_interface.lua
Normal file
131
gamemodes/darkrp/gamemode/modules/base/cl_interface.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
DarkRP.PLAYER.isInRoom = DarkRP.stub{
|
||||
name = "isInRoom",
|
||||
description = "Whether the player is in the same room as the LocalPlayer.",
|
||||
parameters = {},
|
||||
returns = {
|
||||
{
|
||||
name = "inRoom",
|
||||
description = "Whether the player is in the same room.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.deLocalise = DarkRP.stub{
|
||||
name = "deLocalise",
|
||||
description = "Makes sure the string will not be localised when drawn or printed.",
|
||||
parameters = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The text to delocalise.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The delocalised text.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.textWrap = DarkRP.stub{
|
||||
name = "textWrap",
|
||||
description = "Wrap a text around when reaching a certain width.",
|
||||
parameters = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The text to wrap.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "font",
|
||||
description = "The font of the text.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "width",
|
||||
description = "The maximum width in pixels.",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The wrapped string.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.setPreferredJobModel = DarkRP.stub{
|
||||
name = "setPreferredJobModel",
|
||||
description = "Set the model preferred by the player (if the job allows multiple models).",
|
||||
parameters = {
|
||||
{
|
||||
name = "teamNr",
|
||||
description = "The team number of the job.",
|
||||
type = "number",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "model",
|
||||
description = "The preferred model for the job.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getPreferredJobModel = DarkRP.stub{
|
||||
name = "getPreferredJobModel",
|
||||
description = "Get the model preferred by the player (if the job allows multiple models).",
|
||||
parameters = {
|
||||
{
|
||||
name = "teamNr",
|
||||
description = "The team number of the job.",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "model",
|
||||
description = "The preferred model for the job.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "teamChanged",
|
||||
description = "When your team is changed.",
|
||||
deprecated = "Use the OnPlayerChangedTeam hook instead.",
|
||||
parameters = {
|
||||
{
|
||||
name = "before",
|
||||
description = "The team before the change.",
|
||||
type = "number"
|
||||
},
|
||||
{
|
||||
name = "after",
|
||||
description = "The team after the change.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
100
gamemodes/darkrp/gamemode/modules/base/cl_jobmodels.lua
Normal file
100
gamemodes/darkrp/gamemode/modules/base/cl_jobmodels.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
-- Create a table for the preferred playermodels
|
||||
--
|
||||
-- Note: in DarkRP before 2024-09, there was a different table called
|
||||
-- `darkp_playermodels` (note the misspelling of "darkp"). This table was
|
||||
-- missing the server column, meaning that preferred job models would persist
|
||||
-- across multiple servers. To make preferred job models store per server, this
|
||||
-- new table (without the spelling mistake) was created.
|
||||
--
|
||||
-- See the original issue to create the player model preference feature:
|
||||
-- https://github.com/FPtje/DarkRP/issues/979 and the subsequent refactor at
|
||||
-- https://github.com/FPtje/DarkRP/pull/3266
|
||||
sql.Query([[CREATE TABLE IF NOT EXISTS darkrp_playermodels(
|
||||
server TEXT NOT NULL,
|
||||
jobcmd TEXT NOT NULL,
|
||||
model TEXT NOT NULL,
|
||||
PRIMARY KEY (server, jobcmd)
|
||||
);]])
|
||||
|
||||
|
||||
local preferredModels = {}
|
||||
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Interface functions
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.setPreferredJobModel(teamNr, model)
|
||||
local job = RPExtraTeams[teamNr]
|
||||
if not job then return end
|
||||
preferredModels[job.command] = model
|
||||
sql.Query(string.format([[REPLACE INTO darkrp_playermodels(server, jobcmd, model) VALUES(%s, %s, %s);]], sql.SQLStr(game.GetIPAddress()), sql.SQLStr(job.command), sql.SQLStr(model)))
|
||||
|
||||
net.Start("DarkRP_preferredjobmodel")
|
||||
net.WriteUInt(teamNr, 8)
|
||||
net.WriteString(model)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function DarkRP.getPreferredJobModel(teamNr)
|
||||
local job = RPExtraTeams[teamNr]
|
||||
if not job then return end
|
||||
return preferredModels[job.command]
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Load the preferred models
|
||||
---------------------------------------------------------------------------]]
|
||||
local function sendModels()
|
||||
net.Start("DarkRP_preferredjobmodels")
|
||||
for _, job in pairs(RPExtraTeams) do
|
||||
if not preferredModels[job.command] then net.WriteBit(false) continue end
|
||||
|
||||
net.WriteBit(true)
|
||||
net.WriteString(preferredModels[job.command])
|
||||
end
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local function jobHasModel(job, model)
|
||||
return istable(job.model) and table.HasValue(job.model, model) or job.model == model
|
||||
end
|
||||
|
||||
local function setPreferredModels(models)
|
||||
for _, v in ipairs(models) do
|
||||
local job = DarkRP.getJobByCommand(v.jobcmd)
|
||||
if job == nil or not jobHasModel(job, v.model) then continue end
|
||||
|
||||
preferredModels[v.jobcmd] = v.model
|
||||
end
|
||||
end
|
||||
|
||||
-- The old table, darkp_playermodels, acts as a global mapping of preferred
|
||||
-- models for jobs.
|
||||
local function setModelsFromOldTable()
|
||||
local oldTableExists = tobool(sql.QueryValue([[SELECT 1 FROM sqlite_master WHERE type='table' AND name='darkp_playermodels']]))
|
||||
if not oldTableExists then return end
|
||||
|
||||
local models = sql.Query([[SELECT jobcmd, model FROM darkp_playermodels;]])
|
||||
|
||||
if not models then return end
|
||||
setPreferredModels(models)
|
||||
end
|
||||
|
||||
-- The newer table is server specific.
|
||||
local function setModelsFromNewTable()
|
||||
local models = sql.Query(string.format([[SELECT jobcmd, model FROM darkrp_playermodels WHERE server = %s;]], sql.SQLStr(game.GetIPAddress())))
|
||||
|
||||
if not models then return end
|
||||
setPreferredModels(models)
|
||||
end
|
||||
|
||||
timer.Simple(0, function()
|
||||
-- Run after the jobs have loaded, to make sure the jobs can be looked up.
|
||||
|
||||
-- Set models from the old table, before overriding them with data from the
|
||||
-- new table. That way, server specific preferences always have precedence.
|
||||
setModelsFromOldTable()
|
||||
setModelsFromNewTable()
|
||||
|
||||
sendModels()
|
||||
end)
|
||||
107
gamemodes/darkrp/gamemode/modules/base/cl_util.lua
Normal file
107
gamemodes/darkrp/gamemode/modules/base/cl_util.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Show a black screen
|
||||
---------------------------------------------------------------------------]]
|
||||
local function blackScreen(um)
|
||||
local toggle = um:ReadBool()
|
||||
if toggle then
|
||||
local black = color_black
|
||||
local w, h = ScrW(), ScrH()
|
||||
hook.Add("HUDPaintBackground", "BlackScreen", function()
|
||||
surface.SetDrawColor(black)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end)
|
||||
else
|
||||
hook.Remove("HUDPaintBackground", "BlackScreen")
|
||||
end
|
||||
end
|
||||
usermessage.Hook("blackScreen", blackScreen)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Wrap strings to not become wider than the given amount of pixels
|
||||
---------------------------------------------------------------------------]]
|
||||
local function charWrap(text, remainingWidth, maxWidth)
|
||||
local totalWidth = 0
|
||||
|
||||
text = text:gsub(".", function(char)
|
||||
totalWidth = totalWidth + surface.GetTextSize(char)
|
||||
|
||||
-- Wrap around when the max width is reached
|
||||
if totalWidth >= remainingWidth then
|
||||
-- totalWidth needs to include the character width because it's inserted in a new line
|
||||
totalWidth = surface.GetTextSize(char)
|
||||
remainingWidth = maxWidth
|
||||
return "\n" .. char
|
||||
end
|
||||
|
||||
return char
|
||||
end)
|
||||
|
||||
return text, totalWidth
|
||||
end
|
||||
|
||||
function DarkRP.textWrap(text, font, maxWidth)
|
||||
local totalWidth = 0
|
||||
|
||||
surface.SetFont(font)
|
||||
|
||||
local spaceWidth = surface.GetTextSize(' ')
|
||||
text = text:gsub("(%s?[%S]+)", function(word)
|
||||
local char = string.sub(word, 1, 1)
|
||||
if char == "\n" or char == "\t" then
|
||||
totalWidth = 0
|
||||
end
|
||||
|
||||
local wordlen = surface.GetTextSize(word)
|
||||
totalWidth = totalWidth + wordlen
|
||||
|
||||
-- Wrap around when the max width is reached
|
||||
if wordlen >= maxWidth then -- Split the word if the word is too big
|
||||
local splitWord, splitPoint = charWrap(word, maxWidth - (totalWidth - wordlen), maxWidth)
|
||||
totalWidth = splitPoint
|
||||
return splitWord
|
||||
elseif totalWidth < maxWidth then
|
||||
return word
|
||||
end
|
||||
|
||||
-- Split before the word
|
||||
if char == ' ' then
|
||||
totalWidth = wordlen - spaceWidth
|
||||
return '\n' .. string.sub(word, 2)
|
||||
end
|
||||
|
||||
totalWidth = wordlen
|
||||
return '\n' .. word
|
||||
end)
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Decides whether a given player is in the same room as the local player
|
||||
note: uses a heuristic
|
||||
---------------------------------------------------------------------------]]
|
||||
function plyMeta:isInRoom()
|
||||
local tracedata = {}
|
||||
tracedata.start = LocalPlayer():GetShootPos()
|
||||
tracedata.endpos = self:GetShootPos()
|
||||
local trace = util.TraceLine(tracedata)
|
||||
|
||||
return not trace.HitWorld
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Key name to key int mapping
|
||||
---------------------------------------------------------------------------]]
|
||||
local keyNames
|
||||
function input.KeyNameToNumber(str)
|
||||
if not keyNames then
|
||||
keyNames = {}
|
||||
for i = 1, 107, 1 do
|
||||
keyNames[input.GetKeyName(i)] = i
|
||||
end
|
||||
end
|
||||
|
||||
return keyNames[str]
|
||||
end
|
||||
628
gamemodes/darkrp/gamemode/modules/base/sh_checkitems.lua
Normal file
628
gamemodes/darkrp/gamemode/modules/base/sh_checkitems.lua
Normal file
@@ -0,0 +1,628 @@
|
||||
--[[
|
||||
The base elements are shared by every custom item
|
||||
]]
|
||||
local baseSchema = tc.checkTable{
|
||||
buttonColor =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isnumber)),
|
||||
"The buttonColor must be a Color value."
|
||||
),
|
||||
|
||||
category =
|
||||
tc.addHint(
|
||||
tc.optional(isstring),
|
||||
"The category must be the name of an existing category!"
|
||||
),
|
||||
|
||||
customCheck =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The customCheck must be a function."
|
||||
),
|
||||
|
||||
CustomCheckFailMsg =
|
||||
tc.addHint(
|
||||
tc.optional(isstring, isfunction),
|
||||
"The CustomCheckFailMsg must be either a string or a function."
|
||||
),
|
||||
|
||||
sortOrder =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The sortOrder must be a number."
|
||||
),
|
||||
|
||||
label =
|
||||
tc.addHint(
|
||||
tc.optional(isstring),
|
||||
"The label must be a valid string."
|
||||
),
|
||||
}
|
||||
|
||||
--[[
|
||||
Properties shared by anything buyable
|
||||
]]
|
||||
local buyableSchema = fn.FAnd{baseSchema, tc.checkTable{
|
||||
allowed =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isnumber), isnumber),
|
||||
"The allowed field must be either an existing team or a table of existing teams.",
|
||||
{"Is there a job here that doesn't exist (anymore)?"}
|
||||
),
|
||||
|
||||
getPrice =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The getPrice must be a function."
|
||||
),
|
||||
|
||||
model =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The model must be valid."
|
||||
),
|
||||
|
||||
price =
|
||||
tc.addHint(
|
||||
function(v, tbl) return isnumber(v) or isfunction(tbl.getPrice) end,
|
||||
"The price must be an existing number or (for advanced users) the getPrice field must be a function."
|
||||
),
|
||||
|
||||
spawn =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The spawn must be a function."
|
||||
),
|
||||
allowPurchaseWhileDead =
|
||||
tc.addHint(
|
||||
tc.default(false),
|
||||
"The allowPurchaseWhileDead must be either true or false"
|
||||
)
|
||||
}}
|
||||
|
||||
-- The command of an entity must be unique
|
||||
local uniqueEntity = function(cmd, tbl)
|
||||
for _, v in pairs(DarkRPEntities) do
|
||||
if v.cmd ~= cmd then continue end
|
||||
|
||||
return
|
||||
false,
|
||||
"This entity does not have a unique command.",
|
||||
{
|
||||
"There must be some other entity that has the same thing for 'cmd'.",
|
||||
"Fix this by changing the 'cmd' field of your entity to something else."
|
||||
}
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- The command of a job must be unique
|
||||
local uniqueJob = function(v, tbl)
|
||||
local job = DarkRP.getJobByCommand(v)
|
||||
|
||||
if not job then return true end
|
||||
|
||||
return
|
||||
false,
|
||||
"This job does not have a unique command.",
|
||||
{
|
||||
"There must be some other job that has the same command.",
|
||||
"Fix this by changing the 'command' of your job to something else."
|
||||
}
|
||||
end
|
||||
|
||||
--[[
|
||||
Validate jobs
|
||||
]]
|
||||
DarkRP.validateJob = fn.FAnd{baseSchema, tc.checkTable{
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name must be a valid string."
|
||||
),
|
||||
|
||||
color =
|
||||
tc.addHint(
|
||||
tc.tableOf(isnumber),
|
||||
"The color must be a Color value.",
|
||||
{"Color values look like this: Color(r, g, b, a), where r, g, b and a are numbers between 0 and 255."}
|
||||
),
|
||||
|
||||
model =
|
||||
tc.addHint(
|
||||
fn.FOr{isstring, tc.nonEmpty(tc.tableOf(isstring))},
|
||||
"The model must either be a table of correct model strings or a single correct model string.",
|
||||
{
|
||||
"This error could happens when the model does not exist on the server.",
|
||||
"Are you sure the model path is right?",
|
||||
"Is the model from an addon that is not properly installed?"
|
||||
}
|
||||
),
|
||||
|
||||
description =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The description must be a string."
|
||||
),
|
||||
|
||||
weapons =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isstring)),
|
||||
"The weapons must be a valid table of strings.",
|
||||
{"Example: weapons = {\"med_kit\", \"weapon_bugbait\"},"}
|
||||
),
|
||||
|
||||
command =
|
||||
fn.FAnd
|
||||
{
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The command must be a string."
|
||||
),
|
||||
uniqueJob
|
||||
},
|
||||
|
||||
max =
|
||||
tc.addHint(
|
||||
fn.FAnd{isnumber, fp{fn.Lte, 0}},
|
||||
"The max must be a number greater than or equal to zero.",
|
||||
{
|
||||
"Zero means infinite.",
|
||||
"A decimal between 0 and 1 is seen as a percentage."
|
||||
}
|
||||
),
|
||||
|
||||
salary =
|
||||
tc.addHint(
|
||||
fn.FAnd{isnumber, fp{fn.Lte, 0}},
|
||||
"The salary must be a number and it must be greater than zero."
|
||||
),
|
||||
|
||||
admin =
|
||||
tc.default(0,
|
||||
tc.addHint(
|
||||
fn.FAnd{isnumber, fp{fn.Lte, 0}, fp{fn.Gte, 2}},
|
||||
"The admin value must be a number and it must be greater than or equal to zero and smaller than three."
|
||||
)
|
||||
),
|
||||
|
||||
vote =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The vote must be either true or false."
|
||||
),
|
||||
|
||||
ammo =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isnumber)),
|
||||
"The ammo must be a table containing numbers.",
|
||||
{"See example on https://darkrp.miraheze.org/wiki/DarkRP:CustomJobFields"}
|
||||
),
|
||||
|
||||
hasLicense =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The hasLicense must be either true or false."
|
||||
),
|
||||
|
||||
NeedToChangeFrom =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isnumber), isnumber),
|
||||
"The NeedToChangeFrom must be either an existing team or a table of existing teams",
|
||||
{"Is there a job here that doesn't exist (anymore)?"}
|
||||
),
|
||||
|
||||
modelScale =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The modelScale must be a number."
|
||||
),
|
||||
|
||||
maxpocket =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The maxPocket must be a number."
|
||||
),
|
||||
|
||||
maps =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(isstring)),
|
||||
"The maps value must be a table of valid map names."
|
||||
),
|
||||
|
||||
candemote =
|
||||
tc.default(true,
|
||||
tc.addHint(
|
||||
isbool,
|
||||
"The candemote value must be either true or false."
|
||||
)
|
||||
),
|
||||
|
||||
mayor =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The mayor value must be either true or false."
|
||||
),
|
||||
|
||||
chief =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The chief value must be either true or false."
|
||||
),
|
||||
|
||||
medic =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The medic value must be either true or false."
|
||||
),
|
||||
|
||||
cook =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The cook value must be either true or false."
|
||||
),
|
||||
|
||||
hobo =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The hobo value must be either true or false."
|
||||
),
|
||||
|
||||
playerClass =
|
||||
tc.addHint(
|
||||
tc.optional(isstring),
|
||||
"The playerClass must be a valid string."
|
||||
),
|
||||
|
||||
CanPlayerSuicide =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The CanPlayerSuicide must be a function."
|
||||
),
|
||||
|
||||
PlayerCanPickupWeapon =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerCanPickupWeapon must be a function."
|
||||
),
|
||||
|
||||
PlayerDeath =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerDeath must be a function."
|
||||
),
|
||||
|
||||
PlayerLoadout =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerLoadout must be a function."
|
||||
),
|
||||
|
||||
PlayerSelectSpawn =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerSelectSpawn must be a function."
|
||||
),
|
||||
|
||||
PlayerSetModel =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerSetModel must be a function."
|
||||
),
|
||||
|
||||
PlayerSpawn =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerSpawn must be a function."
|
||||
),
|
||||
|
||||
PlayerSpawnProp =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The PlayerSpawnProp must be a function."
|
||||
),
|
||||
|
||||
RequiresVote =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The RequiresVote must be a function."
|
||||
),
|
||||
|
||||
ShowSpare1 =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The ShowSpare1 must be a function."
|
||||
),
|
||||
|
||||
ShowSpare2 =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The ShowSpare2 must be a function."
|
||||
),
|
||||
|
||||
canStartVote =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The canStartVote must be a function."
|
||||
),
|
||||
|
||||
canStartVoteReason =
|
||||
tc.addHint(
|
||||
tc.optional(isstring, isfunction),
|
||||
"The canStartVoteReason must be either a string or a function."
|
||||
),
|
||||
}}
|
||||
|
||||
--[[
|
||||
Validate shipments
|
||||
]]
|
||||
DarkRP.validateShipment = fn.FAnd{buyableSchema, tc.checkTable{
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name must be a valid string."
|
||||
),
|
||||
|
||||
entity =
|
||||
tc.addHint(
|
||||
isstring, "The entity of the shipment must be a string."
|
||||
),
|
||||
|
||||
amount =
|
||||
tc.addHint(
|
||||
fn.FAnd{isnumber, fp{fn.Lte, 0}}, "The amount must be a number and it must be greater than zero."
|
||||
),
|
||||
|
||||
separate =
|
||||
tc.addHint(
|
||||
tc.optional(isbool), "the separate field must be either true or false."
|
||||
),
|
||||
|
||||
pricesep =
|
||||
tc.addHint(
|
||||
function(v, tbl) return not tbl.separate or isnumber(v) and v >= 0 end,
|
||||
"The pricesep must be a number and it must be greater than or equal to zero."
|
||||
),
|
||||
|
||||
noship =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The noship must be either true or false."
|
||||
),
|
||||
|
||||
shipmodel =
|
||||
tc.addHint(
|
||||
tc.optional(isstring),
|
||||
"The shipmodel must be a valid model."
|
||||
),
|
||||
|
||||
weight =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The weight must be a number."
|
||||
),
|
||||
|
||||
spareammo =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The spareammo must be a number."
|
||||
),
|
||||
|
||||
clip1 =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The clip1 must be a number."
|
||||
),
|
||||
|
||||
clip2 =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The clip2 must be a number."
|
||||
),
|
||||
|
||||
shipmentClass =
|
||||
tc.addHint(
|
||||
tc.optional(isstring),
|
||||
"The shipmentClass must be a string."
|
||||
),
|
||||
|
||||
onBought =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The onBought must be a function."
|
||||
),
|
||||
|
||||
}}
|
||||
|
||||
--[[
|
||||
Validate vehicles
|
||||
]]
|
||||
DarkRP.validateVehicle = fn.FAnd{buyableSchema, tc.checkTable{
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name of the vehicle must be a string."
|
||||
),
|
||||
|
||||
distance =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The distance must be a number."
|
||||
),
|
||||
|
||||
angle =
|
||||
tc.addHint(
|
||||
tc.optional(isangle),
|
||||
"The distance must be a valid Angle."
|
||||
),
|
||||
}}
|
||||
|
||||
--[[
|
||||
Validate Entities
|
||||
]]
|
||||
DarkRP.validateEntity = fn.FAnd{buyableSchema, tc.checkTable{
|
||||
ent =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The ent field must be a string."
|
||||
),
|
||||
|
||||
max =
|
||||
tc.addHint(
|
||||
function(v, tbl) return isnumber(v) or isfunction(tbl.getMax) end,
|
||||
"The max must be an existing number or (for advanced users) the getMax field must be a function."
|
||||
),
|
||||
|
||||
cmd =
|
||||
fn.FAnd
|
||||
{
|
||||
tc.addHint(isstring, "The cmd must be a valid string."),
|
||||
uniqueEntity
|
||||
},
|
||||
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name must be a valid string."
|
||||
),
|
||||
|
||||
allowTools =
|
||||
tc.default(false,
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"The allowTools must be either true or false."
|
||||
)
|
||||
),
|
||||
|
||||
delay =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The delay must be a number."
|
||||
),
|
||||
}}
|
||||
|
||||
|
||||
-- Checks whether a team already has an agenda assigned.
|
||||
-- Jobs cannot have multiple agendas.
|
||||
|
||||
local overlappingAgendaCheck = function(t, tbl)
|
||||
local agenda = DarkRP.getAgendas()[t]
|
||||
|
||||
-- Team being -1 means the job is disabled
|
||||
if agenda == nil or t == -1 then return true end
|
||||
|
||||
local teamName = team.GetName(t)
|
||||
local err = "At least one job has multiple agendas assigned to them"
|
||||
local hints = {
|
||||
string.format([[The problem lies with the job called "%s"]], teamName),
|
||||
string.format([[It is assigned to agendas "%s" and "%s"]], agenda.Title or "unknown", tbl.Title or "unknown"),
|
||||
[[A job can only have ONE agenda. Otherwise things would become confusing, since only ONE agenda is always drawn on the screen.]]
|
||||
}
|
||||
|
||||
if agenda.Title == tbl.Title then
|
||||
table.insert(hints, "The titles of the two agendas are the same. It looks like perhaps you've made the same agenda more than once.")
|
||||
table.insert(hints, "Removing one of them should get rid of this error.")
|
||||
end
|
||||
|
||||
return false, err, hints
|
||||
end
|
||||
|
||||
--[[
|
||||
Validate Agendas
|
||||
]]
|
||||
local managerNumberCheck = tc.addHint(
|
||||
isnumber,
|
||||
"The Manager must either be a single team or a non-empty table of existing teams.",
|
||||
{"Is there a job here that doesn't exist (anymore)?"}
|
||||
)
|
||||
|
||||
DarkRP.validateAgenda = tc.checkTable{
|
||||
Title =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The title must be a string."
|
||||
),
|
||||
|
||||
-- Custom function to ensure the right error message is thrown
|
||||
Manager = function(manager, tbl)
|
||||
-- Check whether the manager is an existing team
|
||||
-- that does not already have an agenda assigned
|
||||
if isnumber(manager) then
|
||||
return fn.FAnd{overlappingAgendaCheck}(manager, tbl)
|
||||
|
||||
-- Check whether the manager is a table of existing teams
|
||||
-- and that none of the teams already have agendas assigned
|
||||
elseif istable(manager) then
|
||||
return tc.nonEmpty(
|
||||
tc.tableOf(
|
||||
fn.FAnd{managerNumberCheck, overlappingAgendaCheck}
|
||||
)
|
||||
)(manager, tbl)
|
||||
end
|
||||
|
||||
return managerNumberCheck(manager, tbl)
|
||||
end,
|
||||
Listeners =
|
||||
tc.default({}, -- Default to empty table
|
||||
-- Checks for a table of valid teams that do not already have an
|
||||
-- agenda assigned
|
||||
fn.FAnd{
|
||||
tc.addHint(
|
||||
tc.tableOf(isnumber),
|
||||
"The Listeners must be a table of existing teams.",
|
||||
{
|
||||
"Is there a job here that doesn't exist (anymore)?",
|
||||
"Are you trying to have multiple manager jobs in this agenda? In that case you must put the list of manager jobs in curly braces.",
|
||||
[[Like so: DarkRP.createAgenda("Some agenda", {TEAM_MANAGER1, TEAM_MANAGER2}, {TEAM_LISTENER1, TEAM_LISTENER2})]]
|
||||
}
|
||||
),
|
||||
tc.tableOf(overlappingAgendaCheck)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
--[[
|
||||
Validate Categories
|
||||
]]
|
||||
DarkRP.validateCategory = tc.checkTable{
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name must be a string."
|
||||
),
|
||||
|
||||
categorises =
|
||||
tc.addHint(
|
||||
tc.oneOf{"jobs", "entities", "shipments", "weapons", "vehicles", "ammo"},
|
||||
[[The categorises must be one of "jobs", "entities", "shipments", "weapons", "vehicles", "ammo"]],
|
||||
{
|
||||
"Mind that this is case sensitive.",
|
||||
"Also mind the quotation marks."
|
||||
}
|
||||
),
|
||||
|
||||
startExpanded =
|
||||
tc.addHint(
|
||||
isbool,
|
||||
"The startExpanded must be either true or false."
|
||||
),
|
||||
|
||||
color =
|
||||
tc.addHint(
|
||||
tc.tableOf(isnumber),
|
||||
"The color must be a Color value."
|
||||
),
|
||||
|
||||
canSee =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"The canSee must be a function."
|
||||
),
|
||||
|
||||
sortOrder =
|
||||
tc.addHint(
|
||||
tc.optional(isnumber),
|
||||
"The sortOrder must be a number."
|
||||
),
|
||||
}
|
||||
71
gamemodes/darkrp/gamemode/modules/base/sh_commands.lua
Normal file
71
gamemodes/darkrp/gamemode/modules/base/sh_commands.lua
Normal file
@@ -0,0 +1,71 @@
|
||||
DarkRP.declareChatCommand{
|
||||
command = "rpname",
|
||||
description = "Set your RP name",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "name",
|
||||
description = "Set your RP name",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "nick",
|
||||
description = "Set your RP name",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "buy",
|
||||
description = "Buy a pistol",
|
||||
delay = 1.5,
|
||||
condition = fn.FAnd {
|
||||
fn.Compose{fn.Curry(fn.GetValue, 2)("enablebuypistol"), fn.Curry(fn.GetValue, 2)("Config"), gmod.GetGamemode},
|
||||
fn.Compose{fn.Not, fn.Curry(fn.GetValue, 2)("noguns"), fn.Curry(fn.GetValue, 2)("Config"), gmod.GetGamemode}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "buyshipment",
|
||||
description = "Buy a shipment",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "buyvehicle",
|
||||
description = "Buy a vehicle",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "buyammo",
|
||||
description = "Purchase ammo",
|
||||
delay = 1.5,
|
||||
condition = fn.Compose{fn.Not, fn.Curry(fn.GetValue, 2)("noguns"), fn.Curry(fn.GetValue, 2)("Config"), gmod.GetGamemode}
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "price",
|
||||
description = "Set the price of the microwave or gunlab you're looking at",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "setprice",
|
||||
description = "Set the price of the microwave or gunlab you're looking at",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forcerpname",
|
||||
description = "Forcefully change a player's RP name",
|
||||
delay = 0.5,
|
||||
tableArgs = true
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "freerpname",
|
||||
description = "Remove a RP name from the database so a player can use it",
|
||||
delay = 1.5
|
||||
}
|
||||
922
gamemodes/darkrp/gamemode/modules/base/sh_createitems.lua
Normal file
922
gamemodes/darkrp/gamemode/modules/base/sh_createitems.lua
Normal file
@@ -0,0 +1,922 @@
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- Job commands --
|
||||
-----------------------------------------------------------
|
||||
local function declareTeamCommands(CTeam)
|
||||
local k = 0
|
||||
for num, v in pairs(RPExtraTeams) do
|
||||
if v.command == CTeam.command then
|
||||
k = num
|
||||
end
|
||||
end
|
||||
|
||||
local chatcommandCondition = function(ply)
|
||||
local plyTeam = ply:Team()
|
||||
|
||||
if plyTeam == k then return false end
|
||||
if CTeam.admin == 1 and not ply:IsAdmin() or CTeam.admin == 2 and not ply:IsSuperAdmin() then return false end
|
||||
if isnumber(CTeam.NeedToChangeFrom) and plyTeam ~= CTeam.NeedToChangeFrom then return false end
|
||||
if istable(CTeam.NeedToChangeFrom) and not table.HasValue(CTeam.NeedToChangeFrom, plyTeam) then return false end
|
||||
if CTeam.customCheck and CTeam.customCheck(ply) == false then return false end
|
||||
if ply:isArrested() then return false end
|
||||
local numPlayers = team.NumPlayers(k)
|
||||
if CTeam.max ~= 0 and ((CTeam.max % 1 == 0 and numPlayers >= CTeam.max) or (CTeam.max % 1 ~= 0 and (numPlayers + 1) / player.GetCount() > CTeam.max)) then return false end
|
||||
if ply.LastJob and 10 - (CurTime() - ply.LastJob) >= 0 then return false end
|
||||
if ply.LastVoteCop and CurTime() - ply.LastVoteCop < 80 then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if CTeam.vote or CTeam.RequiresVote then
|
||||
DarkRP.declareChatCommand{
|
||||
command = "vote" .. CTeam.command,
|
||||
description = "Vote to become " .. CTeam.name .. ".",
|
||||
delay = 1.5,
|
||||
condition =
|
||||
function(ply)
|
||||
if CTeam.RequiresVote and not CTeam.RequiresVote(ply, k) then return false end
|
||||
if CTeam.canStartVote and not CTeam.canStartVote(ply) then return false end
|
||||
|
||||
return chatcommandCondition(ply)
|
||||
end
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = CTeam.command,
|
||||
description = "Become " .. CTeam.name .. " and skip the vote.",
|
||||
delay = 1.5,
|
||||
condition =
|
||||
function(ply)
|
||||
local requiresVote = CTeam.RequiresVote and CTeam.RequiresVote(ply, k)
|
||||
|
||||
if requiresVote then return false end
|
||||
if requiresVote ~= false and CTeam.admin == 0 and not ply:IsAdmin() or CTeam.admin == 1 and not ply:IsSuperAdmin() then return false end
|
||||
if CTeam.canStartVote and not CTeam.canStartVote(ply) then return false end
|
||||
|
||||
return chatcommandCondition(ply)
|
||||
end
|
||||
}
|
||||
else
|
||||
DarkRP.declareChatCommand{
|
||||
command = CTeam.command,
|
||||
description = "Become " .. CTeam.name .. ".",
|
||||
delay = 1.5,
|
||||
condition = chatcommandCondition
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function addTeamCommands(CTeam, max)
|
||||
if CLIENT then return end
|
||||
|
||||
local k = 0
|
||||
for num, v in pairs(RPExtraTeams) do
|
||||
if v.command == CTeam.command then
|
||||
k = num
|
||||
end
|
||||
end
|
||||
|
||||
if CTeam.vote or CTeam.RequiresVote then
|
||||
DarkRP.defineChatCommand("vote" .. CTeam.command, function(ply)
|
||||
if CTeam.RequiresVote and not CTeam.RequiresVote(ply, k) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("job_doesnt_require_vote_currently"))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if CTeam.canStartVote and not CTeam.canStartVote(ply) then
|
||||
local reason = isfunction(CTeam.canStartVoteReason) and CTeam.canStartVoteReason(ply, CTeam) or CTeam.canStartVoteReason or ""
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/vote" .. CTeam.command, reason))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if CTeam.admin == 1 and not ply:IsAdmin() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", "/" .. "vote" .. CTeam.command))
|
||||
|
||||
return ""
|
||||
elseif CTeam.admin > 1 and not ply:IsSuperAdmin() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_sadmin", "/" .. "vote" .. CTeam.command))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if isnumber(CTeam.NeedToChangeFrom) and ply:Team() ~= CTeam.NeedToChangeFrom then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_to_be_before", team.GetName(CTeam.NeedToChangeFrom), CTeam.name))
|
||||
|
||||
return ""
|
||||
elseif istable(CTeam.NeedToChangeFrom) and not table.HasValue(CTeam.NeedToChangeFrom, ply:Team()) then
|
||||
local teamnames = ""
|
||||
|
||||
for _, b in pairs(CTeam.NeedToChangeFrom) do
|
||||
teamnames = teamnames .. " or " .. team.GetName(b)
|
||||
end
|
||||
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_to_be_before", string.sub(teamnames, 5), CTeam.name))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if CTeam.customCheck and not CTeam.customCheck(ply) then
|
||||
local message = isfunction(CTeam.CustomCheckFailMsg) and CTeam.CustomCheckFailMsg(ply, CTeam) or CTeam.CustomCheckFailMsg or DarkRP.getPhrase("unable", team.GetName(t), "")
|
||||
DarkRP.notify(ply, 1, 4, message)
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
local allowed, time = ply:changeAllowed(k)
|
||||
if not allowed then
|
||||
local notif = time and DarkRP.getPhrase("have_to_wait", math.ceil(time), "/job, " .. DarkRP.getPhrase("banned_or_demoted")) or DarkRP.getPhrase("unable", team.GetName(k), DarkRP.getPhrase("banned_or_demoted"))
|
||||
DarkRP.notify(ply, 1, 4, notif)
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if ply:Team() == k then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", CTeam.command, ""))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
local numPlayers = team.NumPlayers(k)
|
||||
if max ~= 0 and ((max % 1 == 0 and numPlayers >= max) or (max % 1 ~= 0 and (numPlayers + 1) / player.GetCount() > max)) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("team_limit_reached", CTeam.name))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if ply.LastJob and 10 - (CurTime() - ply.LastJob) >= 0 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("have_to_wait", math.ceil(10 - (CurTime() - ply.LastJob)), GAMEMODE.Config.chatCommandPrefix .. CTeam.command))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
ply.LastVoteCop = ply.LastVoteCop or -80
|
||||
|
||||
if CurTime() - ply.LastVoteCop < 80 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("have_to_wait", math.ceil(80 - (CurTime() - ply:GetTable().LastVoteCop)), GAMEMODE.Config.chatCommandPrefix .. CTeam.command))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
DarkRP.createVote(DarkRP.getPhrase("wants_to_be", ply:Nick(), CTeam.name), "job", ply, 20, function(vote, choice)
|
||||
local target = vote.target
|
||||
if not IsValid(target) then return end
|
||||
|
||||
if choice >= 0 then
|
||||
target:changeTeam(k)
|
||||
else
|
||||
DarkRP.notifyAll(1, 4, DarkRP.getPhrase("has_not_been_made_team", target:Nick(), CTeam.name))
|
||||
end
|
||||
end, nil, nil, {
|
||||
targetTeam = k
|
||||
})
|
||||
|
||||
ply.LastVoteCop = CurTime()
|
||||
|
||||
return ""
|
||||
end)
|
||||
|
||||
local function onJobCommand(ply, hasPriv)
|
||||
if hasPriv then
|
||||
ply:changeTeam(k)
|
||||
return
|
||||
end
|
||||
|
||||
local a = CTeam.admin
|
||||
if a > 0 and not ply:IsAdmin()
|
||||
or a > 1 and not ply:IsSuperAdmin()
|
||||
then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", CTeam.name))
|
||||
return
|
||||
end
|
||||
|
||||
if not CTeam.RequiresVote and
|
||||
(a == 0 and not ply:IsAdmin()
|
||||
or a == 1 and not ply:IsSuperAdmin()
|
||||
or a == 2)
|
||||
or CTeam.RequiresVote and CTeam.RequiresVote(ply, k)
|
||||
then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_to_make_vote", CTeam.name))
|
||||
return
|
||||
end
|
||||
|
||||
ply:changeTeam(k)
|
||||
end
|
||||
DarkRP.defineChatCommand(CTeam.command, function(ply)
|
||||
CAMI.PlayerHasAccess(ply, "DarkRP_GetJob_" .. CTeam.command, fp{onJobCommand, ply})
|
||||
|
||||
return ""
|
||||
end)
|
||||
else
|
||||
DarkRP.defineChatCommand(CTeam.command, function(ply)
|
||||
if CTeam.admin == 1 and not ply:IsAdmin() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", "/" .. CTeam.command))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if CTeam.admin > 1 and not ply:IsSuperAdmin() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_sadmin", "/" .. CTeam.command))
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
ply:changeTeam(k)
|
||||
|
||||
return ""
|
||||
end)
|
||||
end
|
||||
|
||||
concommand.Add("rp_" .. CTeam.command, function(ply, cmd, args)
|
||||
if ply:EntIndex() ~= 0 and not ply:IsAdmin() then
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_admin", cmd))
|
||||
return
|
||||
end
|
||||
|
||||
if CTeam.admin > 1 and not ply:IsSuperAdmin() and ply:EntIndex() ~= 0 then
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_sadmin", cmd))
|
||||
return
|
||||
end
|
||||
|
||||
if CTeam.vote then
|
||||
if CTeam.admin >= 1 and ply:EntIndex() ~= 0 and not ply:IsSuperAdmin() then
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_sadmin", cmd))
|
||||
return
|
||||
elseif CTeam.admin > 1 and ply:IsSuperAdmin() and ply:EntIndex() ~= 0 then
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_to_make_vote", CTeam.name))
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not args or not args[1] then
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(args[1])
|
||||
|
||||
if not target then
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("could_not_find", tostring(args[1])))
|
||||
return
|
||||
end
|
||||
|
||||
target:changeTeam(k, true)
|
||||
local nick
|
||||
if (ply:EntIndex() ~= 0) then
|
||||
nick = ply:Nick()
|
||||
else
|
||||
nick = "Console"
|
||||
end
|
||||
DarkRP.notify(target, 0, 4, DarkRP.getPhrase("x_made_you_a_y", nick, CTeam.name))
|
||||
end)
|
||||
end
|
||||
|
||||
local function addEntityCommands(tblEnt)
|
||||
DarkRP.declareChatCommand{
|
||||
command = tblEnt.cmd,
|
||||
description = "Purchase a " .. tblEnt.name,
|
||||
delay = tblEnt.delay or GAMEMODE.Config.EntitySpamTime,
|
||||
condition =
|
||||
function(ply)
|
||||
if not tblEnt.allowPurchaseWhileDead and not ply:Alive() then return false end
|
||||
if ply:isArrested() then return false end
|
||||
if istable(tblEnt.allowed) and not table.HasValue(tblEnt.allowed, ply:Team()) then return false end
|
||||
if not ply:canAfford(tblEnt.price) then return false end
|
||||
if tblEnt.customCheck and tblEnt.customCheck(ply) == false then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
if CLIENT then return end
|
||||
|
||||
-- Default spawning function of an entity
|
||||
-- used if tblEnt.spawn is not defined
|
||||
local function defaultSpawn(ply, tr, tblE)
|
||||
local ent = ents.Create(tblE.ent)
|
||||
|
||||
if not ent:IsValid() then error("Entity '" .. tblE.ent .. "' does not exist or is not valid.") end
|
||||
if ent.Setowning_ent then ent:Setowning_ent(ply) end
|
||||
|
||||
ent:SetPos(tr.HitPos)
|
||||
-- These must be set before :Spawn()
|
||||
ent.SID = ply.SID
|
||||
ent.allowed = tblE.allowed
|
||||
ent.DarkRPItem = tblE
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
DarkRP.placeEntity(ent, tr, ply)
|
||||
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if phys:IsValid() then phys:Wake() end
|
||||
|
||||
return ent
|
||||
end
|
||||
|
||||
local function buythis(ply, args)
|
||||
if ply:isArrested() then return "" end
|
||||
if not tblEnt.allowPurchaseWhileDead and not ply:Alive() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_alive_to_do_x", DarkRP.getPhrase("buy_x", tblEnt.name)))
|
||||
return ""
|
||||
end
|
||||
if istable(tblEnt.allowed) and not table.HasValue(tblEnt.allowed, ply:Team()) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("incorrect_job", tblEnt.name))
|
||||
return ""
|
||||
end
|
||||
|
||||
if tblEnt.customCheck and not tblEnt.customCheck(ply) then
|
||||
local message = isfunction(tblEnt.CustomCheckFailMsg) and tblEnt.CustomCheckFailMsg(ply, tblEnt) or
|
||||
tblEnt.CustomCheckFailMsg or
|
||||
DarkRP.getPhrase("not_allowed_to_purchase")
|
||||
DarkRP.notify(ply, 1, 4, message)
|
||||
return ""
|
||||
end
|
||||
|
||||
if ply:customEntityLimitReached(tblEnt) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("limit", tblEnt.name))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canbuy, suppress, message, price = hook.Call("canBuyCustomEntity", nil, ply, tblEnt)
|
||||
|
||||
local cost = price or tblEnt.getPrice and tblEnt.getPrice(ply, tblEnt.price) or tblEnt.price
|
||||
|
||||
if not ply:canAfford(cost) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("cant_afford", tblEnt.name))
|
||||
return ""
|
||||
end
|
||||
|
||||
if canbuy == false then
|
||||
if not suppress and message then DarkRP.notify(ply, 1, 4, message) end
|
||||
return ""
|
||||
end
|
||||
|
||||
ply:addMoney(-cost)
|
||||
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
trace.filter = ply
|
||||
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local ent = (tblEnt.spawn or defaultSpawn)(ply, tr, tblEnt)
|
||||
ent.onlyremover = not tblEnt.allowTools
|
||||
-- Repeat these properties to alleviate work in tblEnt.spawn:
|
||||
ent.SID = ply.SID
|
||||
ent.allowed = tblEnt.allowed
|
||||
ent.DarkRPItem = tblEnt
|
||||
|
||||
hook.Call("playerBoughtCustomEntity", nil, ply, tblEnt, ent, cost)
|
||||
|
||||
if cost == 0 then
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_got_yourself", tblEnt.name))
|
||||
else
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", tblEnt.name, DarkRP.formatMoney(cost), ""))
|
||||
end
|
||||
|
||||
ply:addCustomEntity(tblEnt)
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand(tblEnt.cmd, buythis)
|
||||
end
|
||||
|
||||
RPExtraTeams = {}
|
||||
local jobByCmd = {}
|
||||
DarkRP.getJobByCommand = function(cmd)
|
||||
if not jobByCmd[cmd] then return nil, nil end
|
||||
return RPExtraTeams[jobByCmd[cmd]], jobByCmd[cmd]
|
||||
end
|
||||
plyMeta.getJobTable = function(ply)
|
||||
local tbl = RPExtraTeams[ply:Team()]
|
||||
-- don't error when the player has not fully joined yet
|
||||
if not tbl and (ply.DarkRPInitialised or ply.DarkRPDataRetrievalFailed) then
|
||||
DarkRP.error(
|
||||
string.format("There is a player with an invalid team!\n\nThe player's name is %s, their team number is \"%s\", which has the name \"%s\"",
|
||||
ply:EntIndex() == 0 and "Console" or IsValid(ply) and ply:Nick() or "unknown",
|
||||
ply:Team(),
|
||||
team.GetName(ply:Team())),
|
||||
1,
|
||||
{
|
||||
"It is the server owner's responsibility to figure out why that player has no valid team.",
|
||||
"This error is very likely to be caused by an earlier error. If you don't see any errors in your own console, look at the server console."
|
||||
}
|
||||
)
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
function DarkRP.createJob(Name, colorOrTable, model, Description, Weapons, command, maximum_amount_of_this_class, Salary, admin, Vote, Haslicense, NeedToChangeFrom, CustomCheck)
|
||||
local tableSyntaxUsed = not IsColor(colorOrTable)
|
||||
|
||||
local CustomTeam = tableSyntaxUsed and colorOrTable or
|
||||
{color = colorOrTable, model = model, description = Description, weapons = Weapons, command = command,
|
||||
max = maximum_amount_of_this_class, salary = Salary, admin = admin or 0, vote = tobool(Vote), hasLicense = Haslicense,
|
||||
NeedToChangeFrom = NeedToChangeFrom, customCheck = CustomCheck
|
||||
}
|
||||
CustomTeam.name = Name
|
||||
CustomTeam.default = DarkRP.DARKRP_LOADING
|
||||
|
||||
-- Disabled job
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["jobs"][CustomTeam.command] then return end
|
||||
|
||||
local valid, err, hints = DarkRP.validateJob(CustomTeam)
|
||||
if not valid then DarkRP.error(string.format("Corrupt team: %s!\n%s", CustomTeam.name or "", err), 2, hints) end
|
||||
|
||||
if not (GM or GAMEMODE):CustomObjFitsMap(CustomTeam) then return end
|
||||
|
||||
local jobCount = #RPExtraTeams + 1
|
||||
|
||||
CustomTeam.team = jobCount
|
||||
|
||||
CustomTeam.salary = math.floor(CustomTeam.salary)
|
||||
|
||||
CustomTeam.customCheck = CustomTeam.customCheck and fp{DarkRP.simplerrRun, CustomTeam.customCheck}
|
||||
CustomTeam.CustomCheckFailMsg = isfunction(CustomTeam.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, CustomTeam.CustomCheckFailMsg} or CustomTeam.CustomCheckFailMsg
|
||||
CustomTeam.CanPlayerSuicide = CustomTeam.CanPlayerSuicide and fp{DarkRP.simplerrRun, CustomTeam.CanPlayerSuicide}
|
||||
CustomTeam.PlayerCanPickupWeapon = CustomTeam.PlayerCanPickupWeapon and fp{DarkRP.simplerrRun, CustomTeam.PlayerCanPickupWeapon}
|
||||
CustomTeam.PlayerDeath = CustomTeam.PlayerDeath and fp{DarkRP.simplerrRun, CustomTeam.PlayerDeath}
|
||||
CustomTeam.PlayerLoadout = CustomTeam.PlayerLoadout and fp{DarkRP.simplerrRun, CustomTeam.PlayerLoadout}
|
||||
CustomTeam.PlayerSelectSpawn = CustomTeam.PlayerSelectSpawn and fp{DarkRP.simplerrRun, CustomTeam.PlayerSelectSpawn}
|
||||
CustomTeam.PlayerSetModel = CustomTeam.PlayerSetModel and fp{DarkRP.simplerrRun, CustomTeam.PlayerSetModel}
|
||||
CustomTeam.PlayerSpawn = CustomTeam.PlayerSpawn and fp{DarkRP.simplerrRun, CustomTeam.PlayerSpawn}
|
||||
CustomTeam.PlayerSpawnProp = CustomTeam.PlayerSpawnProp and fp{DarkRP.simplerrRun, CustomTeam.PlayerSpawnProp}
|
||||
CustomTeam.RequiresVote = CustomTeam.RequiresVote and fp{DarkRP.simplerrRun, CustomTeam.RequiresVote}
|
||||
CustomTeam.ShowSpare1 = CustomTeam.ShowSpare1 and fp{DarkRP.simplerrRun, CustomTeam.ShowSpare1}
|
||||
CustomTeam.ShowSpare2 = CustomTeam.ShowSpare2 and fp{DarkRP.simplerrRun, CustomTeam.ShowSpare2}
|
||||
CustomTeam.canStartVote = CustomTeam.canStartVote and fp{DarkRP.simplerrRun, CustomTeam.canStartVote}
|
||||
|
||||
jobByCmd[CustomTeam.command] = table.insert(RPExtraTeams, CustomTeam)
|
||||
DarkRP.addToCategory(CustomTeam, "jobs", CustomTeam.category)
|
||||
|
||||
team.SetUp(jobCount, Name, CustomTeam.color)
|
||||
|
||||
timer.Simple(0, function()
|
||||
declareTeamCommands(CustomTeam)
|
||||
addTeamCommands(CustomTeam, CustomTeam.max)
|
||||
end)
|
||||
|
||||
-- Precache model here. Not right before the job change is done
|
||||
if istable(CustomTeam.model) then
|
||||
for _, v in pairs(CustomTeam.model) do util.PrecacheModel(v) end
|
||||
else
|
||||
util.PrecacheModel(CustomTeam.model)
|
||||
end
|
||||
return jobCount
|
||||
end
|
||||
AddExtraTeam = DarkRP.createJob
|
||||
|
||||
local function removeCustomItem(tbl, category, hookName, reloadF4, i)
|
||||
local item = tbl[i]
|
||||
tbl[i] = nil
|
||||
if category then DarkRP.removeFromCategory(item, category) end
|
||||
if istable(item) and (item.command or item.cmd) then DarkRP.removeChatCommand(item.command or item.cmd) end
|
||||
hook.Run(hookName, i, item)
|
||||
if CLIENT and reloadF4 and IsValid(DarkRP.getF4MenuPanel()) then DarkRP.getF4MenuPanel():Remove() end -- Rebuild entire F4 menu frame
|
||||
end
|
||||
|
||||
function DarkRP.removeJob(i)
|
||||
local job = RPExtraTeams[i]
|
||||
jobByCmd[job.command] = nil
|
||||
|
||||
DarkRP.removeChatCommand("vote" .. job.command)
|
||||
removeCustomItem(RPExtraTeams, "jobs", "onJobRemoved", true, i)
|
||||
end
|
||||
|
||||
RPExtraTeamDoors = {}
|
||||
RPExtraTeamDoorIDs = {}
|
||||
local maxTeamDoorID = 0
|
||||
function DarkRP.createEntityGroup(name, ...)
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["doorgroups"][name] then return end
|
||||
RPExtraTeamDoors[name] = {...}
|
||||
RPExtraTeamDoors[name].name = name
|
||||
|
||||
maxTeamDoorID = maxTeamDoorID + 1
|
||||
RPExtraTeamDoorIDs[name] = maxTeamDoorID
|
||||
end
|
||||
AddDoorGroup = DarkRP.createEntityGroup
|
||||
|
||||
DarkRP.removeEntityGroup = fp{removeCustomItem, RPExtraTeamDoors, nil, "onEntityGroupRemoved", false}
|
||||
|
||||
CustomVehicles = {}
|
||||
CustomShipments = {}
|
||||
local shipByName = {}
|
||||
DarkRP.getShipmentByName = function(name)
|
||||
name = string.lower(name or "")
|
||||
|
||||
if not shipByName[name] then return nil, nil end
|
||||
return CustomShipments[shipByName[name]], shipByName[name]
|
||||
end
|
||||
|
||||
function DarkRP.createShipment(name, model, entity, price, Amount_of_guns_in_one_shipment, Sold_separately, price_separately, noshipment, classes, shipmodel, CustomCheck)
|
||||
local tableSyntaxUsed = istable(model)
|
||||
|
||||
price = tonumber(price)
|
||||
local shipmentmodel = shipmodel or "models/Items/item_item_crate.mdl"
|
||||
|
||||
local customShipment = tableSyntaxUsed and model or
|
||||
{model = model, entity = entity, price = price, amount = Amount_of_guns_in_one_shipment,
|
||||
seperate = Sold_separately, pricesep = price_separately, noship = noshipment, allowed = classes,
|
||||
shipmodel = shipmentmodel, customCheck = CustomCheck, weight = 5}
|
||||
|
||||
-- The pains of backwards compatibility when dealing with ancient spelling errors...
|
||||
if customShipment.separate ~= nil then
|
||||
customShipment.seperate = customShipment.separate
|
||||
end
|
||||
customShipment.separate = customShipment.seperate
|
||||
|
||||
if customShipment.allowed == nil then
|
||||
customShipment.allowed = {}
|
||||
for k in pairs(team.GetAllTeams()) do
|
||||
table.insert(customShipment.allowed, k)
|
||||
end
|
||||
end
|
||||
|
||||
customShipment.name = name
|
||||
customShipment.default = DarkRP.DARKRP_LOADING
|
||||
customShipment.shipmodel = customShipment.shipmodel or shipmentmodel
|
||||
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["shipments"][customShipment.name] then return end
|
||||
|
||||
local valid, err, hints = DarkRP.validateShipment(customShipment)
|
||||
if not valid then DarkRP.error(string.format("Corrupt shipment: %s!\n%s", name or "", err), 2, hints) end
|
||||
|
||||
customShipment.spawn = customShipment.spawn and fp{DarkRP.simplerrRun, customShipment.spawn}
|
||||
customShipment.allowed = isnumber(customShipment.allowed) and {customShipment.allowed} or customShipment.allowed
|
||||
customShipment.customCheck = customShipment.customCheck and fp{DarkRP.simplerrRun, customShipment.customCheck}
|
||||
customShipment.CustomCheckFailMsg = isfunction(customShipment.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, customShipment.CustomCheckFailMsg} or customShipment.CustomCheckFailMsg
|
||||
|
||||
if not customShipment.noship then DarkRP.addToCategory(customShipment, "shipments", customShipment.category) end
|
||||
if customShipment.separate then DarkRP.addToCategory(customShipment, "weapons", customShipment.category) end
|
||||
|
||||
shipByName[string.lower(name or "")] = table.insert(CustomShipments, customShipment)
|
||||
util.PrecacheModel(customShipment.model)
|
||||
end
|
||||
AddCustomShipment = DarkRP.createShipment
|
||||
|
||||
function DarkRP.removeShipment(i)
|
||||
local ship = CustomShipments[i]
|
||||
shipByName[ship.name] = nil
|
||||
removeCustomItem(CustomShipments, "shipments", "onShipmentRemoved", true, i)
|
||||
end
|
||||
|
||||
function DarkRP.createVehicle(Name_of_vehicle, model, price, Jobs_that_can_buy_it, customcheck)
|
||||
local vehicle = istable(Name_of_vehicle) and Name_of_vehicle or
|
||||
{name = Name_of_vehicle, model = model, price = price, allowed = Jobs_that_can_buy_it, customCheck = customcheck}
|
||||
|
||||
vehicle.default = DarkRP.DARKRP_LOADING
|
||||
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["vehicles"][vehicle.name] then return end
|
||||
|
||||
local found = false
|
||||
for k in pairs(DarkRP.getAvailableVehicles()) do
|
||||
if string.lower(k) == string.lower(vehicle.name) then found = true break end
|
||||
end
|
||||
|
||||
local valid, err, hints = DarkRP.validateVehicle(vehicle)
|
||||
if not valid then DarkRP.error(string.format("Corrupt vehicle: %s!\n%s", vehicle.name or "", err), 2, hints) end
|
||||
|
||||
if not found then DarkRP.error("Vehicle invalid: " .. vehicle.name .. ". Unknown vehicle name.", 2) end
|
||||
|
||||
vehicle.customCheck = vehicle.customCheck and fp{DarkRP.simplerrRun, vehicle.customCheck}
|
||||
vehicle.CustomCheckFailMsg = isfunction(vehicle.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, vehicle.CustomCheckFailMsg} or vehicle.CustomCheckFailMsg
|
||||
|
||||
table.insert(CustomVehicles, vehicle)
|
||||
DarkRP.addToCategory(vehicle, "vehicles", vehicle.category)
|
||||
end
|
||||
AddCustomVehicle = DarkRP.createVehicle
|
||||
|
||||
DarkRP.removeVehicle = fp{removeCustomItem, CustomVehicles, "vehicles", "onVehicleRemoved", true}
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Decides whether a custom job or shipmet or whatever can be used in a certain map
|
||||
---------------------------------------------------------------------------]]
|
||||
function GM:CustomObjFitsMap(obj)
|
||||
if not obj or not obj.maps then return true end
|
||||
|
||||
local map = string.lower(game.GetMap())
|
||||
for _, v in pairs(obj.maps) do
|
||||
if string.lower(v) == map then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
DarkRPEntities = {}
|
||||
function DarkRP.createEntity(name, entity, model, price, max, command, classes, CustomCheck)
|
||||
local tableSyntaxUsed = istable(entity)
|
||||
|
||||
local tblEnt = tableSyntaxUsed and entity or
|
||||
{ent = entity, model = model, price = price, max = max,
|
||||
cmd = command, allowed = classes, customCheck = CustomCheck}
|
||||
tblEnt.name = name
|
||||
tblEnt.default = DarkRP.DARKRP_LOADING
|
||||
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["entities"][tblEnt.name] then return end
|
||||
|
||||
if isnumber(tblEnt.allowed) then
|
||||
tblEnt.allowed = {tblEnt.allowed}
|
||||
end
|
||||
|
||||
local valid, err, hints = DarkRP.validateEntity(tblEnt)
|
||||
if not valid then DarkRP.error(string.format("Corrupt entity: %s!\n%s", name or "", err), 2, hints) end
|
||||
|
||||
tblEnt.customCheck = tblEnt.customCheck and fp{DarkRP.simplerrRun, tblEnt.customCheck}
|
||||
tblEnt.CustomCheckFailMsg = isfunction(tblEnt.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, tblEnt.CustomCheckFailMsg} or tblEnt.CustomCheckFailMsg
|
||||
tblEnt.getPrice = tblEnt.getPrice and fp{DarkRP.simplerrRun, tblEnt.getPrice}
|
||||
tblEnt.getMax = tblEnt.getMax and fp{DarkRP.simplerrRun, tblEnt.getMax}
|
||||
tblEnt.spawn = tblEnt.spawn and fp{DarkRP.simplerrRun, tblEnt.spawn}
|
||||
|
||||
-- if SERVER and FPP then
|
||||
-- FPP.AddDefaultBlocked(blockTypes, tblEnt.ent)
|
||||
-- end
|
||||
|
||||
table.insert(DarkRPEntities, tblEnt)
|
||||
DarkRP.addToCategory(tblEnt, "entities", tblEnt.category)
|
||||
timer.Simple(0, function() addEntityCommands(tblEnt) end)
|
||||
end
|
||||
AddEntity = DarkRP.createEntity
|
||||
|
||||
DarkRP.removeEntity = fp{removeCustomItem, DarkRPEntities, "entities", "onEntityRemoved", true}
|
||||
|
||||
-- here for backwards compatibility
|
||||
DarkRPAgendas = {}
|
||||
|
||||
local agendas = {}
|
||||
-- Returns the agenda managed by the player
|
||||
plyMeta.getAgenda = fn.Compose{fn.Curry(fn.Flip(fn.GetValue), 2)(DarkRPAgendas), plyMeta.Team}
|
||||
|
||||
-- Returns the agenda this player is member of
|
||||
function plyMeta:getAgendaTable()
|
||||
return agendas[self:Team()]
|
||||
end
|
||||
|
||||
DarkRP.getAgendas = fp{fn.Id, agendas}
|
||||
|
||||
function DarkRP.createAgenda(Title, Manager, Listeners)
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["agendas"][Title] then return end
|
||||
|
||||
local agenda = {Manager = Manager, Title = Title, Listeners = Listeners, ManagersByKey = {}}
|
||||
agenda.default = DarkRP.DARKRP_LOADING
|
||||
|
||||
local valid, err, hints = DarkRP.validateAgenda(agenda)
|
||||
if not valid then DarkRP.error(string.format("Corrupt agenda: %s!\n%s", agenda.Title or "", err), 2, hints) end
|
||||
|
||||
for _, v in pairs(agenda.Listeners) do
|
||||
agendas[v] = agenda
|
||||
end
|
||||
|
||||
for _, v in pairs(istable(agenda.Manager) and agenda.Manager or {agenda.Manager}) do
|
||||
agendas[v] = agenda
|
||||
DarkRPAgendas[v] = agenda -- backwards compat
|
||||
agenda.ManagersByKey[v] = true
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
timer.Simple(0, function()
|
||||
-- Run after scripts have loaded
|
||||
agenda.text = hook.Run("agendaUpdated", nil, agenda, "")
|
||||
end)
|
||||
end
|
||||
end
|
||||
AddAgenda = DarkRP.createAgenda
|
||||
|
||||
function DarkRP.removeAgenda(title)
|
||||
local agenda
|
||||
for k, v in pairs(agendas) do
|
||||
if v.Title == title then
|
||||
agenda = v
|
||||
agendas[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(DarkRPAgendas) do
|
||||
if v.Title == title then DarkRPAgendas[k] = nil end
|
||||
end
|
||||
hook.Run("onAgendaRemoved", title, agenda)
|
||||
end
|
||||
|
||||
GM.DarkRPGroupChats = {}
|
||||
local groupChatNumber = 0
|
||||
function DarkRP.createGroupChat(funcOrTeam, ...)
|
||||
local gm = GM or GAMEMODE
|
||||
gm.DarkRPGroupChats = gm.DarkRPGroupChats or {}
|
||||
if DarkRP.DARKRP_LOADING then
|
||||
groupChatNumber = groupChatNumber + 1
|
||||
if DarkRP.disabledDefaults["groupchat"][groupChatNumber] then return end
|
||||
end
|
||||
-- People can enter either functions or a list of teams as parameter(s)
|
||||
if isfunction(funcOrTeam) then
|
||||
table.insert(gm.DarkRPGroupChats, fp{DarkRP.simplerrRun, funcOrTeam})
|
||||
else
|
||||
local teams = {funcOrTeam, ...}
|
||||
table.insert(gm.DarkRPGroupChats, function(ply) return table.HasValue(teams, ply:Team()) end)
|
||||
end
|
||||
end
|
||||
GM.AddGroupChat = function(_, ...) DarkRP.createGroupChat(...) end
|
||||
|
||||
DarkRP.removeGroupChat = fp{removeCustomItem, GM.DarkRPGroupChats, nil, "onGroupChatRemoved", false}
|
||||
|
||||
DarkRP.getGroupChats = fp{fn.Id, GM.DarkRPGroupChats}
|
||||
|
||||
GM.AmmoTypes = {}
|
||||
|
||||
function DarkRP.createAmmoType(ammoType, name, model, price, amountGiven, customCheck)
|
||||
local gm = GM or GAMEMODE
|
||||
gm.AmmoTypes = gm.AmmoTypes or {}
|
||||
local ammo = istable(name) and name or {
|
||||
name = name,
|
||||
model = model,
|
||||
price = price,
|
||||
amountGiven = amountGiven,
|
||||
customCheck = customCheck
|
||||
}
|
||||
ammo.ammoType = ammoType
|
||||
ammo.default = DarkRP.DARKRP_LOADING
|
||||
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["ammo"][ammo.name] then return end
|
||||
|
||||
ammo.customCheck = ammo.customCheck and fp{DarkRP.simplerrRun, ammo.customCheck}
|
||||
ammo.CustomCheckFailMsg = isfunction(ammo.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, ammo.CustomCheckFailMsg} or ammo.CustomCheckFailMsg
|
||||
ammo.id = table.insert(gm.AmmoTypes, ammo)
|
||||
|
||||
DarkRP.addToCategory(ammo, "ammo", ammo.category)
|
||||
end
|
||||
GM.AddAmmoType = function(_, ...) DarkRP.createAmmoType(...) end
|
||||
|
||||
DarkRP.removeAmmoType = fp{removeCustomItem, GM.AmmoTypes, "ammo", "onAmmoTypeRemoved", true}
|
||||
|
||||
local demoteGroups = {}
|
||||
function DarkRP.createDemoteGroup(name, tbl)
|
||||
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["demotegroups"][name] then return end
|
||||
if not tbl or not tbl[1] then error("No members in the demote group!") end
|
||||
|
||||
local set = demoteGroups[tbl[1]] or disjoint.MakeSet(tbl[1])
|
||||
set.name = name
|
||||
for i = 2, #tbl do
|
||||
set = (demoteGroups[tbl[i]] or disjoint.MakeSet(tbl[i])) + set
|
||||
set.name = name
|
||||
end
|
||||
|
||||
for _, teamNr in ipairs(tbl) do
|
||||
if demoteGroups[teamNr] then
|
||||
-- Unify the sets if there was already one there
|
||||
demoteGroups[teamNr] = demoteGroups[teamNr] + set
|
||||
else
|
||||
demoteGroups[teamNr] = set
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.removeDemoteGroup(name)
|
||||
local foundSet
|
||||
for k, v in pairs(demoteGroups) do
|
||||
local set = disjoint.FindSet(v)
|
||||
if set.name == name then
|
||||
foundSet = set
|
||||
demoteGroups[k] = nil
|
||||
end
|
||||
end
|
||||
hook.Run("onDemoteGroupRemoved", name, foundSet)
|
||||
end
|
||||
|
||||
function DarkRP.getDemoteGroup(teamNr)
|
||||
demoteGroups[teamNr] = demoteGroups[teamNr] or disjoint.MakeSet(teamNr)
|
||||
return disjoint.FindSet(demoteGroups[teamNr])
|
||||
end
|
||||
|
||||
DarkRP.getDemoteGroups = fp{fn.Id, demoteGroups}
|
||||
|
||||
local categories = {
|
||||
jobs = {},
|
||||
entities = {},
|
||||
shipments = {},
|
||||
weapons = {},
|
||||
vehicles = {},
|
||||
ammo = {},
|
||||
}
|
||||
local categoriesMerged = false -- whether categories and custom items are merged.
|
||||
|
||||
DarkRP.getCategories = fp{fn.Id, categories}
|
||||
|
||||
local categoryOrder = function(a, b)
|
||||
local aso = a.sortOrder or 100
|
||||
local bso = b.sortOrder or 100
|
||||
return aso < bso or aso == bso and a.name < b.name
|
||||
end
|
||||
|
||||
local function insertCategory(destination, tbl)
|
||||
-- Override existing category of applicable
|
||||
for k, cat in pairs(destination) do
|
||||
if cat.name ~= tbl.name then continue end
|
||||
|
||||
destination[k] = tbl
|
||||
tbl.members = cat.members
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(destination, tbl)
|
||||
local i = #destination
|
||||
|
||||
while i > 1 do
|
||||
if categoryOrder(destination[i - 1], tbl) then break end
|
||||
destination[i - 1], destination[i] = destination[i], destination[i - 1]
|
||||
i = i - 1
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.createCategory(tbl)
|
||||
local valid, err, hints = DarkRP.validateCategory(tbl)
|
||||
if not valid then DarkRP.error(string.format("Corrupt category: %s!\n%s", tbl.name or "", err), 2, hints) end
|
||||
tbl.members = {}
|
||||
|
||||
local destination = categories[tbl.categorises]
|
||||
insertCategory(destination, tbl)
|
||||
|
||||
-- Too many people made the mistake of not creating a category for weapons as well as shipments
|
||||
-- when having shipments that can also be sold separately.
|
||||
if tbl.categorises == "shipments" then
|
||||
insertCategory(categories.weapons, table.Copy(tbl))
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.addToCategory(item, kind, cat)
|
||||
cat = cat or "Другое"
|
||||
item.category = cat
|
||||
|
||||
-- The merge process will take care of the category:
|
||||
if not categoriesMerged then return end
|
||||
|
||||
-- Post-merge: manual insertion into category
|
||||
local cats = categories[kind]
|
||||
for _, c in ipairs(cats) do
|
||||
if c.name ~= cat then continue end
|
||||
|
||||
insertCategory(c.members, item)
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.errorNoHalt(string.format([[The category of "%s" ("%s") does not exist!]], item.name, cat), 2, {
|
||||
"Make sure the category is created with DarkRP.createCategory.",
|
||||
"The category name is case sensitive!",
|
||||
"Categories must be created before DarkRP finished loading.",
|
||||
})
|
||||
end
|
||||
|
||||
function DarkRP.removeFromCategory(item, kind)
|
||||
local cats = categories[kind]
|
||||
if not cats then DarkRP.error(string.format("Invalid category kind '%s'.", kind), 2) end
|
||||
local cat = item.category
|
||||
if not cat then return end
|
||||
for _, v in pairs(cats) do
|
||||
if v.name ~= item.category then continue end
|
||||
for k, mem in pairs(v.members) do
|
||||
if mem ~= item then continue end
|
||||
table.remove(v.members, k)
|
||||
break
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Assign custom stuff to their categories
|
||||
local function mergeCategories(customs, catKind, path)
|
||||
local cats = categories[catKind]
|
||||
local catByName = {}
|
||||
for _, v in pairs(cats) do catByName[v.name] = v end
|
||||
for _, v in pairs(customs) do
|
||||
-- Override default thing categories:
|
||||
local catName = v.default and (GAMEMODE.Config.CategoryOverride[catKind] or {})[v.name] or v.category or "Другое"
|
||||
local cat = catByName[catName]
|
||||
if not cat then
|
||||
DarkRP.errorNoHalt(string.format([[The category of "%s" ("%s") does not exist!]], v.name, catName), 3, {
|
||||
"Make sure the category is created with DarkRP.createCategory.",
|
||||
"The category name is case sensitive!",
|
||||
"Categories must be created before DarkRP finished loading."
|
||||
}, path, -1, path)
|
||||
cat = catByName.Другое
|
||||
end
|
||||
|
||||
cat.members = cat.members or {}
|
||||
table.insert(cat.members, v)
|
||||
end
|
||||
|
||||
-- Sort category members
|
||||
for _, v in pairs(cats) do table.sort(v.members, categoryOrder) end
|
||||
end
|
||||
|
||||
hook.Add("loadCustomDarkRPItems", "mergeCategories", function()
|
||||
local shipments = fn.Filter(fc{fn.Not, fp{fn.GetValue, "noship"}}, CustomShipments)
|
||||
local guns = fn.Filter(fp{fn.GetValue, "separate"}, CustomShipments)
|
||||
|
||||
mergeCategories(RPExtraTeams, "jobs", "your jobs")
|
||||
mergeCategories(DarkRPEntities, "entities", "your custom entities")
|
||||
mergeCategories(shipments, "shipments", "your custom shipments")
|
||||
mergeCategories(guns, "weapons", "your custom weapons")
|
||||
mergeCategories(CustomVehicles, "vehicles", "your custom vehicles")
|
||||
mergeCategories(GAMEMODE.AmmoTypes, "ammo", "your custom ammo")
|
||||
|
||||
categoriesMerged = true
|
||||
end)
|
||||
100
gamemodes/darkrp/gamemode/modules/base/sh_entityvars.lua
Normal file
100
gamemodes/darkrp/gamemode/modules/base/sh_entityvars.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
DarkRP.RegisteredDarkRPVarsMaxId = DarkRP.RegisteredDarkRPVarsMaxId or 0
|
||||
DarkRP.RegisteredDarkRPVars = DarkRP.RegisteredDarkRPVars or {}
|
||||
DarkRP.RegisteredDarkRPVarsById = DarkRP.RegisteredDarkRPVarsById or {}
|
||||
|
||||
-- the amount of bits assigned to the value that determines which DarkRPVar we're sending/receiving
|
||||
local DARKRP_ID_BITS = 8
|
||||
local UNKNOWN_DARKRPVAR = 255 -- Should be equal to 2^DARKRP_ID_BITS - 1
|
||||
DarkRP.DARKRP_ID_BITS = DARKRP_ID_BITS
|
||||
|
||||
function DarkRP.registerDarkRPVar(name, writeFn, readFn)
|
||||
-- After a reload, only update the write and read function
|
||||
if DarkRP.RegisteredDarkRPVars[name] then
|
||||
DarkRP.RegisteredDarkRPVars[name].writeFn = writeFn
|
||||
DarkRP.RegisteredDarkRPVars[name].readFn = readFn
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.RegisteredDarkRPVarsMaxId = DarkRP.RegisteredDarkRPVarsMaxId + 1
|
||||
|
||||
-- UNKNOWN_DARKRPVAR is reserved for unknown values
|
||||
if DarkRP.RegisteredDarkRPVarsMaxId >= UNKNOWN_DARKRPVAR then DarkRP.error(string.format("Too many DarkRPVar registrations! DarkRPVar '%s' triggered this error", name), 2) end
|
||||
|
||||
DarkRP.RegisteredDarkRPVars[name] = {id = DarkRP.RegisteredDarkRPVarsMaxId, name = name, writeFn = writeFn, readFn = readFn}
|
||||
DarkRP.RegisteredDarkRPVarsById[DarkRP.RegisteredDarkRPVarsMaxId] = DarkRP.RegisteredDarkRPVars[name]
|
||||
end
|
||||
|
||||
-- Unknown values have unknown types and unknown identifiers, so this is sent inefficiently
|
||||
local function writeUnknown(name, value)
|
||||
net.WriteUInt(UNKNOWN_DARKRPVAR, DARKRP_ID_BITS)
|
||||
net.WriteString(name)
|
||||
net.WriteType(value)
|
||||
end
|
||||
|
||||
-- Read the value of a DarkRPVar that was not registered
|
||||
local function readUnknown()
|
||||
return net.ReadString(), net.ReadType(net.ReadUInt(8))
|
||||
end
|
||||
|
||||
function DarkRP.writeNetDarkRPVar(name, value)
|
||||
local DarkRPVar = DarkRP.RegisteredDarkRPVars[name]
|
||||
if not DarkRPVar then return writeUnknown(name, value) end
|
||||
|
||||
net.WriteUInt(DarkRPVar.id, DARKRP_ID_BITS)
|
||||
return DarkRPVar.writeFn(value)
|
||||
end
|
||||
|
||||
function DarkRP.writeNetDarkRPVarRemoval(name)
|
||||
local DarkRPVar = DarkRP.RegisteredDarkRPVars[name]
|
||||
if not DarkRPVar then
|
||||
net.WriteUInt(UNKNOWN_DARKRPVAR, DARKRP_ID_BITS)
|
||||
net.WriteString(name)
|
||||
return
|
||||
end
|
||||
|
||||
net.WriteUInt(DarkRPVar.id, DARKRP_ID_BITS)
|
||||
end
|
||||
|
||||
function DarkRP.readNetDarkRPVar()
|
||||
local DarkRPVarId = net.ReadUInt(DARKRP_ID_BITS)
|
||||
local DarkRPVar = DarkRP.RegisteredDarkRPVarsById[DarkRPVarId]
|
||||
|
||||
if DarkRPVarId == UNKNOWN_DARKRPVAR then
|
||||
local name, value = readUnknown()
|
||||
|
||||
return name, value
|
||||
end
|
||||
|
||||
local val = DarkRPVar.readFn(value)
|
||||
|
||||
return DarkRPVar.name, val
|
||||
end
|
||||
|
||||
function DarkRP.readNetDarkRPVarRemoval()
|
||||
local id = net.ReadUInt(DARKRP_ID_BITS)
|
||||
return id == UNKNOWN_DARKRPVAR and net.ReadString() or DarkRP.RegisteredDarkRPVarsById[id].name
|
||||
end
|
||||
|
||||
-- The money is a double because it accepts higher values than Int and UInt, which are undefined for >32 bits
|
||||
DarkRP.registerDarkRPVar("money", net.WriteDouble, net.ReadDouble)
|
||||
DarkRP.registerDarkRPVar("salary", fp{fn.Flip(net.WriteInt), 32}, fp{net.ReadInt, 32})
|
||||
DarkRP.registerDarkRPVar("rpname", net.WriteString, net.ReadString)
|
||||
DarkRP.registerDarkRPVar("job", net.WriteString, net.ReadString)
|
||||
DarkRP.registerDarkRPVar("HasGunlicense", net.WriteBit, fc{tobool, net.ReadBit})
|
||||
DarkRP.registerDarkRPVar("Arrested", net.WriteBit, fc{tobool, net.ReadBit})
|
||||
DarkRP.registerDarkRPVar("wanted", net.WriteBit, fc{tobool, net.ReadBit})
|
||||
DarkRP.registerDarkRPVar("wantedReason", net.WriteString, net.ReadString)
|
||||
DarkRP.registerDarkRPVar("agenda", net.WriteString, net.ReadString)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
RP name override
|
||||
---------------------------------------------------------------------------]]
|
||||
local pmeta = FindMetaTable("Player")
|
||||
pmeta.SteamName = pmeta.SteamName or pmeta.Name
|
||||
function pmeta:Name()
|
||||
if not self:IsValid() then DarkRP.error("Attempt to call Name/Nick/GetName on a non-existing player!", SERVER and 1 or 2) end
|
||||
return GAMEMODE.Config.allowrpnames and self:getDarkRPVar("rpname")
|
||||
or self:SteamName()
|
||||
end
|
||||
pmeta.GetName = pmeta.Name
|
||||
pmeta.Nick = pmeta.Name
|
||||
@@ -0,0 +1,86 @@
|
||||
function GM:SetupMove(ply, mv, cmd)
|
||||
if ply:isArrested() then
|
||||
mv:SetMaxClientSpeed(self.Config.arrestspeed)
|
||||
end
|
||||
return self.Sandbox.SetupMove(self, ply, mv, cmd)
|
||||
end
|
||||
|
||||
function GM:StartCommand(ply, usrcmd)
|
||||
-- Used in arrest_stick and unarrest_stick but addons can use it too!
|
||||
local wep = ply:GetActiveWeapon()
|
||||
if wep:IsValid() and isfunction(wep.startDarkRPCommand) then
|
||||
wep:startDarkRPCommand(usrcmd)
|
||||
end
|
||||
end
|
||||
|
||||
function GM:OnPlayerChangedTeam(ply, oldTeam, newTeam)
|
||||
if RPExtraTeams[oldTeam] and RPExtraTeams[oldTeam].OnPlayerLeftTeam then
|
||||
RPExtraTeams[oldTeam].OnPlayerLeftTeam(ply, newTeam)
|
||||
end
|
||||
|
||||
if RPExtraTeams[newTeam] and RPExtraTeams[newTeam].OnPlayerChangedTeam then
|
||||
RPExtraTeams[newTeam].OnPlayerChangedTeam(ply, oldTeam, newTeam)
|
||||
end
|
||||
|
||||
if CLIENT then return end
|
||||
|
||||
local agenda = ply:getAgendaTable()
|
||||
|
||||
-- Remove agenda text when last manager left
|
||||
if agenda and agenda.ManagersByKey[oldTeam] then
|
||||
local found = false
|
||||
for man, _ in pairs(agenda.ManagersByKey) do
|
||||
if team.NumPlayers(man) > 0 then found = true break end
|
||||
end
|
||||
if not found then agenda.text = nil end
|
||||
end
|
||||
|
||||
ply:setSelfDarkRPVar("agenda", agenda and agenda.text or nil)
|
||||
end
|
||||
|
||||
hook.Add("loadCustomDarkRPItems", "CAMI privs", function()
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_SeeEvents",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_GetAdminWeapons",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_SetDoorOwner",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_ChangeDoorSettings",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_AdminCommands",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_SetMoney",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_SetLicense",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
|
||||
for _, v in pairs(RPExtraTeams) do
|
||||
if not v.vote or v.admin and v.admin > 1 then continue end
|
||||
|
||||
local toAdmin = {[0] = "admin", [1] = "superadmin"}
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "DarkRP_GetJob_" .. v.command,
|
||||
MinAccess = toAdmin[v.admin or 0]-- Add privileges for the teams that are voted for
|
||||
}
|
||||
end
|
||||
end)
|
||||
1527
gamemodes/darkrp/gamemode/modules/base/sh_interface.lua
Normal file
1527
gamemodes/darkrp/gamemode/modules/base/sh_interface.lua
Normal file
File diff suppressed because it is too large
Load Diff
44
gamemodes/darkrp/gamemode/modules/base/sh_playerclass.lua
Normal file
44
gamemodes/darkrp/gamemode/modules/base/sh_playerclass.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
local PLAYER_CLASS = {}
|
||||
|
||||
-- Value of -1 = set to config value, if a corresponding setting exists
|
||||
PLAYER_CLASS.DisplayName = "DarkRP Base Player Class"
|
||||
PLAYER_CLASS.WalkSpeed = -1
|
||||
PLAYER_CLASS.RunSpeed = -1
|
||||
PLAYER_CLASS.DuckSpeed = 0.3
|
||||
PLAYER_CLASS.UnDuckSpeed = 0.3
|
||||
PLAYER_CLASS.TeammateNoCollide = false
|
||||
PLAYER_CLASS.StartHealth = -1
|
||||
|
||||
function PLAYER_CLASS:Loadout()
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:SetModel()
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:ShouldDrawLocal()
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:CreateMove(cmd)
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:CalcView(view)
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:GetHandsModel()
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:StartMove(mv, cmd)
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
function PLAYER_CLASS:FinishMove(mv)
|
||||
-- Let gamemode decide
|
||||
end
|
||||
|
||||
player_manager.RegisterClass("player_darkrp", PLAYER_CLASS, "player_sandbox")
|
||||
86
gamemodes/darkrp/gamemode/modules/base/sh_simplerr.lua
Normal file
86
gamemodes/darkrp/gamemode/modules/base/sh_simplerr.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
-- simplerrRun: Run a function with the given parameters and send any runtime errors to admins
|
||||
DarkRP.simplerrRun = fc{
|
||||
fn.Snd, -- On success ignore the first return value
|
||||
simplerr.wrapError,
|
||||
simplerr.wrapHook,
|
||||
simplerr.wrapLog,
|
||||
simplerr.safeCall
|
||||
}
|
||||
|
||||
-- error: throw a runtime error without exiting the stack
|
||||
-- parameters: msg, [stackNr], [hints], [path], [line]
|
||||
DarkRP.errorNoHalt = fc{
|
||||
simplerr.wrapHook,
|
||||
simplerr.wrapLog,
|
||||
simplerr.runError,
|
||||
function(msg, err, ...) return msg, err and err + 3 or 4, ... end -- Raise error level one higher
|
||||
}
|
||||
|
||||
-- error: throw a runtime error
|
||||
-- parameters: msg, [stackNr], [hints], [path], [line]
|
||||
DarkRP.error = fc{
|
||||
simplerr.wrapError,
|
||||
DarkRP.errorNoHalt
|
||||
}
|
||||
|
||||
-- Print errors from the server in the console and show a message in chat
|
||||
if CLIENT then
|
||||
local function showError(count, errs)
|
||||
local one = count == 1
|
||||
chat.AddText(Color(255, 0, 0), string.format("There %s %i Lua problem%s!", one and "is" or "are", count, one and "" or 's'))
|
||||
chat.AddText(color_white, "\tPlease check your console for more information!")
|
||||
chat.AddText(color_white, "\tNote: This error likely breaks your server. Make sure to solve the error!")
|
||||
|
||||
for i = 1, count do
|
||||
MsgC(Color(137, 222, 255), errs[i] .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("DarkRP_simplerrError", function()
|
||||
local count = net.ReadUInt(16)
|
||||
local errs = {}
|
||||
|
||||
for i = 1, count do
|
||||
table.insert(errs, net.ReadString())
|
||||
end
|
||||
|
||||
showError(count, errs)
|
||||
end)
|
||||
hook.Add("onSimplerrError", "DarkRP_Simplerr", function(err) showError(1, {err}) end)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Serverside part
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
util.AddNetworkString("DarkRP_simplerrError")
|
||||
|
||||
-- Send all errors to the client
|
||||
local function sendErrors(plys, errs)
|
||||
local count = #errs
|
||||
local one = count == 1
|
||||
|
||||
DarkRP.notify(plys, 1, 120, string.format("There %s %i Lua problem%s!\nPlease check your console for more information!", one and "is" or "are", count, one and "" or 's'))
|
||||
net.Start("DarkRP_simplerrError")
|
||||
net.WriteUInt(#errs, 16)
|
||||
fn.ForEach(fn.Flip(net.WriteString), errs)
|
||||
net.Send(plys)
|
||||
end
|
||||
|
||||
-- Annoy all admins when an error occurs
|
||||
local function annoyAdmins(err)
|
||||
local admins = fn.Filter(plyMeta.IsAdmin, player.GetAll())
|
||||
sendErrors(admins, {err})
|
||||
end
|
||||
hook.Add("onSimplerrError", "DarkRP_Simplerr", annoyAdmins)
|
||||
|
||||
-- Annoy joining admin with errors
|
||||
local function annoyAdmin(ply)
|
||||
if not IsValid(ply) or not ply:IsAdmin() then return end
|
||||
local errs = table.Copy(simplerr.getLog())
|
||||
if table.IsEmpty(errs) then return end
|
||||
|
||||
fn.Map(fp{fn.GetValue, "err"}, errs)
|
||||
sendErrors(ply, errs)
|
||||
end
|
||||
hook.Add("PlayerInitialSpawn", "DarkRP_Simplerr", function(ply) timer.Simple(1, fp{annoyAdmin, ply}) end)
|
||||
432
gamemodes/darkrp/gamemode/modules/base/sh_util.lua
Normal file
432
gamemodes/darkrp/gamemode/modules/base/sh_util.lua
Normal file
@@ -0,0 +1,432 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
Utility functions
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local vector = FindMetaTable("Vector")
|
||||
local meta = FindMetaTable("Player")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Decides whether the vector could be seen by the player if they were to look at it
|
||||
---------------------------------------------------------------------------]]
|
||||
function vector:isInSight(filter, ply)
|
||||
ply = ply or LocalPlayer()
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = self
|
||||
trace.filter = filter
|
||||
trace.mask = -1
|
||||
local TheTrace = util.TraceLine(trace)
|
||||
|
||||
return not TheTrace.Hit, TheTrace.HitPos
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Turn a money amount into a pretty string
|
||||
---------------------------------------------------------------------------]]
|
||||
local function attachCurrency(str)
|
||||
local config = GAMEMODE.Config
|
||||
return config.currencyLeft and config.currency .. str or str .. config.currency
|
||||
end
|
||||
|
||||
function DarkRP.formatMoney(n)
|
||||
if not n then return attachCurrency("0") end
|
||||
|
||||
if n >= 1e14 then return attachCurrency(tostring(n)) end
|
||||
if n <= -1e14 then return "-" .. attachCurrency(tostring(math.abs(n))) end
|
||||
|
||||
local config = GAMEMODE.Config
|
||||
|
||||
local negative = n < 0
|
||||
|
||||
n = tostring(math.abs(n))
|
||||
|
||||
local dp = string.find(n, ".", 1, true) or #n + 1
|
||||
|
||||
for i = dp - 4, 1, -3 do
|
||||
n = n:sub(1, i) .. config.currencyThousandSeparator .. n:sub(i + 1)
|
||||
end
|
||||
|
||||
-- Make sure the amount is padded with zeroes
|
||||
if n[#n - 1] == "." then
|
||||
n = n .. "0"
|
||||
end
|
||||
|
||||
return (negative and "-" or "") .. attachCurrency(n)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find a player based on given information
|
||||
|
||||
Note that there is a searching priority:
|
||||
* UserID
|
||||
* SteamID64
|
||||
* SteamID
|
||||
* Nick
|
||||
* SteamName
|
||||
|
||||
Note also that there are _separate_ loops. This is to make sure the function
|
||||
gives the same result, regardless of the order in which players are iterated
|
||||
over.
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.findPlayer(info)
|
||||
if not info or info == "" then return nil end
|
||||
local pls = player.GetAll()
|
||||
|
||||
local count = #pls
|
||||
local numberInfo = tonumber(info)
|
||||
|
||||
-- First check if the input matches a player by UserID or SteamID64. This is
|
||||
-- only necessary if the input can be parsed as a number.
|
||||
if numberInfo then
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if numberInfo == v:UserID() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if info == v:SteamID64() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local lowerInfo = string.lower(tostring(info))
|
||||
if string.StartsWith(lowerInfo, "steam_") then
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if info == v:SteamID() then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if string.find(string.lower(v:Nick()), lowerInfo, 1, true) ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
for k = 1, count do
|
||||
local v = pls[k]
|
||||
|
||||
if string.find(string.lower(v:SteamName()), lowerInfo, 1, true) ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find multiple players based on a string criterium
|
||||
Taken from FAdmin]]
|
||||
---------------------------------------------------------------------------*/
|
||||
function DarkRP.findPlayers(info)
|
||||
if not info then return nil end
|
||||
local pls = player.GetAll()
|
||||
local found = {}
|
||||
local players
|
||||
|
||||
if string.lower(info) == "*" or string.lower(info) == "<all>" then return pls end
|
||||
|
||||
local InfoPlayers = {}
|
||||
for A in string.gmatch(info .. ";", "([a-zA-Z0-9:_.]*)[;(,%s)%c]") do
|
||||
if A ~= "" then
|
||||
table.insert(InfoPlayers, A)
|
||||
end
|
||||
end
|
||||
|
||||
for _, PlayerInfo in ipairs(InfoPlayers) do
|
||||
-- Playerinfo is always to be treated as UserID when it's a number
|
||||
-- otherwise people with numbers in their names could get confused with UserID's of other players
|
||||
if tonumber(PlayerInfo) then
|
||||
local foundPlayer = Player(PlayerInfo)
|
||||
if IsValid(foundPlayer) and not found[foundPlayer] then
|
||||
found[foundPlayer] = true
|
||||
players = players or {}
|
||||
table.insert(players, foundPlayer)
|
||||
end
|
||||
continue
|
||||
end
|
||||
|
||||
local stringPlayerInfo = string.lower(PlayerInfo)
|
||||
for _, v in ipairs(pls) do
|
||||
-- Prevent duplicates
|
||||
if found[v] then continue end
|
||||
local steamId = v:SteamID()
|
||||
|
||||
-- Find by Steam ID
|
||||
if (PlayerInfo == steamId or steamId == "UNKNOWN") or
|
||||
-- Find by Partial Nick
|
||||
string.find(string.lower(v:Nick()), stringPlayerInfo, 1, true) ~= nil or
|
||||
-- Find by steam name
|
||||
(v.SteamName and string.find(string.lower(v:SteamName()), stringPlayerInfo, 1, true) ~= nil) then
|
||||
found[v] = true
|
||||
players = players or {}
|
||||
table.insert(players, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return players
|
||||
end
|
||||
|
||||
function meta:getEyeSightHitEntity(searchDistance, hitDistance, filter)
|
||||
searchDistance = searchDistance or 100
|
||||
hitDistance = (hitDistance or 15) * (hitDistance or 15)
|
||||
|
||||
filter = filter or function(p) return p:IsPlayer() and p ~= self end
|
||||
|
||||
self:LagCompensation(true)
|
||||
|
||||
local shootPos = self:GetShootPos()
|
||||
local entities = ents.FindInSphere(shootPos, searchDistance)
|
||||
local aimvec = self:GetAimVector()
|
||||
|
||||
local smallestDistance = math.huge
|
||||
local foundEnt
|
||||
|
||||
for _, ent in ipairs(entities) do
|
||||
if not IsValid(ent) or filter(ent) == false then continue end
|
||||
|
||||
local center = ent:GetPos()
|
||||
|
||||
-- project the center vector on the aim vector
|
||||
local projected = shootPos + (center - shootPos):Dot(aimvec) * aimvec
|
||||
|
||||
if aimvec:Dot((projected - shootPos):GetNormalized()) < 0 then continue end
|
||||
|
||||
-- the point on the model that has the smallest distance to your line of sight
|
||||
local nearestPoint = ent:NearestPoint(projected)
|
||||
local distance = nearestPoint:DistToSqr(projected)
|
||||
|
||||
if distance < smallestDistance then
|
||||
local trace = {
|
||||
start = self:GetShootPos(),
|
||||
endpos = nearestPoint,
|
||||
filter = {self, ent}
|
||||
}
|
||||
local traceLine = util.TraceLine(trace)
|
||||
if traceLine.Hit then continue end
|
||||
|
||||
smallestDistance = distance
|
||||
foundEnt = ent
|
||||
end
|
||||
end
|
||||
|
||||
self:LagCompensation(false)
|
||||
|
||||
if smallestDistance < hitDistance then
|
||||
return foundEnt, math.sqrt(smallestDistance)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Print the currently available vehicles
|
||||
---------------------------------------------------------------------------]]
|
||||
local function GetAvailableVehicles(ply)
|
||||
if SERVER and IsValid(ply) and not ply:IsAdmin() then return end
|
||||
local print = SERVER and ServerLog or Msg
|
||||
|
||||
print(DarkRP.getPhrase("rp_getvehicles") .. "\n")
|
||||
for k in pairs(DarkRP.getAvailableVehicles()) do
|
||||
print("\"" .. k .. "\"" .. "\n")
|
||||
end
|
||||
end
|
||||
if SERVER then
|
||||
concommand.Add("rp_getvehicles_sv", GetAvailableVehicles)
|
||||
else
|
||||
concommand.Add("rp_getvehicles", GetAvailableVehicles)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Whether a player has a DarkRP privilege
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:hasDarkRPPrivilege(priv)
|
||||
if FAdmin then
|
||||
return FAdmin.Access.PlayerHasPrivilege(self, priv)
|
||||
end
|
||||
return self:IsAdmin()
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Convenience function to return the players sorted by name
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.nickSortedPlayers()
|
||||
local plys = player.GetAll()
|
||||
table.sort(plys, function(a,b) return a:Nick() < b:Nick() end)
|
||||
return plys
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Convert a string to a table of arguments
|
||||
---------------------------------------------------------------------------]]
|
||||
local bitlshift, stringgmatch, stringsub, tableinsert = bit.lshift, string.gmatch, string.sub, table.insert
|
||||
function DarkRP.explodeArg(arg)
|
||||
local args = {}
|
||||
|
||||
local from, to, diff = 1, 0, 0
|
||||
local inQuotes, wasQuotes = false, false
|
||||
|
||||
for c in stringgmatch(arg, '.') do
|
||||
to = to + 1
|
||||
|
||||
if c == '"' then
|
||||
inQuotes = not inQuotes
|
||||
wasQuotes = true
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
if c == ' ' and not inQuotes then
|
||||
diff = wasQuotes and 1 or 0
|
||||
wasQuotes = false
|
||||
tableinsert(args, stringsub(arg, from + diff, to - 1 - diff))
|
||||
from = to + 1
|
||||
end
|
||||
end
|
||||
diff = wasQuotes and 1 or 0
|
||||
|
||||
if from ~= to + 1 then tableinsert(args, stringsub(arg, from + diff, to + 1 - bitlshift(diff, 1))) end
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Initialize Physics, throw an error on failure
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.ValidatedPhysicsInit(ent, solidType, hint)
|
||||
solidType = solidType or SOLID_VPHYSICS
|
||||
|
||||
if ent:PhysicsInit(solidType) then return true end
|
||||
|
||||
local class = ent:GetClass()
|
||||
|
||||
if solidType == SOLID_BSP then
|
||||
DarkRP.errorNoHalt(string.format("%s has no physics and will be motionless", class), 2, {
|
||||
"Is this a brush model? SOLID_BSP physics cannot initialize on entities that don't have brush models",
|
||||
"The physics limit may have been hit",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if solidType == SOLID_VPHYSICS then
|
||||
local mdl = ent:GetModel()
|
||||
|
||||
if not mdl or mdl == "" then
|
||||
DarkRP.errorNoHalt(string.format("Cannot init physics on entity \"%s\" because it has no model", class), 2, {hint})
|
||||
return false
|
||||
end
|
||||
|
||||
mdl = string.lower(mdl)
|
||||
|
||||
if util.IsValidProp(mdl) then
|
||||
-- Has physics, we must have hit the limit
|
||||
DarkRP.errorNoHalt(string.format("physics limit hit - %s will be motionless", class), 2, {hint})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if not file.Exists(mdl, "GAME") then
|
||||
DarkRP.errorNoHalt(string.format("%s has missing model \"%s\" and will be invisible and motionless", class, mdl), 2, {
|
||||
"Is the model path correct?",
|
||||
"Is the model from an addon that is not installed?",
|
||||
"Is the model from a game that isn't (properly) mounted? E.g. Counter Strike: Source",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
DarkRP.errorNoHalt(string.format("%s has model \"%s\" with no physics and will be motionless", class, mdl), 2, {
|
||||
"Does this model have an associated physics model (modelname.phy)?",
|
||||
"Is this model supposed to have physics? Many models, like effects and view models aren't made to have physics",
|
||||
hint
|
||||
})
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
DarkRP.errorNoHalt(string.format("Unable to initilize physics on entity \"%s\"", class, {hint}), 2)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Like tonumber, but makes sure it's an integer
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.toInt(value)
|
||||
value = tonumber(value)
|
||||
return value and math.floor(value)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Check the database for integrity errors. Use in cases when stuff doesn't load
|
||||
on restart, or you get corruption errors.
|
||||
---------------------------------------------------------------------------]]
|
||||
if SERVER then util.AddNetworkString("DarkRP_databaseCheckMessage") end
|
||||
if CLIENT then net.Receive("DarkRP_databaseCheckMessage", fc{print, net.ReadString}) end
|
||||
|
||||
local function checkDatabase(ply)
|
||||
local dbFile = SERVER and "sv.db" or "cl.db"
|
||||
local display = (CLIENT or not IsValid(ply)) and print or function(msg)
|
||||
net.Start("DarkRP_databaseCheckMessage")
|
||||
net.WriteString(msg)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
if SERVER and IsValid(ply) and not ply:IsSuperAdmin() then
|
||||
display("You must be superadmin")
|
||||
return
|
||||
end
|
||||
|
||||
if MySQLite and MySQLite.isMySQL() then
|
||||
display(string.format([[WARNING: DarkRP is using MySQL. This only
|
||||
checks the local SQLite database stored in the %s file in the
|
||||
garrysmod/ folder. The check will continue.]], dbFile))
|
||||
end
|
||||
|
||||
local check = sql.QueryValue("PRAGMA INTEGRITY_CHECK")
|
||||
if check == false then
|
||||
display([[The query to check the database failed. Shit's surely
|
||||
fucked, but the cause is unknown.]])
|
||||
return
|
||||
end
|
||||
|
||||
if check == "ok" then
|
||||
display(string.format("Your %s database file is good.", dbFile))
|
||||
return
|
||||
end
|
||||
|
||||
display(string.format([[There are errors in your %s database file. It's corrupt!
|
||||
|
||||
This can cause the following problems:
|
||||
- Data not loading, think of blocked models, doors, players' money and RP names
|
||||
- Settings resetting to their default values
|
||||
- Lua errors on startup
|
||||
|
||||
The cause of the problem is that the %s file in your garrysmod/ folder on
|
||||
%s is corrupt. How this came to be is unknown, but here's what you can do to solve it:
|
||||
|
||||
- Delete %s, and run a file integrity check. Warning: You will lose ALL data stored in it!
|
||||
- Take the file and try to repair it. This is sadly something that requires some technical knowledge,
|
||||
and may not always succeed.
|
||||
|
||||
The specific error, by the way, is as follows:
|
||||
%s
|
||||
]], dbFile, dbFile, SERVER and "the server" or "your own computer", dbFile, check))
|
||||
|
||||
end
|
||||
concommand.Add("darkrp_check_db_" .. (SERVER and "sv" or "cl"), checkDatabase)
|
||||
627
gamemodes/darkrp/gamemode/modules/base/sv_data.lua
Normal file
627
gamemodes/darkrp/gamemode/modules/base/sv_data.lua
Normal file
@@ -0,0 +1,627 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
Functions and variables
|
||||
---------------------------------------------------------------------------]]
|
||||
local setUpNonOwnableDoors,
|
||||
setUpTeamOwnableDoors,
|
||||
setUpGroupDoors,
|
||||
migrateDB
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Database initialize
|
||||
---------------------------------------------------------]]
|
||||
function DarkRP.initDatabase()
|
||||
MySQLite.begin()
|
||||
-- Gotta love the difference between SQLite and MySQL
|
||||
local is_mysql = MySQLite.isMySQL()
|
||||
local AUTOINCREMENT = is_mysql and "AUTO_INCREMENT" or "AUTOINCREMENT"
|
||||
-- in MySQL, the engine is set to InnoDB. InnoDB has been the default
|
||||
-- for a while, but people might be running old versions of MySQL.
|
||||
-- SQLite has no database engine, so it is not explicitly set.
|
||||
local ENGINE_INNODB = is_mysql and "ENGINE=InnoDB" or ""
|
||||
|
||||
-- Table that holds all position data (jail, spawns etc.)
|
||||
-- Queue these queries because other queries depend on the existence of the darkrp_position table
|
||||
-- Race conditions could occur if the queries are executed simultaneously
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_position(
|
||||
id INTEGER NOT NULL PRIMARY KEY ]] .. AUTOINCREMENT .. [[,
|
||||
map VARCHAR(45) NOT NULL,
|
||||
type CHAR(1) NOT NULL,
|
||||
x INTEGER NOT NULL,
|
||||
y INTEGER NOT NULL,
|
||||
z INTEGER NOT NULL
|
||||
) ]] .. ENGINE_INNODB .. [[;
|
||||
]])
|
||||
|
||||
-- team spawns require extra data
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_jobspawn(
|
||||
id INTEGER NOT NULL PRIMARY KEY REFERENCES darkrp_position(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
|
||||
teamcmd VARCHAR(255) NOT NULL
|
||||
) ]] .. ENGINE_INNODB .. [[;
|
||||
]])
|
||||
|
||||
-- This table is kept for compatibility with older addons and websites
|
||||
-- See https://github.com/FPtje/DarkRP/issues/819
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS playerinformation(
|
||||
uid BIGINT NOT NULL,
|
||||
steamID VARCHAR(50) NOT NULL PRIMARY KEY
|
||||
) ]] .. ENGINE_INNODB .. [[
|
||||
]])
|
||||
|
||||
-- Player information
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_player(
|
||||
uid BIGINT NOT NULL PRIMARY KEY,
|
||||
rpname VARCHAR(45),
|
||||
salary INTEGER NOT NULL DEFAULT 45,
|
||||
wallet BIGINT NOT NULL
|
||||
) ]] .. ENGINE_INNODB .. [[;
|
||||
]])
|
||||
|
||||
-- Door data
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_door(
|
||||
idx INTEGER NOT NULL,
|
||||
map VARCHAR(45) NOT NULL,
|
||||
title VARCHAR(25),
|
||||
isLocked BOOLEAN,
|
||||
isDisabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY(idx, map)
|
||||
) ]] .. ENGINE_INNODB .. [[;
|
||||
]])
|
||||
|
||||
-- Some doors are owned by certain teams
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_doorjobs(
|
||||
idx INTEGER NOT NULL,
|
||||
map VARCHAR(45) NOT NULL,
|
||||
job VARCHAR(255) NOT NULL,
|
||||
|
||||
PRIMARY KEY(idx, map, job)
|
||||
) ]] .. ENGINE_INNODB .. [[;
|
||||
]])
|
||||
|
||||
-- Door groups
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_doorgroups(
|
||||
idx INTEGER NOT NULL,
|
||||
map VARCHAR(45) NOT NULL,
|
||||
doorgroup VARCHAR(100) NOT NULL,
|
||||
|
||||
PRIMARY KEY(idx, map)
|
||||
) ]] .. ENGINE_INNODB .. [[
|
||||
]])
|
||||
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS darkrp_dbversion(version INTEGER NOT NULL PRIMARY KEY) ]] .. ENGINE_INNODB .. [[
|
||||
]])
|
||||
|
||||
-- Load the last DBVersion into DarkRP.DBVersion, to allow checks to see whether migration is needed.
|
||||
MySQLite.queueQuery([[
|
||||
SELECT MAX(version) AS version FROM darkrp_dbversion
|
||||
]], function(data)
|
||||
-- The database is created with the schema of the latest version. On
|
||||
-- initialization the version is not set yet. Set it to the latest
|
||||
-- version.
|
||||
if not data or not data[1] or not tonumber(data[1].version) then
|
||||
DarkRP.DBVersion = 20211228
|
||||
MySQLite.query([[
|
||||
REPLACE INTO darkrp_dbversion VALUES(20211228)
|
||||
]])
|
||||
else
|
||||
DarkRP.DBVersion = tonumber(data[1].version)
|
||||
end
|
||||
end)
|
||||
|
||||
MySQLite.commit(fp{migrateDB, -- Migrate the database
|
||||
function() -- Initialize the data after all the tables have been created
|
||||
setUpNonOwnableDoors()
|
||||
setUpTeamOwnableDoors()
|
||||
setUpGroupDoors()
|
||||
|
||||
if MySQLite.isMySQL() then -- In a listen server, the connection with the external database is often made AFTER the listen server host has joined,
|
||||
--so he walks around with the settings from the SQLite database
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
DarkRP.offlinePlayerData(v:SteamID(), function(data)
|
||||
local Data = data and data[1]
|
||||
if not IsValid(v) or not Data then return end
|
||||
|
||||
v:setDarkRPVar("rpname", Data.rpname)
|
||||
v:setSelfDarkRPVar("salary", Data.salary)
|
||||
v:setDarkRPVar("money", Data.wallet)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Call("DarkRPDBInitialized")
|
||||
end})
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Database migration
|
||||
backwards compatibility with older versions of DarkRP
|
||||
---------------------------------------------------------------------------]]
|
||||
function migrateDB(callback)
|
||||
-- Simple function that checks the database version, migrates if
|
||||
-- necessary, and recurses to perform the next migration, until the last
|
||||
-- migration has been performed.
|
||||
-- Calls callback when the migration is finished or not necessary.
|
||||
local function migrate(version)
|
||||
if version < 20160610 then
|
||||
MySQLite.begin()
|
||||
if MySQLite.isMySQL() then
|
||||
-- if only SQLite were this easy
|
||||
MySQLite.queueQuery([[DROP INDEX rpname ON darkrp_player]])
|
||||
else
|
||||
-- darkrp_player used to have a UNIQUE rpname field.
|
||||
-- This sucks, get rid of it
|
||||
MySQLite.queueQuery([[PRAGMA foreign_keys=OFF]])
|
||||
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE IF NOT EXISTS new_darkrp_player(
|
||||
uid BIGINT NOT NULL PRIMARY KEY,
|
||||
rpname VARCHAR(45),
|
||||
salary INTEGER NOT NULL DEFAULT 45,
|
||||
wallet INTEGER NOT NULL
|
||||
);
|
||||
]])
|
||||
|
||||
MySQLite.queueQuery([[INSERT INTO new_darkrp_player SELECT * FROM darkrp_player]])
|
||||
|
||||
MySQLite.queueQuery([[DROP TABLE darkrp_player]])
|
||||
|
||||
MySQLite.queueQuery([[ALTER TABLE new_darkrp_player RENAME TO darkrp_player]])
|
||||
|
||||
MySQLite.queueQuery([[PRAGMA foreign_keys=ON]])
|
||||
end
|
||||
MySQLite.queueQuery([[REPLACE INTO darkrp_dbversion VALUES(20160610)]])
|
||||
MySQLite.commit(fp{migrate, 20160610})
|
||||
return
|
||||
end
|
||||
|
||||
if version < 20181013 then
|
||||
-- migrate from darkrp_jobown to darkrp_doorjobs
|
||||
MySQLite.tableExists("darkrp_jobown", function(exists)
|
||||
if not exists then migrate(20181013) return end
|
||||
|
||||
MySQLite.begin()
|
||||
-- Create a temporary table that links job IDs to job commands
|
||||
MySQLite.queueQuery("CREATE TABLE IF NOT EXISTS TempJobCommands(id INT NOT NULL PRIMARY KEY, cmd VARCHAR(255) NOT NULL);")
|
||||
if MySQLite.isMySQL() then
|
||||
local jobCommands = {}
|
||||
for k, v in pairs(RPExtraTeams) do
|
||||
table.insert(jobCommands, "(" .. k .. "," .. MySQLite.SQLStr(v.command) .. ")")
|
||||
end
|
||||
|
||||
-- This WOULD work with SQLite if the implementation in GMod wasn't out of date.
|
||||
MySQLite.queueQuery("INSERT IGNORE INTO TempJobCommands VALUES " .. table.concat(jobCommands, ",") .. ";")
|
||||
else
|
||||
for k, v in pairs(RPExtraTeams) do
|
||||
MySQLite.queueQuery("INSERT INTO TempJobCommands VALUES(" .. k .. ", " .. MySQLite.SQLStr(v.command) .. ");")
|
||||
end
|
||||
end
|
||||
|
||||
MySQLite.queueQuery("REPLACE INTO darkrp_doorjobs SELECT darkrp_jobown.idx AS idx, darkrp_jobown.map AS map, TempJobCommands.cmd AS job FROM darkrp_jobown JOIN TempJobCommands ON darkrp_jobown.job = TempJobCommands.id;")
|
||||
|
||||
-- Clean up the transition table and the old table
|
||||
MySQLite.queueQuery("DROP TABLE TempJobCommands;")
|
||||
MySQLite.queueQuery("DROP TABLE darkrp_jobown;")
|
||||
MySQLite.queueQuery([[REPLACE INTO darkrp_dbversion VALUES(20181013)]])
|
||||
MySQLite.commit(fp{migrate, 20181013})
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
if version < 20181014 then
|
||||
MySQLite.query([[SELECT * FROM darkrp_jobspawn]], function(oldData)
|
||||
oldData = oldData or {}
|
||||
MySQLite.begin()
|
||||
|
||||
MySQLite.queueQuery([[DROP TABLE darkrp_jobspawn]])
|
||||
|
||||
MySQLite.queueQuery([[
|
||||
CREATE TABLE darkrp_jobspawn(
|
||||
id INTEGER NOT NULL PRIMARY KEY REFERENCES darkrp_position(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE,
|
||||
|
||||
teamcmd VARCHAR(255) NOT NULL
|
||||
);
|
||||
]])
|
||||
|
||||
for i, row in pairs(oldData) do
|
||||
local teamcmd = (RPExtraTeams[tonumber(row.team)] or {}).command
|
||||
if not teamcmd then continue end
|
||||
|
||||
MySQLite.queueQuery(string.format([[INSERT INTO darkrp_jobspawn(id, teamcmd) VALUES(%s, %s)]], row.id, MySQLite.SQLStr(teamcmd)))
|
||||
end
|
||||
|
||||
MySQLite.queueQuery([[REPLACE INTO darkrp_dbversion VALUES(20181014)]])
|
||||
|
||||
MySQLite.commit(fp{migrate, 20181014})
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
if version < 20190914 then
|
||||
MySQLite.begin()
|
||||
-- Migration not necessary for SQLite, since BIGINT and
|
||||
-- INTEGER are considered the same in SQLite
|
||||
-- https://www.sqlite.org/datatype3.html
|
||||
if MySQLite.isMySQL() then
|
||||
MySQLite.queueQuery([[DROP TRIGGER IF EXISTS JobPositionFKDelete]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_player MODIFY wallet BIGINT;]])
|
||||
end
|
||||
MySQLite.queueQuery([[REPLACE INTO darkrp_dbversion VALUES(20190914)]])
|
||||
MySQLite.commit(fp{migrate, 20190914})
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if version < 20211228 then
|
||||
MySQLite.begin()
|
||||
-- Migrate all tables to InnoDB if they weren't already.
|
||||
-- See https://github.com/FPtje/DarkRP/issues/3157
|
||||
if MySQLite.isMySQL() then
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_dbversion ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_door ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_doorgroups ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_doorjobs ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_jobspawn ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_player ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE darkrp_position ENGINE = InnoDB;]])
|
||||
MySQLite.queueQuery([[ALTER TABLE playerinformation ENGINE = InnoDB;]])
|
||||
end
|
||||
|
||||
MySQLite.queueQuery([[REPLACE INTO darkrp_dbversion VALUES(20211228)]])
|
||||
MySQLite.commit(fp{migrate, 20211228})
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- All migrations finished
|
||||
callback()
|
||||
end
|
||||
migrate(DarkRP.DBVersion)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Players
|
||||
---------------------------------------------------------]]
|
||||
function DarkRP.storeRPName(ply, name)
|
||||
if not name or string.len(name) < 2 then return end
|
||||
hook.Call("onPlayerChangedName", nil, ply, ply:getDarkRPVar("rpname"), name)
|
||||
ply:setDarkRPVar("rpname", name)
|
||||
|
||||
MySQLite.query([[UPDATE darkrp_player SET rpname = ]] .. MySQLite.SQLStr(name) .. [[ WHERE UID = ]] .. ply:SteamID64() .. ";")
|
||||
MySQLite.query([[UPDATE darkrp_player SET rpname = ]] .. MySQLite.SQLStr(name) .. [[ WHERE UID = ]] .. ply:UniqueID() .. ";")
|
||||
end
|
||||
|
||||
function DarkRP.retrieveRPNames(name, callback)
|
||||
MySQLite.query("SELECT COUNT(*) AS count FROM darkrp_player WHERE rpname = " .. MySQLite.SQLStr(name) .. ";", function(r)
|
||||
callback(tonumber(r[1].count) > 0)
|
||||
end)
|
||||
end
|
||||
|
||||
function DarkRP.offlinePlayerData(steamid, callback, failed)
|
||||
local sid64 = util.SteamIDTo64(steamid)
|
||||
local uniqueid = util.CRC("gm_" .. string.upper(steamid) .. "_gm")
|
||||
|
||||
MySQLite.query(string.format([[REPLACE INTO playerinformation VALUES(%s, %s);]], MySQLite.SQLStr(sid64), MySQLite.SQLStr(steamid)), nil, failed)
|
||||
|
||||
local query = [[
|
||||
SELECT rpname, wallet, salary, "SID64" AS kind
|
||||
FROM darkrp_player
|
||||
where uid = %s
|
||||
|
||||
UNION
|
||||
|
||||
SELECT rpname, wallet, salary, "UniqueID" AS kind
|
||||
FROM darkrp_player
|
||||
where uid = %s
|
||||
;
|
||||
]]
|
||||
|
||||
MySQLite.query(
|
||||
query:format(sid64, uniqueid),
|
||||
function(data, ...)
|
||||
-- The database has no record of the player data in SteamID64 form
|
||||
-- Otherwise the first row would have kind SID64
|
||||
if data and data[1] and data[1].kind == "UniqueID" then
|
||||
-- The rpname must be unique
|
||||
-- adding a new row with uid = SteamID64, but the same rpname will remove the uid=UniqueID row
|
||||
|
||||
local replquery = [[
|
||||
REPLACE INTO darkrp_player(uid, rpname, wallet, salary)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
]]
|
||||
|
||||
MySQLite.begin()
|
||||
MySQLite.queueQuery(
|
||||
replquery:format(
|
||||
sid64,
|
||||
data[1].rpname == "NULL" and "NULL" or MySQLite.SQLStr(data[1].rpname),
|
||||
data[1].wallet,
|
||||
data[1].salary
|
||||
),
|
||||
nil,
|
||||
failed
|
||||
)
|
||||
MySQLite.commit()
|
||||
end
|
||||
|
||||
return callback and callback(data, ...)
|
||||
end
|
||||
, failed
|
||||
)
|
||||
end
|
||||
|
||||
function DarkRP.retrievePlayerData(ply, callback, failed, attempts, err)
|
||||
attempts = attempts or 0
|
||||
|
||||
if attempts > 3 then return failed(err) end
|
||||
|
||||
DarkRP.offlinePlayerData(ply:SteamID(), callback, function(sqlErr)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
DarkRP.retrievePlayerData(ply, callback, failed, attempts + 1, sqlErr)
|
||||
end)
|
||||
end
|
||||
|
||||
function DarkRP.createPlayerData(ply, name, wallet, salary, onError)
|
||||
MySQLite.query([[REPLACE INTO darkrp_player VALUES(]] ..
|
||||
ply:SteamID64() .. [[, ]] ..
|
||||
MySQLite.SQLStr(utf8.force(name)) .. [[, ]] ..
|
||||
salary .. [[, ]] ..
|
||||
wallet .. ");", nil, onError)
|
||||
|
||||
-- Backwards compatibility
|
||||
MySQLite.query([[REPLACE INTO darkrp_player VALUES(]] ..
|
||||
ply:UniqueID() .. [[, ]] ..
|
||||
MySQLite.SQLStr(utf8.force(name)) .. [[, ]] ..
|
||||
salary .. [[, ]] ..
|
||||
wallet .. ");", nil, onError)
|
||||
end
|
||||
|
||||
function DarkRP.storeMoney(ply, amount)
|
||||
if not isnumber(amount) or amount < 0 or amount >= 1 / 0 then
|
||||
DarkRP.errorNoHalt("Some addon attempted to store a invalid money amount " .. tostring(amount) .. " for Player " .. ply:Nick() .. " (" .. ply:SteamID() .. ")", 1, {
|
||||
"This money amount will not be stored in the database, but it may be set in the game.",
|
||||
"The database simply stores the last valid, non-negative amount of money.",
|
||||
"Please try to find the very first time this error happened for this player. Then look at the files mentioned in this error.",
|
||||
"That will tell you which addon is causing this.",
|
||||
"IMPORTANT: This is NOT a DarkRP bug!",
|
||||
"Note: The player can simply rejoin to fix their negative money, until whatever causes this happens again."
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Also keep deprecated UniqueID data at least somewhat up to date
|
||||
MySQLite.query([[UPDATE darkrp_player SET wallet = ]] .. amount .. [[ WHERE uid = ]] .. ply:UniqueID() .. [[ OR uid = ]] .. ply:SteamID64())
|
||||
end
|
||||
|
||||
function DarkRP.storeOfflineMoney(sid64, amount)
|
||||
if isnumber(sid64) or isstring(sid64) and string.len(sid64) < 17 then -- smaller than 76561197960265728 is not a SteamID64
|
||||
DarkRP.errorNoHalt([[Some addon is giving DarkRP.storeOfflineMoney a UniqueID as its first argument, but this function now expects a SteamID64]], 1, {
|
||||
"The function used to take UniqueIDs, but it does not anymore.",
|
||||
"If you are a server owner, please look closely to the files mentioned in this error",
|
||||
"After all, these files will tell you WHICH addon is doing it",
|
||||
"This is NOT a DarkRP bug!",
|
||||
"Your server will continue working normally",
|
||||
"But whichever addon just tried to store an offline player's money",
|
||||
"Will NOT take effect!"
|
||||
})
|
||||
end
|
||||
|
||||
if not isnumber(amount) or amount < 0 or amount >= 1 / 0 then
|
||||
DarkRP.errorNoHalt("Some addon attempted to store a invalid money amount " .. tostring(amount) .. " for an offline player with steamID64 " .. sid64, 1, {
|
||||
"This money amount will not be stored in the database.",
|
||||
"Please try to find the very first time this error happened for this player. Then look at the files mentioned in this error.",
|
||||
"That will tell you which addon is causing this.",
|
||||
"IMPORTANT: This is NOT a DarkRP bug!"
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Also store on deprecated UniqueID
|
||||
local uniqueid = util.CRC("gm_" .. string.upper(util.SteamIDFrom64(sid64)) .. "_gm")
|
||||
MySQLite.query([[UPDATE darkrp_player SET wallet = ]] .. amount .. [[ WHERE uid = ]] .. uniqueid .. [[ OR uid = ]] .. sid64)
|
||||
end
|
||||
|
||||
local function resetAllMoney(ply, cmd, args)
|
||||
if ply:EntIndex() ~= 0 and not ply:IsSuperAdmin() then return end
|
||||
MySQLite.query("UPDATE darkrp_player SET wallet = " .. GAMEMODE.Config.startingmoney .. " ;")
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
v:setDarkRPVar("money", GAMEMODE.Config.startingmoney)
|
||||
end
|
||||
if ply:IsPlayer() then
|
||||
DarkRP.notifyAll(0, 4, DarkRP.getPhrase("reset_money", ply:Nick()))
|
||||
else
|
||||
DarkRP.notifyAll(0, 4, DarkRP.getPhrase("reset_money", "Console"))
|
||||
end
|
||||
end
|
||||
concommand.Add("rp_resetallmoney", resetAllMoney)
|
||||
|
||||
function DarkRP.storeSalary(ply, amount)
|
||||
ply:setSelfDarkRPVar("salary", math.floor(amount))
|
||||
|
||||
return amount
|
||||
end
|
||||
|
||||
function DarkRP.retrieveSalary(ply, callback)
|
||||
local val =
|
||||
ply:getJobTable() and ply:getJobTable().salary or
|
||||
RPExtraTeams[GAMEMODE.DefaultTeam].salary or
|
||||
(GM or GAMEMODE).Config.normalsalary
|
||||
|
||||
if callback then callback(val) end
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Players
|
||||
---------------------------------------------------------------------------]]
|
||||
local meta = FindMetaTable("Player")
|
||||
function meta:restorePlayerData()
|
||||
self.DarkRPUnInitialized = true
|
||||
|
||||
local function onError(err)
|
||||
if not IsValid(self) then return end
|
||||
self.DarkRPUnInitialized = true -- no information should be saved from here, or the playerdata might be reset
|
||||
|
||||
self:setDarkRPVar("money", GAMEMODE.Config.startingmoney)
|
||||
self:setSelfDarkRPVar("salary", DarkRP.retrieveSalary(self))
|
||||
local name = string.sub(string.gsub(self:SteamName(), "\\\"", "\""), 1, 30)
|
||||
self:setDarkRPVar("rpname", name)
|
||||
|
||||
self.DarkRPDataRetrievalFailed = true -- marker on the player that says shit is fucked
|
||||
DarkRP.error("Failed to retrieve player information from the database. ", nil, {"This means your database or the connection to your database is fucked.", "This is the error given by the database:\n\t\t" .. tostring(err)})
|
||||
end
|
||||
|
||||
DarkRP.retrievePlayerData(self, function(data)
|
||||
if not IsValid(self) then return end
|
||||
|
||||
self.DarkRPUnInitialized = nil
|
||||
|
||||
local info = data and data[1] or {}
|
||||
if not info.rpname or info.rpname == "NULL" then info.rpname = string.sub(string.gsub(self:SteamName(), "\\\"", "\""), 1, 30) end
|
||||
|
||||
info.wallet = info.wallet or GAMEMODE.Config.startingmoney
|
||||
info.salary = DarkRP.retrieveSalary(self)
|
||||
|
||||
self:setDarkRPVar("money", tonumber(info.wallet))
|
||||
self:setSelfDarkRPVar("salary", tonumber(info.salary))
|
||||
|
||||
self:setDarkRPVar("rpname", info.rpname)
|
||||
|
||||
if not data then
|
||||
info = hook.Call("onPlayerFirstJoined", nil, self, info) or info
|
||||
DarkRP.createPlayerData(self, info.rpname, info.wallet, info.salary, onError)
|
||||
end
|
||||
end, onError)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Doors
|
||||
---------------------------------------------------------]]
|
||||
function DarkRP.storeDoorData(ent)
|
||||
if not ent:CreatedByMap() then return end
|
||||
local map = string.lower(game.GetMap())
|
||||
local nonOwnable = ent:getKeysNonOwnable()
|
||||
local title = ent:getKeysTitle()
|
||||
|
||||
MySQLite.query([[REPLACE INTO darkrp_door VALUES(]] .. ent:doorIndex() .. [[, ]] .. MySQLite.SQLStr(map) .. [[, ]] .. (title and MySQLite.SQLStr(title) or "NULL") .. [[, ]] .. "NULL" .. [[, ]] .. (nonOwnable and 1 or 0) .. [[);]])
|
||||
end
|
||||
|
||||
function setUpNonOwnableDoors()
|
||||
MySQLite.query("SELECT idx, title, isLocked, isDisabled FROM darkrp_door WHERE map = " .. MySQLite.SQLStr(string.lower(game.GetMap())) .. ";", function(r)
|
||||
if not r then return end
|
||||
|
||||
for _, row in pairs(r) do
|
||||
local e = DarkRP.doorIndexToEnt(tonumber(row.idx))
|
||||
|
||||
if not IsValid(e) then continue end
|
||||
if e:isKeysOwnable() then
|
||||
if tobool(row.isDisabled) then
|
||||
e:setKeysNonOwnable(tobool(row.isDisabled))
|
||||
end
|
||||
if row.isLocked and row.isLocked ~= "NULL" then
|
||||
e:Fire((tobool(row.isLocked) and "" or "un") .. "lock", "", 0)
|
||||
end
|
||||
e:setKeysTitle(row.title ~= "NULL" and row.title or nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local keyValueActions = {
|
||||
["DarkRPNonOwnable"] = function(ent, val) ent:setKeysNonOwnable(tobool(val)) end,
|
||||
["DarkRPTitle"] = function(ent, val) ent:setKeysTitle(val) end,
|
||||
["DarkRPDoorGroup"] = function(ent, val) if RPExtraTeamDoors[val] then ent:setDoorGroup(val) end end,
|
||||
["DarkRPCanLockpick"] = function(ent, val) ent.DarkRPCanLockpick = tobool(val) end
|
||||
}
|
||||
|
||||
local function onKeyValue(ent, key, value)
|
||||
if not ent:isDoor() then return end
|
||||
|
||||
if keyValueActions[key] then
|
||||
keyValueActions[key](ent, value)
|
||||
end
|
||||
end
|
||||
hook.Add("EntityKeyValue", "darkrp_doors", onKeyValue)
|
||||
|
||||
function DarkRP.storeTeamDoorOwnability(ent)
|
||||
if not ent:CreatedByMap() then return end
|
||||
local map = string.lower(game.GetMap())
|
||||
|
||||
MySQLite.query("DELETE FROM darkrp_doorjobs WHERE idx = " .. ent:doorIndex() .. " AND map = " .. MySQLite.SQLStr(map) .. ";")
|
||||
for k in pairs(ent:getKeysDoorTeams() or {}) do
|
||||
MySQLite.query("INSERT INTO darkrp_doorjobs VALUES(" .. ent:doorIndex() .. ", " .. MySQLite.SQLStr(map) .. ", " .. MySQLite.SQLStr(RPExtraTeams[k].command) .. ");")
|
||||
end
|
||||
end
|
||||
|
||||
function setUpTeamOwnableDoors()
|
||||
MySQLite.query("SELECT idx, job FROM darkrp_doorjobs WHERE map = " .. MySQLite.SQLStr(string.lower(game.GetMap())) .. ";", function(r)
|
||||
if not r then return end
|
||||
local map = string.lower(game.GetMap())
|
||||
|
||||
for _, row in pairs(r) do
|
||||
row.idx = tonumber(row.idx)
|
||||
|
||||
local e = DarkRP.doorIndexToEnt(row.idx)
|
||||
if not IsValid(e) then continue end
|
||||
|
||||
local _, job = DarkRP.getJobByCommand(row.job)
|
||||
|
||||
if job then
|
||||
e:addKeysDoorTeam(job)
|
||||
else
|
||||
print(("can't find job %s for door %d, removing from database"):format(row.job, row.idx))
|
||||
MySQLite.query(("DELETE FROM darkrp_doorjobs WHERE idx = %d AND map = %s AND job = %s;"):format(row.idx, MySQLite.SQLStr(map), MySQLite.SQLStr(row.job)))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function DarkRP.storeDoorGroup(ent, group)
|
||||
if not ent:CreatedByMap() then return end
|
||||
local map = MySQLite.SQLStr(string.lower(game.GetMap()))
|
||||
local index = ent:doorIndex()
|
||||
|
||||
if group == "" or not group then
|
||||
MySQLite.query("DELETE FROM darkrp_doorgroups WHERE map = " .. map .. " AND idx = " .. index .. ";")
|
||||
return
|
||||
end
|
||||
|
||||
MySQLite.query("REPLACE INTO darkrp_doorgroups VALUES(" .. index .. ", " .. map .. ", " .. MySQLite.SQLStr(group) .. ");");
|
||||
end
|
||||
|
||||
function setUpGroupDoors()
|
||||
local map = MySQLite.SQLStr(string.lower(game.GetMap()))
|
||||
MySQLite.query("SELECT idx, doorgroup FROM darkrp_doorgroups WHERE map = " .. map, function(data)
|
||||
if not data then return end
|
||||
|
||||
for _, row in pairs(data) do
|
||||
local ent = DarkRP.doorIndexToEnt(tonumber(row.idx))
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() then
|
||||
continue
|
||||
end
|
||||
|
||||
if not RPExtraTeamDoorIDs[row.doorgroup] then continue end
|
||||
ent:setDoorGroup(row.doorgroup)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("PostCleanupMap", "DarkRP.hooks", function()
|
||||
setUpNonOwnableDoors()
|
||||
setUpTeamOwnableDoors()
|
||||
setUpGroupDoors()
|
||||
end)
|
||||
304
gamemodes/darkrp/gamemode/modules/base/sv_entityvars.lua
Normal file
304
gamemodes/darkrp/gamemode/modules/base/sv_entityvars.lua
Normal file
@@ -0,0 +1,304 @@
|
||||
local meta = FindMetaTable("Player")
|
||||
|
||||
DarkRP.ServerDarkRPVars = DarkRP.ServerDarkRPVars or {}
|
||||
DarkRP.ServerPrivateDarkRPVars = DarkRP.ServerPrivateDarkRPVars or {}
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Pooled networking strings
|
||||
---------------------------------------------------------------------------]]
|
||||
util.AddNetworkString("DarkRP_InitializeVars")
|
||||
util.AddNetworkString("DarkRP_PlayerVar")
|
||||
util.AddNetworkString("DarkRP_PlayerVarRemoval")
|
||||
util.AddNetworkString("DarkRP_DarkRPVarDisconnect")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Player vars
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local warningsShown = {}
|
||||
local function checkDarkRPVarRegistration(name)
|
||||
local DarkRPVar = DarkRP.RegisteredDarkRPVars[name]
|
||||
if DarkRPVar then return end
|
||||
|
||||
if warningsShown[name] then return end
|
||||
warningsShown[name] = true
|
||||
|
||||
DarkRP.errorNoHalt(string.format([[Warning! DarkRPVar '%s' wasn't registered!
|
||||
Please contact the author of the DarkRP Addon to fix this.
|
||||
Until this is fixed you don't need to worry about anything. Everything will keep working.
|
||||
It's just that registering DarkRPVars would make DarkRP faster.]], name), 4)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Remove a player's DarkRPVar
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:removeDarkRPVar(var, target)
|
||||
local vars = DarkRP.ServerDarkRPVars[self]
|
||||
hook.Call("DarkRPVarChanged", nil, self, var, vars and vars[var], nil)
|
||||
target = target or player.GetAll()
|
||||
|
||||
DarkRP.ServerDarkRPVars[self] = DarkRP.ServerDarkRPVars[self] or {}
|
||||
DarkRP.ServerDarkRPVars[self][var] = nil
|
||||
|
||||
checkDarkRPVarRegistration(var)
|
||||
|
||||
net.Start("DarkRP_PlayerVarRemoval")
|
||||
net.WriteUInt(self:UserID(), 16)
|
||||
DarkRP.writeNetDarkRPVarRemoval(var)
|
||||
net.Send(target)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Set a player's DarkRPVar
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:setDarkRPVar(var, value, target)
|
||||
target = target or player.GetAll()
|
||||
|
||||
if value == nil then return self:removeDarkRPVar(var, target) end
|
||||
|
||||
local vars = DarkRP.ServerDarkRPVars[self]
|
||||
hook.Call("DarkRPVarChanged", nil, self, var, vars and vars[var], value)
|
||||
|
||||
DarkRP.ServerDarkRPVars[self] = DarkRP.ServerDarkRPVars[self] or {}
|
||||
DarkRP.ServerDarkRPVars[self][var] = value
|
||||
|
||||
checkDarkRPVarRegistration(var)
|
||||
|
||||
net.Start("DarkRP_PlayerVar")
|
||||
net.WriteUInt(self:UserID(), 16)
|
||||
DarkRP.writeNetDarkRPVar(var, value)
|
||||
net.Send(target)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Set a private DarkRPVar
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:setSelfDarkRPVar(var, value)
|
||||
DarkRP.ServerPrivateDarkRPVars[self] = DarkRP.ServerPrivateDarkRPVars[self] or {}
|
||||
DarkRP.ServerPrivateDarkRPVars[self][var] = true
|
||||
|
||||
self:setDarkRPVar(var, value, self)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Get a DarkRPVar
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:getDarkRPVar(var, fallback)
|
||||
local vars = DarkRP.ServerDarkRPVars[self]
|
||||
if vars == nil then return fallback end
|
||||
|
||||
local results = vars[var]
|
||||
if results == nil then return fallback end
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Backwards compatibility: Set ply.DarkRPVars attribute
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:setDarkRPVarsAttribute()
|
||||
DarkRP.ServerDarkRPVars[self] = DarkRP.ServerDarkRPVars[self] or {}
|
||||
-- With a reference to the table, ply.DarkRPVars should always remain
|
||||
-- up-to-date. One needs only be careful that DarkRP.ServerDarkRPVars[ply]
|
||||
-- is never replaced by a different table.
|
||||
self.DarkRPVars = DarkRP.ServerDarkRPVars[self]
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Send the DarkRPVars to a client
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:sendDarkRPVars()
|
||||
if self:EntIndex() == 0 then return end
|
||||
|
||||
local plys = player.GetAll()
|
||||
|
||||
net.Start("DarkRP_InitializeVars")
|
||||
net.WriteUInt(#plys, 8)
|
||||
for _, target in ipairs(plys) do
|
||||
net.WriteUInt(target:UserID(), 16)
|
||||
|
||||
local vars = {}
|
||||
for var, value in pairs(DarkRP.ServerDarkRPVars[target] or {}) do
|
||||
if self ~= target and (DarkRP.ServerPrivateDarkRPVars[target] or {})[var] then continue end
|
||||
table.insert(vars, var)
|
||||
end
|
||||
|
||||
local vars_cnt = #vars
|
||||
net.WriteUInt(vars_cnt, DarkRP.DARKRP_ID_BITS + 2) -- Allow for three times as many unknown DarkRPVars than the limit
|
||||
for i = 1, vars_cnt, 1 do
|
||||
DarkRP.writeNetDarkRPVar(vars[i], DarkRP.ServerDarkRPVars[target][vars[i]])
|
||||
end
|
||||
end
|
||||
net.Send(self)
|
||||
end
|
||||
concommand.Add("_sendDarkRPvars", function(ply)
|
||||
if ply.DarkRPVarsSent and ply.DarkRPVarsSent > (CurTime() - 3) then return end -- prevent spammers
|
||||
ply.DarkRPVarsSent = CurTime()
|
||||
ply:sendDarkRPVars()
|
||||
end)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Admin DarkRPVar commands
|
||||
---------------------------------------------------------------------------]]
|
||||
local function setRPName(ply, args)
|
||||
if not args[2] or string.len(args[2]) < 2 or string.len(args[2]) > 30 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), "<2/>30"))
|
||||
return
|
||||
end
|
||||
|
||||
local name = table.concat(args, " ", 2)
|
||||
|
||||
local target = DarkRP.findPlayer(args[1])
|
||||
|
||||
if not target then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", args[1]))
|
||||
return
|
||||
end
|
||||
|
||||
local oldname = target:Nick()
|
||||
|
||||
DarkRP.retrieveRPNames(name, function(taken)
|
||||
if not IsValid(target) then return end
|
||||
|
||||
if taken then
|
||||
DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("unable", "RPname", DarkRP.getPhrase("already_taken")))
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.storeRPName(target, name)
|
||||
target:setDarkRPVar("rpname", name)
|
||||
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_set_x_name", oldname, name))
|
||||
|
||||
local nick = ""
|
||||
if ply:EntIndex() == 0 then
|
||||
nick = "Console"
|
||||
else
|
||||
nick = ply:Nick()
|
||||
end
|
||||
DarkRP.notify(target, 0, 4, DarkRP.getPhrase("x_set_your_name", nick, name))
|
||||
if ply:EntIndex() == 0 then
|
||||
DarkRP.log("Console set " .. target:SteamName() .. "'s name to " .. name, Color(30, 30, 30))
|
||||
else
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") set " .. target:SteamName() .. "'s name to " .. name, Color(30, 30, 30))
|
||||
end
|
||||
end)
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forcerpname", "DarkRP_AdminCommands", setRPName)
|
||||
|
||||
local function freerpname(ply, args)
|
||||
local name = args ~= "" and args or IsValid(ply) and ply:Nick() or ""
|
||||
|
||||
MySQLite.query(("UPDATE darkrp_player SET rpname = NULL WHERE rpname = %s"):format(MySQLite.SQLStr(name)))
|
||||
|
||||
local nick = IsValid(ply) and ply:Nick() or "Console"
|
||||
DarkRP.log(("%s has freed the rp name '%s'"):format(nick, name), Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, ("'%s' has been freed"):format(name))
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("freerpname", "DarkRP_AdminCommands", freerpname)
|
||||
|
||||
local function RPName(ply, args)
|
||||
if ply.LastNameChange and ply.LastNameChange > (CurTime() - 5) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("have_to_wait", math.ceil(5 - (CurTime() - ply.LastNameChange)), "/rpname"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not GAMEMODE.Config.allowrpnames then
|
||||
DarkRP.notify(ply, 1, 6, DarkRP.getPhrase("disabled", "/rpname", ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
args = args:find"^%s*$" and '' or args:match"^%s*(.*%S)"
|
||||
|
||||
local canChangeName, reason = hook.Call("CanChangeRPName", GAMEMODE, ply, args)
|
||||
if canChangeName == false then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/rpname", reason or ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
ply:setRPName(args)
|
||||
ply.LastNameChange = CurTime()
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("rpname", RPName)
|
||||
DarkRP.defineChatCommand("name", RPName)
|
||||
DarkRP.defineChatCommand("nick", RPName)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Setting the RP name
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:setRPName(name, firstRun)
|
||||
-- Make sure nobody on this server already has this RP name
|
||||
local lowername = string.lower(tostring(name))
|
||||
DarkRP.retrieveRPNames(name, function(taken)
|
||||
if not IsValid(self) or string.len(lowername) < 2 and not firstrun then return end
|
||||
-- If we found that this name exists for another player
|
||||
if taken then
|
||||
if firstRun then
|
||||
-- If we just connected and another player happens to be using our steam name as their RP name
|
||||
-- Put a 1 after our steam name
|
||||
DarkRP.storeRPName(self, name .. " 1")
|
||||
DarkRP.notify(self, 0, 12, DarkRP.getPhrase("someone_stole_steam_name"))
|
||||
else
|
||||
DarkRP.notify(self, 1, 5, DarkRP.getPhrase("unable", "/rpname", DarkRP.getPhrase("already_taken")))
|
||||
return ""
|
||||
end
|
||||
else
|
||||
if not firstRun then -- Don't save the steam name in the database
|
||||
DarkRP.notifyAll(2, 6, DarkRP.getPhrase("rpname_changed", self:SteamName(), name))
|
||||
DarkRP.storeRPName(self, name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Maximum entity values
|
||||
---------------------------------------------------------------------------]]
|
||||
local maxEntities = {}
|
||||
function meta:addCustomEntity(entTable)
|
||||
maxEntities[self] = maxEntities[self] or {}
|
||||
maxEntities[self][entTable.cmd] = maxEntities[self][entTable.cmd] or 0
|
||||
maxEntities[self][entTable.cmd] = maxEntities[self][entTable.cmd] + 1
|
||||
end
|
||||
|
||||
function meta:removeCustomEntity(entTable)
|
||||
maxEntities[self] = maxEntities[self] or {}
|
||||
maxEntities[self][entTable.cmd] = maxEntities[self][entTable.cmd] or 0
|
||||
maxEntities[self][entTable.cmd] = maxEntities[self][entTable.cmd] - 1
|
||||
end
|
||||
|
||||
function meta:customEntityLimitReached(entTable)
|
||||
maxEntities[self] = maxEntities[self] or {}
|
||||
maxEntities[self][entTable.cmd] = maxEntities[self][entTable.cmd] or 0
|
||||
local max = entTable.getMax and entTable.getMax(self) or entTable.max
|
||||
|
||||
return max ~= 0 and maxEntities[self][entTable.cmd] >= max
|
||||
end
|
||||
|
||||
function meta:customEntityCount(entTable)
|
||||
local entities = maxEntities[self]
|
||||
if entities == nil then return 0 end
|
||||
|
||||
entities = entities[entTable.cmd]
|
||||
if entities == nil then return 0 end
|
||||
|
||||
return entities
|
||||
end
|
||||
|
||||
-- We use EntityRemoved to clear players of tables, because it is always called
|
||||
-- after the PlayerDisconnected hook. This is called _after_ the GAMEMODE
|
||||
-- function, to make sure that all regular hooks can still use DarkRPVars until
|
||||
-- the very end. See https://github.com/FPtje/DarkRP/pull/3270
|
||||
(GAMEMODE or GM).DarkRPPostEntityRemoved = function(_gm, ent)
|
||||
if not ent:IsPlayer() then return end
|
||||
|
||||
maxEntities[ent] = nil
|
||||
DarkRP.ServerDarkRPVars[ent] = nil
|
||||
DarkRP.ServerPrivateDarkRPVars[ent] = nil
|
||||
|
||||
net.Start("DarkRP_DarkRPVarDisconnect")
|
||||
net.WriteUInt(ent:UserID(), 16)
|
||||
net.Broadcast()
|
||||
end
|
||||
1146
gamemodes/darkrp/gamemode/modules/base/sv_gamemode_functions.lua
Normal file
1146
gamemodes/darkrp/gamemode/modules/base/sv_gamemode_functions.lua
Normal file
File diff suppressed because it is too large
Load Diff
1325
gamemodes/darkrp/gamemode/modules/base/sv_interface.lua
Normal file
1325
gamemodes/darkrp/gamemode/modules/base/sv_interface.lua
Normal file
File diff suppressed because it is too large
Load Diff
38
gamemodes/darkrp/gamemode/modules/base/sv_jobmodels.lua
Normal file
38
gamemodes/darkrp/gamemode/modules/base/sv_jobmodels.lua
Normal file
@@ -0,0 +1,38 @@
|
||||
util.AddNetworkString("DarkRP_preferredjobmodels")
|
||||
util.AddNetworkString("DarkRP_preferredjobmodel")
|
||||
|
||||
local preferredJobModels = {}
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
|
||||
local received = {}
|
||||
net.Receive("DarkRP_preferredjobmodels", function(len, ply)
|
||||
preferredJobModels[ply] = {}
|
||||
|
||||
for i in pairs(RPExtraTeams) do
|
||||
if net.ReadBit() == 0 then continue end
|
||||
|
||||
preferredJobModels[ply][i] = net.ReadString()
|
||||
end
|
||||
|
||||
if not received[ply] and preferredJobModels[ply][ply:Team()] then
|
||||
gamemode.Call("PlayerSetModel", ply)
|
||||
end
|
||||
|
||||
received[ply] = true
|
||||
end)
|
||||
|
||||
net.Receive("DarkRP_preferredjobmodel", function(len, ply)
|
||||
local teamNr = net.ReadUInt(8)
|
||||
local model = net.ReadString()
|
||||
|
||||
if not RPExtraTeams[teamNr] then return end
|
||||
|
||||
preferredJobModels[ply] = preferredJobModels[ply] or {}
|
||||
preferredJobModels[ply][teamNr] = model
|
||||
end)
|
||||
|
||||
function plyMeta:getPreferredModel(TeamNr)
|
||||
preferredJobModels[self] = preferredJobModels[self] or {}
|
||||
|
||||
return preferredJobModels[self][TeamNr]
|
||||
end
|
||||
466
gamemodes/darkrp/gamemode/modules/base/sv_purchasing.lua
Normal file
466
gamemodes/darkrp/gamemode/modules/base/sv_purchasing.lua
Normal file
@@ -0,0 +1,466 @@
|
||||
function DarkRP.hooks:canBuyPistol(ply, shipment)
|
||||
local price = shipment.getPrice and shipment.getPrice(ply, shipment.pricesep) or shipment.pricesep or 0
|
||||
|
||||
if not GAMEMODE:CustomObjFitsMap(shipment) then
|
||||
return false, false, "Custom object does not fit map"
|
||||
end
|
||||
|
||||
if ply:isArrested() then
|
||||
return false, false, DarkRP.getPhrase("unable", "/buy", "")
|
||||
end
|
||||
|
||||
if shipment.customCheck and not shipment.customCheck(ply) then
|
||||
local message = isfunction(shipment.CustomCheckFailMsg) and shipment.CustomCheckFailMsg(ply, shipment) or
|
||||
shipment.CustomCheckFailMsg or
|
||||
DarkRP.getPhrase("not_allowed_to_purchase")
|
||||
return false, false, message
|
||||
end
|
||||
|
||||
if not ply:canAfford(price) then
|
||||
return false, false, DarkRP.getPhrase("cant_afford", "/buy")
|
||||
end
|
||||
|
||||
if not GAMEMODE.Config.restrictbuypistol or
|
||||
(GAMEMODE.Config.restrictbuypistol and (not shipment.allowed[1] or table.HasValue(shipment.allowed, ply:Team()))) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function BuyPistol(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not GAMEMODE.Config.enablebuypistol then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("disabled", "/buy", ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
if GAMEMODE.Config.noguns then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("disabled", "/buy", ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local shipment = DarkRP.getShipmentByName(args)
|
||||
if not shipment or not shipment.separate then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unavailable", DarkRP.getPhrase("weapon_")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canbuy, suppress, message, price = hook.Call("canBuyPistol", DarkRP.hooks, ply, shipment)
|
||||
|
||||
if not canbuy then
|
||||
message = message or DarkRP.getPhrase("incorrect_job", "/buy")
|
||||
if not suppress then DarkRP.notify(ply, 1, 4, message) end
|
||||
return ""
|
||||
end
|
||||
|
||||
local cost = price or shipment.getPrice and shipment.getPrice(ply, shipment.pricesep) or shipment.pricesep or 0
|
||||
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
trace.filter = ply
|
||||
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local defaultClip, clipSize
|
||||
local wep_tbl = weapons.Get(shipment.entity)
|
||||
if wep_tbl and wep_tbl.Primary then
|
||||
defaultClip = wep_tbl.Primary.DefaultClip
|
||||
clipSize = wep_tbl.Primary.ClipSize
|
||||
end
|
||||
|
||||
local weapon = ents.Create("spawned_weapon")
|
||||
weapon:SetModel(shipment.model)
|
||||
weapon:SetWeaponClass(shipment.entity)
|
||||
weapon:SetPos(tr.HitPos)
|
||||
weapon.ammoadd = shipment.spareammo or defaultClip
|
||||
weapon.clip1 = shipment.clip1 or clipSize
|
||||
weapon.clip2 = shipment.clip2
|
||||
weapon.nodupe = true
|
||||
weapon:Spawn()
|
||||
|
||||
DarkRP.placeEntity(weapon, tr, ply)
|
||||
|
||||
if shipment.onBought then
|
||||
shipment.onBought(ply, shipment, weapon)
|
||||
end
|
||||
hook.Call("playerBoughtPistol", nil, ply, shipment, weapon, cost)
|
||||
|
||||
if IsValid(weapon) then
|
||||
ply:addMoney(-cost)
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", args, DarkRP.formatMoney(cost)))
|
||||
else
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/buy", args))
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("buy", BuyPistol, 0.2)
|
||||
|
||||
function DarkRP.hooks:canBuyShipment(ply, shipment)
|
||||
if not GAMEMODE:CustomObjFitsMap(shipment) then
|
||||
return false, false, "Custom object does not fit map"
|
||||
end
|
||||
|
||||
if ply.LastShipmentSpawn and ply.LastShipmentSpawn > (CurTime() - GAMEMODE.Config.ShipmentSpamTime) then
|
||||
return false, false, DarkRP.getPhrase("shipment_antispam_wait")
|
||||
end
|
||||
|
||||
if ply:isArrested() then
|
||||
return false, false, DarkRP.getPhrase("unable", "/buyshipment", "")
|
||||
end
|
||||
|
||||
if shipment.customCheck and not shipment.customCheck(ply) then
|
||||
local message = isfunction(shipment.CustomCheckFailMsg) and shipment.CustomCheckFailMsg(ply, shipment) or
|
||||
shipment.CustomCheckFailMsg or
|
||||
DarkRP.getPhrase("not_allowed_to_purchase")
|
||||
return false, false, message
|
||||
end
|
||||
|
||||
local canbecome = false
|
||||
for _, b in pairs(shipment.allowed) do
|
||||
if ply:Team() == b then
|
||||
canbecome = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not canbecome then
|
||||
return false, false, DarkRP.getPhrase("incorrect_job", "/buyshipment")
|
||||
end
|
||||
|
||||
local cost = shipment.getPrice and shipment.getPrice(ply, shipment.price) or shipment.price
|
||||
|
||||
if not ply:canAfford(cost) then
|
||||
return false, false, DarkRP.getPhrase("cant_afford", DarkRP.getPhrase("shipment"))
|
||||
end
|
||||
|
||||
if not shipment.allowPurchaseWhileDead and not ply:Alive() then
|
||||
return false, false, DarkRP.getPhrase("must_be_alive_to_do_x", DarkRP.getPhrase("buy_x", DarkRP.getPhrase("shipments")))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function BuyShipment(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local found, foundKey = DarkRP.getShipmentByName(args)
|
||||
if not found or found.noship or not GAMEMODE:CustomObjFitsMap(found) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unavailable", DarkRP.getPhrase("shipment")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canbuy, suppress, message, price = hook.Call("canBuyShipment", DarkRP.hooks, ply, found)
|
||||
|
||||
if not canbuy then
|
||||
message = message or DarkRP.getPhrase("incorrect_job", "/buy")
|
||||
if not suppress then DarkRP.notify(ply, 1, 4, message) end
|
||||
return ""
|
||||
end
|
||||
|
||||
local cost = price or found.getPrice and found.getPrice(ply, found.price) or found.price
|
||||
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
trace.filter = ply
|
||||
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local crate = ents.Create(found.shipmentClass or "spawned_shipment")
|
||||
crate.SID = ply.SID
|
||||
crate:Setowning_ent(ply)
|
||||
crate:SetContents(foundKey, found.amount)
|
||||
|
||||
crate:SetPos(Vector(tr.HitPos.x, tr.HitPos.y, tr.HitPos.z))
|
||||
crate.nodupe = true
|
||||
crate.ammoadd = found.spareammo
|
||||
crate.clip1 = found.clip1
|
||||
crate.clip2 = found.clip2
|
||||
crate:Spawn()
|
||||
crate:SetPlayer(ply)
|
||||
|
||||
DarkRP.placeEntity(crate, tr, ply)
|
||||
|
||||
local phys = crate:GetPhysicsObject()
|
||||
phys:Wake()
|
||||
if found.weight then
|
||||
phys:SetMass(found.weight)
|
||||
end
|
||||
|
||||
if CustomShipments[foundKey].onBought then
|
||||
CustomShipments[foundKey].onBought(ply, CustomShipments[foundKey], crate)
|
||||
end
|
||||
hook.Call("playerBoughtShipment", nil, ply, CustomShipments[foundKey], crate, cost)
|
||||
|
||||
if IsValid(crate) then
|
||||
ply:addMoney(-cost)
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", args, DarkRP.formatMoney(cost)))
|
||||
else
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/buyshipment", arg))
|
||||
end
|
||||
|
||||
ply.LastShipmentSpawn = CurTime()
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("buyshipment", BuyShipment)
|
||||
|
||||
function DarkRP.hooks:canBuyVehicle(ply, vehicle)
|
||||
if not vehicle.allowPurchaseWhileDead and not ply:Alive() then
|
||||
return false, false, DarkRP.getPhrase("must_be_alive_to_do_x", DarkRP.getPhrase("buy_x", vehicle.name))
|
||||
end
|
||||
if not GAMEMODE:CustomObjFitsMap(vehicle) then
|
||||
return false, false, "Custom object does not fit map"
|
||||
end
|
||||
|
||||
if ply:isArrested() then
|
||||
return false, false, DarkRP.getPhrase("unable", "/buyvehicle", "")
|
||||
end
|
||||
|
||||
if vehicle.allowed and not table.HasValue(vehicle.allowed, ply:Team()) then
|
||||
return false, false, DarkRP.getPhrase("incorrect_job", "/buyvehicle")
|
||||
end
|
||||
|
||||
if vehicle.customCheck and not vehicle.customCheck(ply) then
|
||||
local message = isfunction(vehicle.CustomCheckFailMsg) and vehicle.CustomCheckFailMsg(ply, vehicle) or
|
||||
vehicle.CustomCheckFailMsg or
|
||||
DarkRP.getPhrase("not_allowed_to_purchase")
|
||||
return false, false, message
|
||||
end
|
||||
|
||||
ply.Vehicles = ply.Vehicles or 0
|
||||
if GAMEMODE.Config.maxvehicles and ply.Vehicles >= GAMEMODE.Config.maxvehicles then
|
||||
return false, false, DarkRP.getPhrase("limit", DarkRP.getPhrase("vehicle"))
|
||||
end
|
||||
|
||||
local cost = vehicle.getPrice and vehicle.getPrice(ply, vehicle.price) or vehicle.price
|
||||
if not ply:canAfford(cost) then
|
||||
return false, false, DarkRP.getPhrase("cant_afford", DarkRP.getPhrase("vehicle"))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function BuyVehicle(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local found = false
|
||||
-- Allow people to have multiple vehicles with the same name
|
||||
-- vehicles are bought through the command
|
||||
for k, v in pairs(CustomVehicles) do
|
||||
if v.command and string.lower(v.command) == string.lower(args) then
|
||||
found = CustomVehicles[k]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
for k,v in pairs(CustomVehicles) do
|
||||
if string.lower(v.name) == string.lower(args) then found = CustomVehicles[k] break end
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unavailable", DarkRP.getPhrase("vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local Vehicle = DarkRP.getAvailableVehicles()[found.name]
|
||||
if not Vehicle then DarkRP.notify(ply, 1, 4, "Incorrect vehicle, fix your vehicles.") return "" end
|
||||
|
||||
local canbuy, suppress, message, price = hook.Call("canBuyVehicle", DarkRP.hooks, ply, found)
|
||||
|
||||
if not canbuy then
|
||||
message = message or DarkRP.getPhrase("incorrect_job", "/buy")
|
||||
if not suppress then DarkRP.notify(ply, 1, 4, message) end
|
||||
return ""
|
||||
end
|
||||
|
||||
local cost = price or found.getPrice and found.getPrice(ply, found.price) or found.price
|
||||
|
||||
ply:addMoney(-cost)
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", found.label or found.name, DarkRP.formatMoney(cost)))
|
||||
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * (found.distance or 85)
|
||||
trace.filter = ply
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local ent = ents.Create(Vehicle.Class)
|
||||
if not ent:IsValid() then error("Vehicle '" .. Vehicle.Class .. "' does not exist or is not valid.") end
|
||||
|
||||
ent:SetModel(Vehicle.Model)
|
||||
if Vehicle.KeyValues then
|
||||
for k, v in pairs(Vehicle.KeyValues) do
|
||||
ent:SetKeyValue(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
ent:SetPos(tr.HitPos)
|
||||
ent.VehicleName = found.name
|
||||
ent.VehicleTable = Vehicle
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
ent.SID = ply.SID
|
||||
ent.ClassOverride = Vehicle.Class
|
||||
if Vehicle.Members then
|
||||
table.Merge(ent, Vehicle.Members)
|
||||
end
|
||||
ent:CPPISetOwner(ply)
|
||||
ent:keysOwn(ply)
|
||||
|
||||
DarkRP.placeEntity(ent, tr, ply)
|
||||
|
||||
local angOff = found.angle or Angle(0, 0, 0)
|
||||
ent:SetAngles(ent:GetAngles() + angOff)
|
||||
|
||||
hook.Call("PlayerSpawnedVehicle", GAMEMODE, ply, ent) -- VUMod compatability
|
||||
hook.Call("playerBoughtCustomVehicle", nil, ply, found, ent, cost)
|
||||
|
||||
if found.onBought then
|
||||
found.onBought(ply, found, ent)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("buyvehicle", BuyVehicle)
|
||||
|
||||
function DarkRP.hooks:canBuyAmmo(ply, ammo)
|
||||
if not GAMEMODE:CustomObjFitsMap(ammo) then
|
||||
return false, false, "Custom object does not fit map"
|
||||
end
|
||||
|
||||
if ply:isArrested() then
|
||||
return false, false, DarkRP.getPhrase("unable", "/buyammo", "")
|
||||
end
|
||||
|
||||
if ammo.allowed and not table.HasValue(ammo.allowed, ply:Team()) then
|
||||
return false, false, DarkRP.getPhrase("incorrect_job", "/buyammo")
|
||||
end
|
||||
|
||||
if ammo.customCheck and not ammo.customCheck(ply) then
|
||||
local message = isfunction(ammo.CustomCheckFailMsg) and ammo.CustomCheckFailMsg(ply, ammo) or
|
||||
ammo.CustomCheckFailMsg or
|
||||
DarkRP.getPhrase("not_allowed_to_purchase")
|
||||
return false, false, message
|
||||
end
|
||||
|
||||
local cost = ammo.getPrice and ammo.getPrice(ply, ammo.price) or ammo.price
|
||||
if not ply:canAfford(cost) then
|
||||
return false, false, DarkRP.getPhrase("cant_afford", DarkRP.getPhrase("ammo"))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function BuyAmmo(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
if GAMEMODE.Config.noguns then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("disabled", DarkRP.getPhrase("ammo"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local found
|
||||
local num = tonumber(args)
|
||||
if num and GAMEMODE.AmmoTypes[num] then
|
||||
found = GAMEMODE.AmmoTypes[num]
|
||||
else
|
||||
for _, v in pairs(GAMEMODE.AmmoTypes) do
|
||||
if v.ammoType ~= args then continue end
|
||||
|
||||
found = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not found then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unavailable", DarkRP.getPhrase("ammo")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canbuy, suppress, message, price = hook.Call("canBuyAmmo", DarkRP.hooks, ply, found)
|
||||
|
||||
if not canbuy then
|
||||
message = message or DarkRP.getPhrase("incorrect_job", "/buy")
|
||||
if not suppress then DarkRP.notify(ply, 1, 4, message) end
|
||||
return ""
|
||||
end
|
||||
|
||||
local cost = price or found.getPrice and found.getPrice(ply, found.price) or found.price
|
||||
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", found.name, DarkRP.formatMoney(cost)))
|
||||
ply:addMoney(-cost)
|
||||
|
||||
-- local trace = {}
|
||||
-- trace.start = ply:EyePos()
|
||||
-- trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
-- trace.filter = ply
|
||||
|
||||
-- local tr = util.TraceLine(trace)
|
||||
|
||||
-- local ammo = ents.Create("spawned_ammo")
|
||||
-- ammo:SetModel(found.model)
|
||||
-- ammo:SetPos(tr.HitPos)
|
||||
-- ammo.nodupe = true
|
||||
-- ammo.amountGiven, ammo.ammoType = found.amountGiven, found.ammoType
|
||||
-- ammo:Spawn()
|
||||
|
||||
-- DarkRP.placeEntity(ammo, tr, ply)
|
||||
|
||||
-- hook.Call("playerBoughtAmmo", nil, ply, found, ammo, cost)
|
||||
--
|
||||
ply:GiveAmmo(found.amountGiven, found.ammoType)
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("buyammo", BuyAmmo, 1)
|
||||
|
||||
local function SetPrice(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local price = DarkRP.toInt(args)
|
||||
if not price then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
price = math.Clamp(price, GAMEMODE.Config.pricemin, (GAMEMODE.Config.pricecap ~= 0 and GAMEMODE.Config.pricecap) or 500)
|
||||
local trace = {}
|
||||
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
trace.filter = ply
|
||||
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local ent = tr.Entity
|
||||
|
||||
if IsValid(ent) and ent.CanSetPrice and ent.SID == ply.SID then
|
||||
ent:Setprice(price)
|
||||
else
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("any_lab")))
|
||||
end
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("price", SetPrice)
|
||||
DarkRP.defineChatCommand("setprice", SetPrice)
|
||||
235
gamemodes/darkrp/gamemode/modules/base/sv_util.lua
Normal file
235
gamemodes/darkrp/gamemode/modules/base/sv_util.lua
Normal file
@@ -0,0 +1,235 @@
|
||||
function DarkRP.notify(ply, msgtype, len, msg)
|
||||
if not istable(ply) then
|
||||
if not IsValid(ply) then
|
||||
-- Dedicated server console
|
||||
print(msg)
|
||||
return
|
||||
end
|
||||
|
||||
ply = {ply}
|
||||
end
|
||||
|
||||
local rcp = RecipientFilter()
|
||||
for _, v in pairs(ply) do
|
||||
rcp:AddPlayer(v)
|
||||
end
|
||||
|
||||
if hook.Run("onNotify", rcp:GetPlayers(), msgtype, len, msg) == true then return end
|
||||
|
||||
umsg.Start("_Notify", rcp)
|
||||
umsg.String(msg)
|
||||
umsg.Short(msgtype)
|
||||
umsg.Long(len)
|
||||
umsg.End()
|
||||
end
|
||||
|
||||
function DarkRP.notifyAll(msgtype, len, msg)
|
||||
if hook.Run("onNotify", player.GetAll(), msgtype, len, msg) == true then return end
|
||||
|
||||
umsg.Start("_Notify")
|
||||
umsg.String(msg)
|
||||
umsg.Short(msgtype)
|
||||
umsg.Long(len)
|
||||
umsg.End()
|
||||
end
|
||||
|
||||
function DarkRP.printMessageAll(msgtype, msg)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
v:PrintMessage(msgtype, msg)
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.printConsoleMessage(ply, msg)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(msg)
|
||||
else
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, msg)
|
||||
end
|
||||
end
|
||||
|
||||
util.AddNetworkString("DarkRP_Chat")
|
||||
|
||||
function DarkRP.talkToRange(ply, PlayerName, Message, size)
|
||||
local ents = player.GetHumans()
|
||||
local col = team.GetColor(ply:Team())
|
||||
local filter = {}
|
||||
|
||||
local plyPos = ply:EyePos()
|
||||
local sizeSqr = size * size
|
||||
|
||||
for _, v in ipairs(ents) do
|
||||
if (v:EyePos():DistToSqr(plyPos) <= sizeSqr) and (v == ply or hook.Run("PlayerCanSeePlayersChat", PlayerName .. ": " .. Message, false, v, ply) ~= false) then
|
||||
table.insert(filter, v)
|
||||
end
|
||||
end
|
||||
|
||||
if PlayerName == ply:Nick() then PlayerName = "" end -- If it's just normal chat, why not cut down on networking and get the name on the client
|
||||
|
||||
net.Start("DarkRP_Chat")
|
||||
net.WriteUInt(col.r, 8)
|
||||
net.WriteUInt(col.g, 8)
|
||||
net.WriteUInt(col.b, 8)
|
||||
net.WriteString(PlayerName)
|
||||
net.WriteEntity(ply)
|
||||
net.WriteUInt(255, 8)
|
||||
net.WriteUInt(255, 8)
|
||||
net.WriteUInt(255, 8)
|
||||
net.WriteString(Message)
|
||||
net.Send(filter)
|
||||
end
|
||||
|
||||
function DarkRP.talkToPerson(receiver, col1, text1, col2, text2, sender)
|
||||
if not IsValid(receiver) then return end
|
||||
if receiver:IsBot() then return end
|
||||
local concatenatedText = (text1 or "") .. ": " .. (text2 or "")
|
||||
|
||||
if sender == receiver or hook.Run("PlayerCanSeePlayersChat", concatenatedText, false, receiver, sender) ~= false then
|
||||
net.Start("DarkRP_Chat")
|
||||
net.WriteUInt(col1.r, 8)
|
||||
net.WriteUInt(col1.g, 8)
|
||||
net.WriteUInt(col1.b, 8)
|
||||
net.WriteString(text1)
|
||||
|
||||
sender = sender or Entity(0)
|
||||
net.WriteEntity(sender)
|
||||
|
||||
col2 = col2 or color_black
|
||||
net.WriteUInt(col2.r, 8)
|
||||
net.WriteUInt(col2.g, 8)
|
||||
net.WriteUInt(col2.b, 8)
|
||||
net.WriteString(text2 or "")
|
||||
net.Send(receiver)
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.isEmpty(vector, ignore)
|
||||
ignore = ignore or {}
|
||||
|
||||
local point = util.PointContents(vector)
|
||||
local a = point ~= CONTENTS_SOLID
|
||||
and point ~= CONTENTS_MOVEABLE
|
||||
and point ~= CONTENTS_LADDER
|
||||
and point ~= CONTENTS_PLAYERCLIP
|
||||
and point ~= CONTENTS_MONSTERCLIP
|
||||
if not a then return false end
|
||||
|
||||
local b = true
|
||||
|
||||
for _, v in ipairs(ents.FindInSphere(vector, 35)) do
|
||||
if (v:IsNPC() or v:IsPlayer() or v:GetClass() == "prop_physics" or v.NotEmptyPos) and not table.HasValue(ignore, v) then
|
||||
b = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return a and b
|
||||
end
|
||||
|
||||
function DarkRP.placeEntity(ent, tr, ply)
|
||||
if IsValid(ply) then
|
||||
local ang = ply:EyeAngles()
|
||||
ang.pitch = 0
|
||||
ang.yaw = ang.yaw + 180
|
||||
ang.roll = 0
|
||||
ent:SetAngles(ang)
|
||||
end
|
||||
|
||||
local vFlushPoint = tr.HitPos - (tr.HitNormal * 512)
|
||||
vFlushPoint = ent:NearestPoint(vFlushPoint)
|
||||
vFlushPoint = ent:GetPos() - vFlushPoint
|
||||
vFlushPoint = tr.HitPos + vFlushPoint
|
||||
ent:SetPos(vFlushPoint)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find an empty position near the position given in the first parameter
|
||||
pos - The position to use as a center for looking around
|
||||
ignore - what entities to ignore when looking for the position (the position can be within the entity)
|
||||
distance - how far to look
|
||||
step - how big the steps are
|
||||
area - the position relative to pos that should also be free
|
||||
|
||||
Performance: O(N^2) (The Lua part, that is, I don't know about the C++ counterpart)
|
||||
Don't call this function too often or with big inputs.
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.findEmptyPos(pos, ignore, distance, step, area)
|
||||
if DarkRP.isEmpty(pos, ignore) and DarkRP.isEmpty(pos + area, ignore) then
|
||||
return pos
|
||||
end
|
||||
|
||||
for j = step, distance, step do
|
||||
for i = -1, 1, 2 do -- alternate in direction
|
||||
local k = j * i
|
||||
|
||||
-- Look North/South
|
||||
if DarkRP.isEmpty(pos + Vector(k, 0, 0), ignore) and DarkRP.isEmpty(pos + Vector(k, 0, 0) + area, ignore) then
|
||||
return pos + Vector(k, 0, 0)
|
||||
end
|
||||
|
||||
-- Look East/West
|
||||
if DarkRP.isEmpty(pos + Vector(0, k, 0), ignore) and DarkRP.isEmpty(pos + Vector(0, k, 0) + area, ignore) then
|
||||
return pos + Vector(0, k, 0)
|
||||
end
|
||||
|
||||
-- Look Up/Down
|
||||
if DarkRP.isEmpty(pos + Vector(0, 0, k), ignore) and DarkRP.isEmpty(pos + Vector(0, 0, k) + area, ignore) then
|
||||
return pos + Vector(0, 0, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return pos
|
||||
end
|
||||
|
||||
local meta = FindMetaTable("Player")
|
||||
function meta:applyPlayerClassVars(applyHealth)
|
||||
local playerClass = baseclass.Get(player_manager.GetPlayerClass(self))
|
||||
|
||||
self:SetWalkSpeed(playerClass.WalkSpeed >= 0 and playerClass.WalkSpeed or GAMEMODE.Config.walkspeed)
|
||||
self:SetRunSpeed(playerClass.RunSpeed >= 0 and playerClass.RunSpeed or (self:isCP() and GAMEMODE.Config.runspeedcp or GAMEMODE.Config.runspeed))
|
||||
|
||||
hook.Call("UpdatePlayerSpeed", GAMEMODE, self) -- Backwards compatitibly, do not use
|
||||
|
||||
self:SetCrouchedWalkSpeed(playerClass.CrouchedWalkSpeed)
|
||||
self:SetDuckSpeed(playerClass.DuckSpeed)
|
||||
self:SetUnDuckSpeed(playerClass.UnDuckSpeed)
|
||||
self:SetJumpPower(playerClass.JumpPower)
|
||||
self:AllowFlashlight(playerClass.CanUseFlashlight)
|
||||
|
||||
self:SetMaxHealth(playerClass.MaxHealth >= 0 and playerClass.MaxHealth or (tonumber(GAMEMODE.Config.startinghealth) or 100))
|
||||
if applyHealth then
|
||||
self:SetHealth(playerClass.StartHealth >= 0 and playerClass.StartHealth or (tonumber(GAMEMODE.Config.startinghealth) or 100))
|
||||
end
|
||||
self:SetArmor(playerClass.StartArmor)
|
||||
|
||||
self.dropWeaponOnDeath = playerClass.DropWeaponOnDie
|
||||
self:SetNoCollideWithTeammates(playerClass.TeammateNoCollide)
|
||||
self:SetAvoidPlayers(playerClass.AvoidPlayers)
|
||||
|
||||
hook.Call("playerClassVarsApplied", nil, self)
|
||||
end
|
||||
|
||||
local function LookPersonUp(ply, cmd, args)
|
||||
if not args[1] then
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return
|
||||
end
|
||||
local P = DarkRP.findPlayer(args[1])
|
||||
if not IsValid(P) then
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("could_not_find", tostring(args[1])))
|
||||
return
|
||||
end
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("name", P:Nick()))
|
||||
DarkRP.printConsoleMessage(ply, "Steam " .. DarkRP.getPhrase("name", P:SteamName()))
|
||||
DarkRP.printConsoleMessage(ply, "Steam ID: " .. P:SteamID())
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("job", team.GetName(P:Team())))
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("kills", P:Frags()))
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("deaths", P:Deaths()))
|
||||
|
||||
CAMI.PlayerHasAccess(ply, "DarkRP_AdminCommands", function(access)
|
||||
if not access then return end
|
||||
|
||||
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("wallet", DarkRP.formatMoney(P:getDarkRPVar("money")), ""))
|
||||
end)
|
||||
end
|
||||
concommand.Add("rp_lookup", LookPersonUp)
|
||||
90
gamemodes/darkrp/gamemode/modules/chat/cl_chat.lua
Normal file
90
gamemodes/darkrp/gamemode/modules/chat/cl_chat.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
Gamemode function
|
||||
---------------------------------------------------------------------------]]
|
||||
function GM:OnPlayerChat()
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Add a message to chat
|
||||
---------------------------------------------------------------------------]]
|
||||
local function AddToChat(bits)
|
||||
local col1 = Color(net.ReadUInt(8), net.ReadUInt(8), net.ReadUInt(8))
|
||||
|
||||
local prefixText = net.ReadString()
|
||||
local ply = net.ReadEntity()
|
||||
ply = IsValid(ply) and ply or LocalPlayer()
|
||||
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if prefixText == "" or not prefixText then
|
||||
prefixText = ply:Nick()
|
||||
prefixText = prefixText ~= "" and prefixText or ply:SteamName()
|
||||
end
|
||||
|
||||
local col2 = Color(net.ReadUInt(8), net.ReadUInt(8), net.ReadUInt(8))
|
||||
|
||||
local text = net.ReadString()
|
||||
local shouldShow
|
||||
if text and text ~= "" then
|
||||
if IsValid(ply) then
|
||||
shouldShow = hook.Call("OnPlayerChat", GAMEMODE, ply, text, false, not ply:Alive(), prefixText, col1, col2)
|
||||
end
|
||||
|
||||
if shouldShow ~= true then
|
||||
chat.AddNonParsedText(col1, prefixText, col2, ": " .. text)
|
||||
end
|
||||
else
|
||||
shouldShow = hook.Call("ChatText", GAMEMODE, "0", prefixText, prefixText, "darkrp")
|
||||
|
||||
if shouldShow ~= true then
|
||||
chat.AddNonParsedText(col1, prefixText)
|
||||
end
|
||||
end
|
||||
chat.PlaySound()
|
||||
end
|
||||
net.Receive("DarkRP_Chat", AddToChat)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Credits
|
||||
|
||||
Please only ADD to the credits.
|
||||
---------------------------------------------------------------------------]]
|
||||
local creds =
|
||||
[[
|
||||
|
||||
LightRP was created by Rick darkalonio. LightRP was sandbox with some added RP elements.
|
||||
LightRP was released at the end of January 2007
|
||||
|
||||
DarkRP was created as a spoof of LightRP by Rickster, somewhere during the summer of 2007.
|
||||
Note: There was a DarkRP in 2006, but that was an entirely different gamemode.
|
||||
|
||||
Rickster went to serve his country and went to Afghanistan. During that time, the following people updated DarkRP:
|
||||
Picwizdan
|
||||
Sibre
|
||||
[GNC] Matt
|
||||
PhilXYZ
|
||||
Chromebolt A.K.A. Unib5 (STEAM_0:1:19045957)
|
||||
|
||||
In 2008, Unib5 was administrator on a DarkRP server called EuroRP, owned by Jiggu. FPtje frequently joined this server to prop kill en masse. While Jiggu loved watching the chaos unfold, Unib5 hated it and banned FPtje on sight. Since Jiggu kept unbanning FPtje, Unib5 felt powerless. In an attempt to stop FPtje, Unib5 put FPtje's favourite prop killing props (the locker and the sawblade) in the default blacklist of DarkRP in an update. This in turn enraged FPtje, as he swore to make an update in secret that would suddenly pop up and overthrow the established version. As a result, DarkRP 2.3.1 was released in December 2008. After a bit of a fight, FPtje became the official updater of DarkRP.
|
||||
|
||||
Current developer:
|
||||
Falco A.K.A. FPtje Atheos (STEAM_0:0:8944068)
|
||||
|
||||
People who have contributed (ordered by commits, with at least two commits)
|
||||
Bo98
|
||||
Drakehawke (STEAM_0:0:22342869) (64 commits on old SVN)
|
||||
FiG-Scorn
|
||||
Noiwex
|
||||
KoZ
|
||||
Eusion (STEAM_0:0:20450406) (3 commits on old SVN)
|
||||
Gangleider
|
||||
MattWalton12
|
||||
TypicalRookie
|
||||
]]
|
||||
|
||||
local function credits(um)
|
||||
chat.AddNonParsedText(Color(255, 0, 0, 255), "[", Color(50,50,50,255), GAMEMODE.Name, Color(255, 0, 0, 255), "] ", color_white, DarkRP.getPhrase("credits_see_console"))
|
||||
|
||||
MsgC(Color(255, 0, 0, 255), DarkRP.getPhrase("credits_for", GAMEMODE.Name), color_white, creds)
|
||||
end
|
||||
usermessage.Hook("DarkRP_Credits", credits)
|
||||
207
gamemodes/darkrp/gamemode/modules/chat/cl_chatlisteners.lua
Normal file
207
gamemodes/darkrp/gamemode/modules/chat/cl_chatlisteners.lua
Normal file
@@ -0,0 +1,207 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
This module finds out for you who can see you talk or speak through the microphone
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Variables
|
||||
---------------------------------------------------------------------------]]
|
||||
local receivers
|
||||
local currentChatText = {}
|
||||
local receiverConfigs = {}
|
||||
local currentConfig = {text = "", hearFunc = fn.Id} -- Default config is not loaded yet
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
addChatReceiver
|
||||
Add a chat command with specific receivers
|
||||
|
||||
prefix: the chat command itself ("/pm", "/ooc", "/me" are some examples)
|
||||
text: the text that shows up when it says "Some people can hear you X"
|
||||
hearFunc: a function(ply, splitText) that decides whether this player can or cannot hear you.
|
||||
return true if the player can hear you
|
||||
false if the player cannot
|
||||
nil if you want to prevent the text from showing up temporarily
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.addChatReceiver(prefix, text, hearFunc)
|
||||
receiverConfigs[prefix] = {
|
||||
text = text,
|
||||
hearFunc = hearFunc
|
||||
}
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
removeChatReceiver
|
||||
Remove a chat command.
|
||||
|
||||
prefix: the command, like in addChatReceiver
|
||||
---------------------------------------------------------------------------]]
|
||||
function DarkRP.removeChatReceiver(prefix)
|
||||
receiverConfigs[prefix] = nil
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Draw the results to the screen
|
||||
---------------------------------------------------------------------------]]
|
||||
local function drawChatReceivers()
|
||||
if not receivers then return end
|
||||
|
||||
local fontHeight = draw.GetFontHeight("DarkRPHUD1")
|
||||
local x, y = chat.GetChatBoxPos()
|
||||
y = y - fontHeight - 4
|
||||
|
||||
local receiversCount = #receivers
|
||||
-- No one hears you
|
||||
if receiversCount == 0 then
|
||||
draw.WordBox(2, x, y, DarkRP.getPhrase("hear_noone", currentConfig.text), "DarkRPHUD1", Color(0,0,0,160), Color(255,0,0,255))
|
||||
return
|
||||
-- Everyone hears you
|
||||
elseif receiversCount == player.GetCount() - 1 then
|
||||
draw.WordBox(2, x, y, DarkRP.getPhrase("hear_everyone"), "DarkRPHUD1", Color(0,0,0,160), Color(0,255,0,255))
|
||||
return
|
||||
end
|
||||
|
||||
draw.WordBox(2, x, y - (receiversCount * (fontHeight + 4)), DarkRP.getPhrase("hear_certain_persons", currentConfig.text), "DarkRPHUD1", Color(0,0,0,160), Color(0,255,0,255))
|
||||
for i = 1, receiversCount, 1 do
|
||||
if not IsValid(receivers[i]) then
|
||||
receivers[i] = receivers[#receivers]
|
||||
receivers[#receivers] = nil
|
||||
continue
|
||||
end
|
||||
|
||||
draw.WordBox(2, x, y - (i - 1) * (fontHeight + 4), receivers[i]:Nick(), "DarkRPHUD1", Color(0, 0, 0, 160), color_white)
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find out who could hear the player if they were to speak now
|
||||
---------------------------------------------------------------------------]]
|
||||
local function chatGetRecipients()
|
||||
if not currentConfig then return end
|
||||
|
||||
receivers = {}
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
local hidePly = hook.Run("chatHideRecipient", ply)
|
||||
if not IsValid(ply) or ply == LocalPlayer() or ply:GetNoDraw() or hidePly then continue end
|
||||
|
||||
local val = currentConfig.hearFunc(ply, currentChatText)
|
||||
|
||||
-- Return nil to disable the chat recipients temporarily.
|
||||
if val == nil then
|
||||
receivers = nil
|
||||
return
|
||||
elseif val == true then
|
||||
table.insert(receivers, ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Called when the player starts typing
|
||||
---------------------------------------------------------------------------]]
|
||||
local function startFind()
|
||||
local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_ChatReceivers")
|
||||
if shouldDraw == false then return end
|
||||
|
||||
currentConfig = receiverConfigs[""]
|
||||
hook.Add("Think", "DarkRP_chatRecipients", chatGetRecipients)
|
||||
hook.Add("HUDPaint", "DarkRP_DrawChatReceivers", drawChatReceivers)
|
||||
end
|
||||
hook.Add("StartChat", "DarkRP_StartFindChatReceivers", startFind)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Called when the player stops typing
|
||||
---------------------------------------------------------------------------]]
|
||||
local function stopFind()
|
||||
hook.Remove("Think", "DarkRP_chatRecipients")
|
||||
hook.Remove("HUDPaint", "DarkRP_DrawChatReceivers")
|
||||
end
|
||||
hook.Add("FinishChat", "DarkRP_StopFindChatReceivers", stopFind)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Find out which chat command the user is typing
|
||||
---------------------------------------------------------------------------]]
|
||||
local function findConfig(text)
|
||||
local split = string.Explode(' ', text)
|
||||
local prefix = string.lower(split[1])
|
||||
|
||||
currentChatText = split
|
||||
|
||||
currentConfig = receiverConfigs[prefix] or receiverConfigs[""]
|
||||
end
|
||||
hook.Add("ChatTextChanged", "DarkRP_FindChatRecipients", findConfig)
|
||||
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Default chat receievers. If you want to add your own ones, don't add them to this file. Add them to a clientside module file instead.
|
||||
---------------------------------------------------------------------------]]
|
||||
-- Load after the custom languages have been loaded
|
||||
local function loadChatReceivers()
|
||||
-- Default talk chat receiver has no prefix
|
||||
DarkRP.addChatReceiver("", DarkRP.getPhrase("talk"), function(ply)
|
||||
if GAMEMODE.Config.alltalk then return nil end
|
||||
|
||||
return LocalPlayer():GetPos():DistToSqr(ply:GetPos()) <
|
||||
GAMEMODE.Config.talkDistance * GAMEMODE.Config.talkDistance
|
||||
end)
|
||||
|
||||
DarkRP.addChatReceiver("/ooc", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)
|
||||
DarkRP.addChatReceiver("//", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)
|
||||
DarkRP.addChatReceiver("/a", DarkRP.getPhrase("speak_in_ooc"), function(ply) return true end)
|
||||
DarkRP.addChatReceiver("/w", DarkRP.getPhrase("whisper"), function(ply) return LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < GAMEMODE.Config.whisperDistance * GAMEMODE.Config.whisperDistance end)
|
||||
DarkRP.addChatReceiver("/y", DarkRP.getPhrase("yell"), function(ply) return LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < GAMEMODE.Config.yellDistance * GAMEMODE.Config.yellDistance end)
|
||||
DarkRP.addChatReceiver("/me", DarkRP.getPhrase("perform_your_action"), function(ply) return LocalPlayer():GetPos():DistToSqr(ply:GetPos()) < GAMEMODE.Config.meDistance * GAMEMODE.Config.meDistance end)
|
||||
DarkRP.addChatReceiver("/g", DarkRP.getPhrase("talk_to_your_group"), function(ply)
|
||||
for _, func in pairs(GAMEMODE.DarkRPGroupChats) do
|
||||
if func(LocalPlayer()) and func(ply) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
|
||||
DarkRP.addChatReceiver("/pm", "PM", function(ply, text)
|
||||
if not isstring(text[2]) then return false end
|
||||
text[2] = string.lower(tostring(text[2]))
|
||||
|
||||
return string.find(string.lower(ply:Nick()), text[2], 1, true) ~= nil or
|
||||
string.find(string.lower(ply:SteamName()), text[2], 1, true) ~= nil or
|
||||
string.lower(ply:SteamID()) == text[2]
|
||||
end)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Voice chat receivers
|
||||
---------------------------------------------------------------------------]]
|
||||
local voiceDistance = GM.Config.voiceDistance * GM.Config.voiceDistance
|
||||
DarkRP.addChatReceiver("speak", DarkRP.getPhrase("speak"), function(ply)
|
||||
if not LocalPlayer().DRPIsTalking then return nil end
|
||||
if LocalPlayer():GetPos():DistToSqr(ply:GetPos()) > voiceDistance then return false end
|
||||
|
||||
return not GAMEMODE.Config.dynamicvoice or ply:isInRoom()
|
||||
end)
|
||||
end
|
||||
hook.Add("loadCustomDarkRPItems", "loadChatListeners", loadChatReceivers)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Called when the player starts using their voice
|
||||
---------------------------------------------------------------------------]]
|
||||
local function startFindVoice(ply)
|
||||
if ply ~= LocalPlayer() then return end
|
||||
|
||||
local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_ChatReceivers")
|
||||
if shouldDraw == false then return end
|
||||
|
||||
currentConfig = receiverConfigs["speak"]
|
||||
hook.Add("Think", "DarkRP_chatRecipients", chatGetRecipients)
|
||||
hook.Add("HUDPaint", "DarkRP_DrawChatReceivers", drawChatReceivers)
|
||||
end
|
||||
hook.Add("PlayerStartVoice", "DarkRP_VoiceChatReceiverFinder", startFindVoice)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Called when the player stops using their voice
|
||||
---------------------------------------------------------------------------]]
|
||||
local function stopFindVoice(ply)
|
||||
if ply ~= LocalPlayer() then return end
|
||||
|
||||
stopFind()
|
||||
end
|
||||
hook.Add("PlayerEndVoice", "DarkRP_VoiceChatReceiverFinder", stopFindVoice)
|
||||
56
gamemodes/darkrp/gamemode/modules/chat/cl_interface.lua
Normal file
56
gamemodes/darkrp/gamemode/modules/chat/cl_interface.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
DarkRP.addChatReceiver = DarkRP.stub{
|
||||
name = "addChatReceiver",
|
||||
description = "Add a chat command with specific receivers",
|
||||
parameters = {
|
||||
{
|
||||
name = "prefix",
|
||||
description = "The chat command itself (\"/pm\", \"/ooc\", \"/me\" are some examples)",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "text",
|
||||
description = "The text that shows up when it says \"Some people can hear you X\"",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "hearFunc",
|
||||
description = "A function(ply, splitText) that decides whether this player can or cannot hear you.",
|
||||
type = "function",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.removeChatReceiver = DarkRP.stub{
|
||||
name = "removeChatReceiver",
|
||||
description = "Remove a chat command receiver",
|
||||
parameters = {
|
||||
{
|
||||
name = "prefix",
|
||||
description = "The chat command itself (\"/pm\", \"/ooc\", \"/me\" are some examples)",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "chatHideRecipient",
|
||||
description = "Hide a receipent from who can hear/see your text GUI.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who spoke.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
216
gamemodes/darkrp/gamemode/modules/chat/sh_chatcommands.lua
Normal file
216
gamemodes/darkrp/gamemode/modules/chat/sh_chatcommands.lua
Normal file
@@ -0,0 +1,216 @@
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
DarkRP.chatCommands = DarkRP.chatCommands or {}
|
||||
|
||||
local validChatCommand = {
|
||||
command = isstring,
|
||||
description = isstring,
|
||||
condition = fn.FOr{fn.Curry(fn.Eq, 2)(nil), isfunction},
|
||||
delay = isnumber,
|
||||
tableArgs = fn.FOr{fn.Curry(fn.Eq, 2)(nil), isbool},
|
||||
}
|
||||
|
||||
local checkChatCommand = function(tbl)
|
||||
for k in pairs(validChatCommand) do
|
||||
if not validChatCommand[k](tbl[k]) then
|
||||
return false, k
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function DarkRP.declareChatCommand(tbl)
|
||||
local valid, element = checkChatCommand(tbl)
|
||||
if not valid then
|
||||
DarkRP.error("Incorrect chat command! " .. element .. " is invalid!", 2)
|
||||
end
|
||||
|
||||
tbl.command = string.lower(tbl.command)
|
||||
DarkRP.chatCommands[tbl.command] = DarkRP.chatCommands[tbl.command] or tbl
|
||||
for k, v in pairs(tbl) do
|
||||
DarkRP.chatCommands[tbl.command][k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.removeChatCommand(command)
|
||||
DarkRP.chatCommands[string.lower(command)] = nil
|
||||
end
|
||||
|
||||
function DarkRP.chatCommandAlias(command, ...)
|
||||
local name
|
||||
for k, v in ipairs{...} do
|
||||
name = string.lower(v)
|
||||
|
||||
DarkRP.chatCommands[name] = {command = name}
|
||||
setmetatable(DarkRP.chatCommands[name], {
|
||||
__index = DarkRP.chatCommands[command]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function DarkRP.getChatCommand(command)
|
||||
return DarkRP.chatCommands[string.lower(command)]
|
||||
end
|
||||
|
||||
function DarkRP.getChatCommands()
|
||||
return DarkRP.chatCommands
|
||||
end
|
||||
|
||||
function DarkRP.getSortedChatCommands()
|
||||
local tbl = fn.Compose{table.ClearKeys, table.Copy, DarkRP.getChatCommands}()
|
||||
table.SortByMember(tbl, "command", true)
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- chat commands that have been defined, but not declared
|
||||
DarkRP.getIncompleteChatCommands = fn.Curry(fn.Filter, 3)(fn.Compose{fn.Not, checkChatCommand})(DarkRP.chatCommands)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Chat commands
|
||||
---------------------------------------------------------------------------]]
|
||||
DarkRP.declareChatCommand{
|
||||
command = "pm",
|
||||
description = "Send a private message to someone.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "w",
|
||||
description = "Say something in whisper voice.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "y",
|
||||
description = "Yell something out loud.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "me",
|
||||
description = "Chat roleplay to say you're doing things that you can't show otherwise.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "/",
|
||||
description = "Global server chat.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "a",
|
||||
description = "Global server chat.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "ooc",
|
||||
description = "Global server chat.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "broadcast",
|
||||
description = "Broadcast something as a mayor.",
|
||||
delay = 1.5,
|
||||
condition = plyMeta.isMayor
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "channel",
|
||||
description = "Tune into a radio channel.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "radio",
|
||||
description = "Say something through the radio.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "g",
|
||||
description = "Group chat.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "credits",
|
||||
description = "Send the DarkRP credits to someone.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
|
||||
local function init()
|
||||
if not DarkRP then
|
||||
MsgC(Color(255,0,0), "DarkRP Classic Advert tried to run, but DarkRP wasn't declared!\n")
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.removeChatCommand("advert")
|
||||
DarkRP.declareChatCommand({
|
||||
command = "advert",
|
||||
description = "Displays an advertisement to everyone in chat.",
|
||||
delay = 1.5
|
||||
})
|
||||
|
||||
if SERVER then
|
||||
DarkRP.defineChatCommand("advert",function(ply,args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", "argument", ""))
|
||||
return ""
|
||||
end
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", "argument", ""))
|
||||
return
|
||||
end
|
||||
for k,v in pairs(player.GetAll()) do
|
||||
local col = team.GetColor(ply:Team())
|
||||
DarkRP.talkToPerson(v, col, "[Реклама] " .. ply:Nick(), Color(255, 255, 0, 255), text, ply)
|
||||
end
|
||||
end
|
||||
hook.Call("playerAdverted", nil, ply, args)
|
||||
return args, DoSay
|
||||
end, 1.5)
|
||||
else
|
||||
DarkRP.addChatReceiver("/advert", "advertise", function(ply) return true end)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
if #player.GetAll() > 0 then
|
||||
init()
|
||||
else
|
||||
hook.Add("PlayerInitialSpawn", "dfca-load", init)
|
||||
end
|
||||
else
|
||||
hook.Add("InitPostEntity", "dfca-load", init)
|
||||
end
|
||||
|
||||
|
||||
if SERVER then
|
||||
|
||||
local function Roll(ply, args)
|
||||
local DoSay = function()
|
||||
if GAMEMODE.Config.alltalk then
|
||||
for _, target in pairs(player.GetAll()) do
|
||||
DarkRP.talkToPerson(target, team.GetColor(ply:Team()), ply:Nick().. " выпало " ..math.random(1,100).." из 100.")
|
||||
end
|
||||
else
|
||||
DarkRP.talkToRange(ply, ply:Nick().. " выпало " ..math.random(1,100).." из 100.", "", 250)
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("roll", Roll, 1.5)
|
||||
|
||||
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "roll",
|
||||
description = "write an roll",
|
||||
delay = 1.5
|
||||
}
|
||||
end
|
||||
121
gamemodes/darkrp/gamemode/modules/chat/sh_interface.lua
Normal file
121
gamemodes/darkrp/gamemode/modules/chat/sh_interface.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
DarkRP.declareChatCommand = DarkRP.stub{
|
||||
name = "declareChatCommand",
|
||||
description = "Declare a chat command (describe it)",
|
||||
parameters = {
|
||||
{
|
||||
name = "table",
|
||||
description = "The description of the chat command. Has to contain a string: command, string: description, number: delay, optional function: condition",
|
||||
type = "table",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.removeChatCommand = DarkRP.stub{
|
||||
name = "removeChatCommand",
|
||||
description = "Remove a chat command",
|
||||
parameters = {
|
||||
{
|
||||
name = "command",
|
||||
description = "The chat command to remove",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.chatCommandAlias = DarkRP.stub{
|
||||
name = "chatCommandAlias",
|
||||
description = "Create an alias for a chat command",
|
||||
parameters = {
|
||||
{
|
||||
name = "command",
|
||||
description = "An already existing chat command.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "alias",
|
||||
description = "One or more aliases for the chat command.",
|
||||
type = "vararg",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getChatCommand = DarkRP.stub{
|
||||
name = "getChatCommand",
|
||||
description = "Get the information on a chat command.",
|
||||
parameters = {
|
||||
{
|
||||
name = "command",
|
||||
description = "The chat command",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "chatTable",
|
||||
description = "A table containing the information of the chat command.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getChatCommands = DarkRP.stub{
|
||||
name = "getChatCommands",
|
||||
description = "Get every chat command.",
|
||||
parameters = {
|
||||
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "commands",
|
||||
description = "A table containing every command. Table indices are the command strings.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getSortedChatCommands = DarkRP.stub{
|
||||
name = "getSortedChatCommands",
|
||||
description = "Get every chat command, sorted by their name.",
|
||||
parameters = {
|
||||
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "commands",
|
||||
description = "A table containing every command.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getIncompleteChatCommands = DarkRP.stub{
|
||||
name = "getIncompleteChatCommands",
|
||||
description = "chat commands that have been defined, but not declared. Information about these chat commands is missing.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "commands",
|
||||
description = "A table containing the undeclared chat commands.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
183
gamemodes/darkrp/gamemode/modules/chat/sv_chat.lua
Normal file
183
gamemodes/darkrp/gamemode/modules/chat/sv_chat.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
local function registerCommandDefinition(cmd, callback)
|
||||
local chatcommands = DarkRP.getChatCommands()
|
||||
|
||||
chatcommands[cmd] = chatcommands[cmd] or {}
|
||||
chatcommands[cmd].callback = callback
|
||||
chatcommands[cmd].command = chatcommands[cmd].command or cmd
|
||||
end
|
||||
|
||||
function DarkRP.defineChatCommand(cmd, callback)
|
||||
cmd = string.lower(cmd)
|
||||
local detour = function(ply, arg, ...)
|
||||
local canChatCommand = gamemode.Call("canChatCommand", ply, cmd, arg, ...)
|
||||
if not canChatCommand then
|
||||
return ""
|
||||
end
|
||||
|
||||
local ret = {callback(ply, arg, ...)}
|
||||
local overrideTxt, overrideDoSayFunc = hook.Run("onChatCommand", ply, cmd, arg, ret, ...)
|
||||
|
||||
if overrideTxt then return overrideTxt, overrideDoSayFunc end
|
||||
return unpack(ret)
|
||||
end
|
||||
|
||||
registerCommandDefinition(cmd, detour)
|
||||
end
|
||||
|
||||
function DarkRP.definePrivilegedChatCommand(cmd, priv, callback, extraInfoTbl)
|
||||
cmd = string.lower(cmd)
|
||||
|
||||
local function onCAMIResult(ply, arg, hasAccess, reason)
|
||||
if hasAccess then return callback(ply, arg) end
|
||||
|
||||
local notify = ply:EntIndex() == 0 and print or fp{DarkRP.notify, ply, 1, 4}
|
||||
notify(DarkRP.getPhrase("no_privilege"))
|
||||
end
|
||||
|
||||
local function callbackdetour(ply, arg, ...)
|
||||
local canChatCommand = gamemode.Call("canChatCommand", ply, cmd, arg)
|
||||
if not canChatCommand then
|
||||
return ""
|
||||
end
|
||||
|
||||
CAMI.PlayerHasAccess(ply, priv, fp{onCAMIResult, ply, arg}, nil, extraInfoTbl)
|
||||
|
||||
local overrideTxt, overrideDoSayFunc = hook.Run("onChatCommand", ply, cmd, arg, {""})
|
||||
|
||||
if overrideTxt then return overrideTxt, overrideDoSayFunc end
|
||||
return ""
|
||||
end
|
||||
|
||||
registerCommandDefinition(cmd, callbackdetour)
|
||||
end
|
||||
|
||||
local function RP_PlayerChat(ply, text, teamonly)
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. "): " .. text)
|
||||
local callback = ""
|
||||
local DoSayFunc
|
||||
local groupSay = DarkRP.getChatCommand("g")
|
||||
|
||||
-- Extract the chat command
|
||||
local tblCmd = fn.Compose{
|
||||
DarkRP.getChatCommand,
|
||||
string.lower,
|
||||
fn.Curry(fn.Flip(string.sub), 2)(2), -- extract prefix
|
||||
fn.Curry(fn.GetValue, 2)(1), -- Get the first word
|
||||
fn.Curry(string.Explode, 2)(' ') -- split by spaces
|
||||
}(text)
|
||||
|
||||
if string.sub(text, 1, 1) == GAMEMODE.Config.chatCommandPrefix and tblCmd then
|
||||
local args = string.sub(text, string.len(tblCmd.command) + 3, string.len(text))
|
||||
args = tblCmd.tableArgs and DarkRP.explodeArg(args) or args
|
||||
|
||||
ply.DrpCommandDelays = ply.DrpCommandDelays or {}
|
||||
if tblCmd.delay and ply.DrpCommandDelays[tblCmd.command] and ply.DrpCommandDelays[tblCmd.command] > CurTime() - tblCmd.delay then
|
||||
return ""
|
||||
end
|
||||
|
||||
ply.DrpCommandDelays[tblCmd.command] = CurTime()
|
||||
|
||||
callback, DoSayFunc = tblCmd.callback(ply, args)
|
||||
if callback == "" then
|
||||
return "", "", DoSayFunc
|
||||
end
|
||||
text = string.sub(text, string.len(tblCmd.command) + 3, string.len(text))
|
||||
elseif teamonly and groupSay then
|
||||
callback, DoSayFunc = groupSay.callback(ply, text)
|
||||
return text, "", DoSayFunc
|
||||
end
|
||||
|
||||
if callback ~= "" then
|
||||
callback = callback or "" .. " "
|
||||
end
|
||||
|
||||
return text, callback, DoSayFunc;
|
||||
end
|
||||
|
||||
local function RP_ActualDoSay(ply, text, callback)
|
||||
callback = callback or ""
|
||||
if text == "" then return "" end
|
||||
local col = team.GetColor(ply:Team())
|
||||
local col2 = color_white
|
||||
if not ply:Alive() then
|
||||
col2 = Color(255, 200, 200, 255)
|
||||
col = col2
|
||||
end
|
||||
|
||||
if GAMEMODE.Config.alltalk then
|
||||
local name = ply:Nick()
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
DarkRP.talkToPerson(v, col, callback .. name, col2, text, ply)
|
||||
end
|
||||
else
|
||||
DarkRP.talkToRange(ply, callback .. ply:Nick(), text, GAMEMODE.Config.talkDistance)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
function GM:canChatCommand(ply, cmd, ...)
|
||||
if not ply.DarkRPUnInitialized then return true end
|
||||
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("data_not_loaded_one"))
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("data_not_loaded_two"))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
g_DarkRPOldHookCall = g_DarkRPOldHookCall or hook.Call
|
||||
|
||||
local GM = GM
|
||||
function hook.Call(name, gm, ply, text, teamonly, ...)
|
||||
if name == "PlayerSay" then
|
||||
local dead = not ply:Alive()
|
||||
|
||||
local text2 = text
|
||||
local callback
|
||||
local DoSayFunc
|
||||
|
||||
text2 = g_DarkRPOldHookCall(name, gm, ply, text, teamonly, dead) or text2
|
||||
|
||||
text2, callback, DoSayFunc = RP_PlayerChat(ply, text2, teamonly)
|
||||
if tostring(text2) == " " then text2, callback = callback, text2 end
|
||||
if not GM.Config.deadtalk and dead then return "" end
|
||||
|
||||
if game.IsDedicated() then
|
||||
ServerLog("\"" .. ply:Nick() .. "<" .. ply:UserID() .. ">" .. "<" .. ply:SteamID() .. ">" .. "<" .. team.GetName(ply:Team()) .. ">\" say \"" .. text .. "\"\n" .. "\n")
|
||||
end
|
||||
|
||||
if DoSayFunc then DoSayFunc(text2) return "" end
|
||||
RP_ActualDoSay(ply, text2, callback)
|
||||
|
||||
hook.Call("PostPlayerSay", nil, ply, text2, teamonly, dead)
|
||||
return ""
|
||||
end
|
||||
|
||||
return g_DarkRPOldHookCall(name, gm, ply, text, teamonly, ...)
|
||||
end
|
||||
|
||||
local function ConCommand(ply, _, args)
|
||||
if not args[1] then return end
|
||||
local cmd = string.lower(args[1])
|
||||
local tbl = DarkRP.getChatCommand(cmd)
|
||||
|
||||
if not tbl then return end
|
||||
|
||||
table.remove(args, 1) -- Remove subcommand
|
||||
local arg = tbl.tableArgs and args or table.concat(args, ' ')
|
||||
local time = CurTime()
|
||||
|
||||
if not tbl then return end
|
||||
|
||||
ply.DrpCommandDelays = ply.DrpCommandDelays or {}
|
||||
|
||||
if IsValid(ply) then -- Server console isn't valid
|
||||
if tbl.delay and ply.DrpCommandDelays[cmd] and ply.DrpCommandDelays[cmd] > time - tbl.delay then
|
||||
return
|
||||
end
|
||||
|
||||
ply.DrpCommandDelays[cmd] = time
|
||||
end
|
||||
|
||||
tbl.callback(ply, arg)
|
||||
end
|
||||
concommand.Add("darkrp", ConCommand)
|
||||
241
gamemodes/darkrp/gamemode/modules/chat/sv_chatcommands.lua
Normal file
241
gamemodes/darkrp/gamemode/modules/chat/sv_chatcommands.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
--[[---------------------------------------------------------
|
||||
Talking
|
||||
---------------------------------------------------------]]
|
||||
local function PM(ply, args)
|
||||
local namepos = string.find(args, " ")
|
||||
if not namepos then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local name = string.sub(args, 1, namepos - 1)
|
||||
local msg = string.sub(args, namepos + 1)
|
||||
|
||||
if msg == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(name)
|
||||
if target == ply then return "" end
|
||||
|
||||
if target then
|
||||
local col = team.GetColor(ply:Team())
|
||||
local pname = ply:Nick()
|
||||
local col2 = color_white
|
||||
DarkRP.talkToPerson(target, col, "(PM) " .. pname, col2, msg, ply)
|
||||
DarkRP.talkToPerson(ply, col, "(PM) " .. pname, col2, msg, ply)
|
||||
else
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", tostring(name)))
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("pm", PM, 1.5)
|
||||
|
||||
local function Whisper(ply, args)
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
DarkRP.talkToRange(ply, "(" .. DarkRP.getPhrase("whisper") .. ") " .. ply:Nick(), text, GAMEMODE.Config.whisperDistance)
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("w", Whisper, 1.5)
|
||||
|
||||
local function Yell(ply, args)
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
DarkRP.talkToRange(ply, "(" .. DarkRP.getPhrase("yell") .. ") " .. ply:Nick(), text, GAMEMODE.Config.yellDistance)
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("y", Yell, 1.5)
|
||||
|
||||
local function Me(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
if GAMEMODE.Config.alltalk then
|
||||
local col = team.GetColor(ply:Team())
|
||||
local name = ply:Nick()
|
||||
for _, target in ipairs(player.GetAll()) do
|
||||
DarkRP.talkToPerson(target, col, name .. " " .. text)
|
||||
end
|
||||
else
|
||||
DarkRP.talkToRange(ply, ply:Nick() .. " " .. text, "", GAMEMODE.Config.meDistance)
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("me", Me, 1.5)
|
||||
|
||||
local function OOC(ply, args)
|
||||
if not GAMEMODE.Config.ooc then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("disabled", DarkRP.getPhrase("ooc"), ""))
|
||||
return ""
|
||||
end
|
||||
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
local col = team.GetColor(ply:Team())
|
||||
local col2 = color_white
|
||||
if not ply:Alive() then
|
||||
col2 = Color(255, 200, 200, 255)
|
||||
col = col2
|
||||
end
|
||||
|
||||
local phrase = DarkRP.getPhrase("ooc")
|
||||
local name = ply:Nick()
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
DarkRP.talkToPerson(v, col, "(" .. phrase .. ") " .. name, col2, text, ply)
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("/", OOC, true, 1.5)
|
||||
DarkRP.defineChatCommand("a", OOC, true, 1.5)
|
||||
DarkRP.defineChatCommand("ooc", OOC, true, 1.5)
|
||||
|
||||
local function MayorBroadcast(ply, args)
|
||||
if args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
local Team = ply:Team()
|
||||
if not RPExtraTeams[Team] or not RPExtraTeams[Team].mayor then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("incorrect_job", DarkRP.getPhrase("broadcast")))
|
||||
return ""
|
||||
end
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return
|
||||
end
|
||||
|
||||
local col = team.GetColor(ply:Team())
|
||||
local col2 = Color(170, 0, 0, 255)
|
||||
local phrase = DarkRP.getPhrase("broadcast")
|
||||
local name = ply:Nick()
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
DarkRP.talkToPerson(v, col, phrase .. " " .. name, col2, text, ply)
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("broadcast", MayorBroadcast, 1.5)
|
||||
|
||||
local function SetRadioChannel(ply,args)
|
||||
local channel = DarkRP.toInt(args)
|
||||
if channel == nil or channel < 0 or channel > 100 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), "0<" .. DarkRP.getPhrase("channel") .. "<100"))
|
||||
return ""
|
||||
end
|
||||
DarkRP.notify(ply, 2, 4, DarkRP.getPhrase("channel_set_to_x", args))
|
||||
ply.RadioChannel = channel
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("channel", SetRadioChannel)
|
||||
|
||||
local function SayThroughRadio(ply,args)
|
||||
if not ply.RadioChannel then ply.RadioChannel = 1 end
|
||||
local radioChannel = ply.RadioChannel
|
||||
if not args or args == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return ""
|
||||
end
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return
|
||||
end
|
||||
local col = Color(180, 180, 180, 255)
|
||||
local phrase = DarkRP.getPhrase("radio_x", radioChannel)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v.RadioChannel == radioChannel then
|
||||
DarkRP.talkToPerson(v, col, phrase, col, text, ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("radio", SayThroughRadio, 1.5)
|
||||
|
||||
local function GroupMsg(ply, args)
|
||||
local DoSay = function(text)
|
||||
if text == "" then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
|
||||
return
|
||||
end
|
||||
|
||||
local col = team.GetColor(ply:Team())
|
||||
|
||||
local groupChats = {}
|
||||
for _, func in pairs(GAMEMODE.DarkRPGroupChats) do
|
||||
-- not the group of the player
|
||||
if not func(ply) then continue end
|
||||
|
||||
table.insert(groupChats, func)
|
||||
end
|
||||
|
||||
if table.IsEmpty(groupChats) then return "" end
|
||||
|
||||
local phrase = DarkRP.getPhrase("group")
|
||||
local name = ply:Nick()
|
||||
local color = color_white
|
||||
for _, target in ipairs(player.GetAll()) do
|
||||
-- The target is in any of the group chats
|
||||
for _, func in ipairs(groupChats) do
|
||||
if not func(target, ply) then continue end
|
||||
|
||||
DarkRP.talkToPerson(target, col, phrase .. " " .. name, color, text, ply)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return args, DoSay
|
||||
end
|
||||
DarkRP.defineChatCommand("g", GroupMsg, 0)
|
||||
|
||||
-- here's the new easter egg. Easier to find, more subtle, doesn't only credit FPtje and unib5
|
||||
-- WARNING: DO NOT EDIT THIS
|
||||
-- You can edit DarkRP but you HAVE to credit the original authors!
|
||||
-- You even have to credit all the previous authors when you rename the gamemode.
|
||||
-- local CreditsWait = true
|
||||
local function GetDarkRPAuthors(ply, args)
|
||||
local target = DarkRP.findPlayer(args) -- Only send to one player. Prevents spamming
|
||||
target = IsValid(target) and target or ply
|
||||
|
||||
if target ~= ply then
|
||||
if ply.CreditsWait then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("wait_with_that")) return "" end
|
||||
ply.CreditsWait = true
|
||||
timer.Simple(60, function() if IsValid(ply) then ply.CreditsWait = nil end end) -- so people don't spam it
|
||||
end
|
||||
|
||||
local rf = RecipientFilter()
|
||||
rf:AddPlayer(target)
|
||||
if ply ~= target then
|
||||
rf:AddPlayer(ply)
|
||||
end
|
||||
|
||||
umsg.Start("DarkRP_Credits", rf)
|
||||
umsg.End()
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("credits", GetDarkRPAuthors, 50)
|
||||
145
gamemodes/darkrp/gamemode/modules/chat/sv_interface.lua
Normal file
145
gamemodes/darkrp/gamemode/modules/chat/sv_interface.lua
Normal file
@@ -0,0 +1,145 @@
|
||||
DarkRP.defineChatCommand = DarkRP.stub{
|
||||
name = "defineChatCommand",
|
||||
description = "Create a chat command that calls the function",
|
||||
parameters = {
|
||||
{
|
||||
name = "chat command",
|
||||
description = "The registered chat command",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "callback",
|
||||
description = "The function that is called when the chat command is executed",
|
||||
type = "function",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.definePrivilegedChatCommand = DarkRP.stub{
|
||||
name = "definePrivilegedChatCommand",
|
||||
description = "Create a chat command that calls the function if the player has the right CAMI privilege. Will automatically notify the user when they don't have access. Note that chat command functions registered with this function can NOT override the chat that will appear after the command has been executed.",
|
||||
parameters = {
|
||||
{
|
||||
name = "chat command",
|
||||
description = "The registered chat command",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "privilege",
|
||||
description = "The name of the CAMI privilege",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "callback",
|
||||
description = "The function that is called when the chat command is executed",
|
||||
type = "function",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "PostPlayerSay",
|
||||
description = "Called after a player has said something.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who spoke.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "text",
|
||||
description = "The thing they said.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "teamonly",
|
||||
description = "Whether they said it to their team only.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "dead",
|
||||
description = "Whether they are dead.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canChatCommand",
|
||||
description = "Called when a player tries to run any chat command or uses the DarkRP console command. ",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who spoke.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "command",
|
||||
description = "The thing they said.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "arguments",
|
||||
description = "The arguments of the chat command, given as one string.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "canChatCommand",
|
||||
description = "Whether the player is allowed to run the chat command.",
|
||||
type = "boolean"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onChatCommand",
|
||||
description = "Called after a player has run any chat command or uses the DarkRP console command. Note: the chat command has already been run. Use canChatCommand if you want to stop chat commands from being run.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who spoke.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "command",
|
||||
description = "The thing they said.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "arguments",
|
||||
description = "The arguments of the chat command, given either as one string or a table of strings. That depends on whether the command is declared to use table arguments.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "return",
|
||||
description = "The return value of the chat command function. Should contain a chat text override and/or a say function. See the return values of this hook for a description",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "overrideText",
|
||||
description = "Overrides the text a chat command will put in everyone's chat box. Return nil to not change behaviour.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "overrideSayFunc",
|
||||
description = "Say functions handle what needs to be said to whom. The say function for PMs for example make sure only the sender and receiver see the message. You can override this behaviour by returning a different say function in this hook. Return nil to not change behaviour.",
|
||||
type = "function"
|
||||
},
|
||||
}
|
||||
}
|
||||
70
gamemodes/darkrp/gamemode/modules/chatindicator/cl_init.lua
Normal file
70
gamemodes/darkrp/gamemode/modules/chatindicator/cl_init.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
local function drawIndicator(ply)
|
||||
if not ply:IsTyping() then
|
||||
if ply.indicator then
|
||||
ply.indicator:Remove()
|
||||
ply.indicator = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local chatIndicator = hook.Call("DrawChatIndicator", nil, ply)
|
||||
if chatIndicator == true then return end
|
||||
|
||||
local indicator = ply.indicator
|
||||
if not IsValid(indicator) then
|
||||
indicator = ClientsideModel("models/extras/info_speech.mdl", RENDERGROUP_OPAQUE)
|
||||
if not IsValid(indicator) then return end -- In case the non networked entity limit is hit (still prints a red error message, but doesn't spam client console with lua errors)
|
||||
ply.indicator = indicator
|
||||
end
|
||||
indicator:SetNoDraw(true)
|
||||
indicator:SetModelScale(0.6)
|
||||
|
||||
local ragdoll = ply:GetRagdollEntity()
|
||||
if IsValid(ragdoll) then
|
||||
local maxs = ragdoll:OBBMaxs()
|
||||
indicator:SetPos(ragdoll:GetPos() + Vector(0, 0, maxs.z) + Vector(0, 0, 12))
|
||||
else
|
||||
indicator:SetPos(ply:GetPos() + Vector(0, 0, 72 * ply:GetModelScale()) + Vector(0, 0, 12))
|
||||
end
|
||||
|
||||
local curTime = CurTime()
|
||||
local angle = indicator:GetAngles()
|
||||
angle.y = (angle.y + (360 * (curTime - (indicator.lastDraw or 0)))) % 360
|
||||
indicator:SetAngles(angle)
|
||||
indicator.lastDraw = curTime
|
||||
|
||||
indicator:SetupBones()
|
||||
indicator:DrawModel()
|
||||
end
|
||||
|
||||
hook.Add("PostPlayerDraw", "DarkRP_ChatIndicator", drawIndicator)
|
||||
hook.Add("CreateClientsideRagdoll", "DarkRP_ChatIndicator", function(ent, ragdoll)
|
||||
if not ent:IsPlayer() then return end
|
||||
|
||||
local oldRenderOverride = ragdoll.RenderOverride -- Just in case - best be safe
|
||||
ragdoll.RenderOverride = function(self)
|
||||
if ent:IsValid() then
|
||||
drawIndicator(ent)
|
||||
end
|
||||
|
||||
if oldRenderOverride then
|
||||
oldRenderOverride(self)
|
||||
else
|
||||
self:DrawModel()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- CSEnts aren't GC'd.
|
||||
-- https://github.com/Facepunch/garrysmod-issues/issues/1387
|
||||
gameevent.Listen("player_disconnect")
|
||||
hook.Add("player_disconnect", "DarkRP_ChatIndicator", function(data)
|
||||
local ply = Player(data.userid)
|
||||
|
||||
if not IsValid(ply) then return end -- disconnected while joining
|
||||
|
||||
if ply.indicator then
|
||||
ply.indicator:Remove()
|
||||
ply.indicator = nil
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,18 @@
|
||||
DarkRP.hookStub{
|
||||
name = "DrawChatIndicator",
|
||||
description = "Call when the Chat Indicator is drawn. Return to overwrite.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player the indicator should be drawn for.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "override",
|
||||
description = "Return true in your hook to disable the default drawing.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
377
gamemodes/darkrp/gamemode/modules/chatsounds.lua
Normal file
377
gamemodes/darkrp/gamemode/modules/chatsounds.lua
Normal file
@@ -0,0 +1,377 @@
|
||||
-- This module will make voice sounds play when certain words are typed in the chat
|
||||
-- You can add/remove sounds as you wish using DarkRP.setChatSound, just follow the format used here
|
||||
-- To disable them completely, set GM.Config.chatsounds to false
|
||||
-- TODO: Add female sounds & detect gender of model, and use combine sounds for CPs
|
||||
|
||||
local sounds = {}
|
||||
sounds["ammo"] = {"vo/npc/male01/ammo03.wav", "vo/npc/male01/ammo04.wav", "vo/npc/male01/ammo05.wav"}
|
||||
|
||||
sounds["behind you"] = {"vo/npc/male01/behindyou01.wav", "vo/npc/male01/behindyou02.wav"}
|
||||
|
||||
sounds["better reload"] = {"vo/npc/male01/youdbetterreload01.wav"}
|
||||
|
||||
sounds["bullshit"] = {"vo/npc/male01/question26.wav"}
|
||||
|
||||
sounds["bull shit"] = sounds["bullshit"]
|
||||
|
||||
sounds["cheese"] = {"vo/npc/male01/question06.wav"}
|
||||
|
||||
sounds["combine"] = {"vo/npc/male01/combine01.wav", "vo/npc/male01/combine02.wav"}
|
||||
|
||||
sounds["coming"] = {"vo/npc/male01/squad_approach04.wav"}
|
||||
|
||||
sounds["cops"] = {"vo/npc/male01/civilprotection01.wav", "vo/npc/male01/civilprotection02.wav", "vo/npc/male01/cps01.wav", "vo/npc/male01/cps02.wav"}
|
||||
|
||||
sounds["cp"] = sounds["cops"]
|
||||
sounds["cps"] = sounds["cops"]
|
||||
|
||||
sounds["cut it"] = {"vo/trainyard/male01/cit_hit01.wav", "vo/trainyard/male01/cit_hit02.wav", "vo/trainyard/male01/cit_hit03.wav", "vo/trainyard/male01/cit_hit04.wav", "vo/trainyard/male01/cit_hit05.wav"}
|
||||
|
||||
sounds["dont tell me"] = {"vo/npc/male01/gordead_ans03.wav"}
|
||||
|
||||
sounds["de ja vu"] = {"vo/npc/male01/question05.wav"}
|
||||
|
||||
sounds["dejavu"] = sounds["de ja vu"]
|
||||
|
||||
sounds["excuse me"] = {"vo/npc/male01/excuseme01.wav", "vo/npc/male01/excuseme02.wav"}
|
||||
|
||||
sounds["fantastic"] = {"vo/npc/male01/fantastic01.wav", "vo/npc/male01/fantastic02.wav"}
|
||||
|
||||
sounds["figures"] = {"vo/npc/male01/answer03.wav"}
|
||||
|
||||
sounds["finally"] = {"vo/npc/male01/finally.wav"}
|
||||
|
||||
sounds["follow"] = {"vo/coast/odessa/male01/stairman_follow01.wav", "vo/npc/male01/squad_away03.wav", "vo/coast/cardock/le_followme.wav"}
|
||||
|
||||
sounds["focus"] = {"vo/npc/male01/answer18.wav", "vo/npc/male01/answer19.wav"}
|
||||
|
||||
sounds["freeman"] = {"vo/npc/male01/freeman.wav", "vo/npc/male01/docfreeman01.wav", "vo/npc/male01/docfreeman02.wav"}
|
||||
|
||||
sounds["get down"] = {"vo/npc/male01/getdown02.wav"}
|
||||
|
||||
sounds["get in"] = {"vo/canals/gunboat_getin.wav"}
|
||||
|
||||
sounds["get out"] = {"vo/npc/male01/gethellout.wav"}
|
||||
|
||||
sounds["good god"] = {"vo/npc/male01/goodgod.wav", "vo/npc/male01/gordead_ans04.wav"}
|
||||
|
||||
sounds["gosh"] = sounds["good god"]
|
||||
|
||||
sounds["got one"] = {"vo/npc/male01/gotone01.wav", "vo/npc/male01/gotone01.wav"}
|
||||
|
||||
sounds["gotta reload"] = {"vo/npc/male01/gottareload01.wav"}
|
||||
|
||||
sounds["gtfo"] = sounds["get out"]
|
||||
|
||||
sounds["hacks"] = {"vo/npc/male01/hacks01.wav", "vo/npc/male01/hacks02.wav", "vo/npc/male01/thehacks01.wav", "vo/npc/male01/thehacks02.wav"}
|
||||
|
||||
sounds["hax"] = sounds["hacks"]
|
||||
sounds["haxx"] = sounds["hacks"]
|
||||
|
||||
sounds["help"] = {"vo/npc/male01/help01.wav"}
|
||||
|
||||
sounds["here they come"] = {"vo/npc/male01/heretheycome01.wav", "vo/npc/male01/incoming02.wav"}
|
||||
|
||||
sounds["hello"] = {"vo/npc/male01/hi01.wav", "vo/npc/male01/hi02.wav"}
|
||||
|
||||
sounds["hey"] = sounds["hello"]
|
||||
sounds["hi"] = sounds["hello"]
|
||||
|
||||
sounds["heads up"] = {"vo/npc/male01/headsup01.wav", "vo/npc/male01/headsup02.wav"}
|
||||
|
||||
sounds["he's dead"] = {"vo/npc/male01/gordead_ques01.wav", "vo/npc/male01/gordead_ques07.wav"}
|
||||
|
||||
sounds["he is dead"] = sounds["he's dead"]
|
||||
|
||||
sounds["how about that"] = {"vo/npc/male01/answer25.wav"}
|
||||
|
||||
sounds["i know"] = {"vo/npc/male01/answer08.wav"}
|
||||
|
||||
sounds["ill stay here"] = {"vo/npc/male01/illstayhere01.wav", "vo/npc/male01/holddownspot01.wav", "vo/npc/male01/holddownspot02.wav", "vo/npc/male01/imstickinghere01.wav", "vo/npc/male01/littlecorner01.wav"}
|
||||
|
||||
sounds["i'll stay here"] = sounds["ill stay here"]
|
||||
sounds["i will stay here"] = sounds["ill stay here"]
|
||||
|
||||
sounds["im busy"] = {"vo/npc/male01/busy02.wav"}
|
||||
|
||||
sounds["i'm busy"] = sounds["im busy"]
|
||||
|
||||
sounds["im with you"] = {"vo/npc/male01/answer13.wav"}
|
||||
|
||||
sounds["i'm with you"] = sounds["im with you"]
|
||||
|
||||
sounds["isnt good"] = {"vo/trainyard/male01/cit_window_use01.wav"}
|
||||
|
||||
sounds["isn't good"] = sounds["isnt good"]
|
||||
sounds["incoming"] = sounds["here they come"]
|
||||
|
||||
sounds["it cant be"] = {"vo/npc/male01/gordead_ques06.wav"}
|
||||
|
||||
sounds["it can't be"] = sounds["it cant be"]
|
||||
|
||||
sounds["it is okay"] = {"vo/npc/male01/answer02.wav"}
|
||||
|
||||
sounds["it's okay"] = sounds["it is okay"]
|
||||
|
||||
sounds["kay"] = {"vo/npc/male01/ok01.wav", "vo/npc/male01/ok02.wav"}
|
||||
|
||||
sounds["kk"] = sounds["kay"]
|
||||
|
||||
sounds["lead the way"] = {"vo/npc/male01/leadtheway01.wav", "vo/npc/male01/leadtheway02.wav"}
|
||||
|
||||
sounds["lead on"] = sounds["lead the way"]
|
||||
|
||||
sounds["lets go"] = {"vo/npc/male01/letsgo01.wav", "vo/npc/male01/letsgo02.wav"}
|
||||
|
||||
sounds["let's go"] = sounds["lets go"]
|
||||
|
||||
sounds["never"] = {"vo/Citadel/eli_nonever.wav"}
|
||||
|
||||
sounds["never can tell"] = {"vo/npc/male01/answer23.wav"}
|
||||
|
||||
sounds["nice"] = {"vo/npc/male01/nice.wav"}
|
||||
|
||||
sounds["no"] = {"vo/Citadel/br_no.wav", "vo/Citadel/eli_notobreen.wav"}
|
||||
|
||||
sounds["not good"] = sounds["isnt good"]
|
||||
|
||||
sounds["not sure"] = {"vo/npc/male01/answer21.wav"}
|
||||
|
||||
sounds["now what"] = {"vo/npc/male01/gordead_ans01.wav", "vo/npc/male01/gordead_ans15.wav"}
|
||||
|
||||
sounds["oh no"] = {"vo/npc/male01/gordead_ans05.wav", "vo/npc/male01/ohno.wav"}
|
||||
|
||||
sounds["oh my god"] = sounds["good god"]
|
||||
sounds["omg"] = sounds["good god"]
|
||||
sounds["omfg"] = sounds["good god"]
|
||||
sounds["ok"] = sounds["kay"]
|
||||
sounds["okay"] = sounds["kay"]
|
||||
|
||||
sounds["oops"] = {"vo/npc/male01/whoops01.wav"}
|
||||
|
||||
sounds["over here"] = {"vo/npc/male01/overhere01.wav", "vo/npc/male01/squad_away02.wav"}
|
||||
|
||||
sounds["over there"] = {"vo/npc/male01/overthere01.wav", "vo/npc/male01/overthere02.wav"}
|
||||
|
||||
sounds["pardon me"] = {"vo/npc/male01/pardonme01.wav", "vo/npc/male01/pardonme02.wav"}
|
||||
|
||||
sounds["please no"] = {"vo/npc/male01/gordead_ans06.wav"}
|
||||
|
||||
sounds["right on"] = {"vo/npc/male01/answer18.wav"}
|
||||
|
||||
sounds["run"] = {"vo/npc/male01/strider_run.wav"}
|
||||
|
||||
sounds["same here"] = {"vo/npc/male01/answer07.wav"}
|
||||
|
||||
sounds["shut up"] = {"vo/npc/male01/answer17.wav"}
|
||||
|
||||
sounds["spread the word"] = {"vo/npc/male01/gordead_ans10.wav"}
|
||||
|
||||
sounds["stop it"] = sounds["cut it"]
|
||||
sounds["stop that"] = sounds["cut it"]
|
||||
|
||||
sounds["stop looking at me"] = {"vo/npc/male01/vquestion01.wav"}
|
||||
|
||||
sounds["sorry"] = {"vo/npc/male01/sorry01.wav", "vo/npc/male01/sorry02.wav", "vo/npc/male01/sorry03.wav"}
|
||||
|
||||
sounds["sry"] = sounds["sorry"]
|
||||
|
||||
sounds["take cover"] = {"vo/npc/male01/takecover02.wav"}
|
||||
|
||||
sounds["take this medkit"] = {"vo/npc/male01/health01.wav", "vo/npc/male01/health02.wav", "vo/npc/male01/health03.wav", "vo/npc/male01/health04.wav"}
|
||||
|
||||
sounds["task at hand"] = {"vo/npc/male01/answer18.wav"}
|
||||
|
||||
sounds["talking to me"] = {"vo/npc/male01/answer30.wav"}
|
||||
|
||||
sounds["thats you"] = {"vo/npc/male01/answer01.wav"}
|
||||
|
||||
sounds["this cant be"] = sounds["it cant be"]
|
||||
sounds["this can't be"] = sounds["it cant be"]
|
||||
|
||||
sounds["this is bad"] = {"vo/npc/male01/gordead_ques10.wav"}
|
||||
|
||||
sounds["too much info"] = {"vo/npc/male01/answer26.wav"}
|
||||
|
||||
sounds["too much information"] = sounds["too much info"]
|
||||
|
||||
sounds["uhoh"] = {"vo/npc/male01/uhoh.wav"}
|
||||
|
||||
sounds["uh oh"] = sounds["uhoh"]
|
||||
|
||||
sounds["wait"] = {"vo/trainyard/man_waitaminute.wav"}
|
||||
|
||||
sounds["wait for me"] = {"vo/npc/male01/squad_reinforce_single04.wav"}
|
||||
|
||||
sounds["wait for us"] = {"vo/npc/male01/squad_reinforce_group04.wav"}
|
||||
|
||||
sounds["wanna bet"] = {"vo/npc/male01/answer27.wav"}
|
||||
|
||||
sounds["watch out"] = {"vo/npc/male01/watchout.wav"}
|
||||
|
||||
sounds["we are done for"] = {"vo/npc/male01/gordead_ans14.wav"}
|
||||
|
||||
sounds["we're done for"] = sounds["we are done for"]
|
||||
|
||||
sounds["what now"] = {"vo/npc/male01/gordead_ques16.wav"}
|
||||
|
||||
sounds["whatever you say"] = {"vo/npc/male01/squad_affirm03.wav"}
|
||||
|
||||
sounds["whats the use"] = {"vo/npc/male01/gordead_ans11.wav"}
|
||||
|
||||
sounds["what's the use"] = sounds["whats the use"]
|
||||
|
||||
sounds["whats the point"] = {"vo/npc/male01/gordead_ans12.wav"}
|
||||
|
||||
sounds["what's the point"] = sounds["whats the point"]
|
||||
sounds["whoops"] = sounds["oops"]
|
||||
|
||||
sounds["why go on"] = {"vo/npc/male01/gordead_ans13.wav"}
|
||||
|
||||
sounds["why telling me"] = {"vo/npc/male01/answer24.wav"}
|
||||
|
||||
sounds["yeah"] = {"vo/npc/male01/yeah02.wav"}
|
||||
|
||||
sounds["yes"] = sounds["yeah"]
|
||||
|
||||
sounds["you and me both"] = {"vo/npc/male01/answer14.wav"}
|
||||
|
||||
sounds["you never know"] = {"vo/npc/male01/answer22.wav"}
|
||||
|
||||
sounds["you sure"] = {"vo/npc/male01/answer37.wav"}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canChatSound",
|
||||
description = "Whether a chat sound can be played.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who triggered the chat sound.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "chatPhrase",
|
||||
description = "The chat sound phrase that has been detected.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "chatText",
|
||||
description = "The whole chat text the player sent that contains the chat sound phrase.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "canChatSound",
|
||||
description = "False if the chat sound should not be played.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onChatSound",
|
||||
description = "When a chat sound is played.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who triggered the chat sound.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "chatPhrase",
|
||||
description = "The chat sound phrase that was detected.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "chatText",
|
||||
description = "The whole chat text the player sent that contains the chat sound phrase.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
}
|
||||
}
|
||||
|
||||
local function CheckChat(ply, text)
|
||||
if not GAMEMODE.Config.chatsounds or ply.nextSpeechSound and ply.nextSpeechSound > CurTime() then return end
|
||||
local prefix = string.sub(text, 0, 1)
|
||||
if prefix == "/" or prefix == "!" or prefix == "@" then return end -- should cover most chat commands for various mods/addons
|
||||
local longestMatch = nil
|
||||
local longestMatchLength = 0
|
||||
for k, v in pairs(sounds) do
|
||||
local res1, res2 = string.find(string.lower(text), k)
|
||||
if not res1 then continue end
|
||||
local charBefore = text[res1 - 1]
|
||||
local charAfter = text[res2 + 1]
|
||||
local length = res2 - res1
|
||||
-- Check whether the match is not part of a larger word (e.g. "no" should not match when "know" is said)
|
||||
if charBefore and charBefore ~= "" and charBefore ~= " " then continue end
|
||||
if charAfter and charAfter ~= "" and charAfter ~= " " then continue end
|
||||
|
||||
if length > longestMatchLength then
|
||||
longestMatch = k
|
||||
longestMatchLength = length
|
||||
end
|
||||
end
|
||||
|
||||
if not longestMatch then return end
|
||||
|
||||
local canChatSound = hook.Call("canChatSound", nil, ply, longestMatch, text)
|
||||
if canChatSound == false then return end
|
||||
ply:EmitSound(table.Random(sounds[longestMatch]), 80, 100)
|
||||
ply.nextSpeechSound = CurTime() + GAMEMODE.Config.chatsoundsdelay -- make sure they don't spam HAX HAX HAX, if the server owner so desires
|
||||
hook.Call("onChatSound", nil, ply, longestMatch, text)
|
||||
end
|
||||
hook.Add("PostPlayerSay", "ChatSounds", CheckChat)
|
||||
|
||||
DarkRP.getChatSound = DarkRP.stub{
|
||||
name = "getChatSound",
|
||||
description = "Get a chat sound (play a noise when someone says something) associated with the given phrase.",
|
||||
parameters = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The text that triggers the chat sound.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "soundPaths",
|
||||
description = "A table of string sound paths associated with the given text.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
function DarkRP.getChatSound(text)
|
||||
return sounds[string.lower(text or "")]
|
||||
end
|
||||
|
||||
DarkRP.setChatSound = DarkRP.stub{
|
||||
name = "setChatSound",
|
||||
description = "Set a chat sound (play a noise when someone says something)",
|
||||
parameters = {
|
||||
{
|
||||
name = "text",
|
||||
description = "The text that should trigger the sound.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "sounds",
|
||||
description = "A table of string sound paths.",
|
||||
type = "table",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
function DarkRP.setChatSound(text, sndTable)
|
||||
sounds[string.lower(text or "")] = sndTable
|
||||
end
|
||||
56
gamemodes/darkrp/gamemode/modules/cppi/sh_cppi.lua
Normal file
56
gamemodes/darkrp/gamemode/modules/cppi/sh_cppi.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
if CPPI then return end
|
||||
CPPI = {}
|
||||
CPPI.CPPI_DEFER = 100100 --\100\100 = dd
|
||||
CPPI.CPPI_NOTIMPLEMENTED = 7080
|
||||
|
||||
function CPPI:GetName()
|
||||
return "DarkRP"
|
||||
end
|
||||
|
||||
function CPPI:GetVersion()
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function CPPI:GetInterfaceVersion()
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function CPPI:GetNameFromUID(uid)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
function PLAYER:CPPIGetFriends()
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
local ENTITY = FindMetaTable("Entity")
|
||||
function ENTITY:CPPIGetOwner()
|
||||
return NULL, CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function ENTITY:CPPISetOwner(ply)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function ENTITY:CPPISetOwnerUID(UID)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function ENTITY:CPPICanTool(ply, tool)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function ENTITY:CPPICanPhysgun(ply)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function ENTITY:CPPICanPickup(ply)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
|
||||
function ENTITY:CPPICanPunt(ply)
|
||||
return CPPI.CPPI_NOTIMPLEMENTED
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
local MotdMessage =
|
||||
[[
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
DarkRP Message of the day!
|
||||
---------------------------------------------------------------------------
|
||||
]]
|
||||
|
||||
local endMOTD = "---------------------------------------------------------------------------\n"
|
||||
|
||||
local function drawMOTD(text)
|
||||
MsgC(Color(255, 20, 20, 255), MotdMessage, color_white, text, Color(255, 20, 20, 255), endMOTD)
|
||||
end
|
||||
|
||||
local function receiveMOTD(html, len, headers, code)
|
||||
if not headers or headers.Status and string.sub(headers.Status, 1, 3) ~= "200" then return end
|
||||
drawMOTD(html)
|
||||
end
|
||||
|
||||
local function showMOTD()
|
||||
http.Fetch("https://raw.githubusercontent.com/FPtje/DarkRPMotd/master/motd.txt", receiveMOTD, fn.Id)
|
||||
end
|
||||
timer.Simple(5, showMOTD)
|
||||
|
||||
concommand.Add("DarkRP_motd", showMOTD)
|
||||
44
gamemodes/darkrp/gamemode/modules/deathpov/cl_init.lua
Normal file
44
gamemodes/darkrp/gamemode/modules/deathpov/cl_init.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
local view = {
|
||||
origin = Vector(0, 0, 0),
|
||||
angles = Angle(0, 0, 0),
|
||||
fov = 90,
|
||||
znear = 1
|
||||
}
|
||||
|
||||
local deathpov = GM.Config.deathpov
|
||||
hook.Add("CalcView", "rp_deathPOV", function(ply, origin, angles, fov)
|
||||
-- Entity:Alive() is being slow as hell, we might actually see ourselves from third person for frame or two
|
||||
if not deathpov or ply:Health() > 0 then return end
|
||||
|
||||
local Ragdoll = ply:GetRagdollEntity()
|
||||
if not IsValid(Ragdoll) then return end
|
||||
|
||||
local head = Ragdoll:LookupAttachment("eyes")
|
||||
head = Ragdoll:GetAttachment(head)
|
||||
if not head or not head.Pos then return end
|
||||
|
||||
if not Ragdoll.BonesRattled then
|
||||
Ragdoll.BonesRattled = true
|
||||
|
||||
Ragdoll:InvalidateBoneCache()
|
||||
Ragdoll:SetupBones()
|
||||
|
||||
local matrix
|
||||
|
||||
for bone = 0, (Ragdoll:GetBoneCount() or 1) do
|
||||
if Ragdoll:GetBoneName(bone):lower():find("head") then
|
||||
matrix = Ragdoll:GetBoneMatrix(bone)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(matrix) then
|
||||
matrix:SetScale(Vector(0, 0, 0))
|
||||
end
|
||||
end
|
||||
|
||||
view.origin = head.Pos + head.Ang:Up() * 8
|
||||
view.angles = head.Ang
|
||||
|
||||
return view
|
||||
end)
|
||||
248
gamemodes/darkrp/gamemode/modules/dermaskin/cl_dermaskin.lua
Normal file
248
gamemodes/darkrp/gamemode/modules/dermaskin/cl_dermaskin.lua
Normal file
@@ -0,0 +1,248 @@
|
||||
-- Skin for DarkRP gui's
|
||||
SKIN = {}
|
||||
|
||||
SKIN.PrintName = "DarkRP"
|
||||
SKIN.Author = "FPtje Falco"
|
||||
SKIN.DermaVersion = 1
|
||||
SKIN.GwenTexture = Material("darkrp/darkrpderma.png")
|
||||
|
||||
|
||||
SKIN.colTextEntryText = color_white
|
||||
SKIN.colTextEntryTextCursor = color_white
|
||||
|
||||
SKIN.colTextEntryTextPlaceholder = Color(200, 200, 200, 200) -- Unofficial but will probably be named this
|
||||
|
||||
SKIN.tex = {}
|
||||
|
||||
SKIN.tex.Selection = GWEN.CreateTextureBorder(384, 32, 31, 31, 4, 4, 4, 4)
|
||||
|
||||
SKIN.tex.Panels = {}
|
||||
SKIN.tex.Panels.Normal = GWEN.CreateTextureBorder(256, 0, 63, 63, 16, 16, 16, 16)
|
||||
SKIN.tex.Panels.Bright = GWEN.CreateTextureBorder(256 + 64, 0, 63, 63, 16, 16, 16, 16)
|
||||
SKIN.tex.Panels.Dark = GWEN.CreateTextureBorder(256, 64, 63, 63, 16, 16, 16, 16)
|
||||
SKIN.tex.Panels.Highlight = GWEN.CreateTextureBorder(256 + 64, 64, 63, 63, 16, 16, 16, 16)
|
||||
|
||||
SKIN.tex.Button = GWEN.CreateTextureBorder(480, 0, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Button_Hovered = GWEN.CreateTextureBorder(480, 32, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Button_Dead = GWEN.CreateTextureBorder(480, 64, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Button_Down = GWEN.CreateTextureBorder(480, 96, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Shadow = GWEN.CreateTextureBorder(448, 0, 31, 31, 8, 8, 8, 8)
|
||||
|
||||
SKIN.tex.Tree = GWEN.CreateTextureBorder(256, 128, 127, 127, 16, 16, 16, 16)
|
||||
SKIN.tex.Checkbox_Checked = GWEN.CreateTextureNormal(448, 32, 15, 15)
|
||||
SKIN.tex.Checkbox = GWEN.CreateTextureNormal(464, 32, 15, 15)
|
||||
SKIN.tex.CheckboxD_Checked = GWEN.CreateTextureNormal(448, 48, 15, 15)
|
||||
SKIN.tex.CheckboxD = GWEN.CreateTextureNormal(464, 48, 15, 15)
|
||||
--SKIN.tex.RadioButton_Checked = GWEN.CreateTextureNormal(448, 64, 15, 15)
|
||||
--SKIN.tex.RadioButton = GWEN.CreateTextureNormal(464, 64, 15, 15)
|
||||
--SKIN.tex.RadioButtonD_Checked = GWEN.CreateTextureNormal(448, 80, 15, 15)
|
||||
--SKIN.tex.RadioButtonD = GWEN.CreateTextureNormal(464, 80, 15, 15)
|
||||
SKIN.tex.TreePlus = GWEN.CreateTextureNormal(448, 96, 15, 15)
|
||||
SKIN.tex.TreeMinus = GWEN.CreateTextureNormal(464, 96, 15, 15)
|
||||
--SKIN.tex.Menu_Strip = GWEN.CreateTextureBorder(0, 128, 127, 21, 1, 1, 1, 1)
|
||||
SKIN.tex.TextBox = GWEN.CreateTextureBorder(0, 150, 127, 21, 4, 4, 4, 4)
|
||||
SKIN.tex.TextBox_Focus = GWEN.CreateTextureBorder(0, 172, 127, 21, 4, 4, 4, 4)
|
||||
SKIN.tex.TextBox_Disabled = GWEN.CreateTextureBorder(0, 193, 127, 21, 4, 4, 4, 4)
|
||||
SKIN.tex.MenuBG_Margin = GWEN.CreateTextureBorder(128, 128, 127, 63, 24, 8, 8, 8)
|
||||
SKIN.tex.MenuBG = GWEN.CreateTextureBorder(128, 192, 127, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.MenuBG_Hover = GWEN.CreateTextureBorder(128, 256, 127, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.MenuBG_Spacer = GWEN.CreateTextureNormal(128, 288, 127, 3)
|
||||
SKIN.tex.Tab_Control = GWEN.CreateTextureBorder(0, 256, 127, 127, 8, 8, 8, 8)
|
||||
SKIN.tex.TabB_Active = GWEN.CreateTextureBorder(0, 416, 63, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.TabB_Inactive = GWEN.CreateTextureBorder(0 + 128, 416, 63, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.TabT_Active = GWEN.CreateTextureBorder(0, 384, 63, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.TabT_Inactive = GWEN.CreateTextureBorder(0 + 128, 384, 63, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.TabL_Active = GWEN.CreateTextureBorder(64, 384, 31, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.TabL_Inactive = GWEN.CreateTextureBorder(64 + 128, 384, 31, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.TabR_Active = GWEN.CreateTextureBorder(96, 384, 31, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.TabR_Inactive = GWEN.CreateTextureBorder(96 + 128, 384, 31, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.Tab_Bar = GWEN.CreateTextureBorder(128, 352, 127, 31, 4, 4, 4, 4)
|
||||
|
||||
SKIN.tex.Window = {}
|
||||
|
||||
SKIN.tex.Window.Normal = GWEN.CreateTextureBorder(0, 0, 127, 127, 8, 32, 8, 8)
|
||||
SKIN.tex.Window.Inactive = GWEN.CreateTextureBorder(128, 0, 127, 127, 8, 32, 8, 8)
|
||||
SKIN.tex.Window.Close = GWEN.CreateTextureNormal(0, 224, 24, 24)
|
||||
SKIN.tex.Window.Close_Hover = GWEN.CreateTextureNormal(32, 224, 24, 24)
|
||||
SKIN.tex.Window.Close_Down = GWEN.CreateTextureNormal(64, 224, 24, 24)
|
||||
SKIN.tex.Window.Close_Disabled = GWEN.CreateTextureNormal(96, 224, 24, 24)
|
||||
|
||||
SKIN.tex.Window.Maxi = GWEN.CreateTextureNormal(32 + 96 * 2, 448, 31, 31)
|
||||
SKIN.tex.Window.Maxi_Hover = GWEN.CreateTextureNormal(64 + 96 * 2, 448, 31, 31)
|
||||
SKIN.tex.Window.Maxi_Down = GWEN.CreateTextureNormal(96 + 96 * 2, 448, 31, 31)
|
||||
|
||||
SKIN.tex.Window.Restore = GWEN.CreateTextureNormal(32 + 96 * 2, 448 + 32, 31, 31)
|
||||
SKIN.tex.Window.Restore_Hover = GWEN.CreateTextureNormal(64 + 96 * 2, 448 + 32, 31, 31)
|
||||
SKIN.tex.Window.Restore_Down = GWEN.CreateTextureNormal(96 + 96 * 2, 448 + 32, 31, 31)
|
||||
|
||||
SKIN.tex.Window.Mini = GWEN.CreateTextureNormal(32 + 96, 448, 31, 31)
|
||||
SKIN.tex.Window.Mini_Hover = GWEN.CreateTextureNormal(64 + 96, 448, 31, 31)
|
||||
SKIN.tex.Window.Mini_Down = GWEN.CreateTextureNormal(96 + 96, 448, 31, 31)
|
||||
|
||||
SKIN.tex.Scroller = {}
|
||||
SKIN.tex.Scroller.TrackV = GWEN.CreateTextureBorder(384, 208, 15, 127, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonV_Normal = GWEN.CreateTextureBorder(384 + 16, 208, 15, 127, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonV_Hover = GWEN.CreateTextureBorder(384 + 32, 208, 15, 127, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonV_Down = GWEN.CreateTextureBorder(384 + 48, 208, 15, 127, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonV_Disabled = GWEN.CreateTextureBorder(384 + 64, 208, 15, 127, 4, 4, 4, 4)
|
||||
|
||||
SKIN.tex.Scroller.TrackH = GWEN.CreateTextureBorder(384, 128, 127, 15, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonH_Normal = GWEN.CreateTextureBorder(384, 128 + 16, 127, 15, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonH_Hover = GWEN.CreateTextureBorder(384, 128 + 32, 127, 15, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonH_Down = GWEN.CreateTextureBorder(384, 128 + 48, 127, 15, 4, 4, 4, 4)
|
||||
SKIN.tex.Scroller.ButtonH_Disabled = GWEN.CreateTextureBorder(384, 128 + 64, 127, 15, 4, 4, 4, 4)
|
||||
|
||||
SKIN.tex.Scroller.LeftButton_Normal = GWEN.CreateTextureBorder(464, 208, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.LeftButton_Hover = GWEN.CreateTextureBorder(480, 208, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.LeftButton_Down = GWEN.CreateTextureBorder(464, 272, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.LeftButton_Disabled = GWEN.CreateTextureBorder(480 + 48, 272, 15, 15, 2, 2, 2, 2)
|
||||
|
||||
SKIN.tex.Scroller.UpButton_Normal = GWEN.CreateTextureBorder(464, 208 + 16, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.UpButton_Hover = GWEN.CreateTextureBorder(480, 208 + 16, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.UpButton_Down = GWEN.CreateTextureBorder(464, 272 + 16, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.UpButton_Disabled = GWEN.CreateTextureBorder(480 + 48, 272 + 16, 15, 15, 2, 2, 2, 2)
|
||||
|
||||
SKIN.tex.Scroller.RightButton_Normal = GWEN.CreateTextureBorder(464, 208 + 32, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.RightButton_Hover = GWEN.CreateTextureBorder(480, 208 + 32, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.RightButton_Down = GWEN.CreateTextureBorder(464, 272 + 32, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.RightButton_Disabled = GWEN.CreateTextureBorder(480 + 48, 272 + 32, 15, 15, 2, 2, 2, 2)
|
||||
|
||||
SKIN.tex.Scroller.DownButton_Normal = GWEN.CreateTextureBorder(464, 208 + 48, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.DownButton_Hover = GWEN.CreateTextureBorder(480, 208 + 48, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.DownButton_Down = GWEN.CreateTextureBorder(464, 272 + 48, 15, 15, 2, 2, 2, 2)
|
||||
SKIN.tex.Scroller.DownButton_Disabled = GWEN.CreateTextureBorder(480 + 48, 272 + 48, 15, 15, 2, 2, 2, 2)
|
||||
|
||||
SKIN.tex.Menu = {}
|
||||
SKIN.tex.Menu.RightArrow = GWEN.CreateTextureNormal(464, 112, 15, 15)
|
||||
|
||||
SKIN.tex.Input = {}
|
||||
SKIN.tex.Input.ListBox = GWEN.CreateTextureBorder(256, 256, 63, 127, 8, 8, 8, 8)
|
||||
|
||||
SKIN.tex.Input.ComboBox = {}
|
||||
SKIN.tex.Input.ComboBox.Normal = GWEN.CreateTextureBorder(384, 336, 127, 31, 8, 8, 32, 8)
|
||||
SKIN.tex.Input.ComboBox.Hover = GWEN.CreateTextureBorder(384, 336 + 32, 127, 31, 8, 8, 32, 8)
|
||||
SKIN.tex.Input.ComboBox.Down = GWEN.CreateTextureBorder(384, 336 + 64, 127, 31, 8, 8, 32, 8)
|
||||
SKIN.tex.Input.ComboBox.Disabled = GWEN.CreateTextureBorder(384, 336 + 96, 127, 31, 8, 8, 32, 8)
|
||||
|
||||
SKIN.tex.Input.ComboBox.Button = {}
|
||||
SKIN.tex.Input.ComboBox.Button.Normal = GWEN.CreateTextureNormal(496, 272, 15, 15)
|
||||
SKIN.tex.Input.ComboBox.Button.Hover = GWEN.CreateTextureNormal(496, 272 + 16, 15, 15)
|
||||
SKIN.tex.Input.ComboBox.Button.Down = GWEN.CreateTextureNormal(496, 272 + 32, 15, 15)
|
||||
SKIN.tex.Input.ComboBox.Button.Disabled = GWEN.CreateTextureNormal(496, 272 + 48, 15, 15)
|
||||
|
||||
SKIN.tex.Input.UpDown = {}
|
||||
SKIN.tex.Input.UpDown.Up = {}
|
||||
SKIN.tex.Input.UpDown.Up.Normal = GWEN.CreateTextureCentered(384, 112, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Up.Hover = GWEN.CreateTextureCentered(384 + 8, 112, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Up.Down = GWEN.CreateTextureCentered(384 + 16, 112, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Up.Disabled = GWEN.CreateTextureCentered(384 + 24, 112, 7, 7)
|
||||
|
||||
SKIN.tex.Input.UpDown.Down = {}
|
||||
SKIN.tex.Input.UpDown.Down.Normal = GWEN.CreateTextureCentered(384, 120, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Down.Hover = GWEN.CreateTextureCentered(384 + 8, 120, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Down.Down = GWEN.CreateTextureCentered(384 + 16, 120, 7, 7)
|
||||
SKIN.tex.Input.UpDown.Down.Disabled = GWEN.CreateTextureCentered(384 + 24, 120, 7, 7)
|
||||
|
||||
SKIN.tex.Input.Slider = {}
|
||||
SKIN.tex.Input.Slider.H = {}
|
||||
SKIN.tex.Input.Slider.H.Normal = GWEN.CreateTextureNormal(416, 32, 15, 15)
|
||||
SKIN.tex.Input.Slider.H.Hover = GWEN.CreateTextureNormal(416, 32 + 16, 15, 15)
|
||||
SKIN.tex.Input.Slider.H.Down = GWEN.CreateTextureNormal(416, 32 + 32, 15, 15)
|
||||
SKIN.tex.Input.Slider.H.Disabled = GWEN.CreateTextureNormal(416, 32 + 48, 15, 15)
|
||||
|
||||
SKIN.tex.Input.Slider.V = {}
|
||||
SKIN.tex.Input.Slider.V.Normal = GWEN.CreateTextureNormal(416 + 16, 32, 15, 15)
|
||||
SKIN.tex.Input.Slider.V.Hover = GWEN.CreateTextureNormal(416 + 16, 32 + 16, 15, 15)
|
||||
SKIN.tex.Input.Slider.V.Down = GWEN.CreateTextureNormal(416 + 16, 32 + 32, 15, 15)
|
||||
SKIN.tex.Input.Slider.V.Disabled = GWEN.CreateTextureNormal(416 + 16, 32 + 48, 15, 15)
|
||||
|
||||
SKIN.tex.Input.ListBox = {}
|
||||
SKIN.tex.Input.ListBox.Background = GWEN.CreateTextureBorder(256, 256, 63, 127, 8, 8, 8, 8)
|
||||
SKIN.tex.Input.ListBox.Hovered = GWEN.CreateTextureBorder(320, 320, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Input.ListBox.EvenLine = GWEN.CreateTextureBorder(352, 256, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Input.ListBox.OddLine = GWEN.CreateTextureBorder(352, 288, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Input.ListBox.EvenLineSelected = GWEN.CreateTextureBorder(320, 270, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.Input.ListBox.OddLineSelected = GWEN.CreateTextureBorder(320, 288, 31, 31, 8, 8, 8, 8)
|
||||
|
||||
SKIN.tex.ProgressBar = {}
|
||||
SKIN.tex.ProgressBar.Back = GWEN.CreateTextureBorder(384, 0, 31, 31, 8, 8, 8, 8)
|
||||
SKIN.tex.ProgressBar.Front = GWEN.CreateTextureBorder(384 + 32, 0, 31, 31, 8, 8, 8, 8)
|
||||
|
||||
|
||||
SKIN.tex.CategoryList = {}
|
||||
SKIN.tex.CategoryList.Outer = GWEN.CreateTextureBorder(256, 384, 63, 63, 8, 8, 8, 8)
|
||||
SKIN.tex.CategoryList.Inner = GWEN.CreateTextureBorder(256 + 64, 384, 63, 63, 8, 21, 8, 8)
|
||||
SKIN.tex.CategoryList.Header = GWEN.CreateTextureBorder(320, 352, 63, 31, 8, 8, 8, 8)
|
||||
|
||||
SKIN.tex.Tooltip = GWEN.CreateTextureBorder(384, 64, 31, 31, 8, 8, 8, 8)
|
||||
|
||||
SKIN.Colours = {}
|
||||
|
||||
SKIN.Colours.Window = {}
|
||||
SKIN.Colours.Window.TitleActive = GWEN.TextureColor(4 + 8 * 0, 508)
|
||||
SKIN.Colours.Window.TitleInactive = GWEN.TextureColor(4 + 8 * 1, 508)
|
||||
|
||||
SKIN.Colours.Button = {}
|
||||
SKIN.Colours.Button.Normal = GWEN.TextureColor(4 + 8 * 2, 508)
|
||||
SKIN.Colours.Button.Hover = GWEN.TextureColor(4 + 8 * 3, 508)
|
||||
SKIN.Colours.Button.Down = GWEN.TextureColor(4 + 8 * 2, 500)
|
||||
SKIN.Colours.Button.Disabled = GWEN.TextureColor(4 + 8 * 3, 500)
|
||||
|
||||
SKIN.Colours.Tab = {}
|
||||
SKIN.Colours.Tab.Active = {}
|
||||
SKIN.Colours.Tab.Active.Normal = GWEN.TextureColor(4 + 8 * 4, 508)
|
||||
SKIN.Colours.Tab.Active.Hover = GWEN.TextureColor(4 + 8 * 5, 508)
|
||||
SKIN.Colours.Tab.Active.Down = GWEN.TextureColor(4 + 8 * 4, 500)
|
||||
SKIN.Colours.Tab.Active.Disabled = GWEN.TextureColor(4 + 8 * 5, 500)
|
||||
|
||||
SKIN.Colours.Tab.Inactive = {}
|
||||
SKIN.Colours.Tab.Inactive.Normal = GWEN.TextureColor(4 + 8 * 6, 508)
|
||||
SKIN.Colours.Tab.Inactive.Hover = GWEN.TextureColor(4 + 8 * 7, 508)
|
||||
SKIN.Colours.Tab.Inactive.Down = GWEN.TextureColor(4 + 8 * 6, 500)
|
||||
SKIN.Colours.Tab.Inactive.Disabled = GWEN.TextureColor(4 + 8 * 7, 500)
|
||||
|
||||
SKIN.Colours.Label = {}
|
||||
SKIN.Colours.Label.Default = GWEN.TextureColor(4 + 8 * 8, 508)
|
||||
SKIN.Colours.Label.Bright = GWEN.TextureColor(4 + 8 * 9, 508)
|
||||
SKIN.Colours.Label.Dark = GWEN.TextureColor(4 + 8 * 8, 500)
|
||||
SKIN.Colours.Label.Highlight = GWEN.TextureColor(4 + 8 * 9, 500)
|
||||
|
||||
SKIN.Colours.Tree = {}
|
||||
SKIN.Colours.Tree.Lines = GWEN.TextureColor(4 + 8 * 10, 508)
|
||||
---- !!!
|
||||
SKIN.Colours.Tree.Normal = GWEN.TextureColor(4 + 8 * 11, 508)
|
||||
SKIN.Colours.Tree.Hover = GWEN.TextureColor(4 + 8 * 10, 500)
|
||||
SKIN.Colours.Tree.Selected = GWEN.TextureColor(4 + 8 * 11, 500)
|
||||
|
||||
SKIN.Colours.Properties = {}
|
||||
SKIN.Colours.Properties.Line_Normal = GWEN.TextureColor(4 + 8 * 12, 508)
|
||||
SKIN.Colours.Properties.Line_Selected = GWEN.TextureColor(4 + 8 * 13, 508)
|
||||
SKIN.Colours.Properties.Line_Hover = GWEN.TextureColor(4 + 8 * 12, 500)
|
||||
SKIN.Colours.Properties.Title = GWEN.TextureColor(4 + 8 * 13, 500)
|
||||
SKIN.Colours.Properties.Column_Normal = GWEN.TextureColor(4 + 8 * 14, 508)
|
||||
SKIN.Colours.Properties.Column_Selected = GWEN.TextureColor(4 + 8 * 15, 508)
|
||||
SKIN.Colours.Properties.Column_Hover = GWEN.TextureColor(4 + 8 * 14, 500)
|
||||
SKIN.Colours.Properties.Border = GWEN.TextureColor(4 + 8 * 15, 500)
|
||||
SKIN.Colours.Properties.Label_Normal = GWEN.TextureColor(4 + 8 * 16, 508)
|
||||
SKIN.Colours.Properties.Label_Selected = GWEN.TextureColor(4 + 8 * 17, 508)
|
||||
SKIN.Colours.Properties.Label_Hover = GWEN.TextureColor(4 + 8 * 16, 500)
|
||||
|
||||
SKIN.Colours.Category = {}
|
||||
SKIN.Colours.Category.Header = GWEN.TextureColor(4 + 8 * 18, 500)
|
||||
SKIN.Colours.Category.Header_Closed = GWEN.TextureColor(4 + 8 * 19, 500)
|
||||
SKIN.Colours.Category.Line = {}
|
||||
SKIN.Colours.Category.Line.Text = GWEN.TextureColor(4 + 8 * 20, 508)
|
||||
SKIN.Colours.Category.Line.Text_Hover = GWEN.TextureColor(4 + 8 * 21, 508)
|
||||
SKIN.Colours.Category.Line.Text_Selected = GWEN.TextureColor(4 + 8 * 20, 500)
|
||||
SKIN.Colours.Category.Line.Button = GWEN.TextureColor(4 + 8 * 21, 500)
|
||||
SKIN.Colours.Category.Line.Button_Hover = GWEN.TextureColor(4 + 8 * 22, 508)
|
||||
SKIN.Colours.Category.Line.Button_Selected = GWEN.TextureColor(4 + 8 * 23, 508)
|
||||
SKIN.Colours.Category.LineAlt = {}
|
||||
SKIN.Colours.Category.LineAlt.Text = GWEN.TextureColor(4 + 8 * 22, 500)
|
||||
SKIN.Colours.Category.LineAlt.Text_Hover = GWEN.TextureColor(4 + 8 * 23, 500)
|
||||
SKIN.Colours.Category.LineAlt.Text_Selected = GWEN.TextureColor(4 + 8 * 24, 508)
|
||||
SKIN.Colours.Category.LineAlt.Button = GWEN.TextureColor(4 + 8 * 25, 508)
|
||||
SKIN.Colours.Category.LineAlt.Button_Hover = GWEN.TextureColor(4 + 8 * 24, 500)
|
||||
SKIN.Colours.Category.LineAlt.Button_Selected = GWEN.TextureColor(4 + 8 * 25, 500)
|
||||
|
||||
derma.DefineSkin("DarkRP", "The official SKIN for DarkRP", SKIN)
|
||||
@@ -0,0 +1 @@
|
||||
resource.AddFile("materials/darkrp/darkrpderma.png")
|
||||
165
gamemodes/darkrp/gamemode/modules/doorsystem/cl_doors.lua
Normal file
165
gamemodes/darkrp/gamemode/modules/doorsystem/cl_doors.lua
Normal file
@@ -0,0 +1,165 @@
|
||||
local meta = FindMetaTable("Entity")
|
||||
local black = color_black
|
||||
local white = Color(255, 255, 255, 200)
|
||||
local red = Color(128, 30, 30, 255)
|
||||
local changeDoorAccess = false
|
||||
|
||||
local function updatePrivs()
|
||||
CAMI.PlayerHasAccess(LocalPlayer(), "DarkRP_ChangeDoorSettings", function(b, _)
|
||||
changeDoorAccess = b
|
||||
end)
|
||||
end
|
||||
-- Timer due to lack of "on privilege changed" hook
|
||||
hook.Add("InitPostEntity", "Load door privileges", function()
|
||||
updatePrivs()
|
||||
timer.Create("Door changeDoorAccess checker", 1, 0, updatePrivs)
|
||||
end)
|
||||
|
||||
function meta:drawOwnableInfo()
|
||||
local ply = LocalPlayer()
|
||||
if ply:InVehicle() and not ply:GetAllowWeaponsInVehicle() then return end
|
||||
|
||||
-- Look, if you want to change the way door ownership is drawn, don't edit this file, use the hook instead!
|
||||
local doorDrawing = hook.Call("HUDDrawDoorData", nil, self)
|
||||
if doorDrawing == true then return end
|
||||
|
||||
local blocked = self:getKeysNonOwnable()
|
||||
local doorTeams = self:getKeysDoorTeams()
|
||||
local doorGroup = self:getKeysDoorGroup()
|
||||
local playerOwned = self:isKeysOwned() or table.GetFirstValue(self:getKeysCoOwners() or {}) ~= nil
|
||||
local owned = playerOwned or doorGroup or doorTeams
|
||||
|
||||
local doorInfo = {}
|
||||
|
||||
local title = self:getKeysTitle()
|
||||
if title then table.insert(doorInfo, title) end
|
||||
|
||||
if owned then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("keys_owned_by"))
|
||||
end
|
||||
|
||||
if playerOwned then
|
||||
if self:isKeysOwned() then table.insert(doorInfo, self:getDoorOwner():Nick()) end
|
||||
for k in pairs(self:getKeysCoOwners() or {}) do
|
||||
local ent = Player(k)
|
||||
if not IsValid(ent) or not ent:IsPlayer() then continue end
|
||||
table.insert(doorInfo, ent:Nick())
|
||||
end
|
||||
|
||||
local allowedCoOwn = self:getKeysAllowedToOwn()
|
||||
if allowedCoOwn and not fn.Null(allowedCoOwn) then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("keys_other_allowed"))
|
||||
|
||||
for k in pairs(allowedCoOwn) do
|
||||
local ent = Player(k)
|
||||
if not IsValid(ent) or not ent:IsPlayer() then continue end
|
||||
table.insert(doorInfo, ent:Nick())
|
||||
end
|
||||
end
|
||||
elseif doorGroup then
|
||||
table.insert(doorInfo, doorGroup)
|
||||
elseif doorTeams then
|
||||
for k, v in pairs(doorTeams) do
|
||||
if not v or not RPExtraTeams[k] then continue end
|
||||
|
||||
table.insert(doorInfo, RPExtraTeams[k].name)
|
||||
end
|
||||
elseif blocked and changeDoorAccess then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("keys_allow_ownership"))
|
||||
elseif not blocked then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("keys_unowned"))
|
||||
if changeDoorAccess then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("keys_disallow_ownership"))
|
||||
end
|
||||
end
|
||||
|
||||
if self:IsVehicle() then
|
||||
local driver = self:GetDriver()
|
||||
if driver:IsPlayer() then
|
||||
table.insert(doorInfo, DarkRP.getPhrase("driver", driver:Nick()))
|
||||
end
|
||||
end
|
||||
|
||||
local x, y = ScrW() / 2, ScrH() / 2
|
||||
local text = table.concat(doorInfo, "\n")
|
||||
draw.DrawNonParsedText(text, "Roboto20", x , y + 1 , black, 1)
|
||||
draw.DrawNonParsedText(text, "Roboto20", x, y, (blocked or owned) and white or red, 1)
|
||||
end
|
||||
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Door data
|
||||
---------------------------------------------------------------------------]]
|
||||
DarkRP.doorData = DarkRP.doorData or {}
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Interface functions
|
||||
---------------------------------------------------------------------------]]
|
||||
function meta:getDoorData()
|
||||
local doorData = DarkRP.doorData[self:EntIndex()] or {}
|
||||
|
||||
self.DoorData = doorData -- Backwards compatibility
|
||||
|
||||
return doorData
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Networking
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Retrieve all the data for all doors
|
||||
---------------------------------------------------------------------------]]
|
||||
local function retrieveAllDoorData(len)
|
||||
local count = net.ReadUInt(16)
|
||||
|
||||
for i = 1, count do
|
||||
local ix = net.ReadUInt(16)
|
||||
local varCount = net.ReadUInt(8)
|
||||
|
||||
DarkRP.doorData[ix] = DarkRP.doorData[ix] or {}
|
||||
|
||||
for vc = 1, varCount do
|
||||
local name, value = DarkRP.readNetDoorVar()
|
||||
DarkRP.doorData[ix][name] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
net.Receive("DarkRP_AllDoorData", retrieveAllDoorData)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Update changed variables
|
||||
---------------------------------------------------------------------------]]
|
||||
local function updateDoorData()
|
||||
local door = net.ReadUInt(32)
|
||||
|
||||
DarkRP.doorData[door] = DarkRP.doorData[door] or {}
|
||||
|
||||
local var, value = DarkRP.readNetDoorVar()
|
||||
|
||||
DarkRP.doorData[door][var] = value
|
||||
end
|
||||
net.Receive("DarkRP_UpdateDoorData", updateDoorData)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Set a value of a single doorvar to nil
|
||||
---------------------------------------------------------------------------]]
|
||||
local function removeDoorVar()
|
||||
local door = net.ReadUInt(16)
|
||||
local id = net.ReadUInt(8)
|
||||
|
||||
local name = id == 0 and net.ReadString() or DarkRP.getDoorVars()[id].name
|
||||
|
||||
if not DarkRP.doorData[door] then return end
|
||||
DarkRP.doorData[door][name] = nil
|
||||
end
|
||||
net.Receive("DarkRP_RemoveDoorVar", removeDoorVar)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Remove doordata of removed entity
|
||||
---------------------------------------------------------------------------]]
|
||||
local function removeDoorData()
|
||||
local door = net.ReadUInt(32)
|
||||
DarkRP.doorData[door] = nil
|
||||
end
|
||||
net.Receive("DarkRP_RemoveDoorData", removeDoorData)
|
||||
@@ -0,0 +1,48 @@
|
||||
DarkRP.readNetDoorVar = DarkRP.stub{
|
||||
name = "readNetDoorVar",
|
||||
description = "Internal function. You probably shouldn't need this. DarkRP calls this function when reading DoorVar net messages. This function reads the net data for a specific DoorVar.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "name",
|
||||
description = "The name of the DoorVar.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "value",
|
||||
description = "The value of the DoorVar.",
|
||||
type = "any"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.drawOwnableInfo = DarkRP.stub{
|
||||
name = "drawOwnableInfo",
|
||||
description = "Draw the ownability information on a door or vehicle.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "HUDDrawDoorData",
|
||||
description = "Called when DarkRP is about to draw the door ownability information of a door or vehicle. Override this hook to ",
|
||||
parameters = {
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door or vehicle of which the ownability information is about to be drawn.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "override",
|
||||
description = "Return true in your hook to disable the default drawing and use your own.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
323
gamemodes/darkrp/gamemode/modules/doorsystem/sh_doors.lua
Normal file
323
gamemodes/darkrp/gamemode/modules/doorsystem/sh_doors.lua
Normal file
@@ -0,0 +1,323 @@
|
||||
local meta = FindMetaTable("Entity")
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
|
||||
local ownableDoors = {
|
||||
["func_door"] = true,
|
||||
["func_door_rotating"] = true,
|
||||
["prop_door_rotating"] = true
|
||||
}
|
||||
local unOwnableDoors = {
|
||||
["func_door"] = true,
|
||||
["func_door_rotating"] = true,
|
||||
["prop_door_rotating"] = true,
|
||||
["func_movelinear"] = true,
|
||||
["prop_dynamic"] = true
|
||||
}
|
||||
function meta:isKeysOwnable()
|
||||
if not IsValid(self) then return false end
|
||||
|
||||
local class = self:GetClass()
|
||||
|
||||
if (ownableDoors[class] or
|
||||
(GAMEMODE.Config.allowvehicleowning and self:IsVehicle() and (not IsValid(self:GetParent()) or not self:GetParent():IsVehicle()))) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function meta:isDoor()
|
||||
local class = self:GetClass()
|
||||
|
||||
if unOwnableDoors[class] then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function meta:isKeysOwned()
|
||||
if IsValid(self:getDoorOwner()) then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function meta:getDoorOwner()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.owner and Player(doorData.owner) or nil
|
||||
end
|
||||
|
||||
function meta:isMasterOwner(ply)
|
||||
return ply == self:getDoorOwner()
|
||||
end
|
||||
|
||||
function meta:isKeysOwnedBy(ply)
|
||||
if self:isMasterOwner(ply) then return true end
|
||||
|
||||
local coOwners = self:getKeysCoOwners()
|
||||
return coOwners and coOwners[ply:UserID()] or false
|
||||
end
|
||||
|
||||
function meta:isKeysAllowedToOwn(ply)
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return false end
|
||||
|
||||
return doorData.allowedToOwn and doorData.allowedToOwn[ply:UserID()] or false
|
||||
end
|
||||
|
||||
function meta:getKeysNonOwnable()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.nonOwnable
|
||||
end
|
||||
|
||||
function meta:getKeysTitle()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.title
|
||||
end
|
||||
|
||||
function meta:getKeysDoorGroup()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.groupOwn
|
||||
end
|
||||
|
||||
function meta:getKeysDoorTeams()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData or table.IsEmpty(doorData.teamOwn or {}) then return nil end
|
||||
|
||||
return doorData.teamOwn
|
||||
end
|
||||
|
||||
function meta:getKeysAllowedToOwn()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.allowedToOwn
|
||||
end
|
||||
|
||||
function meta:getKeysCoOwners()
|
||||
local doorData = self:getDoorData()
|
||||
if not doorData then return nil end
|
||||
|
||||
return doorData.extraOwners
|
||||
end
|
||||
|
||||
local function canLockUnlock(ply, ent)
|
||||
local Team = ply:Team()
|
||||
local group = ent:getKeysDoorGroup()
|
||||
local teamOwn = ent:getKeysDoorTeams()
|
||||
|
||||
return ent:isKeysOwnedBy(ply) or
|
||||
(group and table.HasValue(RPExtraTeamDoors[group] or {}, Team)) or
|
||||
(teamOwn and teamOwn[Team])
|
||||
end
|
||||
|
||||
function plyMeta:canKeysLock(ent)
|
||||
local canLock = hook.Run("canKeysLock", self, ent)
|
||||
|
||||
if canLock ~= nil then return canLock end
|
||||
return canLockUnlock(self, ent)
|
||||
end
|
||||
|
||||
function plyMeta:canKeysUnlock(ent)
|
||||
local canUnlock = hook.Run("canKeysUnlock", self, ent)
|
||||
|
||||
if canUnlock ~= nil then return canUnlock end
|
||||
return canLockUnlock(self, ent)
|
||||
end
|
||||
|
||||
local netDoorVars = {}
|
||||
local netDoorVarsByName = {}
|
||||
|
||||
DarkRP.getDoorVars = fp{fn.Id, netDoorVars}
|
||||
DarkRP.getDoorVarsByName = fp{fn.Id, netDoorVarsByName}
|
||||
|
||||
function DarkRP.registerDoorVar(name, writeFn, readFn)
|
||||
netDoorVarsByName[name] = {name = name, write = writeFn, read = readFn}
|
||||
|
||||
netDoorVarsByName[name].id = table.insert(netDoorVars, netDoorVarsByName[name])
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function DarkRP.writeNetDoorVar(name, value)
|
||||
local var = netDoorVarsByName[name]
|
||||
|
||||
-- Not registered, send inefficiently
|
||||
if not var then
|
||||
net.WriteUInt(0, 8) -- indicate unregistered
|
||||
net.WriteString(name)
|
||||
net.WriteType(value)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
net.WriteUInt(var.id, 8)
|
||||
var.write(value)
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function DarkRP.readNetDoorVar()
|
||||
local id = net.ReadUInt(8)
|
||||
|
||||
-- unregistered var
|
||||
if id == 0 then
|
||||
return net.ReadString(), net.ReadType(net.ReadUInt(8))
|
||||
end
|
||||
|
||||
if not netDoorVars[id] then
|
||||
DarkRP.error("Unregistered DarkRP Doorvar clientside: " .. id, 2, {"Some addon is registering some DoorVar serverside, but not clientside."})
|
||||
end
|
||||
|
||||
return netDoorVars[id].name, netDoorVars[id].read()
|
||||
end
|
||||
end
|
||||
|
||||
DarkRP.registerDoorVar("groupOwn",
|
||||
function(val)
|
||||
net.WriteUInt(RPExtraTeamDoorIDs[val], 16)
|
||||
end,
|
||||
function()
|
||||
local id = net.ReadUInt(16)
|
||||
for name, id2 in pairs(RPExtraTeamDoorIDs) do
|
||||
if id == id2 then return name end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- Net helper function for writing tables with numbers as keys and bools as values
|
||||
local function writeNumBoolTbl(tbl)
|
||||
net.WriteUInt(table.Count(tbl), 10)
|
||||
|
||||
for num, _ in pairs(tbl) do
|
||||
net.WriteUInt(num, 16)
|
||||
end
|
||||
end
|
||||
|
||||
-- Net helper function for reading tables with numbers as keys and bools as values
|
||||
local function readNumBoolTbl(tbl)
|
||||
local res = {}
|
||||
local count = net.ReadUInt(10)
|
||||
|
||||
for i = 1, count do
|
||||
res[net.ReadUInt(16)] = true
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
DarkRP.registerDoorVar("owner", fp{fn.Flip(net.WriteInt), 16}, fp{net.ReadUInt, 16})
|
||||
DarkRP.registerDoorVar("nonOwnable", net.WriteBool, net.ReadBool)
|
||||
DarkRP.registerDoorVar("teamOwn", writeNumBoolTbl, readNumBoolTbl)
|
||||
DarkRP.registerDoorVar("allowedToOwn", writeNumBoolTbl, readNumBoolTbl)
|
||||
DarkRP.registerDoorVar("extraOwners", writeNumBoolTbl, readNumBoolTbl)
|
||||
DarkRP.registerDoorVar("title", net.WriteString, net.ReadString)
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Commands
|
||||
---------------------------------------------------------------------------]]
|
||||
DarkRP.declareChatCommand{
|
||||
command = "toggleownable",
|
||||
description = "Toggle ownability status on this door.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "togglegroupownable",
|
||||
description = "Set this door group ownable.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "toggleteamownable",
|
||||
description = "Toggle this door ownable by a given team.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "toggleown",
|
||||
description = "Own or unown the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "unownalldoors",
|
||||
description = "Sell all of your doors.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.chatCommandAlias("unownalldoors", "sellalldoors")
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "title",
|
||||
description = "Set the title of the door you're looking at.",
|
||||
delay = 1.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "removeowner",
|
||||
description = "Remove an owner from the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "ro",
|
||||
description = "Remove an owner from the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "addowner",
|
||||
description = "Invite someone to co-own the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "ao",
|
||||
description = "Invite someone to co-own the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forceunlock",
|
||||
description = "Force the door you're looking at to be unlocked. This is saved.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forceremoveowner",
|
||||
description = "Forcefully remove an owner from the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forceunownall",
|
||||
description = "Force a player to unown all the doors and vehicles they have.",
|
||||
delay = 0.5,
|
||||
tableArgs = true
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forcelock",
|
||||
description = "Force the door you're looking at to be locked. This is saved.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forceunown",
|
||||
description = "Forcefully remove any owners from the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "forceown",
|
||||
description = "Forcefully make someone own the door you're looking at.",
|
||||
delay = 0.5
|
||||
}
|
||||
377
gamemodes/darkrp/gamemode/modules/doorsystem/sh_interface.lua
Normal file
377
gamemodes/darkrp/gamemode/modules/doorsystem/sh_interface.lua
Normal file
@@ -0,0 +1,377 @@
|
||||
DarkRP.ENTITY.getDoorData = DarkRP.stub{
|
||||
name = "getDoorData",
|
||||
description = "Internal function to get the door/vehicle data.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "doordata",
|
||||
description = "All the DarkRP information on a door or vehicle.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isKeysOwnable = DarkRP.stub{
|
||||
name = "isKeysOwnable",
|
||||
description = "Whether this door can be bought.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether the door can be bought.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isDoor = DarkRP.stub{
|
||||
name = "isDoor",
|
||||
description = "Whether this entity is considered a door in DarkRP.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether it's a door.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isKeysOwned = DarkRP.stub{
|
||||
name = "isKeysOwned",
|
||||
description = "Whether this door is owned by someone.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether it's owned.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getDoorOwner = DarkRP.stub{
|
||||
name = "getDoorOwner",
|
||||
description = "Get the owner of a door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "owner",
|
||||
description = "The owner of the door.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isMasterOwner = DarkRP.stub{
|
||||
name = "isMasterOwner",
|
||||
description = "Whether the player is the main owner of the door (as opposed to a co-owner).",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to query.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether this player is the master owner.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isKeysOwnedBy = DarkRP.stub{
|
||||
name = "isKeysOwnedBy",
|
||||
description = "Whether this door is owned or co-owned by this player",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to query.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether this door is (co-)owned by the player.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isKeysAllowedToOwn = DarkRP.stub{
|
||||
name = "isKeysAllowedToOwn",
|
||||
description = "Whether this player is allowed to co-own a door, as decided by the master door owner.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to query.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether this door is (co-)ownable by the player.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysNonOwnable = DarkRP.stub{
|
||||
name = "getKeysNonOwnable",
|
||||
description = "Whether ownability of this door/vehicle is disabled.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "title",
|
||||
description = "The ownability status.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysTitle = DarkRP.stub{
|
||||
name = "getKeysTitle",
|
||||
description = "Get the title of this door or vehicle.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "title",
|
||||
description = "The title of the door or vehicle.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysDoorGroup = DarkRP.stub{
|
||||
name = "getKeysDoorGroup",
|
||||
description = "The door group of a door if it exists.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "group",
|
||||
description = "The door group.",
|
||||
type = "string"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysDoorTeams = DarkRP.stub{
|
||||
name = "getKeysDoorTeams",
|
||||
description = "The teams that are allowed to open this door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "teams",
|
||||
description = "The door teams.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysAllowedToOwn = DarkRP.stub{
|
||||
name = "getKeysAllowedToOwn",
|
||||
description = "The list of people of which the master door owner has added as allowed to own.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "players",
|
||||
description = "The list of people allowed to own.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.getKeysCoOwners = DarkRP.stub{
|
||||
name = "getKeysCoOwners",
|
||||
description = "The list of people who co-own the door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "players",
|
||||
description = "The list of people allowed to own. The keys of this table are UserIDs, the values are booleans.",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.canKeysLock = DarkRP.stub{
|
||||
name = "canKeysLock",
|
||||
description = "Whether the player can lock a given door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "door",
|
||||
description = "The door",
|
||||
optional = false,
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to lock the door.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.canKeysUnlock = DarkRP.stub{
|
||||
name = "canKeysUnlock",
|
||||
description = "Whether the player can unlock a given door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "door",
|
||||
description = "The door",
|
||||
optional = false,
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to unlock the door.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.registerDoorVar = DarkRP.stub{
|
||||
name = "registerDoorVar",
|
||||
description = "Register a door variable by name. You should definitely register door variables. Registering DarkRPVars will make networking much more efficient.",
|
||||
parameters = {
|
||||
{
|
||||
name = "name",
|
||||
description = "The name of the door var.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "writeFn",
|
||||
description = "The function that writes a value for this door var. Examples: net.WriteString, function(val) net.WriteUInt(val, 8) end.",
|
||||
type = "function",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "readFn",
|
||||
description = "The function that reads and returns a value for this door var. Examples: net.ReadString, function() return net.ReadUInt(8) end.",
|
||||
type = "function",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getDoorVars = DarkRP.stub{
|
||||
name = "getDoorVars",
|
||||
description = "Internal function, retrieves all the registered door variables.",
|
||||
parameters = {
|
||||
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "doorvars",
|
||||
description = "The door variables, indexed by number",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.getDoorVarsByName = DarkRP.stub{
|
||||
name = "getDoorVarsByName",
|
||||
description = "Internal function, retrieves all the registered door variables, indeded by their names.",
|
||||
parameters = {
|
||||
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "doorvars",
|
||||
description = "The door variables, indexed by name",
|
||||
type = "table"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canKeysLock",
|
||||
description = "Whether the player can lock a given door. This hook is run when ply:canKeysLock is called.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "door",
|
||||
description = "The door",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to lock the door.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canKeysUnlock",
|
||||
description = "Whether the player can unlock a given door. This hook is run when ply:canKeysUnlock is called.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "door",
|
||||
description = "The door",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to unlock the door.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
local function ccDoorUnOwn(ply, args)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(DarkRP.getPhrase("cmd_cant_be_run_server_console"))
|
||||
return
|
||||
end
|
||||
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or not ent:getDoorOwner() or ply:EyePos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
ent:Fire("unlock", "", 0)
|
||||
ent:keysUnOwn()
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-unowned a door with forceunown", Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, "Forcefully unowned")
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forceunown", "DarkRP_SetDoorOwner", ccDoorUnOwn)
|
||||
|
||||
local function unownAll(ply, args)
|
||||
local target = DarkRP.findPlayer(args[1])
|
||||
|
||||
if not IsValid(target) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", args))
|
||||
return
|
||||
end
|
||||
target:keysUnOwnAll()
|
||||
|
||||
if ply:EntIndex() == 0 then
|
||||
DarkRP.log("Console force-unowned all doors owned by " .. target:Nick(), Color(30, 30, 30))
|
||||
else
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-unowned all doors owned by " .. target:Nick(), Color(30, 30, 30))
|
||||
end
|
||||
|
||||
DarkRP.notify(ply, 0, 4, "All doors of " .. target:Nick() .. " are now unowned")
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forceunownall", "DarkRP_SetDoorOwner", unownAll)
|
||||
|
||||
local function ccAddOwner(ply, args)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(DarkRP.getPhrase("cmd_cant_be_run_server_console"))
|
||||
return
|
||||
end
|
||||
|
||||
local trace = ply:GetEyeTrace()
|
||||
|
||||
local ent = trace.Entity
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ent:getKeysNonOwnable() or ent:getKeysDoorGroup() or ent:getKeysDoorTeams() or ply:EyePos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(args)
|
||||
|
||||
if not target then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", args))
|
||||
return
|
||||
end
|
||||
|
||||
if ent:isKeysOwned() then
|
||||
if not ent:isKeysOwnedBy(target) and not ent:isKeysAllowedToOwn(target) then
|
||||
ent:addKeysAllowedToOwn(target)
|
||||
else
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("rp_addowner_already_owns_door", target))
|
||||
end
|
||||
return
|
||||
end
|
||||
ent:keysOwn(target)
|
||||
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-added a door owner with forceown", Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, "Forcefully added " .. target:Nick())
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forceown", "DarkRP_SetDoorOwner", ccAddOwner)
|
||||
|
||||
local function ccRemoveOwner(ply, args)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(DarkRP.getPhrase("cmd_cant_be_run_server_console"))
|
||||
return
|
||||
end
|
||||
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ent:getKeysNonOwnable() or ent:getKeysDoorGroup() or ent:getKeysDoorTeams() or ply:EyePos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(args)
|
||||
|
||||
if not target then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", args))
|
||||
return
|
||||
end
|
||||
|
||||
if ent:isKeysAllowedToOwn(target) then
|
||||
ent:removeKeysAllowedToOwn(target)
|
||||
end
|
||||
|
||||
if ent:isMasterOwner(target) then
|
||||
ent:keysUnOwn()
|
||||
elseif ent:isKeysOwnedBy(target) then
|
||||
ent:removeKeysDoorOwner(target)
|
||||
end
|
||||
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-removed a door owner with forceremoveowner", Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, "Forcefully removed " .. target:Nick())
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forceremoveowner", "DarkRP_SetDoorOwner", ccRemoveOwner)
|
||||
|
||||
local function ccLock(ply, args)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(DarkRP.getPhrase("cmd_cant_be_run_server_console"))
|
||||
return
|
||||
end
|
||||
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:EyePos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("locked"))
|
||||
|
||||
ent:keysLock()
|
||||
|
||||
if not ent:CreatedByMap() then return end
|
||||
MySQLite.query(string.format([[REPLACE INTO darkrp_door VALUES(%s, %s, %s, 1, %s);]],
|
||||
MySQLite.SQLStr(ent:doorIndex()),
|
||||
MySQLite.SQLStr(string.lower(game.GetMap())),
|
||||
MySQLite.SQLStr(ent:getKeysTitle() or ""),
|
||||
ent:getKeysNonOwnable() and 1 or 0
|
||||
))
|
||||
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-locked a door with forcelock (locked door is saved)", Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, "Forcefully locked")
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forcelock", "DarkRP_ChangeDoorSettings", ccLock)
|
||||
|
||||
local function ccUnLock(ply, args)
|
||||
if ply:EntIndex() == 0 then
|
||||
print(DarkRP.getPhrase("cmd_cant_be_run_server_console"))
|
||||
return
|
||||
end
|
||||
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:EyePos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("unlocked"))
|
||||
ent:keysUnLock()
|
||||
|
||||
if not ent:CreatedByMap() then return end
|
||||
MySQLite.query(string.format([[REPLACE INTO darkrp_door VALUES(%s, %s, %s, 0, %s);]],
|
||||
MySQLite.SQLStr(ent:doorIndex()),
|
||||
MySQLite.SQLStr(string.lower(game.GetMap())),
|
||||
MySQLite.SQLStr(ent:getKeysTitle() or ""),
|
||||
ent:getKeysNonOwnable() and 1 or 0
|
||||
))
|
||||
|
||||
DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") force-unlocked a door with forcelock (unlocked door is saved)", Color(30, 30, 30))
|
||||
DarkRP.notify(ply, 0, 4, "Forcefully unlocked")
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("forceunlock", "DarkRP_ChangeDoorSettings", ccUnLock)
|
||||
592
gamemodes/darkrp/gamemode/modules/doorsystem/sv_doors.lua
Normal file
592
gamemodes/darkrp/gamemode/modules/doorsystem/sv_doors.lua
Normal file
@@ -0,0 +1,592 @@
|
||||
local meta = FindMetaTable("Entity")
|
||||
local pmeta = FindMetaTable("Player")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Functions
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
function meta:doorIndex()
|
||||
return self:CreatedByMap() and self:MapCreationID() or nil
|
||||
end
|
||||
|
||||
function DarkRP.doorToEntIndex(num)
|
||||
local ent = ents.GetMapCreatedEntity(num)
|
||||
|
||||
return IsValid(ent) and ent:EntIndex() or nil
|
||||
end
|
||||
|
||||
function DarkRP.doorIndexToEnt(num)
|
||||
return ents.GetMapCreatedEntity(num) or NULL
|
||||
end
|
||||
|
||||
function meta:isLocked()
|
||||
local save = self:GetSaveTable()
|
||||
return save and ((self:isDoor() and save.m_bLocked) or (self:IsVehicle() and save.VehicleLocked))
|
||||
end
|
||||
|
||||
function meta:keysLock()
|
||||
self:Fire("lock", "", 0)
|
||||
if isfunction(self.Lock) then self:Lock(true) end -- SCars
|
||||
if IsValid(self.EntOwner) and self.EntOwner ~= self then return self.EntOwner:keysLock() end -- SCars
|
||||
|
||||
hook.Call("onKeysLocked", nil, self)
|
||||
end
|
||||
|
||||
function meta:keysUnLock()
|
||||
self:Fire("unlock", "", 0)
|
||||
if isfunction(self.UnLock) then self:UnLock(true) end -- SCars
|
||||
if IsValid(self.EntOwner) and self.EntOwner ~= self then return self.EntOwner:keysUnLock() end -- SCars
|
||||
|
||||
hook.Call("onKeysUnlocked", nil, self)
|
||||
end
|
||||
|
||||
function meta:keysOwn(ply)
|
||||
if self:isKeysAllowedToOwn(ply) then
|
||||
self:addKeysDoorOwner(ply)
|
||||
return
|
||||
end
|
||||
|
||||
local Owner = self:CPPIGetOwner()
|
||||
|
||||
-- Increase vehicle count
|
||||
if self:IsVehicle() then
|
||||
if IsValid(ply) then
|
||||
ply.Vehicles = ply.Vehicles or 0
|
||||
ply.Vehicles = ply.Vehicles + 1
|
||||
|
||||
self.SID = ply.SID
|
||||
end
|
||||
|
||||
-- Decrease vehicle count of the original owner
|
||||
if IsValid(Owner) and Owner ~= ply then
|
||||
Owner.Vehicles = Owner.Vehicles or 1
|
||||
Owner.Vehicles = Owner.Vehicles - 1
|
||||
end
|
||||
end
|
||||
|
||||
if self:IsVehicle() then
|
||||
self:CPPISetOwner(ply)
|
||||
end
|
||||
|
||||
if not self:isKeysOwned() and not self:isKeysOwnedBy(ply) then
|
||||
local doorData = self:getDoorData()
|
||||
doorData.owner = ply:UserID()
|
||||
DarkRP.updateDoorData(self, "owner")
|
||||
end
|
||||
|
||||
ply.OwnedNumz = ply.OwnedNumz or 0
|
||||
if ply.OwnedNumz == 0 and GAMEMODE.Config.propertytax then
|
||||
timer.Create(ply:SteamID64() .. "propertytax", 270, 0, function() ply.doPropertyTax(ply) end)
|
||||
end
|
||||
|
||||
ply.OwnedNumz = ply.OwnedNumz + 1
|
||||
|
||||
ply.Ownedz[self:EntIndex()] = self
|
||||
end
|
||||
|
||||
function meta:keysUnOwn(ply)
|
||||
if not ply then
|
||||
ply = self:getDoorOwner()
|
||||
|
||||
if not IsValid(ply) then return end
|
||||
end
|
||||
|
||||
if self:isMasterOwner(ply) then
|
||||
local doorData = self:getDoorData()
|
||||
self:removeAllKeysExtraOwners()
|
||||
self:setKeysTitle(nil)
|
||||
doorData.owner = nil
|
||||
DarkRP.updateDoorData(self, "owner")
|
||||
else
|
||||
self:removeKeysDoorOwner(ply)
|
||||
end
|
||||
|
||||
ply.Ownedz[self:EntIndex()] = nil
|
||||
ply.OwnedNumz = math.Clamp((ply.OwnedNumz or 1) - 1, 0, math.huge)
|
||||
end
|
||||
|
||||
function pmeta:keysUnOwnAll()
|
||||
for entIndex, ent in pairs(self.Ownedz or {}) do
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() then self.Ownedz[entIndex] = nil continue end
|
||||
if ent:isMasterOwner(self) then
|
||||
ent:Fire("unlock", "", 0.6)
|
||||
end
|
||||
ent:keysUnOwn(self)
|
||||
end
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply == self then continue end
|
||||
|
||||
for _, ent in pairs(ply.Ownedz or {}) do
|
||||
if IsValid(ent) and ent:isKeysAllowedToOwn(self) then
|
||||
ent:removeKeysAllowedToOwn(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.OwnedNumz = 0
|
||||
end
|
||||
|
||||
local function taxesUnOwnAll(ply, taxables)
|
||||
for _, ent in ipairs(taxables) do
|
||||
if ent:isMasterOwner(ply) then
|
||||
ent:Fire("unlock", "", 0.6)
|
||||
end
|
||||
|
||||
ent:keysUnOwn(ply)
|
||||
end
|
||||
end
|
||||
|
||||
function pmeta:doPropertyTax()
|
||||
if not GAMEMODE.Config.propertytax then return end
|
||||
if self:isCP() and GAMEMODE.Config.cit_propertytax then return end
|
||||
|
||||
local taxables = {}
|
||||
|
||||
for entIndex, ent in pairs(self.Ownedz or {}) do
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() then self.Ownedz[entIndex] = nil continue end
|
||||
local isAllowed = hook.Call("canTaxEntity", nil, self, ent)
|
||||
if isAllowed == false then continue end
|
||||
|
||||
table.insert(taxables, ent)
|
||||
end
|
||||
|
||||
-- co-owned doors
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply == self then continue end
|
||||
|
||||
for _, ent in pairs(ply.Ownedz or {}) do
|
||||
if not IsValid(ent) or not ent:isKeysOwnedBy(self) then continue end
|
||||
|
||||
local isAllowed = hook.Call("canTaxEntity", nil, self, ent)
|
||||
if isAllowed == false then continue end
|
||||
|
||||
table.insert(taxables, ent)
|
||||
end
|
||||
end
|
||||
|
||||
local numowned = #taxables
|
||||
|
||||
if numowned <= 0 then return end
|
||||
|
||||
local price = 10
|
||||
local tax = price * numowned + math.random(-5, 5)
|
||||
|
||||
local shouldTax, taxOverride = hook.Call("canPropertyTax", nil, self, tax)
|
||||
|
||||
if shouldTax == false then return end
|
||||
|
||||
tax = taxOverride or tax
|
||||
if tax == 0 then return end
|
||||
|
||||
local canAfford = self:canAfford(tax)
|
||||
|
||||
if canAfford then
|
||||
self:addMoney(-tax)
|
||||
DarkRP.notify(self, 0, 5, DarkRP.getPhrase("property_tax", DarkRP.formatMoney(tax)))
|
||||
else
|
||||
taxesUnOwnAll(self, taxables)
|
||||
DarkRP.notify(self, 1, 8, DarkRP.getPhrase("property_tax_cant_afford"))
|
||||
end
|
||||
|
||||
hook.Call("onPropertyTax", nil, self, tax, canAfford)
|
||||
end
|
||||
|
||||
function pmeta:initiateTax()
|
||||
local taxtime = GAMEMODE.Config.wallettaxtime
|
||||
local uid = self:SteamID64() -- so we can destroy the timer if the player leaves
|
||||
timer.Create("rp_tax_" .. uid, taxtime or 600, 0, function()
|
||||
if not IsValid(self) then
|
||||
timer.Remove("rp_tax_" .. uid)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if not GAMEMODE.Config.wallettax then
|
||||
return -- Don't remove the hook in case it's turned on afterwards.
|
||||
end
|
||||
|
||||
local money = self:getDarkRPVar("money")
|
||||
local mintax = GAMEMODE.Config.wallettaxmin / 100
|
||||
local maxtax = GAMEMODE.Config.wallettaxmax / 100 -- convert to decimals for percentage calculations
|
||||
local startMoney = GAMEMODE.Config.startingmoney
|
||||
|
||||
|
||||
-- Variate the taxes between twice the starting money ($1000 by default) and 200 - 2 times the starting money (100.000 by default)
|
||||
local tax = (money - (startMoney * 2)) / (startMoney * 198)
|
||||
tax = math.Min(maxtax, mintax + (maxtax - mintax) * tax)
|
||||
|
||||
local taxAmount = tax * money
|
||||
|
||||
local shouldTax, amount = hook.Call("canTax", GAMEMODE, self, taxAmount)
|
||||
|
||||
if shouldTax == false then return end
|
||||
|
||||
taxAmount = amount or taxAmount
|
||||
taxAmount = math.Max(0, taxAmount)
|
||||
|
||||
self:addMoney(-taxAmount)
|
||||
DarkRP.notify(self, 3, 7, DarkRP.getPhrase("taxday", math.Round(taxAmount / money * 100, 3)))
|
||||
|
||||
hook.Call("onPaidTax", DarkRP.hooks, self, tax, money)
|
||||
end)
|
||||
end
|
||||
|
||||
function GM:canTax(ply)
|
||||
-- Don't tax players if they have less than twice the starting amount
|
||||
if ply:getDarkRPVar("money") < (GAMEMODE.Config.startingmoney * 2) then return false end
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Commands
|
||||
---------------------------------------------------------------------------]]
|
||||
local function SetDoorOwnable(ply)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or (not ent:isDoor() and not ent:IsVehicle()) or ply:GetPos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(ent:getDoorOwner()) then
|
||||
ent:keysUnOwn(ent:getDoorOwner())
|
||||
end
|
||||
ent:setKeysNonOwnable(not ent:getKeysNonOwnable())
|
||||
ent:removeAllKeysExtraOwners()
|
||||
ent:removeAllKeysAllowedToOwn()
|
||||
ent:removeAllKeysDoorTeams()
|
||||
ent:setDoorGroup(nil)
|
||||
ent:setKeysTitle(nil)
|
||||
|
||||
-- Save it for future map loads
|
||||
DarkRP.storeDoorData(ent)
|
||||
DarkRP.storeDoorGroup(ent, nil)
|
||||
DarkRP.storeTeamDoorOwnability(ent)
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("toggleownable", "DarkRP_ChangeDoorSettings", SetDoorOwnable)
|
||||
|
||||
local function SetDoorGroupOwnable(ply, arg)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or (not ent:isDoor() and not ent:IsVehicle()) or ply:GetPos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return
|
||||
end
|
||||
|
||||
if not RPExtraTeamDoors[arg] and arg ~= "" then DarkRP.notify(ply, 1, 10, DarkRP.getPhrase("door_group_doesnt_exist")) return "" end
|
||||
|
||||
ent:keysUnOwn()
|
||||
|
||||
|
||||
ent:removeAllKeysDoorTeams()
|
||||
local group = arg ~= "" and arg or nil
|
||||
ent:setDoorGroup(group)
|
||||
|
||||
-- Save it for future map loads
|
||||
DarkRP.storeDoorGroup(ent, group)
|
||||
DarkRP.storeTeamDoorOwnability(ent)
|
||||
|
||||
|
||||
DarkRP.notify(ply, 0, 8, DarkRP.getPhrase("door_group_set"))
|
||||
return ""
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("togglegroupownable", "DarkRP_ChangeDoorSettings", SetDoorGroupOwnable)
|
||||
|
||||
local function SetDoorTeamOwnable(ply, arg)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or (not ent:isDoor() and not ent:IsVehicle()) or ply:GetPos():DistToSqr(ent:GetPos()) > 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
arg = tonumber(arg)
|
||||
if not arg then DarkRP.notify(ply, 1, 10, DarkRP.getPhrase("job_doesnt_exist")) return "" end
|
||||
if not RPExtraTeams[arg] and arg ~= nil then DarkRP.notify(ply, 1, 10, DarkRP.getPhrase("job_doesnt_exist")) return "" end
|
||||
if IsValid(ent:getDoorOwner()) then
|
||||
ent:keysUnOwn(ent:getDoorOwner())
|
||||
end
|
||||
|
||||
ent:setDoorGroup(nil)
|
||||
DarkRP.storeDoorGroup(ent, nil)
|
||||
|
||||
local doorTeams = ent:getKeysDoorTeams()
|
||||
if not doorTeams or not doorTeams[arg] then
|
||||
ent:addKeysDoorTeam(arg)
|
||||
else
|
||||
ent:removeKeysDoorTeam(arg)
|
||||
end
|
||||
|
||||
DarkRP.notify(ply, 0, 8, DarkRP.getPhrase("door_group_set"))
|
||||
DarkRP.storeTeamDoorOwnability(ent)
|
||||
|
||||
ent:keysUnOwn()
|
||||
return ""
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("toggleteamownable", "DarkRP_ChangeDoorSettings", SetDoorTeamOwnable)
|
||||
|
||||
local function OwnDoor(ply)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:GetPos():DistToSqr(ent:GetPos()) >= 40000 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local Owner = ent:CPPIGetOwner()
|
||||
|
||||
if ply:isArrested() then
|
||||
DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("door_unown_arrested"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if ent:getKeysNonOwnable() or ent:getKeysDoorGroup() or not fn.Null(ent:getKeysDoorTeams() or {}) then
|
||||
DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("door_unownable"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if ent:isKeysOwnedBy(ply) then
|
||||
local bAllowed, strReason = hook.Call("playerSell" .. (ent:IsVehicle() and "Vehicle" or "Door"), GAMEMODE, ply, ent)
|
||||
|
||||
if bAllowed == false then
|
||||
if strReason and strReason ~= "" then
|
||||
DarkRP.notify(ply, 1, 4, strReason)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
if ent:isMasterOwner(ply) then
|
||||
ent:removeAllKeysExtraOwners()
|
||||
ent:removeAllKeysAllowedToOwn()
|
||||
ent:Fire("unlock", "", 0)
|
||||
end
|
||||
|
||||
ent:keysUnOwn(ply)
|
||||
ent:setKeysTitle(nil)
|
||||
local GiveMoneyBack = math.floor((hook.Call("get" .. (ent:IsVehicle() and "Vehicle" or "Door") .. "Cost", GAMEMODE, ply, ent) * 0.666) + 0.5)
|
||||
hook.Call("playerKeysSold", GAMEMODE, ply, ent, GiveMoneyBack)
|
||||
ply:addMoney(GiveMoneyBack)
|
||||
local bSuppress = hook.Call("hideSellDoorMessage", GAMEMODE, ply, ent)
|
||||
if not bSuppress then
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("door_sold", DarkRP.formatMoney(GiveMoneyBack)))
|
||||
end
|
||||
|
||||
else
|
||||
if ent:isKeysOwned() and not ent:isKeysAllowedToOwn(ply) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("door_already_owned"))
|
||||
return ""
|
||||
end
|
||||
|
||||
local iCost = hook.Call("get" .. (ent:IsVehicle() and "Vehicle" or "Door") .. "Cost", GAMEMODE, ply, ent)
|
||||
if not ply:canAfford(iCost) then
|
||||
DarkRP.notify(ply, 1, 4, ent:IsVehicle() and DarkRP.getPhrase("vehicle_cannot_afford") or DarkRP.getPhrase("door_cannot_afford"))
|
||||
return ""
|
||||
end
|
||||
|
||||
local bAllowed, strReason, bSuppress = hook.Call("playerBuy" .. (ent:IsVehicle() and "Vehicle" or "Door"), GAMEMODE, ply, ent)
|
||||
if bAllowed == false then
|
||||
if strReason and strReason ~= "" then
|
||||
DarkRP.notify(ply, 1, 4, strReason)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
local bVehicle = ent:IsVehicle()
|
||||
|
||||
if bVehicle and (ply.Vehicles or 0) >= GAMEMODE.Config.maxvehicles and Owner ~= ply then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("limit", DarkRP.getPhrase("vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not bVehicle and (ply.OwnedNumz or 0) >= GAMEMODE.Config.maxdoors then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("limit", DarkRP.getPhrase("door")))
|
||||
return ""
|
||||
end
|
||||
|
||||
ply:addMoney(-iCost)
|
||||
if not bSuppress then
|
||||
DarkRP.notify(ply, 0, 4, bVehicle and DarkRP.getPhrase("vehicle_bought", DarkRP.formatMoney(iCost), "") or DarkRP.getPhrase("door_bought", DarkRP.formatMoney(iCost), ""))
|
||||
end
|
||||
|
||||
ent:keysOwn(ply)
|
||||
hook.Call("playerBought" .. (bVehicle and "Vehicle" or "Door"), GAMEMODE, ply, ent, iCost)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("toggleown", OwnDoor)
|
||||
|
||||
local function UnOwnAll(ply, cmd, args)
|
||||
local amount = 0
|
||||
local cost = 0
|
||||
|
||||
local unownables = {}
|
||||
for entIndex, ent in pairs(ply.Ownedz or {}) do
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() then ply.Ownedz[entIndex] = nil continue end
|
||||
table.insert(unownables, ent)
|
||||
end
|
||||
|
||||
for _, otherPly in ipairs(player.GetAll()) do
|
||||
if ply == otherPly then continue end
|
||||
|
||||
for _, ent in pairs(otherPly.Ownedz or {}) do
|
||||
if IsValid(ent) and ent:isKeysOwnedBy(ply) then
|
||||
table.insert(unownables, ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for entIndex, ent in ipairs(unownables) do
|
||||
local bAllowed, _strReason = hook.Call("playerSell" .. (ent:IsVehicle() and "Vehicle" or "Door"), GAMEMODE, ply, ent)
|
||||
|
||||
if bAllowed == false then continue end
|
||||
|
||||
if ent:isMasterOwner(ply) then
|
||||
ent:Fire("unlock", "", 0)
|
||||
end
|
||||
|
||||
ent:keysUnOwn(ply)
|
||||
amount = amount + 1
|
||||
|
||||
local GiveMoneyBack = math.floor((hook.Call("get" .. (ent:IsVehicle() and "Vehicle" or "Door") .. "Cost", GAMEMODE, ply, ent) * 0.666) + 0.5)
|
||||
hook.Call("playerKeysSold", GAMEMODE, ply, ent, GiveMoneyBack)
|
||||
cost = cost + GiveMoneyBack
|
||||
end
|
||||
|
||||
if amount == 0 then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("no_doors_owned")) return "" end
|
||||
|
||||
ply:addMoney(math.floor(cost))
|
||||
|
||||
DarkRP.notify(ply, 2, 4, DarkRP.getPhrase("sold_x_doors", amount, DarkRP.formatMoney(math.floor(cost))))
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("unownalldoors", UnOwnAll)
|
||||
|
||||
local function SetDoorTitle(ply, args)
|
||||
local trace = ply:GetEyeTrace()
|
||||
|
||||
local ent = trace.Entity
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:GetPos():DistToSqr(ent:GetPos()) >= 12100 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
if ent:isKeysOwnedBy(ply) then
|
||||
ent:setKeysTitle(args)
|
||||
return ""
|
||||
end
|
||||
|
||||
local function onCAMIResult(allowed)
|
||||
if not allowed then
|
||||
DarkRP.notify(ply, 1, 6, DarkRP.getPhrase("no_privilege"))
|
||||
return
|
||||
end
|
||||
|
||||
local hasTeams = not fn.Null(ent:getKeysDoorTeams() or {})
|
||||
if ent:isKeysOwned() or ent:getKeysNonOwnable() or ent:getKeysDoorGroup() or hasTeams then
|
||||
ent:setKeysTitle(args)
|
||||
end
|
||||
|
||||
if ent:getKeysNonOwnable() or ent:getKeysDoorGroup() or hasTeams then
|
||||
DarkRP.storeDoorData(trace.Entity)
|
||||
end
|
||||
end
|
||||
|
||||
CAMI.PlayerHasAccess(ply, "DarkRP_ChangeDoorSettings", onCAMIResult)
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("title", SetDoorTitle)
|
||||
|
||||
local function RemoveDoorOwner(ply, args)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:GetPos():DistToSqr(ent:GetPos()) >= 12100 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(args)
|
||||
|
||||
if ent:getKeysNonOwnable() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("door_rem_owners_unownable"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not target then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", tostring(args)))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not ent:isKeysOwnedBy(ply) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("do_not_own_ent"))
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
local canDo = hook.Call("onAllowedToOwnRemoved", nil, ply, ent, target)
|
||||
if canDo == false then return "" end
|
||||
|
||||
if ent:isKeysAllowedToOwn(target) then
|
||||
ent:removeKeysAllowedToOwn(target)
|
||||
end
|
||||
|
||||
if ent:isKeysOwnedBy(target) then
|
||||
ent:removeKeysDoorOwner(target)
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("removeowner", RemoveDoorOwner)
|
||||
DarkRP.defineChatCommand("ro", RemoveDoorOwner)
|
||||
|
||||
local function AddDoorOwner(ply, args)
|
||||
local trace = ply:GetEyeTrace()
|
||||
local ent = trace.Entity
|
||||
|
||||
if not IsValid(ent) or not ent:isKeysOwnable() or ply:GetPos():DistToSqr(ent:GetPos()) >= 12100 then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("must_be_looking_at", DarkRP.getPhrase("door_or_vehicle")))
|
||||
return ""
|
||||
end
|
||||
|
||||
local target = DarkRP.findPlayer(args)
|
||||
|
||||
if ent:getKeysNonOwnable() then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("door_add_owners_unownable"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not target then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("could_not_find", tostring(args)))
|
||||
return ""
|
||||
end
|
||||
|
||||
if not ent:isKeysOwnedBy(ply) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("do_not_own_ent"))
|
||||
return ""
|
||||
end
|
||||
|
||||
if ent:isKeysOwnedBy(target) or ent:isKeysAllowedToOwn(target) then
|
||||
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("rp_addowner_already_owns_door", target:Nick()))
|
||||
return ""
|
||||
end
|
||||
|
||||
local canDo = hook.Call("onAllowedToOwnAdded", nil, ply, ent, target)
|
||||
if canDo == false then return "" end
|
||||
|
||||
ent:addKeysAllowedToOwn(target)
|
||||
|
||||
|
||||
return ""
|
||||
end
|
||||
DarkRP.defineChatCommand("addowner", AddDoorOwner)
|
||||
DarkRP.defineChatCommand("ao", AddDoorOwner)
|
||||
179
gamemodes/darkrp/gamemode/modules/doorsystem/sv_doorvars.lua
Normal file
179
gamemodes/darkrp/gamemode/modules/doorsystem/sv_doorvars.lua
Normal file
@@ -0,0 +1,179 @@
|
||||
util.AddNetworkString("DarkRP_UpdateDoorData")
|
||||
util.AddNetworkString("DarkRP_RemoveDoorData")
|
||||
util.AddNetworkString("DarkRP_RemoveDoorVar")
|
||||
util.AddNetworkString("DarkRP_AllDoorData")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Interface functions
|
||||
---------------------------------------------------------------------------]]
|
||||
local eMeta = FindMetaTable("Entity")
|
||||
function eMeta:getDoorData()
|
||||
if not self:isKeysOwnable() then return {} end
|
||||
|
||||
self.DoorData = self.DoorData or {}
|
||||
return self.DoorData
|
||||
end
|
||||
|
||||
function eMeta:setKeysNonOwnable(ownable)
|
||||
self:getDoorData().nonOwnable = ownable or nil
|
||||
DarkRP.updateDoorData(self, "nonOwnable")
|
||||
end
|
||||
|
||||
function eMeta:setKeysTitle(title)
|
||||
self:getDoorData().title = title ~= "" and title or nil
|
||||
DarkRP.updateDoorData(self, "title")
|
||||
end
|
||||
|
||||
function eMeta:setDoorGroup(group)
|
||||
self:getDoorData().groupOwn = group
|
||||
DarkRP.updateDoorData(self, "groupOwn")
|
||||
end
|
||||
|
||||
function eMeta:addKeysDoorTeam(t)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.teamOwn = doorData.teamOwn or {}
|
||||
doorData.teamOwn[t] = true
|
||||
|
||||
DarkRP.updateDoorData(self, "teamOwn")
|
||||
end
|
||||
|
||||
function eMeta:removeKeysDoorTeam(t)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.teamOwn = doorData.teamOwn or {}
|
||||
doorData.teamOwn[t] = nil
|
||||
|
||||
if fn.Null(doorData.teamOwn) then
|
||||
doorData.teamOwn = nil
|
||||
end
|
||||
|
||||
DarkRP.updateDoorData(self, "teamOwn")
|
||||
end
|
||||
|
||||
function eMeta:removeAllKeysDoorTeams()
|
||||
local doorData = self:getDoorData()
|
||||
doorData.teamOwn = nil
|
||||
|
||||
DarkRP.updateDoorData(self, "teamOwn")
|
||||
end
|
||||
|
||||
function eMeta:addKeysAllowedToOwn(ply)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.allowedToOwn = doorData.allowedToOwn or {}
|
||||
doorData.allowedToOwn[ply:UserID()] = true
|
||||
|
||||
DarkRP.updateDoorData(self, "allowedToOwn")
|
||||
end
|
||||
|
||||
function eMeta:removeKeysAllowedToOwn(ply)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.allowedToOwn = doorData.allowedToOwn or {}
|
||||
doorData.allowedToOwn[ply:UserID()] = nil
|
||||
|
||||
if fn.Null(doorData.allowedToOwn) then
|
||||
doorData.allowedToOwn = nil
|
||||
end
|
||||
|
||||
DarkRP.updateDoorData(self, "allowedToOwn")
|
||||
end
|
||||
|
||||
function eMeta:removeAllKeysAllowedToOwn()
|
||||
local doorData = self:getDoorData()
|
||||
doorData.allowedToOwn = nil
|
||||
|
||||
DarkRP.updateDoorData(self, "allowedToOwn")
|
||||
end
|
||||
|
||||
function eMeta:addKeysDoorOwner(ply)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.extraOwners = doorData.extraOwners or {}
|
||||
doorData.extraOwners[ply:UserID()] = true
|
||||
|
||||
DarkRP.updateDoorData(self, "extraOwners")
|
||||
|
||||
self:removeKeysAllowedToOwn(ply)
|
||||
end
|
||||
|
||||
function eMeta:removeKeysDoorOwner(ply)
|
||||
local doorData = self:getDoorData()
|
||||
doorData.extraOwners = doorData.extraOwners or {}
|
||||
doorData.extraOwners[ply:UserID()] = nil
|
||||
|
||||
if fn.Null(doorData.extraOwners) then
|
||||
doorData.extraOwners = nil
|
||||
end
|
||||
|
||||
DarkRP.updateDoorData(self, "extraOwners")
|
||||
end
|
||||
|
||||
function eMeta:removeAllKeysExtraOwners()
|
||||
local doorData = self:getDoorData()
|
||||
doorData.extraOwners = nil
|
||||
|
||||
DarkRP.updateDoorData(self, "extraOwners")
|
||||
end
|
||||
|
||||
function eMeta:removeDoorData()
|
||||
net.Start("DarkRP_RemoveDoorData")
|
||||
net.WriteUInt(self:EntIndex(), 32)
|
||||
net.Send(player.GetAll())
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Networking
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
function plyMeta:sendDoorData()
|
||||
if self:EntIndex() == 0 then return end
|
||||
|
||||
local res = {}
|
||||
for _, v in ipairs(ents.GetAll()) do
|
||||
if not v:getDoorData() or table.IsEmpty(v:getDoorData()) then continue end
|
||||
|
||||
res[v:EntIndex()] = v:getDoorData()
|
||||
end
|
||||
|
||||
net.Start("DarkRP_AllDoorData")
|
||||
net.WriteUInt(table.Count(res), 16)
|
||||
|
||||
for ix, vars in pairs(res) do
|
||||
net.WriteUInt(ix, 16)
|
||||
|
||||
net.WriteUInt(table.Count(vars), 8)
|
||||
|
||||
for varName, value in pairs(vars) do
|
||||
DarkRP.writeNetDoorVar(varName, value)
|
||||
end
|
||||
end
|
||||
net.Send(self)
|
||||
end
|
||||
|
||||
concommand.Add("_sendAllDoorData", fn.Id) -- Backwards compatibility
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "DarkRP_DoorData", plyMeta.sendDoorData)
|
||||
|
||||
function DarkRP.updateDoorData(door, member)
|
||||
if not IsValid(door) or not door:getDoorData() then error("Calling updateDoorData on a door that has no data!") end
|
||||
|
||||
local value = door:getDoorData()[member]
|
||||
|
||||
if value == nil then
|
||||
local doorvar = DarkRP.getDoorVarsByName()[member]
|
||||
net.Start("DarkRP_RemoveDoorVar")
|
||||
net.WriteUInt(door:EntIndex(), 16)
|
||||
if not doorvar then
|
||||
net.WriteUInt(0, 8)
|
||||
net.WriteString(member)
|
||||
else
|
||||
net.WriteUInt(doorvar.id, 8)
|
||||
end
|
||||
net.Broadcast()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("DarkRP_UpdateDoorData")
|
||||
net.WriteUInt(door:EntIndex(), 32)
|
||||
DarkRP.writeNetDoorVar(member, value)
|
||||
net.Broadcast()
|
||||
end
|
||||
851
gamemodes/darkrp/gamemode/modules/doorsystem/sv_interface.lua
Normal file
851
gamemodes/darkrp/gamemode/modules/doorsystem/sv_interface.lua
Normal file
@@ -0,0 +1,851 @@
|
||||
DarkRP.doorToEntIndex = DarkRP.stub{
|
||||
name = "doorToEntIndex",
|
||||
description = "Get an ENT index from a door index.",
|
||||
parameters = {
|
||||
{
|
||||
name = "index",
|
||||
description = "The door index",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "index",
|
||||
description = "The ENT index",
|
||||
type = "number",
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.doorIndexToEnt = DarkRP.stub{
|
||||
name = "doorIndexToEnt",
|
||||
description = "Get the entity of a door index (inverse of ent:doorIndexToEnt()). Note: the door MUST have been created by the map!",
|
||||
parameters = {
|
||||
{
|
||||
name = "index",
|
||||
description = "The door index",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "door",
|
||||
description = "The door entity",
|
||||
type = "Entity",
|
||||
}
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.writeNetDoorVar = DarkRP.stub{
|
||||
name = "writeNetDoorVar",
|
||||
description = "Internal function. You probably shouldn't need this. DarkRP calls this function when sending DoorVar net messages. This function writes the net data for a specific DoorVar.",
|
||||
parameters = {
|
||||
{
|
||||
name = "name",
|
||||
description = "The name of the DoorVar.",
|
||||
type = "string",
|
||||
optional = false
|
||||
},
|
||||
{
|
||||
name = "value",
|
||||
description = "The value of the DoorVar.",
|
||||
type = "any",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.doorIndex = DarkRP.stub{
|
||||
name = "doorIndex",
|
||||
description = "Get the door index of a door. Use this to store door information in the database.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "index",
|
||||
description = "The door index.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.keysLock = DarkRP.stub{
|
||||
name = "keysLock",
|
||||
description = "Lock this door or vehicle.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.keysUnLock = DarkRP.stub{
|
||||
name = "keysUnLock",
|
||||
description = "Unlock this door or vehicle.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.addKeysAllowedToOwn = DarkRP.stub{
|
||||
name = "addKeysAllowedToOwn",
|
||||
description = "Make this player allowed to co-own the door or vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to give permission to co-own.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.addKeysDoorOwner = DarkRP.stub{
|
||||
name = "addKeysDoorOwner",
|
||||
description = "Make this player a co-owner of the door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to add as co-owner.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeKeysDoorOwner = DarkRP.stub{
|
||||
name = "removeKeysDoorOwner",
|
||||
description = "Remove this player as co-owner",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to remove from the co-owners list.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.keysOwn = DarkRP.stub{
|
||||
name = "keysOwn",
|
||||
description = "Make the player the master owner of the door",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player set as master owner.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.keysUnOwn = DarkRP.stub{
|
||||
name = "keysUnOwn",
|
||||
description = "Make this player unown the door/vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.setKeysNonOwnable = DarkRP.stub{
|
||||
name = "setKeysNonOwnable",
|
||||
description = "Set whether this door or vehicle is ownable or not.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ownable",
|
||||
description = "Whether the door or vehicle is blocked from ownership.",
|
||||
type = "boolean",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.setKeysTitle = DarkRP.stub{
|
||||
name = "setKeysTitle",
|
||||
description = "Set the title of a door or vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "title",
|
||||
description = "The title of the door.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.setDoorGroup = DarkRP.stub{
|
||||
name = "setDoorGroup",
|
||||
description = "Set the door group of a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "group",
|
||||
description = "The door group.",
|
||||
type = "string",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.addKeysDoorTeam = DarkRP.stub{
|
||||
name = "addKeysDoorTeam",
|
||||
description = "Allow a team to lock/unlock a door..",
|
||||
parameters = {
|
||||
{
|
||||
name = "team",
|
||||
description = "The team to add to team owners.",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeKeysDoorTeam = DarkRP.stub{
|
||||
name = "removeKeysDoorTeam",
|
||||
description = "Disallow a team from locking/unlocking a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "team",
|
||||
description = "The team to remove from team owners.",
|
||||
type = "number",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeAllKeysDoorTeams = DarkRP.stub{
|
||||
name = "removeAllKeysDoorTeams",
|
||||
description = "Disallow all teams from locking/unlocking a door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeAllKeysExtraOwners = DarkRP.stub{
|
||||
name = "removeAllKeysExtraOwners",
|
||||
description = "Remove all co-owners from a door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeAllKeysAllowedToOwn = DarkRP.stub{
|
||||
name = "removeAllKeysAllowedToOwn",
|
||||
description = "Disallow all people from owning the door.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.removeKeysAllowedToOwn = DarkRP.stub{
|
||||
name = "removeKeysAllowedToOwn",
|
||||
description = "Remove a player from being allowed to co-own a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player to be removed.",
|
||||
type = "Player",
|
||||
optional = false
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.ENTITY.isLocked = DarkRP.stub{
|
||||
name = "isLocked",
|
||||
description = "Whether this door/vehicle is locked.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "answer",
|
||||
description = "Whether it's locked.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
metatable = DarkRP.ENTITY
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.keysUnOwnAll = DarkRP.stub{
|
||||
name = "keysUnOwnAll",
|
||||
description = "Unown every door and vehicle owned by this player.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.sendDoorData = DarkRP.stub{
|
||||
name = "sendDoorData",
|
||||
description = "Internal function. Sends all door data to a player.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.doPropertyTax = DarkRP.stub{
|
||||
name = "doPropertyTax",
|
||||
description = "Tax a player based on the amount of doors and vehicles they have.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.PLAYER.initiateTax = DarkRP.stub{
|
||||
name = "initiateTax",
|
||||
description = "Internal function, starts the timer that taxes the player every once in a while.",
|
||||
parameters = {
|
||||
},
|
||||
returns = {
|
||||
},
|
||||
metatable = DarkRP.PLAYER
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onKeysLocked",
|
||||
description = "Called when a door or vehicle was locked.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ent",
|
||||
description = "The entity that was locked.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onKeysUnlocked",
|
||||
description = "Called when a door or vehicle was unlocked.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ent",
|
||||
description = "The entity that was unlocked.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerKeysSold",
|
||||
description = "When a player sold a door or vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who sold the door or vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The entity that was sold.",
|
||||
type = "Entity"
|
||||
},
|
||||
{
|
||||
name = "GiveMoneyBack",
|
||||
description = "The amount of money refunded to the player",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "hideSellDoorMessage",
|
||||
description = "Whether to hide the door/vehicle sold notification",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who sold the door or vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The entity that was sold.",
|
||||
type = "Player"
|
||||
},
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "hide",
|
||||
description = "Whether to hide the notification.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "getDoorCost",
|
||||
description = "Get the cost of a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who has the intention to purchase the door.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "cost",
|
||||
description = "The price of the door.",
|
||||
type = "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "getVehicleCost",
|
||||
description = "Get the cost of a vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who has the intention to purchase the vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The vehicle",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "cost",
|
||||
description = "The price of the vehicle.",
|
||||
type = "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerBuyDoor",
|
||||
description = "When a player purchases a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who is to buy the door.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to buy the door.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "reason",
|
||||
description = "The reason why a player is not allowed to buy the door, if applicable.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "surpress",
|
||||
description = "Whether to show the reason in a notification to the player, return true here to surpress the message.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerSellDoor",
|
||||
description = "When a player is about to sell a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who is to sell the door.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to sell the door.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "reason",
|
||||
description = "The reason why a player is not allowed to sell the door, if applicable.",
|
||||
type = "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onAllowedToOwnAdded",
|
||||
description = "When a player adds a co-owner to a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who adds the co-owner.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door.",
|
||||
type = "Entity"
|
||||
},
|
||||
{
|
||||
name = "target",
|
||||
description = "The target who will be allowed to own the door.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to add this player as co-owner.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onAllowedToOwnRemoved",
|
||||
description = "When a player removes a co-owner to a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who removes the co-owner.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door.",
|
||||
type = "Entity"
|
||||
},
|
||||
{
|
||||
name = "target",
|
||||
description = "The target who will not be allowed to own the door anymore.",
|
||||
type = "Player"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to remove this player as co-owner.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerBuyVehicle",
|
||||
description = "When a player purchases a vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who is to buy the vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The vehicle.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to buy the vehicle.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "reason",
|
||||
description = "The reason why a player is not allowed to buy the vehicle, if applicable.",
|
||||
type = "string"
|
||||
},
|
||||
{
|
||||
name = "surpress",
|
||||
description = "Whether to show the reason in a notification to the player, return true here to surpress the message.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerSellVehicle",
|
||||
description = "When a player is about to sell a vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who is to sell the vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The vehicle.",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "allowed",
|
||||
description = "Whether the player is allowed to sell the vehicle.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "reason",
|
||||
description = "The reason why a player is not allowed to sell the vehicle, if applicable.",
|
||||
type = "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerBoughtDoor",
|
||||
description = "Called when a player has purchased a door.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who has purchased the door.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The purchased door.",
|
||||
type = "Entity"
|
||||
},
|
||||
{
|
||||
name = "cost",
|
||||
description = "The cost of the purchased door.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "playerBoughtVehicle",
|
||||
description = "Called when a player has purchased a vehicle.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who has purchased the vehicle.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The purchased vehicle.",
|
||||
type = "Entity"
|
||||
},
|
||||
{
|
||||
name = "cost",
|
||||
description = "The cost of the purchased vehicle.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canTax",
|
||||
description = "Called before a player pays taxes. This hook differs from onPaidTax in that this hook can prevent the taxing and change the tax amount.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who was taxed.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "tax",
|
||||
description = "The amount of money that is about to be taxed from the player.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "shouldTax",
|
||||
description = "Whether the player is to be taxed. Return false here to prevent the player from being taxed.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "tax",
|
||||
description = "Overrides the amount of money that is taxed.",
|
||||
type = "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onPaidTax",
|
||||
description = "Called when a player has paid tax.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player who was taxed.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "tax",
|
||||
description = "The percentage of tax taken from his wallet.",
|
||||
type = "number"
|
||||
},
|
||||
{
|
||||
name = "wallet",
|
||||
description = "The amount of money the player had before the tax was applied.",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canTaxEntity",
|
||||
description = "Called right before a player's property is taxed. Decides per entity whether it can be taxed.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player whose property will be taxed.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "ent",
|
||||
description = "The door or vehicle that is to be taxed",
|
||||
type = "Entity"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "shouldTax",
|
||||
description = "Return false here to prevent this specific entity from being taxed.",
|
||||
type = "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "canPropertyTax",
|
||||
description = "Called right before a player's property is taxed. This hook differs from onPropertyTax in that onPropertyTax is called AFTER the taxing. With this hook, one can influence the taxing process.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player whose property will be taxed.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "tax",
|
||||
description = "The amount of money that will be taxed (unless overridden by this hook).",
|
||||
type = "number"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
{
|
||||
name = "shouldTax",
|
||||
description = "Return false here to prevent the doors from being taxed.",
|
||||
type = "boolean"
|
||||
},
|
||||
{
|
||||
name = "taxOverride",
|
||||
description = "Override the tax amount.",
|
||||
type = "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DarkRP.hookStub{
|
||||
name = "onPropertyTax",
|
||||
description = "Called right AFTER a player's property is taxed. Please use canPropertyTax if you want to influence the taxing process.",
|
||||
parameters = {
|
||||
{
|
||||
name = "ply",
|
||||
description = "The player whose property has been taxed.",
|
||||
type = "Player"
|
||||
},
|
||||
{
|
||||
name = "tax",
|
||||
description = "The amount of money that has been taxed.",
|
||||
type = "number"
|
||||
},
|
||||
{
|
||||
name = "couldAfford",
|
||||
description = "Whether the player was able to afford the tax.",
|
||||
type = "boolean"
|
||||
}
|
||||
},
|
||||
returns = {
|
||||
}
|
||||
}
|
||||
13
gamemodes/darkrp/gamemode/modules/events/sh_events.lua
Normal file
13
gamemodes/darkrp/gamemode/modules/events/sh_events.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
DarkRP.declareChatCommand{
|
||||
command = "enablestorm",
|
||||
description = "Enable meteor storms.",
|
||||
delay = 1.5,
|
||||
condition = hasCommandsPriv
|
||||
}
|
||||
|
||||
DarkRP.declareChatCommand{
|
||||
command = "disablestorm",
|
||||
description = "Disable meteor storms.",
|
||||
delay = 1.5,
|
||||
condition = hasCommandsPriv
|
||||
}
|
||||
224
gamemodes/darkrp/gamemode/modules/events/sv_events.lua
Normal file
224
gamemodes/darkrp/gamemode/modules/events/sv_events.lua
Normal file
@@ -0,0 +1,224 @@
|
||||
resource.AddFile("sound/earthquake.mp3")
|
||||
util.PrecacheSound("earthquake.mp3")
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Variables
|
||||
---------------------------------------------------------]]
|
||||
local timeLeft = 10
|
||||
local stormOn = false
|
||||
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Meteor storm
|
||||
---------------------------------------------------------]]
|
||||
local function StormStart()
|
||||
local phrase = DarkRP.getPhrase("meteor_approaching")
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:Alive() then
|
||||
v:PrintMessage(HUD_PRINTCENTER, phrase)
|
||||
v:PrintMessage(HUD_PRINTTALK, phrase)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function StormEnd()
|
||||
local phrase = DarkRP.getPhrase("meteor_passing")
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:Alive() then
|
||||
v:PrintMessage(HUD_PRINTCENTER, phrase)
|
||||
v:PrintMessage(HUD_PRINTTALK, phrase)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ControlStorm()
|
||||
timeLeft = timeLeft - 1
|
||||
|
||||
if timeLeft < 1 then
|
||||
if stormOn then
|
||||
timeLeft = math.random(300, 500)
|
||||
stormOn = false
|
||||
timer.Stop("start")
|
||||
StormEnd()
|
||||
else
|
||||
timeLeft = math.random(60, 90)
|
||||
stormOn = true
|
||||
timer.Start("start")
|
||||
StormStart()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function AttackEnt(ent)
|
||||
local meteor = ents.Create("meteor")
|
||||
meteor.nodupe = true
|
||||
meteor:Spawn()
|
||||
meteor:SetMeteorTarget(ent)
|
||||
end
|
||||
|
||||
local function StartShower()
|
||||
timer.Adjust("start", math.random(0.1, 1), 0, StartShower)
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if math.random(0, 2) == 0 and v:Alive() then
|
||||
AttackEnt(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function StartStorm(ply)
|
||||
timer.Start("stormControl")
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("meteor_enabled"))
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("enablestorm", "DarkRP_AdminCommands", StartStorm)
|
||||
|
||||
local function StopStorm(ply)
|
||||
timer.Stop("stormControl")
|
||||
stormOn = false
|
||||
timer.Stop("start")
|
||||
StormEnd()
|
||||
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("meteor_disabled"))
|
||||
end
|
||||
DarkRP.definePrivilegedChatCommand("disablestorm", "DarkRP_AdminCommands", StopStorm)
|
||||
|
||||
timer.Create("start", 1, 0, StartShower)
|
||||
timer.Create("stormControl", 1, 0, ControlStorm)
|
||||
|
||||
timer.Stop("start")
|
||||
timer.Stop("stormControl")
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Earthquake
|
||||
---------------------------------------------------------]]
|
||||
local lastmagnitudes = {} -- The magnitudes of the last tremors
|
||||
local tremor
|
||||
|
||||
local function createTremor()
|
||||
tremor = ents.Create("env_physexplosion")
|
||||
tremor:SetPos(Vector(0, 0, 0))
|
||||
tremor:SetKeyValue("radius", 9999999999)
|
||||
tremor:SetKeyValue("spawnflags", 7)
|
||||
tremor.nodupe = true
|
||||
tremor:Spawn()
|
||||
end
|
||||
|
||||
hook.Add("PostCleanupMap", "DarkRP_events", createTremor)
|
||||
|
||||
hook.Add("InitPostEntity", "DarkRP_SetupTremor", function()
|
||||
createTremor()
|
||||
end)
|
||||
|
||||
local function TremorReport()
|
||||
local mag = table.remove(lastmagnitudes, 1)
|
||||
if mag then
|
||||
if mag < 6.5 then
|
||||
DarkRP.notifyAll(0, 3, DarkRP.getPhrase("earthtremor_report", tostring(mag)))
|
||||
return
|
||||
end
|
||||
DarkRP.notifyAll(0, 3, DarkRP.getPhrase("earthquake_report", tostring(mag)))
|
||||
end
|
||||
end
|
||||
|
||||
local function EarthQuakeTest()
|
||||
if not GAMEMODE.Config.earthquakes then return end
|
||||
|
||||
if GAMEMODE.Config.quakechance and math.random(0, GAMEMODE.Config.quakechance) < 1 then
|
||||
local en = ents.FindByClass("prop_physics")
|
||||
local plys = player.GetAll()
|
||||
|
||||
local force = math.random(10, 1000)
|
||||
tremor:SetKeyValue("magnitude", force / 6)
|
||||
|
||||
for _, v in ipairs(plys) do
|
||||
v:EmitSound("earthquake.mp3", force / 6, 100)
|
||||
end
|
||||
tremor:Fire("explode","",0.5)
|
||||
util.ScreenShake(Vector(0, 0, 0), force, math.random(25, 50), math.random(5, 12), 9999999999)
|
||||
table.insert(lastmagnitudes, math.floor((force / 10) + .5) / 10)
|
||||
timer.Simple(10, function() TremorReport() end)
|
||||
for _, e in ipairs(en) do
|
||||
local rand = math.random(650, 1000)
|
||||
if rand < force and rand % 2 == 0 then
|
||||
e:Fire("enablemotion", "", 0)
|
||||
constraint.RemoveAll(e)
|
||||
end
|
||||
if e:IsOnGround() then
|
||||
e:TakeDamage((force / 100) + 15, game.GetWorld())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
timer.Create("EarthquakeTest", 1, 0, EarthQuakeTest)
|
||||
|
||||
--[[---------------------------------------------------------
|
||||
Flammable
|
||||
---------------------------------------------------------]]
|
||||
-- Class names as index
|
||||
local flammablePropsKV = {
|
||||
drug = true,
|
||||
drug_lab = true,
|
||||
food = true,
|
||||
gunlab = true,
|
||||
letter = true,
|
||||
microwave = true,
|
||||
money_printer = true,
|
||||
spawned_shipment = true,
|
||||
spawned_weapon = true,
|
||||
spawned_money = true
|
||||
}
|
||||
|
||||
local flammableProps = {} -- Numbers as index
|
||||
for k in pairs(flammablePropsKV) do table.insert(flammableProps, k) end
|
||||
|
||||
|
||||
local function IsFlammable(ent)
|
||||
return flammablePropsKV[ent:GetClass()] ~= nil
|
||||
end
|
||||
|
||||
-- FireSpread from SeriousRP
|
||||
local function FireSpread(ent, chanceDiv)
|
||||
if not ent:IsOnFire() then return end
|
||||
|
||||
if ent:isMoneyBag() then
|
||||
ent:Remove()
|
||||
end
|
||||
|
||||
local rand = math.random(0, 300 / chanceDiv)
|
||||
|
||||
if rand > 1 then return end
|
||||
local en = ents.FindInSphere(ent:GetPos(), math.random(20, 90))
|
||||
|
||||
for _, v in ipairs(en) do
|
||||
if not IsFlammable(v) or v == ent then continue end
|
||||
|
||||
if not v.burned then
|
||||
v:Ignite(math.random(5,180), 0)
|
||||
v.burned = true
|
||||
break -- Don't ignite all entities in sphere at once, just one at a time
|
||||
end
|
||||
|
||||
local color = v:GetColor()
|
||||
if (color.r - 51) >= 0 then color.r = color.r - 51 end
|
||||
if (color.g - 51) >= 0 then color.g = color.g - 51 end
|
||||
if (color.b - 51) >= 0 then color.b = color.b - 51 end
|
||||
v:SetColor(color)
|
||||
if (color.r + color.g + color.b) < 103 and math.random(1, 100) < 35 then
|
||||
v:Fire("enablemotion", "", 0)
|
||||
constraint.RemoveAll(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function FlammablePropThink()
|
||||
local class = flammableProps[math.random(#flammableProps)]
|
||||
local entities = ents.FindByClass(class)
|
||||
local ent = entities[math.random(#entities)]
|
||||
|
||||
if class ~= "letter" then return end
|
||||
|
||||
if not ent then return end
|
||||
|
||||
-- The amount of classes and the amount of entities in a class
|
||||
-- affect the chance of fire spreading. This should be minimized.
|
||||
FireSpread(ent, #entities * #flammableProps)
|
||||
end
|
||||
timer.Create("FlammableProps", 0.1, 0, FlammablePropThink)
|
||||
88
gamemodes/darkrp/gamemode/modules/fadmin/cl_fadmin.lua
Normal file
88
gamemodes/darkrp/gamemode/modules/fadmin/cl_fadmin.lua
Normal file
@@ -0,0 +1,88 @@
|
||||
local function IncludeFolder(fol)
|
||||
fol = string.lower(fol)
|
||||
local _, folders = file.Find(fol .. "*", "LUA")
|
||||
|
||||
for _, folder in SortedPairs(folders, true) do
|
||||
if folder ~= "." and folder ~= ".." then
|
||||
for _, File in SortedPairs(file.Find(fol .. folder .. "/sh_*.lua", "LUA"), true) do
|
||||
include(fol .. folder .. "/" .. File)
|
||||
end
|
||||
|
||||
for _, File in SortedPairs(file.Find(fol .. folder .. "/cl_*.lua", "LUA"), true) do
|
||||
include(fol .. folder .. "/" .. File)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
IncludeFolder(GM.FolderName .. "/gamemode/modules/fadmin/fadmin/")
|
||||
IncludeFolder(GM.FolderName .. "/gamemode/modules/fadmin/fadmin/playeractions/")
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
FAdmin global settings
|
||||
---------------------------------------------------------------------------]]
|
||||
net.Receive("FAdmin_GlobalSetting", function(len)
|
||||
local setting, value = net.ReadString(), net.ReadType(net.ReadUInt(8))
|
||||
|
||||
FAdmin.GlobalSetting = FAdmin.GlobalSetting or {}
|
||||
FAdmin.GlobalSetting[setting] = value
|
||||
end)
|
||||
|
||||
net.Receive("FAdmin_PlayerSetting", function(len)
|
||||
local uid, setting, value = net.ReadUInt(16), net.ReadString(), net.ReadType(net.ReadUInt(8))
|
||||
|
||||
FAdmin.PlayerSettings = FAdmin.PlayerSettings or {}
|
||||
FAdmin.PlayerSettings[uid] = FAdmin.PlayerSettings[uid] or {}
|
||||
FAdmin.PlayerSettings[uid][setting] = value
|
||||
end)
|
||||
|
||||
timer.Create("FAdmin_CleanPlayerSettings", 300, 0, function()
|
||||
if not FAdmin.PlayerSettings then return end
|
||||
|
||||
-- find highest userID
|
||||
local max = math.huge
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if IsValid(v) and v:UserID() > max then max = v:UserID() end
|
||||
end
|
||||
|
||||
-- Anything lower than the maximal UserID can be culled
|
||||
-- This prevents data from joining players from being removed
|
||||
-- New players always get a strictly higher UserID than any player before them
|
||||
for uid in pairs(FAdmin.PlayerSettings) do
|
||||
if IsValid(Player(uid)) or uid > max then continue end
|
||||
|
||||
FAdmin.PlayerSettings[uid] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
local plyMeta = FindMetaTable("Player")
|
||||
|
||||
function plyMeta:FAdmin_GetGlobal(setting)
|
||||
local uid = self:UserID()
|
||||
return FAdmin.PlayerSettings and FAdmin.PlayerSettings[uid] and FAdmin.PlayerSettings[uid][setting] or nil
|
||||
end
|
||||
|
||||
net.Receive("FAdmin_GlobalPlayerSettings", function(len)
|
||||
local globalCount = net.ReadUInt(8)
|
||||
|
||||
FAdmin.GlobalSetting = FAdmin.GlobalSetting or {}
|
||||
|
||||
for i = 1, globalCount do
|
||||
FAdmin.GlobalSetting[net.ReadString()] = net.ReadType(net.ReadUInt(8))
|
||||
end
|
||||
|
||||
local plyCount = net.ReadUInt(8)
|
||||
FAdmin.PlayerSettings = FAdmin.PlayerSettings or {}
|
||||
|
||||
for i = 1, plyCount do
|
||||
local uid = net.ReadUInt(16)
|
||||
local count = net.ReadUInt(8)
|
||||
|
||||
FAdmin.PlayerSettings[uid] = FAdmin.PlayerSettings[uid] or {}
|
||||
|
||||
for j = 1, count do
|
||||
FAdmin.PlayerSettings[uid][net.ReadString()] = net.ReadType(net.ReadUInt(8))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
if not FAdmin or not FAdmin.StartHooks then return end
|
||||
FAdmin.StartHooks["DarkRP"] = function()
|
||||
-- DarkRP information:
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Money", function(ply) if LocalPlayer():IsAdmin() then return DarkRP.formatMoney(ply:getDarkRPVar("money")) end end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Steam name", function(ply) return ply:SteamName() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Wanted", function(ply) if ply:getDarkRPVar("wanted") then return tostring(ply:getDarkRPVar("wantedReason") or "N/A") end end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Community link", function(ply) return FAdmin.SteamToProfile(ply) end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Rank", function(ply)
|
||||
if FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "SeeAdmins") then
|
||||
return ply:GetUserGroup()
|
||||
end
|
||||
end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Wanted reason", function(ply)
|
||||
if ply:isWanted() and LocalPlayer():isCP() then
|
||||
return ply:getWantedReason()
|
||||
end
|
||||
end)
|
||||
|
||||
-- Warrant
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Warrant", "fadmin/icons/message", Color(0, 0, 200, 255),
|
||||
function(ply) return LocalPlayer():isCP() end,
|
||||
function(ply, button)
|
||||
Derma_StringRequest("Warrant reason", "Enter the reason for the warrant", "", function(Reason)
|
||||
RunConsoleCommand("darkrp", "warrant", ply:SteamID(), Reason)
|
||||
end)
|
||||
end)
|
||||
|
||||
--wanted
|
||||
FAdmin.ScoreBoard.Player:AddActionButton(function(ply)
|
||||
return ((ply:getDarkRPVar("wanted") and "Unw") or "W") .. "anted"
|
||||
end,
|
||||
function(ply) return "fadmin/icons/jail", ply:getDarkRPVar("wanted") and "fadmin/icons/disable" end,
|
||||
Color(0, 0, 200, 255),
|
||||
function(ply) return LocalPlayer():isCP() end,
|
||||
function(ply, button)
|
||||
if not ply:getDarkRPVar("wanted") then
|
||||
Derma_StringRequest("wanted reason", "Enter the reason to arrest this player", "", function(Reason)
|
||||
RunConsoleCommand("darkrp", "wanted", ply:SteamID(), Reason)
|
||||
end)
|
||||
else
|
||||
RunConsoleCommand("darkrp", "unwanted", ply:UserID())
|
||||
end
|
||||
end)
|
||||
|
||||
--Teamban
|
||||
local function teamban(ply, button)
|
||||
local menu = DermaMenu()
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
menu:AddPanel(Padding)
|
||||
|
||||
local Title = vgui.Create("DLabel")
|
||||
Title:SetText(" Jobs:\n")
|
||||
Title:SetFont("UiBold")
|
||||
Title:SizeToContents()
|
||||
Title:SetTextColor(color_black)
|
||||
menu:AddPanel(Title)
|
||||
|
||||
local command = "teamban"
|
||||
local uid = ply:UserID()
|
||||
for k, v in SortedPairsByMemberValue(RPExtraTeams, "name") do
|
||||
local submenu = menu:AddSubMenu(v.name)
|
||||
submenu:AddOption("2 minutes", function() RunConsoleCommand("darkrp", command, uid, k, 120) end)
|
||||
submenu:AddOption("Half an hour", function() RunConsoleCommand("darkrp", command, uid, k, 1800) end)
|
||||
submenu:AddOption("An hour", function() RunConsoleCommand("darkrp", command, uid, k, 3600) end)
|
||||
submenu:AddOption("Until restart", function() RunConsoleCommand("darkrp", command, uid, k, 0) end)
|
||||
end
|
||||
menu:Open()
|
||||
end
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Ban from job", "fadmin/icons/changeteam", Color(200, 0, 0, 255),
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "DarkRP_AdminCommands", ply) end, teamban)
|
||||
|
||||
local function teamunban(ply, button)
|
||||
local menu = DermaMenu()
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
menu:AddPanel(Padding)
|
||||
|
||||
local Title = vgui.Create("DLabel")
|
||||
Title:SetText(" Jobs:\n")
|
||||
Title:SetFont("UiBold")
|
||||
Title:SizeToContents()
|
||||
Title:SetTextColor(color_black)
|
||||
menu:AddPanel(Title)
|
||||
|
||||
local command = "teamunban"
|
||||
local uid = ply:UserID()
|
||||
for k, v in SortedPairsByMemberValue(RPExtraTeams, "name") do
|
||||
menu:AddOption(v.name, function() RunConsoleCommand("darkrp", command, uid, k) end)
|
||||
end
|
||||
menu:Open()
|
||||
end
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Unban from job", function() return "fadmin/icons/changeteam", "fadmin/icons/disable" end, Color(200, 0, 0, 255),
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "DarkRP_AdminCommands", ply) end, teamunban)
|
||||
end
|
||||
@@ -0,0 +1,262 @@
|
||||
local ContinueNewGroup
|
||||
local EditGroups
|
||||
|
||||
local function RetrievePRIVS(len)
|
||||
FAdmin.Access.Groups = net.ReadTable()
|
||||
|
||||
for k, v in pairs(FAdmin.Access.Groups) do
|
||||
if CAMI.GetUsergroup(k) then continue end
|
||||
|
||||
CAMI.RegisterUsergroup({
|
||||
Name = k,
|
||||
Inherits = FAdmin.Access.ADMIN[v.ADMIN]
|
||||
}, "FAdmin")
|
||||
end
|
||||
|
||||
-- Remove any groups that are removed from FAdmin from CAMI.
|
||||
for k in pairs(CAMI.GetUsergroups()) do
|
||||
if FAdmin.Access.Groups[k] then continue end
|
||||
|
||||
CAMI.UnregisterUsergroup(k, "FAdmin")
|
||||
end
|
||||
end
|
||||
net.Receive("FADMIN_SendGroups", RetrievePRIVS)
|
||||
|
||||
local function addPriv(um)
|
||||
local group = um:ReadString()
|
||||
FAdmin.Access.Groups[group] = FAdmin.Access.Groups[group] or {}
|
||||
FAdmin.Access.Groups[group].PRIVS[um:ReadString()] = true
|
||||
end
|
||||
usermessage.Hook("FAdmin_AddPriv", addPriv)
|
||||
|
||||
local function removePriv(um)
|
||||
FAdmin.Access.Groups[um:ReadString()].PRIVS[um:ReadString()] = nil
|
||||
end
|
||||
usermessage.Hook("FAdmin_RemovePriv", removePriv)
|
||||
|
||||
local function addGroupUI(ply, func)
|
||||
Derma_StringRequest("Set name",
|
||||
"What will be the name of the new group?",
|
||||
"",
|
||||
function(text)
|
||||
if text == "" then return end
|
||||
Derma_Query("On what access will this team be based? (the new group will inherit all the privileges from the group)", "Admin access",
|
||||
"user", function() ContinueNewGroup(ply, text, 0, func) end,
|
||||
"admin", function() ContinueNewGroup(ply, text, 1, func) end,
|
||||
"superadmin", function() ContinueNewGroup(ply, text, 2, func) end)
|
||||
end)
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["1SetAccess"] = function() -- 1 in hook name so it will be executed first.
|
||||
FAdmin.Commands.AddCommand("setaccess", nil, "<Player>", "<Group name>", "[new group based on (number)]", "[new group privileges]")
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Set access", "fadmin/icons/access", Color(155, 0, 0, 255),
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "SetAccess") or LocalPlayer():IsSuperAdmin() end, function(ply)
|
||||
local menu = DermaMenu()
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
menu:AddPanel(Padding)
|
||||
|
||||
local Title = vgui.Create("DLabel")
|
||||
Title:SetText(" Set access:\n")
|
||||
Title:SetFont("UiBold")
|
||||
Title:SizeToContents()
|
||||
Title:SetTextColor(color_black)
|
||||
|
||||
menu:AddPanel(Title)
|
||||
|
||||
for k in SortedPairsByMemberValue(FAdmin.Access.Groups, "ADMIN", true) do
|
||||
menu:AddOption(k, function()
|
||||
if not IsValid(ply) then return end
|
||||
RunConsoleCommand("_FAdmin", "setaccess", ply:UserID(), k)
|
||||
end)
|
||||
end
|
||||
|
||||
menu:AddOption("New...", function() addGroupUI(ply) end)
|
||||
menu:Open()
|
||||
end)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddPlayerAction("Edit groups", "fadmin/icons/access", Color(0, 155, 0, 255), function() return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "ManageGroups") or FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "ManagePrivileges") end, EditGroups)
|
||||
|
||||
-- Admin immunity
|
||||
FAdmin.ScoreBoard.Server:AddServerSetting(function()
|
||||
return (FAdmin.GlobalSetting.Immunity and "Disable" or "Enable") .. " Admin immunity"
|
||||
end,
|
||||
function()
|
||||
return "fadmin/icons/access", FAdmin.GlobalSetting.Immunity and "fadmin/icons/disable"
|
||||
end, Color(0, 0, 155, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "ManageGroups") end, function(button)
|
||||
button:SetImage2((not FAdmin.GlobalSetting.Immunity and "fadmin/icons/disable") or "null")
|
||||
button:SetText((not FAdmin.GlobalSetting.Immunity and "Disable" or "Enable") .. " Admin immunity")
|
||||
button:GetParent():InvalidateLayout()
|
||||
|
||||
RunConsoleCommand("_Fadmin", "immunity", (FAdmin.GlobalSetting.Immunity and 0) or 1)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
ContinueNewGroup = function(ply, name, admin_access, func)
|
||||
if IsValid(ply) then
|
||||
RunConsoleCommand("_FAdmin", "setaccess", ply:UserID(), name, admin_access)
|
||||
else
|
||||
RunConsoleCommand("_FAdmin", "AddGroup", name, admin_access)
|
||||
end
|
||||
|
||||
if func then
|
||||
func(name, admin_access)
|
||||
end
|
||||
end
|
||||
|
||||
EditGroups = function()
|
||||
local frame, SelectedGroup, AddGroup, RemGroup, Privileges, SelectedPrivs, AddPriv, RemPriv, lblImmunity, nmbrImmunity
|
||||
|
||||
frame = vgui.Create("DFrame")
|
||||
frame:SetTitle("Create, edit and remove groups")
|
||||
frame:MakePopup()
|
||||
frame:SetVisible(true)
|
||||
frame:SetSize(640, 480)
|
||||
frame:Center()
|
||||
|
||||
SelectedGroup = vgui.Create("DComboBox", frame)
|
||||
SelectedGroup:SetPos(5, 30)
|
||||
SelectedGroup:SetWidth(145)
|
||||
|
||||
for _, v in pairs(FAdmin.Access.Groups) do
|
||||
v.immunity = v.immunity or 0
|
||||
end
|
||||
for k in SortedPairsByMemberValue(FAdmin.Access.Groups, "immunity", true) do
|
||||
SelectedGroup:AddChoice(k)
|
||||
end
|
||||
|
||||
AddGroup = vgui.Create("DButton", frame)
|
||||
AddGroup:SetPos(155, 30)
|
||||
AddGroup:SetSize(60, 22)
|
||||
AddGroup:SetText("Add Group")
|
||||
AddGroup.DoClick = function()
|
||||
addGroupUI(nil, function(name, admin, privs)
|
||||
SelectedGroup:AddChoice(name)
|
||||
SelectedGroup:SetValue(name)
|
||||
RemGroup:SetDisabled(false)
|
||||
|
||||
Privileges:Clear()
|
||||
SelectedPrivs:Clear()
|
||||
nmbrImmunity:SetText(FAdmin.Access.Groups[FAdmin.Access.ADMIN[admin + 1]].immunity)
|
||||
nmbrImmunity:SetDisabled(false)
|
||||
nmbrImmunity:SetEditable(true)
|
||||
|
||||
for priv, am in SortedPairs(FAdmin.Access.Privileges) do
|
||||
if am <= admin + 1 then
|
||||
SelectedPrivs:AddLine(priv)
|
||||
else
|
||||
Privileges:AddLine(priv)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
RemGroup = vgui.Create("DButton", frame)
|
||||
RemGroup:SetPos(220, 30)
|
||||
RemGroup:SetSize(85, 22)
|
||||
RemGroup:SetText("Remove Group")
|
||||
RemGroup.DoClick = function()
|
||||
RunConsoleCommand("_FAdmin", "RemoveGroup", SelectedGroup:GetValue())
|
||||
|
||||
for k, v in pairs(SelectedGroup.Choices) do
|
||||
if v ~= SelectedGroup:GetValue() then continue end
|
||||
|
||||
SelectedGroup.Choices[k] = nil
|
||||
break
|
||||
end
|
||||
table.ClearKeys(SelectedGroup.Choices)
|
||||
|
||||
SelectedGroup:SetValue("user")
|
||||
SelectedGroup:OnSelect(1, "user")
|
||||
end
|
||||
|
||||
Privileges = vgui.Create("DListView", frame)
|
||||
Privileges:SetPos(5, 55)
|
||||
Privileges:SetSize(300, 420)
|
||||
Privileges:AddColumn("Available privileges")
|
||||
|
||||
SelectedPrivs = vgui.Create("DListView", frame)
|
||||
SelectedPrivs:SetPos(340, 55)
|
||||
SelectedPrivs:SetSize(295, 420)
|
||||
SelectedPrivs:AddColumn("Selected Privileges")
|
||||
|
||||
function SelectedGroup:OnSelect(index, value, data)
|
||||
if not FAdmin.Access.Groups[value] then return end
|
||||
|
||||
RemGroup:SetDisabled(false)
|
||||
if table.HasValue(FAdmin.Access.ADMIN, value) then
|
||||
RemGroup:SetDisabled(true)
|
||||
end
|
||||
|
||||
Privileges:Clear()
|
||||
SelectedPrivs:Clear()
|
||||
|
||||
for priv, _ in SortedPairs(FAdmin.Access.Privileges) do
|
||||
if FAdmin.Access.Groups[value].PRIVS[priv] then
|
||||
SelectedPrivs:AddLine(priv)
|
||||
else
|
||||
Privileges:AddLine(priv)
|
||||
end
|
||||
end
|
||||
|
||||
if nmbrImmunity then
|
||||
nmbrImmunity:SetText(FAdmin.Access.Groups[value].immunity or "")
|
||||
if table.HasValue({"superadmin", "admin", "user", "noaccess"}, string.lower(value)) then
|
||||
nmbrImmunity:SetDisabled(true)
|
||||
nmbrImmunity:SetEditable(false)
|
||||
else
|
||||
nmbrImmunity:SetDisabled(false)
|
||||
nmbrImmunity:SetEditable(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
SelectedGroup:SetValue("user")
|
||||
SelectedGroup:OnSelect(1, "user")
|
||||
|
||||
AddPriv = vgui.Create("DButton", frame)
|
||||
AddPriv:SetPos(310, 55)
|
||||
AddPriv:SetSize(25, 25)
|
||||
AddPriv:SetText(">")
|
||||
AddPriv.DoClick = function()
|
||||
for _, v in ipairs(Privileges:GetSelected()) do
|
||||
local priv = v.Columns[1]:GetValue()
|
||||
RunConsoleCommand("FAdmin", "AddPrivilege", SelectedGroup:GetValue(), priv)
|
||||
SelectedPrivs:AddLine(priv)
|
||||
Privileges:RemoveLine(v.m_iID)
|
||||
end
|
||||
end
|
||||
|
||||
RemPriv = vgui.Create("DButton", frame)
|
||||
RemPriv:SetPos(310, 85)
|
||||
RemPriv:SetSize(25, 25)
|
||||
RemPriv:SetText("<")
|
||||
RemPriv.DoClick = function()
|
||||
for _, v in ipairs(SelectedPrivs:GetSelected()) do
|
||||
local priv = v.Columns[1]:GetValue()
|
||||
if SelectedGroup:GetValue() == LocalPlayer():GetUserGroup() and priv == "ManagePrivileges" then
|
||||
return Derma_Message("You shouldn't be removing ManagePrivileges. It will make you unable to edit the groups. This is preventing you from locking yourself out of the system.", "Clever move.")
|
||||
end
|
||||
RunConsoleCommand("FAdmin", "RemovePrivilege", SelectedGroup:GetValue(), priv)
|
||||
Privileges:AddLine(priv)
|
||||
SelectedPrivs:RemoveLine(v.m_iID)
|
||||
end
|
||||
end
|
||||
|
||||
lblImmunity = vgui.Create("DLabel", frame)
|
||||
lblImmunity:SetPos(340, 30)
|
||||
lblImmunity:SetText("Immunity number (higher is more immune)")
|
||||
lblImmunity:SizeToContents()
|
||||
|
||||
nmbrImmunity = vgui.Create("DTextEntry", frame)
|
||||
nmbrImmunity:SetPos(545, 28)
|
||||
nmbrImmunity:SetWide(90)
|
||||
nmbrImmunity:SetNumeric(true)
|
||||
nmbrImmunity:SetText(FAdmin.Access.Groups.user.immunity)
|
||||
nmbrImmunity:SetDisabled(true)
|
||||
nmbrImmunity:SetEditable(false)
|
||||
nmbrImmunity.OnEnter = function(self) RunConsoleCommand("FAdmin", "SetImmunity", SelectedGroup:GetValue(), self:GetValue()) end
|
||||
end
|
||||
@@ -0,0 +1,273 @@
|
||||
CreateConVar("_FAdmin_immunity", 1, {FCVAR_GAMEDLL, FCVAR_REPLICATED, FCVAR_ARCHIVE, FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
FAdmin.Access = FAdmin.Access or {}
|
||||
FAdmin.Access.ADMIN = {"user", "admin", "superadmin"}
|
||||
FAdmin.Access.ADMIN[0] = "user"
|
||||
|
||||
FAdmin.Access.Groups = FAdmin.Access.Groups or {}
|
||||
FAdmin.Access.Privileges = FAdmin.Access.Privileges or {}
|
||||
|
||||
function FAdmin.Access.AddGroup(name, admin_access --[[0 = not admin, 1 = admin, 2 = superadmin]], privs, immunity, fromCAMI, CAMIsrc)
|
||||
FAdmin.Access.Groups[name] = FAdmin.Access.Groups[name] or {ADMIN = admin_access, PRIVS = privs or {}, immunity = immunity}
|
||||
|
||||
--Make sure things that come from CAMI come with a CAMIsrc
|
||||
assert((fromCAMI and CAMIsrc ~= nil) or ((not fromCAMI) and CAMIsrc == nil))
|
||||
--If the CAMIsrc is a string, save it, otherwise save an empty string
|
||||
if not isstring(CAMIsrc) then
|
||||
CAMIsrc = ""
|
||||
end
|
||||
|
||||
-- Register custom usergroups with CAMI
|
||||
if name ~= "user" and name ~= "admin" and name ~= "superadmin" and not fromCAMI then
|
||||
CAMI.RegisterUsergroup({
|
||||
Name = name,
|
||||
Inherits = FAdmin.Access.ADMIN[admin_access]
|
||||
}, "FAdmin")
|
||||
end
|
||||
|
||||
-- Add newly created privileges on server reload
|
||||
for p, _ in pairs(privs or {}) do
|
||||
FAdmin.Access.Groups[name].PRIVS[p] = true
|
||||
end
|
||||
|
||||
if not SERVER then return end
|
||||
|
||||
MySQLite.queryValue("SELECT COUNT(*) FROM FADMIN_GROUPS WHERE NAME = " .. MySQLite.SQLStr(name) .. ";", function(val)
|
||||
if tonumber(val or 0) > 0 then return end
|
||||
|
||||
MySQLite.query("REPLACE INTO FADMIN_GROUPS VALUES(" .. MySQLite.SQLStr(name) .. ", " .. tonumber(admin_access) .. ");", function()
|
||||
for priv, _ in pairs(privs or {}) do
|
||||
MySQLite.query("REPLACE INTO FADMIN_PRIVILEGES VALUES(" .. MySQLite.SQLStr(name) .. ", " .. MySQLite.SQLStr(priv) .. ");")
|
||||
end
|
||||
if fromCAMI then
|
||||
MySQLite.query("REPLACE INTO FADMIN_GROUPS_SRC VALUES(" .. MySQLite.SQLStr(name) .. ", " .. MySQLite.SQLStr(CAMIsrc) .. ");")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
if immunity then
|
||||
MySQLite.query("REPLACE INTO FAdmin_Immunity VALUES(" .. MySQLite.SQLStr(name) .. ", " .. tonumber(immunity) .. ");")
|
||||
end
|
||||
|
||||
if FAdmin.Access.SendGroups and privs then
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
FAdmin.Access.SendGroups(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.Access.OnUsergroupRegistered(usergroup, source)
|
||||
-- Don't re-add usergroups coming from FAdmin itself
|
||||
if source == "FAdmin" then return end
|
||||
|
||||
local inheritRoot = CAMI.InheritanceRoot(usergroup.Inherits)
|
||||
local admin_access = table.KeyFromValue(FAdmin.Access.ADMIN, inheritRoot) or 1
|
||||
|
||||
-- Add groups registered to CAMI to FAdmin. Assume privileges from either the usergroup it inherits or its inheritance root.
|
||||
-- Immunity is unknown and can be set by the user later. FAdmin immunity only applies to FAdmin anyway.
|
||||
local parent = FAdmin.Access.Groups[usergroup.Inherits] or FAdmin.Access.Groups[inheritRoot] or {}
|
||||
FAdmin.Access.AddGroup(usergroup.Name, admin_access - 1, table.Copy(parent.PRIVS) or {}, parent.immunity or 10, true, source)
|
||||
end
|
||||
|
||||
|
||||
function FAdmin.Access.OnUsergroupUnregistered(usergroup, source)
|
||||
if table.HasValue({"superadmin", "admin", "user", "noaccess"}, usergroup.Name) then return end
|
||||
|
||||
FAdmin.Access.Groups[usergroup.Name] = nil
|
||||
|
||||
if not SERVER then return end
|
||||
|
||||
MySQLite.query("DELETE FROM FADMIN_GROUPS WHERE NAME = " .. MySQLite.SQLStr(usergroup.Name) .. ";")
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
FAdmin.Access.SendGroups(v)
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.Access.RemoveGroup(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManageGroups") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if not args[1] then return false end
|
||||
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
if not FAdmin.Access.Groups[args[1]] or table.HasValue({"superadmin", "admin", "user"}, string.lower(args[1])) then return true, args[1] end
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if (not plyGroup or FAdmin.Access.Groups[args[1]].immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to remove usergroups with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
CAMI.UnregisterUsergroup(args[1], "FAdmin")
|
||||
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Group succesfully removed")
|
||||
end
|
||||
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
|
||||
local oldplyIsAdmin = PLAYER.IsAdmin
|
||||
function PLAYER:IsAdmin(...)
|
||||
local usergroup = self:GetUserGroup()
|
||||
|
||||
if not FAdmin or not FAdmin.Access or not FAdmin.Access.Groups or not FAdmin.Access.Groups[usergroup] then return oldplyIsAdmin(self, ...) or game.SinglePlayer() end
|
||||
|
||||
if (FAdmin.Access.Groups[usergroup] and FAdmin.Access.Groups[usergroup].ADMIN >= 1 --[[1 = admin]]) or (self.IsListenServerHost and self:IsListenServerHost()) then
|
||||
return true
|
||||
end
|
||||
|
||||
if CLIENT and tonumber(self:FAdmin_GetGlobal("FAdmin_admin")) and self:FAdmin_GetGlobal("FAdmin_admin") >= 1 then return true end
|
||||
|
||||
return oldplyIsAdmin(self, ...) or game.SinglePlayer()
|
||||
end
|
||||
|
||||
local oldplyIsSuperAdmin = PLAYER.IsSuperAdmin
|
||||
function PLAYER:IsSuperAdmin(...)
|
||||
local usergroup = self:GetUserGroup()
|
||||
if not FAdmin or not FAdmin.Access or not FAdmin.Access.Groups or not FAdmin.Access.Groups[usergroup] then return oldplyIsSuperAdmin(self, ...) or game.SinglePlayer() end
|
||||
if (FAdmin.Access.Groups[usergroup] and FAdmin.Access.Groups[usergroup].ADMIN >= 2 --[[2 = superadmin]]) or (self.IsListenServerHost and self:IsListenServerHost()) then
|
||||
return true
|
||||
end
|
||||
if CLIENT and tonumber(self:FAdmin_GetGlobal("FAdmin_admin")) and self:FAdmin_GetGlobal("FAdmin_admin") >= 2 then return true end
|
||||
return oldplyIsSuperAdmin(self, ...) or game.SinglePlayer()
|
||||
end
|
||||
|
||||
--Privileges
|
||||
function FAdmin.Access.AddPrivilege(Name, admin_access)
|
||||
FAdmin.Access.Privileges[Name] = admin_access
|
||||
end
|
||||
|
||||
hook.Add("CAMI.OnPrivilegeRegistered", "FAdmin", function(privilege)
|
||||
FAdmin.Access.AddPrivilege(privilege.Name, table.KeyFromValue(FAdmin.Access.ADMIN, CAMI.InheritanceRoot(privilege.MinAccess)) or 3)
|
||||
|
||||
-- Register privilege and add to respective usergroups
|
||||
if SERVER then FAdmin.Access.RegisterCAMIPrivilege(privilege) end
|
||||
end)
|
||||
|
||||
for _, camipriv in pairs(CAMI.GetPrivileges()) do
|
||||
FAdmin.Access.AddPrivilege(camipriv.Name, table.KeyFromValue(FAdmin.Access.ADMIN, CAMI.InheritanceRoot(camipriv.MinAccess)) or 3)
|
||||
-- Register if the database has already loaded
|
||||
if SERVER and FAdmin.Access.RegisterCAMIPrivilege then FAdmin.Access.RegisterCAMIPrivilege(camipriv) end
|
||||
end
|
||||
|
||||
hook.Add("CAMI.OnPrivilegeUnregistered", "FAdmin", function(privilege)
|
||||
FAdmin.Access.Privileges[privilege.Name] = nil
|
||||
end)
|
||||
|
||||
function FAdmin.Access.PlayerIsHost(ply)
|
||||
return ply:EntIndex() == 0 or game.SinglePlayer() or (ply.IsListenServerHost and ply:IsListenServerHost())
|
||||
end
|
||||
|
||||
function FAdmin.Access.PlayerHasPrivilege(ply, priv, target, ignoreImmunity)
|
||||
-- This is the server console
|
||||
if FAdmin.Access.PlayerIsHost(ply) then return true end
|
||||
-- Privilege does not exist
|
||||
if not FAdmin.Access.Privileges[priv] then return ply:IsAdmin() end
|
||||
|
||||
local Usergroup = ply:GetUserGroup()
|
||||
|
||||
local canTarget = hook.Call("FAdmin_CanTarget", nil, ply, priv, target)
|
||||
if canTarget ~= nil then
|
||||
return canTarget
|
||||
end
|
||||
|
||||
if FAdmin.GlobalSetting.Immunity and
|
||||
not ignoreImmunity and
|
||||
not isstring(target) and IsValid(target) and target ~= ply and
|
||||
FAdmin.Access.Groups[Usergroup] and FAdmin.Access.Groups[target:GetUserGroup()] and
|
||||
FAdmin.Access.Groups[Usergroup].immunity and FAdmin.Access.Groups[target:GetUserGroup()].immunity and
|
||||
FAdmin.Access.Groups[target:GetUserGroup()].immunity >= FAdmin.Access.Groups[Usergroup].immunity then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Defer answer when usergroup is unknown
|
||||
if not FAdmin.Access.Groups[Usergroup] then return end
|
||||
|
||||
if FAdmin.Access.Groups[Usergroup].PRIVS[priv] then
|
||||
return true
|
||||
end
|
||||
|
||||
if CLIENT and ply.FADMIN_PRIVS and ply.FADMIN_PRIVS[priv] then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
hook.Add("CAMI.PlayerHasAccess", "FAdmin", function(actor, privilegeName, callback, target, extraInfo)
|
||||
-- FAdmin doesn't know. Defer answer.
|
||||
if not FAdmin.Access.Privileges[privilegeName] then return end
|
||||
|
||||
local res = FAdmin.Access.PlayerHasPrivilege(actor, privilegeName, target, extraInfo and extraInfo.IgnoreImmunity)
|
||||
|
||||
-- Defer again
|
||||
if res == nil then return end
|
||||
|
||||
-- Publish the answer
|
||||
callback(res, "FAdmin")
|
||||
|
||||
-- FAdmin knows the answer. Prevent other hooks from running.
|
||||
return true
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.SteamIDHasAccess", "FAdmin", function(actorSteam, privilegeName, callback, targetSteam, extraInfo)
|
||||
-- The client just doesn't know
|
||||
if CLIENT then return end
|
||||
|
||||
if not targetSteam or extraInfo and extraInfo.IgnoreImmunity then
|
||||
MySQLite.query(string.format(
|
||||
[[SELECT COUNT(*) AS c
|
||||
FROM FAdmin_PlayerGroup l
|
||||
JOIN FADMIN_PRIVILEGES r ON l.groupname = r.NAME
|
||||
WHERE l.steamid = %s AND r.PRIVILEGE = %s]],
|
||||
MySQLite.SQLStr(actorSteam),
|
||||
MySQLite.SQLStr(privilegeName)
|
||||
), function(res) callback(tonumber(res[1].c) > 0) end)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
MySQLite.query(string.format(
|
||||
[[SELECT ll.i AND rr.c AS res
|
||||
FROM (SELECT li.immunity >= ri.immunity AS i
|
||||
FROM FAdmin_PlayerGroup lg
|
||||
JOIN FAdmin_Immunity li ON lg.groupname = li.groupname
|
||||
JOIN FAdmin_PlayerGroup rg
|
||||
JOIN FAdmin_Immunity ri ON rg.groupname = ri.groupname
|
||||
WHERE lg.steamid = %s AND rg.steamid = %s) AS ll
|
||||
JOIN (SELECT COUNT(*) AS c
|
||||
FROM FAdmin_PlayerGroup l
|
||||
JOIN FADMIN_PRIVILEGES r ON l.groupname = r.NAME
|
||||
WHERE l.steamid = %s AND r.PRIVILEGE = %s) AS rr]],
|
||||
MySQLite.SQLStr(actorSteam),
|
||||
MySQLite.SQLStr(targetSteam),
|
||||
MySQLite.SQLStr(actorSteam),
|
||||
MySQLite.SQLStr(privilegeName)
|
||||
), function(res) callback(res and res[1] and tobool(res[1].res) or false) end)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
FAdmin.StartHooks["AccessFunctions"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "setaccess",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " set the usergroup of ", "targets", " to ", "extraInfo.1"},
|
||||
receivers = "everyone",
|
||||
writeExtraInfo = function(i) net.WriteString(i[1]) end,
|
||||
readExtraInfo = function() return {net.ReadString()} end,
|
||||
extraInfoColors = {Color(255, 102, 0)}
|
||||
}
|
||||
|
||||
FAdmin.Access.AddPrivilege("SetAccess", 3) -- AddPrivilege is shared, run on both client and server
|
||||
FAdmin.Access.AddPrivilege("ManagePrivileges", 3)
|
||||
FAdmin.Access.AddPrivilege("ManageGroups", 3)
|
||||
FAdmin.Access.AddPrivilege("SeeAdmins", 1)
|
||||
FAdmin.Commands.AddCommand("RemoveGroup", FAdmin.Access.RemoveGroup)
|
||||
|
||||
FAdmin.Commands.AddCommand("Admins", function(ply)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "SeeAdmins") then return false end
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, v:Nick() .. "\t|\t" .. v:GetUserGroup())
|
||||
end
|
||||
return true
|
||||
end
|
||||
)
|
||||
end
|
||||
@@ -0,0 +1,449 @@
|
||||
--Immunity
|
||||
cvars.AddChangeCallback("_FAdmin_immunity", function(Cvar, Previous, New)
|
||||
FAdmin.SetGlobalSetting("Immunity", (tonumber(New) == 1 and true) or false)
|
||||
FAdmin.SaveSetting("_FAdmin_immunity", tonumber(New))
|
||||
end)
|
||||
|
||||
hook.Add("DatabaseInitialized", "InitializeFAdminGroups", function()
|
||||
MySQLite.query("CREATE TABLE IF NOT EXISTS FADMIN_GROUPS(NAME VARCHAR(40) NOT NULL PRIMARY KEY, ADMIN_ACCESS INTEGER NOT NULL);")
|
||||
MySQLite.query("CREATE TABLE IF NOT EXISTS FAdmin_PlayerGroup(steamid VARCHAR(40) NOT NULL, groupname VARCHAR(40) NOT NULL, PRIMARY KEY(steamid));")
|
||||
MySQLite.query("CREATE TABLE IF NOT EXISTS FAdmin_Immunity(groupname VARCHAR(40) NOT NULL, immunity INTEGER NOT NULL, PRIMARY KEY(groupname));")
|
||||
MySQLite.query("CREATE TABLE IF NOT EXISTS FAdmin_CAMIPrivileges(privname VARCHAR(255) NOT NULL PRIMARY KEY);")
|
||||
MySQLite.query("CREATE TABLE IF NOT EXISTS FADMIN_GROUPS_SRC(NAME VARCHAR(40) NOT NULL PRIMARY KEY REFERENCES FADMIN_GROUPS(NAME) ON DELETE CASCADE, SRC VARCHAR(40));")
|
||||
MySQLite.query([[CREATE TABLE IF NOT EXISTS FADMIN_PRIVILEGES(
|
||||
NAME VARCHAR(40),
|
||||
PRIVILEGE VARCHAR(100),
|
||||
PRIMARY KEY(NAME, PRIVILEGE),
|
||||
FOREIGN KEY(NAME) REFERENCES FADMIN_GROUPS(NAME)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
);]], function()
|
||||
|
||||
-- Remove SetAccess workaround
|
||||
MySQLite.query([[DELETE FROM FADMIN_PRIVILEGES WHERE NAME = "user" AND PRIVILEGE = "SetAccess";]])
|
||||
|
||||
MySQLite.query("SELECT g.NAME, g.ADMIN_ACCESS, p.PRIVILEGE, i.immunity, s.src FROM FADMIN_GROUPS g LEFT OUTER JOIN FADMIN_PRIVILEGES p ON g.NAME = p.NAME LEFT OUTER JOIN FAdmin_Immunity i ON g.NAME = i.groupname LEFT OUTER JOIN FADMIN_GROUPS_SRC s ON g.NAME = s.NAME;", function(data)
|
||||
if not data then return end
|
||||
|
||||
for _, v in pairs(data) do
|
||||
FAdmin.Access.Groups[v.NAME] = FAdmin.Access.Groups[v.NAME] or
|
||||
{ADMIN = tonumber(v.ADMIN_ACCESS), PRIVS = {}}
|
||||
|
||||
if v.PRIVILEGE and v.PRIVILEGE ~= "NULL" then
|
||||
FAdmin.Access.Groups[v.NAME].PRIVS[v.PRIVILEGE] = true
|
||||
end
|
||||
|
||||
if v.immunity and v.immunity ~= "NULL" then
|
||||
FAdmin.Access.Groups[v.NAME].immunity = tonumber(v.immunity)
|
||||
end
|
||||
|
||||
if CAMI.GetUsergroup(v.NAME) then continue end
|
||||
|
||||
CAMI.RegisterUsergroup({
|
||||
Name = v.NAME,
|
||||
Inherits = FAdmin.Access.ADMIN[v.ADMIN_ACCESS] or "user"
|
||||
}, v.SRC)
|
||||
end
|
||||
|
||||
-- Send groups to early joiners and listen server hosts
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
FAdmin.Access.SendGroups(v)
|
||||
end
|
||||
|
||||
-- See if there are any CAMI usergroups that FAdmin doesn't know about yet.
|
||||
-- FAdmin doesn't start listening immediately because the database might not have initialised.
|
||||
-- Besides, other admin mods might add usergroups before FAdmin's Lua files are even run
|
||||
for _, v in pairs(CAMI.GetUsergroups()) do
|
||||
if FAdmin.Access.Groups[v.Name] then continue end
|
||||
|
||||
FAdmin.Access.OnUsergroupRegistered(v,"")
|
||||
end
|
||||
|
||||
-- Start listening for CAMI usergroup registrations.
|
||||
hook.Add("CAMI.OnUsergroupRegistered", "FAdmin", FAdmin.Access.OnUsergroupRegistered)
|
||||
hook.Add("CAMI.OnUsergroupUnregistered", "FAdmin", FAdmin.Access.OnUsergroupUnregistered)
|
||||
|
||||
FAdmin.Access.RegisterCAMIPrivileges()
|
||||
end)
|
||||
|
||||
local function createGroups(privs)
|
||||
FAdmin.Access.AddGroup("superadmin", 2, privs.superadmin, 100)
|
||||
FAdmin.Access.AddGroup("admin", 1, privs.admin, 50)
|
||||
FAdmin.Access.AddGroup("user", 0, privs.user, 10)
|
||||
FAdmin.Access.AddGroup("noaccess", 0, privs.noaccess, 0)
|
||||
end
|
||||
|
||||
MySQLite.query("SELECT DISTINCT PRIVILEGE FROM FADMIN_PRIVILEGES;", function(privTbl)
|
||||
local privs = {}
|
||||
local hasPrivs = {"noaccess", "user", "admin", "superadmin"}
|
||||
|
||||
-- No privileges registered to anyone. Reset everything
|
||||
if not privTbl or table.IsEmpty(privTbl) then
|
||||
for priv, access in pairs(FAdmin.Access.Privileges) do
|
||||
for i = access + 1, #hasPrivs, 1 do
|
||||
privs[hasPrivs[i]] = privs[hasPrivs[i]] or {}
|
||||
privs[hasPrivs[i]][priv] = true
|
||||
end
|
||||
end
|
||||
|
||||
createGroups(privs)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Check for newly created privileges and assign them to the default usergroups
|
||||
-- No privilege can be revoke from every group
|
||||
local privSet = {}
|
||||
for _, priv in ipairs(privTbl) do
|
||||
privSet[priv.PRIVILEGE] = true
|
||||
end
|
||||
|
||||
for priv, access in pairs(FAdmin.Access.Privileges) do
|
||||
if privSet[priv] then continue end
|
||||
|
||||
for i = access + 1, #hasPrivs do
|
||||
MySQLite.query(("REPLACE INTO FADMIN_PRIVILEGES VALUES(%s, %s);"):format(MySQLite.SQLStr(hasPrivs[i]), MySQLite.SQLStr(priv)))
|
||||
end
|
||||
end
|
||||
|
||||
createGroups(privs)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Assign a privilege to its respective usergroups when they are seen for the first time
|
||||
function FAdmin.Access.RegisterCAMIPrivilege(priv)
|
||||
-- Privileges haven't been loaded yet or has already been seen
|
||||
if not FAdmin.CAMIPrivs or FAdmin.CAMIPrivs[priv.Name] then return end
|
||||
|
||||
FAdmin.CAMIPrivs[priv.Name] = true
|
||||
|
||||
for groupName, groupdata in pairs(FAdmin.Access.Groups) do
|
||||
if FAdmin.Access.Privileges[priv.Name] - 1 > groupdata.ADMIN then continue end
|
||||
groupdata.PRIVS[priv.Name] = true
|
||||
|
||||
MySQLite.query(string.format([[REPLACE INTO FADMIN_PRIVILEGES VALUES(%s, %s);]], MySQLite.SQLStr(groupName), MySQLite.SQLStr(priv.Name)))
|
||||
end
|
||||
|
||||
MySQLite.query(string.format([[REPLACE INTO FAdmin_CAMIPrivileges VALUES(%s);]], MySQLite.SQLStr(priv.Name)))
|
||||
end
|
||||
|
||||
-- Assign privileges to their respective usergroups when they are seen for the first time
|
||||
function FAdmin.Access.RegisterCAMIPrivileges()
|
||||
MySQLite.query([[SELECT privname FROM FAdmin_CAMIPrivileges]], function(data)
|
||||
FAdmin.CAMIPrivs = {}
|
||||
|
||||
for _, row in ipairs(data or {}) do
|
||||
FAdmin.CAMIPrivs[row.privname] = true
|
||||
end
|
||||
|
||||
|
||||
for privName, _ in pairs(CAMI.GetPrivileges()) do
|
||||
if FAdmin.CAMIPrivs[privName] then continue end
|
||||
FAdmin.CAMIPrivs[privName] = true
|
||||
|
||||
for groupName, groupdata in pairs(FAdmin.Access.Groups) do
|
||||
if FAdmin.Access.Privileges[privName] - 1 > groupdata.ADMIN then continue end
|
||||
groupdata.PRIVS[privName] = true
|
||||
|
||||
MySQLite.query(string.format([[REPLACE INTO FADMIN_PRIVILEGES VALUES(%s, %s);]], MySQLite.SQLStr(groupName), MySQLite.SQLStr(privName)))
|
||||
end
|
||||
|
||||
MySQLite.query(string.format([[REPLACE INTO FAdmin_CAMIPrivileges VALUES(%s);]], MySQLite.SQLStr(privName)))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function FAdmin.Access.PlayerSetGroup(ply, group)
|
||||
if not FAdmin.Access.Groups[group] then return end
|
||||
ply = isstring(ply) and FAdmin.FindPlayer(ply) and FAdmin.FindPlayer(ply)[1] or ply
|
||||
|
||||
if not isstring(ply) and IsValid(ply) then
|
||||
ply:SetUserGroup(group)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Remove("PlayerInitialSpawn", "PlayerAuthSpawn") -- Remove Garry's usergroup setter.
|
||||
|
||||
-- Update the database only when an end users indicates that a player's usergroup is to be changed.
|
||||
hook.Add("CAMI.PlayerUsergroupChanged", "FAdmin", function(ply, old, new, source)
|
||||
MySQLite.query("REPLACE INTO FAdmin_PlayerGroup VALUES(" .. MySQLite.SQLStr(ply:SteamID()) .. ", " .. MySQLite.SQLStr(new) .. ");")
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.SteamIDUsergroupChanged", "FAdmin", function(steamId, old, new, source)
|
||||
MySQLite.query("REPLACE INTO FAdmin_PlayerGroup VALUES(" .. MySQLite.SQLStr(steamId) .. ", " .. MySQLite.SQLStr(new) .. ");")
|
||||
end)
|
||||
|
||||
function FAdmin.Access.SetRoot(ply, cmd, args) -- FAdmin setroot player. Sets the player to superadmin
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "SetAccess") then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "No access!")
|
||||
FAdmin.Messages.SendMessage(ply, 5, "Please use RCon to set yourself to superadmin if you are the owner of the server")
|
||||
return false
|
||||
end
|
||||
|
||||
local group = FAdmin.Access.Groups["superadmin"]
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if (not plyGroup or group.immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to assign anyone a usergroup with a higher rank than your own")
|
||||
FAdmin.Messages.SendMessage(ply, 5, "Please use RCon to set yourself to superadmin if you are the owner of the server")
|
||||
return false
|
||||
end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not IsValid(target) then continue end
|
||||
|
||||
local target_previous_group = target:GetUserGroup()
|
||||
FAdmin.Access.PlayerSetGroup(target, "superadmin")
|
||||
|
||||
-- An end user changed the usergroup. Register with CAMI
|
||||
CAMI.SignalUserGroupChanged(target, target_previous_group, "superadmin", "FAdmin")
|
||||
|
||||
FAdmin.Messages.SendMessage(ply, 2, "User set to superadmin!")
|
||||
end
|
||||
|
||||
FAdmin.Messages.FireNotification("setaccess", ply, targets, {"superadmin"})
|
||||
return true, targets, "superadmin"
|
||||
end
|
||||
|
||||
-- AddGroup <Groupname> <Adminstatus> <Privileges>
|
||||
local function AddGroup(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManageGroups") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
local admin = tonumber(args[2])
|
||||
if not args[1] or not admin then FAdmin.Messages.SendMessage(ply, 5, "Incorrect arguments!") return false end
|
||||
local privs = {}
|
||||
|
||||
for priv, am in SortedPairs(FAdmin.Access.Privileges) do
|
||||
-- The user cannot create groups with privileges they don't have
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, priv) then continue end
|
||||
if am <= admin + 1 then privs[priv] = true end
|
||||
end
|
||||
|
||||
local immunity = FAdmin.Access.Groups[FAdmin.Access.ADMIN[admin + 1]].immunity
|
||||
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
if (not plyGroup or immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to create usergroups with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
FAdmin.Access.AddGroup(args[1], admin, privs, immunity) -- Add new group
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Group created")
|
||||
FAdmin.Access.SendGroups()
|
||||
|
||||
return true, args[1]
|
||||
end
|
||||
|
||||
local function AddPrivilege(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManagePrivileges") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
local group, priv = args[1], args[2]
|
||||
|
||||
if not FAdmin.Access.Groups[group] or not FAdmin.Access.Privileges[priv] then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "Invalid arguments")
|
||||
return false
|
||||
end
|
||||
|
||||
-- The player cannot add privileges that they themselves do not have
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, priv) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to assign privileges that you don't have yourself")
|
||||
return false
|
||||
end
|
||||
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if (not plyGroup or FAdmin.Access.Groups[group].immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to manage the privileges of a usergroup with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
FAdmin.Access.Groups[group].PRIVS[priv] = true
|
||||
|
||||
MySQLite.query("REPLACE INTO FADMIN_PRIVILEGES VALUES(" .. MySQLite.SQLStr(group) .. ", " .. MySQLite.SQLStr(priv) .. ");")
|
||||
SendUserMessage("FAdmin_AddPriv", player.GetAll(), group, priv)
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Privilege Added!")
|
||||
|
||||
return true, group, priv
|
||||
end
|
||||
|
||||
local function RemovePrivilege(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManagePrivileges") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
local group, priv = args[1], args[2]
|
||||
if not FAdmin.Access.Groups[group] or not FAdmin.Access.Privileges[priv] then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "Invalid arguments")
|
||||
return false
|
||||
end
|
||||
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if (not plyGroup or FAdmin.Access.Groups[group].immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to manage the privileges of a usergroup with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
FAdmin.Access.Groups[group].PRIVS[priv] = nil
|
||||
|
||||
MySQLite.query("DELETE FROM FADMIN_PRIVILEGES WHERE NAME = " .. MySQLite.SQLStr(group) .. " AND PRIVILEGE = " .. MySQLite.SQLStr(priv) .. ";")
|
||||
SendUserMessage("FAdmin_RemovePriv", player.GetAll(), group, priv)
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Privilege Removed!")
|
||||
|
||||
return true, group, priv
|
||||
end
|
||||
|
||||
function FAdmin.Access.SendGroups(ply)
|
||||
if not FAdmin.Access.Groups then return end
|
||||
|
||||
net.Start("FADMIN_SendGroups")
|
||||
net.WriteTable(FAdmin.Access.Groups)
|
||||
net.Send(IsValid(ply) and ply or player.GetAll())
|
||||
end
|
||||
|
||||
-- FAdmin SetAccess <player> <groupname> [new_groupadmin, new_groupprivs]
|
||||
function FAdmin.Access.SetAccess(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "SetAccess") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
local admin = tonumber(args[3])
|
||||
local group = FAdmin.Access.Groups[args[2]]
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
if not args[2] or not group and not admin then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Group not found")
|
||||
return false
|
||||
elseif args[2] and not group and admin then
|
||||
local privs = {}
|
||||
for priv, am in SortedPairs(FAdmin.Access.Privileges) do
|
||||
if am <= admin + 1 then privs[priv] = true end
|
||||
end
|
||||
|
||||
local immunity = FAdmin.Access.Groups[FAdmin.Access.ADMIN[admin + 1]].immunity
|
||||
-- Creating and setting a group with a higher rank than one's own
|
||||
if (not plyGroup or immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to assign anyone a usergroup with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
FAdmin.Access.AddGroup(args[2], tonumber(args[3]), privs, immunity) -- Add new group
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Group created")
|
||||
FAdmin.Access.SendGroups()
|
||||
end
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if group and (not plyGroup or group.immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to assign anyone a usergroup with a higher rank than your own")
|
||||
return false
|
||||
end
|
||||
|
||||
if not targets and (string.find(args[1], "^STEAM_[0-9]:[01]:[0-9]+$") or args[1] == "BOT" or (string.find(args[1], "STEAM_") and #args == 6)) then
|
||||
local target, groupname = args[1], args[2]
|
||||
-- The console splits arguments on colons. Very annoying.
|
||||
if args[1] == "STEAM_0" then
|
||||
target = table.concat(args, "", 1, 5)
|
||||
groupname = args[6]
|
||||
end
|
||||
FAdmin.Access.PlayerSetGroup(target, groupname)
|
||||
|
||||
MySQLite.queryValue(string.format("SELECT groupname FROM FAdmin_PlayerGroup WHERE steamid = %s", MySQLite.SQLStr(target)), function(val)
|
||||
CAMI.SignalSteamIDUserGroupChanged(target, val or "user", groupname, "FAdmin")
|
||||
end)
|
||||
FAdmin.Messages.SendMessage(ply, 4, "User access set!")
|
||||
return true, target, groupname
|
||||
elseif not targets then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not IsValid(target) then continue end
|
||||
|
||||
local target_previous_group = target:GetUserGroup()
|
||||
FAdmin.Access.PlayerSetGroup(target, args[2])
|
||||
|
||||
-- An end user changed the usergroup. Register with CAMI
|
||||
CAMI.SignalUserGroupChanged(target, target_previous_group, args[2], "FAdmin")
|
||||
end
|
||||
|
||||
FAdmin.Messages.SendMessage(ply, 4, "User access set!")
|
||||
FAdmin.Messages.FireNotification("setaccess", ply, targets, {args[2]})
|
||||
return true, targets, args[2]
|
||||
end
|
||||
|
||||
--hooks and stuff
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "FAdmin_SetAccess", function(ply)
|
||||
MySQLite.queryValue("SELECT groupname FROM FAdmin_PlayerGroup WHERE steamid = " .. MySQLite.SQLStr(ply:SteamID()) .. ";", function(Group)
|
||||
if not Group then return end
|
||||
ply:SetUserGroup(Group)
|
||||
|
||||
if FAdmin.Access.Groups[Group] then
|
||||
ply:FAdmin_SetGlobal("FAdmin_admin", FAdmin.Access.Groups[Group].ADMIN_ACCESS)
|
||||
end
|
||||
end, function(err) ErrorNoHalt(err) MsgN() end)
|
||||
FAdmin.Access.SendGroups(ply)
|
||||
end)
|
||||
|
||||
local function toggleImmunity(ply, cmd, args)
|
||||
-- ManageGroups privilege because they can handle immunity settings
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManageGroups") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
if not args[1] then FAdmin.Messages.SendMessage(ply, 5, "Invalid argument!") return false end
|
||||
RunConsoleCommand("_FAdmin_immunity", args[1])
|
||||
local OnOff = (tonumber(args[1]) == 1 and "on") or "off"
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), ply:Nick() .. " turned " .. OnOff .. " admin immunity!", "Admin immunity has been turned " .. OnOff, "Turned admin immunity " .. OnOff)
|
||||
|
||||
return true, OnOff
|
||||
end
|
||||
|
||||
|
||||
local function setImmunity(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ManageGroups") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
local group, immunity = args[1], tonumber(args[2])
|
||||
|
||||
if not FAdmin.Access.Groups[group] or not immunity then return false end
|
||||
|
||||
local plyGroup = FAdmin.Access.Groups[ply:EntIndex() == 0 and "superadmin" or ply:GetUserGroup()]
|
||||
|
||||
-- Setting a group with a higher rank than one's own
|
||||
if (not plyGroup or FAdmin.Access.Groups[group].immunity > plyGroup.immunity) and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to change the immunity of a group with a higher rank than your")
|
||||
return false
|
||||
end
|
||||
|
||||
if immunity > plyGroup.immunity and not FAdmin.Access.PlayerIsHost(ply) then
|
||||
FAdmin.Messages.SendMessage(ply, 5, "You're not allowed to set the immunity to any value higher than your own group's immunity")
|
||||
return false
|
||||
end
|
||||
|
||||
FAdmin.Access.Groups[group].immunity = immunity
|
||||
MySQLite.query("REPLACE INTO FAdmin_Immunity VALUES(" .. MySQLite.SQLStr(group) .. ", " .. tonumber(immunity) .. ");")
|
||||
|
||||
FAdmin.Access.SendGroups(ply)
|
||||
|
||||
return true, group, immunity
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["Access"] = function() --Run all functions that depend on other plugins
|
||||
FAdmin.Commands.AddCommand("setroot", FAdmin.Access.SetRoot)
|
||||
FAdmin.Commands.AddCommand("setaccess", FAdmin.Access.SetAccess)
|
||||
|
||||
FAdmin.Commands.AddCommand("AddGroup", AddGroup)
|
||||
|
||||
FAdmin.Commands.AddCommand("AddPrivilege", AddPrivilege)
|
||||
FAdmin.Commands.AddCommand("RemovePrivilege", RemovePrivilege)
|
||||
|
||||
FAdmin.Commands.AddCommand("immunity", toggleImmunity)
|
||||
FAdmin.Commands.AddCommand("SetImmunity", setImmunity)
|
||||
|
||||
FAdmin.SetGlobalSetting("Immunity", GetConVar("_FAdmin_immunity"):GetBool())
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
net.Receive("FAdmin_ReceiveAdminMessage", function(len)
|
||||
local FromPly = net.ReadEntity()
|
||||
local Text = net.ReadString()
|
||||
local Team = FromPly:IsPlayer() and FromPly:Team() or 1
|
||||
local Nick = FromPly:IsPlayer() and FromPly:Nick() or "Console"
|
||||
local prefix = (FAdmin.Access.PlayerHasPrivilege(FromPly, "AdminChat") or FromPly:IsAdmin()) and "[Admin Chat] " or "[To admins] "
|
||||
|
||||
chat.AddNonParsedText(Color(255, 0, 0, 255), prefix, team.GetColor(Team), Nick .. ": ", color_white, Text)
|
||||
end)
|
||||
|
||||
FAdmin.StartHooks["Chatting"] = function()
|
||||
FAdmin.Commands.AddCommand("adminhelp", nil, "<text>")
|
||||
FAdmin.Commands.AddCommand("//", nil, "<text>")
|
||||
|
||||
FAdmin.Access.AddPrivilege("AdminChat", 2)
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
util.AddNetworkString("FAdmin_ReceiveAdminMessage")
|
||||
local function ToAdmins(ply, cmd, args)
|
||||
if not args[1] then return false end
|
||||
|
||||
local text = table.concat(args, " ")
|
||||
local send = {}
|
||||
|
||||
if IsValid(ply) then table.insert(send, ply) end
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if FAdmin.Access.PlayerHasPrivilege(v, "AdminChat") or v:IsAdmin() then
|
||||
table.insert(send, v)
|
||||
end
|
||||
end
|
||||
|
||||
net.Start("FAdmin_ReceiveAdminMessage")
|
||||
net.WriteEntity(ply)
|
||||
net.WriteString(text)
|
||||
net.Send(send)
|
||||
|
||||
return true, text
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["Chatting"] = function()
|
||||
FAdmin.Commands.AddCommand("adminhelp", ToAdmins)
|
||||
FAdmin.Commands.AddCommand("//", ToAdmins)
|
||||
|
||||
FAdmin.Access.AddPrivilege("AdminChat", 2)
|
||||
end
|
||||
@@ -0,0 +1,87 @@
|
||||
local PANEL = {}
|
||||
|
||||
AccessorFunc(PANEL, "gamemodeList", "GamemodeList")
|
||||
AccessorFunc(PANEL, "mapList", "MapList")
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetMouseInputEnabled(true)
|
||||
self:SetKeyboardInputEnabled(false)
|
||||
|
||||
self:SetDeleteOnClose(false)
|
||||
|
||||
self:SetTitle("Change level")
|
||||
self:SetSize(630, ScrH() * 0.8)
|
||||
|
||||
self.gamemodeList = {}
|
||||
self.mapList = {}
|
||||
|
||||
self.catList = vgui.Create("DCategoryList", self)
|
||||
self.catList:Dock(FILL)
|
||||
|
||||
self.topPanel = vgui.Create("DPanel", self)
|
||||
self.topPanel:SetPaintBackground(false)
|
||||
self.topPanel:DockMargin(0, 0, 0, 4)
|
||||
self.topPanel:Dock(TOP)
|
||||
self.gmLabel = vgui.Create("DLabel", self.topPanel)
|
||||
self.gmLabel:SetText("Gamemode:")
|
||||
self.gmLabel:Dock(LEFT)
|
||||
self.gmComboBox = vgui.Create("DComboBox", self.topPanel)
|
||||
self.gmComboBox:Dock(FILL)
|
||||
self.gmComboBox:SetValue("(current)")
|
||||
|
||||
self.bottomPanel = vgui.Create("DPanel", self)
|
||||
self.bottomPanel:SetPaintBackground(false)
|
||||
self.bottomPanel:DockMargin(0, 4, 0, 0)
|
||||
self.bottomPanel:Dock(BOTTOM)
|
||||
self.changeButton = vgui.Create("DButton", self.bottomPanel)
|
||||
self.changeButton:SetText("Change level")
|
||||
self.changeButton:Dock(RIGHT)
|
||||
self.changeButton:SetWidth(100)
|
||||
self.changeButton:SetEnabled(false)
|
||||
self.changeButton.DoClick = function()
|
||||
if not IsValid(self.selectedIconPanel) then return end
|
||||
local _,gmName = self.gmComboBox:GetSelected()
|
||||
local mapName = self.selectedIconPanel:GetText()
|
||||
RunConsoleCommand("_FAdmin", "Changelevel", gmName and gmName or mapName, gmName and mapName)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:Refresh()
|
||||
for _, gmInfo in ipairs(self:GetGamemodeList()) do
|
||||
self.gmComboBox:AddChoice(gmInfo.title, gmInfo.name)
|
||||
end
|
||||
self.gmComboBox:SetValue("(current)")
|
||||
|
||||
for catName, maps in pairs(self:GetMapList()) do
|
||||
local cat = self.catList:Add(catName)
|
||||
local iconLayout = vgui.Create("DIconLayout")
|
||||
iconLayout:SetSpaceX(5)
|
||||
iconLayout:SetSpaceY(5)
|
||||
for _, map in ipairs(maps) do
|
||||
local icon = iconLayout:Add("FAdmin_MapIcon")
|
||||
icon:SetText(map)
|
||||
icon:SetDark(true)
|
||||
local mat = Material("maps/thumb/" .. map .. ".png")
|
||||
if mat:IsError() then mat = Material("maps/thumb/noicon.png") end
|
||||
icon:SetMaterial(mat)
|
||||
local onToggled = icon.OnToggled
|
||||
icon.OnToggled = function(iconSelf, selected)
|
||||
onToggled(iconSelf, selected)
|
||||
if IsValid(self.selectedIconPanel) then
|
||||
if selected and self.selectedIconPanel ~= iconSelf then
|
||||
self.selectedIconPanel:Toggle()
|
||||
elseif not selected and self.selectedIconPanel == iconSelf then
|
||||
self.selectedIconPanel = nil
|
||||
self.changeButton:SetEnabled(false)
|
||||
return
|
||||
end
|
||||
end
|
||||
self.selectedIconPanel = iconSelf
|
||||
self.changeButton:SetEnabled(true)
|
||||
end
|
||||
end
|
||||
cat:SetContents(iconLayout)
|
||||
end
|
||||
end
|
||||
|
||||
vgui.Register("FAdmin_Changelevel", PANEL, "DFrame")
|
||||
@@ -0,0 +1,46 @@
|
||||
local mapList = {}
|
||||
local gamemodeList = {}
|
||||
net.Receive("FAdmin_ChangelevelInfo", function(len)
|
||||
mapList = {}
|
||||
local mapLen = net.ReadUInt(16)
|
||||
|
||||
for i = 1, mapLen, 1 do
|
||||
local cat = net.ReadString()
|
||||
mapList[cat] = {}
|
||||
local catLen = net.ReadUInt(16)
|
||||
|
||||
for j = 1, catLen, 1 do
|
||||
mapList[cat][j] = net.ReadString()
|
||||
end
|
||||
end
|
||||
|
||||
gamemodeList = {}
|
||||
local gmLen = net.ReadUInt(16)
|
||||
|
||||
for i = 1, gmLen, 1 do
|
||||
gamemodeList[i] = {
|
||||
name = net.ReadString(),
|
||||
title = net.ReadString()
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
local Changelevel
|
||||
FAdmin.StartHooks["ChangeLevel"] = function()
|
||||
FAdmin.Access.AddPrivilege("changelevel", 2)
|
||||
FAdmin.Commands.AddCommand("changelevel", "[gamemode]", "<map>")
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerAction("Changelevel", "icon16/world.png", Color(155, 0, 0, 255), function() return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "changelevel") end,
|
||||
function(ply, button)
|
||||
local refresh = not Changelevel or table.Count(Changelevel:GetMapList()) ~= table.Count(mapList)
|
||||
Changelevel = Changelevel or vgui.Create("FAdmin_Changelevel")
|
||||
if refresh then
|
||||
Changelevel:SetGamemodeList(gamemodeList)
|
||||
Changelevel:SetMapList(mapList)
|
||||
Changelevel:Refresh()
|
||||
end
|
||||
Changelevel:SetVisible(true)
|
||||
Changelevel:Center()
|
||||
Changelevel:MakePopup()
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.BaseClass.Init(self)
|
||||
self:SetPaintBackground(true)
|
||||
self:SetIsToggle(true)
|
||||
self:SetSize(96, 110)
|
||||
end
|
||||
|
||||
function PANEL:Paint(w, h)
|
||||
if self.m_bToggle then
|
||||
surface.SetDrawColor(255, 155, 20, 255)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function PANEL:UpdateColours(skin)
|
||||
return self:SetTextStyleColor(skin.Colours.Button.Normal)
|
||||
end
|
||||
|
||||
function PANEL:OnToggled(selected)
|
||||
self:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
function PANEL:OnMousePressed(code)
|
||||
DButton.OnMousePressed(self, code)
|
||||
end
|
||||
|
||||
function PANEL:OnMouseReleased(code)
|
||||
DButton.OnMouseReleased(self, code)
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
self.m_Image:SetPos(0, 0)
|
||||
local w,h = self:GetSize()
|
||||
h = h - 14
|
||||
self.m_Image:SetSize(w, h)
|
||||
self:SetTextInset(5, w / 2)
|
||||
DLabel.PerformLayout(self)
|
||||
end
|
||||
|
||||
vgui.Register("FAdmin_MapIcon", PANEL, "DImageButton")
|
||||
@@ -0,0 +1,266 @@
|
||||
util.AddNetworkString("FAdmin_ChangelevelInfo")
|
||||
|
||||
local function ChangeLevel(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "changelevel") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
local map = args[2] or args[1] -- Changelevel gamemode map OR changelevel map
|
||||
local gameMode = args[2] and args[1]
|
||||
|
||||
if gameMode then
|
||||
RunConsoleCommand("gamemode", gameMode)
|
||||
end
|
||||
|
||||
RunConsoleCommand("changelevel", map)
|
||||
|
||||
return true, map, gameMode
|
||||
end
|
||||
|
||||
local mapNames = {}
|
||||
local mapPatterns = {}
|
||||
|
||||
local ignorePatterns = {
|
||||
"^asi-", "^background", "^c[%d]m", "^devtest", "^ep1_background", "^ep2_background", "^mp_coop_", "^sp_a", "^styleguide"
|
||||
}
|
||||
|
||||
local ignoreMaps = {
|
||||
-- Prefixes
|
||||
["ddd_"] = true,
|
||||
["sdk_"] = true,
|
||||
["test_"] = true,
|
||||
["vst_"] = true,
|
||||
-- Maps
|
||||
["c4a1y"] = true,
|
||||
["cp_docks"] = true,
|
||||
["cp_parkour"] = true,
|
||||
["cp_sequence"] = true,
|
||||
["cp_terrace"] = true,
|
||||
["cp_test"] = true,
|
||||
["credits"] = true,
|
||||
["curling_stadium"] = true,
|
||||
["d2_coast_02"] = true,
|
||||
["d3_c17_02_camera"] = true,
|
||||
["duel_"] = true,
|
||||
["e1912"] = true,
|
||||
["ep1_citadel_00_demo"] = true,
|
||||
["ffa_community"] = true,
|
||||
["free_"] = true,
|
||||
["intro"] = true,
|
||||
["lobby"] = true,
|
||||
["practice_box"] = true,
|
||||
["test"] = true,
|
||||
["tut_training"] = true,
|
||||
["tutorial_standards"] = true,
|
||||
["tutorial_standards_vs"] = true
|
||||
}
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "FAdmin_ChangelevelInfo", function(ply)
|
||||
local mapList = {}
|
||||
local maps = file.Find("maps/*.bsp", "GAME")
|
||||
|
||||
for _, v in ipairs(maps) do
|
||||
local name = string.lower(string.gsub(v, "%.bsp$", ""))
|
||||
if ignoreMaps[name] then continue end
|
||||
|
||||
local prefix = string.match(name, "^(.-_)")
|
||||
if ignoreMaps[prefix] then continue end
|
||||
|
||||
for _, ignore in ipairs(ignorePatterns) do
|
||||
if string.find(name, ignore) then
|
||||
goto mapContinue
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if the map has a simple name or prefix
|
||||
local mapCategory = mapNames[name] or mapNames[prefix]
|
||||
|
||||
-- Check if the map has an embedded prefix, or is TTT/Sandbox
|
||||
if not mapCategory then
|
||||
for pattern, category in pairs(mapPatterns) do
|
||||
if string.find(name, pattern) then
|
||||
mapCategory = category
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Throw all uncategorized maps into Other
|
||||
mapCategory = mapCategory or "Other"
|
||||
-- Don't show CS:GO maps
|
||||
if mapCategory == "Counter-Strike" and not file.Exists("maps/" .. name .. ".bsp", "cstrike") then
|
||||
continue
|
||||
end
|
||||
|
||||
if not mapList[mapCategory] then
|
||||
mapList[mapCategory] = {}
|
||||
end
|
||||
|
||||
table.insert(mapList[mapCategory], name)
|
||||
::mapContinue::
|
||||
end
|
||||
|
||||
local gamemodeList = engine.GetGamemodes()
|
||||
net.Start("FAdmin_ChangelevelInfo")
|
||||
net.WriteUInt(table.Count(mapList), 16) -- 65536 should be enough
|
||||
for cat, mps in pairs(mapList) do
|
||||
net.WriteString(cat)
|
||||
net.WriteUInt(#mps, 16)
|
||||
for _, map in pairs(mps) do
|
||||
net.WriteString(map)
|
||||
end
|
||||
end
|
||||
net.WriteUInt(#gamemodeList, 16)
|
||||
for _, gmInfo in ipairs(gamemodeList) do
|
||||
net.WriteString(gmInfo.name)
|
||||
net.WriteString(gmInfo.title)
|
||||
end
|
||||
net.Send(ply)
|
||||
end)
|
||||
|
||||
FAdmin.StartHooks["ChangeLevel"] = function()
|
||||
FAdmin.Commands.AddCommand("changelevel", ChangeLevel)
|
||||
|
||||
FAdmin.Access.AddPrivilege("changelevel", 2)
|
||||
|
||||
mapNames = {}
|
||||
mapPatterns = {}
|
||||
|
||||
mapNames["aoc_"] = "Age of Chivalry"
|
||||
|
||||
mapNames["ar_"] = "Counter-Strike"
|
||||
mapNames["cs_"] = "Counter-Strike"
|
||||
mapNames["de_"] = "Counter-Strike"
|
||||
mapNames["es_"] = "Counter-Strike"
|
||||
mapNames["fy_"] = "Counter-Strike"
|
||||
mapNames["gd_"] = "Counter-Strike"
|
||||
mapNames["training1"] = "Counter-Strike"
|
||||
|
||||
mapNames["dod_"] = "Day Of Defeat"
|
||||
|
||||
mapNames["de_dam"] = "DIPRIP"
|
||||
mapNames["dm_city"] = "DIPRIP"
|
||||
mapNames["dm_refinery"] = "DIPRIP"
|
||||
mapNames["dm_supermarket"] = "DIPRIP"
|
||||
mapNames["dm_village"] = "DIPRIP"
|
||||
mapNames["ur_city"] = "DIPRIP"
|
||||
mapNames["ur_refinery"] = "DIPRIP"
|
||||
mapNames["ur_supermarket"] = "DIPRIP"
|
||||
mapNames["ur_village"] = "DIPRIP"
|
||||
|
||||
mapNames["dys_"] = "Dystopia"
|
||||
mapNames["pb_dojo"] = "Dystopia"
|
||||
mapNames["pb_rooftop"] = "Dystopia"
|
||||
mapNames["pb_round"] = "Dystopia"
|
||||
mapNames["pb_urbandome"] = "Dystopia"
|
||||
mapNames["sav_dojo6"] = "Dystopia"
|
||||
mapNames["varena"] = "Dystopia"
|
||||
|
||||
mapNames["d1_"] = "Half-Life 2"
|
||||
mapNames["d2_"] = "Half-Life 2"
|
||||
mapNames["d3_"] = "Half-Life 2"
|
||||
|
||||
mapNames["dm_"] = "Half-Life 2: Deathmatch"
|
||||
mapNames["halls3"] = "Half-Life 2: Deathmatch"
|
||||
|
||||
mapNames["ep1_"] = "Half-Life 2: Episode 1"
|
||||
mapNames["ep2_"] = "Half-Life 2: Episode 2"
|
||||
mapNames["ep3_"] = "Half-Life 2: Episode 3"
|
||||
|
||||
mapNames["d2_lostcoast"] = "Half-Life 2: Lost Coast"
|
||||
|
||||
mapPatterns["^c[%d]a"] = "Half-Life"
|
||||
mapPatterns["^t0a"] = "Half-Life"
|
||||
|
||||
mapNames["boot_camp"] = "Half-Life Deathmatch"
|
||||
mapNames["bounce"] = "Half-Life Deathmatch"
|
||||
mapNames["crossfire"] = "Half-Life Deathmatch"
|
||||
mapNames["datacore"] = "Half-Life Deathmatch"
|
||||
mapNames["frenzy"] = "Half-Life Deathmatch"
|
||||
mapNames["lambda_bunker"] = "Half-Life Deathmatch"
|
||||
mapNames["rapidcore"] = "Half-Life Deathmatch"
|
||||
mapNames["snarkpit"] = "Half-Life Deathmatch"
|
||||
mapNames["stalkyard"] = "Half-Life Deathmatch"
|
||||
mapNames["subtransit"] = "Half-Life Deathmatch"
|
||||
mapNames["undertow"] = "Half-Life Deathmatch"
|
||||
|
||||
mapNames["ins_"] = "Insurgency"
|
||||
|
||||
mapNames["l4d_"] = "Left 4 Dead"
|
||||
|
||||
mapNames["clocktower"] = "Nuclear Dawn"
|
||||
mapNames["coast"] = "Nuclear Dawn"
|
||||
mapNames["downtown"] = "Nuclear Dawn"
|
||||
mapNames["gate"] = "Nuclear Dawn"
|
||||
mapNames["hydro"] = "Nuclear Dawn"
|
||||
mapNames["metro"] = "Nuclear Dawn"
|
||||
mapNames["metro_training"] = "Nuclear Dawn"
|
||||
mapNames["oasis"] = "Nuclear Dawn"
|
||||
mapNames["oilfield"] = "Nuclear Dawn"
|
||||
mapNames["silo"] = "Nuclear Dawn"
|
||||
mapNames["sk_metro"] = "Nuclear Dawn"
|
||||
mapNames["training"] = "Nuclear Dawn"
|
||||
|
||||
mapNames["bt_"] = "Pirates, Vikings, & Knights II"
|
||||
mapNames["lts_"] = "Pirates, Vikings, & Knights II"
|
||||
mapNames["te_"] = "Pirates, Vikings, & Knights II"
|
||||
mapNames["tw_"] = "Pirates, Vikings, & Knights II"
|
||||
|
||||
mapNames["escape_"] = "Portal"
|
||||
mapNames["testchmb_"] = "Portal"
|
||||
|
||||
mapNames["achievement_"] = "Team Fortress 2"
|
||||
mapNames["arena_"] = "Team Fortress 2"
|
||||
mapNames["cp_"] = "Team Fortress 2"
|
||||
mapNames["ctf_"] = "Team Fortress 2"
|
||||
mapNames["itemtest"] = "Team Fortress 2"
|
||||
mapNames["koth_"] = "Team Fortress 2"
|
||||
mapNames["mvm_"] = "Team Fortress 2"
|
||||
mapNames["pl_"] = "Team Fortress 2"
|
||||
mapNames["plr_"] = "Team Fortress 2"
|
||||
mapNames["rd_"] = "Team Fortress 2"
|
||||
mapNames["pd_"] = "Team Fortress 2"
|
||||
mapNames["sd_"] = "Team Fortress 2"
|
||||
mapNames["tc_"] = "Team Fortress 2"
|
||||
mapNames["tr_"] = "Team Fortress 2"
|
||||
mapNames["trade_"] = "Team Fortress 2"
|
||||
mapNames["pass_"] = "Team Fortress 2"
|
||||
|
||||
mapNames["zpa_"] = "Zombie Panic! Source"
|
||||
mapNames["zpl_"] = "Zombie Panic! Source"
|
||||
mapNames["zpo_"] = "Zombie Panic! Source"
|
||||
mapNames["zps_"] = "Zombie Panic! Source"
|
||||
|
||||
mapNames["bhop_"] = "Bunny Hop"
|
||||
mapNames["cinema_"] = "Cinema"
|
||||
mapNames["theater_"] = "Cinema"
|
||||
mapNames["xc_"] = "Climb"
|
||||
mapNames["deathrun_"] = "Deathrun"
|
||||
mapNames["dr_"] = "Deathrun"
|
||||
mapNames["fm_"] = "Flood"
|
||||
mapNames["gmt_"] = "GMod Tower"
|
||||
mapNames["gg_"] = "Gun Game"
|
||||
mapNames["scoutzknivez"] = "Gun Game"
|
||||
mapNames["ba_"] = "Jailbreak"
|
||||
mapNames["jail_"] = "Jailbreak"
|
||||
mapNames["jb_"] = "Jailbreak"
|
||||
mapNames["mg_"] = "Minigames"
|
||||
mapNames["pw_"] = "Pirate Ship Wars"
|
||||
mapNames["ph_"] = "Prop Hunt"
|
||||
mapNames["rp_"] = "Roleplay"
|
||||
mapNames["slb_"] = "Sled Build"
|
||||
mapNames["sb_"] = "Spacebuild"
|
||||
mapNames["slender_"] = "Stop it Slender"
|
||||
mapNames["gms_"] = "Stranded"
|
||||
mapNames["surf_"] = "Surf"
|
||||
mapNames["ts_"] = "The Stalker"
|
||||
mapNames["zm_"] = "Zombie Survival"
|
||||
mapNames["zombiesurvival_"] = "Zombie Survival"
|
||||
mapNames["zs_"] = "Zombie Survival"
|
||||
|
||||
for _, gm in ipairs(engine.GetGamemodes()) do
|
||||
if gm.maps ~= "" then
|
||||
for _, pattern in ipairs(string.Split(gm.maps, "|")) do
|
||||
-- When in doubt, just try to match it with string.find later
|
||||
mapPatterns[string.lower(pattern)] = gm.title or "Unnammed Gamemode"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
local function SortedPairsByFunction(Table, Sorted, SortDown)
|
||||
local CopyTable = {}
|
||||
for _, v in pairs(Table) do
|
||||
table.insert(CopyTable, {NAME = tostring(v:Nick()), PLY = v})
|
||||
end
|
||||
table.SortByMember(CopyTable, "NAME", SortDown)
|
||||
|
||||
local SortedTable = {}
|
||||
for _, v in ipairs(CopyTable) do
|
||||
if not IsValid(v.PLY) or not v.PLY[Sorted] then continue end
|
||||
local SortBy = (Sorted ~= "Team" and v.PLY[Sorted](v.PLY)) or (v.PLY:getDarkRPVar("job") or team.GetName(v.PLY[Sorted](v.PLY)))
|
||||
SortedTable[SortBy] = SortedTable[SortBy] or {}
|
||||
table.insert(SortedTable[SortBy], v.PLY)
|
||||
end
|
||||
|
||||
local SecondSort = {}
|
||||
for _, v in SortedPairs(SortedTable, SortDown) do
|
||||
table.insert(SecondSort, v)
|
||||
end
|
||||
|
||||
CopyTable = {}
|
||||
for _, v in ipairs(SecondSort) do
|
||||
for _, b in pairs(v) do
|
||||
table.insert(CopyTable, b)
|
||||
end
|
||||
end
|
||||
|
||||
return ipairs(CopyTable)
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Main.PlayerListView(Sorted, SortDown)
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:Clear(true)
|
||||
for _, ply in SortedPairsByFunction(player.GetAll(), Sorted, SortDown) do
|
||||
local Row = vgui.Create("FadminPlayerRow")
|
||||
Row:SetPlayer(ply)
|
||||
Row:Dock(TOP)
|
||||
Row:InvalidateLayout()
|
||||
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:AddItem(Row)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,308 @@
|
||||
FAdmin.PlayerIcon = {}
|
||||
FAdmin.PlayerIcon.RightClickOptions = {}
|
||||
|
||||
function FAdmin.PlayerIcon.AddRightClickOption(name, func)
|
||||
FAdmin.PlayerIcon.RightClickOptions[name] = func
|
||||
end
|
||||
|
||||
-- FAdminPanelList
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self.Padding = 5
|
||||
end
|
||||
|
||||
function PANEL:SizeToContents()
|
||||
local w, h = self:GetSize()
|
||||
|
||||
-- Fix size of w to have the same size as the scoreboard
|
||||
w = math.Clamp(w, ScrW() * 0.9, ScrW() * 0.9)
|
||||
h = math.Min(h, ScrH() * 0.95)
|
||||
|
||||
-- It fucks up when there's only one icon in
|
||||
if #self:GetChildren() == 1 then
|
||||
h = math.Max(0, 120)
|
||||
end
|
||||
|
||||
self:SetSize(w, h)
|
||||
self:PerformLayout()
|
||||
end
|
||||
|
||||
function PANEL:Paint()
|
||||
end
|
||||
|
||||
derma.DefineControl("FAdminPanelList", "DPanellist adapted for FAdmin", PANEL, "DPanelList")
|
||||
|
||||
-- FAdminPlayerCatagoryHeader
|
||||
local PANEL2 = {}
|
||||
|
||||
function PANEL2:PerformLayout()
|
||||
self:SetFont("Trebuchet24")
|
||||
end
|
||||
|
||||
derma.DefineControl("FAdminPlayerCatagoryHeader", "DCatagoryCollapse header adapted for FAdmin", PANEL2, "DCategoryHeader")
|
||||
|
||||
-- FAdminPlayerCatagory
|
||||
local PANEL3 = {}
|
||||
|
||||
function PANEL3:Init()
|
||||
if self.Header then
|
||||
self.Header:Remove() -- the old header is still there don't ask me why
|
||||
end
|
||||
self.Header = vgui.Create("FAdminPlayerCatagoryHeader", self)
|
||||
self.Header:SetSize(20, 25)
|
||||
self:SetPadding(5)
|
||||
self.Header:Dock(TOP)
|
||||
|
||||
self:SetExpanded(true)
|
||||
self:SetMouseInputEnabled(true)
|
||||
|
||||
self:SetAnimTime(0.2)
|
||||
self.animSlide = Derma_Anim("Anim", self, self.AnimSlide)
|
||||
|
||||
self:SetPaintBackgroundEnabled(true)
|
||||
|
||||
end
|
||||
|
||||
function PANEL3:Paint()
|
||||
if self.CatagoryColor then
|
||||
draw.RoundedBox(4, 0, 0, self:GetWide(), self.Header:GetTall(), self.CatagoryColor)
|
||||
end
|
||||
end
|
||||
|
||||
derma.DefineControl("FAdminPlayerCatagory", "DCatagoryCollapse adapted for FAdmin", PANEL3, "DCollapsibleCategory")
|
||||
|
||||
-- FAdmin player row (from the sandbox player row)
|
||||
PANEL = {}
|
||||
|
||||
local PlayerRowSize = CreateClientConVar("FAdmin_PlayerRowSize", 30, true, false)
|
||||
function PANEL:Init()
|
||||
self.Size = PlayerRowSize:GetInt()
|
||||
|
||||
self.lblName = vgui.Create("DLabel", self)
|
||||
self.lblFrags = vgui.Create("DLabel", self)
|
||||
self.lblTeam = vgui.Create("DLabel", self)
|
||||
self.lblDeaths = vgui.Create("DLabel", self)
|
||||
self.lblPing = vgui.Create("DLabel", self)
|
||||
self.lblWanted = vgui.Create("DLabel", self)
|
||||
|
||||
-- If you don't do this it'll block your clicks
|
||||
self.lblName:SetMouseInputEnabled(false)
|
||||
self.lblTeam:SetMouseInputEnabled(false)
|
||||
self.lblFrags:SetMouseInputEnabled(false)
|
||||
self.lblDeaths:SetMouseInputEnabled(false)
|
||||
self.lblPing:SetMouseInputEnabled(false)
|
||||
self.lblWanted:SetMouseInputEnabled(false)
|
||||
|
||||
self.lblName:SetColor(Color(255,255,255,200))
|
||||
self.lblTeam:SetColor(Color(255,255,255,200))
|
||||
self.lblFrags:SetColor(Color(255,255,255,200))
|
||||
self.lblDeaths:SetColor(Color(255,255,255,200))
|
||||
self.lblPing:SetColor(Color(255,255,255,200))
|
||||
self.lblWanted:SetColor(Color(255,255,255,200))
|
||||
|
||||
self.imgAvatar = vgui.Create("AvatarImage", self)
|
||||
|
||||
self:SetCursor("hand")
|
||||
end
|
||||
|
||||
function PANEL:Paint()
|
||||
if not IsValid(self.Player) then return end
|
||||
|
||||
self.Size = PlayerRowSize:GetInt()
|
||||
self.imgAvatar:SetSize(self.Size - 4, self.Size - 4)
|
||||
|
||||
local color = Color(100, 150, 245, 255)
|
||||
|
||||
|
||||
if GAMEMODE.Name == "Sandbox" then
|
||||
color = Color(100, 150, 245, 255)
|
||||
if self.Player:Team() == TEAM_CONNECTING then
|
||||
color = Color(200, 120, 50, 255)
|
||||
elseif self.Player:IsAdmin() then
|
||||
color = Color(30, 200, 50, 255)
|
||||
end
|
||||
|
||||
if self.Player:GetFriendStatus() == "friend" then
|
||||
color = Color(236, 181, 113, 255)
|
||||
end
|
||||
else
|
||||
color = team.GetColor(self.Player:Team())
|
||||
end
|
||||
|
||||
local hooks = hook.GetTable().FAdmin_PlayerRowColour
|
||||
if hooks then
|
||||
for _, v in pairs(hooks) do
|
||||
color = (v and v(self.Player, color)) or color
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
draw.RoundedBox(4, 0, 0, self:GetWide(), self.Size, color)
|
||||
|
||||
surface.SetTexture(0)
|
||||
if self.Player == LocalPlayer() or self.Player:GetFriendStatus() == "friend" then
|
||||
surface.SetDrawColor(255, 255, 255, 50 + math.sin(RealTime() * 2) * 50)
|
||||
end
|
||||
surface.DrawTexturedRect(0, 0, self:GetWide(), self.Size)
|
||||
return true
|
||||
end
|
||||
|
||||
function PANEL:SetPlayer(ply)
|
||||
self.Player = ply
|
||||
|
||||
self.imgAvatar:SetPlayer(ply)
|
||||
|
||||
self:UpdatePlayerData()
|
||||
end
|
||||
|
||||
function PANEL:UpdatePlayerData()
|
||||
if not self.Player then return end
|
||||
if not self.Player:IsValid() then return end
|
||||
|
||||
self.lblName:SetText(DarkRP.deLocalise(self.Player:Nick()))
|
||||
self.lblTeam:SetText((self.Player.DarkRPVars and DarkRP.deLocalise(self.Player:getDarkRPVar("job") or "")) or team.GetName(self.Player:Team()))
|
||||
self.lblTeam:SizeToContents()
|
||||
self.lblFrags:SetText(self.Player:Frags())
|
||||
self.lblDeaths:SetText(self.Player:Deaths())
|
||||
self.lblPing:SetText(self.Player:Ping())
|
||||
self.lblWanted:SetText(self.Player:isWanted() and DarkRP.getPhrase("Wanted_text") or "")
|
||||
end
|
||||
|
||||
function PANEL:ApplySchemeSettings()
|
||||
self.lblName:SetFont("ScoreboardPlayerNameBig")
|
||||
self.lblTeam:SetFont("ScoreboardPlayerNameBig")
|
||||
self.lblFrags:SetFont("ScoreboardPlayerName")
|
||||
self.lblDeaths:SetFont("ScoreboardPlayerName")
|
||||
self.lblPing:SetFont("ScoreboardPlayerName")
|
||||
self.lblWanted:SetFont("ScoreboardPlayerNameBig")
|
||||
|
||||
self.lblName:SetFGColor(color_white)
|
||||
self.lblTeam:SetFGColor(color_white)
|
||||
self.lblFrags:SetFGColor(color_white)
|
||||
self.lblDeaths:SetFGColor(color_white)
|
||||
self.lblPing:SetFGColor(color_white)
|
||||
self.lblWanted:SetFGColor(color_white)
|
||||
end
|
||||
|
||||
function PANEL:DoClick(x, y)
|
||||
if not IsValid(self.Player) then self:Remove() return end
|
||||
FAdmin.ScoreBoard.ChangeView("Player", self.Player)
|
||||
end
|
||||
|
||||
function PANEL:DoRightClick()
|
||||
if table.IsEmpty(FAdmin.PlayerIcon.RightClickOptions) then return end
|
||||
local menu = DermaMenu()
|
||||
|
||||
menu:SetPos(gui.MouseX(), gui.MouseY())
|
||||
|
||||
for Name, func in SortedPairs(FAdmin.PlayerIcon.RightClickOptions) do
|
||||
menu:AddOption(Name, function() if IsValid(self.Player) then func(self.Player, self) end end)
|
||||
end
|
||||
|
||||
menu:Open()
|
||||
end
|
||||
|
||||
function PANEL:Think()
|
||||
if not self.PlayerUpdate or self.PlayerUpdate < CurTime() then
|
||||
self.PlayerUpdate = CurTime() + 0.5
|
||||
self:UpdatePlayerData()
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
self.imgAvatar:SetPos(2, 2)
|
||||
self.imgAvatar:SetSize(32, 32)
|
||||
|
||||
self:SetSize(self:GetWide(), self.Size)
|
||||
|
||||
self.lblName:SizeToContents()
|
||||
self.lblName:SetPos(24, 2)
|
||||
self.lblName:MoveRightOf(self.imgAvatar, 8)
|
||||
|
||||
local COLUMN_SIZE = 75
|
||||
|
||||
self.lblPing:SetPos(self:GetWide() - COLUMN_SIZE * 0.4, 0)
|
||||
self.lblDeaths:SetPos(self:GetWide() - COLUMN_SIZE * 1.4, 0)
|
||||
self.lblFrags:SetPos(self:GetWide() - COLUMN_SIZE * 2.4, 0)
|
||||
|
||||
self.lblTeam:SetPos(self:GetWide() / 2 - (0.5 * self.lblTeam:GetWide()))
|
||||
|
||||
self.lblWanted:SizeToContents()
|
||||
self.lblWanted:SetPos(math.floor(self:GetWide() / 4), 2)
|
||||
end
|
||||
vgui.Register("FadminPlayerRow", PANEL, "Button")
|
||||
|
||||
-- FAdminActionButton
|
||||
local PANEL6 = {}
|
||||
|
||||
function PANEL6:Init()
|
||||
self:SetDrawBackground(false)
|
||||
self:SetDrawBorder(false)
|
||||
self:SetStretchToFit(false)
|
||||
self:SetSize(120, 40)
|
||||
|
||||
self.TextLabel = vgui.Create("DLabel", self)
|
||||
self.TextLabel:SetColor(Color(200,200,200,200))
|
||||
self.TextLabel:SetFont("Roboto20")
|
||||
|
||||
self.m_Image2 = vgui.Create("DImage", self)
|
||||
|
||||
self.BorderColor = Color(190,40,0,255)
|
||||
end
|
||||
|
||||
function PANEL6:SetText(text)
|
||||
self.TextLabel:SetText(text)
|
||||
self.TextLabel:SizeToContents()
|
||||
|
||||
self:SetWide(self.TextLabel:GetWide() + 44)
|
||||
end
|
||||
|
||||
function PANEL6:PerformLayout()
|
||||
self.m_Image:SetSize(32,32)
|
||||
self.m_Image:SetPos(4,4)
|
||||
|
||||
self.m_Image2:SetSize(32, 32)
|
||||
self.m_Image2:SetPos(4,4)
|
||||
|
||||
self.TextLabel:SetPos(38, 8)
|
||||
end
|
||||
|
||||
function PANEL6:SetImage2(Mat, bckp)
|
||||
self.m_Image2:SetImage(Mat, bckp)
|
||||
end
|
||||
|
||||
function PANEL6:SetBorderColor(Col)
|
||||
self.BorderColor = Col or Color(190,40,0,255)
|
||||
end
|
||||
|
||||
function PANEL6:Paint()
|
||||
local BorderColor = self.BorderColor
|
||||
if self.Hovered then
|
||||
BorderColor = Color(math.Min(BorderColor.r + 40, 255), math.Min(BorderColor.g + 40, 255), math.Min(BorderColor.b + 40, 255), BorderColor.a)
|
||||
end
|
||||
if self.Depressed then
|
||||
BorderColor = color_transparent
|
||||
end
|
||||
draw.RoundedBox(4, 0, 0, self:GetWide(), self:GetTall(), BorderColor)
|
||||
draw.RoundedBox(4, 2, 2, self:GetWide() - 4, self:GetTall() - 4, Color(40, 40, 40, 255))
|
||||
end
|
||||
|
||||
function PANEL6:OnMousePressed(mouse)
|
||||
if self:GetDisabled() then return end
|
||||
|
||||
self.m_Image:SetSize(24,24)
|
||||
self.m_Image:SetPos(8,8)
|
||||
self.Depressed = true
|
||||
end
|
||||
|
||||
function PANEL6:OnMouseReleased(mouse)
|
||||
if self:GetDisabled() then return end
|
||||
|
||||
self.m_Image:SetSize(32,32)
|
||||
self.m_Image:SetPos(4,4)
|
||||
self.Depressed = false
|
||||
self:DoClick()
|
||||
end
|
||||
|
||||
derma.DefineControl("FAdminActionButton", "Button for doing actions", PANEL6, "DImageButton")
|
||||
@@ -0,0 +1,146 @@
|
||||
local OverrideScoreboard = CreateClientConVar("FAdmin_OverrideScoreboard", 0, true, false) -- Set if it's a scoreboard or not
|
||||
|
||||
function FAdmin.ScoreBoard.ChangeView(newView, ...)
|
||||
if FAdmin.ScoreBoard.CurrentView == newView or not FAdmin.ScoreBoard.Visible then return end
|
||||
|
||||
for _, v in pairs(FAdmin.ScoreBoard[FAdmin.ScoreBoard.CurrentView].Controls) do
|
||||
v:SetVisible(false)
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.CurrentView = newView
|
||||
FAdmin.ScoreBoard[newView].Show(...)
|
||||
FAdmin.ScoreBoard.ChangeGmodLogo(FAdmin.ScoreBoard[newView].Logo)
|
||||
|
||||
FAdmin.ScoreBoard.Controls.BackButton = FAdmin.ScoreBoard.Controls.BackButton or vgui.Create("DButton")
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetPos(FAdmin.ScoreBoard.X, FAdmin.ScoreBoard.Y)
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetText("")
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetTooltip("Click me to go back!")
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetCursor("hand")
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetSize(100,90)
|
||||
FAdmin.ScoreBoard.Controls.BackButton:SetZPos(999)
|
||||
|
||||
function FAdmin.ScoreBoard.Controls.BackButton:DoClick()
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
end
|
||||
FAdmin.ScoreBoard.Controls.BackButton.Paint = function() end
|
||||
end
|
||||
|
||||
--"fadmin/back", gui/gmod_tool
|
||||
local GmodLogo, TempGmodLogo, GmodLogoColor = surface.GetTextureID("gui/gmod_logo"), surface.GetTextureID("gui/gmod_logo"), color_white
|
||||
function FAdmin.ScoreBoard.ChangeGmodLogo(new)
|
||||
if surface.GetTextureID(new) == TempGmodLogo then return end
|
||||
TempGmodLogo = surface.GetTextureID(new)
|
||||
for i = 0, 0.5, 0.01 do
|
||||
timer.Simple(i, function() GmodLogoColor = Color(255,255,255,GmodLogoColor.a-5.1) end)
|
||||
end
|
||||
timer.Simple(0.5, function() GmodLogo = surface.GetTextureID(new) end)
|
||||
for i = 0.5, 1, 0.01 do
|
||||
timer.Simple(i, function()
|
||||
GmodLogoColor = Color(255, 255, 255, GmodLogoColor.a + 5.1)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Background()
|
||||
surface.SetDrawColor(0,0,0,200)
|
||||
surface.DrawRect(FAdmin.ScoreBoard.X, FAdmin.ScoreBoard.Y, FAdmin.ScoreBoard.Width, FAdmin.ScoreBoard.Height)
|
||||
|
||||
surface.SetTexture(GmodLogo)
|
||||
surface.SetDrawColor(255,255,255,GmodLogoColor.a)
|
||||
surface.DrawTexturedRect(FAdmin.ScoreBoard.X - 20, FAdmin.ScoreBoard.Y, 128, 128)
|
||||
end
|
||||
|
||||
|
||||
function FAdmin.ScoreBoard.DrawScoreBoard()
|
||||
if (input.IsMouseDown(MOUSE_4) or input.IsKeyDown(KEY_BACKSPACE)) and not FAdmin.ScoreBoard.DontGoBack then
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
elseif FAdmin.ScoreBoard.DontGoBack then
|
||||
FAdmin.ScoreBoard.DontGoBack = input.IsMouseDown(MOUSE_4) or input.IsKeyDown(KEY_BACKSPACE)
|
||||
end
|
||||
FAdmin.ScoreBoard.Background()
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.ShowScoreBoard()
|
||||
FAdmin.ScoreBoard.Visible = true
|
||||
FAdmin.ScoreBoard.DontGoBack = input.IsMouseDown(MOUSE_4) or input.IsKeyDown(KEY_BACKSPACE)
|
||||
|
||||
FAdmin.ScoreBoard.Controls.Hostname = FAdmin.ScoreBoard.Controls.Hostname or vgui.Create("DLabel")
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SetText(DarkRP.deLocalise(GetHostName()))
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SetFont("ScoreboardHeader")
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SetColor(Color(200,200,200,200))
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SetPos(FAdmin.ScoreBoard.X + 90, FAdmin.ScoreBoard.Y + 20)
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SizeToContents()
|
||||
FAdmin.ScoreBoard.Controls.Hostname:SetVisible(true)
|
||||
|
||||
FAdmin.ScoreBoard.Controls.Description = FAdmin.ScoreBoard.Controls.Description or vgui.Create("DLabel")
|
||||
FAdmin.ScoreBoard.Controls.Description:SetText(string.format("%s\n%s", GAMEMODE.Name, GAMEMODE.Author))
|
||||
FAdmin.ScoreBoard.Controls.Description:SetFont("ScoreboardSubtitle")
|
||||
FAdmin.ScoreBoard.Controls.Description:SetColor(Color(200,200,200,200))
|
||||
FAdmin.ScoreBoard.Controls.Description:SetPos(FAdmin.ScoreBoard.X + 90, FAdmin.ScoreBoard.Y + 50)
|
||||
FAdmin.ScoreBoard.Controls.Description:SizeToContents()
|
||||
if FAdmin.ScoreBoard.X + FAdmin.ScoreBoard.Width / 9.5 + FAdmin.ScoreBoard.Controls.Description:GetWide() > FAdmin.ScoreBoard.Width - 150 then
|
||||
FAdmin.ScoreBoard.Controls.Description:SetFont("Trebuchet18")
|
||||
FAdmin.ScoreBoard.Controls.Description:SetPos(FAdmin.ScoreBoard.X + 90, FAdmin.ScoreBoard.Y + 50)
|
||||
end
|
||||
FAdmin.ScoreBoard.Controls.Description:SetVisible(true)
|
||||
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel = FAdmin.ScoreBoard.Controls.ServerSettingsLabel or vgui.Create("DLabel")
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SetFont("ScoreboardSubtitle")
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SetText("Server settings")
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SetColor(Color(200,200,200,200))
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SizeToContents()
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SetPos(FAdmin.ScoreBoard.Width-150, FAdmin.ScoreBoard.Y + 68)
|
||||
FAdmin.ScoreBoard.Controls.ServerSettingsLabel:SetVisible(true)
|
||||
|
||||
FAdmin.ScoreBoard.Controls.ServerSettings = FAdmin.ScoreBoard.Controls.ServerSettings or vgui.Create("DImageButton")
|
||||
FAdmin.ScoreBoard.Controls.ServerSettings:SetMaterial("vgui/gmod_tool")
|
||||
FAdmin.ScoreBoard.Controls.ServerSettings:SetPos(FAdmin.ScoreBoard.Width-200, FAdmin.ScoreBoard.Y - 20)
|
||||
FAdmin.ScoreBoard.Controls.ServerSettings:SizeToContents()
|
||||
FAdmin.ScoreBoard.Controls.ServerSettings:SetVisible(true)
|
||||
|
||||
function FAdmin.ScoreBoard.Controls.ServerSettings:DoClick()
|
||||
FAdmin.ScoreBoard.ChangeView("Server")
|
||||
end
|
||||
|
||||
if FAdmin.ScoreBoard.Controls.BackButton then FAdmin.ScoreBoard.Controls.BackButton:SetVisible(true) end
|
||||
|
||||
FAdmin.ScoreBoard[FAdmin.ScoreBoard.CurrentView].Show()
|
||||
|
||||
gui.EnableScreenClicker(true)
|
||||
hook.Add("HUDPaint", "FAdmin_ScoreBoard", FAdmin.ScoreBoard.DrawScoreBoard)
|
||||
hook.Call("FAdmin_ShowFAdminMenu")
|
||||
return true
|
||||
end
|
||||
concommand.Add("+FAdmin_menu", FAdmin.ScoreBoard.ShowScoreBoard)
|
||||
|
||||
hook.Add("ScoreboardShow", "FAdmin_scoreboard", function()
|
||||
if FAdmin.GlobalSetting.FAdmin or OverrideScoreboard:GetBool() then -- Don't show scoreboard when FAdmin is not installed on server
|
||||
return FAdmin.ScoreBoard.ShowScoreBoard()
|
||||
end
|
||||
end)
|
||||
|
||||
function FAdmin.ScoreBoard.HideScoreBoard()
|
||||
if not FAdmin.GlobalSetting.FAdmin then return end
|
||||
FAdmin.ScoreBoard.Visible = false
|
||||
CloseDermaMenus()
|
||||
|
||||
gui.EnableScreenClicker(false)
|
||||
hook.Remove("HUDPaint", "FAdmin_ScoreBoard")
|
||||
|
||||
for _, v in pairs(FAdmin.ScoreBoard[FAdmin.ScoreBoard.CurrentView].Controls) do
|
||||
v:SetVisible(false)
|
||||
end
|
||||
|
||||
for _, v in pairs(FAdmin.ScoreBoard.Controls) do
|
||||
v:SetVisible(false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
concommand.Add("-FAdmin_menu", FAdmin.ScoreBoard.HideScoreBoard)
|
||||
|
||||
hook.Add("ScoreboardHide", "FAdmin_scoreboard", function()
|
||||
if FAdmin.GlobalSetting.FAdmin or OverrideScoreboard:GetBool() then -- Don't show scoreboard when FAdmin is not installed on server
|
||||
return FAdmin.ScoreBoard.HideScoreBoard()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,110 @@
|
||||
local Sorted, SortDown = CreateClientConVar("FAdmin_SortPlayerList", "Team", true), CreateClientConVar("FAdmin_SortPlayerListDown", 1, true)
|
||||
local allowedSorts = {
|
||||
["Name"] = true,
|
||||
["Team"] = true,
|
||||
["Frags"] = true,
|
||||
["Deaths"] = true,
|
||||
["Ping"] = true
|
||||
}
|
||||
|
||||
function FAdmin.ScoreBoard.Main.Show()
|
||||
local Sort = {}
|
||||
local ScreenWidth, ScreenHeight = ScrW(), ScrH()
|
||||
|
||||
FAdmin.ScoreBoard.X = ScreenWidth * 0.05
|
||||
FAdmin.ScoreBoard.Y = ScreenHeight * 0.025
|
||||
FAdmin.ScoreBoard.Width = ScreenWidth * 0.9
|
||||
FAdmin.ScoreBoard.Height = ScreenHeight * 0.95
|
||||
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList = FAdmin.ScoreBoard.Main.Controls.FAdminPanelList or vgui.Create("DPanelList")
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:Clear(true)
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList.Padding = 3
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:EnableVerticalScrollbar(true)
|
||||
|
||||
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:Clear(true)
|
||||
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 90 + 30 + 20)
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:SetSize(FAdmin.ScoreBoard.Width - 40, FAdmin.ScoreBoard.Height - 90 - 30 - 20 - 20)
|
||||
|
||||
Sort.Name = Sort.Name or vgui.Create("DLabel")
|
||||
Sort.Name:SetText("Sort by: Name")
|
||||
Sort.Name:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 90 + 30)
|
||||
Sort.Name.Type = "Name"
|
||||
Sort.Name:SetVisible(true)
|
||||
|
||||
Sort.Team = Sort.Team or vgui.Create("DLabel")
|
||||
Sort.Team:SetText("Team")
|
||||
Sort.Team:SetPos(ScreenWidth * 0.5 - 30, FAdmin.ScoreBoard.Y + 90 + 30)
|
||||
Sort.Team.Type = "Team"
|
||||
Sort.Team:SetVisible(true)
|
||||
|
||||
Sort.Frags = Sort.Frags or vgui.Create("DLabel")
|
||||
Sort.Frags:SetText("Kills")
|
||||
Sort.Frags:SetPos(FAdmin.ScoreBoard.X + FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:GetWide() - 200, FAdmin.ScoreBoard.Y + 90 + 30)
|
||||
Sort.Frags.Type = "Frags"
|
||||
Sort.Frags:SetVisible(true)
|
||||
|
||||
Sort.Deaths = Sort.Deaths or vgui.Create("DLabel")
|
||||
Sort.Deaths:SetText("Deaths")
|
||||
Sort.Deaths:SetPos(FAdmin.ScoreBoard.X + FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:GetWide() - 140, FAdmin.ScoreBoard.Y + 90 + 30)
|
||||
Sort.Deaths.Type = "Deaths"
|
||||
Sort.Deaths:SetVisible(true)
|
||||
|
||||
Sort.Ping = Sort.Ping or vgui.Create("DLabel")
|
||||
Sort.Ping:SetText("Ping")
|
||||
Sort.Ping:SetPos(FAdmin.ScoreBoard.X + FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:GetWide() - 50, FAdmin.ScoreBoard.Y + 90 + 30)
|
||||
Sort.Ping.Type = "Ping"
|
||||
Sort.Ping:SetVisible(true)
|
||||
|
||||
local sortBy = Sorted:GetString()
|
||||
sortBy = allowedSorts[sortBy] and sortBy or "Team"
|
||||
|
||||
FAdmin.ScoreBoard.Main.PlayerListView(sortBy, SortDown:GetBool())
|
||||
|
||||
for _, v in pairs(Sort) do
|
||||
v:SetFont("Trebuchet20")
|
||||
v:SizeToContents()
|
||||
|
||||
local X, Y = v:GetPos()
|
||||
|
||||
v.BtnSort = vgui.Create("DButton")
|
||||
v.BtnSort:SetText("")
|
||||
v.BtnSort.Type = "Down"
|
||||
v.BtnSort.Paint = function(panel, w, h) derma.SkinHook("Paint", "ButtonDown", panel, w, h) end
|
||||
v.BtnSort:SetSkin(GAMEMODE.Config.DarkRPSkin)
|
||||
if Sorted:GetString() == v.Type then
|
||||
v.BtnSort.Depressed = true
|
||||
v.BtnSort.Type = (SortDown:GetBool() and "Down") or "Up"
|
||||
end
|
||||
v.BtnSort:SetSize(16, 16)
|
||||
v.BtnSort:SetPos(X + v:GetWide() + 5, Y + 4)
|
||||
function v.BtnSort.DoClick()
|
||||
for _, b in pairs(Sort) do
|
||||
b.BtnSort.Depressed = b.BtnSort == v.BtnSort
|
||||
end
|
||||
v.BtnSort.Type = (v.BtnSort.Type == "Down" and "Up") or "Down"
|
||||
v.BtnSort.Paint = function(panel, w, h)
|
||||
derma.SkinHook("Paint", "Button" .. v.BtnSort.Type, panel, w, h)
|
||||
end
|
||||
|
||||
RunConsoleCommand("FAdmin_SortPlayerList", v.Type)
|
||||
RunConsoleCommand("FAdmin_SortPlayerListDown", (v.BtnSort.Type == "Down" and "1") or "0")
|
||||
FAdmin.ScoreBoard.Main.Controls.FAdminPanelList:Clear(true)
|
||||
FAdmin.ScoreBoard.Main.PlayerListView(v.Type, v.BtnSort.Type == "Down")
|
||||
end
|
||||
table.insert(FAdmin.ScoreBoard.Main.Controls, v) -- Add them to the table so they get removed when you close the scoreboard
|
||||
table.insert(FAdmin.ScoreBoard.Main.Controls, v.BtnSort)
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Main.AddPlayerRightClick(Name, func)
|
||||
FAdmin.PlayerIcon.RightClickOptions[Name] = func
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["CopySteamID"] = function()
|
||||
FAdmin.ScoreBoard.Main.AddPlayerRightClick("Copy SteamID", function(ply) SetClipboardText(ply:SteamID()) end)
|
||||
end
|
||||
@@ -0,0 +1,181 @@
|
||||
FAdmin.ScoreBoard.Player.Information = {}
|
||||
FAdmin.ScoreBoard.Player.ActionButtons = {}
|
||||
|
||||
function FAdmin.ScoreBoard.Player.Show(ply)
|
||||
ply = ply or FAdmin.ScoreBoard.Player.Player
|
||||
FAdmin.ScoreBoard.Player.Player = ply
|
||||
|
||||
if not IsValid(ply) or not IsValid(FAdmin.ScoreBoard.Player.Player) then FAdmin.ScoreBoard.ChangeView("Main") return end
|
||||
|
||||
local ScreenHeight = ScrH()
|
||||
|
||||
FAdmin.ScoreBoard.Player.Controls.AvatarBackground = vgui.Create("AvatarImage")
|
||||
FAdmin.ScoreBoard.Player.Controls.AvatarBackground:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 100)
|
||||
FAdmin.ScoreBoard.Player.Controls.AvatarBackground:SetSize(184, 184)
|
||||
FAdmin.ScoreBoard.Player.Controls.AvatarBackground:SetPlayer(ply, 184)
|
||||
FAdmin.ScoreBoard.Player.Controls.AvatarBackground:SetVisible(true)
|
||||
|
||||
FAdmin.ScoreBoard.Player.InfoPanels = FAdmin.ScoreBoard.Player.InfoPanels or {}
|
||||
for k, v in pairs(FAdmin.ScoreBoard.Player.InfoPanels) do
|
||||
if IsValid(v) then
|
||||
v:Remove()
|
||||
FAdmin.ScoreBoard.Player.InfoPanels[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(FAdmin.ScoreBoard.Player.Controls.InfoPanel1) then
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:Remove()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1 = vgui.Create("DListLayout")
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 100 + 184 + 5 --[[ + Avatar size]])
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:SetSize(184, ScreenHeight * 0.1 + 2)
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:Clear(true)
|
||||
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel2 = FAdmin.ScoreBoard.Player.Controls.InfoPanel2 or vgui.Create("FAdminPanelList")
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel2:SetPos(FAdmin.ScoreBoard.X + 25 + 184 --[[+ Avatar]], FAdmin.ScoreBoard.Y + 100)
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel2:SetSize(FAdmin.ScoreBoard.Width - 184 - 30 - 10, 184 + 5 + ScreenHeight * 0.1 + 2)
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel2:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel2:Clear(true)
|
||||
|
||||
local function AddInfoPanel()
|
||||
local pan = FAdmin.ScoreBoard.Player.Controls.InfoPanel2:Add("DListLayout")
|
||||
pan:SetSize(1, FAdmin.ScoreBoard.Player.Controls.InfoPanel2:GetTall())
|
||||
|
||||
table.insert(FAdmin.ScoreBoard.Player.InfoPanels, pan)
|
||||
return pan
|
||||
end
|
||||
|
||||
local SelectedPanel = AddInfoPanel() -- Make first panel to put the first things in
|
||||
|
||||
for k, v in pairs(FAdmin.ScoreBoard.Player.Information) do
|
||||
SelectedPanel:Dock(LEFT)
|
||||
local Value = v.func(FAdmin.ScoreBoard.Player.Player)
|
||||
--if not Value or Value == "" then return --[[ Value = "N/A" ]] end
|
||||
if Value and Value ~= "" then
|
||||
|
||||
local Text = vgui.Create("DLabel")
|
||||
Text:Dock(LEFT)
|
||||
Text:SetFont("TabLarge")
|
||||
Text:SetText(v.name .. ": " .. Value)
|
||||
Text:SizeToContents()
|
||||
Text:SetColor(Color(200,200,200,200))
|
||||
Text:SetTooltip("Click to copy " .. v.name .. " to clipboard")
|
||||
Text:SetMouseInputEnabled(true)
|
||||
|
||||
function Text:OnMousePressed(mcode)
|
||||
self:SetTooltip(v.name .. " copied to clipboard!")
|
||||
ChangeTooltip(self)
|
||||
SetClipboardText(Value)
|
||||
self:SetTooltip("Click to copy " .. v.name .. " to clipboard")
|
||||
end
|
||||
|
||||
timer.Create("FAdmin_Scoreboard_text_update_" .. v.name, 1, 0, function()
|
||||
if not IsValid(ply) or not IsValid(FAdmin.ScoreBoard.Player.Player) or not IsValid(Text) then
|
||||
timer.Remove("FAdmin_Scoreboard_text_update_" .. v.name)
|
||||
if FAdmin.ScoreBoard.Visible and (not IsValid(ply) or not IsValid(FAdmin.ScoreBoard.Player.Player)) then FAdmin.ScoreBoard.ChangeView("Main") end
|
||||
return
|
||||
end
|
||||
Value = v.func(FAdmin.ScoreBoard.Player.Player)
|
||||
if not Value or Value == "" then Value = "N/A" end
|
||||
Text:SetText(v.name .. ": " .. Value)
|
||||
end)
|
||||
|
||||
if (#FAdmin.ScoreBoard.Player.Controls.InfoPanel1:GetChildren() * 17 + 17) <= FAdmin.ScoreBoard.Player.Controls.InfoPanel1:GetTall() and not v.NewPanel then
|
||||
FAdmin.ScoreBoard.Player.Controls.InfoPanel1:Add(Text)
|
||||
else
|
||||
if #SelectedPanel:GetChildren() * 17 + 17 >= SelectedPanel:GetTall() or v.NewPanel then
|
||||
SelectedPanel = AddInfoPanel() -- Add new panel if the last one is full
|
||||
end
|
||||
SelectedPanel:Add(Text)
|
||||
if Text:GetWide() > SelectedPanel:GetWide() then
|
||||
SelectedPanel:SetWide(Text:GetWide() + 40)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local CatColor = team.GetColor(ply:Team())
|
||||
if GAMEMODE.Name == "Sandbox" then
|
||||
CatColor = Color(100, 150, 245, 255)
|
||||
if ply:Team() == TEAM_CONNECTING then
|
||||
CatColor = Color(200, 120, 50, 255)
|
||||
elseif ply:IsAdmin() then
|
||||
CatColor = Color(30, 200, 50, 255)
|
||||
end
|
||||
|
||||
if ply:GetFriendStatus() == "friend" then
|
||||
CatColor = Color(236, 181, 113, 255)
|
||||
end
|
||||
end
|
||||
CatColor = hook.Run("FAdmin_PlayerRowColour", ply, CatColor) or CatColor
|
||||
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat = FAdmin.ScoreBoard.Player.Controls.ButtonCat or vgui.Create("FAdminPlayerCatagory")
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat:SetLabel(" Player options!")
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat.CatagoryColor = CatColor
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat:SetSize(FAdmin.ScoreBoard.Width - 40, 100)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 100 + FAdmin.ScoreBoard.Player.Controls.InfoPanel2:GetTall() + 5)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonCat:SetVisible(true)
|
||||
|
||||
function FAdmin.ScoreBoard.Player.Controls.ButtonCat:Toggle()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel = FAdmin.ScoreBoard.Player.Controls.ButtonPanel or vgui.Create("FAdminPanelList", FAdmin.ScoreBoard.Player.Controls.ButtonCat)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:SetSpacing(5)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:EnableHorizontal(true)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:EnableVerticalScrollbar(true)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:SizeToContents()
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:SetSize(0, (ScreenHeight - FAdmin.ScoreBoard.Y - 40) - (FAdmin.ScoreBoard.Y + 100 + FAdmin.ScoreBoard.Player.Controls.InfoPanel2:GetTall() + 5))
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:Clear()
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:DockMargin(5, 5, 5, 5)
|
||||
|
||||
for _, v in ipairs(FAdmin.ScoreBoard.Player.ActionButtons) do
|
||||
if v.Visible == true or (isfunction(v.Visible) and v.Visible(FAdmin.ScoreBoard.Player.Player) == true) then
|
||||
local ActionButton = vgui.Create("FAdminActionButton")
|
||||
local imageType = TypeID(v.Image)
|
||||
if imageType == TYPE_STRING then
|
||||
ActionButton:SetImage(v.Image or "icon16/exclamation")
|
||||
elseif imageType == TYPE_TABLE then
|
||||
ActionButton:SetImage(v.Image[1])
|
||||
if v.Image[2] then ActionButton:SetImage2(v.Image[2]) end
|
||||
elseif imageType == TYPE_FUNCTION then
|
||||
local img1, img2 = v.Image(ply)
|
||||
ActionButton:SetImage(img1)
|
||||
if img2 then ActionButton:SetImage2(img2) end
|
||||
else
|
||||
ActionButton:SetImage("icon16/exclamation")
|
||||
end
|
||||
local name = v.Name
|
||||
if isfunction(name) then name = name(FAdmin.ScoreBoard.Player.Player) end
|
||||
ActionButton:SetText(DarkRP.deLocalise(name))
|
||||
ActionButton:SetBorderColor(v.color)
|
||||
|
||||
function ActionButton:DoClick()
|
||||
if not IsValid(FAdmin.ScoreBoard.Player.Player) then return end
|
||||
return v.Action(FAdmin.ScoreBoard.Player.Player, self)
|
||||
end
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:AddItem(ActionButton)
|
||||
if v.OnButtonCreated then
|
||||
v.OnButtonCreated(FAdmin.ScoreBoard.Player.Player, ActionButton)
|
||||
end
|
||||
end
|
||||
end
|
||||
FAdmin.ScoreBoard.Player.Controls.ButtonPanel:Dock(TOP)
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Player:AddInformation(name, func, ForceNewPanel) -- ForeNewPanel is to start a new column
|
||||
table.insert(FAdmin.ScoreBoard.Player.Information, {name = name, func = func, NewPanel = ForceNewPanel})
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Player:AddActionButton(Name, Image, color, Visible, Action, OnButtonCreated)
|
||||
table.insert(FAdmin.ScoreBoard.Player.ActionButtons, {Name = Name, Image = Image, color = color, Visible = Visible, Action = Action, OnButtonCreated = OnButtonCreated})
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Name", function(ply) return ply:Nick() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Kills", function(ply) return ply:Frags() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Deaths", function(ply) return ply:Deaths() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Health", function(ply) return ply:Health() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("Ping", function(ply) return ply:Ping() end)
|
||||
FAdmin.ScoreBoard.Player:AddInformation("SteamID", function(ply) return ply:SteamID() end, true)
|
||||
@@ -0,0 +1,227 @@
|
||||
FAdmin.ScoreBoard.Server.Information = {} -- Compatibility for autoreload
|
||||
FAdmin.ScoreBoard.Server.ActionButtons = {} -- Refresh server buttons when reloading gamemode
|
||||
|
||||
local function MakeServerOptions()
|
||||
local _, YPos, Width = 20, FAdmin.ScoreBoard.Y + 120 + FAdmin.ScoreBoard.Height / 5 + 20, (FAdmin.ScoreBoard.Width - 40) / 3
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat = FAdmin.ScoreBoard.Server.Controls.ServerActionsCat or vgui.Create("FAdminPlayerCatagory")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:SetLabel(" Server Actions")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat.CatagoryColor = Color(155, 0, 0, 255)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:SetSize(Width-5, FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:SetPos(FAdmin.ScoreBoard.X + 20, YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:SetVisible(true)
|
||||
function FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:Toggle()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActions = FAdmin.ScoreBoard.Server.Controls.ServerActions or vgui.Create("FAdminPanelList")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActionsCat:SetContents(FAdmin.ScoreBoard.Server.Controls.ServerActions)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerActions:SetTall(FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
for k, v in ipairs(FAdmin.ScoreBoard.Server.Controls.ServerActions:GetChildren()) do
|
||||
if k == 1 then continue end
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat = FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat or vgui.Create("FAdminPlayerCatagory")
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:SetLabel(" Player Actions")
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat.CatagoryColor = Color(0, 155, 0, 255)
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:SetSize(Width-5, FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:SetPos(FAdmin.ScoreBoard.X + 20 + Width, YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:SetVisible(true)
|
||||
function FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:Toggle()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActions = FAdmin.ScoreBoard.Server.Controls.PlayerActions or vgui.Create("FAdminPanelList")
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActionsCat:SetContents(FAdmin.ScoreBoard.Server.Controls.PlayerActions)
|
||||
FAdmin.ScoreBoard.Server.Controls.PlayerActions:SetTall(FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
for k, v in ipairs(FAdmin.ScoreBoard.Server.Controls.PlayerActions:GetChildren()) do
|
||||
if k == 1 then continue end
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat = FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat or vgui.Create("FAdminPlayerCatagory")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:SetLabel(" Server Settings")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat.CatagoryColor = Color(0, 0, 155, 255)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:SetSize(Width-5, FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:SetPos(FAdmin.ScoreBoard.X + 20 + Width * 2, YPos)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:SetVisible(true)
|
||||
function FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:Toggle()
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettings = FAdmin.ScoreBoard.Server.Controls.ServerSettings or vgui.Create("FAdminPanelList")
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettingsCat:SetContents(FAdmin.ScoreBoard.Server.Controls.ServerSettings)
|
||||
FAdmin.ScoreBoard.Server.Controls.ServerSettings:SetTall(FAdmin.ScoreBoard.Height - 20 - YPos)
|
||||
for k, v in ipairs(FAdmin.ScoreBoard.Server.Controls.ServerSettings:GetChildren()) do
|
||||
if k == 1 then continue end
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
for k, v in ipairs(FAdmin.ScoreBoard.Server.ActionButtons) do
|
||||
local visible = v.Visible == true or (isfunction(v.Visible) and v.Visible(LocalPlayer()) == true)
|
||||
|
||||
local ActionButton = vgui.Create("FAdminActionButton")
|
||||
local imageType = TypeID(v.Image)
|
||||
if imageType == TYPE_STRING then
|
||||
ActionButton:SetImage(v.Image or "icon16/exclamation")
|
||||
elseif imageType == TYPE_TABLE then
|
||||
ActionButton:SetImage(v.Image[1])
|
||||
if v.Image[2] then ActionButton:SetImage2(v.Image[2]) end
|
||||
elseif imageType == TYPE_FUNCTION then
|
||||
local img1, img2 = v.Image()
|
||||
ActionButton:SetImage(img1)
|
||||
if img2 then ActionButton:SetImage2(img2) end
|
||||
else
|
||||
ActionButton:SetImage("icon16/exclamation")
|
||||
end
|
||||
local name = v.Name
|
||||
if isfunction(name) then name = name() end
|
||||
ActionButton:SetText(DarkRP.deLocalise(name))
|
||||
ActionButton:SetBorderColor(visible and v.color or Color(120, 120, 120))
|
||||
ActionButton:SetDisabled(not visible)
|
||||
ActionButton:Dock(TOP)
|
||||
|
||||
function ActionButton:DoClick()
|
||||
return v.Action(self)
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server.Controls[v.TYPE]:Add(ActionButton)
|
||||
if v.OnButtonCreated then
|
||||
v.OnButtonCreated(ActionButton)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Server:AddServerAction(Name, Image, color, Visible, Action, OnButtonCreated)
|
||||
table.insert(FAdmin.ScoreBoard.Server.ActionButtons, {TYPE = "ServerActions", Name = Name, Image = Image, color = color, Visible = Visible, Action = Action, OnButtonCreated = OnButtonCreated})
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Server:AddPlayerAction(Name, Image, color, Visible, Action, OnButtonCreated)
|
||||
table.insert(FAdmin.ScoreBoard.Server.ActionButtons, {TYPE = "PlayerActions", Name = Name, Image = Image, color = color, Visible = Visible, Action = Action, OnButtonCreated = OnButtonCreated})
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Server:AddServerSetting(Name, Image, color, Visible, Action, OnButtonCreated)
|
||||
table.insert(FAdmin.ScoreBoard.Server.ActionButtons, {TYPE = "ServerSettings", Name = Name, Image = Image, color = color, Visible = Visible, Action = Action, OnButtonCreated = OnButtonCreated})
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Server.Show(ply)
|
||||
FAdmin.ScoreBoard.Server.InfoPanels = FAdmin.ScoreBoard.Server.InfoPanels or {}
|
||||
for k, v in pairs(FAdmin.ScoreBoard.Server.InfoPanels) do
|
||||
if IsValid(v) then
|
||||
v:Remove()
|
||||
FAdmin.ScoreBoard.Server.InfoPanels[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(FAdmin.ScoreBoard.Server.Controls.InfoPanel) then
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:Remove()
|
||||
end
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel = vgui.Create("FAdminPanelList")
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:SetPos(FAdmin.ScoreBoard.X + 20, FAdmin.ScoreBoard.Y + 120)
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:SetSize(FAdmin.ScoreBoard.Width - 40, FAdmin.ScoreBoard.Height / 5)
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:SetVisible(true)
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:Clear(true)
|
||||
|
||||
local function AddInfoPanel()
|
||||
local pan = vgui.Create("FAdminPanelList")
|
||||
pan:SetSize(1, FAdmin.ScoreBoard.Server.Controls.InfoPanel:GetTall())
|
||||
pan:Dock(LEFT)
|
||||
FAdmin.ScoreBoard.Server.Controls.InfoPanel:Add(pan)
|
||||
|
||||
table.insert(FAdmin.ScoreBoard.Server.InfoPanels, pan)
|
||||
return pan
|
||||
end
|
||||
|
||||
local SelectedPanel = AddInfoPanel() -- Make first panel to put the first things in
|
||||
|
||||
for _, v in pairs(FAdmin.ScoreBoard.Server.Information) do
|
||||
local Text = vgui.Create("DLabel")
|
||||
Text:SetFont("TabLarge")
|
||||
Text:SetColor(Color(255,255,255,200))
|
||||
Text:Dock(TOP)
|
||||
Text.Func = v.Func
|
||||
|
||||
local EndText
|
||||
local function RefreshText()
|
||||
local Value = v.func()
|
||||
|
||||
if not Value or Value == "" then
|
||||
Value = "N/A"
|
||||
end
|
||||
|
||||
EndText = v.name .. ": " .. Value
|
||||
local strLen = string.len(EndText)
|
||||
|
||||
if strLen > 40 then
|
||||
local NewValue = string.sub(EndText, 1, 40)
|
||||
|
||||
for i = 40, strLen, 34 do
|
||||
NewValue = NewValue .. "\n " .. string.sub(EndText, i + 1, i + 34)
|
||||
end
|
||||
|
||||
EndText = NewValue
|
||||
else
|
||||
local MaxWidth = 240
|
||||
surface.SetFont("TabLarge")
|
||||
local TextWidth = surface.GetTextSize(v.name .. ": " .. Value)
|
||||
|
||||
if TextWidth <= MaxWidth then
|
||||
local SpacesAmount = (MaxWidth - TextWidth) / 3
|
||||
local Spaces = ""
|
||||
|
||||
for i = 1, SpacesAmount, 1 do
|
||||
Spaces = Spaces .. " "
|
||||
end
|
||||
|
||||
EndText = v.name .. ":" .. Spaces .. Value
|
||||
end
|
||||
end
|
||||
|
||||
Text:SetText(DarkRP.deLocalise(EndText))
|
||||
Text:SizeToContents()
|
||||
Text:SetTooltip("Click to copy " .. v.name .. " to clipboard")
|
||||
Text:SetMouseInputEnabled(true)
|
||||
end
|
||||
|
||||
RefreshText()
|
||||
|
||||
function Text:OnMousePressed(mcode)
|
||||
self:SetTooltip(v.name .. " copied to clipboard!")
|
||||
ChangeTooltip(self)
|
||||
SetClipboardText(v.func() or "")
|
||||
self:SetTooltip("Click to copy " .. v.name .. " to clipboard")
|
||||
end
|
||||
|
||||
timer.Create("FAdmin_Scoreboard_text_update_" .. v.name, 1, 0, function()
|
||||
if not IsValid(Text) then
|
||||
timer.Remove("FAdmin_Scoreboard_text_update_" .. v.name)
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
RefreshText()
|
||||
end)
|
||||
|
||||
if #SelectedPanel:GetChildren() * 17 + 17 >= SelectedPanel:GetTall() or v.NewPanel then
|
||||
SelectedPanel = AddInfoPanel()
|
||||
end
|
||||
-- Add new panel if the last one is full
|
||||
SelectedPanel:Add(Text)
|
||||
if Text:GetWide() > SelectedPanel:GetWide() then
|
||||
SelectedPanel:SetWide(Text:GetWide() + 40)
|
||||
end
|
||||
end
|
||||
|
||||
MakeServerOptions()
|
||||
end
|
||||
|
||||
function FAdmin.ScoreBoard.Server:AddInformation(name, func, ForceNewPanel) -- ForeNewPanel is to start a new column
|
||||
table.insert(FAdmin.ScoreBoard.Server.Information, {name = name, func = func, NewPanel = ForceNewPanel})
|
||||
end
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Hostname", GetHostName)
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Gamemode", function() return GAMEMODE.Name end)
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Author", function() return GAMEMODE.Author end)
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Map", game.GetMap)
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Players", function() return player.GetCount() .. "/" .. game.MaxPlayers() end)
|
||||
FAdmin.ScoreBoard.Server:AddInformation("Ping", function() return LocalPlayer():Ping() end)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
FAdmin.ScoreBoard = FAdmin.ScoreBoard or {}
|
||||
|
||||
local ScreenWidth, ScreenHeight = ScrW(), ScrH()
|
||||
|
||||
FAdmin.ScoreBoard.X = ScreenWidth * 0.05
|
||||
FAdmin.ScoreBoard.Y = ScreenHeight * 0.025
|
||||
FAdmin.ScoreBoard.Width = ScreenWidth * 0.9
|
||||
FAdmin.ScoreBoard.Height = ScreenHeight * 0.95
|
||||
|
||||
FAdmin.ScoreBoard.Controls = FAdmin.ScoreBoard.Controls or {}
|
||||
FAdmin.ScoreBoard.CurrentView = "Main"
|
||||
|
||||
FAdmin.ScoreBoard.Main = FAdmin.ScoreBoard.Main or {}
|
||||
FAdmin.ScoreBoard.Main.Controls = FAdmin.ScoreBoard.Main.Controls or {}
|
||||
FAdmin.ScoreBoard.Main.Logo = "gui/gmod_logo"
|
||||
|
||||
FAdmin.ScoreBoard.Player = FAdmin.ScoreBoard.Player or {}
|
||||
FAdmin.ScoreBoard.Player.Controls = FAdmin.ScoreBoard.Player.Controls or {}
|
||||
FAdmin.ScoreBoard.Player.Player = NULL
|
||||
FAdmin.ScoreBoard.Player.Logo = "fadmin/back"
|
||||
|
||||
FAdmin.ScoreBoard.Server = FAdmin.ScoreBoard.Server or {}
|
||||
FAdmin.ScoreBoard.Server.Controls = FAdmin.ScoreBoard.Server.Controls or {}
|
||||
FAdmin.ScoreBoard.Server.Logo = "fadmin/back"
|
||||
@@ -0,0 +1,53 @@
|
||||
--[[---------------------------------------------------------------------------
|
||||
Common times for several punishment actions
|
||||
---------------------------------------------------------------------------]]
|
||||
FAdmin.PlayerActions.commonTimes = {}
|
||||
FAdmin.PlayerActions.commonTimes[0] = "indefinitely"
|
||||
FAdmin.PlayerActions.commonTimes[10] = "10 seconds"
|
||||
FAdmin.PlayerActions.commonTimes[30] = "30 seconds"
|
||||
FAdmin.PlayerActions.commonTimes[60] = "1 minute"
|
||||
FAdmin.PlayerActions.commonTimes[300] = "5 minutes"
|
||||
FAdmin.PlayerActions.commonTimes[600] = "10 minutes"
|
||||
|
||||
function FAdmin.PlayerActions.addTimeSubmenu(menu, submenuText, submenuClick, submenuItemClick)
|
||||
local SubMenu = menu:AddSubMenu(submenuText, submenuClick)
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
SubMenu:AddPanel(Padding)
|
||||
|
||||
local SubMenuTitle = vgui.Create("DLabel")
|
||||
SubMenuTitle:SetText(" Time:\n")
|
||||
SubMenuTitle:SetFont("UiBold")
|
||||
SubMenuTitle:SizeToContents()
|
||||
SubMenuTitle:SetTextColor(color_black)
|
||||
|
||||
SubMenu:AddPanel(SubMenuTitle)
|
||||
|
||||
for secs, Time in SortedPairs(FAdmin.PlayerActions.commonTimes) do
|
||||
SubMenu:AddOption(Time, function() submenuItemClick(secs) end)
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.PlayerActions.addTimeMenu(ItemClick)
|
||||
local menu = DermaMenu()
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
menu:AddPanel(Padding)
|
||||
|
||||
local Title = vgui.Create("DLabel")
|
||||
Title:SetText(" Time:\n")
|
||||
Title:SetFont("UiBold")
|
||||
Title:SizeToContents()
|
||||
Title:SetTextColor(color_black)
|
||||
|
||||
menu:AddPanel(Title)
|
||||
|
||||
for secs, Time in SortedPairs(FAdmin.PlayerActions.commonTimes) do
|
||||
menu:AddOption(Time, function() ItemClick(secs) end)
|
||||
end
|
||||
menu:Open()
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
FAdmin.StartHooks["CleanUp"] = function()
|
||||
FAdmin.Access.AddPrivilege("CleanUp", 2)
|
||||
FAdmin.Commands.AddCommand("ClearDecals", nil)
|
||||
FAdmin.Commands.AddCommand("StopSounds", nil)
|
||||
FAdmin.Commands.AddCommand("CleanUp", nil)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerAction("Clear decals", "fadmin/icons/cleanup", Color(155, 0, 0, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") end, function(ply, button)
|
||||
RunConsoleCommand("_FAdmin", "ClearDecals")
|
||||
end)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerAction("Stop all sounds", "fadmin/icons/cleanup", Color(155, 0, 0, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") end, function(ply, button)
|
||||
RunConsoleCommand("_FAdmin", "StopSounds")
|
||||
end)
|
||||
|
||||
usermessage.Hook("FAdmin_StopSounds", function()
|
||||
RunConsoleCommand("stopsound") -- bypass for ConCommand blocking it
|
||||
end)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerAction("Clean up server", "fadmin/icons/cleanup", Color(155, 0, 0, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") end, function(ply, button)
|
||||
RunConsoleCommand("_FAdmin", "CleanUp")
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
local function ClearDecals(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
v:ConCommand("r_cleardecals")
|
||||
end
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), "You have removed all decals. NOTE: this does NOT make the server ANY less laggy!", "All decals have been removed. NOTE: this does NOT make the server ANY less laggy!", "Removed all decals.")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function StopSounds(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
umsg.Start("FAdmin_StopSounds")
|
||||
umsg.End()
|
||||
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), "You have stopped all sounds", "All sounds have been stopped", "Stopped all sounds")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function CleanUp(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "CleanUp") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
|
||||
game.CleanUpMap()
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), "You have cleaned up the map", "The map has been cleaned up", "Cleaned up the map")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["CleanUp"] = function()
|
||||
FAdmin.Commands.AddCommand("ClearDecals", ClearDecals)
|
||||
FAdmin.Commands.AddCommand("StopSounds", StopSounds)
|
||||
FAdmin.Commands.AddCommand("CleanUp", CleanUp)
|
||||
|
||||
local oldCleanup = concommand.GetTable()["gmod_admin_cleanup"]
|
||||
concommand.Add("gmod_admin_cleanup", function(ply, cmd, args)
|
||||
if args[1] then return oldCleanup(ply, cmd, args) end
|
||||
return CleanUp(ply, cmd, args)
|
||||
end)
|
||||
|
||||
FAdmin.Access.AddPrivilege("CleanUp", 2)
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
local Options = {}
|
||||
local targets
|
||||
local colorBackground = Color(0, 0, 0, 200)
|
||||
local colorHighlight = Color(255, 125, 0, 200)
|
||||
hook.Add("ChatTextChanged", "FAdmin_Chat_autocomplete", function(text)
|
||||
if not FAdmin.GlobalSetting.FAdmin then return end
|
||||
Options = {}
|
||||
local prefix = GetGlobalString("FAdmin_commandprefix")
|
||||
prefix = prefix ~= '' and prefix or '/'
|
||||
|
||||
if string.sub(text, 1, 1) ~= prefix then targets = nil return end
|
||||
|
||||
local TExplode = string.Explode(" ", string.sub(text, 2))
|
||||
if not TExplode[1] then return end
|
||||
local Command = string.lower(TExplode[1])
|
||||
local Args = table.Copy(TExplode)
|
||||
Args[1] = nil
|
||||
Args = table.ClearKeys(Args)
|
||||
|
||||
|
||||
local optionsCount = 0
|
||||
for k, v in pairs(FAdmin.Commands.List) do
|
||||
if string.find(string.lower(k), Command, 1, true) ~= 1 then continue end
|
||||
|
||||
Options[prefix .. k] = table.Copy(v.ExtraArgs)
|
||||
|
||||
optionsCount = optionsCount + 1
|
||||
end
|
||||
|
||||
local ChatBoxPosX, ChatBoxPosY = chat.GetChatBoxPos()
|
||||
local ChatBoxWidth = chat.GetChatBoxSize() -- Don't need height
|
||||
local DidMakeShorter = false
|
||||
table.sort(Options)
|
||||
local i = 1
|
||||
for k in pairs(Options) do
|
||||
local Pos = ChatBoxPosY + i * 24
|
||||
if Pos + 24 > ScrH() then
|
||||
Options[k] = nil
|
||||
DidMakeShorter = true
|
||||
optionsCount = optionsCount - 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Player arguments
|
||||
local firstVal = table.GetFirstValue(Options)
|
||||
if optionsCount == 1 and firstVal[#Args] and string.match(firstVal[#Args], ".Player.") then
|
||||
local players = {}
|
||||
|
||||
for _, v in ipairs(FAdmin.FindPlayer(Args[#Args]) or {}) do
|
||||
if not IsValid(v) then continue end
|
||||
table.insert(players, v:Nick())
|
||||
end
|
||||
|
||||
targets = table.concat(players, ", ")
|
||||
end
|
||||
|
||||
local xPos = ChatBoxPosX + ChatBoxWidth + 2
|
||||
hook.Add("HUDPaint", "FAdmin_Chat_autocomplete", function()
|
||||
local j = 0
|
||||
for option, args in pairs(Options) do
|
||||
draw.WordBox(4, xPos, ChatBoxPosY + j * 24, option, "UiBold", colorBackground, color_white)
|
||||
|
||||
for k, arg in pairs(args) do
|
||||
draw.WordBox(4, xPos + k * 130, ChatBoxPosY + j * 24, arg, "UiBold", colorBackground, color_white)
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
if targets then
|
||||
draw.WordBox(4, xPos, ChatBoxPosY + j * 24, "Targets: " .. targets, "UiBold", colorHighlight, color_white)
|
||||
end
|
||||
|
||||
if DidMakeShorter then
|
||||
draw.WordBox(4, xPos, ChatBoxPosY + j * 24, "...", "UiBold", colorBackground, color_white)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("FinishChat", "FAdmin_Chat_autocomplete", function() hook.Remove("HUDPaint", "FAdmin_Chat_autocomplete") end)
|
||||
|
||||
local plyIndex = 1
|
||||
|
||||
hook.Add("OnChatTab", "FAdmin_Chat_autocomplete", function(text)
|
||||
if not FAdmin.GlobalSetting.FAdmin then return end
|
||||
|
||||
for command in pairs(Options) do
|
||||
if string.find(text, " ") == nil then
|
||||
return string.sub(command, 1, string.find(command, " "))
|
||||
elseif string.find(text, " ") then
|
||||
plyIndex = plyIndex + 1
|
||||
|
||||
if plyIndex > player.GetCount() then
|
||||
plyIndex = 1
|
||||
end
|
||||
|
||||
return string.sub(command, 1, string.find(command, " ")) .. " " .. string.sub(player.GetAll()[plyIndex]:Nick(), 1, string.find(player.GetAll()[plyIndex]:Nick(), " "))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
FAdmin.StartHooks["Chatcommands"] = function()
|
||||
FAdmin.ScoreBoard.Server:AddServerSetting("Set FAdmin's chat command prefix", "fadmin/icons/message", Color(0, 0, 155, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(ply, "ServerSetting") end, function()
|
||||
local prefix = GetGlobalString("FAdmin_commandprefix")
|
||||
prefix = prefix ~= '' and prefix or '/'
|
||||
Derma_StringRequest("Set chat command prefix", "Make sure it's only one character!", prefix, fp{RunConsoleCommand, "_Fadmin", "CommandPrefix"})
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
local function AutoComplete(command, args)
|
||||
local autocomplete = {}
|
||||
args = string.Explode(" ", args)
|
||||
table.remove(args, 1) --Remove the first space
|
||||
if args[1] == "" then
|
||||
for k in pairs(FAdmin.Commands.List) do
|
||||
table.insert(autocomplete, command .. " " .. k)
|
||||
end
|
||||
elseif not args[2] or args[3] then
|
||||
for k, v in pairs(FAdmin.Commands.List) do
|
||||
if string.sub(k, 1, string.len(args[1])) == args[1] then
|
||||
local ExtraArgs = table.concat(v.ExtraArgs, " ")
|
||||
table.insert(autocomplete, command .. " " .. k .. " " .. ExtraArgs)
|
||||
end
|
||||
end
|
||||
elseif not args[3] and FAdmin.Commands.List[string.lower(args[1])] and FAdmin.Commands.List[string.lower(args[1])].ExtraArgs[1] == "<Player>" then
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if args[2] == "" or table.HasValue(FAdmin.FindPlayer(args[2]) or {}, v) then
|
||||
table.insert(autocomplete, command .. " " .. args[1] .. " " .. v:Nick())
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(autocomplete)
|
||||
return autocomplete
|
||||
end
|
||||
concommand.Add("FAdmin", function(ply, cmd, args)
|
||||
RunConsoleCommand("_" .. cmd, unpack(args))
|
||||
end, AutoComplete)
|
||||
@@ -0,0 +1,6 @@
|
||||
FAdmin.Commands = {}
|
||||
FAdmin.Commands.List = {}
|
||||
|
||||
function FAdmin.Commands.AddCommand(name, callback, ...)
|
||||
FAdmin.Commands.List[string.lower(name)] = {callback = callback, ExtraArgs = {...}}
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
local convar = CreateConVar("FAdmin_commandprefix", "/", {FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
SetGlobalString("FAdmin_commandprefix", convar:GetString())
|
||||
|
||||
cvars.AddChangeCallback("FAdmin_commandprefix", function()
|
||||
SetGlobalString("FAdmin_commandprefix", convar:GetString())
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSay", "FAdminChatCommands", function(ply, text, Team, dead)
|
||||
local prefix = convar:GetString()
|
||||
|
||||
if string.sub(text, 1, 1) ~= prefix then return end
|
||||
|
||||
local TExplode = string.Explode(" ", string.sub(text, 2))
|
||||
if not TExplode then return end
|
||||
|
||||
for k, v in ipairs(TExplode) do
|
||||
if string.sub(v, -1) == "," and TExplode[k + 1] then
|
||||
TExplode[k] = (TExplode[k] or "") .. (TExplode[k + 1] or "")
|
||||
table.remove(TExplode, k + 1)
|
||||
end
|
||||
end
|
||||
table.ClearKeys(TExplode, false)
|
||||
|
||||
local Command = string.lower(TExplode[1])
|
||||
local Args = table.Copy(TExplode)
|
||||
Args[1] = nil
|
||||
Args = table.ClearKeys(Args)
|
||||
if FAdmin.Commands.List[Command] then
|
||||
local res = {FAdmin.Commands.List[Command].callback(ply, Command, Args)}
|
||||
hook.Call("FAdmin_OnCommandExecuted", nil, ply, Command, Args, res)
|
||||
return ""
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
FAdmin.StartHooks["Chatcommands"] = function()
|
||||
convar = convar or GetConVar("FAdmin_commandprefix")
|
||||
|
||||
FAdmin.Commands.AddCommand("CommandPrefix", function(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "ServerSetting") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if not args[1] or string.len(args[1]) ~= 1 then return end
|
||||
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), ply:Nick() .. " set FAdmin's chat command prefix to " .. args[1], "FAdmin's chat command prefix has been set to " .. args[1], "Chat command prefix set to" .. args[1])
|
||||
|
||||
RunConsoleCommand("FAdmin_commandprefix", args[1])
|
||||
|
||||
FAdmin.SaveSetting("FAdmin_commandprefix", args[1])
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,71 @@
|
||||
local function concommand_executed(ply, cmd, args)
|
||||
if not args[1] then return end
|
||||
local name = string.lower(args[1])
|
||||
|
||||
if not name or not FAdmin.Commands.List[name] then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Command does not exist!")
|
||||
return
|
||||
end
|
||||
|
||||
local args2 = args
|
||||
table.remove(args2, 1)
|
||||
for k, v in pairs(args2) do
|
||||
if string.sub(v, -1) == "," and args2[k + 1] then
|
||||
args2[k] = args2[k] .. args2[k + 1]
|
||||
table.remove(args2, k + 1)
|
||||
end
|
||||
end
|
||||
table.ClearKeys(args2)
|
||||
local res = {FAdmin.Commands.List[name].callback(ply, name, args2)}
|
||||
hook.Call("FAdmin_OnCommandExecuted", nil, ply, name, args2, res)
|
||||
end
|
||||
|
||||
local function AutoComplete(command, ...)
|
||||
local autocomplete = {}
|
||||
local args = string.Explode(" ", ...)
|
||||
table.remove(args, 1) --Remove the first space
|
||||
if args[1] == "" then
|
||||
for k in pairs(FAdmin.Commands.List) do
|
||||
table.insert(autocomplete, command .. " " .. k)
|
||||
end
|
||||
elseif not args[2] then
|
||||
for k in pairs(FAdmin.Commands.List) do
|
||||
if string.sub(k, 1, string.len(args[1])) == args[1] then
|
||||
table.insert(autocomplete, command .. " " .. k)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(autocomplete)
|
||||
return autocomplete
|
||||
end
|
||||
concommand.Add("_FAdmin", concommand_executed, AutoComplete)
|
||||
concommand.Add("FAdmin", concommand_executed, AutoComplete)
|
||||
|
||||
-- DO NOT EDIT THIS, NO MATTER HOW MUCH YOU'VE EDITED FADMIN IT DOESN'T GIVE YOU ANY RIGHT TO CHANGE CREDITS AND/OR REMOVE THE AUTHOR
|
||||
FAdmin.Commands.AddCommand("FAdminCredits", function(ply, cmd, args)
|
||||
if ply:SteamID() == "STEAM_0:0:8944068" and args[1] then
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or (#targets == 1 and not IsValid(targets[1])) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
for _, target in ipairs(targets) do
|
||||
if IsValid(target) then
|
||||
concommand_executed(target, "FAdmin", {"FAdminCredits"})
|
||||
end
|
||||
end
|
||||
|
||||
FAdmin.Messages.SendMessage(ply, 4, "Credits sent!")
|
||||
return true
|
||||
end
|
||||
FAdmin.Messages.SendMessage(ply, 2, "FAdmin by (FPtje) Falco, STEAM_0:0:8944068")
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:SteamID() == "STEAM_0:0:8944068" then
|
||||
FAdmin.Messages.SendMessage(ply, 4, "(FPtje) Falco is in the server at this moment")
|
||||
return true
|
||||
end
|
||||
end
|
||||
FAdmin.Messages.SendMessage(ply, 5, "(FPtje) Falco is NOT in the server at this moment")
|
||||
|
||||
return true
|
||||
end)
|
||||
@@ -0,0 +1,17 @@
|
||||
local logging = CreateConVar("FAdmin_logging", 1, {FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
if SERVER then return end
|
||||
FAdmin.StartHooks["Logging"] = function()
|
||||
FAdmin.Access.AddPrivilege("Logging", 3)
|
||||
FAdmin.Commands.AddCommand("Logging", nil)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerSetting(function() return (logging:GetBool() and "Disable" or "Enable") .. " Logging" end,
|
||||
function() return "fadmin/icons/message", logging:GetBool() and "fadmin/icons/disable" end,
|
||||
Color(0, 0, 155, 255), function(ply) return FAdmin.Access.PlayerHasPrivilege(ply, "Logging") end, function(button)
|
||||
button:SetImage2((not logging:GetBool() and "fadmin/icons/disable") or "null")
|
||||
button:SetText((not logging:GetBool() and "Disable" or "Enable") .. " Logging")
|
||||
button:GetParent():InvalidateLayout()
|
||||
|
||||
RunConsoleCommand("_Fadmin", "Logging", logging:GetBool() and 0 or 1)
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,161 @@
|
||||
local logging
|
||||
FAdmin.StartHooks["Logging"] = function()
|
||||
FAdmin.Access.AddPrivilege("Logging", 3)
|
||||
FAdmin.Commands.AddCommand("Logging", function(ply, cmd, args)
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Logging") then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if not tonumber(args[1]) then return end
|
||||
|
||||
local OnOff = (tobool(tonumber(args[1])) and "on") or "off"
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), ply:Nick() .. " turned logging " .. OnOff, "Logging has been turned " .. OnOff, "Turned logging " .. OnOff)
|
||||
|
||||
RunConsoleCommand("FAdmin_logging", args[1])
|
||||
|
||||
FAdmin.SaveSetting("FAdmin_logging", args[1])
|
||||
|
||||
return true, OnOff
|
||||
end)
|
||||
logging = GetConVar("FAdmin_logging")
|
||||
end
|
||||
|
||||
function FAdmin.Log(text)
|
||||
if not text or text == "" then return end
|
||||
if not logging or not logging:GetBool() then return end
|
||||
|
||||
ServerLog("[FAdmin] " .. text .. "\n")
|
||||
end
|
||||
|
||||
hook.Add("PlayerGiveSWEP", "FAdmin_Log", function(ply, class)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Gave themself a " .. (class or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedSENT", "FAdmin_Log", function(ply, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not IsValid(ent) then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (ent:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnSWEP", "FAdmin_Log", function(ply, class)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (class or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedProp", "FAdmin_Log", function(ply, model, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not IsValid(ent) then return end
|
||||
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:IsAdmin() then
|
||||
v:PrintMessage(HUD_PRINTCONSOLE, ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (model or "Unknown"))
|
||||
end
|
||||
end
|
||||
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (model or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedNPC", "FAdmin_Log", function(ply, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not IsValid(ent) then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (ent:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedVehicle", "FAdmin_Log", function(ply, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not IsValid(ent) then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (ent:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedEffect", "FAdmin_Log", function(ply, model, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not model then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (model or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawnedRagdoll", "FAdmin_Log", function(ply, model, ent)
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not IsValid(ent) then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned a " .. (model or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("CanTool", "FAdmin_Log", function(ply, tr, toolclass)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Attempted to use tool " .. (toolclass or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("PlayerLeaveVehicle", "FAdmin_Log", function(ply, vehicle)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") exited a " .. (IsValid(vehicle) and vehicle:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("OnNPCKilled", "FAdmin_Log", function(NPC, Killer, Weapon)
|
||||
if not IsValid(NPC) then return end
|
||||
FAdmin.Log(NPC:GetClass() .. " was killed by " .. (IsValid(Killer) and (Killer:IsPlayer() and Killer:Nick() or Killer:GetClass()) or "Unknown") .. " with a " .. (IsValid(Weapon) and Weapon:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("OnPlayerChangedTeam", "FAdmin_Log", function(ply, oldteam, newteam)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") changed from " .. team.GetName(oldteam) .. " to " .. team.GetName(newteam))
|
||||
end)
|
||||
|
||||
hook.Add("WeaponEquip", "FAdmin_Log", function(weapon)
|
||||
timer.Simple(0, function()
|
||||
if not IsValid(weapon) then return end
|
||||
local ply = weapon:GetOwner()
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Attempted to pick up a " .. weapon:GetClass())
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDeath", "FAdmin_Log", function(ply, inflictor, Killer)
|
||||
local Nick = IsValid(ply) and ply:Nick() or "N/A"
|
||||
local SteamID = IsValid(ply) and ply:SteamID() or "N/A"
|
||||
local KillerName = IsValid(Killer) and (Killer:IsPlayer() and Killer:Nick() or Killer:GetClass()) or "N/A"
|
||||
local InflictorName = IsValid(inflictor) and inflictor:GetClass() or "N/A"
|
||||
|
||||
FAdmin.Log(Nick .. " (" .. SteamID .. ") Got killed by " .. KillerName .. " with a " .. InflictorName)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSilentDeath", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Got killed silently")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDisconnected", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Disconnected")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned for the first time")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawn", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Spawned")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpray", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Sprayed his spray")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerEnteredVehicle", "FAdmin_Log", function(ply, vehicle)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Entered " .. (IsValid(vehicle) and vehicle:GetClass() or "Unknown"))
|
||||
end)
|
||||
|
||||
hook.Add("EntityRemoved", "FAdmin_Log", function(ent)
|
||||
if IsValid(ent) and ent:GetClass() == "prop_physics" then
|
||||
FAdmin.Log(ent:GetClass() .. "(" .. (ent:GetModel() or "<no model>") .. ") Got removed")
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerAuthed", "FAdmin_Log", function(ply, SteamID, _)
|
||||
if not IsValid(ply) then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. (SteamID or "Unknown Steam ID") .. ") is Authed")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerNoClip", "FAdmin_Log", function(ply)
|
||||
if not IsValid(ply) or not ply:IsPlayer() then return end
|
||||
FAdmin.Log(ply:Nick() .. " (" .. ply:SteamID() .. ") Attempted to switch noclip")
|
||||
end)
|
||||
|
||||
hook.Add("ShutDown", "FAdmin_Log", function()
|
||||
FAdmin.shuttingDown = true
|
||||
FAdmin.Log("Server succesfully shut down.")
|
||||
end)
|
||||
@@ -0,0 +1,189 @@
|
||||
local showChat = CreateClientConVar("FAdmin_ShowChatNotifications", 1, true, false)
|
||||
|
||||
local HUDNote_c = 0
|
||||
local HUDNote_i = 1
|
||||
local HUDNotes = {}
|
||||
|
||||
--Notify ripped off the Sandbox notify, changed to my likings
|
||||
function FAdmin.Messages.AddMessage(MsgType, Message)
|
||||
local tab = {}
|
||||
tab.text = Message
|
||||
tab.recv = SysTime()
|
||||
tab.velx = 0
|
||||
tab.vely = -5
|
||||
surface.SetFont("GModNotify")
|
||||
local w, _ = surface.GetTextSize(Message)
|
||||
tab.x = ScrW() / 2 + w * 0.5 + (ScrW() / 20)
|
||||
tab.y = ScrH()
|
||||
tab.a = 255
|
||||
local MsgTypeNames = {"ERROR", "NOTIFY", "QUESTION", "GOOD", "BAD"}
|
||||
if not MsgTypeNames[MsgType] then return end
|
||||
tab.col = FAdmin.Messages.MsgTypes[MsgTypeNames[MsgType]].COLOR
|
||||
|
||||
table.insert(HUDNotes, tab)
|
||||
|
||||
HUDNote_c = HUDNote_c + 1
|
||||
HUDNote_i = HUDNote_i + 1
|
||||
|
||||
LocalPlayer():EmitSound("npc/turret_floor/click1.wav", 30, 100)
|
||||
end
|
||||
|
||||
usermessage.Hook("FAdmin_SendMessage", function(u) FAdmin.Messages.AddMessage(u:ReadShort(), u:ReadString()) end)
|
||||
|
||||
|
||||
local function DrawNotice(k, v, i)
|
||||
local H = ScrH() / 1024
|
||||
local x = v.x - 75 * H
|
||||
local y = v.y - 27
|
||||
surface.SetFont("GModNotify")
|
||||
local w, h = surface.GetTextSize(v.text)
|
||||
h = h + 16
|
||||
local col = v.col
|
||||
|
||||
draw.RoundedBox(4, x - w - h + 24, y - 8, w + h - 16, h, col)
|
||||
-- Draw Icon
|
||||
surface.SetDrawColor(255, 255, 255, v.a)
|
||||
|
||||
draw.DrawNonParsedSimpleText(v.text, "GModNotify", x + 1, y + 1, Color(0, 0, 0, v.a * 0.8), TEXT_ALIGN_RIGHT)
|
||||
draw.DrawNonParsedSimpleText(v.text, "GModNotify", x - 1, y - 1, Color(0, 0, 0, v.a * 0.5), TEXT_ALIGN_RIGHT)
|
||||
draw.DrawNonParsedSimpleText(v.text, "GModNotify", x + 1, y - 1, Color(0, 0, 0, v.a * 0.6), TEXT_ALIGN_RIGHT)
|
||||
draw.DrawNonParsedSimpleText(v.text, "GModNotify", x - 1, y + 1, Color(0, 0, 0, v.a * 0.6), TEXT_ALIGN_RIGHT)
|
||||
draw.DrawNonParsedSimpleText(v.text, "GModNotify", x, y, Color(255, 255, 255, v.a), TEXT_ALIGN_RIGHT)
|
||||
local ideal_y = ScrH() - (HUDNote_c - i) * h
|
||||
local ideal_x = ScrW() / 2 + w * 0.5 + (ScrW() / 20)
|
||||
local timeleft = 6 - (SysTime() - v.recv)
|
||||
|
||||
-- Cartoon style about to go thing
|
||||
if (timeleft < 0.8) then
|
||||
ideal_x = ScrW() / 2 + w * 0.5 + 200
|
||||
end
|
||||
|
||||
-- Gone!
|
||||
if (timeleft < 0.5) then
|
||||
ideal_y = ScrH() + 50
|
||||
end
|
||||
|
||||
local spd = RealFrameTime() * 15
|
||||
v.y = v.y + v.vely * spd
|
||||
v.x = v.x + v.velx * spd
|
||||
local dist = ideal_y - v.y
|
||||
v.vely = v.vely + dist * spd * 1
|
||||
|
||||
if (math.abs(dist) < 2 and math.abs(v.vely) < 0.1) then
|
||||
v.vely = 0
|
||||
end
|
||||
|
||||
dist = ideal_x - v.x
|
||||
v.velx = v.velx + dist * spd * 1
|
||||
|
||||
if math.abs(dist) < 2 and math.abs(v.velx) < 0.1 then
|
||||
v.velx = 0
|
||||
end
|
||||
|
||||
-- Friction.. kind of FPS independant.
|
||||
v.velx = v.velx * (0.95 - RealFrameTime() * 8)
|
||||
v.vely = v.vely * (0.95 - RealFrameTime() * 8)
|
||||
end
|
||||
|
||||
local function HUDPaint()
|
||||
if not HUDNotes then return end
|
||||
local i = 0
|
||||
|
||||
for k, v in ipairs(HUDNotes) do
|
||||
if v ~= 0 then
|
||||
i = i + 1
|
||||
DrawNotice(k, v, i)
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in ipairs(HUDNotes) do
|
||||
if v ~= 0 and v.recv + 6 < SysTime() then
|
||||
HUDNotes[k] = 0
|
||||
HUDNote_c = HUDNote_c - 1
|
||||
|
||||
if HUDNote_c == 0 then
|
||||
HUDNotes = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
hook.Add("HUDPaint", "FAdmin_MessagePaint", HUDPaint)
|
||||
|
||||
local function ConsoleMessage(um)
|
||||
MsgC(Color(255, 0, 0, 255), "(FAdmin) ", Color(200, 0, 200, 255), um:ReadString() .. "\n")
|
||||
end
|
||||
usermessage.Hook("FAdmin_ConsoleMessage", ConsoleMessage)
|
||||
|
||||
|
||||
local red = Color(255, 0, 0)
|
||||
local white = Color(190, 190, 190)
|
||||
local brown = Color(102, 51, 0)
|
||||
local blue = Color(102, 0, 255)
|
||||
|
||||
-- Inserts the instigator into a notification message
|
||||
local function insertInstigator(res, instigator, _)
|
||||
table.insert(res, brown)
|
||||
table.insert(res, FAdmin.PlayerName(instigator))
|
||||
end
|
||||
|
||||
-- Inserts the targets into the notification message
|
||||
local function insertTargets(res, _, targets)
|
||||
table.insert(res, blue)
|
||||
table.insert(res, FAdmin.TargetsToString(targets))
|
||||
end
|
||||
|
||||
local modMessage = {
|
||||
instigator = insertInstigator,
|
||||
you = function(res) table.insert(res, brown) table.insert(res, "you") end,
|
||||
targets = insertTargets,
|
||||
}
|
||||
local function showNotification(notification, instigator, targets, extraInfo)
|
||||
local res = {red, "[", white, "FAdmin", red, "] "}
|
||||
|
||||
for _, text in pairs(notification.message) do
|
||||
if modMessage[text] then modMessage[text](res, instigator, targets) continue end
|
||||
|
||||
if string.sub(text, 1, 10) == "extraInfo." then
|
||||
local id = tonumber(string.sub(text, 11))
|
||||
|
||||
table.insert(res, notification.extraInfoColors and notification.extraInfoColors[id] or white)
|
||||
table.insert(res, extraInfo[id])
|
||||
continue
|
||||
end
|
||||
|
||||
table.insert(res, white)
|
||||
table.insert(res, text)
|
||||
end
|
||||
|
||||
if showChat:GetBool() then
|
||||
chat.AddText(unpack(res))
|
||||
else
|
||||
local msgTbl = {}
|
||||
for i = 8, #res, 2 do table.insert(msgTbl, res[i]) end
|
||||
|
||||
FAdmin.Messages.AddMessage(FAdmin.Messages.MsgTypesByName[notification.msgType], table.concat(msgTbl, ""))
|
||||
|
||||
MsgC(unpack(res))
|
||||
Msg("\n")
|
||||
end
|
||||
end
|
||||
|
||||
local function receiveNotification()
|
||||
local id = net.ReadUInt(16)
|
||||
local notification = FAdmin.Notifications[id]
|
||||
local instigator = net.ReadEntity()
|
||||
|
||||
local targets = {}
|
||||
|
||||
if notification.hasTarget then
|
||||
local targetCount = net.ReadUInt(8)
|
||||
for i = 1, targetCount do
|
||||
table.insert(targets, net.ReadEntity())
|
||||
end
|
||||
end
|
||||
|
||||
local extraInfo = notification.readExtraInfo and notification.readExtraInfo()
|
||||
|
||||
showNotification(notification, instigator, targets, extraInfo)
|
||||
end
|
||||
net.Receive("FAdmin_Notification", receiveNotification)
|
||||
@@ -0,0 +1,135 @@
|
||||
FAdmin.Messages = {}
|
||||
|
||||
FAdmin.Messages.MsgTypes = {
|
||||
ERROR = {TEXTURE = "icon16/exclamation.png", COLOR = Color(255,180,0,80)},
|
||||
NOTIFY = {TEXTURE = "vgui/notices/error", COLOR = Color(255,255,0,80)},
|
||||
QUESTION = {TEXTURE = "vgui/notices/hint", COLOR = Color(0,0,255,80)},
|
||||
GOOD = {TEXTURE = "icon16/tick.png", COLOR = Color(0,255,0,80)},
|
||||
BAD = {TEXTURE = "icon16/cross.png", COLOR = Color(255,0,0,80)}
|
||||
}
|
||||
FAdmin.Messages.MsgTypesByName = {
|
||||
ERROR = 1,
|
||||
NOTIFY = 2,
|
||||
QUESTION = 3,
|
||||
GOOD = 4,
|
||||
BAD = 5,
|
||||
}
|
||||
|
||||
function FAdmin.PlayerName(ply)
|
||||
if CLIENT and ply == LocalPlayer() then return "you" end
|
||||
|
||||
if isstring(ply) then return ply end
|
||||
|
||||
return isentity(ply) and (ply:EntIndex() == 0 and "Console" or ply:Nick()) or "unknown"
|
||||
end
|
||||
|
||||
function FAdmin.TargetsToString(targets)
|
||||
if not istable(targets) then
|
||||
return FAdmin.PlayerName(targets)
|
||||
end
|
||||
|
||||
local targetCount = #targets
|
||||
if targetCount == 0 then
|
||||
return "no one"
|
||||
end
|
||||
|
||||
if targetCount == player.GetCount() and targetCount ~= 1 then
|
||||
return "everyone"
|
||||
end
|
||||
|
||||
targets = table.Copy(targets)
|
||||
local names = fn.Map(FAdmin.PlayerName, targets)
|
||||
|
||||
if #names == 1 then
|
||||
return names[1]
|
||||
end
|
||||
|
||||
return table.concat(names, ", ", 1, #names - 1) .. " and " .. names[#names]
|
||||
end
|
||||
|
||||
FAdmin.Notifications = {}
|
||||
|
||||
local validNotification = tc.checkTable{
|
||||
-- A name to identify the notification by
|
||||
name =
|
||||
tc.addHint(
|
||||
isstring,
|
||||
"The name must be a string!"
|
||||
),
|
||||
|
||||
-- Whether the notification applies to some kind of target
|
||||
hasTarget =
|
||||
tc.addHint(
|
||||
tc.optional(isbool),
|
||||
"hasTarget must either be true, false or nil!"
|
||||
),
|
||||
|
||||
-- Who receives the notification. Can be either one of the list or a function that returns a table of players
|
||||
receivers =
|
||||
tc.addHint(
|
||||
fn.FOr{tc.client, isfunction, tc.oneOf{"everyone", "admins", "superadmins", "self", "targets", "involved", "involved+admins", "involved+superadmins"}},
|
||||
"receivers must either be a function returning a table of players or one of 'admins', 'superadmins', 'everyone', 'self', 'targets', 'involved', 'involved+admins', 'involved+superadmins'"
|
||||
),
|
||||
|
||||
-- A table containing the message in parts. There are special strings
|
||||
message =
|
||||
tc.addHint(
|
||||
tc.tableOf(isstring),
|
||||
"The message field must be a table of strings! with special strings 'targets', 'you', 'instigator', 'extraInfo.#', with # a number."
|
||||
),
|
||||
|
||||
-- The message type when chat notifications are disabled. NOTIFY by default
|
||||
msgType =
|
||||
tc.default(
|
||||
"NOTIFY",
|
||||
tc.addHint(
|
||||
tc.oneOf{"ERROR", "NOTIFY", "QUESTION", "GOOD", "BAD"}, "msgType must be one of 'ERROR', 'NOTIFY', 'QUESTION', 'GOOD', 'BAD'"
|
||||
)
|
||||
),
|
||||
|
||||
-- A function that writes extra data in the net message
|
||||
writeExtraInfo =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"writeExtraInfo must be a function"
|
||||
),
|
||||
|
||||
-- A function that reads the written data, formats it and puts it in a table
|
||||
readExtraInfo =
|
||||
tc.addHint(
|
||||
tc.optional(isfunction),
|
||||
"writeExtraInfo must be a function"
|
||||
),
|
||||
|
||||
-- When using extra information, this table contains the colours of the extraInfo messages
|
||||
extraInfoColors =
|
||||
tc.addHint(
|
||||
tc.optional(tc.tableOf(tc.iscolor)),
|
||||
"extraInfoColors must be a table of colours!"
|
||||
),
|
||||
|
||||
-- Whether the notification is to be logged to console
|
||||
logging =
|
||||
tc.default(true,
|
||||
tc.addHint(
|
||||
isbool,
|
||||
"logging must be a boolean!"
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
FAdmin.NotificationNames = {}
|
||||
|
||||
function FAdmin.Messages.RegisterNotification(tbl)
|
||||
local correct, err = validNotification(tbl)
|
||||
|
||||
if not correct then
|
||||
error(string.format("Incorrect notification format for notification '%s'!\n\n%s", istable(tbl) and tbl.name or "unknown", err), 2)
|
||||
end
|
||||
|
||||
local key = table.insert(FAdmin.Notifications, tbl)
|
||||
FAdmin.NotificationNames[tbl.name] = key
|
||||
|
||||
return key
|
||||
end
|
||||
@@ -0,0 +1,137 @@
|
||||
util.AddNetworkString("FAdmin_Notification")
|
||||
|
||||
function FAdmin.Messages.SendMessage(ply, MsgType, text)
|
||||
if ply:EntIndex() == 0 then
|
||||
ServerLog("FAdmin: " .. text .. "\n")
|
||||
print("FAdmin: " .. text)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
umsg.Start("FAdmin_SendMessage", ply)
|
||||
umsg.Short(MsgType)
|
||||
umsg.String(text)
|
||||
umsg.End()
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, text)
|
||||
end
|
||||
|
||||
function FAdmin.Messages.SendMessageAll(text, MsgType)
|
||||
FAdmin.Log("FAdmin message to everyone: " .. text)
|
||||
umsg.Start("FAdmin_SendMessage")
|
||||
umsg.Short(MsgType)
|
||||
umsg.String(text)
|
||||
umsg.End()
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
ply:PrintMessage(HUD_PRINTCONSOLE, text)
|
||||
end
|
||||
end
|
||||
|
||||
function FAdmin.Messages.ConsoleNotify(ply, message)
|
||||
umsg.Start("FAdmin_ConsoleMessage", ply)
|
||||
umsg.String(message)
|
||||
umsg.End()
|
||||
end
|
||||
|
||||
function FAdmin.Messages.ActionMessage(ply, target, messageToPly, MessageToTarget, LogMSG)
|
||||
if not target then return end
|
||||
local Targets = (target.IsPlayer and target:IsPlayer() and target:Nick()) or ""
|
||||
|
||||
local plyNick = IsValid(ply) and ply:IsPlayer() and ply:Nick() or "Console"
|
||||
local plySteamID = IsValid(ply) and ply:IsPlayer() and ply:SteamID() or "Console"
|
||||
local bad = false
|
||||
|
||||
if ply ~= target then
|
||||
if istable(target) then
|
||||
if table.IsEmpty(target) then Targets = "no one" bad = true end
|
||||
for k, v in ipairs(target) do
|
||||
local suffix = ((k == #target-1) and " and ") or (k ~= #target and ", ") or ""
|
||||
local Name = (v == ply and "yourself") or v:Nick()
|
||||
|
||||
if v ~= ply then FAdmin.Messages.SendMessage(v, 2, string.format(MessageToTarget, plyNick)) end
|
||||
Targets = Targets .. Name .. suffix
|
||||
end
|
||||
else
|
||||
FAdmin.Messages.SendMessage(target, 2, string.format(MessageToTarget, plyNick))
|
||||
end
|
||||
|
||||
FAdmin.Messages.SendMessage(ply, bad and 1 or 4, string.format(messageToPly, Targets))
|
||||
|
||||
else
|
||||
FAdmin.Messages.SendMessage(ply, bad and 1 or 4, string.format(messageToPly, "yourself"))
|
||||
end
|
||||
|
||||
local action = plyNick .. " (" .. plySteamID .. ") " .. string.format(LogMSG, Targets:gsub("yourself", "themselves"))
|
||||
FAdmin.Log("FAdmin Action: " .. action)
|
||||
|
||||
local haspriv = fn.Partial(fn.Flip(FAdmin.Access.PlayerHasPrivilege), "SeeAdmins")
|
||||
local plys = fn.Filter(haspriv, player.GetAll())
|
||||
if table.IsEmpty(plys) then return end
|
||||
FAdmin.Messages.ConsoleNotify(plys, action)
|
||||
end
|
||||
|
||||
|
||||
local function logNotification(notification, instigator, targets, extraInfo)
|
||||
local msgs = table.Copy(notification.message)
|
||||
|
||||
local function replace(val)
|
||||
if val == "instigator" then return FAdmin.PlayerName(instigator) end
|
||||
if val == "targets" then return FAdmin.TargetsToString(targets) end
|
||||
if string.sub(val, 1, 10) == "extraInfo." then return tostring(extraInfo[tonumber(string.sub(val, 11))]) end
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
fn.Map(replace, msgs)
|
||||
|
||||
FAdmin.Log(table.concat(msgs))
|
||||
end
|
||||
|
||||
local receiversToPlayers -- allows usage of variable inside
|
||||
receiversToPlayers = {
|
||||
everyone = player.GetAll,
|
||||
admins = function() return table.ClearKeys(fn.Filter(tc.player.IsAdmin, player.GetAll())) end,
|
||||
superadmins = function() return table.ClearKeys(fn.Filter(tc.player.IsSuperAdmin, player.GetAll())) end,
|
||||
self = fn.Id,
|
||||
targets = function(_, t) return t end,
|
||||
involved = function(i, t) local res = table.Copy(istable(t) and t or {t}) table.insert(res, i) return res end,
|
||||
["involved+admins"] = function(i, t) return table.Add(receiversToPlayers.admins(i, t), receiversToPlayers.involved(i, t)) end,
|
||||
["involved+superadmins"] = function(i, t) return table.Add(receiversToPlayers.superadmins(i, t), receiversToPlayers.involved(i, t)) end,
|
||||
}
|
||||
function FAdmin.Messages.FireNotification(name, instigator, targets, extraInfo)
|
||||
local notId = FAdmin.NotificationNames[name]
|
||||
|
||||
if not notId then
|
||||
error(string.format("Notification '%s' does not exist!", name), 2)
|
||||
end
|
||||
|
||||
local notification = FAdmin.Notifications[notId]
|
||||
local receivers = receiversToPlayers[notification.receivers]
|
||||
receivers = receivers and receivers(instigator, targets) or notification.receivers(instigator, targets)
|
||||
|
||||
local targetCount = istable(targets) and #targets or not IsValid(targets) and 0 or 1
|
||||
|
||||
net.Start("FAdmin_Notification")
|
||||
net.WriteUInt(notId, 16)
|
||||
|
||||
net.WriteEntity(instigator)
|
||||
|
||||
if notification.hasTarget then
|
||||
net.WriteUInt(targetCount, 8)
|
||||
|
||||
if istable(targets) then
|
||||
for _, t in ipairs(targets) do
|
||||
net.WriteEntity(t)
|
||||
end
|
||||
else
|
||||
net.WriteEntity(targets)
|
||||
end
|
||||
end
|
||||
|
||||
if notification.writeExtraInfo then notification.writeExtraInfo(extraInfo) end
|
||||
net.Send(receivers)
|
||||
|
||||
if notification.logging then
|
||||
logNotification(notification, instigator, targets, extraInfo)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
local MOTDPage = CreateConVar("_FAdmin_MOTDPage", "default", {FCVAR_REPLICATED, FCVAR_ARCHIVE, FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
if CLIENT then -- I can't be bothered to make a cl_init when there's a shared file with just one line in it.
|
||||
FAdmin.StartHooks["MOTD"] = function()
|
||||
FAdmin.ScoreBoard.Server:AddServerAction("Place MOTD", "fadmin/icons/motd", Color(155, 0, 0, 255), function(ply) return ply:IsSuperAdmin() end, function()
|
||||
RunConsoleCommand("_FAdmin", "CreateMOTD")
|
||||
end)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddServerSetting("Set MOTD page", "fadmin/icons/motd", Color(0, 0, 155, 255), function(ply) return ply:IsSuperAdmin() end, function()
|
||||
local Window = vgui.Create("DFrame")
|
||||
Window:SetTitle("Set MOTD page")
|
||||
Window:SetDraggable(false)
|
||||
Window:ShowCloseButton(false)
|
||||
Window:SetBackgroundBlur(true)
|
||||
Window:SetDrawOnTop(true)
|
||||
|
||||
local InnerPanel = vgui.Create("DPanel", Window)
|
||||
InnerPanel:SetPaintBackground(false) -- clear background
|
||||
|
||||
local Text = vgui.Create("DLabel", InnerPanel)
|
||||
Text:SetText("Set the MOTD page. Click default to reset the MOTD to default.")
|
||||
Text:SizeToContents()
|
||||
Text:SetContentAlignment(5)
|
||||
Text:SetTextColor(color_white)
|
||||
|
||||
local TextEntry = vgui.Create("DTextEntry", InnerPanel)
|
||||
TextEntry:SetText(MOTDPage:GetString())
|
||||
TextEntry.OnEnter = function() Window:Close() RunConsoleCommand("_FAdmin", "motdpage", TextEntry:GetValue()) end
|
||||
function TextEntry:OnFocusChanged(changed)
|
||||
self:RequestFocus()
|
||||
self:SelectAllText(true)
|
||||
end
|
||||
|
||||
local ButtonPanel = vgui.Create("DPanel", Window)
|
||||
ButtonPanel:SetPaintBackground(false) -- clear background
|
||||
ButtonPanel:SetTall(30)
|
||||
|
||||
local Button = vgui.Create("DButton", ButtonPanel)
|
||||
Button:SetText("OK")
|
||||
Button:SizeToContents()
|
||||
Button:SetTall(20)
|
||||
Button:SetWide(Button:GetWide() + 20)
|
||||
Button:SetPos(5, 5)
|
||||
|
||||
Button.DoClick = function()
|
||||
Window:Close()
|
||||
RunConsoleCommand("_FAdmin", "motdpage", TextEntry:GetValue())
|
||||
end
|
||||
|
||||
local ButtonDefault = vgui.Create("DButton", ButtonPanel)
|
||||
ButtonDefault:SetText("Default")
|
||||
ButtonDefault:SizeToContents()
|
||||
ButtonDefault:SetTall(20)
|
||||
ButtonDefault:SetWide(Button:GetWide() + 20)
|
||||
ButtonDefault:SetPos(5, 5)
|
||||
ButtonDefault.DoClick = function() Window:Close() RunConsoleCommand("_FAdmin", "motdpage", "default") end
|
||||
ButtonDefault:MoveRightOf(Button, 5)
|
||||
|
||||
local ButtonCancel = vgui.Create("DButton", ButtonPanel)
|
||||
ButtonCancel:SetText("Cancel")
|
||||
ButtonCancel:SizeToContents()
|
||||
ButtonCancel:SetTall(20)
|
||||
ButtonCancel:SetWide(Button:GetWide() + 20)
|
||||
ButtonCancel:SetPos(5, 5)
|
||||
ButtonCancel.DoClick = function() Window:Close() end
|
||||
ButtonCancel:MoveRightOf(ButtonDefault, 5)
|
||||
|
||||
ButtonPanel:SetWide(Button:GetWide() + 5 + ButtonCancel:GetWide() + 10 + ButtonDefault:GetWide() + 5)
|
||||
|
||||
local w, h = Text:GetSize()
|
||||
w = math.max(w, 400)
|
||||
Window:SetSize(w + 50, h + 25 + 75 + 10)
|
||||
Window:Center()
|
||||
InnerPanel:StretchToParent(5, 25, 5, 45)
|
||||
Text:StretchToParent(5, 5, 5, 35)
|
||||
TextEntry:StretchToParent(5, nil, 5, nil)
|
||||
TextEntry:AlignBottom(5)
|
||||
TextEntry:RequestFocus()
|
||||
ButtonPanel:CenterHorizontal()
|
||||
ButtonPanel:AlignBottom(8)
|
||||
Window:MakePopup()
|
||||
Window:DoModal()
|
||||
end)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,91 @@
|
||||
FAdmin.MOTD = {}
|
||||
local MOTDPage
|
||||
|
||||
sql.Query([[CREATE TABLE IF NOT EXISTS FADMIN_MOTD(
|
||||
'map' TEXT NOT NULL PRIMARY KEY,
|
||||
x INTEGER NOT NULL,
|
||||
y INTEGER NOT NULL,
|
||||
z INTEGER NOT NULL,
|
||||
pitch INTEGER NOT NULL,
|
||||
yaw INTEGER NOT NULL,
|
||||
roll INTEGER NOT NULL
|
||||
);]])
|
||||
|
||||
local MOTD = sql.Query("SELECT * FROM FADMIN_MOTD WHERE LOWER(map) = " .. MySQLite.SQLStr(string.lower(game.GetMap())) .. ";")
|
||||
|
||||
hook.Add("InitPostEntity", "PlaceMOTD", function()
|
||||
if not MOTD or (not MOTD[1] and not MOTD["1"]) then return end
|
||||
MOTD = MOTD[1] or MOTD["1"]
|
||||
|
||||
local ent = ents.Create("fadmin_motd")
|
||||
ent:SetPos(Vector(MOTD.x, MOTD.y, MOTD.z))
|
||||
ent:SetAngles(Angle(MOTD.pitch % 360, MOTD.yaw % 360, MOTD.roll % 360))
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
if file.Exists("FAdmin/CurMOTDPage.txt", "DATA") and file.Read("FAdmin/CurMOTDPage.txt", "DATA") ~= "" then
|
||||
game.ConsoleCommand("_FAdmin_MOTDPage \"" .. file.Read("FAdmin/CurMOTDPage.txt", "DATA") .. "\"\n")
|
||||
end
|
||||
end)
|
||||
|
||||
function FAdmin.MOTD.SaveMOTD(ent, ply)
|
||||
local pos = ent:GetPos()
|
||||
local ang = ent:GetAngles()
|
||||
|
||||
local map, x, y, z, pitch, yaw, roll =
|
||||
string.lower(game.GetMap()),
|
||||
pos.x, pos.y, pos.z,
|
||||
ang.p, ang.y, ang.r
|
||||
if MOTD then
|
||||
sql.Query([[UPDATE FADMIN_MOTD SET ]]
|
||||
.. "x = " .. MySQLite.SQLStr(x) .. ", "
|
||||
.. "y = " .. MySQLite.SQLStr(y) .. ", "
|
||||
.. "z = " .. MySQLite.SQLStr(z) .. ", "
|
||||
.. "pitch = " .. MySQLite.SQLStr(pitch) .. ", "
|
||||
.. "yaw = " .. MySQLite.SQLStr(yaw) .. ", "
|
||||
.. "roll = " .. MySQLite.SQLStr(roll)
|
||||
.. " WHERE map = " .. MySQLite.SQLStr(map) .. ";")
|
||||
else
|
||||
sql.Query([[INSERT INTO FADMIN_MOTD VALUES(]]
|
||||
.. MySQLite.SQLStr(map) .. ", "
|
||||
.. MySQLite.SQLStr(x) .. ", "
|
||||
.. MySQLite.SQLStr(y) .. ", "
|
||||
.. MySQLite.SQLStr(z) .. ", "
|
||||
.. MySQLite.SQLStr(pitch) .. ", "
|
||||
.. MySQLite.SQLStr(yaw) .. ", "
|
||||
.. MySQLite.SQLStr(roll)
|
||||
.. ");")
|
||||
end
|
||||
FAdmin.Messages.SendMessage(ply, 4, "MOTD position saved!")
|
||||
end
|
||||
|
||||
function FAdmin.MOTD.RemoveMOTD(ent, ply)
|
||||
sql.Query("DELETE FROM FADMIN_MOTD WHERE map = " .. MySQLite.SQLStr(string.lower(game.GetMap())) .. ";")
|
||||
FAdmin.Messages.SendMessage(ply, 4, "MOTD removed!")
|
||||
end
|
||||
|
||||
function FAdmin.MOTD.SetMOTDPage(ply, cmd, args)
|
||||
if not args[1] then
|
||||
FAdmin.Messages.SendMessage(ply, 4, "MOTD is set to: " .. MOTDPage:GetString())
|
||||
return false
|
||||
end
|
||||
if ply:EntIndex() ~= 0 and (not ply.IsSuperAdmin or not ply:IsSuperAdmin()) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
RunConsoleCommand("_FAdmin_MOTDPage", args[1])
|
||||
file.Write("FAdmin/CurMOTDPage.txt", args[1])
|
||||
|
||||
return true, args[1]
|
||||
end
|
||||
|
||||
local function CreateMOTD(ply)
|
||||
if IsValid(ply) and not ply:IsSuperAdmin() then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
local MOTDEnt = ents.Create("fadmin_motd")
|
||||
MOTDEnt:SpawnFunction(ply, ply:GetEyeTrace())
|
||||
|
||||
return true, MOTDEnt
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["MOTD"] = function()
|
||||
MOTDPage = GetConVar("_FAdmin_MOTDPage")
|
||||
FAdmin.Commands.AddCommand("MOTDPage", FAdmin.MOTD.SetMOTDPage)
|
||||
FAdmin.Commands.AddCommand("CreateMOTD", CreateMOTD)
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
local AdminsCanPickUpPlayers = CreateConVar("AdminsCanPickUpPlayers", 1, {FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE})
|
||||
local PlayersCanPickUpPlayers = CreateConVar("PlayersCanPickUpPlayers", 0, {FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
FAdmin.StartHooks["PickUpPlayers"] = function()
|
||||
FAdmin.Access.AddPrivilege("PickUpPlayers", 2)
|
||||
FAdmin.ScoreBoard.Server:AddPlayerAction(function() return (AdminsCanPickUpPlayers:GetBool() and "Disable" or "Enable") .. " Admin>Player pickup" end,
|
||||
function() return "fadmin/icons/pickup", AdminsCanPickUpPlayers:GetBool() and "fadmin/icons/disable" end, Color(0, 155, 0, 255), function(ply) return ply:IsSuperAdmin() end, function(button)
|
||||
button:SetImage2((not AdminsCanPickUpPlayers:GetBool() and "fadmin/icons/disable") or "null")
|
||||
button:SetText((not AdminsCanPickUpPlayers:GetBool() and "Disable" or "Enable") .. " Admin>Player pickup")
|
||||
button:GetParent():InvalidateLayout()
|
||||
RunConsoleCommand("_FAdmin", "AdminsCanPickUpPlayers", AdminsCanPickUpPlayers:GetBool() and "0" or "1")
|
||||
end)
|
||||
|
||||
FAdmin.ScoreBoard.Server:AddPlayerAction(function() return (PlayersCanPickUpPlayers:GetBool() and "Disable" or "Enable") .. " Player>Player pickup" end,
|
||||
function() return "fadmin/icons/pickup", PlayersCanPickUpPlayers:GetBool() and "fadmin/icons/disable" end, Color(0, 155, 0, 255), function(ply) return ply:IsSuperAdmin() end, function(button)
|
||||
button:SetImage2((not PlayersCanPickUpPlayers:GetBool() and "fadmin/icons/disable") or "null")
|
||||
button:SetText((not PlayersCanPickUpPlayers:GetBool() and "Disable" or "Enable") .. " Player>Player pickup")
|
||||
button:GetParent():InvalidateLayout()
|
||||
RunConsoleCommand("_FAdmin", "PlayersCanPickUpPlayers", PlayersCanPickUpPlayers:GetBool() and "0" or "1")
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
local AdminsCanPickUpPlayers = CreateConVar("AdminsCanPickUpPlayers", 1, {FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE})
|
||||
local PlayersCanPickUpPlayers = CreateConVar("PlayersCanPickUpPlayers", 0, {FCVAR_REPLICATED, FCVAR_SERVER_CAN_EXECUTE})
|
||||
|
||||
hook.Add("PhysgunPickup", "FAdmin_PickUpPlayers", function(ply, ent)
|
||||
if not IsValid(ent) or not ent:IsPlayer() then return end
|
||||
|
||||
if PlayersCanPickUpPlayers:GetBool() or AdminsCanPickUpPlayers:GetBool() and
|
||||
FAdmin.Access.PlayerHasPrivilege(ply, "PickUpPlayers", ent) and tobool(ply:GetInfo("cl_pickupplayers")) then
|
||||
ent:SetMoveType(MOVETYPE_NONE)
|
||||
ent:Freeze(true)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PhysgunDrop", "FAdmin_PickUpPlayers", function(ply, ent)
|
||||
if IsValid(ent) and ent:IsPlayer() then
|
||||
ent:SetMoveType(MOVETYPE_WALK)
|
||||
ent:Freeze(false)
|
||||
end
|
||||
end)
|
||||
|
||||
local function ChangeAdmin(ply, cmd, args)
|
||||
if not ply:IsSuperAdmin() then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if not args[1] then return false end
|
||||
|
||||
local Value = tonumber(args[1])
|
||||
if Value ~= 1 and Value ~= 0 then return false end
|
||||
RunConsoleCommand("AdminsCanPickUpPlayers", Value)
|
||||
|
||||
FAdmin.SaveSetting("AdminsCanPickUpPlayers", Value)
|
||||
|
||||
local OnOff = (tobool(Value) and "on") or "off"
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), ply:Nick() .. " turned Admin>Player pickup " .. OnOff, "Admin>Player pickup has been turned " .. OnOff, "Turned Admin>Player pickup " .. OnOff)
|
||||
|
||||
return true, OnOff
|
||||
end
|
||||
|
||||
local function ChangeUser(ply, cmd, args)
|
||||
if not ply:IsSuperAdmin() then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if not args[1] then return false end
|
||||
|
||||
local Value = tonumber(args[1])
|
||||
if Value ~= 1 and Value ~= 0 then return false end
|
||||
RunConsoleCommand("PlayersCanPickUpPlayers", Value)
|
||||
|
||||
FAdmin.SaveSetting("PlayersCanPickUpPlayers", Value)
|
||||
|
||||
local OnOff = (tobool(Value) and "on") or "off"
|
||||
FAdmin.Messages.ActionMessage(ply, player.GetAll(), ply:Nick() .. " turned Player>Player pickup " .. OnOff, "Player>Player pickup has been turned " .. OnOff, "Turned Player>Player pickup " .. OnOff)
|
||||
|
||||
return true, OnOff
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["PickUpPlayers"] = function()
|
||||
FAdmin.Access.AddPrivilege("PickUpPlayers", 2)
|
||||
FAdmin.Commands.AddCommand("AdminsCanPickUpPlayers", ChangeAdmin)
|
||||
FAdmin.Commands.AddCommand("PlayersCanPickUpPlayers", ChangeUser)
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
FAdmin.StartHooks["zzSetTeam"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "setteam",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " set the team of ", "targets", " to ", "extraInfo.1"},
|
||||
readExtraInfo = function()
|
||||
return {team.GetName(net.ReadUInt(16))}
|
||||
end,
|
||||
extraInfoColors = {Color(255, 102, 0)}
|
||||
}
|
||||
|
||||
FAdmin.Access.AddPrivilege("SetTeam", 2)
|
||||
FAdmin.Commands.AddCommand("SetTeam", nil, "<Player>", "<Team>")
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Set team", "fadmin/icons/changeteam", Color(0, 200, 0, 255),
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "SetTeam", ply) end, function(ply, button)
|
||||
local menu = DermaMenu()
|
||||
|
||||
local Padding = vgui.Create("DPanel")
|
||||
Padding:SetPaintBackgroundEnabled(false)
|
||||
Padding:SetSize(1,5)
|
||||
menu:AddPanel(Padding)
|
||||
|
||||
local Title = vgui.Create("DLabel")
|
||||
Title:SetText(" Teams:\n")
|
||||
Title:SetFont("UiBold")
|
||||
Title:SizeToContents()
|
||||
Title:SetTextColor(color_black)
|
||||
|
||||
menu:AddPanel(Title)
|
||||
for k, v in SortedPairsByMemberValue(team.GetAllTeams(), "Name") do
|
||||
local uid = ply:UserID()
|
||||
menu:AddOption(v.Name, function() RunConsoleCommand("_FAdmin", "setteam", uid, k) end)
|
||||
end
|
||||
menu:Open()
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
local function checkDarkRP(ply, target, t)
|
||||
if not DarkRP then return true end
|
||||
|
||||
local TEAM = RPExtraTeams[t]
|
||||
if not TEAM then return true end
|
||||
|
||||
if TEAM.customCheck then
|
||||
local ret = TEAM.customCheck(target)
|
||||
if ret ~= nil and not (ply:IsAdmin() and GAMEMODE.Config.adminBypassJobRestrictions) then return ret end
|
||||
end
|
||||
|
||||
local hookValue = hook.Call("playerCanChangeTeam", nil, target, t, true)
|
||||
if hookValue == false then return false end
|
||||
|
||||
local a = TEAM.admin
|
||||
if a > 0 and not target:IsAdmin()
|
||||
or a > 1 and not target:IsSuperAdmin()
|
||||
then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function SetTeam(ply, cmd, args)
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
local targetsSet = {}
|
||||
for k, v in pairs(team.GetAllTeams()) do
|
||||
if k == tonumber(args[2]) or string.lower(v.Name) == string.lower(args[2] or "") then
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "SetTeam", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
local setTeam = target.changeTeam or target.SetTeam -- DarkRP compatibility
|
||||
if IsValid(target) and checkDarkRP(ply, target, k) then
|
||||
setTeam(target, k, true)
|
||||
table.insert(targetsSet, target)
|
||||
end
|
||||
end
|
||||
|
||||
if not table.IsEmpty(targetsSet) then
|
||||
FAdmin.Messages.FireNotification("setteam", ply, targetsSet, {k})
|
||||
else
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Could not set team")
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return true, targets
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["zzSetTeam"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "setteam",
|
||||
hasTarget = true,
|
||||
receivers = "everyone",
|
||||
writeExtraInfo = function(info) net.WriteUInt(info[1], 16) end,
|
||||
message = {"instigator", " set the team of ", "targets", " to ", "extraInfo.1"},
|
||||
}
|
||||
|
||||
FAdmin.Commands.AddCommand("SetTeam", SetTeam)
|
||||
|
||||
FAdmin.Access.AddPrivilege("SetTeam", 2)
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
FAdmin.StartHooks["Chatmute"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "chatmute",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " chat muted ", "targets", " ", "extraInfo.1"},
|
||||
readExtraInfo = function()
|
||||
local time = net.ReadUInt(16)
|
||||
|
||||
return {time == 0 and FAdmin.PlayerActions.commonTimes[time] or string.format("for %s", FAdmin.PlayerActions.commonTimes[time] or (time .. " seconds"))}
|
||||
end
|
||||
}
|
||||
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "chatunmute",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " chat unmuted ", "targets"},
|
||||
}
|
||||
|
||||
FAdmin.Access.AddPrivilege("Chatmute", 2)
|
||||
FAdmin.Commands.AddCommand("Chatmute", nil, "<Player>")
|
||||
FAdmin.Commands.AddCommand("UnChatmute", nil, "<Player>")
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddActionButton(function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_chatmuted") then return "Unmute chat" end
|
||||
return "Mute chat"
|
||||
end, function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_chatmuted") then return "fadmin/icons/chatmute" end
|
||||
return "fadmin/icons/chatmute", "fadmin/icons/disable"
|
||||
end, Color(255, 130, 0, 255),
|
||||
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "Chatmute", ply) end, function(ply, button)
|
||||
if not ply:FAdmin_GetGlobal("FAdmin_chatmuted") then
|
||||
FAdmin.PlayerActions.addTimeMenu(function(secs)
|
||||
RunConsoleCommand("_FAdmin", "chatmute", ply:UserID(), secs)
|
||||
button:SetImage2("null")
|
||||
button:SetText("Unmute chat")
|
||||
button:GetParent():InvalidateLayout()
|
||||
end)
|
||||
else
|
||||
RunConsoleCommand("_FAdmin", "UnChatmute", ply:UserID())
|
||||
end
|
||||
|
||||
button:SetImage2("fadmin/icons/disable")
|
||||
button:SetText("Mute chat")
|
||||
button:GetParent():InvalidateLayout()
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
local function MuteChat(ply, cmd, args)
|
||||
if not args[1] then return false end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1]) or {}
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
local time = tonumber(args[2] or 0)
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Chatmute", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and not target:FAdmin_GetGlobal("FAdmin_chatmuted") then
|
||||
target:FAdmin_SetGlobal("FAdmin_chatmuted", true)
|
||||
|
||||
if time == 0 then continue end
|
||||
|
||||
timer.Simple(time, function()
|
||||
if not IsValid(target) or not target:FAdmin_GetGlobal("FAdmin_chatmuted") then return false end
|
||||
target:FAdmin_SetGlobal("FAdmin_chatmuted", false)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
FAdmin.Messages.FireNotification("chatmute", ply, targets, {time})
|
||||
|
||||
return true, targets, time
|
||||
end
|
||||
|
||||
local function UnMuteChat(ply, cmd, args)
|
||||
if not args[1] then return false end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Chatmute", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and target:FAdmin_GetGlobal("FAdmin_chatmuted") then
|
||||
target:FAdmin_SetGlobal("FAdmin_chatmuted", false)
|
||||
end
|
||||
end
|
||||
FAdmin.Messages.FireNotification("chatunmute", ply, targets)
|
||||
|
||||
return true, targets
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["Chatmute"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "chatmute",
|
||||
hasTarget = true,
|
||||
receivers = "involved+admins",
|
||||
writeExtraInfo = function(info) net.WriteUInt(info[1], 16) end,
|
||||
message = {"instigator", " chat muted ", "targets", " ", "extraInfo.1"},
|
||||
}
|
||||
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "chatunmute",
|
||||
hasTarget = true,
|
||||
receivers = "involved+admins",
|
||||
message = {"instigator", " chat unmuted ", "targets"},
|
||||
}
|
||||
|
||||
FAdmin.Commands.AddCommand("Chatmute", MuteChat)
|
||||
FAdmin.Commands.AddCommand("UnChatmute", UnMuteChat)
|
||||
|
||||
FAdmin.Access.AddPrivilege("Chatmute", 2)
|
||||
end
|
||||
|
||||
hook.Add("PlayerSay", "FAdmin_Chatmute", function(ply, text, Team, dead)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_chatmuted") then return "" end
|
||||
end)
|
||||
@@ -0,0 +1,26 @@
|
||||
FAdmin.StartHooks["zz_Cloak"] = function()
|
||||
FAdmin.Access.AddPrivilege("Cloak", 2)
|
||||
FAdmin.Commands.AddCommand("Cloak", nil, "<Player>")
|
||||
FAdmin.Commands.AddCommand("Uncloak", nil, "<Player>")
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddActionButton(function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_cloaked") then return "Uncloak" end
|
||||
return "Cloak"
|
||||
end, function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_cloaked") then return "fadmin/icons/cloak", "fadmin/icons/disable" end
|
||||
return "fadmin/icons/cloak"
|
||||
end, Color(0, 200, 0, 255),
|
||||
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "Cloak", ply) end, function(ply, button)
|
||||
if not ply:FAdmin_GetGlobal("FAdmin_cloaked") then
|
||||
RunConsoleCommand("_FAdmin", "Cloak", ply:UserID())
|
||||
else
|
||||
RunConsoleCommand("_FAdmin", "Uncloak", ply:UserID())
|
||||
end
|
||||
|
||||
if not ply:FAdmin_GetGlobal("FAdmin_cloaked") then button:SetImage2("fadmin/icons/disable") button:SetText("Uncloak") button:GetParent():InvalidateLayout() return end
|
||||
button:SetImage2("null")
|
||||
button:SetText("Cloak")
|
||||
button:GetParent():InvalidateLayout()
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,89 @@
|
||||
local CloakThink
|
||||
|
||||
local function Cloak(ply, cmd, args)
|
||||
local targets = FAdmin.FindPlayer(args[1]) or {ply}
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Cloak", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and not target:FAdmin_GetGlobal("FAdmin_cloaked") then
|
||||
target:FAdmin_SetGlobal("FAdmin_cloaked", true)
|
||||
target:SetNoDraw(true)
|
||||
for _, v in ipairs(target:GetWeapons()) do
|
||||
v:SetNoDraw(true)
|
||||
end
|
||||
|
||||
for _, v in ipairs(ents.FindByClass("physgun_beam")) do
|
||||
if v:GetParent() == target then
|
||||
v:SetNoDraw(true)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("Think", "FAdmin_Cloak", CloakThink)
|
||||
end
|
||||
end
|
||||
FAdmin.Messages.ActionMessage(ply, targets, "You have cloaked %s", "You were cloaked by %s", "Cloaked %s")
|
||||
|
||||
return true, targets
|
||||
end
|
||||
|
||||
local function UnCloak(ply, cmd, args)
|
||||
local targets = FAdmin.FindPlayer(args[1]) or {ply}
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Cloak", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and target:FAdmin_GetGlobal("FAdmin_cloaked") then
|
||||
target:FAdmin_SetGlobal("FAdmin_cloaked", false)
|
||||
|
||||
target:SetNoDraw(false)
|
||||
|
||||
for _, v in ipairs(target:GetWeapons()) do
|
||||
v:SetNoDraw(false)
|
||||
end
|
||||
|
||||
for _, v in ipairs(ents.FindByClass("physgun_beam")) do
|
||||
if v:GetParent() == target then
|
||||
v:SetNoDraw(false)
|
||||
end
|
||||
end
|
||||
|
||||
target.FAdmin_CloakWeapon = nil
|
||||
|
||||
local RemoveThink = true
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:FAdmin_GetGlobal("FAdmin_cloaked") then
|
||||
RemoveThink = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if RemoveThink then hook.Remove("Think", "FAdmin_Cloak") end
|
||||
end
|
||||
end
|
||||
FAdmin.Messages.ActionMessage(ply, targets, "You have uncloaked %s", "You were uncloaked by %s", "Uncloaked %s")
|
||||
|
||||
return true, targets
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["Cloak"] = function()
|
||||
FAdmin.Commands.AddCommand("Cloak", Cloak)
|
||||
FAdmin.Commands.AddCommand("Uncloak", UnCloak)
|
||||
|
||||
FAdmin.Access.AddPrivilege("Cloak", 2)
|
||||
end
|
||||
|
||||
function CloakThink()
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
local ActiveWeapon = v:GetActiveWeapon()
|
||||
if v:FAdmin_GetGlobal("FAdmin_cloaked") and ActiveWeapon:IsValid() and ActiveWeapon ~= v.FAdmin_CloakWeapon then
|
||||
v.FAdmin_CloakWeapon = ActiveWeapon
|
||||
ActiveWeapon:SetNoDraw(true)
|
||||
|
||||
if ActiveWeapon:GetClass() == "weapon_physgun" then
|
||||
for a,b in ipairs(ents.FindByClass("physgun_beam")) do
|
||||
if b:GetParent() == v then
|
||||
b:SetNoDraw(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,48 @@
|
||||
FAdmin.StartHooks["Freeze"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "freeze",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " froze ", "targets", " ", "extraInfo.1"},
|
||||
readExtraInfo = function()
|
||||
local time = net.ReadUInt(16)
|
||||
|
||||
return {time == 0 and FAdmin.PlayerActions.commonTimes[time] or string.format("for %s", FAdmin.PlayerActions.commonTimes[time] or (time .. " seconds"))}
|
||||
end
|
||||
}
|
||||
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "unfreeze",
|
||||
hasTarget = true,
|
||||
message = {"instigator", " unfroze ", "targets"},
|
||||
}
|
||||
|
||||
|
||||
FAdmin.Access.AddPrivilege("Freeze", 2)
|
||||
FAdmin.Commands.AddCommand("freeze", nil, "<Player>")
|
||||
FAdmin.Commands.AddCommand("unfreeze", nil, "<Player>")
|
||||
|
||||
FAdmin.ScoreBoard.Player:AddActionButton(function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_frozen") then return "Unfreeze" end
|
||||
return "Freeze"
|
||||
end, function(ply)
|
||||
if ply:FAdmin_GetGlobal("FAdmin_frozen") then return "fadmin/icons/freeze", "fadmin/icons/disable" end
|
||||
return "fadmin/icons/freeze"
|
||||
end, Color(255, 130, 0, 255),
|
||||
|
||||
function(ply) return FAdmin.Access.PlayerHasPrivilege(LocalPlayer(), "Freeze", ply) end, function(ply, button)
|
||||
if not ply:FAdmin_GetGlobal("FAdmin_frozen") then
|
||||
FAdmin.PlayerActions.addTimeMenu(function(secs)
|
||||
RunConsoleCommand("_FAdmin", "freeze", ply:UserID(), secs)
|
||||
button:SetImage2("fadmin/icons/disable")
|
||||
button:SetText("Unfreeze")
|
||||
button:GetParent():InvalidateLayout()
|
||||
end)
|
||||
else
|
||||
RunConsoleCommand("_FAdmin", "unfreeze", ply:UserID())
|
||||
end
|
||||
|
||||
button:SetImage2("null")
|
||||
button:SetText("Freeze")
|
||||
button:GetParent():InvalidateLayout()
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,79 @@
|
||||
local function Freeze(ply, cmd, args)
|
||||
if not args[1] then return false end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
local time = tonumber(args[2]) or 0
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Freeze", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and not target:FAdmin_GetGlobal("FAdmin_frozen") then
|
||||
target:FAdmin_SetGlobal("FAdmin_frozen", true)
|
||||
target:Lock()
|
||||
|
||||
if time == 0 then continue end
|
||||
|
||||
timer.Simple(time, function()
|
||||
if not IsValid(target) or not target:FAdmin_GetGlobal("FAdmin_frozen") then return end
|
||||
target:FAdmin_SetGlobal("FAdmin_frozen", false)
|
||||
target:UnLock()
|
||||
end)
|
||||
end
|
||||
end
|
||||
FAdmin.Messages.FireNotification("freeze", ply, targets, {time})
|
||||
|
||||
return true, targets, time
|
||||
end
|
||||
|
||||
local function Unfreeze(ply, cmd, args)
|
||||
if not args[1] then return false end
|
||||
|
||||
local targets = FAdmin.FindPlayer(args[1])
|
||||
if not targets or #targets == 1 and not IsValid(targets[1]) then
|
||||
FAdmin.Messages.SendMessage(ply, 1, "Player not found")
|
||||
return false
|
||||
end
|
||||
|
||||
for _, target in ipairs(targets) do
|
||||
if not FAdmin.Access.PlayerHasPrivilege(ply, "Freeze", target) then FAdmin.Messages.SendMessage(ply, 5, "No access!") return false end
|
||||
if IsValid(target) and target:FAdmin_GetGlobal("FAdmin_frozen") then
|
||||
target:FAdmin_SetGlobal("FAdmin_frozen", false)
|
||||
target:UnLock()
|
||||
end
|
||||
end
|
||||
|
||||
FAdmin.Messages.FireNotification("unfreeze", ply, targets)
|
||||
|
||||
return true, targets
|
||||
end
|
||||
|
||||
FAdmin.StartHooks["Freeze"] = function()
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "freeze",
|
||||
hasTarget = true,
|
||||
receivers = "involved+admins",
|
||||
writeExtraInfo = function(info) net.WriteUInt(info[1], 16) end,
|
||||
message = {"instigator", " froze ", "targets", " ", "extraInfo.1"},
|
||||
}
|
||||
|
||||
FAdmin.Messages.RegisterNotification{
|
||||
name = "unfreeze",
|
||||
hasTarget = true,
|
||||
receivers = "involved+admins",
|
||||
message = {"instigator", " unfroze ", "targets"},
|
||||
}
|
||||
|
||||
FAdmin.Commands.AddCommand("freeze", Freeze)
|
||||
FAdmin.Commands.AddCommand("unfreeze", Unfreeze)
|
||||
|
||||
FAdmin.Access.AddPrivilege("Freeze", 2)
|
||||
end
|
||||
|
||||
local disallow = function(ply) if ply:FAdmin_GetGlobal("FAdmin_frozen") then return false end end
|
||||
|
||||
hook.Add("PlayerSpawnObject", "FAdmin_jail", disallow)
|
||||
hook.Add("CanPlayerSuicide", "FAdmin_jail", disallow)
|
||||
@@ -0,0 +1,164 @@
|
||||
-- Controls for the give weapons menu. These are litterally copied and edited from the garry's mod code.
|
||||
-- Remaking them in case the gamemode is not derived from sandbox
|
||||
-- Copying from garry's mod code because I'm lazy and because it looks good.
|
||||
|
||||
|
||||
-- Weapon icon:
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
self:SetSize(83, 83)
|
||||
|
||||
self.Label = vgui.Create("DLabel", self)
|
||||
|
||||
self:SetKeepAspect(true)
|
||||
self:SetDrawBorder(true)
|
||||
self.m_Image:SetPaintedManually(true)
|
||||
end
|
||||
|
||||
|
||||
function PANEL:PerformLayout()
|
||||
self.Label:SizeToContents()
|
||||
self.Label:SetFont("TabLarge")
|
||||
self.Label:SetTextColor(color_white)
|
||||
self.Label:SetContentAlignment(5)
|
||||
self.Label:SetWide(self:GetWide())
|
||||
self.Label:AlignBottom(2)
|
||||
|
||||
DImageButton.PerformLayout(self)
|
||||
|
||||
if self.imgAdmin then
|
||||
self.imgAdmin:SizeToContents()
|
||||
self.imgAdmin:AlignTop(4)
|
||||
self.imgAdmin:AlignRight(4)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateAdminIcon()
|
||||
self.imgAdmin = vgui.Create("DImage", self)
|
||||
self.imgAdmin:SetImage("icon16/shield.png") -- SilkIcons are now merged into GMOD as materials/icon16
|
||||
self.imgAdmin:SetTooltip("#Admin Only")
|
||||
end
|
||||
|
||||
function PANEL:Paint()
|
||||
local w, h = self:GetSize()
|
||||
self.m_Image:Paint()
|
||||
|
||||
surface.SetDrawColor(30, 30, 30, 200)
|
||||
surface.DrawRect(0, h - 16, w, 16)
|
||||
end
|
||||
|
||||
function PANEL:Setup(NiceName, SpawnName, IconMaterial, AdminOnly, Parent, IsAmmo)
|
||||
self.Label:SetText(DarkRP.deLocalise(NiceName))
|
||||
|
||||
self.DoClick = function() Parent:DoGiveWeapon(SpawnName, IsAmmo) end
|
||||
self.DoRightClick = function() end
|
||||
|
||||
if not IconMaterial then
|
||||
IconMaterial = "VGUI/entities/" .. SpawnName
|
||||
end
|
||||
|
||||
self:SetOnViewMaterial(IconMaterial, "vgui/swepicon")
|
||||
|
||||
if AdminOnly then self:CreateAdminIcon() end
|
||||
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
local WeaponIcon = vgui.RegisterTable(PANEL, "DImageButton")
|
||||
|
||||
-- Full panel:
|
||||
local PANEL2 = {}
|
||||
|
||||
function PANEL2:Init()
|
||||
self.PanelList = vgui.Create("DPanelList", self)
|
||||
self.PanelList:SetPadding(4)
|
||||
self.PanelList:SetSpacing(2)
|
||||
self.PanelList:EnableVerticalScrollbar(true)
|
||||
end
|
||||
|
||||
function PANEL2:BuildList()
|
||||
self.PanelList:Clear()
|
||||
|
||||
if not self.HideAmmo then
|
||||
local AmmoCat = vgui.Create("DCollapsibleCategory", self)
|
||||
self.PanelList:AddItem(AmmoCat)
|
||||
AmmoCat:SetLabel("Give ammo")
|
||||
|
||||
local AmmoPan = vgui.Create("DPanelList")
|
||||
AmmoCat:SetContents(AmmoPan)
|
||||
AmmoPan:EnableHorizontal(true)
|
||||
AmmoPan:SetPaintBackground(false)
|
||||
AmmoPan:SetSpacing(2)
|
||||
AmmoPan:SetPadding(2)
|
||||
AmmoPan:SetAutoSize(true)
|
||||
|
||||
for k in SortedPairs(FAdmin.AmmoTypes) do
|
||||
local Icon = vgui.CreateFromTable(WeaponIcon, self)
|
||||
Icon:Setup(k, k, "spawnicons/models/items/boxmrounds60x60.png", false, self, true) -- Gets created clientside by GMOD when someone is after that model, or trying to buy ammo.
|
||||
AmmoPan:AddItem(Icon)
|
||||
end
|
||||
end
|
||||
|
||||
local Weapons = weapons.GetList()
|
||||
local Categorised = {}
|
||||
|
||||
Categorised["Half-life 2"] = {}
|
||||
for k, weapon in pairs(FAdmin.HL2Guns) do
|
||||
table.insert(Categorised["Half-life 2"], {PrintName = k, ClassName = weapon, Spawnable = true,
|
||||
Author = "Half-life 2",
|
||||
Contact = "gaben@valvesoftware.com",
|
||||
Instructions = "Shoot!"})
|
||||
end
|
||||
|
||||
for k, weapon in pairs(Weapons) do
|
||||
weapon = weapons.Get(weapon.ClassName)
|
||||
Weapons[k] = weapon
|
||||
weapon.Category = weapon.Category or "Other"
|
||||
|
||||
if not weapon.Spawnable and not weapon.AdminSpawnable then
|
||||
Weapons[k] = nil
|
||||
else
|
||||
Categorised[weapon.Category] = Categorised[weapon.Category] or {}
|
||||
table.insert(Categorised[weapon.Category], weapon)
|
||||
Weapons[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
Weapons = nil
|
||||
|
||||
for CategoryName, v in SortedPairs(Categorised) do
|
||||
local Category = vgui.Create("DCollapsibleCategory", self)
|
||||
self.PanelList:AddItem(Category)
|
||||
Category:SetLabel(CategoryName)
|
||||
Category:SetCookieName("WeaponSpawn." .. CategoryName)
|
||||
|
||||
local Content = vgui.Create("DPanelList")
|
||||
Category:SetContents(Content)
|
||||
Content:EnableHorizontal(true)
|
||||
Content:SetPaintBackground(false)
|
||||
Content:SetSpacing(2)
|
||||
Content:SetPadding(2)
|
||||
Content:SetAutoSize(true)
|
||||
|
||||
for _, WeaponTable in SortedPairsByMemberValue(v, "PrintName") do
|
||||
local Icon = vgui.CreateFromTable(WeaponIcon, self)
|
||||
Icon:Setup(WeaponTable.PrintName or WeaponTable.ClassName, WeaponTable.ClassName, WeaponTable.SpawnMenuIcon, WeaponTable.AdminSpawnable and not WeaponTable.Spawnable, self)
|
||||
|
||||
local Tooltip = Format("Name: %s", WeaponTable.PrintName)
|
||||
if WeaponTable.Author ~= "" then Tooltip = Format("%s\nAuthor: %s", Tooltip, WeaponTable.Author) end
|
||||
if WeaponTable.Contact ~= "" then Tooltip = Format("%s\nContact: %s", Tooltip, WeaponTable.Contact) end
|
||||
if WeaponTable.Instructions ~= "" then Tooltip = Format("%s\n\n%s", Tooltip, WeaponTable.Instructions) end
|
||||
|
||||
Icon:SetTooltip(Tooltip)
|
||||
Content:AddItem(Icon)
|
||||
end
|
||||
end
|
||||
self.PanelList:InvalidateLayout()
|
||||
end
|
||||
|
||||
function PANEL2:PerformLayout()
|
||||
self.PanelList:StretchToParent(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
derma.DefineControl("FAdmin_weaponPanel", "Weapon panel for giving weapons in FAdmin", PANEL2, "Panel")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user