diff --git a/.gitignore b/.gitignore
index d4a9c94a4fd..1d850f44988 100644
--- a/.gitignore
+++ b/.gitignore
@@ -208,3 +208,6 @@ check_regex_output.txt
# local development secrets
/secret
+
+# map build directory
+/_maps2eb
diff --git a/_maps/ship_config_schema.json b/_maps/ship_config_schema.json
index b1cfac1b563..6dff27d4985 100644
--- a/_maps/ship_config_schema.json
+++ b/_maps/ship_config_schema.json
@@ -92,7 +92,7 @@
"title": "Map File Path",
"type": "string",
"description": "The path to the ship class's map file. Use forward slashes (/) for directories, and include the .dmm extension. Map files must be somewhere under the _maps folder.",
- "pattern": "^_maps/([a-zA-Z0-9_/.-]*)dmm$"
+ "pattern": "^.*_maps/([a-zA-Z0-9_/.-]*)dmm$"
},
"job_slots": {
"title": "Job Slots",
diff --git a/code/__DEFINES/__starfly.dm b/code/__DEFINES/__starfly.dm
index 019f3b9e0bd..0de07043d82 100644
--- a/code/__DEFINES/__starfly.dm
+++ b/code/__DEFINES/__starfly.dm
@@ -45,5 +45,5 @@
#define STARFLY13_MODULE_ROSEUS_GALACTIC_ENABLED
#define STARFLY13_MODULE_SINTA_UNATHI_ENABLED
#define STARFLY13_MODULE_STARFLY_BRANDING_ENABLED
-#define STARFLY13_MODULE_STARFLY_SHIPS_ENABLED
+#define STARFLY13_MODULE_STARFLY_MAPS_ENABLED
#define STARFLY13_MODULE_YEOSA_UNATHI_ENABLED
diff --git a/code/__DEFINES/__starflymaps.dm b/code/__DEFINES/__starflymaps.dm
new file mode 100644
index 00000000000..00e145ec0cb
--- /dev/null
+++ b/code/__DEFINES/__starflymaps.dm
@@ -0,0 +1,29 @@
+// __starflymaps.dm
+// Copyright 2026 Patrick Meade.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+//---------------------------------------------------------------------------
+
+// if we weren't provided with a MAPROOT at build time
+#ifndef MAPROOT
+ // if we're using STARFLY-13 maps...
+ #ifdef STARFLY13_MODULE_STARFLY_MAPS_ENABLED
+ // STARFLY-13 maps are located at _maps 2: electric boogaloo
+ #define MAPROOT "_maps2eb"
+ // otherwise...
+ #else
+ // Shiptest maps are located at _maps
+ #define MAPROOT "_maps"
+ #endif // #ifdef STARFLY13_MODULE_STARFLY_MAPS_ENABLED
+#endif // #ifndef MAPROOT
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 4ac4b8c6b80..3c1332eef5f 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -136,7 +136,7 @@ SUBSYSTEM_DEF(mapping)
#define INIT_ANNOUNCE(X) to_chat(world, span_boldannounce("[X]")); log_world(X)
-/datum/controller/subsystem/mapping/proc/preloadTemplates(path = "_maps/templates/") //see master controller setup
+/datum/controller/subsystem/mapping/proc/preloadTemplates(path = MAPROOT + "/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]")
@@ -181,12 +181,12 @@ SUBSYSTEM_DEF(mapping)
#define CHECK_LIST_EXISTS(X) if(!islist(data[X])) { stack_trace("[##X] missing from json!"); continue; }
/datum/controller/subsystem/mapping/proc/load_ship_templates()
ship_purchase_list = list()
- var/list/filelist = flist("_maps/configs/")
+ var/list/filelist = flist(MAPROOT + "/configs/")
filelist = sortList(filelist)
for(var/filename in filelist)
- var/file = file("_maps/configs/" + filename)
+ var/file = file(MAPROOT + "/configs/" + filename)
if(!file)
stack_trace("Could not open map config: [filename]")
continue
@@ -203,8 +203,9 @@ SUBSYSTEM_DEF(mapping)
CHECK_STRING_EXISTS("map_name")
CHECK_STRING_EXISTS("map_path")
CHECK_LIST_EXISTS("job_slots")
- var/datum/map_template/shuttle/S = new(data["map_path"], data["map_name"], TRUE)
- S.file_name = data["map_path"]
+ var/fixed_path = fix_map_path(data["map_path"])
+ var/datum/map_template/shuttle/S = new(fixed_path, data["map_name"], TRUE)
+ S.file_name = fixed_path
S.ship_class = data["map_name"]
if(istext(data["map_short_name"]))
@@ -446,3 +447,20 @@ SUBSYSTEM_DEF(mapping)
height--
var/list/allocation_coords = SSmapping.get_free_allocation(allocation_type, width, height, allocation_jump)
return new /datum/virtual_level(new_name, traits, mapzone, allocation_coords[1], allocation_coords[2], allocation_coords[1] + width, allocation_coords[2] + height, allocation_coords[3])
+
+
+
+/proc/fix_map_path(var/path)
+ if(!istext(path))
+ return path
+
+ // Match everything up to and including "_maps"
+ var/regex/r = regex(@"^.*_maps")
+
+ // If it matches, replace that portion with MAPROOT
+ if(r.Find(path))
+ var/new_path = r.Replace(path, MAPROOT)
+ return new_path
+
+ // If no match, just return original (or you could warn)
+ return path
diff --git a/code/controllers/subsystem/overmap.dm b/code/controllers/subsystem/overmap.dm
index eba96e43c70..c14b2121a56 100644
--- a/code/controllers/subsystem/overmap.dm
+++ b/code/controllers/subsystem/overmap.dm
@@ -1283,5 +1283,5 @@ SUBSYSTEM_DEF(overmap)
name = "Abandoned - New Dawn"
can_be_selected_randomly = FALSE
can_jump_to = FALSE
- json = '_maps/sectors/sunset_starsystem.json'
+ json = MAPROOT + "/sectors/sunset_starsystem.json"
generator_type = OVERMAP_GENERATOR_JSON
diff --git a/code/datums/mapgen/greebles/jungleplanet.dm b/code/datums/mapgen/greebles/jungleplanet.dm
index ab10745e3a5..23f3f17c88c 100644
--- a/code/datums/mapgen/greebles/jungleplanet.dm
+++ b/code/datums/mapgen/greebles/jungleplanet.dm
@@ -126,153 +126,153 @@
/datum/map_template/greeble/jungleplanet
name = "Jungle Greeble 1"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_1.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_1.dmm"
description = "A small attempt at a fishing pier"
clear_everything = TRUE
/datum/map_template/greeble/jungleplanet/two
name = "Jungle Greeble 2"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_2.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_2.dmm"
description = "A simple garden, abandoned."
/datum/map_template/greeble/jungleplanet/three
name = "Jungle Greeble 3"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_3.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_3.dmm"
description = "A grave for the damned."
/datum/map_template/greeble/jungleplanet/four
name = "Jungle Greeble 4"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_4.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_4.dmm"
description = "Beware of bear caves."
/datum/map_template/greeble/jungleplanet/five
name = "Jungle Greeble 5"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_5.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_5.dmm"
description = "Murdered frontiersman bits."
/datum/map_template/greeble/jungleplanet/six
name = "Jungle Greeble 6"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_6.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_6.dmm"
description = "Radioactive hole."
/datum/map_template/greeble/jungleplanet/seven
name = "Jungle Greeble 7"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_7.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_7.dmm"
description = "A radioactive incident."
/datum/map_template/greeble/jungleplanet/eight
name = "Jungle Greeble 8"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_8.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_8.dmm"
description = "Radioactive Hole 2"
/datum/map_template/greeble/jungleplanet/nine
name = "Jungle Greeble 9"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_9.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_9.dmm"
description = "Radioactive debris."
/datum/map_template/greeble/jungleplanet/ten
name = "Jungle Greeble 10"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_10.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_10.dmm"
description = "A simple pond."
/datum/map_template/greeble/jungleplanet/eleven
name = "Jungle Greeble 11"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_11.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_11.dmm"
description = "A radioactive trench left to rot."
/datum/map_template/greeble/jungleplanet/twelve
name = "Jungle Greeble 12"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_12.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_12.dmm"
description = "A friend's grave adorned with a flower"
/datum/map_template/greeble/jungleplanet/thirteen
name = "Jungle Greeble 13"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_13.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_13.dmm"
description = "Industrial decay."
/datum/map_template/greeble/jungleplanet/fourteen
name = "Jungle Greeble 14"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_14.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_14.dmm"
description = "Industrial decay 2"
/datum/map_template/greeble/jungleplanet/fifteen
name = "Jungle Greeble 15"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_15.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_15.dmm"
description = "Industrial decay the third."
/datum/map_template/greeble/jungleplanet/sixteen
name = "Jungle Greeble 16"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_16.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_16.dmm"
description = "A broken road."
/datum/map_template/greeble/jungleplanet/seventeen
name = "Jungle Greeble 17"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_17.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_17.dmm"
description = "Who let the chickens out? Who put them here? A chicken coop."
/datum/map_template/greeble/jungleplanet/eighteen
name = "Jungle Greeble 18"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_18.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_18.dmm"
description = "Mangrooves in the water."
/datum/map_template/greeble/jungleplanet/nineteen
name = "Jungle Greeble 19"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_19.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_19.dmm"
description = "Syndicate-flavored debris."
/datum/map_template/greeble/jungleplanet/twenty
name = "Jungle Greeble 20"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_20.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_20.dmm"
description = "Frontiersmen aren't very good pilots, are they?"
/datum/map_template/greeble/jungleplanet/twentyone
name = "Jungle Greeble 21"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_21.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_21.dmm"
description = "Be careful where you dig! Not every site is safe."
/datum/map_template/greeble/jungleplanet/twentytwo
name = "Jungle Greeble 22"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_22.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_22.dmm"
description = "A crashed vessel, completely out of place."
/datum/map_template/greeble/jungleplanet/twentythree
name = "Jungle Greeble 23"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_23.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_23.dmm"
description = "Not every colonization effort works out."
/datum/map_template/greeble/jungleplanet/twentyfour
name = "Jungle Greeble 24"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_24.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_24.dmm"
description = "A quaint little rest from a hostile world."
/datum/map_template/greeble/jungleplanet/twentyfive
name = "Jungle Greeble 25"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_25.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_25.dmm"
description = "An overgrown landing pad. The trees have reclaimed the concrete."
/datum/map_template/greeble/jungleplanet/twentysix
name = "Jungle Greeble 26"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_26.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_26.dmm"
description = "Another scar from the war. A platform."
/datum/map_template/greeble/jungleplanet/twentyseven
name = "Jungle Greeble 27"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_27.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_27.dmm"
description = "Poor bastard exploded."
/datum/map_template/greeble/jungleplanet/twentyeight
name = "Jungle Greeble 28"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_28.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_28.dmm"
description = "A very small, unbroken trench segment."
/datum/map_template/greeble/jungleplanet/twentynine
name = "Jungle Greeble 29"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_29.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_29.dmm"
description = "A watchtower. Better times to be had."
/datum/map_template/greeble/jungleplanet/thirty
name = "Jungle Greeble 30"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_30.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_30.dmm"
description = "Water has come to the surface here, and the ground around it has eroded."
/* Cave Greebles */
@@ -324,52 +324,52 @@
/datum/map_template/greeble/jungleplanet/cave
name = "Jungle Cave Greeble 1"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_1.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_1.dmm"
description = "Nuclear waste dumping incident"
/datum/map_template/greeble/jungleplanet/cave/two
name = "Jungle Cave Greeble 2"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_2.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_2.dmm"
description = "Broken mineshaft."
/datum/map_template/greeble/jungleplanet/cave/three
name = "Jungle Cave Greeble 3"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_3.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_3.dmm"
description = "A den of wolves."
/datum/map_template/greeble/jungleplanet/cave/four
name = "Jungle Cave Greeble 4"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_4.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_4.dmm"
description = "Deep in the world lay shelters for people long-gone."
/datum/map_template/greeble/jungleplanet/cave/five
name = "Jungle Cave Greeble 5"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_5.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_5.dmm"
description = "Wolves have feasted upon those who sought shelter here."
/datum/map_template/greeble/jungleplanet/cave/six
name = "Jungle Cave Greeble 6"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_6.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_6.dmm"
description = "Radioactive trash incident"
/datum/map_template/greeble/jungleplanet/cave/seven
name = "Jungle Cave Greeble 7"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_7.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_7.dmm"
description = "Oil? Don't let the PGF hear."
/datum/map_template/greeble/jungleplanet/cave/eight
name = "Jungle Cave Greeble 8"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_8.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_8.dmm"
description = "A small damp grotto."
/datum/map_template/greeble/jungleplanet/cave/nine
name = "Jungle Cave Greeble 9"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_9.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_9.dmm"
description = "A blossoming corpse left to its own devices."
/datum/map_template/greeble/jungleplanet/cave/ten
name = "Jungle Cave Greeble 10"
- mappath = "_maps/templates/greebles/jungle/greeble_junglecave_10.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_junglecave_10.dmm"
description = "Diamonds in the rough."
/* Anomaly Greebles
@@ -437,61 +437,61 @@
/datum/map_template/greeble/jungleplanet/anomaly
name = "Jungle Anomaly Greeble 1"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_1.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_1.dmm"
description = "A pool of blood and its conductor."
clear_everything = TRUE
/datum/map_template/greeble/jungleplanet/anomaly/two
name = "Jungle Anomaly Greeble 2"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_2.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_2.dmm"
description = "Death comes for us all eventually."
/datum/map_template/greeble/jungleplanet/anomaly/three
name = "Jungle Anomaly Greeble 3"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_3.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_3.dmm"
description = "This doesn't seem right."
/datum/map_template/greeble/jungleplanet/anomaly/four
name = "Jungle Anomaly Greeble 4"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_4.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_4.dmm"
description = "Jungle carpets."
/datum/map_template/greeble/jungleplanet/anomaly/five
name = "Jungle Anomaly Greeble 5"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_5.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_5.dmm"
description = "A lone heartbeat chokes the land around it."
/datum/map_template/greeble/jungleplanet/anomaly/six
name = "Jungle Anomaly Greeble 6"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_6.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_6.dmm"
description = "Go ahead, dump your waste. The frontier has a way with it."
/datum/map_template/greeble/jungleplanet/anomaly/seven
name = "Jungle Anomaly Greeble 7"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_7.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_7.dmm"
description = "Something from outside! Something different!"
/datum/map_template/greeble/jungleplanet/anomaly/eight
name = "Jungle Anomaly Greeble 8"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_8.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_8.dmm"
description = "KILL YOUR TELEVISION"
/datum/map_template/greeble/jungleplanet/anomaly/nine
name = "Jungle Anomaly Greeble 9"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_9.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_9.dmm"
description = "Feed the fountain of life with your corpse."
/datum/map_template/greeble/jungleplanet/anomaly/ten
name = "Jungle Anomaly Greeble 10"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_10.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_10.dmm"
description = "Not every dead mercenary gets buried."
/datum/map_template/greeble/jungleplanet/anomaly/eleven
name = "Jungle Anomaly Greeble 11"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_11.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_11.dmm"
description = "Reach out and touch me."
/datum/map_template/greeble/jungleplanet/anomaly/twelve
name = "Jungle Anomaly Greeble 12"
- mappath = "_maps/templates/greebles/jungle/greeble_jungle_anomaly_12.dmm"
+ mappath = MAPROOT + "/templates/greebles/jungle/greeble_jungle_anomaly_12.dmm"
description = "Will you join my friends and I?"
diff --git a/code/datums/mapgen/greebles/sandplanet.dm b/code/datums/mapgen/greebles/sandplanet.dm
index 9a9690f0f80..36824698f61 100644
--- a/code/datums/mapgen/greebles/sandplanet.dm
+++ b/code/datums/mapgen/greebles/sandplanet.dm
@@ -79,81 +79,81 @@
/datum/map_template/greeble/whitesands/sulfurpool1
name = "Sulfur Greeble 1"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_1.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_1.dmm"
/datum/map_template/greeble/whitesands/sulfurpool2
name = "Sulfur Greeble 2"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_2.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_2.dmm"
/datum/map_template/greeble/whitesands/sulfurpool3
name = "Sulfur Greeble 3"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_3.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_3.dmm"
/datum/map_template/greeble/whitesands/sulfurpool4
name = "Sulfur Greeble 4"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_4.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_4.dmm"
/datum/map_template/greeble/whitesands/sulfurpool5
name = "Sulfur Greeble 5"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_5.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_5.dmm"
/datum/map_template/greeble/whitesands/sulfurpool6
name = "Sulfur Greeble 6"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_6.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_6.dmm"
/datum/map_template/greeble/whitesands/sulfurpool7
name = "Sulfur Greeble 7"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_7.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_7.dmm"
/datum/map_template/greeble/whitesands/sulfurpool8
name = "Sulfur Greeble 8"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_8.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_8.dmm"
/datum/map_template/greeble/whitesands/sulfurpool9
name = "Sulfur Greeble 9"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_9.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_9.dmm"
/datum/map_template/greeble/whitesands/sulfurpool10
name = "Sulfur Greeble 10"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_10.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_10.dmm"
/datum/map_template/greeble/whitesands/sulfurpool11
name = "Sulfur Greeble 11"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_11.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_11.dmm"
/datum/map_template/greeble/whitesands/sulfurpool12
name = "Sulfur Greeble 12"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_12.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_12.dmm"
/datum/map_template/greeble/whitesands/sulfurpool13
name = "Sulfur Greeble 13"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_13.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_13.dmm"
/datum/map_template/greeble/whitesands/sulfurpool14
name = "Sulfur Greeble 14"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_14.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_14.dmm"
/datum/map_template/greeble/whitesands/sulfurpool15
name = "Sulfur Greeble 15"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_15.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_15.dmm"
/datum/map_template/greeble/whitesands/sulfurpool16
name = "Sulfur Greeble 16"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_16.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_16.dmm"
/datum/map_template/greeble/whitesands/sulfurpool17
name = "Sulfur Greeble 17"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_17.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_17.dmm"
/datum/map_template/greeble/whitesands/sulfurpool18
name = "Sulfur Greeble 18"
- mappath = "_maps/templates/greebles/whitesands/sulfur_pool_18.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/sulfur_pool_18.dmm"
/obj/effect/greeble_spawner/whitesands/oasis
template = /datum/map_template/greeble/whitesands/oasis
/datum/map_template/greeble/whitesands/oasis
name = "Rare Oasis"
- mappath = "_maps/templates/greebles/whitesands/oasis_1.dmm"
+ mappath = MAPROOT + "/templates/greebles/whitesands/oasis_1.dmm"
clear_everything = TRUE
diff --git a/code/datums/mapgen/planetary/moon_generator.dm b/code/datums/mapgen/planetary/moon_generator.dm
index 85cba567e56..fc8d77c6578 100644
--- a/code/datums/mapgen/planetary/moon_generator.dm
+++ b/code/datums/mapgen/planetary/moon_generator.dm
@@ -138,24 +138,24 @@
/datum/map_template/greeble/moon/crater1
name = "Crater 1"
- mappath = "_maps/templates/greebles/moon_crater1.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater1.dmm"
/datum/map_template/greeble/moon/crater2
name = "Crater 2"
- mappath = "_maps/templates/greebles/moon_crater2.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater2.dmm"
/datum/map_template/greeble/moon/crater3
name = "Crater 3"
- mappath = "_maps/templates/greebles/moon_crater3.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater3.dmm"
/datum/map_template/greeble/moon/crater4
name = "Crater 4"
- mappath = "_maps/templates/greebles/moon_crater4.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater4.dmm"
/datum/map_template/greeble/moon/crater5
name = "Crater 5"
- mappath = "_maps/templates/greebles/moon_crater5.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater5.dmm"
/datum/map_template/greeble/moon/crater6
name = "Crater 6"
- mappath = "_maps/templates/greebles/moon_crater6.dmm"
+ mappath = MAPROOT + "/templates/greebles/moon_crater6.dmm"
diff --git a/code/datums/ruins/battlefield.dm b/code/datums/ruins/battlefield.dm
index f93f24657b9..5bdbf642c52 100644
--- a/code/datums/ruins/battlefield.dm
+++ b/code/datums/ruins/battlefield.dm
@@ -1,6 +1,6 @@
///dummy file in case anyone wants to make ruins/punchcards for this
/datum/map_template/ruin/battlefield
- prefix = "_maps/RandomRuins/BattleRuins/"
+ prefix = MAPROOT + "/RandomRuins/BattleRuins/"
ruin_type = RUINTYPE_BATTLEFIELD
allow_duplicates = FALSE
cost = 5
diff --git a/code/datums/ruins/beachplanet.dm b/code/datums/ruins/beachplanet.dm
index 1f1eb158468..6e2fd4f5447 100644
--- a/code/datums/ruins/beachplanet.dm
+++ b/code/datums/ruins/beachplanet.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/beachplanet
- prefix = "_maps/RandomRuins/BeachRuins/"
+ prefix = MAPROOT + "/RandomRuins/BeachRuins/"
ruin_type = RUINTYPE_BEACH
/datum/map_template/ruin/beachplanet/ancient
diff --git a/code/datums/ruins/desert.dm b/code/datums/ruins/desert.dm
index d3371dd538b..86a107deca5 100644
--- a/code/datums/ruins/desert.dm
+++ b/code/datums/ruins/desert.dm
@@ -1,6 +1,6 @@
///dummy file in case anyone wants to make ruins/punchcards for this
/datum/map_template/ruin/desert
- prefix = "_maps/RandomRuins/DesertRuins/"
+ prefix = MAPROOT + "/RandomRuins/DesertRuins/"
ruin_type = RUINTYPE_DESERT
allow_duplicates = FALSE
cost = 5
diff --git a/code/datums/ruins/icemoon.dm b/code/datums/ruins/icemoon.dm
index ac5aa322fd7..ba66cf6e5c5 100644
--- a/code/datums/ruins/icemoon.dm
+++ b/code/datums/ruins/icemoon.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/icemoon
- prefix = "_maps/RandomRuins/IceRuins/"
+ prefix = MAPROOT + "/RandomRuins/IceRuins/"
ruin_type = RUINTYPE_ICE
/datum/map_template/ruin/icemoon/ice_lodge
diff --git a/code/datums/ruins/jungle.dm b/code/datums/ruins/jungle.dm
index 8c437121002..e58123a64cd 100644
--- a/code/datums/ruins/jungle.dm
+++ b/code/datums/ruins/jungle.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/jungle
- prefix = "_maps/RandomRuins/JungleRuins/"
+ prefix = MAPROOT + "/RandomRuins/JungleRuins/"
ruin_type = RUINTYPE_JUNGLE
/datum/map_template/ruin/jungle/syndicate
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index 03ca3b9c742..7ed6e8dbc11 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/lavaland
- prefix = "_maps/RandomRuins/LavaRuins/"
+ prefix = MAPROOT + "/RandomRuins/LavaRuins/"
ruin_type = RUINTYPE_LAVA
/datum/map_template/ruin/lavaland/biodome/winter
diff --git a/code/datums/ruins/moon.dm b/code/datums/ruins/moon.dm
index d9fcf5866b6..95b538f13e5 100644
--- a/code/datums/ruins/moon.dm
+++ b/code/datums/ruins/moon.dm
@@ -1,5 +1,5 @@
/datum/map_template/ruin/moon
- prefix = "_maps/RandomRuins/MoonRuins/"
+ prefix = MAPROOT + "/RandomRuins/MoonRuins/"
ruin_type = RUINTYPE_MOON
diff --git a/code/datums/ruins/reebe.dm b/code/datums/ruins/reebe.dm
index d1a9de3e024..7fe0aa9ddfd 100644
--- a/code/datums/ruins/reebe.dm
+++ b/code/datums/ruins/reebe.dm
@@ -1,5 +1,5 @@
/datum/map_template/ruin/reebe
- prefix = "_maps/RandomRuins/ReebeRuins/"
+ prefix = MAPROOT + "/RandomRuins/ReebeRuins/"
allow_duplicates = FALSE
cost = 5
ruin_type = RUINTYPE_YELLOW
diff --git a/code/datums/ruins/rockplanet.dm b/code/datums/ruins/rockplanet.dm
index f95d0af429b..b14ae2575dd 100644
--- a/code/datums/ruins/rockplanet.dm
+++ b/code/datums/ruins/rockplanet.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/rockplanet
- prefix = "_maps/RandomRuins/RockRuins/"
+ prefix = MAPROOT + "/RandomRuins/RockRuins/"
ruin_type = RUINTYPE_ROCK
diff --git a/code/datums/ruins/shrouded.dm b/code/datums/ruins/shrouded.dm
index 164a0e930e5..0a19ff8edd8 100644
--- a/code/datums/ruins/shrouded.dm
+++ b/code/datums/ruins/shrouded.dm
@@ -1,6 +1,6 @@
///dummy file in case anyone wants to make ruins/punchcards for this
/datum/map_template/ruin/shrouded
- prefix = "_maps/RandomRuins/ShroudRuins/"
+ prefix = MAPROOT + "/RandomRuins/ShroudRuins/"
ruin_type = RUINTYPE_SHROUDED
allow_duplicates = FALSE
cost = 5
diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm
index 6a03372f279..4a3f3ec3093 100644
--- a/code/datums/ruins/space.dm
+++ b/code/datums/ruins/space.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/space
- prefix = "_maps/RandomRuins/SpaceRuins/"
+ prefix = MAPROOT + "/RandomRuins/SpaceRuins/"
cost = 1
allow_duplicates = FALSE
ruin_type = RUINTYPE_SPACE
diff --git a/code/datums/ruins/wasteplanet.dm b/code/datums/ruins/wasteplanet.dm
index 5f119ecbf68..910bf0ac397 100644
--- a/code/datums/ruins/wasteplanet.dm
+++ b/code/datums/ruins/wasteplanet.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/wasteplanet
- prefix = "_maps/RandomRuins/WasteRuins/"
+ prefix = MAPROOT + "/RandomRuins/WasteRuins/"
ruin_type = RUINTYPE_WASTE
/datum/map_template/ruin/wasteplanet/radiation
diff --git a/code/datums/ruins/water.dm b/code/datums/ruins/water.dm
index e276bdd2be0..6571bc65758 100644
--- a/code/datums/ruins/water.dm
+++ b/code/datums/ruins/water.dm
@@ -1,6 +1,6 @@
///dummy file in case anyone wants to make ruins/punchcards for this
/datum/map_template/ruin/water
- prefix = "_maps/RandomRuins/WaterRuins/"
+ prefix = MAPROOT + "/RandomRuins/WaterRuins/"
ruin_type = RUINTYPE_WATER
allow_duplicates = FALSE
cost = 5
diff --git a/code/datums/ruins/whitesands.dm b/code/datums/ruins/whitesands.dm
index fbd9200c368..e3b49ffc7cf 100644
--- a/code/datums/ruins/whitesands.dm
+++ b/code/datums/ruins/whitesands.dm
@@ -1,7 +1,7 @@
// Hey! Listen! Update _maps\map_catalogue.txt with your new ruins!
/datum/map_template/ruin/whitesands
- prefix = "_maps/RandomRuins/SandRuins/"
+ prefix = MAPROOT + "/RandomRuins/SandRuins/"
ruin_type = RUINTYPE_SAND
/datum/map_template/ruin/whitesands/pubbyslopcrash
diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm
index 97979c8c57c..13a59b4aa60 100644
--- a/code/datums/shuttles.dm
+++ b/code/datums/shuttles.dm
@@ -55,7 +55,7 @@
if(path)
mappath = path
else if(category && file_name)
- mappath = "_maps/shuttles/[category]/[file_name].dmm"
+ mappath = MAPROOT + "/shuttles/[category]/[file_name].dmm"
. = ..()
/datum/map_template/shuttle/preload_size(path, cache)
diff --git a/code/modules/mining/shelters.dm b/code/modules/mining/shelters.dm
index 6cd5a217571..1e9be9da05e 100644
--- a/code/modules/mining/shelters.dm
+++ b/code/modules/mining/shelters.dm
@@ -37,7 +37,7 @@
built-in navigation, entertainment, medical facilities and a \
sleeping area! Order now, and we'll throw in a TINY FAN, \
absolutely free!"
- mappath = "_maps/templates/shelter_1.dmm"
+ mappath = MAPROOT + "/templates/shelter_1.dmm"
/datum/map_template/shelter/beta
name = "Shelter Beta"
@@ -47,7 +47,7 @@
running water, a gourmet three course meal, cooking facilities, \
and a deluxe companion to keep you from getting lonely during \
an ash storm."
- mappath = "_maps/templates/shelter_2.dmm"
+ mappath = MAPROOT + "/templates/shelter_2.dmm"
/datum/map_template/shelter/charlie
name = "Shelter Charlie"
@@ -57,4 +57,4 @@
also has a sink. This isn't a survival capsule and so you can \
expect that this won't save you if you're bleeding out to \
death."
- mappath = "_maps/templates/shelter_3.dmm"
+ mappath = MAPROOT + "/templates/shelter_3.dmm"
diff --git a/code/modules/overmap/objects/outpost/outpost_types.dm b/code/modules/overmap/objects/outpost/outpost_types.dm
index 994e0a94f29..7fa028d1ef3 100644
--- a/code/modules/overmap/objects/outpost/outpost_types.dm
+++ b/code/modules/overmap/objects/outpost/outpost_types.dm
@@ -12,7 +12,7 @@
var/outpost_administrator = "Fallback Administration"
/datum/map_template/outpost/New()
- . = ..(path = "_maps/outpost/[name].dmm")
+ . = ..(path = MAPROOT + "/outpost/[name].dmm")
/datum/map_template/outpost/hangar
var/dock_width
diff --git a/code/modules/overmap/star_systems/safezone.dm b/code/modules/overmap/star_systems/safezone.dm
index 44e62fb626d..277d0433b9e 100644
--- a/code/modules/overmap/star_systems/safezone.dm
+++ b/code/modules/overmap/star_systems/safezone.dm
@@ -143,7 +143,7 @@
can_jump_to = FALSE
//the json file itself, you can change the directory of this if '_maps/sectors/*_starsystem.json' isn't a good enough naming scheme
- json = '_maps/sectors/teagarden_starsystem.json'
+ json = MAPROOT + "/sectors/teagarden_starsystem.json"
//to avoid loading shit on top of hte map, and to copy the system information from the file
generator_type = OVERMAP_GENERATOR_JSON
diff --git a/modular_starfly/modules/starfly_ships/README.md b/modular_starfly/modules/starfly_maps/README.md
similarity index 83%
rename from modular_starfly/modules/starfly_ships/README.md
rename to modular_starfly/modules/starfly_maps/README.md
index ed2dfae3857..895c7a157f5 100644
--- a/modular_starfly/modules/starfly_ships/README.md
+++ b/modular_starfly/modules/starfly_maps/README.md
@@ -1,11 +1,11 @@
-# Starfly-13 Ships
+# Starfly-13 Maps
-Module ID: `STARFLY_SHIPS`
+Module ID: `STARFLY_MAPS`
## Description
-Removes non-lore ships and ship configurations in a modular way. New ships will
-appear in thier own modules.
+Module layers STARFLY-13 maps over upstream maps, and removes non-lore maps
+from the game.
### Usage
@@ -17,16 +17,16 @@ module providing a new file will take precedence over upstream. Modules that
provide identical filenames will conflict with each other. Those conflicts
must be resolved between the modules if you expect the ships to work properly.
-Finally, anything defined in this module `STARFLY_SHIPS` will be deleted from
+Finally, anything defined in this module `STARFLY_MAPS` will be deleted from
the `_maps` directory. By providing a file like `_maps/configs/srm_elder.json`
in this module, we instruct the build process to remove that file from the
codebase at build time. It will not be present in the game.
#### tl;dr
-- Add your new ships under `_maps` in your own new modular_starfly modules.
+- Add your new maps under `_maps` in your own new modular_starfly modules.
-- Add empty files in this module `STARFLY_SHIPS` if you want to DELETE a ship
+- Add empty files in this module `STARFLY_MAPS` if you want to DELETE a ship
from the Starfly version of the game.
## TG Proc/File Changes
diff --git a/modular_starfly/modules/starfly_ships/_maps/configs/srm_elder.json b/modular_starfly/modules/starfly_maps/_maps/configs/srm_elder.json
similarity index 100%
rename from modular_starfly/modules/starfly_ships/_maps/configs/srm_elder.json
rename to modular_starfly/modules/starfly_maps/_maps/configs/srm_elder.json
diff --git a/modular_starfly/modules/starfly_ships/_maps/configs/srm_sojourner.json b/modular_starfly/modules/starfly_maps/_maps/configs/srm_sojourner.json
similarity index 100%
rename from modular_starfly/modules/starfly_ships/_maps/configs/srm_sojourner.json
rename to modular_starfly/modules/starfly_maps/_maps/configs/srm_sojourner.json
diff --git a/modular_starfly/modules/starfly_ships/maps.sh b/modular_starfly/modules/starfly_ships/maps.sh
deleted file mode 100755
index c1226a0b291..00000000000
--- a/modular_starfly/modules/starfly_ships/maps.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env bash
-# maps.sh
-
-set -euo pipefail
-
-# if this module isn't enabled, just bail out here (no error)
-if ! grep -q '^#define STARFLY13_MODULE_STARFLY_SHIPS_ENABLED$' code/__DEFINES/__starfly.dm; then
- exit 0
-fi
-
-# define some useful stuff
-dest_maps="_maps"
-modules_root="modular_starfly/modules"
-starfly_maps="$modules_root/starfly_ships/_maps"
-
-# if _maps doesn't exist, just bail out here (error)
-[[ -d "$dest_maps" ]] || { echo "ERROR: $dest_maps not found" >&2; exit 1; }
-
-# otherwise, its time to build up the custom maps staging directory
-export STARFLY_MAPS_DIR=".staging/_maps"
-
-# ensure staging directory exists and is clean
-rm -rf "$STARFLY_MAPS_DIR"
-mkdir -p "$STARFLY_MAPS_DIR"
-
-# copy the original maps into our staging directory
-echo "Copying: $dest_maps -> $STARFLY_MAPS_DIR/"
-cp -a "$dest_maps"/. "$STARFLY_MAPS_DIR"/
-
-# copy the custom maps provided by every other module
-shopt -s nullglob
-for module_dir in "$modules_root"/*/; do
- [[ "${module_dir%/}" == "$modules_root/starfly_ships" ]] && continue
- maps_dir="${module_dir%/}/_maps"
-
- [[ -d "$maps_dir" ]] || continue
-
- echo "Merging: $maps_dir -> $STARFLY_MAPS_DIR/"
- cp -a "$maps_dir"/. "$STARFLY_MAPS_DIR"/
-done
-
-# remove all the maps specified by this module
-if [[ -d "$starfly_maps" ]]; then
- (
- cd "$starfly_maps"
- find . -type f -mindepth 1 -depth -print0
- ) | while IFS= read -r -d '' relpath; do
- echo "Removing: $STARFLY_MAPS_DIR/$relpath"
- rm -rf "$STARFLY_MAPS_DIR/$relpath"
- done
-fi
diff --git a/shiptest.dme b/shiptest.dme
index 67197ed12a9..8e3c61bd905 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -19,6 +19,7 @@
#include "code\_compile_options.dm"
#include "code\world.dm"
#include "code\__DEFINES\__starfly.dm"
+#include "code\__DEFINES\__starflymaps.dm"
#include "code\__DEFINES\_click.dm"
#include "code\__DEFINES\_globals.dm"
#include "code\__DEFINES\_helpers.dm"
diff --git a/tgui/packages/tgui/starfly.ts b/tgui/packages/tgui/starfly.ts
index c683e0deaf0..52fc91b55ab 100644
--- a/tgui/packages/tgui/starfly.ts
+++ b/tgui/packages/tgui/starfly.ts
@@ -32,6 +32,6 @@ export const STARFLY13 = {
MODULE_ROSEUS_GALACTIC_ENABLED: true,
MODULE_SINTA_UNATHI_ENABLED: true,
MODULE_STARFLY_BRANDING_ENABLED: true,
- MODULE_STARFLY_SHIPS_ENABLED: true,
+ MODULE_STARFLY_MAPS_ENABLED: true,
MODULE_YEOSA_UNATHI_ENABLED: true,
};
diff --git a/tools/build/build b/tools/build/build
index f6af20fdcc2..0e202e1bba7 100755
--- a/tools/build/build
+++ b/tools/build/build
@@ -1,5 +1,4 @@
#!/bin/sh
set -e
-modular_starfly/modules/starfly_ships/maps.sh
cd "$(dirname "$0")"
exec ../bootstrap/node build.js "$@"
diff --git a/tools/build/build.js b/tools/build/build.js
index 6624c566bdf..02c11ac6431 100755
--- a/tools/build/build.js
+++ b/tools/build/build.js
@@ -12,6 +12,7 @@ import { env } from "process";
import Juke from "./juke/index.js";
import { DreamDaemon, DreamMaker, NamedVersionFile } from "./lib/byond.js";
import { yarn } from "./lib/yarn.js";
+import buildLayeredMaps from "./lib/maps.js";
Juke.chdir("../..", import.meta.url);
Juke.setup({ file: import.meta.url }).then((code) => {
@@ -26,24 +27,6 @@ Juke.setup({ file: import.meta.url }).then((code) => {
const DME_NAME = "shiptest";
-//---------------------------------------------------------------------------------------------------------------------
-// STARFLY EDIT - ADDITION BEGIN
-// #ifdef STARFLY13_MODULE_STARFLY_SHIPS_ENABLED
-//---------------------------------------------------------------------------------------------------------------------
-const DEFAULT_MAPS_DIR = "_maps";
-const STAGED_MAPS_DIR = process.env.STARFLY_MAPS_DIR; // e.g. ".staging/_maps"
-
-// Use staged maps dir if provided AND it exists; otherwise normal _maps
-const MAPS_DIR =
- STAGED_MAPS_DIR && fs.existsSync(STAGED_MAPS_DIR) ? STAGED_MAPS_DIR : DEFAULT_MAPS_DIR;
-
-const mapsPath = (p) => `${MAPS_DIR}/${p}`;
-const stripMapsPrefix = (p) => p.replace(`${MAPS_DIR}/`, "");
-//---------------------------------------------------------------------------------------------------------------------
-// #endif // #ifdef STARFLY13_MODULE_ADMIN_VERB_FREEZE_ENABLED
-// STARFLY EDIT - ADDITION END
-//---------------------------------------------------------------------------------------------------------------------
-
export const DefineParameter = new Juke.Parameter({
type: "string[]",
alias: "D",
@@ -80,7 +63,7 @@ export const DmMapsIncludeTarget = new Juke.Target({
];
const content =
folders
- .map(stripMapsPrefix)
+ .map((file) => file.replace("_maps/", ""))
.map((file) => `#include "${file}"`)
.join("\n") + "\n";
fs.writeFileSync("_maps/templates.dm", content);
@@ -98,11 +81,12 @@ export const DmTarget = new Juke.Target({
get(DefineParameter).includes("ALL_MAPS") && DmMapsIncludeTarget,
],
inputs: [
- mapsPath("map_files/**"),
+ "_maps/map_files/**",
"code/**",
"html/**",
"icons/**",
"interface/**",
+ "modular_starfly/modules/**",
`${DME_NAME}.dme`,
NamedVersionFile,
],
@@ -113,6 +97,12 @@ export const DmTarget = new Juke.Target({
return [`${DME_NAME}.dmb`, `${DME_NAME}.rsc`];
},
executes: async ({ get }) => {
+ const layered = await buildLayeredMaps({
+ projectRoot: process.cwd(),
+ outputMapsDir: "_maps2eb",
+ logger: Juke.logger,
+ });
+
await DreamMaker(`${DME_NAME}.dme`, {
defines: ["CBT", ...get(DefineParameter)],
warningsAsErrors: get(WarningParameter).includes("error"),
diff --git a/tools/build/lib/maps.js b/tools/build/lib/maps.js
new file mode 100644
index 00000000000..132df8d1b2a
--- /dev/null
+++ b/tools/build/lib/maps.js
@@ -0,0 +1,197 @@
+// maps.js
+// Copyright 2026 Patrick Meade.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+import fs from "fs";
+import path from "path";
+
+const fsp = fs.promises;
+
+async function pathExists(p) {
+ try {
+ await fsp.access(p);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+async function isDirectory(p) {
+ try {
+ const st = await fsp.stat(p);
+ return st.isDirectory();
+ } catch {
+ return false;
+ }
+}
+
+async function mkdirp(dir) {
+ await fsp.mkdir(dir, { recursive: true });
+}
+
+async function removeTree(target) {
+ await fsp.rm(target, { recursive: true, force: true });
+}
+
+async function copyTree(src, dest) {
+ if (!(await isDirectory(src))) {
+ return;
+ }
+
+ await mkdirp(dest);
+
+ const entries = await fsp.readdir(src, { withFileTypes: true });
+ for (const entry of entries) {
+ const srcPath = path.join(src, entry.name);
+ const destPath = path.join(dest, entry.name);
+
+ if (entry.isDirectory()) {
+ await copyTree(srcPath, destPath);
+ } else if (entry.isFile()) {
+ await mkdirp(path.dirname(destPath));
+ await fsp.copyFile(srcPath, destPath);
+ } else if (entry.isSymbolicLink()) {
+ // Optional: preserve symlinks if they ever appear.
+ const linkTarget = await fsp.readlink(srcPath);
+ await mkdirp(path.dirname(destPath));
+ try {
+ await fsp.symlink(linkTarget, destPath);
+ } catch {
+ // On Windows this may fail depending on permissions.
+ // Fall back to copying the linked file if possible.
+ const realSrc = await fsp.realpath(srcPath);
+ const st = await fsp.stat(realSrc);
+ if (st.isDirectory()) {
+ await copyTree(realSrc, destPath);
+ } else {
+ await fsp.copyFile(realSrc, destPath);
+ }
+ }
+ }
+ }
+}
+
+async function* walkFiles(root, base = root) {
+ if (!(await isDirectory(root))) {
+ return;
+ }
+
+ const entries = await fsp.readdir(root, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = path.join(root, entry.name);
+ if (entry.isDirectory()) {
+ yield* walkFiles(fullPath, base);
+ } else if (entry.isFile() || entry.isSymbolicLink()) {
+ yield path.relative(base, fullPath);
+ }
+ }
+}
+
+async function removeMatchesFromTree(removalsRoot, destRoot, logger = console) {
+ if (!(await isDirectory(removalsRoot))) {
+ return;
+ }
+
+ for await (const relPath of walkFiles(removalsRoot)) {
+ const targetPath = path.join(destRoot, relPath);
+ logger.info?.(`Removing: ${targetPath}`) ?? logger.log(`Removing: ${targetPath}`);
+ await fsp.rm(targetPath, { recursive: true, force: true });
+ }
+}
+
+async function readText(file) {
+ return await fsp.readFile(file, "utf8");
+}
+
+export async function buildLayeredMaps({
+ projectRoot = process.cwd(),
+ definesFile = path.join("code", "__DEFINES", "__starfly.dm"),
+ moduleEnableDefine = "STARFLY13_MODULE_STARFLY_MAPS_ENABLED",
+ upstreamMapsDir = "_maps",
+ modulesRoot = path.join("modular_starfly", "modules"),
+ removalModuleName = "starfly_maps",
+ outputMapsDir = "_maps2eb",
+ logger = console,
+} = {}) {
+ const definesPath = path.resolve(projectRoot, definesFile);
+ const upstreamMapsPath = path.resolve(projectRoot, upstreamMapsDir);
+ const modulesRootPath = path.resolve(projectRoot, modulesRoot);
+ const removalMapsPath = path.join(modulesRootPath, removalModuleName, "_maps");
+ const outputMapsPath = path.resolve(projectRoot, outputMapsDir);
+
+ // If the module isn't enabled, bail out with success.
+ if (!(await pathExists(definesPath))) {
+ throw new Error(`Defines file not found: ${definesPath}`);
+ }
+
+ const definesText = await readText(definesPath);
+ const defineLine = `#define ${moduleEnableDefine}`;
+ if (!definesText.split(/\r?\n/).includes(defineLine)) {
+ logger.info?.(
+ `Skipping layered maps build; ${moduleEnableDefine} is not enabled.`
+ ) ?? logger.log(`Skipping layered maps build; ${moduleEnableDefine} is not enabled.`);
+ return {
+ enabled: false,
+ outputMapsDir: outputMapsPath,
+ };
+ }
+
+ if (!(await isDirectory(upstreamMapsPath))) {
+ throw new Error(`Upstream maps directory not found: ${upstreamMapsPath}`);
+ }
+
+ logger.info?.(`Preparing layered maps in: ${outputMapsPath}`) ??
+ logger.log(`Preparing layered maps in: ${outputMapsPath}`);
+
+ await removeTree(outputMapsPath);
+ await mkdirp(outputMapsPath);
+
+ logger.info?.(`Copying: ${upstreamMapsPath} -> ${outputMapsPath}`) ??
+ logger.log(`Copying: ${upstreamMapsPath} -> ${outputMapsPath}`);
+ await copyTree(upstreamMapsPath, outputMapsPath);
+
+ if (await isDirectory(modulesRootPath)) {
+ const moduleEntries = await fsp.readdir(modulesRootPath, { withFileTypes: true });
+
+ for (const entry of moduleEntries) {
+ if (!entry.isDirectory()) {
+ continue;
+ }
+
+ if (entry.name === removalModuleName) {
+ continue;
+ }
+
+ const moduleMapsPath = path.join(modulesRootPath, entry.name, "_maps");
+ if (!(await isDirectory(moduleMapsPath))) {
+ continue;
+ }
+
+ logger.info?.(`Merging: ${moduleMapsPath} -> ${outputMapsPath}`) ??
+ logger.log(`Merging: ${moduleMapsPath} -> ${outputMapsPath}`);
+ await copyTree(moduleMapsPath, outputMapsPath);
+ }
+ }
+
+ await removeMatchesFromTree(removalMapsPath, outputMapsPath, logger);
+
+ return {
+ enabled: true,
+ outputMapsDir: outputMapsPath,
+ maprootDefine: outputMapsDir,
+ };
+}
+
+export default buildLayeredMaps;
diff --git a/tools/deploy.sh b/tools/deploy.sh
index b7bfa4b64c5..62721c16fdd 100755
--- a/tools/deploy.sh
+++ b/tools/deploy.sh
@@ -4,13 +4,15 @@
#First arg is path to where you want to deploy
#creates a work tree free of everything except what's necessary to run the game
+MAPROOT="${MAPROOT:=_maps2eb}"
+
#second arg is working directory if necessary
if [[ $# -eq 2 ]] ; then
cd $2
fi
mkdir -p \
- $1/_maps \
+ $1/${MAPROOT} \
$1/icons/runtime \
$1/sound/runtime \
$1/strings \
@@ -23,7 +25,7 @@ if [ -d ".git" ]; then
fi
cp shiptest.dmb shiptest.rsc $1/
-cp -r _maps/* $1/_maps/
+cp -r ${MAPROOT}/* $1/${MAPROOT}/
cp -r icons/runtime/* $1/icons/runtime/
cp -r sound/runtime/* $1/sound/runtime/
cp -r strings/* $1/strings/
diff --git a/tools/maplint/source/__main__.py b/tools/maplint/source/__main__.py
index dcb575e99bd..e5b171d3920 100644
--- a/tools/maplint/source/__main__.py
+++ b/tools/maplint/source/__main__.py
@@ -1,5 +1,6 @@
import argparse
import glob
+import os
import pathlib
import traceback
import yaml
@@ -13,6 +14,14 @@ def green(text):
def red(text):
return "\033[31m" + str(text) + "\033[0m"
+def get_map_folder():
+ try:
+ map_folder = os.environ['MAPROOT']
+ except KeyError:
+ map_folder = '_maps'
+
+ return map_folder
+
def process_dmm(map_filename, lints: dict[str, lint.Lint]) -> list[MaplintError]:
problems: list[MaplintError] = []
@@ -75,7 +84,8 @@ def main(args):
traceback.print_exc()
any_failed = True
- for map_filename in (args.maps or glob.glob("_maps/**/*.dmm", recursive = True)):
+ map_folder = get_map_folder()
+ for map_filename in (args.maps or glob.glob(f"{map_folder}/**/*.dmm", recursive = True)):
print(map_filename, end = " ")
success = True
diff --git a/tools/starfly/ci/check_grep.sh b/tools/starfly/ci/check_grep.sh
new file mode 100755
index 00000000000..08c66c8178b
--- /dev/null
+++ b/tools/starfly/ci/check_grep.sh
@@ -0,0 +1,305 @@
+#!/bin/bash
+set -euo pipefail
+
+#nb: must be bash to support shopt globstar
+shopt -s globstar extglob
+
+MAPROOT="${MAPROOT:=_maps2eb}"
+
+#ANSI Escape Codes for colors to increase contrast of errors
+RED="\033[0;31m"
+GREEN="\033[0;32m"
+BLUE="\033[0;34m"
+NC="\033[0m" # No Color
+
+st=0
+
+# check for ripgrep
+if command -v rg >/dev/null 2>&1; then
+ grep=rg
+ pcre2_support=1
+ if [ ! rg -P '' >/dev/null 2>&1 ] ; then
+ pcre2_support=0
+ fi
+ code_files="code/**/**.dm"
+ map_files="${MAPROOT}/**/**.dmm"
+ code_x_515="code/**/!(__byond_version_compat).dm"
+else
+ pcre2_support=0
+ grep=grep
+ code_files="-r --include=code/**/**.dm"
+ map_files="-r --include=${MAPROOT}/**/**.dmm"
+ code_x_515="-r --include=code/**/!(__byond_version_compat).dm"
+fi
+
+echo -e "${BLUE}Using grep provider at $(which $grep)${NC}"
+
+part=0
+section() {
+ echo -e "${BLUE}Checking for $1${NC}..."
+ part=0
+}
+
+part() {
+ part=$((part+1))
+ padded=$(printf "%02d" $part)
+ echo -e "${GREEN} $padded- $1${NC}"
+}
+
+section "map issues"
+
+part "TGM"
+if $grep -U '^".+" = \(.+\)' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Non-TGM formatted map detected. Please convert it using Map Merger!${NC}"
+ st=1
+fi;
+part "comments"
+if $grep '//' $map_files | $grep -v '//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE' | $grep -v 'name|desc'; then
+ echo
+ echo -e "${RED}ERROR: Unexpected commented out line detected in this map file. Please remove it.${NC}"
+ st=1
+fi;
+part "iconstate tags"
+if $grep '^\ttag = "icon' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Tag vars from icon state generation detected in maps, please remove them.${NC}"
+ st=1
+fi;
+part "invalid map procs"
+if $grep '(new|newlist|icon|matrix|sound)\(.+\)' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Using unsupported procs in variables in a map file! Please remove all instances of this.${NC}"
+ st=1
+fi;
+part "common spelling mistakes"
+if $grep -i 'nanotransen' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in maps, please remove the extra N(s).${NC}"
+ st=1
+fi;
+if $grep 'NanoTrasen' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in maps, please uncapitalize the T(s).${NC}"
+ st=1
+fi;
+if $grep -i'centcomm' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of CentCom detected in maps, please remove the extra M(s).${NC}"
+ st=1
+fi;
+if $grep 'Solgov' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of SolGov detected in maps, please capitalize the G(s).${NC}"
+ st=1
+fi;
+if $grep 'Solcon' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of SolCon detected in maps, please capitalize the C(s).${NC}"
+ st=1
+fi;
+if $grep -- '-Class' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Misspellings(s) of -class detected in maps, please uncapitalize the C(s).${NC}"
+ st=1
+fi;
+
+section "whitespace issues"
+part "space indentation"
+if $grep '(^ {2})|(^ [^ * ])|(^ +)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Space indentation detected, please use tab indentation.${NC}"
+ st=1
+fi;
+part "mixed indentation"
+if $grep '^\t+ [^ *]' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Mixed indentation detected, please stick to tab indentation.${NC}"
+ st=1
+fi;
+
+#section "unit tests"
+#unit_test_files="code/modules/unit_tests/**/**.dm"
+#part "mob/living/carbon/human usage"
+#if $grep 'allocate\(/mob/living/carbon/human[,\)]' $unit_test_files ||
+# $grep 'new /mob/living/carbon/human\s?\(' $unit_test_files ||
+# $grep 'var/mob/living/carbon/human/\w+\s?=\s?new' $unit_test_files ; then
+# echo
+# echo -e "${RED}ERROR: Usage of mob/living/carbon/human detected in a unit test, please use mob/living/carbon/human/consistent.${NC}"
+# st=1
+#fi;
+
+section "common mistakes"
+part "global vars"
+if $grep '^/*var/' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Unmanaged global var use detected in code, please use the helpers.${NC}"
+ st=1
+fi;
+
+part "proc args with var/"
+if $grep '^/[\w/]\S+\(.*(var/|, ?var/.*).*\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Changed files contains a proc argument starting with 'var'.${NC}"
+ st=1
+fi;
+
+part "src as a trait source" # ideally we'd lint / test for ANY datum reference as a trait source, but 'src' is the most common.
+if $grep -i '(add_trait|remove_trait)\(.+,\s*.+,\s*src\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Using 'src' as a trait source. Source must be a string key - dont't use references to datums as a source, perhaps use 'REF(src)'.${NC}"
+ st=1
+fi;
+if $grep -i '(add_traits|remove_traits)\(.+,\s*src\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Using 'src' as trait sources. Source must be a string key - dont't use references to datums as sources, perhaps use 'REF(src)'.${NC}"
+ st=1
+fi;
+
+part "balloon_alert sanity"
+if $grep 'balloon_alert\(".*"\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Found a balloon alert with improper arguments.${NC}"
+ st=1
+fi;
+
+if $grep 'balloon_alert(.*span_)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Balloon alerts should never contain spans.${NC}"
+ st=1
+fi;
+
+part "balloon_alert idiomatic usage"
+if $grep 'balloon_alert\(.*?, ?"[A-Z]' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Balloon alerts should not start with capital letters. This includes text like 'AI'. If this is a false positive, wrap the text in UNLINT().${NC}"
+ st=1
+fi;
+
+part "update_icon_updates_onmob element usage"
+if $grep 'AddElement\(/datum/element/update_icon_updates_onmob.+ITEM_SLOT_HANDS' $code_files; then
+ echo
+ echo -e "${RED}ERROR: update_icon_updates_onmob element automatically updates ITEM_SLOT_HANDS, this is redundant and should be removed.${NC}"
+ st=1
+fi;
+
+part "common spelling mistakes"
+if $grep -i 'centcomm' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of CentCom detected in code, please remove the extra M(s).${NC}"
+ st=1
+fi;
+if $grep -ni 'nanotransen' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in code, please remove the extra N(s).${NC}"
+ st=1
+fi;
+if $grep 'NanoTrasen' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of Nanotrasen detected in code, please uncapitalize the T(s).${NC}"
+ st=1
+fi;
+if $grep 'Solgov' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of SolGov detected in code, please capitalize the G(s).${NC}"
+ st=1
+fi;
+if $grep 'Solcon' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of SolCon detected in code, please capitalize the C(s).${NC}"
+ st=1
+fi;
+if $grep 'indestructable' $code_files; then
+ echo
+ echo -e "${RED}ERROR: Misspelling(s) of indestructible detected in code, it is indestructIBLE not indestructABLE.${NC}"
+ st=1
+fi;
+
+part "map json sanity"
+if command -v jq >/dev/null 2>&1; then
+ for json in ${MAPROOT}/configs/*.json
+ do
+ map_path=$(jq -r '.map_path' $json)
+ if [ ! -f $map_path ]
+ then
+ echo
+ echo -e "${RED}ERROR: Found an invalid file reference to $map_path in $json ${NC}"
+ st=1
+ fi
+ done
+else
+ echo -e "${RED}jq not found, skipping map json checks${NC}"
+fi
+
+part "updatepaths validity"
+missing_txt_lines=$(find tools/UpdatePaths/Scripts -type f ! -name "*.txt" | wc -l)
+if [ $missing_txt_lines -gt 0 ]; then
+ echo
+ echo -e "${RED}ERROR: Found an UpdatePaths File that doesn't end in .txt! Please add the proper file extension!${NC}"
+ st=1
+fi;
+
+number_prefix_lines=$(find tools/UpdatePaths/Scripts -type f | wc -l)
+valid_number_prefix_lines=$(find tools/UpdatePaths/Scripts -type f | $grep -P "\d+_(.+)" | wc -l)
+if [ $valid_number_prefix_lines -ne $number_prefix_lines ]; then
+ echo
+ echo -e "${RED}ERROR: Detected an UpdatePaths File that doesn't start with the PR number! Please add the proper number prefix!${NC}"
+ st=1
+fi;
+
+section "515 Proc Syntax"
+part "proc ref syntax"
+if $grep '\.proc/' $code_x_515 ; then
+ echo
+ echo -e "${RED}ERROR: Outdated proc reference use detected in code, please use proc reference helpers.${NC}"
+ st=1
+fi;
+
+if [ "$pcre2_support" -eq 1 ]; then
+ section "regexes requiring PCRE2"
+ part "empty variable values"
+ if $grep -PU '{\n\t},' $map_files; then
+ echo
+ echo -e "${RED}ERROR: Empty variable value list detected in map file. Please remove the curly brackets entirely.${NC}"
+ st=1
+ fi;
+ part "to_chat sanity"
+ if $grep -P 'to_chat\((?!.*,).*\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: to_chat() missing arguments.${NC}"
+ st=1
+ fi;
+ part "timer flag sanity"
+ if $grep -P 'addtimer\((?=.*TIMER_OVERRIDE)(?!.*TIMER_UNIQUE).*\)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: TIMER_OVERRIDE used without TIMER_UNIQUE.${NC}"
+ st=1
+ fi
+ part "trailing newlines"
+ if $grep -PU '[^\n]$(?!\n)' $code_files; then
+ echo
+ echo -e "${RED}ERROR: File(s) with no trailing newline detected, please add one.${NC}"
+ st=1
+ fi
+ #part "improper atom initialize args"
+ #if $grep -P '^/(obj|mob|turf|area|atom)/.+/Initialize\((?!mapload).*\)' $code_files; then
+ # echo
+ # echo -e "${RED}ERROR: Initialize override without 'mapload' argument.${NC}"
+ # st=1
+ #fi;
+else
+ echo -e "${RED}pcre2 not supported, skipping checks requiring pcre2"
+ echo -e "if you want to run these checks install ripgrep with pcre2 support.${NC}"
+fi
+
+if [ $st = 0 ]; then
+ echo
+ echo -e "${GREEN}No errors found using $grep!${NC}"
+fi;
+
+if [ $st = 1 ]; then
+ echo
+ echo -e "${RED}Errors found, please fix them and try again.${NC}"
+fi;
+
+exit $st
diff --git a/tools/starfly/ci/check_maps_dir.sh b/tools/starfly/ci/check_maps_dir.sh
new file mode 100755
index 00000000000..667468e8c2f
--- /dev/null
+++ b/tools/starfly/ci/check_maps_dir.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+# check_maps_dir.sh
+
+set -euo pipefail
+
+count=$(fgrep -i -r "\"_maps" code | wc -l)
+if [[ "$count" -ne 3 ]]; then
+ echo "Error: Found $count instances of '\"_maps' expected exactly 3."
+ fgrep -i -r "\"_maps" code
+ exit 1
+fi
+
+count=$(fgrep -i -r " _maps" code | wc -l)
+if [[ "$count" -ne 11 ]]; then
+ echo "Error: Found $count instances of ' _maps' expected exactly 11."
+ fgrep -i -r " _maps" code
+ exit 1
+fi
+
+echo "Success: Hardcoded references to _maps are as expected."
diff --git a/tools/starfly/ci/check_misc.sh b/tools/starfly/ci/check_misc.sh
new file mode 100644
index 00000000000..54c44dd5d49
--- /dev/null
+++ b/tools/starfly/ci/check_misc.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -euo pipefail
+
+MAPROOT="${MAPROOT:=_maps2eb}"
+
+find . -name "*.php" -print0 | xargs -0 -n1 php -l
+find . -name "*.json" -not -path "*/node_modules/*" -print0 | xargs -0 python3 ./tools/json_verifier.py
+find ./${MAPROOT}/configs -name "*.json" -not -path "/data/*" -print0 | xargs -0 python3 ./tools/json_schema_validator.py ./${MAPROOT}/ship_config_schema.json
diff --git a/tools/starfly/ci/lint.sh b/tools/starfly/ci/lint.sh
index 273da0895cb..3253a5edfe8 100755
--- a/tools/starfly/ci/lint.sh
+++ b/tools/starfly/ci/lint.sh
@@ -11,6 +11,9 @@ echo "# Checking shiptest.dme"
echo "## Checking FILE_DIR directives in shiptest.dme"
tools/starfly/ci/check_file_dir.sh
+echo "## Checking codebase for hardcoded references to _maps"
+tools/starfly/ci/check_maps_dir.sh
+
echo "## Checking include order in shiptest.dme"
python3 tools/starfly/python/check_dme_order.py shiptest.dme