From f7e10caa687d6eda20fb9f6c20692cb2470b40e2 Mon Sep 17 00:00:00 2001
From: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Date: Thu, 17 Mar 2022 05:18:33 -0500
Subject: [PATCH] Refactors Lich Phylactery to be a component, makes it
actually work (#65366)
* Refactors phylactery to be a component
- Overall improves lich code
- Changed the SOUL_IMBUE signal to return a bitflag
- Minor style improvements (indentation and such)
* Tweak initialize + fixes a bug with being gibbed
* adds a clarification comment
* And fixes a compile error oops
* reorganizing
* clear the revive timer, just in case
* oops this should drop stuff
* missing no soul trait
* type in a variable name
* god damn devil removal
- this check was removed when devil was and i think it's kinda important to prevent stacking lichdom
* rework this a bit
* plurals
* also, wrong source
* updates the description
* Some review + undoes some changes
- Corrects some comments
- Uses defines
- Rewords the lichdom entry and spell desc
- Sets default time per res to 0 secs to avoid changing behaviors
---
code/__DEFINES/dcs/signals/signals_object.dm | 1 +
code/datums/components/phylactery.dm | 219 ++++++++++++++++++
code/datums/components/stationloving.dm | 3 +-
.../antagonists/wizard/equipment/spellbook.dm | 10 +-
code/modules/mining/equipment/survival_pod.dm | 39 ++--
code/modules/spells/spell_types/lichdom.dm | 198 +++++-----------
code/modules/unit_tests/create_and_destroy.dm | 2 -
tgstation.dme | 1 +
8 files changed, 300 insertions(+), 173 deletions(-)
create mode 100644 code/datums/components/phylactery.dm
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index fa08d5b571f..cd6dd66ff99 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -118,6 +118,7 @@
#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone"
///return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user)
#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul"
+ #define COMPONENT_BLOCK_IMBUE (1 << 0)
///called before marking an object for retrieval, checked in /obj/effect/proc_holder/spell/targeted/summonitem/cast() : (mob/user)
#define COMSIG_ITEM_MARK_RETRIEVAL "item_mark_retrieval"
#define COMPONENT_BLOCK_MARK_RETRIEVAL (1<<0)
diff --git a/code/datums/components/phylactery.dm b/code/datums/components/phylactery.dm
new file mode 100644
index 00000000000..45d9a034b72
--- /dev/null
+++ b/code/datums/components/phylactery.dm
@@ -0,0 +1,219 @@
+/**
+ * ## Phylactery component
+ *
+ * Used for lichtom to turn (almost) any object into a phylactery
+ * A mob linked to a phylactery will repeatedly revive on death.
+ */
+/datum/component/phylactery
+ // Set in initialize.
+ /// The mind of the lich who is linked to this phylactery.
+ var/datum/mind/lich_mind
+ /// The respawn timer of the phylactery.
+ var/base_respawn_time = 3 MINUTES
+ /// How much time is added on to the respawn time per revival.
+ var/time_per_resurrection = 0
+ /// How much stun (paralyze) is caused on respawn per revival.
+ var/stun_per_resurrection = 20 SECONDS
+ /// The color of the phylactery itself. Applied on creation.
+ var/phylactery_color = COLOR_VERY_DARK_LIME_GREEN
+
+ // Internal vars.
+ /// The number of ressurections that have occured from this phylactery.
+ var/num_resurrections = 0
+ /// A timerid to the current revival timer.
+ var/revive_timer
+
+/datum/component/phylactery/Initialize(
+ datum/mind/lich_mind,
+ base_respawn_time = 3 MINUTES,
+ time_per_resurrection = 0 SECONDS,
+ stun_per_resurrection = 20 SECONDS,
+ phylactery_color = COLOR_VERY_DARK_LIME_GREEN,
+)
+ if(!isobj(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(isnull(lich_mind))
+ stack_trace("A [type] was created with no target lich mind!")
+ return COMPONENT_INCOMPATIBLE
+
+ src.lich_mind = lich_mind
+ src.base_respawn_time = base_respawn_time
+ src.time_per_resurrection = time_per_resurrection
+ src.stun_per_resurrection = stun_per_resurrection
+ src.phylactery_color = phylactery_color
+
+ RegisterSignal(lich_mind, COMSIG_PARENT_QDELETING, .proc/on_lich_mind_lost)
+ RegisterSignal(SSdcs, COMSIG_GLOB_MOB_DEATH, .proc/check_if_lich_died)
+
+ var/obj/obj_parent = parent
+ obj_parent.name = "ensouled [obj_parent.name]"
+ obj_parent.add_atom_colour(phylactery_color, ADMIN_COLOUR_PRIORITY)
+ obj_parent.AddComponent(/datum/component/stationloving, FALSE, TRUE)
+
+ RegisterSignal(obj_parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+
+ SSpoints_of_interest.make_point_of_interest(obj_parent)
+
+/datum/component/phylactery/Destroy()
+ var/obj/obj_parent = parent
+ obj_parent.name = initial(obj_parent.name)
+ obj_parent.remove_atom_colour(ADMIN_COLOUR_PRIORITY, phylactery_color)
+ // Stationloving items should really never be made a phylactery so I feel safe in doing this
+ qdel(obj_parent.GetComponent(/datum/component/stationloving))
+
+ UnregisterSignal(obj_parent, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(SSdcs, COMSIG_GLOB_MOB_DEATH)
+ // Sweep up any revive signals left on the mind's current
+ UnregisterSignal(lich_mind.current, COMSIG_LIVING_REVIVE)
+
+ lich_mind = null
+ return ..()
+
+/**
+ * Signal proc for [COMSIG_PARENT_EXAMINE].
+ *
+ * Gives some flavor for the phylactery on examine.
+ */
+/datum/component/phylactery/proc/on_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(IS_WIZARD(user) || isobserver(user))
+ if(user.mind == lich_mind)
+ var/time_to_revive = base_respawn_time + (num_resurrections * time_per_resurrection)
+ examine_list += span_green("Your phylactery. The next time you meet an untimely demise, \
+ you will revive at this object in [time_to_revive / 10 / 60] minute\s.")
+ else
+ examine_list += span_green("A lich's phylactery. This one belongs to [lich_mind].")
+
+ if(num_resurrections > 0)
+ examine_list += span_green("There's [num_resurrections] notches in the side of it.")
+
+ else
+ examine_list += span_green("A terrible aura surrounds this item. Its very existence is offensive to life itself...")
+
+/**
+ * Signal proc for [COMSIG_PARENT_QDELETING] registered on the lich's mind.
+ *
+ * Minds shouldn't be getting deleted but if for some ungodly reason
+ * the lich'd mind is deleted our component should go with it, as
+ * we don't have a reason to exist anymore.
+ */
+/datum/component/phylactery/proc/on_lich_mind_lost(datum/source)
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/**
+ * Signal proc for [COMSIG_GLOB_MOB_DEATH].
+ *
+ * If the mob containing our lich's mind is killed,
+ * we can initiate the revival process.
+ *
+ * We use the global mob death signal here,
+ * instead of registering the normal death signal,
+ * as it's entirely possible the wizard mindswaps
+ * or is gibbed or something wacky happens, and
+ * we need to make sure WHOEVER has our mind is dead
+ */
+/datum/component/phylactery/proc/check_if_lich_died(datum/source, mob/living/died, gibbed)
+ SIGNAL_HANDLER
+
+ if(!died.mind)
+ return
+
+ if(died.mind != lich_mind)
+ return
+
+ // If we aren't gibbed, we need to check if the lich is
+ // revived at some point between returning
+ if(!gibbed)
+ RegisterSignal(died, COMSIG_LIVING_REVIVE, .proc/stop_timer)
+
+ // Start revival
+ var/time_to_revive = base_respawn_time + (num_resurrections * time_per_resurrection)
+ revive_timer = addtimer(CALLBACK(src, .proc/revive_lich, died), time_to_revive, TIMER_UNIQUE|TIMER_STOPPABLE)
+ to_chat(died, span_green("You feel your soul being dragged back to this world... \
+ you will revive at your phylactery in [time_to_revive / 10 / 60] minute\s."))
+
+/**
+ * Signal proc for [COMSIG_LIVING_REVIVE].
+ *
+ * If our lich's mob is revived at some point before returning, stop the timer
+ */
+/datum/component/phylactery/proc/stop_timer(mob/living/source, full_heal, admin_revive)
+ SIGNAL_HANDLER
+
+ deltimer(revive_timer)
+ revive_timer = null
+
+ UnregisterSignal(source, COMSIG_LIVING_REVIVE)
+
+/**
+ * Actually undergo the process of reviving the lich at the site of the phylacery.
+ *
+ * Arguments
+ * * corpse - optional, the old body of the lich. Can be QDELETED or null.
+ */
+/datum/component/phylactery/proc/revive_lich(mob/living/corpse)
+ // If we have a current, and it's not dead, don't yoink their mind
+ // But if we don't have a current (body destroyed) move on like normal
+ if(lich_mind.current && lich_mind.current.stat != DEAD)
+ CRASH("[type] - revive_lich was called when the lich's mind had a current mob that wasn't dead.")
+
+ var/turf/parent_turf = get_turf(parent)
+ if(!istype(parent_turf))
+ CRASH("[type] - revive_lich was called when the phylactery was in an invalid location (nullspace?) (was in: [parent_turf]).")
+
+ revive_timer = null
+ var/mob/living/carbon/human/lich = new(parent_turf)
+ ADD_TRAIT(lich, TRAIT_NO_SOUL, LICH_TRAIT)
+
+ var/obj/item/organ/brain/new_lich_brain = lich.getorganslot(ORGAN_SLOT_BRAIN)
+ if(new_lich_brain) // Prevent MMI cheese
+ new_lich_brain.organ_flags &= ~ORGAN_VITAL
+ new_lich_brain.decoy_override = TRUE
+
+ // Give them some duds
+ lich.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(lich), ITEM_SLOT_FEET)
+ lich.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(lich), ITEM_SLOT_ICLOTHING)
+ lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), ITEM_SLOT_OCLOTHING)
+ lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), ITEM_SLOT_HEAD)
+
+ // Fix their name
+ lich.dna.real_name = lich_mind.name
+ lich.real_name = lich_mind.name
+ // Slap the lich mind in and get their ghost
+ lich_mind.transfer_to(lich)
+ lich_mind.grab_ghost(force = TRUE)
+ // Make sure they're a spooky skeleton, and their DNA is right
+ lich.set_species(/datum/species/skeleton)
+ lich.dna.generate_unique_enzymes()
+
+ to_chat(lich, span_green("Your bones clatter and shudder as you are pulled back into this world!"))
+ num_resurrections++
+ lich.Paralyze(stun_per_resurrection * num_resurrections)
+
+ if(!QDELETED(corpse))
+ UnregisterSignal(corpse, COMSIG_LIVING_REVIVE)
+
+ if(iscarbon(corpse))
+ var/mob/living/carbon/carbon_body = corpse
+ for(var/obj/item/organ/to_drop as anything in carbon_body.internal_organs)
+ // Skip the brain - it can disappear, we don't need it anymore
+ if(istype(to_drop, /obj/item/organ/brain))
+ continue
+
+ // For the rest, drop all the organs onto the floor (for style)
+ to_drop.Remove(carbon_body)
+ to_drop.forceMove(corpse.drop_location())
+
+ var/turf/body_turf = get_turf(corpse)
+ var/wheres_wizdo = dir2text(get_dir(body_turf, parent_turf))
+ if(wheres_wizdo)
+ corpse.visible_message(span_warning("Suddenly, [corpse.name]'s corpse falls to pieces! You see a strange energy rise from the remains, and speed off towards the [wheres_wizdo]!"))
+ body_turf.Beam(parent_turf, icon_state = "lichbeam", time = 1 SECONDS * (num_resurrections + 1))
+
+ corpse.dust(drop_items = TRUE)
+
+ return TRUE
diff --git a/code/datums/components/stationloving.dm b/code/datums/components/stationloving.dm
index c31109ea6bc..8c3788895e2 100644
--- a/code/datums/components/stationloving.dm
+++ b/code/datums/components/stationloving.dm
@@ -66,7 +66,8 @@
/datum/component/stationloving/proc/check_soul_imbue()
SIGNAL_HANDLER
- return disallow_soul_imbue
+ if(disallow_soul_imbue)
+ return COMPONENT_BLOCK_IMBUE
/datum/component/stationloving/proc/check_mark_retrieval()
SIGNAL_HANDLER
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index 6eee7a70790..13a1f267db7 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -237,12 +237,10 @@
/datum/spellbook_entry/lichdom
name = "Bind Soul"
- desc = "A dark necromantic pact that can forever bind your soul to an \
- item of your choosing. So long as both your body and the item remain \
- intact and on the same plane you can revive from death, though the time \
- between reincarnations grows steadily with use, along with the weakness \
- that the new skeleton body will experience upon 'birth'. Note that \
- becoming a lich destroys all internal organs except the brain."
+ desc = "A dark necromantic pact that can forever bind your soul to an item of your choosing, \
+ turning you into an immortal Lich. So long as the item remains intact, you will revive from death, \
+ no matter the circumstances. Be wary - with each revival, your body will become weaker, and \
+ it will become easier for others to find your item of power."
spell_type = /obj/effect/proc_holder/spell/targeted/lichdom
category = "Defensive"
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 75b9c85cff0..0baefc9041b 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -286,25 +286,26 @@
name = "expensive forgery"
icon = 'icons/hud/screen_gen.dmi'
icon_state = "x2"
- var/possible = list(/obj/item/ship_in_a_bottle,
- /obj/item/gun/energy/pulse,
- /obj/item/book/granter/martial/carp,
- /obj/item/melee/supermatter_sword,
- /obj/item/shield/changeling,
- /obj/item/lava_staff,
- /obj/item/energy_katana,
- /obj/item/hierophant_club,
- /obj/item/his_grace,
- /obj/item/gun/energy/minigun,
- /obj/item/gun/ballistic/automatic/l6_saw,
- /obj/item/gun/magic/staff/chaos,
- /obj/item/gun/magic/staff/spellblade,
- /obj/item/gun/magic/wand/death,
- /obj/item/gun/magic/wand/fireball,
- /obj/item/stack/telecrystal/twenty,
- /obj/item/nuke_core,
- /obj/item/phylactery,
- /obj/item/banhammer)
+ var/static/possible = list(
+ /obj/item/ship_in_a_bottle,
+ /obj/item/gun/energy/pulse,
+ /obj/item/book/granter/martial/carp,
+ /obj/item/melee/supermatter_sword,
+ /obj/item/shield/changeling,
+ /obj/item/lava_staff,
+ /obj/item/energy_katana,
+ /obj/item/hierophant_club,
+ /obj/item/his_grace,
+ /obj/item/gun/energy/minigun,
+ /obj/item/gun/ballistic/automatic/l6_saw,
+ /obj/item/gun/magic/staff/chaos,
+ /obj/item/gun/magic/staff/spellblade,
+ /obj/item/gun/magic/wand/death,
+ /obj/item/gun/magic/wand/fireball,
+ /obj/item/stack/telecrystal/twenty,
+ /obj/item/nuke_core,
+ /obj/item/banhammer,
+ )
/obj/item/fakeartefact/Initialize(mapload)
. = ..()
diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm
index aa29741407a..a43f2546e8a 100644
--- a/code/modules/spells/spell_types/lichdom.dm
+++ b/code/modules/spells/spell_types/lichdom.dm
@@ -1,166 +1,74 @@
/obj/effect/proc_holder/spell/targeted/lichdom
name = "Bind Soul"
- desc = "A dark necromantic pact that can forever bind your soul to an \
- item of your choosing. So long as both your body and the item remain \
- intact and on the same plane you can revive from death, though the time \
- between reincarnations grows steadily with use, along with the weakness \
- that the new skeleton body will experience upon 'birth'. Note that \
- becoming a lich destroys all internal organs except the brain."
- school = SCHOOL_NECROMANCY
- charge_max = 10
- clothes_req = FALSE
+ desc = "A spell that binds your soul to an item in your hands. \
+ Binding your soul to an item will turn you into an immortal Lich. \
+ So long as the item remains intact, you will revive from death, \
+ no matter the circumstances."
+ action_icon = 'icons/mob/actions/actions_spells.dmi'
+ action_icon_state = "skeleton"
centcom_cancast = FALSE
invocation = "NECREM IMORTIUM!"
invocation_type = INVOCATION_SHOUT
+ school = SCHOOL_NECROMANCY
+ level_max = 0 // Cannot be improved (yet)
range = -1
- level_max = 0 //cannot be improved
- cooldown_min = 10
+ charge_max = 1 SECONDS
+ cooldown_min = 1 SECONDS
+ clothes_req = FALSE
include_user = TRUE
- action_icon = 'icons/mob/actions/actions_spells.dmi'
- action_icon_state = "skeleton"
+/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets, mob/user = usr)
+ for(var/mob/living/caster in targets)
-/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets,mob/user = usr)
- for(var/mob/M in targets)
- var/list/hand_items = list()
- if(iscarbon(M))
- hand_items = list(M.get_active_held_item(),M.get_inactive_held_item())
- if(!hand_items.len)
- to_chat(M, span_warning("You must hold an item you wish to make your phylactery!"))
+ if(HAS_TRAIT(caster, TRAIT_NO_SOUL))
+ to_chat(caster, span_warning("You don't have a soul to bind!"))
return
- var/obj/item/marked_item
-
- for(var/obj/item/item in hand_items)
- // I ensouled the nuke disk once. But it's probably a really
- // mean tactic, so probably should discourage it.
- if((item.item_flags & ABSTRACT) || HAS_TRAIT(item, TRAIT_NODROP) || SEND_SIGNAL(item, COMSIG_ITEM_IMBUE_SOUL, user))
- continue
- marked_item = item
- to_chat(M, span_warning("You begin to focus your very being into [item]..."))
- break
-
- if(!marked_item)
- to_chat(M, span_warning("None of the items you hold are suitable for emplacement of your fragile soul."))
+ var/obj/item/marked_item = caster.get_active_held_item()
+ if(marked_item.item_flags & ABSTRACT)
+ return
+ if(HAS_TRAIT(marked_item, TRAIT_NODROP))
+ to_chat(caster, span_warning("[marked_item] is stuck to your hand - it wouldn't be a wise idea to place your soul into it."))
+ return
+ // I ensouled the nuke disk once.
+ // But it's a really mean tactic,
+ // so we probably should disallow it.
+ if(SEND_SIGNAL(marked_item, COMSIG_ITEM_IMBUE_SOUL, user) & COMPONENT_BLOCK_IMBUE)
+ to_chat(caster, span_warning("[marked_item] is not suitable for emplacement of your fragile soul."))
return
playsound(user, 'sound/effects/pope_entry.ogg', 100)
- if(!do_after(M, 5 SECONDS, target = marked_item, timed_action_flags = IGNORE_HELD_ITEM))
- to_chat(M, span_warning("Your soul snaps back to your body as you stop ensouling [marked_item]!"))
+ to_chat(caster, span_green("You begin to focus your very being into [marked_item]..."))
+ if(!do_after(caster, 5 SECONDS, target = marked_item, timed_action_flags = IGNORE_HELD_ITEM))
+ to_chat(caster, span_warning("Your soul snaps back to your body as you stop ensouling [marked_item]!"))
return
- marked_item.name = "ensouled [marked_item.name]"
- marked_item.desc += "\nA terrible aura surrounds this item, its very existence is offensive to life itself..."
- marked_item.add_atom_colour("#003300", ADMIN_COLOUR_PRIORITY)
- marked_item.AddComponent(/datum/component/stationloving, FALSE, TRUE)
+ marked_item.AddComponent(/datum/component/phylactery, caster.mind)
- new /obj/item/phylactery(marked_item, M.mind)
+ caster.set_species(/datum/species/skeleton)
+ to_chat(caster, span_userdanger("With a hideous feeling of emptiness you watch in horrified fascination \
+ as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! \
+ As your organs crumble to dust in your fleshless chest you come to terms with your choice. \
+ You're a lich!"))
- to_chat(M, span_userdanger("With a hideous feeling of emptiness you watch in horrified fascination as skin sloughs off bone! Blood boils, nerves disintegrate, eyes boil in their sockets! As your organs crumble to dust in your fleshless chest you come to terms with your choice. You're a lich!"))
- M.set_species(/datum/species/skeleton)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- H.dropItemToGround(H.w_uniform)
- H.dropItemToGround(H.wear_suit)
- H.dropItemToGround(H.head)
- H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), ITEM_SLOT_OCLOTHING)
- H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), ITEM_SLOT_HEAD)
- H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), ITEM_SLOT_ICLOTHING)
+ if(iscarbon(caster))
+ var/mob/living/carbon/carbon_caster = caster
+ var/obj/item/organ/brain/lich_brain = carbon_caster.getorganslot(ORGAN_SLOT_BRAIN)
+ if(lich_brain) // This prevents MMIs being used to stop lich revives
+ lich_brain.organ_flags &= ~ORGAN_VITAL
+ lich_brain.decoy_override = TRUE
- // you only get one phylactery.
- M.mind.RemoveSpell(src)
- ADD_TRAIT(M, TRAIT_NO_SOUL, LICH_TRAIT)
+ if(ishuman(caster))
+ var/mob/living/carbon/human/human_caster = caster
+ human_caster.dropItemToGround(human_caster.w_uniform)
+ human_caster.dropItemToGround(human_caster.wear_suit)
+ human_caster.dropItemToGround(human_caster.head)
+ human_caster.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(human_caster), ITEM_SLOT_OCLOTHING)
+ human_caster.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(human_caster), ITEM_SLOT_HEAD)
+ human_caster.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(human_caster), ITEM_SLOT_ICLOTHING)
-
-/obj/item/phylactery
- name = "phylactery"
- desc = "Stores souls. Revives liches. Also repels mosquitos."
- icon = 'icons/obj/guns/projectiles.dmi'
- icon_state = "bluespace"
- color = COLOR_VERY_DARK_LIME_GREEN
- light_system = MOVABLE_LIGHT
- light_range = 3
- light_color = COLOR_VERY_DARK_LIME_GREEN
- var/resurrections = 0
- var/datum/mind/mind
- var/respawn_time = 1800
-
- var/static/active_phylacteries = 0
-
-/obj/item/phylactery/Initialize(mapload, datum/mind/newmind)
- . = ..()
- if(!mind)
- stack_trace("A phylactery was created with no target mind")
- return INITIALIZE_HINT_QDEL
- mind = newmind
- name = "phylactery of [mind.name]"
-
- if(iscarbon(mind.current))
- var/mob/living/carbon/immortal_mob = mind.current
- var/obj/item/organ/brain/B = immortal_mob.getorganslot(ORGAN_SLOT_BRAIN)
- if(B) // this prevents MMIs being used
- B.organ_flags &= ~ORGAN_VITAL
- B.decoy_override = TRUE
-
- active_phylacteries++
- SSpoints_of_interest.make_point_of_interest(src)
- START_PROCESSING(SSobj, src)
-
-/obj/item/phylactery/Destroy(force=FALSE)
- STOP_PROCESSING(SSobj, src)
- active_phylacteries--
- . = ..()
-
-/obj/item/phylactery/process()
- if(QDELETED(mind))
- qdel(src)
- return
-
- if(!mind.current || (mind.current && mind.current.stat == DEAD))
- addtimer(CALLBACK(src, .proc/rise), respawn_time, TIMER_UNIQUE)
-
-/obj/item/phylactery/proc/rise()
- if(mind.current && mind.current.stat != DEAD)
- return "[mind] already has a living body: [mind.current]"
-
- var/turf/item_turf = get_turf(src)
- if(!item_turf)
- return "[src] is not at a turf? NULLSPACE!?"
-
- var/mob/living/old_body = mind.current
- var/mob/living/carbon/human/lich = new(item_turf)
-
- var/obj/item/organ/brain/B = lich.getorganslot(ORGAN_SLOT_BRAIN)
- if(B) // this prevents MMIs being used
- B.organ_flags &= ~ORGAN_VITAL
- B.decoy_override = TRUE
-
- lich.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal/magic(lich), ITEM_SLOT_FEET)
- lich.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(lich), ITEM_SLOT_ICLOTHING)
- lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), ITEM_SLOT_OCLOTHING)
- lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), ITEM_SLOT_HEAD)
-
- lich.real_name = mind.name
- mind.transfer_to(lich)
- mind.grab_ghost(force=TRUE)
- lich.hardset_dna(null,null,null,lich.real_name,null, new /datum/species/skeleton)
- to_chat(lich, span_warning("Your bones clatter and shudder as you are pulled back into this world!"))
- var/turf/body_turf = get_turf(old_body)
- lich.Paralyze(200 + 200*resurrections)
- resurrections++
- if(old_body?.loc)
- if(iscarbon(old_body))
- var/mob/living/carbon/C = old_body
- for(var/obj/item/W in C)
- C.dropItemToGround(W)
- for(var/X in C.internal_organs)
- var/obj/item/organ/I = X
- I.Remove(C)
- I.forceMove(body_turf)
- var/wheres_wizdo = dir2text(get_dir(body_turf, item_turf))
- if(wheres_wizdo)
- old_body.visible_message(span_warning("Suddenly [old_body.name]'s corpse falls to pieces! You see a strange energy rise from the remains, and speed off towards the [wheres_wizdo]!"))
- body_turf.Beam(item_turf,icon_state="lichbeam", time = 10 + 10 * resurrections)
- old_body.dust()
- return "Respawn of [mind] successful."
+ // You only get one phylactery.
+ caster.mind.RemoveSpell(src)
+ // And no soul. You just sold it
+ ADD_TRAIT(caster, TRAIT_NO_SOUL, LICH_TRAIT)
diff --git a/code/modules/unit_tests/create_and_destroy.dm b/code/modules/unit_tests/create_and_destroy.dm
index 329fe24cc32..cad988d6c63 100644
--- a/code/modules/unit_tests/create_and_destroy.dm
+++ b/code/modules/unit_tests/create_and_destroy.dm
@@ -37,8 +37,6 @@
ignore += typesof(/obj/item/modular_computer/processor)
//Very finiky, blacklisting to make things easier
ignore += typesof(/obj/item/poster/wanted)
- //We can't pass a mind into this
- ignore += typesof(/obj/item/phylactery)
//This expects a seed, we can't pass it
ignore += typesof(/obj/item/food/grown)
//Nothing to hallucinate if there's nothing to hallicinate
diff --git a/tgstation.dme b/tgstation.dme
index abdb106a95a..80d13630ab4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -736,6 +736,7 @@
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\payment.dm"
#include "code\datums\components\pellet_cloud.dm"
+#include "code\datums\components\phylactery.dm"
#include "code\datums\components\pricetag.dm"
#include "code\datums\components\punchcooldown.dm"
#include "code\datums\components\radiation_countdown.dm"