--[[ | 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/ --]] -- Traitor radar rendering local render = render local surface = surface local string = string local player = player local math = math RADAR = {} RADAR.targets = {} RADAR.enable = false RADAR.duration = 30 RADAR.endtime = 0 RADAR.bombs = {} RADAR.bombs_count = 0 RADAR.repeating = true RADAR.samples = {} RADAR.samples_count = 0 RADAR.called_corpses = {} function RADAR:EndScan() self.enable = false self.endtime = CurTime() end function RADAR:Clear() self:EndScan() self.bombs = {} self.samples = {} self.bombs_count = 0 self.samples_count = 0 end function RADAR:Timeout() self:EndScan() if self.repeating and LocalPlayer() and LocalPlayer():IsActiveSpecial() and LocalPlayer():HasEquipmentItem(EQUIP_RADAR) then RunConsoleCommand("ttt_radar_scan") end end -- cache stuff we'll be drawing function RADAR.CacheEnts() -- also do some corpse cleanup here for k, corpse in pairs(RADAR.called_corpses) do if (corpse.called + 45) < CurTime() then RADAR.called_corpses[k] = nil -- will make # inaccurate, no big deal end end if RADAR.bombs_count == 0 then return end -- Update bomb positions for those we know about for idx, b in pairs(RADAR.bombs) do local ent = Entity(idx) if IsValid(ent) then b.pos = ent:GetPos() end end end function RADAR.Bought(is_item, id) if is_item and id == EQUIP_RADAR then RunConsoleCommand("ttt_radar_scan") end end hook.Add("TTTBoughtItem", "RadarBoughtItem", RADAR.Bought) local function DrawTarget(tgt, size, offset, no_shrink) local scrpos = tgt.pos:ToScreen() -- sweet local sz = (IsOffScreen(scrpos) and (not no_shrink)) and size/2 or size scrpos.x = math.Clamp(scrpos.x, sz, ScrW() - sz) scrpos.y = math.Clamp(scrpos.y, sz, ScrH() - sz) if IsOffScreen(scrpos) then return end surface.DrawTexturedRect(scrpos.x - sz, scrpos.y - sz, sz * 2, sz * 2) -- Drawing full size? if sz == size then local text = math.ceil(LocalPlayer():GetPos():Distance(tgt.pos)) local w, h = surface.GetTextSize(text) -- Show range to target surface.SetTextPos(scrpos.x - w/2, scrpos.y + (offset * sz) - h/2) surface.DrawText(text) if tgt.t then -- Show time text = util.SimpleTime(tgt.t - CurTime(), "%02i:%02i") w, h = surface.GetTextSize(text) surface.SetTextPos(scrpos.x - w / 2, scrpos.y + sz / 2) surface.DrawText(text) elseif tgt.nick then -- Show nickname text = tgt.nick w, h = surface.GetTextSize(text) surface.SetTextPos(scrpos.x - w / 2, scrpos.y + sz / 2) surface.DrawText(text) end end end local indicator = surface.GetTextureID("effects/select_ring") local c4warn = surface.GetTextureID("vgui/ttt/icon_c4warn") local sample_scan = surface.GetTextureID("vgui/ttt/sample_scan") local det_beacon = surface.GetTextureID("vgui/ttt/det_beacon") local GetPTranslation = LANG.GetParamTranslation local FormatTime = util.SimpleTime local near_cursor_dist = 180 function RADAR:Draw(client) if not client then return end surface.SetFont("HudSelectionText") -- C4 warnings if self.bombs_count != 0 and client:IsActiveTraitor() then surface.SetTexture(c4warn) surface.SetTextColor(200, 55, 55, 220) surface.SetDrawColor(255, 255, 255, 200) for k, bomb in pairs(self.bombs) do DrawTarget(bomb, 24, 0, true) end end -- Corpse calls if client:IsActiveDetective() and #self.called_corpses then surface.SetTexture(det_beacon) surface.SetTextColor(255, 255, 255, 240) surface.SetDrawColor(255, 255, 255, 230) for k, corpse in pairs(self.called_corpses) do DrawTarget(corpse, 16, 0.5) end end -- Samples if self.samples_count != 0 then surface.SetTexture(sample_scan) surface.SetTextColor(200, 50, 50, 255) surface.SetDrawColor(255, 255, 255, 240) for k, sample in pairs(self.samples) do DrawTarget(sample, 16, 0.5, true) end end -- Player radar if (not self.enable) or (not client:IsActiveSpecial()) then return end surface.SetTexture(indicator) local remaining = math.max(0, RADAR.endtime - CurTime()) local alpha_base = 50 + 180 * (remaining / RADAR.duration) local mpos = Vector(ScrW() / 2, ScrH() / 2, 0) local role, alpha, scrpos, md for k, tgt in pairs(RADAR.targets) do alpha = alpha_base scrpos = tgt.pos:ToScreen() if not scrpos.visible then continue end md = mpos:Distance(Vector(scrpos.x, scrpos.y, 0)) if md < near_cursor_dist then alpha = math.Clamp(alpha * (md / near_cursor_dist), 40, 230) end role = tgt.role or ROLE_INNOCENT if role == ROLE_TRAITOR then surface.SetDrawColor(255, 0, 0, alpha) surface.SetTextColor(255, 0, 0, alpha) elseif role == ROLE_DETECTIVE then surface.SetDrawColor(0, 0, 255, alpha) surface.SetTextColor(0, 0, 255, alpha) elseif role == 3 then -- decoys surface.SetDrawColor(150, 150, 150, alpha) surface.SetTextColor(150, 150, 150, alpha) else surface.SetDrawColor(0, 255, 0, alpha) surface.SetTextColor(0, 255, 0, alpha) end DrawTarget(tgt, 24, 0) end -- Time until next scan surface.SetFont("TabLarge") surface.SetTextColor(255, 0, 0, 230) local text = GetPTranslation("radar_hud", {time = FormatTime(remaining, "%02i:%02i")}) local w, h = surface.GetTextSize(text) surface.SetTextPos(36, ScrH() - 140 - h) surface.DrawText(text) end local function ReceiveC4Warn() local idx = net.ReadUInt(16) local armed = net.ReadBit() == 1 if armed then local pos = net.ReadVector() local etime = net.ReadFloat() RADAR.bombs[idx] = {pos=pos, t=etime} else RADAR.bombs[idx] = nil end RADAR.bombs_count = table.Count(RADAR.bombs) end net.Receive("TTT_C4Warn", ReceiveC4Warn) local function ReceiveCorpseCall() local pos = net.ReadVector() table.insert(RADAR.called_corpses, {pos = pos, called = CurTime()}) end net.Receive("TTT_CorpseCall", ReceiveCorpseCall) local function ReceiveRadarScan() local num_targets = net.ReadUInt(8) RADAR.targets = {} for i=1, num_targets do local r = net.ReadUInt(2) local pos = Vector() pos.x = net.ReadInt(15) pos.y = net.ReadInt(15) pos.z = net.ReadInt(15) table.insert(RADAR.targets, {role=r, pos=pos}) end RADAR.enable = true RADAR.endtime = CurTime() + RADAR.duration timer.Create("radartimeout", RADAR.duration + 1, 1, function() RADAR:Timeout() end) end net.Receive("TTT_Radar", ReceiveRadarScan) local GetTranslation = LANG.GetTranslation function RADAR.CreateMenu(parent, frame) local w, h = parent:GetSize() local dform = vgui.Create("DForm", parent) dform:SetName(GetTranslation("radar_menutitle")) dform:StretchToParent(0,0,0,0) dform:SetAutoSize(false) local owned = LocalPlayer():HasEquipmentItem(EQUIP_RADAR) if not owned then dform:Help(GetTranslation("radar_not_owned")) return dform end local bw, bh = 100, 25 local dscan = vgui.Create("DButton", dform) dscan:SetSize(bw, bh) dscan:SetText(GetTranslation("radar_scan")) dscan.DoClick = function(s) s:SetDisabled(true) RunConsoleCommand("ttt_radar_scan") frame:Close() end dform:AddItem(dscan) local dlabel = vgui.Create("DLabel", dform) dlabel:SetText(GetPTranslation("radar_help", {num = RADAR.duration})) dlabel:SetWrap(true) dlabel:SetTall(50) dform:AddItem(dlabel) local dcheck = vgui.Create("DCheckBoxLabel", dform) dcheck:SetText(GetTranslation("radar_auto")) dcheck:SetIndent(5) dcheck:SetValue(RADAR.repeating) dcheck.OnChange = function(s, val) RADAR.repeating = val end dform:AddItem(dcheck) dform.Think = function(s) if RADAR.enable or not owned then dscan:SetDisabled(true) else dscan:SetDisabled(false) end end dform:SetVisible(true) return dform end