diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index d1a336dbb6..e73d316433 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -533,3 +533,23 @@
/mob/proc/update_gravity()
return
+
+// The real Move() proc is above, but touching that massive block just to put this in isn't worth it.
+/mob/Move(var/newloc, var/direct)
+ . = ..(newloc, direct)
+ if(.)
+ post_move(newloc, direct)
+
+// Called when a mob successfully moves.
+// Would've been an /atom/movable proc but it caused issues.
+/mob/proc/post_move(var/newloc, var/direct)
+ for(var/obj/O in contents)
+ O.on_loc_moved(newloc, direct)
+
+// Received from post_move(), useful for items that need to know that their loc just moved.
+/obj/proc/on_loc_moved(var/newloc, var/direct)
+ return
+
+/obj/item/weapon/storage/on_loc_moved(var/newloc, var/direct)
+ for(var/obj/O in contents)
+ O.on_loc_moved(newloc, direct)
\ No newline at end of file
diff --git a/code/modules/shieldgen/directional_shield.dm b/code/modules/shieldgen/directional_shield.dm
new file mode 100644
index 0000000000..df54a1fa74
--- /dev/null
+++ b/code/modules/shieldgen/directional_shield.dm
@@ -0,0 +1,286 @@
+// This is the actual shield. The projector is a different item.
+/obj/effect/directional_shield
+ name = "directional combat shield"
+ desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \
+ Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \
+ of it."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "directional_shield"
+ density = FALSE // People can move pass these shields.
+ opacity = FALSE
+ anchored = TRUE
+ unacidable = TRUE
+ layer = MOB_LAYER + 0.1
+ mouse_opacity = FALSE
+ var/obj/item/shield_projector/projector = null // The thing creating the shield.
+ var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position.
+ var/y_offset = 0 // Ditto.
+
+/obj/effect/directional_shield/New(var/newloc, var/new_projector)
+ if(new_projector)
+ projector = new_projector
+ var/turf/us = get_turf(src)
+ var/turf/them = get_turf(projector)
+ if(them)
+ x_offset = us.x - them.x
+ y_offset = us.y - them.y
+ ..(newloc)
+
+/obj/effect/directional_shield/proc/relocate()
+ if(!projector)
+ return // Nothing to follow.
+ var/turf/T = get_turf(projector)
+ if(!T)
+ return
+ var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z)
+ if(new_pos)
+ forceMove(new_pos)
+ else
+ qdel(src)
+
+/obj/effect/directional_shield/Destroy()
+ if(projector)
+ projector.active_shields -= src
+ projector = null
+ return ..()
+
+/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
+ if(air_group || (height==0))
+ return TRUE
+ else if(istype(mover, /obj/item/projectile))
+ var/obj/item/projectile/P = mover
+ var/bad_arc = reverse_direction(dir) // Arc of directions from which we cannot block.
+ if(check_shield_arc(src, bad_arc, P)) // This is actually for mobs but it will work for our purposes as well.
+ return FALSE
+ else
+ return TRUE
+ return TRUE
+
+/obj/effect/directional_shield/bullet_act(var/obj/item/projectile/P)
+ adjust_health(-P.get_structure_damage())
+ P.on_hit()
+ playsound(get_turf(src), 'sound/effects/EMPulse.ogg', 75, 1)
+
+// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields.
+/obj/effect/directional_shield/proc/adjust_health(amount)
+ if(projector)
+ projector.adjust_health(amount) // Projector will kill the shield if needed.
+ // If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable.
+
+
+// This actually creates the shields. It's an item so that it can be carried, but it could also be placed inside a stationary object if desired.
+// It should work inside the contents of any mob.
+/obj/item/shield_projector
+ name = "combat shield projector"
+ desc = "A miniturized and compact shield projector. This type has been optimized to diffuse lasers or block high velocity projectiles from the outside, \
+ but allow those projectiles to leave the shield from the inside. Blocking too many damaging projectiles will cause the shield to fail."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "signmaker_sec"
+ var/active = FALSE // If it's on.
+ var/shield_health = 400 // How much damage the shield blocks before breaking. This is a shared health pool for all shields attached to this projector.
+ var/max_shield_health = 400 // Ditto. This is fairly high, but shields are really big, you can't miss them, and laser carbines pump out so much hurt.
+ var/shield_regen_amount = 20 // How much to recharge every process(), after the delay.
+ var/shield_regen_delay = 5 SECONDS // If the shield takes damage, it won't recharge for this long.
+ var/last_damaged_time = null // world.time when the shields took damage, used for the delay.
+ var/list/active_shields = list() // Shields that are active and deployed.
+ var/always_on = FALSE // If true, will always try to reactivate if disabled for whatever reason, ideal if AI mobs are holding this.
+
+/obj/item/shield_projector/New()
+ processing_objects += src
+ ..()
+
+/obj/item/shield_projector/Destroy()
+ destroy_shields()
+ processing_objects -= src
+ return ..()
+
+/obj/item/shield_projector/proc/create_shield(var/newloc, var/new_dir)
+ var/obj/effect/directional_shield/S = new(newloc, src)
+ S.dir = new_dir
+ active_shields += S
+
+/obj/item/shield_projector/proc/create_shields() // Override this for a specific shape. Be sure to call ..() for the checks, however.
+ if(active) // Already made.
+ return FALSE
+ if(shield_health <= 0)
+ return FALSE
+ active = TRUE
+ return TRUE
+
+/obj/item/shield_projector/proc/destroy_shields()
+ for(var/obj/effect/directional_shield/S in active_shields)
+ active_shields -= S
+ qdel(S)
+ active = FALSE
+
+/obj/item/shield_projector/proc/update_shield_positions()
+ for(var/obj/effect/directional_shield/S in active_shields)
+ S.relocate()
+
+/obj/item/shield_projector/proc/adjust_health(amount)
+ shield_health = between(0, shield_health + amount, max_shield_health)
+ if(amount < 0)
+ if(shield_health <= 0)
+ destroy_shields()
+ var/turf/T = get_turf(src)
+ T.visible_message("\The [src] overloads and the shield vanishes!")
+ playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 75, 0)
+ else
+ if(shield_health < max_shield_health / 4) // Play a more urgent sounding beep if it's at 25% health.
+ playsound(get_turf(src), 'sound/machines/defib_success.ogg', 75, 0)
+ else
+ playsound(get_turf(src), 'sound/machines/defib_SafetyOn.ogg', 75, 0)
+ last_damaged_time = world.time
+
+/obj/item/shield_projector/attack_self(var/mob/living/user)
+ if(active)
+ if(always_on)
+ to_chat(user, "You can't seem to deactivate \the [src].")
+ return
+
+ destroy_shields()
+ else
+ set_dir(user.dir) // Needed for linear shields.
+ create_shields()
+ visible_message("\The [user] [!active ? "de":""]activates \the [src].")
+
+/obj/item/shield_projector/process()
+ if(shield_health < max_shield_health && ( (last_damaged_time + shield_regen_delay) < world.time) )
+ adjust_health(shield_regen_amount)
+ if(always_on && !active) // Make shields as soon as possible if this is set.
+ create_shields()
+ if(shield_health == max_shield_health)
+ playsound(get_turf(src), 'sound/machines/defib_ready.ogg', 75, 0)
+ else
+ playsound(get_turf(src), 'sound/machines/defib_safetyOff.ogg', 75, 0)
+
+/obj/item/shield_projector/examine(var/mob/user)
+ ..()
+ if(get_dist(src, user) <= 1)
+ to_chat(user, "\The [src]'s shield matrix is at [round( (shield_health / max_shield_health) * 100, 0.01)]% strength.")
+
+/obj/item/shield_projector/emp_act(var/severity)
+ adjust_health(-max_shield_health / severity) // A strong EMP will kill the shield instantly, but weaker ones won't on the first hit.
+
+/obj/item/shield_projector/Move(var/newloc, var/direct)
+ ..(newloc, direct)
+ update_shield_positions()
+
+/obj/item/shield_projector/on_loc_moved(var/newloc, var/direct)
+ update_shield_positions()
+
+
+// Subtypes
+
+/obj/item/shield_projector/rectangle
+ name = "rectangular combat shield projector"
+ description_info = "This creates a shield in a rectangular shape, which allows projectiles to leave from inside but blocks projectiles from outside. \
+ Everything else can pass through the shield freely, including other people and thrown objects. The shield also cannot block certain effects which \
+ take place over an area, such as flashbangs or explosions."
+ var/size_x = 3 // How big the rectangle will be, in tiles from the center.
+ var/size_y = 3 // Ditto.
+
+// Horrible implementation below.
+/obj/item/shield_projector/rectangle/create_shields()
+ if(!..())
+ return FALSE
+
+ // Make a rectangle in a really terrible way.
+ var/x_dist = size_x
+ var/y_dist = size_y
+
+ var/turf/T = get_turf(src)
+ if(!T)
+ return FALSE
+ // Top left corner.
+ var/turf/T1 = locate(T.x - x_dist, T.y + y_dist, T.z)
+ // Bottom right corner.
+ var/turf/T2 = locate(T.x + x_dist, T.y - y_dist, T.z)
+ if(!T1 || !T2) // If we're on the edge of the map then don't bother.
+ return FALSE
+
+ // Build half of the corners first, as they are 'anchors' for the rest of the code below.
+ create_shield(T1, NORTHWEST)
+ create_shield(T2, SOUTHEAST)
+
+ // Build the edges.
+ // First start with the north side.
+ var/current_x = T1.x + 1 // Start next to the top left corner.
+ var/current_y = T1.y
+ var/length = (x_dist * 2) - 1
+ for(var/i = 1 to length)
+ create_shield(locate(current_x, current_y, T.z), NORTH)
+ current_x++
+
+ // Make the top right corner.
+ create_shield(locate(current_x, current_y, T.z), NORTHEAST)
+
+ // Now for the west edge.
+ current_x = T1.x
+ current_y = T1.y - 1
+ length = (y_dist * 2) - 1
+ for(var/i = 1 to length)
+ create_shield(locate(current_x, current_y, T.z), WEST)
+ current_y--
+
+ // Make the bottom left corner.
+ create_shield(locate(current_x, current_y, T.z), SOUTHWEST)
+
+ // Switch to the second corner, and make the east edge.
+ current_x = T2.x
+ current_y = T2.y + 1
+ length = (y_dist * 2) - 1
+ for(var/i = 1 to length)
+ create_shield(locate(current_x, current_y, T.z), EAST)
+ current_y++
+
+ // There are no more corners to create, so we can just go build the south edge now.
+ current_x = T2.x - 1
+ current_y = T2.y
+ length = (x_dist * 2) - 1
+ for(var/i = 1 to length)
+ create_shield(locate(current_x, current_y, T.z), SOUTH)
+ current_x--
+ // Finally done.
+ return TRUE
+
+/obj/item/shield_projector/line
+ name = "linear combat shield projector"
+ description_info = "This creates a shield in a straight line perpendicular to the direction where the user was facing when it was activated. \
+ The shield allows projectiles to leave from inside but blocks projectiles from outside. Everything else can pass through the shield freely, \
+ including other people and thrown objects. The shield also cannot block certain effects which take place over an area, such as flashbangs or explosions."
+ var/line_length = 5 // How long the line is. Recommended to be an odd number.
+ var/offset_from_center = 2 // How far from the projector will the line's center be.
+
+/obj/item/shield_projector/line/create_shields()
+ if(!..())
+ return FALSE
+
+ var/turf/T = get_turf(src) // This is another 'anchor', or will be once we move away from the projector.
+ for(var/i = 1 to offset_from_center)
+ T = get_step(T, dir)
+ if(!T) // We went off the map or something.
+ return
+ // We're at the right spot now. Build the center piece.
+ create_shield(T, dir)
+
+ var/length_to_build = round( (line_length - 1) / 2)
+ var/turf/temp_T = T
+
+ // First loop, we build the left (from a north perspective) side of the line.
+ for(var/i = 1 to length_to_build)
+ temp_T = get_step(temp_T, turn(dir, 90) )
+ if(!temp_T)
+ break
+ create_shield(temp_T, i == length_to_build ? turn(dir, 45) : dir)
+
+ temp_T = T
+
+ // Second loop, we build the right side.
+ for(var/i = 1 to length_to_build)
+ temp_T = get_step(temp_T, turn(dir, -90) )
+ if(!temp_T)
+ break
+ create_shield(temp_T, i == length_to_build ? turn(dir, -45) : dir)
+ // Finished.
+ return TRUE
\ No newline at end of file
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 4e08b08c8d..ec17008533 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index db0bb55a1f..8acc10a4fb 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 3cec695f70..34e3935791 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -2080,6 +2080,7 @@
#include "code\modules\scripting\Scanner\Tokens.dm"
#include "code\modules\security levels\keycard authentication.dm"
#include "code\modules\security levels\security levels.dm"
+#include "code\modules\shieldgen\directional_shield.dm"
#include "code\modules\shieldgen\emergency_shield.dm"
#include "code\modules\shieldgen\energy_field.dm"
#include "code\modules\shieldgen\handheld_defuser.dm"