Files
lew 483674ac59 Animates the disappearance of the lower half of the body when north-side leaning (#22568)
fixes #22249 

- bugfix: "Leaning on the north side of a wall now correctly hides your
lower body behind the wall instead of drawing you on top of it."
- code_imp: "Adds a COMSIG_MOB_LYING_DOWN signal, sent when a mob
transitions into lying down."

Cutout was previously only cutting out the base because KEEP_TOGETHER
wasn't being applied. Adds the flag on lean, removes it on stop_lean.

Animated the cutout while here. Applies a mask to the whole sprite, then
animates it upwards in step with the lean itself, so the feet
progressively disappear as they should.


https://github.com/user-attachments/assets/fec0a852-247a-4570-a6d1-60dda6fe6a3c
2026-06-01 00:10:15 +00:00

142 lines
4.5 KiB
Plaintext

/// Things with this component can be leaned onto
/datum/component/leanable
/// How much will mobs that lean onto this object be offset
var/leaning_offset = 11
/// List of mobs currently leaning on our parent
var/list/leaning_mobs
/// Is this object currently leanable?
var/is_currently_leanable = TRUE
/datum/component/leanable/Initialize(mob/living/leaner, leaning_offset = 11)
. = ..()
src.leaning_offset = leaning_offset
mousedrop_receive(parent, leaner, leaner)
/datum/component/leanable/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
RegisterSignal(parent, COMSIG_ATOM_DENSITY_CHANGED, PROC_REF(on_density_change))
var/atom/leanable_atom = parent
is_currently_leanable = leanable_atom.density
/datum/component/leanable/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(
COMSIG_MOVABLE_MOVED,
COMSIG_MOUSEDROPPED_ONTO,
COMSIG_ATOM_DENSITY_CHANGED,
))
/datum/component/leanable/Destroy(force)
stop_leaning_leaners()
return ..()
/datum/component/leanable/proc/stop_leaning_leaners(fall)
for (var/mob/living/leaner as anything in leaning_mobs)
if(fall)
leaner.visible_message(SPAN_WARNING("[leaner] loses their balance!"), SPAN_DANGER("You lose balance!"))
leaner.Weaken(rand(3,5))
leaner.stop_leaning()
LAZYCLEARLIST(leaning_mobs)
/datum/component/leanable/proc/on_moved(datum/source)
SIGNAL_HANDLER
for (var/mob/living/leaner as anything in leaning_mobs)
leaner.stop_leaning()
/datum/component/leanable/proc/mousedrop_receive(atom/source, atom/movable/dropped, mob/user, params)
if (dropped != user)
return
if (!iscarbon(dropped))
return
var/mob/living/leaner = dropped
var/can_lean = TRUE
if (HAS_TRAIT_FROM(leaner, TRAIT_UNDENSE, TRAIT_LEANING))
return
if(istype(leaner.l_hand, /obj/item/grab) || istype(leaner.r_hand, /obj/item/grab))
can_lean = FALSE
if(leaner.incapacitated())
can_lean = FALSE
if(leaner.resting)
can_lean = FALSE
if(leaner.buckled_to)
can_lean = FALSE
if(!is_currently_leanable || !can_lean)
return COMPONENT_CANCEL_MOUSEDROPPED_ONTO
leaner.start_leaning(source, leaning_offset)
LAZYADD(leaning_mobs, leaner)
RegisterSignals(leaner, list(COMSIG_LIVING_STOPPED_LEANING, COMSIG_QDELETING), PROC_REF(stopped_leaning), override = TRUE)
return COMPONENT_CANCEL_MOUSEDROPPED_ONTO
/**
* Makes the mob lean on an atom
* Arguments
*
* * atom/lean_target - the target the mob is trying to lean on
* * leaning_offset - pixel offset to apply on the mob when leaning
*/
/mob/living/proc/start_leaning(atom/lean_target, leaning_offset)
var/direction = get_dir(lean_target, src)
var/shift_pixel_x = 0
var/shift_pixel_y = 0
set_dir(direction)
switch(dir)
if(NORTH)
shift_pixel_y = -10
if(SOUTH)
shift_pixel_y = 16
if(EAST)
shift_pixel_x = -10
if(WEST)
shift_pixel_x = 10
animate(src, pixel_x = shift_pixel_x, pixel_y = shift_pixel_y, time = 1)
if(direction == NORTH)
src.appearance_flags |= KEEP_TOGETHER
src.add_filter("cutout", 1, alpha_mask_filter(y = 0, icon = icon('icons/effects/effects.dmi', "white")))
animate(src.get_filter("cutout"), y = 10, time = 1, flags = ANIMATION_PARALLEL)
if(direction == SOUTH)
src.layer = ABOVE_DOOR_LAYER
set_density(FALSE)
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING)
ADD_TRAIT(src, TRAIT_LEANING, TRAIT_SOURCE_WALL_LEANING)
visible_message(
SPAN_NOTICE("[src] leans against [lean_target]."),
SPAN_NOTICE("You lean against [lean_target]."),
)
RegisterSignals(src, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED, COMSIG_MOB_LYING_DOWN), PROC_REF(stop_leaning), src)
/mob/living/proc/stop_leaning()
SIGNAL_HANDLER
set_density(TRUE)
pixel_x = initial(pixel_x)
pixel_y = initial(pixel_y)
layer = initial(layer)
UnregisterSignal(src, list(
COMSIG_MOVABLE_MOVED,
COMSIG_MOB_RESISTED,
COMSIG_MOB_LYING_DOWN,
))
SEND_SIGNAL(src, COMSIG_LIVING_STOPPED_LEANING)
to_chat(src, SPAN_NOTICE("You stop leaning on the wall."))
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING)
REMOVE_TRAIT(src, TRAIT_LEANING, TRAIT_SOURCE_WALL_LEANING)
appearance_flags &= ~KEEP_TOGETHER
remove_filter("cutout")
/datum/component/leanable/proc/stopped_leaning(datum/source)
SIGNAL_HANDLER
LAZYREMOVE(leaning_mobs, source)
UnregisterSignal(source, list(COMSIG_LIVING_STOPPED_LEANING, COMSIG_QDELETING))
/datum/component/leanable/proc/on_density_change()
SIGNAL_HANDLER
is_currently_leanable = !is_currently_leanable
if(!is_currently_leanable)
stop_leaning_leaners(fall = TRUE)
return
stop_leaning_leaners()