mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
everything but shuttles functional commit so let's back it up online
This commit is contained in:
287
modular_chomp/code/modules/overmap/dynamic_sector.dm
Normal file
287
modular_chomp/code/modules/overmap/dynamic_sector.dm
Normal file
@@ -0,0 +1,287 @@
|
||||
// This is an attempt to make space POI's which load/unload on demand, so we can have 50 overmap POI's without
|
||||
// 50 z-levels (which may lead to OOM). No idea what forces I'm working with, so let's see how poorly this goes.
|
||||
|
||||
GLOBAL_VAR_INIT(dynamic_sector_master, null)
|
||||
// Also adjust find_z_levels() if you adjust increase dynamic levels, that part is hard-coded so you don't gloss over world.increment_max_z().
|
||||
#define MAX_DYNAMIC_LEVELS 3
|
||||
#define MAX_DYNAMIC_POI_DIMENSIONS 94 // Keep this an even number if using even world.maxx/maxy
|
||||
|
||||
// The "master" object, which creates + stores + registers the actual z-levels used for dynamic POI's. Handles overmap
|
||||
// sanity checks, + procs, should not be directly accessible by players. Do not use in maps. Only 1 should exist currently.
|
||||
/obj/effect/overmap/visitable/dynamic
|
||||
name = "DO NOT USE IN MAPS"
|
||||
known = FALSE
|
||||
scannable = FALSE
|
||||
invisibility = 101
|
||||
in_space = TRUE
|
||||
scanner_desc = "You should not see this."
|
||||
var/generated_z = FALSE
|
||||
|
||||
// Initialized to match max dynamic levels.
|
||||
map_z = list(null,null,null)
|
||||
|
||||
// Overmap poi's CURRENTLY USING a z-level. Dynamic POI's get referenced here when loading in and removed when unloaded.
|
||||
// Length must match map_z length or bad things may happen.
|
||||
var/list/active_pois[MAX_DYNAMIC_LEVELS]
|
||||
var/list/all_children = list()
|
||||
|
||||
|
||||
// This handles generating and assigning z-levels. Overriding this proc means we don't need a hacky Initialize() override.
|
||||
// This means extra_z_levels won't do anything, which is intentional.
|
||||
/obj/effect/overmap/visitable/dynamic/find_z_levels()
|
||||
if(!generated_z && (GLOB.dynamic_sector_master == src)) // Ensure this only runs once per round.
|
||||
for(var/i = 1; i <= 3; i++) // Hard-coding limit because this is dangerous.
|
||||
world.increment_max_z()
|
||||
map_z[i] = world.maxz
|
||||
generated_z = TRUE
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/Initialize()
|
||||
if(!GLOB.dynamic_sector_master)
|
||||
GLOB.dynamic_sector_master = src
|
||||
. = ..()
|
||||
create_children()
|
||||
|
||||
// Create POI objects for each overmap POI template, link to parent. Initialize() of children handles turf assignment
|
||||
/obj/effect/overmap/visitable/dynamic/proc/create_children()
|
||||
for(var/datum/map_template/dynamic_overmap/poi as anything in subtypesof(/datum/map_template/dynamic_overmap))
|
||||
if(!initial(poi.mappath) || !initial(poi.name)) // Exclude templates without an actual map (or are not included in the mapping subsystem map_templates)
|
||||
continue
|
||||
var/obj/effect/overmap/visitable/dynamic/poi/P = new()
|
||||
P.my_template = SSmapping.map_templates[initial(poi.name)] // Link to the stored map datums.
|
||||
P.parent = src
|
||||
all_children.Add(P)
|
||||
P.seed_overmap()
|
||||
|
||||
// Randomly unload a child POI, if possible. Returns the index of the recovered level if successful, 0 if not.
|
||||
// Should only be called if active_pois[] is full.
|
||||
/obj/effect/overmap/visitable/dynamic/proc/cull_child(mob/user)
|
||||
var/random_index = rand(1,MAX_DYNAMIC_LEVELS)
|
||||
var/obj/effect/overmap/visitable/dynamic/poi/P
|
||||
|
||||
if(is_empty(random_index)) // If this z-level is empty, destroy the poi
|
||||
P = active_pois[random_index]
|
||||
P.destroy_poi(user)
|
||||
active_pois[random_index] = null // Should be set in destroy_poi but this lets us use the z-level again if anything goes horribly wrong.
|
||||
return random_index
|
||||
|
||||
for(var/i = 1, i <= MAX_DYNAMIC_LEVELS, i++) // Level was not empty, check remaining levels
|
||||
if(i == random_index)
|
||||
continue // Don't run this one again.
|
||||
if(is_empty(i))
|
||||
P = active_pois[i]
|
||||
P.destroy_poi(user)
|
||||
active_pois[i] = null
|
||||
return i
|
||||
return 0
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/proc/is_empty(var/index, var/mob/observer)
|
||||
if(!LAZYLEN(map_z))
|
||||
log_and_message_admins("CANARY: [src] tried to check is_empty, but map_z is `[map_z || "null"]`")
|
||||
return TRUE
|
||||
if(!index)
|
||||
index = 1
|
||||
testing("Checking if sector at [map_z[index]] has no players.")
|
||||
for(var/mob/M in global.player_list)
|
||||
if(M != observer && (M.z in map_z[index]))
|
||||
testing("There are people on it.")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// You generally shouldn't destroy these.
|
||||
/obj/effect/overmap/visitable/dynamic/Destroy()
|
||||
for(var/child in all_children)
|
||||
qdel(child)
|
||||
return ..()
|
||||
|
||||
// These are the player-viewable/interactible POI's which tell the parent how to use its z-levels. Spawned by the parent.
|
||||
/obj/effect/overmap/visitable/dynamic/poi
|
||||
name = "bluespace static"
|
||||
unknown_name = "bluespace static"
|
||||
unknown_state = "poi"
|
||||
known = FALSE
|
||||
scannable = TRUE
|
||||
invisibility = 0
|
||||
in_space = TRUE
|
||||
map_z = list(null) // Override parent value
|
||||
active_pois = null
|
||||
var/obj/effect/overmap/visitable/dynamic/parent // The object which created us.
|
||||
var/datum/map_template/dynamic_overmap/my_template
|
||||
var/last_scanned = 0 // Timer to prevent spamming POI loading/unloading since that's probably exploitable.
|
||||
var/loaded = FALSE
|
||||
var/my_index = 0 // Tracks which z-level we're using in the parent. Corresponds to index in parent's active_pois[]
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/poi/Initialize()
|
||||
if(!global.using_map.use_overmap)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
// Normally Initialize() would do this but I need it to call after Initialize(), therefore new proc.
|
||||
/obj/effect/overmap/visitable/dynamic/poi/proc/seed_overmap()
|
||||
start_x = start_x || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
|
||||
start_y = start_y || rand(OVERMAP_EDGE, global.using_map.overmap_size - OVERMAP_EDGE)
|
||||
|
||||
forceMove(locate(start_x, start_y, global.using_map.overmap_z))
|
||||
|
||||
if(!docking_codes)
|
||||
docking_codes = "[ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))][ascii2text(rand(65,90))]"
|
||||
|
||||
if(known)
|
||||
plane = PLANE_LIGHTING_ABOVE
|
||||
for(var/obj/machinery/computer/ship/helm/H in global.machines)
|
||||
H.get_known_sectors()
|
||||
else
|
||||
real_appearance = image(icon, src, my_template.poi_icon)
|
||||
real_appearance.override = TRUE
|
||||
name = unknown_name
|
||||
icon_state = unknown_state
|
||||
color = null
|
||||
desc = "Scan this to find out more information."
|
||||
|
||||
// Grab scanner info from map template, allow the user to load/unload POI's.
|
||||
/obj/effect/overmap/visitable/dynamic/poi/get_scan_data(user)
|
||||
scanner_desc = my_template.scanner_desc
|
||||
if(!known)
|
||||
known = TRUE
|
||||
name = my_template.name
|
||||
icon_state = my_template.poi_icon
|
||||
color = my_template.poi_color
|
||||
desc = initial(desc)
|
||||
|
||||
if(loaded)
|
||||
var/confirm = alert(user, "Sever bluespace link? This location will become permanently inaccessible.", "Are you sure?", "No", "Yes")
|
||||
if(confirm == "Yes")
|
||||
if(is_empty(1)) // Dynamic POI's should only ever have 1 entry in map_z
|
||||
destroy_poi(user) // Delete POI from dynamic z-level
|
||||
else
|
||||
to_chat(user, "Unable to sever link. Location likely contains living realspace entities.")
|
||||
else
|
||||
var/confirm = alert(user, "Transient subspace anomaly detected. Tether object to realspace?", "Stabilize anomaly?", "Yes", "No")
|
||||
if(confirm == "Yes")
|
||||
create_poi(user) // Load POI to dynamic z-level
|
||||
|
||||
return ..()
|
||||
|
||||
// Loads POI template into a dynamic z-level
|
||||
/obj/effect/overmap/visitable/dynamic/poi/proc/create_poi(mob/user)
|
||||
if(!parent) // Ideally this should never happen.
|
||||
log_and_message_admins("Dynamic overmap POI attempted to generate without a parent z-level.")
|
||||
qdel(src)
|
||||
return
|
||||
if(!my_template) // Also shouldn't happen.
|
||||
log_and_message_admins("Dynamic overmap POI attempted to generate without a map template.")
|
||||
qdel(src)
|
||||
return
|
||||
if(loaded) // These may only be created once.
|
||||
return
|
||||
if(last_scanned + 10 SECONDS > world.time)
|
||||
to_chat(user, "\[REDACTED\] matrix is still recharging!")
|
||||
return
|
||||
last_scanned = world.time
|
||||
|
||||
for(var/i = 1, i <= MAX_DYNAMIC_LEVELS, i++)
|
||||
if(!parent.active_pois[i]) // We found an unused z-level
|
||||
my_index = i
|
||||
parent.active_pois[i] = src
|
||||
map_z[1] = parent.map_z[i]
|
||||
map_sectors["[parent.map_z[i]]"] = src // Pass ownership of z-level to child, probably hacky and terribad
|
||||
break // Terminate loop
|
||||
if(!my_index) // No z-levels available
|
||||
var/confirm = alert(user, "\[REDACTED\] matrix at capacity; a bluespace link must be permanently severed to stabilize this anomaly. Continue?", "Are you sure?", "No", "Yes")
|
||||
if(confirm == "Yes")
|
||||
my_index = parent.cull_child(user)
|
||||
if(my_index)
|
||||
parent.active_pois[my_index] = src
|
||||
map_z[1] = parent.map_z[my_index]
|
||||
map_sectors["[parent.map_z[my_index]]"] = src
|
||||
else // Something went wrong, ideally due to all relevant z-levels containing players.
|
||||
to_chat(user, "Unable to sever any bluespace link. All links likely contain living realspace entities.")
|
||||
return
|
||||
else
|
||||
return
|
||||
|
||||
var/turf/T = locate(round(world.maxx/2), round(world.maxy/2), map_z[1]) // Find center turf, or near center for even-dimension maps.
|
||||
if(!istype(T))
|
||||
log_debug("Dynamic overmap POI found [T] instead of a valid turf.")
|
||||
return
|
||||
my_template.load(T, centered=TRUE)
|
||||
loaded = TRUE
|
||||
my_template.update_lighting(T)
|
||||
to_chat(user, "Stabilization tether successfully created.")
|
||||
if(my_template.active_icon)
|
||||
icon_state = my_template.active_icon
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/poi/proc/destroy_poi(mob/user)
|
||||
if(!loaded) // Ideally this should never happen.
|
||||
log_debug("Dynamic overmap POI tried to unload itself but is not loaded.")
|
||||
return
|
||||
if(!parent) // Also shouldn't happen.
|
||||
log_and_message_admins("Dynamic overmap POI attempted to unload without a parent z-level.")
|
||||
loaded = FALSE // Always make sure loaded = FALSE before using qdel(src) in this proc to prevent an infinite loop in Destroy().
|
||||
qdel(src)
|
||||
return
|
||||
if(!((my_index >= 1) && (my_index <= MAX_DYNAMIC_LEVELS))) // Make sure my_index is sane
|
||||
log_debug("Dynamic overmap POI attempted to unload with an invalid index.")
|
||||
loaded = FALSE
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
map_sectors["[parent.map_z[my_index]]"] = parent // Pass ownership back to parent.
|
||||
parent.active_pois[my_index] = null
|
||||
if(!LAZYLEN(map_z)) // If this is 0, how did we get this far?
|
||||
log_and_message_admins("Dynamic overmap POI attempted to unload without a linked z-level.")
|
||||
loaded = FALSE
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
to_chat(user, "Destabilization initiated...")
|
||||
log_debug("Dynamic overmap POI unloading initiated...")
|
||||
// Some math to return a block of MAX_DYNAMIC_POI_DIMENSIONS turfs. Basically, start from center turf and subtract half of max dimensons for bottom left corner, add for top right corner.
|
||||
var/list/turfs_to_reset = block(locate(round(world.maxx/2 - MAX_DYNAMIC_POI_DIMENSIONS/2), round(world.maxy/2 - MAX_DYNAMIC_POI_DIMENSIONS/2), map_z[1]), locate(round(world.maxx/2 + MAX_DYNAMIC_POI_DIMENSIONS/2), round(world.maxy/2 + MAX_DYNAMIC_POI_DIMENSIONS/2), map_z[1]))
|
||||
var/deleted_atoms = 0
|
||||
if(turfs_to_reset.len)
|
||||
for(var/turf/T in turfs_to_reset)
|
||||
for(var/atom/movable/AM in T)
|
||||
if(istype(AM, /mob/observer))
|
||||
continue
|
||||
++deleted_atoms
|
||||
qdel(AM)
|
||||
T = T.ChangeTurf(/turf/space)
|
||||
ChangeArea(T, /area/space)
|
||||
loaded = FALSE
|
||||
map_z = null
|
||||
icon_state = "ring_destroyed"
|
||||
log_debug("Dynamic POI unload complete, [deleted_atoms] atoms destroyed.")
|
||||
to_chat(user, "Subspace pocket collapse successful.")
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/poi/Destroy()
|
||||
if(loaded)
|
||||
if(is_empty()) // If we're deleted while loaded but the z-level is occupied, relinquish ownership instead.
|
||||
destroy_poi()
|
||||
else
|
||||
if(parent && (parent.active_pois.len >= my_index) && (parent.map_z.len >= my_index) && (my_index > 0)) // Unless vars are turbofucked.
|
||||
map_sectors["[parent.map_z[my_index]]"] = parent
|
||||
parent.active_pois[my_index] = null
|
||||
if(parent)
|
||||
parent.all_children.Remove(src)
|
||||
return ..()
|
||||
|
||||
// This assigns map_sectors which we don't want for this subtype since we have other procs to handle it.
|
||||
/obj/effect/overmap/visitable/dynamic/poi/register_z_levels()
|
||||
return
|
||||
|
||||
// Make sure we reassign our z_level to the parent if one exists and somehow this gets called.
|
||||
/obj/effect/overmap/visitable/dynamic/poi/unregister_z_levels()
|
||||
if(parent && LAZYLEN(map_z))
|
||||
map_sectors["[map_z[1]]"] = parent
|
||||
map_z = null
|
||||
return ..()
|
||||
|
||||
// Hahah recursion
|
||||
/obj/effect/overmap/visitable/dynamic/poi/create_children()
|
||||
return
|
||||
|
||||
/obj/effect/overmap/visitable/dynamic/poi/cull_child()
|
||||
return
|
||||
|
||||
#undef MAX_DYNAMIC_LEVELS
|
||||
#undef MAX_DYNAMIC_POI_DIMENSIONS
|
||||
Reference in New Issue
Block a user