mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 17:52:36 +00:00
- Closes #90704 The following changes were implemented for this - Moved sending signals `COMSIG_MOVABLE_TELEPORTING` & `COMSIG_ATOM_INTERCEPT_TELEPORTING` from `check_teleport_valid` into `do_teleport`. These 2 signals were causing all the side effects - Removed signals `COMSIG_ATOM_INTERCEPT_TELEPORTED` & `COMSIG_MOVABLE_TELEPORTED` as the above 2 signals can do their jobs & block the teleport if needed so these signals became obsolete - Removed define `COMPONENT_BLOCK_TELEPORT` in favour of returning a positive value if teleport was successfully blocked. Having 2 signals located in 2 separate files use this same define doesn't look great. 🆑 code: cleans up teleportation code. `check_teleport_valid` is now a pure function with no side effects /🆑
320 lines
12 KiB
Plaintext
320 lines
12 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_float,
|
|
/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.AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/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 TRUE
|
|
|
|
/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))
|
|
LAZYADD(GLOB.chasm_fallen_mobs[get_chasm_category(loc)], arrived)
|
|
|
|
/obj/effect/abstract/chasm_storage/Exited(atom/movable/gone)
|
|
. = ..()
|
|
if(isliving(gone))
|
|
UnregisterSignal(gone, COMSIG_LIVING_REVIVE)
|
|
LAZYREMOVE(GLOB.chasm_fallen_mobs[get_chasm_category(loc)], gone)
|
|
|
|
/obj/effect/abstract/chasm_storage/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
|
|
. = ..()
|
|
var/old_cat = get_chasm_category(old_turf)
|
|
var/new_cat = get_chasm_category(new_turf)
|
|
var/list/mobs = list()
|
|
for(var/mob/fallen in src)
|
|
mobs += fallen
|
|
LAZYREMOVE(GLOB.chasm_fallen_mobs[old_cat], mobs)
|
|
LAZYADD(GLOB.chasm_fallen_mobs[new_cat], mobs)
|
|
|
|
/**
|
|
* Returns a key to store, remove and access fallen mobs depending on the z-level.
|
|
* This stops rescuing people from places that are waaaaaaaay too far-fetched.
|
|
*/
|
|
/proc/get_chasm_category(turf/turf)
|
|
var/z_level = turf?.z
|
|
var/area/area = get_area(turf)
|
|
if(istype(area, /area/shuttle)) //shuttle move between z-levels, so they're a special case.
|
|
return area
|
|
|
|
if(is_away_level(z_level))
|
|
return ZTRAIT_AWAY
|
|
if(is_mining_level(z_level))
|
|
return ZTRAIT_MINING
|
|
if(is_station_level(z_level))
|
|
return ZTRAIT_STATION
|
|
if(is_centcom_level(z_level))
|
|
return ZTRAIT_CENTCOM
|
|
if(is_reserved_level(z_level))
|
|
return ZTRAIT_RESERVED
|
|
|
|
return ZTRAIT_SPACE_RUINS
|
|
|
|
#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
|