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."