From 1d21a7881433b8d7d209f4c659608ae62a10849a Mon Sep 17 00:00:00 2001
From: GDN <96800819+GDNgit@users.noreply.github.com>
Date: Wed, 28 Jun 2023 08:29:39 -0500
Subject: [PATCH] adds contort body to changelings (#21259)
* adds contort body to changelings
* more changes
* sprite by mcramon
* Update code/game/machinery/doors/firedoor.dm
Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
* steels review
---------
Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
---
code/__HELPERS/trait_helpers.dm | 1 +
code/_globalvars/traits.dm | 3 +-
code/datums/components/caltrop.dm | 3 ++
code/game/machinery/OpTable.dm | 4 +++
code/game/machinery/doors/airlock.dm | 8 ++++--
code/game/machinery/doors/firedoor.dm | 8 ++++++
code/game/machinery/doors/windowdoor.dm | 12 ++++++--
.../structures/crates_lockers/crates.dm | 4 ++-
code/game/objects/structures/morgue.dm | 13 +++++----
code/game/objects/structures/railings.dm | 8 +++---
code/game/objects/structures/tables_racks.dm | 12 +++++---
.../changeling/powers/contort_body.dm | 26 ++++++++++++++++++
.../modules/mob/living/living_status_procs.dm | 7 +++--
icons/mob/actions/actions.dmi | Bin 141585 -> 142295 bytes
paradise.dme | 1 +
15 files changed, 89 insertions(+), 21 deletions(-)
create mode 100644 code/modules/antagonists/changeling/powers/contort_body.dm
diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index 387fdae5c68..8dd96b8d9bc 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -203,6 +203,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CULT_IMMUNITY "cult_immunity"
#define TRAIT_FLATTENED "flattened"
#define SM_HALLUCINATION_IMMUNE "supermatter_hallucination_immune"
+#define TRAIT_CONTORTED_BODY "contorted_body"
//***** ITEM AND MOB TRAITS *****//
/// Show what machine/door wires do when held.
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index ddf721f7632..96a02b3ec8a 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -75,7 +75,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO,
"TRAIT_CAN_BE_EATEN_BY_LIZARDS" = TRAIT_EDIBLE_BUG,
"TRAIT_FLATTENED" = TRAIT_FLATTENED,
- "TRAIT_SM_HALLUCINATION_IMMUNE" = SM_HALLUCINATION_IMMUNE
+ "TRAIT_SM_HALLUCINATION_IMMUNE" = SM_HALLUCINATION_IMMUNE,
+ "TRAIT_CONTORTED_BODY" = TRAIT_CONTORTED_BODY
),
/obj/item = list(
"TRAIT_SHOW_WIRE_INFO" = TRAIT_SHOW_WIRE_INFO,
diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm
index c26560d2d20..4446dde3a26 100644
--- a/code/datums/components/caltrop.dm
+++ b/code/datums/components/caltrop.dm
@@ -45,6 +45,9 @@
if(H.flying || H.floating || H.buckled)
return
+ if(IS_HORIZONTAL(H) && HAS_TRAIT(H, TRAIT_CONTORTED_BODY))
+ return TRUE
+
var/damage = rand(min_damage, max_damage)
H.apply_damage(damage, BRUTE, picked_def_zone)
diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm
index 42f993cd9bb..7f74df70595 100644
--- a/code/game/machinery/OpTable.dm
+++ b/code/game/machinery/OpTable.dm
@@ -49,6 +49,10 @@
return TRUE
if(istype(mover) && mover.checkpass(PASSTABLE))
return TRUE
+ if(isliving(mover))
+ var/mob/living/our_mover = mover
+ if(IS_HORIZONTAL(our_mover) && HAS_TRAIT(our_mover, TRAIT_CONTORTED_BODY))
+ return TRUE
else
return FALSE
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index d8ca792f6cc..3a146a7d15f 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -748,8 +748,12 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
attack_ai(user)
/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target, height=0)
- if(istype(mover) && mover.checkpass(PASSDOOR) && !locked) // We really dont want mice/drones to get shocked on a door they're passing through
- return TRUE
+ if(istype(mover) && !locked)
+ if(mover.checkpass(PASSDOOR))
+ return TRUE
+ var/mob/living/living_mover = mover
+ if((istype(living_mover) && HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover))) // We really dont want people to get shocked on a door they're passing through
+ return TRUE
if(isElectrified() && density && isitem(mover))
var/obj/item/I = mover
if(I.flags & CONDUCT)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 303523f8f58..d829305b3d6 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -289,6 +289,14 @@
F.update_icon()
qdel(src)
+/obj/machinery/door/firedoor/CanPass(atom/movable/mover, turf/target)
+ if(..())
+ return TRUE
+ if(isliving(mover) && !locked)
+ var/mob/living/living_mover = mover
+ if(HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover))
+ return TRUE
+
/obj/machinery/door/firedoor/border_only
icon = 'icons/obj/doors/edge_doorfire.dmi'
flags = ON_BORDER
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index c5d34376ead..0d3d3111056 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -134,7 +134,11 @@
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
- return 1
+ return TRUE
+ if(isliving(mover))
+ var/mob/living/living_mover = mover
+ if(HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover))
+ return TRUE
if(get_dir(loc, target) == dir) //Make sure looking at appropriate border
return !density
if(istype(mover, /obj/structure/window))
@@ -162,7 +166,11 @@
/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSGLASS))
- return 1
+ return TRUE
+ if(isliving(mover))
+ var/mob/living/living_mover = mover
+ if(HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY) && IS_HORIZONTAL(living_mover))
+ return TRUE
if(get_dir(loc, target) == dir)
return !density
else
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index bd6009ce3d6..035de168476 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -70,11 +70,13 @@
playsound(loc, close_sound, close_sound_volume, TRUE, -3)
var/itemcount = 0
- for(var/obj/O in get_turf(src))
+ for(var/atom/movable/O in get_turf(src))
if(itemcount >= storage_capacity)
break
if(O.density || O.anchored || istype(O,/obj/structure/closet))
continue
+ if(ismob(O) && !HAS_TRAIT(O, TRAIT_CONTORTED_BODY))
+ continue
if(istype(O, /obj/structure/bed)) //This is only necessary because of rollerbeds and swivel chairs.
var/obj/structure/bed/B = O
if(B.has_buckled_mobs())
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 0e7ad16c155..0b71005e3e8 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -254,18 +254,21 @@
connected = null
return ..()
-/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0)
+/obj/structure/m_tray/CanPass(atom/movable/mover, turf/target, height=0)
if(height == 0)
return TRUE
-
- if(istype(mover) && mover.checkpass(PASSTABLE))
- return TRUE
+ if(istype(mover))
+ if(mover.checkpass(PASSTABLE))
+ return TRUE
+ var/mob/living/our_mover = mover
+ if(istype(our_mover) && IS_HORIZONTAL(our_mover) && HAS_TRAIT(our_mover, TRAIT_CONTORTED_BODY))
+ return TRUE
if(locate(/obj/structure/table) in get_turf(mover))
return TRUE
return FALSE
-/obj/structure/tray/m_tray/CanPathfindPass(obj/item/card/id/ID, dir, caller, no_id = FALSE)
+/obj/structure/m_tray/CanPathfindPass(obj/item/card/id/ID, dir, caller, no_id = FALSE)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index a63c83f8bdf..2d93cf9b154 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -80,8 +80,8 @@
if(istype(mover, /obj/item/projectile))
return TRUE
if(ismob(mover))
- var/mob/M = mover
- if(M.flying)
+ var/mob/living/M = mover
+ if(M.flying || (IS_HORIZONTAL(M) && HAS_TRAIT(M, TRAIT_CONTORTED_BODY)))
return TRUE
if(mover.throwing)
return TRUE
@@ -107,8 +107,8 @@
return TRUE
if(istype(O, /obj/item/projectile))
return TRUE
- if(ismob(O))
- if(M.flying || M.floating)
+ if(istype(M))
+ if(M.flying || M.floating || (IS_HORIZONTAL(M) && HAS_TRAIT(M, TRAIT_CONTORTED_BODY)))
return TRUE
if(O.throwing)
return TRUE
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 5e96f473b7e..cd742fffd70 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -122,8 +122,8 @@
if(istype(mover,/obj/item/projectile))
return (check_cover(mover,target))
if(ismob(mover))
- var/mob/M = mover
- if(M.flying)
+ var/mob/living/M = mover
+ if(M.flying || (IS_HORIZONTAL(M) && HAS_TRAIT(M, TRAIT_CONTORTED_BODY)))
return TRUE
if(istype(mover) && mover.checkpass(PASSTABLE))
return TRUE
@@ -868,8 +868,12 @@
return 1
if(!density) //Because broken racks -Agouri |TODO: SPRITE!|
return 1
- if(istype(mover) && mover.checkpass(PASSTABLE))
- return 1
+ if(istype(mover))
+ if(mover.checkpass(PASSTABLE))
+ return TRUE
+ var/mob/living/living_mover = mover
+ if(istype(living_mover) && IS_HORIZONTAL(living_mover) && HAS_TRAIT(living_mover, TRAIT_CONTORTED_BODY))
+ return TRUE
if(mover.throwing)
return 1
else
diff --git a/code/modules/antagonists/changeling/powers/contort_body.dm b/code/modules/antagonists/changeling/powers/contort_body.dm
new file mode 100644
index 00000000000..c79146f93ec
--- /dev/null
+++ b/code/modules/antagonists/changeling/powers/contort_body.dm
@@ -0,0 +1,26 @@
+/datum/action/changeling/contort_body
+ name = "Contort Body"
+ desc = "We contort our body, allowing us to fit in and under things we normally wouldn't be able to. Costs 5 chemicals."
+ button_icon_state = "contort_body"
+ chemical_cost = 5
+ dna_cost = 2
+ power_type = CHANGELING_PURCHASABLE_POWER
+
+/datum/action/changeling/contort_body/Remove(mob/M)
+ REMOVE_TRAIT(M, TRAIT_CONTORTED_BODY, CHANGELING_TRAIT)
+ ..()
+
+/datum/action/changeling/contort_body/sting_action(mob/living/user)
+ if(HAS_TRAIT_FROM(user, TRAIT_CONTORTED_BODY, CHANGELING_TRAIT))
+ REMOVE_TRAIT(user, TRAIT_CONTORTED_BODY, CHANGELING_TRAIT)
+ to_chat(user, "Our body stiffens and returns to form.")
+ if(IS_HORIZONTAL(user))
+ user.layer = initial(user.layer)
+ else
+ ADD_TRAIT(user, TRAIT_CONTORTED_BODY, CHANGELING_TRAIT)
+ to_chat(user, "We contort our form to allow us to fit in and under things we normally wouldn't be able to.")
+ if(IS_HORIZONTAL(user))
+ user.layer = TURF_LAYER + 0.2
+
+ SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
+ return TRUE
diff --git a/code/modules/mob/living/living_status_procs.dm b/code/modules/mob/living/living_status_procs.dm
index 87d4f424998..6d5bd6726a4 100644
--- a/code/modules/mob/living/living_status_procs.dm
+++ b/code/modules/mob/living/living_status_procs.dm
@@ -111,7 +111,10 @@ STATUS EFFECTS
/mob/living/proc/on_lying_down(new_lying_angle)
if(layer == initial(layer)) //to avoid things like hiding larvas.
- layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs
+ if(HAS_TRAIT(src, TRAIT_CONTORTED_BODY))
+ layer = TURF_LAYER + 0.2
+ else
+ layer = LYING_MOB_LAYER //so mob lying always appear behind standing mobs
pixel_y = PIXEL_Y_OFFSET_LYING
ADD_TRAIT(src, TRAIT_UI_BLOCKED, LYING_DOWN_TRAIT)
ADD_TRAIT(src, TRAIT_CANNOT_PULL, LYING_DOWN_TRAIT)
@@ -120,7 +123,7 @@ STATUS EFFECTS
set_lying_angle(pick(90, 270))
/mob/living/proc/on_standing_up()
- if(layer == LYING_MOB_LAYER)
+ if(layer == LYING_MOB_LAYER || HAS_TRAIT(src, TRAIT_CONTORTED_BODY))
layer = initial(layer)
set_density(initial(density))
REMOVE_TRAITS_IN(src, LYING_DOWN_TRAIT)
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index e52e343bf0394ac416b292b7dd3cb79cc4f11b72..205b92496f7b045308e995c9eb92ac029840283e 100644
GIT binary patch
delta 5651
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
CC3#5z
diff --git a/paradise.dme b/paradise.dme
index 9be879f59bf..898ce5fe0b0 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1312,6 +1312,7 @@
#include "code\modules\antagonists\changeling\powers\become_headslug.dm"
#include "code\modules\antagonists\changeling\powers\biodegrade.dm"
#include "code\modules\antagonists\changeling\powers\chameleon_skin.dm"
+#include "code\modules\antagonists\changeling\powers\contort_body.dm"
#include "code\modules\antagonists\changeling\powers\digitalcamo.dm"
#include "code\modules\antagonists\changeling\powers\epinephrine.dm"
#include "code\modules\antagonists\changeling\powers\fakedeath.dm"