diff --git a/.gitignore b/.gitignore
index 3b85a6ec3040..b1cb02811cb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,6 @@
*.lk
*.int
*.backup
-*.int
### https://raw.github.com/github/gitignore/cc542de017c606138a87ee4880e5f06b3a306def/Global/Linux.gitignore
*~
@@ -127,6 +126,9 @@ celerybeat-schedule
venv/
ENV/
+# IntelliJ IDEA / PyCharm (with plugin)
+.idea
+
# Spyder project settings
.spyderproject
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 1b6e30c12cfa..6f8e630ffb95 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -92,6 +92,20 @@
/datum/config_entry/flag/allow_latejoin_antagonists // If late-joining players can be traitor/changeling
+/datum/config_entry/flag/use_antag_rep // see game_options.txt for details
+
+/datum/config_entry/number/antag_rep_maximum
+ config_entry_value = 200
+ min_val = 0
+
+/datum/config_entry/number/default_antag_tickets
+ config_entry_value = 100
+ min_val = 0
+
+/datum/config_entry/number/max_tickets_per_roll
+ config_entry_value = 100
+ min_val = 0
+
/datum/config_entry/number/midround_antag_time_check // How late (in minutes you want the midround antag system to stay on, setting this to 0 will disable the system)
config_entry_value = 60
min_val = 0
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 4e185300c242..65544c377b41 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -402,6 +402,8 @@ SUBSYSTEM_DEF(job)
else
M = H
+ SSpersistence.antag_rep_change[M.client.ckey] += job.antag_rep
+
to_chat(M, "You are the [rank].")
to_chat(M, "As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.")
to_chat(M, "To speak on your departments radio, use the :h button. To see others, look closely at your headset.")
diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm
index b46a55c4d164..0500de33b6f5 100644
--- a/code/controllers/subsystem/persistence.dm
+++ b/code/controllers/subsystem/persistence.dm
@@ -1,3 +1,5 @@
+#define FILE_ANTAG_REP "data/AntagReputation.json"
+
SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
@@ -11,6 +13,8 @@ SUBSYSTEM_DEF(persistence)
var/list/saved_modes = list(1,2,3)
var/list/saved_trophies = list()
var/list/spawned_objects = list()
+ var/list/antag_rep = list()
+ var/list/antag_rep_change = list()
/datum/controller/subsystem/persistence/Initialize()
LoadSatchels()
@@ -18,6 +22,8 @@ SUBSYSTEM_DEF(persistence)
LoadChiselMessages()
LoadTrophies()
LoadRecentModes()
+ if(CONFIG_GET(flag/use_antag_rep))
+ LoadAntagReputation()
..()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
@@ -152,6 +158,15 @@ SUBSYSTEM_DEF(persistence)
return
saved_modes = json["data"]
+/datum/controller/subsystem/persistence/proc/LoadAntagReputation()
+ var/json = file2text(FILE_ANTAG_REP)
+ if(!json)
+ var/json_file = file(FILE_ANTAG_REP)
+ if(!fexists(json_file))
+ WARNING("Failed to load antag reputation. File likely corrupt.")
+ return
+ return
+ antag_rep = json_decode(json)
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
for(var/A in GLOB.trophy_cases)
@@ -183,6 +198,8 @@ SUBSYSTEM_DEF(persistence)
CollectSecretSatchels()
CollectTrophies()
CollectRoundtype()
+ if(CONFIG_GET(flag/use_antag_rep))
+ CollectAntagReputation()
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
@@ -253,3 +270,18 @@ SUBSYSTEM_DEF(persistence)
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
+
+/datum/controller/subsystem/persistence/proc/CollectAntagReputation()
+ var/ANTAG_REP_MAXIMUM = CONFIG_GET(number/antag_rep_maximum)
+
+ for(var/p_ckey in antag_rep_change)
+// var/start = antag_rep[p_ckey]
+ antag_rep[p_ckey] = max(0, min(antag_rep[p_ckey]+antag_rep_change[p_ckey], ANTAG_REP_MAXIMUM))
+
+// WARNING("AR_DEBUG: [p_ckey]: Committed [antag_rep_change[p_ckey]] reputation, going from [start] to [antag_rep[p_ckey]]")
+
+ antag_rep_change = list()
+
+ fdel(FILE_ANTAG_REP)
+ text2file(json_encode(antag_rep), FILE_ANTAG_REP)
+
diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm
index 2143bba3383b..41b583852d2b 100644
--- a/code/game/gamemodes/brother/traitor_bro.dm
+++ b/code/game/gamemodes/brother/traitor_bro.dm
@@ -37,7 +37,7 @@
var/datum/team/brother_team/team = new
var/team_size = prob(10) ? min(3, possible_brothers.len) : 2
for(var/k = 1 to team_size)
- var/datum/mind/bro = pick(possible_brothers)
+ var/datum/mind/bro = antag_pick(possible_brothers)
possible_brothers -= bro
antag_candidates -= bro
team.add_member(bro)
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 1f95688fa364..a77bf196c292 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -45,7 +45,7 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
for(var/i = 0, i < num_changelings, i++)
if(!antag_candidates.len)
break
- var/datum/mind/changeling = pick(antag_candidates)
+ var/datum/mind/changeling = antag_pick(antag_candidates)
antag_candidates -= changeling
changelings += changeling
changeling.special_role = ROLE_CHANGELING
diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm
index e59cf40ca440..d2f5accea41b 100644
--- a/code/game/gamemodes/changeling/traitor_chan.dm
+++ b/code/game/gamemodes/changeling/traitor_chan.dm
@@ -46,7 +46,7 @@
for(var/j = 0, j < num_changelings, j++)
if(!possible_changelings.len)
break
- var/datum/mind/changeling = pick(possible_changelings)
+ var/datum/mind/changeling = antag_pick(possible_changelings)
antag_candidates -= changeling
possible_changelings -= changeling
changeling.special_role = ROLE_CHANGELING
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 730c8bff7742..bd6a8bf77d73 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -155,7 +155,7 @@ Credit where due:
starter_servants += round(number_players / 10)
starter_servants = min(starter_servants, 8) //max 8 servants (that sould only happen with a ton of players)
while(starter_servants)
- var/datum/mind/servant = pick(antag_candidates)
+ var/datum/mind/servant = antag_pick(antag_candidates)
servants_to_serve += servant
antag_candidates -= servant
servant.assigned_role = ROLE_SERVANT_OF_RATVAR
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index fde8a16b13c3..d343bb35ab70 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -74,7 +74,7 @@
for(var/cultists_number = 1 to recommended_enemies)
if(!antag_candidates.len)
break
- var/datum/mind/cultist = pick(antag_candidates)
+ var/datum/mind/cultist = antag_pick(antag_candidates)
antag_candidates -= cultist
cultists_to_cult += cultist
cultist.special_role = ROLE_CULTIST
diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm
index f25ba6c6b6cc..3007164465d6 100644
--- a/code/game/gamemodes/devil/devil_game_mode.dm
+++ b/code/game/gamemodes/devil/devil_game_mode.dm
@@ -36,7 +36,7 @@
for(var/j = 0, j < num_devils, j++)
if (!antag_candidates.len)
break
- var/datum/mind/devil = pick(antag_candidates)
+ var/datum/mind/devil = antag_pick(antag_candidates)
devils += devil
devil.special_role = traitor_name
devil.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 68f2b383ae49..50ad92d44b63 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -283,6 +283,61 @@
set_security_level(SEC_LEVEL_BLUE)
+// This is a frequency selection system. You may imagine it like a raffle where each player can have some number of tickets. The more tickets you have the more likely you are to
+// "win". The default is 100 tickets. If no players use any extra tickets (earned with the antagonist rep system) calling this function should be equivalent to calling the normal
+// pick() function. By default you may use up to 100 extra tickets per roll, meaning at maximum a player may double their chances compared to a player who has no extra tickets.
+//
+// The odds of being picked are simply (your_tickets / total_tickets). Suppose you have one player using fifty (50) extra tickets, and one who uses no extra:
+// Player A: 150 tickets
+// Player B: 100 tickets
+// Total: 250 tickets
+//
+// The odds become:
+// Player A: 150 / 250 = 0.6 = 60%
+// Player B: 100 / 250 = 0.4 = 40%
+/datum/game_mode/proc/antag_pick(list/datum/candidates)
+ if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1)
+ return pick(candidates)
+
+ // Tickets start at 100
+ var/DEFAULT_ANTAG_TICKETS = CONFIG_GET(number/default_antag_tickets)
+
+ // You may use up to 100 extra tickets (double your odds)
+ var/MAX_TICKETS_PER_ROLL = CONFIG_GET(number/max_tickets_per_roll)
+
+
+ var/total_tickets = 0
+
+ MAX_TICKETS_PER_ROLL += DEFAULT_ANTAG_TICKETS
+
+ var/p_ckey
+ var/p_rep
+
+ for(var/datum/mind/mind in candidates)
+ p_ckey = ckey(mind.key)
+ total_tickets += min(SSpersistence.antag_rep[p_ckey] + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
+
+ var/antag_select = rand(1,total_tickets)
+ var/current = 1
+
+ for(var/datum/mind/mind in candidates)
+ p_ckey = ckey(mind.key)
+ p_rep = SSpersistence.antag_rep[p_ckey]
+ p_rep = p_rep == null ? 0 : p_rep
+
+ if(current <= antag_select)
+ var/subtract = min(p_rep + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL) - DEFAULT_ANTAG_TICKETS
+ SSpersistence.antag_rep_change[p_ckey] = -subtract
+
+// WARNING("AR_DEBUG: Player [mind.key] won spending [subtract] tickets from starting value [SSpersistence.antag_rep[p_ckey]]")
+
+ return mind
+
+ current += min(p_rep + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
+
+ WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()")
+ return pick(candidates)
+
/datum/game_mode/proc/get_players_for_role(role)
var/list/players = list()
var/list/candidates = list()
@@ -382,19 +437,29 @@
if(L.ckey && L.client)
+ var/failed = FALSE
if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something)
msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n"
- continue //AFK client
- if(L.stat)
+ failed = TRUE //AFK client
+ if(!failed && L.stat)
if(L.suiciding) //Suicider
msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n"
- continue //Disconnected client
- if(L.stat == UNCONSCIOUS)
+ failed = TRUE //Disconnected client
+ if(!failed && L.stat == UNCONSCIOUS)
msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n"
- continue //Unconscious
- if(L.stat == DEAD)
+ failed = TRUE //Unconscious
+ if(!failed && L.stat == DEAD)
msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n"
- continue //Dead
+ failed = TRUE //Dead
+
+ var/p_ckey = L.client.ckey
+// WARNING("AR_DEBUG: [p_ckey]: failed - [failed], antag_rep_change: [SSpersistence.antag_rep_change[p_ckey]]")
+
+ // people who died or left should not gain any reputation
+ // people who rolled antagonist still lose it
+ if(failed && SSpersistence.antag_rep_change[p_ckey] > 0)
+// WARNING("AR_DEBUG: Zeroed [p_ckey]'s antag_rep_change")
+ SSpersistence.antag_rep_change[p_ckey] = 0
continue //Happy connected client
for(var/mob/dead/observer/D in GLOB.dead_mob_list)
@@ -477,4 +542,4 @@
if(EMERGENCY_ESCAPED_OR_ENDGAMED)
SSticker.news_report = STATION_EVACUATED
if(SSshuttle.emergency.is_hijacked())
- SSticker.news_report = SHUTTLE_HIJACK
\ No newline at end of file
+ SSticker.news_report = SHUTTLE_HIJACK
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 45f85719d348..1553ee811f48 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -52,7 +52,7 @@
for (var/i=1 to max_headrevs)
if (antag_candidates.len==0)
break
- var/datum/mind/lenin = pick(antag_candidates)
+ var/datum/mind/lenin = antag_pick(antag_candidates)
antag_candidates -= lenin
headrev_candidates += lenin
lenin.restricted_roles = restricted_jobs
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 7409faa706e2..80ce90722b0a 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -49,7 +49,7 @@
for(var/j = 0, j < num_traitors, j++)
if (!antag_candidates.len)
break
- var/datum/mind/traitor = pick(antag_candidates)
+ var/datum/mind/traitor = antag_pick(antag_candidates)
pre_traitors += traitor
traitor.special_role = traitor_name
traitor.restricted_roles = restricted_jobs
@@ -100,4 +100,4 @@
/datum/game_mode/proc/update_traitor_icons_removed(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.leave_hud(traitor_mind.current)
- set_antag_hud(traitor_mind.current, null)
\ No newline at end of file
+ set_antag_hud(traitor_mind.current, null)
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index dd8dad331f44..8a6a7b7903d9 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -19,7 +19,7 @@
var/finished = 0
/datum/game_mode/wizard/pre_setup()
- var/datum/mind/wizard = pick(antag_candidates)
+ var/datum/mind/wizard = antag_pick(antag_candidates)
wizards += wizard
wizard.assigned_role = ROLE_WIZARD
wizard.special_role = ROLE_WIZARD
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 61ec6316d57b..364f1a763361 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -44,6 +44,13 @@
body += "
Show related accounts by: "
body += "\[ CID | "
body += "IP \]"
+ var/rep = 0
+ rep += SSpersistence.antag_rep[M.ckey]
+ body += "
Antagonist reputation: [rep]"
+ body += "
\[increase\] "
+ body += "\[decrease\] "
+ body += "\[set\] "
+ body += "\[zero\]"
body += "
\[ "
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index f4b614e7bba3..3ef790a359b1 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2405,6 +2405,15 @@
usr << browse(dat.Join("
"), "window=related_[C];size=420x300")
+ else if(href_list["modantagrep"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/mob/M = locate(href_list["mob"]) in GLOB.mob_list
+ var/client/C = M.client
+ usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"])
+ show_player_panel(M)
+
/datum/admins/proc/HandleCMode()
if(!check_rights(R_ADMIN))
return
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index bbe7c97bbf6c..eea838b4e8b1 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -47,6 +47,52 @@
admin_ticket_log(M, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/client/proc/cmd_admin_mod_antag_rep(client/C in GLOB.clients, var/operation)
+ set category = "Special Verbs"
+ set name = "Modify Antagonist Reputation"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/msg = ""
+ var/log_text = ""
+
+ if(operation == "zero")
+ log_text = "Set to 0"
+ SSpersistence.antag_rep -= C.ckey
+ else
+ var/prompt = "Please enter the amount of reputation to [operation]:"
+
+ if(operation == "set")
+ prompt = "Please enter the new reputation value:"
+
+ msg = input("Message:", prompt) as num
+
+ if (!msg)
+ return
+
+ var/ANTAG_REP_MAXIMUM = CONFIG_GET(number/antag_rep_maximum)
+
+ if(operation == "set")
+ log_text = "Set to [num2text(msg)]"
+ SSpersistence.antag_rep[C.ckey] = max(0, min(msg, ANTAG_REP_MAXIMUM))
+ else if(operation == "add")
+ log_text = "Added [num2text(msg)]"
+ SSpersistence.antag_rep[C.ckey] = min(SSpersistence.antag_rep[C.ckey]+msg, ANTAG_REP_MAXIMUM)
+ else if(operation == "subtract")
+ log_text = "Subtracted [num2text(msg)]"
+ SSpersistence.antag_rep[C.ckey] = max(SSpersistence.antag_rep[C.ckey]-msg, 0)
+ else
+ to_chat(src, "Invalid operation for antag rep modification: [operation] by user [key_name(usr)]")
+ return
+
+ if(SSpersistence.antag_rep[C.ckey] <= 0)
+ SSpersistence.antag_rep -= C.ckey
+
+ log_admin("[key_name(usr)]: Modified [key_name(C)]'s antagonist reputation [log_text]")
+ message_admins("[key_name_admin(usr)]: Modified [key_name(C)]'s antagonist reputation ([log_text])")
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Modify Antagonist Reputation") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
/client/proc/cmd_admin_world_narrate()
set category = "Special Verbs"
set name = "Global Narrate"
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 38418e9916dd..aede0bef8740 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -15,6 +15,7 @@ Captain
minimal_player_age = 14
exp_requirements = 180
exp_type = EXP_TYPE_CREW
+ antag_rep = 20
outfit = /datum/outfit/job/captain
@@ -69,6 +70,7 @@ Head of Personnel
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SUPPLY
+ antag_rep = 16
outfit = /datum/outfit/job/hop
diff --git a/code/modules/jobs/job_types/cargo_service.dm b/code/modules/jobs/job_types/cargo_service.dm
index c74fbd3b1bf5..9c6c6f566dd9 100644
--- a/code/modules/jobs/job_types/cargo_service.dm
+++ b/code/modules/jobs/job_types/cargo_service.dm
@@ -11,6 +11,7 @@ Quartermaster
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#d7b088"
+ antag_rep = 12
outfit = /datum/outfit/job/quartermaster
@@ -41,6 +42,7 @@ Cargo Technician
spawn_positions = 2
supervisors = "the quartermaster and the head of personnel"
selection_color = "#dcba97"
+ antag_rep = 4
outfit = /datum/outfit/job/cargo_tech
@@ -69,6 +71,7 @@ Shaft Miner
spawn_positions = 3
supervisors = "the quartermaster and the head of personnel"
selection_color = "#dcba97"
+ antag_rep = 8
outfit = /datum/outfit/job/miner
@@ -147,6 +150,7 @@ Bartender
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#bbe291"
+ antag_rep = 4
outfit = /datum/outfit/job/bartender
@@ -180,6 +184,7 @@ Cook
supervisors = "the head of personnel"
selection_color = "#bbe291"
var/cooks = 0 //Counts cooks amount
+ antag_rep = 8
outfit = /datum/outfit/job/cook
@@ -232,6 +237,7 @@ Botanist
spawn_positions = 2
supervisors = "the head of personnel"
selection_color = "#bbe291"
+ antag_rep = 8
outfit = /datum/outfit/job/botanist
@@ -271,6 +277,7 @@ Janitor
supervisors = "the head of personnel"
selection_color = "#bbe291"
var/global/janitors = 0
+ antag_rep = 8
outfit = /datum/outfit/job/janitor
diff --git a/code/modules/jobs/job_types/civilian.dm b/code/modules/jobs/job_types/civilian.dm
index 9a2030d7ed20..a10c15e53f6e 100644
--- a/code/modules/jobs/job_types/civilian.dm
+++ b/code/modules/jobs/job_types/civilian.dm
@@ -11,6 +11,7 @@ Clown
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#dddddd"
+ antag_rep = 4
outfit = /datum/outfit/job/clown
@@ -72,6 +73,7 @@ Mime
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#dddddd"
+ antag_rep = 4
outfit = /datum/outfit/job/mime
@@ -122,6 +124,7 @@ Curator
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#dddddd"
+ antag_rep = 4
outfit = /datum/outfit/job/curator
@@ -167,6 +170,7 @@ Lawyer
supervisors = "the head of personnel"
selection_color = "#dddddd"
var/lawyers = 0 //Counts lawyer amount
+ antag_rep = 8
outfit = /datum/outfit/job/lawyer
diff --git a/code/modules/jobs/job_types/civilian_chaplain.dm b/code/modules/jobs/job_types/civilian_chaplain.dm
index 6b119c19d7b2..00685454b03c 100644
--- a/code/modules/jobs/job_types/civilian_chaplain.dm
+++ b/code/modules/jobs/job_types/civilian_chaplain.dm
@@ -12,6 +12,7 @@ Chaplain
spawn_positions = 1
supervisors = "the head of personnel"
selection_color = "#dddddd"
+ antag_rep = 4
outfit = /datum/outfit/job/chaplain
diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm
index 1b1619cc24b7..064422bfbaee 100644
--- a/code/modules/jobs/job_types/engineering.dm
+++ b/code/modules/jobs/job_types/engineering.dm
@@ -17,6 +17,7 @@ Chief Engineer
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_ENGINEERING
+ antag_rep = 16
outfit = /datum/outfit/job/ce
@@ -76,6 +77,7 @@ Station Engineer
selection_color = "#fff5cc"
exp_requirements = 60
exp_type = EXP_TYPE_CREW
+ antag_rep = 8
outfit = /datum/outfit/job/engineer
@@ -132,6 +134,7 @@ Atmospheric Technician
selection_color = "#fff5cc"
exp_requirements = 60
exp_type = EXP_TYPE_CREW
+ antag_rep = 8
outfit = /datum/outfit/job/atmos
diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm
index 70854d020b97..077759e8b10d 100644
--- a/code/modules/jobs/job_types/job.dm
+++ b/code/modules/jobs/job_types/job.dm
@@ -48,6 +48,9 @@
var/exp_type = ""
var/exp_type_department = ""
+ //The amount of good boy points playing this role will earn you towards a higher chance to roll antagonist next round
+ var/antag_rep = 0
+
//Only override this proc
//H is usually a human unless an /equip override transformed it
/datum/job/proc/after_spawn(mob/living/H, mob/M)
diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm
index 1f2df19f644e..4da656868336 100644
--- a/code/modules/jobs/job_types/medical.dm
+++ b/code/modules/jobs/job_types/medical.dm
@@ -17,6 +17,7 @@ Chief Medical Officer
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_MEDICAL
+ antag_rep = 16
outfit = /datum/outfit/job/cmo
@@ -59,6 +60,7 @@ Medical Doctor
spawn_positions = 3
supervisors = "the chief medical officer"
selection_color = "#ffeef0"
+ antag_rep = 8
outfit = /datum/outfit/job/doctor
@@ -96,6 +98,7 @@ Chemist
selection_color = "#ffeef0"
exp_type = EXP_TYPE_CREW
exp_requirements = 60
+ antag_rep = 8
outfit = /datum/outfit/job/chemist
@@ -131,6 +134,7 @@ Geneticist
selection_color = "#ffeef0"
exp_type = EXP_TYPE_CREW
exp_requirements = 60
+ antag_rep = 8
outfit = /datum/outfit/job/geneticist
@@ -167,6 +171,7 @@ Virologist
selection_color = "#ffeef0"
exp_type = EXP_TYPE_CREW
exp_requirements = 60
+ antag_rep = 8
outfit = /datum/outfit/job/virologist
diff --git a/code/modules/jobs/job_types/science.dm b/code/modules/jobs/job_types/science.dm
index d8579a37b091..4fb13472087b 100644
--- a/code/modules/jobs/job_types/science.dm
+++ b/code/modules/jobs/job_types/science.dm
@@ -17,6 +17,7 @@ Research Director
exp_type_department = EXP_TYPE_SCIENCE
exp_requirements = 180
exp_type = EXP_TYPE_CREW
+ antag_rep = 16
outfit = /datum/outfit/job/rd
@@ -72,6 +73,7 @@ Scientist
selection_color = "#ffeeff"
exp_requirements = 60
exp_type = EXP_TYPE_CREW
+ antag_rep = 8
outfit = /datum/outfit/job/scientist
@@ -106,6 +108,7 @@ Roboticist
selection_color = "#ffeeff"
exp_requirements = 60
exp_type = EXP_TYPE_CREW
+ antag_rep = 8
outfit = /datum/outfit/job/roboticist
diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm
index 442b75c972f6..322922a779ef 100644
--- a/code/modules/jobs/job_types/security.dm
+++ b/code/modules/jobs/job_types/security.dm
@@ -23,6 +23,7 @@ Head of Security
exp_requirements = 300
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SECURITY
+ antag_rep = 20
outfit = /datum/outfit/job/hos
@@ -76,6 +77,7 @@ Warden
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ antag_rep = 16
outfit = /datum/outfit/job/warden
@@ -128,6 +130,7 @@ Detective
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ antag_rep = 12
outfit = /datum/outfit/job/detective
@@ -178,6 +181,7 @@ Security Officer
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ antag_rep = 12
outfit = /datum/outfit/job/security
diff --git a/code/modules/jobs/job_types/silicon.dm b/code/modules/jobs/job_types/silicon.dm
index 4a4893e93d5a..0860c0811361 100644
--- a/code/modules/jobs/job_types/silicon.dm
+++ b/code/modules/jobs/job_types/silicon.dm
@@ -14,6 +14,7 @@ AI
minimal_player_age = 30
exp_requirements = 180
exp_type = EXP_TYPE_CREW
+ antag_rep = 12
/datum/job/ai/equip(mob/living/carbon/human/H)
return H.AIize(FALSE)
@@ -52,4 +53,4 @@ Cyborg
/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M)
if(CONFIG_GET(flag/rename_cyborg)) //name can't be set in robot/New without the client
- R.rename_self("cyborg", M.client)
\ No newline at end of file
+ R.rename_self("cyborg", M.client)
diff --git a/config/config.txt b/config/config.txt
index 9f793a0286b0..f9dc54a0d7f9 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -71,7 +71,6 @@ ENABLE_LOCALHOST_RANK
## Allows admins to bypass job playtime requirements.
#USE_EXP_RESTRICTIONS_ADMIN_BYPASS
-
## log OOC channel
LOG_OOC
diff --git a/config/game_options.txt b/config/game_options.txt
index 758f1df89839..7b53f1459fa1 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -223,6 +223,21 @@ BROTHER_OBJECTIVES_AMOUNT 2
## If late-joining players have a chance to become a traitor/changeling
ALLOW_LATEJOIN_ANTAGONISTS
+## Comment this out to disable the antagonist reputation system. This system rewards players who participate in the game instead of greytiding by giving them slightly higher odds to
+## roll antagonist in subsequent rounds until they get it.
+##
+## For details See the comments for /datum/game_mode/proc/antag_pick in code/game/gamemodes/game_mode.dm
+# USE_ANTAG_REP
+
+## The maximum amount of antagonist reputation tickets a player can bank (not use at once)
+ANTAG_REP_MAXIMUM 200
+
+## The default amount of tickets all users use while rolling
+DEFAULT_ANTAG_TICKETS 100
+
+## The maximum amount of extra tickets a user may use from their ticket bank in addition to the default tickets
+MAX_TICKETS_PER_ROLL 100
+
## Uncomment to allow players to see the set odds of different rounds in secret/random in the get server revision screen. This will NOT tell the current roundtype.
#SHOW_GAME_TYPE_ODDS