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

415 lines
11 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/
--]]
include("shared.lua")
-- Define GM12 fonts for compatibility
surface.CreateFont("DefaultBold", {font = "Tahoma",
size = 13,
weight = 1000})
surface.CreateFont("TabLarge", {font = "Tahoma",
size = 13,
weight = 700,
shadow = true, antialias = false})
surface.CreateFont("Trebuchet22", {font = "Trebuchet MS",
size = 22,
weight = 900})
include("corpse_shd.lua")
include("player_ext_shd.lua")
include("weaponry_shd.lua")
include("vgui/ColoredBox.lua")
include("vgui/SimpleIcon.lua")
include("vgui/ProgressBar.lua")
include("vgui/ScrollLabel.lua")
include("cl_radio.lua")
include("cl_disguise.lua")
include("cl_transfer.lua")
include("cl_targetid.lua")
include("cl_search.lua")
include("cl_radar.lua")
include("cl_tbuttons.lua")
include("cl_scoreboard.lua")
include("cl_tips.lua")
include("cl_help.lua")
include("cl_hud.lua")
include("cl_msgstack.lua")
include("cl_hudpickup.lua")
include("cl_keys.lua")
include("cl_wepswitch.lua")
include("cl_scoring.lua")
include("cl_scoring_events.lua")
include("cl_popups.lua")
include("cl_equip.lua")
include("cl_voice.lua")
function GM:Initialize()
MsgN("TTT Client initializing...")
GAMEMODE.round_state = ROUND_WAIT
LANG.Init()
self.BaseClass:Initialize()
end
function GM:InitPostEntity()
MsgN("TTT Client post-init...")
net.Start("TTT_Spectate")
net.WriteBool(GetConVar("ttt_spectator_mode"):GetBool())
net.SendToServer()
if not game.SinglePlayer() then
timer.Create("idlecheck", 5, 0, CheckIdle)
end
-- make sure player class extensions are loaded up, and then do some
-- initialization on them
if IsValid(LocalPlayer()) and LocalPlayer().GetTraitor then
GAMEMODE:ClearClientState()
end
timer.Create("cache_ents", 1, 0, GAMEMODE.DoCacheEnts)
RunConsoleCommand("_ttt_request_serverlang")
RunConsoleCommand("_ttt_request_rolelist")
end
function GM:DoCacheEnts()
RADAR:CacheEnts()
TBHUD:CacheEnts()
end
function GM:HUDClear()
RADAR:Clear()
TBHUD:Clear()
end
KARMA = {}
function KARMA.IsEnabled() return GetGlobalBool("ttt_karma", false) end
function GetRoundState() return GAMEMODE.round_state end
local function RoundStateChange(o, n)
if n == ROUND_PREP then
-- prep starts
GAMEMODE:ClearClientState()
GAMEMODE:CleanUpMap()
-- show warning to spec mode players
if GetConVar("ttt_spectator_mode"):GetBool() and IsValid(LocalPlayer())then
LANG.Msg("spec_mode_warning")
end
-- reset cached server language in case it has changed
RunConsoleCommand("_ttt_request_serverlang")
elseif n == ROUND_ACTIVE then
-- round starts
VOICE.CycleMuteState(MUTE_NONE)
CLSCORE:ClearPanel()
-- people may have died and been searched during prep
for _, p in player.Iterator() do
p.search_result = nil
end
-- clear blood decals produced during prep
RunConsoleCommand("r_cleardecals")
GAMEMODE.StartingPlayers = #util.GetAlivePlayers()
elseif n == ROUND_POST then
RunConsoleCommand("ttt_cl_traitorpopup_close")
end
-- stricter checks when we're talking about hooks, because this function may
-- be called with for example o = WAIT and n = POST, for newly connecting
-- players, which hooking code may not expect
if n == ROUND_PREP then
-- can enter PREP from any phase due to ttt_roundrestart
hook.Call("TTTPrepareRound", GAMEMODE)
elseif (o == ROUND_PREP) and (n == ROUND_ACTIVE) then
hook.Call("TTTBeginRound", GAMEMODE)
elseif (o == ROUND_ACTIVE) and (n == ROUND_POST) then
hook.Call("TTTEndRound", GAMEMODE)
end
-- whatever round state we get, clear out the voice flags
for k,v in player.Iterator() do
v.traitor_gvoice = false
end
end
concommand.Add("ttt_print_playercount", function() print(GAMEMODE.StartingPlayers) end)
--- optional sound cues on round start and end
CreateConVar("ttt_cl_soundcues", "0", FCVAR_ARCHIVE)
local cues = {
Sound("ttt/thump01e.mp3"),
Sound("ttt/thump02e.mp3")
};
local function PlaySoundCue()
if GetConVar("ttt_cl_soundcues"):GetBool() then
surface.PlaySound(table.Random(cues))
end
end
GM.TTTBeginRound = PlaySoundCue
GM.TTTEndRound = PlaySoundCue
--- usermessages
local function ReceiveRole()
local client = LocalPlayer()
local role = net.ReadUInt(2)
-- after a mapswitch, server might have sent us this before we are even done
-- loading our code
if not client.SetRole then return end
client:SetRole(role)
Msg("You are: ")
if client:IsTraitor() then MsgN("TRAITOR")
elseif client:IsDetective() then MsgN("DETECTIVE")
else MsgN("INNOCENT") end
end
net.Receive("TTT_Role", ReceiveRole)
local function ReceiveRoleList()
local role = net.ReadUInt(2)
local num_ids = net.ReadUInt(8)
for i=1, num_ids do
local eidx = net.ReadUInt(7) + 1 -- we - 1 worldspawn=0
local ply = player.GetByID(eidx)
if IsValid(ply) and ply.SetRole then
ply:SetRole(role)
if ply:IsTraitor() then
ply.traitor_gvoice = false -- assume traitorchat by default
end
end
end
end
net.Receive("TTT_RoleList", ReceiveRoleList)
-- Round state comm
local function ReceiveRoundState()
local o = GetRoundState()
GAMEMODE.round_state = net.ReadUInt(3)
if o != GAMEMODE.round_state then
RoundStateChange(o, GAMEMODE.round_state)
end
MsgN("Round state: " .. GAMEMODE.round_state)
end
net.Receive("TTT_RoundState", ReceiveRoundState)
-- Cleanup at start of new round
function GM:ClearClientState()
GAMEMODE:HUDClear()
local client = LocalPlayer()
if not client.SetRole then return end -- code not loaded yet
client:SetRole(ROLE_INNOCENT)
client.equipment_items = EQUIP_NONE
client.equipment_credits = 0
client.bought = {}
client.last_id = nil
client.radio = nil
client.called_corpses = {}
VOICE.InitBattery()
for _, p in player.Iterator() do
if IsValid(p) then
p.sb_tag = nil
p:SetRole(ROLE_INNOCENT)
p.search_result = nil
end
end
VOICE.CycleMuteState(MUTE_NONE)
RunConsoleCommand("ttt_mute_team_check", "0")
if GAMEMODE.ForcedMouse then
gui.EnableScreenClicker(false)
end
end
net.Receive("TTT_ClearClientState", GM.ClearClientState)
function GM:CleanUpMap()
-- Ragdolls sometimes stay around on clients. Deleting them can create issues
-- so all we can do is try to hide them.
for _, ent in ipairs(ents.FindByClass("prop_ragdoll")) do
if IsValid(ent) and CORPSE.GetPlayerNick(ent, "") != "" then
ent:SetNoDraw(true)
ent:SetSolid(SOLID_NONE)
ent:SetColor(Color(0,0,0,0))
-- Horrible hack to make targetid ignore this ent, because we can't
-- modify the collision group clientside.
ent.NoTarget = true
end
end
-- This cleans up decals since GMod v100
game.CleanUpMap()
end
-- server tells us to call this when our LocalPlayer has spawned
local function PlayerSpawn()
local as_spec = net.ReadBit() == 1
if as_spec then
TIPS.Show()
else
TIPS.Hide()
end
end
net.Receive("TTT_PlayerSpawned", PlayerSpawn)
local function PlayerDeath()
TIPS.Show()
end
net.Receive("TTT_PlayerDied", PlayerDeath)
function GM:ShouldDrawLocalPlayer(ply) return false end
local view = {origin = vector_origin, angles = angle_zero, fov=0}
function GM:CalcView( ply, origin, angles, fov )
view.origin = origin
view.angles = angles
view.fov = fov
-- first person ragdolling
if ply:Team() == TEAM_SPEC and ply:GetObserverMode() == OBS_MODE_IN_EYE then
local tgt = ply:GetObserverTarget()
if IsValid(tgt) and (not tgt:IsPlayer()) then
-- assume if we are in_eye and not speccing a player, we spec a ragdoll
local eyes = tgt:LookupAttachment("eyes") or 0
eyes = tgt:GetAttachment(eyes)
if eyes then
view.origin = eyes.Pos
view.angles = eyes.Ang
end
end
end
local wep = ply:GetActiveWeapon()
if IsValid(wep) then
local func = wep.CalcView
if func then
view.origin, view.angles, view.fov = func( wep, ply, origin*1, angles*1, fov )
end
end
return view
end
function GM:AddDeathNotice() end
function GM:DrawDeathNotice() end
function GM:Tick()
local client = LocalPlayer()
if IsValid(client) then
if client:Alive() and client:Team() != TEAM_SPEC then
WSWITCH:Think()
RADIO:StoreTarget()
end
VOICE.Tick()
end
end
-- Simple client-based idle checking
local idle = {ang = nil, pos = nil, mx = 0, my = 0, t = 0}
function CheckIdle()
local client = LocalPlayer()
if not IsValid(client) then return end
if not idle.ang or not idle.pos then
-- init things
idle.ang = client:GetAngles()
idle.pos = client:GetPos()
idle.mx = gui.MouseX()
idle.my = gui.MouseY()
idle.t = CurTime()
return
end
if GetRoundState() == ROUND_ACTIVE and client:IsTerror() and client:Alive() then
local idle_limit = GetGlobalInt("ttt_idle_limit", 300) or 300
if idle_limit <= 0 then idle_limit = 300 end -- networking sucks sometimes
if client:GetAngles() != idle.ang then
-- Normal players will move their viewing angles all the time
idle.ang = client:GetAngles()
idle.t = CurTime()
elseif gui.MouseX() != idle.mx or gui.MouseY() != idle.my then
-- Players in eg. the Help will move their mouse occasionally
idle.mx = gui.MouseX()
idle.my = gui.MouseY()
idle.t = CurTime()
elseif client:GetPos():Distance(idle.pos) > 10 then
-- Even if players don't move their mouse, they might still walk
idle.pos = client:GetPos()
idle.t = CurTime()
elseif CurTime() > (idle.t + idle_limit) then
RunConsoleCommand("say", "(AUTOMATED MESSAGE) I have been moved to the Spectator team because I was idle/AFK.")
timer.Simple(0.3, function()
RunConsoleCommand("ttt_spectator_mode", 1)
net.Start("TTT_Spectate")
net.WriteBool(true)
net.SendToServer()
RunConsoleCommand("ttt_cl_idlepopup")
end)
elseif CurTime() > (idle.t + (idle_limit / 2)) then
-- will repeat
LANG.Msg("idle_warning")
end
end
end
function GM:OnEntityCreated(ent)
-- Make ragdolls look like the player that has died
if ent:IsRagdoll() then
local ply = CORPSE.GetPlayer(ent)
if IsValid(ply) then
-- Only copy any decals if this ragdoll was recently created
if ent:GetCreationTime() > CurTime() - 1 then
ent:SnatchModelInstance(ply)
end
-- Copy the color for the PlayerColor matproxy
local playerColor = ply:GetPlayerColor()
ent.GetPlayerColor = function()
return playerColor
end
end
end
return self.BaseClass.OnEntityCreated(self, ent)
end