diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm
index 81965514fcb..477bed243c1 100644
--- a/code/__DEFINES/configuration.dm
+++ b/code/__DEFINES/configuration.dm
@@ -20,3 +20,11 @@
#define KEY_MODE_TEXT 0
#define KEY_MODE_TYPE 1
+
+// Flags for respawn config
+/// Respawn not allowed
+#define RESPAWN_FLAG_DISABLED 0
+/// Respawn as much as you'd like
+#define RESPAWN_FLAG_FREE 1
+/// Can respawn, but not as the same character
+#define RESPAWN_FLAG_NEW_CHARACTER 2
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index b5fc855da31..16791b7fd7e 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -262,10 +262,25 @@
/datum/config_entry/string/hostedby
-/datum/config_entry/flag/norespawn
+/// Determines if a player can respawn after dying.
+/// 0 / RESPAWN_FLAG_DISABLED = Cannot respawn (default)
+/// 1 / RESPAWN_FLAG_FREE = Can respawn
+/// 2 / RESPAWN_FLAG_NEW_CHARACTER = Can respawn if choosing a different character
+/datum/config_entry/flag/allow_respawn
+ default = RESPAWN_FLAG_DISABLED
+/datum/config_entry/flag/allow_respawn/ValidateAndSet(str_val)
+ if(!VASProcCallGuard(str_val))
+ return FALSE
+ var/val_as_num = text2num(str_val)
+ if(val_as_num in list(RESPAWN_FLAG_DISABLED, RESPAWN_FLAG_FREE, RESPAWN_FLAG_NEW_CHARACTER))
+ config_entry_value = val_as_num
+ return TRUE
+ return FALSE
+
+/// Determines how long (in deciseconds) before a player is allowed to respawn.
/datum/config_entry/number/respawn_delay
- default = 0
+ default = 0 SECONDS
/datum/config_entry/flag/usewhitelist
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index 265b9d49815..d930c496de6 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -195,7 +195,7 @@
/datum/world_topic/status/Run(list/input)
. = list()
.["version"] = GLOB.game_version
- .["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
+ .["respawn"] = config ? !!CONFIG_GET(flag/allow_respawn) : FALSE // show respawn as true regardless of "respawn as char" or "free respawn"
.["enter"] = !LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS)
.["ai"] = CONFIG_GET(flag/allow_ai)
.["host"] = world.host ? world.host : null
@@ -240,4 +240,3 @@
// Shuttle status, see /__DEFINES/stat.dm
.["shuttle_timer"] = SSshuttle.emergency.timeLeft()
// Shuttle timer, in seconds
-
diff --git a/code/game/world.dm b/code/game/world.dm
index e851bb992d4..de638624319 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -361,8 +361,8 @@ GLOBAL_VAR(restart_counter)
var/server_name = CONFIG_GET(string/servername)
if (server_name)
new_status += "[server_name] "
- if(!CONFIG_GET(flag/norespawn))
- features += "respawn"
+ if(CONFIG_GET(flag/allow_respawn))
+ features += "respawn" // show "respawn" regardless of "respawn as char" or "free respawn"
if(!CONFIG_GET(flag/allow_ai))
features += "AI disabled"
hostedby = CONFIG_GET(string/hostedby)
diff --git a/code/modules/admin/verbs/server.dm b/code/modules/admin/verbs/server.dm
index 811b2a3506d..47fe392c973 100644
--- a/code/modules/admin/verbs/server.dm
+++ b/code/modules/admin/verbs/server.dm
@@ -185,16 +185,42 @@
set category = "Server"
set desc = "Respawn basically"
set name = "Toggle Respawn"
- var/new_nores = !CONFIG_GET(flag/norespawn)
- CONFIG_SET(flag/norespawn, new_nores)
- if (!new_nores)
- to_chat(world, "You may now respawn.", confidential = TRUE)
- else
- to_chat(world, "You may no longer respawn :(", confidential = TRUE)
- message_admins(span_adminnotice("[key_name_admin(usr)] toggled respawn to [!new_nores ? "On" : "Off"]."))
- log_admin("[key_name(usr)] toggled respawn to [!new_nores ? "On" : "Off"].")
+
+ var/respawn_state = CONFIG_GET(flag/allow_respawn)
+ var/new_state = -1
+ var/new_state_text = ""
+ switch(respawn_state)
+ if(RESPAWN_FLAG_DISABLED) // respawn currently disabled
+ new_state = RESPAWN_FLAG_FREE
+ new_state_text = "Enabled"
+ to_chat(world, span_bold("You may now respawn."), confidential = TRUE)
+
+ if(RESPAWN_FLAG_FREE) // respawn currently enabled
+ new_state = RESPAWN_FLAG_NEW_CHARACTER
+ new_state_text = "Enabled, Different Slot"
+ to_chat(world, span_bold("You may now respawn as a different character."), confidential = TRUE)
+
+ if(RESPAWN_FLAG_NEW_CHARACTER) // respawn currently enabled for different slot characters only
+ new_state = RESPAWN_FLAG_DISABLED
+ new_state_text = "Disabled"
+ to_chat(world, span_bold("You may no longer respawn :("), confidential = TRUE)
+
+ else
+ WARNING("Invalid respawn state in config: [respawn_state]")
+
+ if(new_state == -1)
+ to_chat(usr, span_warning("The config for respawn is set incorrectly, please complain to your nearest server host (or fix it yourself). \
+ In the meanwhile respawn has been set to \"Off\"."))
+ new_state = RESPAWN_FLAG_DISABLED
+ new_state_text = "Disabled"
+
+ CONFIG_SET(flag/allow_respawn, new_state)
+
+ message_admins(span_adminnotice("[key_name_admin(usr)] toggled respawn to \"[new_state_text]\"."))
+ log_admin("[key_name(usr)] toggled respawn to \"[new_state_text]\".")
+
world.update_status()
- SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Respawn", "[!new_nores ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
+ SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Respawn", "[new_state_text]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
/datum/admins/proc/delay()
set category = "Server"
diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm
index 24c6754b95c..8931dffcdb4 100644
--- a/code/modules/client/player_details.dm
+++ b/code/modules/client/player_details.dm
@@ -2,14 +2,26 @@
///assoc list of ckey -> /datum/player_details
GLOBAL_LIST_EMPTY(player_details)
+/// Tracks information about a client between log in and log outs
/datum/player_details
- var/list/player_actions = list()
+ /// Action datums assigned to this player
+ var/list/datum/action/player_actions = list()
+ /// Tracks client action logging
var/list/logging = list()
+ /// Callbacks invoked when this client logs in again
var/list/post_login_callbacks = list()
+ /// Callbacks invoked when this client logs out
var/list/post_logout_callbacks = list()
- var/list/played_names = list() //List of names this key played under this round
+ /// List of names this key played under this round
+ var/list/played_names = list()
+ /// Lazylist of preference slots this client has joined the round under
+ /// Numbers are stored as strings
+ var/list/joined_as_slots
+ /// Version of byond this client is using
var/byond_version = "Unknown"
+ /// Tracks achievements they have earned
var/datum/achievement_data/achievements
+ /// World.time this player last died
var/time_of_death = 0
/datum/player_details/New(key)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index a38fe171174..5c07c8c8fd4 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -176,6 +176,12 @@
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
+ // Check that they're picking someone new for new character respawning
+ if(CONFIG_GET(flag/allow_respawn) == RESPAWN_FLAG_NEW_CHARACTER)
+ if("[client.prefs.default_slot]" in client.player_details.joined_as_slots)
+ tgui_alert(usr, "You already have played this character in this round!")
+ return FALSE
+
var/error = IsJobUnavailable(rank)
if(error != JOB_AVAILABLE)
tgui_alert(usr, get_job_unavailable_error_message(error, rank))
@@ -305,6 +311,8 @@
preserved_mind.original_character_slot_index = client.prefs.default_slot
preserved_mind.transfer_to(spawning_mob) //won't transfer key since the mind is not active
preserved_mind.set_original_character(spawning_mob)
+
+ LAZYADD(client.player_details.joined_as_slots, "[client.prefs.default_slot]")
client.init_verbs()
. = spawning_mob
new_character = .
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index cebb747f6ba..f75af00ee7d 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -808,18 +808,26 @@
*
* This sends you back to the lobby creating a new dead mob
*
- * Only works if flag/norespawn is allowed in config
+ * Only works if flag/allow_respawn is allowed in config
*/
/mob/verb/abandon_mob()
set name = "Respawn"
set category = "OOC"
- if (CONFIG_GET(flag/norespawn))
- if (!check_rights_for(usr.client, R_ADMIN))
- to_chat(usr, span_boldnotice("Respawning is not enabled!"))
- return
- else if (tgui_alert(usr, "Respawning is currently disabled, do you want to use your permissions to circumvent it?", "Respawn", list("Yes", "No")) != "Yes")
- return
+ switch(CONFIG_GET(flag/allow_respawn))
+ if(RESPAWN_FLAG_NEW_CHARACTER)
+ if(tgui_alert(usr, "Note, respawning is only allowed as another character. If you don't have another free slot you may not be able to respawn.", "Respawn", list("Ok", "Nevermind")) != "Ok")
+ return
+
+ if(RESPAWN_FLAG_DISABLED)
+ pass() // Normal respawn
+
+ if(RESPAWN_FLAG_DISABLED)
+ if (!check_rights_for(usr.client, R_ADMIN))
+ to_chat(usr, span_boldnotice("Respawning is not enabled!"))
+ return
+ if (tgui_alert(usr, "Respawning is currently disabled, do you want to use your permissions to circumvent it?", "Respawn", list("Yes", "No")) != "Yes")
+ return
if (stat != DEAD)
to_chat(usr, span_boldnotice("You must be dead to use this!"))
@@ -856,15 +864,14 @@
M.key = key
+/// Checks if the mob can respawn yet according to the respawn delay
/mob/proc/check_respawn_delay(override_delay = 0)
if(!override_delay && !CONFIG_GET(number/respawn_delay))
return TRUE
var/death_time = world.time - client.player_details.time_of_death
- var/required_delay = override_delay
- if(!required_delay)
- required_delay = CONFIG_GET(number/respawn_delay)
+ var/required_delay = override_delay || CONFIG_GET(number/respawn_delay)
if(death_time < required_delay)
if(!check_rights_for(usr.client, R_ADMIN))
diff --git a/config/config.txt b/config/config.txt
index 3b3e0d0746e..3208cf84e92 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -136,11 +136,14 @@ VOTE_PERIOD 600
## players' votes default to "No vote" (otherwise, default to "No change")
# DEFAULT_NO_VOTE
-## disable abandon mob
-NORESPAWN
+## Determines if players can respawn after death
+## 0 = Cannot respawn (default)
+## 1 = Can respawn
+## 2 = Can respawn if choosing a different character
+ALLOW_RESPAWN 0
-## Respawn delay (deciseconds), which doesn't allow to return to lobby (default 10 minutes)
-#RESPAWN_DELAY 6000
+## Respawn delay (deciseconds), which doesn't allow to return to lobby
+RESPAWN_DELAY 0
## set a hosted by name for unix platforms
HOSTEDBY Yournamehere