From d2b092dad5a75c01f00c3967dec2a15abfc3db03 Mon Sep 17 00:00:00 2001 From: Ahzpy <88119260+Ahzpy@users.noreply.github.com> Date: Sat, 11 Oct 2025 08:37:45 -0700 Subject: [PATCH] Round Rejoining (#30342) * very simple functionality test * formatting fix * adapt 220 code, add logging, adjust config * fixes * appeases review overlords * fixes * 25 min respawn delay, warning is more noticable * 10 minute respawn delay, cant respawn during wiz or nukies * more logging + logging fix * species logging, revert accidental config change * antag logging + formatting * pleases review overlords again * linters * uses CSS for warning Co-authored-by: warriorstar-orion Signed-off-by: Ahzpy <88119260+Ahzpy@users.noreply.github.com> * deconflicts paradise.dme? * fixes runtime with destroyed bodies * you cant respawn for ragin mages silly * adds respawn button to ghost hud * standardize new ghost button coloring * remove you are not an admin message * muscle memory fixer * update hud locations --------- Signed-off-by: Ahzpy <88119260+Ahzpy@users.noreply.github.com> Co-authored-by: warriorstar-orion --- code/__DEFINES/hud_locations.dm | 13 ++-- code/_onclick/hud/ghost_hud.dm | 12 ++++ .../sections/general_configuration.dm | 5 +- .../mob/dead/observer/observer_base.dm | 61 ++++++++++++++++++ code/modules/mob/new_player/new_player.dm | 6 +- config/example/config.toml | 4 +- icons/mob/screen_ghost.dmi | Bin 1721 -> 3221 bytes 7 files changed, 92 insertions(+), 9 deletions(-) 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 fb4e79a83fd7988eb56290ffc9fddc20a7bbc00c..6fffb2039c70238ac025f78df23522f5dffdbc2f 100644 GIT binary patch literal 3221 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 Hu0mjf&vGev delta 1716 zcmV;l221&s8MzIR8Gi!+0082oihBS60IPabSad{Xb7OL8aCB*JZU6vyoQ06f3c@fD zMc2t!gmx?93wLg$MY^!RP*OXZfi?+~!Q$T=EmQ<0%;w(9nFo`rV|l7-Rh%n%2MXw{ zI;h=pCB=vxn`pKhDGcDv@MHpOhA6YawN3DPv_P2@9a4ztT7QZw=>aA^vjkunsJS@u z0UB*__}#OQDfIgBiWhTz203bxlj|6I@Q%{p_gzWcYy8LLs;rCmAD=y}VDYU%(iH#z z06b7kR7FixSpWb4_Ve%E`~T$I(zDj^mfZBgwXo^5OLLRN-gqe~X2CvPjF1g+IzUGt zIH8Fi2wD^eQ-1_kAtEFzqjGlu0007XQchF<0OhwZN&o-_b4f%&RCwC$n(K0-IuL|e zV!$$tH<^C_cRh2%Lym7EnDYKem+j3~m!wZ4VbqkHKW9~QLuUNh+n0avK##!0SGE$a8y7n^Tioj(SQ~}DJa&S9{Sw_zsvRu0K%6_ z2~5m$2Y-at+aKhOGR62m_B-d}1c>oJRYDLI$PRP@hr z{>|@!FB#kbzIPz)AUaqV7(g%bufGYE0rFmMBJ~Iu+5=(=gWB8utN<=0SQ3a8J`urN z4u3k2wgnMy_OF*f3!pCuK>{cr1;14hgM82{{Jb4IfV>i7d7vO|bI>VRQ|;z8egH6- zNoO5Zm~V>Nfq|1vfm9HmxBK5KprsE0^^pMTMnfXIh)Lf368{_k1ReLX3yG1ZT86iX zPhu1J6@CC9R$y@r^3|~%;1FQ#xBKr8CV%gbX73NDZ;$7{&j)^=5A2!$(~n4ODy1MY zumbFt|I@eOZRlyjCAT@?^ZdTucOV@WuOafxuktYg;BNqI0Qd5J=J%KQn3N67 z=(7TB{&jxux~lwN?<){MEDC}iBr^YYet($nyVcJJP~tj)N|V3K{Pp?1Tm5_hC4UY@ zy$GhFSA_ac^ZT{mt$sd$V&nI={(T>t#4B(wzjr^+|J{{ez)*n6bO9js@8$OdxZ!tQ zSMK{_ikAKaz{d{)!h{Lt@0= z{GWbYC?;cL{5~J}eLfKW?f}oU^AbEK*n1OJ0PK31)d2uB5|2dV2pThH*MH}KfI&ct z7^+GD8=rd``+l^3DB4%&f4|+7izabv0U8D9wEzx)`AjdW&Hu3cyHwt8YXPhPBFw>3 z5o+^4nEj+5ny?b!vH~At0)VUXe*#PxL$KV%wE&F*^hBeKF90PgP@Df53V^W>ja~sp zQwiWJpz}wWVh<28s`EcPz<(?U;I#lB^Ym?epg#Ze1)!JL&j3CI0F%fG2wI1V{Qp{j z5(Fg5y?12QB3FawQ=KoxRel+zP0SbJEwfR2*oS>g8 zfK>o~8T!;Aq80fcxd}z%xZ!)i*Ivb_%>N1ScMScF3akY9$P%y$Re$+EuL2