diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm
index 41ef191583cb..9078d9165e33 100644
--- a/code/__DEFINES/ai.dm
+++ b/code/__DEFINES/ai.dm
@@ -110,7 +110,7 @@
///The implant the AI was created from
#define BB_MOD_IMPLANT "BB_mod_implant"
///Range for a MOD AI controller.
-#define MOD_AI_RANGE 100
+#define MOD_AI_RANGE 200
///Vending machine AI controller blackboard keys
#define BB_VENDING_CURRENT_TARGET "BB_vending_current_target"
diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm
index 44b3e47ec17b..f50b1810a643 100644
--- a/code/__HELPERS/path.dm
+++ b/code/__HELPERS/path.dm
@@ -14,17 +14,17 @@
* * end: What we're trying to path to. It doesn't matter if this is a turf or some other atom, we're gonna just path to the turf it's on anyway
* * max_distance: The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite)
* * mintargetdistance: Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example.
- * * id: An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant
+ * * access: A list representing what access we have and what doors we can open.
* * simulated_only: Whether we consider turfs without atmos simulation (AKA do we want to ignore space)
* * exclude: If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf
* * skip_first: Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break movement for some creatures.
* * diagonal_safety: ensures diagonal moves won't use invalid midstep turfs by splitting them into two orthogonal moves if necessary
*/
-/proc/get_path_to(atom/movable/caller, atom/end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude, skip_first=TRUE, diagonal_safety=TRUE)
+/proc/get_path_to(atom/movable/caller, atom/end, max_distance = 30, mintargetdist, list/access, simulated_only = TRUE, turf/exclude, skip_first=TRUE, diagonal_safety=TRUE)
var/list/path = list()
// We're guarenteed that list will be the first list in pathfinding_finished's argset because of how callback handles the arguments list
var/datum/callback/await = CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(pathfinding_finished), path)
- if(!SSpathfinder.pathfind(caller, end, max_distance, mintargetdist, id, simulated_only, exclude, skip_first, diagonal_safety, await))
+ if(!SSpathfinder.pathfind(caller, end, max_distance, mintargetdist, access, simulated_only, exclude, skip_first, diagonal_safety, await))
return list()
UNTIL(length(path))
@@ -44,7 +44,7 @@
* 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) (next && !next.density && !(simulated_only && isspaceturf(next)) && !cur_turf.LinkBlockedWithAccess(next,caller, id) && (next != avoid))
+#define CAN_STEP(cur_turf, next) (next && !next.density && !(simulated_only && isspaceturf(next)) && !cur_turf.LinkBlockedWithAccess(next, caller, access) && (next != avoid))
/// Another helper macro for JPS, for telling when a node has forced neighbors that need expanding
#define STEP_NOT_HERE_BUT_THERE(cur_turf, dirA, dirB) ((!CAN_STEP(cur_turf, get_step(cur_turf, dirA)) && CAN_STEP(cur_turf, get_step(cur_turf, dirB))))
@@ -110,8 +110,8 @@
var/list/path
// general pathfinding vars/args
- /// An ID card representing what access we have and what doors we can open. Its location relative to the pathing atom is irrelevant
- var/obj/item/card/id/id
+ /// A list representing what access we have and what doors we can open.
+ var/list/access
/// How far away we have to get to the end target before we can call it quits
var/mintargetdist = 0
/// I don't know what this does vs , but they limit how far we can search before giving up on a path
@@ -127,12 +127,12 @@
/// The callback to invoke when we're done working, passing in the completed var/list/path
var/datum/callback/on_finish
-/datum/pathfind/New(atom/movable/caller, atom/goal, id, max_distance, mintargetdist, simulated_only, avoid, skip_first, diagonal_safety, datum/callback/on_finish)
+/datum/pathfind/New(atom/movable/caller, atom/goal, access, max_distance, mintargetdist, simulated_only, avoid, skip_first, diagonal_safety, datum/callback/on_finish)
src.caller = caller
end = get_turf(goal)
open = new /datum/heap(GLOBAL_PROC_REF(HeapPathWeightCompare))
sources = new()
- src.id = id
+ src.access = access
src.max_distance = max_distance
src.mintargetdist = mintargetdist
src.simulated_only = simulated_only
@@ -407,11 +407,11 @@
*
* Arguments:
* * caller: The movable, if one exists, being used for mobility checks to see what tiles it can reach
- * * ID: An ID card that decides if we can gain access to doors that would otherwise block a turf
+ * * access: A list that decides if we can gain access to doors that would otherwise block a turf
* * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space?
* * no_id: When true, doors with public access will count as impassible
*/
-/turf/proc/LinkBlockedWithAccess(turf/destination_turf, atom/movable/caller, ID, no_id = FALSE)
+/turf/proc/LinkBlockedWithAccess(turf/destination_turf, atom/movable/caller, list/access, no_id = FALSE)
if(destination_turf.x != x && destination_turf.y != y) //diagonal
var/in_dir = get_dir(destination_turf,src) // eg. northwest (1+8) = 9 (00001001)
var/first_step_direction_a = in_dir & 3 // eg. north (1+8)&3 (0000 0011) = 1 (0000 0001)
@@ -419,7 +419,7 @@
for(var/first_step_direction in list(first_step_direction_a,first_step_direction_b))
var/turf/midstep_turf = get_step(destination_turf,first_step_direction)
- var/way_blocked = midstep_turf.density || LinkBlockedWithAccess(midstep_turf,caller,ID, no_id = no_id) || midstep_turf.LinkBlockedWithAccess(destination_turf,caller,ID, no_id = no_id)
+ var/way_blocked = midstep_turf.density || LinkBlockedWithAccess(midstep_turf,caller, access, no_id = no_id) || midstep_turf.LinkBlockedWithAccess(destination_turf,caller,access, no_id = no_id)
if(!way_blocked)
return FALSE
return TRUE
@@ -432,7 +432,7 @@
// if(destination_turf.density)
// return TRUE
if(TURF_PATHING_PASS_PROC)
- if(!destination_turf.CanAStarPass(ID, actual_dir , caller, no_id = no_id))
+ if(!destination_turf.CanAStarPass(access, actual_dir , caller, no_id = no_id))
return TRUE
if(TURF_PATHING_PASS_NO)
return TRUE
@@ -444,7 +444,7 @@
continue
if(!border.density && border.can_astar_pass == CANASTARPASS_DENSITY)
continue
- if(!border.CanAStarPass(ID, actual_dir, no_id = no_id))
+ if(!border.CanAStarPass(access, actual_dir, no_id = no_id))
return TRUE
// Destination blockers check
@@ -453,6 +453,6 @@
// This is an optimization because of the massive call count of this code
if(!iter_object.density && iter_object.can_astar_pass == CANASTARPASS_DENSITY)
continue
- if(!iter_object.CanAStarPass(ID, reverse_dir, caller, no_id))
+ if(!iter_object.CanAStarPass(access, reverse_dir, caller, no_id))
return TRUE
return FALSE
diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm
index 208043b3ab36..b853a171ff88 100644
--- a/code/controllers/subsystem/movement/movement_types.dm
+++ b/code/controllers/subsystem/movement/movement_types.dm
@@ -308,7 +308,7 @@
repath_delay,
max_path_length,
minimum_distance,
- obj/item/card/id/id,
+ list/access,
simulated_only,
turf/avoid,
skip_first,
@@ -329,7 +329,7 @@
repath_delay,
max_path_length,
minimum_distance,
- id,
+ access,
simulated_only,
avoid,
skip_first,
@@ -342,8 +342,8 @@
var/max_path_length
///Minimum distance to the target before path returns
var/minimum_distance
- ///An ID card representing what access we have and what doors we can open. Kill me
- var/obj/item/card/id/id
+ ///A list representing what access we have and what doors we can open.
+ var/list/access
///Whether we consider turfs without atmos simulation (AKA do we want to ignore space)
var/simulated_only
///A perticular turf to avoid
@@ -363,24 +363,21 @@
. = ..()
on_finish_callback = CALLBACK(src, PROC_REF(on_finish_pathing))
-/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, obj/item/card/id/id, simulated_only, turf/avoid, skip_first, list/initial_path)
+/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, list/initial_path)
. = ..()
if(!.)
return
src.repath_delay = repath_delay
src.max_path_length = max_path_length
src.minimum_distance = minimum_distance
- src.id = id
+ src.access = access
src.simulated_only = simulated_only
src.avoid = avoid
src.skip_first = skip_first
- movement_path = initial_path.Copy()
-
- if(istype(id, /obj/item/card/id))
- RegisterSignal(id, COMSIG_PARENT_QDELETING, PROC_REF(handle_no_id)) //I prefer erroring to harddels. If this breaks anything consider making id info into a datum or something
+ movement_path = initial_path?.Copy()
/datum/move_loop/has_target/jps/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, obj/item/card/id/id, simulated_only, turf/avoid, skip_first, initial_path)
- if(..() && repath_delay == src.repath_delay && max_path_length == src.max_path_length && minimum_distance == src.minimum_distance && id == src.id && simulated_only == src.simulated_only && avoid == src.avoid)
+ if(..() && repath_delay == src.repath_delay && max_path_length == src.max_path_length && minimum_distance == src.minimum_distance && access ~= src.access && simulated_only == src.simulated_only && avoid == src.avoid)
return TRUE
return FALSE
@@ -394,20 +391,15 @@
movement_path = null
/datum/move_loop/has_target/jps/Destroy()
- id = null //Kill me
avoid = null
return ..()
-/datum/move_loop/has_target/jps/proc/handle_no_id()
- SIGNAL_HANDLER
- id = null
-
///Tries to calculate a new path for this moveloop.
/datum/move_loop/has_target/jps/proc/recalculate_path()
if(!COOLDOWN_FINISHED(src, repath_cooldown))
return
COOLDOWN_START(src, repath_cooldown, repath_delay)
- if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, id, simulated_only, avoid, skip_first, on_finish = on_finish_callback))
+ if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, access, simulated_only, avoid, skip_first, on_finish = on_finish_callback))
is_pathing = TRUE
SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_REPATH)
@@ -428,7 +420,8 @@
var/atom/old_loc = moving.loc
//KAPU NOTE: WE DO NOT HAVE THIS
//moving.Move(next_step, get_dir(moving, next_step), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
- moving.Move(next_step, get_dir(moving, next_step))
+ var/movement_dir = get_dir(moving, next_step)
+ moving.Move(next_step, movement_dir)
. = (old_loc != moving?.loc) ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
// this check if we're on exactly the next tile may be overly brittle for dense objects who may get bumped slightly
@@ -439,7 +432,6 @@
INVOKE_ASYNC(src, PROC_REF(recalculate_path))
return MOVELOOP_FAILURE
-
///Base class of move_to and move_away, deals with the distance and target aspect of things
/datum/move_loop/has_target/dist_bound
var/distance = 0
diff --git a/code/controllers/subsystem/pathfinder.dm b/code/controllers/subsystem/pathfinder.dm
index fbc7674f1cf2..ec4fffa6706f 100644
--- a/code/controllers/subsystem/pathfinder.dm
+++ b/code/controllers/subsystem/pathfinder.dm
@@ -35,8 +35,8 @@ SUBSYSTEM_DEF(pathfinder)
currentrun.len--
/// Initiates a pathfind. Returns true if we're good, FALSE if something's failed
-/datum/controller/subsystem/pathfinder/proc/pathfind(atom/movable/caller, atom/end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude, skip_first=TRUE, diagonal_safety=TRUE, datum/callback/on_finish)
- var/datum/pathfind/path = new(caller, end, id, max_distance, mintargetdist, simulated_only, exclude, skip_first, diagonal_safety, on_finish)
+/datum/controller/subsystem/pathfinder/proc/pathfind(atom/movable/caller, atom/end, max_distance = 30, mintargetdist, list/access=null, simulated_only = TRUE, turf/exclude, skip_first=TRUE, diagonal_safety=TRUE, datum/callback/on_finish)
+ var/datum/pathfind/path = new(caller, end, access, max_distance, mintargetdist, simulated_only, exclude, skip_first, diagonal_safety, on_finish)
if(path.start())
active_pathing += path
return TRUE
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index 1e0f3c8d55d6..e58f3a466e26 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -22,7 +22,7 @@ multiple modular subtrees with behaviors
///Stored arguments for behaviors given during their initial creation
var/list/behavior_args = list()
///Tracks recent pathing attempts, if we fail too many in a row we fail our current plans.
- var/pathing_attempts
+ var/consecutive_pathing_attempts
///Can the AI remain in control if there is a client?
var/continue_processing_when_client = FALSE
///distance to give up on target
diff --git a/code/datums/ai/dog/dog_controller.dm b/code/datums/ai/dog/dog_controller.dm
index 63cf7c7f1bc7..6ddaa5f2467e 100644
--- a/code/datums/ai/dog/dog_controller.dm
+++ b/code/datums/ai/dog/dog_controller.dm
@@ -55,7 +55,7 @@
if(!istype(simple_pawn))
return
- return simple_pawn.access_card
+ return simple_pawn.access_card.GetAccess()
/// Someone has thrown something, see if it's someone we care about and start listening to the thrown item so we can see if we want to fetch it when it lands
/datum/ai_controller/dog/proc/listened_throw(datum/source, mob/living/carbon/carbon_thrower)
diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm
index efbd52443f28..2e94a40176cf 100644
--- a/code/datums/ai/movement/_ai_movement.dm
+++ b/code/datums/ai/movement/_ai_movement.dm
@@ -4,24 +4,28 @@
var/list/moving_controllers = list()
///How many times a given controller can fail on their route before they just give up
var/max_pathing_attempts
+ var/max_path_length = AI_MAX_PATH_LENGTH
//Override this to setup the moveloop you want to use
/datum/ai_movement/proc/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance)
SHOULD_CALL_PARENT(TRUE)
- controller.pathing_attempts = 0
+ controller.consecutive_pathing_attempts = 0
controller.blackboard[BB_CURRENT_MIN_MOVE_DISTANCE] = min_distance
moving_controllers[controller] = current_movement_target
/datum/ai_movement/proc/stop_moving_towards(datum/ai_controller/controller)
- controller.pathing_attempts = 0
+ controller.consecutive_pathing_attempts = 0
moving_controllers -= controller
SSmove_manager.stop_looping(controller.pawn, SSai_movement)
/datum/ai_movement/proc/increment_pathing_failures(datum/ai_controller/controller)
- controller.pathing_attempts++
- if(controller.pathing_attempts >= max_pathing_attempts)
+ controller.consecutive_pathing_attempts++
+ if(controller.consecutive_pathing_attempts >= max_pathing_attempts)
controller.CancelActions()
+/datum/ai_movement/proc/reset_pathing_failures(datum/ai_controller/controller)
+ controller.consecutive_pathing_attempts = 0
+
///Should the movement be allowed to happen? return TRUE if it can, FALSE otherwise
/datum/ai_movement/proc/allowed_to_move(datum/move_loop/source)
SHOULD_BE_PURE(TRUE)
@@ -46,7 +50,7 @@
///Anything to do before moving; any checks if the pawn should be able to move should be placed in allowed_to_move() and called by this proc
/datum/ai_movement/proc/pre_move(datum/move_loop/source)
SIGNAL_HANDLER
- SHOULD_NOT_OVERRIDE(TRUE)
+ SHOULD_CALL_PARENT(TRUE)
var/datum/ai_controller/controller = source.extra_info
@@ -66,7 +70,10 @@
//Anything to do post movement
/datum/ai_movement/proc/post_move(datum/move_loop/source, succeeded)
SIGNAL_HANDLER
- if(succeeded != FALSE)
- return
+ SHOULD_CALL_PARENT(TRUE)
+
var/datum/ai_controller/controller = source.extra_info
+ if(succeeded != MOVELOOP_FAILURE)
+ reset_pathing_failures(controller)
+ return
increment_pathing_failures(controller)
diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm
index 3523da7ecec2..b7ebec2d1c2c 100644
--- a/code/datums/ai/movement/ai_movement_jps.dm
+++ b/code/datums/ai/movement/ai_movement_jps.dm
@@ -2,7 +2,7 @@
* This movement datum represents smart-pathing
*/
/datum/ai_movement/jps
- max_pathing_attempts = 4
+ max_pathing_attempts = 20
/datum/ai_movement/jps/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance)
. = ..()
@@ -12,10 +12,10 @@
var/datum/move_loop/loop = SSmove_manager.jps_move(moving,
current_movement_target,
delay,
- repath_delay = 2 SECONDS,
- max_path_length = AI_MAX_PATH_LENGTH,
+ repath_delay = 0.5 SECONDS,
+ max_path_length = max_path_length,
minimum_distance = controller.get_minimum_distance(),
- id = controller.get_access(),
+ access = controller.get_access(),
subsystem = SSai_movement,
extra_info = controller,
initial_path = controller.blackboard[BB_PATH_TO_USE])
@@ -28,5 +28,22 @@
SIGNAL_HANDLER
var/datum/ai_controller/controller = source.extra_info
- source.id = controller.get_access()
+ source.access = controller.get_access()
source.minimum_distance = controller.get_minimum_distance()
+
+/datum/ai_movement/jps/modsuit
+ max_path_length = MOD_AI_RANGE
+
+/datum/ai_movement/jps/modsuit/pre_move(datum/move_loop/source)
+ . = ..()
+ if(.)
+ return
+ var/datum/move_loop/has_target/jps/moveloop = source
+ if(!length(moveloop.movement_path))
+ return
+
+ var/datum/ai_controller/controller = source.extra_info
+ var/obj/item/mod = controller.pawn
+ var/angle = get_angle(mod, moveloop.movement_path[1])
+ mod.transform = matrix().Turn(angle)
+
diff --git a/code/datums/ai/objects/mod.dm b/code/datums/ai/objects/mod.dm
index ced2ffceb6fc..6af7ec3e7208 100644
--- a/code/datums/ai/objects/mod.dm
+++ b/code/datums/ai/objects/mod.dm
@@ -5,7 +5,7 @@
BB_MOD_IMPLANT,
)
max_target_distance = MOD_AI_RANGE //a little spicy but its one specific item that summons it, and it doesnt run otherwise
- ai_movement = /datum/ai_movement/jps
+ ai_movement = /datum/ai_movement/jps/modsuit
///ID card generated from the suit's required access. Used for pathing.
var/obj/item/card/id/advanced/id_card
@@ -28,7 +28,7 @@
queue_behavior(/datum/ai_behavior/mod_attach)
/datum/ai_controller/mod/get_access()
- return id_card
+ return id_card.GetAccess()
/datum/ai_behavior/mod_attach
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_MOVE_AND_PERFORM
diff --git a/code/datums/ai/oldhostile/hostile_tameable.dm b/code/datums/ai/oldhostile/hostile_tameable.dm
index e3a90870538d..8e0daf6a8a17 100644
--- a/code/datums/ai/oldhostile/hostile_tameable.dm
+++ b/code/datums/ai/oldhostile/hostile_tameable.dm
@@ -62,7 +62,7 @@
if(!istype(simple_pawn))
return
- return simple_pawn.access_card
+ return simple_pawn.access_card.GetAccess()
/datum/ai_controller/hostile_friend/proc/on_ridden_driver_move(atom/movable/movable_parent, mob/living/user, direction)
SIGNAL_HANDLER
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 2626720ecde8..fe0b1a452d6b 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -2252,7 +2252,7 @@
* For turfs this will only be used if pathing_pass_method is TURF_PATHING_PASS_PROC
*
* Arguments:
- * * ID- An ID card representing what access we have (and thus if we can open things like airlocks or windows to pass through them). The ID card's physical location does not matter, just the reference
+ * * access- A list representing what access we have (and thus if we can open things like airlocks or windows to pass through them).
* * to_dir- What direction we're trying to move in, relevant for things like directional windows that only block movement in certain directions
* * caller- The movable we're checking pass flags for, if we're making any such checks
* * no_id: When true, doors with public access will count as impassible
@@ -2260,7 +2260,7 @@
* IMPORTANT NOTE: /turf/proc/LinkBlockedWithAccess assumes that overrides of CanAStarPass will always return true if density is FALSE
* If this is NOT you, ensure you edit your can_astar_pass variable. Check __DEFINES/path.dm
**/
-/atom/proc/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/atom/proc/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
if(caller && (caller.pass_flags & pass_flags_self))
return TRUE
. = !density
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index e47f63a003ba..a085d1d10624 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1279,9 +1279,9 @@
assemblytype = initial(airlock.assemblytype)
update_appearance()
-/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/machinery/door/airlock/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
- return !density || (check_access(ID) && !locked && hasPower() && !no_id)
+ return !density || (check_access_list(access) && !locked && hasPower() && !no_id)
/obj/machinery/door/airlock/emag_act(mob/user, obj/item/card/emag/doorjack/D)
if(!operating && density && hasPower() && !(obj_flags & EMAGGED))
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 9d3e51ef15bb..ed038017af1b 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -411,7 +411,7 @@
if(!(border_dir == dir)) //Make sure looking at appropriate border
return TRUE
-/obj/machinery/door/firedoor/border_only/CanAStarPass(obj/item/card/id/ID, to_dir, no_id = FALSE)
+/obj/machinery/door/firedoor/border_only/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
return !density || (dir != to_dir)
/obj/machinery/door/firedoor/border_only/proc/on_exit(datum/source, atom/movable/leaving, direction)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 749b3cf97108..5a1412758904 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -170,8 +170,8 @@
return ZONE_BLOCKED
//used in the AStar algorithm to determinate if the turf the door is on is passable
-/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir, no_id = FALSE)
- return !density || (dir != to_dir) || (check_access(ID) && hasPower() && !no_id)
+/obj/machinery/door/window/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
+ return !density || (dir != to_dir) || (check_access_list(access) && hasPower() && !no_id)
/obj/machinery/door/window/proc/on_exit(datum/source, atom/movable/leaving, direction)
SIGNAL_HANDLER
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index f3b14d455963..e981033f3993 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -254,7 +254,7 @@
if((mover.pass_flags & PASSGRILLE) || istype(mover, /obj/projectile))
return prob(girderpasschance)
-/obj/structure/girder/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/girder/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
. = !density
if(istype(caller))
. = . || (caller.pass_flags & PASSGRILLE)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 548dec9cce7e..8f1b94060f04 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -145,7 +145,7 @@
if(!. && istype(mover, /obj/projectile))
return prob(30)
-/obj/structure/grille/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/grille/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
. = !density
if(caller)
. = . || (caller.pass_flags & PASSGRILLE)
diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm
index 4a3e3220c603..989f2b5ac6eb 100644
--- a/code/game/objects/structures/plasticflaps.dm
+++ b/code/game/objects/structures/plasticflaps.dm
@@ -63,7 +63,7 @@
return FALSE
return TRUE
-/obj/structure/plasticflaps/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/plasticflaps/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
if(isliving(caller))
if(isbot(caller))
return TRUE
@@ -77,7 +77,7 @@
var/mob/living/L = caller
var/list/grabs = L.active_grabs
for(var/obj/item/hand_item/grab/G in grabs)
- if(!CanAStarPass(ID, to_dir, G.affecting, no_id = no_id))
+ if(!CanAStarPass(access, to_dir, G.affecting, no_id = no_id))
return FALSE
return TRUE //diseases, stings, etc can pass
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index f32729316e97..552d8e15b996 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -85,7 +85,7 @@
return . || mover.throwing || mover.movement_type & (FLYING | FLOATING)
return TRUE
-/obj/structure/railing/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/railing/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
if(!(to_dir & dir))
return TRUE
return ..()
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 1acabadb5d19..680a07755687 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -428,7 +428,7 @@
/obj/structure/window/get_dumping_location()
return null
-/obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/window/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
if(!density)
return TRUE
if(fulltile || (dir == to_dir))
diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm
index 4ef61995244f..f7bef4c2d2f2 100644
--- a/code/game/turfs/open/openspace.dm
+++ b/code/game/turfs/open/openspace.dm
@@ -109,7 +109,7 @@
/turf/open/openspace/rust_heretic_act()
return FALSE
-/turf/open/openspace/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/turf/open/openspace/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
if(caller && !caller.can_z_move(DOWN, src, null , ZMOVE_FALL_FLAGS)) //If we can't fall here (flying/lattice), it's fine to path through
return TRUE
return FALSE
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 27099a445997..4dbad1336076 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -133,3 +133,12 @@
internals_slot = ITEM_SLOT_SUITSTORE
backpack_contents = null
box = null
+
+/datum/outfit/job/captain/mod/post_equip(mob/living/carbon/human/equipped, visualsOnly)
+ . = ..()
+ if(visualsOnly)
+ return
+
+ var/obj/item/mod/control/modsuit = equipped.back
+ var/obj/item/mod/module/pathfinder/module = locate() in modsuit.modules
+ module.implant.implant(equipped, silent = TRUE)
diff --git a/code/modules/mob/living/navigation.dm b/code/modules/mob/living/navigation.dm
index 47270ad6f6ce..90d5ad8cf44b 100644
--- a/code/modules/mob/living/navigation.dm
+++ b/code/modules/mob/living/navigation.dm
@@ -63,7 +63,7 @@
stack_trace("Navigate target ([navigate_target]) is not an atom, somehow.")
return
- var/list/path = get_path_to(src, navigate_target, MAX_NAVIGATE_RANGE, mintargetdist = 1, id = get_idcard(), skip_first = FALSE)
+ var/list/path = get_path_to(src, navigate_target, MAX_NAVIGATE_RANGE, mintargetdist = 1, access = get_idcard()?.GetAccess(), skip_first = FALSE)
if(!length(path))
balloon_alert(src, "no valid path with current access!")
return
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 9c7e7cf8211b..db968714264d 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -599,10 +599,8 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message = TRUE)
bot_reset() //Reset a bot before setting it to call mode.
- //For giving the bot temporary all-access. This method is bad and makes me feel bad. Refactoring access to a component is for another PR.
- var/obj/item/card/id/all_access = new /obj/item/card/id/advanced/gold/captains_spare()
- set_path(get_path_to(src, waypoint, max_distance=200, id = all_access))
- qdel(all_access)
+ var/list/access = SSid_access.accesses_by_region[REGION_ALL_STATION]
+ set_path(get_path_to(src, waypoint, max_distance=200, access = access.Copy()))
calling_ai = caller //Link the AI to the bot!
ai_waypoint = waypoint
@@ -817,12 +815,12 @@ Pass a positive integer as an argument to override a bot's default speed.
// given an optional turf to avoid
/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
check_bot_access()
- set_path(get_path_to(src, patrol_target, max_distance=120, id=access_card, exclude=avoid))
+ set_path(get_path_to(src, patrol_target, max_distance=120, access = access_card?.GetAccess(), exclude=avoid))
/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
check_bot_access()
var/datum/callback/path_complete = CALLBACK(src, PROC_REF(on_summon_path_finish))
- SSpathfinder.pathfind(src, summon_target, max_distance=150, id=access_card, exclude=avoid, on_finish = path_complete)
+ SSpathfinder.pathfind(src, summon_target, max_distance=150, access = access_card?.GetAccess(), exclude=avoid, on_finish = path_complete)
/mob/living/simple_animal/bot/proc/on_summon_path_finish(list/path)
set_path(path)
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index c7d64726c42c..46bc63e73f92 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -264,7 +264,7 @@
return
if(target && path.len == 0 && (get_dist(src,target) > 1))
- path = get_path_to(src, target, max_distance=30, mintargetdist=1, id=access_card)
+ path = get_path_to(src, target, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
mode = BOT_MOVING
if(length(path) == 0)
add_to_ignore(target)
diff --git a/code/modules/mob/living/simple_animal/bot/firebot.dm b/code/modules/mob/living/simple_animal/bot/firebot.dm
index 0f9d69186295..ed7b467bb803 100644
--- a/code/modules/mob/living/simple_animal/bot/firebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/firebot.dm
@@ -228,7 +228,7 @@
if(target_fire && (get_dist(src, target_fire) > 2))
- path = get_path_to(src, target_fire, max_distance=30, mintargetdist=1, id=access_card)
+ path = get_path_to(src, target_fire, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
mode = BOT_MOVING
if(!path.len)
soft_reset()
diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm
index bb72928e4eb4..69fde0a4dd43 100644
--- a/code/modules/mob/living/simple_animal/bot/floorbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm
@@ -248,9 +248,9 @@
if(!length(path))
if(!isturf(target))
var/turf/TL = get_turf(target)
- path = get_path_to(src, TL, max_distance=30, id=access_card,simulated_only = FALSE)
+ path = get_path_to(src, TL, max_distance=30, access = access_card?.GetAccess(), simulated_only = FALSE)
else
- path = get_path_to(src, target, max_distance=30, id=access_card,simulated_only = FALSE)
+ path = get_path_to(src, target, max_distance=30, access = access_card?.GetAccess(), simulated_only = FALSE)
if(!bot_move(target))
add_to_ignore(target)
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index fe034c118432..b363d9d991c6 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -376,10 +376,10 @@
return
if(patient && path.len == 0 && (get_dist(src,patient) > 1))
- path = get_path_to(src, patient, max_distance=30, id=access_card)
+ path = get_path_to(src, patient, max_distance=30, access = access_card?.GetAccess())
mode = BOT_MOVING
if(!path.len) //try to get closer if you can't reach the patient directly
- path = get_path_to(src, patient, max_distance=30, mintargetdist=1, id=access_card)
+ path = get_path_to(src, patient, max_distance=30, mintargetdist=1, access = access_card?.GetAccess())
if(!path.len) //Do not chase a patient we cannot reach.
soft_reset()
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 2d832b93645c..7096b1c7fd91 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -582,7 +582,7 @@
// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
- path = get_path_to(src, target, max_distance=250, id=access_card, exclude=avoid)
+ path = get_path_to(src, target, max_distance=250, access = access_card?.GetAccess(), exclude=avoid)
// sets the current destination
// signals all beacons matching the delivery code
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index f63eeac2fff2..3f9000e3a16b 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -646,6 +646,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
continue
if(istype(AM, /obj/item))
var/obj/item/I = AM
+ if(I.item_flags & ABSTRACT)
+ continue
if(I.w_class < WEIGHT_CLASS_SMALL)
item = I
else if(iscarbon(AM))
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index 2da8bb2c224b..54b02d39c153 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -412,7 +412,7 @@
/obj/item/mod/control/GetAccess()
if(ai_controller)
- return req_access.Copy()
+ return req_access?.Copy()
else
return ..()
diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm
index e974c569986f..06ce0386bc0e 100644
--- a/code/modules/mod/modules/module_pathfinder.dm
+++ b/code/modules/mod/modules/module_pathfinder.dm
@@ -1,6 +1,6 @@
///Pathfinder - Can fly the suit from a long distance to an implant installed in someone.
/obj/item/mod/module/pathfinder
- name = "MOD pathfinder module"
+ name = "\improper MOD recall module"
desc = "This module, brought to you by Nakamura Engineering, has two components. \
The first component is a series of thrusters and a computerized location subroutine installed into the \
very control unit of the suit, allowing it flight at highway speeds, \
@@ -45,15 +45,16 @@
if(!ishuman(target) || !implant)
return
if(!do_after(user, target, 1.5 SECONDS))
- balloon_alert(user, "interrupted!")
return
+
+ var/implant_cache = implant // implant() will make implant null
if(!implant.implant(target, user, deprecise_zone(user.zone_selected)))
- balloon_alert(user, "can't implant!")
return
+
if(target == user)
- to_chat(user, span_notice("You implant yourself with [implant]."))
+ to_chat(user, span_notice("You implant yourself with [implant_cache]."))
else
- target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant]."))
+ target.visible_message(span_notice("[user] implants [target]."), span_notice("[user] implants you with [implant_cache]."))
playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
@@ -64,14 +65,14 @@
return
if(!human_user.equip_to_slot_if_possible(mod, mod.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE))
return
+
mod.quick_deploy(user)
human_user.update_action_buttons(TRUE)
- balloon_alert(human_user, "[mod] attached")
playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
drain_power(use_power_cost)
/obj/item/implant/mod
- name = "MOD pathfinder implant"
+ name = "\improper MOD recall implant"
desc = "Lets you recall a MODsuit to you at any time."
actions_types = list(/datum/action/item_action/mod_recall)
implant_flags = IMPLANT_KNOWN
@@ -91,6 +92,7 @@
/obj/item/implant/mod/Destroy()
if(module?.mod?.ai_controller)
end_recall(successful = FALSE)
+
module = null
jet_icon = null
return ..()
@@ -101,22 +103,44 @@
Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"}
return dat
-/obj/item/implant/mod/proc/recall()
+/obj/item/implant/mod/proc/recall(mob/user)
if(!module?.mod)
- balloon_alert(imp_in, "no connected suit!")
+ to_chat(user, span_warning("There is no suit linked to your [src]."))
return FALSE
+
if(module.mod.open)
- balloon_alert(imp_in, "suit is open!")
+ to_chat(user, span_warning("Unable to recall MOD suit - Maintenance hatch open."))
return FALSE
+
if(module.mod.ai_controller)
- balloon_alert(imp_in, "already in transit!")
- return FALSE
- if(ismob(get_atom_on_turf(module.mod)))
- balloon_alert(imp_in, "already on someone!")
+ to_chat(user, span_warning("Unable to recall MOD suit - Already in transit."))
return FALSE
+
+
if(module.z != z || get_dist(imp_in, module.mod) > MOD_AI_RANGE)
- balloon_alert(imp_in, "too far away!")
+ to_chat(user, span_warning("Unable to recall MOD suit - Out of range."))
return FALSE
+
+ if(!isturf(module.mod.loc))
+ if(!isliving(module.mod.loc))
+ to_chat(user, span_warning("Unable to recall MOD suit - Suit is inside container."))
+ return FALSE
+
+ var/mob/living/L = module.mod.loc
+ if(L.is_holding(module.mod))
+ if(L.dropItemToGround(module.mod))
+ L.visible_message(span_alert("[module.mod] suddenly activates, flying out from [L]'s grasp!"))
+ else
+ to_chat(user, span_warning("Unable to recall MOD suit - Suit is inside container."))
+ return FALSE
+
+ else if(L.get_item_by_slot(ITEM_SLOT_BACK))
+ to_chat(user, span_warning("Unable to recall MOD suit - Suit is being worn."))
+ return FALSE
+ else
+ to_chat(user, span_warning("Unable to recall MOD suit - Suit is inside container."))
+ return FALSE
+
var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
module.mod.ai_controller = mod_ai
mod_ai.current_movement_target = imp_in
@@ -128,12 +152,14 @@
animate(module.mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
module.mod.add_overlay(jet_icon)
RegisterSignal(module.mod, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
- balloon_alert(imp_in, "suit recalled")
+
+ to_chat(user, "[src] pings, \"MOD suit recalled.\"")
return TRUE
/obj/item/implant/mod/proc/end_recall(successful = TRUE)
if(!module?.mod)
return
+
QDEL_NULL(module.mod.ai_controller)
module.mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
REMOVE_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
@@ -142,14 +168,14 @@
module.mod.transform = matrix()
UnregisterSignal(module.mod, COMSIG_MOVABLE_MOVED)
if(!successful)
- balloon_alert(imp_in, "suit lost connection!")
+ to_chat(imp_in, span_warning("[src] buzzes, \"MOD suit stopped travelling due to obstruction.\""))
/obj/item/implant/mod/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
SIGNAL_HANDLER
- var/matrix/mod_matrix = matrix()
- mod_matrix.Turn(get_angle(source, imp_in))
- source.transform = mod_matrix
+ var/distance = get_dist(source, imp_in)
+ if(!(distance %% 20))
+ to_chat(imp_in, "[src] pings, \"Suit is [distance] meters away.\"")
/datum/action/item_action/mod_recall
name = "Recall MOD"
@@ -173,7 +199,8 @@
return
var/obj/item/implant/mod/implant = target
if(!COOLDOWN_FINISHED(src, recall_cooldown))
- implant.balloon_alert(implant.imp_in, "on cooldown!")
+ to_chat(implant.imp_in, span_warning("[implant] is on cooldown."))
return
- if(implant.recall())
+
+ if(implant.recall(implant.imp_in))
COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
diff --git a/code/modules/tables/tables_racks.dm b/code/modules/tables/tables_racks.dm
index 41663aa3b298..03c7c6e59b37 100644
--- a/code/modules/tables/tables_racks.dm
+++ b/code/modules/tables/tables_racks.dm
@@ -199,7 +199,7 @@
if(flipped == TRUE && !(border_dir & dir))
return TRUE
-/obj/structure/table/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller, no_id = FALSE)
+/obj/structure/table/CanAStarPass(list/access, to_dir, atom/movable/caller, no_id = FALSE)
. = !density
if(caller)
. = . || (caller.pass_flags & PASSTABLE) || (flipped == TRUE && (dir != to_dir))
diff --git a/code/modules/wiremod/components/action/pathfind.dm b/code/modules/wiremod/components/action/pathfind.dm
index ad602c32c6ba..26a4d22f09b9 100644
--- a/code/modules/wiremod/components/action/pathfind.dm
+++ b/code/modules/wiremod/components/action/pathfind.dm
@@ -98,7 +98,7 @@
TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
old_dest = destination
- path = get_path_to(src, destination, max_range, id=path_id)
+ path = get_path_to(src, destination, max_range, access = path_id:GetAccess())
if(length(path) == 0 || !path)// Check if we can even path there
next_turf = null
failed.set_output(COMPONENT_SIGNAL)