diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm index cdd2739b3c..b7043d01ff 100644 --- a/code/__DEFINES/maps.dm +++ b/code/__DEFINES/maps.dm @@ -21,7 +21,7 @@ Last space-z level = empty #define CITY_OF_COGS "City of Cogs" #define EMPTY_AREA_1 "Empty Area 1" #define EMPTY_AREA_2 "Empty Area 2" -#define MINING "Mining Asteroid" +#define MINING_ASTEROID "Mining Asteroid" #define EMPTY_AREA_3 "Empty Area 3" #define EMPTY_AREA_4 "Empty Area 4" #define EMPTY_AREA_5 "Empty Area 5" @@ -36,11 +36,8 @@ Last space-z level = empty #define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() } //zlevel defines, can be overridden for different maps in the appropriate _maps file. -#define ZLEVEL_CENTCOM 1 #define ZLEVEL_STATION_PRIMARY 2 -#define ZLEVEL_MINING 5 #define ZLEVEL_LAVALAND 5 -#define ZLEVEL_CITYOFCOGS 6 #define ZLEVEL_EMPTY_SPACE 12 //Unless you modify it in map config should be equal to ZLEVEL_SPACEMAX #define ZLEVEL_TRANSIT 13 @@ -49,3 +46,31 @@ Last space-z level = empty #define ZLEVEL_SPACEMAX 13 #define SPACERUIN_MAP_EDGE_PAD 15 +#define ZLEVEL_SPACE_RUIN_COUNT 5 + +// traits +#define ZTRAIT_CENTCOM "CentCom" +#define ZTRAIT_STATION "Station" +#define ZTRAIT_MINING "Mining" +#define ZTRAIT_REEBE "Reebe" +#define ZTRAIT_TRANSIT "Transit" +#define ZTRAIT_AWAY "Away Mission" +#define ZTRAIT_SPACE_RUINS "Space Ruins" +#define ZTRAIT_LAVA_RUINS "Lava Ruins" +#define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier" + +// trait definitions +#define DL_NAME "name" +#define DL_LINKAGE "linkage" +#define DL_TRAITS "traits" + +#define DECLARE_LEVEL(NAME, LINKAGE, TRAITS) list(DL_NAME = NAME, DL_LINKAGE = LINKAGE, DL_TRAITS = TRAITS) +// corresponds to basemap.dm +#define DEFAULT_MAP_TRAITS list(\ + DECLARE_LEVEL("CentCom", SELFLOOPING, list(ZTRAIT_CENTCOM = TRUE)),\ + DECLARE_LEVEL("Main Station", CROSSLINKED, list(ZTRAIT_STATION = TRUE)),\ + DECLARE_LEVEL("Empty Area 1", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE)),\ + DECLARE_LEVEL("Empty Area 2", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE)),\ + DECLARE_LEVEL("Lavaland", UNAFFECTED, list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 3)),\ + DECLARE_LEVEL("Reebe", UNAFFECTED, list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)),\ +) diff --git a/code/__HELPERS/level_traits.dm b/code/__HELPERS/level_traits.dm index e2a74cf8e0..4d20c9b513 100644 --- a/code/__HELPERS/level_traits.dm +++ b/code/__HELPERS/level_traits.dm @@ -1,17 +1,17 @@ // Helpers for checking whether a z-level conforms to a specific requirement // Basic levels -#define is_centcom_level(z) ((z) == ZLEVEL_CENTCOM) +#define is_centcom_level(z) SSmapping.level_trait(z, ZTRAIT_CENTCOM) -#define is_station_level(z) ((z) in GLOB.station_z_levels) +#define is_station_level(z) SSmapping.level_trait(z, ZTRAIT_STATION) -#define is_mining_level(z) ((z) == ZLEVEL_MINING) +#define is_mining_level(z) SSmapping.level_trait(z, ZTRAIT_MINING) -#define is_reebe(z) ((z) == ZLEVEL_CITYOFCOGS) +#define is_reebe(z) SSmapping.level_trait(z, ZTRAIT_REEBE) -#define is_transit_level(z) ((z) == ZLEVEL_TRANSIT) +#define is_transit_level(z) SSmapping.level_trait(z, ZTRAIT_TRANSIT) -#define is_away_level(z) ((z) > ZLEVEL_SPACEMAX) +#define is_away_level(z) SSmapping.level_trait(z, ZTRAIT_AWAY) // If true, the singularity cannot strip away asteroid turf on this Z #define is_planet_level(z) (GLOB.z_is_planet["z"]) diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm index 51af2b3525..0d8965d10a 100644 --- a/code/_globalvars/lists/mapping.dm +++ b/code/_globalvars/lists/mapping.dm @@ -1,14 +1,7 @@ -#define Z_NORTH 1 -#define Z_EAST 2 -#define Z_SOUTH 3 -#define Z_WEST 4 - GLOBAL_LIST_INIT(cardinals, list(NORTH, SOUTH, EAST, WEST)) GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) -GLOBAL_LIST_INIT(station_z_levels, list(ZLEVEL_STATION_PRIMARY)) - GLOBAL_LIST(global_map) //list/global_map = list(list(1,5),list(4,3))//an array of map Z levels. //Resulting sector map looks like diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index d13d340b03..8046dab41e 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -22,6 +22,9 @@ SUBSYSTEM_DEF(mapping) var/loading_ruins = FALSE + // Z-manager stuff + var/list/z_list + /datum/controller/subsystem/mapping/PreInit() if(!config) #ifdef FORCE_MAP @@ -39,28 +42,34 @@ SUBSYSTEM_DEF(mapping) repopulate_sorted_areas() process_teleport_locs() //Sets up the wizard teleport locations preloadTemplates() + + // Create space levels + for(var/I in 1 to ZLEVEL_SPACE_RUIN_COUNT) + add_new_zlevel("Empty Area [2 + I]", CROSSLINKED, list(ZTRAIT_SPACE_RUINS = TRUE)) + add_new_zlevel("Empty Area [3 + ZLEVEL_SPACE_RUIN_COUNT]", CROSSLINKED, list()) // no ruins + add_new_zlevel("Transit", UNAFFECTED, list(ZTRAIT_TRANSIT = TRUE)) + // Pick a random away mission. createRandomZlevel() - // Generate mining. + if (z_list.len < world.maxz) + add_new_zlevel("Away Mission", UNAFFECTED, list(ZTRAIT_AWAY = TRUE)) + + // Generate mining ruins loading_ruins = TRUE - var/mining_type = config.minetype - if (mining_type == "lavaland") - seedRuins(list(ZLEVEL_LAVALAND), CONFIG_GET(number/lavaland_budget), /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates) - spawn_rivers() + var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS) + if (lava_ruins.len) + seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates) + for (var/lava_z in lava_ruins) + spawn_rivers(lava_z) - // deep space ruins - var/space_zlevels = list() - for(var/i in ZLEVEL_SPACEMIN to ZLEVEL_SPACEMAX) - switch(i) - if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE, ZLEVEL_TRANSIT, ZLEVEL_CITYOFCOGS) - continue - else - space_zlevels += i - - seedRuins(space_zlevels, CONFIG_GET(number/space_budget), /area/space, space_ruins_templates) + // Generate deep space ruins + var/list/space_ruins = levels_by_trait(ZTRAIT_SPACE_RUINS) + if (space_ruins.len) + seedRuins(space_ruins, CONFIG_GET(number/space_budget), /area/space, space_ruins_templates) loading_ruins = FALSE + repopulate_sorted_areas() - // Set up Z-level transistions. + // Set up Z-level transitions. setup_map_transitions() generate_station_area_list() ..() @@ -98,6 +107,8 @@ SUBSYSTEM_DEF(mapping) config = SSmapping.config next_map_config = SSmapping.next_map_config + z_list = SSmapping.z_list + /datum/controller/subsystem/mapping/proc/TryLoadZ(filename, errorList, forceLevel, last) var/static/dmm_suite/loader if(!loader) @@ -107,11 +118,6 @@ SUBSYSTEM_DEF(mapping) if(last) QDEL_NULL(loader) -/datum/controller/subsystem/mapping/proc/CreateSpace(MaxZLevel) - while(world.maxz < MaxZLevel) - ++world.maxz - CHECK_TICK - #define INIT_ANNOUNCE(X) to_chat(world, "[X]"); log_world(X) /datum/controller/subsystem/mapping/proc/loadWorld() //if any of these fail, something has gone horribly, HORRIBLY, wrong @@ -121,6 +127,7 @@ SUBSYSTEM_DEF(mapping) INIT_ANNOUNCE("Loading [config.map_name]...") TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION_PRIMARY) + InitializeDefaultZLevels() INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!") if(SSdbcore.Connect()) var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]") @@ -129,8 +136,6 @@ SUBSYSTEM_DEF(mapping) if(config.minetype != "lavaland") INIT_ANNOUNCE("WARNING: A map without lavaland set as its minetype was loaded! This is being ignored! Update the maploader code!") - CreateSpace(ZLEVEL_SPACEMAX) - if(LAZYLEN(FailedZs)) //but seriously, unless the server's filesystem is messed up this will never happen var/msg = "RED ALERT! The following map files failed to load: [FailedZs[1]]" if(FailedZs.len > 1) diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm index 4bead36bb1..fa9821b85b 100644 --- a/code/controllers/subsystem/minimap.dm +++ b/code/controllers/subsystem/minimap.dm @@ -5,9 +5,10 @@ SUBSYSTEM_DEF(minimap) var/const/MINIMAP_SIZE = 2048 var/const/TILE_SIZE = 8 - var/list/z_levels = list(ZLEVEL_STATION_PRIMARY) + var/list/z_levels /datum/controller/subsystem/minimap/Initialize(timeofday) + z_levels = SSmapping.levels_by_trait(ZTRAIT_STATION) var/hash = md5(SSmapping.config.GetFullMapPath()) if(CONFIG_GET(flag/generate_minimaps)) if(hash == trim(file2text(hash_path()))) diff --git a/code/controllers/subsystem/squeak.dm b/code/controllers/subsystem/squeak.dm index 022b659d8a..861bb0d8d5 100644 --- a/code/controllers/subsystem/squeak.dm +++ b/code/controllers/subsystem/squeak.dm @@ -34,7 +34,7 @@ SUBSYSTEM_DEF(squeak) /datum/controller/subsystem/squeak/proc/find_exposed_wires() exposed_wires.Cut() var/list/all_turfs - for (var/z in GLOB.station_z_levels) + for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) all_turfs += block(locate(1,1,z), locate(world.maxx,world.maxy,z)) for(var/turf/open/floor/plating/T in all_turfs) if(is_blocked_turf(T)) diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm index 9b5cd18774..0616d0e4c6 100644 --- a/code/datums/explosion.dm +++ b/code/datums/explosion.dm @@ -1,6 +1,4 @@ #define EXPLOSION_THROW_SPEED 4 -#define CITYOFCOGS_CAP_MULTIPLIER 0.5 -#define MINING_CAP_MULTIPLIER 3 GLOBAL_LIST_EMPTY(explosions) //Against my better judgement, I will return the explosion datum @@ -54,17 +52,14 @@ GLOBAL_LIST_EMPTY(explosions) var/orig_dev_range = devastation_range var/orig_heavy_range = heavy_impact_range var/orig_light_range = light_impact_range - + var/orig_max_distance = max(devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range) - + //Zlevel specific bomb cap multiplier - var/cap_multiplier = 1 - switch(epicenter.z) - if(ZLEVEL_CITYOFCOGS) - cap_multiplier = CITYOFCOGS_CAP_MULTIPLIER - if(ZLEVEL_MINING) - cap_multiplier = MINING_CAP_MULTIPLIER - + var/cap_multiplier = SSmapping.level_trait(epicenter.z, ZTRAIT_BOMBCAP_MULTIPLIER) + if (isnull(cap_multiplier)) + cap_multiplier = 1 + if(!ignorecap) devastation_range = min(GLOB.MAX_EX_DEVESTATION_RANGE * cap_multiplier, devastation_range) heavy_impact_range = min(GLOB.MAX_EX_HEAVY_RANGE * cap_multiplier, heavy_impact_range) @@ -91,11 +86,11 @@ GLOBAL_LIST_EMPTY(explosions) if(adminlog) message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [epi_area] [ADMIN_COORDJMP(epicenter)]") log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") - + var/x0 = epicenter.x var/y0 = epicenter.y var/z0 = epicenter.z - + SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flash" = flash_range, "flame" = flame_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = epi_area.type)) // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. @@ -199,7 +194,7 @@ GLOBAL_LIST_EMPTY(explosions) //------- EX_ACT AND TURF FIRES ------- if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag - var/list/items = list() + var/list/items = list() for(var/I in T) var/atom/A = I items += A.GetAllContents() @@ -418,6 +413,3 @@ GLOBAL_LIST_EMPTY(explosions) // 10 explosion power is a (1, 3, 6) explosion. // 5 explosion power is a (0, 1, 3) explosion. // 1 explosion power is a (0, 0, 1) explosion. - -#undef CITYOFCOGS_CAP_MULTIPLIER -#undef MINING_CAP_MULTIPLIER diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index c030bd4c2a..f90d6eed32 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -166,9 +166,12 @@ // Safe location finder -/proc/find_safe_turf(zlevel = ZLEVEL_STATION_PRIMARY, list/zlevels, extended_safety_checks = FALSE) +/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE) if(!zlevels) - zlevels = list(zlevel) + if (zlevel) + zlevels = list(zlevel) + else + zlevels = SSmapping.levels_by_trait(ZTRAIT_STATION) var/cycles = 1000 for(var/cycle in 1 to cycles) // DRUNK DIALLING WOOOOOOOOO diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index 2a98d57efd..6fae092c9d 100644 --- a/code/datums/position_point_vector.dm +++ b/code/datums/position_point_vector.dm @@ -1,239 +1,239 @@ -//Designed for things that need precision trajectories like projectiles. -//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels. - -#define RETURN_PRECISE_POSITION(A) new /datum/position(A) -#define RETURN_PRECISE_POINT(A) new /datum/point(A) - -/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess. - var/x = 0 - var/y = 0 - var/z = 0 - var/pixel_x = 0 - var/pixel_y = 0 - -/datum/position/proc/valid() - return x && y && z && !isnull(pixel_x) && !isnull(pixel_y) - -/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point. - if(istype(_x, /datum/point)) - var/datum/point/P = _x - var/turf/T = P.return_turf() - _x = T.x - _y = T.y - _z = T.z - _pixel_x = P.return_px() - _pixel_y = P.return_py() - else if(isatom(_x)) - var/atom/A = _x - _x = A.x - _y = A.y - _z = A.z - _pixel_x = A.pixel_x - _pixel_y = A.pixel_y - x = _x - y = _y - z = _z - pixel_x = _pixel_x - pixel_y = _pixel_y - -/datum/position/proc/return_turf() - return locate(x, y, z) - -/datum/position/proc/return_px() - return pixel_x - -/datum/position/proc/return_py() - return pixel_y - -/datum/position/proc/return_point() - return new /datum/point(src) - -/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below. - var/datum/point/P = new - P.x = round(a.x + (b.x - a.x) / 2, 1) - P.y = round(a.y + (b.y - a.y) / 2, 1) - P.z = a.z - return P - -/proc/pixel_length_between_points(datum/point/a, datum/point/b) - return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2)) - -/proc/angle_between_points(datum/point/a, datum/point/b) - return ATAN2((b.y - a.y), (b.x - a.x)) - -/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP! - var/x = 0 - var/y = 0 - var/z = 0 - -/datum/point/proc/valid() - return x && y && z - -/datum/point/proc/copy_to(datum/point/p = new) - p.x = x - p.y = y - p.z = z - return p - -/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom. - if(istype(_x, /datum/position)) - var/datum/position/P = _x - _x = P.x - _y = P.y - _z = P.z - _pixel_x = P.pixel_x - _pixel_y = P.pixel_y - else if(istype(_x, /atom)) - var/atom/A = _x - _x = A.x - _y = A.y - _z = A.z - _pixel_x = A.pixel_x - _pixel_y = A.pixel_y - initialize_location(_x, _y, _z, _pixel_x, _pixel_y) - -/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0) - if(!isnull(tile_x)) - x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x - if(!isnull(tile_y)) - y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2+ p_y - if(!isnull(tile_z)) - z = tile_z - -/datum/point/proc/return_turf() - return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z) - -/datum/point/proc/return_coordinates() //[turf_x, turf_y, z] - return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z) - -/datum/point/proc/return_position() - return new /datum/position(src) - -/datum/point/proc/return_px() - return MODULUS(x, world.icon_size) - 16 - -/datum/point/proc/return_py() - return MODULUS(y, world.icon_size) - 16 - -/datum/point/proc/mapcheck() - . = FALSE - var/maxx = world.icon_size * world.maxx - var/maxy = world.icon_size * world.maxy - var/move_zx = 0 - var/move_zy = 0 - if(x < 0) - x += maxx - move_zx -= 1 - if(y < 0) - y += maxy - move_zy -= 1 - if(x > maxx) - x -= maxx - move_zx += 1 - if(y > maxy) - y -= maxy - move_zy += 1 - var/datum/space_level/S = GLOB.z_levels_list["[z]"] - if(move_zx != 0) - var/datum/space_level/L = S.neigbours["[move_zx < 0? WEST : EAST]"] - z = L.z_value - . = TRUE - if(move_zy != 0) - var/datum/space_level/L = S.neigbours["[move_zy < 0? SOUTH : NORTH]"] - z = L.z_value - . = TRUE - -/datum/point/vector - var/speed = 32 //pixels per iteration - var/iteration = 0 - var/angle = 0 - var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step. - var/mpy = 0 - var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location(). - var/starting_y = 0 - var/starting_z = 0 - -/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed) - ..() - initialize_trajectory(_speed, _angle) - -/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0) - . = ..() - starting_x = x - starting_y = y - starting_z = z - -/datum/point/vector/copy_to(datum/point/vector/v = new) - ..(v) - v.speed = speed - v.iteration = iteration - v.angle = angle - v.mpx = mpx - v.mpy = mpy - v.starting_x = starting_x - v.starting_y = starting_y - v.starting_z = starting_z - return v - -/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle) - if(!isnull(pixel_speed)) - speed = pixel_speed - set_angle(new_angle) - -/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270. - if(isnull(angle)) - return - angle = new_angle - update_offsets() - -/datum/point/vector/proc/update_offsets() - mpx = sin(angle) * speed - mpy = cos(angle) * speed - -/datum/point/vector/proc/set_speed(new_speed) - if(isnull(new_speed) || speed == new_speed) - return - speed = new_speed - update_offsets() - -/datum/point/vector/proc/increment(multiplier = 1) - iteration++ - x += mpx * 1 - y += mpy * 1 - if(mapcheck()) - on_z_change() - -/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE) - var/datum/point/vector/v = copy_to() - if(force_simulate) - for(var/i in 1 to amount) - v.increment(multiplier) - else - v.increment(multiplier * amount) - return v - -/datum/point/vector/proc/on_z_change() - return - -/datum/point/vector/processed //pixel_speed is per decisecond. - var/last_process = 0 - var/last_move = 0 - var/paused = FALSE - -/datum/point/vector/processed/Destroy() - STOP_PROCESSING(SSprojectiles, src) - -/datum/point/vector/processed/proc/start() - last_process = world.time - last_move = world.time - START_PROCESSING(SSprojectiles, src) - -/datum/point/vector/processed/process() - if(paused) - last_move += world.time - last_process - last_process = world.time - return - var/needed_time = world.time - last_move - last_process = world.time - last_move = world.time - increment(needed_time) +//Designed for things that need precision trajectories like projectiles. +//Don't use this for anything that you don't absolutely have to use this with (like projectiles!) because it isn't worth using a datum unless you need accuracy down to decimal places in pixels. + +#define RETURN_PRECISE_POSITION(A) new /datum/position(A) +#define RETURN_PRECISE_POINT(A) new /datum/point(A) + +/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess. + var/x = 0 + var/y = 0 + var/z = 0 + var/pixel_x = 0 + var/pixel_y = 0 + +/datum/position/proc/valid() + return x && y && z && !isnull(pixel_x) && !isnull(pixel_y) + +/datum/position/New(_x = 0, _y = 0, _z = 0, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/point. + if(istype(_x, /datum/point)) + var/datum/point/P = _x + var/turf/T = P.return_turf() + _x = T.x + _y = T.y + _z = T.z + _pixel_x = P.return_px() + _pixel_y = P.return_py() + else if(isatom(_x)) + var/atom/A = _x + _x = A.x + _y = A.y + _z = A.z + _pixel_x = A.pixel_x + _pixel_y = A.pixel_y + x = _x + y = _y + z = _z + pixel_x = _pixel_x + pixel_y = _pixel_y + +/datum/position/proc/return_turf() + return locate(x, y, z) + +/datum/position/proc/return_px() + return pixel_x + +/datum/position/proc/return_py() + return pixel_y + +/datum/position/proc/return_point() + return new /datum/point(src) + +/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below. + var/datum/point/P = new + P.x = round(a.x + (b.x - a.x) / 2, 1) + P.y = round(a.y + (b.y - a.y) / 2, 1) + P.z = a.z + return P + +/proc/pixel_length_between_points(datum/point/a, datum/point/b) + return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2)) + +/proc/angle_between_points(datum/point/a, datum/point/b) + return ATAN2((b.y - a.y), (b.x - a.x)) + +/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP! + var/x = 0 + var/y = 0 + var/z = 0 + +/datum/point/proc/valid() + return x && y && z + +/datum/point/proc/copy_to(datum/point/p = new) + p.x = x + p.y = y + p.z = z + return p + +/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0) //first argument can also be a /datum/position or /atom. + if(istype(_x, /datum/position)) + var/datum/position/P = _x + _x = P.x + _y = P.y + _z = P.z + _pixel_x = P.pixel_x + _pixel_y = P.pixel_y + else if(istype(_x, /atom)) + var/atom/A = _x + _x = A.x + _y = A.y + _z = A.z + _pixel_x = A.pixel_x + _pixel_y = A.pixel_y + initialize_location(_x, _y, _z, _pixel_x, _pixel_y) + +/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0) + if(!isnull(tile_x)) + x = ((tile_x - 1) * world.icon_size) + world.icon_size / 2 + p_x + if(!isnull(tile_y)) + y = ((tile_y - 1) * world.icon_size) + world.icon_size / 2+ p_y + if(!isnull(tile_z)) + z = tile_z + +/datum/point/proc/return_turf() + return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z) + +/datum/point/proc/return_coordinates() //[turf_x, turf_y, z] + return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z) + +/datum/point/proc/return_position() + return new /datum/position(src) + +/datum/point/proc/return_px() + return MODULUS(x, world.icon_size) - 16 + +/datum/point/proc/return_py() + return MODULUS(y, world.icon_size) - 16 + +/datum/point/proc/mapcheck() + . = FALSE + var/maxx = world.icon_size * world.maxx + var/maxy = world.icon_size * world.maxy + var/move_zx = 0 + var/move_zy = 0 + if(x < 0) + x += maxx + move_zx -= 1 + if(y < 0) + y += maxy + move_zy -= 1 + if(x > maxx) + x -= maxx + move_zx += 1 + if(y > maxy) + y -= maxy + move_zy += 1 + var/datum/space_level/S = SSmapping.get_level(z) + if(move_zx != 0) + var/datum/space_level/L = S.neigbours["[move_zx < 0? WEST : EAST]"] + z = L.z_value + . = TRUE + if(move_zy != 0) + var/datum/space_level/L = S.neigbours["[move_zy < 0? SOUTH : NORTH]"] + z = L.z_value + . = TRUE + +/datum/point/vector + var/speed = 32 //pixels per iteration + var/iteration = 0 + var/angle = 0 + var/mpx = 0 //calculated x/y movement amounts to prevent having to do trig every step. + var/mpy = 0 + var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location(). + var/starting_y = 0 + var/starting_z = 0 + +/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed) + ..() + initialize_trajectory(_speed, _angle) + +/datum/point/vector/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0) + . = ..() + starting_x = x + starting_y = y + starting_z = z + +/datum/point/vector/copy_to(datum/point/vector/v = new) + ..(v) + v.speed = speed + v.iteration = iteration + v.angle = angle + v.mpx = mpx + v.mpy = mpy + v.starting_x = starting_x + v.starting_y = starting_y + v.starting_z = starting_z + return v + +/datum/point/vector/proc/initialize_trajectory(pixel_speed, new_angle) + if(!isnull(pixel_speed)) + speed = pixel_speed + set_angle(new_angle) + +/datum/point/vector/proc/set_angle(new_angle) //calculations use "byond angle" where north is 0 instead of 90, and south is 180 instead of 270. + if(isnull(angle)) + return + angle = new_angle + update_offsets() + +/datum/point/vector/proc/update_offsets() + mpx = sin(angle) * speed + mpy = cos(angle) * speed + +/datum/point/vector/proc/set_speed(new_speed) + if(isnull(new_speed) || speed == new_speed) + return + speed = new_speed + update_offsets() + +/datum/point/vector/proc/increment(multiplier = 1) + iteration++ + x += mpx * 1 + y += mpy * 1 + if(mapcheck()) + on_z_change() + +/datum/point/vector/proc/return_vector_after_increments(amount = 7, multiplier = 1, force_simulate = FALSE) + var/datum/point/vector/v = copy_to() + if(force_simulate) + for(var/i in 1 to amount) + v.increment(multiplier) + else + v.increment(multiplier * amount) + return v + +/datum/point/vector/proc/on_z_change() + return + +/datum/point/vector/processed //pixel_speed is per decisecond. + var/last_process = 0 + var/last_move = 0 + var/paused = FALSE + +/datum/point/vector/processed/Destroy() + STOP_PROCESSING(SSprojectiles, src) + +/datum/point/vector/processed/proc/start() + last_process = world.time + last_move = world.time + START_PROCESSING(SSprojectiles, src) + +/datum/point/vector/processed/process() + if(paused) + last_move += world.time - last_process + last_process = world.time + return + var/needed_time = world.time - last_move + last_process = world.time + last_move = world.time + increment(needed_time) diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 6585da0ba4..4385b732c3 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -8,7 +8,7 @@ var/mach = 0 /datum/station_state/proc/count() - for(var/Z in GLOB.station_z_levels) + for(var/Z in SSmapping.levels_by_trait(ZTRAIT_STATION)) for(var/turf/T in block(locate(1,1,Z), locate(world.maxx,world.maxy,Z))) // don't count shuttles since they may have just left if(istype(T.loc, /area/shuttle)) diff --git a/code/game/gamemodes/cit_objectives.dm b/code/game/gamemodes/cit_objectives.dm index faa376571c..fbcfc675e3 100644 --- a/code/game/gamemodes/cit_objectives.dm +++ b/code/game/gamemodes/cit_objectives.dm @@ -98,9 +98,9 @@ for(var/mob/living/carbon/C in GLOB.mob_list) var/mob/living/simple_animal/borer/D = C.has_brain_worms() var/turf/location = get_turf(C) - if(location.z == ZLEVEL_CENTCOM && D && D.stat != DEAD) + if(is_centcom_level(location.z) && D && D.stat != DEAD) total_borer_hosts++ if(target_amount <= total_borer_hosts) return TRUE else - return FALSE \ No newline at end of file + return FALSE diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 9e0c965271..13ab7a8bb1 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -31,7 +31,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/max_i = 10//number of tries to spawn meteor. while(!isspaceturf(pickedstart)) var/startSide = pick(GLOB.cardinals) - var/startZ = pick(GLOB.station_z_levels) + var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) pickedstart = spaceDebrisStartLoc(startSide, startZ) pickedgoal = spaceDebrisFinishLoc(startSide, startZ) max_i-- @@ -40,7 +40,6 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/Me = pickweight(meteortypes) var/obj/effect/meteor/M = new Me(pickedstart, pickedgoal) M.dest = pickedgoal - M.z_original = M.z /proc/spaceDebrisStartLoc(startSide, Z) var/starty @@ -95,7 +94,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event pass_flags = PASSTABLE var/heavy = 0 var/meteorsound = 'sound/effects/meteorimpact.ogg' - var/z_original = ZLEVEL_STATION_PRIMARY + var/z_original var/threat = 0 // used for determining which meteors are most interesting var/lifetime = DEFAULT_METEOR_LIFETIME var/timerid = null @@ -126,6 +125,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event /obj/effect/meteor/Initialize(mapload, target) . = ..() + z_original = z GLOB.meteor_list += src SSaugury.register_doom(src, threat) SpinAnimation() diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 7c790f63de..f007050560 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -17,7 +17,7 @@ /obj/machinery/computer/camera_advanced/Initialize() . = ..() if(station_lock_override) - z_lock = GLOB.station_z_levels.Copy() + z_lock = SSmapping.levels_by_trait(ZTRAIT_STATION) /obj/machinery/computer/camera_advanced/syndie icon_keyboard = "syndie_key" diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm index a5753edcd1..79846791d3 100644 --- a/code/game/turfs/simulated/river.dm +++ b/code/game/turfs/simulated/river.dm @@ -4,7 +4,7 @@ #define RANDOM_LOWER_X 50 #define RANDOM_LOWER_Y 50 -/proc/spawn_rivers(target_z = ZLEVEL_LAVALAND, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y) +/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y) var/list/river_nodes = list() var/num_spawned = 0 while(num_spawned < nodes) diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index ce50378f4d..fac7d93f9a 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -45,7 +45,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list( /client/proc/start_line_profiling, /client/proc/stop_line_profiling, /client/proc/show_line_profiling, - /client/proc/create_mapping_job_icons + /client/proc/create_mapping_job_icons, + /client/proc/debug_z_levels, )) /obj/effect/debugging/mapfix_marker @@ -292,4 +293,64 @@ GLOBAL_VAR_INIT(say_disabled, FALSE) //Also add the x for(var/x_number in 1 to 4) final.Insert(icon('icons/mob/screen_gen.dmi', "x[x_number == 1 ? "" : x_number]"), "x[x_number == 1 ? "" : x_number]") - fcopy(final, "icons/mob/landmarks.dmi") \ No newline at end of file + fcopy(final, "icons/mob/landmarks.dmi") + +/client/proc/debug_z_levels() + set name = "Debug Z-Levels" + set category = "Mapping" + + var/list/z_list = SSmapping.z_list + var/list/messages = list() + messages += "World: [world.maxx] x [world.maxy] x [world.maxz]
" + + var/list/linked_levels = list() + var/min_x = INFINITY + var/min_y = INFINITY + var/max_x = -INFINITY + var/max_y = -INFINITY + + for(var/z in 1 to max(world.maxz, z_list.len)) + if (z > z_list.len) + messages += "[z]: Unmanaged (out of bounds)
" + continue + var/datum/space_level/S = z_list[z] + if (!S) + messages += "[z]: Unmanaged (null)
" + continue + var/linkage + switch (S.linkage) + if (UNAFFECTED) + linkage = "no linkage" + if (SELFLOOPING) + linkage = "self-looping" + if (CROSSLINKED) + linkage = "linked at ([S.xi], [S.yi])" + linked_levels += S + min_x = min(min_x, S.xi) + min_y = min(min_y, S.yi) + max_x = max(max_x, S.xi) + max_y = max(max_y, S.yi) + else + linkage = "unknown linkage '[S.linkage]'" + + messages += "[z]: [S.name], [linkage], traits: [json_encode(S.traits)]
" + if (S.z_value != z) + messages += "-- z_value is [S.z_value], should be [z]
" + if (S.name == initial(S.name)) + messages += "-- name not set
" + if (z > world.maxz) + messages += "-- exceeds max z" + + var/grid[max_x - min_x + 1][max_y - min_y + 1] + for(var/datum/space_level/S in linked_levels) + grid[S.xi - min_x + 1][S.yi - min_y + 1] = S.z_value + + messages += "" + for(var/y in max_y to min_y step -1) + var/list/part = list() + for(var/x in min_x to max_x) + part += "[grid[x - min_x + 1][y - min_y + 1]]" + messages += "" + messages += "
[part.Join("")]
" + + to_chat(src, messages.Join("")) diff --git a/code/modules/awaymissions/signpost.dm b/code/modules/awaymissions/signpost.dm index f6fd4d9071..b1f85ca537 100644 --- a/code/modules/awaymissions/signpost.dm +++ b/code/modules/awaymissions/signpost.dm @@ -5,12 +5,12 @@ anchored = TRUE density = TRUE var/question = "Travel back?" - var/list/zlevels = list() + var/list/zlevels /obj/structure/signpost/New() . = ..() set_light(2) - zlevels = GLOB.station_z_levels + zlevels = SSmapping.levels_by_trait(ZTRAIT_STATION) /obj/structure/signpost/attackby(obj/item/W, mob/user, params) return attack_hand(user) @@ -44,6 +44,6 @@ zlevels = list() for(var/i in 1 to world.maxz) zlevels += i - zlevels -= ZLEVEL_CENTCOM // no easy victory, even with meme signposts + zlevels -= SSmapping.levels_by_trait(ZTRAIT_CENTCOM) // no easy victory, even with meme signposts // also, could you think of the horror if they ended up in a holodeck // template or something diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 9612fd8018..2bb75cfade 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -32,8 +32,9 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 /datum/round_event/immovable_rod/start() var/datum/round_event_control/immovable_rod/C = control var/startside = pick(GLOB.cardinals) - var/turf/startT = spaceDebrisStartLoc(startside, ZLEVEL_STATION_PRIMARY) - var/turf/endT = spaceDebrisFinishLoc(startside, ZLEVEL_STATION_PRIMARY) + var/z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) + var/turf/startT = spaceDebrisStartLoc(startside, z) + var/turf/endT = spaceDebrisFinishLoc(startside, z) new /obj/effect/immovablerod(startT, endT, C.special_target) /obj/effect/immovablerod @@ -60,7 +61,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 enter_link="(Click to orbit)", source=src, action=NOTIFY_ORBIT) GLOB.poi_list += src - + var/special_target_valid = FALSE if(special_target) var/turf/T = get_turf(special_target) diff --git a/code/modules/mapping/space_management/space_level.dm b/code/modules/mapping/space_management/space_level.dm new file mode 100644 index 0000000000..22a6d6ed6f --- /dev/null +++ b/code/modules/mapping/space_management/space_level.dm @@ -0,0 +1,14 @@ +/datum/space_level + var/name = "Your config settings failed, you need to fix this for the datum space levels to work" + var/list/neigbours = list() + var/list/traits + var/z_value = 1 //actual z placement + var/linkage = SELFLOOPING + var/xi + var/yi //imaginary placements on the grid + +/datum/space_level/New(new_z, new_name, new_linkage = SELFLOOPING, list/new_traits = list()) + z_value = new_z + name = new_name + traits = new_traits + set_linkage(new_linkage) diff --git a/code/modules/space_transition/space_transition.dm b/code/modules/mapping/space_management/space_transition.dm similarity index 75% rename from code/modules/space_transition/space_transition.dm rename to code/modules/mapping/space_management/space_transition.dm index edcad9b620..fa7cab9436 100644 --- a/code/modules/space_transition/space_transition.dm +++ b/code/modules/mapping/space_management/space_transition.dm @@ -1,18 +1,6 @@ -//This is a simple 3 by 3 grid working off the corpse of the space torus. The donut is dead, cube has been avenged! - -GLOBAL_LIST_EMPTY(z_levels_list) - -/datum/space_level - var/name = "Your config settings failed, you need to fix this for the datum space levels to work" - var/list/neigbours = list() - var/z_value = 1 //actual z placement - var/linked = SELFLOOPING - var/xi - var/yi //imaginary placements on the grid - -/datum/space_level/New(transition_type) - linked = transition_type - if(linked == SELFLOOPING) +/datum/space_level/proc/set_linkage(new_linkage) + linkage = new_linkage + if(linkage == SELFLOOPING) neigbours = list() var/list/L = list(TEXT_NORTH,TEXT_SOUTH,TEXT_EAST,TEXT_WEST) for(var/A in L) @@ -69,21 +57,15 @@ GLOBAL_LIST_EMPTY(z_levels_list) if(y-1 >= 1) neigbours |= grid[x][y-1] -/proc/setup_map_transitions() //listamania +/datum/controller/subsystem/mapping/proc/setup_map_transitions() //listamania var/list/SLS = list() - var/datum/space_level/D - var/list/cached_transitions = SSmapping.config.transition_config - var/conf_set_len = cached_transitions.len - var/k = 1 - for(var/A in cached_transitions) - D = new(cached_transitions[A]) - D.name = A - D.z_value = k - if(D.linked != CROSSLINKED) - GLOB.z_levels_list["[D.z_value]"] = D - else + var/list/cached_z_list = z_list + var/conf_set_len = 0 + for(var/A in cached_z_list) + var/datum/space_level/D = A + if (D.linkage == CROSSLINKED) SLS.Add(D) - k++ + conf_set_len++ var/list/point_grid[conf_set_len*2+1][conf_set_len*2+1] var/list/grid = list() var/datum/space_transition_point/P @@ -99,7 +81,7 @@ GLOBAL_LIST_EMPTY(z_levels_list) var/list/used_points = list() grid.Cut() while(SLS.len) - D = pick_n_take(SLS) + var/datum/space_level/D = pick_n_take(SLS) D.xi = P.x D.yi = P.y P.spl = D @@ -108,10 +90,7 @@ GLOBAL_LIST_EMPTY(z_levels_list) possible_points.Remove(used_points) D.set_neigbours(used_points) P = pick(possible_points) - grid["[D.z_value]"] = D - - for(var/A in GLOB.z_levels_list) - grid[A] = GLOB.z_levels_list[A] + CHECK_TICK //Lists below are pre-calculated values arranged in the list in such a way to be easily accessable in the loop by the counter //Its either this or madness with lotsa math @@ -123,13 +102,12 @@ GLOBAL_LIST_EMPTY(z_levels_list) var/list/x_pos_transition = list(1, 1, TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) //values of x for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective x value later in the code var/list/y_pos_transition = list(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2, 1, 1) //values of y for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective y value later in the code - for(var/zlevelnumber = 1, zlevelnumber<=grid.len, zlevelnumber++) - D = grid["[zlevelnumber]"] - if(!D) - CRASH("[zlevelnumber] position has no space level datum.") - if(!(D.neigbours.len)) + for(var/I in cached_z_list) + var/datum/space_level/D = I + if(!D.neigbours.len) continue - for(var/side = 1, side<5, side++) + var/zlevelnumber = D.z_value + for(var/side in 1 to 4) var/turf/beginning = locate(x_pos_beginning[side], y_pos_beginning[side], zlevelnumber) var/turf/ending = locate(x_pos_ending[side], y_pos_ending[side], zlevelnumber) var/list/turfblock = block(beginning, ending) @@ -143,12 +121,8 @@ GLOBAL_LIST_EMPTY(z_levels_list) while(D.neigbours["[dirside]"] && D.neigbours["[dirside]"] != D) D = D.neigbours["[dirside]"] zdestination = D.z_value - D = grid["[zlevelnumber]"] + D = I for(var/turf/open/space/S in turfblock) S.destination_x = x_pos_transition[side] == 1 ? S.x : x_pos_transition[side] S.destination_y = y_pos_transition[side] == 1 ? S.y : y_pos_transition[side] S.destination_z = zdestination - //S.maptext = "[zdestination]" // for debugging - - for(var/A in grid) - GLOB.z_levels_list[A] = grid[A] diff --git a/code/modules/mapping/space_management/traits.dm b/code/modules/mapping/space_management/traits.dm new file mode 100644 index 0000000000..78e2fa2a21 --- /dev/null +++ b/code/modules/mapping/space_management/traits.dm @@ -0,0 +1,46 @@ +// Look up levels[z].traits[trait] +/datum/controller/subsystem/mapping/proc/level_trait(z, trait) + if (!z) + return null + var/list/trait_list + if (z_list) + var/datum/space_level/S = get_level(z) + trait_list = S.traits + else + var/list/default_map_traits = DEFAULT_MAP_TRAITS + trait_list = default_map_traits[z][DL_TRAITS] + return trait_list[trait] + +// Check if levels[z] has any of the specified traits +/datum/controller/subsystem/mapping/proc/level_has_any_trait(z, list/traits) + for (var/I in traits) + if (level_trait(z, I)) + return TRUE + return FALSE + +// Check if levels[z] has all of the specified traits +/datum/controller/subsystem/mapping/proc/level_has_all_traits(z, list/traits) + for (var/I in traits) + if (!level_trait(z, I)) + return FALSE + return TRUE + +// Get a list of all z which have the specified trait +/datum/controller/subsystem/mapping/proc/levels_by_trait(trait) + . = list() + var/list/_z_list = z_list + for(var/A in _z_list) + var/datum/space_level/S = A + if (S.traits[trait]) + . += S.z_value + +// Get a list of all z which have any of the specified traits +/datum/controller/subsystem/mapping/proc/levels_by_any_trait(list/traits) + . = list() + var/list/_z_list = z_list + for(var/A in _z_list) + var/datum/space_level/S = A + for (var/trait in traits) + if (S.traits[trait]) + . += S.z_value + break diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm new file mode 100644 index 0000000000..5cb7261348 --- /dev/null +++ b/code/modules/mapping/space_management/zlevel_manager.dm @@ -0,0 +1,32 @@ +// Populate the space level list and prepare space transitions +/datum/controller/subsystem/mapping/proc/InitializeDefaultZLevels() + if (z_list) // subsystem/Recover or badminnery, no need + return + + z_list = list() + var/list/default_map_traits = DEFAULT_MAP_TRAITS + + if (default_map_traits.len != world.maxz) + WARNING("More or less map attributes pre-defined ([default_map_traits.len]) than existent z-levels ([world.maxz]). Ignoring the larger.") + if (default_map_traits.len > world.maxz) + default_map_traits.Cut(world.maxz + 1) + + for (var/I in 1 to default_map_traits.len) + var/list/features = default_map_traits[I] + var/datum/space_level/S = new(I, features[DL_NAME], features[DL_LINKAGE], features[DL_TRAITS]) + z_list += S + +/datum/controller/subsystem/mapping/proc/add_new_zlevel(name, linkage = SELFLOOPING, traits = list(), z_type = /datum/space_level) + var/new_z = z_list.len + 1 + if (world.maxz < new_z) + ++world.maxz + CHECK_TICK + // TODO: sleep here if the Z level needs to be cleared + var/datum/space_level/S = new z_type(new_z, name, linkage, traits) + z_list += S + return new_z + +/datum/controller/subsystem/mapping/proc/get_level(z) + . = z_list[z] + if (!.) + CRASH("Unmanaged z-level: '[z]'") diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm index df20ce5921..5ea18b631c 100644 --- a/code/modules/mining/aux_base.dm +++ b/code/modules/mining/aux_base.dm @@ -102,7 +102,10 @@ interface with the mining shuttle at the landing site if a mobile beacon is also if(href_list["random"] && !possible_destinations) usr.changeNext_move(CLICK_CD_RAPID) //Anti-spam - var/turf/LZ = safepick(Z_TURFS(ZLEVEL_MINING)) //Pick a random mining Z-level turf + var/list/all_mining_turfs = list() + for (var/z_level in SSmapping.levels_by_trait(ZTRAIT_MINING)) + all_mining_turfs += Z_TURFS(z_level) + var/turf/LZ = safepick(all_mining_turfs) //Pick a random mining Z-level turf if(!ismineralturf(LZ) && !istype(LZ, /turf/open/floor/plating/asteroid)) //Find a suitable mining turf. Reduces chance of landing in a bad area to_chat(usr, "Landing zone scan failed. Please try again.") diff --git a/code/modules/procedural_mapping/mapGenerators/lava_river.dm b/code/modules/procedural_mapping/mapGenerators/lava_river.dm index d825a02846..0486acae56 100644 --- a/code/modules/procedural_mapping/mapGenerators/lava_river.dm +++ b/code/modules/procedural_mapping/mapGenerators/lava_river.dm @@ -1,6 +1,6 @@ /datum/mapGenerator/lavaland - var/start_z = ZLEVEL_LAVALAND + var/start_z var/min_x = 0 var/min_y = 0 var/max_x = 0 @@ -19,11 +19,9 @@ /datum/mapGeneratorModule/river var/river_type = /turf/open/lava/smooth var/river_nodes = 4 - var/start_z = ZLEVEL_LAVALAND /datum/mapGeneratorModule/river/generate() var/datum/mapGenerator/lavaland/L = mother if(!istype(L)) return - start_z = L.start_z - spawn_rivers(start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) + spawn_rivers(L.start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) diff --git a/tgstation.dme b/tgstation.dme index 27146c1614..7f35e065b8 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -1685,6 +1685,10 @@ #include "code\modules\mapping\mapping_helpers.dm" #include "code\modules\mapping\reader.dm" #include "code\modules\mapping\ruins.dm" +#include "code\modules\mapping\space_management\space_level.dm" +#include "code\modules\mapping\space_management\space_transition.dm" +#include "code\modules\mapping\space_management\traits.dm" +#include "code\modules\mapping\space_management\zlevel_manager.dm" #include "code\modules\mining\abandoned_crates.dm" #include "code\modules\mining\aux_base.dm" #include "code\modules\mining\aux_base_camera.dm" @@ -2332,7 +2336,6 @@ #include "code\modules\shuttle\supply.dm" #include "code\modules\shuttle\syndicate.dm" #include "code\modules\shuttle\white_ship.dm" -#include "code\modules\space_transition\space_transition.dm" #include "code\modules\spells\spell.dm" #include "code\modules\spells\spell_types\aimed.dm" #include "code\modules\spells\spell_types\area_teleport.dm"