diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 3179e933e39..904a07a876b 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -90,8 +90,9 @@
return dist
/proc/circlerangeturfs(center=usr,radius=3)
-
var/turf/centerturf = get_turf(center)
+ if(radius == 1)
+ return list(centerturf)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index 90415738e2b..b273a698a83 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -1,7 +1,6 @@
//wrapper
/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
new /datum/teleport/instant/science(arglist(args))
- return
/datum/teleport
var/atom/movable/teleatom //atom to teleport
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 50e258f2a7a..9a7760bf224 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -1,6 +1,6 @@
/obj/effect/portal
name = "portal"
- desc = "Looks unstable. Best to test it carefully."
+ desc = "A bluespace tear in space, reaching directly to another point within this region. Looks unstable. Best to test it carefully."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "portal"
density = TRUE
@@ -8,61 +8,69 @@
var/does_teleport = TRUE // Some portals might just be visual
var/has_lifespan = TRUE // Whether we want to directly control the lifespan or not
var/failchance = 5
+ var/has_failed = FALSE
var/obj/target
var/creator
- var/precision = TRUE
+ var/precision = 1
anchored = TRUE
-/obj/effect/portal/CollidedWith(mob/M as mob|obj)
- set waitfor = FALSE
-
- if(does_teleport)
- src.teleport(M)
-
-/obj/effect/portal/Crossed(AM as mob|obj)
- set waitfor = FALSE
-
- if(does_teleport)
- src.teleport(AM)
-
-/obj/effect/portal/attack_hand(mob/user as mob)
- set waitfor = FALSE
-
- if(does_teleport)
- src.teleport(user)
-
-/obj/effect/portal/New(loc, turf/target, creator=null, lifespan=300, precise = 1)
- ..()
- if(target)
- src.target = target
- if(creator)
- src.creator = creator
+/obj/effect/portal/Initialize(mapload, turf/set_target, set_creator, lifespan = 300, precise = 1)
+ . = ..()
+ if(set_target)
+ target = set_target
+ if(set_creator)
+ creator = set_creator
if(has_lifespan && lifespan > 0)
QDEL_IN(src, lifespan)
+ if(prob(failchance))
+ has_failed = TRUE
precision = precise
-/obj/effect/portal/proc/teleport(atom/movable/M as mob|obj)
+/obj/effect/portal/CollidedWith(mob/M)
+ set waitfor = FALSE
+
+ if(does_teleport)
+ teleport(M)
+
+/obj/effect/portal/Crossed(AM)
+ set waitfor = FALSE
+
+ if(does_teleport)
+ teleport(AM)
+
+/obj/effect/portal/attack_hand(mob/user)
+ set waitfor = FALSE
+
+ if(does_teleport)
+ teleport(user)
+
+/obj/effect/portal/proc/teleport(atom/movable/M)
if(!does_teleport) // just to be safe
return
if(istype(M, /obj/effect)) //sparks don't teleport
return
- if (icon_state == "portal1")
- return
- if (!( target ))
+ if(!target)
qdel(src)
return
- if (istype(M, /atom/movable))
- if(prob(failchance)) //oh dear a problem, put em in deep space
- src.icon_state = "portal1"
+ if(istype(M, /atom/movable))
+ if(has_failed) //oh dear a problem, put em in deep space
+ icon_state = "portal1" // only tell people the portal failed after a teleport has been done
+ desc = "A bluespace tear in space, reaching directly to another point within this region. Definitely unstable."
do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0)
else
do_teleport(M, target, precision)
+/obj/effect/portal/Destroy()
+ if(istype(creator, /obj/item/hand_tele))
+ var/obj/item/hand_tele/HT = creator
+ HT.remove_portal(src)
+ return ..()
+
/obj/effect/portal/spawner
name = "portal"
- desc = "Looks like a one-way portal, don't come too close."
+ desc = "A bluespace tear in space, reaching directly to another point within this region. This one looks like a one-way portal to here, don't come too close."
desc_info = "This portal is a spawner portal. You cannot enter it to teleport, but it will periodically spawn things."
does_teleport = FALSE
has_lifespan = FALSE
diff --git a/code/game/objects/items/weapons/syndie.dm b/code/game/objects/items/weapons/syndie.dm
index 14cc47d182b..160e1eec5e4 100644
--- a/code/game/objects/items/weapons/syndie.dm
+++ b/code/game/objects/items/weapons/syndie.dm
@@ -164,4 +164,4 @@
/obj/item/syndie/teleporter/pickup()
..()
- addtimer(CALLBACK(src, .proc/check_maptext), 1)
+ addtimer(CALLBACK(src, .proc/check_maptext), 1) // invoke async does not work here
diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm
index e0c19e49e07..2eba0834109 100644
--- a/code/game/objects/items/weapons/teleportation.dm
+++ b/code/game/objects/items/weapons/teleportation.dm
@@ -125,7 +125,7 @@ Frequency:
*/
/obj/item/hand_tele
name = "hand tele"
- desc = "A portable item using blue-space technology."
+ desc = "A hand-held bluespace teleporter that can rip open portals to a random nearby location, or lock onto a teleporter with a selected teleportation beacon."
icon = 'icons/obj/device.dmi'
icon_state = "hand_tele"
item_state = "electronic"
@@ -135,62 +135,85 @@ Frequency:
throw_range = 5
origin_tech = list(TECH_MAGNET = 1, TECH_BLUESPACE = 3)
matter = list(DEFAULT_WALL_MATERIAL = 10000)
+ var/list/active_teleporters = list()
+ var/held_maptext = "Ready"
-/obj/item/hand_tele/attack_self(mob/user as mob)
+/obj/item/hand_tele/Initialize()
+ . = ..()
+ if(ismob(loc) || ismob(loc.loc))
+ maptext = held_maptext
+
+/obj/item/hand_tele/attack_self(mob/user)
var/turf/current_location = get_turf(user)//What turf is the user on?
- if(!current_location||current_location.z==2||current_location.z>=7)//If turf was not found or they're on z level 2 or >7 which does not currently exist.
- to_chat(user, "\The [src] is malfunctioning.")
+ if(!current_location || isNotStationLevel(current_location.z))
+ to_chat(user, SPAN_WARNING("\The [src] can't get a bearing on anything right now."))
return
- var/list/L = list( )
+
+ var/list/teleport_options = list()
for(var/obj/machinery/teleport/hub/R in SSmachinery.all_machines)
- var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(R.x - 2, R.y, R.z))
- if (istype(com, /obj/machinery/computer/teleporter) && com.locked && !com.one_time_use)
+ var/obj/machinery/computer/teleporter/com = R.com
+ if(com?.locked && !com.one_time_use)
if(R.icon_state == "tele1")
- L["[com.id] (Active)"] = com.locked
+ teleport_options["[com.id] (Active)"] = com.locked
else
- L["[com.id] (Inactive)"] = com.locked
- var/list/turfs = list( )
+ teleport_options["[com.id] (Inactive)"] = com.locked
+ var/list/potential_turfs = list()
for(var/turf/T in orange(10))
- if(T.x>world.maxx-8 || T.x<8)
+ if(T.x > world.maxx-8 || T.x < 8)
continue //putting them at the edge is dumb
-
- if(T.y>world.maxy-8 || T.y<8)
+ if(T.y > world.maxy-8 || T.y < 8)
continue
-
- if(T.density)
+ if(T.density || turf_contains_dense_objects(T))
continue
+ potential_turfs += T
- var/breakcheck = 0
- for(var/atom/movable/A in T)
- if(A.density && A.opacity && A.anchored)
- breakcheck = 1
- break
+ if(length(potential_turfs))
+ teleport_options["None (Dangerous)"] = pick(potential_turfs)
- if(breakcheck)
- continue
-
- turfs += T
-
- if(turfs.len)
- L["None (Dangerous)"] = pick(turfs)
- var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") in L
- if ((user.get_active_hand() != src || user.stat || user.restrained()))
+ var/teleport_choice = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in teleport_options
+ if(!teleport_choice || user.get_active_hand() != src || use_check_and_message(user))
return
- var/count = 0 //num of portals from this teleport in world
- for(var/obj/effect/portal/PO in world)
- if(PO.creator == src) count++
- if(count >= 3)
- user.show_message("\The [src] is recharging!")
+
+ if(length(active_teleporters) >= 3)
+ user.show_message(SPAN_WARNING("\The [src] is recharging!"))
return
- var/T = L[t1]
- for(var/mob/O in hearers(user, null))
- O.show_message("Locked In.", 2)
- var/obj/effect/portal/P = new /obj/effect/portal( get_turf(src) )
- P.target = T
- P.creator = src
- src.add_fingerprint(user)
- return
+
+ var/T = teleport_options[teleport_choice]
+ audible_message(SPAN_NOTICE("Locked in."), hearing_distance = 3)
+ var/obj/effect/portal/P = new /obj/effect/portal(get_turf(src), T, src)
+ active_teleporters += P
+ if(length(active_teleporters) >= 3)
+ check_maptext("Charge")
+ add_fingerprint(user)
+
+/obj/item/hand_tele/proc/remove_portal(var/obj/effect/portal/P)
+ active_teleporters -= P
+ if(length(active_teleporters) < 3)
+ check_maptext("Ready")
+
+/obj/item/hand_tele/proc/check_maptext(var/new_maptext)
+ if(new_maptext)
+ held_maptext = new_maptext
+ if(ismob(loc) || ismob(loc.loc))
+ maptext = held_maptext
+ else
+ maptext = ""
+
+/obj/item/hand_tele/throw_at()
+ ..()
+ check_maptext()
+
+/obj/item/hand_tele/dropped()
+ ..()
+ check_maptext()
+
+/obj/item/hand_tele/on_give()
+ check_maptext()
+
+/obj/item/hand_tele/pickup()
+ ..()
+ addtimer(CALLBACK(src, .proc/check_maptext), 1) // invoke async does not work here
/obj/item/closet_teleporter
name = "closet teleporter"
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 6550ceb8675..79cd9cb751c 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -744,7 +744,7 @@
to_chat(user, SPAN_NOTICE("\The [src] found no beacons in the world to anchor a wormhole to."))
return
var/chosen_beacon = pick(L)
- var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), chosen_beacon, lifespan = 100)
+ var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), chosen_beacon, null, 100)
J.target = chosen_beacon
playsound(src,'sound/effects/sparks4.ogg', 50, 1)
qdel(src)
diff --git a/html/changelogs/geeves-teleport_safety.yml b/html/changelogs/geeves-teleport_safety.yml
new file mode 100644
index 00000000000..1a9d3bdce97
--- /dev/null
+++ b/html/changelogs/geeves-teleport_safety.yml
@@ -0,0 +1,8 @@
+author: Geeves
+
+delete-after: True
+
+changes:
+ - rscadd: "Hand-teleporters have been made safer to use, they no longer teleport you in a radius around a beacon, but on top of it."
+ - rscadd: "Hand-teles now have a text based indicator that shows you when it's ready for use."
+ - tweak: "Portals no longer have a random chance of failing per teleport, but have a random chance of starting destabilized. It will only be visually apparent after the first teleport."