Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding SSpathfinding. #4302

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# ignore misc BYOND files
Thumbs.db
Thumbs.db:encryptable
*.log
*.int
*.rsc
Expand Down
2 changes: 2 additions & 0 deletions code/__defines/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define STANCE_ATTACKING /decl/mob_controller_stance/attacking
#define STANCE_TIRED /decl/mob_controller_stance/tired
#define STANCE_CONTAINED /decl/mob_controller_stance/contained
#define STANCE_BUSY /decl/mob_controller_stance/busy

//basically 'do nothing'
#define STANCE_COMMANDED_STOP /decl/mob_controller_stance/commanded/stop
//follows a target
Expand Down
1 change: 1 addition & 0 deletions code/__defines/subsystem-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,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 requests

// SS_BACKGROUND
#define SS_PRIORITY_OBJECTS 100 // processing_objects processing.
Expand Down
5 changes: 4 additions & 1 deletion code/controllers/subsystems/mob_ai/auto_movement.dm
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@ SUBSYSTEM_DEF(automove)
if(controller.handle_mover(mover, moving_metadata[mover]) == PROCESS_KILL && !QDELETED(mover))
mover.stop_automove()
if(MC_TICK_CHECK)
processing_atoms.Cut(1, i+1)
if(i >= length(processing_atoms))
processing_atoms.Cut()
else
processing_atoms.Cut(1, i+1)
return
123 changes: 123 additions & 0 deletions code/controllers/subsystems/pathfinding.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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

// Hook to allow legacy use of AStar* to reuse the callback refs
/datum/controller/subsystem/pathfinding/proc/find_path_immediate(start, end, max_nodes, max_node_depth = 30, min_target_dist = 0, min_node_dist, id, datum/exclude, check_tick = FALSE)
return find_path_astar(start, end, _default_adjacency_call, _default_distance_call, max_nodes, max_node_depth, min_target_dist, min_node_dist, id, exclude, check_tick)

/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 = find_path_astar(
get_turf(mover),
target,
adjacency_call,
distance_call,
(metadata?.max_nodes || null),
(metadata?.max_node_depth || 250),
metadata?.min_target_dist,
metadata?.min_node_depth,
(metadata?.id || mover.GetIdCard()),
metadata?.obstacle,
check_tick = TRUE
)
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 = null
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
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
154 changes: 20 additions & 134 deletions code/datums/ai/_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@
/// Aggressive AI var; defined here for reference without casting.
var/try_destroy_surroundings = FALSE

/// Reference to the atom we are targetting.
var/weakref/target_ref

/// Current path for A* pathfinding.
var/list/executing_path
/// A counter for times we have failed to progress along our path.
var/path_frustration = 0
/// A list of any obstacles we should path around in future.
var/list/path_obstacles = null

/// Radius of target scan area when looking for valid targets. Set to 0 to disable target scanning.
var/target_scan_distance = 0
/// Time tracker for next target scan.
var/next_target_scan_time
/// How long minimum between scans.
var/target_scan_delay = 1 SECOND

/datum/mob_controller/New(var/mob/living/target_body)
body = target_body
if(expected_type && !istype(body, expected_type))
Expand All @@ -71,6 +88,7 @@
/datum/mob_controller/Destroy()
LAZYCLEARLIST(_friends)
LAZYCLEARLIST(_enemies)
set_target(null)
if(is_processing)
STOP_PROCESSING(SSmob_ai, src)
if(body)
Expand All @@ -79,12 +97,6 @@
body = null
. = ..()

/datum/mob_controller/proc/get_automove_target(datum/automove_metadata/metadata)
return null

/datum/mob_controller/proc/can_do_automated_move(variant_move_delay)
return body && !body.client

/datum/mob_controller/proc/can_process()
if(!body || !body.loc || ((body.client || body.mind) && !(body.status_flags & ENABLE_AI)))
return FALSE
Expand All @@ -111,13 +123,13 @@
// This is the place to actually do work in the AI.
/datum/mob_controller/proc/do_process()
SHOULD_CALL_PARENT(TRUE)
if(!QDELETED(body) && !QDELETED(src))
if(get_stance() != STANCE_BUSY && !QDELETED(body) && !QDELETED(src))
if(!body.stat)
try_unbuckle()
try_wander()
try_bark()
// Recheck in case we walked into lava or something during wandering.
return !QDELETED(body) && !QDELETED(src)
return get_stance() != STANCE_BUSY && !QDELETED(body) && !QDELETED(src)
return TRUE
return FALSE

Expand All @@ -132,16 +144,6 @@
else if(prob(25))
body.visible_message(SPAN_WARNING("\The [body] struggles against \the [body.buckled]!"))


/datum/mob_controller/proc/get_activity()
return current_activity

/datum/mob_controller/proc/set_activity(new_activity)
if(current_activity != new_activity)
current_activity = new_activity
return TRUE
return FALSE

// The mob will periodically sit up or step 1 tile in a random direction.
/datum/mob_controller/proc/try_wander()
//Movement
Expand Down Expand Up @@ -185,133 +187,17 @@
else if(ispath(do_emote, /decl/emote))
body.emote(do_emote)

/datum/mob_controller/proc/get_target()
return null

/datum/mob_controller/proc/set_target(atom/new_target)
return

/datum/mob_controller/proc/find_target()
return

/datum/mob_controller/proc/valid_target(var/atom/A)
return

/datum/mob_controller/proc/move_to_target(var/move_only = FALSE)
return

/datum/mob_controller/proc/stop_wandering()
stop_wander = TRUE

/datum/mob_controller/proc/resume_wandering()
stop_wander = FALSE

/datum/mob_controller/proc/set_stance(new_stance)
if(stance != new_stance)
stance = new_stance
return TRUE
return FALSE

/datum/mob_controller/proc/get_stance()
return stance

/datum/mob_controller/proc/list_targets(var/dist = 7)
return

/datum/mob_controller/proc/open_fire()
return

/datum/mob_controller/proc/startle()
if(QDELETED(body) || body.stat != UNCONSCIOUS)
return
body.set_stat(CONSCIOUS)
if(body.current_posture?.prone)
body.set_posture(/decl/posture/standing)

/datum/mob_controller/proc/retaliate(atom/source)
SHOULD_CALL_PARENT(TRUE)
if(!istype(body) || body.stat == DEAD)
return FALSE
startle()
if(isliving(source))
remove_friend(source)
return TRUE

/datum/mob_controller/proc/destroy_surroundings()
return

/datum/mob_controller/proc/lose_target()
return

/datum/mob_controller/proc/lost_target()
return

/datum/mob_controller/proc/handle_death(gibbed)
return

/datum/mob_controller/proc/pacify(mob/user)
lose_target()
add_friend(user)

// General-purpose memorise proc, used by /commanded
/datum/mob_controller/proc/memorise(mob/speaker, message)
return

// General-purpose memory checking proc, used by /faithful_hound
/datum/mob_controller/proc/check_memory(mob/speaker, message)
return FALSE

/// General-purpose scooping reaction proc, used by /passive.
/// Returns TRUE if the scoop should proceed, FALSE if it should be canceled.
/datum/mob_controller/proc/scooped_by(mob/initiator)
return TRUE

// Enemy tracking - used on /aggressive
/datum/mob_controller/proc/get_enemies()
return _enemies

/datum/mob_controller/proc/add_enemy(mob/enemy)
if(istype(enemy))
LAZYDISTINCTADD(_enemies, weakref(enemy))

/datum/mob_controller/proc/add_enemies(list/enemies)
for(var/thing in enemies)
if(ismob(thing))
add_friend(thing)
else if(istype(thing, /weakref))
LAZYDISTINCTADD(_enemies, thing)

/datum/mob_controller/proc/remove_enemy(mob/enemy)
LAZYREMOVE(_enemies, weakref(enemy))

/datum/mob_controller/proc/set_enemies(list/new_enemies)
_enemies = new_enemies

/datum/mob_controller/proc/is_enemy(mob/enemy)
. = istype(enemy) && LAZYLEN(_enemies) && (weakref(enemy) in _enemies)

/datum/mob_controller/proc/clear_enemies()
LAZYCLEARLIST(_enemies)

// Friend tracking - used on /aggressive.
/datum/mob_controller/proc/get_friends()
return _friends

/datum/mob_controller/proc/add_friend(mob/friend)
if(istype(friend))
LAZYDISTINCTADD(_friends, weakref(friend))
return TRUE
return FALSE

/datum/mob_controller/proc/remove_friend(mob/friend)
LAZYREMOVE(_friends, weakref(friend))

/datum/mob_controller/proc/set_friends(list/new_friends)
_friends = new_friends

/datum/mob_controller/proc/is_friend(mob/friend)
. = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends)

// By default, randomize the target area a bit to make armor/combat
// a bit more dynamic (and avoid constant organ damage to the chest)
/datum/mob_controller/proc/update_target_zone()
Expand Down
Loading
Loading