Randomising traitor numbers in Trouble in Terrorist Town

A while back a group of friends and I went through a spate of playing TTT(Trouble in Terrorist Town), to the point where we ended up playing during that Christmas day post dinner lull.

For me, TTT scratches the traitor game mechanic that you can find in board games like The Resistance, Battlestar Galactica and Shadows Over Camelot which you don't really get in any other computer game. Unlike these games, the number of traitors is rigidly defined by the total number of players by a formula.
local function GetTraitorCount(ply_count)
   -- get number of traitors: pct of players rounded down
   local traitor_count = math.floor(ply_count * GetConVar("ttt_traitor_pct"):GetFloat())
   -- make sure there is at least 1 traitor
   traitor_count = math.Clamp(traitor_count, 1, GetConVar("ttt_traitor_max"):GetInt())

   return traitor_count
end
This means that for a given number of players, the number of traitors/innocents is the same each round. This is probably ok for the public servers, but if you're playing within a small set of friends, knowing that there is two traitors every round gets stale quite quickly. So I ended up quickly writing some (bad) code that adds a second variable ttt_traitor_pct_max
CreateConVar("ttt_traitor_pct_max", "0.5")

local function GetTraitorCount(ply_count)
    -- get the difference between ttt_traitor_pct and traitor_pct_max as a positive real number
    local traitor_pct_range = math.abs((GetConVar("ttt_traitor_pct_max"):GetFloat() - GetConVar('ttt_traitor_pct'):GetFloat()) * 100)
    -- choose a random value in the traitor percentage range
    local traitor_random = (math.random(0, traitor_pct_range) + 1) / 100
    -- get number of traitors: pct of players rounded down (with our additional random traitors)
    local traitor_count = math.floor(ply_count * (GetConVar("ttt_traitor_pct"):GetFloat() + traitor_random))
    -- make sure there is at least 1 traitor
    traitor_count = math.Clamp(traitor_count, 1, GetConVar("ttt_traitor_max"):GetInt())
    return traitor_count
end
The number of traitors is then a random percentage between 20 percent and 50 percent of the number of players. This leaves you guessing about the number of traitors every round. If you set it high enough you can have rounds where there is only one innocent and if all the traitors play along, you can end up stringing along the one innocent for as long as possible.

This could probably be improved by having the percentage of traitors function to be heavily right skew instead of being totally random between the two percentages as in above code snippet, thus making a large number of traitors less likely so you end up with generally one less or one more traitor every round with only the occasional large percentage of traitors. This is the main reason I haven't opened a pull request with this change to the garry's mod repo

I do think this will probably only work amongst a group of friends that you know. If you are doing that however, you'll probably want to turn on the karma system karma system and turn off auto kicking and banning (they're you're friends after all). With no kicking/banning you'll need to give people an incentive to not kill people at random. Generally I do this by turning on the strict karma system(ttt_karma_strict) and upping the amount of karma damage each kill does(ttt_karma_kill_penalty). This means that people who regularly team kill end up doing very little damage until their karma recovers and end up dying in a straight up shoot out, even if they shoot first.

I keep the (vaguely up to date) version of init.lua with these changes at here https://gist.github.com/joetsoi/7266280
edit: I've now have a fork which I've applied these changes to.

Happy traitoring!

Popular posts

Digging into python memory issues in ckan with heapy

Translating ckan extensions using the ITranslations