diff --git a/code/__DEFINES/hud_locations.dm b/code/__DEFINES/hud_locations.dm
index bcb5899fb4a..3373a77eb5c 100644
--- a/code/__DEFINES/hud_locations.dm
+++ b/code/__DEFINES/hud_locations.dm
@@ -144,12 +144,13 @@
#define UI_BOT_PULL "EAST-2:26,SOUTH:7"
//Ghosts
-#define UI_GHOST_ORBIT "SOUTH:6,CENTER-1.5"
-#define UI_GHOST_REENTER_CORPSE "SOUTH:6,CENTER-.5"
-#define UI_GHOST_TELEPORT "SOUTH:6,CENTER+.5"
-#define UI_GHOST_RESPAWN_LIST "SOUTH:6,CENTER+1.5"
-#define UI_GHOST_RESPAWN_MOB "SOUTH:6+1,CENTER+1.5"
-#define UI_GHOST_RESPAWN_PAI "SOUTH:6+2,CENTER+1.5"
+#define UI_GHOST_ORBIT "SOUTH:6,CENTER-2"
+#define UI_GHOST_REENTER_CORPSE "SOUTH:6,CENTER-1"
+#define UI_GHOST_TELEPORT "SOUTH:6,CENTER"
+#define UI_GHOST_RESPAWN_LIST "SOUTH:6,CENTER+1"
+#define UI_GHOST_RESPAWN_MOB "SOUTH:6+1,CENTER+1"
+#define UI_GHOST_RESPAWN_PAI "SOUTH:6+2,CENTER+1"
+#define UI_GHOST_RESPAWN_CHAR "SOUTH:6,CENTER+2"
//HUD styles. Please ensure HUD_VERSIONS is the same as the maximum index. Index order defines how they are cycled in F12.
#define HUD_STYLE_STANDARD 1
diff --git a/code/_onclick/hud/ghost_hud.dm b/code/_onclick/hud/ghost_hud.dm
index 997eb1650a4..2af8734df6f 100644
--- a/code/_onclick/hud/ghost_hud.dm
+++ b/code/_onclick/hud/ghost_hud.dm
@@ -21,6 +21,14 @@
. = ..()
flick(icon_state + "_anim", src)
+/atom/movable/screen/ghost/respawn_char
+ name = "Respawn as new character"
+ icon_state = "respawn"
+
+/atom/movable/screen/ghost/respawn_char/Click()
+ var/mob/dead/observer/G = usr
+ G.respawn_character()
+
/atom/movable/screen/ghost/teleport
name = "Teleport"
icon_state = "teleport"
@@ -82,6 +90,10 @@
..()
var/atom/movable/screen/using
+ using = new /atom/movable/screen/ghost/respawn_char()
+ using.screen_loc = UI_GHOST_RESPAWN_CHAR
+ static_inventory += using
+
using = new /atom/movable/screen/ghost/orbit()
using.screen_loc = UI_GHOST_ORBIT
static_inventory += using
diff --git a/code/controllers/configuration/sections/general_configuration.dm b/code/controllers/configuration/sections/general_configuration.dm
index 30e306860f2..00fad2808c3 100644
--- a/code/controllers/configuration/sections/general_configuration.dm
+++ b/code/controllers/configuration/sections/general_configuration.dm
@@ -19,7 +19,7 @@
/// Forbid players from rejoining if they use AntagHUD?
var/restrict_antag_hud_rejoin = TRUE
/// Enable respawns by default?
- var/respawn_enabled = FALSE
+ var/respawn_enabled = TRUE
/// Enable CID randomiser buster?
var/enabled_cid_randomiser_buster = FALSE
/// Forbid admins from posessing and flying the singulo round
@@ -82,6 +82,8 @@
var/bomb_cap = 20
/// Time for a brain to keep its spark of life (deciseconds)
var/revival_brain_life = 10 MINUTES
+ /// Time to wait before you can respawn as a new character.
+ var/respawn_delay = 10 MINUTES
/// Enable random AI lawsets from the default=TRUE pool
var/random_ai_lawset = TRUE
/// Enable weather events initialized by SSweather. New weather events can still
@@ -134,6 +136,7 @@
CONFIG_LOAD_NUM(monkey_cube_cap, data["monkey_cube_cap"])
CONFIG_LOAD_NUM(bomb_cap, data["bomb_cap"])
CONFIG_LOAD_NUM(revival_brain_life, data["revival_brain_life"])
+ CONFIG_LOAD_NUM(respawn_delay, data["respawn_delay"])
// Strings
CONFIG_LOAD_STR(server_name, data["server_name"])
diff --git a/code/modules/mob/dead/observer/observer_base.dm b/code/modules/mob/dead/observer/observer_base.dm
index 341fd29a9e2..e3e40840f12 100644
--- a/code/modules/mob/dead/observer/observer_base.dm
+++ b/code/modules/mob/dead/observer/observer_base.dm
@@ -342,6 +342,67 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
GLOB.ghost_hud_panel.ui_interact(src)
+/mob/dead/observer/verb/respawn_character()
+ set name = "Respawn as New Character"
+ set category = "Ghost"
+
+ var/mob/dead/observer/O = usr
+ if(!isobserver(O))
+ to_chat(O, "You are not dead!")
+ return
+
+ if(!SSticker || SSticker.current_state < GAME_STATE_PLAYING)
+ to_chat(O, "You can't respawn before the game starts!")
+ return
+
+ if(GAMEMODE_IS_WIZARD || GAMEMODE_IS_NUCLEAR || GAMEMODE_IS_RAGIN_MAGES)
+ to_chat(O, "You can't respawn for this gamemode.")
+ return
+
+ var/death_time = world.time - O.timeofdeath
+ if(!HAS_TRAIT(O, TRAIT_RESPAWNABLE) && !check_rights(R_ADMIN, FALSE))
+ to_chat(O, "You don't have respawnability!")
+ return
+
+ var/death_time_minutes = round(death_time / 600)
+ var/plural_check = "minutes"
+ if(death_time_minutes == 0)
+ plural_check = ""
+ else if(death_time_minutes == 1)
+ plural_check = "[death_time_minutes] minute and"
+ else if(death_time_minutes > 1)
+ plural_check = "[death_time_minutes] minutes and"
+ var/death_time_seconds = round((death_time - death_time_minutes * 600) / 10, 1)
+
+ if(death_time_minutes < GLOB.configuration.general.respawn_delay / 600 && !check_rights(R_ADMIN, FALSE))
+ to_chat(O, "You have been dead for [plural_check] [death_time_seconds] second\s.")
+ to_chat(O, "You must wait [GLOB.configuration.general.respawn_delay / 600] minute\s before you can respawn.")
+ return
+
+ if(!O.check_ahud_rejoin_eligibility())
+ to_chat(O, "Upon using the antagHUD you forfeited the ability to join the round.")
+ return FALSE
+ if(tgui_alert(O, "Are you sure you want to respawn?\n(If you do this, you won't be able to be cloned!)", "Respawn?", list("Yes", "No")) != "Yes")
+ return
+
+ log_and_message_admins("[key_name(O)][O.mind?.current? (O.mind.current.mind.special_role? " ([O.mind.current.mind.special_role])" : "") : ""] has chosen to respawn as a new character.")
+
+ var/list/warning = list()
+ warning.Add("You have chosen to respawn as a new character!")
+ warning.Add("You will not remember anything from your previous life or time as a ghost.")
+ warning.Add("You MUST choose a different character slot to respawn as!")
+ to_chat(O, chat_box_notice(warning.Join("
")))
+
+ if(!O.client)
+ log_game("[key_name(O)] respawn failed due to disconnect.")
+ return
+
+ var/mob/new_player/NP = new()
+ GLOB.non_respawnable_keys -= O.ckey
+ NP.ckey = O.ckey
+ qdel(O)
+ NP.chose_respawn = TRUE
+
/**
* Toggles on all HUDs for the ghost player.
*
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index cd51546b91d..894fc22329a 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -1,6 +1,8 @@
/mob/new_player
var/ready = FALSE
var/spawning = FALSE //Referenced when you want to delete the new_player later on in the code.
+ /// Has this player chosen to respawn as a new character?
+ var/chose_respawn = FALSE
universal_speak = TRUE
invisibility = 101
@@ -329,7 +331,9 @@
var/mob/living/character = create_character() //creates the human and transfers vars and mind
character = SSjobs.AssignRank(character, rank, TRUE) //equips the human
-
+ if(chose_respawn)
+ SSblackbox.record_feedback("tally", "player_respawn", 1, "[thisjob]")
+ log_and_message_admins("[character.ckey] has respawned as [character.real_name], \a [character.dna?.species ? character.dna.species : "Undefined species"] [rank].")
// AIs don't need a spawnpoint, they must spawn at an empty core
if(character.mind.assigned_role == "AI")
var/mob/living/silicon/ai/ai_character = character.AIize() // AIize the character, but don't move them yet
diff --git a/config/example/config.toml b/config/example/config.toml
index b2fe19e30eb..fde64003b45 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -302,7 +302,7 @@ allow_antag_hud = true
# Forbid players from rejoining if they use antag hud
restrict_antag_hud_rejoin = true
# Do we want to allow player respawns?
-respawn_enabled = false
+respawn_enabled = true
# Enable/disable the buster for the CID randomiser DLL
enable_cid_randomiser_buster = false
# Prevent admins from possessing the singularity
@@ -365,6 +365,8 @@ reactionary_explosions = true
bomb_cap = 20
# Amount of time (deciseconds) for a brain to keep its "spark of life". Set to -1 to disable.
revival_brain_life = 6000 # 10 minutes
+# Amount of time (deciseconds) the player must wait before respawning as a new character.
+respawn_delay = 6000 # 10 minutes
# Enable random silicon lawset (If said lawset has default = TRUE in the code). Disable for always crewsimov
random_ai_lawset = true
# Enable weather events initialized by SSweather. New weather events can still be added during the round if this is disabled.
diff --git a/icons/mob/screen_ghost.dmi b/icons/mob/screen_ghost.dmi
index fb4e79a83fd..6fffb2039c7 100644
Binary files a/icons/mob/screen_ghost.dmi and b/icons/mob/screen_ghost.dmi differ