Skip to content

Commit

Permalink
Adding SSpathfinding.
Browse files Browse the repository at this point in the history
  • Loading branch information
MistakeNot4892 committed Aug 7, 2024
1 parent d13ab1a commit 00f1b91
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 62 deletions.
2 changes: 1 addition & 1 deletion code/__defines/subsystem-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
#define SS_PRIORITY_GHOST_IMAGES 10 // Updates ghost client images.
#define SS_PRIORITY_ZCOPY 10 // Builds appearances for Z-Mimic.
#define SS_PRIORITY_PROJECTILES 10 // Projectile processing!

#define SS_PRIORITY_PATHFINDING 10 // Processing pathfinding requestes
// SS_BACKGROUND
#define SS_PRIORITY_OBJECTS 100 // processing_objects processing.
#define SS_PRIORITY_OVERMAP 95 // Moving objects on the overmap.
Expand Down
120 changes: 120 additions & 0 deletions code/controllers/subsystems/pathfinding.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
SUBSYSTEM_DEF(pathfinding)
name = "Pathfinding"
priority = SS_PRIORITY_PATHFINDING
init_order = SS_INIT_MISC_LATE
wait = 1

var/list/pending = list()
var/list/processing = list()
var/list/mover_metadata = list()

VAR_PRIVATE/static/_default_adjacency_call = TYPE_PROC_REF(/turf, CardinalTurfsWithAccess)
VAR_PRIVATE/static/_default_distance_call = TYPE_PROC_REF(/turf, Distance)

/atom/movable
var/waiting_for_path

/atom/movable/proc/path_found(list/path)
SHOULD_CALL_PARENT(TRUE)
waiting_for_path = null

/atom/movable/proc/path_not_found()
SHOULD_CALL_PARENT(TRUE)
waiting_for_path = null

/datum/controller/subsystem/pathfinding/proc/dequeue_mover(atom/movable/mover, include_processing = TRUE)
if(!istype(mover))
return
mover.waiting_for_path = null
pending -= mover
mover_metadata -= mover
if(include_processing)
processing -= mover


/datum/controller/subsystem/pathfinding/proc/enqueue_mover(atom/movable/mover, atom/target, datum/pathfinding_metadata/metadata)
if(!istype(mover) || mover.waiting_for_path)
return FALSE
if(!istype(target))
return FALSE
pending |= mover
pending[mover] = target
if(istype(metadata))
mover_metadata[mover] = metadata
mover.waiting_for_path = world.time
return TRUE

/datum/controller/subsystem/pathfinding/stat_entry(msg)
. = ..("Q:[length(pending)] P:[length(processing)]")

/datum/controller/subsystem/pathfinding/fire(resumed)

if(!resumed)
processing = pending?.Copy()

var/atom/movable/mover
var/atom/target
var/datum/pathfinding_metadata/metadata
var/i = 0

while(i < processing.len)

i++
mover = processing[i]
target = processing[mover]
metadata = mover_metadata[mover]
dequeue_mover(mover, include_processing = FALSE)

if(!QDELETED(mover) && !QDELETED(target))
try_find_path(mover, target, metadata)

if (MC_TICK_CHECK)
processing.Cut(1, i+1)
return

processing.Cut()

/datum/controller/subsystem/pathfinding/proc/try_find_path(atom/movable/mover, atom/target, datum/pathfinding_metadata/metadata, adjacency_call = _default_adjacency_call, distance_call = _default_distance_call)

var/started_pathing = world.time
mover.waiting_for_path = started_pathing

var/list/path = AStar(
get_turf(mover),
target,
adjacency_call,
distance_call,
(metadata?.max_nodes || 0),
(metadata?.max_node_depth || 250),
metadata?.min_target_dist,
metadata?.min_node_depth,
(metadata?.id || mover.GetIdCard()),
metadata?.obstacle
)
if(mover.waiting_for_path == started_pathing)
if(length(path))
mover.path_found(path)
else
mover.path_not_found()

/datum/pathfinding_metadata
var/max_nodes = 0
var/max_node_depth = 250
var/atom/id = null
var/min_target_dist = null
var/min_node_depth = null
var/obstacle = null

/datum/pathfinding_metadata/New(_max_nodes, _max_node_depth, _id, _min_target_dist, _min_node_depth, _obstacle)

id = _id
obstacle = _obstacle

if(!isnull(_max_nodes))
max_nodes = _max_nodes
if(!isnull(_max_node_depth))
max_node_depth = _max_node_depth
if(!isnull(_min_target_dist))
min_target_dist = _min_target_dist
if(!isnull(_min_node_depth))
min_node_depth = _min_node_depth
24 changes: 23 additions & 1 deletion code/datums/ai/_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
/// Aggressive AI var; defined here for reference without casting.
var/try_destroy_surroundings = FALSE

/// Current path for A* pathfinding.
var/list/executing_path

/datum/mob_controller/New(var/mob/living/target_body)
body = target_body
if(expected_type && !istype(body, expected_type))
Expand All @@ -78,7 +81,26 @@
. = ..()

/datum/mob_controller/proc/get_automove_target(datum/automove_metadata/metadata)
return null
var/turf/move_target = (islist(executing_path) && length(executing_path)) ? executing_path : null
if(istype(move_target) && QDELETED(move_target))
clear_path()

/datum/mob_controller/proc/handle_post_automoved(atom/old_loc)
if(!islist(executing_path) || length(executing_path) <= 0)
return
var/turf/body_turf = get_turf(body)
if(!istype(body_turf))
return
if(executing_path[1] != body_turf)
return
if(length(executing_path) > 1)
executing_path.Cut(1, 2)
else
clear_path()

/datum/mob_controller/proc/clear_path()
executing_path = null
body?.stop_automove()

/datum/mob_controller/proc/can_do_automated_move(variant_move_delay)
return body && !body.client
Expand Down
21 changes: 12 additions & 9 deletions code/datums/movement/automove_controller.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/// Implements automove logic; can be overridden on mob procs if you want to vary the logic from the below.
/decl/automove_controller
var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target.
var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move.
var/completion_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom reaches its target.
var/failure_signal = FALSE // Set to TRUE if you want movement to stop processing when the atom fails to move.
var/try_avoid_obstacles = TRUE // Will try to move 90 degrees around an obstacle.

/decl/automove_controller/proc/handle_mover(atom/movable/mover, datum/automove_metadata/metadata)

Expand Down Expand Up @@ -49,14 +50,16 @@
return TRUE // no idea how we would get into this position

if(mover.SelfMove(target_dir) && (old_loc != mover.loc))
return TRUE
mover.handle_post_automoved(old_loc)
return (mover.get_automove_target() == mover.loc) // We may have transitioned to the next step in a path.

// Try to move around any obstacle.
var/static/list/_alt_dir_rot = list(45, -45)
for(var/alt_dir in shuffle(_alt_dir_rot))
mover.reset_movement_delay()
if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc))
return TRUE
if(try_avoid_obstacles)
// Try to move around any obstacle.
var/static/list/_alt_dir_rot = list(45, -45)
for(var/alt_dir in shuffle(_alt_dir_rot))
mover.reset_movement_delay()
if(mover.SelfMove(turn(target_dir, alt_dir)) && (old_loc != mover.loc))
return TRUE

mover.failed_automove()

Expand Down
3 changes: 3 additions & 0 deletions code/game/atoms_movable.dm
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,6 @@
. = istype(actual_loc) && actual_loc.weather
if(!.) // If we're under or inside shelter, use the z-level rain (for ambience)
. = SSweather.weather_by_z[my_turf.z]

/atom/movable/proc/handle_post_automoved(atom/old_loc)
return
1 change: 1 addition & 0 deletions code/game/objects/structures/fountain.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
w_class = ITEM_SIZE_STRUCTURE
material = /decl/material/solid/stone/marble
used = TRUE
material_alteration = MAT_FLAG_ALTERATION_ALL

/obj/structure/fountain/mundane/Initialize(ml, _mat, _reinf_mat)
. = ..()
Expand Down
46 changes: 30 additions & 16 deletions code/modules/integrated_electronics/subtypes/smart.dm
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
return ..()

/obj/item/integrated_circuit/smart/advanced_pathfinder/do_work()

if(waiting_for_path)
return

if(!assembly)
activate_pin(3)
return
Expand All @@ -119,20 +123,30 @@
if(Pl&&islist(Pl))
idc.access = Pl
var/turf/a_loc = get_turf(assembly)
var/list/P = AStar(a_loc, locate(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), a_loc.z), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT, 3, /atom)))
SSpathfinding.enqueue_mover(
src,
locate(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), a_loc.z),
new /datum/pathfinding_metadata(
_max_node_depth = 200,
_id = idc,
_obstacle = get_turf(get_pin_data_as_type(IC_INPUT, 3, /atom))
)
)

if(!P)
activate_pin(3)
return
else
var/list/Xn = new/list(P.len)
var/list/Yn = new/list(P.len)
var/turf/T
for(var/i =1 to P.len)
T=P[i]
Xn[i] = T.x
Yn[i] = T.y
set_pin_data(IC_OUTPUT, 1, Xn)
set_pin_data(IC_OUTPUT, 2, Yn)
push_data()
activate_pin(2)
/obj/item/integrated_circuit/smart/advanced_pathfinder/path_not_found()
..()
activate_pin(3)

/obj/item/integrated_circuit/smart/advanced_pathfinder/path_found(list/path)
..()
var/list/Xn = new/list(path.len)
var/list/Yn = new/list(path.len)
var/turf/T
for(var/i = 1 to path.len)
T=path[i]
Xn[i] = T.x
Yn[i] = T.y
set_pin_data(IC_OUTPUT, 1, Xn)
set_pin_data(IC_OUTPUT, 2, Yn)
push_data()
activate_pin(2)
59 changes: 47 additions & 12 deletions code/modules/mob/living/bot/bot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
var/frustration = 0
var/max_frustration = 0

var/seeking_patrol_path = FALSE

layer = HIDING_MOB_LAYER

/mob/living/bot/Initialize()
Expand Down Expand Up @@ -291,13 +293,41 @@
return

/mob/living/bot/proc/startPatrol()
if(waiting_for_path)
return
seeking_patrol_path = TRUE
var/turf/T = getPatrolTurf()
if(T)
patrol_path = AStar(get_turf(loc), T, TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_patrol_dist, id = botcard, exclude = obstacle)
if(!patrol_path)
patrol_path = list()
obstacle = null
return
SSpathfinding.enqueue_mover(
src,
T,
new /datum/pathfinding_metadata(
_max_node_depth = 200,
_id = botcard,
_obstacle = obstacle
)
)

/mob/living/bot/path_found(list/path)
..()
if(seeking_patrol_path)
seeking_patrol_path = FALSE
patrol_path = path || list()
else
target_path = path
obstacle = null

/mob/living/bot/path_not_found()
..()
if(seeking_patrol_path)
seeking_patrol_path = FALSE
patrol_path = list()
else
target_path = list()
if(target && target.loc)
ignore_list |= target
resetTarget()
obstacle = null

/mob/living/bot/proc/getPatrolTurf()
var/minDist = INFINITY
Expand Down Expand Up @@ -325,13 +355,18 @@
return

/mob/living/bot/proc/calcTargetPath()
target_path = AStar(get_turf(loc), get_turf(target), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_target_dist, id = botcard, exclude = obstacle)
if(!target_path)
if(target && target.loc)
ignore_list |= target
resetTarget()
obstacle = null
return
if(waiting_for_path)
return
seeking_patrol_path = FALSE
SSpathfinding.enqueue_mover(
src,
get_turf(target),
new /datum/pathfinding_metadata(
_max_node_depth = 200,
_id = botcard,
_obstacle = obstacle
)
)

/mob/living/bot/proc/makeStep(var/list/path)
if(!path.len)
Expand Down
20 changes: 3 additions & 17 deletions code/modules/mob/living/bot/farmbot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -123,23 +123,9 @@
target = source
return

/mob/living/bot/farmbot/calcTargetPath() // We need to land NEXT to the tray, because the tray itself is impassable
for(var/trayDir in list(NORTH, SOUTH, EAST, WEST))
target_path = AStar(get_turf(loc), get_step(get_turf(target), trayDir), TYPE_PROC_REF(/turf, CardinalTurfsWithAccess), TYPE_PROC_REF(/turf, Distance), 0, max_target_dist, id = botcard)
if(target_path)
break
if(!target_path)
ignore_list |= target
target = null
target_path = list()
return

/mob/living/bot/farmbot/stepToTarget() // Same reason
var/turf/T = get_turf(target)
if(!target_path.len || !T.Adjacent(target_path[target_path.len]))
calcTargetPath()
makeStep(target_path)
return
// TODO: move adjacent
// /mob/living/bot/farmbot/calcTargetPath() // We need to land NEXT to the tray, because the tray itself is impassable
// /mob/living/bot/farmbot/stepToTarget() // Same reason

/mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity)
. = ..()
Expand Down
Loading

0 comments on commit 00f1b91

Please sign in to comment.