diff --git a/code/datums/observation/turf_changed.dm b/code/datums/observation/turf_changed.dm
new file mode 100644
index 0000000000..861b409227
--- /dev/null
+++ b/code/datums/observation/turf_changed.dm
@@ -0,0 +1,28 @@
+// Observer Pattern Implementation: Turf Changed
+// Registration type: /turf
+//
+// Raised when: A turf has been changed using the ChangeTurf proc.
+//
+// Arguments that the called proc should expect:
+// /turf/affected: The turf that has changed
+// /old_density: Density before the change
+// /new_density: Density after the change
+// /old_opacity: Opacity before the change
+// /new_opacity: Opacity after the change
+
+var/decl/observ/turf_changed/turf_changed_event = new()
+
+/decl/observ/turf_changed
+ name = "Turf Changed"
+ expected_type = /turf
+
+/************************
+* Turf Changed Handling *
+************************/
+
+/turf/ChangeTurf()
+ var/old_density = density
+ var/old_opacity = opacity
+ . = ..()
+ if(.)
+ turf_changed_event.raise_event(src, old_density, density, old_opacity, opacity)
\ No newline at end of file
diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm
index a5625e9253..2b4eae9a0d 100644
--- a/code/game/machinery/CableLayer.dm
+++ b/code/game/machinery/CableLayer.dm
@@ -73,7 +73,7 @@
visible_message("A red light flashes on \the [src].")
return
cable.use(amount)
- if(deleted(cable))
+ if(deleted(cable))
cable = null
return 1
@@ -104,13 +104,13 @@
NC.cableColor("red")
NC.d1 = 0
NC.d2 = fdirn
- NC.updateicon()
+ NC.update_icon()
var/datum/powernet/PN
if(last_piece && last_piece.d2 != M_Dir)
last_piece.d1 = min(last_piece.d2, M_Dir)
last_piece.d2 = max(last_piece.d2, M_Dir)
- last_piece.updateicon()
+ last_piece.update_icon()
PN = last_piece.powernet
if(!PN)
diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm
index fdf4a16b00..ffe30bf3fd 100644
--- a/code/game/mecha/equipment/tools/medical_tools.dm
+++ b/code/game/mecha/equipment/tools/medical_tools.dm
@@ -348,13 +348,13 @@
NC.cableColor("red")
NC.d1 = 0
NC.d2 = fdirn
- NC.updateicon()
+ NC.update_icon()
var/datum/powernet/PN
if(last_piece && last_piece.d2 != chassis.dir)
last_piece.d1 = min(last_piece.d2, chassis.dir)
last_piece.d2 = max(last_piece.d2, chassis.dir)
- last_piece.updateicon()
+ last_piece.update_icon()
PN = last_piece.powernet
if(!PN)
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index d7e530b761..610746339b 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -333,7 +333,7 @@ steam.start() -- spawns the effect
spawn(0)
var/turf/T = get_turf(src.holder)
if(T != src.oldposition)
- if(istype(T, /turf/space))
+ if(istype(T, /turf/simulated))
var/obj/effect/effect/ion_trails/I = PoolOrNew(/obj/effect/effect/ion_trails, src.oldposition)
src.oldposition = T
I.set_dir(src.holder.dir)
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 5d01438614..0eb48e94e0 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -34,6 +34,11 @@
if(locate(/obj/structure/lattice, get_step(src, dir)))
L = locate(/obj/structure/lattice, get_step(src, dir))
L.updateOverlays(src.loc)
+ if(istype(loc, /turf/simulated/open))
+ var/turf/simulated/open/O = loc
+ spawn(1)
+ if(O) // If we built a new floor with the lattice, the open turf won't exist anymore.
+ O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down.
..()
/obj/structure/lattice/ex_act(severity)
diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm
index 9b9638d908..7379d2c80b 100644
--- a/code/game/turfs/turf_changing.dm
+++ b/code/game/turfs/turf_changing.dm
@@ -9,6 +9,13 @@
if(L)
qdel(L)
+// Called after turf replaces old one
+/turf/proc/post_change()
+ levelupdate()
+ var/turf/simulated/open/T = GetAbove(src)
+ if(istype(T))
+ T.update_icon()
+
//Creates a new turf
/turf/proc/ChangeTurf(var/turf/N, var/tell_universe=1, var/force_lighting_update = 0)
if (!N)
@@ -60,6 +67,7 @@
W.levelupdate()
W.update_icon(1)
+ W.post_change()
. = W
else
@@ -83,6 +91,7 @@
W.levelupdate()
W.update_icon(1)
+ W.post_change()
. = W
lighting_overlay = old_lighting_overlay
diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm
index 5a211d960b..9239f6385f 100644
--- a/code/modules/admin/verbs/buildmode.dm
+++ b/code/modules/admin/verbs/buildmode.dm
@@ -392,11 +392,12 @@
if(holder.buildmode.coordA && holder.buildmode.coordB)
user << "Ladder locations set, building ladders."
- var/obj/structure/ladder/A = new /obj/structure/ladder(holder.buildmode.coordA)
+ var/obj/structure/ladder/A = new /obj/structure/ladder/up(holder.buildmode.coordA)
var/obj/structure/ladder/B = new /obj/structure/ladder(holder.buildmode.coordB)
- A.target = B
- B.target = A
- B.icon_state = "ladderup"
+ A.target_up = B
+ B.target_down = A
+ A.update_icon()
+ B.update_icon()
holder.buildmode.coordA = null
holder.buildmode.coordB = null
if(7) // Move into contents
diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm
index dd0a93fec1..586b624260 100644
--- a/code/modules/multiz/movement.dm
+++ b/code/modules/multiz/movement.dm
@@ -1,51 +1,194 @@
-/obj/item/weapon/tank/jetpack/verb/moveup()
+/mob/verb/up()
set name = "Move Upwards"
- set category = "Object"
+ set category = "IC"
- . = 1
- if(!allow_thrust(0.01, usr))
- usr << "\The [src] is disabled."
+ if(zMove(UP))
+ to_chat(usr, "You move upwards.")
+
+/mob/verb/down()
+ set name = "Move Down"
+ set category = "IC"
+
+ if(zMove(DOWN))
+ to_chat(usr, "You move down.")
+
+/mob/proc/zMove(direction)
+ if(eyeobj)
+ return eyeobj.zMove(direction)
+ if(!can_ztravel())
+ to_chat(usr, "You lack means of travel in that direction.")
return
- var/turf/above = GetAbove(src)
- if(!istype(above))
- usr << "There is nothing of interest in this direction."
- return
+ var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
- if(!istype(above, /turf/space) && !istype(above, /turf/simulated/open))
- usr << "You bump against \the [above]."
- return
+ if(!destination)
+ to_chat(usr, "There is nothing of interest in this direction.")
+ return 0
- for(var/atom/A in above)
- if(A.density)
- usr << "\The [A] blocks you."
- return
+ var/turf/start = get_turf(src)
+ if(!start.CanZPass(src, direction))
+ to_chat(usr, "\The [start] is in the way.")
+ return 0
+ if(!destination.CanZPass(src, direction))
+ to_chat(usr, "\The [destination] blocks your way.")
+ return 0
- usr.Move(above)
- usr << "You move upwards."
+ var/area/area = get_area(src)
+ if(direction == UP && area.has_gravity)
+ var/obj/structure/lattice/lattice = locate() in destination.contents
+ if(lattice)
+ var/pull_up_time = max(5 SECONDS + (src.movement_delay() * 10), 1)
+ to_chat(src, "You grab \the [lattice] and start pulling yourself upward...")
+ destination.audible_message("You hear something climbing up \the [lattice].")
+ if(do_after(src, pull_up_time))
+ to_chat(src, "You pull yourself up.")
+ else
+ to_chat(src, "You gave up on pulling yourself up.")
+ return 0
+ else
+ to_chat(usr, "Gravity stops you from moving upward.")
+ return 0
-/obj/item/weapon/tank/jetpack/verb/movedown()
- set name = "Move Downwards"
- set category = "Object"
+ for(var/atom/A in destination)
+ if(!A.CanPass(src, start, 1.5, 0))
+ to_chat(usr, "\The [A] blocks you.")
+ return 0
+ Move(destination)
+ return 1
- . = 1
- if(!allow_thrust(0.01, usr))
- usr << "\The [src] is disabled."
+/mob/observer/zMove(direction)
+ var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
+ if(destination)
+ forceMove(destination)
+ else
+ to_chat(usr, "There is nothing of interest in this direction.")
+
+/mob/observer/eye/zMove(direction)
+ var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src)
+ if(destination)
+ setLoc(destination)
+ else
+ to_chat(usr, "There is nothing of interest in this direction.")
+
+/mob/proc/can_ztravel()
+ return 0
+
+/mob/observer/can_ztravel()
+ return 1
+
+/mob/living/carbon/human/can_ztravel()
+ if(incapacitated())
+ return 0
+
+ if(Process_Spacemove())
+ return 1
+
+ if(Check_Shoegrip()) //scaling hull with magboots
+ for(var/turf/simulated/T in trange(1,src))
+ if(T.density)
+ return 1
+
+/mob/living/silicon/robot/can_ztravel()
+ if(incapacitated() || is_dead())
+ return 0
+
+ if(Process_Spacemove()) //Checks for active jetpack
+ return 1
+
+ for(var/turf/simulated/T in trange(1,src)) //Robots get "magboots"
+ if(T.density)
+ return 1
+
+
+
+
+//FALLING STUFF
+
+//Holds fall checks that should not be overriden by children
+/atom/movable/proc/fall()
+ if(!isturf(loc))
return
var/turf/below = GetBelow(src)
- if(!istype(below))
- usr << "There is nothing of interest in this direction."
+ if(!below)
return
- if(below.density)
- usr << "You bump against \the [below]."
+ var/turf/T = loc
+ if(!T.CanZPass(src, DOWN) || !below.CanZPass(src, DOWN))
return
+ // No gravity in space, apparently.
+ var/area/area = get_area(src)
+ if(!area.has_gravity())
+ return
+
+ if(throwing)
+ return
+
+ if(can_fall())
+ handle_fall(below)
+
+//For children to override
+/atom/movable/proc/can_fall()
+ if(anchored)
+ return FALSE
+
+ if(locate(/obj/structure/lattice, loc))
+ return FALSE
+
+ // See if something prevents us from falling.
+ var/turf/below = GetBelow(src)
for(var/atom/A in below)
- if(A.density)
- usr << "\The [A] blocks you."
- return
+ if(!A.CanPass(src, src.loc))
+ return FALSE
- usr.Move(below)
- usr << "You move downwards."
+ return TRUE
+
+/obj/effect/can_fall()
+ return FALSE
+
+/obj/effect/decal/cleanable/can_fall()
+ return TRUE
+
+/obj/item/pipe/can_fall()
+ var/turf/simulated/open/below = loc
+ below = below.below
+
+ . = ..()
+
+ if(anchored)
+ return FALSE
+
+ if((locate(/obj/structure/disposalpipe/up) in below) || locate(/obj/machinery/atmospherics/pipe/zpipe/up in below))
+ return FALSE
+
+/mob/living/simple_animal/parrot/can_fall() // Poly can fly.
+ return FALSE
+
+/mob/living/simple_animal/hostile/carp/can_fall() // So can carp apparently.
+ return FALSE
+
+/atom/movable/proc/handle_fall(var/turf/landing)
+ Move(landing)
+ if(locate(/obj/structure/stairs) in landing)
+ return 1
+
+ if(istype(landing, /turf/simulated/open))
+ visible_message("\The [src] falls from the deck above through \the [landing]!", "You hear a whoosh of displaced air.")
+ else
+ visible_message("\The [src] falls from the deck above and slams into \the [landing]!", "You hear something slam into the deck.")
+
+/mob/living/carbon/human/handle_fall(var/turf/landing)
+ if(..())
+ return
+ to_chat(src, "You fall off and hit \the [landing]!")
+ playsound(loc, "punch", 25, 1, -1)
+ var/damage = 20 // Because wounds heal rather quickly, 20 should be enough to discourage jumping off but not be enough to ruin you, at least for the first time.
+ apply_damage(rand(0, damage), BRUTE, BP_HEAD)
+ apply_damage(rand(0, damage), BRUTE, BP_TORSO)
+ apply_damage(rand(0, damage), BRUTE, BP_L_LEG)
+ apply_damage(rand(0, damage), BRUTE, BP_R_LEG)
+ apply_damage(rand(0, damage), BRUTE, BP_L_ARM)
+ apply_damage(rand(0, damage), BRUTE, BP_R_ARM)
+ Weaken(4)
+ updatehealth()
\ No newline at end of file
diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm
index 36a606bfcf..5727808276 100644
--- a/code/modules/multiz/structures.dm
+++ b/code/modules/multiz/structures.dm
@@ -4,54 +4,127 @@
/obj/structure/ladder
name = "ladder"
- desc = "A ladder. You can climb it up and down."
- icon_state = "ladderdown"
+ desc = "A ladder. You can climb it up and down."
+ icon_state = "ladder01"
icon = 'icons/obj/structures.dmi'
density = 0
opacity = 0
anchored = 1
- var/obj/structure/ladder/target
+ var/allowed_directions = DOWN
+ var/obj/structure/ladder/target_up
+ var/obj/structure/ladder/target_down
- initialize()
- // the upper will connect to the lower
- if(icon_state == "ladderup")
- return
+ var/const/climb_time = 2 SECONDS
+/obj/structure/ladder/initialize()
+ // the upper will connect to the lower
+ if(allowed_directions & DOWN) //we only want to do the top one, as it will initialize the ones before it.
for(var/obj/structure/ladder/L in GetBelow(src))
- if(L.icon_state == "ladderup")
- target = L
- L.target = src
+ if(L.allowed_directions & UP)
+ target_down = L
+ L.target_up = src
return
+ update_icon()
- Destroy()
- if(target && icon_state == "ladderdown")
- qdel(target)
- return ..()
+/obj/structure/ladder/Destroy()
+ if(target_down)
+ target_down.target_up = null
+ target_down = null
+ if(target_up)
+ target_up.target_down = null
+ target_up = null
+ return ..()
- attackby(obj/item/C as obj, mob/user as mob)
- . = ..()
- attack_hand(user)
+/obj/structure/ladder/attackby(obj/item/C as obj, mob/user as mob)
+ attack_hand(user)
+ return
+
+/obj/structure/ladder/attack_hand(var/mob/M)
+ if(!M.may_climb_ladders(src))
return
- attack_hand(var/mob/M)
- if(!target || !istype(target.loc, /turf))
- M << "\The [src] is incomplete and can't be climbed."
+ var/obj/structure/ladder/target_ladder = getTargetLadder(M)
+ if(!target_ladder)
+ return
+ if(!M.Move(get_turf(src)))
+ to_chat(M, "You fail to reach \the [src].")
+ return
+
+ var/direction = target_ladder == target_up ? "up" : "down"
+
+ M.visible_message("\The [M] begins climbing [direction] \the [src]!",
+ "You begin climbing [direction] \the [src]!",
+ "You hear the grunting and clanging of a metal ladder being used.")
+
+ target_ladder.audible_message("You hear something coming [direction] \the [src]")
+
+ if(do_after(M, climb_time, src))
+ climbLadder(M, target_ladder)
+
+/obj/structure/ladder/attack_ghost(var/mob/M)
+ var/target_ladder = getTargetLadder(M)
+ if(target_ladder)
+ M.forceMove(get_turf(target_ladder))
+
+/obj/structure/ladder/proc/getTargetLadder(var/mob/M)
+ if((!target_up && !target_down) || (target_up && !istype(target_up.loc, /turf) || (target_down && !istype(target_down.loc,/turf))))
+ to_chat(M, "\The [src] is incomplete and can't be climbed.")
+ return
+ if(target_down && target_up)
+ var/direction = alert(M,"Do you want to go up or down?", "Ladder", "Up", "Down", "Cancel")
+
+ if(direction == "Cancel")
return
- var/turf/T = target.loc
- for(var/atom/A in T)
- if(A.density)
- M << "\A [A] is blocking \the [src]."
- return
+ if(!M.may_climb_ladders(src))
+ return
+
+ switch(direction)
+ if("Up")
+ return target_up
+ if("Down")
+ return target_down
+ else
+ return target_down || target_up
+
+/mob/proc/may_climb_ladders(var/ladder)
+ if(!Adjacent(ladder))
+ to_chat(src, "You need to be next to \the [ladder] to start climbing.")
+ return FALSE
+ if(incapacitated())
+ to_chat(src, "You are physically unable to climb \the [ladder].")
+ return FALSE
+ return TRUE
+
+/mob/observer/ghost/may_climb_ladders(var/ladder)
+ return TRUE
+
+/obj/structure/ladder/proc/climbLadder(var/mob/M, var/target_ladder)
+ var/turf/T = get_turf(target_ladder)
+ for(var/atom/A in T)
+ if(!A.CanPass(M, M.loc, 1.5, 0))
+ to_chat(M, "\The [A] is blocking \the [src].")
+ return FALSE
+ return M.Move(T)
+
+/obj/structure/ladder/CanPass(obj/mover, turf/source, height, airflow)
+ return airflow || !density
+
+/obj/structure/ladder/update_icon()
+ icon_state = "ladder[!!(allowed_directions & UP)][!!(allowed_directions & DOWN)]"
+
+/obj/structure/ladder/up
+ allowed_directions = UP
+ icon_state = "ladder10"
+
+/obj/structure/ladder/updown
+ allowed_directions = UP|DOWN
+ icon_state = "ladder11"
+
+
- M.visible_message("\A [M] climbs [icon_state == "ladderup" ? "up" : "down"] \a [src]!",
- "You climb [icon_state == "ladderup" ? "up" : "down"] \the [src]!",
- "You hear the grunting and clanging of a metal ladder being used.")
- M.Move(T)
- CanPass(obj/mover, turf/source, height, airflow)
- return airflow || !density
/obj/structure/stairs
name = "Stairs"
@@ -61,46 +134,50 @@
opacity = 0
anchored = 1
- initialize()
- for(var/turf/turf in locs)
- var/turf/simulated/open/above = GetAbove(turf)
- if(!above)
- warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
- return qdel(src)
- if(!istype(above))
- above.ChangeTurf(/turf/simulated/open)
+/obj/structure/stairs/initialize()
+ for(var/turf/turf in locs)
+ var/turf/simulated/open/above = GetAbove(turf)
+ if(!above)
+ warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])")
+ return qdel(src)
+ if(!istype(above))
+ above.ChangeTurf(/turf/simulated/open)
- Uncross(atom/movable/A)
- if(A.dir == dir)
- // This is hackish but whatever.
- var/turf/target = get_step(GetAbove(A), dir)
- var/turf/source = A.loc
- if(target.Enter(A, source))
- A.loc = target
- target.Entered(A, source)
- return 0
- return 1
+/obj/structure/stairs/Uncross(atom/movable/A)
+ if(A.dir == dir)
+ // This is hackish but whatever.
+ var/turf/target = get_step(GetAbove(A), dir)
+ var/turf/source = A.loc
+ if(target.Enter(A, source))
+ A.loc = target
+ target.Entered(A, source)
+ if(isliving(A))
+ var/mob/living/L = A
+ if(L.pulling)
+ L.pulling.forceMove(target)
+ return 0
+ return 1
- CanPass(obj/mover, turf/source, height, airflow)
- return airflow || !density
+/obj/structure/stairs/CanPass(obj/mover, turf/source, height, airflow)
+ return airflow || !density
- // type paths to make mapping easier.
- north
- dir = NORTH
- bound_height = 64
- bound_y = -32
- pixel_y = -32
+// type paths to make mapping easier.
+/obj/structure/stairs/north
+ dir = NORTH
+ bound_height = 64
+ bound_y = -32
+ pixel_y = -32
- south
- dir = SOUTH
- bound_height = 64
+/obj/structure/stairs/south
+ dir = SOUTH
+ bound_height = 64
- east
- dir = EAST
- bound_width = 64
- bound_x = -32
- pixel_x = -32
+/obj/structure/stairs/east
+ dir = EAST
+ bound_width = 64
+ bound_x = -32
+ pixel_x = -32
- west
- dir = WEST
- bound_width = 64
\ No newline at end of file
+/obj/structure/stairs/west
+ dir = WEST
+ bound_width = 64
\ No newline at end of file
diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm
index d85a464af2..8389df7112 100644
--- a/code/modules/multiz/turf.dm
+++ b/code/modules/multiz/turf.dm
@@ -1,87 +1,79 @@
+/turf/proc/CanZPass(atom/A, direction)
+ if(z == A.z) //moving FROM this turf
+ return direction == UP //can't go below
+ else
+ if(direction == UP) //on a turf below, trying to enter
+ return 0
+ if(direction == DOWN) //on a turf above, trying to enter
+ return !density
+
+/turf/simulated/open/CanZPass(atom, direction)
+ return 1
+
+/turf/space/CanZPass(atom, direction)
+ return 1
+
/turf/simulated/open
name = "open space"
icon = 'icons/turf/space.dmi'
- icon_state = "black"
- alpha = 16
+ icon_state = ""
layer = 0
density = 0
pathweight = 100000 //Seriously, don't try and path over this one numbnuts
var/turf/below
- var/list/underlay_references
- var/global/overlay_map = list()
+
+/turf/simulated/open/post_change()
+ ..()
+ update()
/turf/simulated/open/initialize()
..()
- below = GetBelow(src)
ASSERT(HasBelow(z))
+ update()
/turf/simulated/open/Entered(var/atom/movable/mover)
- // only fall down in defined areas (read: areas with artificial gravitiy)
- if(!istype(below)) //make sure that there is actually something below
- below = GetBelow(src)
- if(!below)
- return
+ ..()
+ mover.fall()
- // No gravity in space, apparently.
- var/area/area = get_area(src)
- if(area.name == "Space")
- return
-
- // Prevent pipes from falling into the void... if there is a pipe to support it.
- if(mover.anchored || istype(mover, /obj/item/pipe) && \
- (locate(/obj/structure/disposalpipe/up) in below) || \
- locate(/obj/machinery/atmospherics/pipe/zpipe/up in below))
- return
-
- // See if something prevents us from falling.
- var/soft = 0
- for(var/atom/A in below)
- if(A.density)
- if(!istype(A, /obj/structure/window))
- return
- else
- var/obj/structure/window/W = A
- if(W.is_fulltile())
- return
- // Dont break here, since we still need to be sure that it isnt blocked
- if(istype(A, /obj/structure/stairs))
- soft = 1
-
- // We've made sure we can move, now.
- mover.Move(below)
-
- if(!soft)
- if(!istype(mover, /mob))
- if(istype(below, /turf/simulated/open))
- mover.visible_message("\The [mover] falls from the deck above through \the [below]!", "You hear a whoosh of displaced air.")
- else
- mover.visible_message("\The [mover] falls from the deck above and slams into \the [below]!", "You hear something slam into the deck.")
- else
- var/mob/M = mover
- if(istype(below, /turf/simulated/open))
- below.visible_message("\The [mover] falls from the deck above through \the [below]!", "You hear a soft whoosh.[M.stat ? "" : ".. and some screaming."]")
- else
- M.visible_message("\The [mover] falls from the deck above and slams into \the [below]!", "You land on \the [below].", "You hear a soft whoosh and a crunch")
-
- // Handle people getting hurt, it's funny!
- if (istype(mover, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = mover
- var/damage = 5
- H.apply_damage(rand(0, damage), BRUTE, BP_HEAD)
- H.apply_damage(rand(0, damage), BRUTE, BP_TORSO)
- H.apply_damage(rand(0, damage), BRUTE, BP_L_LEG)
- H.apply_damage(rand(0, damage), BRUTE, BP_R_LEG)
- H.apply_damage(rand(0, damage), BRUTE, BP_L_ARM)
- H.apply_damage(rand(0, damage), BRUTE, BP_R_ARM)
- H.weakened = max(H.weakened,2)
- H.updatehealth()
+/turf/simulated/open/proc/update()
+ below = GetBelow(src)
+ turf_changed_event.register(below, src, /turf/simulated/open/update_icon)
+ var/turf/simulated/T = get_step(src,NORTH)
+ if(T)
+ turf_changed_event.register(T, src, /turf/simulated/open/update_icon)
+ levelupdate()
+ for(var/atom/movable/A in src)
+ A.fall()
+ update_icon()
// override to make sure nothing is hidden
/turf/simulated/open/levelupdate()
for(var/obj/O in src)
O.hide(0)
+/turf/simulated/open/update_icon()
+ if(below)
+ underlays = list(image(icon = below.icon, icon_state = below.icon_state))
+ underlays += below.overlays.Copy()
+
+ var/list/noverlays = list()
+ if(!istype(below,/turf/space))
+ noverlays += image(icon =icon, icon_state = "empty", layer = 2.2)
+
+ var/turf/simulated/T = get_step(src,NORTH)
+ if(istype(T) && !istype(T,/turf/simulated/open))
+ noverlays += image(icon ='icons/turf/cliff.dmi', icon_state = "metal", layer = 2.2)
+
+ var/obj/structure/stairs/S = locate() in below
+ if(S && S.loc == below)
+ var/image/I = image(icon = S.icon, icon_state = "below", dir = S.dir, layer = 2.2)
+ I.pixel_x = S.pixel_x
+ I.pixel_y = S.pixel_y
+ noverlays += I
+
+ overlays = noverlays
+
// Straight copy from space.
/turf/simulated/open/attackby(obj/item/C as obj, mob/user as mob)
if (istype(C, /obj/item/stack/rods))
@@ -108,4 +100,14 @@
return
else
user << "The plating is going to need some support."
- return
\ No newline at end of file
+
+ //To lay cable.
+ if(istype(C, /obj/item/stack/cable_coil))
+ var/obj/item/stack/cable_coil/coil = C
+ coil.turf_place(src, user)
+ return
+ return
+
+//Most things use is_plating to test if there is a cover tile on top (like regular floors)
+/turf/simulated/open/is_plating()
+ return 1
\ No newline at end of file
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index c837f502dd..671d57e04e 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -129,12 +129,12 @@ var/list/possible_cable_coil_colours = list(
/obj/structure/cable/hide(var/i)
if(istype(loc, /turf))
invisibility = i ? 101 : 0
- updateicon()
+ update_icon()
/obj/structure/cable/hides_under_flooring()
return 1
-/obj/structure/cable/proc/updateicon()
+/obj/structure/cable/update_icon()
icon_state = "[d1]-[d2]"
alpha = invisibility ? 127 : 255
@@ -635,102 +635,75 @@ obj/structure/cable/proc/cableColor(var/colorC)
//////////////////////////////////////////////
// called when cable_coil is clicked on a turf/simulated/floor
-/obj/item/stack/cable_coil/proc/turf_place(turf/simulated/floor/F, mob/user)
+/obj/item/stack/cable_coil/proc/turf_place(turf/simulated/F, mob/user)
if(!isturf(user.loc))
return
if(get_amount() < 1) // Out of cable
- user << "There is no cable left."
+ to_chat(user, "There is no cable left.")
return
if(get_dist(F,user) > 1) // Too far
- user << "You can't lay cable at a place that far away."
+ to_chat(user, "You can't lay cable at a place that far away.")
return
if(!F.is_plating()) // Ff floor is intact, complain
- user << "You can't lay cable there unless the floor tiles are removed."
+ to_chat(user, "You can't lay cable there unless the floor tiles are removed.")
return
+ var/dirn
+ if(user.loc == F)
+ dirn = user.dir // if laying on the tile we're on, lay in the direction we're facing
else
- var/dirn
+ dirn = get_dir(F, user)
- if(user.loc == F)
- dirn = user.dir // if laying on the tile we're on, lay in the direction we're facing
- else
- dirn = get_dir(F, user)
+ var/end_dir = 0
+ if(istype(F, /turf/simulated/open))
+ if(!can_use(2))
+ to_chat(user, "You don't have enough cable to do this!")
+ return
+ end_dir = DOWN
- for(var/obj/structure/cable/LC in F)
- if((LC.d1 == dirn && LC.d2 == 0 ) || ( LC.d2 == dirn && LC.d1 == 0))
- user << "There's already a cable at that position."
- return
-///// Z-Level Stuff
- // check if the target is open space
- if(istype(F, /turf/simulated/open))
- for(var/obj/structure/cable/LC in F)
- if((LC.d1 == dirn && LC.d2 == 11 ) || ( LC.d2 == dirn && LC.d1 == 11))
- user << "There's already a cable at that position."
- return
+ for(var/obj/structure/cable/LC in F)
+ if((LC.d1 == dirn && LC.d2 == end_dir ) || ( LC.d2 == dirn && LC.d1 == end_dir))
+ to_chat(user, "There's already a cable at that position.")
+ return
- var/obj/structure/cable/C = new(F)
- var/obj/structure/cable/D = new(GetBelow(F))
+ put_cable(F, user, end_dir, dirn)
+ if(end_dir == DOWN)
+ put_cable(GetBelow(F), user, UP, 0)
+ to_chat(user, "You slide some cable downward.")
- C.cableColor(color)
+/obj/item/stack/cable_coil/proc/put_cable(turf/simulated/F, mob/user, d1, d2)
+ if(!istype(F))
+ return
- C.d1 = 11
- C.d2 = dirn
- C.add_fingerprint(user)
- C.updateicon()
+ var/obj/structure/cable/C = new(F)
+ C.cableColor(color)
+ C.d1 = d1
+ C.d2 = d2
+ C.add_fingerprint(user)
+ C.update_icon()
- var/datum/powernet/PN = new()
- PN.add_cable(C)
+ //create a new powernet with the cable, if needed it will be merged later
+ var/datum/powernet/PN = new()
+ PN.add_cable(C)
- C.mergeConnectedNetworks(C.d2)
- C.mergeConnectedNetworksOnTurf()
+ C.mergeConnectedNetworks(C.d1) //merge the powernets...
+ C.mergeConnectedNetworks(C.d2) //...in the two new cable directions
+ C.mergeConnectedNetworksOnTurf()
- D.cableColor(color)
+ if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions
+ C.mergeDiagonalsNetworks(C.d1)
- D.d1 = 12
- D.d2 = 0
- D.add_fingerprint(user)
- D.updateicon()
+ if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions
+ C.mergeDiagonalsNetworks(C.d2)
- PN.add_cable(D)
- D.mergeConnectedNetworksOnTurf()
-
- // do the normal stuff
- else
-///// Z-Level Stuff
- for(var/obj/structure/cable/LC in F)
- if((LC.d1 == dirn && LC.d2 == 0 ) || ( LC.d2 == dirn && LC.d1 == 0))
- user << "There's already a cable at that position."
- return
-
- var/obj/structure/cable/C = new(F)
-
- C.cableColor(color)
-
- //set up the new cable
- C.d1 = 0 //it's a O-X node cable
- C.d2 = dirn
- C.add_fingerprint(user)
- C.updateicon()
-
- //create a new powernet with the cable, if needed it will be merged later
- var/datum/powernet/PN = new()
- PN.add_cable(C)
-
- C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets
- C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets
-
- if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions
- C.mergeDiagonalsNetworks(C.d2)
-
-
- use(1)
- if (C.shock(user, 50))
- if (prob(50)) //fail
- new/obj/item/stack/cable_coil(C.loc, 1, C.color)
- qdel(C)
+ use(1)
+ if (C.shock(user, 50))
+ if (prob(50)) //fail
+ new/obj/item/stack/cable_coil(C.loc, 1, C.color)
+ qdel(C)
// called when cable_coil is click on an installed obj/cable
// or click on a turf that already contains a "node" cable
@@ -745,10 +718,9 @@ obj/structure/cable/proc/cableColor(var/colorC)
return
if(get_dist(C, user) > 1) // make sure it's close enough
- user << "You can't lay cable at a place that far away."
+ to_chat(user, "You can't lay cable at a place that far away.")
return
-
if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing
turf_place(T,user)
return
@@ -758,7 +730,7 @@ obj/structure/cable/proc/cableColor(var/colorC)
// one end of the clicked cable is pointing towards us
if(C.d1 == dirn || C.d2 == dirn)
if(!U.is_plating()) // can't place a cable if the floor is complete
- user << "You can't lay cable there unless the floor tiles are removed."
+ to_chat(user, "You can't lay cable there unless the floor tiles are removed.")
return
else
// cable is pointing at us, we're standing on an open tile
@@ -768,34 +740,9 @@ obj/structure/cable/proc/cableColor(var/colorC)
for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already
if(LC.d1 == fdirn || LC.d2 == fdirn)
- user << "There's already a cable at that position."
+ to_chat(user, "There's already a cable at that position.")
return
-
- var/obj/structure/cable/NC = new(U)
- NC.cableColor(color)
-
- NC.d1 = 0
- NC.d2 = fdirn
- NC.add_fingerprint()
- NC.updateicon()
-
- //create a new powernet with the cable, if needed it will be merged later
- var/datum/powernet/newPN = new()
- newPN.add_cable(NC)
-
- NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets
- NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets
-
- if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions
- NC.mergeDiagonalsNetworks(NC.d2)
-
- use(1)
-
- if (NC.shock(user, 50))
- if (prob(50)) //fail
- new/obj/item/stack/cable_coil(NC.loc, 1, NC.color)
- qdel(NC)
-
+ put_cable(U,user,0,fdirn)
return
// exisiting cable doesn't point at our position, so see if it's a stub
@@ -814,7 +761,7 @@ obj/structure/cable/proc/cableColor(var/colorC)
if(LC == C) // skip the cable we're interacting with
continue
if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction
- user << "There's already a cable at that position."
+ to_chat(user, "There's already a cable at that position.")
return
@@ -824,7 +771,7 @@ obj/structure/cable/proc/cableColor(var/colorC)
C.d2 = nd2
C.add_fingerprint()
- C.updateicon()
+ C.update_icon()
C.mergeConnectedNetworks(C.d1) //merge the powernets...
diff --git a/icons/obj/power_cond_white.dmi b/icons/obj/power_cond_white.dmi
index 8d17183bfa..f9f3d9da7a 100644
Binary files a/icons/obj/power_cond_white.dmi and b/icons/obj/power_cond_white.dmi differ
diff --git a/icons/turf/cliff.dmi b/icons/turf/cliff.dmi
new file mode 100644
index 0000000000..40e8885771
Binary files /dev/null and b/icons/turf/cliff.dmi differ
diff --git a/icons/turf/space.dmi b/icons/turf/space.dmi
index 30d0d1428a..e11f256d88 100644
Binary files a/icons/turf/space.dmi and b/icons/turf/space.dmi differ
diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm
new file mode 100644
index 0000000000..6c5d1ead2d
--- /dev/null
+++ b/maps/example/example-1.dmm
@@ -0,0 +1,73 @@
+"a" = (/turf/space,/area/space)
+"b" = (/turf/simulated/wall,/area/aisat)
+"c" = (/obj/machinery/atmospherics/pipe/zpipe/up{tag = "icon-up (EAST)"; icon_state = "up"; dir = 4},/obj/structure/disposalpipe/up,/turf/simulated/floor/plating,/area/aisat)
+"d" = (/obj/machinery/atmospherics/pipe/simple/visible{dir = 4},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"e" = (/obj/machinery/atmospherics/pipe/manifold/visible{dir = 1},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"f" = (/obj/machinery/atmospherics/binary/pump/high_power{dir = 8},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"g" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (SOUTHWEST)"; icon_state = "intact"; dir = 10},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"h" = (/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"i" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/tiled/dark,/area/aisat)
+"j" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/plating,/area/aisat)
+"k" = (/obj/machinery/atmospherics/pipe/simple/visible,/obj/effect/floor_decal/corner/white/diagonal,/obj/machinery/meter,/turf/simulated/floor/tiled/dark,/area/aisat)
+"l" = (/obj/machinery/atmospherics/portables_connector{dir = 4},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"m" = (/obj/machinery/atmospherics/pipe/manifold/visible{tag = "icon-map (EAST)"; icon_state = "map"; dir = 4},/obj/effect/floor_decal/corner/white/diagonal,/obj/machinery/meter,/turf/simulated/floor/tiled/dark,/area/aisat)
+"n" = (/turf/simulated/floor/tiled/dark,/area/aisat)
+"o" = (/obj/effect/floor_decal/industrial/hatch/yellow,/turf/simulated/floor/tiled/dark,/area/aisat)
+"p" = (/obj/machinery/portable_atmospherics/canister/empty,/obj/effect/floor_decal/corner/white/diagonal,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/dark,/area/aisat)
+"q" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (NORTHEAST)"; icon_state = "intact"; dir = 5},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"r" = (/obj/machinery/atmospherics/binary/pump/high_power{tag = "icon-map_off (EAST)"; icon_state = "map_off"; dir = 4},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"s" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (NORTHWEST)"; icon_state = "intact"; dir = 9},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"t" = (/obj/structure/ladder/up,/turf/simulated/floor/tiled/dark,/area/aisat)
+"u" = (/obj/machinery/portable_atmospherics/canister/air,/obj/effect/floor_decal/corner/white/diagonal,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/dark,/area/aisat)
+"v" = (/obj/machinery/light,/turf/simulated/floor/tiled/dark,/area/aisat)
+"w" = (/obj/machinery/alarm{dir = 4; icon_state = "alarm0"; pixel_x = -22},/obj/machinery/light{icon_state = "tube1"; dir = 8},/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{tag = "icon-pipe-t (NORTH)"; icon_state = "pipe-t"; dir = 1},/turf/simulated/floor/tiled/dark,/area/aisat)
+"x" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/tiled/dark,/area/aisat)
+"y" = (/obj/machinery/light_switch,/turf/simulated/wall,/area/aisat)
+"z" = (/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/tiled/dark,/area/aisat)
+"A" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/obj/machinery/power/apc{dir = 4; name = "east bump"; pixel_x = 24},/obj/structure/cable/yellow{d2 = 2; icon_state = "0-2"},/turf/simulated/floor/tiled/dark,/area/aisat)
+"B" = (/obj/effect/floor_decal/corner/white/diagonal,/obj/structure/table/standard,/obj/item/device/multitool,/turf/simulated/floor/tiled/dark,/area/aisat)
+"C" = (/obj/machinery/light{dir = 1},/obj/structure/table/standard,/obj/item/weapon/storage/belt/utility/full,/turf/simulated/floor/tiled/dark,/area/aisat)
+"D" = (/obj/effect/floor_decal/corner/white/diagonal,/obj/structure/table/standard,/obj/item/clothing/gloves/yellow,/turf/simulated/floor/tiled/dark,/area/aisat)
+"E" = (/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"F" = (/obj/effect/landmark{name = "JoinLate"},/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"G" = (/obj/effect/landmark/start,/turf/simulated/floor/tiled/dark,/area/aisat)
+"H" = (/obj/structure/stairs/south,/turf/simulated/floor/tiled/dark,/area/aisat)
+"I" = (/obj/effect/floor_decal/sign/a,/turf/simulated/floor/tiled/dark,/area/aisat)
+"J" = (/obj/machinery/light,/obj/effect/floor_decal/corner/white/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat)
+"K" = (/obj/structure/cable/yellow,/obj/structure/cable/yellow{d1 = 16; d2 = 0; icon_state = "16-0"},/turf/simulated/floor/plating,/area/aisat)
+"L" = (/turf/simulated/floor/tiled/red,/area/aisat)
+
+(1,1,1) = {"
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaabbbbbbbbbbbbbbbaaaaaaaaa
+aaaaaaaabcdefghihhhhhhbaaaaaaaaa
+aaaaaaaabjhklmhnhhooohbaaaaaaaaa
+aaaaaaaabphqrshthhooohbaaaaaaaaa
+aaaaaaaabphhhhhnhhooohbaaaaaaaaa
+aaaaaaaabuhhhhhnhhhhhhbaaaaaaaaa
+aaaaaaaabuhhhhhvhhhhhhbaaaaaaaaa
+aaaaaaaabwnnnnxyznnnnAbaaaaaaaaa
+aaaaaaaabhhhhhBCDhhhhEbaaaaaaaaa
+aaaaaaaabhhFFFhnhhhhhEbaaaaaaaaa
+aaaaaaaabhhFFFhGhhhhhEbaaaaaaaaa
+aaaaaaaabnbFFFhnhhhhhEbaaaaaaaaa
+aaaaaaaabHbhhhhIhhhhhEbaaaaaaaaa
+aaaaaaaabbbhJhhnhhJhhKbaaaaaaaaa
+aaaaaaaabbbbbLLLLLbbbbbaaaaaaaaa
+aaaaaaaaaaaabLLLLLbaaaaaaaaaaaaa
+aaaaaaaaaaaabLLLLLbaaaaaaaaaaaaa
+aaaaaaaaaaaabLLLLLbaaaaaaaaaaaaa
+aaaaaaaaaaaabLLLLLbaaaaaaaaaaaaa
+aaaaaaaaaaaabbbbbbbaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+"}
diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm
new file mode 100644
index 0000000000..972beeecfc
--- /dev/null
+++ b/maps/example/example-2.dmm
@@ -0,0 +1,69 @@
+"a" = (/turf/space,/area/space)
+"b" = (/obj/effect/landmark/map_data{height = 2; step_x = 0},/turf/space,/area/space)
+"c" = (/turf/simulated/wall,/area/aisat_interior)
+"d" = (/obj/structure/lattice,/obj/machinery/atmospherics/pipe/zpipe/down{tag = "icon-down (EAST)"; icon_state = "down"; dir = 4},/obj/structure/disposalpipe/down,/turf/simulated/open,/area/aisat_interior)
+"e" = (/obj/machinery/atmospherics/pipe/simple/visible{dir = 4},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"f" = (/obj/machinery/atmospherics/pipe/manifold/visible{dir = 1},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"g" = (/obj/machinery/atmospherics/binary/pump/high_power{dir = 8},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"h" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (SOUTHWEST)"; icon_state = "intact"; dir = 10},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"i" = (/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"j" = (/obj/machinery/light{dir = 1},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"k" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/plating,/area/aisat_interior)
+"l" = (/obj/machinery/atmospherics/pipe/simple/visible,/obj/effect/floor_decal/corner/lime/diagonal,/obj/machinery/meter,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"m" = (/obj/machinery/atmospherics/portables_connector{dir = 4},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"n" = (/obj/machinery/atmospherics/pipe/manifold/visible{tag = "icon-map (EAST)"; icon_state = "map"; dir = 4},/obj/effect/floor_decal/corner/lime/diagonal,/obj/machinery/meter,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"o" = (/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"p" = (/turf/simulated/open,/area/aisat_interior)
+"q" = (/obj/machinery/portable_atmospherics/canister/empty,/obj/effect/floor_decal/corner/lime/diagonal,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"r" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (NORTHEAST)"; icon_state = "intact"; dir = 5},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"s" = (/obj/machinery/atmospherics/binary/pump/high_power{tag = "icon-map_off (EAST)"; icon_state = "map_off"; dir = 4},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"t" = (/obj/machinery/atmospherics/pipe/simple/visible{tag = "icon-intact (NORTHWEST)"; icon_state = "intact"; dir = 9},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"u" = (/obj/structure/ladder,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"v" = (/obj/machinery/portable_atmospherics/canister/air,/obj/effect/floor_decal/corner/lime/diagonal,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"w" = (/obj/machinery/light,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"x" = (/obj/machinery/alarm{dir = 4; icon_state = "alarm0"; pixel_x = -22},/obj/machinery/light{icon_state = "tube1"; dir = 8},/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{tag = "icon-pipe-t (NORTH)"; icon_state = "pipe-t"; dir = 1},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"y" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"z" = (/obj/machinery/light_switch,/turf/simulated/wall,/area/aisat_interior)
+"A" = (/obj/machinery/power/fractal_reactor,/obj/structure/cable/yellow{d2 = 4; icon_state = "0-4"},/obj/machinery/light{icon_state = "tube1"; dir = 8},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"B" = (/obj/structure/cable/yellow{d1 = 4; d2 = 8; icon_state = "4-8"},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"C" = (/obj/machinery/power/apc{dir = 4; name = "east bump"; pixel_x = 24},/obj/structure/cable/yellow{d2 = 8; icon_state = "0-8"},/obj/structure/cable/yellow{d1 = 2; d2 = 8; icon_state = "2-8"},/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"D" = (/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"E" = (/turf/simulated/floor/tiled/red,/area/aisat_interior)
+"F" = (/obj/effect/floor_decal/sign/two,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"G" = (/obj/machinery/light,/obj/effect/floor_decal/corner/lime/diagonal,/turf/simulated/floor/tiled/dark,/area/aisat_interior)
+"H" = (/obj/structure/lattice,/obj/structure/cable/yellow{d1 = 32; icon_state = "32-1"},/turf/simulated/open,/area/aisat_interior)
+
+(1,1,1) = {"
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaabaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaacccccccccccccccaaaaaaaaa
+aaaaaaaacdefghijiiiiiicaaaaaaaaa
+aaaaaaaackilmnioiipppicaaaaaaaaa
+aaaaaaaacqirstiuiipppicaaaaaaaaa
+aaaaaaaacqiiiiioiipppicaaaaaaaaa
+aaaaaaaacviiiiioiiiiiicaaaaaaaaa
+aaaaaaaacviiiiiwiiiiiicaaaaaaaaa
+aaaaaaaacxooooyzABBBBCcaaaaaaaaa
+aaaaaaaaciiiiiijiiiiiDcaaaaaaaaa
+aaaaaaaaciiiiiioiiiiiDcaaaaaaaaa
+aaaaaaaaccciiiioiiiiiDcaaaaaaaaa
+aaaaaaaacEciiiioiiiiiDcaaaaaaaaa
+aaaaaaaacEciiiiFiiiiiDcaaaaaaaaa
+aaaaaaaaciiiGiioiiGiiHcaaaaaaaaa
+aaaaaaaacccccEEEEEcccccaaaaaaaaa
+aaaaaaaaaaaacEEEEEcaaaaaaaaaaaaa
+aaaaaaaaaaaacEEEEEcaaaaaaaaaaaaa
+aaaaaaaaaaaacEEEEEcaaaaaaaaaaaaa
+aaaaaaaaaaaacEEEEEcaaaaaaaaaaaaa
+aaaaaaaaaaaacccccccaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+"}
diff --git a/maps/example/example.dm b/maps/example/example.dm
new file mode 100644
index 0000000000..d5e18ea366
--- /dev/null
+++ b/maps/example/example.dm
@@ -0,0 +1,14 @@
+#if !defined(USING_MAP_DATUM)
+
+ #include "example-1.dmm"
+ #include "example-2.dmm"
+
+ #include "example_defines.dm"
+
+ #define USING_MAP_DATUM /datum/map/example
+
+#elif !defined(MAP_OVERRIDE)
+
+ #warn A map has already been included, ignoring Northern Star
+
+#endif
\ No newline at end of file
diff --git a/maps/example/example_defines.dm b/maps/example/example_defines.dm
new file mode 100644
index 0000000000..3c7ae5d30e
--- /dev/null
+++ b/maps/example/example_defines.dm
@@ -0,0 +1,49 @@
+#define Z_LEVEL_FIRST_EXAMPLE 1
+#define Z_LEVEL_SECOND_EXAMPLE 2
+
+/datum/map/example
+ name = "Test Map"
+ full_name = "The Test Map"
+ path = "example"
+
+ lobby_icon = 'icons/misc/title.dmi'
+ lobby_screens = list("mockingjay00")
+
+ station_levels = list(
+ Z_LEVEL_FIRST_EXAMPLE,
+ Z_LEVEL_SECOND_EXAMPLE
+ )
+
+ admin_levels = list()
+ contact_levels = list(
+ Z_LEVEL_FIRST_EXAMPLE,
+ Z_LEVEL_SECOND_EXAMPLE
+ )
+
+ player_levels = list(
+ Z_LEVEL_FIRST_EXAMPLE,
+ Z_LEVEL_SECOND_EXAMPLE
+ )
+
+ sealed_levels = list()
+ empty_levels = list()
+ accessible_z_levels = list("1" = 50, "2" = 50) // The defines can't be used here sadly.
+ base_turf_by_z = list("2" = /turf/simulated/open)
+
+ station_name = "The Funhouse"
+ station_short = "Funhouse"
+ dock_name = "the Maximum Fun Chamber"
+ boss_name = "Mister Fun"
+ boss_short = "Mr. Fun"
+ company_name = "Fun Inc."
+ company_short = "FI"
+ starsys_name = "Vir"
+
+ shuttle_docked_message = "The scheduled shuttle to the %dock_name% has docked with the station at docks one and two. It will depart in approximately %ETD%."
+ shuttle_leaving_dock = "The Crew Transfer Shuttle has left the station. Estimate %ETA% until the shuttle docks at %dock_name%."
+ shuttle_called_message = "A crew transfer to %Dock_name% has been scheduled. The shuttle has been called. Those leaving should procede to docks one and two in approximately %ETA%"
+ shuttle_recall_message = "The scheduled crew transfer has been cancelled."
+ emergency_shuttle_docked_message = "The Emergency Shuttle has docked with the station at docks one and two. You have approximately %ETD% to board the Emergency Shuttle."
+ emergency_shuttle_leaving_dock = "The Emergency Shuttle has left the station. Estimate %ETA% until the shuttle docks at %dock_name%."
+ emergency_shuttle_called_message = "An emergency evacuation shuttle has been called. It will arrive at docks one and two in approximately %ETA%"
+ emergency_shuttle_recall_message = "The emergency shuttle has been recalled."
\ No newline at end of file
diff --git a/maps/northern_star/northern_star.dm b/maps/northern_star/northern_star.dm
index 90edaa6c73..2212774657 100644
--- a/maps/northern_star/northern_star.dm
+++ b/maps/northern_star/northern_star.dm
@@ -6,6 +6,8 @@
#include "polaris-4.dmm"
#include "polaris-5.dmm"
+ #include "northern_star_defines.dm"
+
#define USING_MAP_DATUM /datum/map/northern_star
#elif !defined(MAP_OVERRIDE)
diff --git a/polaris.dme b/polaris.dme
index e1dbe5ff98..45b45aaa9e 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -205,6 +205,7 @@
#include "code\datums\observation\moved.dm"
#include "code\datums\observation\observation.dm"
#include "code\datums\observation\task_triggered.dm"
+#include "code\datums\observation\turf_changed.dm"
#include "code\datums\observation\unequipped.dm"
#include "code\datums\observation\~cleanup.dm"
#include "code\datums\repositories\cameras.dm"
@@ -2173,6 +2174,5 @@
#include "interface\interface.dm"
#include "interface\skin.dmf"
#include "maps\northern_star\northern_star.dm"
-#include "maps\northern_star\northern_star_defines.dm"
#include "maps\~map_system\maps.dm"
// END_INCLUDE