Files
VOREStation/code/game/objects/items/devices/extrapolator.dm
T
Cameron Lennox d5849910e5 Begin clickcode attack_self fix (#18797)
* Begin clickcode attack_self fix

Begins the work to make everything call back to parent for attack_self so that signals are sacred.

* Makes MORE things call the attack_self() parent

Yes, I could make special_handling a var on obj/item HOWEVER i want it to be specific so it can be tracked down later and ONLY the objects that use it can be refactored instead of sitting there literally forever and it just becoming 'a thing'.

* Finishes making the rest of attack_self call parent.

As mentioned, things such as 'specialty_goggles' 'special_handling' and the such are only there to help with attack_self until the attack_self is recoded for those items.

* begone foul demon

* some more cleanup

* These

* GOD this was annoying

* yeh

* Fix this

* fLARES

* Thesee too

* toys!

* Even more!

* More fixes

* Even more

* rest of em

* these too

* Update syndie.dm

* hardref clear

* Update code/game/gamemodes/nuclear/pinpointer.dm

* Update code/game/objects/effects/mines.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/blueprints_vr.dm

* Update code/game/objects/items/contraband_vr.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/gunbox_vr.dm

* Update code/game/objects/items/weapons/gift_wrappaper.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/crayons.dm

* Update code/game/objects/items/gunbox.dm

* these too

* Update maintpanel_stack.dm

* angry warning

* Fixes packaged snacks.

Fixes improper var default.

* Special handling for these

* proper poly types

* Fixes magclaws

Makes the 'features' it had just part  of base magboots that can be adjusted via varswap.

* Fixes jackets

Fixes https://github.com/VOREStation/VOREStation/issues/18941

* Small bugfix

Makes p_Theyre properly capitialize
Makes examine show proper wording

* Update gift_wrappaper.dm
2025-12-29 13:21:10 -05:00

263 lines
11 KiB
Plaintext

/obj/item/extrapolator
name = "virus extrapolator"
icon = 'icons/obj/device.dmi'
icon_state = "extrapolator_scan"
desc = "A bulky scanning device, used to extract genetic material of potential pathogens."
description_info = "Use on hand to change between SCAN and EXTRACT mode."
item_flags = NOBLUDGEON
slot_flags = SLOT_BELT
w_class = ITEMSIZE_NORMAL
/// Whether the extrapolator is curently in use
var/using = FALSE
/// Whether the extrapolator is curently in SCAN or EXTRACT mode
var/scan = TRUE
/// The scanning module installed in the extrapolator. Used to determine extraction speed, and the stealthiest virus that's possible to extract.
var/obj/item/stock_parts/scanning_module/scanner
/// A list of advance IDs that this extrapolator has already extracted.
var/list/extracted_ids = list()
/// How long it takes for the extrapolator to extract a virus.
var/extract_time = 10 SECONDS
/// How long it tkaes for the extrapolator to isolate a symptom.
var/isolate_time = 15 SECONDS
/// The extrapolator can extract any virus with a stealth below this value.
var/maximum_stealth = 3
/// The extrapolator can extract any symptom with a stealth below this value.
var/maximum_level = 5
/// The typepath of the default scanning module that will generate in the extrapolator, if it starts with none.
var/default_scanning_module = /obj/item/stock_parts/scanning_module
/// Cooldown for when the extrapolator can be used next.
COOLDOWN_DECLARE(usage_cooldown)
/obj/item/extrapolator/Initialize(mapload, obj/item/stock_parts/scanning_module/starting_scanner)
. = ..()
starting_scanner = starting_scanner || default_scanning_module
if(ispath(starting_scanner, /obj/item/stock_parts/scanning_module))
scanner = new starting_scanner(src)
else if(istype(starting_scanner))
starting_scanner.forceMove(src)
scanner = starting_scanner
refresh_parts()
/obj/item/extrapolator/attackby(obj/item/item, mob/user, params)
if(istype(item, /obj/item/stock_parts/scanning_module))
if(!scanner)
user.drop_item()
item.loc = src
scanner = item
to_chat(user, span_notice("You install \the [scanner] in [src]."))
refresh_parts()
else
to_chat(user, span_notice("[src] already has \the [scanner] installed."))
return
if(item.has_tool_quality(TOOL_SCREWDRIVER) && scanner)
if(!scanner) // You never know
to_chat(user, span_warning("\The [src] has no scanner to remove!"))
return FALSE
to_chat(user, span_notice("You remove \the [scanner] from \the [src]."))
scanner.forceMove(drop_location())
scanner = null
playsound(src, item.usesound, 50, 1)
return TRUE
/obj/item/extrapolator/attack_self(mob/user)
. = ..(user)
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
if(scan)
icon_state = "extrapolator_sample"
scan = FALSE
to_chat(user, span_notice("You remove the probe from the device and set it to EXTRACT."))
else
icon_state = "extrapolator_scan"
scan = TRUE
to_chat(user, span_notice("You put the probe back into the device and set it to SCAN."))
/obj/item/extrapolator/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
if(!scanner)
. += span_notice("The scanner is missing.")
else
. += span_notice("A class " + span_bold("[scanner.rating]") + " scanning module is installed. It is <i>screwed</i> in place.")
// . += span_notice("Can detect diseases below stealth " + span_bold("[maximum_stealth]") + ".")
. += span_notice("Can extract diseases in " + span_bold("[DisplayTimeText(extract_time)]") + ".")
. += span_notice("Can isolate symptoms <b>[maximum_level >= 9 ? "of any level" : "below level [maximum_level]"]</b>, in <b>[DisplayTimeText(isolate_time)]</b>.")
/obj/item/extrapolator/proc/refresh_parts()
if(!scanner)
return
var/effective_scanner_rating = scanner.rating +1
extract_time = (10 SECONDS) / effective_scanner_rating
isolate_time = (15 SECONDS) / effective_scanner_rating
// maximum_stealth = scanner.rating + 2
maximum_level = scanner.rating + 5
/obj/item/extrapolator/attack(atom/AM, mob/living/user)
return
/obj/item/extrapolator/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!proximity_flag && !scan)
return
if(using)
to_chat(user, span_warning("[icon2html(src, user)] The extrapolator is already in use."))
return
if(!COOLDOWN_FINISHED(src, usage_cooldown))
to_chat(user, span_warning("[icon2html(src, user)] The extrapolator is still recharging!"))
return
if(scanner)
var/list/result = target?.extrapolator_act(user, src, dry_run = TRUE)
var/list/diseases = result && result[EXTRAPOLATOR_RESULT_DISEASES]
if(!length(diseases))
var/list/atom/targets = find_valid_targets(user, target)
var/target_amt = length(targets)
if(target_amt)
target = target_amt > 1 ? tgui_input_list(user, "Select object to analyze", "Viral Extrapolation", targets, default = targets[1]) : targets[1]
if(target)
result = target.extrapolator_act(user, src, dry_run = TRUE)
diseases = result && result[EXTRAPOLATOR_RESULT_DISEASES]
if(!target)
return
if(!length(diseases))
if(scan)
to_chat(user, span_notice("[icon2html(src, user)] \The [src] fails to return any data."))
else
to_chat(user, span_notice("[icon2html(src, user)] \The [src]'s probe detects no diseases."))
return
if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL))
// extrapolator_act did some sort of special behavior, we don't need to do anything further
return
if(scan)
scan(user, target)
else
extrapolate(user, target)
else
to_chat(user, span_warning("The extrapolator has no scanner installed!"))
/obj/item/extrapolator/proc/find_valid_targets(mob/living/user, atom/target)
. = list()
var/turf/target_turf = get_turf(target)
if(!target_turf)
return
for(var/atom/target_to_try in target_turf.contents - target)
var/list/result = target_to_try.extrapolator_act(user, src, dry_run = TRUE)
if(length(result[EXTRAPOLATOR_RESULT_DISEASES]))
. += target_to_try
/obj/item/extrapolator/proc/scan(mob/living/user, atom/target)
. = TRUE
var/list/result = target?.extrapolator_act(user, target)
var/list/diseases = result[EXTRAPOLATOR_RESULT_DISEASES]
if(!length(diseases))
return FALSE
if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL))
return
var/list/message = list()
if(length(diseases))
message += span_boldnotice("[costly_icon2html(target, user)] [target] scan results")
message += span_boldnotice("[icon2html(src, user)] \The [src] detects the following diseases:")
for(var/datum/disease/disease in diseases)
if(istype(disease, /datum/disease/advance))
var/datum/disease/advance/advance_disease = disease
var/list/properties
if(global_flag_check(advance_disease.virus_modifiers, CARRIER))
LAZYADD(properties, "carrier")
if(global_flag_check(advance_disease.virus_modifiers, FALTERED))
LAZYADD(properties, "faltered")
message += span_info("<b>[advance_disease.name]</b>[LAZYLEN(properties) ? " ([properties.Join(", ")])" : ""], [global_flag_check(advance_disease.virus_modifiers, DORMANT) ? "<i>dormant virus</i>" : "stage [advance_disease.stage]/5"]")
if(extracted_ids[advance_disease.GetDiseaseID()])
message += "This virus has been extracted by \the [src] previously."
message += "[advance_disease.name] has the following symptoms:"
for(var/datum/symptom/symptom in advance_disease.symptoms)
message += "[symptom.name]"
else
message += span_info("<b>[disease.name]</b>, [global_flag_check(disease.virus_modifiers, DORMANT) ? "<i>dormant virus</i>" : "stage [disease.stage]/[disease.max_stages]"].")
disease.addToDB()
to_chat(user, examine_block(jointext(message, "\n")), avoid_highlighting = TRUE, trailing_newline = FALSE, type = MESSAGE_TYPE_INFO)
/obj/item/extrapolator/proc/extrapolate(mob/living/user, atom/target, isolate = FALSE)
. = FALSE
var/list/result = target?.extrapolator_act(user, target)
var/list/diseases = result[EXTRAPOLATOR_RESULT_DISEASES]
if(!length(diseases))
return
if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL)) // hardcoded "we handled this ourselves" response
return TRUE
if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_ISOLATE))
isolate = TRUE
//var/list/advance_diseases = list()
/*
for(var/datum/disease/advance/candidate in diseases)
if(candidate.stealth >= maximum_stealth)
continue
advance_diseases += candidate
*/
if(!length(diseases))
to_chat(user, span_warning("[icon2html(src, user)] There are no valid diseases to make a culture from."))
return
var/datum/disease/advance/target_disease = length(diseases) > 1 ? tgui_input_list(user, "Select disease to extract", "Viral Extraction", diseases, default = diseases[1]) : diseases[1]
if(!target_disease)
return
using = TRUE
var/choice = tgui_alert(user, "What would you like to isolate?", "Isolate", list("Symptom", "Disease"))
if(choice == "Symptom")
. = isolate_symptom(user, target, target_disease)
else
. = isolate_disease(user, target, target_disease)
using = FALSE
/obj/item/extrapolator/proc/isolate_symptom(mob/living/user, atom/target, datum/disease/advance/target_disease)
. = FALSE
var/list/symptoms = list()
for(var/datum/symptom/symptom in target_disease.symptoms)
if(symptom.level <= maximum_level)
symptoms += symptom
continue
if(!length(symptoms))
to_chat(user, span_warning("[icon2html(src, user)] There are no symptoms that could be isolated.."))
return
var/datum/symptom/chosen = length(symptoms) > 1 ? tgui_input_list(user, "Select symptom to isolate", "Symptom Extraction", symptoms, default = symptoms[1]) : symptoms[1]
if(!chosen)
return
user.visible_message(span_notice("[user] slots [target] into [src], which begins to whir and beep!"), span_notice("[icon2html(src, user)] You begin isolating " + span_bold("[chosen.name]") + " from [target]..."),)
var/datum/disease/advance/symptom_holder = new
symptom_holder.name = chosen.name
symptom_holder.symptoms += chosen
symptom_holder.Finalize()
symptom_holder.Refresh()
if(do_after(user, extract_time, target = target))
create_culture(user, symptom_holder, target)
return TRUE
/obj/item/extrapolator/proc/isolate_disease(mob/living/user, atom/target, datum/disease/advance/target_disease, timer = 10 SECONDS)
. = FALSE
user.visible_message(span_notice("[user] begins to thoroughly scan [target] with [src]..."), \
span_notice("[icon2html(src, user)] You begin isolating " + span_bold("[target_disease.name]") + " from [target]..."))
if(do_after(user, isolate_time, target = target))
create_culture(user, target_disease, target)
return TRUE
/obj/item/extrapolator/proc/create_culture(mob/living/user, datum/disease/advance/disease)
. = FALSE
disease = disease.Copy()
disease.virus_modifiers &= ~DORMANT
var/list/data = list("viruses" = list(disease))
if(user.get_active_hand() != src)
to_chat(user, span_warning("The extrapolator must be held in your active hand to work!"))
return
var/obj/item/reagent_containers/glass/beaker/vial/culture_bottle = new(user.drop_location())
culture_bottle.name = "[disease.name] culture bottle"
culture_bottle.desc = "A small bottle. Contains [disease.agent] culture in synthblood medium."
culture_bottle.reagents.add_reagent(REAGENT_ID_BLOOD, 5, data)
user.put_in_hands(culture_bottle)
playsound(src, 'sound/machines/ping.ogg', vol = 30, vary = TRUE)
COOLDOWN_START(src, usage_cooldown, 1 SECONDS)
extracted_ids[disease.GetDiseaseID()] = TRUE
return TRUE
/obj/item/extrapolator/tier5
default_scanning_module = /obj/item/stock_parts/scanning_module/omni