From 57cc38c789aa8b664f69de6ccc19ec19cdccafde Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 26 Oct 2024 09:41:34 -0400 Subject: [PATCH] GPSes now show the general direction of crosslinked z-levels (#87437) ## About The Pull Request This makes the GPS UI give the general direction of a GPS on a different linked z-level. https://github.com/user-attachments/assets/98c6dfd8-5ced-4145-b14a-3813821ef30c ## Why It's Good For The Game Makes navigating through space less of a chore, as previously, I believe the only way was to manually write down or memorize what direction was linked to what z-level. ## Changelog :cl: add: GPSes now show the general direction of cross-linked z-levels. /:cl: --- code/__HELPERS/levels.dm | 28 +++++++++++++++++++ code/_globalvars/lists/mapping.dm | 7 +++++ code/datums/components/gps.dm | 18 +++++++----- .../space_management/zlevel_manager.dm | 6 ++-- 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/code/__HELPERS/levels.dm b/code/__HELPERS/levels.dm index ca2cd3c5db3..927d8479b0d 100644 --- a/code/__HELPERS/levels.dm +++ b/code/__HELPERS/levels.dm @@ -58,3 +58,31 @@ // Finally, more specific checks are ran for edge cases, such as lazily loaded map templates or away missions. Not perfect. return istype(what_turf) && what_turf.planetary_atmos && what_turf.has_gravity() + +/** + * Gets the angle between two linked z-levels. + * Returns an angle (in degrees) if the z-levels are crosslinked/neighbors, + * or null if they are not. + * + * Arguments: + * * start: The starting Z level. Can either be a numeric z-level, or a [/datum/space_level]. + * * end: The destination Z level. Can either be a numeric z-level, or a [/datum/space_level]. + */ +/proc/get_linked_z_angle(datum/space_level/start, datum/space_level/end) + if(isnum(start)) + start = SSmapping.get_level(start) + if(isnum(end)) + end = SSmapping.get_level(end) + // Check the neighbors first, and return the appropiate angle if it is a neighbor. + for(var/direction in start.neigbours) + var/datum/space_level/neighbor = start.neigbours[direction] + if(neighbor == end) + var/angle = GLOB.cardinal_angles[direction] + if(!isnull(angle)) + return angle + // Otherwise, if they're both crosslinked, calculate the angle using their grid coordinates. + if(start.linkage == CROSSLINKED && end.linkage == CROSSLINKED) + var/dx = end.xi - start.xi + var/dy = end.yi - start.yi + return round(delta_to_angle(dy, dx)) + return null diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm index a7c7584d23a..78680d4b216 100644 --- a/code/_globalvars/lists/mapping.dm +++ b/code/_globalvars/lists/mapping.dm @@ -93,6 +93,13 @@ GLOBAL_LIST_INIT(alldirs, list( SOUTHWEST, )) +GLOBAL_LIST_INIT(cardinal_angles, list( + "[NORTH]" = 0, + "[SOUTH]" = 180, + "[EAST]" = 90, + "[WEST]" = 270, +)) + /// list of all landmarks created GLOBAL_LIST_EMPTY(landmarks_list) /// list of all job spawn points created diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm index 0b3751856b8..46c20913896 100644 --- a/code/datums/components/gps.dm +++ b/code/datums/components/gps.dm @@ -137,19 +137,23 @@ GLOBAL_LIST_EMPTY(GPS_list) var/list/signals = list() data["signals"] = list() - for(var/gps in GLOB.GPS_list) - var/datum/component/gps/G = gps - if(G.emped || !G.tracking || G == src) + for(var/datum/component/gps/gps as anything in GLOB.GPS_list) + if(gps == src || gps.emped || !gps.tracking) continue - var/turf/pos = get_turf(G.parent) - if(!pos || !global_mode && pos.z != curr.z) + var/turf/pos = get_turf(gps.parent) + if(!pos || (!global_mode && pos.z != curr.z)) continue var/list/signal = list() - signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS + signal["entrytag"] = gps.gpstag //Name or 'tag' of the GPS signal["coords"] = "[pos.x], [pos.y], [pos.z]" - if(pos.z == curr.z) //Distance/Direction calculations for same z-level only + // Distance is calculated for the same z-level only, and direction is calculated for crosslinked/neighboring and same z-levels. + if(pos.z == curr.z) signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs signal["degrees"] = round(get_angle(curr, pos)) //0-360 degree directional bearing, for more precision. + else + var/angle = get_linked_z_angle(curr.z, pos.z) + if(!isnull(angle)) + signal["degrees"] = angle signals += list(signal) //Add this signal to the list of signals data["signals"] = signals return data diff --git a/code/modules/mapping/space_management/zlevel_manager.dm b/code/modules/mapping/space_management/zlevel_manager.dm index b7664f09ff2..61f252edf2b 100644 --- a/code/modules/mapping/space_management/zlevel_manager.dm +++ b/code/modules/mapping/space_management/zlevel_manager.dm @@ -37,7 +37,9 @@ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_Z, S) return S -/datum/controller/subsystem/mapping/proc/get_level(z) - if (z_list && z >= 1 && z <= z_list.len) +/// Returns the /datum/space_level associated with the given z level. +/datum/controller/subsystem/mapping/proc/get_level(z) as /datum/space_level + RETURN_TYPE(/datum/space_level) + if(ISINRANGE(z, 1, length(z_list))) return z_list[z] CRASH("Unmanaged z-level [z]! maxz = [world.maxz], z_list.len = [z_list ? z_list.len : "null"]")