From 78cffc210111b00582f2fc6357ed8a08514ededa Mon Sep 17 00:00:00 2001
From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Date: Sun, 22 Jun 2025 04:02:06 -0400
Subject: [PATCH] Adds a new halloween species: Spirits (#90711)
Adds 2 new species: Spirits and Ghosts
Spirits are available roundstart during Halloween, Wabbajack and
Xenobio's black slime extract
Ghosts are available through Magic mirrors
They fly around, and don't have legs and instead float around. They also
can't get legs implanted onto themselves.
They also do have organs, so they are affected by flashbangs, they do
get hungry, they do need oxygen to survive (they don't take damage in
space but they do suffocate & get slowdown), and can process chems.
Gibbing a ghost gives ectoplasm, an ingredient for ghost burgers.
Chaplains also got a buff here, null rod-type weapons' bane is now
against Spirit mobs, rather than hardcoded revenants. This means it now
includes Spirits/Ghosts, but also Soulscythes & Cult shades.
Also re-adds https://github.com/tgstation/tgstation/pull/81630 which was
reverted in https://github.com/tgstation/tgstation/pull/86506 which I
assume was accidental.
Ghosts have an innate ability to become incorporeal, which allows them
to phase through walls and stuff. Using this will immediately make them
drop any non-ghost limb/organ (not implants cause I thought it would be
funny). This ability is not available if they have holy water in their
system, and like revenants they also can't walk over blessed tiles with
it. They are also invisible to cameras while using this (not the obscura
though).
Sprites taken from observers directly, if anyone wants to make custom
sprites for them feel free. If anyone wants to make this obtainable
somehow in-game as well I wouldn't be opposed, halloween is just where I
thought it would fit most.
This also adds a lot of fixes that I encountered trying to add this,
from systems that have been neglected throughout the years.
https://github.com/user-attachments/assets/e368d710-80a0-4c63-b271-1abe3dd41a5e
We haven't gotten a new halloween species in a long time and thought it
would be fun if you can play as an actual ghost, the soul that remains
after a person passes, so Halloween feels more haunted. It's overall
made in just good fun, with a bonus that Ghosts are a cool species to
play with as well for Wizards & maybe Chaplains in the future (Dead sect
when?)
:cl:
add: Added a new halloween species: Spirits, a species without legs and
instead floats.
add: Added a new magic mirror species: Ghosts, like spirits but with the
ability to become incorporeal, traversing through solid wall.
fix: Mobs unable to use storage items now can't use storage items.
fix: Mobs unable to use items can now not open airlocks & closets
fix: Mobs unable to pick items up can no longer pick items up and
immediately drop, moving one tile at a time.
fix: Mobs with intentional missing limbs (Alien larva) no longer show
their limbs as missing on examine (again)
fix: Golems' pref page had a missing icon, it now has one.
/:cl:
---------
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
(cherry picked from commit 096c0324028ba263e702abcc9deac7510e091941)
---
code/__DEFINES/DNA.dm | 6 +-
.../signals/signals_mob/signals_mob_main.dm | 3 +
code/__DEFINES/is_helpers.dm | 1 +
code/__DEFINES/mobs.dm | 15 +-
code/__DEFINES/surgery.dm | 6 +-
code/__DEFINES/traits/declarations.dm | 6 +
code/__DEFINES/traits/sources.dm | 2 +
code/_globalvars/traits/_traits.dm | 6 +-
code/_onclick/hud/screen_objects.dm | 18 +-
code/datums/mind/_mind.dm | 19 ++
code/datums/storage/storage.dm | 10 +-
code/game/atom/alternate_appearance.dm | 38 +--
code/game/machinery/doors/door.dm | 6 +-
code/game/objects/effects/blessing.dm | 13 +-
code/game/objects/items.dm | 5 +-
.../items/devices/scanners/health_analyzer.dm | 2 +-
code/game/objects/items/stacks/medical.dm | 5 +-
code/game/objects/items/weaponry.dm | 2 +-
.../structures/crates_lockers/closets.dm | 3 +
code/game/turfs/turf.dm | 2 +
code/modules/antagonists/ert/ert.dm | 8 +-
.../antagonists/heretic/heretic_antag.dm | 2 +
.../antagonists/survivalist/survivalist.dm | 8 +-
code/modules/antagonists/wizard/wizard.dm | 12 +-
.../jobs/job_types/chaplain/chaplain.dm | 4 +-
.../job_types/chaplain/chaplain_nullrod.dm | 2 +-
code/modules/library/bibles.dm | 4 +-
code/modules/lost_crew/damages/melee.dm | 2 +-
code/modules/lost_crew/damages/projectile.dm | 2 +-
code/modules/mining/equipment/mining_tools.dm | 2 +-
code/modules/mob/dead/observer/observer.dm | 4 +-
.../living/basic/cult/constructs/wraith.dm | 1 +
.../basic/space_fauna/revenant/_revenant.dm | 2 +-
code/modules/mob/living/brain/brain_item.dm | 7 +
code/modules/mob/living/carbon/carbon.dm | 10 +-
code/modules/mob/living/carbon/examine.dm | 14 +-
.../mob/living/carbon/human/_species.dm | 13 +-
code/modules/mob/living/carbon/human/human.dm | 9 +-
.../mob/living/carbon/human/human_defense.dm | 2 +-
.../carbon/human/species_types/ghost.dm | 256 ++++++++++++++++++
.../carbon/human/species_types/golems.dm | 2 +-
code/modules/mob/living/living.dm | 2 +
code/modules/mob/login.dm | 3 +-
code/modules/photography/camera/camera.dm | 4 +-
.../camera/camera_image_capturing.dm | 12 +-
code/modules/photography/photos/photo.dm | 2 +-
.../guns/ballistic/bows/bow_arrows.dm | 2 +-
.../guns/ballistic/bows/bow_types.dm | 2 +-
code/modules/religion/burdened/psyker.dm | 2 +-
code/modules/religion/deaconize.dm | 2 +-
.../religion/honorbound/honorbound_trauma.dm | 2 +-
.../religion/sparring/sparring_datum.dm | 2 +-
.../surgery/bodyparts/dismemberment.dm | 53 ++--
code/modules/surgery/bodyparts/helpers.dm | 29 +-
.../species_parts/ghost_bodyparts.dm | 51 ++++
.../surgery/organs/internal/ears/_ears.dm | 7 +
.../surgery/organs/internal/eyes/_eyes.dm | 8 +
.../surgery/organs/internal/liver/_liver.dm | 7 +
.../surgery/organs/internal/lungs/_lungs.dm | 7 +
.../organs/internal/stomach/_stomach.dm | 7 +
.../surgery/organs/internal/tongue/_tongue.dm | 9 +
.../modules/surgery/prosthetic_replacement.dm | 2 +-
...enshot_humanoids__datum_species_spirit.png | Bin 0 -> 2011 bytes
..._humanoids__datum_species_spirit_ghost.png | Bin 0 -> 2011 bytes
config/game_options.txt | 1 +
icons/hud/actions.dmi | Bin 8689 -> 9792 bytes
icons/mob/human/species/ghost.dmi | Bin 0 -> 2324 bytes
icons/obj/medical/organs/organs.dmi | Bin 52916 -> 52665 bytes
tgstation.dme | 2 +
69 files changed, 622 insertions(+), 130 deletions(-)
create mode 100644 code/modules/mob/living/carbon/human/species_types/ghost.dm
create mode 100644 code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
create mode 100644 code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_spirit.png
create mode 100644 code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_spirit_ghost.png
create mode 100644 icons/mob/human/species/ghost.dmi
diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index 706f2d760d5..fa90b1ee083 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -213,10 +213,8 @@ GLOBAL_LIST_INIT(organ_process_order, list(
ORGAN_SLOT_XENO_RESINSPINNER,
ORGAN_SLOT_XENO_ACIDGLAND,
ORGAN_SLOT_XENO_NEUROTOXINGLAND,
- ORGAN_SLOT_XENO_EGGSAC,))
-
-//Defines for Golem Species IDs
-#define SPECIES_GOLEM "golem"
+ ORGAN_SLOT_XENO_EGGSAC,
+))
// Defines for used in creating "perks" for the species preference pages.
/// A key that designates UI icon displayed on the perk.
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index 2e11369e52d..53fa1021a0f 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -80,6 +80,9 @@
#define COMSIG_MOB_MIND_BEFORE_MIDROUND_ROLL "mob_mind_transferred_out_of"
#define CANCEL_ROLL (1<<1)
+///signal sent when a mob has their holy role set. Sent to the mob having their role changed.
+#define COMSIG_MOB_MIND_SET_HOLY_ROLE "mob_mind_set_holy_role"
+
///from base of obj/allowed(mob/M): (/obj) returns ACCESS_ALLOWED if mob has id access to the obj
#define COMSIG_MOB_TRIED_ACCESS "tried_access"
#define ACCESS_ALLOWED (1<<0)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 4109dd9a7d3..aafd2954450 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -115,6 +115,7 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
//Human sub-species
#define isabductor(A) (is_species(A, /datum/species/abductor))
+#define isspirit(A) (is_species(A, /datum/species/spirit))
#define isgolem(A) (is_species(A, /datum/species/golem))
#define islizard(A) (is_species(A, /datum/species/lizard))
#define isashwalker(A) (is_species(A, /datum/species/lizard/ashwalker))
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 9dd06dae572..0c48ccdcf43 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -153,18 +153,20 @@
#define BODYTYPE_PLANT (1<<6)
//This limb is shadowy and will regen if shadowheal is active
#define BODYTYPE_SHADOW (1<<7)
+//This limb is a ghost limb and can phase through walls.
+#define BODYTYPE_GHOST (1<<8)
// SKYRAT EDIT ADDITION
/// Nanomachine bodypart
-#define BODYTYPE_NANO (1<<8)
+#define BODYTYPE_NANO (1<<9)
///The limb fits a modular custom shape
-#define BODYSHAPE_CUSTOM (1<<9)
+#define BODYSHAPE_CUSTOM (1<<10)
///The limb fits a taur body
-#define BODYSHAPE_TAUR (1<<10)
+#define BODYSHAPE_TAUR (1<<11)
///The limb causes shoes to no longer be displayed, useful for taurs.
-#define BODYSHAPE_HIDE_SHOES (1<<11)
+#define BODYSHAPE_HIDE_SHOES (1<<12)
///The limb causes glasses and hats to be drawn on layers 5 and 4 respectively. Currently used for snouts with the (Top) suffix, which are drawn on layer 6 and would normally cover facewear
-#define BODYSHAPE_ALT_FACEWEAR_LAYER (1<<12)
+#define BODYSHAPE_ALT_FACEWEAR_LAYER (1<<13)
// SKYRAT EDIT END
@@ -188,11 +190,14 @@
#define SPECIES_DULLAHAN "dullahan"
#define SPECIES_ETHEREAL "ethereal"
#define SPECIES_ETHEREAL_LUSTROUS "lustrous"
+#define SPECIES_GHOST "ghost"
+#define SPECIES_GOLEM "golem"
#define SPECIES_FELINE "felinid"
#define SPECIES_FLYPERSON "fly"
#define SPECIES_HUMAN "human"
#define SPECIES_JELLYPERSON "jelly"
#define SPECIES_SLIMEPERSON "slime"
+#define SPECIES_SPIRIT "spirit"
#define SPECIES_LUMINESCENT "luminescent"
#define SPECIES_STARGAZER "stargazer"
#define SPECIES_LIZARD "lizard"
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index 4ebd7bad0b7..a79522e6e21 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -32,10 +32,12 @@
#define ORGAN_HAZARDOUS (1<<12)
/// This is an external organ, not an inner one. Used in several checks.
#define ORGAN_EXTERNAL (1<<13)
+/// This is a ghost organ, which can be used for wall phasing.
+#define ORGAN_GHOST (1<<14)
/// This is a mutant organ, having this makes you a -derived mutant to health analyzers.
-#define ORGAN_MUTANT (1<<14)
+#define ORGAN_MUTANT (1<<15)
/// BUBBER EDIT ADD - Nanoswarm organ
-#define ORGAN_NANOMACHINE (1<<15)
+#define ORGAN_NANOMACHINE (1<<16)
/// Scarring on the right eye
#define RIGHT_EYE_SCAR (1<<0)
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 7b8d40cd831..7cd1083b798 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -750,6 +750,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Cannot be turned into a funny skeleton by the plasma river
#define TRAIT_NO_PLASMA_TRANSFORM "no_plasma_transform"
+/// Mind trait that allows you to see blessed tiles.
+#define TRAIT_SEE_BLESSED_TILES "sees_blessed_tiles"
+
/// Weather immunities, also protect mobs inside them.
#define TRAIT_LAVA_IMMUNE "lava_immune" //Used by lava turfs and The Floor Is Lava.
#define TRAIT_ASHSTORM_IMMUNE "ashstorm_immune"
@@ -976,6 +979,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// This mob always lands on their feet when they fall, for better or for worse.
#define TRAIT_CATLIKE_GRACE "catlike_grace"
+///Won't show up on cameras when they snap a photo.
+#define TRAIT_INVISIBLE_TO_CAMERA "invisible_to_camera"
+
///if the atom has a sticker attached to it
#define TRAIT_STICKERED "stickered"
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index bd374ac0a67..edea6b63478 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -33,6 +33,8 @@
#define CHANGELING_TRAIT "changeling"
#define CULT_TRAIT "cult"
#define LICH_TRAIT "lich"
+///Comes from a holy source.
+#define HOLY_TRAIT "holy"
#define VENDING_MACHINE_TRAIT "vending_machine"
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 046a269a910..9b72332fd42 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -311,6 +311,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_GARLIC_BREATH" = TRAIT_GARLIC_BREATH,
"TRAIT_GARLIC_REAGENT" = TRAIT_GARLIC_REAGENT,
"TRAIT_GENELESS" = TRAIT_GENELESS,
+ "TRAIT_GHOSTROLE_ON_REVIVE" = TRAIT_GHOSTROLE_ON_REVIVE,
"TRAIT_GIANT" = TRAIT_GIANT,
"TRAIT_GODMODE" = TRAIT_GODMODE,
"TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
@@ -357,6 +358,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_IS_WET" = TRAIT_IS_WET,
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED,
"TRAIT_KISS_OF_DEATH" = TRAIT_KISS_OF_DEATH,
+ "TRAIT_SEE_BLESSED_TILES" = TRAIT_SEE_BLESSED_TILES,
"TRAIT_SYNDIE_KISS" = TRAIT_SYNDIE_KISS,
"TRAIT_KNOCKEDOUT" = TRAIT_KNOCKEDOUT,
"TRAIT_KNOW_ENGI_WIRES" = TRAIT_KNOW_ENGI_WIRES,
@@ -624,9 +626,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOGRAV_ALWAYS_DRIFT" = TRAIT_NOGRAV_ALWAYS_DRIFT,
"TRAIT_SPEECH_BOOSTER" = TRAIT_SPEECH_BOOSTER,
"TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING,
- "TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT,
- "TRAIT_GHOSTROLE_ON_REVIVE" = TRAIT_GHOSTROLE_ON_REVIVE,
"TRAIT_IGNORE_FIRE_PROTECTION" = TRAIT_IGNORE_FIRE_PROTECTION,
+ "TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT,
+ "TRAIT_INVISIBLE_TO_CAMERA" = TRAIT_INVISIBLE_TO_CAMERA,
"TRAIT_LEFT_EYE_SCAR" = TRAIT_LEFT_EYE_SCAR,
"TRAIT_RIGHT_EYE_SCAR" = TRAIT_RIGHT_EYE_SCAR,
"HUMAN_SENSORS_VISIBLE_WITHOUT_SUIT" = HUMAN_SENSORS_VISIBLE_WITHOUT_SUIT,
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 6b40f5858b9..4062759c1fa 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -763,6 +763,9 @@
var/mob/living/carbon/C = usr
C.check_self_for_injuries()
+/atom/movable/screen/healthdoll/proc/update_body_zones()
+ return
+
/atom/movable/screen/healthdoll/living
icon_state = "fullhealth0"
screen_loc = ui_living_healthdoll
@@ -777,15 +780,22 @@
/atom/movable/screen/healthdoll/human/Initialize(mapload, datum/hud/hud_owner)
. = ..()
+ if(isnull(hud_owner)) //we require a hud owner to work properly, so return out.
+ return
+ update_body_zones()
+ update_appearance()
+
+/atom/movable/screen/healthdoll/human/update_body_zones()
limbs = list()
- for(var/i in GLOB.all_body_zones)
+ vis_contents.Cut()
+ var/mob/living/carbon/human/owner = hud.mymob
+ for(var/body_zone in owner.get_all_limbs())
var/atom/movable/screen/healthdoll_limb/limb = new(src, null)
// layer chest above other limbs, it's the center after all
- limb.layer = i == BODY_ZONE_CHEST ? layer + 0.05 : layer
- limbs[i] = limb
+ limb.layer = body_zone == BODY_ZONE_CHEST ? layer + 0.05 : layer
+ limbs[body_zone] = limb
// why viscontents? why not overlays? - because i want to animate filters
vis_contents += limb
- update_appearance()
/atom/movable/screen/healthdoll/human/Destroy()
QDEL_LIST_ASSOC_VAL(limbs)
diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm
index e676a877864..ad05f409ff6 100644
--- a/code/datums/mind/_mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -139,6 +139,9 @@
if(NAMEOF(src, assigned_role))
set_assigned_role(var_value)
. = TRUE
+ if(NAMEOF(src, holy_role))
+ set_holy_role(var_value)
+ . = TRUE
if(!isnull(.))
datum_flags |= DF_VAR_EDITED
return
@@ -508,6 +511,22 @@
. = assigned_role
assigned_role = new_role
+///Sets your holy role, giving/taking away traits related to if you're gaining/losing it.
+/datum/mind/proc/set_holy_role(new_holy_role)
+ if(holy_role == new_holy_role)
+ return
+ var/was_holy = holy_role
+ holy_role = new_holy_role
+ if(holy_role)
+ ADD_TRAIT(src, TRAIT_SEE_BLESSED_TILES, HOLY_TRAIT)
+ else
+ REMOVE_TRAIT(src, TRAIT_SEE_BLESSED_TILES, HOLY_TRAIT)
+ SEND_SIGNAL(current, COMSIG_MOB_MIND_SET_HOLY_ROLE, new_holy_role)
+ //the signal stops tracking when losing holy roles, but since we're gaining it, give us our HUDs if we're becoming holy.
+ if(!was_holy && holy_role)
+ for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
+ blessed_hud.check_hud(current)
+
/// Sets us to the passed job datum, then greets them to their new job.
/// Use this one for when you're assigning this mind to a new job for the first time,
/// or for when someone's receiving a job they'd really want to be greeted to.
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
index 0f5b2b4deb7..ca22f81573e 100644
--- a/code/datums/storage/storage.dm
+++ b/code/datums/storage/storage.dm
@@ -921,7 +921,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return open_storage_on_signal(source, user) ? CLICK_ACTION_SUCCESS : NONE
/// Opens the storage to the mob, showing them the contents to their UI.
-/datum/storage/proc/open_storage(mob/to_show, can_reach_target = parent) // BUBBER EDIT CHANGE - ORIGINAL: /datum/storage/proc/open_storage(mob/to_show)
+/datum/storage/proc/open_storage(mob/living/to_show, can_reach_target = parent) // BUBBER EDIT CHANGE - ORIGINAL: /datum/storage/proc/open_storage(mob/living/to_show)
if(isobserver(to_show))
show_contents(to_show)
return FALSE
@@ -929,6 +929,14 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(!isliving(to_show) || !to_show.can_perform_action(can_reach_target, ALLOW_RESTING | FORBID_TELEKINESIS_REACH)) // BUBBER EDIT CHANGE -- ORIGINAL: if(!isliving(to_show) || !to_show.can_perform_action(parent, ALLOW_RESTING | FORBID_TELEKINESIS_REACH))
return FALSE
+ //because of the check above, it's safe to assume we're living now.
+ if(!(to_show.mobility_flags & MOBILITY_STORAGE))
+ return FALSE
+
+ //because of the check above, it's safe to assume we're living now.
+ if(!(to_show.mobility_flags & MOBILITY_STORAGE))
+ return FALSE
+
if(locked)
if(!silent)
parent.balloon_alert(to_show, "closed!")
diff --git a/code/game/atom/alternate_appearance.dm b/code/game/atom/alternate_appearance.dm
index 09b5c1190d1..6df75d2508a 100644
--- a/code/game/atom/alternate_appearance.dm
+++ b/code/game/atom/alternate_appearance.dm
@@ -101,6 +101,13 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
var/add_ghost_version = FALSE
var/datum/atom_hud/alternate_appearance/basic/observers/ghost_appearance
uses_global_hud_category = FALSE
+ ///List of signals we hook onto which we'll update HUDs when we receive this.
+ var/list/signals_registering = list(
+ COMSIG_MOB_ANTAGONIST_REMOVED,
+ COMSIG_MOB_GHOSTIZED,
+ COMSIG_MOB_MIND_TRANSFERRED_INTO,
+ COMSIG_MOB_MIND_TRANSFERRED_OUT_OF,
+ )
/datum/atom_hud/alternate_appearance/basic/New(key, image/I, options = AA_TARGET_SEE_APPEARANCE)
..()
@@ -131,20 +138,10 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
QDEL_NULL(ghost_appearance)
/datum/atom_hud/alternate_appearance/basic/track_mob(mob/new_viewer)
- RegisterSignals(new_viewer, list(
- COMSIG_MOB_ANTAGONIST_REMOVED,
- COMSIG_MOB_GHOSTIZED,
- COMSIG_MOB_MIND_TRANSFERRED_INTO,
- COMSIG_MOB_MIND_TRANSFERRED_OUT_OF,
- ), PROC_REF(check_hud), override = TRUE)
+ RegisterSignals(new_viewer, signals_registering, PROC_REF(check_hud), override = TRUE)
/datum/atom_hud/alternate_appearance/basic/untrack_mob(mob/former_viewer)
- UnregisterSignal(former_viewer, list(
- COMSIG_MOB_ANTAGONIST_REMOVED,
- COMSIG_MOB_GHOSTIZED,
- COMSIG_MOB_MIND_TRANSFERRED_INTO,
- COMSIG_MOB_MIND_TRANSFERRED_OUT_OF,
- ))
+ UnregisterSignal(former_viewer, signals_registering)
/datum/atom_hud/alternate_appearance/basic/add_atom_to_hud(atom/A)
LAZYINITLIST(A.hud_list)
@@ -189,13 +186,18 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
antag_datum_type = /datum/antagonist/cult
/datum/atom_hud/alternate_appearance/basic/blessed_aware
+ signals_registering = list(
+ COMSIG_MOB_ANTAGONIST_REMOVED,
+ COMSIG_MOB_GHOSTIZED,
+ COMSIG_MOB_MIND_TRANSFERRED_INTO,
+ COMSIG_MOB_MIND_TRANSFERRED_OUT_OF,
+ COMSIG_MOB_MIND_SET_HOLY_ROLE,
+ )
-/datum/atom_hud/alternate_appearance/basic/blessed_aware/mobShouldSee(mob/M)
- if(M.mind?.holy_role)
- return TRUE
- if (istype(M, /mob/living/basic/construct/wraith))
- return TRUE
- if(isrevenant(M) || IS_WIZARD(M) || IS_HERETIC(M))
+/datum/atom_hud/alternate_appearance/basic/blessed_aware/mobShouldSee(mob/viewing_mob)
+ if(!viewing_mob.mind)
+ return FALSE
+ if(HAS_MIND_TRAIT(viewing_mob, TRAIT_SEE_BLESSED_TILES))
return TRUE
return FALSE
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 517fe1a65ae..eda6b70b69d 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -314,7 +314,7 @@
return
return ..()
-/obj/machinery/door/proc/try_to_activate_door(mob/user, access_bypass = FALSE)
+/obj/machinery/door/proc/try_to_activate_door(mob/living/user, access_bypass = FALSE)
add_fingerprint(user)
if(operating || (obj_flags & EMAGGED) || !can_open_with_hands)
return
@@ -328,6 +328,10 @@
run_animation(DOOR_DENY_ANIMATION)
/obj/machinery/door/allowed(mob/M)
+ if(isliving(M))
+ var/mob/living/living_user = M
+ if(!(living_user.mobility_flags & MOBILITY_USE))
+ return FALSE
if(emergency)
return TRUE
if(unrestricted_side(M))
diff --git a/code/game/objects/effects/blessing.dm b/code/game/objects/effects/blessing.dm
index 5308625a4d9..540be85db4e 100644
--- a/code/game/objects/effects/blessing.dm
+++ b/code/game/objects/effects/blessing.dm
@@ -9,19 +9,18 @@
/obj/effect/blessing/Initialize(mapload)
. = ..()
- for(var/obj/effect/blessing/B in loc)
- if(B != src)
- return INITIALIZE_HINT_QDEL
- var/image/I = image(icon = 'icons/effects/effects.dmi', icon_state = "blessed", layer = ABOVE_NORMAL_TURF_LAYER, loc = src)
- I.alpha = 64
- I.appearance_flags = RESET_ALPHA
- add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/blessed_aware, "blessing", I)
+ var/image/blessing_icon = image(icon = 'icons/effects/effects.dmi', icon_state = "blessed", layer = ABOVE_NORMAL_TURF_LAYER, loc = src)
+ blessing_icon.alpha = 64
+ blessing_icon.appearance_flags = RESET_ALPHA
+ add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/blessed_aware, "blessing", blessing_icon)
+
RegisterSignal(loc, COMSIG_ATOM_INTERCEPT_TELEPORTING, PROC_REF(block_cult_teleport))
/obj/effect/blessing/Destroy()
UnregisterSignal(loc, COMSIG_ATOM_INTERCEPT_TELEPORTING)
return ..()
+///Called from intercept teleport signal, blocks cult teleporting from being able to teleport on us.
/obj/effect/blessing/proc/block_cult_teleport(datum/source, channel, turf/origin)
SIGNAL_HANDLER
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index d5525f5393c..3a0e8de4a0a 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -606,12 +606,15 @@
return
return attempt_pickup(user)
-/obj/item/proc/attempt_pickup(mob/user, skip_grav = FALSE)
+/obj/item/proc/attempt_pickup(mob/living/user, skip_grav = FALSE)
. = TRUE
if(!(interaction_flags_item & INTERACT_ITEM_ATTACK_HAND_PICKUP)) //See if we're supposed to auto pickup.
return
+ if(!(user.mobility_flags & MOBILITY_PICKUP))
+ return
+
if(!skip_grav)
//Heavy gravity makes picking up things very slow.
var/grav = user.has_gravity()
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 5d09226154e..92bb20f69d4 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -229,7 +229,7 @@
if(mode == SCANNER_VERBOSE)
// Follow same body zone list every time so it's consistent across all humans
- for(var/zone in GLOB.all_body_zones)
+ for(var/zone in carbontarget.get_all_limbs())
var/obj/item/bodypart/limb = carbontarget.get_bodypart(zone)
if(isnull(limb))
dmgreport += "
"
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 93379d0436b..3f1ae1fb3b4 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -248,8 +248,7 @@
/// Checks a bunch of stuff to see if we can heal the patient, including can_heal
/// Gives a feedback if we can't ultimatly heal the patient (unless silent is TRUE)
/obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
- if(!(healed_zone in GLOB.all_body_zones))
- stack_trace("Invalid zone ([healed_zone || "null"]) passed to try_heal_checks.")
+ if(!(healed_zone in patient.get_all_limbs()))
healed_zone = BODY_ZONE_CHEST
if(!can_heal(patient, user, healed_zone, silent))
@@ -747,7 +746,7 @@
oof_ouch.apply_wound(bone, wound_source = "bone gel")
var/datum/wound/blunt/bone/critical/oof_OUCH = new
oof_OUCH.apply_wound(bone, wound_source = "bone gel")
- for(var/zone in GLOB.all_body_zones)
+ for(var/zone in patient.get_all_limbs())
patient.apply_damage(60, BRUTE, zone)
use(1)
return BRUTELOSS
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index c0c7f060805..ec8ed1ea802 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -919,7 +919,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \
effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune) \
)
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
/obj/item/melee/skateboard/holyboard/proc/on_cult_rune_removed(obj/effect/target, mob/living/user)
SIGNAL_HANDLER
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index a1c3f213d1a..d80fd500987 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -433,6 +433,9 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
return TRUE
if(welded || locked)
return FALSE
+ if(isliving(user))
+ if(!(user.mobility_flags & MOBILITY_USE))
+ return FALSE
if(strong_grab)
if(user)
to_chat(user, span_danger("[pulledby] has an incredibly strong grip on [src], preventing it from opening."))
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 3212516f471..c7517f2ee8d 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -516,6 +516,8 @@ GLOBAL_LIST_EMPTY(station_turfs)
qdel(L)
/turf/proc/Bless()
+ if(locate(/obj/effect/blessing) in src)
+ return
new /obj/effect/blessing(src)
//////////////////////////////
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 325bdd70a60..45c7bd1c957 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -132,14 +132,14 @@
/datum/antagonist/ert/medic/inquisitor/on_gain()
. = ..()
- owner.holy_role = HOLY_ROLE_PRIEST
+ owner.set_holy_role(HOLY_ROLE_PRIEST)
/datum/antagonist/ert/security/inquisitor
outfit = /datum/outfit/centcom/ert/security/inquisitor
/datum/antagonist/ert/security/inquisitor/on_gain()
. = ..()
- owner.holy_role = HOLY_ROLE_PRIEST
+ owner.set_holy_role(HOLY_ROLE_PRIEST)
/datum/antagonist/ert/chaplain
role = "Chaplain"
@@ -150,14 +150,14 @@
/datum/antagonist/ert/chaplain/on_gain()
. = ..()
- owner.holy_role = HOLY_ROLE_PRIEST
+ owner.set_holy_role(HOLY_ROLE_PRIEST)
/datum/antagonist/ert/commander/inquisitor
outfit = /datum/outfit/centcom/ert/commander/inquisitor
/datum/antagonist/ert/commander/inquisitor/on_gain()
. = ..()
- owner.holy_role = HOLY_ROLE_PRIEST
+ owner.set_holy_role(HOLY_ROLE_PRIEST)
/datum/antagonist/ert/intern
name = "CentCom Intern"
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 69ed76df700..b9472e0081c 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -274,6 +274,7 @@
gain_knowledge(starting_knowledge)
+ ADD_TRAIT(owner, TRAIT_SEE_BLESSED_TILES, REF(src))
addtimer(CALLBACK(src, PROC_REF(passive_influence_gain)), passive_gain_timer) // Gain +1 knowledge every 20 minutes.
return ..()
@@ -283,6 +284,7 @@
var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index]
knowledge.on_lose(owner.current, src)
+ REMOVE_TRAIT(owner, TRAIT_SEE_BLESSED_TILES, REF(src))
QDEL_LIST_ASSOC_VAL(researched_knowledge)
return ..()
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 01000da5908..8bb77ab71c0 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -51,11 +51,13 @@
/datum/antagonist/survivalist/magic/on_gain()
. = ..()
- ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
+ owner.add_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
+ for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
+ blessed_hud.check_hud(owner.current)
/datum/antagonist/survivalist/magic/on_removal()
- REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
- return..()
+ owner.remove_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
+ return ..()
/// Applied by the battle royale objective
/datum/antagonist/survivalist/battle_royale
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 617908b5ba6..db9203fdf49 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -59,10 +59,12 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/datum/antagonist/wizard_minion/on_gain()
create_objectives()
. = ..()
- ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
+ owner.add_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
+ for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
+ blessed_hud.check_hud(owner.current)
/datum/antagonist/wizard_minion/on_removal()
- REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
+ owner.remove_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
return ..()
/datum/antagonist/wizard_minion/proc/create_objectives()
@@ -90,7 +92,9 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
. = ..()
if(allow_rename)
rename_wizard()
- ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
+ owner.add_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
+ for(var/datum/atom_hud/alternate_appearance/basic/blessed_aware/blessed_hud in GLOB.active_alternate_appearances)
+ blessed_hud.check_hud(owner.current)
/datum/antagonist/wizard/Destroy()
QDEL_NULL(ritual)
@@ -184,7 +188,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
qdel(spell)
owner.current.actions -= spell
- REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src))
+ owner.remove_traits(list(TRAIT_MAGICALLY_GIFTED, TRAIT_SEE_BLESSED_TILES), REF(src))
return ..()
/datum/antagonist/wizard/proc/equip_wizard()
diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm
index 6de60d67004..6f84c0f840a 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain.dm
@@ -45,7 +45,7 @@
var/obj/item/book/bible/booze/holy_bible = new
if(GLOB.religion)
if(human_spawned.mind)
- human_spawned.mind.holy_role = HOLY_ROLE_PRIEST
+ human_spawned.mind.set_holy_role(HOLY_ROLE_PRIEST)
holy_bible.deity_name = GLOB.deity
holy_bible.name = GLOB.bible_name
// These checks are important as there's no guarantee the "HOLY_ROLE_HIGHPRIEST" chaplain has selected a bible skin.
@@ -62,7 +62,7 @@
GLOB.religious_sect.on_conversion(human_spawned)
return
if(human_spawned.mind)
- human_spawned.mind.holy_role = HOLY_ROLE_HIGHPRIEST
+ human_spawned.mind.set_holy_role(HOLY_ROLE_HIGHPRIEST)
var/new_religion = player_client?.prefs?.read_preference(/datum/preference/name/religion) || DEFAULT_RELIGION
var/new_deity = player_client?.prefs?.read_preference(/datum/preference/name/deity) || DEFAULT_DEITY
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index 4d958b7e7ff..ff0690ffaed 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -35,7 +35,7 @@
on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \
effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \
)
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
if((!GLOB.holy_weapon_type || !station_holy_item) && type == /obj/item/nullrod)
var/list/rods = list()
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 4b84c32873e..112f75aa402 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -371,12 +371,12 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
tip_text = "Clear rune", \
effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \
)
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
/obj/item/book/bible/syndicate/attack_self(mob/living/carbon/human/user, modifiers)
if(!uses || !istype(user))
return
- user.mind.holy_role = HOLY_ROLE_PRIEST
+ user.mind.set_holy_role(HOLY_ROLE_PRIEST)
uses -= 1
to_chat(user, span_userdanger("You try to open the book AND IT BITES YOU!"))
playsound(src.loc, 'sound/effects/snap.ogg', 50, TRUE)
diff --git a/code/modules/lost_crew/damages/melee.dm b/code/modules/lost_crew/damages/melee.dm
index 4896930772a..462e22a2b41 100644
--- a/code/modules/lost_crew/damages/melee.dm
+++ b/code/modules/lost_crew/damages/melee.dm
@@ -13,7 +13,7 @@
var/hits = ((max_hits - min_hits) * severity + min_hits)
for(var/i in 1 to hits)
weapon.attack(body, body) //needs an attacker, no reason it cant be the body as well
- body.zone_selected = pick(GLOB.all_body_zones)
+ body.zone_selected = pick(body.get_all_limbs())
/datum/corpse_damage/cause_of_death/melee_weapon/proc/get_weapon(mob/living/carbon/human/body)
return new weapon(null)
diff --git a/code/modules/lost_crew/damages/projectile.dm b/code/modules/lost_crew/damages/projectile.dm
index 64541cf1eaf..f38ef63dc45 100644
--- a/code/modules/lost_crew/damages/projectile.dm
+++ b/code/modules/lost_crew/damages/projectile.dm
@@ -13,7 +13,7 @@
var/hits = ((max_hits - min_hits) * severity + min_hits)
for(var/i in 1 to hits)
- body.projectile_hit(projectile, def_zone = pick(GLOB.all_body_zones), piercing_hit = TRUE)
+ body.projectile_hit(projectile, def_zone = pick(body.get_all_limbs()), piercing_hit = TRUE)
/datum/corpse_damage/cause_of_death/projectile/laser
projectile = /obj/projectile/beam/laser
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index c881d9dce45..00620f1074d 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -373,7 +373,7 @@
var/atom/throw_target = get_edge_target_turf(target_mob, get_dir(user, get_step_away(target_mob, user)))
target_mob.throw_at(throw_target, 2, 2, user, gentle = TRUE)
target_mob.Knockdown(2 SECONDS)
- var/body_zone = pick(GLOB.all_body_zones)
+ var/body_zone = pick(user.get_all_limbs())
user.apply_damage(force / recoil_factor, BRUTE, body_zone, user.run_armor_check(body_zone, MELEE))
to_chat(user, span_danger("The weight of the Big Slappy recoils!"))
log_combat(user, user, "recoiled Big Slappy into")
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index a2e605fdcc3..db9ded291ca 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -66,6 +66,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/// The POI we're orbiting (orbit menu)
var/orbiting_ref
+ ///The description camera obscuras have when they get a photo of us.
+ var/photo_description = "You can also see a g-g-g-g-ghooooost!"
var/static/list/observer_hud_traits = list(
TRAIT_SECURITY_HUD,
TRAIT_MEDICAL_HUD,
@@ -148,7 +150,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
if(!invisibility || camera.see_ghosts)
- return "You can also see a g-g-g-g-ghooooost!"
+ return photo_description
/mob/dead/observer/narsie_act()
var/old_color = color
diff --git a/code/modules/mob/living/basic/cult/constructs/wraith.dm b/code/modules/mob/living/basic/cult/constructs/wraith.dm
index e73718d4fe3..141c23bdcc2 100644
--- a/code/modules/mob/living/basic/cult/constructs/wraith.dm
+++ b/code/modules/mob/living/basic/cult/constructs/wraith.dm
@@ -20,6 +20,7 @@
/mob/living/basic/construct/wraith/Initialize(mapload)
. = ..()
+ ADD_TRAIT(src, TRAIT_SEE_BLESSED_TILES, INNATE_TRAIT)
var/datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift/jaunt = locate() in actions
if(isnull(jaunt))
return .
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
index 5ac15722a5b..2f8c4318802 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
@@ -95,7 +95,7 @@
/mob/living/basic/revenant/Initialize(mapload)
. = ..()
AddElement(/datum/element/simple_flying)
- add_traits(list(TRAIT_SPACEWALK, TRAIT_SIXTHSENSE, TRAIT_FREE_HYPERSPACE_MOVEMENT), INNATE_TRAIT)
+ add_traits(list(TRAIT_SPACEWALK, TRAIT_SIXTHSENSE, TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_SEE_BLESSED_TILES), INNATE_TRAIT)
grant_actions_by_list(abilities)
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 759b04c91e1..27e50cd4b4f 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -498,6 +498,13 @@
desc = "This juicy piece of meat has a oversized brain stem and cerebellum, with not much of a limbic system to speak of at all. You would expect its owner to be pretty cold blooded."
organ_traits = list(TRAIT_TACKLING_TAILED_DEFENDER)
+/obj/item/organ/brain/ghost
+ name = "ghost brain"
+ desc = "How are you even able to hold this?"
+ icon_state = "brain-ghost"
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/brain/abductor
name = "grey brain"
desc = "A piece of juicy meat found in an ayy lmao's head."
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 0323136b0c6..56ce9cb85c5 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -922,7 +922,7 @@
/// Creates body parts for this carbon completely from scratch.
/// Optionally takes a map of body zones to what type to instantiate instead of them.
/mob/living/carbon/proc/create_bodyparts(list/overrides)
- var/list/bodyparts_paths = bodyparts.Copy()
+ var/list/bodyparts_paths = overrides?.Copy() || bodyparts.Copy()
bodyparts = list()
for(var/obj/item/bodypart/bodypart_path as anything in bodyparts_paths)
var/real_body_part_path = overrides?[initial(bodypart_path.body_zone)] || bodypart_path
@@ -969,7 +969,6 @@
synchronize_bodytypes()
synchronize_bodyshapes()
-
///Proc to hook behavior on bodypart removals. Do not directly call. You're looking for [/obj/item/bodypart/proc/drop_limb()].
/mob/living/carbon/proc/remove_bodypart(obj/item/bodypart/old_bodypart, special)
SHOULD_NOT_OVERRIDE(TRUE)
@@ -1297,6 +1296,13 @@
return
AddComponent(/datum/component/rot, 6 MINUTES, 10 MINUTES, 1)
+/mob/living/carbon/get_photo_description(obj/item/camera/camera)
+ if(HAS_TRAIT(src, TRAIT_INVISIBLE_TO_CAMERA))
+ if(camera.see_ghosts)
+ return /mob/dead/observer::photo_description
+ return null
+ return ..()
+
/**
* This proc is used to determine whether or not the mob can handle touching an acid affected object.
*/
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 558e9e6e28c..225084863c9 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -49,12 +49,10 @@
if(get_bodypart(BODY_ZONE_HEAD) && !get_organ_by_type(/obj/item/organ/brain))
. += span_deadsay("It appears that [t_his] brain is missing...")
- var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/list/disabled = list()
for(var/obj/item/bodypart/body_part as anything in bodyparts)
if(body_part.bodypart_disabled)
disabled += body_part
- missing -= body_part.body_zone
for(var/obj/item/embedded as anything in body_part.embedded_objects)
if(embedded.get_embed().stealthy_embed)
continue
@@ -85,16 +83,16 @@
//stores missing limbs
var/l_limbs_missing = 0
var/r_limbs_missing = 0
- for(var/gone in missing)
- if(gone == BODY_ZONE_HEAD)
- . += span_deadsay("[t_His] [parse_zone(gone)] is missing!")
+ for(var/missing_limb in get_missing_limbs())
+ if(missing_limb == BODY_ZONE_HEAD)
+ . += span_deadsay("[t_His] [parse_zone(missing_limb)] is missing!")
continue
- if(gone == BODY_ZONE_L_ARM || gone == BODY_ZONE_L_LEG)
+ if(missing_limb == BODY_ZONE_L_ARM || missing_limb == BODY_ZONE_L_LEG)
l_limbs_missing++
- else if(gone == BODY_ZONE_R_ARM || gone == BODY_ZONE_R_LEG)
+ else if(missing_limb == BODY_ZONE_R_ARM || missing_limb == BODY_ZONE_R_LEG)
r_limbs_missing++
- . += span_boldwarning("[capitalize(t_his)] [parse_zone(gone)] is missing!")
+ . += span_boldwarning("[capitalize(t_his)] [parse_zone(missing_limb)] is missing!")
if(l_limbs_missing >= 2 && r_limbs_missing == 0)
. += span_tinydanger("[t_He] look[p_s()] all right now...")
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index ccc66e12bc9..16cd84c494d 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -388,6 +388,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
human_who_gained_species.mob_respiration_type = inherent_respiration_type
human_who_gained_species.butcher_results = knife_butcher_results?.Copy()
+ //update body zones to match what they are supposed to have
+ human_who_gained_species.hud_used?.healthdoll.update_body_zones()
+
// BUBBER EDIT BEGIN - Bake digitigrade into the species on apply
try_make_digitigrade(human_who_gained_species)
// BUBBER EDIT END
@@ -438,6 +441,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
human_who_gained_species.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
+ //we don't allow it to update during species transition, so update it now
+ human_who_gained_species.hud_used?.healthdoll.update_appearance()
+
/**
* Proc called when a carbon is no longer this species.
*
@@ -639,6 +645,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
switch(slot)
if(ITEM_SLOT_HANDS)
+ if(!(H.mobility_flags & MOBILITY_PICKUP))
+ return FALSE
if(H.get_empty_held_indexes())
return TRUE
return FALSE
@@ -2066,7 +2074,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
/// Creates body parts for the target completely from scratch based on the species
/datum/species/proc/create_fresh_body(mob/living/carbon/target)
- target.create_bodyparts(bodypart_overrides)
+ var/list/override_limbs = list()
+ for(var/bodypart in bodypart_overrides)
+ override_limbs += bodypart_overrides[bodypart]
+ target.create_bodyparts(override_limbs)
/**
* Checks if the species has a head with these head flags, by default.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 169072407aa..bea69cc45ce 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -747,7 +747,8 @@
// Updates the health bar, also sends signal
. = ..()
// Handles changing limb colors and stuff
- hud_used.healthdoll?.update_appearance()
+ if(!(living_flags & STOP_OVERLAY_UPDATE_BODY_PARTS))
+ hud_used.healthdoll?.update_appearance()
/mob/living/carbon/human/fully_heal(heal_flags = HEAL_ALL)
if(heal_flags & HEAL_NEGATIVE_MUTATIONS)
@@ -1204,6 +1205,12 @@
/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/spirit/ghost
+
/mob/living/carbon/human/species/ethereal
race = /datum/species/ethereal
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 83f2339b7e4..3b469cf4ab3 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -566,7 +566,7 @@
combined_msg += span_notice("You check yourself for injuries.")
- var/list/missing = GLOB.all_body_zones.Copy()
+ var/list/missing = get_all_limbs()
for(var/obj/item/bodypart/body_part as anything in bodyparts)
missing -= body_part.body_zone
diff --git a/code/modules/mob/living/carbon/human/species_types/ghost.dm b/code/modules/mob/living/carbon/human/species_types/ghost.dm
new file mode 100644
index 00000000000..78785177b85
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species_types/ghost.dm
@@ -0,0 +1,256 @@
+///Spirit mob that lacks legs but still roams the station as part of the unliving.
+/datum/species/spirit
+ name = "Spirit"
+ id = SPECIES_SPIRIT
+ 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,
+ )
+ inherent_biotypes = MOB_SPIRIT
+ no_equip_flags = ITEM_SLOT_FEET
+ changesource_flags = MIRROR_BADMIN | WABBAJACK | SLIME_EXTRACT
+ sexes = FALSE
+ meat = /obj/item/ectoplasm
+
+ mutantheart = null
+ mutantappendix = null
+ mutantears = /obj/item/organ/ears/ghost
+ mutantstomach = /obj/item/organ/stomach/ghost
+ mutantliver = /obj/item/organ/liver/ghost
+ mutantlungs = /obj/item/organ/lungs/ghost
+ mutanteyes = /obj/item/organ/eyes/ghost
+ mutantbrain = /obj/item/organ/brain/ghost
+ mutanttongue = /obj/item/organ/tongue/ghost
+
+ bodypart_overrides = list(
+ BODY_ZONE_HEAD = /obj/item/bodypart/head/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,
+ )
+
+ ///Boolean on whether this species type is available at roundstart during halloween, used to deny subtypes.
+ var/halloween_exclusive = TRUE
+
+/datum/species/spirit/check_roundstart_eligible()
+ if(check_holidays(HALLOWEEN) && halloween_exclusive)
+ return TRUE
+ return ..()
+
+/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/spirit/get_species_description()
+ return "Spirits are spirits of long-dead creatures whom, for one reason or another, still roam around."
+
+/datum/species/spirit/get_species_lore()
+ return list(
+ "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/spirit/create_pref_unique_perks()
+ var/list/to_add = list()
+
+ to_add += list(list(
+ SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
+ SPECIES_PERK_ICON = "body",
+ SPECIES_PERK_NAME = "Leg-less",
+ 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_NEGATIVE_PERK,
+ SPECIES_PERK_ICON = "shoe-prints",
+ SPECIES_PERK_NAME = "No Feet",
+ SPECIES_PERK_DESC = "You lack feet, therefore the ability to wear any shoes!",
+ ))
+
+ 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 | MIRROR_PRIDE | MIRROR_MAGIC
+ 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 drop everything
+ * and completely unable to wear anything.
+ */
+/datum/action/innate/toggle_passthrough
+ name = "Toggle passthrough"
+ 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"
+
+/datum/action/innate/toggle_passthrough/Grant(mob/grant_to)
+ . = ..()
+ RegisterSignal(grant_to, COMSIG_CARBON_POST_ATTACH_LIMB, PROC_REF(on_new_limb))
+
+/datum/action/innate/toggle_passthrough/Remove(mob/remove_from)
+ swap_mode(force_off = TRUE)
+ UnregisterSignal(remove_from, COMSIG_CARBON_POST_ATTACH_LIMB)
+ return ..()
+
+/datum/action/innate/toggle_passthrough/Activate()
+ . = ..()
+ if(!iscarbon(owner))
+ 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
+
+ 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)
+ carbon_species.update_no_equip_flags(carbon_owner, ALL)
+ RegisterSignal(carbon_owner, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, PROC_REF(attempt_move))
+ else
+ 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)
+ carbon_species.update_no_equip_flags(carbon_owner, initial(carbon_species.no_equip_flags))
+ UnregisterSignal(carbon_owner, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE)
+
+///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
+ 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
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index e610f05bb54..77921120468 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -85,7 +85,7 @@
to_add += list(list(
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
- SPECIES_PERK_ICON = "pickaxe",
+ SPECIES_PERK_ICON = "tools",
SPECIES_PERK_NAME = "Natural Miners",
SPECIES_PERK_DESC = "Golems can see dimly in the dark, sense minerals, break boulders, and mine stone with their bare hands. \
They can even smelt ores in an internal furnace, if their surrounding environment is hot enough.",
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fe1b697352b..b03e31f1063 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2664,6 +2664,8 @@ GLOBAL_LIST_EMPTY(fire_appearances)
/// Proc to append behavior to the condition of being handsblocked. Called when the condition starts.
/mob/living/proc/on_handsblocked_start()
+ if(active_storage)
+ active_storage.hide_contents(src)
drop_all_held_items()
add_traits(list(TRAIT_UI_BLOCKED, TRAIT_PULL_BLOCKED), TRAIT_HANDS_BLOCKED)
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 6d1d404f35e..f6290670449 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -94,8 +94,7 @@
//Reload alternate appearances
for(var/datum/atom_hud/alternate_appearance/alt_hud as anything in GLOB.active_alternate_appearances)
- if(!alt_hud.apply_to_new_mob(src))
- alt_hud.hide_from(src, absolute = TRUE)
+ alt_hud.check_hud(src)
update_client_colour()
update_mouse_pointer()
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index 1c6ac03dffd..9e9f635fe50 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -305,7 +305,9 @@
mobs_spotted += mob
if(mob.stat == DEAD)
dead_spotted += mob
- desc += mob.get_photo_description(src)
+ var/info = mob.get_photo_description(src)
+ if(!isnull(info))
+ desc += info
var/psize_x = (size_x * 2 + 1) * ICON_SIZE_X
var/psize_y = (size_y * 2 + 1) * ICON_SIZE_Y
diff --git a/code/modules/photography/camera/camera_image_capturing.dm b/code/modules/photography/camera/camera_image_capturing.dm
index 8f850ab1cf2..2b0387d6728 100644
--- a/code/modules/photography/camera/camera_image_capturing.dm
+++ b/code/modules/photography/camera/camera_image_capturing.dm
@@ -44,10 +44,12 @@
lighting_overlay.underlays += backdrop
lighting_overlay.blend_mode = BLEND_MULTIPLY
lighting += lighting_overlay
- for(var/i in T.contents)
- var/atom/A = i
- if(!A.invisibility || (see_ghosts && isobserver(A)))
- atoms += new /obj/effect/appearance_clone(newT, A)
+ for(var/atom/found_atom as anything in T.contents)
+ if(HAS_TRAIT(found_atom, TRAIT_INVISIBLE_TO_CAMERA))
+ if(see_ghosts)
+ atoms += new /obj/effect/appearance_clone(newT, found_atom)
+ else if(!found_atom.invisibility || (see_ghosts && isobserver(found_atom)))
+ atoms += new /obj/effect/appearance_clone(newT, found_atom)
skip_normal = TRUE
wipe_atoms = TRUE
center = locate(cloned_center_x, cloned_center_y, bottom_left.z)
@@ -64,7 +66,7 @@
lighting += lighting_overlay
for(var/atom/movable/A in T)
if(A.invisibility)
- if(!(see_ghosts && isobserver(A)))
+ if(!(see_ghosts && (isobserver(A) || HAS_TRAIT(A, TRAIT_INVISIBLE_TO_CAMERA))))
continue
atoms += A
CHECK_TICK
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index 7189a64dbcb..450746c71a2 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -53,7 +53,7 @@
if(!seen)
P.mobs_seen -= seen_ref
continue
- if(!isobserver(seen))
+ if(!isobserver(seen) && !isspirit(seen))
continue
set_custom_materials(list(/datum/material/hauntium =SHEET_MATERIAL_AMOUNT))
grind_results = list(/datum/reagent/hauntium = 20)
diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
index bdf7897afaf..ff826883b26 100644
--- a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
+++ b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
@@ -117,7 +117,7 @@
/obj/projectile/bullet/arrow/holy/Initialize(mapload)
. = ..()
//50 damage to revenants
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 30)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 30)
/// plastic arrows
// completely dogshit quality and they break when they hit something.
diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_types.dm b/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
index cc6bfbb6477..daa938015cc 100644
--- a/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
+++ b/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
@@ -37,7 +37,7 @@
on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \
effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune) \
)
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
/obj/item/gun/ballistic/bow/divine/proc/on_cult_rune_removed(obj/effect/target, mob/living/user)
SIGNAL_HANDLER
diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm
index 446e794436d..6a608059c49 100644
--- a/code/modules/religion/burdened/psyker.dm
+++ b/code/modules/religion/burdened/psyker.dm
@@ -179,7 +179,7 @@
on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \
effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \
)
- AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25)
+ AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25)
name = pick(possible_names)
desc = possible_names[name]
diff --git a/code/modules/religion/deaconize.dm b/code/modules/religion/deaconize.dm
index 20e8465ea5e..ea20e85054a 100644
--- a/code/modules/religion/deaconize.dm
+++ b/code/modules/religion/deaconize.dm
@@ -65,7 +65,7 @@
if(honor && (potential_deacon in honor.guilty))
honor.guilty -= potential_deacon
to_chat(user, span_notice("[GLOB.deity] has bound [potential_deacon] to the code! They are now a holy role! (albeit the lowest level of such)"))
- potential_deacon.mind.holy_role = HOLY_ROLE_DEACON
+ potential_deacon.mind.set_holy_role(HOLY_ROLE_DEACON)
GLOB.religious_sect.on_conversion(potential_deacon)
playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
return TRUE
diff --git a/code/modules/religion/honorbound/honorbound_trauma.dm b/code/modules/religion/honorbound/honorbound_trauma.dm
index c26eb39f3b6..7b36d75f518 100644
--- a/code/modules/religion/honorbound/honorbound_trauma.dm
+++ b/code/modules/religion/honorbound/honorbound_trauma.dm
@@ -206,7 +206,7 @@
if(SCHOOL_NECROMANCY, SCHOOL_FORBIDDEN, SCHOOL_SANGUINE)
to_chat(user, span_userdanger("[GLOB.deity] is enraged by your use of forbidden magic!"))
lightningbolt(user)
- user.mind.holy_role = NONE
+ user.mind.set_holy_role(NONE)
qdel(src)
owner.add_mood_event("honorbound", /datum/mood_event/banished) //add mood event after we already cleared our events
to_chat(user, span_userdanger("You have been excommunicated! You are no longer holy!"))
diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm
index 1e8abfc9f69..fbe0fe93070 100644
--- a/code/modules/religion/sparring/sparring_datum.dm
+++ b/code/modules/religion/sparring/sparring_datum.dm
@@ -287,7 +287,7 @@
to_chat(loser, span_userdanger("[GLOB.deity] is enraged by your lackluster sparring record!"))
lightningbolt(loser)
loser.add_mood_event("sparring", /datum/mood_event/banished)
- loser.mind.holy_role = NONE
+ loser.mind.set_holy_role(NONE)
to_chat(loser, span_userdanger("You have been excommunicated! You are no longer holy!"))
if(STAKES_MONEY_MATCH)
to_chat(loser, span_userdanger("You've lost all your money to [winner]!"))
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 442ba1975bd..d0d9d1e6e83 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -389,7 +389,7 @@
/mob/living/carbon/proc/regenerate_limbs(list/excluded_zones = list())
SEND_SIGNAL(src, COMSIG_CARBON_REGENERATE_LIMBS, excluded_zones)
- var/list/zone_list = GLOB.all_body_zones.Copy()
+ var/list/zone_list = get_all_limbs()
var/list/dismembered_by_copy = body_zone_dismembered_by?.Copy()
@@ -403,30 +403,31 @@
if(get_bodypart(limb_zone))
return FALSE
limb = newBodyPart(limb_zone, 0, 0)
- if(limb)
- if(!limb.try_attach_limb(src, TRUE))
- qdel(limb)
- return FALSE
- limb.update_limb(is_creating = TRUE)
- if (LAZYLEN(dismembered_by_copy))
- var/datum/scar/scaries = new
- var/datum/wound/loss/phantom_loss = new // stolen valor, really
- phantom_loss.loss_wounding_type = dismembered_by_copy?[limb_zone]
- if (phantom_loss.loss_wounding_type)
- scaries.generate(limb, phantom_loss)
- LAZYREMOVE(dismembered_by_copy, limb_zone) // in case we're using a passed list
- else
- qdel(scaries)
- qdel(phantom_loss)
+ if(!limb)
+ return
+ if(!limb.try_attach_limb(src, TRUE))
+ qdel(limb)
+ return FALSE
+ limb.update_limb(is_creating = TRUE)
+ if (LAZYLEN(dismembered_by_copy))
+ var/datum/scar/scaries = new
+ var/datum/wound/loss/phantom_loss = new // stolen valor, really
+ phantom_loss.loss_wounding_type = dismembered_by_copy?[limb_zone]
+ if (phantom_loss.loss_wounding_type)
+ scaries.generate(limb, phantom_loss)
+ LAZYREMOVE(dismembered_by_copy, limb_zone) // in case we're using a passed list
+ else
+ qdel(scaries)
+ qdel(phantom_loss)
- //Copied from /datum/species/proc/on_species_gain()
- for(var/obj/item/organ/organ_path as anything in dna.species.mutant_organs)
- //Load a persons preferences from DNA
- var/zone = initial(organ_path.zone)
- if(zone != limb_zone)
- continue
- var/obj/item/organ/new_organ = SSwardrobe.provide_type(organ_path)
- new_organ.Insert(src)
+ //Copied from /datum/species/proc/on_species_gain()
+ for(var/obj/item/organ/organ_path as anything in dna.species.mutant_organs)
+ //Load a persons preferences from DNA
+ var/zone = initial(organ_path.zone)
+ if(zone != limb_zone)
+ continue
+ var/obj/item/organ/new_organ = SSwardrobe.provide_type(organ_path)
+ new_organ.Insert(src)
- update_body_parts()
- return TRUE
+ update_body_parts()
+ return TRUE
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index 1e817454451..c5fc38a8b6c 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -11,6 +11,15 @@
if(bodypart.body_zone == zone)
return bodypart
+///Returns TRUE/FALSE on whether the mob should have a limb in a given zone, used for species-restrictions.
+/mob/living/carbon/proc/should_have_limb(zone)
+ if(!zone || !dna)
+ return TRUE
+ var/datum/species/carbon_species = dna.species
+ if(zone in carbon_species.bodypart_overrides)
+ return TRUE
+ return FALSE
+
/// Replaces a single limb and deletes the old one if there was one
/mob/living/carbon/proc/del_and_replace_bodypart(obj/item/bodypart/new_limb, special)
var/obj/item/bodypart/old_limb = get_bodypart(new_limb.body_zone)
@@ -112,9 +121,21 @@
/mob/living/carbon/proc/get_hand_of_item(obj/item/I)
return get_bodypart(get_hand_zone_of_item(I))
-/mob/living/carbon/proc/get_missing_limbs()
+///Returns a list of all limbs this mob should have.
+/mob/living/proc/get_all_limbs() as /list
RETURN_TYPE(/list)
- var/list/full = GLOB.all_body_zones.Copy()
+ return GLOB.all_body_zones.Copy()
+
+///Returns a list of all limbs this mob should have.
+/mob/living/carbon/get_all_limbs()
+ if(dna)
+ return dna.species.bodypart_overrides.Copy()
+ return ..()
+
+///Returns a list of all missing limbs this mob should have on them, but don't.
+/mob/living/carbon/proc/get_missing_limbs() as /list
+ RETURN_TYPE(/list)
+ var/list/full = get_all_limbs()
for(var/zone in full)
if(get_bodypart(zone))
full -= zone
@@ -131,7 +152,7 @@
return list()
/mob/living/carbon/get_disabled_limbs()
- var/list/full = GLOB.all_body_zones.Copy()
+ var/list/full = get_all_limbs()
var/list/disabled = list()
for(var/zone in full)
var/obj/item/bodypart/affecting = get_bodypart(zone)
@@ -172,6 +193,8 @@
// FUCK YOU AUGMENT CODE - With love, Kapu
/mob/living/carbon/proc/newBodyPart(zone)
var/path = dna.species.bodypart_overrides[zone]
+ if(isnull(path))
+ return null
var/obj/item/bodypart/new_bodypart = new path()
return new_bodypart
diff --git a/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
new file mode 100644
index 00000000000..adeaa04067c
--- /dev/null
+++ b/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
@@ -0,0 +1,51 @@
+/obj/item/bodypart/head/ghost
+ icon = 'icons/mob/human/species/ghost.dmi'
+ icon_static = 'icons/mob/human/species/ghost.dmi'
+ icon_state = "ghost_head"
+ biological_state = BIO_FLESH
+ bodytype = BODYTYPE_GHOST
+ limb_id = SPECIES_GHOST
+ is_dimorphic = FALSE
+ should_draw_greyscale = FALSE
+ dmg_overlay_type = null
+
+ head_flags = HEAD_HAIR | HEAD_FACIAL_HAIR | HEAD_DEBRAIN
+ teeth_count = 0
+
+/obj/item/bodypart/chest/ghost
+ icon = 'icons/mob/human/species/ghost.dmi'
+ icon_static = 'icons/mob/human/species/ghost.dmi'
+ icon_state = "ghost_chest"
+ biological_state = BIO_FLESH
+ acceptable_bodyshape = BODYTYPE_GHOST
+ bodytype = BODYTYPE_GHOST
+ limb_id = SPECIES_GHOST
+ is_dimorphic = FALSE
+ should_draw_greyscale = FALSE
+ dmg_overlay_type = null
+ wing_types = 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'
+ icon_state = "ghost_l_arm"
+ biological_state = BIO_FLESH|BIO_JOINTED
+ bodytype = BODYTYPE_GHOST
+ limb_id = SPECIES_GHOST
+ should_draw_greyscale = FALSE
+ dmg_overlay_type = null
+
+/obj/item/bodypart/arm/right/ghost
+ icon = 'icons/mob/human/species/ghost.dmi'
+ icon_static = 'icons/mob/human/species/ghost.dmi'
+ icon_state = "ghost_r_arm"
+ biological_state = BIO_FLESH|BIO_JOINTED
+ bodytype = BODYTYPE_GHOST
+ limb_id = SPECIES_GHOST
+ should_draw_greyscale = FALSE
+ dmg_overlay_type = null
diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm
index 774dfb4478e..826851f5e17 100644
--- a/code/modules/surgery/organs/internal/ears/_ears.dm
+++ b/code/modules/surgery/organs/internal/ears/_ears.dm
@@ -205,6 +205,13 @@
/datum/bodypart_overlay/mutant/cat_ears/color_image(image/overlay, layer, obj/item/bodypart/limb)
return // We color base ears manually above in get_image
+/obj/item/organ/ears/ghost
+ name = "ghost ears"
+ desc = "All the more to hear you... though it can't hear through walls."
+ icon_state = "ears-ghost"
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/ears/penguin
name = "penguin ears"
desc = "The source of a penguin's happy feet."
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index 87c3e5fad6a..26ecc2d864e 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -1133,6 +1133,14 @@
pupils_name = "sensor clusters"
penlight_message = "are two clustered hemispheres of thermal sensors"
+/obj/item/organ/eyes/ghost
+ name = "ghost eyes"
+ desc = "Despite lacking pupils, these can see pretty well."
+ icon_state = "eyes-ghost"
+ blink_animation = FALSE
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/eyes/snail
name = "snail eyes"
desc = "These eyes seem to have a large range, but might be cumbersome with glasses."
diff --git a/code/modules/surgery/organs/internal/liver/_liver.dm b/code/modules/surgery/organs/internal/liver/_liver.dm
index 1bd09e48ed8..d887ad3d36c 100644
--- a/code/modules/surgery/organs/internal/liver/_liver.dm
+++ b/code/modules/surgery/organs/internal/liver/_liver.dm
@@ -220,6 +220,13 @@
liver_resistance = 0.333 * LIVER_DEFAULT_TOX_RESISTANCE // -66%
toxTolerance = 15 // complete toxin immunity like xenos have would be too powerful
+/obj/item/organ/liver/ghost
+ name = "ghost liver"
+ desc = "Processes the last alcohol they drank in their time alive."
+ icon_state = "liver-ghost"
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/liver/cybernetic
name = "basic cybernetic liver"
desc = "A very basic device designed to mimic the functions of a human liver. Handles toxins slightly worse than an organic liver."
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 2204a9c8b8f..f74da8d786d 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -974,6 +974,13 @@
/obj/item/organ/lungs/cybernetic/surplus/hear_breath_noise(mob/living/hearer)
return span_danger("[owner.p_Their()] lungs emit [breath_noise].")
+/obj/item/organ/lungs/ghost
+ name = "ghost lungs"
+ desc = "No one knows what this is even supposed to breathe."
+ icon_state = "lungs-ghost"
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/lungs/lavaland
name = "blackened frilled lungs" // blackened from necropolis exposure
desc = "Exposure to the necropolis has mutated these lungs to breathe the air of Indecipheres, the lava-covered moon."
diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm
index ee7947717fb..585f18ee4cb 100644
--- a/code/modules/surgery/organs/internal/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm
@@ -559,4 +559,11 @@
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
color = COLOR_LIME
+/obj/item/organ/stomach/ghost
+ name = "ghost stomach"
+ desc = "Ghosts eat plenty, you know? And it's not just your life, I swear!"
+ icon_state = "stomach-ghost"
+ movement_type = PHASING
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
#undef STOMACH_METABOLISM_CONSTANT
diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm
index ff5af0562a9..ab0c34545bd 100644
--- a/code/modules/surgery/organs/internal/tongue/_tongue.dm
+++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm
@@ -373,6 +373,15 @@
bomb = 50
fire = 100
+/obj/item/organ/tongue/ghost
+ name = "ghost tongue"
+ desc = "You feel spooked even thinking about someone talking through this."
+ icon_state = "tongue-ghost"
+ movement_type = PHASING
+ say_mod = "boos"
+ sense_of_taste = FALSE
+ organ_flags = parent_type::organ_flags | ORGAN_GHOST
+
/obj/item/organ/tongue/abductor
name = "superlingual matrix"
desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
diff --git a/code/modules/surgery/prosthetic_replacement.dm b/code/modules/surgery/prosthetic_replacement.dm
index f4d8e6ed826..e2e9aec1587 100644
--- a/code/modules/surgery/prosthetic_replacement.dm
+++ b/code/modules/surgery/prosthetic_replacement.dm
@@ -22,7 +22,7 @@
if(!iscarbon(target))
return FALSE
var/mob/living/carbon/carbon_target = target
- if(!carbon_target.get_bodypart(user.zone_selected)) //can only start if limb is missing
+ if(!carbon_target.get_bodypart(user.zone_selected) && carbon_target.should_have_limb(user.zone_selected)) //can only start if limb is missing
return TRUE
return FALSE
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_spirit.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_spirit.png
new file mode 100644
index 0000000000000000000000000000000000000000..c43c67f12a8b01a144cee7919749a370690ce131
GIT binary patch
literal 2011
zcmV<12PF83P)V=-0C=1w$2$&yAP@z>nmxq}_Qqekje!^nxPytSP^bwIy}pIhO@rAkF2Hz4
zkK~f>3yi7lNWE#R5sYyl6(t?9{wZ~>0*2P4wv-DP6RAC~rxY-HYG8wb@s2*4{}>LN
zJdk?;00%2cL_t(|ob6kGNE=ri|J)c`S5^=lh>5uTAmAv4L0MCWaFYGeN(IYGX&Wfa
zK@rNZt<@mwoH$nywOXYcxI!IRY)ffpK#~5@G*?}XEft3wb%b0pX(EN`!n!%z!ES#f
z@A`60E_av9g-t&Yp7-wF`+UEjd%x~|pN!w{$4160g*Kqj*zf|psk2qYXU`nP?F|d1c~#4B<)Pv
zrwXbB`1Ae(eR*Ay8VyF0O6^kw#RA0Ralz?yG632#Jd*cuAX$G#ey7vP#N%;Qtw6B=
zMMXtSJRTP^7-85TD90a<$AzMzA|_qAOis&tK0Kj+;1f^hu!Db;l1tQ?7L>s4t{1ART?q3$Tpzezy6?N~gVkokwBLu((P72>
ziUb&+^b4laVg&Bb~i_g#XIb
zo3Pq!0067iBHj;&BZ-vfapdras`#^Z0YXlkByiX*kjo#R^y|_cX#VI@1Yw>BkZ_v(
zfQ0kWrHE>NMN46a-GX2!LBvfz6dj+s6Ai^pqf8x7a(u<@sk)>mX&>$Wm!Ca5<_fxMQky-g7BX>bxiEj#MyM$
zCr%w>)bPs+5Lyf(w!DJSVo>qO-qY32(B*OgT@O7Ar9+tKVYS)N(%K=Y;g=l)ZhSZL
zGs7^alb25i`Y*y%S}bcY+tnUmS+*7c1VK1=_wL=SMr6XxvCFeato^T=&9*mz7k!`1
z2EdB}klepN8O}lh2ng5$VtK&lA;yz(~y;Mw{*sOJw;
z-JshR67LZv@rCES9vG{Aqb1R_U
z{x!Yo)Fgn{>z(j;Jp0nz%Ii3gtUJ^B08LOW0LO8ab#-+Cuh%Q>0)>Vq@l}P#fy8dC
z^S$@Qu$ZZ|7_sFQ^mMfcjGP5pT03?VZXoeRIQ!rp%;gpUP#nlnBtT1Rhd^TT;cx`g
zexG=6XlPK}BhWJ>E(bbd+|bY{=2um_#IU*Vwoj1=Sp}e-_ucj(x)j0C&>(=s=16aM
zpP?d1op1Iui95p$4UI6DTR_{JO$6C7!0oP2#N)ydj0|5wS&0eFo+bcDiP!=!BE&^5
z!XchhATXC(Qo5-evI;;#&(f%@G(u!F2xq2NN9@ifglrd(zI6hkes6c5*d+>qW=|7(
zyZcgXk|L821d7%HB=!y@n*P+C078qww9Xr_5==p}rwO6OplBBW$ekmQLbeM?&*PMp
zm~id-FeWB%0RZ;Z?7@YvhZH-I4}5aGL7e*b)$GBcgY_?+7m$Oj0HdSB3>ihEOA(Zn
zm>>wi>gw+R06|EN2^bL{kn&0GA-0k<+nGVdD?zurULf)LajzHW2M6J4ZpQut2LJ$P
zzdVl}JKn*sPkzbkI^eC6w}pj;M>zA@NdUmujT`9h?ZwH)M%32T;o15+-gs@B;=_`v
zHbm(7{Kp?1#iegYQbN^6FN6p$u|;}DKnYau2+<-K?L0v`5wtiDX!shGhNO*l9sp`<
zs#A`_(cyBnb*U1-sDF#e1!`?nEWm%kc>*=ID&7{C#QpR7e*s#dV=-0C=1w$2$&yAP@z>nmxq}_Qqekje!^nxPytSP^bwIy}pIhO@rAkF2Hz4
zkK~f>3yi7lNWE#R5sYyl6(t?9{wZ~>0*2P4wv-DP6RAC~rxY-HYG8wb@s2*4{}>LN
zJdk?;00%2cL_t(|ob6kGNE=ri|J)c`S5^=lh>5uTAmAv4L0MCWaFYGeN(IYGX&Wfa
zK@rNZt<@mwoH$nywOXYcxI!IRY)ffpK#~5@G*?}XEft3wb%b0pX(EN`!n!%z!ES#f
z@A`60E_av9g-t&Yp7-wF`+UEjd%x~|pN!w{$4160g*Kqj*zf|psk2qYXU`nP?F|d1c~#4B<)Pv
zrwXbB`1Ae(eR*Ay8VyF0O6^kw#RA0Ralz?yG632#Jd*cuAX$G#ey7vP#N%;Qtw6B=
zMMXtSJRTP^7-85TD90a<$AzMzA|_qAOis&tK0Kj+;1f^hu!Db;l1tQ?7L>s4t{1ART?q3$Tpzezy6?N~gVkokwBLu((P72>
ziUb&+^b4laVg&Bb~i_g#XIb
zo3Pq!0067iBHj;&BZ-vfapdras`#^Z0YXlkByiX*kjo#R^y|_cX#VI@1Yw>BkZ_v(
zfQ0kWrHE>NMN46a-GX2!LBvfz6dj+s6Ai^pqf8x7a(u<@sk)>mX&>$Wm!Ca5<_fxMQky-g7BX>bxiEj#MyM$
zCr%w>)bPs+5Lyf(w!DJSVo>qO-qY32(B*OgT@O7Ar9+tKVYS)N(%K=Y;g=l)ZhSZL
zGs7^alb25i`Y*y%S}bcY+tnUmS+*7c1VK1=_wL=SMr6XxvCFeato^T=&9*mz7k!`1
z2EdB}klepN8O}lh2ng5$VtK&lA;yz(~y;Mw{*sOJw;
z-JshR67LZv@rCES9vG{Aqb1R_U
z{x!Yo)Fgn{>z(j;Jp0nz%Ii3gtUJ^B08LOW0LO8ab#-+Cuh%Q>0)>Vq@l}P#fy8dC
z^S$@Qu$ZZ|7_sFQ^mMfcjGP5pT03?VZXoeRIQ!rp%;gpUP#nlnBtT1Rhd^TT;cx`g
zexG=6XlPK}BhWJ>E(bbd+|bY{=2um_#IU*Vwoj1=Sp}e-_ucj(x)j0C&>(=s=16aM
zpP?d1op1Iui95p$4UI6DTR_{JO$6C7!0oP2#N)ydj0|5wS&0eFo+bcDiP!=!BE&^5
z!XchhATXC(Qo5-evI;;#&(f%@G(u!F2xq2NN9@ifglrd(zI6hkes6c5*d+>qW=|7(
zyZcgXk|L821d7%HB=!y@n*P+C078qww9Xr_5==p}rwO6OplBBW$ekmQLbeM?&*PMp
zm~id-FeWB%0RZ;Z?7@YvhZH-I4}5aGL7e*b)$GBcgY_?+7m$Oj0HdSB3>ihEOA(Zn
zm>>wi>gw+R06|EN2^bL{kn&0GA-0k<+nGVdD?zurULf)LajzHW2M6J4ZpQut2LJ$P
zzdVl}JKn*sPkzbkI^eC6w}pj;M>zA@NdUmujT`9h?ZwH)M%32T;o15+-gs@B;=_`v
zHbm(7{Kp?1#iegYQbN^6FN6p$u|;}DKnYau2+<-K?L0v`5wtiDX!shGhNO*l9sp`<
zs#A`_(cyBnb*U1-sDF#e1!`?nEWm%kc>*=ID&7{C#QpR7e*s#d&Hw$7o4dPo`fUTmU=Q()5(k7y%jZdh0{ilMZS?c5@__(0*56d<{MMAY10HT-w
zr}-mAK|#Sf=x~nfpC2SmDWk~R)$#M;t;up(9&eGda*PPp|FUo+zSy?n3G(?FX
zkL-oBd?Rl!mvsANFXQd%zOMn|o9vfuH`m=A0*x}Z%eS6yxg;87{_vK-|M=;nnPY*$
zD%&hL-_1e-o!whK^*yggMvZ%R7_4&GI|0dyU6wVfz#)gWlD3wO7-G
zo-}x@&l=u@-2h{LN~bIAX}hvYh1@4>ZeT~g{(|$sjSM~+E?|1RSTozP^P`2ez>FTtcd=Jau$0njO!KZbB*g?q^v~D=Gg+KPZ#9_vWA$$`?ScT?*qlugi
zo7G>%3gG=jb$8U2=46huNi8OYn!D#$2PKybmC-70O78-?c$4fLmBR>
zn?tJGzc^vfk|<3t2OI+@rwZVNT_BaszqgSH0n-4*?4w{Ri~;@egU>gRz2dU
zk|nI~)3v_~5K2AsVG_`|j?p%j4xH@;AG^-v(%2KDV6?amc}x
zdGiC#r>v~DjhixCZ+K%>l4Mn=Gybu>N#ISZ@4df%;iTTf)3dVDF)Bm@+JPLT4WGJ}
z4FfkyDd=AUFT9+tPnaAHuX+b7>R-Wum+rnaW!;&SqJ_73&i4?D9)Rbc+iiYCJtw*#
zTS#H2rT!(*4F|~gxU;&e$h(Qk1)A_ZJtg&qB&Vi4zn%I{e;p|_x!l_{CNebfgDU)l
z3X_Z6jyV1a$8?`EPlYzII1wSWIArX%ds3$JIj}EzHXj357(f)2W>DnU+lo0&z{%0p
z+U2;>mi$+N1vbwM5+EH*I=WP)3rqCHrKrL{TADYOqNeZd@6t9S{h4qaV`}^G)4u;>
zZH#ZL-GLc4;@@p7N|*vKM0z>}x@k0?%hXINC2xxoIWR+ut4h>~XR_z5S>uY^hX$XEpqVF_T^
zW%MKB=~4JT>C`~D!D?VgIzhS)+nRCAG3Iv8J|v@(ZLX6~IbVg=wA#_gvoT5$(A6)R
z-G{VritAm78uF#f?so)A@EnU@lL*
z>fEq)9!YvpZZRL&?da-uLDeHWeI(7#((?GyDICWN-ZeNx`#u@;4lmwTEjB{GU8SJI
zn)~$!Q(v}#Cwco$IEjbv^6DS+arW<_aY|fxXd_Aw-c|bxd>8GLDp=Oo(FKvXQow6D
zeWxec87n6qzNg;>!qm%&v!9vr1kCDM1CL;}|C|`yu-N8<^78eJT>x>}oN9u7?3=s3
z7p+GQ>Aa4c7C|HqEym65$YMB|ylvSv55Aba&q1QM0x_GriI4q~y7f#uJ)e54Iwx`D
z7>`++zDQ`cch#d}s)oaVce@a`b3{0YheH+HzS{Xxwud|p*K`1{M0$mv{+nW55JERx
z6TEw>LEtnMzsU4`k*QA>q%57cn^Ft?4E5q55F&+4MCL205Pdbf1|+DC1JiqOwcKr8%VbEAe;@bRx}bHlf;
z)7v$1!*wfBDtfEh)cKheP6lDhpcU-{Z=vtUk7jvR
zW#YvXVk&MDZW>{3n43iSj<}nQ;I-N=(0qe%Y=ImBumh?HDA*
zkkQWuR4k4ir0QSrFRcX?R!`yF-T&OZ77HKs3Rm1$z21MTW}66th4&eO|H-V|L~2Xy
zb$3gSF9L~mo+-pd2Yhp2EzQcMd0UFh@_0h;58Z{|%u-?PXbwzE%bRT=Vne_Uau`Rl
zJLMmLj3E%qaJF{hp_W&*LI{H!PVW`u^WvT{qq5EsMz{HXIE_+06O#$kNi_^_PUSNU
zx8b~HA0B+0r~!*|brhNgvmOLBMJq+WiUS=lwR2*~sA
zu5_%E)A-pIcxSQL^*IEv4yXD1BEv;*%Bez|4vu;(t;>`aR>l5RI75|ITg7FYuL1)V
z;M=YDxzFoc|CqKTzbk{H$EB|}EA3%Acx_3b_brki>^p7+LRRPiKL3i9r3L-i3g4Kn
z1-dyS2T@wj=I%{Xt0@E;-q>(oM?^^9Wt+O^QB;Ob9+Ut2wkIxLGOj0URUmX~btho6
z3Z*@4^D?-w6QQAz2xo2m4HW4Q#~e0dWAlH$^Vo@Vdx1X*MmjtUcf0P}-HmhyezXu`
z?t)5D|5BmtK-ODQtz18f?3O%c`N(Us)~!g|eD+-CIqZ%x9pUsBwS3KrLq1K|zb$Q}
z;eF7g?rL6H#JEm&iB*6R^l!ct6!@~fRj`RK^XDx9z{kd`<>xRM9nT`{IE2QILruzl
zYJJ^Ap3CX5dao!WY_Dqh88PJ|c-_ZuD)M{eERM0XAX?^w321L!OBe^ejet+XAa>PG
zy23tSmnZY*Nk+#1#b3wEW9otg?C`orD`z#|eTyXsBxyqa#4a>d7PkayTu-@OHr@E%
zWcLB0{fuLnN@@fRAxXjlvC8eS`@JFo41z=$-%04fR-F}eUjMSYudfFFRafb4zLZ?6
zHj0)i=UQz33RKa15OMLOESUUgh;jBzm^%gKG@F?B!1{+~K^fhC5t$W-C^(XUJuD7-UPrBpT-+L>D9-)wq
zCq5oucvkzr7){63uc=_$>4nxO^+eoqA$6H)RnDlUds{n=^z2w^eYII697s85mLq@I
zI7rJ-|2I;L>#qK8X56Gx
zBV?wRW2y6Yy6mR3s0f)hQ7*>qWj!kzxQz7roay*Q=hc3ta7Q2j661BvEzu%gwr$uR
zu$%j6pm3hfujQR8Ddbr-brkzfc%8kH4e@^iK%U1&bjm#T1U+D;f)Dr;1ui={ZN#;k9kN^eUp(fNa(2?Hy_1bF>%(yMaX;|bX)l1MUvJ6Yw{|UME0|r$
zL}rT#4W|NQ2HI&6Cn=IJ=^Zyj{v3c(484XgbFnMBH^=bB0bnrL%phsJrNe&0E09+o
z=l(ykchi*Mt;z!ZPmfAN=<@w42-8@68VS8h)a!Zybkjox#P1
z50;x-jC#XkV`IUGms|u_b-lWyFAbP5i;Ihq-~IIu#KGiq^YgH2{3aCBe%u@d1Wx^S
zg?wcZKe(fhJ~J}zMbYIL4abuGbzKB>fG5SmG04UWq(IO#f>529qIf$kp-urEdk3;{
zX|Zh0&9H%^y5D-+uCA#WP9Oa$7P)7g`E$5Y#{i223*m=Brw{w3EMT3OG)B9X$>e;R
zgj16=Z}G;ty9>H3)t1w47(H*WUDpQVr(oh0Nb23mB1pCUv3B0V^aP0nZWnU}?wwP(
zc(7E+D6QE*Fu|J>lg#TwP*xTxNBEZ1p_JNM%mm1#{P%BKIy$7F2#k)2sEJJW30(?o
z1xirOnA*T>i<>@ms^^p0laEP()!!Y%9VsY|JfUDZi4sb#67FQ8h2$4w4%i
z`lh2Zc5^T;r>uMw3}m4c63T|A%<>8fuzW@g&QaVrvit^U_Ln|g~RpmYaBnJzFWdL+{pd@ZQo!MBGQnKdXI66)MX{U8MZXe
zA7w2lbn&mOM0txYqrCMA?Ut=?Kb`|P#8D*!`Q8hS_{4{DrJ0XL>2N=@Iyu!EM0aRu
zXv8P2#ueFy*Y4T2JY`TR4reX{k~?4dzlUfr>3Ja%QE))&I1fk#_5_Nz%GFEt))y8Q
zf~J9pNL4&zes0zn5%9_K2iF@ULpq~U#Mp;INe%4n3nHIUDn=hMtEz7QV!Kch)2NOu
zu@M)Lrmnx5$_okWh3Y}oS9){{`K9_DW0U>vf5@-n{71JovKw$@#L6-T8C;Zsgc|
znJJpukC$o;m2-vtz23n`QlP=$uFK+u^hx@pozCAL=M8eg>#U~B|K{}&1-&|gh0FC}
z%HK@iuS9#!F-PIrK4vUk-Egz^4zxOrGZIm>$_ITg8JmeJf`Pm|123jYMxzRDW(f#lk*Lm7D9Gvm
zM%Q-;m^pPTDk)AtJ$pZdHW2nhGq)aFXODZgE`L|(nWrEtDz-I4rhenj(44)?$qA~c
z;b^{m!%TVm-&NOj^*GlPnwt*ax&m)C*qc|T;edOUDn5jzmI_R<;xc$
zwElrV5ZZWd*}=!lCDr~T;|+=I#c*MR@gIfBL}-WxzUSOfAO0_B#R)&tLruHrY))ac
z3vBVHzP?;YUj`atp<#I;r}|bI3&ut%tH-210Xo};gXc8`zguLcL)+*e1tH<^&PN&9
z8R?4y$;SuGWsRGycX<8Rqs!I3-as)av6*wsaX9I9)+}(J(GBNi(A~S9E|`Af#c*m&
zNIr6W|DyYd^A9M*P!YaFuDk1EqurFtfzU_jty0z}5b-S2GGHfc27KVH?A_gGg%^QA
zcEV6oR<>OjSLAWI-W(_L_D}hahe&V=C7W!3A1S3o&(|b7mHph-B2rKe=<|+YD)EhTvJADtMh*F_TtpBT7*3iC7)xdVgCcIC|Pc
z_M#nF9$C&krsl?_?#EBD4p!naC2*U>w+V=OqF*7CE>z_rLaV;S$H$+%mMJFm#beQ+Z&4}VzW$9XxWW7reo7jHi^l{S6^^qTRrd@bGv~YhS{?^RRoTF{
zGF`i^rw5un!vGQf!tDoQFk+~iHG$b}O=Iv51<&Z0YKLw}x2`G`AOrGnWd0QbsfQ5^
zT`|zUo6`BQ>MCGoZSyQ)i}3CosQn5Hyz(61g4te7e{6lw?XTdx1RmwU^}qk@@v`tE#u%-vv>C)t!Uz(Etjb8
zfpzb-1mVBTmmU1|G!=w~s_K7Vyw|EadU{E6wNWfjCMg&uBFfo#>5K#yy`oVoj_RyT
zFEK@AO>A+fW_EqK>cGP(j$&^r_6HF*wzA8oT&s0TIeb)bOuqXV9CveF*TB;Y{$(hU(V$Vc&fpKh{u6*{}SyWoj`f6BFA
zC*XdsGaZhGdi?(3;je^*o!wMpj`Wd{5lUX(RA?G76pMalJ|jA-^RfY!un#P)4a)U@
zNS{X3_uOz7*EOF#G;F=$zHP96JoQVK`USaYzjWwzh)8vzs*
zf*&z;f;PW|cFt;MK!{APq(wupJZHhU+HCHok@<>1deCPLsxaRGv?F01{OtoEL1Y4s
z5={+$e=8uja9b8=ajCA1^6ocG6p&*@CHmbjf#9o5^o$W(B^wc%$vjt|XYT+L`!_
zr$3Li^!m}}H5;G7%6EpntROfT02~m*&o9m#86UTG-jLN1dqffeizWC}0POwXH0ytc
z$TZ%aI7nB`8gsX6Mlz$N)^i6q-)!(HXR6SaQZ6k~YG~|^9EUS}{!q8nn8aD|noVY}
z(*xaPqi1OPE;dl4LJ>p6xTJTwo!dm7+ARcB2S3*?3&_fT?J?LI+1b-!5v=*))fww!
z`|(t32;L992*#Ms@fvG$KtQYt7WBb9m8^}yuADyC82hN?G)4O+>8-Mc+Cw+Sx4k2LR5_qwZ
z%i8gp71cD>FU1AcheOlwL>e2$o-Sp?O#8
zO(JdA`m8YU7xiRK@b=(QY*XkFh9Pxj>x0gv&AiN4f-f!&u*tMJ;W~80RfxWwWQyW&
z810FCKM_kiVN0=;O$%|8bwZ@gOP5HMeg5D!YAQ-}?&GeV>Vqs(u+r|+Sz?J$A~5o1
zPI@#jpa$%TC?(IZp!-Qd%TY|lw$}onPmN5CT0yo;rq)`lbc0e=eIcUq&U3wiLE>mv
zY3KafHJct(iZ!Mz-37u3H6-3g!T>bHEZT4RIg8RMPyLCHpb
z417)ra_%I~T$Rp}kD4@*&et$)hQgb)u$0EWzFXG3qC%f+g{RiqZy{B#X2Pkf@_V;7
z!@g3t1a1ZR8X0(6il_<9b&z`6Rp4{SX&}xjNzD5xBj}F9MNG^XE80n>v?!iMOovh4
zF@<1*KBcaBZ)=OS)J?(`NZDdiwsc=B=5zJd1n2-0l~z0g!9*x8?0Liz+7%}TO@r~zl%y@D0U-gEyL$#?d%tnoTa_&~IH
zai@W|VBQwnO0;Tmrxt3P1A9)g&OOk8wC1IidL^tfpK^aa$a?ntYyxxrOw#KhA$tKw
zr<^xK7g9*BHs3oNq?XE*e45y6VH*vffvwhtFhPM{BBhGcqdvPjsGN
ziTII9KvZ+m`z0SddBD9U5LGN}Q7=xdxW~rXJuu>m;@K+MaOD9-{M}=~REClTgULO9
zE?HWE%!Rqbz(x@1oNQ`BwoF
z#oW;NvDDOkq!4N!HO+`vhBO5~_k*ul{ecPvwvp$@kTjj#s17OmFJ*duvVn4LzB7*o
z6qJP=mx8F}ZL8Yb8ZCFMmfOozLnt`t7{@8F1oiv4JS)YN^@A}53Je;#K}C4AX{z>X
zcxc^*PcHAeB;L=zeVcAxvQVxsVhw#i)p}VlsJNc9somb{CV`_Mi?AZF`thOh!KGNW
z>vjXy4Q{iU&Ri#a-gD9NWN{ld(#1=&M)f-}wz0T8R4PYXtRkg~@o6z14L}U4ab9Qi
z!NPJGFN<_y8=oVWTX#dnZjeGRJyvwIi4ENg!mO^E`SIyMDmh9(ymByjLC?1;0vR4z
zcp1hJF5-vYHjZGJlh>onwb09!jZAKl>3wb{E3e6~$a$(kSdugJ<3zWWQ~kjo{Ez@}
znE#7UwEs(i{AqY?+ZTZ}zFnVDoReQP5!YsZ^r#p_r>LCy@97AbYN$VkeFVYaM`bmo@nH@%`QjR*Hm
zBR=TqHVYee3$bJF1g{yWfiMmB3~d;4V)dR2FWASHU{Jr~--(6b(8n1w3!}kHR^jk`
z5$oSWOHJr|YW40)&fK;wTeIp!XpFpEDTZpQ7rcP32`?qPVT#C%1e{Uh+Q};fPkTS<
z5t3HnJNM?yn~9AjkUqmv{@7BNfPn!3{`(ey;)&FH;2C9(Q7IQrgfglhmGqN%U32Uq
zyF65%_+EdS7KQ6VgB_*+}1GV
zJFn;>{h8U<><@K!cIBv#XWK=J=G9p>g$#
zV%z0vxAV1RAWKVLp<~^AzB(J_RzSby}NUO-8S8D|8dN|>n%&c7-
zcDJPKh!1ynx*Ru55l9LX(cjysD3?MW@U+u6nn*_|bkzgT&PYV5gUlA6tW5c-1jQ`h
zg@)_C@@v|ZJ_3Dp|%
z(;ciobJeW@?uoKjLU@ubO?~p)u{nZvqs_P3-9Zo#*5hoYcP3u~Hccc@cs!N$s5sTY
zWiff2kkvqsFYx^}4H;gvr*ONn`tvypzvqZ`k6Fv545x^}R+ss5b{8H#xe#q`)%f#X&e_KwIhwiBs}PfJtTA-Ofs#i8wCBCgqDGNT|0m#(dC6ME!i
z!jZ1s;Z-NoIS3*&3qV
zy!<@3;-ph%$OJs%)6?ZVJb1XdxvL=|zp}Fxt*t*NC(n+4^P;DX@OxRByxdeu47;fx1`P0T73(3W#A=LX#nfBM_`LbU2v_DpAVTq4(_Nfc6SU
z$H#%s=ApSSZQJooMhz4>=b2@
literal 8689
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
Cu{GiV
diff --git a/icons/mob/human/species/ghost.dmi b/icons/mob/human/species/ghost.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..42185e3db54b612fd1c531fa31bb1d969dd65690
GIT binary patch
literal 2324
zcmbVOXH=6}8vej2O$1biA`(Cl7(_}C2w;K~ff+zhSDJ{(8YH1dY9@pt;;=$&ln61%
zxPa0XVM72}B19=cDWQcr0wRVo@DUP7vgqubv*(OIcK64~mu)bD!tE&-0%9vxmEr
zthA~$006Qs&JJgHYU`IJxo2lT%MO*;DdfcS-q#(jVuPYXBCm%;gabf)VNNeiy3JNT
zeL9h=zt6?!QeXHHse4)p=~qt#p){39KI@=ipU+2M^-ar_JWGqbdZ+Q18!p9B{!<>D
z4OY|49p!zcznpN5`cNn9rm3KodDqKLsQN$}b5l<`qw^u{2I=6wj5D1FZ@MN1^{w}R
zI`Ttz`&jL!2DXGOteJryqsH`mT`XR}4ESRvU8NJw#}J=9002o-7l%{l;|n?CXTrx$
zD|bvsWL9OW>P4qjoNZkMdSo+~(l*sN=Cy5>e)DRk&%>)O@A6R!JL9=azwr&KQ6t3D
z)G_+a^zAoXE|89Pnz1OEn+*u*;59_ZdVp?AdjPls;Jg*PZeUSB%Mcl5Qm&Yx0>KDw
z((z2CDV6Xh~#Lq
zZ%8Fsz~ufmW%71wvTRvS-D!{#y?=Xk%H%}cO8!{SXTRxjmIzg^O0S#|$jR7xZht7m
z^^ZP0$e5-sa`Oe2O?sEqwzlr;4K3I5T_!}sAe{>sl8r;=y8@6Ax;9!PE|EB0SH6%@
z0gY8Gv+6)wOW{5q#8%y6gK?ZUc*Ua;0!5V;i86QFF)NDCh<@-%g&u#xP&TXFn5sjQ
zt}~^Jzl&a2&G)eiT;kJW)i~Q!qqL)8^vrb$jiR-|;@CDeXkFMwZPl*}e7grKF6;ju
z+pI}lY#4;6+iNZh6g8Ert${Eo{NuZAuAbjC8>~%C+W=1GsaRJH5_4z>*58Qs4D&Ve
z)JU%3GZ-+j1%1Xa7z=IL>w5)Ekez4K7fU0VBobK;QK(Oe3rF>qvJicbEKoScMAho#
zeK=Y@zOQ1$Mk%mYsVYKlr7p3_`~`ny#=aNBY~~J0R8u|`FH140a$w*+uCT2e;q)v^
zadB@lb_oNs~Y*4WQ?eb-LHF17IZQp|EjB}rDc2tHpgQBC}%v452McQWsVEkpP;
zO1F^qPq9WWa5Z=WDQ1`%+)Syl92&v2GiWSjE!ml|GZQ?t53h18LO~#q!4s{KO
zg!@n9;x}9o+AUIZd`dK5($;1zu9Ypu#_v5E|6@V2MfeRJk8E)~8kb^*Jy2+ONUNG4
zZp^HKOS8OiG0>aZK+phe>hq7L$kdkvMKe%u1h;nNe3-KpGL^$3;E$W41qYIANA2%i
zPl9yy^Dv-m&d8iojQ~WVlOQ*pD49Hk?@l7ccbdH~MI-Ag+i$e}8-UCqB|YxuI(0E9
z#!jN6^CSYl#4kStMHbDCYndNU;e3$Sdo9giK)qjZx;9I{|Jbz(0mgF&d6+bNTflfg
zKYRDU(K7O5u{U2&`sPx>YS~el8i`iMP
zv!&5DfN?r@UR}L#byeZEeUZF@8Z^LvQ`5uK=0(rkGHbH*4ow)f(7`Yg2?K(wb&_jO
zF|NHD<7{znAbiT;Epx6@2953MTdm-Rf1Lr3eL~u^K
z{n0u+r)RFK;_-MW0_GN);VnSTaqq(0G_uNVzf6X2t7q{-Ja%VWPxlS)`Vz$d&f32}
zw_y^XOjnnAz8!HR%v|(v!~h0c7BJB3sLS#@QQ1NJh=Bpb_o9cDNIOGPIekN~Q~K~WiVSPVF=_3xSRr`xxnYmFpWYVm`?oo^1{;^^*BWp_FCUq@6)D*ylh
literal 0
HcmV?d00001
diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi
index ba48010d7da822406399bd3b6f1ccaf683391ec2..ea3fe2f29df9ad020f948670a88fd9920966d47d 100644
GIT binary patch
literal 52665
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