diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm index b26217eeb6c..70b7f70e633 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -13,68 +13,62 @@ Indirect connections will not merge the two zones after they reach equilibrium. var/zone/zone_A var/zone/zone_B - var/ref_A - var/ref_B - var/indirect = CONNECTION_DIRECT //If the connection is purely indirect, the zones should not join. - var/last_updated //The tick at which this was last updated. - - var/no_zone_count = 0 - - /connection/New(turf/T,turf/O) + . = ..() + A = T B = O + if(A.zone && B.zone) - if(!A.zone.connections) A.zone.connections = list() + if(!A.zone.connections) + A.zone.connections = list() A.zone.connections += src zone_A = A.zone - ref_A = "\ref[A]" - if(!B.zone.connections) B.zone.connections = list() + if(!B.zone.connections) + B.zone.connections = list() B.zone.connections += src zone_B = B.zone - ref_B = "\ref[B]" - if(ref_A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[ref_A] + if(A in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[A] connections.Add(src) else - air_master.turfs_with_connections[ref_A] = list(src) + air_master.turfs_with_connections[A] = list(src) - if(ref_B in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[ref_B] + if(B in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[B] connections.Add(src) else - air_master.turfs_with_connections[ref_B] = list(src) + air_master.turfs_with_connections[B] = list(src) if(A.CanPass(null, B, 0, 0)) - ConnectZones(A.zone, B.zone, 1) - - if(A.HasDoor(B) || B.HasDoor(A)) + if(!A.CanPass(null, B, 1.5, 1)) indirect = CONNECTION_INDIRECT + ConnectZones(A.zone, B.zone, indirect) + else ConnectZones(A.zone, B.zone) indirect = CONNECTION_CLOSED - else world.log << "Attempted to create connection object for non-zone tiles: [T] ([T.x],[T.y],[T.z]) -> [O] ([O.x],[O.y],[O.z])" - del(src) + SoftDelete() /connection/Del() //remove connections from master lists. - if(ref_B in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[ref_B] + if(B in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[B] connections.Remove(src) - if(ref_A in air_master.turfs_with_connections) - var/list/connections = air_master.turfs_with_connections[ref_A] + if(A in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[A] connections.Remove(src) //Remove connection from zones. @@ -110,6 +104,46 @@ Indirect connections will not merge the two zones after they reach equilibrium. . = ..() +/connection/proc/SoftDelete() + //remove connections from master lists. + if(B in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[B] + connections.Remove(src) + + if(A in air_master.turfs_with_connections) + var/list/connections = air_master.turfs_with_connections[A] + connections.Remove(src) + + //Remove connection from zones. + if(A) + if(A.zone && A.zone.connections) + A.zone.connections.Remove(src) + if(!A.zone.connections.len) + A.zone.connections = null + + if(istype(zone_A) && (!A || A.zone != zone_A)) + if(zone_A.connections) + zone_A.connections.Remove(src) + if(!zone_A.connections.len) + zone_A.connections = null + + if(B) + if(B.zone && B.zone.connections) + B.zone.connections.Remove(src) + if(!B.zone.connections.len) + B.zone.connections = null + + if(istype(zone_B) && (!B || B.zone != zone_B)) + if(zone_B.connections) + zone_B.connections.Remove(src) + if(!zone_B.connections.len) + zone_B.connections = null + + //Disconnect zones while handling unusual conditions. + // e.g. loss of a zone on a turf + DisconnectZones(zone_A, zone_B) + + /connection/proc/ConnectZones(var/zone/zone_1, var/zone/zone_2, open = 0) //Sanity checking @@ -139,6 +173,18 @@ Indirect connections will not merge the two zones after they reach equilibrium. zone_2.connected_zones += zone_1 zone_2.connected_zones[zone_1] = 1 + if(open == CONNECTION_DIRECT) + if(!zone_1.direct_connections) + zone_1.direct_connections = list(src) + else + zone_1.direct_connections += src + + if(!zone_2.direct_connections) + zone_2.direct_connections = list(src) + else + zone_2.direct_connections += src + + //Handle closed connections. else @@ -192,6 +238,15 @@ Indirect connections will not merge the two zones after they reach equilibrium. if(!zone_2.connected_zones.len) zone_2.connected_zones = null + if(indirect == CONNECTION_DIRECT) + zone_1.direct_connections -= src + if(!zone_1.direct_connections.len) + zone_1.direct_connections = null + + zone_2.direct_connections -= src + if(!zone_2.direct_connections.len) + zone_2.direct_connections = null + else //Handle disconnection of closed zones. if( (zone_1 in zone_2.closed_connection_zones) || (zone_2 in zone_1.closed_connection_zones) ) @@ -221,35 +276,23 @@ Indirect connections will not merge the two zones after they reach equilibrium. //Check sanity: existance of turfs if(!A || !B) - del src + SoftDelete() + return + + //Check sanity: loss of zone + if(!A.zone || !B.zone) + SoftDelete() + return //Check sanity: zones are different if(A.zone == B.zone) - del src - - //Check sanity: same turfs as before. - if(ref_A != "\ref[A]" || ref_B != "\ref[B]") - del src + SoftDelete() + return //Handle zones changing on a turf. if((A.zone && A.zone != zone_A) || (B.zone && B.zone != zone_B)) Sanitize() - //Manage sudden loss of a turfs zone. (e.g. a wall being built) - if(!A.zone || !B.zone) - no_zone_count++ - if(no_zone_count >= 5) - //world.log << "Connection removed: [A] or [B] missing a zone." - del src - return 0 - - return 1 - - -/connection/proc/CheckPassSanity() - //Sanity check, first. - Cleanup() - if(A.zone && B.zone) //If no walls are blocking us... @@ -262,7 +305,7 @@ Indirect connections will not merge the two zones after they reach equilibrium. //Make and remove connections to let air pass. if(indirect == CONNECTION_CLOSED) DisconnectZones(A.zone, B.zone) - ConnectZones(A.zone, B.zone, 1) + ConnectZones(A.zone, B.zone, door_pass + 1) if(door_pass) indirect = CONNECTION_DIRECT @@ -277,7 +320,8 @@ Indirect connections will not merge the two zones after they reach equilibrium. //If I can no longer pass air, better delete else - del src + SoftDelete() + return /connection/proc/Sanitize() //If the zones change on connected turfs, update it. @@ -293,9 +337,6 @@ Indirect connections will not merge the two zones after they reach equilibrium. A = temp zone_B = B.zone zone_A = A.zone - var/temp_ref = ref_A - ref_A = ref_B - ref_B = temp_ref return //Handle removal of connections from archived zones. diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm index 6586cbaaecf..762843d5b13 100644 --- a/code/ZAS/Debug.dm +++ b/code/ZAS/Debug.dm @@ -1,3 +1,14 @@ +client/proc/ZoneTick() + set category = "Debug" + set name = "Process Atmos" + + var/result = air_master.Tick() + if(result) + src << "Sucessfully Processed." + + else + src << "Failed to process! ([air_master.tick_progress])" + client/proc/Zone_Info(turf/T as null|turf) set category = "Debug" @@ -120,3 +131,82 @@ zone/proc/DebugDisplay(client/client) var/turf/zloc = pick(Z.contents) client << "\red Illegal air datum shared by: [zloc.loc.name]" + +client/proc/TestZASRebuild() +// var/turf/turf = get_turf(mob) + var/zone/current_zone = mob.loc:zone + if(!current_zone) + src << "There is no zone there!" + return + + var/list/current_adjacents = list() + var/list/overlays = list() + var/adjacent_id + var/lowest_id + + var/list/identical_ids = list() + var/list/turfs = current_zone.contents.Copy() + var/current_identifier = 1 + + for(var/turf/simulated/current in turfs) + lowest_id = null + current_adjacents = list() + + for(var/direction in cardinal) + var/turf/simulated/adjacent = get_step(current, direction) + if(!current.ZAirPass(adjacent)) + continue + if(turfs.Find(adjacent)) + current_adjacents += adjacent + adjacent_id = turfs[adjacent] + + if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) + lowest_id = adjacent_id + + if(!lowest_id) + lowest_id = current_identifier++ + identical_ids += lowest_id + overlays += image('icons/misc/debug_rebuild.dmi',, "[lowest_id]") + + for(var/turf/simulated/adjacent in current_adjacents) + adjacent_id = turfs[adjacent] + if(adjacent_id != lowest_id) + if(adjacent_id) + adjacent.overlays -= overlays[adjacent_id] + identical_ids[adjacent_id] = lowest_id + + turfs[adjacent] = lowest_id + adjacent.overlays += overlays[lowest_id] + + sleep(5) + + if(turfs[current]) + current.overlays += overlays[turfs[current]] + turfs[current] = lowest_id + current.overlays += overlays[lowest_id] + sleep(5) + + var/list/final_arrangement = list() + + for(var/turf/simulated/current in turfs) + current_identifier = identical_ids[turfs[current]] + current.overlays -= overlays[turfs[current]] + current.overlays += overlays[current_identifier] + sleep(5) + + if( current_identifier > final_arrangement.len ) + final_arrangement.len = current_identifier + final_arrangement[current_identifier] = list(current) + + else + final_arrangement[current_identifier] += current + + //lazy but fast + final_arrangement.Remove(null) + + src << "There are [final_arrangement.len] unique segments." + + for(var/turf/current in turfs) + current.overlays -= overlays + + return final_arrangement diff --git a/code/ZAS/FEA_gas_mixture.dm b/code/ZAS/FEA_gas_mixture.dm index 273d7669f1d..58ee345a033 100644 --- a/code/ZAS/FEA_gas_mixture.dm +++ b/code/ZAS/FEA_gas_mixture.dm @@ -8,7 +8,7 @@ What are the archived variables for? #define SPECIFIC_HEAT_AIR 20 #define SPECIFIC_HEAT_CDO 30 #define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \ - (carbon_dioxide*SPECIFIC_HEAT_CDO + (oxygen+nitrogen)*SPECIFIC_HEAT_AIR + toxins*SPECIFIC_HEAT_TOXIN) + max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN) #define MINIMUM_HEAT_CAPACITY 0.0003 #define QUANTIZE(variable) (round(variable,0.0001)) diff --git a/code/ZAS/FEA_system.dm b/code/ZAS/FEA_system.dm index c2a33ad99db..508cbb0125c 100644 --- a/code/ZAS/FEA_system.dm +++ b/code/ZAS/FEA_system.dm @@ -53,7 +53,6 @@ Important Procedures */ -var/kill_air = 0 var/tick_multiplier = 2 atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) @@ -87,17 +86,24 @@ atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0) var/datum/controller/air_system/air_master -/datum/controller/air_system/ - //Geoemetry lists +/datum/controller/air_system + //Geometry lists var/list/turfs_with_connections = list() var/list/active_hotspots = list() //Special functions lists + var/reconsidering_zones = FALSE var/list/tiles_to_reconsider_zones = list() + var/list/tiles_to_reconsider_alternate //Geometry updates lists + var/updating_tiles = FALSE var/list/tiles_to_update = list() + var/list/tiles_to_update_alternate + + var/checking_connections = FALSE var/list/connections_to_check = list() + var/list/connections_to_check_alternate var/current_cycle = 0 var/update_delay = 5 //How long between check should it try to process atmos again. @@ -106,20 +112,7 @@ var/datum/controller/air_system/air_master var/tick_progress = 0 -/* process() - //Call this to process air movements for a cycle - - process_rebuild_select_groups() - //Used by process() - //Warning: Do not call this - - rebuild_group(datum/air_group) - //Used by process_rebuild_select_groups() - //Warning: Do not call this, add the group to air_master.groups_to_rebuild instead - */ - - -/datum/controller/air_system/proc/setup() +/datum/controller/air_system/proc/Setup() //Purpose: Call this at the start to setup air groups geometry // (Warning: Very processor intensive but only must be done once per round) //Called by: Gameticker/Master controller @@ -147,10 +140,11 @@ var/datum/controller/air_system/air_master Total Simulated Turfs: [simulated_turf_count] Total Zones: [zones.len] Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"} - /* - spawn start() -/datum/controller/air_system/proc/start() +// spawn Start() + + +/datum/controller/air_system/proc/Start() //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere. //Called by: Master controller //Inputs: None. @@ -160,45 +154,60 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun set background = 1 while(1) - if(!kill_air) - current_cycle++ - var/success = tick() //Changed so that a runtime does not crash the ticker. + if(!air_processing_killed) + var/success = Tick() //Changed so that a runtime does not crash the ticker. if(!success) //Runtimed. failed_ticks++ if(failed_ticks > 20) world << "ERROR IN ATMOS TICKER. Killing air simulation!" - kill_air = 1 + air_processing_killed = 1 sleep(max(5,update_delay*tick_multiplier)) - */ -/datum/controller/air_system/proc/tick() + +/datum/controller/air_system/proc/Tick() . = 1 //Set the default return value, for runtime detection. + current_cycle++ + + //If there are tiles to update, do so. tick_progress = "update_air_properties" - if(tiles_to_update.len) //If there are tiles to update, do so. + if(tiles_to_update.len) + updating_tiles = TRUE + for(var/turf/simulated/T in tiles_to_update) if(. && T && !T.update_air_properties()) - . = 0 //If a runtime occured, make sure we can sense it. - //message_admins("ZASALERT: Unable run turf/simualted/update_air_properties()") + //If a runtime occured, make sure we can sense it. + . = 0 + + updating_tiles = FALSE + if(.) - tiles_to_update = list() + if(tiles_to_update_alternate) + tiles_to_update = tiles_to_update_alternate + tiles_to_update_alternate = null + else + tiles_to_update = list() + + else if(tiles_to_update_alternate) + tiles_to_update |= tiles_to_update_alternate + tiles_to_update_alternate = null //Check sanity on connection objects. if(.) tick_progress = "connections_to_check" if(connections_to_check.len) - for(var/connection/C in connections_to_check) - C.CheckPassSanity() - connections_to_check = list() + checking_connections = TRUE - //Ensure tiles still have zones. - if(.) - tick_progress = "tiles_to_reconsider_zones" - if(tiles_to_reconsider_zones.len) - for(var/turf/simulated/T in tiles_to_reconsider_zones) - if(!T.zone) - new /zone(T) - tiles_to_reconsider_zones = list() + for(var/connection/C in connections_to_check) + C.Cleanup() + + checking_connections = FALSE + + if(connections_to_check_alternate) + connections_to_check = connections_to_check_alternate + connections_to_check_alternate = null + else + connections_to_check = list() //Process zones. if(.) @@ -210,6 +219,25 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun Z.last_update = current_cycle if(. && Z && !output) . = 0 + + //Ensure tiles still have zones. + if(.) + tick_progress = "tiles_to_reconsider_zones" + if(tiles_to_reconsider_zones.len) + reconsidering_zones = TRUE + + for(var/turf/simulated/T in tiles_to_reconsider_zones) + if(!T.zone) + new /zone(T) + + reconsidering_zones = FALSE + + if(tiles_to_reconsider_alternate) + tiles_to_reconsider_zones = tiles_to_reconsider_alternate + tiles_to_reconsider_alternate = null + else + tiles_to_reconsider_zones = list() + //Process fires. if(.) tick_progress = "active_hotspots (fire)" @@ -218,4 +246,54 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun . = 0 if(.) - tick_progress = "success" \ No newline at end of file + tick_progress = "success" + + +/datum/controller/air_system/proc/AddTurfToUpdate(turf/simulated/outdated_turf) + var/list/tiles_to_check = list() + + if(istype(outdated_turf)) + tiles_to_check |= outdated_turf + + if(istype(outdated_turf, /turf)) + for(var/direction in cardinal) + var/turf/simulated/adjacent_turf = get_step(outdated_turf, direction) + if(istype(adjacent_turf)) + tiles_to_check |= adjacent_turf + + if(updating_tiles) + if(!tiles_to_update_alternate) + tiles_to_update_alternate = tiles_to_check + else + tiles_to_update_alternate |= tiles_to_check + else + tiles_to_update |= tiles_to_check + + +/datum/controller/air_system/proc/AddConnectionToCheck(connection/connection) + if(checking_connections) + if(istype(connection, /list)) + if(!connections_to_check_alternate) + connections_to_check_alternate = connection + + else if(!connections_to_check_alternate) + connections_to_check_alternate = list() + + connections_to_check_alternate |= connection + + else + connections_to_check |= connection + + +/datum/controller/air_system/proc/ReconsiderTileZone(var/turf/simulated/zoneless_turf) + if(zoneless_turf.zone) + return + + if(reconsidering_zones) + if(!tiles_to_reconsider_alternate) + tiles_to_reconsider_alternate = list() + + tiles_to_reconsider_alternate |= zoneless_turf + + else + tiles_to_reconsider_zones |= zoneless_turf diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm index ce24513418e..4a1ffaaea2a 100644 --- a/code/ZAS/Functions.dm +++ b/code/ZAS/Functions.dm @@ -157,14 +157,13 @@ proc/ZConnect(turf/simulated/A,turf/simulated/B) if(!A.zone || !B.zone) return if(A.zone == B.zone) return - if(A.CanPass(null,B,0,1)) + if(A.CanPass(null, B, 1.5, 1) && A.zone.air.compare(B.zone.air)) return ZMerge(A.zone,B.zone) //Ensure the connection isn't already made. - if("\ref[A]" in air_master.turfs_with_connections) - for(var/connection/C in air_master.turfs_with_connections["\ref[A]"]) - C.Cleanup() - if(C && (C.B == B || C.A == B)) + if(A in air_master.turfs_with_connections) + for(var/connection/C in air_master.turfs_with_connections[A]) + if(C.B == B || C.A == B) return //Make the connection. diff --git a/code/ZAS/ZAS_Turfs.dm b/code/ZAS/ZAS_Turfs.dm index 0d4f0496b19..d376d927f54 100644 --- a/code/ZAS/ZAS_Turfs.dm +++ b/code/ZAS/ZAS_Turfs.dm @@ -136,8 +136,8 @@ if(!zone && !blocks_air) //No zone, but not a wall. for(var/direction in DoorDirections) //Check door directions first. - if(air_check_directions&direction) - var/turf/simulated/T = get_step(src,direction) + if(air_check_directions & direction) + var/turf/simulated/T = get_step(src, direction) if(!istype(T)) continue if(T.zone) @@ -145,7 +145,7 @@ break if(!zone) //Still no zone for(var/direction in CounterDoorDirections) //Check the others second. - if(air_check_directions&direction) + if(air_check_directions & direction) var/turf/simulated/T = get_step(src,direction) if(!istype(T)) continue @@ -158,77 +158,23 @@ new/zone(list(src)) //Check pass sanity of the connections. - if("\ref[src]" in air_master.turfs_with_connections) - for(var/connection/C in air_master.turfs_with_connections["\ref[src]"]) - air_master.connections_to_check |= C + if(src in air_master.turfs_with_connections) + air_master.AddConnectionToCheck(air_master.turfs_with_connections[src]) + + if(zone && CanPass(null, src, 0, 0)) - if(zone && !zone.rebuild) for(var/direction in cardinal) var/turf/T = get_step(src,direction) if(!istype(T)) continue //I can connect to air in this direction - if(air_check_directions&direction) + if(air_check_directions & direction && !(air_directions_archived & direction)) - //If either block air, we must look to see if the adjacent turfs need rebuilt. - if(!CanPass(null, T, 0, 0)) - - //Target blocks air - if(!T.CanPass(null, T, 0, 0)) - var/turf/NT = get_step(T, direction) - - //If that turf is in my zone still, rebuild. - if(istype(NT,/turf/simulated) && NT in zone.contents) - zone.rebuild = 1 - - //If that is an unsimulated tile in my zone, see if we need to rebuild or just remove. - else if(istype(NT) && NT in zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - if(consider_rebuild) - zone.rebuild = 1 //Gotta check if we need to rebuild, dammit - else - zone.RemoveTurf(NT) //Not adjacent to anything, and unsimulated. Goodbye~ - - //To make a closed connection through closed door. - ZConnect(T, src) - - //If I block air. - else if(T.zone && !T.zone.rebuild) - var/turf/NT = get_step(src, reverse_direction(direction)) - - //If I am splitting a zone, rebuild. - if(istype(NT,/turf/simulated) && (NT in T.zone.contents || (NT.zone && T in NT.zone.contents))) - T.zone.rebuild = 1 - - //If NT is unsimulated, parse if I should remove it or rebuild. - else if(istype(NT) && NT in T.zone.unsimulated_tiles) - var/consider_rebuild = 0 - for(var/d in cardinal) - var/turf/UT = get_step(NT,d) - if(istype(UT, /turf/simulated) && UT.zone == T.zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild - consider_rebuild = 1 - break - - //Needs rebuilt. - if(consider_rebuild) - T.zone.rebuild = 1 - - //Not adjacent to anything, and unsimulated. Goodbye~ - else - T.zone.RemoveTurf(NT) - - else - //Produce connection through open door. - ZConnect(src,T) + ZConnect(src,T) //Something like a wall was built, changing the geometry. - else if(air_directions_archived&direction) + else if(!(air_check_directions & direction) && air_directions_archived & direction) var/turf/NT = get_step(T, direction) //If the tile is in our own zone, and we cannot connect to it, better rebuild. @@ -290,13 +236,13 @@ return 0 for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction) + if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) continue if(!obstacle.CanPass(null, T, 1.5, 1)) return 0 for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction) + if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) continue if(!obstacle.CanPass(null, src, 1.5, 1)) return 0 @@ -312,13 +258,13 @@ return 0 for(var/obj/obstacle in src) - if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction) + if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) continue if(!obstacle.CanPass(null, T, 0, 0)) return 0 for(var/obj/obstacle in T) - if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction) + if(istype(obstacle, /obj/machinery/door) && !(obstacle:air_properties_vary_with_direction)) continue if(!obstacle.CanPass(null, src, 0, 0)) return 0 diff --git a/code/ZAS/ZAS_Zones.dm b/code/ZAS/ZAS_Zones.dm index 860f57cd370..37aabee83ba 100644 --- a/code/ZAS/ZAS_Zones.dm +++ b/code/ZAS/ZAS_Zones.dm @@ -7,11 +7,15 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs var/rebuild = 0 //If 1, zone will be rebuilt on next process. Not sure if used. var/datum/gas_mixture/air //The air contents of the zone. var/list/contents //All the tiles that are contained in this zone. - var/list/connections // /connection objects which refer to connections with other zones, e.g. through a door. - var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number - //of points they're connected at. - var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed. var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out. + + var/list/connections //connection objects which refer to connections with other zones, e.g. through a door. + var/list/direct_connections //connections which directly connect two zones. + + var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number + //of points they're connected at. + var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed. + var/last_update = 0 var/progress = "nothing" @@ -49,23 +53,22 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs zones.Add(src) - //LEGACY, DO NOT USE. Use the SoftDelete proc. +//DO NOT USE. Use the SoftDelete proc. /zone/Del() //Ensuring the zone list doesn't get clogged with null values. for(var/turf/simulated/T in contents) RemoveTurf(T) - air_master.tiles_to_reconsider_zones += T + air_master.ReconsiderTileZone(T) for(var/zone/Z in connected_zones) if(src in Z.connected_zones) Z.connected_zones.Remove(src) - for(var/connection/C in connections) - air_master.connections_to_check += C + air_master.AddConnectionToCheck(connections) zones.Remove(src) air = null . = ..() - //Handles deletion via garbage collection. +//Handles deletion via garbage collection. /zone/proc/SoftDelete() zones.Remove(src) air = null @@ -73,7 +76,7 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs //Ensuring the zone list doesn't get clogged with null values. for(var/turf/simulated/T in contents) RemoveTurf(T) - air_master.tiles_to_reconsider_zones += T + air_master.ReconsiderTileZone(T) //Removing zone connections and scheduling connection cleanup for(var/zone/Z in connected_zones) @@ -81,8 +84,7 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs Z.connected_zones.Remove(src) connected_zones = null - for(var/connection/C in connections) - air_master.connections_to_check += C + air_master.AddConnectionToCheck(connections) connections = null return 1 @@ -99,7 +101,9 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs contents += T if(air) air.group_multiplier++ + T.zone = src + else if(!unsimulated_tiles) unsimulated_tiles = list() @@ -116,8 +120,13 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs contents -= T if(air) air.group_multiplier-- + if(T.zone == src) T.zone = null + + if(!contents.len) + SoftDelete() + else if(unsimulated_tiles) unsimulated_tiles -= T if(!unsimulated_tiles.len) @@ -226,17 +235,13 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs progress = "problem with: ZMerge(), a couple of misc procs" - for(var/connection/C in connections) - //Check if the connection is valid first. - if(!C.Cleanup()) - continue + if(length(direct_connections)) + for(var/connection/C in direct_connections) - //Do merging if conditions are met. Specifically, if there's a non-door connection - //to somewhere with space, the zones are merged regardless of equilibrium, to speed - //up spacing in areas with double-plated windows. - if(C && C.A.zone && C.B.zone) - //indirect = 2 is a direct connection. - if( C.indirect == 2 ) + //Do merging if conditions are met. Specifically, if there's a non-door connection + //to somewhere with space, the zones are merged regardless of equilibrium, to speed + //up spacing in areas with double-plated windows. + if(C.A.zone && C.B.zone) if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles) ZMerge(C.A.zone,C.B.zone) @@ -266,6 +271,7 @@ var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs //If that zone has already processed, skip it. if(Z.last_update > last_update) continue + if(air && Z.air) if( abs(air.temperature - Z.air.temperature) > vsc.connection_temperature_delta ) ShareHeat(air, Z.air, closed_connection_zones[Z]) @@ -383,7 +389,7 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output) unsim_co2 *= correction_ratio unsim_nitrogen *= correction_ratio unsim_plasma *= correction_ratio - unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen,unsim_co2,unsim_nitrogen,unsim_plasma) + unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma) var ratio = sharing_lookup_table[6] @@ -402,6 +408,9 @@ proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output) co2_avg = (full_co2 + unsim_co2) / (size + share_size) plasma_avg = (full_plasma + unsim_plasma) / (size + share_size) + temp_avg = 0 + + if((full_heat_capacity + unsim_heat_capacity) > 0) temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity) if(sharing_lookup_table.len >= unsimulated_tiles.len) //6 or more interconnecting tiles will max at 42% of air moved per tick. @@ -451,72 +460,146 @@ proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles) /////////////////// //Zone Rebuilding// /////////////////// +//Used for updating zone geometry when a zone is cut into two parts. zone/proc/Rebuild() + var/list/new_zone_contents = IsolateContents() + if(new_zone_contents.len == 1) + return + + var/list/current_contents + var/list/new_zones = list() + + contents = new_zone_contents[1] + air.group_multiplier = contents.len + + for(var/identifier in 2 to new_zone_contents.len) + current_contents = new_zone_contents[identifier] + var/zone/new_zone = new (current_contents) + new_zone.air.copy_from(air) + new_zones += new_zone + + for(var/connection/connection in connections) + connection.Cleanup() + + var/turf/simulated/adjacent + + for(var/turf/unsimulated in unsimulated_tiles) + for(var/direction in cardinal) + adjacent = get_step(unsimulated, direction) + + if(istype(adjacent) && adjacent.CanPass(null, unsimulated, 0, 0)) + for(var/zone/zone in new_zones) + if(adjacent in zone) + zone.AddTurf(unsimulated) + + +//Implements a two-pass connected component labeling algorithm to determine if the zone is, in fact, split. + +/zone/proc/IsolateContents() + var/list/current_adjacents = list() + var/adjacent_id + var/lowest_id + + var/list/identical_ids = list() + var/list/turfs = contents.Copy() + var/current_identifier = 1 + + for(var/turf/simulated/current in turfs) + lowest_id = null + current_adjacents = list() + + for(var/direction in cardinal) + if( !(current.air_check_directions & direction)) + continue + var/turf/simulated/adjacent = get_step(current, direction) + if(adjacent in turfs) + current_adjacents += adjacent + adjacent_id = turfs[adjacent] + + if(adjacent_id && (!lowest_id || adjacent_id < lowest_id)) + lowest_id = adjacent_id + + if(!lowest_id) + lowest_id = current_identifier++ + identical_ids += lowest_id + + for(var/turf/simulated/adjacent in current_adjacents) + adjacent_id = turfs[adjacent] + if(adjacent_id != lowest_id) + if(adjacent_id) + identical_ids[adjacent_id] = lowest_id + turfs[adjacent] = lowest_id + turfs[current] = lowest_id + + var/list/final_arrangement = list() + + for(var/turf/simulated/current in turfs) + current_identifier = identical_ids[turfs[current]] + + if( current_identifier > final_arrangement.len ) + final_arrangement.len = current_identifier + final_arrangement[current_identifier] = list(current) + + else + final_arrangement[current_identifier] += current + + //lazy but fast + final_arrangement.Remove(null) + + return final_arrangement + + +/* + if(!RequiresRebuild()) + return + //Choose a random turf and regenerate the zone from it. - var - turf/simulated/sample = locate() in contents - list/new_contents - problem = 0 + var/list/new_contents + var/list/new_unsimulated - // - var/list/turfs_to_consider = contents.Copy() + var/list/turfs_needing_zones = list() - while(!sample || !sample.CanPass(null, sample, 1.5, 1)) - if(sample) - turfs_to_consider.Remove(sample) - sample = locate() in turfs_to_consider - if(!sample) - break + var/list/zones_to_check_connections = list(src) - if(!istype(sample) || !sample.CanPass(null, sample, 1.5, 1)) //Not a single valid turf. - for(var/turf/simulated/T in contents) - air_master.tiles_to_update |= T + if(!locate(/turf/simulated/floor) in contents) + for(var/turf/simulated/turf in contents) + air_master.ReconsiderTileZone(turf) return SoftDelete() - new_contents = FloodFill(sample) + var/turfs_to_ignore = list() + if(direct_connections) + for(var/connection/connection in direct_connections) + if(connection.A.zone != src) + turfs_to_ignore += A + else if(connection.B.zone != src) + turfs_to_ignore += B - var/list/new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() ) + new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() ) + //Now, we have allocated the new turfs into proper lists, and we can start actually rebuilding. + + //If something isn't carried over, it will need a new zone. + for(var/turf/T in contents) + if(!(T in new_contents)) + RemoveTurf(T) + turfs_needing_zones += T + + //Handle addition of new turfs for(var/turf/S in new_contents) if(!istype(S, /turf/simulated)) new_unsimulated |= S new_contents.Remove(S) - if(contents.len != new_contents.len) - problem = 1 + //If something new is added, we need to deal with it seperately. + else if(!(S in contents) && istype(S, /turf/simulated)) + if(!(S.zone in zones_to_check_connections)) + zones_to_check_connections += S.zone - //If something isn't carried over, there was a complication. - for(var/turf/T in contents) - if(!(T in new_contents)) - T.zone = null - problem = 1 + S.zone.RemoveTurf(S) + AddTurf(S) - if(problem) - //Build some new zones for stuff that wasn't included. - var/list/turf/simulated/rebuild_turfs = contents - new_contents - var/list/turf/simulated/reconsider_turfs = list() - contents = new_contents - for(var/turf/simulated/T in rebuild_turfs) - if(!T.zone && T.CanPass(null, T, 1.5, 1)) - var/zone/Z = new /zone(T) - Z.air.copy_from(air) - else - reconsider_turfs |= T - for(var/turf/simulated/T in reconsider_turfs) - if(!T.zone && T.CanPass(null, T, 1.5, 1)) - var/zone/Z = new /zone(T) - Z.air.copy_from(air) - else if(!T in air_master.tiles_to_update) - air_master.tiles_to_update.Add(T) - - for(var/turf/simulated/T in contents) - if(T.zone && T.zone != src) - T.zone.RemoveTurf(T) - T.zone = src - else if(!T.zone) - T.zone = src - air.group_multiplier = contents.len + //Handle the addition of new unsimulated tiles. unsimulated_tiles = null if(new_unsimulated.len) @@ -528,20 +611,13 @@ zone/proc/Rebuild() if(istype(T) && T.zone && S.CanPass(null, T, 0, 0)) T.zone.AddTurf(S) -//UNUSED -/* -zone/proc/connected_zones() - //A legacy proc for getting connected zones. - . = list() - for(var/connection/C in connections) - var/zone/Z - if(C.A.zone == src) - Z = C.B.zone - else - Z = C.A.zone + //Finally, handle the orphaned turfs + + for(var/turf/simulated/T in turfs_needing_zones) + if(!T.zone) + zones_to_check_connections += new /zone(T) + + for(var/zone/zone in zones_to_check_connections) + for(var/connection/C in zone.connections) + C.Cleanup()*/ - if(Z in .) - .[Z]++ - else - . += Z - .[Z] = 1*/ diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index f880ad66d81..d3ddef9f37e 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -57,7 +57,7 @@ datum/controller/game_controller/proc/setup() if(!air_master) air_master = new /datum/controller/air_system() - air_master.setup() + air_master.Setup() if(!ticker) ticker = new /datum/controller/gameticker() @@ -127,8 +127,7 @@ datum/controller/game_controller/proc/process() timer = world.timeofday last_thing_processed = air_master.type - air_master.current_cycle++ - if(!air_master.tick()) //Runtimed. + if(!air_master.Tick()) //Runtimed. air_master.failed_ticks++ if(air_master.failed_ticks > 5) world << "RUNTIMES IN ATMOS TICKER. Killing air simulation!" @@ -137,7 +136,8 @@ datum/controller/game_controller/proc/process() log_admin("ZASALERT: unable run zone/process() -- [air_master.tick_progress]") air_processing_killed = 1 air_master.failed_ticks = 0 - air_cost = (world.timeofday - timer) / 10 + + air_cost = (world.timeofday - timer) / 10 sleep(breather_ticks) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 6aec44f7f17..0f64866f459 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -257,36 +257,12 @@ return 1 /obj/machinery/door/proc/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 + if(!air_master) + return 0 - var/turf/simulated/source = loc - var/turf/simulated/north = get_step(source,NORTH) - var/turf/simulated/south = get_step(source,SOUTH) - var/turf/simulated/east = get_step(source,EAST) - var/turf/simulated/west = get_step(source,WEST) - - update_heat_protection(loc) - - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west - - if(width > 1) - var/turf/simulated/next_turf = src - var/step_dir = turn(dir, 180) - for(var/current_step = 2, current_step <= width, current_step++) - next_turf = get_step(src, step_dir) - north = get_step(next_turf, step_dir) - east = get_step(next_turf, turn(step_dir, 90)) - south = get_step(next_turf, turn(step_dir, -90)) - - update_heat_protection(next_turf) - - if(istype(north)) air_master.tiles_to_update |= north - if(istype(south)) air_master.tiles_to_update |= south - if(istype(east)) air_master.tiles_to_update |= east + for(var/turf/simulated/turf in locs) + update_heat_protection(turf) + air_master.AddTurfToUpdate(turf) return 1 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 66184774c49..c8d0d4d110b 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -15,13 +15,10 @@ /obj/machinery/door/window/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 + if(!air_master) + return 0 - var/turf/simulated/source = get_turf(src) - var/turf/simulated/target = get_step(source,dir) - - if(istype(source)) air_master.tiles_to_update |= source - if(istype(target)) air_master.tiles_to_update |= target + air_master.AddTurfToUpdate(get_turf(src)) return 1 diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 6b657bda789..640fc00afcd 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -27,19 +27,10 @@ //Looks like copy/pasted code... I doubt 'need_rebuild' is even used here - Nodrak /obj/machinery/shield/proc/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 + if(!air_master) + return 0 - var/turf/simulated/source = get_turf(src) - var/turf/simulated/north = get_step(source,NORTH) - var/turf/simulated/south = get_step(source,SOUTH) - var/turf/simulated/east = get_step(source,EAST) - var/turf/simulated/west = get_step(source,WEST) - - if(istype(source)) air_master.tiles_to_update |= source - if(istype(north)) air_master.tiles_to_update |= north - if(istype(south)) air_master.tiles_to_update |= south - if(istype(east)) air_master.tiles_to_update |= east - if(istype(west)) air_master.tiles_to_update |= west + air_master.AddTurfToUpdate(get_turf(src)) return 1 diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index d3e7144297a..c0f783c52d6 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -1030,19 +1030,10 @@ steam.start() -- spawns the effect proc/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 + if(!air_master) + return 0 - var/turf/simulated/source = get_turf(src) - var/turf/simulated/north = get_step(source,NORTH) - var/turf/simulated/south = get_step(source,SOUTH) - var/turf/simulated/east = get_step(source,EAST) - var/turf/simulated/west = get_step(source,WEST) - - if(istype(source)) air_master.tiles_to_update |= source - if(istype(north)) air_master.tiles_to_update |= north - if(istype(south)) air_master.tiles_to_update |= south - if(istype(east)) air_master.tiles_to_update |= east - if(istype(west)) air_master.tiles_to_update |= west + air_master.AddTurfToUpdate(get_turf(src)) return 1 diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index 04b3bdc19c5..64e9623fcaf 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -156,20 +156,9 @@ return proc/update_nearby_tiles(need_rebuild) //Copypasta from airlock code - if(!air_master) return 0 - - var/turf/simulated/source = loc - var/turf/simulated/north = get_step(source,NORTH) - var/turf/simulated/south = get_step(source,SOUTH) - var/turf/simulated/east = get_step(source,EAST) - var/turf/simulated/west = get_step(source,WEST) - - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west - + if(!air_master) + return 0 + air_master.AddTurfToUpdate(get_turf(src)) return 1 /obj/structure/mineral_door/iron diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 478f6989baa..f13f3f1decc 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -294,12 +294,9 @@ obj/structure/windoor_assembly/Del() return /obj/structure/windoor_assembly/proc/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 + if(!air_master) + return 0 - var/turf/simulated/source = loc - var/turf/simulated/target = get_step(source,dir) - - if(istype(source)) air_master.tiles_to_update += source - if(istype(target)) air_master.tiles_to_update += target + air_master.AddTurfToUpdate(loc) return 1 diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index bb38bbcd854..60799f618e3 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -316,21 +316,9 @@ //This proc has to do with airgroups and atmos, it has nothing to do with smoothwindows, that's update_nearby_tiles(). /obj/structure/window/proc/update_nearby_tiles(need_rebuild) - if(!air_master) return 0 - if(!dir in cardinal) - var/turf/simulated/source = get_turf(src) - if(istype(source)) - air_master.tiles_to_update |= source - for(var/dir in cardinal) - var/turf/simulated/target = get_step(source,dir) - if(istype(target)) air_master.tiles_to_update |= target - return 1 - - var/turf/simulated/source = get_turf(src) - var/turf/simulated/target = get_step(source,dir) - - if(istype(source)) air_master.tiles_to_update |= source - if(istype(target)) air_master.tiles_to_update |= target + if(!air_master) + return 0 + air_master.AddTurfToUpdate(get_turf(src)) return 1 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index dc15cc4c9d8..1a5675609c3 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -231,8 +231,8 @@ W.zone = src.zone W.zone.AddTurf(W) - for(var/turf/simulated/T in orange(src,1)) - air_master.tiles_to_update.Add(T) + if(air_master) + air_master.AddTurfToUpdate(src) W.levelupdate() return W @@ -252,8 +252,7 @@ W.zone.AddTurf(W) if(air_master) - for(var/turf/simulated/T in orange(src,1)) - air_master.tiles_to_update.Add(T) + air_master.AddTurfToUpdate(src) W.levelupdate() return W diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 9cf56cb6ff7..c7168d93af5 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -581,11 +581,11 @@ var/list/admin_verbs_mod = list( set category = "Debug" set name = "Kill Air" set desc = "Toggle Air Processing" - if(kill_air) - kill_air = 0 + if(air_processing_killed) + air_processing_killed = 0 usr << "Enabled air processing." else - kill_air = 1 + air_processing_killed = 1 usr << "Disabled air processing." feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] used 'kill air'.") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index af7f4f545e2..76f1b420461 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -161,6 +161,8 @@ var/intercom_range_display_status = 0 src.verbs += /client/proc/disable_movement src.verbs += /client/proc/Zone_Info src.verbs += /client/proc/Test_ZAS_Connection + src.verbs += /client/proc/ZoneTick + src.verbs += /client/proc/TestZASRebuild //src.verbs += /client/proc/cmd_admin_rejuvenate feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!