mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-25 16:45:42 +00:00
## About The Pull Request This adds a new element for movables that grants turfs they're in traits, changes lava and the chasm component to check for traits instead, ditto for turf slowdown. It also implements another trait that prevents wet floor from slipping people, as well as some other changes (feel free to opine on them really): - Tables and conveyor belts now stop turf slowdown, much like catwalks, as I imagine people walking on them are not really touching the floor. (I'd include protection against lava too... until they melt, but that'd mean finding a way to have these objects burn in the first place, and lava code is still stupid despite a years old refactor I did) - Tables also stop slippery turfs from slipping (bananas, soaps etc. still apply). I wish there were a way to make some objects slippery by coating them in water vapor or splashing water/lube, but that's outside the scope of this PR. - Fixed an edge case in which a mob standing on a lava turf would be left permanently visually on fire if the lava is changed to another kind of turf. - Removed unused code from stone tiles. I'm going to include these traits in that global list for admin-added traits... tomorrow perhaps. 💤 ## Why It's Good For The Game Replacing some hard-coded mechanics with easier to use traits and an element, which I also need for the submerge element PR. ## Changelog 🆑 refactor: Replaced hardcoded "safeties" for lava, chasms and ignoring turf slowdowns on catwalks with traits. balance: much like catwalks, tables and conveyors also disable turf slowdowns. balance: slippery turfs won't slip you when walking on a table. fix: Fixed an edge case in which a mob standing on a lava turf would be left visually but permanently on fire if the lava is changed to another kind of turf. /🆑
274 lines
10 KiB
Plaintext
274 lines
10 KiB
Plaintext
/// List of weakrefs to containers for things which have fallen into chasms
|
|
GLOBAL_LIST_INIT(chasm_storage, list())
|
|
|
|
// 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/singularity,
|
|
/obj/energy_ball,
|
|
/obj/narsie,
|
|
/obj/docking_port,
|
|
/obj/structure/lattice,
|
|
/obj/structure/stone_tile,
|
|
/obj/projectile,
|
|
/obj/effect/projectile,
|
|
/obj/effect/portal,
|
|
/obj/effect/abstract,
|
|
/obj/effect/hotspot,
|
|
/obj/effect/landmark,
|
|
/obj/effect/temp_visual,
|
|
/obj/effect/light_emitter/tendril,
|
|
/obj/effect/collapse,
|
|
/obj/effect/particle_effect/ion_trails,
|
|
/obj/effect/dummy/phased_mob,
|
|
/obj/effect/mapping_helpers,
|
|
/obj/effect/wisp,
|
|
/obj/effect/ebeam,
|
|
/obj/effect/fishing_lure,
|
|
))
|
|
|
|
/datum/component/chasm/Initialize(turf/target, mapload)
|
|
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))
|
|
//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)
|
|
src.parent.AddElement(/datum/element/lazy_fishing_spot, FISHING_SPOT_PRESET_CHASM)
|
|
|
|
/datum/component/chasm/UnregisterFromParent()
|
|
remove_storage()
|
|
|
|
/**
|
|
* Deletes the chasm storage object and removes empty weakrefs from global list
|
|
*/
|
|
/datum/component/chasm/proc/remove_storage()
|
|
if (!storage)
|
|
return
|
|
QDEL_NULL(storage)
|
|
var/list/chasm_storage = list()
|
|
for (var/datum/weakref/ref as anything in GLOB.chasm_storage)
|
|
if (!ref.resolve())
|
|
continue
|
|
chasm_storage += ref
|
|
GLOB.chasm_storage = chasm_storage
|
|
|
|
/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/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 & (FLOATING|FLYING)))
|
|
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
|
|
falling_mob.notransform = TRUE
|
|
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 = new(get_turf(parent))
|
|
RegisterSignal(storage, COMSIG_ATOM_EXITED, PROC_REF(left_chasm))
|
|
GLOB.chasm_storage += WEAKREF(storage)
|
|
|
|
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))
|
|
if (isliving(dropped_thing))
|
|
RegisterSignal(dropped_thing, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
|
|
else
|
|
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))
|
|
|
|
if(isliving(dropped_thing))
|
|
var/mob/living/fallen_mob = dropped_thing
|
|
fallen_mob.notransform = FALSE
|
|
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)
|
|
|
|
#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.
|
|
*/
|
|
/datum/component/chasm/proc/on_revive(mob/living/escapee)
|
|
SIGNAL_HANDLER
|
|
var/atom/parent = src.parent
|
|
parent.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [parent]!"))
|
|
ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
|
|
escapee.forceMove(get_turf(parent))
|
|
escapee.throw_at(get_edge_target_turf(parent, 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
|
|
|
|
/**
|
|
* 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)
|