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/Cross
is called, allowing the overlapped atom to either returnTRUE
to allow the cross, orFALSE
to prevent it. WhenMove()
is called and an atom overlaps another one,/atom/proc/Crossed
is called, allowing the overlapped atom to react to the cross. - Similarly, when an atom attempts to stop overlapping another,
/atom/proc/Uncross
is called to permit or deny it, then/atom/proc/Uncrossed
is called to allow the atom to react to it. - When a movable attempts to enter a turf,
/turf/proc/Enter
is called, allowing the turf to returnTRUE
to permit the entry, orFALSE
to deny it. When a movable succeeds in entering a turf,/turf/proc/Entered
is called, allowing the turf to react to the entry. - Similarly, when an atom attempts to exit a turf,
/turf/proc/Exit
is called to permit or deny it, then/turf/proc/Exited
is called to allow the turf to react to the exit. - When an atom attempts to enter the contents of another one,
/atom/proc/Enter
is called, allowing the containing atom to either returnTRUE
to allow the entry, orFALSE
to prevent it. When an atom successfully enters the contents of another one,/atom/proc/Entered
is called. - Similarly, when an atom attempts to exit another atom’s contents,
/atom/proc/Exit
is called to permit or deny it, then/atom/proc/Exited
is 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_MOVE
is sent when trying to determine if an atom can enter a new turf. Any subscribed handler can returnCOMPONENT_MOVABLE_BLOCK_PRE_MOVE
to prevent the move.COMSIG_MOVABLE_MOVED
is sent after a successful move.COMSIG_MOVABLE_CHECK_CROSS
is sent when trying to determine if an atom can cross over another one. Any subscribed handler can returnCOMPONENT_BLOCK_CROSS
to prevent the cross.- Similarly,
COMSIG_MOVABLE_CHECK_CROSS_OVER
is 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_CROSS
to prevent it. COMSIG_ATOM_ENTERED
is sent if an atom enters this atom’s contents. Similarly,COMSIG_ATOM_EXITED
is 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.