Files
Bubberstation/code/datums/components/chasm.dm
Odairu 6c9cfa1289 Fixes cyborgs not dying when they fall into chasms (#85787)
## About The Pull Request
When a cyborg gets gibbed, its MMI pops out and is still able to talk.
Funny thing about chasms, they gib you, which leaves you still able to
talk despite the fact that you should be dead, so this nulls out the
cyborgs MMI if they fall into a chasm, leaving the player properly dead
albeit round removed
## Why It's Good For The Game
It fixes a bug that almost never happens
## Changelog
🆑
fix: borgs no longer drop MMIs when chasmed
/🆑

---------

Co-authored-by: Odairu <39929315+Odairu@users.noreply.github.com>
2024-08-14 23:26:38 +02:00

287 lines
11 KiB
Plaintext

// Used by /turf/open/chasm and subtypes to implement the "dropping" mechanic
/datum/component/chasm
var/turf/target_turf
var/obj/effect/abstract/chasm_storage/storage
var/fall_message = "GAH! Ah... where are you?"
var/oblivion_message = "You stumble and stare into the abyss before you. It stares back, and you fall into the enveloping dark."
/// List of refs to falling objects -> how many levels deep we've fallen
var/static/list/falling_atoms = list()
var/static/list/forbidden_types = typecacheof(list(
/obj/docking_port,
/obj/effect/abstract,
/obj/effect/collapse,
/obj/effect/constructing_effect,
/obj/effect/dummy/phased_mob,
/obj/effect/ebeam,
/obj/effect/fishing_lure,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/light_emitter/tendril,
/obj/effect/mapping_helpers,
/obj/effect/particle_effect/ion_trails,
/obj/effect/particle_effect/sparks,
/obj/effect/portal,
/obj/effect/projectile,
/obj/effect/spectre_of_resurrection,
/obj/effect/temp_visual,
/obj/effect/wisp,
/obj/energy_ball,
/obj/narsie,
/obj/projectile,
/obj/singularity,
/obj/structure/lattice,
/obj/structure/stone_tile,
/obj/structure/ore_vent,
))
/datum/component/chasm/Initialize(turf/target, mapload)
if(!isturf(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_stopped))
RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_no_longer_stopped))
target_turf = target
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_ENTERED, PROC_REF(entered))
RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(exited))
RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on))
RegisterSignal(parent, COMSIG_ATOM_INTERCEPT_TELEPORTING, PROC_REF(block_teleport))
//allow catwalks to give the turf the CHASM_STOPPED trait before dropping stuff when the turf is changed.
//otherwise don't do anything because turfs and areas are initialized before movables.
if(!mapload)
addtimer(CALLBACK(src, PROC_REF(drop_stuff)), 0)
parent.AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/chasm)
/datum/component/chasm/UnregisterFromParent()
storage = null
/datum/component/chasm/proc/entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
drop_stuff()
/datum/component/chasm/proc/exited(datum/source, atom/movable/exited)
SIGNAL_HANDLER
UnregisterSignal(exited, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED))
/datum/component/chasm/proc/initialized_on(datum/source, atom/movable/movable, mapload)
SIGNAL_HANDLER
drop_stuff(movable)
/datum/component/chasm/proc/block_teleport()
return COMPONENT_BLOCK_TELEPORT
/datum/component/chasm/proc/on_chasm_stopped(datum/source)
SIGNAL_HANDLER
UnregisterSignal(source, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON))
for(var/atom/movable/movable as anything in source)
UnregisterSignal(movable, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED))
/datum/component/chasm/proc/on_chasm_no_longer_stopped(datum/source)
SIGNAL_HANDLER
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(entered))
RegisterSignal(parent, COMSIG_ATOM_EXITED, PROC_REF(exited))
RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on))
drop_stuff()
#define CHASM_NOT_DROPPING 0
#define CHASM_DROPPING 1
///Doesn't drop the movable, but registers a few signals to try again if the conditions change.
#define CHASM_REGISTER_SIGNALS 2
/datum/component/chasm/proc/drop_stuff(atom/movable/dropped_thing)
if(HAS_TRAIT(parent, TRAIT_CHASM_STOPPED))
return
var/atom/atom_parent = parent
var/to_check = dropped_thing ? list(dropped_thing) : atom_parent.contents
for (var/atom/movable/thing as anything in to_check)
var/dropping = droppable(thing)
switch(dropping)
if(CHASM_DROPPING)
INVOKE_ASYNC(src, PROC_REF(drop), thing)
if(CHASM_REGISTER_SIGNALS)
RegisterSignals(thing, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED), PROC_REF(drop_stuff), TRUE)
/datum/component/chasm/proc/droppable(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
// avoid an infinite loop, but allow falling a large distance
if(falling_atoms[falling_ref] && falling_atoms[falling_ref] > 30)
return CHASM_NOT_DROPPING
if(is_type_in_typecache(dropped_thing, forbidden_types) || (!isliving(dropped_thing) && !isobj(dropped_thing)))
return CHASM_NOT_DROPPING
if(dropped_thing.throwing || (dropped_thing.movement_type & MOVETYPES_NOT_TOUCHING_GROUND))
return CHASM_REGISTER_SIGNALS
for(var/atom/thing_to_check as anything in parent)
if(HAS_TRAIT(thing_to_check, TRAIT_CHASM_STOPPER))
return CHASM_NOT_DROPPING
//Flies right over the chasm
if(ismob(dropped_thing))
var/mob/M = dropped_thing
if(M.buckled) //middle statement to prevent infinite loops just in case!
var/mob/buckled_to = M.buckled
if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled))
return CHASM_REGISTER_SIGNALS
if(ishuman(dropped_thing))
var/mob/living/carbon/human/victim = dropped_thing
if(istype(victim.belt, /obj/item/wormhole_jaunter))
var/obj/item/wormhole_jaunter/jaunter = victim.belt
var/turf/chasm = get_turf(victim)
var/fall_into_chasm = jaunter.chasm_react(victim)
if(!fall_into_chasm)
chasm.visible_message(span_boldwarning("[victim] falls into the [chasm]!")) //To freak out any bystanders
return fall_into_chasm ? CHASM_DROPPING : CHASM_NOT_DROPPING
return CHASM_DROPPING
#undef CHASM_NOT_DROPPING
#undef CHASM_DROPPING
#undef CHASM_REGISTER_SIGNALS
/datum/component/chasm/proc/drop(atom/movable/dropped_thing)
var/datum/weakref/falling_ref = WEAKREF(dropped_thing)
//Make sure the item is still there after our sleep
if(!dropped_thing || !falling_ref?.resolve())
falling_atoms -= falling_ref
return
falling_atoms[falling_ref] = (falling_atoms[falling_ref] || 0) + 1
var/turf/below_turf = target_turf
var/atom/parent = src.parent
if(falling_atoms[falling_ref] > 1)
return // We're already handling this
if(below_turf)
if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED))
qdel(dropped_thing)
return
// send to the turf below
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[fall_message]"))
below_turf.visible_message(span_boldwarning("[dropped_thing] falls from above!"))
dropped_thing.forceMove(below_turf)
if(isliving(dropped_thing))
var/mob/living/fallen = dropped_thing
fallen.Paralyze(100)
fallen.adjustBruteLoss(30)
falling_atoms -= falling_ref
return
// send to oblivion
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[oblivion_message]"))
if (isliving(dropped_thing))
var/mob/living/falling_mob = dropped_thing
ADD_TRAIT(falling_mob, TRAIT_NO_TRANSFORM, REF(src))
falling_mob.Paralyze(20 SECONDS)
var/oldtransform = dropped_thing.transform
var/oldcolor = dropped_thing.color
var/oldalpha = dropped_thing.alpha
var/oldoffset = dropped_thing.pixel_y
animate(dropped_thing, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!dropped_thing || QDELETED(dropped_thing))
return
dropped_thing.pixel_y--
sleep(0.2 SECONDS)
//Make sure the item is still there after our sleep
if(!dropped_thing || QDELETED(dropped_thing))
return
if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED))
qdel(dropped_thing)
return
if(!storage)
storage = (locate() in parent) || new(parent)
if(storage.contains(dropped_thing))
return
dropped_thing.alpha = oldalpha
dropped_thing.color = oldcolor
dropped_thing.transform = oldtransform
dropped_thing.pixel_y = oldoffset
if(!dropped_thing.forceMove(storage))
parent.visible_message(span_boldwarning("[parent] spits out [dropped_thing]!"))
dropped_thing.throw_at(get_edge_target_turf(parent, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
else if(isliving(dropped_thing))
var/mob/living/fallen_mob = dropped_thing
REMOVE_TRAIT(fallen_mob, TRAIT_NO_TRANSFORM, REF(src))
if (fallen_mob.stat != DEAD)
fallen_mob.investigate_log("has died from falling into a chasm.", INVESTIGATE_DEATHS)
if(issilicon(fallen_mob))
//Silicons are held together by hopes and dreams, unfortunately, I'm having a nightmare
var/mob/living/silicon/robot/fallen_borg = fallen_mob
fallen_borg.mmi = null
fallen_mob.death(TRUE)
fallen_mob.apply_damage(300)
falling_atoms -= falling_ref
/**
* Called when something has left the chasm depths storage.
* Arguments
*
* * source - Chasm object holder.
* * gone - Item which has just left the chasm contents.
*/
/datum/component/chasm/proc/left_chasm(atom/source, atom/movable/gone)
SIGNAL_HANDLER
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
///Global list needed to let fishermen with a rescue hook fish fallen mobs from any place
GLOBAL_LIST_EMPTY(chasm_fallen_mobs)
/**
* An abstract object which is basically just a bag that the chasm puts people inside
*/
/obj/effect/abstract/chasm_storage
name = "chasm depths"
desc = "The bottom of a hole. You shouldn't be able to interact with this."
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/effect/abstract/chasm_storage/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SECLUDED_LOCATION, INNATE_TRAIT)
/obj/effect/abstract/chasm_storage/Entered(atom/movable/arrived)
. = ..()
if(isliving(arrived))
RegisterSignal(arrived, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
GLOB.chasm_fallen_mobs += arrived
/obj/effect/abstract/chasm_storage/Exited(atom/movable/gone)
. = ..()
if(isliving(gone))
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
GLOB.chasm_fallen_mobs -= gone
#define CHASM_TRAIT "chasm trait"
/**
* Called if something comes back to life inside the pit. Expected sources are badmins and changelings.
* Ethereals should take enough damage to be smashed and not revive.
* Arguments
* escapee - Lucky guy who just came back to life at the bottom of a hole.
*/
/obj/effect/abstract/chasm_storage/proc/on_revive(mob/living/escapee)
SIGNAL_HANDLER
var/turf/turf = get_turf(src)
if(turf.GetComponent(/datum/component/chasm))
turf.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [turf]!"))
else
playsound(turf, 'sound/effects/bang.ogg', 50, TRUE)
turf.visible_message(span_boldwarning("[escapee] busts through [turf], leaping out of the chasm below"))
turf.ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
escapee.forceMove(turf)
escapee.throw_at(get_edge_target_turf(turf, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
REMOVE_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT)
escapee.Paralyze(20 SECONDS, TRUE)
UnregisterSignal(escapee, COMSIG_LIVING_REVIVE)
#undef CHASM_TRAIT