diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index ea604bc996..4bbbf02b16 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -1,4 +1,4 @@
-obj/machinery/atmospherics/binary
+/obj/machinery/atmospherics/binary
dir = SOUTH
initialize_directions = SOUTH|NORTH
use_power = 1
@@ -9,123 +9,131 @@ obj/machinery/atmospherics/binary
var/datum/pipe_network/network1
var/datum/pipe_network/network2
- New()
- ..()
- switch(dir)
- if(NORTH)
- initialize_directions = NORTH|SOUTH
- if(SOUTH)
- initialize_directions = NORTH|SOUTH
- if(EAST)
- initialize_directions = EAST|WEST
- if(WEST)
- initialize_directions = EAST|WEST
- air1 = new
- air2 = new
+/obj/machinery/atmospherics/binary/New()
+ ..()
- air1.volume = 200
- air2.volume = 200
+ air1 = new
+ air2 = new
+
+ air1.volume = 200
+ air2.volume = 200
+
+/obj/machinery/atmospherics/binary/init_dir()
+ switch(dir)
+ if(NORTH)
+ initialize_directions = NORTH|SOUTH
+ if(SOUTH)
+ initialize_directions = NORTH|SOUTH
+ if(EAST)
+ initialize_directions = EAST|WEST
+ if(WEST)
+ initialize_directions = EAST|WEST
// Housekeeping and pipe network stuff below
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
- if(reference == node1)
- network1 = new_network
+/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+ if(reference == node1)
+ network1 = new_network
- else if(reference == node2)
- network2 = new_network
+ else if(reference == node2)
+ network2 = new_network
- if(new_network.normal_members.Find(src))
- return 0
+ if(new_network.normal_members.Find(src))
+ return 0
- new_network.normal_members += src
+ new_network.normal_members += src
- return null
+ return null
- Destroy()
- . = ..()
+/obj/machinery/atmospherics/binary/Destroy()
+ . = ..()
- if(node1)
- node1.disconnect(src)
- qdel(network1)
- if(node2)
- node2.disconnect(src)
- qdel(network2)
+ if(node1)
+ node1.disconnect(src)
+ qdel(network1)
+ if(node2)
+ node2.disconnect(src)
+ qdel(network2)
+ node1 = null
+ node2 = null
+
+/obj/machinery/atmospherics/binary/initialize()
+ if(node1 && node2)
+ return
+
+ init_dir()
+
+ var/node2_connect = dir
+ var/node1_connect = turn(dir, 180)
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node1 = target
+ break
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node2 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/binary/build_network()
+ if(!network1 && node1)
+ network1 = new /datum/pipe_network()
+ network1.normal_members += src
+ network1.build_network(node1, src)
+
+ if(!network2 && node2)
+ network2 = new /datum/pipe_network()
+ network2.normal_members += src
+ network2.build_network(node2, src)
+
+
+/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference)
+ build_network()
+
+ if(reference==node1)
+ return network1
+
+ if(reference==node2)
+ return network2
+
+ return null
+
+/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network1 == old_network)
+ network1 = new_network
+ if(network2 == old_network)
+ network2 = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference)
+ var/list/results = list()
+
+ if(network1 == reference)
+ results += air1
+ if(network2 == reference)
+ results += air2
+
+ return results
+
+/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node1)
+ qdel(network1)
node1 = null
+
+ else if(reference==node2)
+ qdel(network2)
node2 = null
- initialize()
- if(node1 && node2) return
+ update_icon()
+ update_underlays()
- var/node2_connect = dir
- var/node1_connect = turn(dir, 180)
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node1 = target
- break
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node2 = target
- break
-
- update_icon()
- update_underlays()
-
- build_network()
- if(!network1 && node1)
- network1 = new /datum/pipe_network()
- network1.normal_members += src
- network1.build_network(node1, src)
-
- if(!network2 && node2)
- network2 = new /datum/pipe_network()
- network2.normal_members += src
- network2.build_network(node2, src)
-
-
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node1)
- return network1
-
- if(reference==node2)
- return network2
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network1 == old_network)
- network1 = new_network
- if(network2 == old_network)
- network2 = new_network
-
- return 1
-
- return_network_air(datum/pipe_network/reference)
- var/list/results = list()
-
- if(network1 == reference)
- results += air1
- if(network2 == reference)
- results += air2
-
- return results
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node1)
- qdel(network1)
- node1 = null
-
- else if(reference==node2)
- qdel(network2)
- node2 = null
-
- update_icon()
- update_underlays()
-
- return null
\ No newline at end of file
+ return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
index 6690c7c257..8b174b2845 100644
--- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm
@@ -247,6 +247,7 @@
if(P.node || P.mode == 0)
continue
for(var/obj/machinery/atmospherics/target in get_step(src, P.dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
P.node = target
diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm
index c39ca8469b..69fc0561c9 100644
--- a/code/ATMOSPHERICS/components/portables_connector.dm
+++ b/code/ATMOSPHERICS/components/portables_connector.dm
@@ -18,9 +18,11 @@
use_power = 0
level = 1
+/obj/machinery/atmospherics/portables_connector/init_dir()
+ initialize_directions = dir
/obj/machinery/atmospherics/portables_connector/New()
- initialize_directions = dir
+ init_dir()
..()
/obj/machinery/atmospherics/portables_connector/update_icon()
@@ -73,11 +75,15 @@
node = null
/obj/machinery/atmospherics/portables_connector/initialize()
- if(node) return
+ if(node)
+ return
+
+ init_dir()
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index 717540db69..34e6d480a8 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -1,4 +1,4 @@
-obj/machinery/atmospherics/trinary
+/obj/machinery/atmospherics/trinary
dir = SOUTH
initialize_directions = SOUTH|NORTH|WEST
use_power = 0
@@ -13,153 +13,163 @@ obj/machinery/atmospherics/trinary
var/datum/pipe_network/network2
var/datum/pipe_network/network3
- New()
- ..()
- switch(dir)
- if(NORTH)
- initialize_directions = EAST|NORTH|SOUTH
- if(SOUTH)
- initialize_directions = SOUTH|WEST|NORTH
- if(EAST)
- initialize_directions = EAST|WEST|SOUTH
- if(WEST)
- initialize_directions = WEST|NORTH|EAST
- air1 = new
- air2 = new
- air3 = new
+/obj/machinery/atmospherics/trinary/New()
+ ..()
+ init_dir()
- air1.volume = 200
- air2.volume = 200
- air3.volume = 200
+ air1 = new
+ air2 = new
+ air3 = new
+
+ air1.volume = 200
+ air2.volume = 200
+ air3.volume = 200
+
+/obj/machinery/atmospherics/trinary/init_dir()
+ switch(dir)
+ if(NORTH)
+ initialize_directions = EAST|NORTH|SOUTH
+ if(SOUTH)
+ initialize_directions = SOUTH|WEST|NORTH
+ if(EAST)
+ initialize_directions = EAST|WEST|SOUTH
+ if(WEST)
+ initialize_directions = WEST|NORTH|EAST
// Housekeeping and pipe network stuff below
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
- if(reference == node1)
- network1 = new_network
+/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+ if(reference == node1)
+ network1 = new_network
- else if(reference == node2)
- network2 = new_network
+ else if(reference == node2)
+ network2 = new_network
- else if (reference == node3)
- network3 = new_network
+ else if (reference == node3)
+ network3 = new_network
- if(new_network.normal_members.Find(src))
- return 0
+ if(new_network.normal_members.Find(src))
+ return 0
- new_network.normal_members += src
+ new_network.normal_members += src
- return null
+ return null
- Destroy()
- . = ..()
+/obj/machinery/atmospherics/trinary/Destroy()
+ . = ..()
- if(node1)
- node1.disconnect(src)
- qdel(network1)
- if(node2)
- node2.disconnect(src)
- qdel(network2)
- if(node3)
- node3.disconnect(src)
- qdel(network3)
+ if(node1)
+ node1.disconnect(src)
+ qdel(network1)
+ if(node2)
+ node2.disconnect(src)
+ qdel(network2)
+ if(node3)
+ node3.disconnect(src)
+ qdel(network3)
+ node1 = null
+ node2 = null
+ node3 = null
+
+/obj/machinery/atmospherics/trinary/initialize()
+ if(node1 && node2 && node3)
+ return
+
+ init_dir()
+
+ var/node1_connect = turn(dir, -180)
+ var/node2_connect = turn(dir, -90)
+ var/node3_connect = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node1 = target
+ break
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node2 = target
+ break
+ for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node3 = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/trinary/build_network()
+ if(!network1 && node1)
+ network1 = new /datum/pipe_network()
+ network1.normal_members += src
+ network1.build_network(node1, src)
+
+ if(!network2 && node2)
+ network2 = new /datum/pipe_network()
+ network2.normal_members += src
+ network2.build_network(node2, src)
+
+ if(!network3 && node3)
+ network3 = new /datum/pipe_network()
+ network3.normal_members += src
+ network3.build_network(node3, src)
+
+
+/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference)
+ build_network()
+
+ if(reference==node1)
+ return network1
+
+ if(reference==node2)
+ return network2
+
+ if(reference==node3)
+ return network3
+
+ return null
+
+/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network1 == old_network)
+ network1 = new_network
+ if(network2 == old_network)
+ network2 = new_network
+ if(network3 == old_network)
+ network3 = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference)
+ var/list/results = list()
+
+ if(network1 == reference)
+ results += air1
+ if(network2 == reference)
+ results += air2
+ if(network3 == reference)
+ results += air3
+
+ return results
+
+/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node1)
+ qdel(network1)
node1 = null
+
+ else if(reference==node2)
+ qdel(network2)
node2 = null
+
+ else if(reference==node3)
+ qdel(network3)
node3 = null
- initialize()
- if(node1 && node2 && node3) return
+ update_underlays()
- var/node1_connect = turn(dir, -180)
- var/node2_connect = turn(dir, -90)
- var/node3_connect = dir
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node1 = target
- break
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node2 = target
- break
- for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node3 = target
- break
-
- update_icon()
- update_underlays()
-
- build_network()
- if(!network1 && node1)
- network1 = new /datum/pipe_network()
- network1.normal_members += src
- network1.build_network(node1, src)
-
- if(!network2 && node2)
- network2 = new /datum/pipe_network()
- network2.normal_members += src
- network2.build_network(node2, src)
-
- if(!network3 && node3)
- network3 = new /datum/pipe_network()
- network3.normal_members += src
- network3.build_network(node3, src)
-
-
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node1)
- return network1
-
- if(reference==node2)
- return network2
-
- if(reference==node3)
- return network3
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network1 == old_network)
- network1 = new_network
- if(network2 == old_network)
- network2 = new_network
- if(network3 == old_network)
- network3 = new_network
-
- return 1
-
- return_network_air(datum/pipe_network/reference)
- var/list/results = list()
-
- if(network1 == reference)
- results += air1
- if(network2 == reference)
- results += air2
- if(network3 == reference)
- results += air3
-
- return results
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node1)
- qdel(network1)
- node1 = null
-
- else if(reference==node2)
- qdel(network2)
- node2 = null
-
- else if(reference==node3)
- qdel(network3)
- node3 = null
-
- update_underlays()
-
- return null
\ No newline at end of file
+ return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index 81a870743e..9a0a92c75c 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -47,10 +47,10 @@
update_underlays()
/obj/machinery/atmospherics/tvalve/New()
- initialize_directions()
+ init_dir()
..()
-/obj/machinery/atmospherics/tvalve/proc/initialize_directions()
+/obj/machinery/atmospherics/tvalve/init_dir()
switch(dir)
if(NORTH)
initialize_directions = SOUTH|NORTH|EAST
@@ -189,21 +189,26 @@
var/node2_dir
var/node3_dir
+ init_dir()
+
node1_dir = turn(dir, 180)
node2_dir = turn(dir, -90)
node3_dir = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -371,7 +376,7 @@
icon_state = "map_tvalvem1"
state = 1
-/obj/machinery/atmospherics/tvalve/mirrored/initialize_directions()
+/obj/machinery/atmospherics/tvalve/mirrored/init_dir()
switch(dir)
if(NORTH)
initialize_directions = SOUTH|NORTH|WEST
diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm
index 0372483c79..327112d918 100644
--- a/code/ATMOSPHERICS/components/unary/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary/unary_base.dm
@@ -11,83 +11,90 @@
var/welded = 0 //defining this here for ventcrawl stuff
- New()
- ..()
- initialize_directions = dir
- air_contents = new
+/obj/machinery/atmospherics/unary/New()
+ ..()
+ init_dir()
+ air_contents = new
- air_contents.volume = 200
+ air_contents.volume = 200
+
+/obj/machinery/atmospherics/unary/init_dir()
+ initialize_directions = dir
// Housekeeping and pipe network stuff below
- network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
- if(reference == node)
- network = new_network
+/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
+ if(reference == node)
+ network = new_network
- if(new_network.normal_members.Find(src))
- return 0
+ if(new_network.normal_members.Find(src))
+ return 0
- new_network.normal_members += src
+ new_network.normal_members += src
- return null
+ return null
- Destroy()
- . = ..()
+/obj/machinery/atmospherics/unary/Destroy()
+ . = ..()
- if(node)
- node.disconnect(src)
- qdel(network)
+ if(node)
+ node.disconnect(src)
+ qdel(network)
+ node = null
+
+/obj/machinery/atmospherics/unary/initialize()
+ if(node)
+ return
+
+ init_dir()
+
+ var/node_connect = dir
+
+ for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ if (check_connect_types(target,src))
+ node = target
+ break
+
+ update_icon()
+ update_underlays()
+
+/obj/machinery/atmospherics/unary/build_network()
+ if(!network && node)
+ network = new /datum/pipe_network()
+ network.normal_members += src
+ network.build_network(node, src)
+
+
+/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference)
+ build_network()
+
+ if(reference==node)
+ return network
+
+ return null
+
+/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
+ if(network == old_network)
+ network = new_network
+
+ return 1
+
+/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference)
+ var/list/results = list()
+
+ if(network == reference)
+ results += air_contents
+
+ return results
+
+/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference)
+ if(reference==node)
+ qdel(network)
node = null
- initialize()
- if(node) return
+ update_icon()
+ update_underlays()
- var/node_connect = dir
-
- for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
- if(target.initialize_directions & get_dir(target,src))
- if (check_connect_types(target,src))
- node = target
- break
-
- update_icon()
- update_underlays()
-
- build_network()
- if(!network && node)
- network = new /datum/pipe_network()
- network.normal_members += src
- network.build_network(node, src)
-
-
- return_network(obj/machinery/atmospherics/reference)
- build_network()
-
- if(reference==node)
- return network
-
- return null
-
- reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
- if(network == old_network)
- network = new_network
-
- return 1
-
- return_network_air(datum/pipe_network/reference)
- var/list/results = list()
-
- if(network == reference)
- results += air_contents
-
- return results
-
- disconnect(obj/machinery/atmospherics/reference)
- if(reference==node)
- qdel(network)
- node = null
-
- update_icon()
- update_underlays()
-
- return null
\ No newline at end of file
+ return null
\ No newline at end of file
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index 7fa8d60a77..209c4730a0 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -38,13 +38,12 @@
/obj/machinery/atmospherics/valve/hide(var/i)
update_underlays()
-/obj/machinery/atmospherics/valve/New()
+/obj/machinery/atmospherics/valve/init_dir()
switch(dir)
if(NORTH || SOUTH)
initialize_directions = NORTH|SOUTH
if(EAST || WEST)
initialize_directions = EAST|WEST
- ..()
/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
@@ -142,6 +141,7 @@
return
/obj/machinery/atmospherics/valve/initialize()
+ init_dir()
normalize_dir()
var/node1_dir
@@ -155,11 +155,13 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
diff --git a/code/ATMOSPHERICS/he_pipes.dm b/code/ATMOSPHERICS/he_pipes.dm
index 52dedfb089..bdad6a62e5 100644
--- a/code/ATMOSPHERICS/he_pipes.dm
+++ b/code/ATMOSPHERICS/he_pipes.dm
@@ -1,5 +1,5 @@
-obj/machinery/atmospherics/pipe/simple/heat_exchanging
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging
icon = 'icons/atmos/heat.dmi'
icon_state = "intact"
pipe_icon = "hepipe"
@@ -17,93 +17,99 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging
buckle_lying = 1
// BubbleWrap
- New()
- ..()
- initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe
- // BubbleWrap END
- color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default.
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/New()
+ ..()
+ init_dir()
+// BubbleWrap END
+ color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default.
- initialize()
- normalize_dir()
- var/node1_dir
- var/node2_dir
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/init_dir()
+ initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe
- for(var/direction in cardinal)
- if(direction&initialize_directions_he)
- if (!node1_dir)
- node1_dir = direction
- else if (!node2_dir)
- node2_dir = direction
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/initialize()
+ init_dir()
+ normalize_dir()
+ var/node1_dir
+ var/node2_dir
- for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir))
- if(target.initialize_directions_he & get_dir(target,src))
- node1 = target
- break
- for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir))
- if(target.initialize_directions_he & get_dir(target,src))
- node2 = target
- break
- if(!node1 && !node2)
- qdel(src)
- return
+ for(var/direction in cardinal)
+ if(direction&initialize_directions_he)
+ if (!node1_dir)
+ node1_dir = direction
+ else if (!node2_dir)
+ node2_dir = direction
- update_icon()
+ for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir))
+ target.init_dir()
+ if(target.initialize_directions_he & get_dir(target,src))
+ node1 = target
+ break
+ for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir))
+ target.init_dir()
+ if(target.initialize_directions_he & get_dir(target,src))
+ node2 = target
+ break
+ if(!node1 && !node2)
+ qdel(src)
return
+ update_icon()
+ return
- process()
- if(!parent)
- ..()
- else
- var/datum/gas_mixture/pipe_air = return_air()
- if(istype(loc, /turf/simulated/))
- var/environment_temperature = 0
- if(loc:blocks_air)
- environment_temperature = loc:temperature
- else
- var/datum/gas_mixture/environment = loc.return_air()
- environment_temperature = environment.temperature
- if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
- parent.temperature_interact(loc, volume, thermal_conductivity)
- else if(istype(loc, /turf/space/))
- parent.radiate_heat_to_space(surface, 1)
- if(buckled_mob)
- var/hc = pipe_air.heat_capacity()
- var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500)
- pipe_air.temperature = avg_temp
- buckled_mob.bodytemperature = avg_temp
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/process()
+ if(!parent)
+ ..()
+ else
+ var/datum/gas_mixture/pipe_air = return_air()
+ if(istype(loc, /turf/simulated/))
+ var/environment_temperature = 0
+ if(loc:blocks_air)
+ environment_temperature = loc:temperature
+ else
+ var/datum/gas_mixture/environment = loc.return_air()
+ environment_temperature = environment.temperature
+ if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
+ parent.temperature_interact(loc, volume, thermal_conductivity)
+ else if(istype(loc, /turf/space/))
+ parent.radiate_heat_to_space(surface, 1)
- var/heat_limit = 1000
+ if(buckled_mob)
+ var/hc = pipe_air.heat_capacity()
+ var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500)
+ pipe_air.temperature = avg_temp
+ buckled_mob.bodytemperature = avg_temp
- var/mob/living/carbon/human/H = buckled_mob
- if(istype(H) && H.species)
- heat_limit = H.species.heat_level_3
+ var/heat_limit = 1000
- if(pipe_air.temperature > heat_limit + 1)
- buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
+ var/mob/living/carbon/human/H = buckled_mob
+ if(istype(H) && H.species)
+ heat_limit = H.species.heat_level_3
- //fancy radiation glowing
- if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K
- if(abs(pipe_air.temperature - icon_temperature) > 10)
- icon_temperature = pipe_air.temperature
+ if(pipe_air.temperature > heat_limit + 1)
+ buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
- var/h_r = heat2color_r(icon_temperature)
- var/h_g = heat2color_g(icon_temperature)
- var/h_b = heat2color_b(icon_temperature)
+ //fancy radiation glowing
+ if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K
+ if(abs(pipe_air.temperature - icon_temperature) > 10)
+ icon_temperature = pipe_air.temperature
- if(icon_temperature < 2000) //scale up overlay until 2000K
- var/scale = (icon_temperature - 500) / 1500
- h_r = 64 + (h_r - 64)*scale
- h_g = 64 + (h_g - 64)*scale
- h_b = 64 + (h_b - 64)*scale
+ var/h_r = heat2color_r(icon_temperature)
+ var/h_g = heat2color_g(icon_temperature)
+ var/h_b = heat2color_b(icon_temperature)
- animate(src, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING)
+ if(icon_temperature < 2000) //scale up overlay until 2000K
+ var/scale = (icon_temperature - 500) / 1500
+ h_r = 64 + (h_r - 64)*scale
+ h_g = 64 + (h_g - 64)*scale
+ h_b = 64 + (h_b - 64)*scale
+
+ animate(src, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING)
-obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
icon = 'icons/atmos/junction.dmi'
icon_state = "intact"
pipe_icon = "hejunction"
@@ -112,37 +118,38 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
minimum_temperature_difference = 300
thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
- // BubbleWrap
- New()
- .. ()
- switch ( dir )
- if ( SOUTH )
- initialize_directions = NORTH
- initialize_directions_he = SOUTH
- if ( NORTH )
- initialize_directions = SOUTH
- initialize_directions_he = NORTH
- if ( EAST )
- initialize_directions = WEST
- initialize_directions_he = EAST
- if ( WEST )
- initialize_directions = EAST
- initialize_directions_he = WEST
- // BubbleWrap END
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/init_dir()
+ switch ( dir )
+ if ( SOUTH )
+ initialize_directions = NORTH
+ initialize_directions_he = SOUTH
+ if ( NORTH )
+ initialize_directions = SOUTH
+ initialize_directions_he = NORTH
+ if ( EAST )
+ initialize_directions = WEST
+ initialize_directions_he = EAST
+ if ( WEST )
+ initialize_directions = EAST
+ initialize_directions_he = WEST
- initialize()
- for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
- if(target.initialize_directions & get_dir(target,src))
- node1 = target
- break
- for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he))
- if(target.initialize_directions_he & get_dir(target,src))
- node2 = target
- break
- if(!node1&&!node2)
- qdel(src)
- return
+/obj/machinery/atmospherics/pipe/simple/heat_exchanging/initialize()
+ init_dir()
+ for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
+ target.init_dir()
+ if(target.initialize_directions & get_dir(target,src))
+ node1 = target
+ break
+ for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he))
+ target.init_dir()
+ if(target.initialize_directions_he & get_dir(target,src))
+ node2 = target
+ break
- update_icon()
+ if(!node1&&!node2)
+ qdel(src)
return
+
+ update_icon()
+ return
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 9566be9947..c363d85575 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -34,6 +34,10 @@
return 1
+// This is used to set up what directions pipes will connect to. Called inside New(), initialize(), and when pipes look at another pipe, incase they didn't get to initialize() yet.
+/obj/machinery/atmospherics/proc/init_dir()
+ return
+
/obj/machinery/atmospherics/pipe/return_air()
if(!parent)
parent = new /datum/pipeline()
@@ -170,19 +174,9 @@
icon = null
alpha = 255
- switch(dir)
- if(SOUTH || NORTH)
- initialize_directions = SOUTH|NORTH
- if(EAST || WEST)
- initialize_directions = EAST|WEST
- if(NORTHEAST)
- initialize_directions = NORTH|EAST
- if(NORTHWEST)
- initialize_directions = NORTH|WEST
- if(SOUTHEAST)
- initialize_directions = SOUTH|EAST
- if(SOUTHWEST)
- initialize_directions = SOUTH|WEST
+ init_dir()
+
+
/obj/machinery/atmospherics/pipe/simple/hide(var/i)
if(istype(loc, /turf/simulated))
@@ -210,6 +204,21 @@
else return 1
+/obj/machinery/atmospherics/pipe/simple/init_dir()
+ switch(dir)
+ if(SOUTH || NORTH)
+ initialize_directions = SOUTH|NORTH
+ if(EAST || WEST)
+ initialize_directions = EAST|WEST
+ if(NORTHEAST)
+ initialize_directions = NORTH|EAST
+ if(NORTHWEST)
+ initialize_directions = NORTH|WEST
+ if(SOUTHEAST)
+ initialize_directions = SOUTH|EAST
+ if(SOUTHWEST)
+ initialize_directions = SOUTH|WEST
+
/obj/machinery/atmospherics/pipe/simple/proc/burst()
src.visible_message("\The [src] bursts!");
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
@@ -270,6 +279,7 @@
return
/obj/machinery/atmospherics/pipe/simple/initialize()
+ init_dir()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -282,11 +292,13 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -436,6 +448,9 @@
alpha = 255
icon = null
+ init_dir()
+
+/obj/machinery/atmospherics/pipe/manifold/init_dir()
switch(dir)
if(NORTH)
initialize_directions = EAST|SOUTH|WEST
@@ -544,11 +559,13 @@
update_icon()
/obj/machinery/atmospherics/pipe/manifold/initialize()
+ init_dir()
var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir)
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -561,6 +578,7 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -573,6 +591,7 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -823,24 +842,28 @@
/obj/machinery/atmospherics/pipe/manifold4w/initialize()
for(var/obj/machinery/atmospherics/target in get_step(src,1))
+ target.init_dir()
if(target.initialize_directions & 2)
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,2))
+ target.init_dir()
if(target.initialize_directions & 1)
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,4))
+ target.init_dir()
if(target.initialize_directions & 8)
if (check_connect_types(target,src))
node3 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,8))
+ target.init_dir()
if(target.initialize_directions & 4)
if (check_connect_types(target,src))
node4 = target
@@ -958,6 +981,9 @@
/obj/machinery/atmospherics/pipe/cap/New()
..()
+ init_dir()
+
+/obj/machinery/atmospherics/pipe/cap/init_dir()
initialize_directions = dir
/obj/machinery/atmospherics/pipe/cap/hide(var/i)
@@ -1006,7 +1032,9 @@
overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, "cap")
/obj/machinery/atmospherics/pipe/cap/initialize()
+ init_dir()
for(var/obj/machinery/atmospherics/target in get_step(src, dir))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
@@ -1079,9 +1107,12 @@
/obj/machinery/atmospherics/pipe/tank/New()
icon_state = "air"
- initialize_directions = dir
+ init_dir()
..()
+/obj/machinery/atmospherics/pipe/tank/init_dir()
+ initialize_directions = dir
+
/obj/machinery/atmospherics/pipe/tank/process()
if(!parent)
..()
@@ -1110,9 +1141,11 @@
update_underlays()
/obj/machinery/atmospherics/pipe/tank/initialize()
+ init_dir()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -1241,9 +1274,12 @@
var/build_killswitch = 1
/obj/machinery/atmospherics/pipe/vent/New()
- initialize_directions = dir
+ init_dir()
..()
+/obj/machinery/atmospherics/pipe/vent/init_dir()
+ initialize_directions = dir
+
/obj/machinery/atmospherics/pipe/vent/high_volume
name = "Larger vent"
volume = 1000
@@ -1279,9 +1315,11 @@
icon_state = "exposed"
/obj/machinery/atmospherics/pipe/vent/initialize()
+ init_dir()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
+ target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 5b0de767eb..3bb7333a02 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -214,3 +214,11 @@
#define WORLD_ICON_SIZE 32 //Needed for the R-UST port
#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port
+
+// Maploader bounds indices
+#define MAP_MINX 1
+#define MAP_MINY 2
+#define MAP_MINZ 3
+#define MAP_MAXX 4
+#define MAP_MAXY 5
+#define MAP_MAXZ 6
\ No newline at end of file
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index e647cd1df7..8b9040820f 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -662,3 +662,13 @@ proc/dd_sortedTextList(list/incoming)
L.Swap(start++,end--)
return L
+
+//Copies a list, and all lists inside it recusively
+//Does not copy any other reference type
+/proc/deepCopyList(list/l)
+ if(!islist(l))
+ return l
+ . = l.Copy()
+ for(var/i = 1 to l.len)
+ if(islist(.[i]))
+ .[i] = .(.[i])
diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm
index bd175e4fcc..d4fa4a74bb 100644
--- a/code/_helpers/text.dm
+++ b/code/_helpers/text.dm
@@ -331,4 +331,61 @@ proc/TextPreview(var/string,var/len=40)
/proc/strip_improper(var/text)
return replacetext(replacetext(text, "\proper", ""), "\improper", "")
+//Used for applying byonds text macros to strings that are loaded at runtime
+/proc/apply_text_macros(string)
+ var/next_backslash = findtext(string, "\\")
+ if(!next_backslash)
+ return string
+
+ var/leng = length(string)
+
+ var/next_space = findtext(string, " ", next_backslash + 1)
+ if(!next_space)
+ next_space = leng - next_backslash
+
+ if(!next_space) //trailing bs
+ return string
+
+ var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash)
+ var/macro = lowertext(copytext(string, next_backslash + 1, next_space))
+ var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1)
+
+ //See http://www.byond.com/docs/ref/info.html#/DM/text/macros
+ switch(macro)
+ //prefixes/agnostic
+ if("the")
+ rest = text("\the []", rest)
+ if("a")
+ rest = text("\a []", rest)
+ if("an")
+ rest = text("\an []", rest)
+ if("proper")
+ rest = text("\proper []", rest)
+ if("improper")
+ rest = text("\improper []", rest)
+ if("roman")
+ rest = text("\roman []", rest)
+ //postfixes
+ if("th")
+ base = text("[]\th", rest)
+ if("s")
+ base = text("[]\s", rest)
+ if("he")
+ base = text("[]\he", rest)
+ if("she")
+ base = text("[]\she", rest)
+ if("his")
+ base = text("[]\his", rest)
+ if("himself")
+ base = text("[]\himself", rest)
+ if("herself")
+ base = text("[]\herself", rest)
+ if("hers")
+ base = text("[]\hers", rest)
+
+ . = base
+ if(rest)
+ . += .(rest)
+
+
#define gender2text(gender) capitalize(gender)
\ No newline at end of file
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 7c2bc86a65..5056e82e7b 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -47,7 +47,7 @@ var/datum/controller/master/Master = new()
var/static/restart_clear = 0
var/static/restart_timeout = 0
var/static/restart_count = 0
-
+
//current tick limit, assigned before running a subsystem.
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
@@ -541,15 +541,25 @@ var/datum/controller/master/Master = new()
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
/datum/controller/master/StartLoadingMap()
+ if(map_loading)
+ admin_notice("Another map is attempting to be loaded before first map released lock. Delaying.", R_DEBUG)
+ else
+ admin_notice("Map is now being built. Locking.", R_DEBUG)
+
//disallow more than one map to load at once, multithreading it will just cause race conditions
while(map_loading)
stoplag()
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
SS.StartLoadingMap()
+
+ // ZAS might displace objects as the map loads if an air tick is processed mid-load.
+ air_processing_killed = TRUE
map_loading = TRUE
/datum/controller/master/StopLoadingMap(bounds = null)
+ admin_notice("Map is finished. Unlocking.", R_DEBUG)
+ air_processing_killed = FALSE
map_loading = FALSE
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
diff --git a/code/controllers/subsystems/creation.dm b/code/controllers/subsystems/creation.dm
new file mode 100644
index 0000000000..e92a0447c8
--- /dev/null
+++ b/code/controllers/subsystems/creation.dm
@@ -0,0 +1,29 @@
+//
+// Creation subsystem, which is responsible for initializing newly created objects.
+//
+SUBSYSTEM_DEF(creation)
+ name = "Creation"
+ priority = 14
+ wait = 5
+// flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
+ flags = SS_NO_FIRE|SS_NO_INIT
+ runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
+
+ var/list/atoms_needing_initialize = list()
+
+ var/map_loading = FALSE
+
+/datum/controller/subsystem/creation/StartLoadingMap()
+ map_loading = TRUE
+
+/datum/controller/subsystem/creation/StopLoadingMap()
+ map_loading = FALSE
+
+/datum/controller/subsystem/creation/proc/initialize_late_atoms()
+ admin_notice("Initializing atoms in submap.", R_DEBUG)
+ var/total_atoms = atoms_needing_initialize.len
+ for(var/atom/movable/A in atoms_needing_initialize)
+ if(!QDELETED(A))
+ A.initialize()
+ atoms_needing_initialize -= A
+ admin_notice("Initalized [total_atoms] atoms in submap.", R_DEBUG)
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 64ae0722f8..81501979fd 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -21,7 +21,10 @@
/atom/movable/New()
..()
if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING)
- initialize()
+ if(SScreation && SScreation.map_loading) // If a map is being loaded, newly created objects need to wait for it to finish.
+ SScreation.atoms_needing_initialize += src
+ else
+ initialize()
/atom/movable/Destroy()
. = ..()
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index 5e8620fd10..7b330d10f9 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -267,7 +267,7 @@
return 0
/obj/machinery/alarm/proc/master_is_operating()
- return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN))
+ return alarm_area && alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER | BROKEN))
/obj/machinery/alarm/proc/elect_master()
for(var/obj/machinery/alarm/AA in alarm_area)
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 98582f3ccc..527b5f1283 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -90,6 +90,10 @@
if(STATUS_DISPLAY_BLANK) //blank
return 1
if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer
+ if(!emergency_shuttle)
+ message1 = "-ETA-"
+ message2 = "Never" // You're here forever.
+ return 1
if(emergency_shuttle.waiting_to_leave())
message1 = "-ETD-"
if(emergency_shuttle.shuttle.is_launching())
@@ -172,12 +176,16 @@
maptext = new_text
/obj/machinery/status_display/proc/get_shuttle_timer_arrival()
+ if(!emergency_shuttle)
+ return "Error"
var/timeleft = emergency_shuttle.estimate_arrival_time()
if(timeleft < 0)
return ""
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
/obj/machinery/status_display/proc/get_shuttle_timer_departure()
+ if(!emergency_shuttle)
+ return "Error"
var/timeleft = emergency_shuttle.estimate_launch_time()
if(timeleft < 0)
return ""
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 788e65827b..e031b09f5e 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -87,9 +87,11 @@
if(delete_me)
qdel(src)
-/obj/effect/landmark/Destroy()
- landmarks_list -= src
- return ..()
+/obj/effect/landmark/Destroy(var/force = FALSE)
+ if(delete_me || force)
+ landmarks_list -= src
+ return ..()
+ return QDEL_HINT_LETMELIVE
/obj/effect/landmark/start
name = "start"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index ab3fa05d21..f527de5de6 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -136,7 +136,10 @@ var/list/admin_verbs_spawn = list(
/datum/admins/proc/spawn_atom, //allows us to spawn instances,
/client/proc/respawn_character,
/client/proc/virus2_editor,
- /client/proc/spawn_chemdisp_cartridge
+ /client/proc/spawn_chemdisp_cartridge,
+ /client/proc/map_template_load,
+ /client/proc/map_template_upload,
+ /client/proc/map_template_load_on_new_z
)
var/list/admin_verbs_server = list(
/datum/admins/proc/capture_map,
diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm
new file mode 100644
index 0000000000..25fda4bfb3
--- /dev/null
+++ b/code/modules/admin/verbs/map_template_loadverb.dm
@@ -0,0 +1,69 @@
+/client/proc/map_template_load()
+ set category = "Debug"
+ set name = "Map template - Place At Loc"
+
+ var/datum/map_template/template
+
+
+ var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in map_templates
+ if(!map)
+ return
+ template = map_templates[map]
+
+ var/turf/T = get_turf(mob)
+ if(!T)
+ return
+
+ var/list/preview = list()
+ template.preload_size(template.mappath)
+ for(var/S in template.get_affected_turfs(T,centered = TRUE))
+ preview += image('icons/misc/debug_group.dmi',S ,"red")
+ usr.client.images += preview
+ if(alert(usr,"Confirm location.", "Template Confirm","No","Yes") == "Yes")
+ if(template.annihilate && alert(usr,"This template is set to annihilate everything in the red square. \
+ \nEVERYTHING IN THE RED SQUARE WILL BE DELETED, ARE YOU ABSOLUTELY SURE?", "Template Confirm","No","Yes") == "No")
+ usr.client.images -= preview
+ return
+
+ if(template.load(T, centered = TRUE))
+ message_admins("[key_name_admin(usr)] has placed a map template ([template.name]).")
+ else
+ to_chat(usr, "Failed to place map")
+ usr.client.images -= preview
+
+/client/proc/map_template_load_on_new_z()
+ set category = "Debug"
+ set name = "Map template - New Z"
+
+ var/datum/map_template/template
+
+ var/map = input(usr, "Choose a Map Template to place on a new Z-level.","Place Map Template") as null|anything in map_templates
+ if(!map)
+ return
+ template = map_templates[map]
+
+ if(alert(usr,"Confirm map load.", "Template Confirm","No","Yes") == "Yes")
+ if(template.load_new_z())
+ message_admins("[key_name_admin(usr)] has placed a map template ([template.name]) on Z level [world.maxz].")
+ else
+ to_chat(usr, "Failed to place map")
+
+
+/client/proc/map_template_upload()
+ set category = "Debug"
+ set name = "Map Template - Upload"
+
+ var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
+ if(!map)
+ return
+ if(copytext("[map]",-4) != ".dmm")
+ to_chat(usr, "Bad map file: [map]")
+ return
+
+ var/datum/map_template/M = new(map, "[map]")
+ if(M.preload_size(map))
+ to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])")
+ map_templates[M.name] = M
+ message_admins("[key_name_admin(usr)] has uploaded a map template ([map])")
+ else
+ to_chat(usr, "Map template '[map]' failed to load properly")
diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm
index 43f11c7521..740e408062 100644
--- a/code/modules/awaymissions/zlevel.dm
+++ b/code/modules/awaymissions/zlevel.dm
@@ -40,7 +40,8 @@ proc/createRandomZlevel()
var/map = pick(potentialRandomZlevels)
var/file = file(map)
if(isfile(file))
- maploader.load_map(file)
+ var/datum/map_template/template = new(file, "away mission")
+ template.load_new_z()
world.log << "away mission loaded: [map]"
for(var/obj/effect/landmark/L in landmarks_list)
diff --git a/code/modules/maps/dmm_suite.dm b/code/modules/maps/dmm_suite.dm
index c263072f99..a9f9825211 100644
--- a/code/modules/maps/dmm_suite.dm
+++ b/code/modules/maps/dmm_suite.dm
@@ -1,4 +1,4 @@
-var/global/dmm_suite/maploader = new
+var/global/dmm_suite/maploader = null
dmm_suite{
/*
diff --git a/code/modules/maps/tg/dmm_suite.dm b/code/modules/maps/tg/dmm_suite.dm
new file mode 100644
index 0000000000..c4ceec33ee
--- /dev/null
+++ b/code/modules/maps/tg/dmm_suite.dm
@@ -0,0 +1,63 @@
+dmm_suite{
+ /*
+
+ dmm_suite version 1.0
+ Released January 30th, 2011.
+
+ NOTE: Map saving functionality removed
+
+ defines the object /dmm_suite
+ - Provides the proc load_map()
+ - Loads the specified map file onto the specified z-level.
+ - provides the proc write_map()
+ - Returns a text string of the map in dmm format
+ ready for output to a file.
+ - provides the proc save_map()
+ - Returns a .dmm file if map is saved
+ - Returns FALSE if map fails to save
+
+ The dmm_suite provides saving and loading of map files in BYOND's native DMM map
+ format. It approximates the map saving and loading processes of the Dream Maker
+ and Dream Seeker programs so as to allow editing, saving, and loading of maps at
+ runtime.
+
+ ------------------------
+
+ To save a map at runtime, create an instance of /dmm_suite, and then call
+ write_map(), which accepts three arguments:
+ - A turf representing one corner of a three dimensional grid (Required).
+ - Another turf representing the other corner of the same grid (Required).
+ - Any, or a combination, of several bit flags (Optional, see documentation).
+
+ The order in which the turfs are supplied does not matter, the /dmm_writer will
+ determine the grid containing both, in much the same way as DM's block() function.
+ write_map() will then return a string representing the saved map in dmm format;
+ this string can then be saved to a file, or used for any other purose.
+
+ ------------------------
+
+ To load a map at runtime, create an instance of /dmm_suite, and then call load_map(),
+ which accepts two arguments:
+ - A .dmm file to load (Required).
+ - A number representing the z-level on which to start loading the map (Optional).
+
+ The /dmm_suite will load the map file starting on the specified z-level. If no
+ z-level was specified, world.maxz will be increased so as to fit the map. Note
+ that if you wish to load a map onto a z-level that already has objects on it,
+ you will have to handle the removal of those objects. Otherwise the new map will
+ simply load the new objects on top of the old ones.
+
+ Also note that all type paths specified in the .dmm file must exist in the world's
+ code, and that the /dmm_reader trusts that files to be loaded are in fact valid
+ .dmm files. Errors in the .dmm format will cause runtime errors.
+
+ */
+
+ verb/load_map(var/dmm_file as file, var/x_offset as num, var/y_offset as num, var/z_offset as num, var/cropMap as num, var/measureOnly as num, no_changeturf as num){
+ // dmm_file: A .dmm file to load (Required).
+ // z_offset: A number representing the z-level on which to start loading the map (Optional).
+ // cropMap: When true, the map will be cropped to fit the existing world dimensions (Optional).
+ // measureOnly: When true, no changes will be made to the world (Optional).
+ // no_changeturf: When true, turf/AfterChange won't be called on loaded turfs
+ }
+}
\ No newline at end of file
diff --git a/code/modules/maps/tg/map_template.dm b/code/modules/maps/tg/map_template.dm
new file mode 100644
index 0000000000..ae8031638f
--- /dev/null
+++ b/code/modules/maps/tg/map_template.dm
@@ -0,0 +1,165 @@
+var/list/global/map_templates = list()
+
+// Called when the world starts, in world.dm
+/proc/load_map_templates()
+ for(var/T in subtypesof(/datum/map_template))
+ var/datum/map_template/template = T
+ if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
+ continue
+ template = new T()
+ map_templates[template.name] = template
+ return TRUE
+
+/datum/map_template
+ var/name = "Default Template Name"
+ var/desc = "Some text should go here. Maybe."
+ var/width = 0
+ var/height = 0
+ var/mappath = null
+ var/loaded = 0 // Times loaded this round
+ var/annihilate = FALSE // If true, all (movable) atoms at the location where the map is loaded will be deleted before the map is loaded in.
+ var/static/dmm_suite/maploader = new
+
+/datum/map_template/New(path = null, rename = null)
+ if(path)
+ mappath = path
+ if(mappath)
+ preload_size(mappath)
+ if(rename)
+ name = rename
+
+/datum/map_template/proc/preload_size(path)
+ var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE)
+ if(bounds)
+ width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
+ height = bounds[MAP_MAXY]
+ return bounds
+
+/datum/map_template/proc/initTemplateBounds(var/list/bounds)
+ var/list/obj/machinery/atmospherics/atmos_machines = list()
+ var/list/atom/atoms = list()
+ var/list/area/areas = list()
+// var/list/turf/turfs = list()
+
+ for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]),
+ locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
+ var/turf/B = L
+ atoms += B
+ for(var/A in B)
+ atoms += A
+// turfs += B
+ areas |= get_area(B)
+ if(istype(A, /obj/machinery/atmospherics))
+ atmos_machines += A
+
+ var/i = 0
+
+// Apparently when areas get initialize()'d they initialize their turfs as well.
+// If this is ever changed, uncomment the block of code below.
+
+// admin_notice("Initializing newly created simulated turfs in submap.", R_DEBUG)
+// for(var/turf/simulated/T in turfs)
+// T.initialize()
+// i++
+// admin_notice("[i] turf\s initialized.", R_DEBUG)
+// i = 0
+
+ SScreation.initialize_late_atoms()
+
+ admin_notice("Initializing newly created area(s) in submap.", R_DEBUG)
+ for(var/area/A in areas)
+ A.initialize()
+ i++
+ admin_notice("[i] area\s initialized.", R_DEBUG)
+ i = 0
+
+ admin_notice("Initializing atmos pipenets and machinery in submap.", R_DEBUG)
+ for(var/obj/machinery/atmospherics/machine in atmos_machines)
+ machine.initialize()
+ i++
+
+ for(var/obj/machinery/atmospherics/machine in atmos_machines)
+ machine.build_network()
+
+ for(var/obj/machinery/atmospherics/unary/U in machines)
+ if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
+ var/obj/machinery/atmospherics/unary/vent_pump/T = U
+ T.broadcast_status()
+ else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
+ var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
+ T.broadcast_status()
+ admin_notice("[i] pipe\s initialized.", R_DEBUG)
+
+ admin_notice("Rebuilding powernets due to submap creation.", R_DEBUG)
+ makepowernets()
+
+ admin_notice("Submap initializations finished.", R_DEBUG)
+
+/datum/map_template/proc/load_new_z()
+ var/x = round(world.maxx/2)
+ var/y = round(world.maxy/2)
+
+ var/list/bounds = maploader.load_map(file(mappath), x, y)
+ if(!bounds)
+ return FALSE
+
+// repopulate_sorted_areas()
+
+ //initialize things that are normally initialized after map load
+ initTemplateBounds(bounds)
+ log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
+ return TRUE
+
+/datum/map_template/proc/load(turf/T, centered = FALSE)
+ var/old_T = T
+ if(centered)
+ T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
+ if(!T)
+ return
+ if(T.x+width > world.maxx)
+ return
+ if(T.y+height > world.maxy)
+ return
+
+ if(annihilate)
+ annihilate_bounds(old_T, centered)
+
+ var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE)
+ if(!bounds)
+ return
+
+// if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init
+// repopulate_sorted_areas()
+
+ //initialize things that are normally initialized after map load
+ initTemplateBounds(bounds)
+
+ log_game("[name] loaded at at [T.x],[T.y],[T.z]")
+ loaded++
+ return TRUE
+
+/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
+ var/turf/placement = T
+ if(centered)
+ var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
+ if(corner)
+ placement = corner
+ return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))
+
+/datum/map_template/proc/annihilate_bounds(turf/origin, centered = FALSE)
+ var/deleted_atoms = 0
+ admin_notice("Annihilating objects in submap loading locatation.", R_DEBUG)
+ var/list/turfs_to_clean = get_affected_turfs(origin, centered)
+ if(turfs_to_clean.len)
+ for(var/turf/T in turfs_to_clean)
+ for(var/atom/movable/AM in T)
+ ++deleted_atoms
+ qdel(AM)
+ admin_notice("Annihilated [deleted_atoms] objects.", R_DEBUG)
+
+
+//for your ever biggening badminnery kevinz000
+//❤ - Cyberboss
+/proc/load_new_z_level(var/file, var/name)
+ var/datum/map_template/template = new(file, name)
+ template.load_new_z()
\ No newline at end of file
diff --git a/code/modules/maps/tg/reader.dm b/code/modules/maps/tg/reader.dm
new file mode 100644
index 0000000000..e74977d0e5
--- /dev/null
+++ b/code/modules/maps/tg/reader.dm
@@ -0,0 +1,466 @@
+///////////////////////////////////////////////////////////////
+//SS13 Optimized Map loader
+//////////////////////////////////////////////////////////////
+
+/*
+//global datum that will preload variables on atoms instanciation
+GLOBAL_VAR_INIT(use_preloader, FALSE)
+GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
+*/
+
+//global datum that will preload variables on atoms instanciation
+var/global/dmm_suite/preloader/_preloader = new()
+var/global/use_preloader = FALSE
+
+/dmm_suite
+ // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
+ var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g")
+ // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g
+ var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g")
+ // /^[\s\n]+|[\s\n]+$/
+ var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g")
+ var/static/list/modelCache = list()
+ var/static/space_key
+ #ifdef TESTING
+ var/static/turfsSkipped
+ #endif
+
+/**
+ * Construct the model map and control the loading process
+ *
+ * WORKING :
+ *
+ * 1) Makes an associative mapping of model_keys with model
+ * e.g aa = /turf/unsimulated/wall{icon_state = "rock"}
+ * 2) Read the map line by line, parsing the result (using parse_grid)
+ *
+ */
+/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num)
+ //How I wish for RAII
+ if(!measureOnly)
+ Master.StartLoadingMap()
+ space_key = null
+ #ifdef TESTING
+ turfsSkipped = 0
+ #endif
+ . = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf)
+ #ifdef TESTING
+ if(turfsSkipped)
+ testing("Skipped loading [turfsSkipped] default turfs")
+ #endif
+ if(!measureOnly)
+ Master.StopLoadingMap()
+
+/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf)
+ var/tfile = dmm_file//the map file we're creating
+ if(isfile(tfile))
+ tfile = file2text(tfile)
+
+ if(!x_offset)
+ x_offset = 1
+ if(!y_offset)
+ y_offset = 1
+ if(!z_offset)
+ z_offset = world.maxz + 1
+
+ var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
+ var/list/grid_models = list()
+ var/key_len = 0
+
+ var/stored_index = 1
+ while(dmmRegex.Find(tfile, stored_index))
+ stored_index = dmmRegex.next
+
+ // "aa" = (/type{vars=blah})
+ if(dmmRegex.group[1]) // Model
+ var/key = dmmRegex.group[1]
+ if(grid_models[key]) // Duplicate model keys are ignored in DMMs
+ continue
+ if(key_len != length(key))
+ if(!key_len)
+ key_len = length(key)
+ else
+ throw EXCEPTION("Inconsistant key length in DMM")
+ if(!measureOnly)
+ grid_models[key] = dmmRegex.group[2]
+
+ // (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
+ else if(dmmRegex.group[3]) // Coords
+ if(!key_len)
+ throw EXCEPTION("Coords before model definition in DMM")
+
+ var/xcrdStart = text2num(dmmRegex.group[3]) + x_offset - 1
+ //position of the currently processed square
+ var/xcrd
+ var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1
+ var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
+
+ var/zexpansion = zcrd > world.maxz
+ if(zexpansion)
+ if(cropMap)
+ continue
+ else
+ world.maxz = zcrd //create a new z_level if needed
+ if(!no_changeturf)
+ WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems")
+
+ bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart)
+ bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
+ bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
+
+ var/list/gridLines = splittext(dmmRegex.group[6], "\n")
+
+ var/leadingBlanks = 0
+ while(leadingBlanks < gridLines.len && gridLines[++leadingBlanks] == "")
+ if(leadingBlanks > 1)
+ gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines.
+
+ if(!gridLines.len) // Skip it if only blank lines exist.
+ continue
+
+ if(gridLines.len && gridLines[gridLines.len] == "")
+ gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
+
+ bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
+ ycrd += gridLines.len - 1 // Start at the top and work down
+
+ if(!cropMap && ycrd > world.maxy)
+ if(!measureOnly)
+ world.maxy = ycrd // Expand Y here. X is expanded in the loop below
+ bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
+ else
+ bounds[MAP_MAXY] = max(bounds[MAP_MAXY], min(ycrd, world.maxy))
+
+ var/maxx = xcrdStart
+ if(measureOnly)
+ for(var/line in gridLines)
+ maxx = max(maxx, xcrdStart + length(line) / key_len - 1)
+ else
+ for(var/line in gridLines)
+ if(ycrd <= world.maxy && ycrd >= 1)
+ xcrd = xcrdStart
+ for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
+ if(xcrd > world.maxx)
+ if(cropMap)
+ break
+ else
+ world.maxx = xcrd
+
+ if(xcrd >= 1)
+ var/model_key = copytext(line, tpos, tpos + key_len)
+ var/no_afterchange = no_changeturf || zexpansion
+ if(!no_afterchange || (model_key != space_key))
+ if(!grid_models[model_key])
+ throw EXCEPTION("Undefined model key in DMM.")
+ parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion)
+ #ifdef TESTING
+ else
+ ++turfsSkipped
+ #endif
+ CHECK_TICK
+ maxx = max(maxx, xcrd)
+ ++xcrd
+ --ycrd
+
+ bounds[MAP_MAXX] = max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx)
+
+ CHECK_TICK
+
+ if(bounds[1] == 1.#INF) // Shouldn't need to check every item
+ return null
+ else
+ // if(!measureOnly)
+ // if(!no_changeturf)
+ // for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
+ // var/turf/T = t
+ // //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
+ // T.post_change()
+ return bounds
+
+/**
+ * Fill a given tile with its area/turf/objects/mobs
+ * Variable model is one full map line (e.g /turf/unsimulated/wall{icon_state = "rock"}, /area/mine/explored)
+ *
+ * WORKING :
+ *
+ * 1) Read the model string, member by member (delimiter is ',')
+ *
+ * 2) Get the path of the atom and store it into a list
+ *
+ * 3) a) Check if the member has variables (text within '{' and '}')
+ *
+ * 3) b) Construct an associative list with found variables, if any (the atom index in members is the same as its variables in members_attributes)
+ *
+ * 4) Instanciates the atom with its variables
+ *
+ */
+/dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num)
+ /*Method parse_grid()
+ - Accepts a text string containing a comma separated list of type paths of the
+ same construction as those contained in a .dmm file, and instantiates them.
+ */
+
+ var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored)
+ var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
+ var/list/cached = modelCache[model]
+ var/index
+
+ if(cached)
+ members = cached[1]
+ members_attributes = cached[2]
+ else
+
+ /////////////////////////////////////////////////////////
+ //Constructing members and corresponding variables lists
+ ////////////////////////////////////////////////////////
+
+ members = list()
+ members_attributes = list()
+ index = 1
+
+ var/old_position = 1
+ var/dpos
+
+ do
+ //finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/explored)
+ dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...}
+
+ var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp}
+ var/variables_start = findtext(full_def, "{")
+ var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar
+ old_position = dpos + 1
+
+ if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
+ continue
+ members.Add(atom_def)
+
+ //transform the variables in text format into a list (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
+ var/list/fields = list()
+
+ if(variables_start)//if there's any variable
+ full_def = copytext(full_def,variables_start+1,length(full_def))//removing the last '}'
+ fields = readlist(full_def, ";")
+ if(fields.len)
+ if(!trim(fields[fields.len]))
+ --fields.len
+ for(var/I in fields)
+ var/value = fields[I]
+ if(istext(value))
+ fields[I] = apply_text_macros(value)
+
+ //then fill the members_attributes list with the corresponding variables
+ members_attributes.len++
+ members_attributes[index++] = fields
+
+ CHECK_TICK
+ while(dpos != 0)
+
+ //check and see if we can just skip this turf
+ //So you don't have to understand this horrid statement, we can do this if
+ // 1. no_changeturf is set
+ // 2. the space_key isn't set yet
+ // 3. there are exactly 2 members
+ // 4. with no attributes
+ // 5. and the members are world.turf and world.area
+ // Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default)
+ // We can skip calling this proc every time we see XXX
+ if(no_changeturf && !space_key && members.len == 2 && members_attributes.len == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members))
+ space_key = model_key
+ return
+
+
+ modelCache[model] = list(members, members_attributes)
+
+
+ ////////////////
+ //Instanciation
+ ////////////////
+
+ //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
+ var/turf/crds = locate(xcrd,ycrd,zcrd)
+
+ //first instance the /area and remove it from the members list
+ index = members.len
+ if(members[index] != /area/template_noop)
+ var/atom/instance
+ _preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
+ var/atype = members[index]
+ for(var/area/A in world)
+ if(A.type == atype)
+ instance = A
+ break
+ if(!instance)
+ instance = new atype(null)
+ if(crds)
+ instance.contents.Add(crds)
+
+ if(use_preloader && instance)
+ _preloader.load(instance)
+
+ //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
+
+ var/first_turf_index = 1
+ while(!ispath(members[first_turf_index], /turf)) //find first /turf object in members
+ first_turf_index++
+
+ //turn off base new Initialization until the whole thing is loaded
+ SScreation.StartLoadingMap()
+ //instanciate the first /turf
+ var/turf/T
+ if(members[first_turf_index] != /turf/template_noop)
+ T = instance_atom(members[first_turf_index],members_attributes[first_turf_index],crds,no_changeturf)
+
+ if(T)
+ //if others /turf are presents, simulates the underlays piling effect
+ index = first_turf_index + 1
+ while(index <= members.len - 1) // Last item is an /area
+ var/underlay = T.appearance
+ T = instance_atom(members[index],members_attributes[index],crds,no_changeturf)//instance new turf
+ T.underlays += underlay
+ index++
+
+ //finally instance all remainings objects/mobs
+ for(index in 1 to first_turf_index-1)
+ instance_atom(members[index],members_attributes[index],crds,no_changeturf)
+ //Restore initialization to the previous value
+ SScreation.StopLoadingMap()
+
+////////////////
+//Helpers procs
+////////////////
+
+//Instance an atom at (x,y,z) and gives it the variables in attributes
+/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf)
+ _preloader.setup(attributes, path)
+
+ if(crds)
+ if(!no_changeturf && ispath(path, /turf))
+ . = crds.ChangeTurf(path, FALSE, TRUE)
+ else
+ . = create_atom(path, crds)//first preloader pass
+
+ if(use_preloader && .)//second preloader pass, for those atoms that don't ..() in New()
+ _preloader.load(.)
+
+ //custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize
+ if(TICK_CHECK)
+ SScreation.StopLoadingMap()
+ stoplag()
+ SScreation.StartLoadingMap()
+
+/dmm_suite/proc/create_atom(path, crds)
+ set waitfor = FALSE
+ . = new path (crds)
+
+//text trimming (both directions) helper proc
+//optionally removes quotes before and after the text (for variable name)
+/dmm_suite/proc/trim_text(what as text,trim_quotes=0)
+ if(trim_quotes)
+ return trimQuotesRegex.Replace(what, "")
+ else
+ return trimRegex.Replace(what, "")
+
+
+//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape
+//returns 0 if reached the last delimiter
+/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"")
+ var/position = initial_position
+ var/next_delimiter = findtext(text,delimiter,position,0)
+ var/next_opening = findtext(text,opening_escape,position,0)
+
+ while((next_opening != 0) && (next_opening < next_delimiter))
+ position = findtext(text,closing_escape,next_opening + 1,0)+1
+ next_delimiter = findtext(text,delimiter,position,0)
+ next_opening = findtext(text,opening_escape,position,0)
+
+ return next_delimiter
+
+
+//build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
+//return the filled list
+/dmm_suite/proc/readlist(text as text, delimiter=",")
+
+ var/list/to_return = list()
+
+ var/position
+ var/old_position = 1
+
+ do
+ //find next delimiter that is not within "..."
+ position = find_next_delimiter_position(text,old_position,delimiter)
+
+ //check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7))
+ var/equal_position = findtext(text,"=",old_position, position)
+
+ var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list
+ old_position = position + 1
+
+ if(equal_position)//associative var, so do the association
+ var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable
+
+ //Check for string
+ if(findtext(trim_right,"\"",1,2))
+ trim_right = copytext(trim_right,2,findtext(trim_right,"\"",3,0))
+
+ //Check for number
+ else if(isnum(text2num(trim_right)))
+ trim_right = text2num(trim_right)
+
+ //Check for null
+ else if(trim_right == "null")
+ trim_right = null
+
+ //Check for list
+ else if(copytext(trim_right,1,5) == "list")
+ trim_right = readlist(copytext(trim_right,6,length(trim_right)))
+
+ //Check for file
+ else if(copytext(trim_right,1,2) == "'")
+ trim_right = file(copytext(trim_right,2,length(trim_right)))
+
+ //Check for path
+ else if(ispath(text2path(trim_right)))
+ trim_right = text2path(trim_right)
+
+ to_return[trim_left] = trim_right
+
+ else//simple var
+ to_return[trim_left] = null
+
+ while(position != 0)
+
+ return to_return
+
+/dmm_suite/Destroy()
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
+
+//////////////////
+//Preloader datum
+//////////////////
+
+/dmm_suite/preloader
+ parent_type = /datum
+ var/list/attributes
+ var/target_path
+
+/dmm_suite/preloader/proc/setup(list/the_attributes, path)
+ if(the_attributes.len)
+ use_preloader = TRUE
+ attributes = the_attributes
+ target_path = path
+
+/dmm_suite/preloader/proc/load(atom/what)
+ for(var/attribute in attributes)
+ var/value = attributes[attribute]
+ if(islist(value))
+ value = deepCopyList(value)
+ what.vars[attribute] = value
+ use_preloader = FALSE
+
+/area/template_noop
+ name = "Area Passthrough"
+
+/turf/template_noop
+ name = "Turf Passthrough"
+ icon_state = "template_void"
diff --git a/code/world.dm b/code/world.dm
index 9a031dda0f..3d89f0c043 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -82,6 +82,9 @@ var/global/datum/global_init/init = new ()
// This is kinda important. Set up details of what the hell things are made of.
populate_material_list()
+ // Loads all the pre-made submap templates.
+ load_map_templates()
+
if(config.generate_map)
if(using_map.perform_map_generation())
using_map.refresh_mining_turfs()
diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi
index 7f474fa1fe..7c2f111e05 100644
Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ
diff --git a/maps/submaps/_readme.dm b/maps/submaps/_readme.dm
new file mode 100644
index 0000000000..1608dfe5b1
--- /dev/null
+++ b/maps/submaps/_readme.dm
@@ -0,0 +1,27 @@
+/*
+ This file will explain what a 'submap' is. Basically, they are smallish maps which are loaded on top of
+the main map, using the dmm suite's map loading functionality. Generally this will be done by the game
+automatically to sprinkle the play area with hidden buildings and treasure, baddies, or something along
+those lines. Admins can also manually place these down where-ever and whenever they want, potentially
+loading it into a new Z-level, for events and such.
+
+ Submaps have two parts, the map .dmm file itself, and a /datum/map_template object which describes the map.
+Both should be included inside the correct places. Submaps are divided based on where the game expects to place
+them, and the folder containing them also has their map template file. The divisions are mainly based around
+thematic and location differences (e.g. space, caves, outdoors, asteroid, etc).
+
+ When a submap is loading, the game will 'lock' certain parts of itself, disallowing the loading of a second map until the first
+finishes. When this is happening, atmospherics is also disabled, to prevent ZAS from displacing things and
+venting unfinished rooms. There is some noticable lag if the loading takes awhile, but it remains playable
+and it doesn't lock up the came entirely. Small submaps should load in less than a second, while loading, say, polaris3.dmm
+generally takes the server a minute or two, so try to not make your submap too large if possible.
+
+ You can use /area/template_noop and /turf/template_noop to act as 'void' areas/tiles, which won't be applied to the
+main map.
+
+ Map template datums can have the 'annihilate' var set to TRUE if you need to clear everything where the submap
+is being loaded (to clear trees, etc). Be sure to not load the map on top of anything valuable to you when using that, like
+irreplacable objects or players, as they will all get deleted if annihilate is on. If you load the submap before
+other objects have a chance to spawn (before random map gen), you shouldn't need to use annihilate.
+
+*/
\ No newline at end of file
diff --git a/maps/submaps/cave_submaps/cave.dm b/maps/submaps/cave_submaps/cave.dm
new file mode 100644
index 0000000000..cf063c85aa
--- /dev/null
+++ b/maps/submaps/cave_submaps/cave.dm
@@ -0,0 +1,5 @@
+/datum/map_template/cave
+ name = "Cave Content"
+ desc = "Don't dig too deep!"
+
+// To be added: Templates for cave exploration when they are made.
\ No newline at end of file
diff --git a/maps/submaps/space_submaps/space.dm b/maps/submaps/space_submaps/space.dm
new file mode 100644
index 0000000000..cfa45b6965
--- /dev/null
+++ b/maps/submaps/space_submaps/space.dm
@@ -0,0 +1,5 @@
+/datum/map_template/space
+ name = "Space Content"
+ desc = "A map template base. In space."
+
+// To be added: Templates for space exploration when they are made.
\ No newline at end of file
diff --git a/maps/submaps/surface_submaps/forest.dm b/maps/submaps/surface_submaps/forest.dm
new file mode 100644
index 0000000000..dd02c1b204
--- /dev/null
+++ b/maps/submaps/surface_submaps/forest.dm
@@ -0,0 +1,5 @@
+/datum/map_template/surface
+ name = "Surface Content"
+ desc = "Used to make the surface by 17% less boring."
+
+// To be added: Templates for surface exploration when they are made.
\ No newline at end of file
diff --git a/polaris.dme b/polaris.dme
index c9e64f810f..60b1e67867 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -171,6 +171,7 @@
#include "code\controllers\Processes\vote.dm"
#include "code\controllers\ProcessScheduler\core\process.dm"
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
+#include "code\controllers\subsystems\creation.dm"
#include "code\controllers\subsystems\garbage.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"
@@ -1136,6 +1137,7 @@
#include "code\modules\admin\verbs\dice.dm"
#include "code\modules\admin\verbs\getlogs.dm"
#include "code\modules\admin\verbs\grief_fixers.dm"
+#include "code\modules\admin\verbs\map_template_loadverb.dm"
#include "code\modules\admin\verbs\mapping.dm"
#include "code\modules\admin\verbs\massmodvar.dm"
#include "code\modules\admin\verbs\modifyvariables.dm"
@@ -1503,10 +1505,9 @@
#include "code\modules\lore_codex\lore_data\orgs.dm"
#include "code\modules\lore_codex\lore_data\political_parties.dm"
#include "code\modules\lore_codex\lore_data\species.dm"
-#include "code\modules\maps\dmm_suite.dm"
-#include "code\modules\maps\reader.dm"
-#include "code\modules\maps\swapmaps.dm"
-#include "code\modules\maps\writer.dm"
+#include "code\modules\maps\tg\dmm_suite.dm"
+#include "code\modules\maps\tg\map_template.dm"
+#include "code\modules\maps\tg\reader.dm"
#include "code\modules\materials\fifty_spawner_mats.dm"
#include "code\modules\materials\material_recipes.dm"
#include "code\modules\materials\material_sheets.dm"
@@ -2277,5 +2278,9 @@
#include "interface\interface.dm"
#include "interface\skin.dmf"
#include "maps\northern_star\northern_star.dm"
+#include "maps\submaps\_readme.dm"
+#include "maps\submaps\cave_submaps\cave.dm"
+#include "maps\submaps\space_submaps\space.dm"
+#include "maps\submaps\surface_submaps\forest.dm"
#include "maps\~map_system\maps.dm"
// END_INCLUDE