[MIRROR] Motion tracker subsystem (#10232)

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-02-28 04:00:56 -07:00
committed by GitHub
parent 775c8313f3
commit dba0bf4304
25 changed files with 276 additions and 8 deletions

View File

@@ -270,6 +270,9 @@
#define HEARING_SPANS 6 #define HEARING_SPANS 6
#define HEARING_MESSAGE_MODE 7 */ #define HEARING_MESSAGE_MODE 7 */
///from /datum/controller/subsystem/motion_tracker/notice() (/datum/weakref/source_atom,/turf/echo_turf_location)
#define COMSIG_MOVABLE_MOTIONTRACKER "move_motiontracker"
///called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source) ///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" #define COMSIG_MOVABLE_DISPOSING "movable_disposing"

View File

@@ -167,6 +167,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define FIRE_PRIORITY_NIGHTSHIFT 5 #define FIRE_PRIORITY_NIGHTSHIFT 5
#define FIRE_PRIORITY_PLANTS 5 #define FIRE_PRIORITY_PLANTS 5
#define FIRE_PRIORITY_VIS 5 #define FIRE_PRIORITY_VIS 5
#define FIRE_PRIORITY_MOTIONTRACKER 6
#define FIRE_PRIORITY_ORBIT 7 #define FIRE_PRIORITY_ORBIT 7
#define FIRE_PRIORITY_VOTE 8 #define FIRE_PRIORITY_VOTE 8
#define FIRE_PRIORITY_INSTRUMENTS 9 #define FIRE_PRIORITY_INSTRUMENTS 9

View File

@@ -0,0 +1,83 @@
SUBSYSTEM_DEF(motiontracker)
name = "Motion Tracker"
priority = FIRE_PRIORITY_MOTIONTRACKER
wait = 1 SECOND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
flags = SS_NO_INIT
var/min_range = 2
var/max_range = 8
var/all_echos_round = 0
var/all_pings_round = 0
var/list/queued_echo_turfs = list()
var/list/currentrun = list()
var/list/expended_echos = list()
/datum/controller/subsystem/motiontracker/stat_entry(msg)
var/count = 0
if(_listen_lookup)
var/list/track_list = _listen_lookup[COMSIG_MOVABLE_MOTIONTRACKER]
if(islist(track_list))
count = track_list.len
else
count = 1 // listen_lookup optimizes single entries into just returning the only thing
msg = "L: [count] | Q: [queued_echo_turfs.len] | A: [all_echos_round]/[all_pings_round]"
return ..()
/datum/controller/subsystem/motiontracker/fire(resumed = 0)
if(!resumed)
src.currentrun = queued_echo_turfs.Copy()
expended_echos.Cut()
while(currentrun.len)
var/key = currentrun[1] // Because using an index into an associative array gets the key at that index... I hate you byond.
var/list/data = currentrun[key]
var/datum/weakref/AF= data[1]
var/datum/weakref/RF= data[2]
var/count = data[3]
var/list/clients = data[4]
var/turf/At = AF?.resolve()
var/turf/Rt = RF?.resolve()
if(Rt && At && count)
while(count-- > 0)
// Place at root turf offset from signal responder's turf using px offsets. So it will show up over visblocking.
var/image/motion_echo/E = new /image/motion_echo('icons/effects/effects.dmi', Rt, "motion_echo", OBFUSCATION_LAYER, SOUTH)
E.place_from_root(At)
for(var/datum/weakref/C in clients)
E.append_client(C)
currentrun.Remove(key)
expended_echos[key] = data
if(MC_TICK_CHECK)
return
// Removed used keys, incase the current queue grew while we were processing this one
queued_echo_turfs -= expended_echos
// We get this from anything in the world that would cause a motion tracker ping
// From sounds to motions, to mob attacks. This then sends a signal to anyone listening.
/datum/controller/subsystem/motiontracker/proc/ping(var/atom/source, var/hear_chance = 30)
var/turf/T = get_turf(source)
if(!isturf(T)) // ONLY call from turfs
return
if(!prob(hear_chance))
return
if(hear_chance <= 40)
T = get_step(T,pick(cardinal))
if(!T) // incase...
return
// Echo time, we have a turf
if(queued_echo_turfs[REF(T)]) // Already echoing
return
all_pings_round++
SEND_SIGNAL(src, COMSIG_MOVABLE_MOTIONTRACKER, WEAKREF(source), T)
// We get this back from anything that handles the signal, and queues up a turf to draw the echo on
// The logic is in the SIGNAL HANDLER for if it does anything at all with the signal instead of assuming
// everything wants effects drawn, for example the motion tracker item just flicks() and doesn't call this.
/datum/controller/subsystem/motiontracker/proc/queue_echo(var/turf/Rt,var/turf/At,var/echo_count = 1,var/datum/weakref/client)
if(!Rt || !At || !client)
return
var/rfe = REF(At)
if(!queued_echo_turfs[rfe]) // We only care about the final turf, not the root turf for duping
queued_echo_turfs[rfe] = list(WEAKREF(At),WEAKREF(Rt),echo_count,list(client))
all_echos_round++
else
var/list/data = queued_echo_turfs[rfe]
data[4] += list(client)

View File

@@ -1339,6 +1339,8 @@ About the new airlock wires panel:
if(T && T.z == get_z(src)) if(T && T.z == get_z(src))
M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS)
SSmotiontracker.ping(src,100)
if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density)
src.closeOther.close() src.closeOther.close()
return ..() return ..()
@@ -1473,6 +1475,9 @@ About the new airlock wires panel:
if(distance <= world.view * 2) if(distance <= world.view * 2)
if(T && T.z == get_z(src)) if(T && T.z == get_z(src))
M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS) M.playsound_local(get_turf(src), sound, volume, 1, null, 0, TRUE, sound(sound), volume_channel = VOLUME_CHANNEL_DOORS)
SSmotiontracker.ping(src,100)
for(var/turf/turf in locs) for(var/turf/turf in locs)
var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf)
if(killthis) if(killthis)

View File

@@ -141,6 +141,7 @@
domutcheck(M,null) domutcheck(M,null)
M.UpdateAppearance() M.UpdateAppearance()
visible_message("\The [src.name] flashes violently before disintegrating!") visible_message("\The [src.name] flashes violently before disintegrating!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(s) qdel(s)
qdel(src) qdel(src)
@@ -158,6 +159,7 @@
if(istype(M)) if(istype(M))
M.Stun(30) M.Stun(30)
visible_message("\The [src.name] flashes violently before disintegrating!") visible_message("\The [src.name] flashes violently before disintegrating!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(s) qdel(s)
qdel(src) qdel(src)
@@ -173,6 +175,7 @@
if(!target.blocks_air) if(!target.blocks_air)
target.assume_gas(GAS_N2O, 30) target.assume_gas(GAS_N2O, 30)
visible_message("\The [src.name] detonates!") visible_message("\The [src.name] detonates!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(src) qdel(src)
@@ -188,6 +191,7 @@
target.assume_gas(GAS_PHORON, 30) target.assume_gas(GAS_PHORON, 30)
target.hotspot_expose(1000, CELL_VOLUME) target.hotspot_expose(1000, CELL_VOLUME)
visible_message("\The [src.name] detonates!") visible_message("\The [src.name] detonates!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(src) qdel(src)
@@ -229,6 +233,7 @@
return return
src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it src.fragmentate(O, 20, 7, list(/obj/item/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it
visible_message("\The [src.name] detonates!") visible_message("\The [src.name] detonates!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(s) qdel(s)
qdel(src) qdel(src)
@@ -258,6 +263,7 @@
s.set_up(3, 1, src) s.set_up(3, 1, src)
s.start() s.start()
visible_message("\The [src.name] flashes violently before disintegrating!") visible_message("\The [src.name] flashes violently before disintegrating!")
SSmotiontracker.ping(src,100)
empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade empulse(loc, 2, 4, 7, 10, 1) // As strong as an EMP grenade
spawn(0) spawn(0)
qdel(src) qdel(src)
@@ -279,6 +285,7 @@
M.adjust_fire_stacks(5) M.adjust_fire_stacks(5)
M.fire_act() M.fire_act()
visible_message("\The [src.name] bursts into flames!") visible_message("\The [src.name] bursts into flames!")
SSmotiontracker.ping(src,100)
spawn(0) spawn(0)
qdel(src) qdel(src)
@@ -300,6 +307,7 @@
else else
explosion(loc, 0, 0, 2, 2) explosion(loc, 0, 0, 2, 2)
visible_message("\The [src.name] detonates!") visible_message("\The [src.name] detonates!")
SSmotiontracker.ping(src,100)
qdel(s) qdel(s)
qdel(src) qdel(src)

View File

@@ -0,0 +1,27 @@
/image/motion_echo
plane = PLANE_FULLSCREEN
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance_flags = (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
var/list/clients = list()
/image/motion_echo/New(icon, loc, icon_state, layer, dir)
. = ..()
QDEL_IN(src, 2 SECONDS)
/image/motion_echo/proc/place_from_root(var/turf/At)
var/rand_limit = 12
pixel_x += ((At.x - loc.x) * 32) + rand(-rand_limit,rand_limit)
pixel_y += ((At.y - loc.y) * 32) + rand(-rand_limit,rand_limit)
/image/motion_echo/proc/append_client(var/datum/weakref/C)
var/client/CW = C?.resolve()
if(CW)
CW.images += src
clients.Add(C)
/image/motion_echo/Destroy(force)
. = ..()
for(var/datum/weakref/C in clients)
var/client/CW = C?.resolve()
if(CW)
CW.images -= src

View File

@@ -223,6 +223,7 @@
if(prob(50)) if(prob(50))
src.visible_message(span_notice("You hear something squeezing through the ventilation ducts."),2) src.visible_message(span_notice("You hear something squeezing through the ventilation ducts."),2)
SSmotiontracker.ping(src,10)
sleep(travel_time) sleep(travel_time)
if(!exit_vent || exit_vent.welded) if(!exit_vent || exit_vent.welded)
@@ -269,6 +270,7 @@
walk_to(src, target_atom, 5) walk_to(src, target_atom, 5)
if(prob(25)) if(prob(25))
src.visible_message(span_notice("\The [src] skitters[pick(" away"," around","")].")) src.visible_message(span_notice("\The [src] skitters[pick(" away"," around","")]."))
SSmotiontracker.ping(src,10)
else if(amount_grown < 75 && prob(5)) else if(amount_grown < 75 && prob(5))
//vent crawl! //vent crawl!
for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))

View File

@@ -0,0 +1,49 @@
/obj/item/motiontracker
name = "Motion Tracker"
desc = "The \"Vibromaster V1.7\", a handheld motion tracker. Often picks up nearby vibrations as motion however."
icon = 'icons/obj/device_alt.dmi'
icon_state = "pinoff"
item_state = "analyzer"
w_class = ITEMSIZE_SMALL
slot_flags = SLOT_BELT
throwforce = 5
throw_speed = 4
throw_range = 20
matter = list(MAT_STEEL = 30,MAT_GLASS = 20)
origin_tech = list(TECH_MAGNET = 1, TECH_DATA = 1)
pickup_sound = 'sound/items/pickup/device.ogg'
drop_sound = 'sound/items/drop/device.ogg'
/obj/item/motiontracker/Initialize(mapload)
RegisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER, PROC_REF(handle_motion_tracking))
. = ..()
/obj/item/motiontracker/Destroy(force, ...)
if(ismob(loc))
var/mob/M = loc
M.motiontracker_subscribe()
UnregisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER)
. = ..()
/obj/item/motiontracker/proc/handle_motion_tracking(mob/source, var/datum/weakref/RW, var/turf/T)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
var/atom/echo_source = RW?.resolve()
var/atom/scan_pos = src
if(!isturf(loc))
scan_pos = loc
if(!echo_source || get_dist(scan_pos,echo_source) > SSmotiontracker.max_range || scan_pos.z != echo_source.z)
return
flick("pinondirect",src)
/obj/item/motiontracker/Moved(atom/old_loc, direction, forced, movetime)
. = ..()
if(ismob(old_loc))
var/mob/M = old_loc
M.motiontracker_unsubscribe()
if(ismob(loc))
var/mob/M = loc
M.motiontracker_subscribe()

View File

@@ -87,6 +87,7 @@
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
if(T) if(T)
T.hotspot_expose(700,125) T.hotspot_expose(700,125)
SSmotiontracker.ping(src,100)
/obj/item/grenade/attackby(obj/item/W as obj, mob/user as mob) /obj/item/grenade/attackby(obj/item/W as obj, mob/user as mob)

View File

@@ -131,6 +131,7 @@
span_danger("You step on \the [src]!"), span_danger("You step on \the [src]!"),
span_infoplain(span_bold("You hear a loud metallic snap!")) span_infoplain(span_bold("You hear a loud metallic snap!"))
) )
SSmotiontracker.ping(src,100) // Clunk!
attack_mob(L) attack_mob(L)
if(!has_buckled_mobs()) if(!has_buckled_mobs())
anchored = FALSE anchored = FALSE

View File

@@ -32,6 +32,7 @@
if(!ignore_walls && !can_see(turf_source, T, length = maxdistance * 2)) if(!ignore_walls && !can_see(turf_source, T, length = maxdistance * 2))
continue continue
SSmotiontracker.ping(source,vol) // Nearly everything pings this, the quieter the less likely
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel) M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, channel, pressure_affected, S, preference, volume_channel)
/mob/proc/check_sound_preference(list/preference) /mob/proc/check_sound_preference(list/preference)

View File

@@ -85,6 +85,7 @@
src.visible_message(span_infoplain(span_bold("\The [src.name]") + " [deathmessage]")) src.visible_message(span_infoplain(span_bold("\The [src.name]") + " [deathmessage]"))
set_stat(DEAD) set_stat(DEAD)
SSmotiontracker.ping(src,80)
update_canmove() update_canmove()

View File

@@ -195,18 +195,14 @@
var/obj/item/clothing/glasses/V = rig.visor.vision.glasses var/obj/item/clothing/glasses/V = rig.visor.vision.glasses
compiled_vis |= V.enables_planes compiled_vis |= V.enables_planes
//VOREStation Add - NIF Support
if(nif) if(nif)
compiled_vis |= nif.planes_visible() compiled_vis |= nif.planes_visible()
//event hud //event hud
if(vantag_hud) if(vantag_hud)
compiled_vis |= VIS_CH_VANTAG compiled_vis |= VIS_CH_VANTAG
//VOREStation Add End
//Vore Stomach addition start. This goes here.
if(stomach_vision) if(stomach_vision)
compiled_vis += VIS_CH_STOMACH compiled_vis += VIS_CH_STOMACH
//Vore Stomach addition end
//CHOMPAdd Start Soulcatcher //CHOMPAdd Start Soulcatcher
if(soulgem?.flag_check(SOULGEM_SEE_SR_SOULS)) if(soulgem?.flag_check(SOULGEM_SEE_SR_SOULS))

View File

@@ -146,6 +146,7 @@
var/emp_dmg_mod = 1 // Multiplier to all EMP damage sustained by the mob, if it's EMP-sensitive var/emp_dmg_mod = 1 // Multiplier to all EMP damage sustained by the mob, if it's EMP-sensitive
var/emp_stun_mod = 1 // Multiplier to all EMP disorient/etc. sustained by the mob, if it's EMP-sensitive var/emp_stun_mod = 1 // Multiplier to all EMP disorient/etc. sustained by the mob, if it's EMP-sensitive
var/vision_flags = SEE_SELF // Same flags as glasses. var/vision_flags = SEE_SELF // Same flags as glasses.
var/has_vibration_sense = FALSE // Motion tracker subsystem
// Death vars. // Death vars.
var/meat_type = /obj/item/reagent_containers/food/snacks/meat/human var/meat_type = /obj/item/reagent_containers/food/snacks/meat/human

View File

@@ -17,6 +17,7 @@
base_species = "Xenochimera" base_species = "Xenochimera"
selects_bodytype = SELECTS_BODYTYPE_CUSTOM selects_bodytype = SELECTS_BODYTYPE_CUSTOM
digi_allowed = TRUE digi_allowed = TRUE
has_vibration_sense = TRUE
num_alternate_languages = 3 num_alternate_languages = 3
species_language = null species_language = null
@@ -26,7 +27,7 @@
icobase_tail = 1 icobase_tail = 1
inherent_verbs = list( inherent_verbs = list(
/mob/living/carbon/human/proc/reconstitute_form, /mob/living/carbon/human/proc/reconstitute_form,
/mob/living/carbon/human/proc/sonar_ping, ///mob/living/carbon/human/proc/sonar_ping,
/mob/living/carbon/human/proc/tie_hair, /mob/living/carbon/human/proc/tie_hair,
/mob/living/carbon/human/proc/lick_wounds, /mob/living/carbon/human/proc/lick_wounds,
/mob/living/carbon/human/proc/shapeshifter_reassemble) //Xenochimera get all the special verbs since they can't select traits. /mob/living/carbon/human/proc/shapeshifter_reassemble) //Xenochimera get all the special verbs since they can't select traits.

View File

@@ -59,7 +59,8 @@
mob_size = MOB_SMALL mob_size = MOB_SMALL
pass_flags = PASSTABLE pass_flags = PASSTABLE
holder_type = /obj/item/holder/micro //CHOMPEdit from holder/human to holder/micro holder_type = /obj/item/holder/micro //CHOMPEdit from holder/human to holder/micro
// short_sighted = 1 CHOMPEdit: We're fine without near-sightedness for now. // short_sighted = 1
has_vibration_sense = TRUE
gluttonous = 1 gluttonous = 1
blood_volume = 400 blood_volume = 400
hunger_factor = 0.2 hunger_factor = 0.2
@@ -141,7 +142,7 @@
) )
inherent_verbs = list( inherent_verbs = list(
/mob/living/carbon/human/proc/sonar_ping, ///mob/living/carbon/human/proc/sonar_ping,
/mob/living/proc/hide /mob/living/proc/hide
) )

View File

@@ -21,7 +21,7 @@ End Chomp Edit */
vore_belly_default_variant = "T" //Teshari belly sprite vore_belly_default_variant = "T" //Teshari belly sprite
inherent_verbs = list( inherent_verbs = list(
/mob/living/carbon/human/proc/sonar_ping, ///mob/living/carbon/human/proc/sonar_ping,
/mob/living/proc/hide, /mob/living/proc/hide,
/mob/living/proc/toggle_pass_table /mob/living/proc/toggle_pass_table
) )

View File

@@ -384,3 +384,24 @@
is_genetrait = TRUE is_genetrait = TRUE
hidden = FALSE hidden = FALSE
activation_message="Your body feels mundane." activation_message="Your body feels mundane."
/datum/trait/positive/vibration_sense
name = "Vibration Sense"
desc = "Allows you to sense subtle vibrations nearby, even if the source cannot be seen."
cost = 2
var_changes = list("has_vibration_sense" = TRUE)
// Traitgenes edit begin - Made into a gene trait
is_genetrait = TRUE
hidden = FALSE
activation_message="Your ears ring, and hear everything..."
// Traitgenes edit end
/datum/trait/positive/vibration_sense/apply(datum/species/S, mob/living/carbon/human/H, trait_prefs)
. = ..()
H.motiontracker_subscribe()
/datum/trait/positive/vibration_sense/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs)
. = ..()
H.motiontracker_unsubscribe()

View File

@@ -279,6 +279,8 @@ default behaviour is:
/mob/living/Moved(var/atom/oldloc, direct, forced, movetime) /mob/living/Moved(var/atom/oldloc, direct, forced, movetime)
. = ..() . = ..()
handle_footstep(loc) handle_footstep(loc)
if(!forced && movetime)
SSmotiontracker?.ping(src) // Incase of before init "turf enter gravity" this is ?, unfortunately.
// Begin VOREstation edit // Begin VOREstation edit
if(is_shifted) if(is_shifted)
is_shifted = FALSE is_shifted = FALSE

View File

@@ -39,6 +39,8 @@
vore_selected = null // from code/modules/vore/eating/mob_vr vore_selected = null // from code/modules/vore/eating/mob_vr
focus = null focus = null
motiontracker_unsubscribe(TRUE) // Force unsubscribe
if(mind) if(mind)
if(mind.current == src) if(mind.current == src)
mind.current = null mind.current = null

View File

@@ -244,3 +244,5 @@
var/list/resistances var/list/resistances
var/custom_footstep = FOOTSTEP_MOB_SHOE var/custom_footstep = FOOTSTEP_MOB_SHOE
VAR_PRIVATE/is_motion_tracking = FALSE // Prevent multiple unsubs and resubs, also used to check if the vis layer is enabled, use has_motiontracking() to get externally.
VAR_PRIVATE/wants_to_see_motion_echos = TRUE

View File

@@ -0,0 +1,48 @@
/mob/proc/has_motiontracking() // USE THIS
return is_motion_tracking
// Subscribing and unsubscribingto the motion tracker subsystem
/mob/proc/motiontracker_subscribe()
if(!is_motion_tracking)
is_motion_tracking = TRUE
wants_to_see_motion_echos = TRUE
RegisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER, PROC_REF(handle_motion_tracking))
add_verb(src,/mob/proc/toggle_motion_echo_vis)
/mob/proc/motiontracker_unsubscribe(var/destroying = FALSE)
if(is_motion_tracking)
is_motion_tracking = FALSE
UnregisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER)
remove_verb(src,/mob/proc/toggle_motion_echo_vis)
/mob/living/carbon/human/motiontracker_unsubscribe(destroying = FALSE)
// Block unsub if our species has vibration senses
if(!destroying && species?.has_vibration_sense)
return
. = ..()
// For COMSIG_MOVABLE_MOTIONTRACKER
/mob/proc/handle_motion_tracking(mob/source, var/datum/weakref/RW, var/turf/T)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
if(!client || !wants_to_see_motion_echos || stat || is_deaf())
return
var/atom/echo_source = RW?.resolve()
if(!echo_source || get_dist(src,echo_source) > SSmotiontracker.max_range || src.z != echo_source.z)
return
// Blind characters see all pings around them. Otherwise remove the closest, or any we can see. Pings behind walls or in the dark are always visible
if(!is_blind() && (get_dist(src,echo_source) < SSmotiontracker.min_range || (T.get_lumcount() >= 0.20 && can_see(src, T, 7)) ))
return
var/echos = 1
if(prob(30))
echos = rand(1,3)
SSmotiontracker.queue_echo(get_turf(src),T,echos,client ? WEAKREF(client) : null)
/mob/proc/toggle_motion_echo_vis()
set name = "Toggle Vibration Senses"
set desc = "Toggle the visibility of pings revealed by vibration senses or motion trackers."
set category = "Abilities.General"
wants_to_see_motion_echos = !wants_to_see_motion_echos
to_chat(src,"You will [wants_to_see_motion_echos ? "now" : "no longer"] see echos")

View File

@@ -186,3 +186,11 @@
build_path = /obj/item/juke_remote build_path = /obj/item/juke_remote
sort_string = "TCVAE" sort_string = "TCVAE"
department = LATHE_ALL | LATHE_SERVICE | LATHE_PUBLIC // CHOMPAdd department = LATHE_ALL | LATHE_SERVICE | LATHE_PUBLIC // CHOMPAdd
/datum/design/item/general/motion_tracker
name = "Motion Tracker"
id = "motion_tracker"
req_tech = list(TECH_MAGNET = 1, TECH_DATA = 2)
build_path = /obj/item/motiontracker
sort_string = "TAADC"
department = LATHE_ALL | LATHE_SECURITY // CHOMPAdd

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 449 KiB

View File

@@ -401,6 +401,7 @@
#include "code\controllers\subsystems\mapping.dm" #include "code\controllers\subsystems\mapping.dm"
#include "code\controllers\subsystems\media_tracks.dm" #include "code\controllers\subsystems\media_tracks.dm"
#include "code\controllers\subsystems\mobs.dm" #include "code\controllers\subsystems\mobs.dm"
#include "code\controllers\subsystems\motion_tracker.dm"
#include "code\controllers\subsystems\nightshift.dm" #include "code\controllers\subsystems\nightshift.dm"
#include "code\controllers\subsystems\overlays_ch.dm" #include "code\controllers\subsystems\overlays_ch.dm"
#include "code\controllers\subsystems\overmap_renamer_vr.dm" #include "code\controllers\subsystems\overmap_renamer_vr.dm"
@@ -1329,6 +1330,7 @@
#include "code\game\objects\effects\manifest.dm" #include "code\game\objects\effects\manifest.dm"
#include "code\game\objects\effects\mines.dm" #include "code\game\objects\effects\mines.dm"
#include "code\game\objects\effects\misc.dm" #include "code\game\objects\effects\misc.dm"
#include "code\game\objects\effects\motion_echo.dm"
#include "code\game\objects\effects\overlays.dm" #include "code\game\objects\effects\overlays.dm"
#include "code\game\objects\effects\portals.dm" #include "code\game\objects\effects\portals.dm"
#include "code\game\objects\effects\semirandom_mobs_vr.dm" #include "code\game\objects\effects\semirandom_mobs_vr.dm"
@@ -1485,6 +1487,7 @@
#include "code\game\objects\items\devices\scanners\halogen.dm" #include "code\game\objects\items\devices\scanners\halogen.dm"
#include "code\game\objects\items\devices\scanners\health.dm" #include "code\game\objects\items\devices\scanners\health.dm"
#include "code\game\objects\items\devices\scanners\mass_spectrometer.dm" #include "code\game\objects\items\devices\scanners\mass_spectrometer.dm"
#include "code\game\objects\items\devices\scanners\motion_tracker.dm"
#include "code\game\objects\items\devices\scanners\reagents.dm" #include "code\game\objects\items\devices\scanners\reagents.dm"
#include "code\game\objects\items\devices\scanners\sleevemate.dm" #include "code\game\objects\items\devices\scanners\sleevemate.dm"
#include "code\game\objects\items\devices\scanners\slime.dm" #include "code\game\objects\items\devices\scanners\slime.dm"
@@ -3101,6 +3104,7 @@
#include "code\modules\mob\mob_planes.dm" #include "code\modules\mob\mob_planes.dm"
#include "code\modules\mob\mob_planes_vr.dm" #include "code\modules\mob\mob_planes_vr.dm"
#include "code\modules\mob\mob_transformation_simple.dm" #include "code\modules\mob\mob_transformation_simple.dm"
#include "code\modules\mob\motiontracker.dm"
#include "code\modules\mob\say.dm" #include "code\modules\mob\say.dm"
#include "code\modules\mob\say_old.dm" #include "code\modules\mob\say_old.dm"
#include "code\modules\mob\say_vr.dm" #include "code\modules\mob\say_vr.dm"