diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 165690571f1..9b68c38f905 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -6,3 +6,6 @@
#define ANTAG_DATUM_NINJA /datum/antagonist/ninja
#define ANTAG_DATUM_NINJA_FRIENDLY /datum/antagonist/ninja/friendly
#define ANTAG_DATUM_NINJA_RANDOM /datum/antagonist/ninja/randomAllegiance/
+#define ANTAG_DATUM_TRAITOR /datum/antagonist/traitor
+#define ANTAG_DATUM_TRAITOR_CUSTOM /datum/antagonist/traitor/custom
+#define ANTAG_DATUM_IAA /datum/antagonist/traitor/internal_affairs
diff --git a/code/datums/antagonists/datum_iaa.dm b/code/datums/antagonists/datum_iaa.dm
deleted file mode 100644
index eeceee84d41..00000000000
--- a/code/datums/antagonists/datum_iaa.dm
+++ /dev/null
@@ -1,13 +0,0 @@
-/datum/antagonist/iaa
-
-/datum/antagonist/iaa/apply_innate_effects()
- .=..() //in case the base is used in future
- if(owner&&owner.current)
- give_pinpointer(owner.current)
-
-/datum/antagonist/iaa/remove_innate_effects()
- .=..()
- if(owner&&owner.current)
- owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
-
-
diff --git a/code/datums/antagonists/datum_internal_affairs.dm b/code/datums/antagonists/datum_internal_affairs.dm
new file mode 100644
index 00000000000..9fd338b0aae
--- /dev/null
+++ b/code/datums/antagonists/datum_internal_affairs.dm
@@ -0,0 +1,249 @@
+#define PINPOINTER_MINIMUM_RANGE 15
+#define PINPOINTER_EXTRA_RANDOM_RANGE 10
+#define PINPOINTER_PING_TIME 40
+#define PROB_ACTUAL_TRAITOR 20
+#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
+
+/datum/antagonist/traitor/internal_affairs
+ name = "Internal Affairs Agent"
+ employer = "Nanotrasen"
+ var/last_man_standing = FALSE
+ var/list/datum/mind/targets_stolen
+
+/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
+ .=..() //in case the base is used in future
+ if(owner&&owner.current)
+ give_pinpointer(owner.current)
+
+/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
+ .=..()
+ if(owner&&owner.current)
+ owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
+
+/datum/antagonist/traitor/internal_affairs/on_removal()
+ STOP_PROCESSING(SSprocessing,src)
+ .=..()
+
+/datum/status_effect/agent_pinpointer
+ id = "agent_pinpointer"
+ duration = -1
+ tick_interval = PINPOINTER_PING_TIME
+ alert_type = /obj/screen/alert/status_effect/agent_pinpointer
+ var/minimum_range = PINPOINTER_MINIMUM_RANGE
+ var/mob/scan_target = null
+
+/obj/screen/alert/status_effect/agent_pinpointer
+ name = "Internal Affairs Integrated Pinpointer"
+ desc = "Even stealthier than a normal implant."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "pinon"
+
+/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
+ if(!scan_target)
+ linked_alert.icon_state = "pinonnull"
+ return
+ var/turf/here = get_turf(owner)
+ var/turf/there = get_turf(scan_target)
+ if(here.z != there.z)
+ linked_alert.icon_state = "pinonnull"
+ return
+ if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
+ linked_alert.icon_state = "pinondirect"
+ else
+ linked_alert.setDir(get_dir(here, there))
+ switch(get_dist(here, there))
+ if(1 to 8)
+ linked_alert.icon_state = "pinonclose"
+ if(9 to 16)
+ linked_alert.icon_state = "pinonmedium"
+ if(16 to INFINITY)
+ linked_alert.icon_state = "pinonfar"
+
+/datum/status_effect/agent_pinpointer/proc/scan_for_target()
+ scan_target = null
+ if(owner)
+ if(owner.mind)
+ if(owner.mind.objectives)
+ for(var/datum/objective/objective_ in owner.mind.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ var/mob/current = objective.target.current
+ if(current&¤t.stat!=DEAD)
+ scan_target = current
+ break
+
+/datum/status_effect/agent_pinpointer/tick()
+ if(!owner)
+ qdel(src)
+ return
+ scan_for_target()
+ point_to_target()
+
+/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
+ if(owner && owner.current)
+ owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
+
+/proc/is_internal_objective(datum/objective/O)
+ return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
+
+/datum/antagonist/traitor/internal_affairs/proc/replace_escape_objective()
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
+ continue
+ owner.objectives -= objective_
+ var/datum/objective/martyr/martyr_objective = new
+ martyr_objective.owner = owner
+ owner.objectives += martyr_objective
+
+/datum/antagonist/traitor/internal_affairs/proc/reinstate_escape_objective()
+ if(!owner||!owner.objectives)
+ return
+ for (var/objective_ in owner.objectives)
+ if(!istype(objective_, /datum/objective/martyr))
+ continue
+ owner.objectives -= objective_
+ if(issilicon(owner))
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ owner.objectives += survive_objective
+ else
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ owner.objectives += escape_objective
+
+/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
+ if(!owner.current||owner.current.stat==DEAD)
+ return
+ var/already_traitored = last_man_standing
+ to_chat(owner.current, " Target eliminated: [victim.name]")
+ for(var/objective_ in victim.objectives)
+ if(istype(objective_, /datum/objective/assassinate/internal))
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(objective.target==owner)
+ last_man_standing = TRUE
+ else if(targets_stolen.Find(objective.target) == 0)
+ var/datum/objective/assassinate/internal/new_objective = new
+ new_objective.owner = owner
+ new_objective.target = objective.target
+ new_objective.update_explanation_text()
+ owner.objectives += new_objective
+ targets_stolen += objective.target
+ var/status_text = objective.check_completion() ? "neutralised" : "active"
+ to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ else if(istype(objective_, /datum/objective/destroy/internal))
+ var/datum/objective/destroy/internal/objective = objective_
+ var/datum/objective/destroy/internal/new_objective = new
+ if(objective.target==owner)
+ last_man_standing = TRUE
+ else if(targets_stolen.Find(objective.target) == 0)
+ new_objective.owner = owner
+ new_objective.target = objective.target
+ new_objective.update_explanation_text()
+ owner.objectives += new_objective
+ targets_stolen += objective.target
+ var/status_text = objective.check_completion() ? "neutralised" : "active"
+ to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
+ if(last_man_standing&&!already_traitored)
+ for(var/objective_ in owner.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(!objective.check_completion())
+ last_man_standing = FALSE
+ return
+ if(owner.special_role == TRAITOR_AGENT_ROLE)
+ to_chat(owner.current," All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. ")
+ else
+ to_chat(owner.current," All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.")
+ replace_escape_objective(owner)
+
+/datum/antagonist/traitor/internal_affairs/process()
+ if(owner&&owner.current&&owner.current.stat!=DEAD)
+ for(var/objective_ in owner.objectives)
+ if(!is_internal_objective(objective_))
+ continue
+ var/datum/objective/assassinate/internal/objective = objective_
+ if(!objective.target)
+ continue
+ if(objective.check_completion())
+ if(objective.stolen)
+ continue
+ else
+ steal_targets(objective.target)
+ objective.stolen = TRUE
+ else
+ if(objective.stolen)
+ var/fail_msg = "Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! "
+ if(last_man_standing)
+ if(owner.special_role == TRAITOR_AGENT_ROLE)
+ fail_msg += " You no longer have permission to die. "
+ else
+ fail_msg += " The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated."
+ reinstate_escape_objective(owner)
+ last_man_standing = FALSE
+ to_chat(owner.current, fail_msg)
+ objective.stolen = FALSE
+
+/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
+ if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
+
+ // Assassinate
+ var/datum/mind/target_mind = SSticker.mode.target_list[owner]
+ if(issilicon(target_mind.current))
+ var/datum/objective/destroy/internal/destroy_objective = new
+ destroy_objective.owner = owner
+ destroy_objective.target = target_mind
+ destroy_objective.update_explanation_text()
+ owner.objectives += destroy_objective
+ else
+ var/datum/objective/assassinate/internal/kill_objective = new
+ kill_objective.owner = owner
+ kill_objective.target = target_mind
+ kill_objective.update_explanation_text()
+ owner.objectives += kill_objective
+
+ // Escape
+ if(issilicon(owner.current))
+ var/datum/objective/survive/survive_objective = new
+ survive_objective.owner = owner
+ owner.objectives += survive_objective
+ else
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ owner.objectives += escape_objective
+ START_PROCESSING(SSprocessing, src)
+ if(!issilicon(owner.current))
+ give_pinpointer()
+ //Optional traitor objective
+ if(prob(PROB_ACTUAL_TRAITOR))
+ employer = "The Syndicate"
+ owner.special_role = TRAITOR_AGENT_ROLE
+ forge_single_objective()
+
+ else
+ ..() // Give them standard objectives.
+ return
+
+/datum/antagonist/traitor/iaa/greet()
+ var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
+
+ to_chat(owner.current, "You are the [owner.special_role].")
+ if(owner.special_role == TRAITOR_AGENT_ROLE)
+ to_chat(owner.current, "Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.")
+ to_chat(owner.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
+ to_chat(owner.current, " You have been provided with a standard uplink to accomplish your task. ")
+ else
+ to_chat(owner.current, "Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.")
+ to_chat(owner.current, "While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.")
+ to_chat(owner.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.")
+
+ to_chat(owner.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.")
+ owner.announce_objectives()
+
+#undef PROB_ACTUAL_TRAITOR
+#undef PINPOINTER_EXTRA_RANDOM_RANGE
+#undef PINPOINTER_MINIMUM_RANGE
+#undef PINPOINTER_PING_TIME
diff --git a/code/datums/antagonists/datum_traitor.dm b/code/datums/antagonists/datum_traitor.dm
index cfe3da6afe6..075bba803a0 100644
--- a/code/datums/antagonists/datum_traitor.dm
+++ b/code/datums/antagonists/datum_traitor.dm
@@ -1,17 +1,30 @@
/datum/antagonist/traitor
name = "Traitor"
+ var/employer = "The Syndicate"
+ var/give_objectives = TRUE
+
+/datum/antagonist/traitor/custom //used to give custom objectives
+ silent = TRUE
+ give_objectives = FALSE
/datum/antagonist/traitor/on_gain()
- forge_traitor_objectives()
- addMemories()
- .=..()
+ SSticker.mode.traitors+=owner
+ if(give_objectives)
+ forge_traitor_objectives()
+ finalize_traitor()
+ ..()
+
+/datum/antagonist/traitor/on_removal()
+ SSticker.mode.traitors-=owner
+ ..()
+
/datum/antagonist/traitor/proc/forge_traitor_objectives()
if(issilicon(owner.current))
var/objective_count = 0
if(prob(30))
- objective_count+=forge_single_objective(owner)
+ objective_count+=forge_single_objective()
for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
var/datum/objective/assassinate/kill_objective = new
@@ -27,16 +40,16 @@
var/is_hijacker = prob(10)
var/martyr_chance = prob(20)
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
- if(!exchange_blue && traitors.len >= 8) //Set up an exchange if there are enough traitors
- if(!exchange_red)
- exchange_red = owner
+ if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
+ if(!SSticker.mode.exchange_red)
+ SSticker.mode.exchange_red = owner
else
- exchange_blue = owner
- assign_exchange_role(exchange_red)
- assign_exchange_role(exchange_blue)
+ SSticker.mode.exchange_blue = owner
+ assign_exchange_role(SSticker.mode.exchange_red)
+ assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives
for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
- forge_single_objective(owner)
+ forge_single_objective()
if(is_hijacker && objective_count <= config.traitor_objectives_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
if (!(locate(/datum/objective/hijack) in owner.objectives))
@@ -117,10 +130,10 @@
steal_objective.find_target()
owner.objectives += steal_objective
-/datum/antagonist/traitor/proc/greet()
- to_chat(traitor.current, "You are the [traitor_name].")
- traitor.announce_objectives()
- return
+/datum/antagonist/traitor/greet()
+ to_chat(owner.current, "You are the [owner.special_role].")
+ owner.announce_objectives()
+ give_codewords()
/datum/antagonist/traitor/proc/finalize_traitor()
if(issilicon(owner.current))
@@ -128,7 +141,7 @@
owner.current.playsound_local('sound/ambience/antag/Malf.ogg',100,0)
owner.current.grant_language(/datum/language/codespeak)
else
- equip_traitor()
+ equip()
owner.current.playsound_local('sound/ambience/antag/TatorAlert.ogg',100,0)
SSticker.mode.update_traitor_icons_added(owner)
return
@@ -159,82 +172,18 @@
to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
killer.add_malf_picker()
-/datum/antagonist/traitor/proc/equip(safety = 0)
- var/mob/living/human/traitor_mob = owner.current
- if (!traitor_mob||!istype(traitor_mob))
- return
- . = 1
- if (traitor_mob.mind)
- if (traitor_mob.mind.assigned_role == "Clown")
- to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
- traitor_mob.dna.remove_mutation(CLOWNMUT)
-
- var/list/all_contents = traitor_mob.GetAllContents()
- var/obj/item/device/pda/PDA = locate() in all_contents
- var/obj/item/device/radio/R = locate() in all_contents
- var/obj/item/weapon/pen/P = locate() in all_contents //including your PDA-pen!
-
- var/obj/item/uplink_loc
-
- if(traitor_mob.client && traitor_mob.client.prefs)
- switch(traitor_mob.client.prefs.uplink_spawn_loc)
- if(UPLINK_PDA)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_RADIO)
- uplink_loc = R
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = P
- if(UPLINK_PEN)
- uplink_loc = P
- if(!uplink_loc)
- uplink_loc = PDA
- if(!uplink_loc)
- uplink_loc = R
-
- if (!uplink_loc)
- to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
- . = 0
- else
- var/obj/item/device/uplink/U = new(uplink_loc)
- U.owner = "[traitor_mob.key]"
- uplink_loc.hidden_uplink = U
-
- if(uplink_loc == R)
- R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
-
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(R.traitor_frequency)] to unlock its hidden features.")
- traitor_mob.mind.store_memory("Radio Frequency: [format_frequency(R.traitor_frequency)] ([R.name]).")
-
- else if(uplink_loc == PDA)
- PDA.lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]"
-
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[PDA.lock_code]\" into the ringtone select to unlock its hidden features.")
- traitor_mob.mind.store_memory("Uplink Passcode: [PDA.lock_code] ([PDA.name]).")
-
- else if(uplink_loc == P)
- P.traitor_unlock_degrees = rand(1, 360)
-
- to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [P.traitor_unlock_degrees] from its starting position to unlock its hidden features.")
- traitor_mob.mind.store_memory("Uplink Degrees: [P.traitor_unlock_degrees] ([P.name]).")
-
- if(!safety) // If they are not a rev. Can be added on to.
- give_codewords(traitor_mob)
+/datum/antagonist/traitor/proc/equip(safety = 0, employer)
+ SSticker.mode.equip_traitor(owner.current)
/datum/antagonist/traitor/proc/assign_exchange_role()
//set faction
var/faction = "red"
- if(owner == exchange_blue)
+ if(owner == SSticker.mode.exchange_blue)
faction = "blue"
//Assign objectives
var/datum/objective/steal/exchange/exchange_objective = new
- exchange_objective.set_faction(faction,((faction == "red") ? exchange_blue : exchange_red))
+ exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
exchange_objective.owner = owner
owner.objectives += exchange_objective
@@ -248,7 +197,7 @@
var/mob/living/carbon/human/mob = owner.current
var/obj/item/weapon/folder/syndicate/folder
- if(owner == exchange_red)
+ if(owner == SSticker.mode.exchange_red)
folder = new/obj/item/weapon/folder/syndicate/red(mob.loc)
else
folder = new/obj/item/weapon/folder/syndicate/blue(mob.loc)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 3d9fc9c62bc..017265fa7d6 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -186,13 +186,7 @@
/datum/mind/proc/remove_traitor()
if(src in SSticker.mode.traitors)
- SSticker.mode.traitors -= src
- if(isAI(current))
- var/mob/living/silicon/ai/A = current
- A.set_zeroth_law("")
- A.verbs -= /mob/living/silicon/ai/proc/choose_modules
- A.malf_picker.remove_verbs(A)
- qdel(A.malf_picker)
+ src.remove_antag_datum(ANTAG_DATUM_TRAITOR)
special_role = null
remove_antag_equip()
SSticker.mode.update_traitor_icons_removed(src)
@@ -1164,19 +1158,22 @@
if("traitor")
if(!(src in SSticker.mode.traitors))
- SSticker.mode.traitors += src
- special_role = "traitor"
- to_chat(current, "You are a traitor!")
message_admins("[key_name_admin(usr)] has traitor'ed [current].")
log_admin("[key_name(usr)] has traitor'ed [current].")
- if(isAI(current))
- var/mob/living/silicon/ai/A = current
- SSticker.mode.add_law_zero(A)
- SSticker.mode.update_traitor_icons_added(src)
+ make_Traitor()
if("autoobjectives")
- SSticker.mode.forge_traitor_objectives(src)
- to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.")
+ if(!(src in SSticker.mode.traitors))
+ message_admins("[key_name_admin(usr)] has traitor'ed [current] as part of autoobjectives.")
+ log_admin("[key_name(usr)] has traitor'ed [current] as part of autoobjectives.")
+ make_Traitor()
+ else
+ var/datum/antagonist/traitor/traitordatum = has_antag_datum(ANTAG_DATUM_TRAITOR)
+ if(!traitordatum)
+ message_admins("Could not generate objectives for [current] with mind [src] - they have no traitor datum despite being in SSticker.mode.traitors! This is a bug.")
+ else
+ traitordatum.forge_traitor_objectives()
+ to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.")
else if(href_list["devil"])
var/datum/antagonist/devil/devilinfo = has_antag_datum(ANTAG_DATUM_DEVIL)
@@ -1353,7 +1350,7 @@
message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].")
log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].")
if("uplink")
- if(!SSticker.mode.equip_traitor(current, !(src in SSticker.mode.traitors)))
+ if(!SSticker.mode.equip_traitor(current))
to_chat(usr, "Equipping a syndicate failed!")
log_admin("[key_name(usr)] attempted to give [current] an uplink.")
@@ -1384,7 +1381,9 @@
/datum/mind/proc/make_Traitor()
if(!(src in SSticker.mode.traitors))
- add_antag_datum(ANTAG_DATUM_TRAITOR)
+ var/datum/antagonist/traitor/traitordatum = add_antag_datum(ANTAG_DATUM_TRAITOR)
+ return traitordatum
+
/datum/mind/proc/make_Nuke(turf/spawnloc, nuke_code, leader=0, telecrystals = TRUE)
if(!(src in SSticker.mode.syndicates))
diff --git a/code/game/gamemodes/devil/devil agent/devil_agent.dm b/code/game/gamemodes/devil/devil agent/devil_agent.dm
index dfdb957153a..5c48406f23e 100644
--- a/code/game/gamemodes/devil/devil agent/devil_agent.dm
+++ b/code/game/gamemodes/devil/devil agent/devil_agent.dm
@@ -10,8 +10,8 @@
num_modifier = 4
objective_count = 2
- var/list/target_list = list()
- var/list/late_joining_list = list()
+ var/list/devil_target_list = list() //will update to be a child of internal affairs when bothered
+ var/list/devil_late_joining_list = list()
minimum_devils = 3
announce_text = "There are devil agents onboard the station, trying to outbid each other!\n\
@@ -24,7 +24,7 @@
i++
if(i + 1 > devils.len)
i = 0
- target_list[devil] = devils[i + 1]
+ devil_target_list[devil] = devils[i + 1]
..()
/datum/game_mode/devil/devil_agents/add_devil_objectives(datum/mind/devil_mind, quantity)
@@ -32,8 +32,8 @@
/datum/game_mode/devil/devil_agents/proc/give_outsell_objective(datum/mind/devil)
//If you override this method, have it return the number of objectives added.
- if(target_list.len && target_list[devil]) // Is a double agent
- var/datum/mind/target_mind = target_list[devil]
+ if(devil_target_list.len && devil_target_list[devil]) // Is a double agent
+ var/datum/mind/target_mind = devil_target_list[devil]
var/datum/objective/devil/outsell/outsellobjective = new
outsellobjective.owner = devil
outsellobjective.target = target_mind
diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm
index 9953cbe4abc..1f958a634ec 100644
--- a/code/game/gamemodes/traitor/double_agents.dm
+++ b/code/game/gamemodes/traitor/double_agents.dm
@@ -1,13 +1,10 @@
-#define PINPOINTER_MINIMUM_RANGE 15
-#define PINPOINTER_EXTRA_RANDOM_RANGE 10
-#define PINPOINTER_PING_TIME 40
-#define PROB_ACTUAL_TRAITOR 20
-#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
+/datum/game_mode
+ var/list/target_list = list()
+ var/list/late_joining_list = list()
/datum/game_mode/traitor/internal_affairs
name = "Internal Affairs"
config_tag = "internal_affairs"
- employer = "Internal Affairs"
required_players = 25
required_enemies = 5
recommended_enemies = 8
@@ -16,13 +13,12 @@
traitors_possible = 10 //hard limit on traitors if scaling is turned off
num_modifier = 4 // Four additional traitors
+ antag_datum = ANTAG_DATUM_IAA
announce_text = "There are Nanotrasen Internal Affairs Agents trying to kill each other!\n\
IAA: Eliminate your targets and protect yourself!\n\
Crew: Stop the IAA agents before they can cause too much mayhem."
- var/list/target_list = list()
- var/list/late_joining_list = list()
/datum/game_mode/traitor/internal_affairs/post_setup()
@@ -35,226 +31,6 @@
..()
-/datum/status_effect/agent_pinpointer
- id = "agent_pinpointer"
- duration = -1
- tick_interval = PINPOINTER_PING_TIME
- alert_type = /obj/screen/alert/status_effect/agent_pinpointer
- var/minimum_range = PINPOINTER_MINIMUM_RANGE
- var/mob/scan_target = null
-
-/obj/screen/alert/status_effect/agent_pinpointer
- name = "Internal Affairs Integrated Pinpointer"
- desc = "Even stealthier than a normal implant."
- icon = 'icons/obj/device.dmi'
- icon_state = "pinon"
-
-/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
- if(!scan_target)
- linked_alert.icon_state = "pinonnull"
- return
- var/turf/here = get_turf(owner)
- var/turf/there = get_turf(scan_target)
- if(here.z != there.z)
- linked_alert.icon_state = "pinonnull"
- return
- if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
- linked_alert.icon_state = "pinondirect"
- else
- linked_alert.setDir(get_dir(here, there))
- switch(get_dist(here, there))
- if(1 to 8)
- linked_alert.icon_state = "pinonclose"
- if(9 to 16)
- linked_alert.icon_state = "pinonmedium"
- if(16 to INFINITY)
- linked_alert.icon_state = "pinonfar"
-
-
-/datum/status_effect/agent_pinpointer/proc/scan_for_target()
- scan_target = null
- if(owner)
- if(owner.mind)
- if(owner.mind.objectives)
- for(var/datum/objective/objective_ in owner.mind.objectives)
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- var/mob/current = objective.target.current
- if(current&¤t.stat!=DEAD)
- scan_target = current
- break
-
-
-/datum/status_effect/agent_pinpointer/tick()
- if(!owner)
- qdel(src)
- return
- scan_for_target()
- point_to_target()
-
-/proc/give_pinpointer(datum/mind/owner)
- if(owner && owner.current)
- owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
-
-
-/datum/internal_agent_state
- var/traitored = FALSE
- var/datum/mind/owner = null
- var/list/datum/mind/targets_stolen = list()
-
-/proc/is_internal_objective(datum/objective/O)
- return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
-
-/proc/replace_escape_objective(datum/mind/owner)
- if(!owner||!owner.objectives)
- return
- for (var/objective_ in owner.objectives)
- if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
- continue
- owner.objectives -= objective_
- var/datum/objective/martyr/martyr_objective = new
- martyr_objective.owner = owner
- owner.objectives += martyr_objective
-
-/proc/reinstate_escape_objective(datum/mind/owner)
- if(!owner||!owner.objectives)
- return
- for (var/objective_ in owner.objectives)
- if(!istype(objective_, /datum/objective/martyr))
- continue
- owner.objectives -= objective_
- if(issilicon(owner))
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = owner
- owner.objectives += survive_objective
- else
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = owner
- owner.objectives += escape_objective
-
-/datum/internal_agent_state/proc/steal_targets(datum/mind/victim)
- if(!owner.current||owner.current.stat==DEAD) //Should already be guaranteed if this is only called from steal_targets_timer_func, but better to be safe code than sorry code
- return
- var/already_traitored = traitored
- to_chat(owner.current, " Target eliminated: [victim.name]")
- for(var/objective_ in victim.objectives)
- if(istype(objective_, /datum/objective/assassinate/internal))
- var/datum/objective/assassinate/internal/objective = objective_
- if(objective.target==owner)
- traitored = TRUE
- else if(targets_stolen.Find(objective.target) == 0)
- var/datum/objective/assassinate/internal/new_objective = new
- new_objective.owner = owner
- new_objective.target = objective.target
- new_objective.update_explanation_text()
- owner.objectives += new_objective
- targets_stolen += objective.target
- var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
- else if(istype(objective_, /datum/objective/destroy/internal))
- var/datum/objective/destroy/internal/objective = objective_
- var/datum/objective/destroy/internal/new_objective = new
- if(objective.target==owner)
- traitored = TRUE
- else if(targets_stolen.Find(objective.target) == 0)
- new_objective.owner = owner
- new_objective.target = objective.target
- new_objective.update_explanation_text()
- owner.objectives += new_objective
- targets_stolen += objective.target
- var/status_text = objective.check_completion() ? "neutralised" : "active"
- to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ")
- if(traitored&&!already_traitored)
- for(var/objective_ in owner.objectives)
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- if(!objective.check_completion())
- traitored = FALSE
- return
- if(owner.special_role == TRAITOR_AGENT_ROLE)
- to_chat(owner.current," All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. ")
- else
- to_chat(owner.current," All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.")
- replace_escape_objective(owner)
-
-
-
-/datum/internal_agent_state/proc/steal_targets_timer_func()
- if(owner&&owner.current&&owner.current.stat!=DEAD)
- for(var/objective_ in owner.objectives)
- if(!is_internal_objective(objective_))
- continue
- var/datum/objective/assassinate/internal/objective = objective_
- if(!objective.target)
- continue
- if(objective.check_completion())
- if(objective.stolen)
- continue
- else
- steal_targets(objective.target)
- objective.stolen = TRUE
- else
- if(objective.stolen)
- var/fail_msg = "Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! "
- if(traitored)
- if(owner.special_role == TRAITOR_AGENT_ROLE)
- fail_msg += " You no longer have permission to die. "
- else
- fail_msg += " The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated."
- reinstate_escape_objective(owner)
- traitored = FALSE
- to_chat(owner.current, fail_msg)
- objective.stolen = FALSE
- add_steal_targets_timer(owner)
-
-/datum/internal_agent_state/proc/add_steal_targets_timer()
- var/datum/callback/C = new(src, .steal_targets_timer_func)
- addtimer(C, 30)
-
-/datum/game_mode/traitor/internal_affairs/forge_traitor_objectives(datum/mind/traitor)
-
- if(target_list.len && target_list[traitor]) // Is a double agent
-
- // Assassinate
- var/datum/mind/target_mind = target_list[traitor]
- if(issilicon(target_mind.current))
- var/datum/objective/destroy/internal/destroy_objective = new
- destroy_objective.owner = traitor
- destroy_objective.target = target_mind
- destroy_objective.update_explanation_text()
- traitor.objectives += destroy_objective
- else
- var/datum/objective/assassinate/internal/kill_objective = new
- kill_objective.owner = traitor
- kill_objective.target = target_mind
- kill_objective.update_explanation_text()
- traitor.objectives += kill_objective
-
- // Escape
- if(issilicon(traitor.current))
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = traitor
- traitor.objectives += survive_objective
- else
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = traitor
- traitor.objectives += escape_objective
- var/datum/internal_agent_state/state = new
- state.owner=traitor
- state.add_steal_targets_timer()
- if(!issilicon(traitor.current))
- give_pinpointer(traitor)
- //Optional traitor objective
- if(prob(PROB_ACTUAL_TRAITOR))
- traitor.special_role = TRAITOR_AGENT_ROLE
- forge_single_objective(traitor)
-
- else
- ..() // Give them standard objectives.
- return
-
/datum/game_mode/traitor/internal_affairs/add_latejoin_traitor(datum/mind/character)
check_potential_agents()
@@ -299,29 +75,3 @@
late_joining_list -= M
-/datum/game_mode/traitor/internal_affairs/greet_traitor(datum/mind/traitor)
- var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
- if(traitor.special_role == TRAITOR_AGENT_ROLE)
- to_chat(traitor.current, "You are the [TRAITOR_AGENT_ROLE].")
- to_chat(traitor.current, "Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.")
- to_chat(traitor.current, "Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.")
- to_chat(traitor.current, " You have been provided with a standard uplink to accomplish your task. ")
- to_chat(traitor.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.")
- else
- to_chat(traitor.current, "You are the [traitor_name].")
- to_chat(traitor.current, "Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.")
- to_chat(traitor.current, "While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.")
- to_chat(traitor.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.")
- to_chat(traitor.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.")
- traitor.announce_objectives()
-
-
-
-/datum/game_mode/traitor/internal_affairs/give_codewords(mob/living/traitor_mob)
- return
-
-#undef PROB_ACTUAL_TRAITOR
-#undef PINPOINTER_EXTRA_RANDOM_RANGE
-#undef PINPOINTER_MINIMUM_RANGE
-#undef PINPOINTER_PING_TIME
-
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index e0674cc51d2..52666e77fbb 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -1,6 +1,5 @@
/datum/game_mode
var/traitor_name = "traitor"
- var/employer = "The Syndicate"
var/list/datum/mind/traitors = list()
var/datum/mind/exchange_red
@@ -25,6 +24,7 @@
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual.
+ var/antag_datum = ANTAG_DATUM_TRAITOR //what type of antag to create
/datum/game_mode/traitor/pre_setup()
@@ -60,7 +60,9 @@
/datum/game_mode/traitor/post_setup()
for(var/datum/mind/traitor in traitors)
- traitor.add_antag_datum(ANTAG_DATUM_TRAITOR)
+ spawn(rand(10,100))
+ var/datum/antagonist/traitor/traitordatum = traitor.add_antag_datum(antag_datum)
+ traitordatum.finalize_traitor()
if(!exchange_blue)
exchange_blue = -1 //Block latejoiners from getting exchange objectives
modePlayer += traitors
@@ -147,6 +149,69 @@
return 1
+/datum/game_mode/proc/equip_traitor(mob/living/carbon/human/traitor_mob,var/employer = "The Syndicate")
+ if (!istype(traitor_mob))
+ return
+ . = 1
+ if (traitor_mob.mind)
+ if (traitor_mob.mind.assigned_role == "Clown")
+ to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
+ traitor_mob.dna.remove_mutation(CLOWNMUT)
+
+ var/list/all_contents = traitor_mob.GetAllContents()
+ var/obj/item/device/pda/PDA = locate() in all_contents
+ var/obj/item/device/radio/R = locate() in all_contents
+ var/obj/item/weapon/pen/P = locate() in all_contents //including your PDA-pen!
+
+ var/obj/item/uplink_loc
+
+ if(traitor_mob.client && traitor_mob.client.prefs)
+ switch(traitor_mob.client.prefs.uplink_spawn_loc)
+ if(UPLINK_PDA)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_RADIO)
+ uplink_loc = R
+ if(!uplink_loc)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = P
+ if(UPLINK_PEN)
+ uplink_loc = P
+ if(!uplink_loc)
+ uplink_loc = PDA
+ if(!uplink_loc)
+ uplink_loc = R
+
+ if (!uplink_loc)
+ to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
+ . = 0
+ else
+ var/obj/item/device/uplink/U = new(uplink_loc)
+ U.owner = "[traitor_mob.key]"
+ uplink_loc.hidden_uplink = U
+
+ if(uplink_loc == R)
+ R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
+
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(R.traitor_frequency)] to unlock its hidden features.")
+ traitor_mob.mind.store_memory("Radio Frequency: [format_frequency(R.traitor_frequency)] ([R.name]).")
+
+ else if(uplink_loc == PDA)
+ PDA.lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]"
+
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[PDA.lock_code]\" into the ringtone select to unlock its hidden features.")
+ traitor_mob.mind.store_memory("Uplink Passcode: [PDA.lock_code] ([PDA.name]).")
+
+ else if(uplink_loc == P)
+ P.traitor_unlock_degrees = rand(1, 360)
+
+ to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [P.traitor_unlock_degrees] from its starting position to unlock its hidden features.")
+ traitor_mob.mind.store_memory("Uplink Degrees: [P.traitor_unlock_degrees] ([P.name]).")
+
/datum/game_mode/proc/update_traitor_icons_added(datum/mind/traitor_mind)
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index e569eacd2bc..555a701aace 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -337,28 +337,22 @@
for(var/mob/living/carbon/human/H in GLOB.player_list)
if(H.stat == 2 || !H.client || !H.mind) continue
if(is_special_character(H)) continue
- //traitorize(H, objective, 0)
- SSticker.mode.traitors += H.mind
- H.mind.special_role = "traitor"
+ var/datum/antagonist/traitor/traitordatum = H.mind.add_antag_datum(ANTAG_DATUM_TRAITOR_CUSTOM)
var/datum/objective/new_objective = new
new_objective.owner = H
new_objective.explanation_text = objective
H.mind.objectives += new_objective
- SSticker.mode.greet_traitor(H.mind)
- //SSticker.mode.forge_traitor_objectives(H.mind)
- SSticker.mode.finalize_traitor(H.mind)
+ traitordatum.greet()
for(var/mob/living/silicon/A in GLOB.player_list)
if(A.stat == 2 || !A.client || !A.mind) continue
if(ispAI(A)) continue
else if(is_special_character(A)) continue
- SSticker.mode.traitors += A.mind
- A.mind.special_role = "traitor"
+ var/datum/antagonist/traitor/traitordatum = A.mind.add_antag_datum(ANTAG_DATUM_TRAITOR_CUSTOM)
var/datum/objective/new_objective = new
new_objective.owner = A
new_objective.explanation_text = objective
A.mind.objectives += new_objective
- SSticker.mode.greet_traitor(A.mind)
- SSticker.mode.finalize_traitor(A.mind)
+ traitordatum.greet()
message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]")
log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]")
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index ed3765d0cd7..49e54a1e9bb 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -368,10 +368,13 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/player_key = G_found.key
//Now for special roles and equipment.
+ var/datum/antagonist/traitor/traitordatum = new_character.mind.has_antag_datum(ANTAG_DATUM_TRAITOR)
+ if(traitordatum)
+ SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)
+ traitordatum.equip()
+
+
switch(new_character.mind.special_role)
- if("traitor")
- SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)
- SSticker.mode.equip_traitor(new_character)
if("Wizard")
new_character.loc = pick(GLOB.wizardstart)
//SSticker.mode.learn_basic_spells(new_character)
@@ -396,12 +399,12 @@ Traitors and the like can also be revived with the previous role mostly intact.
switch(new_character.mind.assigned_role)
if("Cyborg")//More rigging to make em' work and check if they're traitor.
new_character = new_character.Robotize()
- if(new_character.mind.special_role=="traitor")
- SSticker.mode.add_law_zero(new_character)
+ if(traitordatum)
+ traitordatum.add_law_zero(new_character)
if("AI")
new_character = new_character.AIize()
- if(new_character.mind.special_role=="traitor")
- SSticker.mode.add_law_zero(new_character)
+ if(traitordatum)
+ traitordatum.add_law_zero(new_character)
else
SSjob.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them.
@@ -1205,4 +1208,4 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
return
message_admins("[key_name_admin(usr)] triggered a Centcom recall, with the admiral message of: [message]")
- log_game("[key_name(usr)] triggered a Centcom recall, with the message of: [message]")
\ No newline at end of file
+ log_game("[key_name(usr)] triggered a Centcom recall, with the message of: [message]")
diff --git a/tgstation.dme b/tgstation.dme
index 9193bb24f3e..4abc359bedc 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -13,7 +13,7 @@
// END_PREFERENCES
// BEGIN_INCLUDE
-#include "_maps\basemap.dm"
+#include "_maps\runtimestation.dm"
#include "code\_compile_options.dm"
#include "code\world.dm"
#include "code\__DATASTRUCTURES\globals.dm"
@@ -237,6 +237,8 @@
#include "code\datums\antagonists\antag_datum.dm"
#include "code\datums\antagonists\datum_clockcult.dm"
#include "code\datums\antagonists\datum_cult.dm"
+#include "code\datums\antagonists\datum_internal_affairs.dm"
+#include "code\datums\antagonists\datum_traitor.dm"
#include "code\datums\antagonists\ninja.dm"
#include "code\datums\diseases\_disease.dm"
#include "code\datums\diseases\_MobProcs.dm"