Updates the proximity monitor component (#15836)

* prox component update

* mochi review

* bug fix, makes proximity sensors always active

* GC fixes and removes an unused proc

* Disposal fixes from TM

* fixes runtimes when objects created in nullspace runtimes, fixes portable flasher

* a fresh perspective

* lewcc review

* adds comment about direct loc setting over forceMove
This commit is contained in:
SteelSlayer
2022-04-30 09:50:36 -05:00
committed by GitHub
parent da5ef366f1
commit e49b016df4
16 changed files with 524 additions and 145 deletions
+2 -1
View File
@@ -268,7 +268,8 @@
///called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
#define COMSIG_MOVABLE_DISPOSING "movable_disposing"
///called when the movable is removed from a disposal holder object: /obj/structure/disposalpipe/proc/expel(): (obj/structure/disposalholder/H, turf/T, direction)
#define COMSIG_MOVABLE_EXIT_DISPOSALS "movable_exit_disposals"
// /datum/mind signals
-21
View File
@@ -1966,27 +1966,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return TRUE
return FALSE
/**
* Proc which gets all adjacent turfs to `src`, including the turf that `src` is on.
*
* This is similar to doing `for(var/turf/T in range(1, src))`. However it is slightly more performant.
* Additionally, the above proc becomes more costly the more atoms there are nearby. This proc does not care about that.
*/
/atom/proc/get_all_adjacent_turfs()
var/turf/src_turf = get_turf(src)
var/list/_list = list(
src_turf,
get_step(src_turf, NORTH),
get_step(src_turf, NORTHEAST),
get_step(src_turf, NORTHWEST),
get_step(src_turf, SOUTH),
get_step(src_turf, SOUTHEAST),
get_step(src_turf, SOUTHWEST),
get_step(src_turf, EAST),
get_step(src_turf, WEST)
)
return _list
// Check if the source atom contains another atom
/atom/proc/contains(atom/location)
if(!location)
@@ -0,0 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(fields)
name = "Fields"
wait = 2
priority = FIRE_PRIORITY_FIELDS
flags = SS_KEEP_TIMING | SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+22
View File
@@ -530,6 +530,28 @@
if(C.can_transfer)
target.TakeComponent(comps)
/**
* Transfer a single component from the source datum, to the target.
*
* Arguments:
* * datum/target - the target to move the component to
* * component_instance_or_typepath - either an already created component, or a component typepath
*/
/datum/proc/TransferComponent(datum/target, component_instance_or_typepath)
if(!datum_components)
return
// If the proc was fed a typepath.
var/datum/component/comp = datum_components[component_instance_or_typepath]
if(comp?.can_transfer)
target.TakeComponent(comp)
return
// if the proc was fed a component instance.
for(var/component in datum_components)
var/datum/component/C = datum_components[component]
if(istype(C, component_instance_or_typepath) && C.can_transfer)
target.TakeComponent(C)
return
/**
* Return the object that is the host of any UI's that this component has
*/
+468 -86
View File
@@ -1,139 +1,521 @@
/**
* # Proximity monitor component
* # Basic Proximity Monitor
*
* Attaching this component to an atom means that the atom will be able to detect mobs/objs moving within a 1 tile of it.
* Attaching this component to an atom means that the atom will be able to detect mobs or objects moving within a specified radius of it.
*
* The component creates several `obj/effect/abstract/proximity_checker` objects, which follow the parent atom around, always making sure it's at the center.
* When something crosses one of these `proximiy_checker`s, the parent has the `HasProximity()` proc called on it, with the crossing mob/obj as the argument.
* The component creates several [/obj/effect/abstract/proximity_checker] objects, which follow the `parent` AKA `hasprox_receiver` around, always making sure it's at the center.
* When something crosses one of these proximiy checkers, the `hasprox_receiver` will have the `HasProximity()` proc called on it, with the crossing mob/obj as the argument.
*/
/datum/component/proximity_monitor
var/atom/owner
/// A list of currently created `/obj/effect/abstract/proximity_checker`s in use with this component.
can_transfer = TRUE
var/name = "Proximity detection field"
/// The primary atom the component is attached to and that will be receiving `HasProximity()` calls. Same as the `parent`.
var/atom/hasprox_receiver
/**
* A list which contains references to movable atoms which the `hasprox_receiver` has moved into.
* Used to handle complex situations where the receiver is nested several layers deep into an object.
* For example: inside of a box, that's inside of a bag, which is worn on a human. In this situation there are 3 locations we need to listen to for movement.
*/
var/list/nested_receiver_locs
/// The radius of the field, in tiles.
var/radius
/// A list of currently created [/obj/effect/abstract/proximity_checker] in use with this component.
var/list/proximity_checkers
/// The type of checker object that should be used for the main field.
var/field_checker_type = /obj/effect/abstract/proximity_checker
/// Should the parent always detect proximity and update the field on movement, even if it's not on a turf?
var/always_active
/datum/component/proximity_monitor/Initialize()
/datum/component/proximity_monitor/Initialize(_radius = 1, _always_active = FALSE)
. = ..()
if(!isatom(parent))
if(!ismovable(parent) && !isturf(parent)) // No areas or datums allowed.
return COMPONENT_INCOMPATIBLE
owner = parent
ASSERT(_radius >= 1)
hasprox_receiver = parent
radius = _radius
always_active = _always_active
nested_receiver_locs = list()
create_prox_checkers()
if(isturf(hasprox_receiver.loc))
toggle_checkers(TRUE)
else if(always_active)
toggle_checkers(TRUE)
else
toggle_checkers(FALSE)
/datum/component/proximity_monitor/Destroy(force, silent)
hasprox_receiver = null
nested_receiver_locs.Cut()
QDEL_LIST(proximity_checkers)
owner = null
return ..()
/datum/component/proximity_monitor/RegisterWithParent()
. = ..()
if(ismovable(parent))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/HandleMove)
if(ismovable(hasprox_receiver))
RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_MOVED, .proc/on_receiver_move)
RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_DISPOSING, .proc/on_disposal_enter)
RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_EXIT_DISPOSALS, .proc/on_disposal_exit)
map_nested_locs()
/datum/component/proximity_monitor/UnregisterFromParent()
. = ..()
if(ismovable(parent))
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
if(ismovable(hasprox_receiver))
UnregisterSignal(hasprox_receiver, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_DISPOSING, COMSIG_MOVABLE_EXIT_DISPOSALS))
clear_nested_locs()
/**
* Called when the `parent` receives the `COMSIG_MOVABLE_MOVED` signal, which occurs when it `Move()`s
*
* Code is only ran when there is no `Dir`, which occurs when the parent is teleported, gets placed into a storage item, dropped, or picked up.
* Normal movement, for example moving 1 tile to the west, is handled by the `proximity_checker` objects.
* Called when the `hasprox_receiver` moves.
*
* Arguments:
* * source - this will be the `parent`
* * OldLoc - the location the parent just moved from
* * Dir - the direction the parent just moved in
* * forced - if we were forced to move
* * datum/source - this will be the `hasprox_receiver`
* * atom/old_loc - the location the receiver just moved from
* * dir - the direction the reciever just moved in
*/
/datum/component/proximity_monitor/proc/HandleMove(datum/source, atom/OldLoc, Dir, forced)
if(!Dir) // No dir means the parent teleported, or moved in a non-standard way like getting placed into disposals, onto a table, dropped, picked up, etc.
recenter_prox_checkers()
/datum/component/proximity_monitor/proc/on_receiver_move(datum/source, atom/old_loc, dir)
SIGNAL_HANDLER
// It was just a normal tile-based move, so we return here.
if(dir)
move_prox_checkers(dir)
return
// Moving onto a turf.
if(!isturf(old_loc) && isturf(hasprox_receiver.loc))
toggle_checkers(TRUE)
clear_nested_locs()
// Moving into an object.
else if(always_active)
toggle_checkers(TRUE)
map_nested_locs()
// The receiver moved into something, but isn't `always_active`, so deactivate the checkers.
else
toggle_checkers(FALSE)
recenter_prox_checkers()
/**
* Called in Initialize(). Generates a set of `/obj/effect/abstract/proximity_checker` objects around the parent, and registers signals to them.
* Called when an atom in `nested_receiver_locs` list moves, if one exists.
*
* Arguments:
* * atom/moved_atom - one of the atoms in `nested_receiver_locs`
* * atom/old_loc - the location `moved_atom` just moved from
* * dir - the direction `moved_atom` just moved in
*/
/datum/component/proximity_monitor/proc/on_nested_loc_move(atom/moved_atom, atom/old_loc, dir)
SIGNAL_HANDLER
// It was just a normal tile-based move, so we return here.
if(dir)
move_prox_checkers(dir)
return
// Moving onto a turf.
if(!isturf(old_loc) && isturf(moved_atom.loc))
toggle_checkers(TRUE)
map_nested_locs()
recenter_prox_checkers()
/**
* Called when the receiver or an atom in the `nested_receiver_locs` list moves into a disposals holder object.
*
* This proc recieves arguments, but they aren't needed.
*/
/datum/component/proximity_monitor/proc/on_disposal_enter(datum/source)
SIGNAL_HANDLER
toggle_checkers(FALSE)
/**
* Called when the receiver or an atom in the `nested_receiver_locs` list moves out of a disposals holder object.
*
* This proc recieves arguments, but they aren't needed.
*/
/datum/component/proximity_monitor/proc/on_disposal_exit(datum/source)
SIGNAL_HANDLER
toggle_checkers(TRUE)
/**
* Registers signals to any nested locations the `hasprox_receiver` is in, excluding turfs, so they can be monitored for movement.
*/
/datum/component/proximity_monitor/proc/map_nested_locs()
clear_nested_locs()
var/atom/loc_to_check = hasprox_receiver.loc
while(loc_to_check && !isturf(loc_to_check))
if(loc_to_check in nested_receiver_locs)
continue
nested_receiver_locs += loc_to_check
RegisterSignal(loc_to_check, COMSIG_MOVABLE_MOVED, .proc/on_nested_loc_move)
RegisterSignal(loc_to_check, COMSIG_MOVABLE_DISPOSING, .proc/on_disposal_enter)
RegisterSignal(loc_to_check, COMSIG_MOVABLE_EXIT_DISPOSALS, .proc/on_disposal_exit)
loc_to_check = loc_to_check.loc
/**
* Removes and unregisters signals from all objects currently in the `nested_receiver_locs` list.
*/
/datum/component/proximity_monitor/proc/clear_nested_locs()
for(var/nested_loc in nested_receiver_locs)
UnregisterSignal(nested_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_DISPOSING, COMSIG_MOVABLE_EXIT_DISPOSALS))
nested_receiver_locs = list()
/**
* Relays basic directional movement from the `hasprox_receiver` or `host`, to all objects in the `proximity_checkers` list.
*
* Arguments:
* * move_dir - the direction the checkers should move in
*/
/datum/component/proximity_monitor/proc/move_prox_checkers(move_dir)
for(var/obj/P as anything in proximity_checkers)
P.loc = get_step(P, move_dir)
/**
* Update all of the component's proximity checker's to either become active or not active.
*
* Arguments:
* * new_active - the value to be assigned to the proximity checker's `active` variable
*/
/datum/component/proximity_monitor/proc/toggle_checkers(new_active)
for(var/obj/effect/abstract/proximity_checker/P as anything in proximity_checkers)
P.active = new_active
/**
* Specifies a new radius for the field. Creates or deletes proximity_checkers accordingly.
*
* This proc *can* have a high cost due to the `new`s and `qdel`s of the proximity checkers, depending on the number of calls you need to make to it.
*
* Arguments:
* new_radius - the new value that `proximity_radius` should be set to.
*/
/datum/component/proximity_monitor/proc/set_radius(new_radius)
ASSERT(new_radius >= 1)
var/new_field_size = (1 + new_radius * 2) ** 2
var/old_field_size = length(proximity_checkers)
radius = new_radius
// Radius is decreasing.
if(new_field_size < old_field_size)
for(var/i in 1 to (old_field_size - new_field_size))
qdel(proximity_checkers[length(proximity_checkers)]) // Pop the last entry.
// Radius is increasing.
else
var/turf/parent_turf = get_turf(parent)
for(var/i in 1 to (new_field_size - old_field_size))
create_single_prox_checker(parent_turf)
recenter_prox_checkers()
/**
* Creates a single proximity checker object, at the given location and of the given type. Adds it to the proximity checkers list.
*
* Arguments:
* * turf/T - the turf the checker should be created on
* * checker_type - the type of [/obj/item/abstract/proximity_checker] to create
*/
/datum/component/proximity_monitor/proc/create_single_prox_checker(turf/T, checker_type = field_checker_type)
var/obj/effect/abstract/proximity_checker/P = new checker_type(T, src)
proximity_checkers += P
return P
/**
* Called in Initialize(). Generates a set of [proximity checker][/obj/effect/abstract/proximity_checker] objects around the parent.
*/
/datum/component/proximity_monitor/proc/create_prox_checkers()
proximity_checkers = list()
for(var/turf/T in range(1, get_turf(parent)))
var/obj/effect/abstract/proximity_checker/P = new(T, parent)
proximity_checkers += P
// Basic movement for the proximity_checker objects. The objects will move 1 tile in the direction the parent just moved.
P.RegisterSignal(parent, COMSIG_MOVABLE_MOVED, /obj/effect/abstract/proximity_checker/.proc/HandleMove)
LAZYINITLIST(proximity_checkers)
var/turf/parent_turf = get_turf(parent)
// For whatever reason their turf is null. Create the checkers in nullspace for now. When the parent moves to a valid turf, they can be recenetered.
if(!parent_turf)
// Since we can't use `in range` in nullspace, we need to calculate the number of checkers to create with the below formula.
var/checker_amount = (1 + radius * 2) ** 2
for(var/i in 1 to checker_amount)
create_single_prox_checker(null)
return
for(var/T in RANGE_TURFS(radius, parent_turf))
create_single_prox_checker(T)
/**
* Re-centers all of the parent's `proximity_checker`s around its current location.
* Re-centers all of the `proximity_checker`s around the parent's current location.
*/
/datum/component/proximity_monitor/proc/recenter_prox_checkers()
var/list/prox_checkers = owner.get_all_adjacent_turfs()
for(var/checker in proximity_checkers)
var/obj/effect/abstract/proximity_checker/P = checker
P.loc = pick_n_take(prox_checkers)
var/turf/parent_turf = get_turf(parent)
if(!parent_turf)
toggle_checkers(FALSE)
return // Need a sanity check here for certain situations like objects moving into disposal holders. Their turf will be null in these cases.
var/index = 1
for(var/T in RANGE_TURFS(radius, parent_turf))
var/obj/checker = proximity_checkers[index++]
checker.loc = T
/**
* # Proximity checker abstract object
* # Advanced Proximity Monitor
*
* Inteded for use with the proximity checker component (/datum/component/proximity_monitor).
* This component functions similar to the basic version, however it has some extra features:
*
* First of all, if the field radius is more than 1 tile, you have the option to make a distinction between inner proximity checkers, versus ones along the edge.
* You can specifiy which type of [/obj/effect/abstract/proximity_checker] objects you want to use for both inner, and edge checkers.
*
* Secondly, the advanced proximity monitor has the ability to use processing (the `process` proc). This is optional however.
* Each proximity checker object can process itself or other things on it's turf as needed. It's up to you on how you want to use it.
* Inner and edge checkers can process thing seperately. You can turn off processing for field checkers and have only edge checkers process, and vice versa.
*/
/datum/component/proximity_monitor/advanced
name = "Advanced energy field"
field_checker_type = /obj/effect/abstract/proximity_checker/advanced/inner_field
/// The type of checker object that should be used for the field edges.
var/edge_checker_type = /obj/effect/abstract/proximity_checker/advanced/edge_field
/// Make a distinction between edge checkers and field checkers seperately.
var/uses_edge_checkers = FALSE
/// Do any of the proximity_checker objects need to process things sitting on their tile?
var/requires_processing = FALSE
/// Should the main field checkers process things on their tile?
var/process_field_checkers = FALSE
/// Should the edge field checkers process things on their tile?
var/process_edge_checkers = FALSE
/// A list of proximity_checkers in the inner field. Excludes checkers on the edge of the field.
var/list/field_checkers
/// A list of proximity_checkers on the edge of the field.
var/list/edge_checkers
/datum/component/proximity_monitor/advanced/Initialize(_radius = 1, _always_active = FALSE)
. = ..()
if(requires_processing)
START_PROCESSING(SSfields, src)
/datum/component/proximity_monitor/advanced/Destroy(force, silent)
STOP_PROCESSING(SSfields, src)
QDEL_LIST(field_checkers)
QDEL_LIST(edge_checkers)
return ..()
/datum/component/proximity_monitor/advanced/create_prox_checkers()
if(!uses_edge_checkers)
..() // We don't need to make a distinction between field and edge checkers, use the parent.
if(process_field_checkers)
field_checkers = proximity_checkers.Copy() // Still allows for field checkers to use processing.
return
LAZYINITLIST(proximity_checkers)
LAZYINITLIST(field_checkers)
LAZYINITLIST(edge_checkers)
var/turf/parent_turf = get_turf(parent)
for(var/T in RANGE_TURFS(radius, parent_turf))
if(get_dist(T, parent_turf) == radius)
edge_checkers += create_single_prox_checker(T, edge_checker_type)
continue
field_checkers += create_single_prox_checker(T)
/datum/component/proximity_monitor/advanced/recenter_prox_checkers()
if(!uses_edge_checkers)
return ..() // We don't need to make a distinction between field and edge checkers, use the parent.
var/turf/parent_turf = get_turf(parent)
var/inner_index = 1
var/edge_index = 1
for(var/T in RANGE_TURFS(radius, parent_turf))
var/obj/checker
if(get_dist(T, parent_turf) == radius) // If it's at this distance, it's on the edge of the field.
checker = edge_checkers[edge_index++]
checker.loc = T
continue
checker = field_checkers[inner_index++]
checker.loc = T
/datum/component/proximity_monitor/advanced/process()
if(process_field_checkers)
for(var/checker in field_checkers)
process_inner_checker(checker)
if(process_edge_checkers)
for(var/checker in field_checkers)
process_edge_checker(checker)
/**
* Base proc. All processing-related actions associated with inner proximity checkers should go here.
*
* Arguments:
* * obj/effect/abstract/proximity_checker/advanced/inner_field/F - the proximity checker to process
*/
/datum/component/proximity_monitor/advanced/proc/process_inner_checker(obj/effect/abstract/proximity_checker/advanced/inner_field/F)
return
/**
* Base proc. All processing-related actions associated with edge proximity checkers should go here.
*
* Arguments:
* * obj/effect/abstract/proximity_checker/advanced/edge_field/F - the proximity checker to process
*/
/datum/component/proximity_monitor/advanced/proc/process_edge_checker(obj/effect/abstract/proximity_checker/advanced/edge_field/F)
return
/**
* Base proc. Checks if `AM` can pass the inner field checker.
*
* Arguments:
* * atom/movable/AM - the atom trying to pass the inner field checker object
* * obj/effect/abstract/proximity_checker/advanced/inner_field/F - the proximity checker object `AM` is trying to pass
* * turf/entering - the turf `AM` is entering from
*/
/datum/component/proximity_monitor/advanced/proc/inner_field_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/inner_field/F, turf/entering)
return TRUE
/**
* Base proc. Called when something crosses an inner field checker.
*
* Arguments:
* * atom/movable/AM - the atom crossing the inner field checker object
* * obj/effect/abstract/proximity_checker/advanced/inner_field/F - the proximity checker object `AM` getting crossed
*/
/datum/component/proximity_monitor/advanced/proc/inner_field_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/inner_field/F)
return TRUE
/**
* Base proc. Called when something uncrosses an inner field checker.
*
* Arguments:
* * atom/movable/AM - the atom uncrossing the inner field checker object
* * obj/effect/abstract/proximity_checker/advanced/inner_field/F - the proximity checker object `AM` getting uncrossed
*/
/datum/component/proximity_monitor/advanced/proc/inner_field_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/inner_field/F)
return TRUE
/**
* Base proc. Checks if `AM` can pass the edge field checker.
*
* Arguments:
* * atom/movable/AM - the atom trying to pass the edge field checker object
* * obj/effect/abstract/proximity_checker/advanced/edge_field/F - the proximity checker object `AM` is trying to pass
* * turf/entering - the turf `AM` is entering from
*/
/datum/component/proximity_monitor/advanced/proc/edge_field_canpass(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/edge_field/F, turf/entering)
return TRUE
/**
* Base proc. Called when something crosses an edge field checker.
*
* Arguments:
* * atom/movable/AM - the atom crossing the edge field checker object
* * obj/effect/abstract/proximity_checker/advanced/edge_field/F - the proximity checker object `AM` getting crossed
*/
/datum/component/proximity_monitor/advanced/proc/edge_field_crossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/edge_field/F)
return TRUE
/**
* Base proc. Called when something uncrosses an edge field checker.
*
* Arguments:
* * atom/movable/AM - the atom uncrossing the edge field checker object
* * obj/effect/abstract/proximity_checker/advanced/edge_field/F - the proximity checker object `AM` getting uncrossed
*/
/datum/component/proximity_monitor/advanced/proc/edge_field_uncrossed(atom/movable/AM, obj/effect/abstract/proximity_checker/advanced/edge_field/F)
return TRUE
/**
* # Basic Proximity Checker
*
* Inteded for use with the proximity checker component [/datum/component/proximity_monitor].
* Whenever a movable atom crosses this object, it calls `HasProximity()` on the object which is listening for proximity (`hasprox_receiver`).
*
* Because these objects try to make the smallest footprint possible, when these objects move **they should use direct `loc` setting only, and not `forceMove`!**
* The overhead for forceMove is very unnecessary, because for example turfs and areas don't need to know when these have entered or exited them, etc.
* These are invisible objects who's sole purpose is to simply inform the receiver that something has moved within X tiles of the it.
*/
/obj/effect/abstract/proximity_checker
name = "Proximity checker"
name = "proximity checker"
/// The component that this object is in use with, and that will receive `HasProximity()` calls.
var/datum/component/proximity_monitor/monitor
/// Whether or not the proximity checker is listening for things crossing it.
var/active
/// The linked atom which has the proximity_monitor component, and will recieve the `HasProximity()` calls.
var/atom/hasprox_receiver
// If this object is initialized without a `_hasprox_receiver` arg, it is qdel'd.
/obj/effect/abstract/proximity_checker/Initialize(mapload, atom/_hasprox_receiver)
if(_hasprox_receiver)
hasprox_receiver = _hasprox_receiver
RegisterSignal(hasprox_receiver, COMSIG_PARENT_QDELETING, .proc/OnParentDeletion)
if(isturf(hasprox_receiver.loc)) // if the reciever is inside a locker/crate/etc, they don't detect proximity
active = TRUE
else
stack_trace("/obj/effect/abstract/proximity_checker created without a receiver")
return INITIALIZE_HINT_QDEL
return ..()
/obj/effect/abstract/proximity_checker/Initialize(mapload, datum/component/proximity_monitor/P)
. = ..()
monitor = P
/obj/effect/abstract/proximity_checker/Destroy()
hasprox_receiver = null
monitor.proximity_checkers -= src
monitor = null
return ..()
/**
* Called when the `hasprox_receiver` receives the `COMSIG_PARENT_QDELETING` signal. When the receiver is deleted, so is this object.
* Called when something crossed over the proximity_checker. Notifies the `hasprox_receiver` it has proximity with something.
*
* Arugments:
* * source - this will be the `hasprox_receiver`
* * force - the force flag taken from the qdel proc currently running on `hasprox_receiver`
*/
/obj/effect/abstract/proximity_checker/proc/OnParentDeletion(datum/source, force = FALSE)
qdel(src)
/**
* Something crossed over the proximity_checker. Notify the `hasprox_receiver` it has proximity with something. Only fires if the checker is `active`.
* Arguments:
* * atom/movable/AM - the atom crossing the proximity checker
* * oldloc - the location `AM` used to be at
*/
/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM, oldloc)
set waitfor = FALSE
if(active)
hasprox_receiver.HasProximity(AM)
. = ..()
if(active && AM != monitor.hasprox_receiver && !(AM in monitor.nested_receiver_locs))
monitor.hasprox_receiver.HasProximity(AM)
/**
* Moves the proximity_checker 1 tile in the `Dir` direction.
* # Advanced Proximity Checker
*
* If `Dir` is null it will be recentered around the receiver via the `recenter_prox_checkers()` proc.
* If the new location of the receiver is NOT a turf, set `active` to FALSE, so that it does not receive proximity calls.
* If the new location of the receiver IS a turf, set `active` to TRUE, so that it can receive proximity calls again.
*
* Arguments:
* * source - this will be the `hasprox_receiver`
* * OldLoc - the location the `hasprox_receiver` just moved from
* * Dir - the direction the `hasprox_receiver` just moved in
* * forced - if we were forced to move
* Like basic proximity checkers, these objects can also detect proximity.
* However these are meant for when you need to have some additional (more advanced) behavior on top of what basic proximity checkers can do.
*/
/obj/effect/abstract/proximity_checker/proc/HandleMove(datum/source, atom/OldLoc, Dir, forced)
if(Dir)
loc = get_step(src, Dir) // Basic movement 1 tile in some direction.
return
if(!isturf(hasprox_receiver.loc))
active = FALSE // Receiver shouldn't detect proximity while picked up, in a backpack, closet, etc.
else
active = TRUE // Receiver can detect proximity again because it's on a turf.
/obj/effect/abstract/proximity_checker/advanced
name = "advanced proximity checker"
/// `hasprox_receivers`s advanced proximity monitor component.
var/datum/component/proximity_monitor/advanced/advanced_monitor
/obj/effect/abstract/proximity_checker/advanced/Initialize(mapload, datum/component/proximity_monitor/advanced/P, _always_active)
advanced_monitor = P
return ..()
/obj/effect/abstract/proximity_checker/advanced/Destroy()
advanced_monitor = null
return ..()
/**
* # Inner Field Proximity Checker
*
* An advanced proximity checker object which sits on the the inner tiles of a field.
*/
/obj/effect/abstract/proximity_checker/advanced/inner_field
name = "inner field"
/obj/effect/abstract/proximity_checker/advanced/inner_field/Destroy()
advanced_monitor.field_checkers -= src
return ..()
/obj/effect/abstract/proximity_checker/advanced/inner_field/Crossed(atom/movable/AM, oldloc)
. = ..()
return advanced_monitor.inner_field_crossed(AM, src)
/obj/effect/abstract/proximity_checker/advanced/inner_field/Uncrossed(atom/movable/AM, oldloc)
. = ..()
return advanced_monitor.inner_field_uncrossed(AM, src)
/obj/effect/abstract/proximity_checker/advanced/inner_field/CanPass(atom/movable/mover, turf/target, height)
. = ..()
return advanced_monitor.inner_field_canpass(mover, src, target)
/**
* # Edge Field Proximity Checker
*
* An advanced proximity checker object which sits on the outer edge tiles of a field.
*/
/obj/effect/abstract/proximity_checker/advanced/edge_field
name = "edge field"
/obj/effect/abstract/proximity_checker/advanced/edge_field/Destroy()
advanced_monitor.edge_checkers -= src
return ..()
/obj/effect/abstract/proximity_checker/advanced/edge_field/Crossed(atom/movable/AM, oldloc)
. = ..()
return advanced_monitor.edge_field_crossed(AM, src)
/obj/effect/abstract/proximity_checker/advanced/edge_field/Uncrossed(atom/movable/AM)
. = ..()
return advanced_monitor.edge_field_uncrossed(AM, src)
/obj/effect/abstract/proximity_checker/advanced/edge_field/CanPass(atom/movable/mover, turf/target, height)
. = ..()
return advanced_monitor.edge_field_canpass(mover, src, target)
+10
View File
@@ -61,6 +61,7 @@
density = FALSE
icon = null
icon_state = null
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
// Most of these overrides procs below are overkill, but better safe than sorry.
/obj/effect/abstract/swarmer_act()
@@ -81,6 +82,15 @@
/obj/effect/abstract/ex_act(severity)
return
/obj/effect/abstract/blob_act()
return
/obj/effect/abstract/acid_act()
return
/obj/effect/abstract/fire_act()
return
/obj/effect/decal
plane = FLOOR_PLANE
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
@@ -62,8 +62,6 @@
to_chat(user, "<span class='notice'>You attach [A] to the valve controls and secure it.</span>")
A.holder = src
A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb).
if(istype(attached_device, /obj/item/assembly/prox_sensor))
AddComponent(/datum/component/proximity_monitor)
investigate_log("[key_name(user)] attached a [A] to a transfer valve.", INVESTIGATE_BOMB)
add_attack_logs(user, src, "attached [A] to a transfer valve", ATKLOG_FEW)
@@ -139,7 +137,6 @@
attached_device.forceMove(get_turf(src))
attached_device.holder = null
attached_device = null
qdel(GetComponent(/datum/component/proximity_monitor))
update_icon()
else
. = FALSE
@@ -176,7 +176,7 @@
if(I.reagents.total_volume)
to_chat(user, "<span class='notice'>You add [I] to the assembly.</span>")
user.drop_item()
I.loc = src
I.forceMove(src)
beakers += I
else
to_chat(user, "<span class='notice'>[I] is empty.</span>")
@@ -190,10 +190,8 @@
user.drop_item()
nadeassembly = A
if(nadeassembly.has_prox_sensors())
AddComponent(/datum/component/proximity_monitor)
A.master = src
A.loc = src
A.forceMove(src)
assemblyattacher = user.ckey
stage = WIRED
to_chat(user, "<span class='notice'>You add [A] to [src]!</span>")
@@ -218,13 +216,12 @@
payload_name = null
label = null
if(nadeassembly)
nadeassembly.loc = get_turf(src)
nadeassembly.forceMove(get_turf(src))
nadeassembly.master = null
nadeassembly = null
qdel(GetComponent(/datum/component/proximity_monitor))
if(beakers.len)
for(var/obj/O in beakers)
O.loc = get_turf(src)
O.forceMove(get_turf(src))
beakers = list()
update_icon()
@@ -307,8 +304,6 @@
/obj/item/grenade/chem_grenade/proc/CreateDefaultTrigger(typekey)
if(ispath(typekey,/obj/item/assembly))
nadeassembly = new(src)
if(nadeassembly.has_prox_sensors())
AddComponent(/datum/component/proximity_monitor)
nadeassembly.a_left = new /obj/item/assembly/igniter(nadeassembly)
nadeassembly.a_left.holder = nadeassembly
nadeassembly.a_left.secured = 1
+4 -8
View File
@@ -12,10 +12,6 @@
var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank
origin_tech = "materials=1;engineering=1"
/obj/item/onetankbomb/ComponentInitialize()
. = ..()
AddComponent(/datum/component/proximity_monitor)
/obj/item/onetankbomb/examine(mob/user)
. = ..()
. += bombtank.examine(user)
@@ -41,10 +37,10 @@
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
to_chat(user, "<span class='notice'>You disassemble [src].</span>")
bombassembly.loc = user.loc
bombassembly.forceMove(user.loc)
bombassembly.master = null
bombassembly = null
bombtank.loc = user.loc
bombtank.forceMove(user.loc)
bombtank.master = null
bombtank = null
qdel(src)
@@ -119,11 +115,11 @@
R.bombassembly = S //Tell the bomb about its assembly part
S.master = R //Tell the assembly about its new owner
S.loc = R //Move the assembly out of the fucking way
S.forceMove(R) //Move the assembly out of the fucking way
R.bombtank = src //Same for tank
master = R
loc = R
forceMove(R)
R.update_icon()
return
+2 -4
View File
@@ -50,8 +50,6 @@
A2.holder = src
a_left = A1
a_right = A2
if(has_prox_sensors())
AddComponent(/datum/component/proximity_monitor)
name = "[A1.name]-[A2.name] assembly"
update_icon()
return TRUE
@@ -183,10 +181,10 @@
return FALSE
if(a_left)
a_left.holder = null
a_left.loc = T
a_left.forceMove(T)
if(a_right)
a_right.holder = null
a_right.loc = T
a_right.forceMove(T)
qdel(src)
+1 -1
View File
@@ -15,7 +15,7 @@
/obj/item/assembly/prox_sensor/ComponentInitialize()
. = ..()
AddComponent(/datum/component/proximity_monitor)
AddComponent(/datum/component/proximity_monitor, _always_active = TRUE)
/obj/item/assembly/prox_sensor/describe()
if(timing)
@@ -146,7 +146,6 @@
trigger.forceMove(src)
trigger.master = src
trigger.holder = src
AddComponent(/datum/component/proximity_monitor)
to_chat(user, "<span class='notice'>You attach [W] to [src].</span>")
return TRUE
else if(istype(W, /obj/item/assembly))
@@ -166,7 +165,6 @@
trigger.master = null
trigger.holder = null
trigger = null
qdel(GetComponent(/datum/component/proximity_monitor))
/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C)
if(istype(C))
@@ -188,10 +186,6 @@
M.Jitter(20)
return
/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM)
if(trigger)
trigger.HasProximity(AM)
/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces)
if(trigger)
@@ -170,7 +170,6 @@
to_chat(usr, "<span class='notice'>You detach [assembly] from [src]</span>")
usr.put_in_hands(assembly)
assembly = null
qdel(GetComponent(/datum/component/proximity_monitor))
update_icon()
else
to_chat(usr, "<span class='notice'>There is no assembly to remove.</span>")
@@ -187,8 +186,6 @@
assembly = W
user.drop_item()
W.forceMove(src)
if(assembly.has_prox_sensors())
AddComponent(/datum/component/proximity_monitor)
overlays += "assembly"
else
..()
@@ -153,7 +153,6 @@
usr.visible_message("<span class='notice'>[usr] detaches [rig] from [src].</span>", "<span class='notice'>You detach [rig] from [src].</span>")
rig.forceMove(get_turf(usr))
rig = null
qdel(GetComponent(/datum/component/proximity_monitor))
lastrigger = null
overlays.Cut()
@@ -176,8 +175,6 @@
rig = H
user.drop_item()
H.forceMove(src)
if(rig.has_prox_sensors())
AddComponent(/datum/component/proximity_monitor)
var/icon/test = getFlatIcon(H)
test.Shift(NORTH, 1)
test.Shift(EAST, 6)
+4
View File
@@ -804,6 +804,8 @@
for(var/atom/movable/AM in H)
AM.forceMove(T)
AM.pipe_eject(direction)
SEND_SIGNAL(AM, COMSIG_MOVABLE_EXIT_DISPOSALS)
spawn(1)
if(AM)
AM.throw_at(target, 100, 1)
@@ -819,6 +821,8 @@
AM.forceMove(T)
AM.pipe_eject(0)
SEND_SIGNAL(AM, COMSIG_MOVABLE_EXIT_DISPOSALS)
spawn(1)
if(AM)
AM.throw_at(target, 5, 1)
+1
View File
@@ -278,6 +278,7 @@
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\dcs.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
#include "code\controllers\subsystem\processing\fields.dm"
#include "code\controllers\subsystem\processing\instruments.dm"
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\processing.dm"