mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-17 21:24:01 +00:00
* Chasms prevent teleportation (#80102) ## About The Pull Request Chasms prevent teleportation Fixes #80101 ## Why It's Good For The Game Being able to teleport people into chasms using beacons as an anchor is bad. It has very little counterplay besides fishing out the beacon, changing the teleport target (assuming it's not hidden), or killing someone holding a handtele. The alternate idea for this PR was to see if the chasm added some sort of trait to the things falling in it and make it so the teleporter would ignore things with that trait, but it seems like chasms don't do that. ## Changelog 🆑 Tattle balance: you can no longer teleport into chasms /🆑 --------- Co-authored-by: tattle <article.disaster@ gmail.com> * Chasms prevent teleportation --------- Co-authored-by: tattle <66640614+dragomagol@users.noreply.github.com> Co-authored-by: tattle <article.disaster@ gmail.com>
279 lines
11 KiB
Plaintext
279 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,
|
|
))
|
|
|
|
/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
|
|
|
|
//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)
|
|
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
|