From 1210a6e42f7057e861be164a6376ae5e2cef148b Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Mon, 18 Jan 2016 16:50:35 +0100 Subject: [PATCH 1/4] Adds the moved event. Allows movable atoms to follow other movable atoms, as quickly as BYOND allows. Also supports following atoms inside other atoms. However, this only works fully when Move() or forceMove() is utilized. Setting loc directly will not raise the moved event. --- baystation12.dme | 3 +- code/_onclick/hud/movable_screen_objects.dm | 2 +- code/_onclick/observer.dm | 2 +- code/datums/observation/move.dm | 45 ++++++++++ code/game/objects/items/glassjar.dm | 6 +- .../structures/crates_lockers/closets.dm | 1 + code/modules/admin/verbs/adminjump.dm | 2 +- code/modules/mob/dead/observer/observer.dm | 85 ++++++------------- code/modules/mob/freelook/update_triggers.dm | 5 +- code/modules/mob/living/living_defense.dm | 16 ++-- .../modules/mob/living/silicon/decoy/decoy.dm | 5 +- 11 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 code/datums/observation/move.dm diff --git a/baystation12.dme b/baystation12.dme index 139ebc26a95..4f127ae8759 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -212,6 +212,7 @@ #include "code\datums\helper_datums\global_iterator.dm" #include "code\datums\helper_datums\teleport.dm" #include "code\datums\helper_datums\topic_input.dm" +#include "code\datums\observation\move.dm" #include "code\datums\observation\observation.dm" #include "code\datums\uplink\ammunition.dm" #include "code\datums\uplink\announcements.dm" @@ -1882,10 +1883,10 @@ #include "code\modules\xgm\xgm_gas_data.dm" #include "code\modules\xgm\xgm_gas_mixture.dm" #include "code\unit_tests\equipment_tests.dm" +#include "code\unit_tests\map_tests.dm" #include "code\unit_tests\mob_tests.dm" #include "code\unit_tests\unit_test.dm" #include "code\unit_tests\zas_tests.dm" -#include "code\unit_tests\map_tests.dm" #include "code\ZAS\_docs.dm" #include "code\ZAS\Airflow.dm" #include "code\ZAS\Atom.dm" diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm index d543b8b12a4..7e132390f69 100644 --- a/code/_onclick/hud/movable_screen_objects.dm +++ b/code/_onclick/hud/movable_screen_objects.dm @@ -10,7 +10,7 @@ /obj/screen/movable var/snap2grid = FALSE - var/moved = FALSE + var/has_moved = FALSE //Snap Screen Object //Tied to the grid, snaps to the nearest turf diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 55fe373301e..a504f3eb4aa 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -25,7 +25,7 @@ // Otherwise jump else - following = null + stop_following() forceMove(get_turf(A)) /mob/dead/observer/ClickOn(var/atom/A, var/params) diff --git a/code/datums/observation/move.dm b/code/datums/observation/move.dm new file mode 100644 index 00000000000..a8800c1bc44 --- /dev/null +++ b/code/datums/observation/move.dm @@ -0,0 +1,45 @@ + +/*********************** +* Movement Handling * +***********************/ +/atom/movable + var/datum/observ/moved + +/atom/movable/Move() + var/old_loc = loc + if(..() && moved) + moved.raise_event(list(src, old_loc, loc)) + +/atom/movable/init_observers() + . = ..() + if(.) + moved = new() + +/atom/movable/destroy_observers() + . = ..() + if(.) + qdel(moved) + moved = null + +/atom/movable/proc/move_to_destination(var/atom/movable/am, var/old_loc, var/new_loc) + var/turf/T = get_turf(new_loc) + if(T && T != loc) + forceMove(T) + +/atom/movable/proc/recursive_move(var/atom/movable/am, var/old_loc, var/new_loc) + moved.raise_event(list(src, old_loc, new_loc)) + +/atom/Entered(var/atom/movable/am, atom/old_loc) + ..() + if(am.moved) + am.moved.raise_event(list(am, old_loc, src.loc)) + +/atom/movable/Entered(var/atom/movable/am, atom/old_loc) + ..() + if(src.moved) + src.moved.register(am, /atom/movable/proc/recursive_move) + +/atom/movable/Exited(var/atom/movable/am, atom/old_loc) + ..() + if(src.moved) + src.moved.unregister(am) diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm index 54062ff8530..bcf94b4a99a 100644 --- a/code/game/objects/items/glassjar.dm +++ b/code/game/objects/items/glassjar.dm @@ -26,14 +26,14 @@ return var/mob/L = A user.visible_message("[user] scoops [L] into \the [src].", "You scoop [L] into \the [src].") - L.loc = src + L.forceMove(src) contains = 2 update_icon() return else if(istype(A, /obj/effect/spider/spiderling)) var/obj/effect/spider/spiderling/S = A user.visible_message("[user] scoops [S] into \the [src].", "You scoop [S] into \the [src].") - S.loc = src + S.forceMove(src) processing_objects.Remove(S) // No growing inside jars contains = 3 update_icon() @@ -73,7 +73,7 @@ var/obj/item/weapon/spacecash/S = W user.visible_message("[user] puts [S.worth] [S.worth > 1 ? "thalers" : "thaler"] into \the [src].") user.drop_from_inventory(S) - S.loc = src + S.forceMove(src) update_icon() /obj/item/glass_jar/update_icon() // Also updates name and desc diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 69a37f00728..7a83eb1298c 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -22,6 +22,7 @@ var/store_mobs = 1 /obj/structure/closet/initialize() + ..() if(!opened) // if closed, any item at the crate's loc is put in the contents var/obj/item/I for(I in src.loc) diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index a8d98beaf0d..1c788d148b2 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -2,7 +2,7 @@ return /mob/dead/observer/on_mob_jump() - following = null + stop_following() /client/proc/Jump(var/area/A in return_sorted_areas()) set name = "Jump to Area" diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 4a82b68b3e7..3f80e5fa324 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -305,8 +305,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp else usr << "No area available." + stop_following() usr.forceMove(pick(L)) - following = null /mob/dead/observer/verb/follow(input in getmobs()) set category = "Ghost" @@ -319,67 +319,30 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp // This is the ghost's follow verb with an argument /mob/dead/observer/proc/ManualFollow(var/atom/movable/target) - if(!target) + if(!target || target == following || target == src) return - var/turf/targetloc = get_turf(target) - if(check_holy(targetloc)) - usr << "You cannot follow a mob standing on holy grounds!" + stop_following() + following = target + following.moved.register(src, /atom/movable/proc/move_to_destination) + following.destruction.register(src, /mob/dead/observer/proc/stop_following) + + src << "Now following \the [following]" + move_to_destination(following, following.loc, following.loc) + +/mob/dead/observer/proc/stop_following() + if(following) + src << "No longer following \the [following]" + following.moved.unregister(src) + following.destruction.unregister(src) + following = null + +/mob/dead/observer/move_to_destination(var/atom/movable/am, var/old_loc, var/new_loc) + var/turf/T = get_turf(new_loc) + if(check_holy(T)) + usr << "You cannot follow something standing on holy grounds!" return - if(target != src) - if(following && following == target) - return - following = target - src << "Now following \the [target]" - if(ismob(target)) - forceMove(get_turf(target)) - var/mob/M = target - M.following_mobs += src - else - spawn(0) - while(target && following == target && client) - var/turf/T = get_turf(target) - if(!T) - break - // To stop the ghost flickering. - if(loc != T) - forceMove(T) - sleep(15) - -/mob/proc/update_following() - . = get_turf(src) - for(var/mob/dead/observer/M in following_mobs) - if(M.following != src) - following_mobs -= M - else - if(M.loc != .) - M.forceMove(.) - -/mob - var/list/following_mobs = list() - -/mob/Destroy() - for(var/mob/dead/observer/M in following_mobs) - M.following = null - following_mobs = null - return ..() - -/mob/dead/observer/Destroy() - if(ismob(following)) - var/mob/M = following - M.following_mobs -= src - following = null - return ..() - -/mob/Move() - . = ..() - if(.) - update_following() - -/mob/Life() - // to catch teleports etc which directly set loc - update_following() - return ..() + ..() /mob/proc/check_holy(var/turf/T) return 0 @@ -404,8 +367,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/turf/T = get_turf(M) //Turf of the destination mob if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. + stop_following() forceMove(T) - following = null else src << "This mob is not located in the game world." /* @@ -433,7 +396,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp src << "\red You are dead! You have no mind to store memory!" /mob/dead/observer/Post_Incorpmove() - following = null + stop_following() /mob/dead/observer/verb/analyze_air() set name = "Analyze Air" diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm index 405e4bdd9e5..482025426eb 100644 --- a/code/modules/mob/freelook/update_triggers.dm +++ b/code/modules/mob/freelook/update_triggers.dm @@ -15,7 +15,7 @@ /turf/simulated/Destroy() updateVisibility(src) - ..() + return ..() /turf/simulated/New() ..() @@ -26,9 +26,10 @@ /obj/structure/Destroy() updateVisibility(src) - ..() + return ..() /obj/structure/New() + ..() updateVisibility(src) // EFFECTS diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 64ca2469b07..02974e8d647 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -16,19 +16,19 @@ var/armor = getarmor(def_zone, attack_flag) var/absorb = 0 - + //Roll armour if(prob(armor)) absorb += 1 if(prob(armor)) absorb += 1 - + //Roll penetration if(prob(armour_pen)) absorb -= 1 if(prob(armour_pen)) absorb -= 1 - + if(absorb >= 2) if(absorb_text) show_message("[absorb_text]") @@ -129,7 +129,7 @@ //returns 0 if the effects failed to apply for some reason, 1 otherwise. /mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) - if(!effective_force || blocked >= 2) + if(!effective_force || blocked >= 2) return 0 //Hulk modifier @@ -298,7 +298,7 @@ return 0 //Scale quadratically so that single digit numbers of fire stacks don't burn ridiculously hot. - //lower limit of 700 K, same as matches and roughly the temperature of a cool flame. + //lower limit of 700 K, same as matches and roughly the temperature of a cool flame. return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2), 700) /mob/living/proc/reagent_permeability() @@ -339,7 +339,7 @@ hud_used.hide_actions_toggle = new(hud_used) hud_used.hide_actions_toggle.UpdateIcon() - if(!hud_used.hide_actions_toggle.moved) + if(!hud_used.hide_actions_toggle.has_moved) hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(1) //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,1) @@ -362,7 +362,7 @@ client.screen += B - if(!B.moved) + if(!B.has_moved) B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number) //hud_used.SetButtonCoords(B,button_number) @@ -370,7 +370,7 @@ if(!hud_used.hide_actions_toggle) hud_used.hide_actions_toggle = new(hud_used) hud_used.hide_actions_toggle.InitialiseIcon(src) - if(!hud_used.hide_actions_toggle.moved) + if(!hud_used.hide_actions_toggle.has_moved) hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1) //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,button_number+1) client.screen += hud_used.hide_actions_toggle diff --git a/code/modules/mob/living/silicon/decoy/decoy.dm b/code/modules/mob/living/silicon/decoy/decoy.dm index bbbfe19dadb..86ae4097cda 100644 --- a/code/modules/mob/living/silicon/decoy/decoy.dm +++ b/code/modules/mob/living/silicon/decoy/decoy.dm @@ -9,4 +9,7 @@ src.icon = 'icons/mob/AI.dmi' src.icon_state = "ai" src.anchored = 1 - src.canmove = 0 \ No newline at end of file + src.canmove = 0 + +/mob/living/silicon/decoy/initialize() + return From cc09a86601fc59e4c83ed46c5fd8955d81d5972f Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Mon, 18 Jan 2016 22:38:53 +0100 Subject: [PATCH 2/4] Optimizes moved registrations. Now limits moved registrations to when strictly necessary. That is: * When entering a movable atom, the entering object only registers to its moved event if it has moved listeners on its own. * Instead, when registering to a moved event, registrations occur recursively until a non-/atom/movable is encountered or a prior movement registration from the same object is reached. --- code/datums/observation/move.dm | 26 ++++++++++++++----- code/datums/observation/observation.dm | 24 +++++++++++++---- .../objects/items/weapons/storage/storage.dm | 3 +-- code/modules/mob/living/carbon/brain/MMI.dm | 5 ++-- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/code/datums/observation/move.dm b/code/datums/observation/move.dm index a8800c1bc44..d9888246d67 100644 --- a/code/datums/observation/move.dm +++ b/code/datums/observation/move.dm @@ -1,19 +1,33 @@ +/datum/observ/moved/New(var/atom/movable/event_holder) + if(!istype(event_holder)) + CRASH("Improper event holder type: '[event_holder]'/[event_holder.type]") + ..() + +/datum/observ/moved/register(var/datum/procOwner, var/proc_call) + . = ..() + var/atom/movable/child = event_holder + if(.) + var/atom/movable/parent = child.loc + while(istype(parent) && parent.moved && !parent.moved.is_listening(child)) + parent.moved.register(child, /atom/movable/proc/recursive_move) + child = parent + parent = child.loc /*********************** * Movement Handling * ***********************/ /atom/movable - var/datum/observ/moved + var/datum/observ/moved/moved /atom/movable/Move() var/old_loc = loc if(..() && moved) moved.raise_event(list(src, old_loc, loc)) -/atom/movable/init_observers() +/atom/movable/init_observers(var/event_holder) . = ..() if(.) - moved = new() + moved = new(event_holder) /atom/movable/destroy_observers() . = ..() @@ -32,14 +46,14 @@ /atom/Entered(var/atom/movable/am, atom/old_loc) ..() if(am.moved) - am.moved.raise_event(list(am, old_loc, src.loc)) + am.moved.raise_event(list(am, old_loc, am.loc)) /atom/movable/Entered(var/atom/movable/am, atom/old_loc) ..() - if(src.moved) + if(src.moved && am.moved && am.moved.has_listeners() && !src.moved.is_listening(am)) src.moved.register(am, /atom/movable/proc/recursive_move) /atom/movable/Exited(var/atom/movable/am, atom/old_loc) ..() - if(src.moved) + if(src.moved && src.moved.is_listening(am, /atom/movable/proc/recursive_move)) src.moved.unregister(am) diff --git a/code/datums/observation/observation.dm b/code/datums/observation/observation.dm index eefbcb1fc22..742e367b5a9 100644 --- a/code/datums/observation/observation.dm +++ b/code/datums/observation/observation.dm @@ -1,26 +1,40 @@ /datum/observ + var/event_holder var/list/listeners +/datum/observ/New(var/event_holder) + src.event_holder = event_holder + ..() + /datum/observ/Destroy() + event_holder = null if(listeners) for(var/listener in listeners) unregister(listener) listeners.Cut() return ..() +/datum/observ/proc/is_listening(var/datum/procOwner, var/proc_call) + return listeners && (procOwner in listeners) && (!proc_call || listeners[procOwner] == proc_call) + +/datum/observ/proc/has_listeners() + return listeners && listeners.len + /datum/observ/proc/register(var/datum/procOwner, var/proc_call) if(!(procOwner && procOwner.destruction)) - return + return FALSE if(!listeners) listeners = list() listeners[procOwner] = proc_call procOwner.destruction.register(src, /datum/observ/proc/unregister) + return TRUE /datum/observ/proc/unregister(var/datum/procOwner) if(!(listeners && procOwner && procOwner.destruction)) - return + return FALSE listeners -= procOwner procOwner.destruction.unregister(src) + return TRUE /datum/observ/proc/raise_event(var/list/args = list()) if(!listeners) @@ -35,15 +49,15 @@ var/datum/observ/destruction /datum/New() - init_observers() + init_observers(src) ..() /datum/Destroy() destroy_observers() return ..() -/datum/proc/init_observers() - destruction = new() +/datum/proc/init_observers(var/event_holder) + destruction = new(event_holder) return TRUE /datum/proc/destroy_observers() diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index c6ee578dcee..310d5f3f101 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -501,7 +501,7 @@ remove_from_storage(I, T) /obj/item/weapon/storage/New() - + ..() if(allow_quick_empty) verbs += /obj/item/weapon/storage/verb/quick_empty else @@ -559,7 +559,6 @@ src.closer.icon_state = "x" src.closer.layer = 20 orient2hud() - return /obj/item/weapon/storage/emp_act(severity) if(!istype(src.loc, /mob/living)) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 07004f97546..c07647a57b7 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -2,12 +2,11 @@ /obj/item/device/mmi/digital/New() src.brainmob = new(src) + src.brainmob.stat = CONSCIOUS src.brainmob.add_language("Robot Talk") - src.brainmob.loc = src src.brainmob.container = src - src.brainmob.stat = 0 src.brainmob.silent = 0 - dead_mob_list -= src.brainmob + ..() /obj/item/device/mmi/digital/transfer_identity(var/mob/living/carbon/H) brainmob.dna = H.dna From 8468c4951e8518beb6b8197fe1a881a54264bf7b Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Wed, 20 Jan 2016 11:12:08 +0100 Subject: [PATCH 3/4] Fixes observers leaving null entries in the moved listener list when returning to their bodies. --- code/modules/mob/dead/observer/observer.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 3f80e5fa324..82192830b4a 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -86,6 +86,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images ..() /mob/dead/observer/Destroy() + stop_following() qdel(ghost_multitool) ghost_multitool = null @@ -94,7 +95,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images qdel(ghostimage) ghostimage = null updateallghostimages() - ..() + return ..() /mob/dead/observer/Topic(href, href_list) if (href_list["track"]) @@ -225,6 +226,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(!found_rune) usr << "The astral cord that ties your body and your spirit has been severed. You are likely to wander the realm beyond until your body is finally dead and thus reunited with you." return + stop_following() mind.current.ajourn=0 mind.current.key = key mind.current.teleop = null From a58b9a92e2ab44e14a052ac0d9f467400f99d9b9 Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Wed, 20 Jan 2016 12:51:21 +0100 Subject: [PATCH 4/4] "Moved" event unit tests. --- baystation12.dme | 1 + code/unit_tests/observation_tests.dm | 108 +++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 code/unit_tests/observation_tests.dm diff --git a/baystation12.dme b/baystation12.dme index 4f127ae8759..4956ec5f809 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1885,6 +1885,7 @@ #include "code\unit_tests\equipment_tests.dm" #include "code\unit_tests\map_tests.dm" #include "code\unit_tests\mob_tests.dm" +#include "code\unit_tests\observation_tests.dm" #include "code\unit_tests\unit_test.dm" #include "code\unit_tests\zas_tests.dm" #include "code\ZAS\_docs.dm" diff --git a/code/unit_tests/observation_tests.dm b/code/unit_tests/observation_tests.dm new file mode 100644 index 00000000000..caa2468791d --- /dev/null +++ b/code/unit_tests/observation_tests.dm @@ -0,0 +1,108 @@ +/proc/is_listening_to_movement(var/atom/movable/listening_to, var/listener) + return listening_to.moved.is_listening(listener) + +datum/unit_test/observation + name = "OBSERVATION template" + async = 0 + +datum/unit_test/observation/moved_observer_shall_register_on_follow + name = "OBSERVATION: Moved - Observer Shall Register on Follow" + +datum/unit_test/observation/moved_observer_shall_register_on_follow/start_test() + var/turf/T = locate(20,20,1) + var/mob/living/carbon/human/H = new(T) + var/mob/dead/observer/O = new(T) + + O.ManualFollow(H) + if(is_listening_to_movement(H, O)) + pass("The observer is now following the mob.") + else + fail("The observer is not following the mob.") + + qdel(H) + qdel(O) + return 1 + +datum/unit_test/observation/moved_observer_shall_unregister_on_nofollow + name = "OBSERVATION: Moved - Observer Shall Unregister on NoFollow" + +datum/unit_test/observation/moved_observer_shall_unregister_on_nofollow/start_test() + var/turf/T = locate(20,20,1) + var/mob/living/carbon/human/H = new(T) + var/mob/dead/observer/O = new(T) + + O.ManualFollow(H) + O.stop_following() + if(!is_listening_to_movement(H, O)) + pass("The observer is no longer following the mob.") + else + fail("The observer is still following the mob.") + + qdel(H) + qdel(O) + return 1 + +datum/unit_test/observation/moved_shall_not_register_on_enter_without_listeners + name = "OBSERVATION: Moved - Shall Not Register on Enter Without Listeners" + +datum/unit_test/observation/moved_shall_not_register_on_enter_without_listeners/start_test() + var/turf/T = locate(20,20,1) + var/mob/living/carbon/human/H = new(T) + var/obj/structure/closet/C = new(T) + + H.forceMove(C) + if(!is_listening_to_movement(C, H)) + pass("The mob did not register to the closet's moved event.") + else + fail("The mob has registered to the closet's moved event.") + + qdel(C) + qdel(H) + return 1 + +datum/unit_test/observation/moved_shall_registers_recursively_on_new_listener + name = "OBSERVATION: Moved - Shall Register Recursively on New Listener" + +datum/unit_test/observation/moved_shall_registers_recursively_on_new_listener/start_test() + var/turf/T = locate(20,20,1) + var/mob/living/carbon/human/H = new(T) + var/obj/structure/closet/C = new(T) + var/mob/dead/observer/O = new(T) + + H.forceMove(C) + O.ManualFollow(H) + var/listening_to_closet = is_listening_to_movement(C, H) + var/listening_to_human = is_listening_to_movement(H, O) + if(listening_to_closet && listening_to_human) + pass("Recursive moved registration succesful.") + else + fail("Recursive moved registration failed. Human listening to closet: [listening_to_closet] - Observer listening to human: [listening_to_human]") + + qdel(C) + qdel(H) + qdel(O) + return 1 + +datum/unit_test/observation/moved_shall_registers_recursively_with_existing_listener + name = "OBSERVATION: Moved - Shall Register Recursively with Existing Listener" + +datum/unit_test/observation/moved_shall_registers_recursively_with_existing_listener/start_test() + var/turf/T = locate(20,20,1) + var/mob/living/carbon/human/H = new(T) + var/obj/structure/closet/C = new(T) + var/mob/dead/observer/O = new(T) + + O.ManualFollow(H) + H.forceMove(C) + var/listening_to_closet = is_listening_to_movement(C, H) + var/listening_to_human = is_listening_to_movement(H, O) + if(listening_to_closet && listening_to_human) + pass("Recursive moved registration succesful.") + else + fail("Recursive moved registration failed. Human listening to closet: [listening_to_closet] - Observer listening to human: [listening_to_human]") + + qdel(C) + qdel(H) + qdel(O) + + return 1