Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teleportation Cleanup & Cult Teleports Respecting Anti-Magic #11466

Merged
merged 8 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/__DEFINES/dcs/signals/signals_atom/signals_atom.dm
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
#define COMSIG_ATOM_CREATEDBY_PROCESSING "atom_createdby_processing"
///when an atom is processed (mob/living/user, obj/item/I, list/atom/results)
#define COMSIG_ATOM_PROCESSED "atom_processed"
///! called when teleporting into a protected turf: (channel, turf/origin)
///! from the base of atom/intercept_teleport: (channel, turf/origin, turf/destination)
#define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport"
#define COMPONENT_BLOCK_TELEPORT 1
///called when an atom starts orbiting another atom: (atom)
Expand Down
10 changes: 2 additions & 8 deletions code/__DEFINES/movement.dm
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,8 @@
#define TELEPORT_ALLOW_CLOCKWORK 2
/// Everyone but abductors is restricted
#define TELEPORT_ALLOW_ABDUCTORS 3

//Teleport modes
/// Default teleport mode
#define TELEPORT_MODE_DEFAULT 0
/// A clockwork teleport
#define TELEPORT_MODE_CLOCKWORK 2
/// An abductor teleport
#define TELEPORT_MODE_ABDUCTORS 3
/// Everyone but wizards is restricted
#define TELEPORT_ALLOW_WIZARD 4

// Jetpack Thrust
/// Thrust needed with gravity
Expand Down
153 changes: 100 additions & 53 deletions code/datums/helper_datums/teleport.dm
Original file line number Diff line number Diff line change
@@ -1,13 +1,85 @@
// teleatom: atom to teleport
// destination: destination to teleport to
// precision: teleport precision (0 is most precise, the default)
// effectin: effect to show right before teleportation
// effectout: effect to show right after teleportation
// asoundin: soundfile to play before teleportation
// asoundout: soundfile to play after teleportation
// no_effects: disable the default effectin/effectout of sparks
// forced: whether or not to ignore no_teleport
/proc/do_teleport(atom/movable/teleatom, atom/destination, precision=null, datum/effect_system/effectin=null, datum/effect_system/effectout=null, asoundin=null, asoundout=null, no_effects=FALSE, channel=TELEPORT_CHANNEL_BLUESPACE, forced = FALSE, teleport_mode = TELEPORT_MODE_DEFAULT, commit = TRUE, no_wake = FALSE)
/**
* Returns FALSE if we SHOULDN'T do_teleport() with the given arguments
*
* Arguments:
* * teleatom: The atom to teleport
* * dest_turf: The destination turf for the atom to go
* * channel: Which teleport channel/type should we try to use (for blocking checks), defaults to TELEPORT_CHANNEL_BLUESPACE
* * bypass_area_restriction: Should we ignore SOFT atom and area TRAIT_NO_TELEPORT restriction and other area-related restrictions? Defaults to FALSE
* * teleport_mode: Teleport mode for religion/faction checks
*/
/proc/check_teleport(atom/movable/teleatom, turf/dest_turf, channel = TELEPORT_CHANNEL_BLUESPACE, bypass_area_restriction = FALSE, teleport_mode = TELEPORT_ALLOW_ALL)
var/turf/cur_turf = get_turf(teleatom)

if(!istype(dest_turf))
stack_trace("Destination [dest_turf] is not a turf.")
return FALSE
if(!istype(cur_turf) || dest_turf.is_transition_turf())
return FALSE

// Checks bluespace anchors
if(channel != TELEPORT_CHANNEL_WORMHOLE && channel != TELEPORT_CHANNEL_FREE)
var/cur_zlevel = cur_turf.get_virtual_z_level()
var/dest_zlevel = dest_turf.get_virtual_z_level()
for (var/obj/machinery/bluespace_anchor/anchor as() in GLOB.active_bluespace_anchors)
var/anchor_zlevel = anchor.get_virtual_z_level()
// Not in range of our current turf or destination turf
if((cur_zlevel != anchor_zlevel && get_dist(cur_turf, anchor) > anchor.range) && (dest_zlevel != anchor_zlevel && get_dist(dest_turf, anchor) > anchor.range))
continue

// Try to activate the anchor, this also does the effect
if(!anchor.try_activate())
continue

// We're anchored, return false
return FALSE

// Checks antimagic
if(ismob(teleatom))
var/mob/tele_mob = teleatom
if(channel == TELEPORT_CHANNEL_CULT && tele_mob.anti_magic_check(magic = FALSE, holy = TRUE, self = TRUE))
return FALSE
if(channel == TELEPORT_CHANNEL_MAGIC && tele_mob.anti_magic_check(magic = TRUE, holy = FALSE, self = TRUE))
return FALSE

// Check for NO_TELEPORT restrictions
if(!bypass_area_restriction)
var/area/cur_area = cur_turf.loc
var/area/dest_area = dest_turf.loc
if(HAS_TRAIT(teleatom, TRAIT_NO_TELEPORT))
return FALSE
if(cur_area.teleport_restriction && cur_area.teleport_restriction != teleport_mode)
return FALSE
if(dest_area.teleport_restriction && dest_area.teleport_restriction != teleport_mode)
return FALSE

// Check for intercepting the teleport
if(cur_turf.intercept_teleport(channel, cur_turf, dest_turf) == COMPONENT_BLOCK_TELEPORT)
return FALSE
if(dest_turf.intercept_teleport(channel, cur_turf, dest_turf) == COMPONENT_BLOCK_TELEPORT)
return FALSE
if(teleatom.intercept_teleport(channel, cur_turf, dest_turf) == COMPONENT_BLOCK_TELEPORT)
return FALSE

return TRUE

/**
* Returns TRUE if the teleport has been successful
*
* Arguments:
* * teleatom: atom to teleport
* * destination: destination to teleport to
* * precision: teleport precision (0 is most precise, the default)
* * effectin: effect to show right before teleportation
* * asoundin: soundfile to play before teleportation
* * asoundout: soundfile to play after teleportation
* * no_effects: disable the default effectin/effectout of sparks
* * channel: Which teleport channel/type should we try to use (for blocking checks)
* * ignore_check_teleport: Set this to true ONLY if you have already run check_teleport
* * bypass_area_restriction: Should we ignore SOFT atom and area TRAIT_NO_TELEPORT restriction and other area-related restrictions? Defaults to FALSE
* * no_wake: Whether or not we want a teleport wake to be created
*/
/proc/do_teleport(atom/movable/teleatom, atom/destination, precision=null, datum/effect_system/effectin=null, datum/effect_system/effectout=null, asoundin=null, asoundout=null, no_effects=FALSE, channel=TELEPORT_CHANNEL_BLUESPACE, bypass_area_restriction = FALSE, teleport_mode = TELEPORT_ALLOW_ALL, ignore_check_teleport = FALSE, no_wake = FALSE)
// teleporting most effects just deletes them
var/static/list/delete_atoms = typecacheof(list(
/obj/effect,
Expand All @@ -23,22 +95,6 @@
qdel(teleatom)
return FALSE

//Check bluespace anchors
if(channel != TELEPORT_CHANNEL_WORMHOLE && channel != TELEPORT_CHANNEL_FREE)
for (var/obj/machinery/bluespace_anchor/anchor as() in GLOB.active_bluespace_anchors)
//Not nearby
if (anchor.get_virtual_z_level() != teleatom.get_virtual_z_level() || (get_dist(teleatom, anchor) > anchor.range && get_dist(destination, anchor) > anchor.range))
continue
//Check it
if(!anchor.try_activate())
continue
do_sparks(5, FALSE, teleatom)
playsound(anchor, 'sound/magic/repulse.ogg', 80, TRUE)
if(ismob(teleatom))
to_chat(teleatom, "<span class='warning'>You feel like you are being held in place.</span>")
//Anchored...
return FALSE

// argument handling
// if the precision is not specified, default to 0, but apply BoH penalties
if (isnull(precision))
Expand All @@ -48,8 +104,7 @@
if(istype(teleatom, /obj/item/storage/backpack/holding))
precision = rand(1,100)

var/static/list/bag_cache = typecacheof(/obj/item/storage/backpack/holding)
var/list/bagholding = typecache_filter_list(teleatom.GetAllContents(), bag_cache)
var/list/bagholding = teleatom.GetAllContents(/obj/item/storage/backpack/holding)
if(bagholding.len)
precision = max(rand(1,100)*bagholding.len,100)
if(isliving(teleatom))
Expand All @@ -69,34 +124,25 @@
var/turf/curturf = get_turf(teleatom)
var/turf/destturf = get_teleport_turf(get_turf(destination), precision)

if(!destturf || !curturf || destturf.is_transition_turf())
return FALSE

var/area/A = get_area(curturf)
var/area/B = get_area(destturf)
if(!forced && (HAS_TRAIT(teleatom, TRAIT_NO_TELEPORT)))
return FALSE

//Either area has teleport restriction and teleport mode isn't allowed in that area
if(!forced && ((A.teleport_restriction && A.teleport_restriction != teleport_mode) || (B.teleport_restriction && B.teleport_restriction != teleport_mode)))
return FALSE

if(SEND_SIGNAL(destturf, COMSIG_ATOM_INTERCEPT_TELEPORT, channel, curturf, destturf))
return FALSE

if(isobserver(teleatom))
teleatom.abstract_move(destturf)
return TRUE

if (!commit)
return TRUE
if(!ignore_check_teleport) // If we've already done it let's not check again
if(!check_teleport(teleatom, destturf, channel, bypass_area_restriction, teleport_mode))
return FALSE

// If we leave behind a wake, then create that here.
// Only leave a wake if we are going to a location that we can actually teleport to.
if (!no_wake && (channel == TELEPORT_CHANNEL_BLUESPACE || channel == TELEPORT_CHANNEL_CULT || channel == TELEPORT_CHANNEL_MAGIC) && A.teleport_restriction == TELEPORT_MODE_DEFAULT && B.teleport_restriction == TELEPORT_MODE_DEFAULT && teleport_mode == TELEPORT_MODE_DEFAULT)
new /obj/effect/temp_visual/teleportation_wake(get_turf(teleatom), destturf)
if (!no_wake && (channel == TELEPORT_CHANNEL_BLUESPACE || channel == TELEPORT_CHANNEL_CULT || channel == TELEPORT_CHANNEL_MAGIC))
var/area/cur_area = curturf.loc
var/area/dest_area = destturf.loc
if(cur_area.teleport_restriction == TELEPORT_ALLOW_ALL && dest_area.teleport_restriction == TELEPORT_ALLOW_ALL && teleport_mode == TELEPORT_ALLOW_ALL)
new /obj/effect/temp_visual/teleportation_wake(get_turf(teleatom), destturf)

tele_play_specials(teleatom, curturf, effectin, asoundin)

// Actually teleport them
var/success = teleatom.forceMove(destturf)
if (success)
log_game("[key_name(teleatom)] has teleported from [loc_name(curturf)] to [loc_name(destturf)]")
Expand All @@ -113,12 +159,13 @@
return TRUE

/proc/tele_play_specials(atom/movable/teleatom, atom/location, datum/effect_system/effect, sound)
if (location && !isobserver(teleatom))
if (sound)
playsound(location, sound, 60, 1)
if (effect)
effect.attach(location)
effect.start()
if (!istype(location) || isobserver(teleatom))
return
if (sound)
playsound(location, sound, 60, 1)
if (effect)
effect.attach(location)
effect.start()

// Safe location finder
/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE, dense_atoms = TRUE)
Expand Down
2 changes: 1 addition & 1 deletion code/game/area/areas/centcom.dm
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
teleport_restriction = TELEPORT_ALLOW_NONE
teleport_restriction = TELEPORT_ALLOW_WIZARD
area_flags = VALID_TERRITORY | UNIQUE_AREA
flags_1 = NONE
network_root_id = "MAGIC_NET"
Expand Down
21 changes: 21 additions & 0 deletions code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,27 @@ CREATION_TEST_IGNORE_SUBTYPES(/atom)
/atom/proc/teleport_act()
SEND_SIGNAL(src,COMSIG_ATOM_TELEPORT_ACT)

/**
* Intercept our atom being teleported if we need to
*
* return COMPONENT_BLOCK_TELEPORT to explicity block teleportation
*/
/atom/proc/intercept_teleport(channel, turf/origin, turf/destination)
. = SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_TELEPORT, channel, origin, destination)

if(. == COMPONENT_BLOCK_TELEPORT)
return

// Recursively check contents by default. This can be overriden if we want different behavior.
for(var/atom/thing in contents)
// For the purposes of intercepting teleports, mobs on the turf don't count.
// We're already doing logic for intercepting teleports on the teleatom-level
if(isturf(src) && ismob(thing))
continue
var/result = thing.intercept_teleport(channel, origin, destination)
if(result == COMPONENT_BLOCK_TELEPORT)
return result

/**
* Respond to our atom being checked by a virus extrapolator.
*
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/scrolls.dm
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.")
return

if(do_teleport(user, pick(L), channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE))
if(do_teleport(user, pick(L), channel = TELEPORT_CHANNEL_MAGIC, bypass_area_restriction = TRUE))
smoke.start()
uses--
else
Expand Down
9 changes: 3 additions & 6 deletions code/game/objects/items/teleportation.dm
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,10 @@
qdel(target_effect)
qdel(source_effect)
return
var/area/A = get_area(teleport_target)
if(A.teleport_restriction)
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
current_location = get_turf(user) //Recheck.
current_area = current_location.loc
if(!current_location || current_area.teleport_restriction || is_away_level(current_location.z) || is_centcom_level(current_location.z) || !isturf(user.loc))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
var/turf/dest_turf = get_teleport_turf(get_turf(teleport_target))
if(isnull(current_area) || !check_teleport(user, dest_turf, channel = TELEPORT_CHANNEL_BLUESPACE) || is_away_level(current_location.z) || is_centcom_level(current_location.z))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(teleport_target)), src, 300, 1, null, atmos_link_override)
Expand Down Expand Up @@ -359,7 +356,7 @@

// Check if we can move here
current_area = current_location.loc
if(!do_teleport(C, current_location, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, commit = FALSE))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
if(!check_teleport(C, current_location, channel = TELEPORT_CHANNEL_BLINK))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
current_location = previous
break
// If it contains objects, try to break it
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/abductor/machinery/experiment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
H.uncuff()
H.cauterise_wounds()
if(console && console.pad && console.pad.teleport_target)
do_teleport(H, console.pad.teleport_target, channel = TELEPORT_CHANNEL_BLINK, no_effects = TRUE, teleport_mode = TELEPORT_MODE_ABDUCTORS)
do_teleport(H, console.pad.teleport_target, channel = TELEPORT_CHANNEL_BLINK, no_effects = TRUE, teleport_mode = TELEPORT_ALLOW_ABDUCTORS)
return
//Area not chosen / It's not safe area - teleport to arrivals
SSjob.SendToLateJoin(H, FALSE)
Expand Down
8 changes: 4 additions & 4 deletions code/modules/antagonists/abductor/machinery/pad.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

/obj/machinery/abductor/pad/proc/Warp(mob/living/target)
if(!target.buckled)
do_teleport(target, get_turf(src), no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_MODE_ABDUCTORS)
do_teleport(target, get_turf(src), no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_ALLOW_ABDUCTORS)

/obj/machinery/abductor/pad/proc/Send()
if(teleport_target == null)
teleport_target = GLOB.teleportlocs[pick(GLOB.teleportlocs)]
flick("alien-pad", src)
for(var/mob/living/target in loc)
do_teleport(target, teleport_target, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_MODE_ABDUCTORS)
do_teleport(target, teleport_target, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_ALLOW_ABDUCTORS)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)
to_chat(target, "<span class='warning'>The instability of the warp leaves you disoriented!</span>")
target.SetSleeping(60)
Expand All @@ -33,7 +33,7 @@

/obj/machinery/abductor/pad/proc/doMobToLoc(place, atom/movable/target)
flick("alien-pad", src)
do_teleport(target, place, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_MODE_ABDUCTORS)
do_teleport(target, place, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_ALLOW_ABDUCTORS)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)

/obj/machinery/abductor/pad/proc/MobToLoc(place,mob/living/target)
Expand All @@ -43,7 +43,7 @@
/obj/machinery/abductor/pad/proc/doPadToLoc(place)
flick("alien-pad", src)
for(var/mob/living/target in get_turf(src))
do_teleport(target, place, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_MODE_ABDUCTORS)
do_teleport(target, place, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLINK, teleport_mode = TELEPORT_ALLOW_ABDUCTORS)
new /obj/effect/temp_visual/dir_setting/ninja(get_turf(target), target.dir)

/obj/machinery/abductor/pad/proc/PadToLoc(place)
Expand Down
4 changes: 2 additions & 2 deletions code/modules/antagonists/clock_cult/helpers/servant_warp.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
playsound(target_location, 'sound/magic/magic_missile.ogg', 50, TRUE)
do_sparks(5, TRUE, servant)
do_sparks(5, TRUE, target_location)
do_teleport(M, target_location, channel = TELEPORT_CHANNEL_BLINK, no_effects = TRUE, teleport_mode = TELEPORT_MODE_CLOCKWORK)
do_teleport(M, target_location, channel = TELEPORT_CHANNEL_CULT, no_effects = TRUE, teleport_mode = TELEPORT_ALLOW_CLOCKWORK)
new /obj/effect/temp_visual/ratvar/warp(target_location)
to_chat(servant, "<span class='inathneq'>You warp to [get_area(target_location)].</span>")
if(istype(P) && bring_dragging)
do_teleport(P, target_location, channel = TELEPORT_CHANNEL_BLINK, no_effects = TRUE, teleport_mode = TELEPORT_MODE_CLOCKWORK)
do_teleport(P, target_location, channel = TELEPORT_CHANNEL_CULT, no_effects = TRUE, teleport_mode = TELEPORT_ALLOW_CLOCKWORK)
P.Paralyze(30)
to_chat(P, "<span class='warning'>You feel sick and confused...</span>")
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
if(do_after(M, 50, target=src))
var/obj/effect/landmark/city_of_cogs/target_spawn = pick(GLOB.city_of_cogs_spawns)
var/turf/T = get_turf(target_spawn)
do_teleport(M, T, no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, forced = TRUE)
do_teleport(M, T, no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, bypass_area_restriction = TRUE)
var/mob/living/M_mob = M
if(istype(M_mob))
if(M_mob.client)
Expand All @@ -43,4 +43,4 @@
//So we can push crates in too
var/obj/effect/landmark/city_of_cogs/target_spawn = pick(GLOB.city_of_cogs_spawns)
var/turf/T = get_turf(target_spawn)
do_teleport(M, T, no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, forced = TRUE)
do_teleport(M, T, no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, bypass_area_restriction = TRUE)
2 changes: 1 addition & 1 deletion code/modules/antagonists/cult/runes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/effect/rune/teleport)
visible_message("<span class='warning'>There is a sharp crack of inrushing air, and everything above the rune disappears!</span>", null, "<i>You hear a sharp crack.</i>")
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>")
else
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs briefly, but nothing happens":" try send everything above the rune away, but the teleportation fails"].</span>")
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs briefly, but nothing happens":" try send everything above the rune away, but the teleportation fails"].</span>")
if(is_mining_level(z) && !is_mining_level(target.z)) //No effect if you stay on lavaland
actual_selected_rune.handle_portal("lava")
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@
return

// Send 'em to the destination. If the teleport fails, just disembowel them and stop the chain
if(!destination || !do_teleport(sac_target, destination, asoundin = 'sound/magic/repulse.ogg', asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE, no_wake = TRUE))
if(!destination || !do_teleport(sac_target, destination, asoundin = 'sound/magic/repulse.ogg', asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, bypass_area_restriction = TRUE, no_wake = TRUE))
disembowel_target(sac_target)
return

Expand Down Expand Up @@ -374,7 +374,7 @@
safe_turf = get_turf(backup_loc)
stack_trace("[type] - return_target was unable to find a safe turf for [sac_target] to return to. Defaulting to observer start turf.")

if(!do_teleport(sac_target, safe_turf, asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, forced = TRUE, no_wake = TRUE))
if(!do_teleport(sac_target, safe_turf, asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_FREE, bypass_area_restriction = TRUE, no_wake = TRUE))
safe_turf = get_turf(backup_loc)
sac_target.forceMove(safe_turf)
stack_trace("[type] - return_target was unable to teleport [sac_target] to the observer start turf. Forcemoving.")
Expand Down
Loading
Loading