SUBSYSTEM_DEF(mapping) name = "Mapping" init_order = INIT_ORDER_MAPPING flags = SS_NO_FIRE var/list/nuke_tiles = list() var/list/nuke_threats = list() var/datum/map_config/config var/datum/map_config/next_map_config var/list/map_templates = list() var/list/ruins_templates = list() var/list/space_ruins_templates = list() var/list/lava_ruins_templates = list() var/list/shuttle_templates = list() var/list/shelter_templates = list() var/list/areas_in_z = list() var/loading_ruins = FALSE // Z-manager stuff var/station_start // should only be used for maploading-related tasks var/space_levels_so_far = 0 var/list/z_list var/datum/space_level/transit var/datum/space_level/empty_space var/dmm_suite/loader /datum/controller/subsystem/mapping/PreInit() if(!config) #ifdef FORCE_MAP config = load_map_config(FORCE_MAP) #else config = load_map_config(error_if_missing = FALSE) #endif return ..() /datum/controller/subsystem/mapping/Initialize(timeofday) if(config.defaulted) to_chat(world, "Unable to load next map config, defaulting to Box Station") loader = new loadWorld() repopulate_sorted_areas() process_teleport_locs() //Sets up the wizard teleport locations preloadTemplates() #ifndef LOWMEMORYMODE // Create space ruin levels while (space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT) ++space_levels_so_far add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE) // and one level with no ruins empty_space = add_new_zlevel("Empty Area [1 + space_levels_so_far]", list(ZTRAIT_LINKAGE = CROSSLINKED)) // and the transit level transit = add_new_zlevel("Transit", list(ZTRAIT_TRANSIT = TRUE)) // Pick a random away mission. createRandomZlevel() // Generate mining ruins loading_ruins = TRUE 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) // 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 #endif repopulate_sorted_areas() // Set up Z-level transitions. setup_map_transitions() generate_station_area_list() QDEL_NULL(loader) ..() /* Nuke threats, for making the blue tiles on the station go RED Used by the AI doomsday and the self destruct nuke. */ /datum/controller/subsystem/mapping/proc/add_nuke_threat(datum/nuke) nuke_threats[nuke] = TRUE check_nuke_threats() /datum/controller/subsystem/mapping/proc/remove_nuke_threat(datum/nuke) nuke_threats -= nuke check_nuke_threats() /datum/controller/subsystem/mapping/proc/check_nuke_threats() for(var/datum/d in nuke_threats) if(!istype(d) || QDELETED(d)) nuke_threats -= d for(var/N in nuke_tiles) var/turf/open/floor/circuit/C = N C.update_icon() /datum/controller/subsystem/mapping/Recover() flags |= SS_NO_INIT map_templates = SSmapping.map_templates ruins_templates = SSmapping.ruins_templates space_ruins_templates = SSmapping.space_ruins_templates lava_ruins_templates = SSmapping.lava_ruins_templates shuttle_templates = SSmapping.shuttle_templates shelter_templates = SSmapping.shelter_templates config = SSmapping.config next_map_config = SSmapping.next_map_config z_list = SSmapping.z_list #define INIT_ANNOUNCE(X) to_chat(world, "[X]"); log_world(X) /datum/controller/subsystem/mapping/proc/LoadGroup(list/errorList, name, path, files, list/traits, list/default_traits) var/start_time = REALTIMEOFDAY if (!islist(files)) // handle single-level maps files = list(files) // check that the total z count of all maps matches the list of traits var/total_z = 0 for (var/file in files) var/full_path = "_maps/[path]/[file]" var/bounds = loader.load_map(file(full_path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE) files[file] = total_z // save the start Z of this file total_z += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1 if (!length(traits)) // null or empty - default for (var/i in 1 to total_z) traits += list(default_traits) else if (total_z != traits.len) // mismatch INIT_ANNOUNCE("WARNING: [traits.len] trait sets specified for [total_z] z-levels in [path]!") if (total_z < traits.len) // ignore extra traits traits.Cut(total_z + 1) while (total_z > traits.len) // fall back to defaults on extra levels traits += list(default_traits) // preload the relevant space_level datums var/start_z = world.maxz + 1 var/i = 0 for (var/level in traits) add_new_zlevel("[name][i ? " [i + 1]" : ""]", level) ++i // load the maps for (var/file in files) var/full_path = "_maps/[path]/[file]" if(!loader.load_map(file(full_path), 0, 0, start_z + files[file], no_changeturf = TRUE)) errorList |= full_path INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!") /datum/controller/subsystem/mapping/proc/loadWorld() //if any of these fail, something has gone horribly, HORRIBLY, wrong var/list/FailedZs = list() // ensure we have space_level datums for compiled-in maps InitializeDefaultZLevels() // load the station station_start = world.maxz + 1 INIT_ANNOUNCE("Loading [config.map_name]...") LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION) 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]") query_round_map_name.Execute() #ifndef LOWMEMORYMODE // TODO: remove this when the DB is prepared for the z-levels getting reordered while (world.maxz < (5 - 1) && space_levels_so_far < ZLEVEL_SPACE_RUIN_COUNT) ++space_levels_so_far add_new_zlevel("Empty Area [space_levels_so_far]", ZTRAITS_SPACE) // load mining if(config.minetype == "lavaland") LoadGroup(FailedZs, "Lavaland", "map_files/Mining", "Lavaland.dmm", default_traits = ZTRAITS_LAVALAND) else if (!isnull(config.minetype)) INIT_ANNOUNCE("WARNING: An unknown minetype '[config.minetype]' was set! This is being ignored! Update the maploader code!") // load Reebe LoadGroup(FailedZs, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE) #endif 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) for(var/I in 2 to FailedZs.len) msg += ", [FailedZs[I]]" msg += ". Yell at your server host!" INIT_ANNOUNCE(msg) #undef INIT_ANNOUNCE GLOBAL_LIST_EMPTY(the_station_areas) /datum/controller/subsystem/mapping/proc/generate_station_area_list() var/list/station_areas_blacklist = typecacheof(list(/area/space, /area/mine, /area/ruin, /area/asteroid/nearstation)) for(var/area/A in world) var/turf/picked = safepick(get_area_turfs(A.type)) if(picked && is_station_level(picked.z)) if(!(A.type in GLOB.the_station_areas) && !is_type_in_typecache(A, station_areas_blacklist)) GLOB.the_station_areas.Add(A.type) if(!GLOB.the_station_areas.len) log_world("ERROR: Station areas list failed to generate!") /datum/controller/subsystem/mapping/proc/maprotate() var/players = GLOB.clients.len var/list/mapvotes = list() //count votes var/amv = CONFIG_GET(flag/allow_map_voting) if(amv) for (var/client/c in GLOB.clients) var/vote = c.prefs.preferred_map if (!vote) if (global.config.defaultmap) mapvotes[global.config.defaultmap.map_name] += 1 continue mapvotes[vote] += 1 else for(var/M in global.config.maplist) mapvotes[M] = 1 //filter votes for (var/map in mapvotes) if (!map) mapvotes.Remove(map) if (!(map in global.config.maplist)) mapvotes.Remove(map) continue var/datum/map_config/VM = global.config.maplist[map] if (!VM) mapvotes.Remove(map) continue if (VM.voteweight <= 0) mapvotes.Remove(map) continue if (VM.config_min_users > 0 && players < VM.config_min_users) mapvotes.Remove(map) continue if (VM.config_max_users > 0 && players > VM.config_max_users) mapvotes.Remove(map) continue if(amv) mapvotes[map] = mapvotes[map]*VM.voteweight var/pickedmap = pickweight(mapvotes) if (!pickedmap) return var/datum/map_config/VM = global.config.maplist[pickedmap] message_admins("Randomly rotating map to [VM.map_name]") . = changemap(VM) if (. && VM.map_name != config.map_name) to_chat(world, "Map rotation has chosen [VM.map_name] for next round!") /datum/controller/subsystem/mapping/proc/changemap(var/datum/map_config/VM) if(!VM.MakeNextMap()) next_map_config = load_map_config(default_to_box = TRUE) message_admins("Failed to set new map with next_map.json for [VM.map_name]! Using default as backup!") return next_map_config = VM return TRUE /datum/controller/subsystem/mapping/proc/preloadTemplates(path = "_maps/templates/") //see master controller setup var/list/filelist = flist(path) for(var/map in filelist) var/datum/map_template/T = new(path = "[path][map]", rename = "[map]") map_templates[T.name] = T preloadRuinTemplates() preloadShuttleTemplates() preloadShelterTemplates() /datum/controller/subsystem/mapping/proc/preloadRuinTemplates() // Still supporting bans by filename var/list/banned = generateMapList("[global.config.directory]/lavaruinblacklist.txt") banned += generateMapList("[global.config.directory]/spaceruinblacklist.txt") for(var/item in sortList(subtypesof(/datum/map_template/ruin), /proc/cmp_ruincost_priority)) var/datum/map_template/ruin/ruin_type = item // screen out the abstract subtypes if(!initial(ruin_type.id)) continue var/datum/map_template/ruin/R = new ruin_type() if(banned.Find(R.mappath)) continue map_templates[R.name] = R ruins_templates[R.name] = R if(istype(R, /datum/map_template/ruin/lavaland)) lava_ruins_templates[R.name] = R else if(istype(R, /datum/map_template/ruin/space)) space_ruins_templates[R.name] = R /datum/controller/subsystem/mapping/proc/preloadShuttleTemplates() var/list/unbuyable = generateMapList("[global.config.directory]/unbuyableshuttles.txt") for(var/item in subtypesof(/datum/map_template/shuttle)) var/datum/map_template/shuttle/shuttle_type = item if(!(initial(shuttle_type.suffix))) continue var/datum/map_template/shuttle/S = new shuttle_type() if(unbuyable.Find(S.mappath)) S.can_be_bought = FALSE shuttle_templates[S.shuttle_id] = S map_templates[S.shuttle_id] = S /datum/controller/subsystem/mapping/proc/preloadShelterTemplates() for(var/item in subtypesof(/datum/map_template/shelter)) var/datum/map_template/shelter/shelter_type = item if(!(initial(shelter_type.mappath))) continue var/datum/map_template/shelter/S = new shelter_type() shelter_templates[S.shelter_id] = S map_templates[S.shelter_id] = S