Initial commit
This commit is contained in:
449
gamemodes/darkrp/gamemode/modules/fspectate/cl_init.lua
Normal file
449
gamemodes/darkrp/gamemode/modules/fspectate/cl_init.lua
Normal file
@@ -0,0 +1,449 @@
|
||||
FSpectate = {}
|
||||
|
||||
local stopSpectating, startFreeRoam
|
||||
local isSpectating = false
|
||||
local specEnt
|
||||
local thirdperson = true
|
||||
local isRoaming = false
|
||||
local roamPos -- the position when roaming free
|
||||
local roamVelocity = Vector(0)
|
||||
local thirdPersonDistance = 100
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Retrieve the current spectated player
|
||||
---------------------------------------------------------------------------*/
|
||||
function FSpectate.getSpecEnt()
|
||||
if isSpectating and not isRoaming then
|
||||
return IsValid(specEnt) and specEnt or nil
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
startHooks
|
||||
FAdmin tab buttons
|
||||
---------------------------------------------------------------------------*/
|
||||
hook.Add("Initialize", "FSpectate", function()
|
||||
surface.CreateFont("UiBold", {
|
||||
size = 16,
|
||||
weight = 800,
|
||||
antialias = true,
|
||||
shadow = false,
|
||||
font = "Verdana"})
|
||||
|
||||
if not FAdmin then return end
|
||||
FAdmin.StartHooks["zzSpectate"] = function()
|
||||
FAdmin.Commands.AddCommand("Spectate", nil, "<Player>")
|
||||
|
||||
-- Right click option
|
||||
FAdmin.ScoreBoard.Main.AddPlayerRightClick("Spectate", function(ply)
|
||||
if not IsValid(ply) then return end
|
||||
RunConsoleCommand("FSpectate", ply:UserID())
|
||||
end)
|
||||
|
||||
local canSpectate = false
|
||||
local function calcAccess()
|
||||
CAMI.PlayerHasAccess(LocalPlayer(), "FSpectate", function(b, _)
|
||||
canSpectate = b
|
||||
end)
|
||||
end
|
||||
|
||||
-- Spectate option in player menu
|
||||
FAdmin.ScoreBoard.Player:AddActionButton("Spectate", "fadmin/icons/spectate", Color(0, 200, 0, 255), function(ply) calcAccess() return canSpectate and ply ~= LocalPlayer() end, function(ply)
|
||||
if not IsValid(ply) then return end
|
||||
RunConsoleCommand("FSpectate", ply:UserID())
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Get the thirdperson position
|
||||
---------------------------------------------------------------------------*/
|
||||
local function getThirdPersonPos(ent)
|
||||
local aimvector = LocalPlayer():GetAimVector()
|
||||
local startPos = ent:IsPlayer() and ent:GetShootPos() or ent:LocalToWorld(ent:OBBCenter())
|
||||
local endpos = startPos - aimvector * thirdPersonDistance
|
||||
|
||||
local tracer = {
|
||||
start = startPos,
|
||||
endpos = endpos,
|
||||
filter = specEnt
|
||||
}
|
||||
|
||||
local trace = util.TraceLine(tracer)
|
||||
|
||||
return trace.HitPos + trace.HitNormal * 10
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Get the CalcView table
|
||||
---------------------------------------------------------------------------*/
|
||||
local view = {}
|
||||
local function getCalcView()
|
||||
if not isRoaming then
|
||||
if thirdperson then
|
||||
view.origin = getThirdPersonPos(specEnt)
|
||||
view.angles = LocalPlayer():EyeAngles()
|
||||
else
|
||||
view.origin = specEnt:IsPlayer() and specEnt:GetShootPos() or specEnt:LocalToWorld(specEnt:OBBCenter())
|
||||
view.angles = specEnt:IsPlayer() and specEnt:EyeAngles() or specEnt:GetAngles()
|
||||
end
|
||||
|
||||
roamPos = view.origin
|
||||
view.drawviewer = false
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
view.origin = roamPos
|
||||
view.angles = LocalPlayer():EyeAngles()
|
||||
view.drawviewer = true
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
specCalcView
|
||||
Override the view for the player to look through the spectated player's eyes
|
||||
---------------------------------------------------------------------------*/
|
||||
local function specCalcView(ply, origin, angles, fov)
|
||||
if not IsValid(specEnt) and not isRoaming then
|
||||
startFreeRoam()
|
||||
return
|
||||
end
|
||||
|
||||
view = getCalcView()
|
||||
|
||||
if IsValid(specEnt) then
|
||||
specEnt:SetNoDraw(not thirdperson)
|
||||
end
|
||||
|
||||
return view
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Find the right player to spectate
|
||||
---------------------------------------------------------------------------*/
|
||||
local function findNearestObject()
|
||||
local aimvec = LocalPlayer():GetAimVector()
|
||||
|
||||
local fromPos = not isRoaming and IsValid(specEnt) and specEnt:EyePos() or roamPos
|
||||
|
||||
local lookingAt = util.QuickTrace(fromPos, aimvec * 5000, LocalPlayer())
|
||||
local ent = lookingAt.Entity
|
||||
|
||||
if IsValid(ent) then return ent end
|
||||
|
||||
local foundPly, foundDot = nil, 0
|
||||
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if not IsValid(ply) or ply == LocalPlayer() then continue end
|
||||
|
||||
local pos = ply:GetShootPos()
|
||||
local dot = (pos - fromPos):GetNormalized():Dot(aimvec)
|
||||
|
||||
-- Discard players you're not looking at
|
||||
if dot < 0.97 then continue end
|
||||
-- not a better alternative
|
||||
if dot < foundDot then continue end
|
||||
|
||||
local trace = util.QuickTrace(fromPos, pos - fromPos, ply)
|
||||
|
||||
if trace.Hit then continue end
|
||||
|
||||
foundPly, foundDot = ply, dot
|
||||
end
|
||||
|
||||
return foundPly
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Spectate the person you're looking at while you're roaming
|
||||
---------------------------------------------------------------------------*/
|
||||
local function spectateLookingAt()
|
||||
local obj = findNearestObject()
|
||||
|
||||
if not IsValid(obj) then return end
|
||||
|
||||
isRoaming = false
|
||||
specEnt = obj
|
||||
|
||||
net.Start("FSpectateTarget")
|
||||
net.WriteEntity(obj)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
specBinds
|
||||
Change binds to perform spectate specific tasks
|
||||
---------------------------------------------------------------------------*/
|
||||
-- Manual keysDown table, so I can return true in plyBindPress and still detect key presses
|
||||
local keysDown = {}
|
||||
local function specBinds(ply, bind, pressed)
|
||||
local key = input.LookupBinding(bind)
|
||||
|
||||
if bind == "+jump" then
|
||||
stopSpectating()
|
||||
return true
|
||||
elseif bind == "+reload" and pressed then
|
||||
local pos = getCalcView().origin - Vector(0, 0, 64)
|
||||
RunConsoleCommand("FTPToPos", string.format("%d, %d, %d", pos.x, pos.y, pos.z),
|
||||
string.format("%d, %d, %d", roamVelocity.x, roamVelocity.y, roamVelocity.z))
|
||||
stopSpectating()
|
||||
elseif bind == "+attack" and pressed then
|
||||
if not isRoaming then
|
||||
startFreeRoam()
|
||||
else
|
||||
spectateLookingAt()
|
||||
end
|
||||
return true
|
||||
elseif bind == "+attack2" and pressed then
|
||||
if isRoaming then
|
||||
roamPos = roamPos + LocalPlayer():GetAimVector() * 500
|
||||
return true
|
||||
end
|
||||
thirdperson = not thirdperson
|
||||
|
||||
return true
|
||||
elseif isRoaming and not LocalPlayer():KeyDown(IN_USE) then
|
||||
local keybind = string.upper(string.match(bind, "+([a-z A-Z 0-9]+)") or "")
|
||||
if not keybind or keybind == "USE" or keybind == "SHOWSCORES" or string.find(bind, "messagemode") then return end
|
||||
|
||||
keysDown[keybind] = pressed
|
||||
|
||||
return true
|
||||
elseif not isRoaming and thirdperson and (key == "MWHEELDOWN" or key == "MWHEELUP") then
|
||||
thirdPersonDistance = thirdPersonDistance + 10 * (key == "MWHEELDOWN" and 1 or -1)
|
||||
end
|
||||
-- Do not return otherwise, spectating admins should be able to move to avoid getting detected
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Scoreboardshow
|
||||
Set to main view when roaming, open on a player when spectating
|
||||
---------------------------------------------------------------------------*/
|
||||
local function fadminmenushow()
|
||||
if isRoaming then
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
elseif IsValid(specEnt) and specEnt:IsPlayer() then
|
||||
FAdmin.ScoreBoard.ChangeView("Main")
|
||||
FAdmin.ScoreBoard.ChangeView("Player", specEnt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
RenderScreenspaceEffects
|
||||
Draws the lines from players' eyes to where they are looking
|
||||
---------------------------------------------------------------------------*/
|
||||
local LineMat = Material("cable/new_cable_lit")
|
||||
local linesToDraw = {}
|
||||
local function lookingLines()
|
||||
if not linesToDraw[0] then return end
|
||||
|
||||
render.SetMaterial(LineMat)
|
||||
|
||||
cam.Start3D(view.origin, view.angles)
|
||||
for i = 0, #linesToDraw, 3 do
|
||||
render.DrawBeam(linesToDraw[i], linesToDraw[i + 1], 4, 0.01, 10, linesToDraw[i + 2])
|
||||
end
|
||||
cam.End3D()
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
gunpos
|
||||
Gets the position of a player's gun
|
||||
---------------------------------------------------------------------------*/
|
||||
local function gunpos(ply)
|
||||
local wep = ply:GetActiveWeapon()
|
||||
if not IsValid(wep) then return ply:EyePos() end
|
||||
local att = wep:GetAttachment(1)
|
||||
if not att then return ply:EyePos() end
|
||||
return att.Pos
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Spectate think
|
||||
Free roaming position updates
|
||||
---------------------------------------------------------------------------*/
|
||||
local function specThink()
|
||||
local ply = LocalPlayer()
|
||||
|
||||
-- Update linesToDraw
|
||||
local pls = player.GetAll()
|
||||
local lastPly = 0
|
||||
local skip = 0
|
||||
for i = 0, #pls - 1 do
|
||||
local p = pls[i + 1]
|
||||
if not IsValid(p) then continue end
|
||||
if not isRoaming and p == specEnt and not thirdperson then skip = skip + 3 continue end
|
||||
|
||||
local tr = p:GetEyeTrace()
|
||||
local sp = gunpos(p)
|
||||
|
||||
local pos = i * 3 - skip
|
||||
|
||||
linesToDraw[pos] = tr.HitPos
|
||||
linesToDraw[pos + 1] = sp
|
||||
linesToDraw[pos + 2] = team.GetColor(p:Team())
|
||||
lastPly = i
|
||||
end
|
||||
|
||||
-- Remove entries from linesToDraw that don't match with a player anymore
|
||||
for i = #linesToDraw, lastPly * 3 + 3, -1 do linesToDraw[i] = nil end
|
||||
|
||||
if not isRoaming or keysDown["USE"] then return end
|
||||
|
||||
local roamSpeed = 1000
|
||||
local aimVec = ply:GetAimVector()
|
||||
local direction
|
||||
local frametime = RealFrameTime()
|
||||
|
||||
if keysDown["FORWARD"] then
|
||||
direction = aimVec
|
||||
elseif keysDown["BACK"] then
|
||||
direction = -aimVec
|
||||
end
|
||||
|
||||
if keysDown["MOVELEFT"] then
|
||||
local right = aimVec:Angle():Right()
|
||||
direction = direction and (direction - right):GetNormalized() or -right
|
||||
elseif keysDown["MOVERIGHT"] then
|
||||
local right = aimVec:Angle():Right()
|
||||
direction = direction and (direction + right):GetNormalized() or right
|
||||
end
|
||||
|
||||
if keysDown["SPEED"] then
|
||||
roamSpeed = 2500
|
||||
elseif keysDown["WALK"] or keysDown["DUCK"] then
|
||||
roamSpeed = 300
|
||||
end
|
||||
|
||||
roamVelocity = (direction or Vector(0, 0, 0)) * roamSpeed
|
||||
|
||||
roamPos = roamPos + roamVelocity * frametime
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Draw help on the screen
|
||||
---------------------------------------------------------------------------*/
|
||||
local uiForeground, uiBackground = Color(240, 240, 255, 255), Color(20, 20, 20, 120)
|
||||
local red = Color(255, 0, 0, 255)
|
||||
local function drawHelp()
|
||||
local scrHalfH = math.floor(ScrH() / 2)
|
||||
draw.WordBox(2, 10, scrHalfH, "Left click: (Un)select player to spectate", "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, 10, scrHalfH + 20, isRoaming and "Right click: quickly move forwards" or "Right click: toggle thirdperson", "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, 10, scrHalfH + 40, "Jump: Stop spectating", "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, 10, scrHalfH + 60, "Reload: Stop spectating and teleport", "UiBold", uiBackground, uiForeground)
|
||||
|
||||
if FAdmin then
|
||||
draw.WordBox(2, 10, scrHalfH + 80, "Opening FAdmin's menu while spectating a player", "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, 10, scrHalfH + 100, "\twill open their page!", "UiBold", uiBackground, uiForeground)
|
||||
end
|
||||
|
||||
|
||||
local target = findNearestObject()
|
||||
local pls = player.GetAll()
|
||||
for i = 1, #pls do
|
||||
local ply = pls[i]
|
||||
if not IsValid(ply) then continue end
|
||||
if not isRoaming and ply == specEnt then continue end
|
||||
|
||||
local pos = ply:GetShootPos():ToScreen()
|
||||
if not pos.visible then continue end
|
||||
|
||||
local x, y = pos.x, pos.y
|
||||
|
||||
draw.RoundedBox(2, x, y - 6, 12, 12, team.GetColor(ply:Team()))
|
||||
draw.WordBox(2, x, y - 66, ply:Nick(), "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, x, y - 46, "Health: " .. ply:Health(), "UiBold", uiBackground, uiForeground)
|
||||
draw.WordBox(2, x, y - 26, ply:GetUserGroup(), "UiBold", uiBackground, uiForeground)
|
||||
end
|
||||
|
||||
if not isRoaming then return end
|
||||
|
||||
if not IsValid(target) then return end
|
||||
|
||||
local center = target:LocalToWorld(target:OBBCenter())
|
||||
local eyeAng = EyeAngles()
|
||||
local rightUp = eyeAng:Right() * 16 + eyeAng:Up() * 36
|
||||
local topRight = (center + rightUp):ToScreen()
|
||||
local bottomLeft = (center - rightUp):ToScreen()
|
||||
|
||||
draw.RoundedBox(12, bottomLeft.x, bottomLeft.y, math.max(20, topRight.x - bottomLeft.x), topRight.y - bottomLeft.y, red)
|
||||
draw.WordBox(2, bottomLeft.x, bottomLeft.y + 12, "Left click to spectate!", "UiBold", uiBackground, uiForeground)
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
Start roaming free, rather than spectating a given player
|
||||
---------------------------------------------------------------------------*/
|
||||
startFreeRoam = function()
|
||||
roamPos = isSpectating and roamPos or LocalPlayer():GetShootPos()
|
||||
|
||||
if IsValid(specEnt) then
|
||||
if specEnt:IsPlayer() then
|
||||
roamPos = thirdperson and getThirdPersonPos(specEnt) or specEnt:GetShootPos()
|
||||
end
|
||||
specEnt:SetNoDraw(false)
|
||||
end
|
||||
|
||||
specEnt = nil
|
||||
isRoaming = true
|
||||
keysDown = {}
|
||||
end
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
specEnt
|
||||
Spectate a player
|
||||
---------------------------------------------------------------------------*/
|
||||
local function startSpectate(um)
|
||||
isRoaming = net.ReadBool()
|
||||
specEnt = net.ReadEntity()
|
||||
specEnt = IsValid(specEnt) and specEnt or nil
|
||||
|
||||
if isRoaming then
|
||||
startFreeRoam()
|
||||
end
|
||||
|
||||
isSpectating = true
|
||||
keysDown = {}
|
||||
|
||||
hook.Add("CalcView", "FSpectate", specCalcView)
|
||||
hook.Add("PlayerBindPress", "FSpectate", specBinds)
|
||||
hook.Add("ShouldDrawLocalPlayer", "FSpectate", function() return isRoaming or thirdperson end)
|
||||
hook.Add("Think", "FSpectate", specThink)
|
||||
hook.Add("HUDPaint", "FSpectate", drawHelp)
|
||||
hook.Add("FAdmin_ShowFAdminMenu", "FSpectate", fadminmenushow)
|
||||
hook.Add("RenderScreenspaceEffects", "FSpectate", lookingLines)
|
||||
|
||||
timer.Create("FSpectatePosUpdate", 0.5, 0, function()
|
||||
if not isRoaming then return end
|
||||
|
||||
RunConsoleCommand("_FSpectatePosUpdate", roamPos.x, roamPos.y, roamPos.z)
|
||||
end)
|
||||
end
|
||||
net.Receive("FSpectate", startSpectate)
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
stopSpectating
|
||||
Stop spectating a player
|
||||
---------------------------------------------------------------------------*/
|
||||
stopSpectating = function()
|
||||
hook.Remove("CalcView", "FSpectate")
|
||||
hook.Remove("PlayerBindPress", "FSpectate")
|
||||
hook.Remove("ShouldDrawLocalPlayer", "FSpectate")
|
||||
hook.Remove("Think", "FSpectate")
|
||||
hook.Remove("HUDPaint", "FSpectate")
|
||||
hook.Remove("FAdmin_ShowFAdminMenu", "FSpectate")
|
||||
hook.Remove("RenderScreenspaceEffects", "FSpectate")
|
||||
|
||||
timer.Remove("FSpectatePosUpdate")
|
||||
|
||||
if IsValid(specEnt) then
|
||||
specEnt:SetNoDraw(false)
|
||||
end
|
||||
|
||||
RunConsoleCommand("FSpectate_StopSpectating")
|
||||
isSpectating = false
|
||||
end
|
||||
11
gamemodes/darkrp/gamemode/modules/fspectate/sh_init.lua
Normal file
11
gamemodes/darkrp/gamemode/modules/fspectate/sh_init.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
if not CAMI then return end
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "FSpectate",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "FSpectateTeleport",
|
||||
MinAccess = "admin"
|
||||
}
|
||||
227
gamemodes/darkrp/gamemode/modules/fspectate/sv_init.lua
Normal file
227
gamemodes/darkrp/gamemode/modules/fspectate/sv_init.lua
Normal file
@@ -0,0 +1,227 @@
|
||||
util.AddNetworkString("FSpectate")
|
||||
util.AddNetworkString("FSpectateTarget")
|
||||
|
||||
local function findPlayer(info)
|
||||
if not info or info == "" then return nil end
|
||||
local pls = player.GetAll()
|
||||
|
||||
for k = 1, #pls do
|
||||
local v = pls[k]
|
||||
if tonumber(info) == v:UserID() then
|
||||
return v
|
||||
end
|
||||
|
||||
if info == v:SteamID() then
|
||||
return v
|
||||
end
|
||||
|
||||
if string.find(string.lower(v:Nick()), string.lower(tostring(info)), 1, true) ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local FSpectating = {}
|
||||
-- For Lua Refresh
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
FSpectating[ply] = ply.FSpectating
|
||||
end
|
||||
|
||||
local function clearInvalidSpectators()
|
||||
for ply, _ in pairs(FSpectating) do
|
||||
if not IsValid(ply) then
|
||||
FSpectating[ply] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function startSpectating(ply, target)
|
||||
local canSpectate = hook.Call("FSpectate_canSpectate", nil, ply, target)
|
||||
if canSpectate == false then return end
|
||||
|
||||
-- Clear invalid spectators from the FSpectating table to prevent build up.
|
||||
clearInvalidSpectators()
|
||||
|
||||
ply.FSpectatingEnt = target
|
||||
ply.FSpectating = true
|
||||
FSpectating[ply] = true
|
||||
|
||||
ply:ExitVehicle()
|
||||
|
||||
net.Start("FSpectate")
|
||||
net.WriteBool(target == nil)
|
||||
if IsValid(ply.FSpectatingEnt) then
|
||||
net.WriteEntity(ply.FSpectatingEnt)
|
||||
end
|
||||
net.Send(ply)
|
||||
|
||||
local targetText = IsValid(target) and target:IsPlayer() and (target:Nick() .. " (" .. target:SteamID() .. ")") or IsValid(target) and "an entity" or ""
|
||||
ply:ChatPrint("You are now spectating " .. targetText)
|
||||
hook.Call("FSpectate_start", nil, ply, target)
|
||||
end
|
||||
|
||||
local function Spectate(ply, cmd, args)
|
||||
CAMI.PlayerHasAccess(ply, "FSpectate", function(b, _)
|
||||
if not b then ply:ChatPrint("No Access!") return end
|
||||
|
||||
local target = findPlayer(args[1])
|
||||
if target == ply then ply:ChatPrint("Invalid target!") return end
|
||||
|
||||
startSpectating(ply, target)
|
||||
end)
|
||||
end
|
||||
concommand.Add("FSpectate", Spectate)
|
||||
|
||||
net.Receive("FSpectateTarget", function(_, ply)
|
||||
CAMI.PlayerHasAccess(ply, "FSpectate", function(b, _)
|
||||
if not b then ply:ChatPrint("No Access!") return end
|
||||
|
||||
startSpectating(ply, net.ReadEntity())
|
||||
end)
|
||||
end)
|
||||
|
||||
local function TPToPos(ply, cmd, args)
|
||||
CAMI.PlayerHasAccess(ply, "FSpectateTeleport", function(b, _)
|
||||
if not b then ply:ChatPrint("No Access!") return end
|
||||
|
||||
local x, y, z = string.match(args[1] or "", "([-0-9\\.]+),%s?([-0-9\\.]+),%s?([-0-9\\.]+)")
|
||||
local vx, vy, vz = string.match(args[2] or "", "([-0-9\\.]+),%s?([-0-9\\.]+),%s?([-0-9\\.]+)")
|
||||
local pos = Vector(tonumber(x), tonumber(y), tonumber(z))
|
||||
local vel = Vector(tonumber(vx or 0), tonumber(vy or 0), tonumber(vz or 0))
|
||||
|
||||
if not args[1] or not x or not y or not z then return end
|
||||
|
||||
ply:SetPos(pos)
|
||||
|
||||
if vx and vy and vz then ply:SetVelocity(vel) end
|
||||
hook.Call("FTPToPos", nil, ply, pos)
|
||||
end)
|
||||
end
|
||||
concommand.Add("FTPToPos", TPToPos)
|
||||
|
||||
local function SpectateVisibility(ply, viewEnt)
|
||||
if not ply.FSpectating then return end
|
||||
|
||||
if IsValid(ply.FSpectatingEnt) then
|
||||
AddOriginToPVS(ply.FSpectatingEnt:IsPlayer() and ply.FSpectatingEnt:GetShootPos() or ply.FSpectatingEnt:GetPos())
|
||||
end
|
||||
|
||||
if ply.FSpectatePos then
|
||||
AddOriginToPVS(ply.FSpectatePos)
|
||||
end
|
||||
end
|
||||
hook.Add("SetupPlayerVisibility", "FSpectate", SpectateVisibility)
|
||||
|
||||
local function setSpectatePos(ply, cmd, args)
|
||||
CAMI.PlayerHasAccess(ply, "FSpectate", function(b, _)
|
||||
if not b then return end
|
||||
|
||||
if not ply.FSpectating or not args[3] then return end
|
||||
local x, y, z = tonumber(args[1] or 0), tonumber(args[2] or 0), tonumber(args[3] or 0)
|
||||
|
||||
ply.FSpectatePos = Vector(x, y, z)
|
||||
|
||||
-- A position update request implies that the spectator is not spectating another player (anymore)
|
||||
ply.FSpectatingEnt = nil
|
||||
end)
|
||||
end
|
||||
concommand.Add("_FSpectatePosUpdate", setSpectatePos)
|
||||
|
||||
local function endSpectate(ply, cmd, args)
|
||||
ply.FSpectatingEnt = nil
|
||||
ply.FSpectating = nil
|
||||
ply.FSpectatePos = nil
|
||||
FSpectating[ply] = nil
|
||||
hook.Call("FSpectate_stop", nil, ply)
|
||||
end
|
||||
concommand.Add("FSpectate_StopSpectating", endSpectate)
|
||||
|
||||
local vrad = DarkRP and GM.Config.voiceradius
|
||||
local voiceDistance = DarkRP and GM.Config.voiceDistance * GM.Config.voiceDistance or 302500 -- Default 550 units
|
||||
local function playerVoice(listener, talker)
|
||||
if not FSpectating[listener] then return end
|
||||
|
||||
local canHearLocal, surround = GAMEMODE:PlayerCanHearPlayersVoice(listener, talker)
|
||||
|
||||
-- No need to check other stuff
|
||||
if canHearLocal then
|
||||
return canHearLocal, surround
|
||||
end
|
||||
|
||||
local FSpectatingEnt = listener.FSpectatingEnt
|
||||
if not IsValid(FSpectatingEnt) or not FSpectatingEnt:IsPlayer() then
|
||||
local spectatePos = IsValid(FSpectatingEnt) and FSpectatingEnt:GetPos() or listener.FSpectatePos
|
||||
if not vrad or not spectatePos then return end
|
||||
|
||||
-- Return whether the listener is a in distance smaller than 550
|
||||
return spectatePos:DistToSqr(talker:GetPos()) < voiceDistance, surround
|
||||
end
|
||||
|
||||
-- you can always hear the person you're spectating
|
||||
if FSpectatingEnt == talker then
|
||||
return true, surround
|
||||
end
|
||||
|
||||
-- You can hear someone if your spectate target can hear them
|
||||
local canHear = GAMEMODE:PlayerCanHearPlayersVoice(FSpectatingEnt, talker)
|
||||
|
||||
return canHear, surround
|
||||
end
|
||||
hook.Add("PlayerCanHearPlayersVoice", "FSpectate", playerVoice)
|
||||
|
||||
local function playerSay(talker, message)
|
||||
local split = string.Explode(" ", message)
|
||||
|
||||
if split[1] and (split[1] == "!spectate" or split[1] == "/spectate") then
|
||||
Spectate(talker, split[1], {split[2]})
|
||||
return ""
|
||||
end
|
||||
|
||||
if not DarkRP then return end
|
||||
|
||||
local talkerTeam = team.GetColor(talker:Team())
|
||||
local talkerName = talker:Nick()
|
||||
local col = Color(255, 255, 255, 255)
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply == talker or not ply.FSpectating then continue end
|
||||
|
||||
local shootPos = talker:GetShootPos()
|
||||
local FSpectatingEnt = ply.FSpectatingEnt
|
||||
if
|
||||
-- Make sure you don't get it twice
|
||||
ply:GetShootPos():DistToSqr(shootPos) > 62500 and
|
||||
(
|
||||
-- the person is saying it close to where you are roaming
|
||||
ply.FSpectatePos and shootPos:DistToSqr(ply.FSpectatePos) <= 360000 or
|
||||
|
||||
-- The person you're spectating or someone near the person you're spectating is saying it
|
||||
(IsValid(FSpectatingEnt) and FSpectatingEnt:IsPlayer() and
|
||||
shootPos:DistToSqr(FSpectatingEnt:GetShootPos()) <= 90000) or
|
||||
|
||||
-- Close to the object you're spectating
|
||||
(IsValid(FSpectatingEnt) and not FSpectatingEnt:IsPlayer() and
|
||||
talker:GetPos():DistToSqr(FSpectatingEnt:GetPos()) <= 90000
|
||||
)
|
||||
) then
|
||||
|
||||
DarkRP.talkToPerson(ply, talkerTeam, talkerName, col, message, talker)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
hook.Add("PlayerSay", "FSpectate", playerSay)
|
||||
|
||||
-- ULX' !spectate command conflicts with mine
|
||||
-- The concommand "ulx spectate" should still work.
|
||||
local function fixAdminModIncompat()
|
||||
if ULib then
|
||||
ULib.removeSayCommand("!spectate")
|
||||
end
|
||||
|
||||
if serverguard then
|
||||
serverguard.command:Remove("spectate")
|
||||
end
|
||||
end
|
||||
hook.Add("InitPostEntity", "FSpectate", fixAdminModIncompat)
|
||||
Reference in New Issue
Block a user