mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-14 02:43:16 +00:00
* Adds a Fugitive Locator machine to each bounty hunter shuttle (#77270) ## About The Pull Request This adds a simple but important piece of machinery to every bounty hunter shuttle, the Fugitive Locator. Functioning near identically to the Booty Locator (the pirate loot tracker machine), this machine selects a random fugitive, reads their _real_ name, and reads the name of the area they occupy. It has a 40 second cooldown, and will track a random fugitive each time.  The locator will report any location you are in, be it space, lavaland, some distant ruin or the wastes, the hunters will always know where to start looking for you. Since a lot of this code was referenced from the booty locator, that has also been touched up slightly. A bit of code was moved out of the way, and the cooldown now uses a COOLDOWN_DECLARE() instead of time tracking. Lastly, this renames the "fugitive tracker" (which tracks the bounty hunter shuttle) to the "bounty shuttle tracker", because the name is misleading. ## Why It's Good For The Game This seeks to solve the issue of fugitive hunters not knowing who their targets are, and having no way of knowing where their targets may be. I decided on this particular implementation because it gives only a vague area and identity for the fugitives to work with. This allows for fugitives to make use of hiding spots, disguises, potted plants, and other popular tactics that would be invalidated if the mechanism were a hud tracker or pinpointer of sorts. The intent isn't to lead hunters straight to their targets, but to give them somewhere to start. As for the impact this may have on the Fugitive experience -- It will be more difficult to coast through the round in a single hiding spot or disappear from the z-level and never be heard from again. Directing the hunters towards the fugitives should lead to a more active experience, encouraging evasion over just plain hiding. Just remember: the less fugitives there are, the easier it is for the hunters to consistently track your area, so make sure you're looking out for each other! ## Changelog 🆑 add: Fugitive shuttles now have a "Fugitive Tracker" machine, which gives a readout on the location of a random fugitive on a 40 second cooldown. spellcheck: Renames the fugitive pinpointer to the bounty shuttle pinpointer. /🆑 * Adds a Fugitive Locator machine to each bounty hunter shuttle --------- Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
254 lines
8.4 KiB
Plaintext
254 lines
8.4 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 = "pinpointer"
|
|
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
|
|
throw_speed = 3
|
|
throw_range = 7
|
|
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2.5)
|
|
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(mapload)
|
|
. = ..()
|
|
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_notice("[user] [active ? "" : "de"]activates [user.p_their()] pinpointer."), span_notice("You [active ? "" : "de"]activate your pinpointer."))
|
|
|
|
/obj/item/pinpointer/examine(mob/user)
|
|
. = ..()
|
|
if(target)
|
|
. += "It is currently tracking [target]."
|
|
|
|
/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_appearance()
|
|
|
|
/obj/item/pinpointer/process()
|
|
if(!active)
|
|
return PROCESS_KILL
|
|
if(process_scan)
|
|
scan_for_target()
|
|
update_appearance()
|
|
|
|
/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"
|
|
worn_icon_state = "pinpointer_crew"
|
|
custom_price = PAYCHECK_CREW * 4
|
|
custom_premium_price = PAYCHECK_CREW * 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 (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_notice("[user] deactivates [user.p_their()] pinpointer."), span_notice("You deactivate your pinpointer."))
|
|
return
|
|
|
|
if (has_owner && !pinpointer_owner)
|
|
pinpointer_owner = user
|
|
|
|
if (pinpointer_owner && pinpointer_owner != user)
|
|
to_chat(user, span_notice("The pinpointer doesn't respond. It seems to only recognise its owner."))
|
|
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 = "[crewmember_name] ([name_counts[crewmember_name]])"
|
|
names[crewmember_name] = H
|
|
name_counts[crewmember_name] = 1
|
|
|
|
if(!length(names))
|
|
user.visible_message(span_notice("[user]'s pinpointer fails to detect a signal."), span_notice("Your pinpointer fails to detect a signal."))
|
|
return
|
|
var/pinpoint_target = tgui_input_list(user, "Person to track", "Pinpoint", sort_list(names))
|
|
if(isnull(pinpoint_target))
|
|
return
|
|
if(isnull(names[pinpoint_target]))
|
|
return
|
|
if(QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated())
|
|
return
|
|
target = names[pinpoint_target]
|
|
toggle_on()
|
|
user.visible_message(span_notice("[user] activates [user.p_their()] pinpointer."), span_notice("You activate your pinpointer."))
|
|
|
|
/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/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 = "bounty shuttle pinpointer"
|
|
desc = "A handheld tracking device that locates the bounty hunter shuttle for quick escapes."
|
|
icon_state = "pinpointer_hunter"
|
|
worn_icon_state = "pinpointer_black"
|
|
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
|
|
. = ..()
|
|
|
|
///list of all sheets with sniffable = TRUE for the sniffer to locate
|
|
GLOBAL_LIST_EMPTY(sniffable_sheets)
|
|
|
|
/obj/item/pinpointer/material_sniffer
|
|
name = "material sniffer"
|
|
desc = "A handheld tracking device that locates sheets of glass and iron."
|
|
icon_state = "pinpointer_sniffer"
|
|
worn_icon_state = "pinpointer_black"
|
|
|
|
/obj/item/pinpointer/material_sniffer/scan_for_target()
|
|
if(target || !GLOB.sniffable_sheets.len)
|
|
return
|
|
var/obj/item/stack/sheet/new_sheet_target
|
|
var/closest_distance = INFINITY
|
|
for(var/obj/item/stack/sheet/potential_sheet as anything in GLOB.sniffable_sheets)
|
|
// not enough for lag reasons, and shouldn't even be on this
|
|
if(potential_sheet.amount < 10)
|
|
GLOB.sniffable_sheets -= potential_sheet
|
|
continue
|
|
//held by someone
|
|
if(isliving(potential_sheet.loc))
|
|
continue
|
|
//not on scanner's z
|
|
if(potential_sheet.z != z)
|
|
continue
|
|
var/distance_from_sniffer = get_dist(src, potential_sheet)
|
|
if(distance_from_sniffer < closest_distance)
|
|
closest_distance = distance_from_sniffer
|
|
new_sheet_target = potential_sheet
|
|
if(!new_sheet_target)
|
|
target = null
|
|
return
|
|
say("Located [new_sheet_target.amount] [new_sheet_target.singular_name]s!")
|
|
target = new_sheet_target
|