diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index 9c3c3571fe..8a34fb50b7 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -22,6 +22,9 @@ var/global/list/joblist = list() //list of all jobstypes, minus borg and AI var/list/mannequins_ +// Times that players are allowed to respawn ("ckey" = world.time) +GLOBAL_LIST_EMPTY(respawn_timers) + // Posters var/global/list/poster_designs = list() var/global/list/NT_poster_designs = list() diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 98a3f817a3..3a649b4ad4 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -68,7 +68,11 @@ var/list/gamemode_cache = list() var/static/allow_ai_shells = FALSE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable. var/static/give_free_ai_shell = FALSE // allows a specific spawner object to instantiate a premade AI Shell var/static/hostedby = null + var/static/respawn = 1 + var/static/respawn_time = 3000 // time before a dead player is allowed to respawn (in ds, though the config file asks for minutes, and it's converted below) + var/static/respawn_message = "Make sure to play a different character, and please roleplay correctly!" + var/static/guest_jobban = 1 var/static/usewhitelist = 0 var/static/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0 @@ -482,6 +486,13 @@ var/list/gamemode_cache = list() if ("norespawn") config.respawn = 0 + if ("respawn_time") + var/raw_minutes = text2num(value) + config.respawn_time = raw_minutes MINUTES + + if ("respawn_message") + config.respawn_message = value + if ("servername") config.server_name = value diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index d85a88cbef..2898145795 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -265,32 +265,39 @@ Ccomp's first proc. /client/proc/allow_character_respawn() set category = "Special Verbs" set name = "Allow player to respawn" - set desc = "Let's the player bypass the wait to respawn or allow them to re-enter their corpse." + set desc = "Let a player bypass the wait to respawn or allow them to re-enter their corpse." if(!holder) return - var/list/ghosts= get_ghosts(1,1) - - var/target = input("Please, select a ghost!", "COME BACK TO LIFE!", null, null) as null|anything in ghosts + var/target = input("Select a ckey to allow to rejoin", "Allow Respawn Selector") as null|anything in GLOB.respawn_timers if(!target) - to_chat(src, "Hrm, appears you didn't select a ghost") // Sanity check, if no ghosts in the list we don't want to edit a null variable and cause a runtime error. return + + if(GLOB.respawn_timers[target] == -1) // Their respawn timer is set to -1, which is 'not allowed to respawn' + var/response = alert(src, "Are you sure you wish to allow this individual to respawn? They would normally not be able to.","Allow impossible respawn?","No","Yes") + if(response == "No") + return + + GLOB.respawn_timers -= target - var/mob/observer/dead/G = ghosts[target] - if(G.has_enabled_antagHUD && config.antag_hud_restricted) - var/response = alert(src, "Are you sure you wish to allow this individual to play?","Ghost has used AntagHUD","Yes","No") - if(response == "No") return - G.timeofdeath=-19999 /* time of death is checked in /mob/verb/abandon_mob() which is the Respawn verb. - timeofdeath is used for bodies on autopsy but since we're messing with a ghost I'm pretty sure - there won't be an autopsy. - */ - G.has_enabled_antagHUD = 2 - G.can_reenter_corpse = 1 + var/found_client = FALSE + for(var/c in GLOB.clients) + var/client/C = c + if(C.ckey == target) + found_client = C + to_chat(C, "You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead.") + if(isobserver(C.mob)) + var/mob/observer/dead/G = C.mob + G.can_reenter_corpse = 1 + to_chat(C, "You can also re-enter your corpse, if you still have one!") + break - G:show_message(text("You may now respawn. You should roleplay as if you learned nothing about the round during your time with the dead."), 1) - log_admin("[key_name(usr)] allowed [key_name(G)] to bypass the respawn time limit") - message_admins("Admin [key_name_admin(usr)] allowed [key_name_admin(G)] to bypass the respawn time limit", 1) + if(!found_client) + to_chat(src, "The associated client didn't appear to be connected, so they couldn't be notified, but they can now respawn if they reconnect.") + + log_admin("[key_name(usr)] allowed [found_client ? key_name(found_client) : target] to bypass the respawn time limit") + message_admins("Admin [key_name_admin(usr)] allowed [found_client ? key_name_admin(found_client) : target] to bypass the respawn time limit", 1) /client/proc/toggle_antagHUD_use() diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 5670793983..9dca9c6fdc 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -229,6 +229,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/mob/observer/dead/ghost = ghostize(0) // 0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 if(ghost) ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. + ghost.set_respawn_timer() announce_ghost_joinleave(ghost) /mob/observer/dead/can_use_hands() return 0 @@ -304,6 +305,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/response = alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?","Yes","No") if(response == "No") return can_reenter_corpse = FALSE + set_respawn_timer(-1) // Foreeeever if(!has_enabled_antagHUD && !client.holder) has_enabled_antagHUD = TRUE diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index c2f5c586eb..9d6735ee8b 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -100,7 +100,8 @@ if(mind) mind.store_memory("Time of death: [stationtime2text()]", 0) living_mob_list -= src dead_mob_list |= src - + + set_respawn_timer() updateicon() handle_regular_hud_updates() handle_vision() diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index 87751aa006..e953c09597 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -29,4 +29,5 @@ var/global/list/empty_playable_ai_cores = list() global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") //Handle job slot/tater cleanup. + set_respawn_timer() clear_client() \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index db7ec7fd81..7bff8715aa 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -341,46 +341,56 @@ return */ +/mob/proc/set_respawn_timer(var/time) + // Try to figure out what time to use + + // Special cases, can never respawn + if(ticker?.mode?.deny_respawn) + time = -1 + else if(!config.abandon_allowed) + time = -1 + else if(!config.respawn) + time = -1 + + // Special case for observing before game start + else if(ticker?.current_state <= GAME_STATE_SETTING_UP) + time = 1 MINUTE + + // Wasn't given a time, use the config time + else if(!time) + time = config.respawn_time + + var/keytouse = ckey + // Try harder to find a key to use + if(!keytouse && key) + keytouse = ckey(key) + else if(!keytouse && mind?.key) + keytouse = ckey(mind.key) + + GLOB.respawn_timers[keytouse] = world.time + time + +/mob/observer/dead/set_respawn_timer() + if(config.antag_hud_restricted && has_enabled_antagHUD) + ..(-1) + else + return // Don't set it, no need + /mob/verb/abandon_mob() - set name = "Respawn" + set name = "Return to Menu" set category = "OOC" - if (!( config.abandon_allowed )) - to_chat(usr, "Respawn is disabled.") - return - if ((stat != 2 || !( ticker ))) + if(stat != DEAD || !ticker) to_chat(usr, "You must be dead to use this!") return - if (ticker.mode && ticker.mode.deny_respawn) //BS12 EDIT - to_chat(usr, "Respawn is disabled for this roundtype.") - return - else - var/deathtime = world.time - src.timeofdeath - if(istype(src,/mob/observer/dead)) - var/mob/observer/dead/G = src - if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) - to_chat(usr, "By using the antagHUD you forfeit the ability to join the round.") - return - var/deathtimeminutes = round(deathtime / 600) - var/pluralcheck = "minute" - if(deathtimeminutes == 0) - pluralcheck = "" - else if(deathtimeminutes == 1) - pluralcheck = " [deathtimeminutes] minute and" - else if(deathtimeminutes > 1) - pluralcheck = " [deathtimeminutes] minutes and" - var/deathtimeseconds = round((deathtime - deathtimeminutes * 600) / 10,1) - to_chat(usr, "You have been dead for[pluralcheck] [deathtimeseconds] seconds.") - if ((deathtime < (1 * 600)) && (ticker && ticker.current_state > GAME_STATE_PREGAME)) //VOREStation Edit: lower respawn timer - to_chat(usr, "You must wait 1 minute to respawn!") + // Final chance to abort "respawning" + if(mind && timeofdeath) // They had spawned before + var/choice = alert(usr, "Returning to the menu will prevent your character from being revived in-round. Are you sure?", "Confirmation", "No, wait", "Yes, leave") + if(choice == "No, wait") return - else - to_chat(usr, "You can respawn now, enjoy your new life!") - - log_game("[usr.name]/[usr.key] used abandon mob.") - - to_chat(usr, "Make sure to play a different character, and please roleplay correctly!") + + // Beyond this point, you're going to respawn + to_chat(usr, config.respawn_message) if(!client) log_game("[usr.key] AM failed due to disconnect.") diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 30f5853524..939856c7a2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -120,8 +120,9 @@ new_player_panel_proc() if(href_list["observe"]) + var/alert_time = ticker?.current_state <= GAME_STATE_SETTING_UP ? 1 : round(config.respawn_time/10/60) - if(alert(src,"Are you sure you wish to observe? You will have to wait 60 seconds before being able to respawn!","Player Setup","Yes","No") == "Yes") //Vorestation edit - Rykka corrected to 60 seconds to match current spawn time + if(alert(src,"Are you sure you wish to observe? You will have to wait up to [alert_time] minute\s before being able to spawn into the game!","Player Setup","Yes","No") == "Yes") if(!client) return 1 //Make a new mannequin quickly, and allow the observer to take the appearance @@ -143,7 +144,6 @@ observer.forceMove(O.loc) else to_chat(src, "Could not locate an observer spawn point. Use the Teleport verb to jump to the station map.") - observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. announce_ghost_joinleave(src) @@ -154,6 +154,7 @@ if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. observer.verbs -= /mob/observer/dead/verb/toggle_antagHUD // Poor guys, don't know what they are missing! observer.key = key + observer.set_respawn_timer(time_till_respawn()) // Will keep their existing time if any, or return 0 and pass 0 into set_respawn_timer which will use the defaults qdel(src) return 1 @@ -163,6 +164,13 @@ if(!ticker || ticker.current_state != GAME_STATE_PLAYING) to_chat(usr, "The round is either not ready, or has already finished...") return + + var/time_till_respawn = time_till_respawn() + if(time_till_respawn == -1) // Special case, never allowed to respawn + to_chat(usr, "Respawning is not allowed!") + else if(time_till_respawn) // Nonzero time to respawn + to_chat(usr, "You can't respawn yet! You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.") + return /* if(client.prefs.species != "Human" && !check_rights(R_ADMIN, 0)) //VORESTATION EDITS: THE COMMENTED OUT AREAS FROM LINE 154 TO 178 if (config.usealienwhitelist) @@ -341,6 +349,23 @@ popup.set_content(dat) popup.open() +/mob/new_player/proc/time_till_respawn() + if(!ckey) + return -1 // What? + + var/timer = GLOB.respawn_timers[ckey] + // No timer at all + if(!timer) + return 0 + // Special case, infinite timer + if(timer == -1) + return -1 + // Timer expired + if(timer <= world.time) + GLOB.respawn_timers -= ckey + return 0 + // Timer still going + return timer - world.time /mob/new_player/proc/IsJobAvailable(rank) var/datum/job/job = job_master.GetJob(rank) diff --git a/config/example/config.txt b/config/example/config.txt index 713d741b17..eaff246194 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -196,10 +196,15 @@ ANTAG_HUD_RESTRICTED ## allow AI job ALLOW_AI - -## disable abandon mob +## Disable respawning # NORESPAWN +## set a respawn time (in minutes) +# RESPAWN_TIME 5 + +## set a message to give to players when they respawn +# RESPAWN_MESSAGE Remember to play a different character or something! + ## disables calling del(src) on newmobs if they logout before spawnin in # DONT_DEL_NEWMOB