Files
Bubberstation/code/game/objects/items/pinpointer.dm
Andrew db66ac5c50 Various crew tracking fixes (#56591)
Nanite Monitoring program now makes people trackable as if they have enabled their suit sensors, as stated in the program description.

Now you can track people with Nanites Monitoring program using following devices:

    Crew pinpointer (arrow)
    Crew pinpointer - contractor version (arrow)
    Crew pinpointer (proximity)
    Lifeline radar (modular computers software)

Also fixed multi-Z tracking for the first three devices. When the target is on different Z of the same station, it shows "?" icon.

Also, the stationary crew monitor will now work properly on multi-Z stations.
2021-02-05 17:25:13 -08:00

233 lines
7.6 KiB
Plaintext

//Pinpointers are used to track atoms from a distance as long as they're on the same z-level. The captain and nuke ops have ones that track the nuclear authentication disk.
/obj/item/pinpointer
name = "pinpointer"
desc = "A handheld tracking device that locks onto certain signals."
icon = 'icons/obj/device.dmi'
icon_state = "pinpointer"
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
inhand_icon_state = "electronic"
worn_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron = 500, /datum/material/glass = 250)
var/active = FALSE
var/atom/movable/target //The thing we're searching for
var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination
var/alert = FALSE // TRUE to display things more seriously
var/process_scan = TRUE // some pinpointers change target every time they scan, which means we can't have it change very process but instead when it turns on.
var/icon_suffix = "" // for special pinpointer icons
/obj/item/pinpointer/Initialize()
. = ..()
GLOB.pinpointer_list += src
/obj/item/pinpointer/Destroy()
STOP_PROCESSING(SSfastprocess, src)
GLOB.pinpointer_list -= src
target = null
return ..()
/obj/item/pinpointer/attack_self(mob/living/user)
if(!process_scan) //since it's not scanning on process, it scans here.
scan_for_target()
toggle_on()
user.visible_message("<span class='notice'>[user] [active ? "" : "de"]activates [user.p_their()] pinpointer.</span>", "<span class='notice'>You [active ? "" : "de"]activate your pinpointer.</span>")
/obj/item/pinpointer/proc/toggle_on()
active = !active
playsound(src, 'sound/items/screwdriver2.ogg', 50, TRUE)
if(active)
START_PROCESSING(SSfastprocess, src)
else
target = null
STOP_PROCESSING(SSfastprocess, src)
update_icon()
/obj/item/pinpointer/process()
if(!active)
return PROCESS_KILL
if(process_scan)
scan_for_target()
update_icon()
/obj/item/pinpointer/proc/scan_for_target()
return
/obj/item/pinpointer/update_overlays()
. = ..()
if(!active)
return
if(!target)
. += "pinon[alert ? "alert" : ""]null[icon_suffix]"
return
var/turf/here = get_turf(src)
var/turf/there = get_turf(target)
if(!here || !there || here.z != there.z)
. += "pinon[alert ? "alert" : ""]null[icon_suffix]"
return
. += get_direction_icon(here, there)
///Called by update_icon after sanity. There is a target
/obj/item/pinpointer/proc/get_direction_icon(here, there)
if(get_dist_euclidian(here,there) <= minimum_range)
return "pinon[alert ? "alert" : ""]direct[icon_suffix]"
else
setDir(get_dir(here, there))
switch(get_dist(here, there))
if(1 to 8)
return "pinon[alert ? "alert" : "close"][icon_suffix]"
if(9 to 16)
return "pinon[alert ? "alert" : "medium"][icon_suffix]"
if(16 to INFINITY)
return "pinon[alert ? "alert" : "far"][icon_suffix]"
/obj/item/pinpointer/crew // A replacement for the old crew monitoring consoles
name = "crew pinpointer"
desc = "A handheld tracking device that points to crew suit sensors."
icon_state = "pinpointer_crew"
custom_price = PAYCHECK_MEDIUM * 4
custom_premium_price = PAYCHECK_MEDIUM * 6
var/has_owner = FALSE
var/pinpointer_owner = null
var/ignore_suit_sensor_level = FALSE /// Do we find people even if their suit sensors are turned off
/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H)
var/turf/here = get_turf(src)
var/turf/there = get_turf(H)
if(here && there && (there.z == here.z || (is_station_level(here.z) && is_station_level(there.z)))) // Device and target should be on the same level or different levels of the same station
if (H in GLOB.nanite_sensors_list)
return TRUE
if (istype(H.w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = H.w_uniform
if(U.has_sensor && (U.sensor_mode >= SENSOR_COORDS || ignore_suit_sensor_level)) // Suit sensors must be on maximum or a contractor pinpointer
return TRUE
return FALSE
/obj/item/pinpointer/crew/attack_self(mob/living/user)
if(active)
toggle_on()
user.visible_message("<span class='notice'>[user] deactivates [user.p_their()] pinpointer.</span>", "<span class='notice'>You deactivate your pinpointer.</span>")
return
if (has_owner && !pinpointer_owner)
pinpointer_owner = user
if (pinpointer_owner && pinpointer_owner != user)
to_chat(user, "<span class='notice'>The pinpointer doesn't respond. It seems to only recognise its owner.</span>")
return
var/list/name_counts = list()
var/list/names = list()
for(var/i in GLOB.human_list)
var/mob/living/carbon/human/H = i
if(!trackable(H))
continue
var/crewmember_name = "Unknown"
if(H.wear_id)
var/obj/item/card/id/I = H.wear_id.GetID()
if(I?.registered_name)
crewmember_name = I.registered_name
while(crewmember_name in name_counts)
name_counts[crewmember_name]++
crewmember_name = text("[] ([])", crewmember_name, name_counts[crewmember_name])
names[crewmember_name] = H
name_counts[crewmember_name] = 1
if(!names.len)
user.visible_message("<span class='notice'>[user]'s pinpointer fails to detect a signal.</span>", "<span class='notice'>Your pinpointer fails to detect a signal.</span>")
return
var/A = input(user, "Person to track", "Pinpoint") in sortList(names)
if(!A || QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated())
return
target = names[A]
toggle_on()
user.visible_message("<span class='notice'>[user] activates [user.p_their()] pinpointer.</span>", "<span class='notice'>You activate your pinpointer.</span>")
/obj/item/pinpointer/crew/scan_for_target()
if(target)
if(ishuman(target))
var/mob/living/carbon/human/H = target
if(!trackable(H))
target = null
if(!target) //target can be set to null from above code, or elsewhere
active = FALSE
/obj/item/pinpointer/crew/prox //Weaker version of crew monitor primarily for EMT
name = "proximity crew pinpointer"
desc = "A handheld tracking device that displays its proximity to crew suit sensors."
icon_state = "pinpointer_crewprox"
custom_price = PAYCHECK_MEDIUM * 3
/obj/item/pinpointer/crew/prox/get_direction_icon(here, there)
var/size = ""
if(here == there)
size = "small"
else
switch(get_dist(here, there))
if(1 to 4)
size = "xtrlarge"
if(5 to 16)
size = "large"
//17 through 28 use the normal pinion, "pinondirect"
if(29 to INFINITY)
size = "small"
return "pinondirect[size]"
/obj/item/pinpointer/pair
name = "pair pinpointer"
desc = "A handheld tracking device that locks onto its other half of the matching pair."
var/other_pair
/obj/item/pinpointer/pair/Destroy()
other_pair = null
. = ..()
/obj/item/pinpointer/pair/scan_for_target()
target = other_pair
/obj/item/pinpointer/pair/examine(mob/user)
. = ..()
if(!active || !target)
return
var/mob/mob_holder = get(target, /mob)
if(istype(mob_holder))
. += "Its pair is being held by [mob_holder]."
return
/obj/item/storage/box/pinpointer_pairs
name = "pinpointer pair box"
/obj/item/storage/box/pinpointer_pairs/PopulateContents()
var/obj/item/pinpointer/pair/A = new(src)
var/obj/item/pinpointer/pair/B = new(src)
A.other_pair = B
B.other_pair = A
/obj/item/pinpointer/shuttle
name = "fugitive pinpointer"
desc = "A handheld tracking device that locates the bounty hunter shuttle for quick escapes."
icon_state = "pinpointer_hunter"
icon_suffix = "_hunter"
var/obj/shuttleport
/obj/item/pinpointer/shuttle/Initialize(mapload)
. = ..()
shuttleport = SSshuttle.getShuttle("huntership")
/obj/item/pinpointer/shuttle/scan_for_target()
target = shuttleport
/obj/item/pinpointer/shuttle/Destroy()
shuttleport = null
. = ..()