/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." 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) . = ..() 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 screwed 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 [maximum_level >= 9 ? "of any level" : "below level [maximum_level]"], in [DisplayTimeText(isolate_time)].") /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("[advance_disease.name][LAZYLEN(properties) ? " ([properties.Join(", ")])" : ""], [global_flag_check(advance_disease.virus_modifiers, DORMANT) ? "dormant virus" : "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("[disease.name], [global_flag_check(disease.virus_modifiers, DORMANT) ? "dormant virus" : "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