diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 45a0f86a98..31704d5451 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -511,6 +511,19 @@
message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]")
log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]")
+ else if(href_list["traitor_class"])
+ var/static/list/choices
+ if(!choices)
+ choices = list()
+ for(var/C in GLOB.traitor_classes)
+ var/datum/traitor_class/t = C
+ choices[initial(t.employer)] = C
+ var/datum/antagonist/traitor/T = locate(href_list["target_antag"]) in antag_datums
+ if(T)
+ var/selected_type = input("Select traitor class:", "Traitor class", T.traitor_kind.employer) as null|anything in choices
+ selected_type = choices[selected_type]
+ T.set_traitor_kind(selected_type)
+
else if (href_list["obj_delete"])
var/datum/objective/objective
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index 19e53b09b4..88aab2c4ce 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -104,7 +104,7 @@ GLOBAL_VAR(antag_prototypes)
var/special_statuses = get_special_statuses()
if(length(special_statuses))
- out += get_special_statuses() + "
"
+ out += special_statuses + "
"
if(!GLOB.antag_prototypes)
GLOB.antag_prototypes = list()
diff --git a/code/modules/antagonists/traitor/classes/hijack.dm b/code/modules/antagonists/traitor/classes/hijack.dm
index 59e0591600..dbfa78eb96 100644
--- a/code/modules/antagonists/traitor/classes/hijack.dm
+++ b/code/modules/antagonists/traitor/classes/hijack.dm
@@ -16,3 +16,6 @@
/datum/traitor_class/human/hijack/finalize_traitor(datum/antagonist/traitor/T)
T.hijack_speed=1
return TRUE
+
+/datum/traitor_class/human/hijack/clean_up_traitor(datum/antagonist/traitor/T)
+ T.hijack_speed = 0.5
diff --git a/code/modules/antagonists/traitor/classes/traitor_class.dm b/code/modules/antagonists/traitor/classes/traitor_class.dm
index 012a269572..260e978bec 100644
--- a/code/modules/antagonists/traitor/classes/traitor_class.dm
+++ b/code/modules/antagonists/traitor/classes/traitor_class.dm
@@ -38,3 +38,7 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/finalize_traitor(datum/antagonist/traitor/T)
// Finalization. Return TRUE if should play standard traitor sound/equip, return FALSE if both are special case
return TRUE
+
+/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
+ // Any effects that need to be cleaned up if traitor class is being swapped.
+
\ No newline at end of file
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index f7dc7ad6d1..cf10f87bf8 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -15,36 +15,53 @@
threat = 5
/datum/antagonist/traitor/New()
- ..()
if(!GLOB.traitor_classes.len)//Only need to fill the list when it's needed.
for(var/I in subtypesof(/datum/traitor_class))
new I
+ ..()
-/datum/antagonist/traitor/proc/set_traitor_kind(var/kind)
+/datum/antagonist/traitor/proc/set_traitor_kind(kind)
+ var/swap_from_old = FALSE
+ if(traitor_kind)
+ traitor_kind.remove_innate_effects(owner.current)
+ traitor_kind.clean_up_traitor(src)
+ swap_from_old = TRUE
traitor_kind = GLOB.traitor_classes[kind]
+ traitor_kind.apply_innate_effects(owner.current)
+ if(give_objectives)
+ for(var/O in objectives)
+ qdel(O)
+ traitor_kind.forge_objectives(src)
+ if(swap_from_old)
+ traitor_kind.finalize_traitor(src)
+ traitor_kind.greet(src)
+ owner.announce_objectives()
+
+/proc/get_random_traitor_kind(var/list/blacklist = list())
+ var/chaos_weight = 0
+ if(istype(SSticker.mode,/datum/game_mode/dynamic))
+ var/datum/game_mode/dynamic/mode = SSticker.mode
+ chaos_weight = (mode.threat - 50)/50
+ var/list/weights = list()
+ for(var/C in GLOB.traitor_classes)
+ if(!(C in blacklist))
+ var/datum/traitor_class/class = GLOB.traitor_classes[C]
+ var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
+ weights[C] = weight * 1000
+ var/choice = pickweightAllowZero(weights)
+ if(!choice)
+ choice = TRAITOR_HUMAN // it's an "easter egg"
+ var/datum/traitor_class/actual_class = GLOB.traitor_classes[choice]
+ actual_class.weight *= 0.8 // less likely this round
+ return choice
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
set_traitor_kind(TRAITOR_AI)
else
- var/chaos_weight = 0
- if(istype(SSticker.mode,/datum/game_mode/dynamic))
- var/datum/game_mode/dynamic/mode = SSticker.mode
- chaos_weight = (mode.threat - 50)/50
- var/list/weights = list()
- for(var/C in GLOB.traitor_classes)
- var/datum/traitor_class/class = GLOB.traitor_classes[C]
- var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
- weights[C] = weight
- var/choice = pickweightAllowZero(weights)
- if(!choice)
- choice = GLOB.traitor_classes[TRAITOR_HUMAN] // it's an "easter egg"
- set_traitor_kind(choice)
- traitor_kind.weight *= 0.8 // less likely this round
+ set_traitor_kind(get_random_traitor_kind())
SSticker.mode.traitors += owner
owner.special_role = special_role
- if(give_objectives)
- traitor_kind.forge_objectives(src)
finalize_traitor()
..()
@@ -97,7 +114,8 @@
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
/datum/antagonist/traitor/antag_panel_objectives()
- . = ..()
+ . += "Traitor class: [traitor_kind.employer]
"
+ . += ..()
if(contractor_hub?.assigned_targets && length(contractor_hub.assigned_targets))
. += "Contract Targets:
"
for(var/datum/mind/M in contractor_hub.assigned_targets)
diff --git a/code/modules/uplink/uplink_items/uplink_bundles.dm b/code/modules/uplink/uplink_items/uplink_bundles.dm
index 7d0e52947a..f3e95fa61c 100644
--- a/code/modules/uplink/uplink_items/uplink_bundles.dm
+++ b/code/modules/uplink/uplink_items/uplink_bundles.dm
@@ -150,6 +150,23 @@
U.purchase_log.LogPurchase(goods, I, 0)
return C
+/datum/uplink_item/bundles_TC/reroll
+ name = "Renegotiate Contract"
+ desc = "Selecting this will inform the syndicate that you wish to change employers. Can only be done once; no take-backs."
+ item = /obj/effect/gibspawner/generic
+ cost = 0
+ cant_discount = TRUE
+ restricted = TRUE
+ limited_stock = 1
+
+/datum/uplink_item/bundles_TC/reroll/purchase(mob/user, datum/component/uplink/U)
+ var/datum/antagonist/traitor/T = user?.mind?.has_antag_datum(/datum/antagonist/traitor)
+ if(istype(T))
+ var/new_traitor_kind = get_random_traitor_kind(list(T.traitor_kind.type))
+ T.set_traitor_kind(new_traitor_kind)
+ else
+ to_chat(user,"Invalid user for contract renegotiation.")
+
/datum/uplink_item/bundles_TC/random
name = "Random Item"
desc = "Picking this will purchase a random item. Useful if you have some TC to spare or if you haven't decided on a strategy yet."