From 45a7c68da19d454798d3e933b251e005e680e68f Mon Sep 17 00:00:00 2001
From: silicons <2003111+silicons@users.noreply.github.com>
Date: Fri, 11 Dec 2020 15:05:00 -0700
Subject: [PATCH] wow
---
code/__DEFINES/role_preferences.dm | 1 +
.../configuration/entries/general.dm | 2 -
.../configuration/entries/respawns.dm | 24 ++++++++
code/controllers/subsystem/ticker.dm | 5 ++
code/modules/admin/topic.dm | 6 ++
code/modules/client/preferences.dm | 16 ++++-
code/modules/jobs/job_types/_job.dm | 12 ++++
code/modules/jobs/job_types/ai.dm | 1 +
code/modules/jobs/job_types/assistant.dm | 1 +
code/modules/jobs/job_types/captain.dm | 1 +
code/modules/jobs/job_types/chief_engineer.dm | 1 +
.../jobs/job_types/chief_medical_officer.dm | 1 +
code/modules/jobs/job_types/cyborg.dm | 1 +
code/modules/jobs/job_types/detective.dm | 1 +
.../jobs/job_types/head_of_personnel.dm | 2 +-
.../jobs/job_types/head_of_security.dm | 1 +
code/modules/jobs/job_types/quartermaster.dm | 1 +
.../jobs/job_types/research_director.dm | 1 +
.../jobs/job_types/security_officer.dm | 1 +
code/modules/jobs/job_types/warden.dm | 1 +
.../modules/mob/dead/new_player/new_player.dm | 5 ++
code/modules/mob/dead/observer/respawn.dm | 60 +++++++++++++++++++
code/modules/mob/mob.dm | 33 ----------
config/config.txt | 4 +-
config/respawns.txt | 17 ++++++
tgstation.dme | 2 +
26 files changed, 160 insertions(+), 41 deletions(-)
create mode 100644 code/controllers/configuration/entries/respawns.dm
create mode 100644 code/modules/mob/dead/observer/respawn.dm
create mode 100644 config/respawns.txt
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index d7487eb54b..605b3f6485 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -40,6 +40,7 @@
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
#define ROLE_GHOSTCAFE "ghostcafe"
#define ROLE_MINOR_ANTAG "minorantag"
+#define ROLE_RESPAWN "respawnsystem"
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
//(in game days played) to play that role
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 90ec3bc289..3c93952b65 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -172,8 +172,6 @@
/datum/config_entry/string/hostedby
-/datum/config_entry/flag/norespawn
-
/datum/config_entry/flag/guest_jobban
/datum/config_entry/flag/usewhitelist
diff --git a/code/controllers/configuration/entries/respawns.dm b/code/controllers/configuration/entries/respawns.dm
new file mode 100644
index 0000000000..b19c8bf9d8
--- /dev/null
+++ b/code/controllers/configuration/entries/respawns.dm
@@ -0,0 +1,24 @@
+/// Allows usage of respawn system
+/datum/config_entry/flag/respawns_enabled
+ config_entry_value = FALSE
+
+/// Minutes before allowing respawns.
+/datum/config_entry/number/respawn_delay
+ config_entry_value = 15.0
+ integer = FALSE
+
+/// Allows respawning as non-assistant. Overrides all others of this type.
+/datum/config_entry/flag/allow_non_assistant_respawn
+ config_entry_value = FALSE
+
+/// Allows respawning as a combat role, defined as security/head.
+/datum/config_entry/flag/allow_combat_role_respawn
+ config_entry_value = FALSE
+
+/// Allows respawning as the same character as a previous life
+/datum/config_entry/flag/allow_same_character_respawn
+ config_entry_value = FALSE
+
+/// Observing penalizes for respawns, not just joining.
+/datum/config_entry/flag/respawn_penalty_includes_observe
+ config_entry_value = FALSE
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 95d8928368..730ee3d6ab 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -371,6 +371,11 @@ SUBSYSTEM_DEF(ticker)
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
GLOB.joined_player_list += player.ckey
player.create_character(FALSE)
+ if(player.new_character && player.client && player.client.prefs) // we cannot afford a runtime, ever
+ LAZYOR(player.client.prefs.slots_joined_as, player.client.prefs.slot)
+ LAZYOR(player.client.prefs.characters_joined_as, player.new_character.real_name))
+ else
+ stack_trace("WARNING: Either a player did not have a new_character, did not have a client, or did not have preferences. This is VERY bad.")
else
player.new_player_panel()
CHECK_TICK
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index ad8dd168eb..fd8204a296 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -936,6 +936,12 @@
else
dat += "
Mind Transfer Potion | "
+ //Respawns
+ if(jobban_isbanned(M, ROLE_RESPAWN))
+ dat += "Respawns | "
+ else
+ dat += "Respawns | "
+
dat += ""
usr << browse(dat, "window=jobban2;size=800x450")
return
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 16d6ca351d..dddb78a729 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -12,11 +12,23 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
var/max_save_slots = 24
- //non-preference stuff
- var/muted = 0
+ // Intra-round persistence begin
+ /// Flags for admin mutes
+ var/muted = NONE
+ /// Last IP the person was seen on
var/last_ip
+ /// Last CID the person was seen on
var/last_id
+ /// Do we log their clicks to disk?
var/log_clicks = FALSE
+ /// Characters they have joined the round under - Lazylist of names
+ var/list/characters_joined_as
+ /// Slots they have joined the round under - Lazylist of numbers
+ var/list/slots_joined_as
+ /// Are we currently subject to respawn restrictions? Usually set by us using the "respawn" verb, but can be lifted by admins.
+ var/respawn_restrictions_active = FALSE
+
+ // Intra-round persistence end
var/icon/custom_holoform_icon
var/list/cached_holoform_icons
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index b0ac5248c1..bb55ad45ca 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -73,6 +73,12 @@
/// Starting skill modifiers.
var/list/starting_modifiers
+ // These can be flags but I don't care because they're never changed
+ /// Can you always join as this job even while respawning (should probably only be on for assistant)
+ var/always_can_respawn_as = FALSE
+ /// Is this job considered a combat role for respawning? (usually sec/command)
+ var/considered_combat_role = FALSE
+
/**
* Checks if we should be created on a certain map
*/
@@ -118,6 +124,12 @@
//Used for a special check of whether to allow a client to latejoin as this job.
/datum/job/proc/special_check_latejoin(client/C)
+ var/joined = LAZYLEN(C.prefs?.characters_joined_as)
+ if(C.prefs?.respawn_restrictions_active && (joined || CONFIG_GET(flag/respawn_penalty_includes_observe)))
+ if(!CONFIG_GET(flag/allow_non_assistant_respawn) && always_can_respawn_as)
+ return FALSE
+ if(!CONFIG_GET(flag/allow_combat_role_respawn) && considered_combat_role)
+ return FALSE
return TRUE
/datum/job/proc/GetAntagRep()
diff --git a/code/modules/jobs/job_types/ai.dm b/code/modules/jobs/job_types/ai.dm
index a7401791ab..d0eb690e94 100644
--- a/code/modules/jobs/job_types/ai.dm
+++ b/code/modules/jobs/job_types/ai.dm
@@ -16,6 +16,7 @@
display_order = JOB_DISPLAY_ORDER_AI
var/do_special_check = TRUE
threat = 5
+ considered_combat_role = TRUE
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
diff --git a/code/modules/jobs/job_types/assistant.dm b/code/modules/jobs/job_types/assistant.dm
index b8fc963989..db5390f323 100644
--- a/code/modules/jobs/job_types/assistant.dm
+++ b/code/modules/jobs/job_types/assistant.dm
@@ -18,6 +18,7 @@ Assistant
paycheck_department = ACCOUNT_CIV
display_order = JOB_DISPLAY_ORDER_ASSISTANT
dresscodecompliant = FALSE
+ always_can_respawn_as = TRUE
threat = 0.2
/datum/job/assistant/get_access()
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 047a07062d..4806bf5546 100644
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -14,6 +14,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_COMMAND
exp_type_department = EXP_TYPE_COMMAND
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/captain
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index 18be8c9835..902be0bdc8 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_ENGINEERING
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/ce
plasma_outfit = /datum/outfit/plasmaman/ce
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index 627a7a2ca1..bb5fc68809 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_MEDICAL
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/cmo
plasma_outfit = /datum/outfit/plasmaman/cmo
diff --git a/code/modules/jobs/job_types/cyborg.dm b/code/modules/jobs/job_types/cyborg.dm
index 4f74542b2a..761882894f 100644
--- a/code/modules/jobs/job_types/cyborg.dm
+++ b/code/modules/jobs/job_types/cyborg.dm
@@ -11,6 +11,7 @@
minimal_player_age = 21
exp_requirements = 120
exp_type = EXP_TYPE_CREW
+ considered_combat_role = TRUE
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 65724765e1..c704326879 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -15,6 +15,7 @@
outfit = /datum/outfit/job/detective
plasma_outfit = /datum/outfit/plasmaman/detective
+ considered_combat_role = TRUE
access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_BRIG, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM)
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 41fb4b99da..f1b7d3e8c4 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -15,7 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SERVICE
-
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/hop
plasma_outfit = /datum/outfit/plasmaman/hop
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index cfd8d7f6c0..c772a8acae 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -14,6 +14,7 @@
minimal_player_age = 10
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ considered_combat_role = TRUE
exp_type_department = EXP_TYPE_SECURITY
outfit = /datum/outfit/job/hos
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 4c6b8e064f..301acff5c4 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -15,6 +15,7 @@
exp_requirements = 180
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SUPPLY
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/quartermaster
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index 33f7df8260..6c2cb94d13 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -15,6 +15,7 @@
exp_type_department = EXP_TYPE_SCIENCE
exp_requirements = 180
exp_type = EXP_TYPE_CREW
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/rd
plasma_outfit = /datum/outfit/plasmaman/rd
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index bc83eb752d..bdae7fe028 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -12,6 +12,7 @@
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/security
plasma_outfit = /datum/outfit/plasmaman/security
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index c909342d6f..dae3094ebe 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -12,6 +12,7 @@
minimal_player_age = 7
exp_requirements = 300
exp_type = EXP_TYPE_CREW
+ considered_combat_role = TRUE
outfit = /datum/outfit/job/warden
plasma_outfit = /datum/outfit/plasmaman/warden
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 91123c0c7f..a06fee2cf7 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -382,6 +382,9 @@
alert(src, "An administrator has disabled late join spawning.")
return FALSE
+ if(!respawn_latejoin_check(notify = TRUE))
+ return FALSE
+
var/arrivals_docked = TRUE
if(SSshuttle.arrivals)
close_spawn_windows() //In case we get held up
@@ -445,6 +448,8 @@
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
+ LAZYOR(prefs.slots_joined_as, prefs.slot)
+ LAZYOR(prefs.characters_joined_as, character.real_name))
if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais.
if(SSshuttle.emergency)
diff --git a/code/modules/mob/dead/observer/respawn.dm b/code/modules/mob/dead/observer/respawn.dm
new file mode 100644
index 0000000000..d162e19531
--- /dev/null
+++ b/code/modules/mob/dead/observer/respawn.dm
@@ -0,0 +1,60 @@
+// ADMIN VERBS BEGIN
+/**
+ * Fully returns a player to lobby, allowing them to bypass all respawn restrictions
+ * Works on ghosts or new players (lobby players)
+ * If a lobby player is selected, their restrictions are removed.
+ */
+/client/proc/admin_cmd_respawn_return_to_lobby()
+
+/**
+ * Allows a ghost to bypass respawn delay without lifting respawn restrictions
+ */
+/client/proc/admin_cmd_remove_ghost_respawn_timer()
+
+// ADMIN VERBS END
+
+/**
+ * Checks if we can latejoin on the currently selected slot, taking into account respawn status.
+ */
+/mob/dead/new_player/proc/respawn_latejoin_check(notify = FALSE)
+
+/**
+ * Attempts to respawn.
+ */
+/mob/dead/observer/verb/respawn()
+ set name = "Respawn"
+ set category = "OOC"
+
+
+/mob/verb/abandon_mob()
+ set name = "Respawn"
+ set category = "OOC"
+
+ if (CONFIG_GET(flag/norespawn))
+ return
+ if ((stat != DEAD || !( SSticker )))
+ to_chat(usr, "You must be dead to use this!")
+ return
+
+ log_game("[key_name(usr)] used abandon mob.")
+
+ to_chat(usr, "Please roleplay correctly!")
+
+ if(!client)
+ log_game("[key_name(usr)] AM failed due to disconnect.")
+ return
+ client.screen.Cut()
+ client.screen += client.void
+ if(!client)
+ log_game("[key_name(usr)] AM failed due to disconnect.")
+ return
+
+ var/mob/dead/new_player/M = new /mob/dead/new_player()
+ if(!client)
+ log_game("[key_name(usr)] AM failed due to disconnect.")
+ qdel(M)
+ return
+
+ M.key = key
+// M.Login() //wat
+ return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 3416a8b337..bac80d7ae7 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -457,39 +457,6 @@
else
to_chat(src, "You don't have a mind datum for some reason, so you can't add a note to it.")
-/mob/verb/abandon_mob()
- set name = "Respawn"
- set category = "OOC"
-
- if (CONFIG_GET(flag/norespawn))
- return
- if ((stat != DEAD || !( SSticker )))
- to_chat(usr, "You must be dead to use this!")
- return
-
- log_game("[key_name(usr)] used abandon mob.")
-
- to_chat(usr, "Please roleplay correctly!")
-
- if(!client)
- log_game("[key_name(usr)] AM failed due to disconnect.")
- return
- client.screen.Cut()
- client.screen += client.void
- if(!client)
- log_game("[key_name(usr)] AM failed due to disconnect.")
- return
-
- var/mob/dead/new_player/M = new /mob/dead/new_player()
- if(!client)
- log_game("[key_name(usr)] AM failed due to disconnect.")
- qdel(M)
- return
-
- M.key = key
-// M.Login() //wat
- return
-
/mob/proc/transfer_ckey(mob/new_mob, send_signal = TRUE)
if(!new_mob || (!ckey && new_mob.ckey))
CRASH("transfer_ckey() called [new_mob ? "on ckey-less mob with a player mob as target" : "without a valid mob target"]!")
diff --git a/config/config.txt b/config/config.txt
index 5be76972ab..72df74beb1 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -11,6 +11,7 @@ $include dynamic_config.txt
$include plushies/defines.txt
$include job_threats.txt
$include policy.txt
+$include respawns.txt
# You can use the @ character at the beginning of a config option to lock it from being edited in-game
# Example usage:
@@ -206,9 +207,6 @@ VOTE_AUTOTRANSFER_MAXIMUM 4
## players' votes default to "No vote" (otherwise, default to "No change")
# DEFAULT_NO_VOTE
-## disable abandon mob
-NORESPAWN
-
## disables calling del(src) on newmobs if they logout before spawnin in
# DONT_DEL_NEWMOB
diff --git a/config/respawns.txt b/config/respawns.txt
new file mode 100644
index 0000000000..97b196135a
--- /dev/null
+++ b/config/respawns.txt
@@ -0,0 +1,17 @@
+## Allow usage of the respawn system
+RESPAWNS_ENABLED
+
+## Minutes delay before allowing respawns, either from death or observing. Not an integer.
+RESPAWN_DELAY 15.0
+
+## Allow respawning as anything but an assistant.
+# ALLOW_NON_ASSISTANT_RESPAWN
+
+## Allow respawning as security and command. Only works if ALLOW_NON_ASSISTANT_RESPAWN is on.
+# ALLOW_COMBAT_ROLE_RESPAWN
+
+## Allow respawning as the same character
+# ALLOW_SAME_CHARACTER_RESPAWN
+
+## Observing is considered a respawn for the purposes of role lockouts. Defaults to disabled. When disabled, only RESPAWNING rather than returning from observe locks you out.
+# RESPAWN_PENALTY_INCLUDES_OBSERVE
diff --git a/tgstation.dme b/tgstation.dme
index 1387748839..bdc384776c 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -294,6 +294,7 @@
#include "code\controllers\configuration\entries\plushies.dm"
#include "code\controllers\configuration\entries\policy.dm"
#include "code\controllers\configuration\entries\resources.dm"
+#include "code\controllers\configuration\entries\respawns.dm"
#include "code\controllers\configuration\entries\stamina_combat.dm"
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\adjacent_air.dm"
@@ -2451,6 +2452,7 @@
#include "code\modules\mob\dead\observer\observer.dm"
#include "code\modules\mob\dead\observer\observer_movement.dm"
#include "code\modules\mob\dead\observer\orbit.dm"
+#include "code\modules\mob\dead\observer\respawn.dm"
#include "code\modules\mob\dead\observer\say.dm"
#include "code\modules\mob\living\blood.dm"
#include "code\modules\mob\living\bloodcrawl.dm"