From caec310a1dd2841bb8f9f00ffb9e3e603ba2d151 Mon Sep 17 00:00:00 2001
From: Zwei <35403274+Gottfrei@users.noreply.github.com>
Date: Tue, 9 Jul 2024 02:23:53 +0300
Subject: [PATCH] refactor: Chasms And Portals Refactor (#5330)
---
code/__DEFINES/dcs/signals.dm | 10 +-
code/__DEFINES/logs.dm | 1 +
code/__DEFINES/span.dm | 2 +-
code/__DEFINES/traits/declarations.dm | 8 +
code/__HELPERS/atoms.dm | 10 +
code/__HELPERS/string_lists.dm | 15 +
code/__HELPERS/unsorted.dm | 8 -
code/_globalvars/traits.dm | 4 +
code/_onclick/observer.dm | 4 -
code/controllers/subsystem/processing/dcs.dm | 6 +-
code/datums/components/chasm.dm | 303 +++++++++++++
code/datums/elements/_element.dm | 10 +-
code/datums/elements/give_turf_traits.dm | 88 ++++
code/datums/helper_datums/teleport.dm | 26 +-
code/game/atoms_movable.dm | 5 +
.../effects/decals/Cleanable/humans.dm | 3 +-
code/game/objects/effects/portals.dm | 60 ++-
code/game/objects/structures/lattice.dm | 9 +
code/game/objects/structures/tables_racks.dm | 19 +-
code/game/turfs/simulated/floor.dm | 8 -
code/game/turfs/simulated/floor/chasm.dm | 398 +++++-------------
code/game/turfs/simulated/floor/lava.dm | 13 +-
code/game/turfs/turf.dm | 6 +-
.../contractor/items/extraction_items.dm | 12 +-
code/modules/events/wormholes.dm | 2 +-
.../mining/equipment/wormhole_jaunter.dm | 138 +++---
.../ruins/lavalandruin_code/sin_ruins.dm | 35 +-
.../ruins/objects_and_mobs/necropolis_gate.dm | 19 +-
paradise.dme | 3 +
29 files changed, 783 insertions(+), 442 deletions(-)
create mode 100644 code/__HELPERS/string_lists.dm
create mode 100644 code/datums/components/chasm.dm
create mode 100644 code/datums/elements/give_turf_traits.dm
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 1821ae38792..2bf288ee665 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -120,11 +120,15 @@
#define COMSIG_ATOM_ENTERED "atom_entered"
/// Sent from the atom that just Entered src. From base of atom/Entered(): (/atom/destination, atom/old_loc, list/atom/old_locs)
#define COMSIG_ATOM_ENTERING "atom_entering"
+///from base of atom/movable/Moved(): (atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+#define COMSIG_ATOM_ABSTRACT_ENTERED "atom_abstract_entered"
///from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc)
#define COMSIG_ATOM_EXIT "atom_exit"
#define COMPONENT_ATOM_BLOCK_EXIT (1<<0)
///from base of atom/Exited(): (atom/movable/departed, atom/newloc)
#define COMSIG_ATOM_EXITED "atom_exited"
+///from base of atom/movable/Moved(): (atom/movable/gone, direction)
+#define COMSIG_ATOM_ABSTRACT_EXITED "atom_abstract_exited"
///from base of atom/Bumped(): (/atom/movable)
#define COMSIG_ATOM_BUMPED "atom_bumped"
///from base of atom/ex_act(): (severity, target)
@@ -190,8 +194,8 @@
///from base of atom/analyser_act(): (mob/living/user, obj/item/I)
#define COMSIG_ATOM_ANALYSER_ACT "atom_analyser_act"
#define COMPONENT_BLOCK_TOOL_ATTACK (1<<0)
-///called when teleporting into a protected turf: (channel, turf/origin)
-#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport"
+///called when teleporting into a possibly protected turf: (turf/origin)
+#define COMSIG_ATOM_INTERCEPT_TELEPORTING "intercept_teleporting"
#define COMPONENT_BLOCK_TELEPORT (1<<0)
///called when an atom is added to the hearers on get_hearers_in_view(): (list/processing_list, list/hearers)
#define COMSIG_ATOM_HEARER_IN_VIEW "atom_hearer_in_view"
@@ -353,6 +357,8 @@
///from datum/component/drift/allow_final_movement(): ()
#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input"
#define DRIFT_ALLOW_INPUT (1<<0)
+///Called before a movable is being teleported from `initTeleport()`: (turf/origin, turf/destination)
+#define COMSIG_MOVABLE_TELEPORTING "movable_teleporting"
// /datum/mind signals
diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm
index 927c2db76d1..5cbec474769 100644
--- a/code/__DEFINES/logs.dm
+++ b/code/__DEFINES/logs.dm
@@ -29,6 +29,7 @@
#define INVESTIGATE_RESEARCH "research"
#define INVESTIGATE_SYNDIE_CARGO "syndicate_cargo"
#define INVESTIGATE_WIRES "wires"
+#define INVESTIGATE_DEATHS "deaths"
//This is an external call, "true" and "false" are how rust parses out booleans
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index 3e0ebad0486..4dfdb21c86c 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -24,7 +24,7 @@
//#define span_blueteamradio(str) ("" + str + "")
//#define span_bold(str) ("" + str + "")
#define span_boldannounce(str) ("" + str + "")
-//#define span_bolddanger(str) ("" + str + "")
+#define span_bolddanger(str) ("" + str + "")
//#define span_boldnicegreen(str) ("" + str + "")
#define span_boldnotice(str) ("" + str + "")
#define span_boldwarning(str) ("" + str + "")
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 3f7d5f163ac..1ee614ed4ac 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -11,6 +11,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
//atom/movable traits
/// Buckling yourself to objects with this trait won't immobilize you
#define TRAIT_NO_IMMOBILIZE "no_immobilize"
+///Chasms will be safe to cross if there is something with this trait on it
+#define TRAIT_CHASM_STOPPER "chasm_stopper"
+/// `do_teleport` will not allow this atom to teleport
+#define TRAIT_NO_TELEPORT "no-teleport"
//turf traits
/// Prevent mobs on the turf from being affected by anything below that turf, such as a pulse demon going under it. Added by a /obj/structure with creates_cover set to TRUE
@@ -19,6 +23,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_TURF_IGNORE_SLOWDOWN "turf_ignore_slowdown"
///Mobs won't slip on a wet turf while it has this trait
#define TRAIT_TURF_IGNORE_SLIPPERY "turf_ignore_slippery"
+///Chasms will be safe to cross while they've this trait.
+#define TRAIT_CHASM_STOPPED "chasm_stopped"
+///Lava will be safe to cross while it has this trait.
+#define TRAIT_LAVA_STOPPED "lava_stopped"
//mob traits
#define TRAIT_PACIFISM "pacifism"
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index 08549761e39..d9f58ee60a1 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -19,6 +19,16 @@
if(istype(checked_atom, type))
. += checked_atom
+
+///Returns true if the src countain the atom target
+/atom/proc/contains(atom/target)
+ if(!target)
+ return FALSE
+ for(var/atom/location = target.loc, location, location = location.loc)
+ if(location == src)
+ return TRUE
+
+
/// Forces atom to drop all the important items while dereferencing them from their
/// containers both ways. To be used to preserve important items before mob gib/self-gib.
/// Returns a list with all saved items.
diff --git a/code/__HELPERS/string_lists.dm b/code/__HELPERS/string_lists.dm
new file mode 100644
index 00000000000..f4a73a6149e
--- /dev/null
+++ b/code/__HELPERS/string_lists.dm
@@ -0,0 +1,15 @@
+GLOBAL_LIST_EMPTY(string_lists)
+
+/**
+ * Caches lists with non-numeric stringify-able values (text or typepath).
+ */
+/proc/string_list(list/values)
+ var/string_id = values.Join("-")
+
+ . = GLOB.string_lists[string_id]
+
+ if(.)
+ return .
+
+ return GLOB.string_lists[string_id] = values
+
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index de26ca4a3b7..a8486e0dccd 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -2005,14 +2005,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
)
return _list
-// Check if the source atom contains another atom
-/atom/proc/contains(atom/location)
- if(!location)
- return FALSE
- if(location == src)
- return TRUE
-
- return contains(location.loc)
/**
* Returns the clean name of an audio channel.
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 6d5d260661c..fda0ec192ca 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -11,6 +11,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CMAGGED" = TRAIT_CMAGGED,
),
/atom/movable = list(
+ "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
"TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING,
"TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING,
"TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
@@ -19,6 +20,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_IMMOBILIZE" = TRAIT_NO_IMMOBILIZE,
+ "TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT,
),
/mob = list(
"TRAIT_AI_UNTRACKABLE" = TRAIT_AI_UNTRACKABLE,
@@ -77,6 +79,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_WIELDED" = TRAIT_WIELDED,
),
/turf = list(
+ "TRAIT_CHASM_STOPPED" = TRAIT_CHASM_STOPPED,
+ "TRAIT_LAVA_STOPPED" = TRAIT_LAVA_STOPPED,
"TRAIT_TURF_COVERED" = TRAIT_TURF_COVERED,
"TRAIT_TURF_IGNORE_SLIPPERY" = TRAIT_TURF_IGNORE_SLIPPERY,
"TRAIT_TURF_IGNORE_SLOWDOWN" = TRAIT_TURF_IGNORE_SLOWDOWN,
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 37063d0ada9..a6a40b43d24 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -88,10 +88,6 @@
if(com && com.target)
user.forceMove(get_turf(com.target))
-/obj/effect/portal/attack_ghost(mob/user)
- if(target)
- user.forceMove(get_turf(target))
-
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
if(awaygate)
user.forceMove(awaygate.loc)
diff --git a/code/controllers/subsystem/processing/dcs.dm b/code/controllers/subsystem/processing/dcs.dm
index 1d0c12f1ece..1edcb56c40c 100644
--- a/code/controllers/subsystem/processing/dcs.dm
+++ b/code/controllers/subsystem/processing/dcs.dm
@@ -10,7 +10,7 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
/datum/controller/subsystem/processing/dcs/Recover()
comp_lookup = SSdcs.comp_lookup
-/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments)
+/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments, init_element = TRUE)
var/datum/element/eletype = arguments[1]
var/element_id = eletype
@@ -21,8 +21,8 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
element_id = GetIdFromArguments(arguments)
. = elements_by_type[element_id]
- if(.)
- return
+ if(. || !init_element)
+ return .
. = elements_by_type[element_id] = new eletype
/****
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
new file mode 100644
index 00000000000..7a66e651e14
--- /dev/null
+++ b/code/datums/components/chasm.dm
@@ -0,0 +1,303 @@
+// 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/docking_port,
+ /obj/structure/lattice,
+ /obj/structure/stone_tile,
+ /obj/item/projectile,
+ /obj/effect/portal,
+ /obj/effect/hotspot,
+ /obj/effect/landmark,
+ /obj/effect/temp_visual,
+ /obj/effect/light_emitter/tendril,
+ /obj/effect/collapse,
+ /obj/effect/abstract,
+ /obj/effect/particle_effect/smoke,
+ /obj/effect/particle_effect/ion_trails,
+ /obj/effect/particle_effect/sparks,
+ /obj/effect/particle_effect/expl_particles,
+ /obj/effect/wisp,
+ /obj/effect/ebeam,
+ /obj/effect/spawner,
+ /obj/structure/railing,
+ /mob/living/simple_animal/hostile/megafauna, //failsafe
+ ))
+
+
+/datum/component/chasm/Initialize(turf/target_turf, 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))
+ src.target_turf = target_turf
+ 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)
+
+
+/datum/component/chasm/UnregisterFromParent()
+ storage = null
+
+
+/datum/component/chasm/proc/entered(datum/source, atom/movable/arrived)
+ SIGNAL_HANDLER
+
+ drop_stuff(arrived)
+
+
+/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()
+ SIGNAL_HANDLER
+
+ return COMPONENT_BLOCK_TELEPORT
+
+
+/datum/component/chasm/proc/on_chasm_stopped(datum/source)
+ SIGNAL_HANDLER
+
+ var/atom/atom_parent = parent
+ UnregisterSignal(atom_parent, list(COMSIG_ATOM_ABSTRACT_ENTERED, COMSIG_ATOM_ABSTRACT_EXITED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON))
+ for(var/atom/movable/movable as anything in atom_parent)
+ 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_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))
+ 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/list/to_check = dropped_thing ? list(dropped_thing) : atom_parent.contents
+ for(var/atom/movable/thing in to_check)
+ var/dropping = droppable(thing)
+ switch(dropping)
+ if(CHASM_DROPPING)
+ INVOKE_ASYNC(src, PROC_REF(drop), thing)
+ if(CHASM_REGISTER_SIGNALS)
+ RegisterSignal(thing, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED), PROC_REF(drop_stuff), override = TRUE)
+
+
+/datum/component/chasm/proc/droppable(atom/movable/dropped_thing)
+ var/atom/atom_parent = parent
+ if(!dropped_thing.simulated)
+ return CHASM_NOT_DROPPING
+ 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 atom_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/dropped_mob = dropped_thing
+ if(dropped_mob.buckled) //middle statement to prevent infinite loops just in case!
+ var/mob/buckled_to = dropped_mob.buckled
+ if((!ismob(buckled_to) || (buckled_to.buckled != dropped_mob)) && !droppable(buckled_to))
+ return CHASM_REGISTER_SIGNALS
+ if(isliving(dropped_mob))
+ var/mob/living/dropped_living = dropped_mob
+ if(dropped_living.incorporeal_move)
+ return CHASM_NOT_DROPPING
+ if(ishuman(dropped_mob))
+ var/obj/item/wormhole_jaunter/jaunter = locate() in dropped_mob
+ if(jaunter)
+ var/turf/chasm = get_turf(dropped_mob)
+ var/fall_into_chasm = jaunter.chasm_react(dropped_mob)
+ if(!fall_into_chasm)
+ chasm.visible_message(span_boldwarning("[dropped_mob] 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/atom/atom_parent = parent
+ 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
+
+ if(falling_atoms[falling_ref] > 1)
+ return // We're already handling this
+
+ if(below_turf)
+ // send to the turf below
+ dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [atom_parent]!"), span_userdanger("[fall_message]"))
+ below_turf.visible_message(span_boldwarning("[dropped_thing] falls from above!"))
+ playsound(below_turf, 'sound/effects/break_stone.ogg', 50, TRUE)
+ dropped_thing.forceMove(below_turf)
+ if(isliving(dropped_thing))
+ var/mob/living/fallen = dropped_thing
+ fallen.Weaken(10 SECONDS)
+ fallen.adjustBruteLoss(30)
+ falling_atoms -= falling_ref
+ return
+
+ // send to oblivion
+ dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [atom_parent]!"), span_userdanger("[oblivion_message]"))
+ if(isliving(dropped_thing))
+ var/mob/living/falling_mob = dropped_thing
+ ADD_TRAIT(falling_mob, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
+ falling_mob.Stun(20 SECONDS)
+
+ var/oldtransform = dropped_thing.transform
+ var/oldcolor = dropped_thing.color
+ var/oldalpha = dropped_thing.alpha
+ var/oldoffset = dropped_thing.pixel_y
+
+ if(ishuman(dropped_thing))
+ var/mob/living/carbon/human/dropped_human = dropped_thing
+ if(dropped_human.stat != DEAD && prob(25))
+ playsound(atom_parent, 'sound/effects/wilhelm_scream.ogg', 150)
+
+ 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(QDELETED(dropped_thing))
+ return
+ dropped_thing.pixel_y--
+ sleep(0.2 SECONDS)
+
+ //Make sure the item is still there after our sleep
+ if(QDELETED(dropped_thing))
+ return
+
+ if(isrobot(dropped_thing))
+ var/mob/living/silicon/robot/robot = dropped_thing
+ qdel(robot.mmi)
+ qdel(dropped_thing)
+ falling_atoms -= falling_ref
+ return
+
+ if(!storage)
+ storage = (locate() in atom_parent) || new(atom_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))
+ atom_parent.visible_message(span_boldwarning("[atom_parent] spits out [dropped_thing]!"))
+ dropped_thing.throw_at(get_edge_target_turf(atom_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, UNIQUE_TRAIT_SOURCE(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(1000)
+
+ falling_atoms -= falling_ref
+
+
+
+/**
+ * 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/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ . = ..()
+ if(isliving(arrived))
+ RegisterSignal(arrived, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
+
+
+/obj/effect/abstract/chasm_storage/Exited(atom/movable/departed, atom/newLoc)
+ . = ..()
+ if(isliving(departed))
+ UnregisterSignal(departed, COMSIG_LIVING_REVIVE)
+
+
+/obj/effect/abstract/chasm_storage/proc/get_fish(mob/fish, atom/new_loc)
+ if(!(fish in src))
+ stack_trace("Attempting to remove [fish] which is not in [src] contents")
+ return
+ UnregisterSignal(fish, COMSIG_LIVING_REVIVE)
+ if(new_loc)
+ fish.forceMove(new_loc)
+
+
+/**
+ * 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/ourturf = get_turf(src)
+ if(ourturf.GetComponent(/datum/component/chasm))
+ ourturf.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [ourturf]!"))
+ else
+ playsound(ourturf, 'sound/effects/bang.ogg', 50, TRUE)
+ ourturf.visible_message(span_boldwarning("[escapee] busts through [ourturf], leaping out of the chasm below!"))
+ ourturf.ChangeTurf(ourturf.baseturf)
+ ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
+ escapee.forceMove(ourturf)
+ escapee.throw_at(get_edge_target_turf(ourturf, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
+ REMOVE_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT)
+ escapee.Sleeping(20 SECONDS)
+ UnregisterSignal(escapee, COMSIG_LIVING_REVIVE)
+
diff --git a/code/datums/elements/_element.dm b/code/datums/elements/_element.dm
index b9a1dca80e1..98540c5c0d2 100644
--- a/code/datums/elements/_element.dm
+++ b/code/datums/elements/_element.dm
@@ -41,7 +41,13 @@
/// Finds the singleton for the element type given and attaches it to src
/datum/proc/_AddElement(list/arguments)
+ if(QDELING(src))
+ var/datum/element/element_type = arguments[1]
+ stack_trace("We just tried to add the element [element_type] to a qdeleted datum, something is fucked")
+ return
var/datum/element/ele = SSdcs.GetElement(arguments)
+ if(!ele) // We couldn't fetch the element, likely because it was not an element.
+ return // the crash message has already been sent
arguments[1] = src
if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
@@ -51,5 +57,7 @@
* You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
*/
/datum/proc/_RemoveElement(list/arguments)
- var/datum/element/ele = SSdcs.GetElement(arguments)
+ var/datum/element/ele = SSdcs.GetElement(arguments, init_element = FALSE)
+ if(!ele) // We couldn't fetch the element, likely because it didn't exist.
+ return
ele.Detach(src)
diff --git a/code/datums/elements/give_turf_traits.dm b/code/datums/elements/give_turf_traits.dm
new file mode 100644
index 00000000000..8e9ac9132c6
--- /dev/null
+++ b/code/datums/elements/give_turf_traits.dm
@@ -0,0 +1,88 @@
+/// A bespoke element that adds a set of traits to the turf while occupied by at least one attached movabled.
+/datum/element/give_turf_traits
+ element_flags = ELEMENT_DETACH|ELEMENT_BESPOKE
+ id_arg_index = 2
+ ///A list of traits that are added to the turf while occupied.
+ var/list/traits
+ ///List of sources we are using to reapply traits when turf changes
+ var/list/trait_sources = list()
+
+
+/datum/element/give_turf_traits/Attach(atom/movable/target, list/traits)
+ . = ..()
+ if(!istype(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.traits = traits
+
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
+ if(isturf(target.loc))
+ add_to_occupied_turfs(target.loc, target)
+
+
+/datum/element/give_turf_traits/Detach(atom/movable/source)
+ UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
+ if(isturf(source.loc))
+ remove_from_occupied_turfs(source.loc, source)
+ return ..()
+
+
+/// Removes the trait from the old turf and adds it to the new one.
+/datum/element/give_turf_traits/proc/on_moved(atom/movable/source, atom/old_loc)
+ SIGNAL_HANDLER
+ if(isturf(old_loc))
+ remove_from_occupied_turfs(old_loc, source)
+
+ if(isturf(source.loc))
+ add_to_occupied_turfs(source.loc, source)
+
+
+/**
+ * Registers the turf signals if it was previously unoccupied and adds it to the list of occupied turfs.
+ * Otherwise, it just adds the movable to the assoc value of lists occupying the turf.
+ */
+/datum/element/give_turf_traits/proc/add_to_occupied_turfs(turf/location, atom/movable/source)
+ var/trait_source = UNIQUE_TRAIT_SOURCE(source)
+ if(isnull(trait_sources) || isnull(trait_sources[location]))
+ RegisterSignal(location, COMSIG_TURF_CHANGE, PROC_REF(pre_change_turf))
+
+ LAZYADDASSOCLIST(trait_sources, location, trait_source)
+ var/update_movespeeds = (TRAIT_TURF_IGNORE_SLOWDOWN in traits) && !HAS_TRAIT(location, TRAIT_TURF_IGNORE_SLOWDOWN)
+ for(var/trait in traits)
+ ADD_TRAIT(location, trait, trait_source)
+ if(update_movespeeds)
+ for(var/mob/living/living in location)
+ living.update_turf_movespeed(location)
+
+
+/**
+ * Unregisters the turf signals if it's no longer unoccupied and removes it from the list of occupied turfs.
+ * Otherwise, it just removes the movable from the assoc value of lists occupying the turf.
+ */
+/datum/element/give_turf_traits/proc/remove_from_occupied_turfs(turf/location, atom/movable/source)
+ var/trait_source = UNIQUE_TRAIT_SOURCE(source)
+ LAZYREMOVEASSOC(trait_sources, location, trait_source)
+ if(isnull(trait_sources) || isnull(trait_sources[location]))
+ UnregisterSignal(location, COMSIG_TURF_CHANGE)
+
+ for(var/trait in traits)
+ REMOVE_TRAIT(location, trait, trait_source)
+
+ if((TRAIT_TURF_IGNORE_SLOWDOWN in traits) && !HAS_TRAIT(location, TRAIT_TURF_IGNORE_SLOWDOWN))
+ for(var/mob/living/living in location)
+ living.update_turf_movespeed(location)
+
+
+/// Signals and components are carried over when the turf is changed, so they've to be readded post-change.
+/datum/element/give_turf_traits/proc/pre_change_turf(turf/changed, path, list/post_change_callbacks)
+ SIGNAL_HANDLER
+
+ post_change_callbacks += CALLBACK(src, PROC_REF(reoccupy_turf))
+
+
+/// Reapply turf traits to the provided turf
+/datum/element/give_turf_traits/proc/reoccupy_turf(turf/changed)
+ for(var/trait in traits)
+ for(var/source in trait_sources[changed])
+ ADD_TRAIT(changed, trait, source)
+
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index 34a2a31f8e6..0d116755c7c 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -113,28 +113,29 @@
destturf = get_turf(destination)
if(!is_teleport_allowed(destturf.z) && !ignore_area_flag)
- return 0
+ return FALSE
// Only check the destination zlevel for is_teleport_allowed. Checking origin as well breaks ERT teleporters.
var/area/destarea = get_area(destturf)
if(!ignore_area_flag)
if(curarea.tele_proof)
- return 0
+ return FALSE
if(destarea.tele_proof)
- return 0
+ return FALSE
if(!destturf || !curturf)
- return 0
+ return FALSE
- playSpecials(curturf,effectin,soundin)
+ if(SEND_SIGNAL(teleatom, COMSIG_MOVABLE_TELEPORTING, curturf, destturf) & COMPONENT_BLOCK_TELEPORT)
+ return FALSE
+ if(SEND_SIGNAL(destturf, COMSIG_ATOM_INTERCEPT_TELEPORTING, curturf) & COMPONENT_BLOCK_TELEPORT)
+ return FALSE
- if(force_teleport)
- teleatom.forceMove(destturf)
- playSpecials(destturf,effectout,soundout)
- else
- if(teleatom.Move(destturf))
- playSpecials(destturf,effectout,soundout)
+ playSpecials(curturf, effectin, soundin)
+ var/success = teleatom.forceMove(destturf)
+ if(success)
+ playSpecials(destturf, effectout, soundout)
if(isliving(teleatom))
var/mob/living/L = teleatom
@@ -143,10 +144,9 @@
if(L.has_buckled_mobs())
L.unbuckle_all_mobs(force = TRUE)
- destarea.Entered(teleatom)
teleatom.on_teleported()
- return 1
+ return TRUE
/datum/teleport/proc/teleport()
if(teleportChecks())
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index dd2de880f16..7833c5e7318 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -700,6 +700,11 @@
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs, momentum_change)
+ if(old_loc)
+ SEND_SIGNAL(old_loc, COMSIG_ATOM_ABSTRACT_EXITED, src, movement_dir)
+ if(loc)
+ SEND_SIGNAL(loc, COMSIG_ATOM_ABSTRACT_ENTERED, src, old_loc, old_locs)
+
var/turf/old_turf = get_turf(old_loc)
var/turf/new_turf = get_turf(src)
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index 1e978b1cc3b..0804e2bb9b4 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -44,7 +44,8 @@
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
COMSIG_ATOM_EXITED = PROC_REF(on_exited),
)
- AddElement(/datum/element/connect_loc, loc_connections)
+ if(!QDELING(src))
+ AddElement(/datum/element/connect_loc, loc_connections)
/obj/effect/decal/cleanable/blood/Destroy()
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index dfa4168220b..2242c56422d 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -8,6 +8,8 @@
icon_state = "portal"
base_icon_state = "portal"
anchored = TRUE
+ density = TRUE // dense for receiving bumbs
+ layer = HIGH_OBJ_LAYER
var/obj/item/target
/// The UID and `name` of the object that created this portal. For example, a wormhole jaunter.
@@ -24,7 +26,10 @@
var/can_multitool_to_remove = FALSE
var/can_mecha_pass = FALSE
var/ignore_tele_proof_area_setting = FALSE
- var/one_use = FALSE // Does this portal go away after one teleport?
+ // Does this portal go away after one teleport?
+ var/one_use = FALSE
+ /// Does this portal bypass teleport restrictions? like TRAIT_NO_TELEPORT
+ var/force_teleport = FALSE
/// The time after which the effects should play again. Too many effects can lag the server
var/effect_cooldown = 0
///Whether or not portal use will cause sparks
@@ -55,11 +60,6 @@
if(lifespan > 0)
QDEL_IN(src, lifespan)
- var/static/list/loc_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
- )
- AddElement(/datum/element/connect_loc, loc_connections)
-
/obj/effect/portal/Destroy()
GLOB.portals -= src
@@ -97,22 +97,14 @@
return
-/obj/effect/portal/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
- SIGNAL_HANDLER
+/obj/effect/portal/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(!force_teleport && (HAS_TRAIT(mover, TRAIT_NO_TELEPORT) || !can_teleport(mover)))
+ return TRUE
- if(isobserver(arrived))
- return
- if(target && (get_turf(old_loc) == get_turf(target)))
- return
-
- if(istype(arrived, /obj/effect/portal))
- qdel(arrived)
- qdel(src)
- log_debug("Portal [src] crossed by another portal [arrived]")
- return
-
- teleport(arrived)
+/obj/effect/portal/Bumped(atom/movable/moving_atom)
+ teleport(moving_atom)
/obj/effect/portal/attack_tk(mob/user)
@@ -122,11 +114,19 @@
/obj/effect/portal/attack_hand(mob/user)
. = ..()
if(.)
- return
- if(get_turf(user) == get_turf(src))
+ return .
+ if(Adjacent(user))
teleport(user)
+
+
+/obj/effect/portal/attack_robot(mob/living/user)
if(Adjacent(user))
- user.forceMove(get_turf(src))
+ teleport(user)
+
+
+/obj/effect/portal/attack_ghost(mob/user)
+ if(target)
+ teleport(user)
/obj/effect/portal/multitool_act(mob/user, obj/item/I)
@@ -136,20 +136,20 @@
if(can_multitool_to_remove)
qdel(src)
else
- user.forceMove(get_turf(src))
+ teleport(user)
-/obj/effect/portal/proc/can_teleport(atom/movable/M)
+/obj/effect/portal/proc/can_teleport(atom/movable/M, silent = FALSE)
. = TRUE
if(!istype(M))
- . = FALSE
+ return FALSE
if(!M.simulated || iseffect(M))
- . = FALSE
+ return FALSE
if(!can_mecha_pass && M.anchored && ismecha(M))
- . = FALSE
+ return FALSE
/obj/effect/portal/proc/teleport(atom/movable/M)
@@ -171,8 +171,6 @@
investigate_log("[M] has used a portal, [creator_string].", INVESTIGATE_TELEPORTATION)
if(prob(failchance))
- icon_state = fail_icon
- update_icon(UPDATE_ICON_STATE)
var/list/target_z = levels_by_trait(SPAWN_RUINS)
target_z -= M.z
if(!attempt_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), pick(target_z)), 0, FALSE)) // Try to send them to deep space.
@@ -219,7 +217,7 @@
. += span_warning("[src] is shaking, it looks very unstable!")
-/obj/effect/portal/hand_tele/can_teleport(atom/movable/M)
+/obj/effect/portal/hand_tele/can_teleport(atom/movable/M, silent = FALSE)
if(inactive)
return FALSE
return ..()
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 4aee9e4c7bc..7d0456b0e7e 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -181,6 +181,15 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
number_of_rods = 3
+
+/obj/structure/lattice/catwalk/fireproof/Initialize(mapload)
+ . = ..()
+ var/static/list/give_turf_traits
+ if(!give_turf_traits)
+ give_turf_traits = string_list(list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_TURF_IGNORE_SLOWDOWN))
+ AddElement(/datum/element/give_turf_traits, give_turf_traits)
+
+
/obj/structure/lattice/catwalk/fireproof/wirecutter_act(mob/living/user, obj/item/I)
to_chat(user, "Вы начали срезать усиленные прутья, это займёт некоторое время...")
if(!I.use_tool(src, user, 80, volume = I.tool_volume))
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 704a5e829b1..383791b1e4a 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -51,6 +51,7 @@
COMSIG_ATOM_EXIT = PROC_REF(on_exit),
)
AddElement(/datum/element/connect_loc, loc_connections)
+ update_flipped_turf()
/obj/structure/table/examine(mob/user)
@@ -222,9 +223,9 @@
return FALSE // Blocked
-/obj/structure/table/can_touch(mob/living/user)
+/obj/structure/table/can_touch(mob/living/user, flip = FALSE)
. = ..()
- if(. && flipped)
+ if(. && !flip && flipped)
to_chat(user, span_notice("You cannot climb on the flipped table."))
return FALSE
@@ -376,7 +377,7 @@
/obj/structure/table/proc/actual_flip(mob/living/user)
- if(!can_be_flipped || !can_touch(user))
+ if(!can_be_flipped || !can_touch(user, flip = TRUE))
return FALSE
if(!flipped)
@@ -412,6 +413,7 @@
smooth = NONE
flags |= ON_BORDER
playsound(loc, flip_sound, 100, TRUE)
+ update_flipped_turf()
for(var/check_dir in list(turn(direction, 90), turn(direction, -90)))
var/obj/structure/table/other_table = locate(/obj/structure/table, get_step(src, check_dir))
@@ -442,6 +444,7 @@
smooth = initial(smooth)
flags &= ~ON_BORDER
playsound(loc, flip_sound, 100, TRUE)
+ update_flipped_turf()
for(var/check_dir in list(turn(dir, 90), turn(dir, -90)))
var/obj/structure/table/other_table = locate(/obj/structure/table, get_step(src, check_dir))
@@ -457,6 +460,16 @@
return TRUE
+/obj/structure/table/proc/update_flipped_turf()
+ var/static/list/give_turf_traits
+ if(!give_turf_traits)
+ give_turf_traits = string_list(list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY))
+ if(flipped)
+ RemoveElement(/datum/element/give_turf_traits, give_turf_traits)
+ else
+ AddElement(/datum/element/give_turf_traits, give_turf_traits)
+
+
/*
* Glass Tables
*/
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 632e8bea7bf..34a00143c8e 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -110,14 +110,6 @@ GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","dama
return FALSE
return TRUE
-// Checks if there is foothold over the turf
-/turf/simulated/floor/proc/find_safeties()
- var/static/list/safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile))
- var/list/found_safeties = typecache_filter_list(contents, safeties_typecache)
- for(var/obj/structure/stone_tile/S in found_safeties)
- if(S.fallen)
- LAZYREMOVE(found_safeties, S)
- return LAZYLEN(found_safeties)
/turf/simulated/floor/blob_act(obj/structure/blob/B)
return
diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm
index 666224db0df..f9db1ce8585 100644
--- a/code/game/turfs/simulated/floor/chasm.dm
+++ b/code/game/turfs/simulated/floor/chasm.dm
@@ -9,339 +9,181 @@
canSmoothWith = SMOOTH_GROUP_TURF_CHASM
smoothing_groups = SMOOTH_GROUP_TURF_CHASM
density = TRUE //This will prevent hostile mobs from pathing into chasms, while the canpass override will still let it function like an open turf
- layer = 1.7
- intact = 0
+ layer = PLATING_LAYER
+ intact = FALSE
explosion_vertical_block = 0
- var/static/list/falling_atoms = list() //Atoms currently falling into the chasm
- var/static/list/forbidden_types = typecacheof(list(
- /obj/singularity,
- /obj/docking_port,
- /obj/structure/lattice,
- /obj/structure/stone_tile,
- /obj/item/projectile,
- /obj/effect/portal,
- /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/abstract,
- /obj/effect/ebeam,
- /obj/effect/spawner,
- /obj/structure/railing,
- /obj/machinery/atmospherics/pipe/simple,
- /mob/living/simple_animal/hostile/megafauna //failsafe
- ))
- var/drop_x = 1
- var/drop_y = 1
- var/drop_z = 1
-
footstep = null
barefootstep = null
clawfootstep = null
heavyfootstep = null
-/turf/simulated/floor/chasm/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+
+/turf/simulated/floor/chasm/Initialize(mapload)
. = ..()
- START_PROCESSING(SSprocessing, src)
- drop_stuff(arrived)
+ apply_components(mapload)
-/turf/simulated/floor/chasm/CanPathfindPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
- return ((caller.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || ismegafauna(caller))
-/turf/simulated/floor/chasm/process()
- if(!drop_stuff())
- STOP_PROCESSING(SSprocessing, src)
+/// Handles adding the chasm component to the turf (So stuff falls into it!)
+/turf/simulated/floor/chasm/proc/apply_components(mapload)
+ AddComponent(/datum/component/chasm, GET_TURF_BELOW(src), mapload)
-/turf/simulated/floor/chasm/Initialize()
- . = ..()
- drop_z = level_name_to_num(EMPTY_AREA)
-/turf/simulated/floor/chasm/ex_act()
- return
+/// Lets people walk into chasms.
+/turf/simulated/floor/chasm/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ return TRUE
-/turf/simulated/floor/chasm/acid_act(acidpwr, acid_volume)
- return
-/turf/simulated/floor/chasm/singularity_act()
- return
+/turf/simulated/floor/chasm/proc/set_target(turf/target)
+ var/datum/component/chasm/chasm_component = GetComponent(/datum/component/chasm)
+ chasm_component.target_turf = target
-/turf/simulated/floor/chasm/singularity_pull(S, current_size)
- return
-/turf/simulated/floor/chasm/crowbar_act()
- return
+/turf/simulated/floor/chasm/proc/drop(atom/movable/AM)
+ var/datum/component/chasm/chasm_component = GetComponent(/datum/component/chasm)
+ chasm_component.drop(AM)
-/turf/simulated/floor/chasm/make_plating()
- return
-/turf/simulated/floor/chasm/remove_plating()
- return
+/turf/simulated/floor/chasm/CanPathfindPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+ return ((caller.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || ismegafauna(caller))
-/turf/simulated/floor/chasm/rcd_act()
- return RCD_NO_ACT
/turf/simulated/floor/chasm/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
underlay_appearance.icon = 'icons/turf/floors.dmi'
underlay_appearance.icon_state = "basalt"
return TRUE
-/turf/simulated/floor/chasm/attackby(obj/item/C, mob/user, params, area/area_restriction)
+
+/turf/simulated/floor/chasm/is_safe()
+ if(HAS_TRAIT(src, TRAIT_CHASM_STOPPED) && ..())
+ return TRUE
+ return FALSE
+
+
+/turf/simulated/floor/chasm/attackby(obj/item/I, mob/user, params)
..()
- if(istype(C, /obj/item/stack/fireproof_rods))
- var/obj/item/stack/fireproof_rods/R = C
- var/obj/structure/lattice/fireproof/L = locate(/obj/structure/lattice, src)
- var/obj/structure/lattice/catwalk/fireproof/W = locate(/obj/structure/lattice/catwalk/fireproof, src)
- if(W)
+ if(istype(I, /obj/item/stack/fireproof_rods))
+ var/obj/item/stack/fireproof_rods/rods = I
+ var/obj/structure/lattice/catwalk/fireproof/catwalk = locate() in contents
+ if(catwalk)
to_chat(user, span_warning("Здесь уже есть мостик!"))
return
- if(!L)
- if(R.use(1))
+ var/obj/structure/lattice/fireproof/lattice = locate() in contents
+ if(!lattice)
+ if(rods.use(1))
to_chat(user, span_notice("Вы установили прочную решётку."))
- playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
+ playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
new /obj/structure/lattice/fireproof(src)
else
- to_chat(user, span_warning("Вам нужен один огнеупорный стержень для постройки решётки."))
+ to_chat(user, span_warning("Вам нужен один огнеупорный стержень для постройки решётки!"))
+ return
+ if(!rods.use(2))
+ to_chat(user, span_warning("Вам нужно два огнеупорных стержня для постройки мостика!"))
return
- if(L)
- if(R.use(2))
- qdel(L)
- playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
- to_chat(user, span_notice("Вы установили мостик."))
- new /obj/structure/lattice/catwalk/fireproof(src)
- if(istype(C, /obj/item/twohanded/fishingrod))
- var/obj/item/twohanded/fishingrod/rod = C
+ to_chat(user, span_notice("Вы установили мостик."))
+ playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
+ new /obj/structure/lattice/catwalk/fireproof(src)
+ return
+
+ else if(istype(I, /obj/item/twohanded/fishingrod))
+ var/obj/item/twohanded/fishingrod/rod = I
if(!HAS_TRAIT(rod, TRAIT_WIELDED))
- to_chat(user, span_warning("You need to wield the rod in both hands before you can fish in the chasm!"))
+ to_chat(user, span_warning("Для того чтобы начать ловлю следует взять удочку в обе руки!"))
return
- user.visible_message(span_warning("[user] throws a fishing rod into the chasm and tries to catch something!"),
- span_notice("You started to fishing."),
- span_notice("You hear the sound of a fishing rod."))
+ user.visible_message(
+ span_notice("[user] забрасывает удочку в пропасть, надеясь что-либо поймать!"),
+ span_notice("Вы приступили к рыбалке."),
+ span_italics("Вы слышите долгий потрескивающий звук."),
+ )
playsound(rod, 'sound/effects/fishing_rod_throw.ogg', 30)
- if(do_after(user, 6 SECONDS, src))
- if(!HAS_TRAIT(rod, TRAIT_WIELDED))
- return
- var/list/fishing_contents = list()
- for(var/turf/simulated/floor/chasm/chasm in range(4, src))
- fishing_contents += chasm.GetAllContents()
- if(!length(fishing_contents))
- to_chat(user, span_warning("There's nothing here!"))
- return
- var/found = FALSE
- for(var/mob/M in fishing_contents)
- M.forceMove(get_turf(user))
- UnregisterSignal(M, COMSIG_LIVING_REVIVE)
- found = TRUE
- break
- if(found)
- to_chat(user, span_warning("You reel in something!"))
- playsound(rod, 'sound/effects/fishing_rod_catch.ogg', 30)
- else
- to_chat(user, span_warning("There's nothing here!"))
- return
+ if(!do_after(user, 6 SECONDS, src, extra_checks = CALLBACK(src, PROC_REF(rod_checks), rod)))
+ return
-/turf/simulated/floor/chasm/is_safe()
- if(find_safeties() && ..())
- return TRUE
- return FALSE
+ var/list/fishing_contents = list()
+ for(var/turf/simulated/floor/chasm/chasm in range(4, src))
+ fishing_contents += chasm.get_fish()
-/turf/simulated/floor/chasm/proc/drop_stuff(AM)
- . = 0
- if(find_safeties())
- return FALSE
- var/thing_to_check = src
- if(AM)
- thing_to_check = list(AM)
- for(var/thing in thing_to_check)
- if(droppable(thing))
- . = 1
- INVOKE_ASYNC(src, PROC_REF(drop), thing)
-
-/turf/simulated/floor/chasm/proc/droppable(atom/movable/AM)
- if(falling_atoms[AM])
- return FALSE
- if(!isliving(AM) && !isobj(AM))
- return FALSE
- if(iseffect(AM) && !(istype(AM, /obj/effect/mob_spawn)))
- return FALSE
- if(!AM.simulated || is_type_in_typecache(AM, forbidden_types) || AM.throwing)
- return FALSE
- //Flies right over the chasm
- if(AM.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
- return FALSE
- if(isliving(AM))
- var/mob/living/M = AM
- if(M.incorporeal_move)
- return FALSE
- if(ishuman(AM))
- var/mob/living/carbon/human/H = AM
- for(var/obj/item/wormhole_jaunter/J in H.GetAllContents())
- //To freak out any bystanders
- visible_message(span_boldwarning("[H] falls into [src]!"))
- J.chasm_react(H)
- return FALSE
- return TRUE
+ if(!length(fishing_contents))
+ to_chat(user, span_boldwarning("Не клюёт!"))
+ return
-/turf/simulated/floor/chasm/proc/drop(atom/movable/AM)
- //Make sure the item is still there after our sleep
- if(!AM || QDELETED(AM))
- return
- falling_atoms[AM] = TRUE
- var/turf/T = locate(drop_x, drop_y, drop_z)
- if(T)
- AM.visible_message(span_boldwarning("[AM] falls into [src]!"), span_userdanger("GAH! Ah... where are you?"))
- T.visible_message(span_boldwarning("[AM] falls from above!"))
- AM.forceMove(T)
- if(isliving(AM))
- var/mob/living/L = AM
- L.Weaken(10 SECONDS)
- L.adjustBruteLoss(30)
- falling_atoms -= AM
+ var/mob/fish = pick(fishing_contents)
+ var/obj/effect/abstract/chasm_storage/pool = fish.loc
+ pool.get_fish(fish, user.loc)
+ to_chat(user, span_boldnotice("Попался [fish.name]!"))
+ playsound(rod, 'sound/effects/fishing_rod_catch.ogg', 30)
-/turf/simulated/floor/chasm/straight_down
- var/obj/effect/abstract/chasm_storage/storage
-/turf/simulated/floor/chasm/straight_down/Initialize()
- ..()
- var/found_storage = FALSE
- for(var/obj/effect/abstract/chasm_storage/C in contents)
- storage = C
- found_storage = TRUE
- break
- if(!found_storage)
- storage = new /obj/effect/abstract/chasm_storage(src)
- drop_x = x
- drop_y = y
- drop_z = z - 1
- var/turf/T = locate(drop_x, drop_y, drop_z)
- if(T)
- T.visible_message(span_boldwarning("The ceiling gives way!"))
- playsound(T, 'sound/effects/break_stone.ogg', 50, 1)
+/turf/simulated/floor/chasm/proc/rod_checks(obj/item/twohanded/fishingrod/rod)
+ return HAS_TRAIT(rod, TRAIT_WIELDED)
-/turf/simulated/floor/chasm/straight_down/lava_land_surface
- oxygen = 14
- nitrogen = 23
- temperature = 300
- planetary_atmos = TRUE
- baseturf = /turf/simulated/floor/chasm/straight_down/lava_land_surface //Chasms should not turn into lava
- light_range = 2
- light_power = 0.75
- light_color = LIGHT_COLOR_LAVA //let's just say you're falling into lava, that makes sense right
-/turf/simulated/floor/chasm/straight_down/lava_land_surface/drop(atom/movable/AM)
- //Make sure the item is still there after our sleep
- if(!AM || QDELETED(AM) || AM.anchored)
- return
- falling_atoms[AM] = TRUE
- AM.visible_message(span_boldwarning("[AM] falls into [src]!"), span_userdanger("You stumble and stare into an abyss before you. It stares back, and you fall \
- into the enveloping dark."))
- if(isliving(AM))
- var/mob/living/L = AM
- ADD_TRAIT(L, TRAIT_NO_TRANSFORM, CHASM_TRAIT)
- L.Stun(400 SECONDS)
- var/oldtransform = AM.transform
- var/oldcolor = AM.color
- var/oldalpha = AM.alpha
- animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
- if(iscarbon(AM) && prob(25))
- playsound(AM.loc, 'sound/effects/wilhelm_scream.ogg', 150)
- for(var/i in 1 to 5)
- //Make sure the item is still there after our sleep
- if(!AM || QDELETED(AM))
- return
- AM.pixel_y--
- sleep(2)
+/turf/simulated/floor/chasm/proc/get_fish()
+ . = list()
+ var/obj/effect/abstract/chasm_storage/pool = locate() in contents
+ if(pool)
+ for(var/mob/fish in pool.contents)
+ . += fish
- //Make sure the item is still there after our sleep
- if(!AM || QDELETED(AM))
- return
- if(isrobot(AM))
- var/mob/living/silicon/robot/S = AM
- qdel(S.mmi)
- qdel(AM)
- return
+/turf/simulated/floor/chasm/ex_act()
+ return
- falling_atoms -= AM
- if(istype(AM, /obj/item/grenade/jaunter_grenade))
- AM.forceMove(storage)
- return
+/turf/simulated/floor/chasm/acid_act(acidpwr, acid_volume)
+ return
- if(isliving(AM))
- if(!storage)
- storage = new(get_turf(src))
- if(storage.contains(AM))
- return
+/turf/simulated/floor/chasm/singularity_act()
+ return
- AM.alpha = oldalpha
- AM.color = oldcolor
- AM.transform = oldtransform
- if(!AM.forceMove(storage))
- visible_message(span_boldwarning("[src] spits out [AM]!"))
- AM.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1, 10), rand(1, 10))
+/turf/simulated/floor/chasm/singularity_pull(S, current_size)
+ return
- var/mob/living/fallen_mob = AM
- if(fallen_mob.stat != DEAD)
- fallen_mob.death(TRUE)
- REMOVE_TRAIT(fallen_mob, TRAIT_NO_TRANSFORM, CHASM_TRAIT)
- fallen_mob.apply_damage(1000)
- else
- qdel(AM)
+/turf/simulated/floor/chasm/crowbar_act()
+ return
- if(!isliving(AM) && AM && !QDELETED(AM)) //It's indestructible and not human
- visible_message(span_boldwarning("[src] spits out the [AM]!"))
- AM.alpha = oldalpha
- AM.color = oldcolor
- AM.transform = oldtransform
- AM.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10))
+/turf/simulated/floor/chasm/make_plating()
+ return
-/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
+/turf/simulated/floor/chasm/remove_plating()
+ return
-/obj/effect/abstract/chasm_storage/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
- . = ..()
- if(isliving(arrived))
- RegisterSignal(arrived, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
+/turf/simulated/floor/chasm/rcd_act()
+ return RCD_NO_ACT
-/obj/effect/abstract/chasm_storage/Exited(atom/movable/departed, atom/newLoc)
- . = ..()
- if(isliving(departed))
- UnregisterSignal(departed, COMSIG_LIVING_REVIVE)
-
-
-/**
- * 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/ourturf = get_turf(src)
- if(istype(ourturf, /turf/simulated/floor/chasm/straight_down/lava_land_surface))
- ourturf.visible_message(span_boldwarning("After a long climb, [escapee] leaps out of [ourturf]!"))
- else
- playsound(ourturf, 'sound/effects/bang.ogg', 50, TRUE)
- ourturf.visible_message(span_boldwarning("[escapee] busts through [ourturf], leaping out of the chasm below!"))
- ourturf.ChangeTurf(ourturf.baseturf)
- ADD_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT) //Otherwise they instantly fall back in
- escapee.forceMove(ourturf)
- escapee.throw_at(get_edge_target_turf(ourturf, pick(GLOB.alldirs)), rand(2, 10), rand(2, 10))
- REMOVE_TRAIT(escapee, TRAIT_MOVE_FLYING, CHASM_TRAIT)
- escapee.Sleeping(20 SECONDS)
- UnregisterSignal(escapee, COMSIG_LIVING_REVIVE)
+
+/turf/simulated/floor/chasm/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0, max_wet_time = MAXIMUM_WET_TIME, permanent = FALSE, should_display_overlay = TRUE)
+ return
+
+
+/turf/simulated/floor/chasm/MakeDry(wet_setting = TURF_WET_WATER, immediate = FALSE, amount = INFINITY)
+ return
+
+
+// Subtypes
+
+/turf/simulated/floor/chasm/straight_down
+
+
+/turf/simulated/floor/chasm/straight_down/apply_components(mapload)
+ AddComponent(/datum/component/chasm, null, mapload) //Don't pass anything for below_turf.
+
+
+/turf/simulated/floor/chasm/straight_down/lava_land_surface
+ oxygen = 14
+ nitrogen = 23
+ temperature = 300
+ planetary_atmos = TRUE
+ baseturf = /turf/simulated/floor/chasm/straight_down/lava_land_surface //Chasms should not turn into lava
+ light_range = 2
+ light_power = 0.75
+ light_color = LIGHT_COLOR_LAVA //let's just say you're falling into lava, that makes sense right
/turf/simulated/floor/chasm/straight_down/lava_land_surface/normal_air
@@ -349,9 +191,3 @@
nitrogen = MOLES_N2STANDARD
temperature = T20C
-
-/// Lets people walk into chasms.
-/turf/simulated/floor/chasm/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- return TRUE
-
diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm
index 0ec9373f060..4ab3c491dc2 100644
--- a/code/game/turfs/simulated/floor/lava.dm
+++ b/code/game/turfs/simulated/floor/lava.dm
@@ -55,7 +55,7 @@
return TRUE
/turf/simulated/floor/plating/lava/is_safe()
- if(find_safeties() && ..())
+ if(HAS_TRAIT(src, TRAIT_LAVA_STOPPED) && ..())
return TRUE
return FALSE
@@ -63,10 +63,10 @@
. = FALSE
if(locate(/obj/vehicle/lavaboat) in src.contents)
- return FALSE
+ return .
- if(find_safeties())
- return FALSE
+ if(HAS_TRAIT(src, TRAIT_LAVA_STOPPED))
+ return .
var/thing_to_check = src
if(AM)
@@ -206,8 +206,9 @@
/turf/simulated/floor/plating/lava/smooth/lava_land_surface/plasma/burn_stuff(AM)
. = FALSE
- if(find_safeties())
- return FALSE
+
+ if(HAS_TRAIT(src, TRAIT_LAVA_STOPPED))
+ return .
var/thing_to_check = src
if(AM)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 5c9507a701a..425d5461577 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -296,7 +296,8 @@
var/old_baseturf = baseturf
- SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path)
+ var/list/post_change_callbacks = list()
+ SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path, post_change_callbacks)
changing_turf = TRUE
qdel(src) //Just get the side effects and call Destroy
@@ -313,6 +314,9 @@
if(old_signal_procs)
LAZYOR(W.signal_procs, old_signal_procs)
+ for(var/datum/callback/callback as anything in post_change_callbacks)
+ callback.InvokeAsync(W)
+
if(copy_existing_baseturf)
W.baseturf = old_baseturf
diff --git a/code/modules/antagonists/traitor/contractor/items/extraction_items.dm b/code/modules/antagonists/traitor/contractor/items/extraction_items.dm
index 60a1e97ec3d..a0fe0fef134 100644
--- a/code/modules/antagonists/traitor/contractor/items/extraction_items.dm
+++ b/code/modules/antagonists/traitor/contractor/items/extraction_items.dm
@@ -39,19 +39,23 @@
/// The mind of the kidnapping target. Prevents non-targets from taking the portal.
var/datum/mind/target_mind = null
-/obj/effect/portal/redspace/contractor/can_teleport(atom/movable/A)
+
+/obj/effect/portal/redspace/contractor/can_teleport(atom/movable/A, silent = FALSE)
var/mob/living/M = A
if(!istype(M))
return FALSE
if(M == usr && M.mind == contractor_mind)
- to_chat(M, "The portal is here to extract the contract target, not you!")
+ if(!silent)
+ to_chat(M, "The portal is here to extract the contract target, not you!")
return FALSE
if(M.mind != target_mind)
if(usr?.mind == contractor_mind) // Contractor shoving a non-target into the portal
- to_chat(M, "Somehow you are not sure [M] is the target you have to kidnap.")
+ if(!silent)
+ to_chat(M, "Somehow you are not sure [M] is the target you have to kidnap.")
return FALSE
else if(usr == M) // Non-target trying to enter the portal
- to_chat(M, "Somehow you are not sure this is a good idea.")
+ if(!silent)
+ to_chat(M, "Somehow you are not sure this is a good idea.")
return FALSE
return FALSE
return ..()
diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm
index a610566a156..4e712da6002 100644
--- a/code/modules/events/wormholes.dm
+++ b/code/modules/events/wormholes.dm
@@ -64,7 +64,7 @@
. = list() // we need no mask here
-/obj/effect/portal/wormhole/can_teleport(atom/movable/M)
+/obj/effect/portal/wormhole/can_teleport(atom/movable/M, silent = FALSE)
. = ..()
if(istype(M, /obj/singularity))
diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm
index c795f5b2670..32bd1eb420a 100644
--- a/code/modules/mining/equipment/wormhole_jaunter.dm
+++ b/code/modules/mining/equipment/wormhole_jaunter.dm
@@ -13,49 +13,62 @@
slot_flags = ITEM_SLOT_BELT
var/emagged = FALSE
+
/obj/item/wormhole_jaunter/attack_self(mob/user)
- user.visible_message("[user.name] activates the [name]!")
+ user.visible_message(span_notice("[user.name] activates the [name]!"))
+ SSblackbox.record_feedback("tally", "jaunter", 1, "User") // user activated
activate(user, TRUE)
+
/obj/item/wormhole_jaunter/proc/turf_check(mob/user)
- var/turf/device_turf = get_turf(user)
+ var/turf/device_turf = get_turf(src)
if(!device_turf || !is_teleport_allowed(device_turf.z))
- to_chat(user, "You're having difficulties getting the [name] to work.")
+ if(user)
+ to_chat(user, span_notice("You're having difficulties getting [src] to work."))
return FALSE
return TRUE
-/obj/item/wormhole_jaunter/proc/get_destinations(mob/user)
- var/list/destinations = list()
- for(var/obj/item/radio/beacon/B in GLOB.global_radios)
- var/turf/T = get_turf(B)
- if(is_station_level(T.z))
- destinations += B
+/obj/item/wormhole_jaunter/proc/get_destinations()
+ . = list()
+ for(var/obj/item/radio/beacon/beacon in GLOB.global_radios)
+ var/turf/beacon_turf = get_turf(beacon)
+ if(is_station_level(beacon_turf.z))
+ . += beacon
- return destinations
-/obj/item/wormhole_jaunter/proc/activate(mob/user, adjacent)
+/obj/item/wormhole_jaunter/proc/activate(mob/user, adjacent, teleport)
if(!turf_check(user))
- return
+ return FALSE
+
+ var/list/destinations = get_destinations()
+ if(!length(destinations))
+ if(user)
+ to_chat(user, span_notice("\The [src] found no beacons in the world to anchor a wormhole to."))
+ else
+ visible_message(span_notice("\The [src] found no beacons in the world to anchor a wormhole to!"))
+ return TRUE // used for chasm code
+
+ var/chosen_beacon = pick(destinations)
+
+ var/obj/effect/portal/jaunt_tunnel/tunnel = new(get_turf(src), get_turf(chosen_beacon), src, 100, user)
+ tunnel.emagged = emagged
+ if(teleport)
+ tunnel.teleport(user)
+ else if(adjacent)
+ try_move_adjacent(tunnel)
- var/list/L = get_destinations(user)
- if(!L.len)
- to_chat(user, "The [name] found no beacons in the world to anchor a wormhole to.")
- return
- var/chosen_beacon = pick(L)
- var/obj/effect/portal/jaunt_tunnel/J = new(get_turf(src), get_turf(chosen_beacon), src, 100, user)
- J.emagged = emagged
- if(adjacent)
- try_move_adjacent(J)
- else
- J.teleport(user)
- playsound(src,'sound/effects/sparks4.ogg',50,1)
qdel(src)
+ return FALSE // used for chasm code
+
/obj/item/wormhole_jaunter/proc/chasm_react(mob/user)
- to_chat(user, "Your [name] activates, saving you from the chasm!")
- SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation
- activate(user, FALSE)
+ . = activate(user, FALSE, TRUE)
+
+ if(!.)
+ to_chat(user, span_notice("Your [name] activates, saving you from the chasm!"))
+ SSblackbox.record_feedback("tally", "jaunter", 1, "Chasm") // chasm automatic activation
+
/obj/item/wormhole_jaunter/emag_act(mob/user)
if(!emagged)
@@ -67,6 +80,7 @@
do_sparks(5, 0, T)
playsound(T, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+
/obj/effect/portal/jaunt_tunnel
name = "jaunt tunnel"
icon = 'icons/effects/effects.dmi'
@@ -80,7 +94,7 @@
. = list() // we need no mask here
-/obj/effect/portal/jaunt_tunnel/can_teleport(atom/movable/M)
+/obj/effect/portal/jaunt_tunnel/can_teleport(atom/movable/M, silent = FALSE)
if(!emagged && ismegafauna(M))
return FALSE
return ..()
@@ -104,42 +118,58 @@
/// Mob that threw the grenade.
var/mob/living/thrower
+
/obj/item/grenade/jaunter_grenade/Destroy()
thrower = null
return ..()
+
/obj/item/grenade/jaunter_grenade/attack_self(mob/user)
. = ..()
thrower = user
+
/obj/item/grenade/jaunter_grenade/prime()
update_mob()
+
var/list/destinations = list()
- for(var/obj/item/radio/beacon/B in GLOB.global_radios)
- var/turf/BT = get_turf(B)
- if(is_station_level(BT.z))
- destinations += BT
- var/turf/T = get_turf(src)
- if(istype(T, /turf/simulated/floor/chasm/straight_down/lava_land_surface))
- for(var/turf/simulated/floor/chasm/straight_down/lava_land_surface/chasm_turfs in range(5, T))
- for(var/obj/effect/abstract/chasm_storage/C in chasm_turfs)
- var/found_mob = FALSE
- for(var/mob/M in C)
- found_mob = TRUE
- do_teleport(M, pick(destinations))
- if(found_mob)
- new /obj/effect/temp_visual/thunderbolt(chasm_turfs) //Visual feedback it worked.
- playsound(src, 'sound/magic/lightningbolt.ogg', 100, TRUE)
- qdel(src)
- else
- var/list/portal_turfs = list()
- for(var/turf/PT in circleviewturfs(T, 3))
- if(!PT.density)
- portal_turfs += PT
- playsound(src, 'sound/magic/lightningbolt.ogg', 100, TRUE)
- for(var/turf/drunk_dial in shuffle(destinations))
- var/drunken_opening = pick_n_take(portal_turfs)
- new /obj/effect/portal/jaunt_tunnel(drunken_opening, drunk_dial, src, 100, thrower)
- new /obj/effect/temp_visual/thunderbolt(drunken_opening)
+ for(var/obj/item/radio/beacon/beacon in GLOB.global_radios)
+ var/turf/beacon_turf = get_turf(beacon)
+ if(is_station_level(beacon_turf.z))
+ destinations += beacon_turf
+ if(!length(destinations))
+ return
+
+ var/turf/our_turf = get_turf(src)
+ if(!our_turf)
+ return
+
+ if(ischasm(our_turf))
+ for(var/turf/simulated/floor/chasm/chasm in RANGE_TURFS(5, our_turf))
+ var/obj/effect/abstract/chasm_storage/pool = locate() in chasm.contents
+ if(!pool)
+ continue
+ var/found_mob = FALSE
+ for(var/mob/fish in pool.contents)
+ found_mob = TRUE
+ pool.get_fish(fish)
+ do_teleport(fish, pick(destinations))
+ if(found_mob)
+ new /obj/effect/temp_visual/thunderbolt(chasm) // visual feedback if it worked.
+ playsound(src, 'sound/magic/lightningbolt.ogg', 100, TRUE)
qdel(src)
+ return
+
+ var/list/portal_turfs = list()
+ for(var/turf/turf as anything in circleviewturfs(our_turf, 3))
+ if(!turf.density)
+ portal_turfs += turf
+ playsound(our_turf, 'sound/magic/lightningbolt.ogg', 100, TRUE)
+ for(var/turf/drunk_dial as anything in shuffle(destinations))
+ if(!length(portal_turfs))
+ break
+ var/drunken_opening = pick_n_take(portal_turfs)
+ new /obj/effect/portal/jaunt_tunnel(drunken_opening, drunk_dial, src, 10 SECONDS, thrower)
+ new /obj/effect/temp_visual/thunderbolt(drunken_opening)
+ qdel(src)
diff --git a/code/modules/ruins/lavalandruin_code/sin_ruins.dm b/code/modules/ruins/lavalandruin_code/sin_ruins.dm
index e4301392d80..15b89d897e1 100644
--- a/code/modules/ruins/lavalandruin_code/sin_ruins.dm
+++ b/code/modules/ruins/lavalandruin_code/sin_ruins.dm
@@ -112,29 +112,28 @@
desc = "Pride cometh before the..."
icon_state = "magic_mirror"
+
/obj/structure/mirror/magic/pride/curse(mob/user)
- user.visible_message("The ground splits beneath [user] as [user.p_their()] hand leaves the mirror!", \
- "Perfect. Much better! Now nobody will be able to resist yo-")
+ user.visible_message(
+ span_bolddanger("The ground splits beneath [user] as [user.p_their()] hand leaves the mirror!"),
+ span_notice("Perfect. Much better! Now nobody will be able to resist yo-"),
+ )
- var/turf/T = get_turf(user)
+ var/turf/user_turf = get_turf(user)
var/list/levels = GLOB.space_manager.z_list.Copy()
for(var/level in levels)
- if(!is_teleport_allowed(level))
- levels -= level
- continue
- if(is_taipan(level))
- levels -= level
- continue
- if(text2num(level) == T.z)
+ if(!is_teleport_allowed(level) || is_taipan(level) || text2num(level) == user_turf.z)
levels -= level
- continue
-
- T.ChangeTurf(/turf/simulated/floor/chasm)
- var/turf/simulated/floor/chasm/C = T
- C.drop_x = T.x
- C.drop_y = T.y
- C.drop_z = text2num(pick(levels))
- C.drop(user)
+
+ var/turf/dest
+ if(length(levels))
+ dest = locate(user_turf.x, user_turf.y, pick(levels))
+
+ user_turf.ChangeTurf(/turf/simulated/floor/chasm)
+ var/turf/simulated/floor/chasm/new_chasm = user_turf
+ new_chasm.set_target(dest)
+ new_chasm.drop(user)
+
// Envy
/obj/item/kitchen/knife/envy //Envy's knife: Found in the Envy ruin. Attackers take on the appearance of whoever they strike.
diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
index d01bfe2da65..a8a5635b9ff 100644
--- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
@@ -291,6 +291,21 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
+ toggle_fallen(FALSE, TRUE)
+
+
+/obj/structure/stone_tile/proc/toggle_fallen(new_fallen, init)
+ if(new_fallen == fallen && !init)
+ return
+ . = new_fallen
+ fallen = new_fallen
+ var/static/list/give_turf_traits
+ if(!give_turf_traits)
+ give_turf_traits = string_list(list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_TURF_IGNORE_SLOWDOWN))
+ if(fallen)
+ RemoveElement(/datum/element/give_turf_traits, give_turf_traits)
+ else
+ AddElement(/datum/element/give_turf_traits, give_turf_traits)
/obj/structure/stone_tile/Destroy(force)
@@ -339,7 +354,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
else
playsound(src, 'sound/mecha/mechmove04.ogg', 50, TRUE)
animate(src, alpha = 0, pixel_y = pixel_y - 3, time = 5)
- fallen = TRUE
+ toggle_fallen(TRUE)
if(break_that_sucker)
QDEL_IN(src, 10)
else
@@ -351,7 +366,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
animate(src, alpha = initial(alpha), pixel_x = initial(pixel_x), pixel_y = initial(pixel_y), time = 30)
sleep(30)
falling = FALSE
- fallen = FALSE
+ toggle_fallen(FALSE)
/obj/structure/stone_tile/proc/crossed_effect(atom/movable/AM, oldloc)
return
diff --git a/paradise.dme b/paradise.dme
index 0c95889e1d3..2271fa95a53 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -175,6 +175,7 @@
#include "code\__HELPERS\shell.dm"
#include "code\__HELPERS\stat_tracking.dm"
#include "code\__HELPERS\string_assoc_lists.dm"
+#include "code\__HELPERS\string_lists.dm"
#include "code\__HELPERS\text.dm"
#include "code\__HELPERS\time.dm"
#include "code\__HELPERS\tool_helpers.dm"
@@ -399,6 +400,7 @@
#include "code\datums\components\boomerang.dm"
#include "code\datums\components\boss_music.dm"
#include "code\datums\components\caltrop.dm"
+#include "code\datums\components\chasm.dm"
#include "code\datums\components\combo_attacks.dm"
#include "code\datums\components\connect_loc_behalf.dm"
#include "code\datums\components\connect_mob_behalf.dm"
@@ -504,6 +506,7 @@
#include "code\datums\elements\connect_loc.dm"
#include "code\datums\elements\falling_hazard.dm"
#include "code\datums\elements\footstep.dm"
+#include "code\datums\elements\give_turf_traits.dm"
#include "code\datums\elements\light_blocking.dm"
#include "code\datums\elements\movetype_handler.dm"
#include "code\datums\elements\openspace_item_click_handler.dm"