diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 2d9264adf5..5d18337a9f 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -459,3 +459,6 @@ /datum/config_entry/number/max_bunker_days config_entry_value = 7 min_val = 1 + +/datum/config_entry/flag/minimaps_enabled + config_entry_value = TRUE diff --git a/code/controllers/subsystem/minimaps.dm b/code/controllers/subsystem/minimaps.dm new file mode 100644 index 0000000000..75de71ca96 --- /dev/null +++ b/code/controllers/subsystem/minimaps.dm @@ -0,0 +1,20 @@ +SUBSYSTEM_DEF(minimaps) + name = "Minimaps" + flags = SS_NO_FIRE + var/list/station_minimaps + var/datum/minimap_group/station_minimap + +/datum/controller/subsystem/minimaps/Initialize() + if(!CONFIG_GET(flag/minimaps_enabled)) + to_chat(world, "Minimaps disabled! Skipping init.") + return ..() + build_minimaps() + return ..() + +/datum/controller/subsystem/minimaps/proc/build_minimaps() + station_minimaps = list() + for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) + var/datum/space_level/SL = SSmapping.get_level(z) + var/name = (SL.name == initial(SL.name))? "[z] - Station" : "[z] - [SL.name]" + station_minimaps += new /datum/minimap(z, name = name) + station_minimap = new(station_minimaps, "Station") diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 00dc0d98d6..ac56d6f17c 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -63,6 +63,9 @@ var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with + /// Color on minimaps, if it's null (which is default) it makes one at random. + var/minimap_color + /** * These two vars allow for multiple unique areas to be linked to a master area * and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing @@ -96,7 +99,14 @@ GLOBAL_LIST_EMPTY(teleportlocs) // === /area/New() - // This interacts with the map loader, so it needs to be set immediately + if(!minimap_color) // goes in New() because otherwise it doesn't fucking work + // generate one using the icon_state + if(icon_state && icon_state != "unknown") + var/icon/I = new(icon, icon_state, dir) + I.Scale(1,1) + minimap_color = I.GetPixel(1,1) + else // no icon state? use random. + minimap_color = rgb(rand(50,70),rand(50,70),rand(50,70)) // This interacts with the map loader, so it needs to be set immediately // rather than waiting for atoms to initialize. if (unique) GLOB.areas_by_type[type] = src diff --git a/code/modules/client/verbs/minimap.dm b/code/modules/client/verbs/minimap.dm new file mode 100644 index 0000000000..3d213dc210 --- /dev/null +++ b/code/modules/client/verbs/minimap.dm @@ -0,0 +1,10 @@ +/client/verb/show_station_minimap() + set category = "OOC" + set name = "Show Station Minimap" + set desc = "Shows a minimap of the currently loaded station map." + + if(!CONFIG_GET(flag/minimaps_enabled)) + to_chat(usr, "Minimap generation is not enabled in the server's configuration.") + return + + SSminimaps.station_minimap.show(src) diff --git a/code/modules/mapping/minimaps.dm b/code/modules/mapping/minimaps.dm new file mode 100644 index 0000000000..c347a8d7a6 --- /dev/null +++ b/code/modules/mapping/minimaps.dm @@ -0,0 +1,155 @@ +/datum/minimap + var/name + var/icon/map_icon + var/icon/meta_icon + var/icon/overlay_icon + var/list/color_area_names = list() + var/minx + var/maxx + var/miny + var/maxy + var/z_level + var/id = 0 + var/static/next_id = 0 + +/datum/minimap/New(z, x1 = 1, y1 = 1, x2 = world.maxx, y2 = world.maxy, name) + src.name = name + id = ++next_id + z_level = z + + var/crop_x1 = x2 + var/crop_x2 = x1 + var/crop_y1 = y2 + var/crop_y2 = y1 + + // do the generating + map_icon = new('html/blank.png') + meta_icon = new('html/blank.png') + map_icon.Scale(x2-x1+1, y2-y1+1) // arrays start at 1 + meta_icon.Scale(x2-x1+1, y2-y1+1) + var/list/area_to_color = list() + for(var/turf/T in block(locate(x1,y1,z),locate(x2,y2,z))) + var/area/A = T.loc + var/img_x = T.x - x1 + 1 // arrays start at 1 + var/img_y = T.y - y1 + 1 + if(!istype(A, /area/space) || istype(T, /turf/closed/wall)) + crop_x1 = min(crop_x1, T.x) + crop_x2 = max(crop_x2, T.x) + crop_y1 = min(crop_y1, T.y) + crop_y2 = max(crop_y2, T.y) + var/meta_color = area_to_color[A] + if(!meta_color) + meta_color = rgb(rand(0,255),rand(0,255),rand(0,255)) // technically conflicts could happen but it's like very unlikely and it's not that big of a deal if one happens + area_to_color[A] = meta_color + color_area_names[meta_color] = A.name + meta_icon.DrawBox(meta_color, img_x, img_y) + if(istype(T, /turf/closed/wall)) + map_icon.DrawBox("#000000", img_x, img_y) + else if(!istype(A, /area/space)) + var/color = A.minimap_color || "#FF00FF" + if(locate(/obj/machinery/power/solar) in T) + color = "#02026a" + if((locate(/obj/effect/spawner/structure/window) in T) || (locate(/obj/structure/grille) in T)) + color = BlendRGB(color, "#000000", 0.5) + map_icon.DrawBox(color, img_x, img_y) + map_icon.Crop(crop_x1, crop_y1, crop_x2, crop_y2) + meta_icon.Crop(crop_x1, crop_y1, crop_x2, crop_y2) + minx = crop_x1 + maxx = crop_x2 + miny = crop_y1 + maxy = crop_y2 + overlay_icon = new(map_icon) + overlay_icon.Scale(16, 16) + +/datum/minimap/proc/send(mob/user) + register_asset("minimap-[id].png", map_icon) + register_asset("minimap-[id]-meta.png", meta_icon) + send_asset_list(user, list("minimap-[id].png" = map_icon, "minimap-[id]-meta.png" = meta_icon), verify=FALSE) + +/datum/minimap_group + var/list/minimaps + var/static/next_id = 0 + var/id + var/name + +/datum/minimap_group/New(list/maps, name) + id = ++next_id + src.name = name + minimaps = maps || list() + +/datum/minimap_group/proc/show(mob/user) + if(!length(minimaps)) + to_chat(user, "ERROR: Attempted to access an empty datum/minimap_group. This should probably not happen.") + return + var/list/datas = list() + var/list/info = list() + var/datum/minimap/first_map = minimaps[1] + for(var/i in 1 to length(minimaps)) + var/datum/minimap/M = minimaps[i] + M.send(user) + info += "
" + datas += json_encode(M.color_area_names); + info = info.Join() + + var/html = {" + + + + + + +[name] + +[info] +"} + + user << browse(html, "window=minimap_[id];size=768x[round(768 / first_map.map_icon.Width() * first_map.map_icon.Height() + 50)]") diff --git a/html/blank.png b/html/blank.png new file mode 100644 index 0000000000..86c9630485 Binary files /dev/null and b/html/blank.png differ diff --git a/tgstation.dme b/tgstation.dme index 713b635c9c..e54b2626d3 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -269,6 +269,7 @@ #include "code\controllers\subsystem\mapping.dm" #include "code\controllers\subsystem\materials.dm" #include "code\controllers\subsystem\medals.dm" +#include "code\controllers\subsystem\minimaps.dm" #include "code\controllers\subsystem\minor_mapping.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\moods.dm" @@ -1679,6 +1680,7 @@ #include "code\modules\client\verbs\aooc.dm" #include "code\modules\client\verbs\etips.dm" #include "code\modules\client\verbs\looc.dm" +#include "code\modules\client\verbs\minimap.dm" #include "code\modules\client\verbs\ooc.dm" #include "code\modules\client\verbs\ping.dm" #include "code\modules\client\verbs\suicide.dm" @@ -2075,6 +2077,7 @@ #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\mapping\dmm_suite.dm" #include "code\modules\mapping\map_template.dm" +#include "code\modules\mapping\minimaps.dm" #include "code\modules\mapping\preloader.dm" #include "code\modules\mapping\reader.dm" #include "code\modules\mapping\ruins.dm"