--[[ | 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/ --]] ---- Customized scoring local math = math local string = string local table = table local pairs = pairs SCORE = SCORE or {} SCORE.Events = SCORE.Events or {} include("scoring_shd.lua") -- One might wonder why all the key names in the event tables are so annoyingly -- short. Well, the serialisation module in gmod (glon) does not do any -- compression. At all. This means the difference between all events having a -- "time_added" key versus a "t" key is very significant for the amount of data -- we need to send. It's a pain, but I'm not going to code my own compression, -- so doing it manually is the only way. -- One decent way to reduce data sent turned out to be rounding the time floats. -- We don't actually need to know about 10000ths of seconds after all. function SCORE:AddEvent(entry, t_override) entry.t = t_override or CurTime() table.insert(self.Events, entry) end local function CopyDmg(dmg) local wep = util.WeaponFromDamage(dmg) local g, n if wep then local id = WepToEnum(wep) if id then g = id else -- we can convert each standard TTT weapon name to a preset ID, but -- that's not workable with custom SWEPs from people, so we'll just -- have to pay the byte tax there g = wep:GetClass() end else local infl = dmg:GetInflictor() if IsValid(infl) and infl.ScoreName then n = infl.ScoreName end end -- t = type, a = amount, g = gun, h = headshot, n = name return { t = dmg:GetDamageType(), a = dmg:GetDamage(), h = false, g = g, n = n } end function SCORE:HandleKill(victim, attacker, dmginfo) if not (IsValid(victim) and victim:IsPlayer()) then return end local e = { id=EVENT_KILL, att={ni="", sid64=-1, tr=false}, vic={ni=victim:Nick(), sid64=victim:SteamID64(), tr=false}, dmg=CopyDmg(dmginfo)}; e.dmg.h = victim.was_headshot e.vic.tr = victim:GetTraitor() if IsValid(attacker) and attacker:IsPlayer() then e.att.ni = attacker:Nick() e.att.sid64 = attacker:SteamID64() e.att.tr = attacker:GetTraitor() -- If a traitor gets himself killed by another traitor's C4, it's his own -- damn fault for ignoring the indicator. if dmginfo:IsExplosionDamage() and attacker:GetTraitor() and victim:GetTraitor() then local infl = dmginfo:GetInflictor() if IsValid(infl) and infl:GetClass() == "ttt_c4" then e.att = table.Copy(e.vic) end end end self:AddEvent(e) end function SCORE:HandleSpawn(ply) if ply:Team() == TEAM_TERROR then self:AddEvent({id=EVENT_SPAWN, ni=ply:Nick(), sid64=ply:SteamID64()}) end end function SCORE:HandleSelection() local traitors = {} local detectives = {} for k, ply in player.Iterator() do if ply:GetTraitor() then table.insert(traitors, ply:SteamID64()) elseif ply:GetDetective() then table.insert(detectives, ply:SteamID64()) end end self:AddEvent({id=EVENT_SELECTED, traitor_ids=traitors, detective_ids=detectives}) end function SCORE:HandleBodyFound(finder, found) self:AddEvent({id=EVENT_BODYFOUND, ni=finder:Nick(), sid64=finder:SteamID64(), b=found:Nick()}) end function SCORE:HandleC4Explosion(planter, arm_time, exp_time) local nick = "Someone" if IsValid(planter) and planter:IsPlayer() then nick = planter:Nick() end self:AddEvent({id=EVENT_C4PLANT, ni=nick}, arm_time) self:AddEvent({id=EVENT_C4EXPLODE, ni=nick}, exp_time) end function SCORE:HandleC4Disarm(disarmer, owner, success) if disarmer == owner then return end if not IsValid(disarmer) then return end local ev = { id = EVENT_C4DISARM, ni = disarmer:Nick(), s = success }; if IsValid(owner) then ev.own = owner:Nick() end self:AddEvent(ev) end function SCORE:HandleCreditFound(finder, found_nick, credits) self:AddEvent({id=EVENT_CREDITFOUND, ni=finder:Nick(), sid64=finder:SteamID64(), b=found_nick, cr=credits}) end function SCORE:ApplyEventLogScores(wintype) local scores = {} local traitors = {} local detectives = {} for k, ply in player.Iterator() do scores[ply:SteamID64()] = {} if ply:GetTraitor() then table.insert(traitors, ply:SteamID64()) elseif ply:GetDetective() then table.insert(detectives, ply:SteamID64()) end end -- individual scores, and count those left alive local alive = {traitors = 0, innos = 0} local dead = {traitors = 0, innos = 0} local scored_log = ScoreEventLog(self.Events, scores, traitors, detectives) local ply = nil for sid64, s in pairs(scored_log) do ply = player.GetBySteamID64(sid64) if ply and ply:ShouldScore() then ply:AddFrags(KillsToPoints(s, ply:GetTraitor())) end end -- team scores local bonus = ScoreTeamBonus(scored_log, wintype) for sid64, s in pairs(scored_log) do ply = player.GetBySteamID64(sid64) if ply and ply:ShouldScore() then ply:AddFrags(ply:GetTraitor() and bonus.traitors or bonus.innos) end end -- count deaths local events = self.Events for i = 1, #events do local e = events[i] if e.id == EVENT_KILL then local victim = player.GetBySteamID64(e.vic.sid64) if IsValid(victim) and victim:ShouldScore() then victim:AddDeaths(1) end end end end function SCORE:RoundStateChange(newstate) self:AddEvent({id=EVENT_GAME, state=newstate}) end function SCORE:RoundComplete(wintype) self:AddEvent({id=EVENT_FINISH, win=wintype}) end function SCORE:Reset() self.Events = {} end function SCORE:StreamToClients() local events = util.TableToJSON(self.Events) if events == nil then ErrorNoHalt("Round report event encoding failed!\n") return end events = util.Compress(events) if events == "" then ErrorNoHalt("Round report event compression failed!\n") return end -- divide into happy lil bits. -- this was necessary with user messages, now it's -- a just-in-case thing if a round somehow manages to be > 64K local len = #events local MaxStreamLength = SCORE.MaxStreamLength if len <= MaxStreamLength then net.Start("TTT_ReportStream") net.WriteUInt(len, 16) net.WriteData(events, len) net.Broadcast() else local curpos = 0 repeat net.Start("TTT_ReportStream_Part") net.WriteData(string.sub(events, curpos + 1, curpos + MaxStreamLength + 1), MaxStreamLength) net.Broadcast() curpos = curpos + MaxStreamLength + 1 until(len - curpos <= MaxStreamLength) net.Start("TTT_ReportStream") net.WriteUInt(len, 16) net.WriteData(string.sub(events, curpos + 1, len), len - curpos) net.Broadcast() end end