Files
CHOMPStation2/code/_helpers/pathfinding_ch/common.dm
Cadyn ecd8125771 Ai update (#8023)
Co-authored-by: silicons <2003111+silicons@users.noreply.github.com>
Co-authored-by: silicons <no@you.cat>
2024-03-31 13:38:23 +02:00

200 lines
7.1 KiB
Plaintext

//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2023 Citadel Station developers. *//
/**
* Default object used during pathfinder checks
*/
GLOBAL_DATUM_INIT(generic_pathfinding_actor, /atom/movable/pathfinding_predicate, new)
/atom/movable/pathfinding_predicate
invisibility = INVISIBILITY_ABSTRACT
//pass_flags = ATOM_PASS_CLICK
//pass_flags_self = NONE
/**
* datum used for pathfinding
*
* pathfinding is a specific version of otherwise generic graph/grid searches
* we only path via cardinals due to ss13's movement treating diagonals as two cardinal moves
* pixel movement is explicitly non-supported at this time
*
* for overmaps / similar pixel-move-ish tasks, please write a new pathfinding system if you want
* accurate results.
*/
/datum/pathfinding
//* basics
/// thing trying to get a path
var/atom/movable/actor
/// start turf
var/turf/start
/// goal turf
var/turf/goal
//* options
/// how far away to the end we want to get; 0 = get ontop of the tile, 1 = get adjacent to the tile
/// keep in mind that pathing with 0 to a dense object is usually going to fail!
/// this is in byond distance, *not* pathfinding distance
/// this means that 1 tile away diagonally = 1, 2 diagonally away = 2, etc.
var/target_distance
/// if target distance is one, we require adjacency
var/require_adjacency_when_going_adjacent = TRUE
/// how far away total we can search
/// this is not distance from source we want to go, this is how far away we can *search*
/// (the former might be the case for some algorithms, though).
/// this should not be used to limit pathfinding max distance / path distance
/// this just tells the algorithm when it should give up
/// different algorithms respond differently to this.
var/max_path_length
/// context to call adjacency/distance call on
/// null = global proc
var/datum/context
/// checks if we can go to a turf
/// defaults to default density / canpass / etc checks
/// called with (turf/A, turf/B, atom/movable/actor, datum/pathfinding/pathfinding)
/// it should return the distance to that turf
var/adjacency_call = /proc/default_pathfinding_adjacency
/// checks distance from turf to target / end turf
/// defaults to just get dist
/// called with (turf/current, turf/goal)
var/heuristic_call = /proc/default_pathfinding_heuristic
/// danger flags to ignore
var/turf_path_danger_ignore = NONE
//* ss13-specific things
/// access list ; used to get through doors and other objects if set
var/list/ss13_with_access
/datum/pathfinding/New(atom/movable/actor, turf/start, turf/goal, target_distance, max_path_length)
src.actor = actor
src.start = start
src.goal = goal
src.target_distance = target_distance
src.max_path_length = max_path_length
/**
* returns raw list of nodes returned by algorithm
*/
/datum/pathfinding/proc/search()
RETURN_TYPE(/list)
CRASH("Not implemented on base type.")
/datum/pathfinding/proc/debug_log_string()
return json_encode(vars)
/datum/pathfinding_context
/datum/pathfinding_context/proc/adjacency(turf/A, turf/B, atom/movable/actor, datum/pathfinding/search)
return default_pathfinding_adjacency(A, B, actor, search)
/datum/pathfinding_context/proc/heuristic(turf/current, turf/goal)
return default_pathfinding_heuristic(current, goal)
/datum/pathfinding_context/ignoring
/// ignore typecache
var/list/turf_ignore_typecache
/// ignore instance cache
var/list/turf_ignore_cache
/datum/pathfinding_context/ignoring/adjacency(turf/A, turf/B, atom/movable/actor, datum/pathfinding/search)
if(!isnull(turf_ignore_typecache) && turf_ignore_typecache[B.type])
return FALSE
if(!isnull(turf_ignore_cache) && turf_ignore_cache[B.type])
return FALSE
return default_pathfinding_adjacency(A, B, actor, search)
//* ENSURE BELOW PROCS MATCH EACH OTHER IN THEIR PAIRS *//
//* This allows for fast default implementations while *//
//* allowing for advanced checks when a pathfinding *//
//* context is supplied. *//
/proc/default_pathfinding_adjacency(turf/A, turf/B, atom/movable/actor, datum/pathfinding/search)
// we really need to optimize this furthur
// this currently catches abstract stuff like lighting objects
// not great for performance.
if(B.density)
return FALSE
if((B.turf_path_danger & search.turf_path_danger_ignore) != B.turf_path_danger)
return FALSE
var/dir = get_dir(A, B)
if(dir & (dir - 1))
var/td1 = dir & (NORTH|SOUTH)
var/td2 = dir & (EAST|WEST)
var/turf/scan = get_step(A, td1)
if(!isnull(scan) && default_pathfinding_adjacency(A, scan, actor, search) && default_pathfinding_adjacency(scan, B, actor, search))
return TRUE
scan = get_step(A, td2)
if(!isnull(scan) && default_pathfinding_adjacency(A, scan, actor, search) && default_pathfinding_adjacency(scan, B, actor, search))
return TRUE
return FALSE
var/rdir = turn(dir, 180)
for(var/atom/movable/AM as anything in A)
if(!AM.can_pathfinding_exit(actor, dir, search))
return FALSE
for(var/atom/movable/AM as anything in B)
if(!AM.can_pathfinding_enter(actor, rdir, search))
return FALSE
return TRUE
/proc/default_pathfinding_heuristic(turf/current, turf/goal)
return max(abs(current.x - goal.x), abs(current.y - goal.y))
/proc/jps_pathfinding_adjacency(turf/A, turf/B, atom/movable/actor, datum/pathfinding/search)
// we really need to optimize this furthur
// this currently catches abstract stuff like lighting objects
// not great for performance.
if(B.density)
return FALSE
if((B.turf_path_danger & search.turf_path_danger_ignore) != B.turf_path_danger)
return FALSE
var/dir = get_dir(A, B)
if(dir & (dir - 1))
var/td1 = dir & (NORTH|SOUTH)
var/td2 = dir & (EAST|WEST)
var/turf/scan = get_step(A, td1)
if(!isnull(scan) && jps_pathfinding_adjacency(A, scan, actor, search) && jps_pathfinding_adjacency(scan, B, actor, search))
return TRUE
scan = get_step(A, td2)
if(!isnull(scan) && jps_pathfinding_adjacency(A, scan, actor, search) && jps_pathfinding_adjacency(scan, B, actor, search))
return TRUE
return FALSE
for(var/atom/movable/AM as anything in B)
if(!AM.can_pathfinding_pass(actor, search))
return FALSE
return TRUE
/**
* This is a pretty hot proc used during pathfinding to see if something
* should be able to pass through this movable in a certain direction.
*
* dir is where they're coming from
*/
/atom/movable/proc/can_pathfinding_enter(atom/movable/actor, dir, datum/pathfinding/search)
return !density /*|| (pass_flags_self & actor.pass_flags)*/
/**
* This is a pretty hot proc used during pathfinding to see if something
* should be able to pass out of this movable in a certain direction.
*
* dir is where they're going to
*/
/atom/movable/proc/can_pathfinding_exit(atom/movable/actor, dir, datum/pathfinding/search)
return !(flags & ON_BORDER) || !density /*|| (pass_flags_self & actor.pass_flags)*/
/**
* basically, non directional pathfinding enter/exit checks
*
* this is used for JPS because it does not at all play nicely with situations where one direction
* is blocked and another isn't.
*/
/atom/movable/proc/can_pathfinding_pass(atom/movable/actor, datum/pathfinding/search)
return !density /*|| (pass_flags_self & actor.pass_flags)*/