diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index d8dd3bb44c8..b067e6f0812 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1153,3 +1153,10 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
if(degrees)
appearance_flags |= PIXEL_SCALE
transform = M
+
+///Setter for the `density` variable to append behavior related to its changing.
+/atom/proc/set_density(new_value)
+ if(density == new_value)
+ return
+ . = density
+ density = new_value
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index c736026bca8..48a60768b28 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -1,6 +1,17 @@
GLOBAL_LIST_INIT(rod_recipes, list ( \
- new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \
- new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \
+ null,
+ new /datum/stack_recipe("railing", /obj/structure/railing, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ new /datum/stack_recipe("railing corner", /obj/structure/railing/corner, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ null,
+ new /datum/stack_recipe_list("chainlink fence", list( \
+ new/datum/stack_recipe("chainlink fence", /obj/structure/fence, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("chainlink fence post", /obj/structure/fence/post, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("chainlink fence corner", /obj/structure/fence/corner, 5, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("chainlink fence door", /obj/structure/fence/door, 10, time = 10, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("chainlink fence end", /obj/structure/fence/end, 3, time = 10, one_per_turf = 1, on_floor = 1), \
+ )), \
))
/obj/item/stack/rods
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
new file mode 100644
index 00000000000..361cef688d6
--- /dev/null
+++ b/code/game/objects/structures/fence.dm
@@ -0,0 +1,168 @@
+//Chain link fences
+//Sprites ported from /VG/
+
+
+#define CUT_TIME 100
+#define CLIMB_TIME 150
+
+#define NO_HOLE 0 //section is intact
+#define MEDIUM_HOLE 1 //medium hole in the section - can climb through
+#define LARGE_HOLE 2 //large hole in the section - can walk through
+#define MAX_HOLE_SIZE LARGE_HOLE
+#define HOLE_REPAIR (hole_size * 2) //How many rods to fix these sections
+
+/obj/structure/fence
+ name = "fence"
+ desc = "A chain link fence. Not as effective as a wall, but generally it keeps people out."
+ density = TRUE
+ anchored = TRUE
+
+ icon = 'icons/obj/fence.dmi'
+ icon_state = "straight"
+
+ var/cuttable = TRUE
+ var/hole_size= NO_HOLE
+ var/invulnerable = FALSE
+
+/obj/structure/fence/Initialize()
+ . = ..()
+
+ update_cut_status()
+
+/obj/structure/fence/examine(mob/user)
+ . = ..()
+
+ switch(hole_size)
+ if(MEDIUM_HOLE)
+ . += "There is a large hole in \the [src]."
+ if(LARGE_HOLE)
+ . += "\The [src] has been completely cut through."
+
+/obj/structure/fence/end
+ icon_state = "end"
+ cuttable = FALSE
+
+/obj/structure/fence/corner
+ icon_state = "corner"
+ cuttable = FALSE
+
+/obj/structure/fence/post
+ icon_state = "post"
+ cuttable = FALSE
+
+/obj/structure/fence/cut/medium
+ icon_state = "straight_cut2"
+ hole_size = MEDIUM_HOLE
+
+/obj/structure/fence/cut/large
+ icon_state = "straight_cut3"
+ hole_size = LARGE_HOLE
+
+
+/obj/structure/fence/wirecutter_act(mob/living/user, obj/item/W)
+ if(!cuttable)
+ to_chat(user, "This section of the fence can't be cut!")
+ return
+ if(invulnerable)
+ to_chat(user, "This fence is too strong to cut through!")
+ return
+ var/current_stage = hole_size
+ if(current_stage >= MAX_HOLE_SIZE)
+ to_chat(user, "This fence has too much cut out of it already!")
+ return
+
+ user.visible_message("\The [user] starts cutting through \the [src] with \the [W].",\
+ "You start cutting through \the [src] with \the [W].")
+ if(W.use_tool(src, user, CUT_TIME * W.toolspeed, volume = W.tool_volume))
+ if(current_stage == hole_size)
+ switch(++hole_size)
+ if(MEDIUM_HOLE)
+ visible_message("\The [user] cuts into \the [src] some more.")
+ to_chat(user, "You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.")
+ climbable = TRUE
+ if(LARGE_HOLE)
+ visible_message("\The [user] completely cuts through \the [src].")
+ to_chat(user, "The hole in \the [src] is now big enough to walk through.")
+ climbable = FALSE
+ update_cut_status()
+ return
+
+
+/obj/structure/fence/attackby(obj/item/C, mob/user)
+ . = ..()
+ if(istype(C, /obj/item/stack/rods))
+ if(hole_size == NO_HOLE)
+ return
+ var/obj/item/stack/rods/R = C
+ if(R.get_amount() < HOLE_REPAIR)
+ to_chat(user, "You need [HOLE_REPAIR] rods to fix this fence!")
+ return TRUE
+ else
+ to_chat(user, "You begin repairing the fence...")
+ if(do_after(user, 30 * C.toolspeed, target = src))
+ if(R.get_amount() >= HOLE_REPAIR && !(hole_size == NO_HOLE))
+ hole_size = NO_HOLE
+ playsound(src, C.usesound, 80, 1)
+ R.use(2)
+ to_chat(user, "You repair the fence.")
+ update_cut_status()
+ return TRUE
+
+/obj/structure/fence/proc/update_cut_status()
+ if(!cuttable)
+ return
+ var/new_density = TRUE
+ switch(hole_size)
+ if(NO_HOLE)
+ icon_state = initial(icon_state)
+ if(MEDIUM_HOLE)
+ icon_state = "straight_cut2"
+ if(LARGE_HOLE)
+ icon_state = "straight_cut3"
+ new_density = FALSE
+ set_density(new_density)
+
+//FENCE DOORS
+
+/obj/structure/fence/door
+ name = "fence door"
+ desc = "Not very useful without a real lock."
+ icon_state = "door_closed"
+ cuttable = FALSE
+ var/open = FALSE
+
+/obj/structure/fence/door/Initialize()
+ . = ..()
+ update_door_status()
+
+/obj/structure/fence/door/opened
+ icon_state = "door_opened"
+ open = TRUE
+ density = TRUE
+
+/obj/structure/fence/door/attack_hand(mob/user, list/modifiers)
+ if(can_open(user))
+ toggle(user)
+
+ return TRUE
+
+/obj/structure/fence/door/proc/toggle(mob/user)
+ open = !open
+ visible_message("\The [user] [open ? "opens" : "closes"] \the [src].")
+ update_door_status()
+ playsound(src, 'sound/machines/door_open.ogg', 100, TRUE)
+
+/obj/structure/fence/door/proc/update_door_status()
+ set_density(!density)
+ icon_state = density ? "door_closed" : "door_opened"
+
+/obj/structure/fence/door/proc/can_open(mob/user)
+ return TRUE
+
+#undef CUT_TIME
+#undef CLIMB_TIME
+
+#undef NO_HOLE
+#undef MEDIUM_HOLE
+#undef LARGE_HOLE
+#undef MAX_HOLE_SIZE
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
new file mode 100644
index 00000000000..ecfa334b337
--- /dev/null
+++ b/code/game/objects/structures/railings.dm
@@ -0,0 +1,124 @@
+/obj/structure/railing
+ name = "railing"
+ desc = "Basic railing meant to protect idiots like you from falling."
+ icon = 'icons/obj/fence.dmi'
+ icon_state = "railing"
+ density = TRUE
+ anchored = TRUE
+ climbable = TRUE
+ var/currently_climbed = FALSE
+
+/obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch
+ icon_state = "railing_corner"
+ density = FALSE
+ climbable = FALSE
+
+/obj/structure/railing/Initialize()
+ . = ..()
+
+/obj/structure/railing/attackby(obj/item/I, mob/living/user, params)
+ ..()
+ add_fingerprint(user)
+
+/obj/structure/railing/welder_act(mob/living/user, obj/item/I)
+ if(user.intent == INTENT_HELP)
+ if(obj_integrity < max_integrity)
+ if(!I.tool_start_check(user, amount = 0))
+ return
+ to_chat(user, "You begin repairing [src]...")
+ if(I.use_tool(src, user, 40, volume = 50))
+ obj_integrity = max_integrity
+ to_chat(user, "You repair [src].")
+ return
+
+ to_chat(user, "[src] is already in good condition!")
+ return
+
+/obj/structure/railing/wirecutter_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(!anchored)
+ to_chat(user, "You cut apart the railing.")
+ I.play_tool_sound(src, 100)
+ deconstruct()
+ return TRUE
+
+/obj/structure/railing/deconstruct(disassembled)
+ if(!(flags & NODECONSTRUCT))
+ var/obj/item/stack/rods/rod = new /obj/item/stack/rods(drop_location(), 3)
+ transfer_fingerprints_to(rod)
+ return ..()
+
+///Implements behaviour that makes it possible to unanchor the railing.
+/obj/structure/railing/wrench_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(flags & NODECONSTRUCT)
+ return
+ to_chat(user, "You begin to [anchored ? "unfasten the railing from":"fasten the railing to"] the floor...")
+ if(I.use_tool(src, user, volume = 75, extra_checks = CALLBACK(src, .proc/check_anchored, anchored)))
+ if(!anchored)
+ anchored = TRUE
+ else
+ anchored = FALSE
+ to_chat(user, "You [anchored ? "fasten the railing to":"unfasten the railing from"] the floor.")
+ return TRUE
+
+/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
+ ..()
+ var/mob/living/M = mover
+ if(M.flying || M.floating || mover.throwing)
+ return TRUE
+ if(get_dir(loc, target) == dir)
+ return !density
+ return TRUE
+
+/obj/structure/railing/corner/CanPass()
+ ..()
+ return TRUE
+
+/obj/structure/railing/CheckExit(atom/movable/O, target)
+ var/mob/living/M = O
+ if(M.flying | M.floating)
+ return TRUE
+ if(O.throwing)
+ return TRUE
+ if(O.move_force >= MOVE_FORCE_EXTREMELY_STRONG)
+ return TRUE
+ if(currently_climbed)
+ if(get_turf(M) == get_turf(src))
+ var/turf/T = target
+ if(T.density)
+ return TRUE
+ for(var/atom/A in T)
+ if(A.density)
+ return FALSE
+ currently_climbed = FALSE
+ return TRUE
+ if(get_dir(O.loc, target) == dir)
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/do_climb(mob/living/user)
+ . = ..()
+ if(!climbable)
+ return
+ if(get_turf(user) == get_turf(src))
+ currently_climbed = TRUE
+
+/obj/structure/railing/proc/can_be_rotated(mob/user, rotation_type)
+ if(anchored)
+ to_chat(user, "[src] cannot be rotated while it is fastened to the floor!")
+ return FALSE
+
+ var/target_dir = turn(dir, -90)
+
+ if(!valid_window_location(loc, target_dir)) //Expanded to include rails, as well!
+ to_chat(user, "[src] cannot be rotated in that direction!")
+ return FALSE
+ return TRUE
+
+/obj/structure/railing/proc/check_anchored(checked_anchored)
+ if(anchored == checked_anchored)
+ return TRUE
+
+/obj/structure/railing/proc/after_rotation(mob/user,rotation_type)
+ add_fingerprint(user)
diff --git a/icons/obj/fence.dmi b/icons/obj/fence.dmi
new file mode 100644
index 00000000000..9debf926053
Binary files /dev/null and b/icons/obj/fence.dmi differ
diff --git a/paradise.dme b/paradise.dme
index cfe41674e94..7c5da49fd52 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1059,6 +1059,7 @@
#include "code\game\objects\structures\engicart.dm"
#include "code\game\objects\structures\extinguisher.dm"
#include "code\game\objects\structures\false_walls.dm"
+#include "code\game\objects\structures\fence.dm"
#include "code\game\objects\structures\flora.dm"
#include "code\game\objects\structures\fluff.dm"
#include "code\game\objects\structures\foodcart.dm"
@@ -1079,6 +1080,7 @@
#include "code\game\objects\structures\morgue.dm"
#include "code\game\objects\structures\noticeboard.dm"
#include "code\game\objects\structures\plasticflaps.dm"
+#include "code\game\objects\structures\railings.dm"
#include "code\game\objects\structures\reflector.dm"
#include "code\game\objects\structures\safe.dm"
#include "code\game\objects\structures\signs.dm"