Files
Bubberstation/code/game/objects/items/teleportation.dm
SkyratBot 6c7b083333 [MIRROR] Fix hand teleporter and other portals looping forever (#6598)
* Fix hand teleporter and other portals looping forever (#59894)

Makes portals use Bumped() instead of COMSIG_ATOM_ENTERED for detecting atoms crossing the event horizon.

Removes unused and potential loop causing forceMove argument from do_teleport()

* Fix hand teleporter and other portals looping forever

Co-authored-by: Wayland-Smithy <64715958+Wayland-Smithy@users.noreply.github.com>
2021-06-30 01:58:39 +01:00

315 lines
11 KiB
Plaintext

#define SOURCE_PORTAL 1
#define DESTINATION_PORTAL 2
/* Teleportation devices.
* Contains:
* Locator
* Hand-tele
*/
/*
* Locator
*/
/obj/item/locator
name = "bluespace locator"
desc = "Used to track portable teleportation beacons and targets with embedded tracking implants."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/temp = null
flags_1 = CONDUCT_1
w_class = WEIGHT_CLASS_SMALL
inhand_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron=400)
var/tracking_range = 20
/obj/item/locator/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "BluespaceLocator", name)
ui.open()
/obj/item/locator/ui_data(mob/user)
var/list/data = list()
data["trackingrange"] = tracking_range;
// Get our current turf location.
var/turf/sr = get_turf(src)
if (sr)
// Check every teleport beacon.
var/list/tele_beacons = list()
for(var/obj/item/beacon/W in GLOB.teleportbeacons)
// Get the tracking beacon's turf location.
var/turf/tr = get_turf(W)
// Make sure it's on a turf and that its Z-level matches the tracker's Z-level
if (tr && tr.z == sr.z)
// Get the distance between the beacon's turf and our turf
var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
// If the target is too far away, skip over this beacon.
if(distance > tracking_range)
continue
var/beacon_name
if(W.renamed)
beacon_name = W.name
else
var/area/A = get_area(W)
beacon_name = A.name
var/D = dir2text(get_dir(sr, tr))
tele_beacons += list(list(name = beacon_name, direction = D, distance = distance))
data["telebeacons"] = tele_beacons
var/list/track_implants = list()
for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
if (!W.imp_in || !isliving(W.loc))
continue
else
var/mob/living/M = W.loc
if (M.stat == DEAD)
if (M.timeofdeath + W.lifespan_postmortem < world.time)
continue
var/turf/tr = get_turf(W)
var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if(distance > tracking_range)
continue
var/D = dir2text(get_dir(sr, tr))
track_implants += list(list(name = W.imp_in.name, direction = D, distance = distance))
data["trackimplants"] = track_implants
return data
#define PORTAL_LOCATION_DANGEROUS "portal_location_dangerous"
#define PORTAL_DANGEROUS_EDGE_LIMIT 8
/*
* Hand-tele
*/
/obj/item/hand_tele
name = "hand tele"
desc = "A portable item using blue-space technology. One of the buttons opens a portal, the other re-opens your last destination."
icon = 'icons/obj/device.dmi'
icon_state = "hand_tele"
inhand_icon_state = "electronic"
worn_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 5
custom_materials = list(/datum/material/iron=10000)
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 30, BIO = 0, RAD = 0, FIRE = 100, ACID = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/list/active_portal_pairs
var/max_portal_pairs = 3
var/atmos_link_override
/**
* Represents the last place we teleported to, for making quick portals.
* Can be in the following states:
* - null, meaning either this hand tele hasn't been used yet, or the last place it was portalled to was removed.
* - PORTAL_LOCATION_DANGEROUS, meaning the last place it teleported to was the "None (Dangerous)" location.
* - A weakref to a /obj/machinery/computer/teleporter, meaning the last place it teleported to was a pre-setup location.
*/
var/last_portal_location
/obj/item/hand_tele/Initialize()
. = ..()
active_portal_pairs = list()
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
return TRUE
return ..()
/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
if(is_parent_of_portal(target))
qdel(target)
to_chat(user, span_notice("You dispel [target] with \the [src]!"))
return TRUE
return FALSE
/obj/item/hand_tele/afterattack(atom/target, mob/user)
try_dispel_portal(target, user)
. = ..()
/obj/item/hand_tele/pre_attack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
var/portal_location = last_portal_location
if (isweakref(portal_location))
var/datum/weakref/last_portal_location_ref = last_portal_location
portal_location = last_portal_location_ref.resolve()
if (isnull(portal_location))
to_chat(user, span_warning("[src] flashes briefly. No target is locked in."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
try_create_portal_to(user, portal_location)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/hand_tele/attack_self(mob/user)
if (!can_teleport_notifies(user))
return
//SKYRAT EDIT BEGIN
var/turf/my_turf = get_turf(src)
if(is_away_level(my_turf.z))
to_chat(user, "<span class='warning'>[src] cannot be used here!</span>")
return
//SKYRAT EDIT END
var/list/locations = list()
for(var/obj/machinery/computer/teleporter/computer in GLOB.machines)
if(!computer.target)
continue
var/area/computer_area = get_area(computer.target)
if(!computer_area || (computer_area.area_flags & NOTELEPORT))
continue
if(computer.power_station?.teleporter_hub && computer.power_station.engaged)
locations["[get_area(computer.target)] (Active)"] = computer
else
locations["[get_area(computer.target)] (Inactive)"] = computer
locations["None (Dangerous)"] = PORTAL_LOCATION_DANGEROUS
var/teleport_location_key = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in locations
if (!teleport_location_key || user.get_active_held_item() != src || user.incapacitated())
return
// Not always a datum, but needed for IS_WEAKREF_OF to cast properly.
var/datum/teleport_location = locations[teleport_location_key]
if (!try_create_portal_to(user, teleport_location))
return
if (teleport_location == PORTAL_LOCATION_DANGEROUS)
last_portal_location = PORTAL_LOCATION_DANGEROUS
else if (!IS_WEAKREF_OF(teleport_location, last_portal_location))
if (isweakref(teleport_location))
var/datum/weakref/about_to_replace_location_ref = last_portal_location
var/obj/machinery/computer/teleporter/about_to_replace_location = about_to_replace_location_ref.resolve()
if (about_to_replace_location)
UnregisterSignal(about_to_replace_location, COMSIG_TELEPORTER_NEW_TARGET)
RegisterSignal(teleport_location, COMSIG_TELEPORTER_NEW_TARGET, .proc/on_teleporter_new_target)
last_portal_location = WEAKREF(teleport_location)
/// Takes either PORTAL_LOCATION_DANGEROUS or an /obj/machinery/computer/teleport/computer.
/obj/item/hand_tele/proc/try_create_portal_to(mob/user, teleport_location)
if (active_portal_pairs.len >= max_portal_pairs)
user.show_message(span_notice("[src] is recharging!"))
return
var/teleport_turf
if (teleport_location == PORTAL_LOCATION_DANGEROUS)
var/list/dangerous_turfs = list()
for(var/turf/dangerous_turf in urange(10, orange=1))
if(dangerous_turf.x > world.maxx - PORTAL_DANGEROUS_EDGE_LIMIT || dangerous_turf.x < PORTAL_DANGEROUS_EDGE_LIMIT)
continue //putting them at the edge is dumb
if(dangerous_turf.y > world.maxy - PORTAL_DANGEROUS_EDGE_LIMIT || dangerous_turf.y < PORTAL_DANGEROUS_EDGE_LIMIT)
continue
var/area/dangerous_area = dangerous_turf.loc
if(dangerous_area.area_flags & NOTELEPORT)
continue
dangerous_turfs += dangerous_turf
teleport_turf = pick(dangerous_turfs)
else
var/obj/machinery/computer/teleporter/computer = teleport_location
teleport_turf = computer.target
if (teleport_turf == null)
to_chat(user, span_notice("[src] vibrates, then stops. Maybe you should try something else."))
return
var/area/teleport_area = get_area(teleport_turf)
if (teleport_area.area_flags & NOTELEPORT)
to_chat(user, span_notice("[src] is malfunctioning."))
return
if (!can_teleport_notifies(user))
return
var/list/obj/effect/portal/created = create_portal_pair(get_turf(user), get_teleport_turf(get_turf(teleport_turf)), 300, 1, null, atmos_link_override)
if(LAZYLEN(created) != 2)
return
var/obj/effect/portal/portal1 = created[1]
var/obj/effect/portal/portal2 = created[2]
RegisterSignal(portal1, COMSIG_PARENT_QDELETING, .proc/on_portal_destroy)
RegisterSignal(portal2, COMSIG_PARENT_QDELETING, .proc/on_portal_destroy)
var/turf/check_turf = get_turf(get_step(user, user.dir))
if(check_turf.CanPass(user, get_dir(check_turf, user)))
portal1.forceMove(check_turf)
active_portal_pairs[portal1] = portal2
investigate_log("was used by [key_name(user)] at [AREACOORD(user)] to create a portal pair with destinations [AREACOORD(portal1)] and [AREACOORD(portal2)].", INVESTIGATE_PORTAL)
add_fingerprint(user)
user.show_message(span_notice("Locked in."), MSG_AUDIBLE)
return TRUE
/obj/item/hand_tele/proc/can_teleport_notifies(mob/user)
var/turf/current_location = get_turf(user)
var/area/current_area = current_location.loc
if (!current_location || (current_area.area_flags & NOTELEPORT) || is_away_level(current_location.z) || !isturf(user.loc))
to_chat(user, span_notice("[src] is malfunctioning."))
return FALSE
return TRUE
/obj/item/hand_tele/proc/on_teleporter_new_target(datum/source)
SIGNAL_HANDLER
if (IS_WEAKREF_OF(source, last_portal_location))
last_portal_location = null
UnregisterSignal(source, COMSIG_TELEPORTER_NEW_TARGET)
/obj/item/hand_tele/proc/on_portal_destroy(obj/effect/portal/P)
SIGNAL_HANDLER
active_portal_pairs -= P //If this portal pair is made by us it'll be erased along with the other portal by the portal.
/obj/item/hand_tele/proc/is_parent_of_portal(obj/effect/portal/P)
if(!istype(P))
return FALSE
if(active_portal_pairs[P])
return SOURCE_PORTAL
for(var/i in active_portal_pairs)
if(active_portal_pairs[i] == P)
return DESTINATION_PORTAL
return FALSE
/obj/item/hand_tele/suicide_act(mob/user)
if(iscarbon(user))
user.visible_message(span_suicide("[user] is creating a weak portal and sticking [user.p_their()] head through! It looks like [user.p_theyre()] trying to commit suicide!"))
var/mob/living/carbon/itemUser = user
var/obj/item/bodypart/head/head = itemUser.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.drop_limb()
var/list/safeLevels = SSmapping.levels_by_any_trait(list(ZTRAIT_SPACE_RUINS, ZTRAIT_LAVA_RUINS, ZTRAIT_STATION, ZTRAIT_MINING))
head.forceMove(locate(rand(1, world.maxx), rand(1, world.maxy), pick(safeLevels)))
itemUser.visible_message(span_suicide("The portal snaps closed taking [user]'s head with it!"))
else
itemUser.visible_message(span_suicide("[user] looks even further depressed as they realize they do not have a head...and suddenly dies of shame!"))
return (BRUTELOSS)
#undef PORTAL_LOCATION_DANGEROUS
#undef PORTAL_DANGEROUS_EDGE_LIMIT