diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 34f168611fa..7541a735ce8 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -275,6 +275,19 @@ Ccomp's first proc. 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. */ + var/datum/preferences/P + + if (G.client) + P = G.client.prefs + else if (G.ckey) + P = preferences_datums[G.ckey] + else + src << "Something went wrong, couldn't find the target's preferences datum" + return 0 + + for (var/entry in P.time_of_death)//Set all the prefs' times of death to a huge negative value so any respawn timers will be fine + P.time_of_death[entry] = -99999 + G.has_enabled_antagHUD = 2 G.can_reenter_corpse = 1 diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index ee26efd3fec..4a81eb47ee6 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -42,6 +42,7 @@ datum/preferences var/last_ip var/last_id var/list/notifications = list() //A list of datums, for the dynamic server greeting window. + var/list/time_of_death = list()//This is a list of last times of death for various things with different respawn timers //game-preferences var/lastchangelog = "" //Saved changlog filesize to detect if there was a change diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index bde0c1db5c8..e4b5b17ce51 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -32,6 +32,9 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images incorporeal_move = 1 /mob/dead/observer/New(mob/body) + if (istype(body, /mob/dead/observer)) + return//A ghost can't become a ghost. + sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF see_invisible = SEE_INVISIBLE_OBSERVER see_in_dark = 100 @@ -44,7 +47,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images updateallghostimages() var/turf/T - if(ismob(body)) + if (ismob(body)) T = get_turf(body) //Where is the body located? attack_log = body.attack_log //preserve our attack logs by copying them to our ghost @@ -95,7 +98,17 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images if(target) ManualFollow(target) +/mob/dead/observer/proc/initialise_postkey() + //This function should be run after a ghost has been created and had a ckey assigned + //Death times are initialised if they were unset + //get/set death_time functions are in mob_helpers.dm + if (!get_death_time(ANIMAL)) + set_death_time(ANIMAL, world.time - RESPAWN_ANIMAL)//allow instant mouse spawning + if (!get_death_time(MINISYNTH)) + set_death_time(MINISYNTH, world.time - RESPAWN_MINISYNTH) //allow instant drone spawning + if (!get_death_time(CREW)) + set_death_time(CREW, world.time) /mob/dead/attackby(obj/item/W, mob/user) if(istype(W,/obj/item/weapon/book/tome)) @@ -146,15 +159,30 @@ Works together with spawning an observer, noted above. return 1 /mob/proc/ghostize(var/can_reenter_corpse = 1) - if(key) + if(ckey) var/mob/dead/observer/ghost = new(src) //Transfer safety to observer spawning proc. ghost.can_reenter_corpse = can_reenter_corpse ghost.timeofdeath = src.stat == DEAD ? src.timeofdeath : world.time - ghost.key = key + + + //This is duplicated for robustness in cases where death might not be called. + //It is also set in the mob/death proc + if (isanimal(src)) + set_death_time(ANIMAL, world.time) + else if (ispAI(src) || isdrone(src)) + set_death_time(MINISYNTH, world.time) + else + set_death_time(CREW, world.time)//Crew is the fallback + + + ghost.ckey = ckey + ghost.client = client + ghost.initialise_postkey() if(ghost.client) - ghost.client.time_died_as_mouse = ghost.timeofdeath - if(ghost.client && !ghost.client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. - ghost.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! + + + if(!ghost.client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. + ghost.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! return ghost /* @@ -429,11 +457,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp src << "You may not spawn as a mouse on this Z-level." return - var/timedifference = world.time - client.time_died_as_mouse - if(client.time_died_as_mouse && timedifference <= mouse_respawn_time * 600) - var/timedifference_text - timedifference_text = time2text(mouse_respawn_time * 600 - timedifference,"mm:ss") - src << "You may only spawn again as a mouse more than [mouse_respawn_time] minutes after your death. You have [timedifference_text] left." + var/timedifference = world.time - get_death_time(ANIMAL)//get/set death_time functions are in mob_helpers.dm + if(timedifference <= RESPAWN_ANIMAL) + var/timedifference_text = time2text(RESPAWN_ANIMAL - timedifference,"mm:ss") + var/basetime = time2text(RESPAWN_ANIMAL,"mm:ss") + src << "You may only spawn again as a mouse more than [basetime] minutes after your death. You have [timedifference_text] left." return var/response = alert(src, "Are you -sure- you want to become a mouse?","Are you sure you want to squeek?","Squeek!","Nope!") diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index fe7ba68a5e2..6e644454791 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -80,6 +80,12 @@ healths.icon_state = "health6" timeofdeath = world.time + if (isanimal(src)) + set_death_time(ANIMAL, world.time) + else if (ispAI(src) || isdrone(src)) + set_death_time(MINISYNTH, world.time) + else if (isliving(src)) + set_death_time(CREW, world.time)//Crew is the fallback if(mind) mind.store_memory("Time of death: [worldtime2text()]", 0) living_mob_list -= src dead_mob_list |= src diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm index a292ead1b14..99fbe1c576f 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -99,7 +99,7 @@ src << "That verb is not currently permitted." return - if (!src.stat) + if (!src.stat || !client) return if (usr != src) @@ -108,30 +108,22 @@ if(jobban_isbanned(src,"Cyborg")) usr << "You are banned from playing synthetics and cannot spawn as a drone." return - + if(!MayRespawn(1)) return - var/deathtime = world.time - src.timeofdeath + var/deathtime = world.time - get_death_time(MINISYNTH)//get/set death_time functions are in mob_helpers.dm if(istype(src,/mob/dead/observer)) var/mob/dead/observer/G = src if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) usr << "Upon using the antagHUD you forfeighted 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) - if (deathtime < 6000) - usr << "You have been dead for[pluralcheck] [deathtimeseconds] seconds." - usr << "You must wait 10 minutes to respawn as a drone!" + if (deathtime < RESPAWN_MINISYNTH) + var/timedifference_text = time2text(RESPAWN_MINISYNTH - deathtime,"mm:ss") + var/basetime = time2text(RESPAWN_MINISYNTH,"mm:ss") + usr << "You may only spawn again as a drone more than [basetime] minutes after your death. You have [timedifference_text] left." return var/list/all_fabricators = list() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index edcdaafde79..5f5c9c51020 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -15,7 +15,19 @@ spellremove(src) for(var/infection in viruses) qdel(infection) - ghostize() + + + + //Added this to prevent nonliving mobs from ghostising + //The only non 'living' mobs are: + //observers (ie ghosts), + //new_player, an abstraction used to handle people who are sitting in the lobby + //Freelook, an abstraction used to handle the AI looking through cameras, and possibly remote viewing mutation + + //None of these mobs can 'die' in any sense, and none of them should be able to become ghosts. + //Ghosts are the only ones that even technically 'exist' and aren't just an abstraction using mob code for convenience + if (istype(src, /mob/living)) + ghostize() ..() /mob/proc/remove_screen_obj_references() @@ -365,6 +377,9 @@ set name = "Respawn" set category = "OOC" + if (!client) + return//This shouldnt happen + if (!( config.abandon_allowed )) usr << "Respawn is disabled." return @@ -375,25 +390,19 @@ usr << "Respawn is disabled for this roundtype." return else - var/deathtime = world.time - src.timeofdeath + if(istype(src,/mob/dead/observer)) var/mob/dead/observer/G = src if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) usr << "\blue Upon using the antagHUD you forfeighted 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) - usr << "You have been dead for[pluralcheck] [deathtimeseconds] seconds." - if (deathtime < 18000) - usr << "You must wait 30 minutes to respawn!" + var/deathtime = world.time - get_death_time(CREW)//get/set death_time functions are in mob_helpers.dm + if (deathtime < RESPAWN_CREW) + var/timedifference_text = time2text(RESPAWN_CREW - deathtime,"mm:ss") + var/basetime = time2text(RESPAWN_CREW,"mm:ss") + usr << "You may only respawn than [basetime] minutes after your death. You have [timedifference_text] left." + return else usr << "You can respawn now, enjoy your new life!" diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index fd7498e7583..0bd3306a938 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -117,6 +117,11 @@ return 1 return 0 +/proc/isdrone(A) + if(istype(A, /mob/living/silicon/robot/drone)) + return 1 + return 0 + /proc/iscarbon(A) if(istype(A, /mob/living/carbon)) return 1 @@ -937,4 +942,27 @@ proc/is_blind(A) return null//If we get here, the holder must be buried many layers deep in nested containers. Shouldn't happen +//This proc retrieves the relevant time of death from +/mob/proc/get_death_time(var/which) + var/datum/preferences/P + if (client) + P = client.prefs + else if (ckey) + P = preferences_datums[ckey] + else return null + + return P.time_of_death[which] + +/mob/proc/set_death_time(var/which, var/value) + var/datum/preferences/P + if (client) + P = client.prefs + else if (ckey) + P = preferences_datums[ckey] + else + return 0 + + P.time_of_death[which] = value + return 1 + #undef SAFE_PERP diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 27287825849..5f62f4869eb 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -107,8 +107,7 @@ if(alert(src,"Are you sure you wish to observe? You will have to wait 30 minutes before being able to respawn!","Player Setup","Yes","No") == "Yes") if(!client) return 1 - var/mob/dead/observer/observer = new() - + var/mob/dead/observer/observer = new /mob/dead/observer(src) spawning = 1 src << sound(null, repeat = 0, wait = 0, volume = 85, channel = 1) // MAD JAMS cant last forever yo @@ -134,7 +133,8 @@ observer.name = observer.real_name 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/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! - observer.key = key + observer.ckey = ckey + observer.initialise_postkey() qdel(src) return 1 diff --git a/code/setup.dm b/code/setup.dm index 1822607977d..6c3a09e9cf3 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -1032,3 +1032,18 @@ var/list/be_special_flags = list( dview_mob.see_invisible = invis_flags; \ for(type in view(range, dview_mob)) #define END_FOR_DVIEW dview_mob.loc = null + + +//Time of Death constants +//Used with a list in preference datums to track times of death +#define CREW "crew"//Used for crewmembers, AI, cyborgs, nymphs, antags +#define ANIMAL "animal"//Used for mice and any other simple animals +#define MINISYNTH "minisynth"//Used for drones and pAIs + + +//Respawn Times +//How long you must wait after death before being allowed to respawn +//These times are in deciseconds. One minute = 600 deciseconds +#define RESPAWN_CREW 18000 +#define RESPAWN_ANIMAL 3000 +#define RESPAWN_MINISYNTH 6000 \ No newline at end of file diff --git a/html/changelogs/Nanako-Respawn.yml b/html/changelogs/Nanako-Respawn.yml new file mode 100644 index 00000000000..bce306dd4ae --- /dev/null +++ b/html/changelogs/Nanako-Respawn.yml @@ -0,0 +1,38 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Nanako + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Respawn timers are now tracked individually for playing as animals (mice), small synthetics (drones and pAIs) and crew (everything else). This means you can now play as a mouse or drone while waiting to respawn as a full crewmember." + - tweak: "You can now spawn as a drone immediately upon joining as an observer, without having to wait ten minutes. There is still a cooldown between respawning as a drone if you just died as one." + - tweak: "Slightly improved the error messages if you try to respawn when you've not waited long enough."