Fresh GMOD server install, lua errors.

Post Reply
guccifi3d
New to forums
New to forums
Posts: 5
https://www.youtube.com/channel/UC40BgXanDqOYoVCYFDSTfHA
Joined: Fri Oct 02, 2015 6:27 am

Fresh GMOD server install, lua errors.

Post by guccifi3d »

I'm new to GMOD server hosting and I'm trying to get a vanilla TTT server up and running.

Currently having these lua errors and I'm lost on whats causing it.



[ERROR] gamemodes/terrortown/gamemode/cl_voice.lua:666: attempt to compare number with nil
1. Draw - gamemodes/terrortown/gamemode/cl_voice.lua:666
2. unknown - gamemodes/terrortown/gamemode/cl_hud.lua:350

Any help would be appreciated.
guccifi3d
New to forums
New to forums
Posts: 5
Joined: Fri Oct 02, 2015 6:27 am

Re: Fresh GMOD server install, lua errors.

Post by guccifi3d »

Also if you need the code for lua, here it is.

Please note that I haven't touched the code at all, its default settings.

---- Voicechat popup, radio commands, text chat stuff

DEFINE_BASECLASS("gamemode_base")

local GetTranslation = LANG.GetTranslation
local GetPTranslation = LANG.GetParamTranslation
local string = string

local function LastWordsRecv()
local sender = net.ReadEntity()
local words = net.ReadString()

local was_detective = IsValid(sender) and sender:IsDetective()
local nick = IsValid(sender) and sender:Nick() or "<Unknown>"

chat.AddText(Color( 150, 150, 150 ),
Format("(%s) ", string.upper(GetTranslation("last_words"))),
was_detective and Color(50, 200, 255) or Color(0, 200, 0),
nick,
COLOR_WHITE,
": " .. words)
end
net.Receive("TTT_LastWordsMsg", LastWordsRecv)

local function RoleChatRecv()
-- virtually always our role, but future equipment might allow listening in
local role = net.ReadUInt(2)
local sender = net.ReadEntity()
if not IsValid(sender) then return end

local text = net.ReadString()

if role == ROLE_TRAITOR then
chat.AddText(Color( 255, 30, 40 ),
Format("(%s) ", string.upper(GetTranslation("traitor"))),
Color( 255, 200, 20),
sender:Nick(),
Color( 255, 255, 200),
": " .. text)

elseif role == ROLE_DETECTIVE then
chat.AddText(Color( 20, 100, 255 ),
Format("(%s) ", string.upper(GetTranslation("detective"))),
Color( 25, 200, 255),
sender:Nick(),
Color( 200, 255, 255),
": " .. text)
end
end
net.Receive("TTT_RoleChat", RoleChatRecv)

-- special processing for certain special chat types
function GM:ChatText(idx, name, text, type)

if type == "joinleave" then
if string.find(text, "Changed name during a round") then
-- prevent nick from showing up
chat.AddText(LANG.GetTranslation("name_kick"))
return true
end
end

if idx == 0 and type == "none" then
if string.sub(text, 1, 6) == "(VOTE)" then
chat.AddText(Color(255, 180, 0), string.sub(text, 8))

return true
end
end

return BaseClass.ChatText(self, idx, name, text, type)
end


-- Detectives have a blue name, in both chat and radio messages
local function AddDetectiveText(ply, text)
chat.AddText(Color(50, 200, 255),
ply:Nick(),
Color(255,255,255),
": " .. text)
end

function GM:OnPlayerChat(ply, text, teamchat, dead)
if not IsValid(ply) then return BaseClass.OnPlayerChat(self, ply, text, teamchat, dead) end

if ply:IsActiveDetective() then
AddDetectiveText(ply, text)
return true
end

local team = ply:Team() == TEAM_SPEC

if team and not dead then
dead = true
end

if teamchat and ((not team and not ply:IsSpecial()) or team) then
teamchat = false
end

return BaseClass.OnPlayerChat(self, ply, text, teamchat, dead)
end

local last_chat = ""
function GM:ChatTextChanged(text)
last_chat = text
end

function ChatInterrupt()
local client = LocalPlayer()
local id = net.ReadUInt(32)

local last_seen = IsValid(client.last_id) and client.last_id:EntIndex() or 0

local last_words = "."
if last_chat == "" then
if RADIO.LastRadio.t > CurTime() - 2 then
last_words = RADIO.LastRadio.msg
end
else
last_words = last_chat
end

RunConsoleCommand("_deathrec", tostring(id), tostring(last_seen), last_words)
end
net.Receive("TTT_InterruptChat", ChatInterrupt)

--- Radio

RADIO = {}
RADIO.Show = false

RADIO.StoredTarget = {nick="", t=0}
RADIO.LastRadio = {msg="", t=0}

-- [key] -> command
RADIO.Commands = {
{cmd="yes", text="quick_yes", format=false},
{cmd="no", text="quick_no", format=false},
{cmd="help", text="quick_help", format=false},
{cmd="imwith", text="quick_imwith", format=true},
{cmd="see", text="quick_see", format=true},
{cmd="suspect", text="quick_suspect", format=true},
{cmd="traitor", text="quick_traitor", format=true},
{cmd="innocent", text="quick_inno", format=true},
{cmd="check", text="quick_check", format=false}
};

local radioframe = nil

function RADIO:ShowRadioCommands(state)
if not state then
if radioframe and radioframe:IsValid() then
radioframe:Remove()
radioframe = nil

-- don't capture keys
self.Show = false
end
else
local client = LocalPlayer()
if not IsValid(client) then return end

if not radioframe then

local w, h = 200, 300

radioframe = vgui.Create("DForm")
radioframe:SetName(GetTranslation("quick_title"))
radioframe:SetSize(w, h)
radioframe:SetMouseInputEnabled(false)
radioframe:SetKeyboardInputEnabled(false)

radioframe:CenterVertical()

-- ASS
radioframe.ForceResize = function(s)
local w, label = 0, nil
for k,v in pairs(s.Items) do
label = v:GetChild(0)
if label:GetWide() > w then
w = label:GetWide()
end
end
s:SetWide(w + 20)
end

for key, command in pairs(self.Commands) do
local dlabel = vgui.Create("DLabel", radioframe)
local id = key .. ": "
local txt = id
if command.format then
txt = txt .. GetPTranslation(command.text, {player = GetTranslation("quick_nobody")})
else
txt = txt .. GetTranslation(command.text)
end

dlabel:SetText(txt)
dlabel:SetFont("TabLarge")
dlabel:SetTextColor(COLOR_WHITE)
dlabel:SizeToContents()

if command.format then
dlabel.target = nil
dlabel.id = id
dlabel.txt = GetTranslation(command.text)
dlabel.Think = function(s)
local tgt, v = RADIO:GetTarget()
if s.target != tgt then
s.target = tgt

tgt = string.Interp(s.txt, {player = RADIO.ToPrintable(tgt)})
if v then
tgt = util.Capitalize(tgt)
end

s:SetText(s.id .. tgt)
s:SizeToContents()
radioframe:ForceResize()
end
end
end

radioframe:AddItem(dlabel)
end

radioframe:ForceResize()
end

radioframe:MakePopup()

-- grabs input on init(), which happens in makepopup
radioframe:SetMouseInputEnabled(false)
radioframe:SetKeyboardInputEnabled(false)

-- capture slot keys while we're open
self.Show = true

timer.Create("radiocmdshow", 3, 1,
function() RADIO:ShowRadioCommands(false) end)
end
end

function RADIO:SendCommand(slotidx)
local c = self.Commands[slotidx]
if c then
RunConsoleCommand("ttt_radio", c.cmd)

self:ShowRadioCommands(false)
end
end

function RADIO:GetTargetType()
if not IsValid(LocalPlayer()) then return end
local trace = LocalPlayer():GetEyeTrace(MASK_SHOT)

if not trace or (not trace.Hit) or (not IsValid(trace.Entity)) then return end

local ent = trace.Entity

if ent:IsPlayer() then
if ent:GetNWBool("disguised", false) then
return "quick_disg", true
else
return ent, false
end
elseif ent:GetClass() == "prop_ragdoll" and CORPSE.GetPlayerNick(ent, "") != "" then

if DetectiveMode() and not CORPSE.GetFound(ent, false) then
return "quick_corpse", true
else
return ent, false
end
end
end


function RADIO.ToPrintable(target)
if type(target) == "string" then
return GetTranslation(target)
elseif IsValid(target) then
if target:IsPlayer() then
return target:Nick()
elseif target:GetClass() == "prop_ragdoll" then
return GetPTranslation("quick_corpse_id", {player = CORPSE.GetPlayerNick(target, "A Terrorist")})
end
end
end

function RADIO:GetTarget()
local client = LocalPlayer()
if IsValid(client) then

local current, vague = self:GetTargetType()
if current then return current, vague end

local stored = self.StoredTarget
if stored.target and stored.t > (CurTime() - 3) then
return stored.target, stored.vague
end
end
return "quick_nobody", true
end

function RADIO:StoreTarget()
local current, vague = self:GetTargetType()
if current then
self.StoredTarget.target = current
self.StoredTarget.vague = vague
self.StoredTarget.t = CurTime()
end
end

-- Radio commands are a console cmd instead of directly sent from RADIO, because
-- this way players can bind keys to them
local function RadioCommand(ply, cmd, arg)
if not IsValid(ply) or #arg != 1 then
print("ttt_radio failed, too many arguments?")
return
end

if RADIO.LastRadio.t > (CurTime() - 0.5) then return end

local msg_type = arg[1]
local target, vague = RADIO:GetTarget()
local msg_name = nil

-- this will not be what is shown, but what is stored in case this message
-- has to be used as last words (which will always be english for now)
local text = nil

for _, msg in pairs(RADIO.Commands) do
if msg.cmd == msg_type then
local eng = LANG.GetTranslationFromLanguage(msg.text, "english")
text = msg.format and string.Interp(eng, {player = RADIO.ToPrintable(target)}) or eng

msg_name = msg.text
break
end
end

if not text then
print("ttt_radio failed, argument not valid radiocommand")
return
end

if vague then
text = util.Capitalize(text)
end

RADIO.LastRadio.t = CurTime()
RADIO.LastRadio.msg = text

-- target is either a lang string or an entity
target = type(target) == "string" and target or tostring(target:EntIndex())

RunConsoleCommand("_ttt_radio_send", msg_name, tostring(target))
end

local function RadioComplete(cmd, arg)
local c = {}
for k, cmd in pairs(RADIO.Commands) do
table.insert(c, cmd.cmd)
end
return c
end
concommand.Add("ttt_radio", RadioCommand, RadioComplete)


local function RadioMsgRecv()
local sender = net.ReadEntity()
local msg = net.ReadString()
local param = net.ReadString()

if not (IsValid(sender) and sender:IsPlayer()) then return end

GAMEMODE:PlayerSentRadioCommand(sender, msg, param)

-- if param is a language string, translate it
-- else it's a nickname
local lang_param = LANG.GetNameParam(param)
if lang_param then
if lang_param == "quick_corpse_id" then
-- special case where nested translation is needed
param = GetPTranslation(lang_param, {player = net.ReadString()})
else
param = GetTranslation(lang_param)
end
end

local text = GetPTranslation(msg, {player = param})

-- don't want to capitalize nicks, but everything else is fair game
if lang_param then
text = util.Capitalize(text)
end

if sender:IsDetective() then
AddDetectiveText(sender, text)
else
chat.AddText(sender,
COLOR_WHITE,
": " .. text)
end
end
net.Receive("TTT_RadioMsg", RadioMsgRecv)


local radio_gestures = {
quick_yes = ACT_GMOD_GESTURE_AGREE,
quick_no = ACT_GMOD_GESTURE_DISAGREE,
quick_see = ACT_GMOD_GESTURE_WAVE,
quick_check = ACT_SIGNAL_GROUP,
quick_suspect = ACT_SIGNAL_HALT
};

function GM:PlayerSentRadioCommand(ply, name, target)
local act = radio_gestures[name]
if act then
ply:AnimPerformGesture(act)
end
end


--- voicechat stuff
VOICE = {}

local MutedState = nil

-- voice popups, copied from base gamemode and modified

g_VoicePanelList = nil

-- 255 at 100
-- 5 at 5000
local function VoiceNotifyThink(pnl)
if not (IsValid(pnl) and LocalPlayer() and IsValid(pnl.ply)) then return end
if not (GetGlobalBool("ttt_locational_voice", false) and (not pnl.ply:IsSpec()) and (pnl.ply != LocalPlayer())) then return end
if LocalPlayer():IsActiveTraitor() && pnl.ply:IsActiveTraitor() then return end

local d = LocalPlayer():GetPos():Distance(pnl.ply:GetPos())

pnl:SetAlpha(math.max(-0.1 * d + 255, 15))
end

local PlayerVoicePanels = {}

function GM:PlayerStartVoice( ply )
local client = LocalPlayer()
if not IsValid(g_VoicePanelList) or not IsValid(client) then return end

-- There'd be an extra one if voice_loopback is on, so remove it.
GAMEMODE:PlayerEndVoice(ply, true)

if not IsValid(ply) then return end

-- Tell server this is global
if client == ply then
if client:IsActiveTraitor() then
if (not client:KeyDown(IN_SPEED)) and (not client:KeyDownLast(IN_SPEED)) then
client.traitor_gvoice = true
RunConsoleCommand("tvog", "1")
else
client.traitor_gvoice = false
RunConsoleCommand("tvog", "0")
end
end

VOICE.SetSpeaking(true)
end

local pnl = g_VoicePanelList:Add("VoiceNotify")
pnl:Setup(ply)
pnl:Dock(TOP)

local oldThink = pnl.Think
pnl.Think = function( self )
oldThink( self )
VoiceNotifyThink( self )
end

local shade = Color(0, 0, 0, 150)
pnl.Paint = function(s, w, h)
if not IsValid(s.ply) then return end
draw.RoundedBox(4, 0, 0, w, h, s.Color)
draw.RoundedBox(4, 1, 1, w-2, h-2, shade)
end

if client:IsActiveTraitor() then
if ply == client then
if not client.traitor_gvoice then
pnl.Color = Color(200, 20, 20, 255)
end
elseif ply:IsActiveTraitor() then
if not ply.traitor_gvoice then
pnl.Color = Color(200, 20, 20, 255)
end
end
end

if ply:IsActiveDetective() then
pnl.Color = Color(20, 20, 200, 255)
end

PlayerVoicePanels[ply] = pnl

-- run ear gesture
if not (ply:IsActiveTraitor() and (not ply.traitor_gvoice)) then
ply:AnimPerformGesture(ACT_GMOD_IN_CHAT)
end
end

local function ReceiveVoiceState()
local idx = net.ReadUInt(7) + 1 -- we -1 serverside
local state = net.ReadBit() == 1

-- prevent glitching due to chat starting/ending across round boundary
if GAMEMODE.round_state != ROUND_ACTIVE then return end
if (not IsValid(LocalPlayer())) or (not LocalPlayer():IsActiveTraitor()) then return end

local ply = player.GetByID(idx)
if IsValid(ply) then
ply.traitor_gvoice = state

if IsValid(PlayerVoicePanels[ply]) then
PlayerVoicePanels[ply].Color = state and Color(0,200,0) or Color(200, 0, 0)
end
end
end
net.Receive("TTT_TraitorVoiceState", ReceiveVoiceState)

local function VoiceClean()
for ply, pnl in pairs( PlayerVoicePanels ) do
if (not IsValid(pnl)) or (not IsValid(ply)) then
GAMEMODE:PlayerEndVoice(ply)
end
end
end
timer.Create( "VoiceClean", 10, 0, VoiceClean )


function GM:PlayerEndVoice(ply, no_reset)
if IsValid( PlayerVoicePanels[ply] ) then
PlayerVoicePanels[ply]:Remove()
PlayerVoicePanels[ply] = nil
end

if IsValid(ply) and not no_reset then
ply.traitor_gvoice = false
end

if ply == LocalPlayer() then
VOICE.SetSpeaking(false)
end
end

local function CreateVoiceVGUI()
g_VoicePanelList = vgui.Create( "DPanel" )

g_VoicePanelList:ParentToHUD()
g_VoicePanelList:SetPos(25, 25)
g_VoicePanelList:SetSize(200, ScrH() - 200)
g_VoicePanelList:SetDrawBackground(false)

MutedState = vgui.Create("DLabel")
MutedState:SetPos(ScrW() - 200, ScrH() - 50)
MutedState:SetSize(200, 50)
MutedState:SetFont("Trebuchet18")
MutedState:SetText("")
MutedState:SetTextColor(Color(240, 240, 240, 250))
MutedState:SetVisible(false)
end
hook.Add( "InitPostEntity", "CreateVoiceVGUI", CreateVoiceVGUI )

MUTE_NONE = 0
MUTE_TERROR = TEAM_TERROR
MUTE_SPEC = TEAM_SPEC

local MuteStates = {MUTE_NONE, MUTE_TERROR, MUTE_SPEC}

local MuteText = {
[MUTE_NONE] = "",
[MUTE_TERROR] = "mute_living",
[MUTE_SPEC] = "mute_specs"
};

local function SetMuteState(state)
if MutedState then
MutedState:SetText(string.upper(GetTranslation(MuteText[state])))
MutedState:SetVisible(state != MUTE_NONE)
end
end

local mute_state = MUTE_NONE
function VOICE.CycleMuteState(force_state)
mute_state = force_state or next(MuteText, mute_state)

if not mute_state then mute_state = MUTE_NONE end

SetMuteState(mute_state)

return mute_state
end

local battery_max = 100
local battery_min = 10
function VOICE.InitBattery()
LocalPlayer().voice_battery = battery_max
end

local function GetRechargeRate()
local r = GetGlobalFloat("ttt_voice_drain_recharge", 0.05)
if LocalPlayer().voice_battery < battery_min then
r = r / 2
end
return r
end

local function GetDrainRate()
if not GetGlobalBool("ttt_voice_drain", false) then return 0 end

if GetRoundState() != ROUND_ACTIVE then return 0 end
local ply = LocalPlayer()
if (not IsValid(ply)) or ply:IsSpec() then return 0 end

if ply:IsAdmin() or ply:IsDetective() then
return GetGlobalFloat("ttt_voice_drain_admin", 0)
else
return GetGlobalFloat("ttt_voice_drain_normal", 0)
end
end

local function IsTraitorChatting(client)
return client:IsActiveTraitor() and (not client.traitor_gvoice)
end

function VOICE.Tick()
if not GetGlobalBool("ttt_voice_drain", false) then return end

local client = LocalPlayer()
if VOICE.IsSpeaking() and (not IsTraitorChatting(client)) then
client.voice_battery = client.voice_battery - GetDrainRate()

if not VOICE.CanSpeak() then
client.voice_battery = 0
RunConsoleCommand("-voicerecord")
end
elseif client.voice_battery < battery_max then
client.voice_battery = client.voice_battery + GetRechargeRate()
end
end

-- Player:IsSpeaking() does not work for localplayer
function VOICE.IsSpeaking() return LocalPlayer().speaking end
function VOICE.SetSpeaking(state) LocalPlayer().speaking = state end

function VOICE.CanSpeak()
if not GetGlobalBool("ttt_voice_drain", false) then return true end

return LocalPlayer().voice_battery > battery_min or IsTraitorChatting(LocalPlayer())
end

local speaker = surface.GetTextureID("voice/icntlk_sv")
function VOICE.Draw(client)
local b = client.voice_battery
if b >= battery_max then return end

local x = 25
local y = 10
local w = 200
local h = 6

if b < battery_min and CurTime() % 0.2 < 0.1 then
surface.SetDrawColor(200, 0, 0, 155)
else
surface.SetDrawColor(0, 200, 0, 255)
end
surface.DrawOutlinedRect(x, y, w, h)

surface.SetTexture(speaker)
surface.DrawTexturedRect(5, 5, 16, 16)

x = x + 1
y = y + 1
w = w - 2
h = h - 2

surface.SetDrawColor(0, 200, 0, 150)
surface.DrawRect(x, y, w * math.Clamp((client.voice_battery - 10) / 90, 0, 1), h)
end






-- HUD HUD HUD

local table = table
local surface = surface
local draw = draw
local math = math
local string = string

local GetTranslation = LANG.GetTranslation
local GetPTranslation = LANG.GetParamTranslation
local GetLang = LANG.GetUnsafeLanguageTable
local interp = string.Interp

-- Fonts
surface.CreateFont("TraitorState", {font = "Trebuchet24",
size = 28,
weight = 1000})
surface.CreateFont("TimeLeft", {font = "Trebuchet24",
size = 24,
weight = 800})
surface.CreateFont("HealthAmmo", {font = "Trebuchet24",
size = 24,
weight = 750})
-- Color presets
local bg_colors = {
background_main = Color(0, 0, 10, 200),

noround = Color(100,100,100,200),
traitor = Color(200, 25, 25, 200),
innocent = Color(25, 200, 25, 200),
detective = Color(25, 25, 200, 200)
};

local health_colors = {
border = COLOR_WHITE,
background = Color(100, 25, 25, 222),
fill = Color(200, 50, 50, 250)
};

local ammo_colors = {
border = COLOR_WHITE,
background = Color(20, 20, 5, 222),
fill = Color(205, 155, 0, 255)
};


-- Modified RoundedBox
local Tex_Corner8 = surface.GetTextureID( "gui/corner8" )
local function RoundedMeter( bs, x, y, w, h, color)
surface.SetDrawColor(clr(color))

surface.DrawRect( x+bs, y, w-bs*2, h )
surface.DrawRect( x, y+bs, bs, h-bs*2 )

surface.SetTexture( Tex_Corner8 )
surface.DrawTexturedRectRotated( x + bs/2 , y + bs/2, bs, bs, 0 )
surface.DrawTexturedRectRotated( x + bs/2 , y + h -bs/2, bs, bs, 90 )

if w > 14 then
surface.DrawRect( x+w-bs, y+bs, bs, h-bs*2 )
surface.DrawTexturedRectRotated( x + w - bs/2 , y + bs/2, bs, bs, 270 )
surface.DrawTexturedRectRotated( x + w - bs/2 , y + h - bs/2, bs, bs, 180 )
else
surface.DrawRect( x + math.max(w-bs, bs), y, bs/2, h )
end

end

---- The bar painting is loosely based on:
---- http://wiki.garrysmod.com/?title=Creating_a_HUD

-- Paints a graphical meter bar
local function PaintBar(x, y, w, h, colors, value)
-- Background
-- slightly enlarged to make a subtle border
draw.RoundedBox(8, x-1, y-1, w+2, h+2, colors.background)

-- Fill
local width = w * math.Clamp(value, 0, 1)

if width > 0 then
RoundedMeter(8, x, y, width, h, colors.fill)
end
end

local roundstate_string = {
[ROUND_WAIT] = "round_wait",
[ROUND_PREP] = "round_prep",
[ROUND_ACTIVE] = "round_active",
[ROUND_POST] = "round_post"
};

-- Returns player's ammo information
local function GetAmmo(ply)
local weap = ply:GetActiveWeapon()
if not weap or not ply:Alive() then return -1 end

local ammo_inv = weap:Ammo1() or 0
local ammo_clip = weap:Clip1() or 0
local ammo_max = weap.Primary.ClipSize or 0

return ammo_clip, ammo_max, ammo_inv
end

local function DrawBg(x, y, width, height, client)
-- Traitor area sizes
local th = 30
local tw = 170

-- Adjust for these
y = y - th
height = height + th

-- main bg area, invariant
-- encompasses entire area
draw.RoundedBox(8, x, y, width, height, bg_colors.background_main)

-- main border, traitor based
local col = bg_colors.innocent
if GAMEMODE.round_state != ROUND_ACTIVE then
col = bg_colors.noround
elseif client:GetTraitor() then
col = bg_colors.traitor
elseif client:GetDetective() then
col = bg_colors.detective
end

draw.RoundedBox(8, x, y, tw, th, col)
end

local sf = surface
local dr = draw

local function ShadowedText(text, font, x, y, color, xalign, yalign)

dr.SimpleText(text, font, x+2, y+2, COLOR_BLACK, xalign, yalign)

dr.SimpleText(text, font, x, y, color, xalign, yalign)
end

local margin = 10

-- Paint punch-o-meter
local function PunchPaint(client)
local L = GetLang()
local punch = client:GetNWFloat("specpunches", 0)

local width, height = 200, 25
local x = ScrW() / 2 - width/2
local y = margin/2 + height

PaintBar(x, y, width, height, ammo_colors, punch)

local color = bg_colors.background_main

dr.SimpleText(L.punch_title, "HealthAmmo", ScrW() / 2, y, color, TEXT_ALIGN_CENTER)

dr.SimpleText(L.punch_help, "TabLarge", ScrW() / 2, margin, COLOR_WHITE, TEXT_ALIGN_CENTER)

local bonus = client:GetNWInt("bonuspunches", 0)
if bonus != 0 then
local text
if bonus < 0 then
text = interp(L.punch_bonus, {num = bonus})
else
text = interp(L.punch_malus, {num = bonus})
end

dr.SimpleText(text, "TabLarge", ScrW() / 2, y * 2, COLOR_WHITE, TEXT_ALIGN_CENTER)
end
end

local key_params = { usekey = Key("+use", "USE") }

local function SpecHUDPaint(client)
local L = GetLang() -- for fast direct table lookups

-- Draw round state
local x = margin
local height = 32
local width = 250
local round_y = ScrH() - height - margin

-- move up a little on low resolutions to allow space for spectator hints
if ScrW() < 1000 then round_y = round_y - 15 end

local time_x = x + 170
local time_y = round_y + 4

draw.RoundedBox(8, x, round_y, width, height, bg_colors.background_main)
draw.RoundedBox(8, x, round_y, time_x - x, height, bg_colors.noround)

local text = L[ roundstate_string[GAMEMODE.round_state] ]
ShadowedText(text, "TraitorState", x + margin, round_y, COLOR_WHITE)

-- Draw round/prep/post time remaining
local text = util.SimpleTime(math.max(0, GetGlobalFloat("ttt_round_end", 0) - CurTime()), "%02i:%02i")
ShadowedText(text, "TimeLeft", time_x + margin, time_y, COLOR_WHITE)

local tgt = client:GetObserverTarget()
if IsValid(tgt) and tgt:IsPlayer() then
ShadowedText(tgt:Nick(), "TimeLeft", ScrW() / 2, margin, COLOR_WHITE, TEXT_ALIGN_CENTER)

elseif IsValid(tgt) and tgt:GetNWEntity("spec_owner", nil) == client then
PunchPaint(client)
else
ShadowedText(interp(L.spec_help, key_params), "TabLarge", ScrW() / 2, margin, COLOR_WHITE, TEXT_ALIGN_CENTER)
end
end

local ttt_health_label = CreateClientConVar("ttt_health_label", "0", true)

local function InfoPaint(client)
local L = GetLang()

local width = 250
local height = 90

local x = margin
local y = ScrH() - margin - height

DrawBg(x, y, width, height, client)

local bar_height = 25
local bar_width = width - (margin*2)

-- Draw health
local health = math.max(0, client:Health())
local health_y = y + margin

PaintBar(x + margin, health_y, bar_width, bar_height, health_colors, health/100)

ShadowedText(tostring(health), "HealthAmmo", bar_width, health_y, COLOR_WHITE, TEXT_ALIGN_RIGHT, TEXT_ALIGN_RIGHT)

if ttt_health_label:GetBool() then
local health_status = util.HealthToString(health)
draw.SimpleText(L[health_status], "TabLarge", x + margin*2, health_y + bar_height/2, COLOR_WHITE, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end

-- Draw ammo
if client:GetActiveWeapon().Primary then
local ammo_clip, ammo_max, ammo_inv = GetAmmo(client)
if ammo_clip != -1 then
local ammo_y = health_y + bar_height + margin
PaintBar(x+margin, ammo_y, bar_width, bar_height, ammo_colors, ammo_clip/ammo_max)
local text = string.format("%i + %02i", ammo_clip, ammo_inv)

ShadowedText(text, "HealthAmmo", bar_width, ammo_y, COLOR_WHITE, TEXT_ALIGN_RIGHT, TEXT_ALIGN_RIGHT)
end
end

-- Draw traitor state
local round_state = GAMEMODE.round_state

local traitor_y = y - 30
local text = nil
if round_state == ROUND_ACTIVE then
text = L[ client:GetRoleStringRaw() ]
else
text = L[ roundstate_string[round_state] ]
end

ShadowedText(text, "TraitorState", x + margin + 73, traitor_y, COLOR_WHITE, TEXT_ALIGN_CENTER)

-- Draw round time
local is_haste = HasteMode() and round_state == ROUND_ACTIVE
local is_traitor = client:IsActiveTraitor()

local endtime = GetGlobalFloat("ttt_round_end", 0) - CurTime()

local text
local font = "TimeLeft"
local color = COLOR_WHITE
local rx = x + margin + 170
local ry = traitor_y + 3

-- Time displays differently depending on whether haste mode is on,
-- whether the player is traitor or not, and whether it is overtime.
if is_haste then
local hastetime = GetGlobalFloat("ttt_haste_end", 0) - CurTime()
if hastetime < 0 then
if (not is_traitor) or (math.ceil(CurTime()) % 7 <= 2) then
-- innocent or blinking "overtime"
text = L.overtime
font = "Trebuchet18"

-- need to hack the position a little because of the font switch
ry = ry + 5
rx = rx - 3
else
-- traitor and not blinking "overtime" right now, so standard endtime display
text = util.SimpleTime(math.max(0, endtime), "%02i:%02i")
color = COLOR_RED
end
else
-- still in starting period
local t = hastetime
if is_traitor and math.ceil(CurTime()) % 6 < 2 then
t = endtime
color = COLOR_RED
end
text = util.SimpleTime(math.max(0, t), "%02i:%02i")
end
else
-- bog standard time when haste mode is off (or round not active)
text = util.SimpleTime(math.max(0, endtime), "%02i:%02i")
end

ShadowedText(text, font, rx, ry, color)

if is_haste then
dr.SimpleText(L.hastemode, "TabLarge", x + margin + 165, traitor_y - 8)
end

end

-- Paints player status HUD element in the bottom left
function GM:HUDPaint()
local client = LocalPlayer()

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTTargetID" ) then
hook.Call( "HUDDrawTargetID", GAMEMODE )
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTMStack" ) then
MSTACK:Draw(client)
end

if (not client:Alive()) or client:Team() == TEAM_SPEC then
if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTSpecHUD" ) then
SpecHUDPaint(client)
end

return
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTRadar" ) then
RADAR:Draw(client)
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTTButton" ) then
TBHUD:Draw(client)
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTWSwitch" ) then
WSWITCH:Draw(client)
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTVoice" ) then
VOICE.Draw(client)
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTDisguise" ) then
DISGUISE.Draw(client)
end

if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTPickupHistory" ) then
hook.Call( "HUDDrawPickupHistory", GAMEMODE )
end

-- Draw bottom left info panel
if hook.Call( "HUDShouldDraw", GAMEMODE, "TTTInfoPanel" ) then
InfoPaint(client)
end
end

-- Hide the standard HUD stuff
local hud = {"CHudHealth", "CHudBattery", "CHudAmmo", "CHudSecondaryAmmo"}
function GM:HUDShouldDraw(name)
for k, v in pairs(hud) do
if name == v then return false end
end

return true
end
Post Reply