Merge pull request #8277 from Neerti/cynosure_map

Cynosure map
This commit is contained in:
Atermonera
2021-09-04 20:50:48 -07:00
committed by GitHub
97 changed files with 606906 additions and 71 deletions

View File

@@ -57,6 +57,7 @@ What is the naming convention for planes or layers?
// Turf Planes
#define PLATING_PLANE -44 // Plating
#define BELOW_UTILITY 2.05 // Below 'utility' objects like pipes/wires.
#define DISPOSAL_LAYER 2.1 // Under objects, even when planeswapped
#define PIPES_LAYER 2.2 // Under objects, even when planeswapped
#define WIRES_LAYER 2.3 // Under objects, even when planeswapped

View File

@@ -25,4 +25,4 @@
#define OUTDOORS_YES 1 // This being 1 helps with backwards compatibility.
#define OUTDOORS_NO 0 // Ditto.
#define OUTDOORS_AREA -1 // If a turf has this, it will defer to the area's settings on init.
// Note that after init, it will be either YES or NO.
// Note that after init, it will be either YES or NO.

View File

@@ -5,6 +5,7 @@ SUBSYSTEM_DEF(mapping)
flags = SS_NO_FIRE
var/list/map_templates = list()
var/list/map_template_types = list()
var/dmm_suite/maploader = null
var/obj/effect/landmark/engine_loader/engine_loader
var/list/shelter_templates = list()
@@ -40,6 +41,7 @@ SUBSYSTEM_DEF(mapping)
continue
template = new T()
map_templates[template.name] = template
map_template_types[template.type] = template
return TRUE
/datum/controller/subsystem/mapping/proc/loadEngine()

View File

@@ -59,6 +59,7 @@ SUBSYSTEM_DEF(planets)
P.planet_floors -= T
T.vis_contents -= P.weather_holder.visuals
T.vis_contents -= P.weather_holder.special_visuals
T.outdoors = OUTDOORS_NO
/datum/controller/subsystem/planets/fire(resumed = 0)

View File

@@ -122,6 +122,7 @@
if("cycle_ext")
//If airlock is already cycled in this direction, just toggle the doors.
if(!memory["purge"] && abs(memory["external_sensor_pressure"] - memory["chamber_sensor_pressure"]) <= SKIPCYCLE_MARGIN)
toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "close")
toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "toggle")
//only respond to these commands if the airlock isn't already doing something
//prevents the controller from getting confused and doing strange things
@@ -130,6 +131,7 @@
if("cycle_int")
if(!memory["purge"] && abs(memory["internal_sensor_pressure"] - memory["chamber_sensor_pressure"]) <= SKIPCYCLE_MARGIN)
toggleDoor(memory["exterior_status"], tag_exterior_door, memory["secure"], "close")
toggleDoor(memory["interior_status"], tag_interior_door, memory["secure"], "toggle")
else if(state == target_state)
begin_cycle_in()

View File

@@ -6,6 +6,7 @@
density = 0
anchored = 1.0
w_class = ITEMSIZE_NORMAL
layer = BELOW_UTILITY
plane = PLATING_PLANE
/obj/structure/lattice/Initialize()

View File

@@ -226,6 +226,10 @@
oxygen = 0
nitrogen = ATMOSTANK_NITROGEN
/turf/simulated/floor/reinforced/supermatter_core
oxygen = 0
nitrogen = MOLES_N2STANDARD
/turf/simulated/floor/reinforced/oxygen
oxygen = ATMOSTANK_OXYGEN
nitrogen = 0

View File

@@ -203,4 +203,4 @@ var/list/shoreline_icon_cache = list()
return
poisonlevel *= 1 - L.get_water_protection()
if(poisonlevel > 0)
L.adjustToxLoss(poisonlevel)
L.adjustToxLoss(poisonlevel)

View File

@@ -49,6 +49,8 @@
if(T)
GLOB.turf_entered_event.register(T, src, .proc/BelowOpenUpdated)
GLOB.turf_exited_event.register(T, src, .proc/BelowOpenUpdated)
if(is_outdoors())
SSplanets.addTurf(src)
/turf/simulated/open/Entered(var/atom/movable/mover, var/atom/oldloc)
..()

View File

@@ -5,13 +5,49 @@
var/known = 1 //shows up on nav computers automatically
var/scannable //if set to TRUE will show up on ship sensors for detailed scans
var/scanner_name //name for scans, replaces name once scanned
var/scanner_desc //description for scans
var/skybox_icon //Icon file to use for skybox
var/skybox_icon_state //Icon state to use for skybox
var/skybox_pixel_x //Shift from lower left corner of skybox
var/skybox_pixel_y //Shift from lower left corner of skybox
var/image/cached_skybox_image //Cachey
//Overlay of how this object should look on other skyboxes
/obj/effect/overmap/proc/get_skybox_representation()
return
if(!cached_skybox_image)
build_skybox_representation()
return cached_skybox_image
/obj/effect/overmap/proc/build_skybox_representation()
if(!skybox_icon)
return
var/image/I = image(icon = skybox_icon, icon_state = skybox_icon_state)
if(isnull(skybox_pixel_x))
skybox_pixel_x = rand(200,600)
if(isnull(skybox_pixel_y))
skybox_pixel_y = rand(200,600)
I.pixel_x = skybox_pixel_x
I.pixel_y = skybox_pixel_y
cached_skybox_image = I
/obj/effect/overmap/proc/expire_skybox_representation()
cached_skybox_image = null
/obj/effect/overmap/proc/update_skybox_representation()
expire_skybox_representation()
build_skybox_representation()
for(var/obj/effect/overmap/visitable/O in loc)
SSskybox.rebuild_skyboxes(O.map_z)
/obj/effect/overmap/proc/get_scan_data(mob/user)
return desc
if(scanner_name && (name != scanner_name)) //A silly check, but 'name' is part of appearance, so more expensive than you might think
name = scanner_name
var/dat = {"\[b\]Scan conducted at\[/b\]: [stationtime2text()] [stationdate2text()]\n\[b\]Grid coordinates\[/b\]: [x],[y]\n\n[scanner_desc]"}
return dat
/obj/effect/overmap/Initialize()
. = ..()

View File

@@ -22,29 +22,29 @@
/obj/effect/overmap/visitable/planet/get_skybox_representation()
var/image/skybox_image = image('icons/skybox/planet.dmi', "")
skybox_image.overlays += get_base_image()
skybox_image.add_overlay(get_base_image())
// for(var/datum/exoplanet_theme/theme in themes)
// skybox_image.overlays += theme.get_planet_image_extra()
// skybox_image.add_overlay(theme.get_planet_image_extra())
if(mountain_color)
var/image/mountains = image('icons/skybox/planet.dmi', "mountains")
mountains.color = mountain_color
mountains.appearance_flags = PIXEL_SCALE
skybox_image.overlays += mountains
skybox_image.add_overlay(mountains)
if(water_color)
var/image/water = image('icons/skybox/planet.dmi', "water")
water.color = water_color
water.appearance_flags = PIXEL_SCALE
// water.transform = water.transform.Turn(rand(0,360))
skybox_image.overlays += water
skybox_image.add_overlay(water)
if(icecaps)
var/image/ice = image('icons/skybox/planet.dmi', icecaps)
ice.color = ice_color
ice.appearance_flags = PIXEL_SCALE
skybox_image.overlays += ice
skybox_image.add_overlay(ice)
if(atmosphere && atmosphere.return_pressure() > SOUND_MINIMUM_PRESSURE)
@@ -55,20 +55,20 @@
var/image/clouds = image('icons/skybox/planet.dmi', "weak_clouds")
if(water_color)
clouds.overlays += image('icons/skybox/planet.dmi', "clouds")
clouds.add_overlay(image('icons/skybox/planet.dmi', "clouds"))
clouds.color = atmo_color
skybox_image.overlays += clouds
skybox_image.add_overlay(clouds)
var/image/atmo = image('icons/skybox/planet.dmi', "atmoring")
skybox_image.underlays += atmo
var/image/shadow = image('icons/skybox/planet.dmi', "shadow")
shadow.blend_mode = BLEND_MULTIPLY
skybox_image.overlays += shadow
skybox_image.add_overlay(shadow)
var/image/light = image('icons/skybox/planet.dmi', "lightrim")
skybox_image.overlays += light
skybox_image.add_overlay(light)
if(has_rings)
var/image/rings = image('icons/skybox/planet_rings.dmi')
@@ -79,7 +79,7 @@
rings.color = ring_color
rings.pixel_x = -128
rings.pixel_y = -128
skybox_image.overlays += rings
skybox_image.add_overlay(rings)
skybox_image.pixel_x = rand(0,64) + skybox_offset_x
skybox_image.pixel_y = rand(128,256) + skybox_offset_y

View File

@@ -42,7 +42,7 @@
if(moving_status == SHUTTLE_INTRANSIT)
return FALSE //already going somewhere, current_location may be an intransit location instead of in a sector
var/our_sector = waypoint_sector(current_location)
if(!our_sector && myship?.landmark && next_location == myship.landmark)
if(myship?.landmark && next_location == myship.landmark)
return TRUE //We're not on the overmap yet (admin spawned probably), and we're trying to hook up with our openspace sector
return get_dist(our_sector, waypoint_sector(next_location)) <= range

View File

@@ -4,8 +4,9 @@
/obj/effect/overmap/visitable
name = "map object"
scannable = TRUE
scanner_desc = "!! No Data Available !!"
var/list/map_z = null
var/list/map_z = list()
var/list/extra_z_levels //if you need to manually insist that these z-levels are part of this sector, for things like edge-of-map step trigger transitions rather than multi-z complexes
var/list/initial_generic_waypoints //store landmark_tag of landmarks that should be added to the actual lists below on init.
@@ -30,8 +31,7 @@
if(. == INITIALIZE_HINT_QDEL)
return
if(!map_z) // If map_z is already defined, we don't need to find where we are
find_z_levels() // This populates map_z and assigns z levels to the ship.
find_z_levels() // This populates map_z and assigns z levels to the ship.
register_z_levels() // This makes external calls to update global z level information.
if(!global.using_map.overmap_z)
@@ -57,8 +57,6 @@
//This is called later in the init order by SSshuttles to populate sector objects. Importantly for subtypes, shuttles will be created by then.
/obj/effect/overmap/visitable/proc/populate_sector_objects()
for(var/obj/machinery/computer/ship/S in global.machines)
S.attempt_hook_up(src)
/obj/effect/overmap/visitable/proc/get_areas()
. = list()
@@ -104,7 +102,7 @@
//Helper for init.
/obj/effect/overmap/visitable/proc/check_ownership(obj/object)
if((object.z in map_z) && !(get_area(object) in SSshuttles.shuttle_areas))
if((get_z(object) in map_z) && !(get_area(object) in SSshuttles.shuttle_areas))
return 1
//If shuttle_name is false, will add to generic waypoints; otherwise will add to restricted. Does not do checks.

View File

@@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(all_waypoints)
circuit = /obj/item/weapon/circuitboard/helm
core_skill = /datum/skill/pilot
var/autopilot = 0
var/autopilot_disabled = TRUE
var/list/known_sectors = list()
var/dx //desitnation
var/dy //coordinates
@@ -35,7 +36,7 @@ GLOBAL_LIST_EMPTY(all_waypoints)
/obj/machinery/computer/ship/helm/proc/get_known_sectors()
var/area/overmap/map = locate() in world
for(var/obj/effect/overmap/visitable/sector/S in map)
if (S.known)
if(S.known)
var/datum/computer_file/data/waypoint/R = new()
R.fields["name"] = S.name
R.fields["x"] = S.x
@@ -44,7 +45,7 @@ GLOBAL_LIST_EMPTY(all_waypoints)
/obj/machinery/computer/ship/helm/process()
..()
if (autopilot && dx && dy)
if(autopilot && dx && dy && !autopilot_disabled)
var/turf/T = locate(dx,dy,global.using_map.overmap_z)
if(linked.loc == T)
if(linked.is_still())
@@ -59,13 +60,13 @@ GLOBAL_LIST_EMPTY(all_waypoints)
var/heading = linked.get_heading()
// Destination is current grid or speedlimit is exceeded
if ((get_dist(linked.loc, T) <= brake_path) || speed > speedlimit)
if((get_dist(linked.loc, T) <= brake_path) || speed > speedlimit)
linked.decelerate()
// Heading does not match direction
else if (heading & ~direction)
else if(heading & ~direction)
linked.accelerate(turn(heading & ~direction, 180), accellimit)
// All other cases, move toward direction
else if (speed + acceleration <= speedlimit)
else if(speed + acceleration <= speedlimit)
linked.accelerate(direction, accellimit)
linked.operator_skill = null//if this is on you can't dodge meteors
return

View File

@@ -6,7 +6,6 @@
circuit = /obj/item/weapon/circuitboard/sensors
extra_view = 4
var/obj/machinery/shipsensors/sensors
whitelisted_types = list(/obj/effect/overmap/visitable) // Stationary emplacements can support sensors
/obj/machinery/computer/ship/sensors/attempt_hook_up(obj/effect/overmap/visitable/ship/sector)
if(!(. = ..()))

View File

@@ -7,28 +7,17 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
var/obj/effect/overmap/visitable/ship/linked
var/list/viewers // Weakrefs to mobs in direct-view mode.
var/extra_view = 0 // how much the view is increased by when the mob is in overmap mode.
var/list/whitelisted_types = list(/obj/effect/overmap/visitable/ship)
var/list/blacklisted_types = list()
/obj/machinery/computer/ship/New()
. = ..()
var/list/L = list()
for(var/type in whitelisted_types)
L |= typesof(type)
for(var/type in blacklisted_types)
L -= typesof(type)
whitelisted_types = L
// A late init operation called in SSshuttles, used to attach the thing to the right ship.
/obj/machinery/computer/ship/proc/attempt_hook_up(obj/effect/overmap/visitable/sector)
if(!sector || !(sector.type in whitelisted_types))
return FALSE
/obj/machinery/computer/ship/proc/attempt_hook_up(obj/effect/overmap/visitable/ship/sector)
if(!istype(sector))
return
if(sector.check_ownership(src))
linked = sector
return TRUE
return 1
/obj/machinery/computer/ship/proc/sync_linked(var/user = null)
var/obj/effect/overmap/visitable/sector = get_overmap_sector(z)
var/obj/effect/overmap/visitable/ship/sector = get_overmap_sector(z)
if(!sector)
return
. = attempt_hook_up_recursive(sector)
@@ -36,10 +25,10 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
to_chat(user, "<span class='notice'>[src] reconnected to [linked]</span>")
user << browse(null, "window=[src]") // close reconnect dialog
/obj/machinery/computer/ship/proc/attempt_hook_up_recursive(obj/effect/overmap/visitable/sector)
/obj/machinery/computer/ship/proc/attempt_hook_up_recursive(obj/effect/overmap/visitable/ship/sector)
if(attempt_hook_up(sector))
return sector
for(var/obj/effect/overmap/visitable/candidate in sector)
for(var/obj/effect/overmap/visitable/ship/candidate in sector)
if((. = .(candidate)))
return
@@ -48,6 +37,14 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
popup.set_content("<center><strong><font color = 'red'>Error</strong></font><br>Unable to connect to [flavor].<br><a href='?src=\ref[src];sync=1'>Reconnect</a></center>")
popup.open()
/obj/machinery/computer/ship/Topic(href, href_list)
if(..())
return TRUE
if(href_list["sync"])
if(sync_linked(usr))
interface_interact(usr)
return TRUE
// In computer_shims for now - we had to define it.
// /obj/machinery/computer/ship/interface_interact(var/mob/user)
// ui_interact(user)
@@ -71,6 +68,7 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
if(linked)
apply_visual(user)
user.reset_view(linked)
user.set_machine(src)
if(isliving(user))
var/mob/living/L = user
L.looking_elsewhere = 1
@@ -104,7 +102,7 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
look(user)
/obj/machinery/computer/ship/check_eye(var/mob/user)
if (!get_dist(user, src) > 1 || user.blinded || !linked )
if(!get_dist(user, src) > 1 || user.blinded || !linked)
unlook(user)
return -1
else

View File

@@ -11,6 +11,7 @@
moving_state = "shuttle_moving"
/obj/effect/overmap/visitable/ship/landable/Destroy()
GLOB.shuttle_pre_move_event.unregister(SSshuttles.shuttles[shuttle], src)
GLOB.shuttle_moved_event.unregister(SSshuttles.shuttles[shuttle], src)
return ..()
@@ -34,13 +35,18 @@
// We autobuild our z levels.
/obj/effect/overmap/visitable/ship/landable/find_z_levels()
src.landmark = new(null, shuttle) // Create in nullspace since we lazy-create overmap z
add_landmark(landmark, shuttle)
/obj/effect/overmap/visitable/ship/landable/proc/setup_overmap_location()
if(LAZYLEN(map_z))
return // We're already set up!
for(var/i = 0 to multiz)
world.increment_max_z()
map_z += world.maxz
var/turf/center_loc = locate(round(world.maxx/2), round(world.maxy/2), world.maxz)
landmark = new (center_loc, shuttle)
add_landmark(landmark, shuttle)
landmark.forceMove(center_loc)
var/visitor_dir = fore_dir
for(var/landmark_name in list("FORE", "PORT", "AFT", "STARBOARD"))
@@ -51,6 +57,8 @@
if(multiz)
new /obj/effect/landmark/map_data(center_loc, (multiz + 1))
register_z_levels()
testing("Setup overmap location for \"[name]\" containing Z [english_list(map_z)]")
/obj/effect/overmap/visitable/ship/landable/get_areas()
var/datum/shuttle/shuttle_datum = SSshuttles.shuttles[shuttle]
@@ -64,13 +72,19 @@
if(istype(shuttle_datum,/datum/shuttle/autodock/overmap))
var/datum/shuttle/autodock/overmap/oms = shuttle_datum
oms.myship = src
GLOB.shuttle_pre_move_event.register(shuttle_datum, src, .proc/pre_shuttle_jump)
GLOB.shuttle_moved_event.register(shuttle_datum, src, .proc/on_shuttle_jump)
on_landing(landmark, shuttle_datum.current_location) // We "land" at round start to properly place ourselves on the overmap.
//
// Center Landmark
//
/obj/effect/shuttle_landmark/ship
name = "Open Space"
landmark_tag = "ship"
flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G
flags = SLANDMARK_FLAG_ZERO_G // *Not* AUTOSET, these must be world.turf and world.area for lazy loading to work.
var/shuttle_name
var/list/visitors // landmark -> visiting shuttle stationed there
@@ -78,6 +92,7 @@
landmark_tag += "_[shuttle_name]"
src.shuttle_name = shuttle_name
. = ..()
base_turf = world.turf
/obj/effect/shuttle_landmark/ship/Destroy()
var/obj/effect/overmap/visitable/ship/landable/ship = get_overmap_sector(z)
@@ -85,10 +100,22 @@
ship.landmark = null
. = ..()
/obj/effect/shuttle_landmark/ship/is_valid(datum/shuttle/shuttle)
return (isnull(loc) || ..()) // If it doesn't exist yet, its clear
/obj/effect/shuttle_landmark/ship/create_warning_effect(var/datum/shuttle/shuttle)
if(isnull(loc))
return
..()
/obj/effect/shuttle_landmark/ship/cannot_depart(datum/shuttle/shuttle)
if(LAZYLEN(visitors))
return "Grappled by other shuttle; cannot manouver."
//
// Visitor Landmark
//
/obj/effect/shuttle_landmark/visiting_shuttle
flags = SLANDMARK_FLAG_AUTOSET | SLANDMARK_FLAG_ZERO_G
var/obj/effect/shuttle_landmark/ship/core_landmark
@@ -125,6 +152,17 @@
GLOB.shuttle_moved_event.unregister(shuttle, src)
LAZYREMOVE(core_landmark.visitors, src)
//
// More ship procs
//
/obj/effect/overmap/visitable/ship/landable/proc/pre_shuttle_jump(datum/shuttle/given_shuttle, obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into)
if(given_shuttle != SSshuttles.shuttles[shuttle])
return
if(into == landmark)
setup_overmap_location() // They're coming boys, better actually exist!
GLOB.shuttle_pre_move_event.unregister(SSshuttles.shuttles[shuttle], src)
/obj/effect/overmap/visitable/ship/landable/proc/on_shuttle_jump(datum/shuttle/given_shuttle, obj/effect/shuttle_landmark/from, obj/effect/shuttle_landmark/into)
if(given_shuttle != SSshuttles.shuttles[shuttle])
return

View File

@@ -10,8 +10,9 @@
// Uses Lorentzian dynamics to avoid going too fast.
/obj/effect/overmap/visitable/ship
name = "generic ship"
desc = "Space faring vessel."
name = "spacecraft"
desc = "This marker represents a spaceship. Scan it for more information."
scanner_desc = "Unknown spacefaring vessel."
dir = NORTH
icon_state = "ship"
appearance_flags = TILE_BOUND|KEEP_TOGETHER|LONG_GLIDE
@@ -58,8 +59,19 @@
/obj/effect/overmap/visitable/ship/get_scan_data(mob/user)
. = ..()
if(!is_still())
. += "<br>Heading: [get_heading_degrees()], speed [get_speed() * 1000]"
. += {"\n\[i\]Heading\[/i\]: [get_heading_degrees()]\n\[i\]Velocity\[/i\]: [get_speed() * 1000]"}
else
. += {"\n\[i\]Vessel was stationary at time of scan.\[/i\]\n"}
var/life = 0
for(var/mob/living/L in living_mob_list)
if(L.z in map_z) //Things inside things we'll consider shielded, otherwise we'd want to use get_z(L)
life++
. += {"\[i\]Life Signs\[/i\]: [life ? life : "None"]"}
//Projected acceleration based on information from engines
/obj/effect/overmap/visitable/ship/proc/get_acceleration()
@@ -239,6 +251,8 @@
/obj/effect/overmap/visitable/ship/populate_sector_objects()
..()
for(var/obj/machinery/computer/ship/S in global.machines)
S.attempt_hook_up(src)
for(var/datum/ship_engine/E in ship_engines)
if(check_ownership(E.holder))
engines |= E

View File

@@ -559,4 +559,4 @@ var/datum/planet/sif/planet_sif = null
if(!istype(T))
return
if(T.is_outdoors())
SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high))
SSradiation.radiate(T, rand(fallout_rad_low, fallout_rad_high))

View File

@@ -252,6 +252,11 @@
log_shuttle("Shuttle [src] aborting attempt_move() because current_location=[current_location] refuses.")
return FALSE
// Observer pattern pre-move
var/old_location = current_location
GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination)
current_location.shuttle_departed(src)
if(debug_logging)
log_shuttle("[src] moving to [destination]. Areas are [english_list(shuttle_area)]")
var/list/translation = list()
@@ -259,11 +264,6 @@
if(debug_logging)
log_shuttle("Translating [A]")
translation += get_turf_translation(get_turf(current_location), get_turf(destination), A.contents)
var/old_location = current_location
// Observer pattern pre-move
GLOB.shuttle_pre_move_event.raise_event(src, old_location, destination)
current_location.shuttle_departed(src)
// Actually do it! (This never fails)
perform_shuttle_move(destination, translation)

View File

@@ -42,23 +42,27 @@
var/list/zs_to_test = using_map.unit_test_z_levels || list(1) //Either you set it, or you just get z1
for(var/area/A in world)
if((A.z in zs_to_test) && !(A.type in exempt_areas))
if(is_type_in_list(A.type, exempt_areas))
continue
if((A.z in zs_to_test)) //is_type_in_list
area_test_count++
var/area_good = 1
var/area_good = TRUE
var/bad_msg = "--------------- [A.name]([A.type])"
if(is_type_in_list(A.type, exempt_from_apc))
if(isnull(A.apc))
log_unit_test("[bad_msg] lacks an APC.")
area_good = FALSE
if(isnull(A.apc) && !(A.type in exempt_from_apc))
log_unit_test("[bad_msg] lacks an APC.")
area_good = 0
if(is_type_in_list(A.type, exempt_from_atmos))
if(!A.air_scrub_info.len)
log_unit_test("[bad_msg] lacks an Air scrubber.")
area_good = FALSE
if(!A.air_scrub_info.len && !(A.type in exempt_from_atmos))
log_unit_test("[bad_msg] lacks an Air scrubber.")
area_good = 0
if(!A.air_vent_info.len && !(A.type in exempt_from_atmos))
log_unit_test("[bad_msg] lacks an Air vent.")
area_good = 0
if(is_type_in_list(A.type, exempt_from_atmos))
if(!A.air_vent_info.len)
log_unit_test("[bad_msg] lacks an Air vent.")
area_good = FALSE
if(!area_good)
bad_areas.Add(A)