mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-24 13:30:42 +01:00
Dynamic Turf Reservations Port (#22798)
Ports https://github.com/tgstation/tgstation/pull/38098 and https://github.com/tgstation/tgstation/pull/77786 Prereq for shuttle rotation. No player-facing changes.
This commit is contained in:
@@ -19,10 +19,14 @@
|
||||
|
||||
var/loaded = 0 // Times loaded this round
|
||||
var/static/dmm_suite/maploader = new
|
||||
var/datum/parsed_map/cached_map
|
||||
var/keep_cached_map = FALSE
|
||||
var/list/shuttles_to_initialise = list()
|
||||
var/base_turf_for_zs = null
|
||||
var/accessibility_weight = 0
|
||||
var/template_flags = TEMPLATE_FLAG_ALLOW_DUPLICATES
|
||||
/// Last admin-facing map loader failure reason.
|
||||
var/last_load_error = null
|
||||
|
||||
///A list of groups, as strings, that this template belongs to. When adding new map templates, try to keep this balanced on the CI execution time, or consider adding a new one
|
||||
///ONLY IF IT'S THE LONGEST RUNNING CI POD AND THEY ARE ALREADY BALANCED
|
||||
@@ -38,26 +42,107 @@
|
||||
if(rename)
|
||||
name = rename
|
||||
|
||||
/datum/map_template/proc/preload_size(path)
|
||||
var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
||||
/datum/map_template/proc/preload_size(path = null, cache = FALSE)
|
||||
last_load_error = null
|
||||
if(path)
|
||||
mappath = path
|
||||
if(!mappath)
|
||||
last_load_error = "no map source assigned"
|
||||
return FALSE
|
||||
|
||||
var/datum/map_load_metadata/M = maploader.load_map_impl(file(mappath), 1, 1, cropMap=FALSE, measureOnly=TRUE, no_changeturf=TRUE)
|
||||
var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
||||
var/map_source = get_map_source()
|
||||
if(!map_source)
|
||||
last_load_error = "unable to normalize map source ([describe_map_source()])"
|
||||
return FALSE
|
||||
|
||||
var/datum/map_load_metadata/M = maploader.load_map_impl(map_source, 1, 1, 1, cropMap = FALSE, measureOnly = TRUE, no_changeturf = TRUE, cache_metadata = cache)
|
||||
if(M)
|
||||
bounds = extend_bounds_if_needed(bounds, M.bounds)
|
||||
if(cache)
|
||||
cached_map = M.parsed_map
|
||||
else
|
||||
last_load_error = "DMM parser returned no measured bounds for [describe_map_source()]"
|
||||
return FALSE
|
||||
|
||||
width = bounds[MAP_MAXX] - bounds[MAP_MINX] + 1
|
||||
height = bounds[MAP_MAXY] - bounds[MAP_MINX] + 1
|
||||
height = bounds[MAP_MAXY] - bounds[MAP_MINY] + 1
|
||||
return bounds
|
||||
|
||||
/datum/map_template/proc/get_map_source()
|
||||
if(islist(mappath))
|
||||
var/list/map_sources = mappath
|
||||
if(!length(map_sources))
|
||||
return null
|
||||
return map_sources[1]
|
||||
if(isfile(mappath))
|
||||
return mappath
|
||||
return file(mappath)
|
||||
|
||||
/datum/map_template/proc/describe_map_source()
|
||||
if(islist(mappath))
|
||||
var/list/map_sources = mappath
|
||||
if(!length(map_sources))
|
||||
return "empty source list"
|
||||
var/source = map_sources[1]
|
||||
if(isfile(source))
|
||||
return "source list with file [source]"
|
||||
if(istext(source))
|
||||
return "source list with text length [length(source)]"
|
||||
return "source list with [source]"
|
||||
if(isfile(mappath))
|
||||
return "file [mappath]"
|
||||
if(istext(mappath))
|
||||
return "path [mappath]"
|
||||
return "[mappath]"
|
||||
|
||||
/// Takes in a type path, locates an instance of that type in the cached map, and calculates its offset from the origin of the map, returns this offset in the form list(x, y).
|
||||
/datum/map_template/proc/discover_offset(obj/marker)
|
||||
if(!cached_map && mappath)
|
||||
preload_size(mappath, TRUE)
|
||||
if(!cached_map || !cached_map.key_len)
|
||||
return null
|
||||
|
||||
var/key
|
||||
var/found_marker = FALSE
|
||||
var/list/models = cached_map.grid_models
|
||||
for(key in models)
|
||||
if(findtext(models[key], "[marker]")) // Yay compile time checks
|
||||
found_marker = TRUE
|
||||
break // This works by assuming there will ever only be one mobile dock in a template at most
|
||||
if(!found_marker)
|
||||
return null
|
||||
|
||||
for(var/datum/grid_set/gset as anything in cached_map.gridSets)
|
||||
var/ycrd = gset.ycrd
|
||||
for(var/line in gset.gridLines)
|
||||
var/xcrd = gset.xcrd
|
||||
for(var/j in 1 to length(line) step cached_map.key_len)
|
||||
if(key == copytext(line, j, j + cached_map.key_len))
|
||||
return list(xcrd, ycrd)
|
||||
++xcrd
|
||||
--ycrd
|
||||
return null
|
||||
|
||||
/datum/map_template/proc/post_load()
|
||||
return
|
||||
|
||||
/datum/map_template/proc/update_blacklist(turf/T, list/input_blacklist)
|
||||
return
|
||||
|
||||
/datum/map_template/proc/load_new_z(var/no_changeturf = TRUE)
|
||||
last_load_error = null
|
||||
var/x = round((world.maxx - width)/2)
|
||||
var/y = round((world.maxy - height)/2)
|
||||
|
||||
if (x < 1) x = 1
|
||||
if (y < 1) y = 1
|
||||
|
||||
var/map_source = get_map_source()
|
||||
if(!map_source)
|
||||
last_load_error = "unable to normalize map source ([describe_map_source()])"
|
||||
return FALSE
|
||||
|
||||
var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
||||
var/list/atoms_to_initialise = list()
|
||||
var/shuttle_state = pre_init_shuttles()
|
||||
@@ -72,13 +157,15 @@
|
||||
base_level = level
|
||||
GLOB.map_templates["[level.z_value]"] = src
|
||||
|
||||
var/datum/map_load_metadata/M = maploader.load_map(file(mappath), x, y, base_level.z_value, no_changeturf = no_changeturf)
|
||||
var/datum/map_load_metadata/M = maploader.load_map(map_source, x, y, base_level.z_value, no_changeturf = no_changeturf)
|
||||
|
||||
if(M)
|
||||
bounds = extend_bounds_if_needed(bounds, M.bounds)
|
||||
atoms_to_initialise += M.atoms_to_initialise
|
||||
else
|
||||
SSicon_smooth.can_fire = TRUE
|
||||
SSshuttle.block_queue = shuttle_state
|
||||
last_load_error = "DMM loader failed while loading new z from [describe_map_source()]"
|
||||
return FALSE
|
||||
|
||||
for (var/z_index = bounds[MAP_MINZ]; z_index <= bounds[MAP_MAXZ]; z_index++)
|
||||
@@ -173,26 +260,39 @@
|
||||
T.static_lighting_build_overlay()
|
||||
|
||||
/datum/map_template/proc/load(turf/T, centered = FALSE)
|
||||
last_load_error = null
|
||||
if(centered)
|
||||
T = locate(T.x - round(width / 2) , T.y - round(height / 2) , T.z)
|
||||
if(!T)
|
||||
last_load_error = "invalid placement turf"
|
||||
return
|
||||
if(T.x + width > world.maxx)
|
||||
if(T.x + width - 1 > world.maxx)
|
||||
last_load_error = "template would exceed world maxx ([T.x] + [width] - 1 > [world.maxx])"
|
||||
return
|
||||
if(T.y + height > world.maxy)
|
||||
if(T.y + height - 1 > world.maxy)
|
||||
last_load_error = "template would exceed world maxy ([T.y] + [height] - 1 > [world.maxy])"
|
||||
return
|
||||
|
||||
var/list/atoms_to_initialise = list()
|
||||
var/list/bounds
|
||||
var/map_source = get_map_source()
|
||||
if(!map_source)
|
||||
last_load_error = "unable to normalize map source ([describe_map_source()])"
|
||||
return FALSE
|
||||
|
||||
var/shuttle_state = pre_init_shuttles()
|
||||
|
||||
//Since SSicon_smooth.add_to_queue() manually wakes the subsystem, we have to use enable/disable.
|
||||
SSicon_smooth.can_fire = FALSE
|
||||
|
||||
var/datum/map_load_metadata/M = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE)
|
||||
var/datum/map_load_metadata/M = maploader.load_map(map_source, T.x, T.y, T.z, cropMap=TRUE)
|
||||
if(M)
|
||||
atoms_to_initialise += M.atoms_to_initialise
|
||||
bounds = M.bounds
|
||||
else
|
||||
SSicon_smooth.can_fire = TRUE
|
||||
SSshuttle.block_queue = shuttle_state
|
||||
last_load_error = "DMM loader failed while placing from [describe_map_source()]"
|
||||
return FALSE
|
||||
|
||||
//initialize things that are normally initialized after map load
|
||||
@@ -202,7 +302,8 @@
|
||||
SSicon_smooth.can_fire = TRUE
|
||||
message_admins("[name] loaded at [T.x], [T.y], [T.z]")
|
||||
log_game("[name] loaded at [T.x], [T.y], [T.z]")
|
||||
return TRUE
|
||||
cached_map = keep_cached_map ? cached_map : null
|
||||
return bounds
|
||||
|
||||
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
@@ -6,9 +6,29 @@
|
||||
GLOBAL_VAR_INIT(use_preloader, FALSE)
|
||||
GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
|
||||
/datum/grid_set
|
||||
var/xcrd
|
||||
var/ycrd
|
||||
var/zcrd
|
||||
var/gridLines
|
||||
|
||||
/datum/parsed_map
|
||||
var/original_path
|
||||
/// The length of a key in this file. This is promised by the standard to be static.
|
||||
var/key_len = 0
|
||||
/// The length of a line in this file. Not promised by dmm but standard dmm uses it, so we can trust it.
|
||||
var/line_len = 0
|
||||
var/list/grid_models = list()
|
||||
var/list/gridSets = list()
|
||||
/// Unoffset bounds. Null on parse failure.
|
||||
var/list/parsed_bounds
|
||||
/// Offset bounds. Same as parsed_bounds until load().
|
||||
var/list/bounds
|
||||
|
||||
/datum/map_load_metadata
|
||||
var/bounds
|
||||
var/list/atoms_to_initialise
|
||||
var/datum/parsed_map/parsed_map
|
||||
|
||||
/dmm_suite
|
||||
// /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
|
||||
@@ -57,8 +77,11 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
. = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y)
|
||||
Master.StopLoadingMap()
|
||||
|
||||
/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY)
|
||||
/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, cache_metadata = FALSE)
|
||||
var/tfile = dmm_file//the map file we're creating
|
||||
var/datum/parsed_map/parsed_map
|
||||
if(cache_metadata)
|
||||
parsed_map = new
|
||||
|
||||
/*#### WARNING AURORA SNOWFLAKE SECTION ####*/
|
||||
|
||||
@@ -66,6 +89,8 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
// name/path of dmm file, new var so as to not rename the `tfile` var
|
||||
// to maybe maintain compatibility with other codebases
|
||||
var/tfilepath = "[tfile]"
|
||||
if(parsed_map)
|
||||
parsed_map.original_path = tfilepath
|
||||
tfile = null
|
||||
// use bapi to read, parse, process, mapmanip etc
|
||||
// this will "crash"/stacktrace on fail
|
||||
@@ -91,6 +116,9 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
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/line_len = 0
|
||||
if(parsed_map)
|
||||
parsed_map.grid_models = grid_models
|
||||
|
||||
var/stored_index = 1
|
||||
|
||||
@@ -114,7 +142,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
key_len = length(key)
|
||||
else
|
||||
CRASH("Inconsistent key length in DMM")
|
||||
if(!measureOnly)
|
||||
if(!measureOnly || cache_metadata)
|
||||
grid_models[key] = regexOutput[2]
|
||||
|
||||
// (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
||||
@@ -123,15 +151,24 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
CRASH("Coords before model definition in DMM")
|
||||
|
||||
var/curr_x = text2num(regexOutput[3])
|
||||
var/curr_y = text2num(regexOutput[4])
|
||||
var/curr_z = text2num(regexOutput[5])
|
||||
|
||||
if(curr_x < x_lower || curr_x > x_upper)
|
||||
continue
|
||||
|
||||
var/datum/grid_set/gridSet
|
||||
if(parsed_map)
|
||||
gridSet = new
|
||||
gridSet.xcrd = curr_x
|
||||
gridSet.ycrd = curr_y
|
||||
gridSet.zcrd = curr_z
|
||||
|
||||
var/xcrdStart = curr_x + x_offset - 1
|
||||
//position of the currently processed square
|
||||
var/xcrd
|
||||
var/ycrd = text2num(regexOutput[4]) + y_offset - 1
|
||||
var/zcrd = text2num(regexOutput[5]) + z_offset - 1
|
||||
var/ycrd = curr_y + y_offset - 1
|
||||
var/zcrd = curr_z + z_offset - 1
|
||||
|
||||
var/zexpansion = zcrd > world.maxz
|
||||
if(zexpansion && !measureOnly) // don't actually expand the world if we're only measuring bounds
|
||||
@@ -149,6 +186,8 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
|
||||
|
||||
var/list/gridLines = splittext(regexOutput[6], "\n")
|
||||
if(parsed_map)
|
||||
gridSet.gridLines = gridLines
|
||||
|
||||
var/leadingBlanks = 0
|
||||
while(leadingBlanks < length(gridLines) && gridLines[++leadingBlanks] == "")
|
||||
@@ -161,8 +200,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
if(gridLines[length(gridLines)] == "")
|
||||
gridLines.Cut(length(gridLines)) // Remove only one blank line at the end.
|
||||
|
||||
if(parsed_map)
|
||||
parsed_map.gridSets += gridSet
|
||||
|
||||
bounds[MAP_MINY] = min(bounds[MAP_MINY], clamp(ycrd, y_lower, y_upper))
|
||||
ycrd += length(gridLines) - 1 // Start at the top and work down
|
||||
if(parsed_map)
|
||||
gridSet.ycrd += length(gridLines) - 1
|
||||
|
||||
if(!cropMap && ycrd > world.maxy)
|
||||
if(!measureOnly)
|
||||
@@ -172,6 +216,9 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
else
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], clamp(min(ycrd, world.maxy), y_lower, y_upper))
|
||||
|
||||
if(!line_len && length(gridLines))
|
||||
line_len = length(gridLines[1])
|
||||
|
||||
var/maxx = xcrdStart
|
||||
if(measureOnly)
|
||||
for(var/line in gridLines)
|
||||
@@ -236,6 +283,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
|
||||
var/datum/map_load_metadata/M = new
|
||||
M.bounds = bounds
|
||||
M.atoms_to_initialise = atoms_to_initialise
|
||||
if(parsed_map)
|
||||
parsed_map.key_len = key_len
|
||||
parsed_map.line_len = line_len
|
||||
parsed_map.grid_models = grid_models.Copy()
|
||||
parsed_map.parsed_bounds = bounds.Copy()
|
||||
parsed_map.bounds = bounds.Copy()
|
||||
M.parsed_map = parsed_map
|
||||
return M
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* THIS IS ONLY A PLACEHOLDER AND DOESN'T WORK */
|
||||
|
||||
//Yes, they can only be rectangular.
|
||||
//Yes, I'm sorry.
|
||||
/datum/turf_reservation
|
||||
@@ -28,11 +26,202 @@
|
||||
var/list/top_right_turfs = list()
|
||||
|
||||
/// The turf type the reservation is initially made with
|
||||
var/turf_type = /turf/space
|
||||
var/turf_type = RESERVED_TURF_TYPE
|
||||
|
||||
/// Do we override baseturfs with turf_type?
|
||||
var/turf_type_is_baseturf = TRUE
|
||||
|
||||
///Distance away from the cordon where we can put a "sort-cordon" and run some extra code (see make_repel). 0 makes nothing happen
|
||||
var/pre_cordon_distance = 0
|
||||
|
||||
/datum/turf_reservation/transit
|
||||
turf_type = /turf/space/transit
|
||||
pre_cordon_distance = 7
|
||||
|
||||
/datum/turf_reservation/New()
|
||||
LAZYADD(SSmapping.turf_reservations, src)
|
||||
|
||||
/datum/turf_reservation/Destroy()
|
||||
Release()
|
||||
LAZYREMOVE(SSmapping.turf_reservations, src)
|
||||
return ..()
|
||||
|
||||
/datum/turf_reservation/proc/Release()
|
||||
bottom_left_turfs.Cut()
|
||||
top_right_turfs.Cut()
|
||||
|
||||
var/list/release_turfs = list()
|
||||
release_turfs |= reserved_turfs
|
||||
release_turfs |= cordon_turfs
|
||||
|
||||
reserved_turfs.Cut()
|
||||
cordon_turfs.Cut()
|
||||
|
||||
for(var/turf/pre_cordon_turf as anything in pre_cordon_turfs)
|
||||
stop_repel(pre_cordon_turf)
|
||||
pre_cordon_turfs.Cut()
|
||||
|
||||
for(var/turf/released_turf as anything in release_turfs)
|
||||
SSmapping.used_turfs -= released_turf
|
||||
SEND_SIGNAL(released_turf, COMSIG_TURF_RESERVATION_RELEASED, src)
|
||||
released_turf.blocks_air = TRUE
|
||||
|
||||
// Makes the linter happy, even tho we don't await this
|
||||
INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping, reserve_turfs), release_turfs)
|
||||
|
||||
/datum/turf_reservation/proc/calculate_cordon_turfs(turf/bottom_left, turf/top_right)
|
||||
if(bottom_left.x < 2 || bottom_left.y < 2 || top_right.x > (world.maxx - 2) || top_right.y > (world.maxy - 2))
|
||||
return FALSE
|
||||
|
||||
var/list/possible_turfs = CORNER_OUTLINE(bottom_left, width, height)
|
||||
possible_turfs -= cordon_turfs
|
||||
for(var/turf/cordon_turf as anything in possible_turfs)
|
||||
if(!(cordon_turf.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
return FALSE
|
||||
|
||||
cordon_turfs |= possible_turfs
|
||||
|
||||
if(pre_cordon_distance && width > pre_cordon_distance * 2 && height > pre_cordon_distance * 2)
|
||||
var/turf/offset_turf = locate(bottom_left.x + pre_cordon_distance, bottom_left.y + pre_cordon_distance, bottom_left.z)
|
||||
var/list/to_add = CORNER_OUTLINE(offset_turf, width - pre_cordon_distance * 2, height - pre_cordon_distance * 2) //we step-by-stop move inwards from the outer cordon
|
||||
for(var/turf/turf_being_added as anything in to_add)
|
||||
pre_cordon_turfs |= turf_being_added //add one by one so we can filter out duplicates
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/turf_reservation/proc/generate_cordon()
|
||||
var/area/space/space_area = locate(/area/space)
|
||||
var/list/prepared_cordon_turfs = list()
|
||||
for(var/turf/cordon_turf as anything in cordon_turfs)
|
||||
SSmapping.unused_turfs["[cordon_turf.z]"] -= cordon_turf
|
||||
cordon_turf = SSmapping.reset_turf_for_reservation(cordon_turf, space_area)
|
||||
if(!cordon_turf)
|
||||
continue
|
||||
cordon_turf.turf_flags &= ~UNUSED_RESERVATION_TURF
|
||||
SSmapping.used_turfs[cordon_turf] = src
|
||||
prepared_cordon_turfs += cordon_turf
|
||||
cordon_turfs = prepared_cordon_turfs
|
||||
|
||||
for(var/turf/pre_cordon_turf as anything in pre_cordon_turfs)
|
||||
make_repel(pre_cordon_turf)
|
||||
|
||||
/// Register signals in the cordon "danger zone" to do something with whoever trespasses
|
||||
/datum/turf_reservation/proc/make_repel(turf/pre_cordon_turf)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
//Okay so hear me out. If we place a special turf IN the reserved area, it will be overwritten, so we can't do that
|
||||
//But signals are preserved even between turf changes, so even if we register a signal now it will stay even if that turf is overriden by the template
|
||||
RegisterSignals(pre_cordon_turf, list(COMSIG_QDELETING, COMSIG_TURF_RESERVATION_RELEASED), PROC_REF(on_stop_repel))
|
||||
|
||||
/datum/turf_reservation/proc/on_stop_repel(turf/pre_cordon_turf)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
stop_repel(pre_cordon_turf)
|
||||
|
||||
/// Unregister all the signals we added in RegisterRepelSignals
|
||||
/datum/turf_reservation/proc/stop_repel(turf/pre_cordon_turf)
|
||||
UnregisterSignal(pre_cordon_turf, list(COMSIG_QDELETING, COMSIG_TURF_RESERVATION_RELEASED))
|
||||
|
||||
/datum/turf_reservation/transit/make_repel(turf/pre_cordon_turf)
|
||||
..()
|
||||
// this does dump_in_space stuff in tg
|
||||
|
||||
/datum/turf_reservation/proc/prepare_reserved_turf(turf/T)
|
||||
RETURN_TYPE(/turf)
|
||||
if(!istype(T))
|
||||
return
|
||||
if(T.type != turf_type)
|
||||
T = T.ChangeTurf(turf_type, TRUE, FALSE, TRUE)
|
||||
if(!T)
|
||||
return
|
||||
if(turf_type_is_baseturf)
|
||||
T.baseturf = turf_type
|
||||
T.blocks_air = TRUE
|
||||
return T
|
||||
|
||||
/datum/turf_reservation/turf_not_baseturf
|
||||
turf_type_is_baseturf = FALSE
|
||||
|
||||
/// Internal proc which handles reserving the area for the reservation.
|
||||
/datum/turf_reservation/proc/reserve_area(width, height, zlevel)
|
||||
src.width = width
|
||||
src.height = height
|
||||
if(width > world.maxx || height > world.maxy || width < 1 || height < 1)
|
||||
return FALSE
|
||||
|
||||
var/list/available_turfs = SSmapping.unused_turfs["[zlevel]"]
|
||||
if(!length(available_turfs))
|
||||
return FALSE
|
||||
|
||||
var/turf/bottom_left
|
||||
var/turf/top_right
|
||||
var/list/final_turfs = list()
|
||||
var/passing = FALSE
|
||||
|
||||
for(var/candidate in available_turfs)
|
||||
CHECK_TICK
|
||||
bottom_left = candidate
|
||||
if(!(bottom_left.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
if(bottom_left.x + width > world.maxx || bottom_left.y + height > world.maxy)
|
||||
continue
|
||||
|
||||
top_right = locate(bottom_left.x + width - 1, bottom_left.y + height - 1, bottom_left.z)
|
||||
if(!(top_right.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
|
||||
final_turfs = block(bottom_left, top_right)
|
||||
if(!length(final_turfs))
|
||||
continue
|
||||
|
||||
passing = TRUE
|
||||
for(var/turf/checking as anything in final_turfs)
|
||||
if(!(checking.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
passing = FALSE
|
||||
break
|
||||
|
||||
if(passing)
|
||||
passing = calculate_cordon_turfs(bottom_left, top_right)
|
||||
if(passing)
|
||||
break
|
||||
|
||||
if(!passing || !istype(bottom_left) || !istype(top_right))
|
||||
cordon_turfs.Cut()
|
||||
pre_cordon_turfs.Cut()
|
||||
return FALSE
|
||||
|
||||
for(var/turf/T as anything in final_turfs)
|
||||
SSmapping.unused_turfs["[T.z]"] -= T
|
||||
var/turf/reserved_turf = prepare_reserved_turf(T)
|
||||
if(!reserved_turf)
|
||||
continue
|
||||
reserved_turfs |= reserved_turf
|
||||
SSmapping.used_turfs[reserved_turf] = src
|
||||
reserved_turf.turf_flags = (reserved_turf.turf_flags | RESERVATION_TURF) & ~UNUSED_RESERVATION_TURF
|
||||
if(T == bottom_left)
|
||||
bottom_left = reserved_turf
|
||||
if(T == top_right)
|
||||
top_right = reserved_turf
|
||||
|
||||
bottom_left_turfs += bottom_left
|
||||
top_right_turfs += top_right
|
||||
return TRUE
|
||||
|
||||
/datum/turf_reservation/proc/reserve(width, height, z_size, z_reservation)
|
||||
src.z_size = z_size
|
||||
var/failed_reservation = FALSE
|
||||
for(var/i = 1 to z_size)
|
||||
if(!reserve_area(width, height, z_reservation))
|
||||
failed_reservation = TRUE
|
||||
break
|
||||
|
||||
if(failed_reservation)
|
||||
Release()
|
||||
return FALSE
|
||||
|
||||
generate_cordon()
|
||||
return TRUE
|
||||
|
||||
/// Calculates the effective bounds information for the given turf. Returns a list of the information, or null if not applicable.
|
||||
/datum/turf_reservation/proc/calculate_turf_bounds_information(turf/target)
|
||||
for(var/z_idx in 1 to z_size)
|
||||
|
||||
Reference in New Issue
Block a user