diff --git a/aurorastation.dme b/aurorastation.dme
index aa75cd311d7..b2646e9f75a 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -43,6 +43,7 @@
#include "code\__DEFINES\battle_monsters.dm"
#include "code\__DEFINES\byond_compat.dm"
#include "code\__DEFINES\callbacks.dm"
+#include "code\__DEFINES\canonicity.dm"
#include "code\__DEFINES\chemistry.dm"
#include "code\__DEFINES\circuitboard.dm"
#include "code\__DEFINES\color.dm"
@@ -134,7 +135,6 @@
#include "code\__DEFINES\rust_bapi.dm"
#include "code\__DEFINES\rust_g.dm"
#include "code\__DEFINES\rust_g_overrides.dm"
-#include "code\__DEFINES\scenarios.dm"
#include "code\__DEFINES\ship_locations.dm"
#include "code\__DEFINES\ship_weapons.dm"
#include "code\__DEFINES\shuttle.dm"
@@ -482,6 +482,7 @@
#include "code\datums\progressbar.dm"
#include "code\datums\records.dm"
#include "code\datums\room_template.dm"
+#include "code\datums\round_canonicity.dm"
#include "code\datums\ruins.dm"
#include "code\datums\signals.dm"
#include "code\datums\sound_player.dm"
diff --git a/code/__DEFINES/canonicity.dm b/code/__DEFINES/canonicity.dm
new file mode 100644
index 00000000000..e22bd555d86
--- /dev/null
+++ b/code/__DEFINES/canonicity.dm
@@ -0,0 +1,33 @@
+/// Nothing in the round is canon; such as in a non-canon event. Things are forgotten the next round.
+#define ROUND_NON_CANON 0
+/// Actions taken during this round are canon. Antagonist actions are not considered here; see below.
+#define ROUND_FULL_CANON 1
+
+/// Antagonist actions are not expected during this round.
+#define ANTAGONIST_ACTIONS_NOT_EXPECTED 0
+/// Antagonist actions are NOT canon during this round.
+#define ANTAGONIST_ACTIONS_NOT_CANON 1
+/// Antagonist actions are canon during this round.
+#define ANTAGONIST_ACTIONS_CANON 2
+
+
+/// Character deaths are automatically not-canon. Usually the case in non-canon events.
+#define NO_CHARACTER_DEATH 0
+/// If character deaths are limited, and thus can be retconned if all player parties agree.
+#define LIMITED_CHARACTER_DEATH 1
+/// If players are FORCED to keep character deaths canon. In this case, ALL CHARACTER DEATHS MUST GO THROUGH HEADMINS AND LOREMASTERS TO BE RETCONNED. THERE ARE NO EXCEPTIONS!
+#define FORCED_CHARACTER_DEATH 2
+
+/// Away sites are NOT canon in this round.
+#define AWAY_SITE_NOT_CANON 0
+/// It's canon that you went to an away site, but the exact details are not (to prevent everyone from knowing the exact layout of the propellant station or whatever)
+#define AWAY_SITE_CANON_LIMITED 1
+/// The away site in its entirety is canon.
+#define AWAY_SITE_CANON_FULL 2
+
+/// Offships are not canon at all.
+#define OFFSHIP_NOT_CANON 0
+/// Offship actions are canon, barring extreme actions like bombing the main ship or whatever.
+#define OFFSHIP_CANON_LIMITED 1
+/// Offship actions are fully canon.
+#define OFFSHIP_CANON_FULL 2
diff --git a/code/__DEFINES/scenarios.dm b/code/__DEFINES/scenarios.dm
deleted file mode 100644
index dd6fe994341..00000000000
--- a/code/__DEFINES/scenarios.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-/// This mission is canon.
-#define SCENARIO_TYPE_CANON "canon"
-/// This mission is not canon.
-#define SCENARIO_TYPE_NONCANON "non-canon"
diff --git a/code/controllers/subsystems/processing/odyssey.dm b/code/controllers/subsystems/processing/odyssey.dm
index 442f3393efc..6e242bc7f0b 100644
--- a/code/controllers/subsystems/processing/odyssey.dm
+++ b/code/controllers/subsystems/processing/odyssey.dm
@@ -79,6 +79,8 @@ SUBSYSTEM_DEF(odyssey)
setup_scenario_variables()
var/list/possible_station_levels = SSmapping.levels_by_all_traits(list(ZTRAIT_STATION))
main_map = GLOB.map_sectors["[pick(possible_station_levels)]"]
+ if(!SSticker.round_canon_admin_forced)
+ SSticker.set_round_canon(scenario.scenario_canonicity_type, TRUE)
// Now that we actually have an odyssey, the subsystem can fire!
can_fire = TRUE
@@ -95,13 +97,13 @@ SUBSYSTEM_DEF(odyssey)
ody_gamemode.required_players = scenario.min_player_amount
ody_gamemode.required_enemies = scenario.min_actor_amount
- //Setting the scenario_type variable for use here in UI info and chat notices.
- if(!length(scenario.possible_scenario_types))
- scenario.scenario_type = SCENARIO_TYPE_NONCANON
+ //Setting the scenario_canonicity_type variable for use here in UI info and chat notices.
+ if(!length(scenario.possible_scenario_canonicity_types))
+ scenario.scenario_canonicity_type = /singleton/canonicity/limited
else if(SSatlas.current_sector in ALL_EVENT_ONLY_SECTORS) // If we are in an exclusive event area for an arc (EG. The Horizon finds itself isolated and alone), we may not want canon odysseys spawning.
- scenario.scenario_type = SCENARIO_TYPE_NONCANON // Noncanon odysseys are fine though!
+ scenario.scenario_canonicity_type = /singleton/canonicity/limited // Noncanon odysseys are fine though!
else
- scenario.scenario_type = pick(scenario.possible_scenario_types)
+ scenario.scenario_canonicity_type = pick(scenario.possible_scenario_canonicity_types)
site_landing_restricted = scenario.site_landing_restricted
@@ -153,7 +155,7 @@ SUBSYSTEM_DEF(odyssey)
if(scenario)
data["scenario_name"] = SSodyssey.scenario.name
data["scenario_desc"] = SSodyssey.scenario.desc
- data["scenario_canonicity"] = SSodyssey.scenario.scenario_type == SCENARIO_TYPE_CANON ? "Canon" : "Non-Canon"
+ data["scenario_canonicity"] = SSticker.round_canon.name
data["is_storyteller"] = isstoryteller(user) || check_rights(R_ADMIN, FALSE, user)
if(length(scenario.roles))
diff --git a/code/controllers/subsystems/statpanel.dm b/code/controllers/subsystems/statpanel.dm
index 575d914a0db..c2d01563ea2 100644
--- a/code/controllers/subsystems/statpanel.dm
+++ b/code/controllers/subsystems/statpanel.dm
@@ -36,6 +36,14 @@ SUBSYSTEM_DEF(statpanels)
"Last Transfer Vote: [GLOB.last_transfer_vote ? time2text(GLOB.last_transfer_vote, "hh:mm") : "Never"]",
"Next Port Visit: [SSatlas.current_sector.next_port_visit_string]"
)
+
+ if(istype(SSticker.round_canon))
+ global_data[++global_data.len] = list(
+ "Round Canon: ",
+ "[SSticker.round_canon.name]",
+ "src=[REF(src)];open_canon_panel=1"
+ )
+
if(eta_status)
global_data += eta_status
@@ -100,6 +108,10 @@ SUBSYSTEM_DEF(statpanels)
if(MC_TICK_CHECK)
return
+/datum/controller/subsystem/statpanels/Topic(href, href_list)
+ if(href_list["open_canon_panel"])
+ SSticker.round_canon.ui_interact(usr)
+
/datum/controller/subsystem/statpanels/proc/set_status_tab(client/target)
if(!global_data)//statbrowser hasnt fired yet and we were called from immediate_send_stat_data()
var/list/preliminary_stats = list("The server is initializing...")
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index abe7e493ffe..b4de60f1a38 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -71,6 +71,11 @@ SUBSYSTEM_DEF(ticker)
var/total_players_ready = 0
var/list/ready_player_jobs
+ /// The round canonicity. Set by either admins or by the gamemode.
+ var/singleton/canonicity/round_canon
+ /// Round canon forced by admins in the lobby; prevents gamemodes from overriding.
+ var/round_canon_admin_forced = FALSE
+
/datum/controller/subsystem/ticker/Initialize(timeofday)
pregame()
restart_timeout = GLOB.config.restart_timeout
@@ -808,6 +813,18 @@ SUBSYSTEM_DEF(ticker)
return TRUE
. = ..()
+/datum/controller/subsystem/ticker/proc/set_round_canon(canon_type, pre_game = FALSE, announce = FALSE)
+ round_canon = GET_SINGLETON(canon_type)
+ if(!istype(round_canon))
+ round_canon = GET_SINGLETON(/singleton/canonicity/limited)
+
+ if(pre_game)
+ round_canon.pre_game_setup()
+
+ if(announce)
+ var/announcement = SPAN_NOTICE("The round canonicity has been set to [SPAN_DANGER(round_canon.name)].
For more information, press the [round_canon.name] button in your Status panel.")
+ to_world(EXAMINE_BLOCK_ODYSSEY(FONT_LARGE(announcement)))
+
#undef SETUP_OK
#undef SETUP_REVOTE
#undef SETUP_REATTEMPT
diff --git a/code/datums/round_canonicity.dm b/code/datums/round_canonicity.dm
new file mode 100644
index 00000000000..9c5587d76ae
--- /dev/null
+++ b/code/datums/round_canonicity.dm
@@ -0,0 +1,162 @@
+/singleton/canonicity
+ /// This is the name of the canonicity type.
+ var/name = "Round Canonicity"
+ /// The description. Should fill in the users about the details of the round canonicity.
+ var/desc = "This is a base type, report this to a dev if it somehow happens to be used."
+ /// If the round itself is canon or not. Non-canon rounds are things like non-canon events, where everything is "forgotten" the next round.
+ var/round_canon
+ /// If antagonist actions are canon during this round.
+ var/antagonist_actions_canon
+ /// If character deaths are canon during this round.
+ var/character_death_canon
+ /// If away sites are canon or not. It's a bit more complex than just that; see the relevant defines.
+ var/away_site_canon
+ /// If offships are canon or not. It's a bit more complex than just that; see the relevant defines.
+ var/offship_canon
+
+/**
+ * This proc is automatically called on /datum/game_mode/proc/pre_game_setup().
+ * See that proc for details.
+ */
+/singleton/canonicity/proc/pre_game_setup()
+ return
+
+/**
+ * Called when someone clicks on the stat panel entry to open the canon panel TGUI.
+ */
+/singleton/canonicity/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CanonPanel", "Canon Panel")
+ ui.open()
+
+/singleton/canonicity/ui_state(mob/user)
+ return GLOB.always_state
+
+/singleton/canonicity/ui_status(mob/user, datum/ui_state/state)
+ return UI_INTERACTIVE
+
+/singleton/canonicity/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["name"] = name
+ data["desc"] = desc
+
+ data["round_canon_info"] = round_canon_info()
+ data["antagonist_actions_canon_info"] = antagonist_actions_canon_info()
+ data["character_death_canon_info"] = character_death_canon_info()
+ data["away_site_canon_info"] = away_site_canon_info()
+ data["offship_canon_info"] = offship_canon_info()
+
+ data["is_storyteller"] = isstoryteller(user)
+ data["is_admin"] = check_rights(R_ADMIN, FALSE, user)
+
+ return data
+
+/singleton/canonicity/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ switch(action)
+ if("edit_round_canon_type")
+ var/singleton/canonicity/canon_types = GET_SINGLETON_SUBTYPE_LIST(/singleton/canonicity)
+ var/singleton/canonicity/selected_canon = tgui_input_list(ui.user, "Select a new canonicity for this round.", "Canon Panel", canon_types)
+ if(istype(selected_canon))
+ var/changer = ui.user
+ SSticker.set_round_canon(selected_canon.type, FALSE, TRUE)
+ SStgui.close_all_uis(src)
+ message_admins(SPAN_DANGER("[changer] has changed the round canonicity to [SSticker.round_canon.name]."))
+
+/singleton/canonicity/proc/round_canon_info()
+ . = list()
+ switch(round_canon)
+ if(ROUND_FULL_CANON)
+ . += "This round is canon. All actions taken by non-antagonists during this round are considered canon."
+ if(ROUND_NON_CANON)
+ . += "This round is non-canon. All actions taken during this round are considered non-canon."
+
+/singleton/canonicity/proc/antagonist_actions_canon_info()
+ . = list()
+ switch(antagonist_actions_canon)
+ if(ANTAGONIST_ACTIONS_NOT_EXPECTED)
+ . += "Antagonist actions are not expected during this round."
+ if(ANTAGONIST_ACTIONS_CANON)
+ . += "Antagonist actions are canon during this round. This includes event antagonists."
+ if(ANTAGONIST_ACTIONS_NOT_CANON)
+ . += "Antagonist actions are not canon during this round."
+ . += "This includes actions by non-antagonists directly influenced by antagonists."
+
+/singleton/canonicity/proc/character_death_canon_info()
+ . = list()
+ switch(character_death_canon)
+ if(LIMITED_CHARACTER_DEATH)
+ . += "Character deaths are limited during this round. If all involved parties agree, character deaths can be retconned. If you would like to contest a death, or determine if a party counts as involved, please adminhelp."
+ . += "To count as involved, a player has to be an active participant of your death, meaning that they must have intentionally contributed to it. As an example, if you die to a carp on an expedition, you can retcon this death without asking others."
+ if(FORCED_CHARACTER_DEATH)
+ . += "Character deaths are forced canon during this round. All character deaths must go through headmins and loremasters to be retconned, no exceptions!"
+ if(NO_CHARACTER_DEATH)
+ . += "Character deaths are not canon during this round."
+
+/singleton/canonicity/proc/away_site_canon_info()
+ . = list()
+ switch(away_site_canon)
+ if(AWAY_SITE_NOT_CANON)
+ . += "Away sites are not canon this round."
+ if(AWAY_SITE_CANON_LIMITED)
+ . += "Away site canon is limited. This means that while it is canon that you go to an away site, the exact details of where you went are not. You can say that you went to a greimorian infested station, for example, \
+ but you cannot remember the same away site across rounds."
+ if(AWAY_SITE_CANON_FULL)
+ . += "Away sites are canon, including the exact details."
+
+/singleton/canonicity/proc/offship_canon_info()
+ . = list()
+ switch(offship_canon)
+ if(OFFSHIP_NOT_CANON)
+ . += "Offship actions are not canon during this round."
+ if(OFFSHIP_CANON_LIMITED)
+ . += "Offship action canon is limited. This means that offship actions are canon, barring hostile actions taken against the [SSatlas.current_map.full_name]. If anything is unclear, make sure to adminhelp."
+ if(OFFSHIP_CANON_FULL)
+ . += "Offship actions are canon during this round."
+
+/singleton/canonicity/extended
+ name = "Extended Canon"
+ desc = "This type of canonicity is in place for Extended rounds or canon events."
+ round_canon = ROUND_FULL_CANON
+ antagonist_actions_canon = ANTAGONIST_ACTIONS_NOT_EXPECTED
+ character_death_canon = LIMITED_CHARACTER_DEATH
+ away_site_canon = AWAY_SITE_CANON_LIMITED
+ offship_canon = OFFSHIP_CANON_LIMITED
+
+/singleton/canonicity/odyssey
+ name = "Odyssey Canon"
+ desc = "This type of canonicity is in place for Odyssey rounds, where antagonists are present and their actions are considered canon."
+ round_canon = ROUND_FULL_CANON
+ antagonist_actions_canon = ANTAGONIST_ACTIONS_CANON
+ character_death_canon = LIMITED_CHARACTER_DEATH
+ away_site_canon = AWAY_SITE_CANON_LIMITED
+ offship_canon = OFFSHIP_CANON_LIMITED
+
+/singleton/canonicity/limited
+ name = "Limited Canon"
+ desc = "This type of canonicity is in place for Secret rounds or Odysseys where the antagonist actions are not considered canon."
+ round_canon = ROUND_FULL_CANON
+ antagonist_actions_canon = ANTAGONIST_ACTIONS_NOT_CANON
+ character_death_canon = LIMITED_CHARACTER_DEATH
+ away_site_canon = AWAY_SITE_CANON_LIMITED
+ offship_canon = OFFSHIP_CANON_LIMITED
+
+/singleton/canonicity/canon_event
+ name = "Full Canon"
+ desc = "This type of canonicity is in place for canon event rounds."
+ round_canon = ROUND_FULL_CANON
+ antagonist_actions_canon = ANTAGONIST_ACTIONS_CANON
+ character_death_canon = FORCED_CHARACTER_DEATH
+ away_site_canon = AWAY_SITE_CANON_FULL
+ offship_canon = OFFSHIP_CANON_FULL
+
+/singleton/canonicity/non_canon_event
+ name = "Non-Canon Event"
+ desc = "This type of canonicity is in place for non-canon events."
+ round_canon = ROUND_NON_CANON
+ antagonist_actions_canon = ANTAGONIST_ACTIONS_NOT_CANON
+ character_death_canon = NO_CHARACTER_DEATH
+ away_site_canon = AWAY_SITE_NOT_CANON
+ offship_canon = OFFSHIP_NOT_CANON
diff --git a/code/datums/scenarios/scenario.dm b/code/datums/scenarios/scenario.dm
index ebe1355ece1..318fe989d3f 100644
--- a/code/datums/scenarios/scenario.dm
+++ b/code/datums/scenarios/scenario.dm
@@ -5,10 +5,10 @@
var/desc = "A generic scenario that should not be in the rotation."
/// What sectors this scenario can spawn in. An empty list is all sectors.
var/list/sector_whitelist = list()
- /// The type of scenario this is. NOT a boolean or a bitfield. This is automatically set on /datum/controller/subsystem/odyssey/proc/setup_scenario_variables().
- var/scenario_type
- /// A list containing the options for type of scenario. An empty list will default to SCENARIO_TYPE_NONCANON.
- var/list/possible_scenario_types = list()
+ /// The canonicity of the scenario. Subtype of /singleton/canonicity. This is automatically set on /datum/controller/subsystem/odyssey/proc/setup_scenario_variables().
+ var/scenario_canonicity_type
+ /// A list containing the options for type of scenario. An empty list will default to /singleton/canonicity/limited.
+ var/list/possible_scenario_canonicity_types = list()
/// Whether or not landing on the Odyssey away site is restricted by default. Has to be either unrestricted by Storytellers manually, or happens forcefully at 40 minutes in.
var/site_landing_restricted = TRUE
diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm
index 506320196ce..7c44e39056b 100644
--- a/code/game/gamemodes/extended/extended.dm
+++ b/code/game/gamemodes/extended/extended.dm
@@ -4,3 +4,4 @@
required_players = 0
round_description = "Just have fun and role-play!"
extended_round_description = "There are no antagonists during extended, unless an admin decides to be cheeky. Just play your character, mess around with your job, and have fun."
+ canon_type = /singleton/canonicity/extended
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index b6c2aa88a30..6481c72ea11 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -34,12 +34,18 @@ GLOBAL_LIST_EMPTY(additional_antag_types)
var/event_delay_mod_moderate // Modifies the timing of random events.
var/event_delay_mod_major // As above.
+ /// The canon type of this gamemode. THIS SHOULD ALWAYS BE SET.
+ var/canon_type = /singleton/canonicity/limited
+
/datum/game_mode/New()
..()
// Enforce some formatting.
// This will probably break something.
name = capitalize(lowertext(name))
config_tag = lowertext(config_tag)
+ if(!canon_type)
+ message_admins("Gamemode [name] has no canon_type set! Defaulting to limited canon.")
+ canon_type = /singleton/canonicity/limited
/datum/game_mode/Topic(href, href_list[])
if(..())
@@ -498,6 +504,8 @@ GLOBAL_LIST_EMPTY(additional_antag_types)
* This proc should always return TRUE on a success and FALSE if something went wrong, so that if the initialization failed, the game can reset to lobby state.
*/
/datum/game_mode/proc/pre_game_setup()
+ if(!SSticker.round_canon_admin_forced)
+ SSticker.set_round_canon(canon_type, TRUE)
return TRUE
//////////////////////////
diff --git a/code/game/gamemodes/odyssey/odyssey.dm b/code/game/gamemodes/odyssey/odyssey.dm
index 545369992df..ea2e3a9017c 100644
--- a/code/game/gamemodes/odyssey/odyssey.dm
+++ b/code/game/gamemodes/odyssey/odyssey.dm
@@ -16,10 +16,9 @@
return FALSE
var/odyssey_message = "The scenario picked for this round is: [SPAN_BOLD(SPAN_NOTICE(SSodyssey.scenario.name))].
\
- It is a [SPAN_BOLD(SSodyssey.scenario.scenario_type)] scenario.
"
+ It is a [SPAN_BOLD(SSticker.round_canon.name)] scenario.
"
- if(SSodyssey.scenario.scenario_type == SCENARIO_TYPE_CANON)
- odyssey_message += SPAN_DANGER("A Canon Odyssey scenario follows the same rules as Extended canonicity, meaning that character deaths may be retconned if all parties agree. Adminhelp or refer to the rules for more information.
")
+ odyssey_message += SPAN_DANGER("For information on the canonicity of the scenario, refer to the Canon Panel by pressing the [SSticker.round_canon.name] button in your Status panel. Remember that Storytellers and Admins may change this at will.
")
odyssey_message += "Please keep in mind that the Storyteller and the Actors may alter the story as they see fit, and remember to go along with what they have planned!"
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 82876c1d459..ef57f1f2bd5 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -1302,7 +1302,11 @@ var/global/enabled_spooking = 0
var/datum/antagonist/antag = GLOB.all_antag_types[antag_type]
message_admins("[key_name(usr)] attempting to force latespawn with template [antag.id].")
- antag.attempt_auto_spawn()
+ if(antag.attempt_auto_spawn())
+ if(SSticker.round_canon.antagonist_actions_canon == ANTAGONIST_ACTIONS_NOT_EXPECTED)
+ var/change_canon = tgui_alert(usr, "You have added antagonists to a gamemode type that does not expect antagonists. Would you like to also change the round canon to Limited?", "Canon Change", list("Yes", "No"))
+ if(change_canon == "Yes")
+ SSticker.set_round_canon(/singleton/canonicity/limited)
/datum/admins/proc/force_mode_latespawn()
set category = "Admin"
@@ -1322,6 +1326,28 @@ var/global/enabled_spooking = 0
log_and_message_admins("attempting to force mode autospawn.")
SSticker.mode.process_autoantag()
+/datum/admins/proc/force_round_canon_pregame()
+ set category = "Admin"
+ set name = "Set Pre-Game Round Canon"
+
+ if (!istype(src,/datum/admins))
+ src = usr.client.holder
+ if (!istype(src,/datum/admins) || !check_rights(R_ADMIN))
+ to_chat(usr, "Error: you are not an admin!")
+ return
+
+ if(SSticker.current_state == GAME_STATE_PLAYING)
+ to_chat(usr, SPAN_WARNING("You cannot use this verb after the round has started! Use the Canon Panel from the Status panel."))
+ return
+
+ var/singleton/canonicity/canon_type = tgui_input_list(usr, "Choose a round canon type.", "Force Round Canon", GET_SINGLETON_SUBTYPE_LIST(/singleton/canonicity))
+ if(!istype(canon_type))
+ return
+
+ SSticker.set_round_canon(canon_type, TRUE, TRUE)
+ SSticker.round_canon_admin_forced = TRUE
+ log_and_message_admins("forced the round canon to [canon_type.name].")
+
/datum/admins/proc/paralyze_mob(mob/living/H as mob)
set category = "Admin"
set name = "Toggle Winded"
@@ -1434,26 +1460,6 @@ var/global/enabled_spooking = 0
log_and_message_admins("has manually set the Odyssey to [chosen_scenario.name]", usr)
feedback_add_details("admin_verb","SEOT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/admins/proc/set_odyssey_canonicity()
- set name = "Set Odyssey Canonicity"
- set category = "Special Verbs"
-
- if(!check_rights(R_ADMIN))
- return
-
- if(!SSodyssey.scenario)
- to_chat(usr, SPAN_WARNING("There needs to be an Odyssey selected first! Use the Set Odyssey Type verb."))
- return
-
- var/canonicity = tgui_input_list(usr, "Set the Odyssey canonicity.", "Set Odyssey Canonicity", list(SCENARIO_TYPE_CANON, SCENARIO_TYPE_NONCANON))
- if(!canonicity)
- return
-
- SSodyssey.scenario.scenario_type = canonicity
- to_world(FONT_LARGE(EXAMINE_BLOCK_ODYSSEY(SPAN_NOTICE("The scenario canonicity has been changed to [SPAN_BOLD(canonicity)] by an administrator."))))
- log_and_message_admins("has set the Odyssey canonicity to [canonicity]", usr)
- feedback_add_details("admin_verb","SEOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
/datum/admins/proc/open_narrate_panel()
set category = "Special Verbs.Narration/Messaging"
set name = "Narrate Panel"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 616f925493d..ccd6100a6bf 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -18,6 +18,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
// /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage */
/datum/admins/proc/show_game_mode, /*Configuration window for the current game mode.*/
/datum/admins/proc/force_mode_latespawn, /*Force the mode to try a latespawn proc*/
+ /datum/admins/proc/force_round_canon_pregame,
/datum/admins/proc/force_antag_latespawn, /*Force a specific template to try a latespawn proc*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
/datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/
@@ -149,7 +150,6 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/apply_sunstate,
/datum/admins/proc/ccannoucment,
/datum/admins/proc/set_odyssey,
- /datum/admins/proc/set_odyssey_canonicity,
/datum/admins/proc/toggle_persistence
))
@@ -332,6 +332,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/colorooc,
/client/proc/add_client_color,
/datum/admins/proc/force_mode_latespawn,
+ /datum/admins/proc/force_round_canon_pregame,
/datum/admins/proc/toggleenter,
/datum/admins/proc/toggleguests,
/datum/admins/proc/capture_map_part,
@@ -413,7 +414,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/cmd_display_harddel_log,
/datum/admins/proc/ccannoucment,
/datum/admins/proc/set_odyssey,
- /datum/admins/proc/set_odyssey_canonicity,
/client/proc/cmd_display_init_log,
/client/proc/cmd_generate_lag,
/client/proc/toggledebuglogs,
diff --git a/html/changelogs/mattatlas-canonicity.yml b/html/changelogs/mattatlas-canonicity.yml
new file mode 100644
index 00000000000..9893205f486
--- /dev/null
+++ b/html/changelogs/mattatlas-canonicity.yml
@@ -0,0 +1,59 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# - (fixes bugs)
+# wip
+# - (work in progress)
+# qol
+# - (quality of life)
+# soundadd
+# - (adds a sound)
+# sounddel
+# - (removes a sound)
+# rscadd
+# - (adds a feature)
+# rscdel
+# - (removes a feature)
+# imageadd
+# - (adds an image or sprite)
+# imagedel
+# - (removes an image or sprite)
+# spellcheck
+# - (fixes spelling or grammar)
+# experiment
+# - (experimental change)
+# balance
+# - (balance changes)
+# code_imp
+# - (misc internal code change)
+# refactor
+# - (refactors code)
+# config
+# - (makes a change to the config files)
+# admin
+# - (makes changes to administrator tools)
+# server
+# - (miscellaneous changes to server)
+#################################
+
+# Your name.
+author: MattAtlas
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
+# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "All gamemodes and Odysseys now have a defined round canonicity. You can now view canonicity status for the current round in the Status panel, and get more information by clicking the round canon button. Admins and Storytellers can modify this."
+ - rscadd: "Storytellers can now change round canonicity in any round they are part of. Do this through the Canon panel."
diff --git a/maps/away/scenarios/decrepit_shipyard/decrepit_shipyard_scenario.dm b/maps/away/scenarios/decrepit_shipyard/decrepit_shipyard_scenario.dm
index df27574fdb1..a0bef42aba3 100644
--- a/maps/away/scenarios/decrepit_shipyard/decrepit_shipyard_scenario.dm
+++ b/maps/away/scenarios/decrepit_shipyard/decrepit_shipyard_scenario.dm
@@ -5,7 +5,7 @@
SCCV Horizon, one of the few vessels within the perimeter capable of responding, was dispatched to investigate further. \
"
- possible_scenario_types = list(SCENARIO_TYPE_NONCANON, SCENARIO_TYPE_CANON)
+ possible_scenario_canonicity_types = list(/singleton/canonicity/odyssey, /singleton/canonicity/limited)
scenario_site_id = "decrepit_shipyard"
diff --git a/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_scenario.dm b/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_scenario.dm
index ec80a1516df..34bedab00c2 100644
--- a/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_scenario.dm
+++ b/maps/away/scenarios/enviro_testing_facility/enviro_testing_facility_scenario.dm
@@ -4,7 +4,7 @@
SCCV Horizon, the closest ship in this sector, was dispatched to investigate."
scenario_site_id = "enviro_testing_facility"
- possible_scenario_types = list(SCENARIO_TYPE_NONCANON, SCENARIO_TYPE_CANON)
+ possible_scenario_canonicity_types = list(/singleton/canonicity/odyssey, /singleton/canonicity/limited)
min_player_amount = 0
min_actor_amount = 0
diff --git a/maps/away/scenarios/ruined_propellant_depot/ruined_propellant_depot_scenario.dm b/maps/away/scenarios/ruined_propellant_depot/ruined_propellant_depot_scenario.dm
index 5e63ec24da6..09f2cad2f47 100644
--- a/maps/away/scenarios/ruined_propellant_depot/ruined_propellant_depot_scenario.dm
+++ b/maps/away/scenarios/ruined_propellant_depot/ruined_propellant_depot_scenario.dm
@@ -5,7 +5,7 @@
SCCV Horizon, the closest ship in this sector, was sent by CC to investigate. \
"
- possible_scenario_types = list(SCENARIO_TYPE_NONCANON, SCENARIO_TYPE_CANON)
+ possible_scenario_canonicity_types = list(/singleton/canonicity/odyssey, /singleton/canonicity/limited)
scenario_site_id = "ruined_propellant_depot"
diff --git a/tgui/packages/tgui/interfaces/CanonPanel.tsx b/tgui/packages/tgui/interfaces/CanonPanel.tsx
new file mode 100644
index 00000000000..777f386e7bb
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CanonPanel.tsx
@@ -0,0 +1,97 @@
+import {
+ Box,
+ Button,
+ LabeledList,
+ NoticeBox,
+ Section,
+} from 'tgui-core/components';
+import type { BooleanLike } from 'tgui-core/react';
+import { useBackend } from '../backend';
+import { Window } from '../layouts';
+
+export type CanonData = {
+ name: string;
+ desc: string;
+
+ round_canon_info: string[];
+ antagonist_actions_canon_info: string[];
+ character_death_canon_info: string[];
+ away_site_canon_info: string[];
+ offship_canon_info: string[];
+
+ is_storyteller: BooleanLike;
+ is_admin: BooleanLike;
+};
+
+export const CanonPanel = (props) => {
+ const { act, data } = useBackend();
+ return (
+
+
+ act('edit_round_canon_type')}
+ />
+ ) : undefined
+ }
+ >
+
+ This round's canonicity is {data.name}.
+
+ {data.desc}
+
+
+ {data.round_canon_info.map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
+ {data.antagonist_actions_canon_info.map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
+ {data.character_death_canon_info.map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
+
+ {data.away_site_canon_info.map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
+ {data.offship_canon_info.map((info, index) => (
+
+ {info}
+
+ ))}
+
+
+
+
+ );
+};