diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm
index 3b5cb5d7950..4ffbb978a65 100644
--- a/code/__DEFINES/text.dm
+++ b/code/__DEFINES/text.dm
@@ -84,8 +84,6 @@
#define ARCADE_FILE "arcade.json"
/// File location for boomer meme catchphrases
#define BOOMER_FILE "boomer.json"
-/// File location for locations on the station
-#define LOCATIONS_FILE "locations.json"
/// File location for wanted posters messages
#define WANTED_FILE "wanted_message.json"
/// File location for really dumb suggestions memes
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index f3af1b68531..9a05956537e 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -183,15 +183,6 @@ GLOBAL_VAR(command_name)
return name
-
-//Traitors and traitor silicons will get these. Revs will not.
-GLOBAL_VAR(syndicate_code_phrase) //Code phrase for traitors.
-GLOBAL_VAR(syndicate_code_response) //Code response for traitors.
-
-//Cached regex search - for checking if codewords are used.
-GLOBAL_DATUM(syndicate_code_phrase_regex, /regex)
-GLOBAL_DATUM(syndicate_code_response_regex, /regex)
-
/*
Should be expanded.
How this works:
@@ -220,14 +211,16 @@ GLOBAL_DATUM(syndicate_code_response_regex, /regex)
25; 5
)
- var/list/safety = list(1,2,3)//Tells the proc which options to remove later on.
- var/nouns = strings(ION_FILE, "ionabstract")
- var/objects = strings(ION_FILE, "ionobjects")
- var/adjectives = strings(ION_FILE, "ionadjectives")
- var/threats = strings(ION_FILE, "ionthreats")
- var/foods = strings(ION_FILE, "ionfood")
- var/drinks = strings(ION_FILE, "iondrinks")
- var/locations = strings(LOCATIONS_FILE, "locations")
+ var/list/safety = list(1, 2, 3)//Tells the proc which options to remove later on.
+ var/list/nouns = strings(ION_FILE, "ionabstract")
+ var/list/objects = strings(ION_FILE, "ionobjects")
+ var/list/adjectives = strings(ION_FILE, "ionadjectives")
+ var/list/threats = strings(ION_FILE, "ionthreats")
+ var/list/foods = strings(ION_FILE, "ionfood")
+ var/list/drinks = strings(ION_FILE, "iondrinks")
+ var/list/locations = list()
+ for(var/area/area_type as anything in list(/area/space) | GLOB.the_station_areas)
+ locations |= format_text(area_type::name)
var/list/names = list()
for(var/datum/record/crew/target in GLOB.manifest.general)//Picks from crew manifest.
diff --git a/code/controllers/subsystem/dynamic/dynamic_ruleset_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_ruleset_roundstart.dm
index 2143b6cbb00..3df35073247 100644
--- a/code/controllers/subsystem/dynamic/dynamic_ruleset_roundstart.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_ruleset_roundstart.dm
@@ -442,6 +442,7 @@
new_head.give_flash = TRUE
new_head.give_hud = TRUE
new_head.remove_clumsy = TRUE
+ new_head.roundstart = TRUE
candidate.add_antag_datum(new_head, GLOB.revolution_handler.revs)
GLOB.revolution_handler.start_revolution()
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 06a13b68b82..4dbe10f0def 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -111,22 +111,6 @@ SUBSYSTEM_DEF(ticker)
else
set_lobby_music("[global.config.directory]/title_music/sounds/[pick(music)]")
- if(!GLOB.syndicate_code_phrase)
- GLOB.syndicate_code_phrase = generate_code_phrase(return_list=TRUE)
-
- var/codewords = jointext(GLOB.syndicate_code_phrase, "|")
- var/regex/codeword_match = new("([codewords])", "ig")
-
- GLOB.syndicate_code_phrase_regex = codeword_match
-
- if(!GLOB.syndicate_code_response)
- GLOB.syndicate_code_response = generate_code_phrase(return_list=TRUE)
-
- var/codewords = jointext(GLOB.syndicate_code_response, "|")
- var/regex/codeword_match = new("([codewords])", "ig")
-
- GLOB.syndicate_code_response_regex = codeword_match
-
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * (1 SECONDS))
return SS_INIT_SUCCESS
@@ -141,9 +125,9 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, span_notice("Welcome to [station_name()]!"))
for(var/channel_tag in CONFIG_GET(str_list/channel_announce_new_game))
send2chat(new /datum/tgs_message_content("New round starting on [SSmapping.current_map.map_name]!"), channel_tag)
+
current_state = GAME_STATE_PREGAME
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME)
-
fire()
if(GAME_STATE_PREGAME)
//lobby stats for statpanels
diff --git a/code/controllers/subsystem/traitor.dm b/code/controllers/subsystem/traitor.dm
index edb0f237f5e..cd13366bdb9 100644
--- a/code/controllers/subsystem/traitor.dm
+++ b/code/controllers/subsystem/traitor.dm
@@ -21,11 +21,24 @@ SUBSYSTEM_DEF(traitor)
var/list/datum/uplink_handler/uplink_handlers = list()
/// The current scaling per minute of progression.
var/current_progression_scaling = 1 MINUTES
+ /// List of code words for traitors
+ var/syndicate_code_phrase
+ /// List of code responses for traitors
+ var/syndicate_code_response
+ /// Regex of code words for traitors
+ var/regex/syndicate_code_phrase_regex
+ /// Regex of code responses for traitors
+ var/regex/syndicate_code_response_regex
/datum/controller/subsystem/traitor/Initialize()
current_progression_scaling = 1 MINUTES * CONFIG_GET(number/traitor_scaling_multiplier)
for(var/theft_item in subtypesof(/datum/objective_item/steal))
new theft_item
+
+ syndicate_code_phrase = generate_code_phrase(return_list = TRUE)
+ syndicate_code_phrase_regex = new("([jointext(syndicate_code_phrase, "|")])", "ig")
+ syndicate_code_response = generate_code_phrase(return_list = TRUE)
+ syndicate_code_response_regex = new("([jointext(syndicate_code_response, "|")])", "ig")
return SS_INIT_SUCCESS
/datum/controller/subsystem/traitor/fire(resumed)
diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
index 83b84c2e7b9..763ab99c4ce 100644
--- a/code/modules/antagonists/malf_ai/malf_ai.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai.dm
@@ -118,8 +118,8 @@
if(istype(datum_owner))
datum_owner.hack_software = TRUE
- datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_phrase_regex, "blue", src)
- datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_response_regex, "red", src)
+ datum_owner.AddComponent(/datum/component/codeword_hearing, SStraitor.syndicate_code_phrase_regex, "blue", src)
+ datum_owner.AddComponent(/datum/component/codeword_hearing, SStraitor.syndicate_code_response_regex, "red", src)
/datum/antagonist/malf_ai/remove_innate_effects(mob/living/mob_override)
var/mob/living/silicon/ai/datum_owner = mob_override || owner.current
@@ -135,8 +135,8 @@
if(!owner.current)
return
- var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
- var/responses = jointext(GLOB.syndicate_code_response, ", ")
+ var/phrases = jointext(SStraitor.syndicate_code_phrase, ", ")
+ var/responses = jointext(SStraitor.syndicate_code_response, ", ")
antag_memory += "Code Phrase: [span_blue("[phrases]")]
"
antag_memory += "Code Response: [span_red("[responses]")]
"
@@ -176,8 +176,8 @@
data["has_codewords"] = should_give_codewords
if(should_give_codewords)
- data["phrases"] = jointext(GLOB.syndicate_code_phrase, ", ")
- data["responses"] = jointext(GLOB.syndicate_code_response, ", ")
+ data["phrases"] = jointext(SStraitor.syndicate_code_phrase, ", ")
+ data["responses"] = jointext(SStraitor.syndicate_code_response, ", ")
data["intro"] = malfunction_flavor["introduction"]
data["allies"] = malfunction_flavor["allies"]
data["goal"] = malfunction_flavor["goal"]
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index b3f37afae86..3d457a67c28 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -7,8 +7,9 @@
antag_hud_name = "rev"
suicide_cry = "VIVA LA REVOLUTION!!"
stinger_sound = 'sound/music/antag/revolutionary_tide.ogg'
- var/datum/team/revolution/rev_team
+ ui_name = "AntagInfoRevolution"
+ var/datum/team/revolution/rev_team
/// When this antagonist is being de-antagged, this is the source. Can be a mob (for mindshield/blunt force trauma) or a #define string.
var/deconversion_source
@@ -20,18 +21,6 @@
return ..()
/datum/antagonist/rev/admin_add(datum/mind/new_owner, mob/admin)
- // No revolution exists which means admin adding this will create a new revolution team
- // This causes problems because revolution teams (currently) require a dynamic datum to process its victory / defeat conditions
- if(!(locate(/datum/team/revolution) in GLOB.antagonist_teams))
- var/confirm = tgui_alert(admin, "Notice: Revolutions do not function 100% when created via traitor panel instead of dynamic. \
- The leaders will be able to convert as normal, but the shuttle will not be blocked and there will be no announcements when either side wins. \
- Are you sure?", "Be Wary", list("Yes", "No"))
- if(QDELETED(src) || QDELETED(new_owner.current) || confirm != "Yes")
- return
-
- go_through_with_admin_add(new_owner, admin)
-
-/datum/antagonist/rev/proc/go_through_with_admin_add(datum/mind/new_owner, mob/admin)
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has rev'ed [key_name_admin(new_owner)].")
log_admin("[key_name(admin)] has rev'ed [key_name(new_owner)].")
@@ -58,11 +47,6 @@
equip_rev()
owner.current.log_message("has been converted to the revolution!", LOG_ATTACK, color="red")
-/datum/antagonist/rev/greet()
- . = ..()
- to_chat(owner, span_userdanger("Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!"))
- owner.announce_objectives()
-
/datum/antagonist/rev/create_team(datum/team/revolution/new_team)
if(!new_team)
GLOB.revolution_handler ||= new()
@@ -98,7 +82,20 @@
message_admins("[key_name_admin(admin)] has head-rev'ed [O].")
log_admin("[key_name(admin)] has head-rev'ed [O].")
-/datum/antagonist/rev/head/go_through_with_admin_add(datum/mind/new_owner, mob/admin)
+/datum/antagonist/rev/ui_static_data(mob/user)
+ . = ..()
+ .["leader"] = (pref_flag == ROLE_REV_HEAD)
+ .["heads"] = list()
+ for(var/datum/mind/head_of_staff as anything in SSjob.get_all_heads())
+ .["heads"] += list(list("name" = head_of_staff.name, "role" = head_of_staff.assigned_role.title))
+
+/datum/antagonist/rev/head/ui_static_data(mob/user)
+ . = ..()
+ .["code_phrases"] = rev_team.head_chose_phrase_raw
+ .["code_responses"] = rev_team.head_code_responses_raw
+ .["lone_wolf"] = !roundstart || length(rev_team.get_head_revolutionaries()) == 1
+
+/datum/antagonist/rev/head/admin_add(datum/mind/new_owner, mob/admin)
give_flash = TRUE
give_hud = TRUE
remove_clumsy = TRUE
@@ -161,6 +158,7 @@
var/remove_clumsy = FALSE
var/give_flash = FALSE
var/give_hud = TRUE
+ var/roundstart = FALSE
/datum/antagonist/rev/head/pre_mindshield(mob/implanter, mob/living/mob_override)
return COMPONENT_MINDSHIELD_RESISTED
@@ -182,12 +180,18 @@
real_mob.AddComponentFrom(REF(src), /datum/component/can_flash_from_behind)
RegisterSignal(real_mob, COMSIG_MOB_SUCCESSFUL_FLASHED_MOB, PROC_REF(on_flash_success))
+ real_mob.AddComponent(/datum/component/codeword_hearing, rev_team.head_code_phrases, "blue", src)
+ real_mob.AddComponent(/datum/component/codeword_hearing, rev_team.head_code_responses, "red", src)
+
/datum/antagonist/rev/head/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/real_mob = mob_override || owner.current
real_mob.RemoveComponentSource(REF(src), /datum/component/can_flash_from_behind)
UnregisterSignal(real_mob, COMSIG_MOB_SUCCESSFUL_FLASHED_MOB)
+ for(var/datum/component/codeword_hearing/component as anything in real_mob.GetComponents(/datum/component/codeword_hearing))
+ component.delete_if_from_source(src)
+
/// Signal proc for [COMSIG_MOB_SUCCESSFUL_FLASHED_MOB].
/// Bread and butter of revolution conversion, successfully flashing a carbon will make them a revolutionary
/datum/antagonist/rev/head/proc/on_flash_success(mob/living/source, mob/living/flashed, obj/item/assembly/flash/flash, deviation)
@@ -371,6 +375,23 @@
/// List of all ex-revs. Useful because dynamic removes antag status when it ends, so this can be kept for the roundend report.
var/list/datum/mind/ex_revs = list()
+ /// List of code phrases
+ VAR_FINAL/list/head_chose_phrase_raw
+ /// Regex for code phrases that only headrevs hear
+ VAR_FINAL/regex/head_code_phrases
+ /// List of code responses
+ VAR_FINAL/list/head_code_responses_raw
+ /// Regex for code responses that only headrevs hear
+ VAR_FINAL/regex/head_code_responses
+
+/datum/team/revolution/New(starting_members)
+ . = ..()
+ head_chose_phrase_raw = generate_code_phrase(return_list = TRUE)
+ head_code_phrases = new("([jointext(head_chose_phrase_raw, "|")])", "ig")
+
+ head_chose_phrase_raw = generate_code_phrase(return_list = TRUE)
+ head_code_responses = new("([jointext(head_chose_phrase_raw, "|")])", "ig")
+
/// Saves all current headrevs and revs
/datum/team/revolution/proc/save_members()
ex_headrevs = get_head_revolutionaries()
diff --git a/code/modules/antagonists/revolution/revolution_handler.dm b/code/modules/antagonists/revolution/revolution_handler.dm
index b429500d7c8..3ffc270f464 100644
--- a/code/modules/antagonists/revolution/revolution_handler.dm
+++ b/code/modules/antagonists/revolution/revolution_handler.dm
@@ -115,7 +115,7 @@ GLOBAL_DATUM(revolution_handler, /datum/revolution_handler)
/datum/revolution_handler/proc/check_rev_victory()
for(var/datum/objective/mutiny/objective in revs.objectives)
- if(!(objective.check_completion()))
+ if(!objective.check_completion())
return FALSE
return TRUE
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index df36231c572..15262859459 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -208,8 +208,8 @@
handle_clown_mutation(datum_owner, mob_override ? null : "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
if(should_give_codewords)
- datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_phrase_regex, "blue", src)
- datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_response_regex, "red", src)
+ datum_owner.AddComponent(/datum/component/codeword_hearing, SStraitor.syndicate_code_phrase_regex, "blue", src)
+ datum_owner.AddComponent(/datum/component/codeword_hearing, SStraitor.syndicate_code_response_regex, "red", src)
/datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override)
var/mob/living/datum_owner = mob_override || owner.current
@@ -229,8 +229,8 @@
var/list/data = list()
data["has_codewords"] = should_give_codewords
if(should_give_codewords)
- data["phrases"] = jointext(GLOB.syndicate_code_phrase, ", ")
- data["responses"] = jointext(GLOB.syndicate_code_response, ", ")
+ data["phrases"] = jointext(SStraitor.syndicate_code_phrase, ", ")
+ data["responses"] = jointext(SStraitor.syndicate_code_response, ", ")
data["theme"] = traitor_flavor["ui_theme"]
data["code"] = uplink?.unlock_code
data["failsafe_code"] = uplink?.failsafe_code
@@ -315,8 +315,8 @@
return sent_data
/datum/antagonist/traitor/roundend_report_footer()
- var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
- var/responses = jointext(GLOB.syndicate_code_response, ", ")
+ var/phrases = jointext(SStraitor.syndicate_code_phrase, ", ")
+ var/responses = jointext(SStraitor.syndicate_code_response, ", ")
var/message = "
The code phrases were: [phrases]
\
The code responses were: [span_redtext("[responses]")]
"
diff --git a/strings/locations.json b/strings/locations.json
deleted file mode 100644
index 1ae1e628ab5..00000000000
--- a/strings/locations.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
- "locations": [
- "Aft Maintenance",
- "Aft Primary Hallway",
- "AI Chamber",
- "AI Satellite Antechamber",
- "AI Satellite Exterior",
- "AI Upload Chamber",
- "Armory",
- "Atmospherics Engine",
- "Atmospherics",
- "Atrium",
- "Auxiliary Base Construction",
- "Auxiliary Restrooms",
- "Auxiliary Tool Storage",
- "Bar",
- "Bridge",
- "Brig Control",
- "Brig",
- "Captain's Office",
- "Captain's Quarters",
- "Cargo Bay",
- "Cargo Office",
- "Chapel Office",
- "Chapel",
- "Chemistry",
- "Command Hallway",
- "Construction Area",
- "Corporate Showroom",
- "Council Chamber",
- "Courtroom",
- "Custodial Closet",
- "Customs",
- "Cytology Lab",
- "Delivery Office",
- "Departure Lounge",
- "Detective's Office",
- "Dormitories",
- "Engineering Foyer",
- "Engineering Storage",
- "Engineering",
- "EVA Storage",
- "Experimentation Lab",
- "Firing Range",
- "Gateway",
- "Genetics Lab",
- "Gravity Generator Room",
- "Hydroponics",
- "Incinerator",
- "Kitchen",
- "Law Office",
- "Library",
- "Locker Room",
- "Mech Bay",
- "Medbay Central",
- "Medbay Maintenance",
- "Medbay Storage",
- "Mining Office",
- "Morgue Maintenance",
- "Morgue",
- "Primary Tool Storage",
- "Prison Wing",
- "Prisoner Education Chamber",
- "Recreation Area",
- "Recreational Holodeck",
- "Research and Development",
- "Research Division",
- "Research Testing Range",
- "Restrooms",
- "Robotics Lab",
- "Science Maintenance",
- "Security Checkpoint",
- "Security Office",
- "Service Hallway",
- "Space",
- "Supermatter Engine",
- "Surgery",
- "Technical Storage",
- "Teleporter Room",
- "Testing Lab",
- "Theatre",
- "Toxins Mixing Chamber",
- "Toxins Mixing Lab",
- "Toxins Storage",
- "Toxins Test Area",
- "Transfer Centre",
- "Transit Tube",
- "Vacant Commissary",
- "Vacant Office",
- "Vault",
- "Virology",
- "Warehouse",
- "Waste Disposal",
- "Xenobiology Lab"
- ]
-}
diff --git a/tgui/packages/tgui/interfaces/AntagInfoRevolution.tsx b/tgui/packages/tgui/interfaces/AntagInfoRevolution.tsx
new file mode 100644
index 00000000000..ccd0addf090
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AntagInfoRevolution.tsx
@@ -0,0 +1,128 @@
+import { Section, Stack } from 'tgui-core/components';
+import type { BooleanLike } from 'tgui-core/react';
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
+import type { Objective } from './common/Objectives';
+
+type Head = {
+ name: string;
+ role: string;
+};
+
+type Info = {
+ antag_name: string;
+ objectives: Objective[];
+ leader: BooleanLike;
+ code_phrases?: string[];
+ code_responses?: string[];
+ heads: Head[];
+ lone_wolf: BooleanLike;
+};
+
+// Takes [a, b, c] and returns "a, b, and c"
+function formatCodes(text: string[]) {
+ if (text.length === 0) return '';
+ if (text.length === 1) return text[0];
+ if (text.length === 2) return `${text[0]} and ${text[1]}`;
+ return `${text.slice(0, -1).join(', ')}, and ${text[text.length - 1]}`;
+}
+
+export const AntagInfoRevolution = () => {
+ const { data } = useBackend();
+ const { leader, code_phrases, code_responses, heads, lone_wolf } = data;
+ return (
+
+
+
+
+
+ Viva la Revolution!
+
+
+ {leader ? (
+
+ {lone_wolf ? (
+
+ - You are a lone leader of the revolution. It is
+ recommended to act swiftly and decisively - when the
+ revolution is sufficiently large, more leaders will be
+ promoted.
+
+ ) : (
+
+ - There are multiple leaders of the revolution. It is
+ recommended to work together and establish a plan BEFORE
+ you start converting the crew - being outed early can
+ prove extremely detrimental.
+
+ )}
+
+ - Convert the crew to your cause with a flash - any flash
+ will work.
+
+
+ - Mindshields will prevent conversion. You can identify them
+ via the flashing blue border around their job icon.
+
+
+ - The revolution is lost if you and your fellow leaders are
+ all killed or exiled. Do not let that happen!
+
+
+ ) : (
+
+
+ - Help your cause. Do not harm your fellow freedom fighters.
+
+
+ - You can identify your comrades by the red "R" icons, and
+ your leaders by the blue "R" icons.
+
+
+ - The revolution is lost if all of your leaders are killed
+ or exiled. Do not let that happen!
+
+
+ )}
+
+ {heads.length > 0 && (
+ <>
+
+
+
+
+ You must kill or exile the heads of staff:
+
+ {heads.map((head, i) => (
+
+ - {head.name}, the {head.role}
+
+ ))}
+
+
+ >
+ )}
+ {code_phrases?.length && code_responses?.length && (
+ <>
+
+
+
+
+ To identify your fellow leaders, use the following code:
+
+
+ Phrases: {formatCodes(code_phrases)}
+
+
+ Responses: {formatCodes(code_responses)}
+
+
+
+ >
+ )}
+
+
+
+
+ );
+};