diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88eec1aece6..4b5975725c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: tools/ci/install_byond.sh source $HOME/BYOND/byond/bin/byondsetup tools/ci/generate_maplist.sh - tools/ci/dm.sh -Mci_map_testing paradise.dme + tools/ci/dm.sh -DCIMAP paradise.dme unit_tests_and_sql: name: Unit Tests + SQL Validation diff --git a/_maps/base_map.dm b/_maps/base_map.dm new file mode 100644 index 00000000000..2c44a73bb67 --- /dev/null +++ b/_maps/base_map.dm @@ -0,0 +1,5 @@ +#include "map_files\generic\centcomm.dmm" +#define CC_TRANSITION_CONFIG DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)) +#ifdef CIMAP +#include "ci_map_testing.dm" +#endif diff --git a/_maps/cyberiad.dm b/_maps/cyberiad.dm deleted file mode 100644 index ebdcba2a08e..00000000000 --- a/_maps/cyberiad.dm +++ /dev/null @@ -1,27 +0,0 @@ -/* -All z-levels should be identical in size. Their numbers should not matter. -The order of z-levels should not matter as long as their attributes are properly defined at MAP_TRANSITION_CONFIG. -Old code checked for the number of the z-level (for example whether there are any revheads on Z1), -currently it should check for the define (for example whether there are any revheads on any z-levels defined as STATION_LEVEL). -z1 = centcomm -z2 = station -z3 = lavaland -*/ - -#if !defined(USING_MAP_DATUM) - #include "map_files\generic\centcomm.dmm" - #include "map_files\cyberiad\cyberiad.dmm" - #include "map_files\generic\Lavaland.dmm" - - #define MAP_TRANSITION_CONFIG list(\ -DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) - - #define USING_MAP_DATUM /datum/map/cyberiad - -#elif !defined(MAP_OVERRIDE) - - #warn a map has already been included, ignoring Cyberiad. - -#endif diff --git a/_maps/delta.dm b/_maps/delta.dm deleted file mode 100644 index 89540207d8d..00000000000 --- a/_maps/delta.dm +++ /dev/null @@ -1,30 +0,0 @@ -/* -All z-levels should be identical in size. Their numbers should not matter. -The order of z-levels should not matter as long as their attributes are properly defined at MAP_TRANSITION_CONFIG. -Old code checked for the number of the z-level (for example whether there are any revheads on Z1), -currently it should check for the define (for example whether there are any revheads on any z-levels defined as STATION_LEVEL). -z1 = centcomm -z2 = station -z3 = lavaland - -Original design by Okand37 of TG Station -Lovingly ported by Purpose2 to Paradise -*/ - -#if !defined(USING_MAP_DATUM) - #include "map_files\generic\centcomm.dmm" - #include "map_files\delta\delta.dmm" - #include "map_files\generic\Lavaland.dmm" - - #define MAP_TRANSITION_CONFIG list(\ -DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) - - #define USING_MAP_DATUM /datum/map/delta - -#elif !defined(MAP_OVERRIDE) - - #warn a map has already been included, ignoring Delta. - -#endif diff --git a/_maps/map_datums.dm b/_maps/map_datums.dm new file mode 100644 index 00000000000..0e355540dc8 --- /dev/null +++ b/_maps/map_datums.dm @@ -0,0 +1 @@ +// See code/modules/mapping for map datums diff --git a/_maps/metastation.dm b/_maps/metastation.dm deleted file mode 100644 index 1e71d218835..00000000000 --- a/_maps/metastation.dm +++ /dev/null @@ -1,27 +0,0 @@ -/* -All z-levels should be identical in size. Their numbers should not matter. -The order of z-levels should not matter as long as their attributes are properly defined at MAP_TRANSITION_CONFIG. -Old code checked for the number of the z-level (for example whether there are any revheads on Z1), -currently it should check for the define (for example whether there are any revheads on any z-levels defined as STATION_LEVEL). -z1 = centcomm -z2 = station -z3 = lavaland -*/ - -#if !defined(USING_MAP_DATUM) - #include "map_files\generic\centcomm.dmm" - #include "map_files\MetaStation\MetaStation.dmm" - #include "map_files\generic\Lavaland.dmm" - - #define MAP_TRANSITION_CONFIG list(\ -DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) - - #define USING_MAP_DATUM /datum/map/metastation - -#elif !defined(MAP_OVERRIDE) - - #warn a map has already been included, ignoring MetaStation. - -#endif diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 29e0f9b52b9..b8b47aa8656 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -15,9 +15,9 @@ GLOBAL_VAR(church_name) return name -GLOBAL_VAR(command_name) +// TODO: Remove this. Its always gonna be NAS Trurl /proc/command_name() - return GLOB.using_map.dock_name + return "NAS Trurl" GLOBAL_VAR(religion_name) /proc/religion_name() @@ -31,11 +31,8 @@ GLOBAL_VAR(religion_name) return capitalize(name) -/proc/system_name() - return GLOB.using_map.starsys_name - /proc/station_name() - return GLOB.using_map.station_name + return SSmapping.map_datum.fluff_name /proc/new_station_name() var/random = rand(1,5) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 68acde69228..11a04c7cc3c 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -653,6 +653,62 @@ // return the split html object to the caller return s +//Used for applying byonds text macros to strings that are loaded at runtime +/proc/apply_text_macros(string) + var/next_backslash = findtext(string, "\\") + if(!next_backslash) + return string + + var/leng = length(string) + + var/next_space = findtext(string, " ", next_backslash + length(string[next_backslash])) + if(!next_space) + next_space = leng - next_backslash + + if(!next_space) //trailing bs + return string + + var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash) + var/macro = lowertext(copytext(string, next_backslash + length(string[next_backslash]), next_space)) + var/rest = next_backslash > leng ? "" : copytext(string, next_space + length(string[next_space])) + + //See https://secure.byond.com/docs/ref/info.html#/DM/text/macros + switch(macro) + //prefixes/agnostic + if("the") + rest = text("\the []", rest) + if("a") + rest = text("\a []", rest) + if("an") + rest = text("\an []", rest) + if("proper") + rest = text("\proper []", rest) + if("improper") + rest = text("\improper []", rest) + if("roman") + rest = text("\roman []", rest) + //postfixes + if("th") + base = text("[]\th", rest) + if("s") + base = text("[]\s", rest) + if("he") + base = text("[]\he", rest) + if("she") + base = text("[]\she", rest) + if("his") + base = text("[]\his", rest) + if("himself") + base = text("[]\himself", rest) + if("herself") + base = text("[]\herself", rest) + if("hers") + base = text("[]\hers", rest) + + . = base + if(rest) + . += .(rest) + /** * Proc to generate a "rank colour" from a client * @@ -671,3 +727,4 @@ if(C.donator_level) return "#e67e22" // Patreon orange return null + diff --git a/code/_globalvars/mapping.dm b/code/_globalvars/mapping.dm index 2b435e0bede..caf4a2a58bb 100644 --- a/code/_globalvars/mapping.dm +++ b/code/_globalvars/mapping.dm @@ -8,9 +8,6 @@ GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, S GLOBAL_LIST_INIT(alldirs2, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST, NORTH, SOUTH, EAST, WEST)) GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) -// This must exist early on or shit breaks bad -GLOBAL_DATUM_INIT(using_map, /datum/map, new USING_MAP_DATUM) - GLOBAL_LIST(global_map) // This is the array of zlevels | list(list(1,5),list(4,3)) | becomes a 2D array of zlevels //Resulting sector map looks like //|_1_|_4_| diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 6d28489127a..554fa2581a4 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -86,9 +86,6 @@ GLOBAL_VAR_INIT(copier_items_printed, 0) GLOBAL_VAR_INIT(copier_max_items, 300) GLOBAL_VAR_INIT(copier_items_printed_logged, FALSE) - -GLOBAL_VAR(map_name) // Self explanatory - GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 3b39682efbf..26be0f214b7 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -275,6 +275,9 @@ /// Enable auto profiler of rounds var/auto_profile = FALSE + // Enable map voting + var/map_voting_enabled = FALSE + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -770,6 +773,8 @@ max_client_cid_history = text2num(value) if("enable_auto_profiler") auto_profile = TRUE + if("enable_map_voting") + map_voting_enabled = TRUE else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm index 81aab3b0759..4cfe06083e3 100644 --- a/code/controllers/subsystem/dbcore.dm +++ b/code/controllers/subsystem/dbcore.dm @@ -193,7 +193,7 @@ SUBSYSTEM_DEF(dbcore) if(!IsConnected()) return var/datum/db_query/query_round_end = SSdbcore.NewQuery( - "UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = :game_mode_result, station_name = :station_name WHERE id = :round_id", + "UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = :game_mode_result WHERE id = :round_id", list("game_mode_result" = SSticker.mode_result, "station_name" = station_name(), "round_id" = GLOB.round_id) ) query_round_end.Execute() diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index ac8165aca7a..87846d051f0 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -1,31 +1,51 @@ SUBSYSTEM_DEF(mapping) - name = "Mapping" - init_order = INIT_ORDER_MAPPING // 9 - flags = SS_NO_FIRE + name = "Mapping" + init_order = INIT_ORDER_MAPPING // 9 + flags = SS_NO_FIRE + /// What map datum are we using + var/datum/map/map_datum + /// What map will be used next round + var/datum/map/next_map + +// This has to be here because world/New() uses [station_name()], which looks this datum up +/datum/controller/subsystem/mapping/PreInit() + . = ..() + if(map_datum) // Dont do this again if we are recovering + return + if(fexists("data/next_map.txt")) + var/list/lines = file2list("data/next_map.txt") + // Check its valid + try + map_datum = text2path(lines[1]) + map_datum = new map_datum + catch + map_datum = new /datum/map/cyberiad // Assume cyberiad if non-existent + fdel("data/next_map.txt") // Remove to avoid the same map existing forever + else + map_datum = new /datum/map/cyberiad // Assume cyberiad if non-existent + +/datum/controller/subsystem/mapping/Shutdown() + if(next_map) // Save map for next round + var/F = file("data/next_map.txt") + F << next_map.type /datum/controller/subsystem/mapping/Initialize(timeofday) // Load all Z level templates preloadTemplates() + + // Load the station + loadStation() + + // Load lavaland + loadLavaland() + // Pick a random away mission. if(!config.disable_away_missions) load_away_mission() + // Seed space ruins if(!config.disable_space_ruins) - // load in extra levels of space ruins - var/load_zlevels_timer = start_watch() - log_startup_progress("Creating random space levels...") - var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max) - for(var/i = 1, i <= num_extra_space, i++) - GLOB.space_manager.add_new_zlevel("Ruin Area #[i]", linkage = CROSSLINKED, traits = list(REACHABLE, SPAWN_RUINS)) - log_startup_progress("Loaded random space levels in [stop_watch(load_zlevels_timer)]s.") - - // Now spawn ruins, random budget between 20 and 30 for all zlevels combined. - // While this may seem like a high number, the amount of ruin Z levels can be anywhere between 3 and 7. - // Note that this budget is not split evenly accross all zlevels - log_startup_progress("Seeding ruins...") - var/seed_ruins_timer = start_watch() - seedRuins(levels_by_trait(SPAWN_RUINS), rand(20, 30), /area/space, GLOB.space_ruins_templates) - log_startup_progress("Successfully seeded ruins in [stop_watch(seed_ruins_timer)]s.") + handleRuins() // Makes a blank space level for the sake of randomness GLOB.space_manager.add_new_zlevel("Empty Area", linkage = CROSSLINKED, traits = list(REACHABLE)) @@ -63,12 +83,6 @@ SUBSYSTEM_DEF(mapping) GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs) - // Map name. Break these down into SSmapping controller vars instaed of GLOBs at some point - if(GLOB.using_map && GLOB.using_map.name) - GLOB.map_name = "[GLOB.using_map.name]" - else - GLOB.map_name = "Unknown" - // World name if(config && config.server_name) world.name = "[config.server_name]: [station_name()]" @@ -77,6 +91,56 @@ SUBSYSTEM_DEF(mapping) return ..() +// Do not confuse with seedRuins() +/datum/controller/subsystem/mapping/proc/handleRuins() + // load in extra levels of space ruins + var/load_zlevels_timer = start_watch() + log_startup_progress("Creating random space levels...") + var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max) + for(var/i in 1 to num_extra_space) + GLOB.space_manager.add_new_zlevel("Ruin Area #[i]", linkage = CROSSLINKED, traits = list(REACHABLE, SPAWN_RUINS)) + log_startup_progress("Loaded random space levels in [stop_watch(load_zlevels_timer)]s.") + + // Now spawn ruins, random budget between 20 and 30 for all zlevels combined. + // While this may seem like a high number, the amount of ruin Z levels can be anywhere between 3 and 7. + // Note that this budget is not split evenly accross all zlevels + log_startup_progress("Seeding ruins...") + var/seed_ruins_timer = start_watch() + seedRuins(levels_by_trait(SPAWN_RUINS), rand(20, 30), /area/space, GLOB.space_ruins_templates) + log_startup_progress("Successfully seeded ruins in [stop_watch(seed_ruins_timer)]s.") + +// Loads in the station +/datum/controller/subsystem/mapping/proc/loadStation() + ASSERT(map_datum.map_path) + if(!fexists(map_datum.map_path)) + // Make a VERY OBVIOUS error + to_chat(world, "ERROR: The path specified for the map to load is invalid. No station has been loaded!") + return + + var/watch = start_watch() + log_startup_progress("Loading [map_datum.fluff_name]...") + // This should always be Z2, but you never know + var/map_z_level = GLOB.space_manager.add_new_zlevel(MAIN_STATION, linkage = CROSSLINKED, traits = list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)) + GLOB.maploader.load_map(file(map_datum.map_path), z_offset = map_z_level) + log_startup_progress("Loaded [map_datum.fluff_name] in [stop_watch(watch)]s") + + // Save station name in the DB + if(!SSdbcore.IsConnected()) + return + var/datum/db_query/query_set_map = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET start_datetime=NOW(), map_name=:mapname, station_name=:stationname WHERE id=:round_id", + list("mapname" = map_datum.technical_name, "stationname" = map_datum.fluff_name, "round_id" = GLOB.round_id) + ) + query_set_map.Execute(async = FALSE) // This happens during a time of intense server lag, so should be non-async + qdel(query_set_map) + +// Loads in lavaland +/datum/controller/subsystem/mapping/proc/loadLavaland() + var/watch = start_watch() + log_startup_progress("Loading Lavaland...") + var/lavaland_z_level = GLOB.space_manager.add_new_zlevel(MINING, linkage = SELFLOOPING, traits = list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK)) + GLOB.maploader.load_map(file("_maps/map_files/generic/Lavaland.dmm"), z_offset = lavaland_z_level) + log_startup_progress("Loaded Lavaland in [stop_watch(watch)]s") /datum/controller/subsystem/mapping/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = /area/space, list/potentialRuins) if(!z_levels || !z_levels.len) diff --git a/code/controllers/subsystem/titlescreen.dm b/code/controllers/subsystem/titlescreen.dm index 7fe59ec9093..90ea775885e 100644 --- a/code/controllers/subsystem/titlescreen.dm +++ b/code/controllers/subsystem/titlescreen.dm @@ -16,8 +16,6 @@ SUBSYSTEM_DEF(title) else if(L.len > 1) if(use_rare_screens && lowertext(L[1]) == "rare") title_screens += S - else if(GLOB.using_map && (lowertext(L[1]) == lowertext(GLOB.using_map.name))) - title_screens += S if(!isemptylist(title_screens)) if(length(title_screens) > 1) diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 346297d900f..743d14a3962 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -177,6 +177,16 @@ SUBSYSTEM_DEF(vote) if("crew_transfer") if(. == "Initiate Crew Transfer") init_shift_change(null, 1) + if("map") + // Find target map. + var/datum/map/top_voted_map + for(var/x in subtypesof(/datum/map)) + var/datum/map/M = x + // Set top voted map + if(. == "[initial(M.fluff_name)] ([initial(M.technical_name)])") + top_voted_map = M + to_chat(world, "Map for next round: [initial(top_voted_map.fluff_name)] ([initial(top_voted_map.technical_name)])") + SSmapping.next_map = new top_voted_map if(restart) @@ -197,7 +207,7 @@ SUBSYSTEM_DEF(vote) return vote return 0 -/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key) +/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, code_invoked = FALSE) if(!mode) if(started_time != null && !check_rights(R_ADMIN)) var/next_allowed_time = (started_time + config.vote_delay) @@ -223,6 +233,14 @@ SUBSYSTEM_DEF(vote) return 0 question = "End the shift?" choices.Add("Initiate Crew Transfer", "Continue The Round") + if("map") + if(!(check_rights(R_SERVER) || code_invoked)) + return FALSE + question = "Map for next round" + for(var/x in subtypesof(/datum/map)) + var/datum/map/M = x + choices.Add("[initial(M.fluff_name)] ([initial(M.technical_name)])") + if("custom") question = html_encode(input(usr,"What is the vote for?") as text|null) if(!question) return 0 @@ -248,11 +266,7 @@ SUBSYSTEM_DEF(vote) Click here or type vote to place your vote. You have [config.vote_period/10] seconds to vote."}) switch(vote_type) - if("crew_transfer") - SEND_SOUND(world, sound('sound/ambience/alarm4.ogg')) - if("gamemode") - SEND_SOUND(world, sound('sound/ambience/alarm4.ogg')) - if("custom") + if("crew_transfer", "gamemode", "custom", "map") SEND_SOUND(world, sound('sound/ambience/alarm4.ogg')) if(mode == "gamemode" && SSticker.ticker_going) SSticker.ticker_going = FALSE @@ -321,6 +335,12 @@ SUBSYSTEM_DEF(vote) if(admin) dat += "\t([config.allow_vote_mode?"Allowed":"Disallowed"])" + dat += "

