mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
391
gamemodes/terrortown/gamemode/gamemsg.lua
Normal file
391
gamemodes/terrortown/gamemode/gamemsg.lua
Normal file
@@ -0,0 +1,391 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
---- Communicating game state to players
|
||||
|
||||
local net = net
|
||||
local string = string
|
||||
local table = table
|
||||
local ipairs = ipairs
|
||||
local IsValid = IsValid
|
||||
|
||||
-- NOTE: most uses of the Msg functions here have been moved to the LANG
|
||||
-- functions. These functions are essentially deprecated, though they won't be
|
||||
-- removed and can safely be used by SWEPs and the like.
|
||||
|
||||
function GameMsg(msg)
|
||||
net.Start("TTT_GameMsg")
|
||||
net.WriteString(msg)
|
||||
net.WriteBit(false)
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
function CustomMsg(ply_or_rf, msg, clr)
|
||||
clr = clr or COLOR_WHITE
|
||||
|
||||
net.Start("TTT_GameMsgColor")
|
||||
net.WriteString(msg)
|
||||
net.WriteUInt(clr.r, 8)
|
||||
net.WriteUInt(clr.g, 8)
|
||||
net.WriteUInt(clr.b, 8)
|
||||
if ply_or_rf then net.Send(ply_or_rf)
|
||||
else net.Broadcast() end
|
||||
end
|
||||
|
||||
-- Basic status message to single player or a recipientfilter
|
||||
function PlayerMsg(ply_or_rf, msg, traitor_only)
|
||||
net.Start("TTT_GameMsg")
|
||||
net.WriteString(msg)
|
||||
net.WriteBit(traitor_only)
|
||||
if ply_or_rf then net.Send(ply_or_rf)
|
||||
else net.Broadcast() end
|
||||
end
|
||||
|
||||
-- Traitor-specific message that will appear in a special color
|
||||
function TraitorMsg(ply_or_rfilter, msg)
|
||||
PlayerMsg(ply_or_rfilter, msg, true)
|
||||
end
|
||||
|
||||
-- Traitorchat
|
||||
local function RoleChatMsg(sender, role, msg)
|
||||
net.Start("TTT_RoleChat")
|
||||
net.WriteUInt(role, 2)
|
||||
net.WritePlayer(sender)
|
||||
net.WriteString(msg)
|
||||
net.Send(GetRoleFilter(role))
|
||||
end
|
||||
|
||||
|
||||
-- Round start info popup
|
||||
function ShowRoundStartPopup()
|
||||
for k, v in player.Iterator() do
|
||||
if IsValid(v) and v:Team() == TEAM_TERROR and v:Alive() then
|
||||
v:ConCommand("ttt_cl_startpopup")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function GetPlayerFilter(pred)
|
||||
local filter = {}
|
||||
for k, v in player.Iterator() do
|
||||
if IsValid(v) and pred(v) then
|
||||
table.insert(filter, v)
|
||||
end
|
||||
end
|
||||
return filter
|
||||
end
|
||||
|
||||
function GetTraitorFilter(alive_only)
|
||||
return GetPlayerFilter(function(p) return p:GetTraitor() and (not alive_only or p:IsTerror()) end)
|
||||
end
|
||||
|
||||
function GetDetectiveFilter(alive_only)
|
||||
return GetPlayerFilter(function(p) return p:IsDetective() and (not alive_only or p:IsTerror()) end)
|
||||
end
|
||||
|
||||
function GetInnocentFilter(alive_only)
|
||||
return GetPlayerFilter(function(p) return (not p:IsTraitor()) and (not alive_only or p:IsTerror()) end)
|
||||
end
|
||||
|
||||
function GetRoleFilter(role, alive_only)
|
||||
return GetPlayerFilter(function(p) return p:IsRole(role) and (not alive_only or p:IsTerror()) end)
|
||||
end
|
||||
|
||||
---- Communication control
|
||||
CreateConVar("ttt_limit_spectator_chat", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY)
|
||||
CreateConVar("ttt_limit_spectator_voice", "1", FCVAR_ARCHIVE + FCVAR_NOTIFY)
|
||||
|
||||
function GM:PlayerCanSeePlayersChat(text, team_only, listener, speaker)
|
||||
if (not IsValid(listener)) then return false end
|
||||
if (not IsValid(speaker)) then
|
||||
if isentity(speaker) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local sTeam = speaker:Team() == TEAM_SPEC
|
||||
local lTeam = listener:Team() == TEAM_SPEC
|
||||
|
||||
if (GetRoundState() != ROUND_ACTIVE) or -- Round isn't active
|
||||
(not GetConVar("ttt_limit_spectator_chat"):GetBool()) or -- Spectators can chat freely
|
||||
(not DetectiveMode()) or -- Mumbling
|
||||
(not sTeam and ((team_only and not speaker:IsSpecial()) or (not team_only))) or -- If someone alive talks (and not a special role in teamchat's case)
|
||||
(not sTeam and team_only and speaker:GetRole() == listener:GetRole()) or
|
||||
(sTeam and lTeam) then -- If the speaker and listener are spectators
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local mumbles = {"mumble", "mm", "hmm", "hum", "mum", "mbm", "mble", "ham", "mammaries", "political situation", "mrmm", "hrm",
|
||||
"uzbekistan", "mumu", "cheese export", "hmhm", "mmh", "mumble", "mphrrt", "mrh", "hmm", "mumble", "mbmm", "hmml", "mfrrm"}
|
||||
|
||||
-- While a round is active, spectators can only talk among themselves. When they
|
||||
-- try to speak to all players they could divulge information about who killed
|
||||
-- them. So we mumblify them. In detective mode, we shut them up entirely.
|
||||
function GM:PlayerSay(ply, text, team_only)
|
||||
if not IsValid(ply) then return text or "" end
|
||||
|
||||
if GetRoundState() == ROUND_ACTIVE then
|
||||
local team = ply:Team() == TEAM_SPEC
|
||||
if team and not DetectiveMode() then
|
||||
local filtered = {}
|
||||
for k, v in ipairs(string.Explode(" ", text)) do
|
||||
-- grab word characters and whitelisted interpunction
|
||||
-- necessary or leetspeek will be used (by trolls especially)
|
||||
local word, interp = string.match(v, "(%a*)([%.,;!%?]*)")
|
||||
if word != "" then
|
||||
table.insert(filtered, mumbles[math.random(1, #mumbles)] .. interp)
|
||||
end
|
||||
end
|
||||
|
||||
-- make sure we have something to say
|
||||
if table.IsEmpty(filtered) then
|
||||
table.insert(filtered, mumbles[math.random(1, #mumbles)])
|
||||
end
|
||||
|
||||
table.insert(filtered, 1, "[MUMBLED]")
|
||||
return table.concat(filtered, " ")
|
||||
elseif team_only and not team and ply:IsSpecial() then
|
||||
RoleChatMsg(ply, ply:GetRole(), text)
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
return text or ""
|
||||
end
|
||||
|
||||
|
||||
-- Mute players when we are about to run map cleanup, because it might cause
|
||||
-- net buffer overflows on clients.
|
||||
local mute_all = false
|
||||
function MuteForRestart(state)
|
||||
mute_all = state
|
||||
end
|
||||
|
||||
|
||||
local loc_voice = CreateConVar("ttt_locational_voice", "0")
|
||||
|
||||
-- Of course voice has to be limited as well
|
||||
function GM:PlayerCanHearPlayersVoice(listener, speaker)
|
||||
-- Enforced silence
|
||||
if mute_all then
|
||||
return false, false
|
||||
end
|
||||
|
||||
if (not IsValid(speaker)) or (not IsValid(listener)) or (listener == speaker) then
|
||||
return false, false
|
||||
end
|
||||
|
||||
-- limited if specific convar is on, or we're in detective mode
|
||||
local limit = DetectiveMode() or GetConVar("ttt_limit_spectator_voice"):GetBool()
|
||||
|
||||
-- Spectators should not be heard by living players during round
|
||||
if speaker:IsSpec() and (not listener:IsSpec()) and limit and GetRoundState() == ROUND_ACTIVE then
|
||||
return false, false
|
||||
end
|
||||
|
||||
-- Specific mute
|
||||
if listener:IsSpec() and listener.mute_team == speaker:Team() or listener.mute_team == MUTE_ALL then
|
||||
return false, false
|
||||
end
|
||||
|
||||
-- Specs should not hear each other locationally
|
||||
if speaker:IsSpec() and listener:IsSpec() then
|
||||
return true, false
|
||||
end
|
||||
|
||||
-- Traitors "team"chat by default, non-locationally
|
||||
if speaker:IsActiveTraitor() then
|
||||
if speaker.traitor_gvoice then
|
||||
return true, loc_voice:GetBool()
|
||||
elseif listener:IsActiveTraitor() then
|
||||
return true, false
|
||||
else
|
||||
-- unless traitor_gvoice is true, normal innos can't hear speaker
|
||||
return false, false
|
||||
end
|
||||
end
|
||||
|
||||
return true, (loc_voice:GetBool() and GetRoundState() != ROUND_POST)
|
||||
end
|
||||
|
||||
local function SendTraitorVoiceState(speaker, state)
|
||||
-- send umsg to living traitors that this is traitor-only talk
|
||||
local rf = GetTraitorFilter(true)
|
||||
|
||||
-- make it as small as possible, to get there as fast as possible
|
||||
-- we can fit it into a mere byte by being cheeky.
|
||||
net.Start("TTT_TraitorVoiceState")
|
||||
net.WriteUInt(speaker:EntIndex() - 1, 7) -- player ids can only be 1-128
|
||||
net.WriteBit(state)
|
||||
if rf then net.Send(rf)
|
||||
else net.Broadcast() end
|
||||
end
|
||||
|
||||
|
||||
local function TraitorGlobalVoice(ply, cmd, args)
|
||||
if not IsValid(ply) or not ply:IsActiveTraitor() then return end
|
||||
if #args != 1 then return end
|
||||
local state = tonumber(args[1])
|
||||
|
||||
ply.traitor_gvoice = (state == 1)
|
||||
|
||||
SendTraitorVoiceState(ply, ply.traitor_gvoice)
|
||||
end
|
||||
concommand.Add("tvog", TraitorGlobalVoice)
|
||||
|
||||
local MuteModes = {
|
||||
[MUTE_NONE] = "mute_off",
|
||||
[MUTE_TERROR] = "mute_living",
|
||||
[MUTE_ALL] = "mute_all",
|
||||
[MUTE_SPEC] = "mute_specs"
|
||||
}
|
||||
|
||||
local function MuteTeam(ply, cmd, args)
|
||||
if not IsValid(ply) then return end
|
||||
if not (#args == 1 and tonumber(args[1])) then return end
|
||||
if not ply:IsSpec() then
|
||||
ply.mute_team = -1
|
||||
return
|
||||
end
|
||||
|
||||
local t = tonumber(args[1])
|
||||
ply.mute_team = t
|
||||
|
||||
-- remove all ifs
|
||||
LANG.Msg(ply, MuteModes[t])
|
||||
|
||||
end
|
||||
concommand.Add("ttt_mute_team", MuteTeam)
|
||||
|
||||
local ttt_lastwords = CreateConVar("ttt_lastwords_chatprint", "0")
|
||||
|
||||
local LastWordContext = {
|
||||
[KILL_NORMAL] = "",
|
||||
[KILL_SUICIDE] = " *kills self*",
|
||||
[KILL_FALL] = " *SPLUT*",
|
||||
[KILL_BURN] = " *crackle*"
|
||||
};
|
||||
|
||||
local function LastWordsMsg(ply, words)
|
||||
-- only append "--" if there's no ending interpunction
|
||||
local final = string.match(words, "[\\.\\!\\?]$") != nil
|
||||
|
||||
-- add optional context relating to death type
|
||||
local context = LastWordContext[ply.death_type] or ""
|
||||
local lastWordsStr = words .. (final and "" or "--") .. context
|
||||
|
||||
net.Start("TTT_LastWordsMsg")
|
||||
net.WritePlayer(ply)
|
||||
net.WriteString(lastWordsStr)
|
||||
net.Broadcast()
|
||||
|
||||
hook.Run("TTTLastWordsMsg", ply, lastWordsStr)
|
||||
end
|
||||
|
||||
local function LastWords(ply, cmd, args)
|
||||
if IsValid(ply) and (not ply:Alive()) and #args > 1 then
|
||||
local id = tonumber(args[1])
|
||||
if id and ply.last_words_id and id == ply.last_words_id then
|
||||
-- never allow multiple last word stuff
|
||||
ply.last_words_id = nil
|
||||
|
||||
-- we will be storing this on the ragdoll
|
||||
local rag = ply.server_ragdoll
|
||||
if not (IsValid(rag) and rag.player_ragdoll) then
|
||||
rag = nil
|
||||
end
|
||||
|
||||
--- last id'd person
|
||||
local last_seen = tonumber(args[2])
|
||||
if last_seen then
|
||||
local ent = Entity(last_seen)
|
||||
if IsValid(ent) and ent:IsPlayer() and rag and (not rag.lastid) then
|
||||
rag.lastid = {ent=ent, t=CurTime()}
|
||||
end
|
||||
end
|
||||
|
||||
--- last words
|
||||
local words = string.Trim(args[3])
|
||||
|
||||
-- nothing of interest
|
||||
if string.len(words) < 2 then return end
|
||||
|
||||
-- ignore admin commands
|
||||
local firstchar = string.sub(words, 1, 1)
|
||||
if firstchar == "!" or firstchar == "@" or firstchar == "/" then return end
|
||||
|
||||
|
||||
if ttt_lastwords:GetBool() or ply.death_type == KILL_FALL then
|
||||
LastWordsMsg(ply, words)
|
||||
end
|
||||
|
||||
if rag and (not rag.last_words) then
|
||||
rag.last_words = words
|
||||
end
|
||||
else
|
||||
ply.last_words_id = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
concommand.Add("_deathrec", LastWords)
|
||||
|
||||
-- Override or hook in plugin for spam prevention and whatnot. Return true
|
||||
-- to block a command.
|
||||
function GM:TTTPlayerRadioCommand(ply, msg_name, msg_target)
|
||||
if ply.LastRadioCommand and ply.LastRadioCommand > (CurTime() - 0.5) then return true end
|
||||
ply.LastRadioCommand = CurTime()
|
||||
end
|
||||
|
||||
local function RadioCommand(ply, cmd, args)
|
||||
if IsValid(ply) and ply:IsTerror() and #args == 2 then
|
||||
local msg_name = args[1]
|
||||
local msg_target = args[2]
|
||||
|
||||
local name = ""
|
||||
local rag_name = nil
|
||||
|
||||
if tonumber(msg_target) then
|
||||
-- player or corpse ent idx
|
||||
local ent = Entity(tonumber(msg_target))
|
||||
if IsValid(ent) then
|
||||
if ent:IsPlayer() then
|
||||
name = ent:Nick()
|
||||
elseif ent:GetClass() == "prop_ragdoll" then
|
||||
name = LANG.NameParam("quick_corpse_id")
|
||||
rag_name = CORPSE.GetPlayerNick(ent, "A Terrorist")
|
||||
end
|
||||
end
|
||||
|
||||
msg_target = ent
|
||||
else
|
||||
-- lang string
|
||||
name = LANG.NameParam(msg_target)
|
||||
end
|
||||
|
||||
if hook.Call("TTTPlayerRadioCommand", GAMEMODE, ply, msg_name, msg_target) then
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("TTT_RadioMsg")
|
||||
net.WritePlayer(ply)
|
||||
net.WriteString(msg_name)
|
||||
net.WriteString(name)
|
||||
if rag_name then
|
||||
net.WriteString(rag_name)
|
||||
end
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
concommand.Add("_ttt_radio_send", RadioCommand)
|
||||
Reference in New Issue
Block a user