Files
VOREStation/code/datums/components/recursive_move.dm
T
Will 2194b87de0 Fixing client eye (#18577)
* signal foundation

* reset_perspective implemented

* you too

* setting mob

* no

* fix

* tweak

* remote view element

* these too

* use element

* cleanup more manual code

* fix element

* mutation signal

* handle being dropped from holders, and fix pai hud

* handle qdel

* it's a component now

* ugly holder fix

* another fix

* follow view target

* item remote view

* doc update

* unneeded

* this needs a recode to work better

* many fixes

* these are all unneeded

* almost working viewerlist remotes

* this uses component too

* this needs to die to it's item

* don't allow spamming tgui menus

* tg style args

* fixing behaviors

* fuk

* working view release from holders

* only final matters

* comment order and disposal fix

* cryotube loc fix

* no mob should reset its view every life tick

* major improvements

* still forbid z level change even if we allow moving

* this too

* don't doubledip

* qdel on self is unneeded

* wipe remote views on logout

* vore bellies need to manually clear views

* fixAI hud

* belly release fixes

* cannot use binoculars in a vore belly

* pai card can be picked up and dropped correctly

* ventcrawl fix and distracted fix

* this is better

* forcemove

* vr console fix

* use flag for this

* belly stuff

* various cleanups

* oops

* fixes statue spell

* unneeded perspective clear

* automatic instead

* continued cleanup

* that was dumb

* needed

* none of this works

* are these even needed

* lets lock down to these

* lets try to make this work

* extremely close to working

* needs to solve final pai issues

* mob eye change signal

* Revert "mob eye change signal"

This reverts commit eedd5da934.

* significant progress

* safety

* expected to be not null

* likely not needed

* don't spam component changes

* endview on logout

* accessors

* egg fixing

* Revert "egg fixing"

This reverts commit 6a54049c69.

* getting closer

* even closer

* needs type

* close...

* extremely close to working

* fixing pai stuff

* this too

* promising fixes

* docs

* this is recursive move's responsibility tbh

* unneeded now

* oops

* better decouple

* topmost check

* cleanup

* holder released from egg fix

* pai fix for reset view

* debug info

* some better pai ejection code

* better way

* unneeded

* needs to be null

* better vision restore

* use correct handling

* no longer needed

* required

* handle decouple on mecha too

* name clarity

* do not allow double dipping zoom items

* ethereal jaunt needs a full cleanup later

* fix blackscreen flicker

* remove set machine from pda

* Update code/game/objects/items.dm

* Update code/game/objects/items.dm

* Update code/game/objects/items.dm

* Update code/game/objects/items.dm

---------

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-10-12 22:18:08 -04:00

130 lines
4.9 KiB
Plaintext

/**
* Recursive move listener
* Can be added willy-nilly to anything where the COMSIG_OBSERVER_MOVE signal should also trigger on a parent moving.
* Previously there was a system where COMSIG_OBSERVER_MOVE was always recursively propogated, but that was unnecessary bloat.
*/
/datum/component/recursive_move
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS //This makes it so pretty much nothing happens when a duplicate component is created since we only use it to regenerate our parent list
var/atom/movable/holder
var/list/parents = list()
var/noparents = FALSE
/datum/component/recursive_move/RegisterWithParent()
. = ..()
holder = parent
RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(on_holder_qdel))
spawn(0) // Delayed action if our holder is spawned in nullspace and then loc = target, hopefully this catches it. VV Add item does this, for example.
if(!QDELETED(src))
setup_parents()
/datum/component/recursive_move/InheritComponent(datum/component/recursive_move/C, i_am_original)
if(!i_am_original)
return
reset_parents()
setup_parents()
/datum/component/recursive_move/proc/setup_parents()
SIGNAL_HANDLER
if(length(parents)) // safety check just incase this was called without clearing
reset_parents()
var/atom/movable/cur_parent = holder?.loc // first loc could be null
var/recursion = 0 // safety check - max iterations
while(istype(cur_parent) && (recursion < 64))
if(cur_parent == cur_parent.loc) //safety check incase a thing is somehow inside itself, cancel
log_runtime("RECURSIVE_MOVE: Parent is inside itself. ([holder]) ([holder.type])")
reset_parents()
break
if(cur_parent in parents) //safety check incase of circular contents. (A inside B, B inside C, C inside A), cancel
log_runtime("RECURSIVE_MOVE: Parent is inside a circular inventory. ([holder]) ([holder.type])")
reset_parents()
break
recursion++
parents += cur_parent
RegisterSignal(cur_parent, COMSIG_ATOM_EXITED, PROC_REF(heirarchy_changed))
RegisterSignal(cur_parent, COMSIG_QDELETING, PROC_REF(on_qdel))
cur_parent = cur_parent.loc
if(recursion >= 64) // If we escaped due to iteration limit, cancel
log_runtime("RECURSIVE_MOVE: Parent hit recursion limit. ([holder]) ([holder.type])")
reset_parents()
parents.Cut()
if(length(parents))
//Only need to watch top parent for movement. Everything is covered by Exited
RegisterSignal(parents[parents.len], COMSIG_ATOM_ENTERING, PROC_REF(top_moved))
//If we have no parents of type atom/movable then we wait to see if that changes, checking every time our holder moves.
if(!length(parents) && !noparents)
noparents = TRUE
RegisterSignal(holder, COMSIG_ATOM_ENTERING, PROC_REF(setup_parents))
if(length(parents) && noparents)
noparents = FALSE
UnregisterSignal(holder, COMSIG_ATOM_ENTERING)
/datum/component/recursive_move/proc/unregister_signals()
if(noparents) // safety check
noparents = FALSE
UnregisterSignal(holder, COMSIG_ATOM_ENTERING)
if(!length(parents))
return
for(var/atom/movable/cur_parent in parents)
UnregisterSignal(cur_parent, COMSIG_QDELETING)
UnregisterSignal(cur_parent, COMSIG_ATOM_EXITED)
UnregisterSignal(parents[parents.len], COMSIG_ATOM_ENTERING)
//Parent at top of heirarchy moved.
/datum/component/recursive_move/proc/top_moved(var/atom/movable/am, var/atom/new_loc, var/atom/old_loc)
SIGNAL_HANDLER
SEND_SIGNAL(holder, COMSIG_OBSERVER_MOVED, old_loc, new_loc)
//One of the parents other than the top parent moved.
/datum/component/recursive_move/proc/heirarchy_changed(var/atom/old_loc, var/atom/movable/am, var/atom/new_loc)
SIGNAL_HANDLER
SEND_SIGNAL(holder, COMSIG_OBSERVER_MOVED, old_loc, new_loc)
//Rebuild our list of parents
reset_parents()
setup_parents()
//Some things will move their contents on qdel so we should prepare ourselves to be moved.
//If this qdel does destroy our holder, on_holder_qdel will handle preperations for GC
/datum/component/recursive_move/proc/on_qdel()
SIGNAL_HANDLER
reset_parents()
noparents = TRUE
RegisterSignal(holder, COMSIG_ATOM_ENTERING, PROC_REF(setup_parents))
/datum/component/recursive_move/proc/on_holder_qdel()
SIGNAL_HANDLER
UnregisterSignal(holder, COMSIG_QDELETING)
reset_parents()
holder = null
qdel(src)
/datum/component/recursive_move/Destroy()
. = ..()
reset_parents()
if(holder) UnregisterSignal(holder, COMSIG_QDELETING)
holder = null
/datum/component/recursive_move/proc/reset_parents()
unregister_signals()
parents.Cut()
//the banana peel of testing stays
/obj/item/bananapeel/test
name = "banana peel of testing"
desc = "spams world log with debugging information"
/obj/item/bananapeel/test/proc/shmove(var/atom/source, var/atom/old_loc, var/atom/new_loc)
SIGNAL_HANDLER
world.log << "the [source] moved from [old_loc]([old_loc.x],[old_loc.y],[old_loc.z]) to [new_loc]([new_loc.x],[new_loc.y],[new_loc.z])"
/obj/item/bananapeel/test/Initialize(mapload)
. = ..()
AddComponent(/datum/component/recursive_move)
RegisterSignal(src, COMSIG_OBSERVER_MOVED, PROC_REF(shmove))