Files
Bubberstation/code/game/objects/items/devices/scanners/sequence_scanner.dm
SkyratBot d7dba29acb [MIRROR] Genetic Sequencers now can scan genetic makeup, not just mutations [MDB IGNORE] (#22643)
* Genetic Sequencers now can scan genetic makeup, not just mutations (#76567)

## About The Pull Request
Adds a feature to the genetic sequencer geneticists has, you can now
right click someone to copy their genetic makeup and export it to the
DNA scanner console.
## Why It's Good For The Game
QOL but mostly more ammo for antagonist geneticists, the enzyme feature
of genetics is seldom used since you need someone to willingly (if they
are alive) step inside the machine which is close to impossible. Not to
mention the esoteric way you can change them.

I feel like the art disguising and deception has been dying and tried to
make it easier in #76508 which got closed for stealing genetics neesh.
this fixes that problem while still serving the same end goal in an
albeit less soulful way
## Changelog
🆑
add: You can now use the genetic sequencer secondary click (RMB) to scan
someone
/🆑

* Genetic Sequencers now can scan genetic makeup, not just mutations

---------

Co-authored-by: Singul0 <127663818+Singul0@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
2023-07-23 14:10:10 -04:00

165 lines
6.4 KiB
Plaintext

/obj/item/sequence_scanner
name = "genetic sequence scanner"
icon = 'icons/obj/device.dmi'
icon_state = "gene"
inhand_icon_state = "healthanalyzer"
worn_icon_state = "healthanalyzer"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
desc = "A hand-held scanner for analyzing someones gene sequence on the fly. Use on a DNA console to update the internal database."
flags_1 = CONDUCT_1
item_flags = NOBLUDGEON
slot_flags = ITEM_SLOT_BELT
throwforce = 3
w_class = WEIGHT_CLASS_TINY
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2)
var/list/discovered = list() //hit a dna console to update the scanners database
var/list/buffer
var/ready = TRUE
var/cooldown = 200
/// genetic makeup data that's scanned
var/list/genetic_makeup_buffer = list()
/obj/item/sequence_scanner/examine(mob/user)
. = ..()
. += span_notice("Use primary attack to scan mutations, Secondary attack to scan genetic makeup")
if(LAZYLEN(genetic_makeup_buffer) > 0)
. += span_notice("It has the genetic makeup of \"[genetic_makeup_buffer["name"]]\" stored inside its buffer")
/obj/item/sequence_scanner/attack(mob/living/target, mob/living/carbon/human/user)
add_fingerprint(user)
//no scanning if its a husk or DNA-less Species
if (!HAS_TRAIT(target, TRAIT_GENELESS) && !HAS_TRAIT(target, TRAIT_BADDNA))
user.visible_message(span_notice("[user] analyzes [target]'s genetic sequence."))
balloon_alert(user, "sequence analyzed")
playsound(user.loc, 'sound/items/healthanalyzer.ogg', 50) // close enough
gene_scan(target, user)
else
user.visible_message(span_notice("[user] fails to analyze [target]'s genetic sequence."), span_warning("[target] has no readable genetic sequence!"))
/obj/item/sequence_scanner/attack_secondary(mob/living/target, mob/living/carbon/human/user, max_interact_count = 1)
add_fingerprint(user)
//no scanning if its a husk, DNA-less Species or DNA that isn't able to be copied by a changeling/disease
if (!HAS_TRAIT(target, TRAIT_GENELESS) && !HAS_TRAIT(target, TRAIT_BADDNA) && !HAS_TRAIT(target, TRAIT_NO_DNA_COPY))
user.visible_message(span_warning("[user] is scanning [target]'s genetic makeup."))
if(!do_after(user, 3 SECONDS))
balloon_alert(user, "scan failed!")
user.visible_message(span_warning("[user] fails to scan [target]'s genetic makeup."))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
makeup_scan(target, user)
balloon_alert(user, "makeup scanned")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
else
user.visible_message(span_notice("[user] fails to analyze [target]'s genetic makeup."), span_warning("[target] has no readable genetic makeup!"))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/sequence_scanner/afterattack_secondary(obj/object, mob/user, proximity)
. = ..()
if(!istype(object) || !proximity)
return
if(istype(object, /obj/machinery/computer/scan_consolenew))
var/obj/machinery/computer/scan_consolenew/console = object
var/buffer_index = tgui_input_number(user, "Slot:", "Which slot to export:", 1, LAZYLEN(console.genetic_makeup_buffer), 1)
console.genetic_makeup_buffer[buffer_index] = genetic_makeup_buffer
/obj/item/sequence_scanner/attack_self(mob/user)
display_sequence(user)
/obj/item/sequence_scanner/attack_self_tk(mob/user)
return
/obj/item/sequence_scanner/afterattack(obj/object, mob/user, proximity)
. = ..()
if(!istype(object) || !proximity)
return
if(istype(object, /obj/machinery/computer/scan_consolenew))
var/obj/machinery/computer/scan_consolenew/console = object
if(console.stored_research)
to_chat(user, span_notice("[name] linked to central research database."))
discovered = console.stored_research.discovered_mutations
else
to_chat(user,span_warning("No database to update from."))
///proc for scanning someone's mutations
/obj/item/sequence_scanner/proc/gene_scan(mob/living/carbon/target, mob/living/user)
if(!iscarbon(target) || !target.has_dna())
return
//add target mutations to list as well as extra mutations.
//dupe list as scanner could modify target data
buffer = LAZYLISTDUPLICATE(target.dna.mutation_index)
var/list/active_mutations = list()
for(var/datum/mutation/human/mutation in target.dna.mutations)
LAZYOR(buffer, mutation.type)
active_mutations.Add(mutation.type)
to_chat(user, span_notice("Subject [target.name]'s DNA sequence has been saved to buffer."))
for(var/mutation in buffer)
//highlight activated mutations
if(LAZYFIND(active_mutations, mutation))
to_chat(user, span_boldnotice("[get_display_name(mutation)]"))
else
to_chat(user, span_notice("[get_display_name(mutation)]"))
///proc for scanning someone's genetic makeup
/obj/item/sequence_scanner/proc/makeup_scan(mob/living/carbon/target, mob/living/user)
if(!iscarbon(target) || !target.has_dna())
return
genetic_makeup_buffer = list(
"label"="Analyzer Slot:[target.real_name]",
"UI"=target.dna.unique_identity,
"UE"=target.dna.unique_enzymes,
"UF"=target.dna.unique_features,
"name"=target.real_name,
"blood_type"=target.dna.blood_type)
/obj/item/sequence_scanner/proc/display_sequence(mob/living/user)
if(!LAZYLEN(buffer) || !ready)
return
var/list/options = list()
for(var/mutation in buffer)
options += get_display_name(mutation)
var/answer = tgui_input_list(user, "Analyze Potential", "Sequence Analyzer", sort_list(options))
if(isnull(answer))
return
if(!ready || !user.can_perform_action(src, NEED_LITERACY|NEED_LIGHT|FORBID_TELEKINESIS_REACH))
return
var/sequence
for(var/mutation in buffer) //this physically hurts but i dont know what anything else short of an assoc list
if(get_display_name(mutation) == answer)
sequence = buffer[mutation]
break
if(sequence)
var/display
for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1)
if(i)
display += "-"
display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
to_chat(user, "[span_boldnotice("[display]")]<br>")
ready = FALSE
icon_state = "[icon_state]_recharging"
addtimer(CALLBACK(src, PROC_REF(recharge)), cooldown, TIMER_UNIQUE)
/obj/item/sequence_scanner/proc/recharge()
icon_state = initial(icon_state)
ready = TRUE
/obj/item/sequence_scanner/proc/get_display_name(mutation)
var/datum/mutation/human/human_mutation = GET_INITIALIZED_MUTATION(mutation)
if(!human_mutation)
return "ERROR"
if(mutation in discovered)
return "[human_mutation.name] ([human_mutation.alias])"
else
return human_mutation.alias