Movement Signals
Background
Traditionally, BYOND games rely on several native procs to respond to certain kinds of in-game movement:
- If one atom attempts to overlap another one,
/atom/proc/Crossis called, allowing the overlapped atom to either returnTRUEto allow the cross, orFALSEto prevent it. WhenMove()is called and an atom overlaps another one,/atom/proc/Crossedis called, allowing the overlapped atom to react to the cross. - Similarly, when an atom attempts to stop overlapping another,
/atom/proc/Uncrossis called to permit or deny it, then/atom/proc/Uncrossedis called to allow the atom to react to it. - When a movable attempts to enter a turf,
/turf/proc/Enteris called, allowing the turf to returnTRUEto permit the entry, orFALSEto deny it. When a movable succeeds in entering a turf,/turf/proc/Enteredis called, allowing the turf to react to the entry. - Similarly, when an atom attempts to exit a turf,
/turf/proc/Exitis called to permit or deny it, then/turf/proc/Exitedis called to allow the turf to react to the exit. - When an atom attempts to enter the contents of another one,
/atom/proc/Enteris called, allowing the containing atom to either returnTRUEto allow the entry, orFALSEto prevent it. When an atom successfully enters the contents of another one,/atom/proc/Enteredis called. - Similarly, when an atom attempts to exit another atom’s contents,
/atom/proc/Exitis called to permit or deny it, then/atom/proc/Exitedis called to allow the turf to react to the exit.
As one can imagine, with a lot of objects moving around in the game world, calling all of these procs can be expensive, especially if we don’t care about what happens most of the time.
In order to avoid making all of these calls, and provide more fine-grained
control over what happens on atom/turf interactions, we implement our own
version of the native /atom/movable/Move, and use signal handlers and
components to process the interactions we care about.
Signal Handlers
There are several signal handlers used to replicate the native BYOND atom crossover/entry behavior:
COMSIG_MOVABLE_PRE_MOVEis sent when trying to determine if an atom can enter a new turf. Any subscribed handler can returnCOMPONENT_MOVABLE_BLOCK_PRE_MOVEto prevent the move.COMSIG_MOVABLE_MOVEDis sent after a successful move.COMSIG_MOVABLE_CHECK_CROSSis sent when trying to determine if an atom can cross over another one. Any subscribed handler can returnCOMPONENT_BLOCK_CROSSto prevent the cross.- Similarly,
COMSIG_MOVABLE_CHECK_CROSS_OVERis sent if an atom will allow another one to cross over it–the reverse ofCOMSIG_MOVABLE_CHECK_CROSS. Again, any subscribed handler can returnCOMPONENT_BLOCK_CROSSto prevent it. COMSIG_ATOM_ENTEREDis sent if an atom enters this atom’s contents. Similarly,COMSIG_ATOM_EXITEDis sent if an atom exits this atom’s contents.
The connect_loc Element
The above signals are not appropriate for all cases. For example, it may seem
like beartraps should listen to COMSIG_MOVABLE_MOVED to see if they should
activate. However, this will fire every time a movement crossing occurs, even if
it wouldn’t normally trigger the trap, such as if the trap is being thrown and
intersects abstract lighting objects. Instead, we use COMSIG_ATOM_ENTERED with
the connect_loc element. As a refresher, COMSIG_ATOM_ENTERED fires when an
atom enters the contents of another. By using the connect_loc element, we can
have the beartrap listen for signals on the turf it’s located on. Then, if the
turf has a COMSIG_ATOM_ENTERED signal fire, we can have the beartrap respond.
Before:
/obj/item/restraints/legcuffs/beartrap/proc/Crossed(mob/living/crossed)
if(istype(crossed))
spring_trap()
After:
/obj/item/restraints/legcuffs/beartrap/Initialize(mapload)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/item/restraints/legcuffs/beartrap/proc/on_atom_entered(datum/source, mob/living/entered)
if(istype(entered))
spring_trap()
Note that the list of connections is static so that a new element instance isn’t
created for every new beartrap.
Summary
The procs /atom/movable/Cross, /atom/movable/Crossed,
/atom/movable/Uncross, and /atom/movable/Uncrossed, should not be used for
new code.
If you care about every time a movable attempts to overlap you, listen to
COMSIG_MOVABLE_PRE_MOVE, and return COMPONENT_MOVABLE_BLOCK_PRE_MOVE to
prevent it from happening.
If you want to prevent a movable from crossing you, listen to
COMSIG_MOVABLE_CHECK_CROSS and return COMPONENT_BLOCK_CROSS to prevent it. If you
want to prevent a movable from being crossed by you, listen to
COMSIG_MOVABLE_CHECK_CROSS_OVER and return COMPONENT_BLOCK_CROSS to prevent it.
If you care about an atom entering or exiting your contents, listen to
COMSIG_ATOM_ENTERED or COMSIG_ATOM_EXITED.
If you care about an atom entering or exiting your location, or any other signal
firing on your location, create a static list of signals mapped to procs and
add a /datum/element/connect_loc to yourself.