Files
wnsrc/gamemodes/terrortown/gamemode/player_ext.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

367 lines
9.3 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- serverside extensions to player table
local plymeta = FindMetaTable( "Player" )
if not plymeta then Error("FAILED TO FIND PLAYER TABLE") return end
function plymeta:SetRagdollSpec(s)
if s then
self.spec_ragdoll_start = CurTime()
end
self.spec_ragdoll = s
end
function plymeta:GetRagdollSpec() return self.spec_ragdoll end
AccessorFunc(plymeta, "force_spec", "ForceSpec", FORCE_BOOL)
--- Karma
-- The base/start karma is determined once per round and determines the player's
-- damage penalty. It is networked and shown on clients.
function plymeta:SetBaseKarma(k)
self:SetNWFloat("karma", k)
end
-- The live karma starts equal to the base karma, but is updated "live" as the
-- player damages/kills others. When another player damages/kills this one, the
-- live karma is used to determine his karma penalty.
AccessorFunc(plymeta, "live_karma", "LiveKarma", FORCE_NUMBER)
-- The damage factor scales how much damage the player deals, so if it is .9
-- then the player only deals 90% of his original damage.
AccessorFunc(plymeta, "dmg_factor", "DamageFactor", FORCE_NUMBER)
-- If a player does not damage team members in a round, he has a "clean" round
-- and gets a bonus for it.
AccessorFunc(plymeta, "clean_round", "CleanRound", FORCE_BOOL)
function plymeta:InitKarma()
KARMA.InitPlayer(self)
end
--- Equipment credits
function plymeta:SetCredits(amt)
self.equipment_credits = amt
self:SendCredits()
end
function plymeta:AddCredits(amt)
self:SetCredits(self:GetCredits() + amt)
end
function plymeta:SubtractCredits(amt) self:AddCredits(-amt) end
function plymeta:SetDefaultCredits()
if self:GetTraitor() then
local c = GetConVar("ttt_credits_starting"):GetInt()
if CountTraitors() == 1 then
c = c + GetConVar("ttt_credits_alonebonus"):GetInt()
end
self:SetCredits(c)
elseif self:GetDetective() then
self:SetCredits(GetConVar("ttt_det_credits_starting"):GetInt())
else
self:SetCredits(0)
end
end
function plymeta:SendCredits()
net.Start("TTT_Credits")
net.WriteUInt(self:GetCredits(), 8)
net.Send(self)
end
--- Equipment items
function plymeta:AddEquipmentItem(id)
id = tonumber(id)
if id then
self.equipment_items = bit.bor(self.equipment_items, id)
self:SendEquipment()
end
end
-- We do this instead of an NW var in order to limit the info to just this ply
function plymeta:SendEquipment()
net.Start("TTT_Equipment")
net.WriteUInt(self.equipment_items, 16)
net.Send(self)
end
function plymeta:ResetEquipment()
self.equipment_items = EQUIP_NONE
self:SendEquipment()
end
function plymeta:SendBought()
-- Send all as string, even though equipment are numbers, for simplicity
net.Start("TTT_Bought")
net.WriteUInt(#self.bought, 8)
for k, v in pairs(self.bought) do
net.WriteString(v)
end
net.Send(self)
end
local function ResendBought(ply)
if IsValid(ply) then ply:SendBought() end
end
concommand.Add("ttt_resend_bought", ResendBought)
function plymeta:ResetBought()
self.bought = {}
self:SendBought()
end
function plymeta:AddBought(id)
if not self.bought then self.bought = {} end
table.insert(self.bought, tostring(id))
self:SendBought()
end
-- Strips player of all equipment
function plymeta:StripAll()
-- standard stuff
self:StripAmmo()
self:StripWeapons()
-- our stuff
self:ResetEquipment()
self:SetCredits(0)
end
-- Sets all flags (force_spec, etc) to their default
function plymeta:ResetStatus()
self:SetRole(ROLE_INNOCENT)
self:SetRagdollSpec(false)
self:SetForceSpec(false)
self:ResetRoundFlags()
end
-- Sets round-based misc flags to default position. Called at PlayerSpawn.
function plymeta:ResetRoundFlags()
-- equipment
self:ResetEquipment()
self:SetCredits(0)
self:ResetBought()
-- equipment stuff
self.bomb_wire = nil
self.radar_charge = 0
self.decoy = nil
-- corpse
self:SetNWBool("body_found", false)
self.kills = {}
self.dying_wep = nil
self.was_headshot = false
-- communication
self.mute_team = -1
self.traitor_gvoice = false
self:SetNWBool("disguised", false)
-- karma
self:SetCleanRound(true)
self:Freeze(false)
end
function plymeta:GiveEquipmentItem(id)
if self:HasEquipmentItem(id) then
return false
elseif id and id > EQUIP_NONE then
self:AddEquipmentItem(id)
return true
end
end
-- Forced specs and latejoin specs should not get points
function plymeta:ShouldScore()
if self:GetForceSpec() then
return false
elseif self:IsSpec() and self:Alive() then
return false
else
return true
end
end
function plymeta:RecordKill(victim)
if not IsValid(victim) then return end
if not self.kills then
self.kills = {}
end
table.insert(self.kills, victim:SteamID64())
end
function plymeta:SetSpeed(slowed)
-- For player movement prediction to work properly, ply:SetSpeed turned out
-- to be a bad idea. It now uses GM:SetupMove, and the TTTPlayerSpeedModifier
-- hook is provided to let you change player speed without messing up
-- prediction. It needs to be hooked on both client and server and return the
-- same results (ie. same implementation).
error "Player:SetSpeed has been removed - please remove this call and use the TTTPlayerSpeedModifier hook in both CLIENT and SERVER environments"
end
function plymeta:ResetLastWords()
if not IsValid(self) then return end -- timers are dangerous things
self.last_words_id = nil
end
function plymeta:SendLastWords(dmginfo)
-- Use a pseudo unique id to prevent people from abusing the concmd
self.last_words_id = math.floor(CurTime() + math.random(500))
-- See if the damage was interesting
local dtype = KILL_NORMAL
if dmginfo:GetAttacker() == self or dmginfo:GetInflictor() == self then
dtype = KILL_SUICIDE
elseif dmginfo:IsDamageType(DMG_BURN) then
dtype = KILL_BURN
elseif dmginfo:IsFallDamage() then
dtype = KILL_FALL
end
self.death_type = dtype
net.Start("TTT_InterruptChat")
net.WriteUInt(self.last_words_id, 32)
net.Send(self)
-- any longer than this and you're out of luck
local ply = self
timer.Simple(2, function() ply:ResetLastWords() end)
end
function plymeta:ResetViewRoll()
local ang = self:EyeAngles()
if ang.r != 0 then
ang.r = 0
self:SetEyeAngles(ang)
end
end
function plymeta:ShouldSpawn()
-- do not spawn players who have not been through initspawn
if (not self:IsSpec()) and (not self:IsTerror()) then return false end
-- do not spawn forced specs
if self:IsSpec() and self:GetForceSpec() then return false end
return true
end
-- Preps a player for a new round, spawning them if they should. If dead_only is
-- true, only spawns if player is dead, else just makes sure he is healed.
function plymeta:SpawnForRound(dead_only)
hook.Call("PlayerSetModel", GAMEMODE, self)
hook.Call("TTTPlayerSetColor", GAMEMODE, self)
-- wrong alive status and not a willing spec who unforced after prep started
-- (and will therefore be "alive")
if dead_only and self:Alive() and (not self:IsSpec()) then
-- if the player does not need respawn, make sure he has full health
self:SetHealth(self:GetMaxHealth())
return false
end
if not self:ShouldSpawn() then return false end
-- reset propspec state that they may have gotten during prep
PROPSPEC.Clear(self)
-- respawn anyone else
if self:Team() == TEAM_SPEC then
self:UnSpectate()
end
self:StripAll()
self:SetTeam(TEAM_TERROR)
self:Spawn()
-- tell caller that we spawned
return true
end
function plymeta:InitialSpawn()
self.has_spawned = false
-- The team the player spawns on depends on the round state
self:SetTeam(GetRoundState() == ROUND_PREP and TEAM_TERROR or TEAM_SPEC)
-- Change some gmod defaults
self:SetCanZoom(false)
self:SetJumpPower(160)
self:SetCrouchedWalkSpeed(0.3)
self:SetRunSpeed(220)
self:SetWalkSpeed(220)
self:SetMaxSpeed(220)
-- Always spawn innocent initially, traitor will be selected later
self:ResetStatus()
-- Start off with clean, full karma (unless it can and should be loaded)
self:InitKarma()
-- We never have weapons here, but this inits our equipment state
self:StripAll()
end
function plymeta:KickBan(length, reason)
-- see admin.lua
PerformKickBan(self, length, reason)
end
local oldSpectate = plymeta.Spectate
function plymeta:Spectate(type)
oldSpectate(self, type)
-- NPCs should never see spectators. A workaround for the fact that gmod NPCs
-- do not ignore them by default.
self:SetNoTarget(true)
if type == OBS_MODE_ROAMING then
self:SetMoveType(MOVETYPE_NOCLIP)
end
end
local oldSpectateEntity = plymeta.SpectateEntity
function plymeta:SpectateEntity(ent)
oldSpectateEntity(self, ent)
if IsValid(ent) and ent:IsPlayer() then
self:SetupHands(ent)
end
end
local oldUnSpectate = plymeta.UnSpectate
function plymeta:UnSpectate()
oldUnSpectate(self)
self:SetNoTarget(false)
end
function plymeta:GetAvoidDetective()
return self:GetInfoNum("ttt_avoid_detective", 0) > 0
end