-- Award/highlight generator functions take the events and the scores as -- produced by SCORING/CLSCORING and return a table if successful, or nil if -- not and another one should be tried. -- some globals we'll use a lot local table = table local pairs = pairs -- a common pattern local function FindHighest(tbl) local m_num = 0 local m_id = nil for id, num in pairs(tbl) do if num > m_num then m_id = id m_num = num end end return m_id, m_num end local function FirstSuicide(events, scores, players, traitors) local fs = nil local fnum = 0 for k, e in pairs(events) do if e.id == EVENT_KILL and e.att.uid == e.vic.uid then fnum = fnum + 1 if fs == nil then fs = e end end end if fs then local award = {nick=fs.att.ni} if not award.nick then return nil end if fnum > 1 then award.title = "Suicide Cult Leader" award.text = "showed the other suiciders how to do it by being the first to go." else award.title = "Lonely and Depressed" award.text = "was the only one who killed himself." end -- only high interest if many people died this way award.priority = fnum return award else return nil end end local function ExplosiveGrant(events, scores, players, traitors) local bombers = {} for k, e in pairs(events) do if e.id == EVENT_KILL and (e.dmg.t & DMG_BLAST == DMG_BLAST) then bombers[e.att.uid] = (bombers[e.att.uid] or 0) + 1 end end local award = {title="Explosives Research Grant"} if table.Count(bombers) > 0 then for uid, num in pairs(bombers) do -- award goes to whoever reaches this first I guess if num > 2 then award.nick = players[uid] if not award.nick then return nil end -- if player disconnected or something award.text = "was recognized for his research on explosions. " .. num .. " test subjects helped out." -- rare award, high interest award.priority = 10 + num return award end end end return nil end local function ExplodedSelf(events, scores, players, traitors) for k, e in pairs(events) do if e.id == EVENT_KILL and (e.dmg.t & DMG_BLAST == DMG_BLAST) and e.att.uid == e.vic.uid then return {title="Field Research", text="tested his own resistance to explosions. It was not high enough.", nick=e.vic.ni, priority=math.random(1, 4)} end end return nil end local function FirstBlood(events, scores, players, traitors) for k, e in pairs(events) do if e.id == EVENT_KILL and e.att.uid != e.vic.uid and e.att.uid != -1 then local award = {nick=e.att.ni} if not award.nick or award.nick == "" then return nil end if e.att.tr and not e.vic.tr then -- traitor legit k award.title = "First Blood" award.text = "delivered the first innocent death at a traitor's hands." elseif e.att.tr and e.vic.tr then -- traitor tk award.title = "First Bloody Stupid Kill" award.text = "scored the first kill by shooting a fellow traitor. Good job." elseif not e.att.tr and not e.vic.tr then -- inno tk award.title = "First Blooper" award.text = "was the first to kill. Too bad it was an innocent comrade." else -- inno legit k award.title = "First Blow" award.text = "struck the first blow for the innocent terrorists by making the first death a traitor's." end -- more interesting if there were many players and therefore many kills award.priority = math.random(-3, math.Round(table.Count(players) / 4)) return award end end end local function AllKills(events, scores, players, traitors) -- see if there is one killer responsible for all kills of either team local tr_killers = {} local in_killers = {} for id, s in pairs(scores) do if s.innos > 0 then table.insert(in_killers, id) elseif s.traitors > 0 then table.insert(tr_killers, id) end end if table.Count(tr_killers) == 1 then local id = tr_killers[1] if not table.HasValue(traitors, id) then local killer = players[id] if not killer then return nil end return {nick=killer, title="Deadliest Among Equals", text="was responsible for every kill made by the innocent this round.", priority=math.random(0, table.Count(players))} end end if table.Count(in_killers) == 1 then local id = in_killers[1] if table.HasValue(traitors, id) then local killer = players[id] if not killer then return nil end return {nick=killer, title="Lone Wolf", text="was responsible for every kill made by a traitor this round.", priority=math.random(0, table.Count(players))} end end return nil end local function NumKills_Traitor(events, scores, players, traitors) local trs = {} for id, s in pairs(scores) do if table.HasValue(traitors, id) then if s.innos > 0 then table.insert(trs, id) end end end local choices = table.Count(trs) if choices > 0 then -- award a random killer local pick = math.random(1, choices) local uid = trs[pick] local nick = players[uid] if not nick then return nil end local kills = scores[uid].innos if kills == 1 then return {title="I Got One, Boss!", nick=nick, text="managed to kill a single innocent. Sweet!", priority=0} elseif kills == 2 then return {title="A Bullet For Two", nick=nick, text="showed the first one was not a lucky shot by killing another.", priority=1} elseif kills == 3 then return {title="Serial Traitor", nick=nick, text="ended three innocent lives of terrorism today.", priotity=kills} elseif kills >= 4 and kills < 7 then return {title="Wolf Among More Sheep-Like Wolves", nick=nick, text="eats innocent terrorists for dinner. A dinner of ".. kills .. " courses.", priority=kills + 2} elseif kills >= 7 then return {title="Counter-Terrorism Operative", nick=nick, text="gets paid per kill. Can now buy another luxury yacht.", priotity=kills + 5} end else return nil end end local function NumKills_Inno(events, scores, players, traitors) local ins = {} for id, s in pairs(scores) do if not table.HasValue(traitors, id) then if s.traitors > 0 then table.insert(ins, id) end end end local choices = table.Count(ins) if table.Count(ins) > 0 then -- award a random killer local pick = math.random(1, choices) local uid = ins[pick] local nick = players[uid] if not nick then return nil end local kills = scores[uid].traitors if kills == 1 then return {title="Betray This", nick=nick, text="found a traitor. Shot a traitor. Easy.", priority = 0} elseif kills == 2 then return {title="Applied to the Justice Squad", nick=nick, text="escorted two traitors to the great beyond.", priority = 1} elseif kills == 3 then return {title="Do Traitors Dream Of Traitorous Sheep?", nick=nick, text="put three traitors to rest.", priority= 5} elseif kills >= 4 then return {title="Internal Affairs Employee", nick=nick, text="gets paid per kill. Can now order his fifth swimming pool.", priority=kills + 10} end else return nil end end local function FallDeath(events, scores, players, traitors) for k, e in pairs(events) do if e.id == EVENT_KILL and (e.dmg.t & DMG_FALL == DMG_FALL) then if e.att.ni != "" then return {title="No Mr. Bond, I Expect You To Fall", nick=e.att.ni, text="pushed someone off a great height.", priority=math.random(7, 15)} else return {title="Floored", nick=e.vic.ni, text="let his body hit the floor after falling from a significant altitude.", priority=math.random(1, 5)} end end end return nil end local function FallKill(events, scores, players, traitors) for k, e in pairs(events) do if e.id == EVENT_KILL and (e.dmg.t & DMG_CRUSH == DMG_CRUSH) and (e.dmg.t & DMG_PHYSGUN == DMG_PHYSGUN) then if e.att.ni != "" then return {title="The Human Meteorite", nick=e.att.ni, text="crushed a man by falling on him from a great height.", priority=math.random(10, 15)} end end end end local function Headshots(events, scores, players, traitors) local hs = {} for k, e in pairs(events) do if e.id == EVENT_KILL and e.dmg.h and (e.dmg.t & DMG_BULLET == DMG_BULLET) then hs[e.att.uid] = (hs[e.att.uid] or 0) + 1 end end if table.Count(hs) == 0 then return nil end -- find the one with the most shots local m_id, m_num = FindHighest(hs) if not m_id then return nil end local nick = players[m_id] if not nick then return nil end local award = {nick=nick, priority=m_num / 2} if m_num > 1 and m_num < 4 then award.title = "Efficiency" award.text = "discovered the joy of headshots and made ".. m_num .."." elseif m_num >= 4 and m_num < 6 then award.title = "Neurology" award.text = "removed the brains from " .. m_num .. " heads for a closer examination." elseif m_num >= 6 then award.title = "Videogames Made Me Do It" award.text = "applied his murder simulation training and headshotted ".. m_num .. " foes." award.priority = m_num + 5 else return nil end return award end local function UsedAmmoMost(events, ammotype) local user = {} for k, e in pairs(events) do if e.id == EVENT_KILL and e.dmg.g == ammotype then user[e.att.uid] = (user[e.att.uid] or 0) + 1 end end if table.Count(user) == 0 then return nil end local m_id, m_num = FindHighest(user) if not m_id then return nil end return {uid=m_id, kills=m_num} end local function CrowbarUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_CROWBAR) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills + math.random(0, 4)} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Thunk Thunk Thunk" award.text = "has a mean swing with the crowbar, as " .. kills .. " victims found out." elseif kills >= 4 then award.title = "Freeman" award.text = "covered his crowbar in the brains of no less than " .. kills .. " people." award.priority = kills + math.random(5, 10) else return nil end return award end local function PistolUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_PISTOL) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Persistent Little Bugger" award.text = "scored " .. kills .. " kills using the pistol. Then he went on to hug someone to death." elseif kills >= 4 then award.title = "Small Caliber Slaughter" award.text = "killed a small army of " .. kills .. " with the pistol. Presumably installed a tiny shotgun inside the barrel." award.priority = award.priority + math.random(0, 5) else return nil end return award end local function ShotgunUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_SHOTGUN) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Easy Mode" award.text = "applies the buckshot where it hurts, murdering ".. kills .." targets." award.priority = math.Round(kills / 2) elseif kills >= 4 then award.title = "A Thousand Little Pellets" award.text = "didn't really like his buckshot, so he gave it all away. " .. kills .. " recipients did not live to enjoy it." else return nil end return award end local function RifleUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_RIFLE) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Point and Click" award.text = "shows all you need for " .. kills .. " kills is a rifle and a steady hand." award.priority = math.Round(kills / 2) elseif kills >= 4 then award.title = "I Can See Your Head From Here" award.text = "knows his rifle. Now " .. kills .. " other people know his rifle too." else return nil end return award end local function DeagleUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_DEAGLE) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "It's Like A Tiny Rifle" award.text = "is getting the hang of the Desert Eagle and killed " .. kills .. " people." award.priority = math.Round(kills / 2) elseif kills >= 4 then award.title = "Eagle Master" award.text = "blew away ".. kills .." people with the deagle." award.priority = kills + math.random(2, 6) else return nil end return award end local function MAC10User(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_MAC10) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Pray and Slay" award.text = "killed ".. kills .." people with the MAC10, but won't say how much ammo he needed." award.priority = math.Round(kills / 2) elseif kills >= 4 then award.title = "Mac and Cheese" award.text = "wonders what would happen if he could wield two MAC10s. " .. kills .. " times two?" else return nil end return award end local function SilencedPistolUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_SIPISTOL) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 3 then award.title = "Be Quiet" award.text = "shut ".. kills .. " people up with the silenced pistol." elseif kills >= 3 then award.title = "Silenced Assassin" award.text = "killed ".. kills .." people who did not hear themselves die." else return nil end return award end local function KnifeUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_KNIFE) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills == 1 then if table.HasValue(traitors, most.uid) then award.title = "Knife Knowing You" award.text = "stabbed someone in the face over the internet." award.priority = 0 else award.title = "Where Did You Get That From?" award.text = "was not a Traitor, but still killed someone with a knife." award.priority = 5 end elseif kills > 1 and kills < 4 then award.title = "Such A Knife Man" award.text = "found ".. kills .. " knives lying around, and made use of them." elseif kills >= 4 then award.title = "World's Knifest Man" award.text = "killed ".. kills .." people with a knife. Don't ask me how." else return nil end return award end local function FlareUser(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_FLARE) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 3 then award.title = "To The Rescue" award.text = "used his flares to signal for ".. kills .. " deaths." elseif kills >= 3 then award.title = "Flare Indicates Fire" award.text = "taught ".. kills .." men about the danger of wearing flammable clothing." else return nil end return award end local function M249User(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_M249) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "A H.U.G.E Spread" award.text = "was in tune with his H.U.G.E, somehow managing to make his bullets hit ".. kills .. " people." elseif kills >= 4 then award.title = "A Patient Para" award.text = "just kept firing, and saw his H.U.G.E patience rewarded with ".. kills .." kills." else return nil end return award end local function M16User(events, scores, players, traitors) local most = UsedAmmoMost(events, AMMO_M16) if not most then return nil end local nick = players[most.uid] if not nick then return nil end local award = {nick=nick, priority=most.kills} local kills = most.kills if kills > 1 and kills < 4 then award.title = "Putt Putt Putt" award.text = "picked off ".. kills .. " people with the M16." elseif kills >= 4 then award.title = "Mid-range Madness" award.text = "knows how to take down targets with the M16, scoring ".. kills .." kills." else return nil end return award end local function TeamKiller(events, scores, players, traitors) local num_traitors = table.Count(traitors) local num_inno = table.Count(players) - num_traitors -- find biggest tker local tker = nil local pct = 0 for id, s in pairs(scores) do local kills = s.innos local team = num_inno - 1 if table.HasValue(traitors, id) then kills = s.traitors team = num_traitors - 1 end if kills > 0 and (kills / team) > pct then pct = kills / team tker = id end end -- no tks if pct == 0 or tker == nil then return nil end local nick = players[tker] if not nick then return nil end local was_traitor = table.HasValue(traitors, tker) local kills = (was_traitor and scores[tker].traitors > 0 and scores[tker].traitors) or (scores[tker].innos > 0 and scores[tker].innos) or 0 local award = {nick=nick, priority=kills} if kills == 1 then award.title = "Made An Oopsie" award.text = "had his finger slip just when he was aiming at a buddy." award.priority = 0 elseif kills == 2 then award.title = "Double-Oops" award.text = "thought he got a Traitor twice, but he was wrong both times." elseif kills == 3 then award.title = "Karma-conscious" award.text = "couldn't stop after killing two teammates. Three is his lucky number." elseif pct >= 1.0 then award.title = "Teamkiller" award.text = "murdered the entirety of his team. OMGBANBANBAN." award.priority = kills + math.random(3, 6) elseif pct >= 0.75 and not was_traitor then award.title = "Roleplayer" award.text = "was roleplaying a madman, honest. That's why he killed most of his team." award.priority = kills + 10 elseif pct > 0.5 then award.title = "Moron" award.text = "couldn't figure out which side he was on, and killed over half of his comrades." award.priority = kills + math.random(2, 7) elseif pct >= 0.25 then award.title = "Redneck" award.text = "protected his turf real good by killing over a quarter of his teammates." else return nil end return award end local function Burner(events, scores, players, traitors) local brn = {} for k, e in pairs(events) do if e.id == EVENT_KILL and (e.dmg.t & DMG_BURN == DMG_BURN) then brn[e.att.uid] = (brn[e.att.uid] or 0) + 1 end end if table.Count(brn) == 0 then return nil end -- find the one with the most burnings local m_id, m_num = FindHighest(brn) if not m_id then return nil end local nick = players[m_id] if not nick then return nil end local award = {nick=nick, priority=m_num * 2} if m_num > 1 and m_num < 4 then award.title = "Like Grandma Used To Make Them" award.text = "fried several people to a nice crisp." elseif m_num >= 4 and m_num < 7 then award.title = "Pyroid" award.text = "was heard cackling loudly after burning one of his many victims." elseif m_num >= 7 then award.title = "Pyrrhic Victory" award.text = "burned them all, but is now all out of incendiary grenades! How will he cope!" award.priority = m_num + math.random(0, 4) else return nil end return award end local function Coroner(events, scores, players, traitors) local finders = {} for k, e in pairs(events) do if e.id == EVENT_BODYFOUND then finders[e.uid] = (finders[e.uid] or 0) + 1 end end if table.Count(finders) == 0 then return end local m_id, m_num = FindHighest(finders) if not m_id then return nil end local nick = players[m_id] if not nick then return nil end local award = {nick=nick, priority=m_num} if m_num > 2 and m_num < 6 then award.title = "Coroner" award.text = "found ".. m_num .." bodies lying around." elseif m_num >= 6 and m_num < 10 then award.title = "Gotta Catch Em All" award.text = "found ".. m_num .. " corpses for his collection." elseif m_num >= 10 then award.title = "Death Scent" award.text = "keeps stumbling on random corpses, " .. m_num .. " times this round." award.priority = m_num + math.random(0, 4) else return nil end return award end local function CreditFound(events, scores, players, traitors) local finders = {} for k, e in pairs(events) do if e.id == EVENT_CREDITFOUND then finders[e.uid] = (finders[e.uid] or 0) + e.cr end end if table.Count(finders) == 0 then return end local m_id, m_num = FindHighest(finders) if not m_id then return nil end local nick = players[m_id] if not nick then return nil end local award = {nick=nick} if m_num > 1 then award.title = "Recycler" award.text = "scrounged up " .. m_num .. " leftover credits from corpses." award.priority = m_num + math.random(0, m_num) else return nil end return award end local function TimeOfDeath(events, scores, players, traitors) local near = 10 local time_near_start = CLSCORE.StartTime + near local time_near_end = nil local traitor_win = nil local e = nil for i=#events, 1, -1 do e = events[i] if e.id == EVENT_FINISH then time_near_end = e.t - near traitor_win = (e.win == WIN_TRAITOR) elseif e.id == EVENT_KILL and e.vic then if time_near_end and e.t > time_near_end and e.vic.tr == traitor_win then return { nick = e.vic.ni, title = "Pyrrhic Victory", text = "died only seconds before his team won the round.", priority = (e.t - time_near_end) * 2 }; elseif e.t < time_near_start then return { nick = e.vic.ni, title = "I Hate This Game", text = "died right after the start of the round.", priority = (time_near_start - e.t) * 2 }; end end end end local function TestAward(events, scores, players) return {title="A bag full of testing", nick="A guy with a pretty long name", text="a sentence that is also pretty long in order to describe things well.", priority=1} end -- new award functions must be added to this to be used by CLSCORE AWARDS = { FirstSuicide, ExplosiveGrant, ExplodedSelf, FirstBlood, AllKills, NumKills_Traitor, NumKills_Inno, FallDeath, Headshots, PistolUser, ShotgunUser, RifleUser, DeagleUser, MAC10User, CrowbarUser, TeamKiller, Burner, SilencedPistolUser, KnifeUser, FlareUser, Coroner, M249User, M16User, CreditFound, FallKill, TimeOfDeath }