Skip to content

Commit 3f40954

Browse files
committed
CanReach rewrite
1 parent 20d5ab5 commit 3f40954

File tree

29 files changed

+95
-93
lines changed

29 files changed

+95
-93
lines changed

code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm

-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,6 @@
6060
#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del"
6161
///from base of atom/has_gravity(): (turf/location, list/forced_gravities)
6262
#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity"
63-
///from internal loop in atom/movable/proc/CanReach(): (list/next)
64-
#define COMSIG_ATOM_CANREACH "atom_can_reach"
65-
#define COMPONENT_ALLOW_REACH (1<<0)
6663
///for when an atom has been created through processing (atom/original_atom, list/chosen_processing_option)
6764
#define COMSIG_ATOM_CREATEDBY_PROCESSING "atom_createdby_processing"
6865
///when an atom is processed (mob/living/user, obj/item/I, list/atom/results)

code/__DEFINES/inventory.dm

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
//Inventory depth: limits how many nested storage items you can access directly.
1818
//1: stuff in mob, 2: stuff in backpack, 3: stuff in box in backpack, etc
19-
#define INVENTORY_DEPTH 3
20-
#define STORAGE_VIEW_DEPTH 2
19+
///
20+
#define REACH_DEPTH_SELF 1
21+
/// A storage depth ontop of SELF. REACH_DEPTH_STORAGE(1) would allow an item inside of a backpack you are carrying.
22+
#define REACH_DEPTH_STORAGE(level) (level + REACH_DEPTH_SELF)
23+
/// An arbitrary depth value for keeping storage UIs open on Move().
24+
#define REACH_DEPTH_STORAGE_SANITY 5
2125

2226
//ITEM INVENTORY SLOT BITMASKS
2327
/// Suit slot (armors, costumes, space suits, etc.)

code/_onclick/click.dm

+39-39
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@
169169
UnarmedAttack(item_atom, TRUE, modifiers)
170170

171171
//Standard reach turf to turf or reaching inside storage
172-
if(CanReach(A,W))
172+
if(A.IsReachableBy(src, W))
173173
if(W)
174174
W.melee_attack_chain(src, A, params)
175175
else
@@ -212,49 +212,49 @@
212212
return FALSE
213213

214214
/**
215-
* A backwards depth-limited breadth-first-search to see if the target is
216-
* logically "in" anything adjacent to us.
215+
* Returns TRUE if a movable can "Reach" this atom. This is defined as adjacency
216+
*
217+
* This is used for crafting by hitting the floor with items.
218+
* The inital use case is glass sheets breaking in to shards when the floor is hit.
219+
* Args:
220+
* * user: The movable trying to reach us.
221+
* * tool: An optional item being used.
222+
* * depth: How deep nested inside of an atom contents stack an object can be.
223+
* * direct_access: Do not override. Used for recursion.
217224
*/
218-
/atom/movable/proc/CanReach(atom/ultimate_target, obj/item/tool, view_only = FALSE)
219-
var/list/direct_access = DirectAccess()
220-
var/depth = 1 + (view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)
221-
222-
var/list/closed = list()
223-
var/list/checking = list(ultimate_target)
224-
225-
while (checking.len && depth > 0)
226-
var/list/next = list()
227-
--depth
228-
229-
for(var/atom/target in checking) // will filter out nulls
230-
if(closed[target] || isarea(target)) // avoid infinity situations
231-
continue
232-
233-
// Before we check CanBeReached() (adjacency), let's do some less expensive checks to see if we need to bother.
234-
// A target that is not a turf or on a turf is unclickable, exceptions:
235-
// * The object is directly accessible to the user (DirectAccess())
236-
// * The object has the ALWAYS_CLICKABLE trait.
237-
// * The object is inside of an atom with an associated atom_storage datum.
238-
// If any of the above are true, we THEN check adjacency.
239-
if(isturf(target) || isturf(target.loc) || (target in direct_access) || (HAS_TRAIT(target, TRAIT_SKIP_BASIC_REACH_CHECK)) || target.loc?.atom_storage)
240-
if(target.CanBeReached(src, tool))
241-
return TRUE
225+
/atom/proc/IsReachableBy(atom/movable/user, obj/item/tool, depth = INFINITY, direct_access = user.DirectAccess())
226+
SHOULD_NOT_OVERRIDE(TRUE)
242227

243-
closed[target] = TRUE
228+
if(isnull(user))
229+
return FALSE
244230

245-
if (!target.loc)
246-
continue
231+
if(src in direct_access)
232+
return TRUE
247233

248-
if(target.loc.atom_storage)
249-
next += target.loc
234+
if(isturf(loc) || isturf(src) || HAS_TRAIT(src, TRAIT_SKIP_BASIC_REACH_CHECK))
235+
if(CheckReachableAdjacency(user, tool))
236+
return TRUE
250237

251-
checking = next
252-
return FALSE
238+
depth--
239+
if(depth <= 0)
240+
return FALSE
253241

254-
/// Reciprocal function for CanReach().
255-
/atom/proc/CanBeReached(atom/movable/reacher, obj/item/tool)
242+
if(isnull(loc) || isarea(loc) || !loc.IsContainedAtomAccessible(src))
243+
return FALSE
244+
245+
return loc.IsReachableBy(user, tool, depth, direct_access)
246+
247+
/// Checks if a reacher is adjacent to us.
248+
/atom/proc/CheckReachableAdjacency(atom/movable/reacher, obj/item/tool)
256249
return reacher.Adjacent(src) || (tool && CheckToolReach(reacher, src, tool.reach))
257250

251+
/// Returns TRUE if an atom contained within our contents is reachable.
252+
/atom/proc/IsContainedAtomAccessible(atom/contained)
253+
return TRUE
254+
255+
/atom/movable/IsContainedAtomAccessible(atom/contained)
256+
return !!atom_storage
257+
258258
/atom/movable/proc/DirectAccess()
259259
return list(src, loc)
260260

@@ -284,7 +284,7 @@
284284
dummy.invisibility = INVISIBILITY_ABSTRACT
285285
for(var/i in 1 to reach) //Limit it to that many tries
286286
var/turf/T = get_step(dummy, get_dir(dummy, there))
287-
if(dummy.CanReach(there))
287+
if(there.IsReachableBy(dummy))
288288
qdel(dummy)
289289
return TRUE
290290
if(!dummy.Move(T)) //we're blocked!
@@ -388,7 +388,7 @@
388388
return FALSE
389389

390390
/mob/living/CtrlClick(mob/user, list/params)
391-
if(!isliving(user) || !user.CanReach(src) || user.incapacitated())
391+
if(!isliving(user) || !IsReachableBy(user) || user.incapacitated())
392392
return ..()
393393

394394
if(world.time < user.next_move)
@@ -404,7 +404,7 @@
404404

405405
/mob/living/carbon/human/CtrlClick(mob/user, list/params)
406406

407-
if(!ishuman(user) || !user.CanReach(src) || user.incapacitated())
407+
if(!ishuman(user) || !IsReachableBy(user) || user.incapacitated())
408408
return ..()
409409

410410
if(world.time < user.next_move)

code/_onclick/hud/screen_objects.dm

+2-2
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@
340340
if(ismecha(user.loc)) // stops inventory actions in a mech
341341
return TRUE
342342

343-
if(!user.CanReach(dropping))
343+
if(!dropping.IsReachableBy(user))
344344
return TRUE
345345

346346
var/obj/item/I = dropping
@@ -588,7 +588,7 @@
588588
if(ismecha(user.loc)) // stops inventory actions in a mech
589589
return TRUE
590590

591-
if(!user.CanReach(dropping))
591+
if(!dropping.IsReachableBy(user))
592592
return TRUE
593593

594594
var/obj/item/I = dropping

code/datums/ai/_ai_behavior.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
if(atom_basic_score < best_score)
4343
continue
4444

45-
if(pawn.CanReach(A))
45+
if(A.IsReachableBy(pawn))
4646
best_score = atom_basic_score
4747
ideal_atom = A
4848
continue

code/datums/ai/_ai_controller.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ multiple modular subtrees with behaviors
180180

181181
///Stops pawns from performing such actions that should require the target to be adjacent.
182182
var/atom/movable/moving_pawn = pawn
183-
var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || moving_pawn.CanReach(current_movement_target)
183+
var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || current_movement_target.IsReachableBy(moving_pawn)
184184
if(can_reach && current_behavior.required_distance >= get_dist(moving_pawn, current_movement_target)) ///Are we close enough to engage?
185185
if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop.
186186
ai_movement.stop_moving_towards(src)

code/datums/ai/dog/dog_behaviors.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
var/obj/item/fetch_thing = controller.blackboard[BB_FETCH_TARGET]
1212

1313
//either we can't pick it up, or we'd rather eat it, so stop trying.
14-
if(fetch_thing.anchored || !isturf(fetch_thing.loc) || IS_EDIBLE(fetch_thing) || !living_pawn.CanReach(fetch_thing))
14+
if(fetch_thing.anchored || !isturf(fetch_thing.loc) || IS_EDIBLE(fetch_thing) || !fetch_thing.IsReachableBy(living_pawn))
1515
return BEHAVIOR_PERFORM_FAILURE
1616

1717
return BEHAVIOR_PERFORM_SUCCESS

code/datums/ai/generic/generic_behaviors.dm

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
9393
var/atom/target = controller.blackboard[target_key]
9494

95-
if(!target || !pawn.CanReach(target))
95+
if(!target || !target.IsReachableBy(pawn))
9696
return BEHAVIOR_PERFORM_COOLDOWN | BEHAVIOR_PERFORM_FAILURE
9797

9898
pawn.set_combat_mode(FALSE)
@@ -118,7 +118,7 @@
118118
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
119119
var/atom/target = controller.blackboard[target_key]
120120

121-
if(!target || !pawn.CanReach(target) || !isliving(target))
121+
if(!target || !target.IsReachableBy(pawn) || !isliving(target))
122122
return BEHAVIOR_PERFORM_COOLDOWN | BEHAVIOR_PERFORM_FAILURE
123123

124124
var/mob/living/living_target = target

code/datums/ai/monkey/monkey_behaviors.dm

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
var/mob/living/victim = target.loc
7171
var/mob/living/living_pawn = controller.pawn
7272

73-
if(!istype(victim) || !living_pawn.CanReach(victim))
73+
if(!istype(victim) || !victim.IsReachableBy(living_pawn))
7474
finish_action(controller, FALSE)
7575
return
7676

@@ -82,7 +82,7 @@
8282

8383
var/success = FALSE
8484

85-
if(do_after(living_pawn, victim, MONKEY_ITEM_SNATCH_DELAY, DO_PUBLIC, display = image('icons/hud/do_after.dmi', "pickpocket")) && target && living_pawn.CanReach(victim))
85+
if(do_after(living_pawn, victim, MONKEY_ITEM_SNATCH_DELAY, DO_PUBLIC, display = image('icons/hud/do_after.dmi', "pickpocket")) && target && victim.IsReachableBy(living_pawn))
8686

8787
for(var/obj/item/I in victim.held_items)
8888
if(I == target)
@@ -188,7 +188,7 @@
188188
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE)
189189

190190
// attack with weapon if we have one
191-
if(living_pawn.CanReach(target, weapon))
191+
if(target.IsReachableBy(living_pawn, weapon))
192192
if(weapon)
193193
weapon.melee_attack_chain(living_pawn, target)
194194
else

code/datums/elements/openspace_item_click_handler.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@
2121
return
2222
var/turf/turf_above = GetAbove(target)
2323
if(turf_above?.z == user.z)
24-
INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, handle_openspace_click), turf_above, user, user.CanReach(turf_above, source), click_parameters)
24+
INVOKE_ASYNC(source, TYPE_PROC_REF(/obj/item, handle_openspace_click), turf_above, user, turf_above.IsReachableBy(user, source), click_parameters)

code/datums/status_effects/neutral.dm

+4-4
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@
135135
if(give_alert_override)
136136
give_alert_type = give_alert_override
137137

138-
if(offered && owner.CanReach(offered) && !IS_DEAD_OR_INCAP(offered) && offered.can_hold_items())
138+
if(offered && offered.IsReachableBy(owner) && !IS_DEAD_OR_INCAP(offered) && offered.can_hold_items())
139139
register_candidate(offered)
140140
else
141141
for(var/mob/living/carbon/possible_taker in orange(1, owner))
142-
if(!owner.CanReach(possible_taker) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items())
142+
if(!possible_taker.IsReachableBy(owner) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items())
143143
continue
144144
register_candidate(possible_taker)
145145

@@ -178,7 +178,7 @@
178178
/// One of our possible takers moved, see if they left us hanging
179179
/datum/status_effect/offering/proc/check_taker_in_range(mob/living/carbon/taker)
180180
SIGNAL_HANDLER
181-
if(owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker))
181+
if(taker.IsReachableBy(owner) && !IS_DEAD_OR_INCAP(taker))
182182
return
183183

184184
to_chat(taker, span_warning("You moved out of range of [owner]!"))
@@ -190,7 +190,7 @@
190190

191191
for(var/i in possible_takers)
192192
var/mob/living/carbon/checking_taker = i
193-
if(!istype(checking_taker) || !owner.CanReach(checking_taker) || IS_DEAD_OR_INCAP(checking_taker))
193+
if(!istype(checking_taker) || !checking_taker.IsReachableBy(owner) || IS_DEAD_OR_INCAP(checking_taker))
194194
remove_candidate(checking_taker)
195195

196196
/// We lost the item, give it up

code/datums/wires/_wires.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
/atom/proc/attempt_wire_interaction(mob/user)
1515
if(!wires)
1616
return WIRE_INTERACTION_FAIL
17-
if(!user.CanReach(src))
17+
if(!IsReachableBy(user))
1818
return WIRE_INTERACTION_FAIL
1919
wires.interact(user)
2020
return WIRE_INTERACTION_BLOCK

code/game/machinery/doors/airlock.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@
804804
security_level = AIRLOCK_SECURITY_PLASTEEL_O
805805
return .
806806
if(note)
807-
if(user.CanReach(src))
807+
if(IsReachableBy(user))
808808
user.visible_message(span_notice("[user] cuts down [note] from [src]."), span_notice("You remove [note] from [src]."))
809809
else //telekinesis
810810
visible_message(span_notice("[tool] cuts down [note] from [src]."))

code/game/machinery/fax_machine.dm

+3-3
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ GLOBAL_LIST_EMPTY(fax_machines)
590590
if(!paper)
591591
return
592592

593-
if(user && user.CanReach(src))
593+
if(user && IsReachableBy(user))
594594
user.put_in_hands(paper)
595595
else
596596
paper.forceMove(drop_location())
@@ -622,7 +622,7 @@ GLOBAL_LIST_EMPTY(fax_machines)
622622
if(!silent)
623623
z_flick("fax_receive", src)
624624
balloon_alert_to_viewers("removed [stored_paper]")
625-
if(user && user.CanReach(src))
625+
if(user && IsReachableBy(user))
626626
user.put_in_hands(stored_paper)
627627
else
628628
stored_paper.forceMove(drop_location())
@@ -642,7 +642,7 @@ GLOBAL_LIST_EMPTY(fax_machines)
642642
if(!silent)
643643
z_flick("fax_receive", src)
644644
balloon_alert_to_viewers("removed [received_paper]")
645-
if(user && user.CanReach(src))
645+
if(user && IsReachableBy(user))
646646
user.put_in_hands(received_paper)
647647
else
648648
received_paper.forceMove(drop_location())

code/game/objects/buckling.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@
343343
* user - The mob unbuckling buckled_mob
344344
*/
345345
/atom/movable/proc/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
346-
if(!(buckled_mob in buckled_mobs) || !user.CanReach(buckled_mob))
346+
if(!(buckled_mob in buckled_mobs) || !buckled_mob.IsReachableBy(user))
347347
return
348348
var/mob/living/M = unbuckle_mob(buckled_mob)
349349
if(M)

code/game/objects/items/dyekit.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
return
3636

3737
var/new_grad_color = input(user, "Choose a secondary hair color:", "Character Preference",human_target.grad_color) as color|null
38-
if(!new_grad_color || !user.canUseTopic(src, USE_CLOSE|USE_DEXTERITY) || !user.CanReach(target))
38+
if(!new_grad_color || !user.canUseTopic(src, USE_CLOSE|USE_DEXTERITY) || !target.IsReachableBy(user))
3939
return
4040

4141
to_chat(user, span_notice("You start applying the hair dye..."))

code/game/objects/items/stacks/medical.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
to_chat(user, span_warning("You need at least two gauzes to do this!"))
169169
return
170170
new /obj/item/stack/sheet/cloth(I.drop_location())
171-
if(user.CanReach(src))
171+
if(IsReachableBy(user))
172172
user.visible_message(span_notice("[user] cuts [src] into pieces of cloth with [I]."), \
173173
span_notice("You cut [src] into pieces of cloth with [I]."), \
174174
span_hear("You hear cutting."))

code/game/objects/structures/bedsheet_bin.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ LINEN BINS
3838
dying_key = DYE_REGISTRY_DOUBLE_BEDSHEET
3939

4040
/obj/item/bedsheet/attack_self(mob/living/user)
41-
if(!user.CanReach(src)) //No telekenetic grabbing.
41+
if(!IsReachableBy(user)) //No telekenetic grabbing.
4242
return
4343
if(!user.dropItemToGround(src))
4444
return

code/game/objects/structures/watercloset.dm

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
log_combat(user, swirlie, "swirlied (brute)")
3030
swirlie.adjustBruteLoss(5)
3131

32-
else if(cistern && !open && user.CanReach(src))
32+
else if(cistern && !open && IsReachableBy(user))
3333
if(!contents.len)
3434
to_chat(user, span_notice("The cistern is empty."))
3535
else

code/modules/antagonists/wizard/equipment/soulstone.dm

+2-2
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@
335335
/obj/item/soulstone/proc/check_menu(mob/user, obj/structure/constructshell/shell)
336336
if(!istype(user))
337337
return FALSE
338-
if(user.incapacitated() || !user.is_holding(src) || !user.CanReach(shell, src))
338+
if(user.incapacitated() || !user.is_holding(src) || !shell.IsReachableBy(user, src))
339339
return FALSE
340340
return TRUE
341341

@@ -448,7 +448,7 @@
448448
var/list/consenting_candidates = poll_ghost_candidates("Would you like to play as a Shade?", "Cultist", ROLE_CULTIST, 50, POLL_IGNORE_SHADE)
449449
if(consenting_candidates.len)
450450
chosen_ghost = pick(consenting_candidates)
451-
if(!victim || user.incapacitated() || !user.is_holding(src) || !user.CanReach(victim, src))
451+
if(!victim || user.incapacitated() || !user.is_holding(src) || !victim.IsReachableBy(user, src))
452452
return FALSE
453453
if(!chosen_ghost || !chosen_ghost.client)
454454
to_chat(user, span_danger("There were no spirits willing to become a shade."))

code/modules/art/paintings.dm

+2-2
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@
248248
/obj/item/canvas/proc/can_select_frame(mob/user)
249249
if(!istype(loc, /obj/structure/sign/painting))
250250
return FALSE
251-
if(!user?.CanReach(loc) || IS_DEAD_OR_INCAP(user))
251+
if(!loc.IsReachableBy(user) || IS_DEAD_OR_INCAP(user))
252252
return FALSE
253253
if(!last_patron || !IS_WEAKREF_OF(user?.mind, last_patron))
254254
return FALSE
@@ -634,7 +634,7 @@
634634
var/our_dir = get_dir(user, on_wall)
635635
var/check_dir = our_dir & (EAST|WEST) ? NORTH : EAST
636636
var/turf/closed/wall/second_wall = get_step(on_wall, check_dir)
637-
if(!istype(second_wall) || !user.CanReach(second_wall))
637+
if(!istype(second_wall) || !second_wall.IsReachableBy(user))
638638
to_chat(user, span_warning("You need a reachable wall to the [check_dir == EAST ? "right" : "left"] of this one to mount this frame!"))
639639
return FALSE
640640
if(check_wall_item(second_wall, our_dir, wall_external))

0 commit comments

Comments
 (0)