diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index e86469b58b7..c8ededddd88 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -2,7 +2,7 @@
/obj/effect/portal
name = "portal"
- desc = "Looks unstable. Best to test it with the clown."
+ desc = "Result of bluespace high tech development."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "portal"
@@ -12,10 +12,11 @@
/// The ckey of the mob which was responsible for the creation of the portal. For example, the mob who used a wormhole jaunter.
var/creation_mob_ckey
- var/failchance = 5
+ var/failchance = 0
var/fail_icon = "portal1"
- var/precision = TRUE // how close to the portal you will teleport. FALSE = on the portal, TRUE = adjacent
+ /// How close to the portal you will teleport. FALSE = on the portal, TRUE = adjacent
+ var/precision = FALSE
var/can_multitool_to_remove = FALSE
var/ignore_tele_proof_area_setting = FALSE
var/one_use = FALSE // Does this portal go away after one teleport?
@@ -43,6 +44,7 @@
if(!QDELETED(O))
O.portal_destroyed(src)
target = null
+ do_sparks(5, 0, loc)
return ..()
/obj/effect/portal/singularity_pull()
@@ -143,6 +145,44 @@
do_sparks(5, 0, loc)
qdel(src)
+#define UNSTABLE_TIME_DELAY 2 SECONDS
+
+/obj/effect/portal/hand_tele
+ /// After you touch the portal, it will be unstable with high bad teleport chance, this variable contains time when it will be fine again
+ var/unstable_time = 0
+ /// If this is TRUE, you will not be able to teleport with that portal
+ var/inactive = FALSE
+
+/obj/effect/portal/hand_tele/examine(mob/user, infix, suffix)
+ . = ..()
+ if(unstable_time > world.time)
+ . += "[src] is shaking, it looks very unstable!"
+
+/obj/effect/portal/hand_tele/can_teleport(atom/movable/M)
+ if(inactive)
+ return FALSE
+ return ..()
+
+/obj/effect/portal/hand_tele/teleport(atom/movable/M)
+ . = ..()
+ adjust_unstable()
+
+/obj/effect/portal/hand_tele/proc/adjust_unstable()
+ unstable_time = world.time + UNSTABLE_TIME_DELAY
+ icon_state = fail_icon
+ failchance = 33
+ inactive = TRUE
+ addtimer(CALLBACK(src, PROC_REF(check_unstable), unstable_time), UNSTABLE_TIME_DELAY)
+ addtimer(VARSET_CALLBACK(src, inactive, FALSE), 0.5 SECONDS) // after unstable is setted you have 0.5 safe seconds to think if you want to use it
+
+/obj/effect/portal/hand_tele/proc/check_unstable(current_unstable_time)
+ if(current_unstable_time != unstable_time)
+ return
+ icon_state = initial(icon_state)
+ failchance = 0
+
+#undef UNSTABLE_TIME_DELAY
+
/obj/effect/portal/redspace
name = "redspace portal"
desc = "A portal capable of bypassing bluespace interference."
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index b40ccf64171..12207bc9b9e 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -34,7 +34,7 @@
name = "syndicate teleporter"
desc = "A strange syndicate version of a cult veil shifter. Warranty voided if exposed to EMP."
icon = 'icons/obj/device.dmi'
- icon_state = "syndi-tele"
+ icon_state = "syndi-tele-4"
throwforce = 5
w_class = WEIGHT_CLASS_SMALL
throw_speed = 4
@@ -42,6 +42,13 @@
flags = CONDUCT
item_state = "electronic"
origin_tech = "magnets=3;combat=3;syndicate=3"
+ var/list/icons_charges = list(
+ "syndi-tele-0",
+ "syndi-tele-1",
+ "syndi-tele-2",
+ "syndi-tele-3",
+ "syndi-tele-4",
+ )
var/tp_range = 8
var/inner_tp_range = 3
var/charges = 4
@@ -67,6 +74,7 @@
/obj/item/teleporter/process()
if(prob(10) && charges < max_charges)
charges++
+ update_icon_charges()
/obj/item/teleporter/emp_act(severity)
var/teleported_something = FALSE
@@ -87,6 +95,12 @@
do_sparks(2, 1, src)
qdel(src)
+/obj/item/teleporter/proc/update_icon_charges()
+ if(charges + 1 > length(icons_charges))
+ icon_state = initial(icon_state)
+ return
+ icon_state = icons_charges[charges + 1]
+
/obj/item/teleporter/proc/attempt_teleport(mob/user, EMP_D = FALSE)
dir_correction(user)
if(!charges && !EMP_D) //If it's empd, you are moving no matter what.
@@ -119,6 +133,7 @@
to_chat(M, "[src] will not work here!")
if(charges > 0) //While we want EMP triggered teleports to drain charge, we also do not want it to go negative charge, as such we need this check here
charges--
+ update_icon_charges()
var/turf/destination = pick(turfs)
if(tile_check(destination) || flawless) // Why is there so many bloody floor types
var/turf/fragging_location = destination
diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm
index a086551e688..d3eceff5ae8 100644
--- a/code/game/objects/items/weapons/teleportation.dm
+++ b/code/game/objects/items/weapons/teleportation.dm
@@ -107,7 +107,10 @@ Frequency:
origin_tech = "magnets=3;bluespace=4"
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 30, RAD = 0, FIRE = 100, ACID = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ var/icon_state_inactive = "hand_tele_inactive"
var/active_portals = 0
+ /// Variable contains next time hand tele can be used to make it not EMP proof
+ var/emp_timer = 0
/obj/item/hand_tele/Initialize(mapload)
. = ..()
@@ -115,6 +118,10 @@ Frequency:
/obj/item/hand_tele/attack_self(mob/user)
var/turf/current_location = get_turf(user)//What turf is the user on?
+ if(emp_timer > world.time)
+ do_sparks(5, 0, loc)
+ to_chat(user, "[src] attempts to create a portal, but abruptly shuts off.")
+ return
if(!current_location||!is_teleport_allowed(current_location.z))//If turf was not found or they're somewhere teleproof
to_chat(user, "\The [src] is malfunctioning.")
return
@@ -146,10 +153,30 @@ Frequency:
return
var/T = L[t1]
user.show_message("Locked In.", 2)
- var/obj/effect/portal/P = new /obj/effect/portal(get_turf(src), T, src, creation_mob = user)
+ var/obj/effect/portal/P = new /obj/effect/portal/hand_tele(get_turf(src), T, src, creation_mob = user)
try_move_adjacent(P)
active_portals++
add_fingerprint(user)
+/obj/item/hand_tele/emp_act(severity)
+ make_inactive(severity)
+ return ..()
+
+/obj/item/hand_tele/proc/make_inactive(severity)
+ icon_state = icon_state_inactive
+ var/time = rand(25 SECONDS, 30 SECONDS) * severity
+ emp_timer = world.time + time
+ addtimer(CALLBACK(src, PROC_REF(check_inactive), emp_timer), time)
+
+/obj/item/hand_tele/proc/check_inactive(current_emp_timer)
+ if(emp_timer != current_emp_timer)
+ return
+ icon_state = initial(icon_state)
+
+/obj/item/hand_tele/examine(mob/user)
+ . = ..()
+ if(emp_timer > world.time)
+ . += "It looks inactive."
+
/obj/item/hand_tele/portal_destroyed(obj/effect/portal/P)
active_portals--
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 301cb196729..212df4631b6 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ