mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2025-12-24 17:11:40 +00:00
293 lines
11 KiB
Plaintext
293 lines
11 KiB
Plaintext
// Device used to implant, remove or read specimen tags.
|
|
|
|
/* Notes on specimen tagger and expected flow:
|
|
* - Xenofauna player uses tagger (/obj/item/specimen_tagger) on appropriate critter (appropriate type, has cataloguer info).
|
|
* - Tag (/obj/item/gps/specimen_tag) is created and registered in the critter.
|
|
* - Xenofauna players can then track the tag via GPS to scan, remove, etc.
|
|
* - When persistent specimens are committed, tags will be loaded and assigned to mobs at world init.
|
|
*/
|
|
|
|
/obj/item/specimen_tagger
|
|
name = "specimen tagger"
|
|
desc = "A handheld device used to implant, remove and read xenofauna tracking tags from local specimens. Not for use on crewmembers."
|
|
icon = 'icons/obj/specimen_tagger.dmi'
|
|
icon_state = "tagger"
|
|
force = 0
|
|
item_flags = NOBLUDGEON
|
|
var/tag_id = "FAUNA0"
|
|
var/static/list/unsuitable_tag_types = list(
|
|
// Do not specimen tag your crewmates
|
|
/mob/living/carbon,
|
|
/mob/living/simple_mob/animal/sif/grafadreka/trained,
|
|
// Do not specimen tag pre-mapped critters
|
|
/mob/living/simple_mob/animal/passive/opossum/poppy,
|
|
/mob/living/simple_mob/animal/passive/dog/corgi/Ian,
|
|
/mob/living/simple_mob/animal/passive/cat/runtime
|
|
)
|
|
|
|
/obj/item/specimen_tagger/attack_self(mob/user)
|
|
var/new_tag = input("Please enter desired tag.", name, tag_id) as text
|
|
if(QDELETED(src) || QDELETED(user) || user.incapacitated() || loc != user)
|
|
return TRUE
|
|
new_tag = uppertext(copytext(sanitize(new_tag), 1, 11))
|
|
if(!length(new_tag))
|
|
return TRUE
|
|
tag_id = new_tag
|
|
to_chat(usr, "You set the tracker tag to '[tag_id]'.")
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/examine(mob/user)
|
|
. = ..()
|
|
. += "Use this on a living animal on help intent to read an existing tracker, grab intent to tag an animal with a tracker, and any other intent to remove an existing tracker."
|
|
|
|
/obj/item/specimen_tagger/attack(mob/living/M, mob/living/user, target_zone, attack_modifier)
|
|
switch(user.a_intent)
|
|
if(I_HELP)
|
|
try_read_tag(user, M)
|
|
if(I_GRAB)
|
|
try_implant_tag(user, M)
|
|
else
|
|
try_remove_tag(user, M)
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/proc/try_read_tag(var/mob/user, var/mob/living/target)
|
|
var/obj/item/gps/specimen_tag/xenotag = locate() in target
|
|
if(!istype(xenotag) || !xenotag.has_been_implanted())
|
|
to_chat(user, SPAN_WARNING("\The [target] has not been tagged."))
|
|
return FALSE
|
|
to_chat(user, "<b>Specimen data for [xenotag.gps_tag]:</b>")
|
|
to_chat(user, "<b>Species:</b> [target.real_name]")
|
|
to_chat(user, "<b>Tag duration:</b> [xenotag.age] shift\s")
|
|
to_chat(user, "<b>Tagged by:</b> [xenotag.implanted_by]")
|
|
to_chat(user, "<b>Physical notes:</b> [xenotag.physical_info]")
|
|
to_chat(user, "<b>Behavioral notes:</b> [xenotag.behavioral_info]")
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/proc/check_can_tag(var/mob/user, var/mob/living/target)
|
|
if(QDELETED(target) || !istype(target) || target.stat == DEAD || target.isSynthetic())
|
|
to_chat(user, SPAN_WARNING("Xenofauna specimens need to be living organic creatures."))
|
|
return FALSE
|
|
if(!LAZYLEN(target.get_catalogue_data()))
|
|
to_chat(user, SPAN_WARNING("There's no scientific reason to tag \the [target]."))
|
|
return FALSE
|
|
if(target.key || target.client || is_type_in_list(target, unsuitable_tag_types))
|
|
to_chat(user, SPAN_WARNING("\The [target] is not suitable for tagging."))
|
|
return FALSE
|
|
var/obj/item/gps/specimen_tag/xenotag = locate(/obj/item/gps/specimen_tag) in target
|
|
if(istype(xenotag) && xenotag.has_been_implanted())
|
|
to_chat(user, SPAN_WARNING("\The [target] has already been tagged."))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/proc/try_implant_tag(var/mob/user, var/mob/living/target)
|
|
if(!check_can_tag(user, target))
|
|
return FALSE
|
|
user.visible_message(SPAN_NOTICE("\The [user] begins tagging \the [target] with \the [src]..."))
|
|
if(!do_after(user, 3 SECONDS, target) || !check_can_tag(user, target))
|
|
return FALSE
|
|
var/obj/item/gps/specimen_tag/xenotag = new
|
|
xenotag.SetTag(tag_id)
|
|
xenotag.implanted_by = user.real_name
|
|
if(user.mind)
|
|
var/user_title = user.mind.assigned_role || user.mind.role_alt_title
|
|
if(user_title)
|
|
xenotag.implanted_by = "[xenotag.implanted_by], [user_title]"
|
|
|
|
xenotag.implant(target)
|
|
user.visible_message(SPAN_NOTICE("\The [user] tags \the [target] with \a [xenotag]!"))
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/proc/can_remove_tag(var/mob/user, var/mob/living/target)
|
|
if(!istype(target))
|
|
to_chat(user, SPAN_WARNING("\The [target] is not a xenofauna specimen."))
|
|
return FALSE
|
|
var/obj/item/gps/specimen_tag/xenotag = locate() in target
|
|
if(!istype(xenotag) || !xenotag.has_been_implanted())
|
|
to_chat(user, SPAN_WARNING("\The [target] has not been tagged."))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/specimen_tagger/proc/try_remove_tag(var/mob/user, var/mob/living/target)
|
|
if(!can_remove_tag(user, target))
|
|
return FALSE
|
|
var/obj/item/gps/specimen_tag/xenotag = locate() in target
|
|
if(!istype(xenotag))
|
|
return FALSE
|
|
user.visible_message(SPAN_NOTICE("\The [user] starts removing \the [xenotag] from \the [target] with \the [src]..."))
|
|
if(!do_after(user, 3 SECONDS, target) || !can_remove_tag(user, target))
|
|
return FALSE
|
|
if(!istype(xenotag))
|
|
return FALSE
|
|
qdel(xenotag)
|
|
user.visible_message(SPAN_NOTICE("\The [user] removes \the [xenotag] from \the [target]!"))
|
|
return TRUE
|
|
|
|
// Specimen tag itself.
|
|
/obj/item/gps/specimen_tag
|
|
name = "xenofauna tracker"
|
|
gps_tag = "FAUNA0"
|
|
icon = 'icons/obj/specimen_tag.dmi'
|
|
icon_state = "gps-tag"
|
|
w_class = ITEMSIZE_TINY
|
|
tag_category = "XENOFAUNA"
|
|
|
|
var/age = 0
|
|
var/mob/living/implanted_in
|
|
var/implanted_by
|
|
var/physical_info = "No notes recorded."
|
|
var/behavioral_info = "No notes recorded."
|
|
|
|
/obj/item/gps/specimen_tag/Initialize(mapload, _age, _implanted_by, _specimen_id, _specimen_gender, _physical_info, _behavioral_info, _specimen_type)
|
|
// If we have a specimen, set up our data.
|
|
if(_specimen_type)
|
|
var/mob/living/critter = new _specimen_type(get_turf(src))
|
|
implant(critter, TRUE)
|
|
if(_specimen_gender)
|
|
critter.gender = _specimen_gender
|
|
if(_age)
|
|
age = _age
|
|
if(_specimen_id)
|
|
gps_tag = _specimen_id
|
|
if(_physical_info)
|
|
physical_info = _physical_info
|
|
if(_behavioral_info)
|
|
behavioral_info = _behavioral_info
|
|
if(_implanted_by)
|
|
implanted_by = _implanted_by
|
|
. = ..()
|
|
if(!tracking)
|
|
toggletracking()
|
|
|
|
/obj/item/gps/specimen_tag/Destroy()
|
|
clear_implanted()
|
|
// TODO: persistent tags
|
|
// SSpersistence.forget_value(src, /datum/persistent/specimen)
|
|
. = ..()
|
|
|
|
/obj/item/gps/specimen_tag/Move()
|
|
. = ..()
|
|
if(implanted_in && loc != implanted_in)
|
|
clear_implanted()
|
|
|
|
// Specimen tags are just for tracking, they don't work as held GPS.
|
|
/obj/item/gps/specimen_tag/attack_hand(mob/living/user)
|
|
toggletracking(user)
|
|
return TRUE
|
|
/obj/item/gps/specimen_tag/check_visible_to_holder()
|
|
return FALSE
|
|
/obj/item/gps/specimen_tag/create_compass()
|
|
return
|
|
/obj/item/gps/specimen_tag/display_list()
|
|
return list()
|
|
|
|
/obj/item/gps/specimen_tag/proc/has_been_implanted()
|
|
return !QDELETED(implanted_in) && istype(implanted_in) && loc == implanted_in
|
|
|
|
/obj/item/gps/specimen_tag/proc/implant(var/mob/target, var/implanted_in_init = FALSE)
|
|
forceMove(target)
|
|
implanted_in = target
|
|
GLOB.destroyed_event.register(implanted_in, src, /obj/item/gps/specimen_tag/proc/clear_implanted)
|
|
// TODO: persistent tags
|
|
// SSpersistence.track_value(src, /datum/persistent/specimen)
|
|
if(!implanted_in_init)
|
|
generate_critter_info()
|
|
|
|
/obj/item/gps/specimen_tag/proc/generate_critter_info()
|
|
|
|
var/list/possible_physical_info
|
|
var/list/possible_behavioral_info
|
|
var/list/catalogue_data = implanted_in.get_catalogue_data()
|
|
for(var/catalogue_data_type in catalogue_data)
|
|
if(ispath(catalogue_data_type, /datum/category_item/catalogue/fauna))
|
|
var/datum/category_item/catalogue/fauna/fauna_data = GLOB.catalogue_data.resolve_item(catalogue_data_type)
|
|
var/notes = fauna_data.get_fauna_physical_notes()
|
|
if(!isnull(notes))
|
|
LAZYDISTINCTADD(possible_physical_info, notes)
|
|
notes = fauna_data.get_fauna_behavior_notes()
|
|
if(!isnull(notes))
|
|
LAZYDISTINCTADD(possible_behavioral_info, notes)
|
|
|
|
if(LAZYLEN(possible_physical_info))
|
|
physical_info = pick(possible_physical_info)
|
|
else
|
|
physical_info = "No notes recorded."
|
|
|
|
if(LAZYLEN(possible_behavioral_info))
|
|
behavioral_info = pick(possible_behavioral_info)
|
|
else
|
|
behavioral_info = "No notes recorded."
|
|
|
|
/obj/item/gps/specimen_tag/proc/clear_implanted()
|
|
if(implanted_in)
|
|
GLOB.destroyed_event.unregister(implanted_in, src)
|
|
implanted_in = null
|
|
|
|
/obj/item/gps/specimen_tag/proc/update_from_animal()
|
|
return
|
|
|
|
// Mob helpers/overrides.
|
|
/mob/living/examine(mob/user, infix, suffix)
|
|
. = ..()
|
|
var/obj/item/gps/specimen_tag/xenotag = locate() in src
|
|
if(istype(xenotag) && xenotag.has_been_implanted())
|
|
. += "\The [src] has been tagged with \a [xenotag]."
|
|
|
|
// Lists of strings used by the specimen tracking system to add flavour to
|
|
// specimens. Static lists in procs to allow overriding while also not putting
|
|
// a massive string list on every single cataloguer fauna entry.
|
|
/datum/category_item/catalogue/fauna/proc/get_fauna_physical_notes()
|
|
var/static/list/fauna_physical_notes = list(
|
|
"Perpetually smells like mold no matter what we do about it.",
|
|
"Perpetually smells like mildew no matter what we do about it.",
|
|
"Perpetually smells like sifsap no matter what we do about it.",
|
|
"Seems to always have an itch in the spot it can't reach.",
|
|
"Vocalizations are notably harsh and loud for the species.",
|
|
"Vocalizations are notably soft and quiet for the species.",
|
|
"Has a notch in its left ear.",
|
|
"Has a notch in its right ear.",
|
|
"Is missing its left ear.",
|
|
"Is missing its right ear.",
|
|
"Is missing its left eye.",
|
|
"Is missing its right eye.",
|
|
"Has had the very tip of its tail chewed off.",
|
|
"Might have trouble with its hearing.",
|
|
"Is more scar tissue than animal at this point.",
|
|
"Walks with a limp.",
|
|
"Has two differently colored eyes.",
|
|
"Might have a cold...",
|
|
"Is allergic to nuts.",
|
|
"Is allergic to berries.",
|
|
"Is allergic to dairy."
|
|
)
|
|
return fauna_physical_notes
|
|
|
|
/datum/category_item/catalogue/fauna/proc/get_fauna_behavior_notes()
|
|
var/static/list/fauna_behavior_notes = list(
|
|
"Likes rolling around in the moss with reckless abandon.",
|
|
"Enjoys munching on frostbelles the most, as a treat.",
|
|
"Enjoys munching on wabback the most, as a treat.",
|
|
"Enjoys munching on eyebulbs the most, as a treat.",
|
|
"Constantly sniffs around at everything new.",
|
|
"Seems super friendly! Probably won't bite. Probably.",
|
|
"Seems rather stand-offish. Mind the personal bubble.",
|
|
"Loves a good back scritch.",
|
|
"Loves a good head scritch.",
|
|
"Loves a good behind the ear scritch.",
|
|
"Seems to hate the world and everything in it.",
|
|
"Just tolerates being pet, but certainly doesn't enjoy it.",
|
|
"Often sticks its head into snowbanks to contemplate the state of things.",
|
|
"Enjoys singing along to songs only it can hear. Mostly just sounds like an animal wailing.",
|
|
"Would rather be fishing.",
|
|
"Partakes in many siestas.",
|
|
"Struggles with object permanence.",
|
|
"Is very picky about food; maybe it's a texture thing.",
|
|
"Is a sentient garbage disposal for anything even remotely edible.",
|
|
"Loves swimming and splashing around in water.",
|
|
"Sinks like a rock the moment it enters water.",
|
|
"Gets lost easily.",
|
|
"Enjoys soft things. It'd have a bed of plushies if it knew what a bed was.",
|
|
"Never seems to be in a rush to go anywhere...",
|
|
"Has gotta go fast at all times."
|
|
)
|
|
return fauna_behavior_notes
|