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 e52e343bf03..205b92496f7 100644 Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ 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"