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