diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 5faf4792d7..a8004fff4c 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -270,6 +270,9 @@ #define HEARING_SPANS 6 #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) #define COMSIG_MOVABLE_DISPOSING "movable_disposing" diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index a523f62dda..5d92678e30 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -167,6 +167,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_NIGHTSHIFT 5 #define FIRE_PRIORITY_PLANTS 5 #define FIRE_PRIORITY_VIS 5 +#define FIRE_PRIORITY_MOTIONTRACKER 6 #define FIRE_PRIORITY_ORBIT 7 #define FIRE_PRIORITY_VOTE 8 #define FIRE_PRIORITY_INSTRUMENTS 9 diff --git a/code/controllers/subsystems/motion_tracker.dm b/code/controllers/subsystems/motion_tracker.dm new file mode 100644 index 0000000000..6900e01ac5 --- /dev/null +++ b/code/controllers/subsystems/motion_tracker.dm @@ -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) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index d8b2b46c28..59f6da530a 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1339,6 +1339,8 @@ About the new airlock wires panel: 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) + SSmotiontracker.ping(src,100) + if(src.closeOther != null && istype(src.closeOther, /obj/machinery/door/airlock/) && !src.closeOther.density) src.closeOther.close() return ..() @@ -1473,6 +1475,9 @@ About the new airlock wires panel: if(distance <= world.view * 2) 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) + + SSmotiontracker.ping(src,100) + for(var/turf/turf in locs) var/obj/structure/window/killthis = (locate(/obj/structure/window) in turf) if(killthis) diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 80262c4443..1d7325f3a7 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -141,6 +141,7 @@ domutcheck(M,null) M.UpdateAppearance() visible_message("\The [src.name] flashes violently before disintegrating!") + SSmotiontracker.ping(src,100) spawn(0) qdel(s) qdel(src) @@ -158,6 +159,7 @@ if(istype(M)) M.Stun(30) visible_message("\The [src.name] flashes violently before disintegrating!") + SSmotiontracker.ping(src,100) spawn(0) qdel(s) qdel(src) @@ -173,6 +175,7 @@ if(!target.blocks_air) target.assume_gas(GAS_N2O, 30) visible_message("\The [src.name] detonates!") + SSmotiontracker.ping(src,100) spawn(0) qdel(src) @@ -188,6 +191,7 @@ target.assume_gas(GAS_PHORON, 30) target.hotspot_expose(1000, CELL_VOLUME) visible_message("\The [src.name] detonates!") + SSmotiontracker.ping(src,100) spawn(0) qdel(src) @@ -229,6 +233,7 @@ return 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!") + SSmotiontracker.ping(src,100) spawn(0) qdel(s) qdel(src) @@ -258,6 +263,7 @@ s.set_up(3, 1, src) s.start() 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 spawn(0) qdel(src) @@ -279,6 +285,7 @@ M.adjust_fire_stacks(5) M.fire_act() visible_message("\The [src.name] bursts into flames!") + SSmotiontracker.ping(src,100) spawn(0) qdel(src) @@ -300,6 +307,7 @@ else explosion(loc, 0, 0, 2, 2) visible_message("\The [src.name] detonates!") + SSmotiontracker.ping(src,100) qdel(s) qdel(src) diff --git a/code/game/objects/effects/motion_echo.dm b/code/game/objects/effects/motion_echo.dm new file mode 100644 index 0000000000..19face9b88 --- /dev/null +++ b/code/game/objects/effects/motion_echo.dm @@ -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 diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 83338dbd45..51b4c88c36 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -223,6 +223,7 @@ if(prob(50)) src.visible_message(span_notice("You hear something squeezing through the ventilation ducts."),2) + SSmotiontracker.ping(src,10) sleep(travel_time) if(!exit_vent || exit_vent.welded) @@ -269,6 +270,7 @@ walk_to(src, target_atom, 5) if(prob(25)) src.visible_message(span_notice("\The [src] skitters[pick(" away"," around","")].")) + SSmotiontracker.ping(src,10) else if(amount_grown < 75 && prob(5)) //vent crawl! for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) diff --git a/code/game/objects/items/devices/scanners/motion_tracker.dm b/code/game/objects/items/devices/scanners/motion_tracker.dm new file mode 100644 index 0000000000..b2e38c665c --- /dev/null +++ b/code/game/objects/items/devices/scanners/motion_tracker.dm @@ -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() diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 95c2afb189..99ca4517ec 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -87,6 +87,7 @@ var/turf/T = get_turf(src) if(T) T.hotspot_expose(700,125) + SSmotiontracker.ping(src,100) /obj/item/grenade/attackby(obj/item/W as obj, mob/user as mob) diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index cbd31a5b5b..197c043825 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -131,6 +131,7 @@ span_danger("You step on \the [src]!"), span_infoplain(span_bold("You hear a loud metallic snap!")) ) + SSmotiontracker.ping(src,100) // Clunk! attack_mob(L) if(!has_buckled_mobs()) anchored = FALSE diff --git a/code/game/sound.dm b/code/game/sound.dm index 7e61fca82f..2e64ed2b4b 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -32,6 +32,7 @@ if(!ignore_walls && !can_see(turf_source, T, length = maxdistance * 2)) 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) /mob/proc/check_sound_preference(list/preference) diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index 2c9682d941..7223b1ea7e 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -85,6 +85,7 @@ src.visible_message(span_infoplain(span_bold("\The [src.name]") + " [deathmessage]")) set_stat(DEAD) + SSmotiontracker.ping(src,80) update_canmove() diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index cd8043e008..450e25e2d7 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -195,18 +195,14 @@ var/obj/item/clothing/glasses/V = rig.visor.vision.glasses compiled_vis |= V.enables_planes - //VOREStation Add - NIF Support if(nif) compiled_vis |= nif.planes_visible() //event hud if(vantag_hud) compiled_vis |= VIS_CH_VANTAG - //VOREStation Add End - //Vore Stomach addition start. This goes here. if(stomach_vision) compiled_vis += VIS_CH_STOMACH - //Vore Stomach addition end //CHOMPAdd Start Soulcatcher if(soulgem?.flag_check(SOULGEM_SEE_SR_SOULS)) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 240f2f9cd6..c9e6189c58 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -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_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/has_vibration_sense = FALSE // Motion tracker subsystem // Death vars. var/meat_type = /obj/item/reagent_containers/food/snacks/meat/human diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm index 770ffc50ce..66455ba17f 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_vr.dm @@ -17,6 +17,7 @@ base_species = "Xenochimera" selects_bodytype = SELECTS_BODYTYPE_CUSTOM digi_allowed = TRUE + has_vibration_sense = TRUE num_alternate_languages = 3 species_language = null @@ -26,7 +27,7 @@ icobase_tail = 1 inherent_verbs = list( /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/lick_wounds, /mob/living/carbon/human/proc/shapeshifter_reassemble) //Xenochimera get all the special verbs since they can't select traits. diff --git a/code/modules/mob/living/carbon/human/species/station/teshari.dm b/code/modules/mob/living/carbon/human/species/station/teshari.dm index 5034e26b86..7aced25c20 100644 --- a/code/modules/mob/living/carbon/human/species/station/teshari.dm +++ b/code/modules/mob/living/carbon/human/species/station/teshari.dm @@ -59,7 +59,8 @@ mob_size = MOB_SMALL pass_flags = PASSTABLE 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 blood_volume = 400 hunger_factor = 0.2 @@ -141,7 +142,7 @@ ) inherent_verbs = list( - /mob/living/carbon/human/proc/sonar_ping, + ///mob/living/carbon/human/proc/sonar_ping, /mob/living/proc/hide ) diff --git a/code/modules/mob/living/carbon/human/species/station/teshari_vr.dm b/code/modules/mob/living/carbon/human/species/station/teshari_vr.dm index 4657b6ae66..88e33dfa57 100644 --- a/code/modules/mob/living/carbon/human/species/station/teshari_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/teshari_vr.dm @@ -21,7 +21,7 @@ End Chomp Edit */ vore_belly_default_variant = "T" //Teshari belly sprite inherent_verbs = list( - /mob/living/carbon/human/proc/sonar_ping, + ///mob/living/carbon/human/proc/sonar_ping, /mob/living/proc/hide, /mob/living/proc/toggle_pass_table ) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index 281f024344..0e41e91c31 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -384,3 +384,24 @@ is_genetrait = TRUE hidden = FALSE 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() diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 675da18d5a..8f7d977825 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -279,6 +279,8 @@ default behaviour is: /mob/living/Moved(var/atom/oldloc, direct, forced, movetime) . = ..() handle_footstep(loc) + if(!forced && movetime) + SSmotiontracker?.ping(src) // Incase of before init "turf enter gravity" this is ?, unfortunately. // Begin VOREstation edit if(is_shifted) is_shifted = FALSE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 425d970de8..084ab5c12e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -39,6 +39,8 @@ vore_selected = null // from code/modules/vore/eating/mob_vr focus = null + motiontracker_unsubscribe(TRUE) // Force unsubscribe + if(mind) if(mind.current == src) mind.current = null diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 388ceaf0cc..b7217a530d 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -244,3 +244,5 @@ var/list/resistances 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 diff --git a/code/modules/mob/motiontracker.dm b/code/modules/mob/motiontracker.dm new file mode 100644 index 0000000000..c612330e46 --- /dev/null +++ b/code/modules/mob/motiontracker.dm @@ -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") diff --git a/code/modules/research/designs/misc.dm b/code/modules/research/designs/misc.dm index febea0e2ac..c3a44c1716 100644 --- a/code/modules/research/designs/misc.dm +++ b/code/modules/research/designs/misc.dm @@ -186,3 +186,11 @@ build_path = /obj/item/juke_remote sort_string = "TCVAE" 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 diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index cb756c9b5b..62cd57f9cb 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/vorestation.dme b/vorestation.dme index b21624de05..5b2f885b3f 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -401,6 +401,7 @@ #include "code\controllers\subsystems\mapping.dm" #include "code\controllers\subsystems\media_tracks.dm" #include "code\controllers\subsystems\mobs.dm" +#include "code\controllers\subsystems\motion_tracker.dm" #include "code\controllers\subsystems\nightshift.dm" #include "code\controllers\subsystems\overlays_ch.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\mines.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\portals.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\health.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\sleevemate.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_vr.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_old.dm" #include "code\modules\mob\say_vr.dm"