Pathfinding Visualization, JPS fixes, Misc Improvement (#90233)

## About The Pull Request

[cleans up poor namespacing on light debugging
tools](93cc9070d5)

[Implements a pathfinding visualization
tool](ed91f69ac4)

It holds a set of inputs from the client, and uses them to generate and
display paths from source/target. Left click sets the source, right
click sets the target.

Pathmap support too, if no target is set we display the paths from every
turf in the map to the source, if one is set we build a path TO it from
the source.

I had to add COMSIG_MOB_CLICKON to observers to make this work (tho idk
why it didn't exist already), I also removed the everpresent colorblind
datum from admin datums, only needs to exist if they're using it.

[Adds a mutable_appearance helper that dirlocks them, wallening port
which I thought might be useful here, and will likely be useful
elsewhere in
future](87f752e7c3)

[Fixes an infinite loop in pathmaps if we tried to pull a cached path to
an unreachable target, && not ||
4head](10086a655d)

[Fixes JPS not dealing with border objects properly. They violate some
of the assumptions JPS makes, so we need to backfill them with checks.
These basically read as (if the thing that should normally take this
path can't reach this turf, can
we?)](f56cc4dd43)

## Why It's Good For The Game

Maybe deals with #80619?

Adds more robust testing tools for pathfinding, should allow people to
better understand/debug these systems. I added this with the idea of
adding multiz support but I don't have the time for that rn.

JPS will work around thindows better, that's nice.

https://file.house/IrBiR0bGxoKw1jJJoxgMRQ==.mp4

## Changelog
🆑
fix: Fixed our pathfinding logic getting deeply confused by border
objects
admin: Added clientside displayed pathfinding debug tools, give em a go
/🆑
This commit is contained in:
LemonInTheDark
2025-03-27 16:51:57 -07:00
committed by GitHub
parent b18be2e531
commit 77823ad210
15 changed files with 328 additions and 22 deletions

View File

@@ -15,7 +15,7 @@
#define COMSIG_MOB_REAGENT_CHECK "mob_reagent_check"
///stops the reagent check call
#define COMSIG_MOB_STOP_REAGENT_CHECK (1<<0)
///from base of mob/clickon(): (atom/A, params)
///from base of mob/clickon(): (atom/A, list/modifiers)
#define COMSIG_MOB_CLICKON "mob_clickon"
///from base of mob/MiddleClickOn(): (atom/A)
#define COMSIG_MOB_MIDDLECLICKON "mob_middleclickon"

View File

@@ -307,6 +307,11 @@
/// Layer for light overlays
#define LIGHT_DEBUG_LAYER 6
/// Layer for pathfinding arrows
#define PATH_ARROW_DEBUG_LAYER 7
/// Layer for pathfinding overlays
#define PATH_DEBUG_LAYER 8
///Layer for lobby menu collapse button
#define LOBBY_BELOW_MENU_LAYER 2
///Layer for lobby menu background image and main buttons (Join/Ready, Observe, Character Prefs)

View File

@@ -9,7 +9,8 @@
* If you really want to optimize things, optimize this, cuz this gets called a lot.
* We do early next.density check despite it being already checked in LinkBlockedWithAccess for short-circuit performance
*/
#define CAN_STEP(cur_turf, next, simulated_only, pass_info, avoid) (next && !next.density && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && !cur_turf.LinkBlockedWithAccess(next, pass_info) && (next != avoid))
#define CAN_STEP(cur_turf, next, simulated_only, pass_info, avoid) \
(next && !next.density && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && (next != avoid) && !cur_turf.LinkBlockedWithAccess(next, pass_info))
#define DIAGONAL_DO_NOTHING NONE
#define DIAGONAL_REMOVE_ALL 1

View File

@@ -6,7 +6,11 @@
/// A helper macro for JPS, for telling when a node has forced neighbors that need expanding
/// Only usable in the context of the jps datum because of the datum vars it relies on
/// Checks if we are deviating from our "running" directions
#define STEP_NOT_HERE_BUT_THERE(cur_turf, dirA, dirB) ((!CAN_STEP(cur_turf, get_step(cur_turf, dirA), simulated_only, pass_info, avoid) && CAN_STEP(cur_turf, get_step(cur_turf, dirB), simulated_only, pass_info, avoid)))
/// Checks if a border object stops our parent from reaching a turf we CAN reach
#define TURF_CANT_WE_CAN(parent_turf, dir_parent, cur_turf, dur_cur) ((!CAN_STEP(parent_turf, get_step(parent_turf, dir_parent), simulated_only, pass_info, avoid) && CAN_STEP(cur_turf, get_step(cur_turf, dur_cur), simulated_only, pass_info, avoid)))
/// The JPS Node datum represents a turf that we find interesting enough to add to the open list and possibly search for new tiles from
/datum/jps_node
@@ -212,16 +216,20 @@
switch(heading)
if(NORTH)
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST) \
|| TURF_CANT_WE_CAN(get_step(current_turf, EAST), NORTH, current_turf, NORTHEAST) || TURF_CANT_WE_CAN(get_step(current_turf, WEST), NORTH, current_turf, NORTHWEST))
interesting = TRUE
if(SOUTH)
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST) \
|| TURF_CANT_WE_CAN(get_step(current_turf, EAST), SOUTH, current_turf, SOUTHEAST) || TURF_CANT_WE_CAN(get_step(current_turf, WEST), SOUTH, current_turf, SOUTHWEST))
interesting = TRUE
if(EAST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST) \
|| TURF_CANT_WE_CAN(get_step(current_turf, SOUTH), EAST, current_turf, SOUTHEAST) || TURF_CANT_WE_CAN(get_step(current_turf, NORTH), EAST, current_turf, NORTHEAST))
interesting = TRUE
if(WEST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST) \
|| TURF_CANT_WE_CAN(get_step(current_turf, SOUTH), WEST, current_turf, SOUTHWEST) || TURF_CANT_WE_CAN(get_step(current_turf, NORTH), WEST, current_turf, NORTHWEST))
interesting = TRUE
if(interesting)
@@ -274,22 +282,26 @@
switch(heading)
if(NORTHWEST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, NORTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHWEST) \
|| TURF_CANT_WE_CAN(lag_turf, NORTH, current_turf, EAST) || TURF_CANT_WE_CAN(lag_turf, WEST, current_turf, SOUTH))
interesting = TRUE
else
possible_child_node = (lateral_scan_spec(current_turf, WEST) || lateral_scan_spec(current_turf, NORTH))
if(NORTHEAST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, NORTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, SOUTH, SOUTHEAST) \
|| TURF_CANT_WE_CAN(lag_turf, NORTH, current_turf, WEST) || TURF_CANT_WE_CAN(lag_turf, EAST, current_turf, SOUTH))
interesting = TRUE
else
possible_child_node = (lateral_scan_spec(current_turf, EAST) || lateral_scan_spec(current_turf, NORTH))
if(SOUTHWEST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, EAST, SOUTHEAST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHWEST) \
|| TURF_CANT_WE_CAN(lag_turf, SOUTH, current_turf, EAST) || TURF_CANT_WE_CAN(lag_turf, WEST, current_turf, NORTH))
interesting = TRUE
else
possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, WEST))
if(SOUTHEAST)
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST))
if(STEP_NOT_HERE_BUT_THERE(current_turf, WEST, SOUTHWEST) || STEP_NOT_HERE_BUT_THERE(current_turf, NORTH, NORTHEAST) \
|| TURF_CANT_WE_CAN(lag_turf, SOUTH, current_turf, WEST) || TURF_CANT_WE_CAN(lag_turf, EAST, current_turf, NORTH))
interesting = TRUE
else
possible_child_node = (lateral_scan_spec(current_turf, SOUTH) || lateral_scan_spec(current_turf, EAST))

View File

@@ -77,7 +77,7 @@
var/turf/next_turf = other_end
// Cache for sonic speed
var/next_closest = src.next_closest
while(next_turf != FLOW_PATH_END || next_turf == null)
while(next_turf != FLOW_PATH_END && next_turf != null)
path += next_turf
next_turf = next_closest[next_turf] // We take the first entry cause that's the turf

View File

@@ -15,6 +15,9 @@
return
var/list/modifiers = params2list(params)
if(SEND_SIGNAL(src, COMSIG_MOB_CLICKON, A, modifiers) & COMSIG_MOB_CANCEL_CLICKON)
return
if(LAZYACCESS(modifiers, SHIFT_CLICK))
if(LAZYACCESS(modifiers, MIDDLE_CLICK))
ShiftMiddleClickOn(A)

View File

@@ -49,3 +49,11 @@
check_topdown_validity(appearance)
return appearance
/// Takes an input mutable appearance, returns a copy of it with the hidden flag flipped to avoid inheriting dir from what it's drawn on
/// This inheriting thing is handled by a hidden flag on the /image (MAs are subtypes of /image)
/proc/make_mutable_appearance_directional(mutable_appearance/to_process, dir = NORTH)
// We use the image() proc in combo with a manually set dir to flip this flag
// We can then copy the image's appearance to retain the flag, even on MAs and such
var/image/holder = image(to_process, dir = dir)
return new /mutable_appearance(holder)

View File

@@ -33,9 +33,10 @@ GLOBAL_PROTECT(href_token)
var/datum/filter_editor/filteriffic
var/datum/particle_editor/particle_test
var/datum/colorblind_tester/color_test = new
var/datum/colorblind_tester/color_test
var/datum/plane_master_debug/plane_debug
var/obj/machinery/computer/libraryconsole/admin_only_do_not_map_in_you_fucker/library_manager
var/datum/pathfind_debug/path_debug
/// Whether or not the user tried to connect, but was blocked by 2FA
var/blocked_by_2fa = FALSE
@@ -78,7 +79,8 @@ GLOBAL_PROTECT(href_token)
if(IsAdminAdvancedProcCall())
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
QDEL_NULL(path_debug)
return ..()
/datum/admins/proc/activate()
if(IsAdminAdvancedProcCall())
@@ -99,6 +101,7 @@ GLOBAL_PROTECT(href_token)
GLOB.deadmins[target] = src
GLOB.admin_datums -= target
QDEL_NULL(plane_debug)
QDEL_NULL(path_debug)
deadmined = TRUE
var/client/client = owner || GLOB.directory[target]

View File

@@ -552,6 +552,8 @@ ADMIN_VERB(init_log, R_DEBUG, "Display Initialize() Log", "Displays a list of th
browser.open()
ADMIN_VERB(debug_color_test, R_DEBUG, "Colorblind Testing", "Change your view to a budget version of colorblindness to test for usability.", ADMIN_CATEGORY_DEBUG)
if(!user.holder.color_test)
user.holder.color_test = new()
user.holder.color_test.ui_interact(user.mob)
ADMIN_VERB(debug_plane_masters, R_DEBUG, "Edit/Debug Planes", "Edit and visualize plane masters and their connections (relays).", ADMIN_CATEGORY_DEBUG)
@@ -654,6 +656,14 @@ ADMIN_VERB(run_empty_query, R_DEBUG, "Run Empty Query", "Runs a specified number
message_admins("[key_name_admin(user)] ran [val] empty queries.")
ADMIN_VERB(test_pathfinding, R_DEBUG, "Toggle Pathfind Testing", "Enables/Disables pathfinding testing action buttons", ADMIN_CATEGORY_DEBUG)
BLACKBOX_LOG_ADMIN_VERB("Toggle Pathfind Testing")
log_admin("[key_name(user)] [user.holder.path_debug ? "disabled" : "enabled"] their pathfinding debug tools")
if(!user.holder.path_debug)
user.holder.path_debug = new(user.holder)
else
QDEL_NULL(user.holder.path_debug)
ADMIN_VERB(clear_turf_reservations, R_DEBUG, "Clear Dynamic Turf Reservations", "Deallocates all reserved space, restoring it to round start conditions.", ADMIN_CATEGORY_DEBUG)
var/answer = tgui_alert(
user,

View File

@@ -1,12 +1,12 @@
/proc/debug_sources()
/proc/debug_light_sources()
GLOB.light_debug_enabled = TRUE
var/list/sum = list()
var/total = 0
for(var/datum/light_source/source)
if(!source.source_atom)
continue
source.source_atom.debug()
source.source_atom.debug_lights()
sum[source.source_atom.type] += 1
total += 1
@@ -29,14 +29,14 @@
var/datum/action/spawn_light/let_there_be = new (new_lad.mob.mind || new_lad.mob)
let_there_be.Grant(new_lad.mob)
/proc/undebug_sources()
/proc/undebug_light_sources()
GLOB.light_debug_enabled = FALSE
for(var/datum/weakref/button_ref as anything in GLOB.light_debugged_atoms)
var/atom/button = button_ref.resolve()
if(!button)
GLOB.light_debugged_atoms -= button_ref
continue
button.undebug()
button.undebug_lights()
SEND_GLOBAL_SIGNAL(COMSIG_LIGHT_DEBUG_DISABLED)
SSdcs.UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT)
@@ -44,7 +44,7 @@
GLOBAL_LIST_EMPTY(light_debugged_atoms)
/// Sets up this light source to be debugged, setting up in world buttons to control and move it
/// Also freezes it, so it can't change in future
/atom/proc/debug()
/atom/proc/debug_lights()
if(isturf(src) || HAS_TRAIT(src, TRAIT_LIGHTING_DEBUGGED))
return
ADD_TRAIT(src, TRAIT_LIGHTING_DEBUGGED, LIGHT_DEBUG_TRAIT)
@@ -65,7 +65,7 @@ GLOBAL_LIST_EMPTY(light_debugged_atoms)
new /atom/movable/screen/light_button/move(src)
/// Disables light debugging, so you can let a scene fall to what it visually should be, or just fix admin fuckups
/atom/proc/undebug()
/atom/proc/undebug_lights()
// I don't really want to undebug a light if it's off rn
// Loses control if we turn it back on again
if(isturf(src) || !HAS_TRAIT(src, TRAIT_LIGHTING_DEBUGGED) || !light)

View File

@@ -390,7 +390,7 @@ ADMIN_VERB(check_for_obstructed_atmospherics, R_DEBUG, "Check For Obstructed Atm
ADMIN_VERB_VISIBILITY(modify_lights, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG)
ADMIN_VERB(modify_lights, R_DEBUG, "Toggle Light Debug", "Toggles light debug mode.", ADMIN_CATEGORY_MAPPING)
if(GLOB.light_debug_enabled)
undebug_sources()
undebug_light_sources()
return
for(var/obj/machinery/light/fix_up as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/light))
@@ -398,7 +398,7 @@ ADMIN_VERB(modify_lights, R_DEBUG, "Toggle Light Debug", "Toggles light debug mo
if(initial(fix_up.status) == LIGHT_OK)
fix_up.fix()
CHECK_TICK
debug_sources()
debug_light_sources()
ADMIN_VERB_VISIBILITY(visualize_lights, ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG)
ADMIN_VERB(visualize_lights, R_DEBUG, "Visualize Lighting Corners", "Visualizes the corners of all lights on the station.", ADMIN_CATEGORY_MAPPING)

View File

@@ -0,0 +1,263 @@
GLOBAL_DATUM_INIT(pathfind_dude, /obj/pathfind_guy, new())
/obj/pathfind_guy
/// Enables testing/visualization of pathfinding work
/datum/pathfind_debug
var/datum/admins/owner
var/datum/action/innate/path_debug/jps/jps_debug
var/datum/action/innate/path_debug/sssp/sssp_debug
/datum/pathfind_debug/New(datum/admins/owner)
src.owner = owner
hook_client()
/datum/pathfind_debug/Destroy(force)
QDEL_NULL(jps_debug)
QDEL_NULL(sssp_debug)
return ..()
/datum/pathfind_debug/proc/hook_client()
if(!owner.owner)
return
QDEL_NULL(jps_debug)
QDEL_NULL(sssp_debug)
jps_debug = new
jps_debug.Grant(owner.owner.mob)
sssp_debug = new()
sssp_debug.Grant(owner.owner.mob)
RegisterSignal(owner.owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_logout))
/datum/pathfind_debug/proc/on_logout(mob/logging_out)
SIGNAL_HANDLER
UnregisterSignal(logging_out, COMSIG_MOB_LOGOUT)
var/mob/new_mob = owner.owner?.mob
if(!new_mob)
RegisterSignal(logging_out, COMSIG_MOB_LOGIN, PROC_REF(on_login))
return
hook_client()
/datum/pathfind_debug/proc/on_login(mob/logging_in)
SIGNAL_HANDLER
UnregisterSignal(logging_in, list(COMSIG_MOB_LOGOUT, COMSIG_MOB_LOGIN))
hook_client()
/datum/action/innate/path_debug
var/list/image/display_images = list()
/datum/action/innate/path_debug/Activate()
. = ..()
RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(clicked_somethin))
active = TRUE
/datum/action/innate/path_debug/Deactivate()
UnregisterSignal(owner, COMSIG_MOB_CLICKON)
clear_visuals()
active = FALSE
return ..()
/datum/action/innate/path_debug/proc/clicked_somethin(datum/source, atom/clicked, list/modifiers)
SIGNAL_HANDLER
if(LAZYACCESS(modifiers, SHIFT_CLICK))
return NONE
var/turf/clunked = get_turf(clicked)
if(!clunked)
return NONE
if(LAZYACCESS(modifiers, RIGHT_CLICK))
right_clicked(clunked)
else
left_clicked(clunked)
update_visuals()
if(path_ready())
pathfind()
/datum/action/innate/path_debug/proc/left_clicked(turf/clicked_on)
return
/datum/action/innate/path_debug/proc/right_clicked(turf/clicked_on)
return
/datum/action/innate/path_debug/proc/update_visuals()
clear_visuals()
build_visuals()
owner.client?.images += display_images
/datum/action/innate/path_debug/proc/clear_visuals()
owner.client?.images -= display_images
display_images = list()
/datum/action/innate/path_debug/proc/build_visuals()
return
/datum/action/innate/path_debug/proc/path_ready()
return FALSE
/datum/action/innate/path_debug/proc/pathfind()
INVOKE_ASYNC(src, PROC_REF(run_the_path), GLOB.pathfind_dude)
GLOB.pathfind_dude.moveToNullspace()
/datum/action/innate/path_debug/proc/run_the_path(atom/movable/middle_man)
return
/datum/action/innate/path_debug/proc/render_path(list/turf/draw_list)
if(!length(draw_list))
return list()
var/list/image/turf_images = list()
// Render everything but the first and last
for(var/i in 1 to (length(draw_list) - 1))
var/turf/problem_child = draw_list[i]
var/turf/next = draw_list[i + 1]
turf_images += render_turf(problem_child, get_dir(problem_child, next))
return turf_images
/datum/action/innate/path_debug/proc/render_turf(turf/draw, direction)
var/image/arrow = image('icons/turf/debug.dmi', draw, "arrow", PATH_ARROW_DEBUG_LAYER, direction)
SET_PLANE_EXPLICIT(arrow, BALLOON_CHAT_PLANE, draw)
return arrow
/datum/action/innate/path_debug/jps
name = "JPS Test"
button_icon = 'icons/turf/debug.dmi'
button_icon_state = "jps"
// Mirror vars for jps calls
var/turf/source_turf
var/turf/target_turf
var/max_distance
var/min_distance
var/allowed_on_space
var/turf/blacklisted_turf
var/diagonal_handling
/// List of turfs we are showing to our owner currently
var/list/turf/display_turfs
/datum/action/innate/path_debug/jps/Activate()
. = ..()
max_distance = tgui_input_number(owner, "How far should we be allowed to try and path", "Max Distance", min_value = 1, default = 30)
min_distance = tgui_input_number(owner, "How close should we try and get to the target before stopping", "Min Distance", min_value = 0, default = 0)
allowed_on_space = tgui_alert(owner, "Are we allowed to path over space?", "Space Pathing", buttons = list("Yes", "No")) == "Yes"
var/text_blacklist = tgui_input_text(owner, "Enter any turf path you want to blacklist (You get one)", "Turf Blacklist")
if(text_blacklist)
blacklisted_turf = pick_closest_path(text_blacklist)
else
blacklisted_turf = null
diagonal_handling = DIAGONAL_DO_NOTHING
switch(tgui_input_list(owner, "Pick how you want to handle diagonal moves", "Diagonal Moves", list("Leave Them Be", "Drop All", "Drop Odd Ones")))
if("Leave Them Be")
diagonal_handling = DIAGONAL_DO_NOTHING
if("Drop All")
diagonal_handling = DIAGONAL_REMOVE_ALL
if("Drop Odd Ones")
diagonal_handling = DIAGONAL_REMOVE_CLUNKY
/datum/action/innate/path_debug/jps/Deactivate()
source_turf = null
target_turf = null
display_turfs = list()
return ..()
/datum/action/innate/path_debug/jps/left_clicked(turf/clicked_on)
source_turf = clicked_on
display_turfs = list()
/datum/action/innate/path_debug/jps/right_clicked(turf/clicked_on)
target_turf = clicked_on
display_turfs = list()
/datum/action/innate/path_debug/jps/build_visuals()
. = ..()
if(source_turf)
var/image/start = image('icons/turf/debug.dmi', source_turf, "start", PATH_DEBUG_LAYER)
SET_PLANE_EXPLICIT(start, BALLOON_CHAT_PLANE, source_turf)
display_images += start
if(target_turf)
var/image/end = image('icons/turf/debug.dmi', target_turf, "end", PATH_DEBUG_LAYER)
SET_PLANE_EXPLICIT(end, BALLOON_CHAT_PLANE, target_turf)
display_images += end
display_images += render_path(display_turfs)
/datum/action/innate/path_debug/jps/path_ready()
return (source_turf && target_turf)
/datum/action/innate/path_debug/jps/run_the_path(atom/movable/middle_man)
middle_man.forceMove(source_turf)
display_turfs = get_path_to(middle_man, target_turf, max_distance, min_distance, list(), allowed_on_space, blacklisted_turf, skip_first = FALSE, diagonal_handling = diagonal_handling)
update_visuals()
/datum/action/innate/path_debug/sssp
name = "Pathmap Test"
button_icon = 'icons/turf/debug.dmi'
button_icon_state = "sssp"
// Mirror vars for sssp calls
var/turf/source_turf
var/max_distance
var/allowed_on_space
var/turf/blacklisted_turf
// Turf to display the path to (optional)
var/turf/target_turf
/// List of turfs we are showing to our owner currently
var/datum/path_map/shown_map
/datum/action/innate/path_debug/sssp/Activate()
. = ..()
max_distance = tgui_input_number(owner, "How far should we be allowed to try and path", "Max Distance", min_value = 1, default = 30)
allowed_on_space = tgui_alert(owner, "Are we allowed to path over space?", "Space Pathing", buttons = list("Yes", "No")) == "Yes"
var/text_blacklist = tgui_input_text(owner, "Enter any turf path you want to blacklist (You get one)", "Turf Blacklist")
if(text_blacklist)
blacklisted_turf = pick_closest_path(text_blacklist)
else
blacklisted_turf = null
/datum/action/innate/path_debug/sssp/Deactivate()
source_turf = null
target_turf = null
shown_map = null
return ..()
/datum/action/innate/path_debug/sssp/left_clicked(turf/clicked_on)
source_turf = clicked_on
shown_map = null
/datum/action/innate/path_debug/sssp/right_clicked(turf/clicked_on)
if(clicked_on == target_turf)
target_turf = null
return
target_turf = clicked_on
/datum/action/innate/path_debug/sssp/build_visuals()
. = ..()
if(source_turf)
var/image/start = image('icons/turf/debug.dmi', source_turf, "start", PATH_DEBUG_LAYER)
SET_PLANE_EXPLICIT(start, BALLOON_CHAT_PLANE, source_turf)
display_images += start
if(target_turf)
var/image/end = image('icons/turf/debug.dmi', target_turf, "end", PATH_DEBUG_LAYER)
SET_PLANE_EXPLICIT(end, BALLOON_CHAT_PLANE, target_turf)
display_images += end
if(shown_map)
display_images += render_path(shown_map.get_path_to(target_turf))
else
if(!shown_map)
return
var/list/turf/next_closest = shown_map.next_closest
var/turf/start = shown_map?.start
for(var/turf/next_dude as anything in next_closest)
if(next_dude == start)
continue
display_images += render_turf(next_dude, get_dir(next_dude, next_closest[next_dude]))
/datum/action/innate/path_debug/sssp/path_ready()
return (source_turf && source_turf != shown_map?.start)
/datum/action/innate/path_debug/sssp/run_the_path(atom/movable/middle_man)
middle_man.forceMove(source_turf)
shown_map = get_sssp(middle_man, max_distance, list(), allowed_on_space, blacklisted_turf)
update_visuals()

View File

@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(lighting_sheets)
update()
if(GLOB.light_debug_enabled)
source_atom.debug()
source_atom.debug_lights()
/datum/light_source/Destroy(force)
remove_lum()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -3068,6 +3068,7 @@
#include "code\modules\admin\verbs\mapping.dm"
#include "code\modules\admin\verbs\maprotation.dm"
#include "code\modules\admin\verbs\panicbunker.dm"
#include "code\modules\admin\verbs\path_debugger.dm"
#include "code\modules\admin\verbs\plane_debugger.dm"
#include "code\modules\admin\verbs\player_ticket_history.dm"
#include "code\modules\admin\verbs\playsound.dm"