diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index c1dcfb07e85..3b2cc98097d 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -380,6 +380,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai */ #define TRAIT_AREA_SENSITIVE "area-sensitive" +/// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT(). +#define TRAIT_CLIMBABLE "trait_climbable" + ///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags] #define TRAIT_KEEP_TOGETHER "keep-together" diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm index 8084247669a..1a036412b17 100644 --- a/code/datums/elements/climbable.dm +++ b/code/datums/elements/climbable.dm @@ -22,9 +22,11 @@ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine) RegisterSignal(target, COMSIG_MOUSEDROPPED_ONTO, .proc/mousedrop_receive) RegisterSignal(target, COMSIG_ATOM_BUMPED, .proc/try_speedrun) + ADD_TRAIT(target, TRAIT_CLIMBABLE, src) /datum/element/climbable/Detach(datum/target) UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_EXAMINE, COMSIG_MOUSEDROPPED_ONTO, COMSIG_ATOM_BUMPED)) + REMOVE_TRAIT(target, TRAIT_CLIMBABLE, src) return ..() /datum/element/climbable/proc/on_examine(atom/source, mob/user, list/examine_texts) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 7c045fbfa86..a0e20a787eb 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -212,6 +212,20 @@ GLOBAL_LIST_EMPTY(station_turfs) return TRUE return FALSE +/** + * Checks whether the specified turf is blocked by something dense inside it, but ignores anything with the climbable trait + * + * Works similar to is_blocked_turf(), but ignores climbables and has less options. Primarily added for jaunting checks + */ +/turf/proc/is_blocked_turf_ignore_climbable() + if(density) + return TRUE + + for(var/atom/movable/atom_content as anything in contents) + if(atom_content.density && !(atom_content.flags_1 & ON_BORDER_1) && !HAS_TRAIT(atom_content, TRAIT_CLIMBABLE)) + return TRUE + return FALSE + //zPassIn doesn't necessarily pass an atom! //direction is direction of travel of air /turf/proc/zPassIn(atom/movable/A, direction, turf/source) diff --git a/code/modules/spells/spell_types/ethereal_jaunt.dm b/code/modules/spells/spell_types/ethereal_jaunt.dm index e68b64a1a5d..ca1a1e6997b 100644 --- a/code/modules/spells/spell_types/ethereal_jaunt.dm +++ b/code/modules/spells/spell_types/ethereal_jaunt.dm @@ -22,6 +22,8 @@ var/jaunt_in_type = /obj/effect/temp_visual/wizard /// Visual for exiting the jaunt var/jaunt_out_type = /obj/effect/temp_visual/wizard/out + /// List of valid exit points + var/list/exit_point_list /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast_check(skipcharge = 0,mob/user = usr) . = ..() @@ -51,11 +53,28 @@ ADD_TRAIT(target, TRAIT_IMMOBILIZED, type) sleep(jaunt_out_time) REMOVE_TRAIT(target, TRAIT_IMMOBILIZED, type) + var/turf/exit_point = get_turf(holder) //Hopefully this gets updated, otherwise this is our fallback + LAZYINITLIST(exit_point_list) + RegisterSignal(holder, COMSIG_MOVABLE_MOVED, .proc/update_exit_point, target) sleep(jaunt_duration) + UnregisterSignal(holder, COMSIG_MOVABLE_MOVED) if(target.loc != holder) //mob warped out of the warp qdel(holder) return + + var/found_exit = FALSE + for(var/turf/possible_exit as anything in exit_point_list) + if(possible_exit.is_blocked_turf_ignore_climbable()) + continue + exit_point = possible_exit + found_exit = TRUE + break + if(!found_exit) + to_chat(target, "Unable to find an unobstructed space, you find yourself ripped back to where you started.") + exit_point_list.Cut() + holder.forceMove(exit_point) + mobloc = get_turf(target.loc) jaunt_steam(mobloc) ADD_TRAIT(target, TRAIT_IMMOBILIZED, type) @@ -75,6 +94,23 @@ break REMOVE_TRAIT(target, TRAIT_IMMOBILIZED, type) +/** + * Updates the exit point of the jaunt + * + * Called when the jaunting mob holder moves, this updates the backup exit-jaunt + * location, in case the jaunt ends with the mob still in a wall. Five + * spots are kept in the list, in case the last few changed since we passed + * by (doors closing, engineers building walls, etc) + */ +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/update_exit_point(mob/living/target) + SIGNAL_HANDLER + var/turf/location = get_turf(target) + if(location.is_blocked_turf_ignore_climbable()) + return + exit_point_list.Insert(1, location) + if(length(exit_point_list) >= 5) + exit_point_list.Cut(5) + /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() steam.set_up(10, 0, mobloc)