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"