|
169 | 169 | UnarmedAttack(item_atom, TRUE, modifiers)
|
170 | 170 |
|
171 | 171 | //Standard reach turf to turf or reaching inside storage
|
172 |
| - if(CanReach(A,W)) |
| 172 | + if(A.IsReachableBy(src, W)) |
173 | 173 | if(W)
|
174 | 174 | W.melee_attack_chain(src, A, params)
|
175 | 175 | else
|
|
212 | 212 | return FALSE
|
213 | 213 |
|
214 | 214 | /**
|
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. |
217 | 224 | */
|
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) |
242 | 227 |
|
243 |
| - closed[target] = TRUE |
| 228 | + if(isnull(user)) |
| 229 | + return FALSE |
244 | 230 |
|
245 |
| - if (!target.loc) |
246 |
| - continue |
| 231 | + if(src in direct_access) |
| 232 | + return TRUE |
247 | 233 |
|
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 |
250 | 237 |
|
251 |
| - checking = next |
252 |
| - return FALSE |
| 238 | + depth-- |
| 239 | + if(depth <= 0) |
| 240 | + return FALSE |
253 | 241 |
|
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) |
256 | 249 | return reacher.Adjacent(src) || (tool && CheckToolReach(reacher, src, tool.reach))
|
257 | 250 |
|
| 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 | + |
258 | 258 | /atom/movable/proc/DirectAccess()
|
259 | 259 | return list(src, loc)
|
260 | 260 |
|
|
284 | 284 | dummy.invisibility = INVISIBILITY_ABSTRACT
|
285 | 285 | for(var/i in 1 to reach) //Limit it to that many tries
|
286 | 286 | var/turf/T = get_step(dummy, get_dir(dummy, there))
|
287 |
| - if(dummy.CanReach(there)) |
| 287 | + if(there.IsReachableBy(dummy)) |
288 | 288 | qdel(dummy)
|
289 | 289 | return TRUE
|
290 | 290 | if(!dummy.Move(T)) //we're blocked!
|
|
388 | 388 | return FALSE
|
389 | 389 |
|
390 | 390 | /mob/living/CtrlClick(mob/user, list/params)
|
391 |
| - if(!isliving(user) || !user.CanReach(src) || user.incapacitated()) |
| 391 | + if(!isliving(user) || !IsReachableBy(user) || user.incapacitated()) |
392 | 392 | return ..()
|
393 | 393 |
|
394 | 394 | if(world.time < user.next_move)
|
|
404 | 404 |
|
405 | 405 | /mob/living/carbon/human/CtrlClick(mob/user, list/params)
|
406 | 406 |
|
407 |
| - if(!ishuman(user) || !user.CanReach(src) || user.incapacitated()) |
| 407 | + if(!ishuman(user) || !IsReachableBy(user) || user.incapacitated()) |
408 | 408 | return ..()
|
409 | 409 |
|
410 | 410 | if(world.time < user.next_move)
|
|
0 commit comments