diff --git a/code/game/objects/structures/alien_props.dm b/code/game/objects/structures/props/alien_props.dm
similarity index 90%
rename from code/game/objects/structures/alien_props.dm
rename to code/game/objects/structures/props/alien_props.dm
index eb8ae9d19f..fa11e27b15 100644
--- a/code/game/objects/structures/alien_props.dm
+++ b/code/game/objects/structures/props/alien_props.dm
@@ -6,15 +6,6 @@
icon = 'icons/obj/abductor.dmi'
density = TRUE
anchored = TRUE
- var/interaction_message = null
-
-/obj/structure/prop/alien/attack_hand(mob/living/user) // Used to tell the player that this isn't useful for anything.
- if(!istype(user))
- return FALSE
- if(!interaction_message)
- return ..()
- else
- to_chat(user, interaction_message)
/obj/structure/prop/alien/computer
name = "alien console"
diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm
new file mode 100644
index 0000000000..44df2eb68b
--- /dev/null
+++ b/code/game/objects/structures/props/beam_prism.dm
@@ -0,0 +1,215 @@
+//A series(?) of prisms for PoIs. The base one only works for beams.
+
+/obj/structure/prop/prism
+ name = "prismatic turret"
+ desc = "A raised, externally powered 'turret'. It seems to have a massive crystal ring around its base."
+ description_info = "This device is capable of redirecting any beam projectile."
+ icon = 'icons/obj/props/prism.dmi'
+ icon_state = "prism"
+ density = TRUE
+ anchored = TRUE
+
+ layer = 3.1 //Layer over projectiles.
+ plane = -10 //Layer over projectiles.
+
+ var/rotation_lock = 0 // Can you rotate the prism at all?
+ var/free_rotate = 1 // Does the prism rotate in any direction, or only in the eight standard compass directions?
+ var/external_control_lock = 0 // Does the prism only rotate from the controls of an external switch?
+ var/degrees_from_north = 0 // How far is it rotated clockwise?
+ var/compass_directions = list("North" = 0, "South" = 180, "East" = 90, "West" = 270, "Northwest" = 315, "Northeast" = 45, "Southeast" = 135, "Southwest" = 225)
+ var/interaction_sound = 'sound/mecha/mechmove04.ogg'
+
+ var/redirect_type = /obj/item/projectile/beam
+
+ var/dialID = null
+ var/obj/structure/prop/prismcontrol/remote_dial = null
+
+ interaction_message = "The prismatic turret seems to be able to rotate."
+
+/obj/structure/prop/prism/initialize()
+ if(degrees_from_north)
+ animate(src, transform = turn(NORTH, degrees_from_north), time = 3)
+
+/obj/structure/prop/prism/Destroy()
+ if(remote_dial)
+ remote_dial.my_turrets -= src
+ remote_dial = null
+ ..()
+
+/obj/structure/prop/prism/proc/reset_rotation()
+ var/degrees_to_rotate = -1 * degrees_from_north
+ animate(src, transform = turn(src.transform, degrees_to_rotate), time = 2)
+
+/obj/structure/prop/prism/attack_hand(mob/living/user)
+ ..()
+
+ if(rotation_lock)
+ to_chat(user, "\The [src] is locked at its current bearing.")
+ return
+ if(external_control_lock)
+ to_chat(user, "\The [src]'s motors resist your efforts to rotate it. You may need to find some form of controller.")
+ return
+
+ var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No")
+ if(confirm == "No")
+ visible_message(\
+ "[user.name] decides not to try turning \the [src].",\
+ "You decide not to try turning \the [src].")
+ return
+
+ var/new_bearing
+ if(free_rotate)
+ new_bearing = input("What bearing do you want to rotate \the [src] to?", "[name]") as num
+ new_bearing = round(new_bearing)
+ if(new_bearing <= -1 || new_bearing > 360)
+ to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.")
+ return
+ else
+ var/choice = input("What point do you want to set \the [src] to?", "[name]") as null|anything in compass_directions
+ new_bearing = round(compass_directions[choice])
+
+ var/rotate_degrees = new_bearing - degrees_from_north
+
+ if(new_bearing == 360) // Weird artifact.
+ new_bearing = 0
+ degrees_from_north = new_bearing
+
+ var/two_stage = 0
+ if(rotate_degrees == 180 || rotate_degrees == -180)
+ two_stage = 1
+ var/multiplier = pick(-1, 1)
+ rotate_degrees = multiplier * (rotate_degrees / 2)
+
+ playsound(src, interaction_sound, 50, 1)
+ if(two_stage)
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 3)
+ spawn(3)
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 3)
+ else
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 6) //Can't update transform because it will reset the angle.
+
+/obj/structure/prop/prism/proc/rotate_auto(var/new_bearing)
+ if(rotation_lock)
+ visible_message("\The [src] shudders.")
+ playsound(src, 'sound/effects/clang.ogg', 50, 1)
+ return
+
+ visible_message("\The [src] rotates to a bearing of [new_bearing].")
+
+ var/rotate_degrees = new_bearing - degrees_from_north
+
+ if(new_bearing == 360)
+ new_bearing = 0
+ degrees_from_north = new_bearing
+
+ var/two_stage = 0
+ if(rotate_degrees == 180 || rotate_degrees == -180)
+ two_stage = 1
+ var/multiplier = pick(-1, 1)
+ rotate_degrees = multiplier * (rotate_degrees / 2)
+
+ playsound(src, interaction_sound, 50, 1)
+ if(two_stage)
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 3)
+ spawn(3)
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 3)
+ else
+ animate(src, transform = turn(src.transform, rotate_degrees), time = 6)
+
+/obj/structure/prop/prism/bullet_act(var/obj/item/projectile/Proj)
+ if(istype(Proj, redirect_type))
+ visible_message("\The [src] redirects \the [Proj]!")
+ flick("[initial(icon_state)]+glow", src)
+
+ var/new_x = (1 * round(10 * cos(degrees_from_north - 90))) + x //Vectors vectors vectors.
+ var/new_y = (-1 * round(10 * sin(degrees_from_north - 90))) + y
+ var/turf/curloc = get_turf(src)
+
+ Proj.penetrating += 1 // Needed for the beam to get out of the turret.
+
+ Proj.redirect(new_x, new_y, curloc, null)
+
+/obj/structure/prop/prism/incremental
+ free_rotate = 0
+ description_info = "This device is capable of redirecting any beam projectile, but only locks to specific positions in rotation."
+
+/obj/structure/prop/prism/incremental/externalcont
+ external_control_lock = 1
+ description_info = "This device is capable of redirecting any beam projectile, but can only be rotated by a control dial to specific positions."
+
+/obj/structure/prop/prism/externalcont
+ external_control_lock = 1
+ description_info = "This device is capable of redirecting any beam projectile, but can only be rotated by an external control dial."
+
+/obj/structure/prop/prismcontrol
+ name = "prismatic dial"
+ desc = "A large dial with a crystalline ring."
+ icon = 'icons/obj/props/prism.dmi'
+ icon_state = "dial"
+ density = FALSE
+ anchored = TRUE
+
+ interaction_message = "The dial pulses as your hand nears it."
+ var/list/my_turrets = list()
+ var/dialID = null
+
+/obj/structure/prop/prismcontrol/attack_hand(mob/living/user)
+ ..()
+
+ var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No")
+ if(confirm == "No")
+ visible_message(\
+ "[user.name] decides not to try turning \the [src].",\
+ "You decide not to try turning \the [src].")
+ return
+
+ if(!my_turrets || !my_turrets.len)
+ to_chat(user, "\The [src] doesn't seem to do anything.")
+ return
+
+ var/free_rotate = 1
+ var/list/compass_directions = list()
+ for(var/obj/structure/prop/prism/P in my_turrets)
+ if(!P.free_rotate) //Doesn't use bearing, it uses compass points.
+ free_rotate = 0
+ compass_directions |= P.compass_directions
+
+ var/new_bearing
+ if(free_rotate)
+ new_bearing = input("What bearing do you want to rotate \the [src] to?", "[name]") as num
+ new_bearing = round(new_bearing)
+ if(new_bearing <= -1 || new_bearing > 360)
+ to_chat(user, "Rotating \the [src] [new_bearing] degrees would be a waste of time.")
+ return
+ else
+ var/choice = input("What point do you want to set \the [src] to?", "[name]") as null|anything in compass_directions
+ new_bearing = round(compass_directions[choice])
+
+ confirm = input("Are you certain you want to rotate \the [src]?", "[name]") in list("Yes", "No")
+ if(confirm == "No")
+ visible_message(\
+ "[user.name] decides not to try turning \the [src].",\
+ "You decide not to try turning \the [src].")
+ return
+
+ to_chat(user, "\The [src] clicks into place.")
+ for(var/obj/structure/prop/prism/P in my_turrets)
+ P.rotate_auto(new_bearing)
+
+/obj/structure/prop/prismcontrol/initialize()
+ ..()
+ if(my_turrets.len) //Preset controls.
+ for(var/obj/structure/prop/prism/P in my_turrets)
+ P.remote_dial = src
+ return
+ spawn()
+ for(var/obj/structure/prop/prism/P in orange(src, world.view)) //Don't search a huge area.
+ if(P.dialID == dialID && !P.remote_dial && P.external_control_lock)
+ my_turrets |= P
+ P.remote_dial = src
+
+/obj/structure/prop/prismcontrol/Destroy()
+ for(var/obj/structure/prop/prism/P in my_turrets)
+ P.remote_dial = null
+ my_turrets = list()
+ ..()
diff --git a/code/game/objects/structures/props/projectile_lock.dm b/code/game/objects/structures/props/projectile_lock.dm
new file mode 100644
index 0000000000..5c3fcd2ba0
--- /dev/null
+++ b/code/game/objects/structures/props/projectile_lock.dm
@@ -0,0 +1,53 @@
+//A locking mechanism that pulses when hit by a projectile. The base one responds to high-power lasers.
+
+/obj/structure/prop/lock
+ name = "weird lock"
+ desc = "An esoteric object that responds to.. something."
+ icon = 'icons/obj/props/prism.dmi'
+ icon_state = "lock"
+
+ var/enabled = 0
+ var/lockID = null
+
+ var/list/linked_objects = list()
+
+/obj/structure/prop/lock/Destroy()
+ if(linked_objects.len)
+ for(var/obj/O in linked_objects)
+ if(istype(O, /obj/machinery/door/blast/puzzle))
+ var/obj/machinery/door/blast/puzzle/P = O
+ P.locks -= src
+ linked_objects -= P
+ ..()
+
+/obj/structure/prop/lock/proc/toggle_lock()
+ enabled = !enabled
+
+ if(enabled)
+ icon_state = "[initial(icon_state)]-active"
+ else
+ icon_state = "[initial(icon_state)]"
+
+/obj/structure/prop/lock/projectile
+ name = "beam lock"
+ desc = "An esoteric object that responds to high intensity light."
+
+ var/projectile_key = /obj/item/projectile/beam
+ var/timed = 0
+ var/timing = 0
+ var/time_limit = 1500 // In ticks. Ten is one second.
+
+ interaction_message = "The object remains inert to your touch."
+
+/obj/structure/prop/lock/projectile/bullet_act(var/obj/item/projectile/Proj)
+ if(!istype(Proj, projectile_key) || timing)
+ return
+
+ if(istype(Proj, /obj/item/projectile/beam/heavylaser/cannon) || istype(Proj, /obj/item/projectile/beam/emitter) || (Proj.damage >= 80 && Proj.damtype == BURN))
+ toggle_lock()
+ visible_message("\The [src] [enabled ? "disengages" : "engages"] its locking mechanism.")
+
+ if(timed)
+ timing = 1
+ spawn(time_limit)
+ toggle_lock()
diff --git a/code/game/objects/structures/props/prop.dm b/code/game/objects/structures/props/prop.dm
new file mode 100644
index 0000000000..fea5815674
--- /dev/null
+++ b/code/game/objects/structures/props/prop.dm
@@ -0,0 +1,18 @@
+//The base 'prop' for PoIs or other large junk.
+
+/obj/structure/prop
+ name = "something"
+ desc = "My description is broken, bug a developer."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "safe"
+ density = TRUE
+ anchored = TRUE
+ var/interaction_message = null
+
+/obj/structure/prop/attack_hand(mob/living/user) // Used to tell the player that this isn't useful for anything.
+ if(!istype(user))
+ return FALSE
+ if(!interaction_message)
+ return ..()
+ else
+ to_chat(user, interaction_message)
diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm
new file mode 100644
index 0000000000..b9a32fc0dc
--- /dev/null
+++ b/code/game/objects/structures/props/puzzledoor.dm
@@ -0,0 +1,92 @@
+// An indestructible blast door that can only be opened once its puzzle requirements are completed.
+
+/obj/machinery/door/blast/puzzle
+ name = "puzzle door"
+ desc = "A large, virtually indestructible door that will not open unless certain requirements are met."
+ icon_state_open = "pdoor0"
+ icon_state_opening = "pdoorc0"
+ icon_state_closed = "pdoor1"
+ icon_state_closing = "pdoorc1"
+ icon_state = "pdoor1"
+
+ explosion_resistance = 100
+
+ maxhealth = 9999999 //No.
+
+ var/list/locks = list()
+ var/lockID = null
+ var/checkrange_mult = 1
+
+/obj/machinery/door/blast/puzzle/proc/check_locks()
+ for(var/obj/structure/prop/lock/L in locks)
+ if(!L.enabled)
+ return 0
+ return 1
+
+/obj/machinery/door/blast/puzzle/bullet_act(var/obj/item/projectile/Proj)
+ visible_message("\The [src] is completely unaffected by \the [Proj].")
+ qdel(Proj) //No piercing. No.
+
+/obj/machinery/door/blast/puzzle/ex_act(severity)
+ visible_message("\The [src] is completely unaffected by the blast.")
+ return
+
+/obj/machinery/door/blast/puzzle/initialize()
+ . = ..()
+ implicit_material = get_material_by_name("dungeonium")
+ if(locks.len)
+ return
+ var/check_range = world.view * checkrange_mult
+ for(var/obj/structure/prop/lock/L in orange(src, check_range))
+ if(L.lockID == lockID)
+ L.linked_objects |= src
+ locks |= L
+
+/obj/machinery/door/blast/puzzle/Destroy()
+ if(locks.len)
+ for(var/obj/structure/prop/lock/L in locks)
+ L.linked_objects -= src
+ locks -= L
+ ..()
+
+/obj/machinery/door/blast/puzzle/attack_hand(mob/user as mob)
+ if(check_locks())
+ force_toggle(1, user)
+ else
+ to_chat(user, "\The [src] does not respond to your touch.")
+
+/obj/machinery/door/blast/puzzle/attackby(obj/item/weapon/C as obj, mob/user as mob)
+ if(istype(C, /obj/item/weapon))
+ if(C.pry == 1 && (user.a_intent != I_HURT || (stat & BROKEN)))
+ if(istype(C,/obj/item/weapon/material/twohanded/fireaxe))
+ var/obj/item/weapon/material/twohanded/fireaxe/F = C
+ if(!F.wielded)
+ to_chat(user, "You need to be wielding \the [F] to do that.")
+ return
+
+ if(check_locks())
+ force_toggle(1, user)
+
+ else
+ to_chat(user, "[src]'s arcane workings resist your effort.")
+ return
+
+ else if(src.density && (user.a_intent == I_HURT))
+ var/obj/item/weapon/W = C
+ user.setClickCooldown(user.get_attack_speed(W))
+ if(W.damtype == BRUTE || W.damtype == BURN)
+ user.do_attack_animation(src)
+ user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.")
+
+ else if(istype(C, /obj/item/weapon/plastique))
+ to_chat(user, "On contacting \the [src], a flash of light envelops \the [C] as it is turned to ash. Oh.")
+ qdel(C)
+ return 0
+
+/obj/machinery/door/blast/puzzle/attack_generic(var/mob/user, var/damage)
+ if(check_locks())
+ force_toggle(1, user)
+
+/obj/machinery/door/blast/puzzle/attack_alien(var/mob/user)
+ if(check_locks())
+ force_toggle(1, user)
diff --git a/icons/obj/props/prism.dmi b/icons/obj/props/prism.dmi
new file mode 100644
index 0000000000..0b938c7ef0
Binary files /dev/null and b/icons/obj/props/prism.dmi differ
diff --git a/icons/obj/props/projectile_lock.dmi b/icons/obj/props/projectile_lock.dmi
new file mode 100644
index 0000000000..cf74d73796
Binary files /dev/null and b/icons/obj/props/projectile_lock.dmi differ
diff --git a/vorestation.dme b/vorestation.dme
index 21345fe0dc..1176f15e95 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -1156,7 +1156,6 @@
#include "code\game\objects\random\mob.dm"
#include "code\game\objects\random\random_vr.dm"
#include "code\game\objects\random\spacesuits.dm"
-#include "code\game\objects\structures\alien_props.dm"
#include "code\game\objects\structures\barsign.dm"
#include "code\game\objects\structures\bedsheet_bin.dm"
#include "code\game\objects\structures\bonfire.dm"
@@ -1238,6 +1237,11 @@
#include "code\game\objects\structures\flora\trees.dm"
#include "code\game\objects\structures\ghost_pods\ghost_pods.dm"
#include "code\game\objects\structures\ghost_pods\silicon.dm"
+#include "code\game\objects\structures\props\alien_props.dm"
+#include "code\game\objects\structures\props\beam_prism.dm"
+#include "code\game\objects\structures\props\projectile_lock.dm"
+#include "code\game\objects\structures\props\prop.dm"
+#include "code\game\objects\structures\props\puzzledoor.dm"
#include "code\game\objects\structures\stool_bed_chair_nest\alien_nests.dm"
#include "code\game\objects\structures\stool_bed_chair_nest\bed.dm"
#include "code\game\objects\structures\stool_bed_chair_nest\chairs.dm"