Reverts "Merges ghosts into spirits & a rework", but removes ghosts from Pride's mirror. (#95779)

This commit is contained in:
Y0SH1M4S73R
2026-05-04 16:37:11 -04:00
committed by GitHub
parent 5a409f66c7
commit 9e92c0aa3d
16 changed files with 237 additions and 108 deletions
@@ -55,7 +55,7 @@
it will become easier for others to find your item of power."
spell_type = /datum/action/cooldown/spell/lichdom
category = SPELLBOOK_CATEGORY_DEFENSIVE
no_coexistence_typecache = list(/datum/action/cooldown/spell/splattercasting, /datum/spellbook_entry/perks/wormborn)
no_coexistence_typecache = list(/datum/action/cooldown/spell/splattercasting, /datum/spellbook_entry/perks/wormborn, /datum/spellbook_entry/ghostliness)
/datum/spellbook_entry/chuunibyou
name = "Chuuni Invocations"
@@ -59,4 +59,13 @@
cost = 2 // Puts it at 3 cost if you go for safety instant summons, but teleporting anywhere on screen is pretty good.
category = SPELLBOOK_CATEGORY_MOBILITY
/datum/spellbook_entry/ghostliness
name = "Forsake Body"
desc = "A necromantic spell which permanently severs your soul from your body, and partially anchors it to the material plane. \
In this state, you can enter a state of incorporeality, allowing you to pass through solid matter. This, however, includes \
most such matter on or inside of you."
spell_type = /datum/action/cooldown/spell/ghostliness
category = SPELLBOOK_CATEGORY_MOBILITY
no_coexistence_typecache = list(/datum/spellbook_entry/lichdom, /datum/spellbook_entry/splattercasting)
#undef SPELLBOOK_CATEGORY_MOBILITY
@@ -99,7 +99,7 @@
draining from you over time. You can replenish it from your victims, specifically their necks."
spell_type = /datum/action/cooldown/spell/splattercasting
category = SPELLBOOK_CATEGORY_OFFENSIVE
no_coexistence_typecache = list(/datum/action/cooldown/spell/lichdom)
no_coexistence_typecache = list(/datum/action/cooldown/spell/lichdom, /datum/spellbook_entry/ghostliness)
/datum/spellbook_entry/sanguine_strike
name = "Exsanguinating Strike"
@@ -325,14 +325,6 @@
new /obj/effect/decal/cleanable/blood/gibs/old(src)
new /obj/item/book/granter/crafting_recipe/boneyard_notes(src)
/obj/structure/closet/crate/grave/skeleton
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
affect_mood = TRUE
/obj/structure/closet/crate/grave/skeleton/PopulateContents()
. = ..()
new /mob/living/carbon/human/species/skeleton(src)
//***Fluff items for lore/intrigue
/obj/item/paper/crumpled/muddy/fluff/elephant_graveyard
name = "posted warning"
@@ -1108,8 +1108,11 @@
/mob/living/carbon/human/species/lizard/silverscale
race = /datum/species/lizard/silverscale
/mob/living/carbon/human/species/spirit
race = /datum/species/spirit
/mob/living/carbon/human/species/ghost
race = /datum/species/ghost
race = /datum/species/spirit/ghost
/mob/living/carbon/human/species/ethereal
race = /datum/species/ethereal
@@ -1,23 +1,22 @@
///Spirit mob that lacks legs but still roams the station as part of the unliving.
/datum/species/ghost
name = "Ghost"
id = SPECIES_GHOST
/datum/species/spirit
name = "Spirit"
id = SPECIES_SPIRIT
inherent_traits = list(
TRAIT_GENELESS,
TRAIT_MOVE_FLYING,
TRAIT_NEVER_WOUNDED,
TRAIT_NOBLOOD,
TRAIT_NODISMEMBER,
TRAIT_NO_DNA_COPY,
TRAIT_NO_FLOATING_ANIM,
TRAIT_NO_PLASMA_TRANSFORM,
TRAIT_NO_UNDERWEAR,
TRAIT_RESISTLOWPRESSURE,
TRAIT_UNHUSKABLE,
TRAIT_NO_FLOATING_ANIM,
TRAIT_MOVE_FLYING,
)
inherent_biotypes = MOB_HUMANOID | MOB_SPIRIT | MOB_UNDEAD
inherent_biotypes = MOB_SPIRIT | MOB_UNDEAD
no_equip_flags = ITEM_SLOT_FEET
changesource_flags = MIRROR_BADMIN | WABBAJACK | SLIME_EXTRACT | MIRROR_PRIDE | MIRROR_MAGIC
changesource_flags = MIRROR_BADMIN | MIRROR_PRIDE | MIRROR_MAGIC | WABBAJACK | SLIME_EXTRACT
sexes = FALSE
meat = /obj/item/ectoplasm
@@ -33,45 +32,34 @@
bodypart_overrides = list(
BODY_ZONE_HEAD = /obj/item/bodypart/head/ghost,
BODY_ZONE_CHEST = /obj/item/bodypart/chest/ghost,
BODY_ZONE_CHEST = /obj/item/bodypart/chest/ghost/spirit,
BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/ghost,
BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/ghost,
)
///Innate passthrough ability given to ghosts that allows them to phase but drops their stuff.
var/datum/action/innate/toggle_passthrough/passthrough_ability
///Boolean on whether this species type is available at roundstart during halloween, used to deny subtypes.
var/halloween_exclusive = TRUE
/datum/species/ghost/check_roundstart_eligible()
if(check_holidays(HALLOWEEN))
/datum/species/spirit/check_roundstart_eligible()
if(check_holidays(HALLOWEEN) && halloween_exclusive)
return TRUE
return ..()
/datum/species/ghost/on_species_gain(mob/living/carbon/human/new_ghost, datum/species/old_species, pref_load, regenerate_icons)
. = ..()
passthrough_ability = new(src)
passthrough_ability.Grant(new_ghost)
/datum/species/spirit/get_physical_attributes()
return "Spirits are the spiritual remains of long-passed entities. They lack legs, can fly, but still eat, breathe, hear and see."
/datum/species/ghost/on_species_loss(mob/living/carbon/human/former_ghost, datum/species/new_species, pref_load)
QDEL_NULL(passthrough_ability)
return ..()
/datum/species/ghost/get_physical_attributes()
return "Ghosts are the spiritual remains of long-passed entities. They lack legs, can fly, and phase through walls, \
but still eat, breathe, hear and see."
/datum/species/ghost/get_species_description()
/datum/species/spirit/get_species_description()
return "Spirits are spirits of long-dead creatures whom, for one reason or another, still roam around."
/datum/species/ghost/get_species_lore()
/datum/species/spirit/get_species_lore()
return list(
"Ghosts are one of the spookiest creatures known in the galaxy. \
While they still need their protein to sustain themselves, they are able to control their own bodies, \
going through walls and getting rid of all their posessions at will. \
Most knowledge known about them is kept secret by Nanotrasen's top Chaplains, who are keen \
to keep it private.",
"Spirits are the non-physical remains that linger onto their mortal coil. \
They still need their protein and organs to keep themselves \"alive\", \
which leads to many of them still believing they are still part of the living, \
whether or not they are is a very open-ended debate between philosophers.",
)
/datum/species/ghost/create_pref_unique_perks()
/datum/species/spirit/create_pref_unique_perks()
var/list/to_add = list()
to_add += list(list(
@@ -81,14 +69,6 @@
SPECIES_PERK_DESC = "Ghosts lack legs and float, preventing you from falling into holes in the ground.",
))
to_add += list(list(
SPECIES_PERK_TYPE = SPECIES_NEUTRAL_PERK,
SPECIES_PERK_ICON = "ghost",
SPECIES_PERK_NAME = "Incorporeal",
SPECIES_PERK_DESC = "Ghost carry their tombstones with them and are directly tied to it. \
dropping the tombstone will allow you to phase through solid matter, but leaves you vulnerable.",
))
to_add += list(list(
SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
SPECIES_PERK_ICON = "shoe-prints",
@@ -98,35 +78,102 @@
return to_add
/**
* Ghost subtype
* This is the type of ghost that can actually phase through walls,
* exclusive to magic mirrors & admins, as roundstart-ability to phase anywhere
* is not something that is generally fun to play against.
*/
/datum/species/spirit/ghost
name = "Ghost"
id = SPECIES_GHOST
inherent_traits = list(
TRAIT_GENELESS,
TRAIT_NEVER_WOUNDED,
TRAIT_NOBLOOD,
TRAIT_NODISMEMBER,
TRAIT_NO_DNA_COPY,
TRAIT_NO_PLASMA_TRANSFORM,
TRAIT_NO_UNDERWEAR,
TRAIT_UNHUSKABLE,
TRAIT_NO_FLOATING_ANIM,
TRAIT_MOVE_FLYING,
//ghost-unique
TRAIT_SEE_BLESSED_TILES,
)
//they have a different chest.
bodypart_overrides = list(
BODY_ZONE_HEAD = /obj/item/bodypart/head/ghost,
BODY_ZONE_CHEST = /obj/item/bodypart/chest/ghost,
BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/ghost,
BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/ghost,
)
changesource_flags = MIRROR_BADMIN | WABBAJACK
halloween_exclusive = FALSE
///Innate passthrough ability given to ghosts that allows them to phase but drops their stuff.
var/datum/action/innate/toggle_passthrough/passthrough_ability
/datum/species/spirit/ghost/get_physical_attributes()
return "Ghosts are the spiritual remains of long-passed entities. They lack legs, can fly, can choose at will to become incorporeal, \
but still eat, breathe, hear and see."
/datum/species/spirit/ghost/get_species_lore()
return list(
"Ghosts are one of the spookiest creatures known in the galaxy. \
While they still need their protein to sustain themselves, they are able to control their own bodies, \
going through walls and getting rid of all their posessions at will. \
Most knowledge known about them is kept secret by Nanotrasen's top Chaplains, who are keen \
to keep it private.",
)
/datum/species/spirit/ghost/on_species_gain(mob/living/carbon/human/new_ghost, datum/species/old_species, pref_load, regenerate_icons)
. = ..()
passthrough_ability = new(src)
passthrough_ability.Grant(new_ghost)
for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
blessed_hud.check_hud(new_ghost)
/datum/species/spirit/ghost/on_species_loss(mob/living/carbon/human/former_ghost, datum/species/new_species, pref_load)
. = ..()
QDEL_NULL(passthrough_ability)
//this has to be called after parent so inherent traits is cleared before we update our HUDs
for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
blessed_hud.check_hud(former_ghost)
/datum/species/spirit/ghost/create_pref_unique_perks()
var/list/to_add = ..()
to_add += list(list(
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
SPECIES_PERK_ICON = "ghost",
SPECIES_PERK_NAME = "Incorporeal",
SPECIES_PERK_DESC = "Ghost are able to control their body to the extent where you can willingly make yourself able \
to phase through anything, including your own equipment.",
))
return to_add
/**
* Passthrough ability
*
* Ghost innate ability that allows them to enter ghost mode,
* which gives them the phasing ability, but makes them unable to use anything,
* and they will be tied to a tombstone that, if dug up, will kill them and turn them
* into a skeleton.
* which gives them the phasing ability, but makes them drop everything
* and completely unable to wear anything.
*/
/datum/action/innate/toggle_passthrough
name = "Toggle passthrough"
desc = "Toggles phasing through everything, including your hands. You are tied to your tombstone while this is active. \
At least you know how to keep your clothes on."
desc = "Toggles your ability to phase through everything, including your gear and any incompatible organs/limbs."
button_icon = 'icons/hud/actions.dmi'
button_icon_state = "ghost"
///Grave that appears when we're passing through, which we are also tied to.
var/obj/structure/closet/crate/grave/skeleton/grave
/datum/action/innate/toggle_passthrough/Grant(mob/grant_to)
. = ..()
grave = new()
//contents are initialized when the grave is robbed as that's when the crate is opened for the first time.
RegisterSignal(grave, COMSIG_CLOSET_CONTENTS_INITIALIZED, PROC_REF(on_grave_robbed))
RegisterSignal(grave, COMSIG_CLOSET_POST_OPEN, PROC_REF(post_grave_robbed))
RegisterSignal(grant_to, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(on_new_limb))
/datum/action/innate/toggle_passthrough/Remove(mob/remove_from)
if(!QDELING(remove_from))
swap_mode(force_off = TRUE)
QDEL_NULL(grave)
swap_mode(force_off = TRUE)
UnregisterSignal(remove_from, COMSIG_CARBON_POST_ATTACH_LIMB)
return ..()
/datum/action/innate/toggle_passthrough/Activate()
@@ -135,61 +182,75 @@
return
swap_mode()
/datum/action/innate/toggle_passthrough/IsAvailable(feedback)
if(!isliving(owner))
return FALSE
var/mob/living/living_owner = owner
if(living_owner.has_reagent(/datum/reagent/water/holywater))
return FALSE
//technically you can trap a ghost by blessing them as theyre phasing,
//but they can still be dragged out.
if(locate(/obj/effect/blessing) in get_turf(owner))
return FALSE
var/obj/item/bodypart/chest/their_chest = living_owner.get_bodypart(BODY_ZONE_CHEST)
if(!their_chest || !(their_chest.bodytype & BODYTYPE_GHOST))
return FALSE
return ..()
///Called when the owner of this action gets a new limb, if it isn't a ghost-limb
///the action will turn itself off, and you'll lose said limb if you try using this action again.
/datum/action/innate/toggle_passthrough/proc/on_new_limb(mob/source, obj/item/bodypart/new_part, special)
SIGNAL_HANDLER
if(!iscarbon(owner))
return
if(!(new_part.bodytype & BODYTYPE_GHOST))
swap_mode(force_off = TRUE)
///Swaps the mode, allowing us to phase through stuff but drops everything. Optional 'force_off' arg to prevent being able to turn it on.
/datum/action/innate/toggle_passthrough/proc/swap_mode(force_off)
//we can only turn off, early return if we're trying to turn it on instead.
if(force_off && HAS_TRAIT_FROM(owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT))
return
if(HAS_TRAIT_FROM(owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT))
grave.forceMove(get_turf(owner))
owner.AddComponent(/datum/component/leash, grave, distance = 7)
REMOVE_TRAIT(owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT)
owner.add_traits(list(
var/mob/living/carbon/carbon_owner = owner
var/datum/species/carbon_species = carbon_owner.dna.species
//drop any limbs & organs that can't phase, now that you're phasing.
for(var/obj/item/bodypart/bodypart as anything in carbon_owner.bodyparts)
if(!(bodypart.bodytype & BODYTYPE_GHOST))
bodypart.drop_limb(special = FALSE, dismembered = FALSE, move_to_floor = TRUE)
for(var/obj/item/organ/organ as anything in carbon_owner.organs)
if(!(organ.organ_flags & ORGAN_GHOST))
organ.Remove(owner, special = FALSE)
if(HAS_TRAIT_FROM(carbon_owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT))
REMOVE_TRAIT(carbon_owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT)
carbon_owner.add_traits(list(
TRAIT_MOVE_PHASING,
TRAIT_PIERCEIMMUNE,
TRAIT_INVISIBLE_TO_CAMERA,
TRAIT_HANDS_BLOCKED, //MOBILITY_USE | MOBILITY_PICKUP | MOBILITY_STORAGE
TRAIT_PULL_BLOCKED, //MOBILITY_PULL
TRAIT_UI_BLOCKED, //MOBILITY_UI
), SPECIES_TRAIT)
RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(on_owner_died))
), SPECIES_TRAIT)
carbon_species.update_no_equip_flags(carbon_owner, ALL)
RegisterSignal(carbon_owner, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, PROC_REF(attempt_move))
else
qdel(owner.GetComponent(/datum/component/leash))
owner.forceMove(get_turf(grave))
grave.moveToNullspace()
ADD_TRAIT(owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT)
owner.remove_traits(list(
ADD_TRAIT(carbon_owner, TRAIT_NO_FLOATING_ANIM, SPECIES_TRAIT)
carbon_owner.remove_traits(list(
TRAIT_MOVE_PHASING,
TRAIT_PIERCEIMMUNE,
TRAIT_INVISIBLE_TO_CAMERA,
TRAIT_HANDS_BLOCKED,
TRAIT_PULL_BLOCKED,
TRAIT_UI_BLOCKED,
), SPECIES_TRAIT)
UnregisterSignal(owner, COMSIG_LIVING_DEATH)
), SPECIES_TRAIT)
carbon_species.update_no_equip_flags(carbon_owner, initial(carbon_species.no_equip_flags))
UnregisterSignal(carbon_owner, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE)
/datum/action/innate/toggle_passthrough/proc/on_owner_died(mob/living/owner)
///Called when attempting to move to a new tile while the action is active, returns to cancel moving.
/datum/action/innate/toggle_passthrough/proc/attempt_move(mob/source, new_loc, direct)
SIGNAL_HANDLER
swap_mode(force_off = TRUE)
///Called when the contents are made, which means the grave has been 'opened', therefore robbed.
/datum/action/innate/toggle_passthrough/proc/on_grave_robbed(obj/structure/closet/crate/grave/skeleton/source)
SIGNAL_HANDLER
var/mob/living/carbon/human/species/skeleton/skeletons_in_the_closet = locate() in source.contents
skeletons_in_the_closet.real_name = owner.real_name
skeletons_in_the_closet.name = owner.name
if (owner.mind?.active)
owner.mind?.transfer_to(skeletons_in_the_closet, force_key_move = TRUE)
skeletons_in_the_closet.death(gibbed = TRUE) // This is a lie, but it stops the skeleton from deathgasping as if it only just died
if (isliving(owner)) // You know just in case we give an observer the crazy ability to walk through walls
var/mob/living/living_owner = owner
living_owner.forceMove(source)
living_owner.unequip_everything()
///Called AFTER the contents have been spit out, which means the owner is now in the skeleton. Let's clean up.
/datum/action/innate/toggle_passthrough/proc/post_grave_robbed(obj/structure/closet/crate/grave/skeleton/source)
SIGNAL_HANDLER
qdel(owner)
if(locate(/obj/effect/blessing) in new_loc)
to_chat(source, span_warning("Holy energies block your path!"))
return COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE
+1 -1
View File
@@ -55,7 +55,7 @@
if(!seen)
P.mobs_seen -= seen_ref
continue
if(!isobserver(seen) && !isghostspecies(seen))
if(!isobserver(seen) && !isspirit(seen))
continue
set_custom_materials(list(/datum/material/hauntium =SHEET_MATERIAL_AMOUNT))
break
@@ -859,10 +859,10 @@
randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS
/datum/reagent/mutationtoxin/ghost
name = "Ghost Mutation Toxin"
name = "Spirit Mutation Toxin"
description = "A spiritual toxin."
color = "#5EFF3B" //RGB: 94, 255, 59
race = /datum/species/ghost
race = /datum/species/spirit
taste_description = "ectoplasm"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS
@@ -0,0 +1,57 @@
/datum/action/cooldown/spell/ghostliness
name = "Forsake Body"
desc = "A spell that severs your soul from your body, loosely binding it to the material plane."
button_icon = 'icons/mob/simple/mob.dmi'
button_icon_state = "ghost"
school = SCHOOL_NECROMANCY
cooldown_time = 1 SECONDS
invocation = "GHO'AN GHO'AST!"
invocation_type = INVOCATION_SHOUT
spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC|SPELL_REQUIRES_STATION|SPELL_REQUIRES_MIND
spell_max_level = 1
/datum/action/cooldown/spell/ghostliness/can_cast_spell(feedback = TRUE)
. = ..()
if(!.)
return FALSE
if(!is_valid_target(owner))
if(feedback)
owner.balloon_alert(owner, "no soul!")
return FALSE
return TRUE
/datum/action/cooldown/spell/ghostliness/is_valid_target(atom/cast_on)
return ishuman(cast_on) && !HAS_TRAIT(owner, TRAIT_NO_SOUL)
/datum/action/cooldown/spell/ghostliness/cast(mob/living/carbon/human/cast_on)
. = ..()
if(isspirit(cast_on))
to_chat(cast_on, span_green("You begin to focus on loosening the bonds holding you to the material plane."))
else
to_chat(cast_on, span_green("You begin to focus on your very being, drawing it out of its corporeal vessel..."))
if(!do_after(cast_on, 5 SECONDS))
if(isspirit(cast_on))
to_chat(cast_on, span_warning("Your focus is broken, and you feel your material bindings snap tight once more."))
else
to_chat(cast_on, span_warning("Your focus is broken, and your soul snaps back into place."))
return
if(isspirit(cast_on))
to_chat(cast_on, span_green("You successfully loosen your bonds to the material plane, and can now slip partially out of it."))
else
to_chat(cast_on, span_danger("As the last trailing filament of your essence ceases intersection with your body, \
your perspective abruptly snaps to your new, ghostly figure! Your former vessel falls to the ground, vacant and devoid of volition!"))
var/mob/living/carbon/human/soulless_husk = new(cast_on.drop_location())
soulless_husk.setDir(cast_on.dir)
cast_on.dna.copy_dna(soulless_husk.dna, ALL)
soulless_husk.real_name = cast_on.real_name
soulless_husk.updateappearance(icon_update = TRUE, mutcolor_update = TRUE, mutations_overlay_update = TRUE)
soulless_husk.domutcheck()
ADD_TRAIT(soulless_husk, TRAIT_NO_SOUL, MAGIC_TRAIT)
ADD_TRAIT(soulless_husk, TRAIT_FLOORED, MAGIC_TRAIT)
cast_on.set_species(/datum/species/spirit/ghost)
qdel(src)
@@ -27,6 +27,11 @@
wing_types = null
butcher_replacement = null
//slightly different sprite meant to differentiate spirit from ghost.
/obj/item/bodypart/chest/ghost/spirit
icon_state = "spirit_chest"
limb_id = SPECIES_SPIRIT
/obj/item/bodypart/arm/left/ghost
icon = 'icons/mob/human/species/ghost.dmi'
icon_static = 'icons/mob/human/species/ghost.dmi'
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB