diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index b786d11ea9e..1b7b966d94e 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -24,6 +24,7 @@
glide_size = 8
appearance_flags = TILE_BOUND
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
+ var/floating = FALSE
/atom/movable/SDQL_update(const/var_name, new_value)
if(var_name == "step_x" || var_name == "step_y" || var_name == "step_size" || var_name == "bound_x" || var_name == "bound_y" || var_name == "bound_width" || var_name == "bound_height")
@@ -418,3 +419,15 @@
return FALSE
acted_explosions += ex_id
return TRUE
+
+/atom/movable/proc/float(on)
+ if(throwing)
+ return
+ if(on && !floating)
+ animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
+ sleep(10)
+ animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
+ floating = TRUE
+ else if (!on && floating)
+ animate(src, pixel_y = initial(pixel_y), time = 10)
+ floating = FALSE
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
new file mode 100644
index 00000000000..69fda57d04a
--- /dev/null
+++ b/code/game/objects/structures/life_candle.dm
@@ -0,0 +1,90 @@
+/obj/structure/life_candle
+ name = "life candle"
+ desc = "You are dead. Insert quarter to continue."
+ icon = 'icons/obj/candle.dmi'
+ icon_state = "candle1"
+
+ var/icon_state_active = "candle1_lit"
+ var/icon_state_inactive = "candle1"
+
+ anchored = TRUE
+
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+ var/lit_luminosity = 2
+ var/list/datum/mind/linked_minds = list()
+
+ // If the body is destroyed, what do we spawn for them
+ var/mob_type = /mob/living/carbon/human
+
+ // If the respawned person is given a specific outfit
+ var/datum/outfit/outfit
+ // How long until we respawn them after their death.
+ var/respawn_time = 50
+ var/respawn_sound = 'sound/magic/Staff_animation.ogg'
+
+/obj/structure/life_candle/attack_hand(mob/user)
+ if(!user.mind)
+ return
+ if(user.mind in linked_minds)
+ user.visible_message("[user] reaches out and pinches the flame of [src].", "You sever the connection between yourself and [src].")
+ linked_minds -= user.mind
+ else
+ user.visible_message("[user] touches [src]. It seems to respond to their presence!", "You create a connection between you and [src].")
+ linked_minds |= user.mind
+
+ update_icon()
+ float(linked_minds.len)
+ if(linked_minds.len)
+ START_PROCESSING(SSobj, src)
+ SetLuminosity(lit_luminosity)
+ else
+ STOP_PROCESSING(SSobj, src)
+ SetLuminosity(0)
+
+/obj/structure/life_candle/update_icon()
+ if(linked_minds.len)
+ icon_state = icon_state_active
+ else
+ icon_state = icon_state_inactive
+
+/obj/structure/life_candle/examine(mob/user)
+ . = ..()
+ if(linked_minds.len)
+ user << "[src] is active, and linked to [linked_minds.len] souls."
+ else
+ user << "It is static, still, unmoving."
+
+/obj/structure/life_candle/process()
+ if(!linked_minds.len)
+ STOP_PROCESSING(SSobj, src)
+ return
+
+ for(var/m in linked_minds)
+ var/datum/mind/mind = m
+ if(!mind.current || (mind.current && mind.current.stat == DEAD))
+ addtimer(CALLBACK(src, .proc/respawn, mind), respawn_time, TIMER_UNIQUE)
+
+/obj/structure/life_candle/proc/respawn(datum/mind/mind)
+ var/turf/T = get_turf(src)
+ var/mob/living/body
+ if(mind.current)
+ if(mind.current.stat != DEAD)
+ return
+ else
+ body = mind.current
+ if(!body)
+ body = new mob_type(T)
+ var/mob/ghostie = mind.get_ghost(TRUE)
+ if(ghostie.client && ghostie.client.prefs)
+ ghostie.client.prefs.copy_to(body)
+ mind.transfer_to(body)
+ else
+ body.forceMove(T)
+ body.revive(1,1)
+ mind.grab_ghost(TRUE)
+ body.flash_act()
+
+ if(ishuman(body) && istype(outfit))
+ outfit.equip(body)
+ playsound(T, respawn_sound, 50, 1)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 8590f6bbf13..e540625aa70 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -598,7 +598,7 @@
if(!override)
float(!has_gravity)
-/mob/living/proc/float(on)
+/mob/living/float(on)
if(throwing)
return
var/fixed = 0
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 0f454be53d5..b09fb96b09f 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -44,7 +44,6 @@
var/ventcrawler = 0 //0 No vent crawling, 1 vent crawling in the nude, 2 vent crawling always
var/limb_destroyer = 0 //1 Sets AI behavior that allows mobs to target and dismember limbs with their basic attack.
- var/floating = 0
var/mob_size = MOB_SIZE_HUMAN
var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature..
var/list/image/staticOverlays = list()
@@ -75,4 +74,4 @@
var/list/status_effects //a list of all status effects the mob has
var/list/implants = null
- var/tesla_ignore = FALSE
\ No newline at end of file
+ var/tesla_ignore = FALSE
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index 88dbc1e492b..404b4f57321 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -56,6 +56,5 @@
M.key = key
if(delete_old_mob)
- spawn(1)
- qdel(src)
+ QDEL_IN(src, 1)
return M
diff --git a/tgstation.dme b/tgstation.dme
index 82f7906db53..88ed511512e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -849,6 +849,7 @@
#include "code\game\objects\structures\kitchen_spike.dm"
#include "code\game\objects\structures\ladders.dm"
#include "code\game\objects\structures\lattice.dm"
+#include "code\game\objects\structures\life_candle.dm"
#include "code\game\objects\structures\mineral_doors.dm"
#include "code\game\objects\structures\mirror.dm"
#include "code\game\objects\structures\mop_bucket.dm"