mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-22 11:37:40 +01:00
Adds dynamic airlocks, a flexible mapping tool (#28852)
* move airlock controller defines to the right place * goddamnit * Adds dynamic airlocks, a flexible mapping tool * Apply suggestions from code review Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com> Signed-off-by: warriorstar-orion <orion@snowfrost.garden> * final as anythings * break some long comment lines * rebuild rust just in case --------- Signed-off-by: warriorstar-orion <orion@snowfrost.garden> Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
RESTRICT_TYPE(/datum/dynamic_airlock_linker)
|
||||
|
||||
/// A manager that is used on mapload to generate airlock chambers based on the
|
||||
/// usage of [/obj/effect/map_effect/dynamic_airlock] helpers. These helpers
|
||||
/// make up the "zone" of an airlock chamber, as well as indicate which airlocks
|
||||
/// are exterior or interior, what access should be applied to the doors and
|
||||
/// controls, And what buttons, vents, and airlock controllers should be linked
|
||||
/// together. This is performed on Initialize of one of the related helpers.
|
||||
/// Once done, all the other helpers are deleted to prevent it happening again
|
||||
/// for that zone.
|
||||
/datum/dynamic_airlock_linker
|
||||
/// A list of [/obj/effect/map_effect/dynamic_airlock/door/interior]s to integrate into the airlock.
|
||||
var/list/interior_helpers = list()
|
||||
/// A list of [/obj/effect/map_effect/dynamic_airlock/door/exterior]s to integrate into the airlock.
|
||||
var/list/exterior_helpers = list()
|
||||
/// A collection of "all" access defines collated from any
|
||||
/// [/obj/effect/map_effect/dynamic_airlock/access] helpers present in the airlock.
|
||||
var/list/req_access = list()
|
||||
/// A collection of "any" access defines collated from any
|
||||
/// [/obj/effect/map_effect/dynamic_airlock/access] helpers present in the airlock.
|
||||
var/list/req_one_access = list()
|
||||
/// A list of all [/obj/machinery/airlock_controller]s found in the airlock zone.
|
||||
var/list/airlock_controllers = list()
|
||||
/// A list of all [/obj/machinery/atmospherics/unary/vent_pump/high_volume] vent pumps found in the airlock zone.
|
||||
var/list/vent_pumps = list()
|
||||
|
||||
/// Starting from an arbitrary initial helper, search out from all adjacent
|
||||
/// tiles and find all other helpers that are part of this zone.
|
||||
///
|
||||
/// Right now this is a (relatively) busy proc that can't leave any setup to
|
||||
/// the Initialize of any helpers in its zone, because airlock controllers need
|
||||
/// all of their airlocks etc known when linkage occurs, which is in
|
||||
/// LateInitialize. I'm not sure if that's something that can be "fixed".
|
||||
/datum/dynamic_airlock_linker/proc/extend_zone(obj/effect/map_effect/dynamic_airlock/helper, list/visited_helpers)
|
||||
if(helper in visited_helpers)
|
||||
return
|
||||
|
||||
visited_helpers |= helper
|
||||
for(var/atom/A in get_turf(helper))
|
||||
helper.collect_sibling_item(A)
|
||||
|
||||
helper.collect_neighbor_helpers()
|
||||
|
||||
if(length(helper.sibling_items))
|
||||
for(var/atom/sibling_item as anything in helper.sibling_items)
|
||||
if(istype(sibling_item, /obj/machinery/airlock_controller))
|
||||
airlock_controllers |= sibling_item
|
||||
if(istype(sibling_item, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
vent_pumps |= sibling_item
|
||||
|
||||
if(istype(helper, /obj/effect/map_effect/dynamic_airlock/door/interior))
|
||||
interior_helpers |= helper
|
||||
if(istype(helper, /obj/effect/map_effect/dynamic_airlock/door/exterior))
|
||||
exterior_helpers |= helper
|
||||
|
||||
consume_access_helpers(helper)
|
||||
|
||||
for(var/obj/effect/map_effect/dynamic_airlock/neighbor in helper.neighbor_helpers)
|
||||
extend_zone(neighbor, visited_helpers)
|
||||
|
||||
/// Use all the information discovered about the airlock zone to link up the
|
||||
/// included airlock controllers, buttons, airlocks, and vent pumps. Make sure
|
||||
/// that all helpers found in this process are qdel'd so that no other helper
|
||||
/// attempts to do the same thing with this zone.
|
||||
/datum/dynamic_airlock_linker/proc/build(obj/effect/map_effect/dynamic_airlock/origin_helper)
|
||||
var/list/visited_helpers = list()
|
||||
var/list/interior_airlocks = list()
|
||||
var/list/exterior_airlocks = list()
|
||||
|
||||
extend_zone(origin_helper, visited_helpers)
|
||||
var/id_to_link = UID()
|
||||
|
||||
for(var/obj/machinery/atmospherics/unary/vent_pump/high_volume/vent_pump as anything in vent_pumps)
|
||||
vent_pump.autolink_id = VENT_ID(id_to_link)
|
||||
|
||||
for(var/obj/effect/map_effect/dynamic_airlock/door/door_helper as anything in interior_helpers)
|
||||
if(!door_helper.airlock)
|
||||
stack_trace("dynamic airlock interior door helper without a door at [COORD(door_helper)]")
|
||||
|
||||
door_helper.assign_ids(INT_BTN_ID(id_to_link), INT_DOOR_ID(id_to_link))
|
||||
door_helper.assign_access(src)
|
||||
interior_airlocks |= door_helper.airlock.UID()
|
||||
for(var/obj/effect/map_effect/dynamic_airlock/door/door_helper as anything in exterior_helpers)
|
||||
if(!door_helper.airlock)
|
||||
stack_trace("dynamic airlock exterior door helper without a door at [COORD(door_helper)]")
|
||||
|
||||
door_helper.assign_ids(EXT_BTN_ID(id_to_link), EXT_DOOR_ID(id_to_link))
|
||||
door_helper.assign_access(src)
|
||||
exterior_airlocks |= door_helper.airlock.UID()
|
||||
|
||||
for(var/obj/machinery/airlock_controller/controller as anything in airlock_controllers)
|
||||
controller.vent_link_id = VENT_ID(id_to_link)
|
||||
|
||||
controller.int_door_link_id = INT_DOOR_ID(id_to_link)
|
||||
controller.int_button_link_id = INT_BTN_ID(id_to_link)
|
||||
|
||||
controller.ext_door_link_id = EXT_DOOR_ID(id_to_link)
|
||||
controller.ext_button_link_id = EXT_BTN_ID(id_to_link)
|
||||
|
||||
controller.req_access = req_access
|
||||
controller.req_one_access = req_one_access
|
||||
|
||||
// despite airlock controllers looking this up in GLOB.airlocks in
|
||||
// LateInitialize, we need to hold their hand here because *we're*
|
||||
// (typically) in Initialize, so not all relevant airlocks may have been
|
||||
// added to GLOB.airlocks yet.
|
||||
controller.interior_doors = interior_airlocks.Copy()
|
||||
controller.exterior_doors = exterior_airlocks.Copy()
|
||||
|
||||
QDEL_LIST_CONTENTS(interior_helpers)
|
||||
QDEL_LIST_CONTENTS(exterior_helpers)
|
||||
QDEL_LIST_CONTENTS(visited_helpers)
|
||||
|
||||
airlock_controllers.Cut()
|
||||
interior_airlocks.Cut()
|
||||
exterior_airlocks.Cut()
|
||||
vent_pumps.Cut()
|
||||
|
||||
|
||||
/// Find all the [/obj/effect/mapping_helpers/airlock/access] helpers on the
|
||||
/// passed in helper's tile, and add them to the list of accesses we'll assign
|
||||
/// to the linked objects later. Make sure that all mapping helpers found in
|
||||
/// this process are qdel'd so they don't attempt to assign access to anything
|
||||
/// else later.
|
||||
/datum/dynamic_airlock_linker/proc/consume_access_helpers(obj/effect/map_effect/dynamic_airlock/helper)
|
||||
for(var/obj/effect/mapping_helpers/airlock/access/any/any_helper as anything in get_turf(helper))
|
||||
req_one_access |= any_helper.access
|
||||
qdel(any_helper)
|
||||
for(var/obj/effect/mapping_helpers/airlock/access/all/all_helper as anything in get_turf(helper))
|
||||
req_access |= all_helper.access
|
||||
qdel(all_helper)
|
||||
@@ -0,0 +1,82 @@
|
||||
/// A mapping helper that defines the zone of a dynamically arranged airlock
|
||||
/// chamber. This can be used as an alternative to
|
||||
/// [/obj/effect/spawner/airlock]s.
|
||||
///
|
||||
/// The disadvantage compared to airlock spawners is that all related buttons,
|
||||
/// airlock controllers, doors, vent pumps and piping must be manually mapped
|
||||
/// in. The advantages are that chambers of any arbitrary size and shape can be
|
||||
/// created, and that maps using these helpers instead of the airlock spawners
|
||||
/// can have the RandomOrientation mapmanip applied to them with confidence that
|
||||
/// the airlock chambers will be properly constructed, no matter what the
|
||||
/// orientation of the map is.
|
||||
/obj/effect/map_effect/dynamic_airlock
|
||||
icon = 'icons/effects/mapping_helpers.dmi'
|
||||
icon_state = "airlock_zone"
|
||||
name = "dynamic airlock zone"
|
||||
layer = POINT_LAYER
|
||||
var/list/sibling_items
|
||||
var/list/neighbor_helpers
|
||||
var/list/valid_siblings = list(
|
||||
/obj/machinery/airlock_controller/air_cycler,
|
||||
/obj/machinery/atmospherics/unary/vent_pump/high_volume,
|
||||
)
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/Initialize(mapload)
|
||||
..()
|
||||
|
||||
. = INITIALIZE_HINT_QDEL
|
||||
// One helper out of all the connected ones will actually set everything up
|
||||
// and qdel all the connected ones so the linker doesn't attempt to do its
|
||||
// thing more than once per zone
|
||||
if(QDELETED(src))
|
||||
return
|
||||
|
||||
var/datum/dynamic_airlock_linker/linker = new()
|
||||
linker.build(src)
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/proc/collect_sibling_item(atom/A)
|
||||
if(is_type_in_list(A, valid_siblings))
|
||||
LAZYADD(sibling_items, A)
|
||||
|
||||
/// Look around us in a 1 tile range and aggregate all the adjacent helpers.
|
||||
/obj/effect/map_effect/dynamic_airlock/proc/collect_neighbor_helpers()
|
||||
var/turf/center = get_turf(src)
|
||||
for(var/turf/T as anything in RANGE_EDGE_TURFS(1, center))
|
||||
for(var/obj/effect/map_effect/dynamic_airlock/A as anything in T)
|
||||
LAZYOR(neighbor_helpers, A)
|
||||
|
||||
/// A helper used to indicate what doors are connected to an airlock zone. Comes
|
||||
/// in interior and exterior variants as subtypes.
|
||||
/obj/effect/map_effect/dynamic_airlock/door
|
||||
var/list/buttons = list()
|
||||
var/obj/machinery/door/airlock/external/airlock
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/door/proc/assign_access(datum/dynamic_airlock_linker/linker)
|
||||
for(var/obj/machinery/access_button/button in buttons)
|
||||
button.req_access = linker.req_access
|
||||
button.req_one_access = linker.req_one_access
|
||||
|
||||
airlock.req_access = linker.req_access
|
||||
airlock.req_one_access = linker.req_one_access
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/door/proc/assign_ids(btn_id, door_id)
|
||||
for(var/obj/machinery/access_button/button as anything in buttons)
|
||||
button.autolink_id = btn_id
|
||||
|
||||
airlock.id_tag = door_id
|
||||
airlock.lock()
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/door/collect_sibling_item(atom/A)
|
||||
. = ..()
|
||||
if(istype(A, /obj/machinery/door/airlock/external))
|
||||
airlock = A
|
||||
if(istype(A, /obj/machinery/access_button))
|
||||
buttons |= A
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/door/interior
|
||||
name = "dynamic airlock interior door"
|
||||
icon_state = "airlock_interior"
|
||||
|
||||
/obj/effect/map_effect/dynamic_airlock/door/exterior
|
||||
name = "dynamic airlock exterior door"
|
||||
icon_state = "airlock_exterior"
|
||||
Reference in New Issue
Block a user