From abdafe15fae0b7bdddf0aff5cc2510a58ee399ee Mon Sep 17 00:00:00 2001 From: SiliconMain <65544193+SiliconMain@users.noreply.github.com> Date: Sun, 26 Jul 2020 15:21:24 -0500 Subject: [PATCH] fuck --- code/game/objects/items/devices/scanners.bk | 910 ++++++++++++++++++ code/game/objects/items/devices/scanners.dm | 132 ++- code/game/objects/items/flamethrower.dm | 1 + code/game/objects/items/storage/belt.dm | 2 +- code/game/objects/items/tanks/tanks.dm | 1 + code/game/objects/items/tools/crowbar.dm | 1 + code/game/objects/items/tools/wirecutters.dm | 1 + code/modules/assembly/bomb.dm | 1 + .../machinery/components/components_base.dm | 1 + .../atmospherics/machinery/pipes/pipes.dm | 1 + .../portable/portable_atmospherics.dm | 1 + code/modules/power/singularity/collector.dm | 1 + code/modules/research/designs/tool_designs.dm | 10 + .../research/techweb/nodes/tools_nodes.dm | 2 +- icons/obj/device.dmi | Bin 55482 -> 56123 bytes 15 files changed, 1011 insertions(+), 54 deletions(-) create mode 100644 code/game/objects/items/devices/scanners.bk diff --git a/code/game/objects/items/devices/scanners.bk b/code/game/objects/items/devices/scanners.bk new file mode 100644 index 0000000000..b48bb0b51a --- /dev/null +++ b/code/game/objects/items/devices/scanners.bk @@ -0,0 +1,910 @@ + +/* + +CONTAINS: +T-RAY +HEALTH ANALYZER +GAS ANALYZER +SLIME SCANNER +NANITE SCANNER +GENETICS SCANNER + +*/ +/obj/item/t_scanner + name = "\improper T-ray scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + custom_price = PRICE_REALLY_CHEAP + icon = 'icons/obj/device.dmi' + icon_state = "t-ray0" + var/on = FALSE + slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + custom_materials = list(/datum/material/iron=150) + +/obj/item/t_scanner/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + return TOXLOSS + +/obj/item/t_scanner/attack_self(mob/user) + + on = !on + icon_state = copytext_char(icon_state, 1, -1) + "[on]" + + if(on) + START_PROCESSING(SSobj, src) + +/obj/item/t_scanner/process() + if(!on) + STOP_PROCESSING(SSobj, src) + return null + scan() + +/obj/item/t_scanner/proc/scan() + t_ray_scan(loc) + +/proc/t_ray_scan(mob/viewer, flick_time = 8, distance = 3) + if(!ismob(viewer) || !viewer.client) + return + var/list/t_ray_images = list() + for(var/obj/O in orange(distance, viewer) ) + if(O.level != 1) + continue + + if(O.invisibility == INVISIBILITY_MAXIMUM) + var/image/I = new(loc = get_turf(O)) + var/mutable_appearance/MA = new(O) + MA.alpha = 128 + MA.dir = O.dir + I.appearance = MA + t_ray_images += I + if(t_ray_images.len) + flick_overlay(t_ray_images, list(viewer.client), flick_time) + +/obj/item/healthanalyzer + name = "health analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "health" + item_state = "healthanalyzer" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + desc = "A hand-held body scanner able to distinguish vital signs of the subject." + 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=200) + var/mode = 1 + var/scanmode = 0 + var/advanced = FALSE + +/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") + return BRUTELOSS + +/obj/item/healthanalyzer/attack_self(mob/user) + if(!scanmode) + to_chat(user, "You switch the health analyzer to scan chemical contents.") + scanmode = 1 + else + to_chat(user, "You switch the health analyzer to check physical health.") + scanmode = 0 + +/obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user) + + // Clumsiness/brain damage check + if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) + to_chat(user, "You stupidly try to analyze the floor's vitals!") + user.visible_message("[user] has analyzed the floor's vitals!") + var/msg = "*---------*\nAnalyzing results for The floor:\n\tOverall status: Healthy\n" + msg += "Key: Suffocation/Toxin/Burn/Brute\n" + msg += "\tDamage specifics: 0-0-0-0\n" + msg += "Body temperature: ???\n" + msg += "*---------*" + to_chat(user, msg) + return + + user.visible_message("[user] has analyzed [M]'s vitals.") + + if(scanmode == 0) + healthscan(user, M, mode, advanced) + else if(scanmode == 1) + chemscan(user, M) + + add_fingerprint(user) + + +// Used by the PDA medical scanner too +/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) + if(isliving(user) && (user.incapacitated() || user.eye_blind)) + return + //Damage specifics + var/oxy_loss = M.getOxyLoss() + var/tox_loss = M.getToxLoss() + var/fire_loss = M.getFireLoss() + var/brute_loss = M.getBruteLoss() + var/mob_status = (M.stat == DEAD ? "Deceased" : "[round(M.health/M.maxHealth,0.01)*100] % healthy") + + if(HAS_TRAIT(M, TRAIT_FAKEDEATH) && !advanced) + mob_status = "Deceased" + oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss + + var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]" + + // Damage descriptions + if(brute_loss > 10) + msg += "\n\t[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected." + if(fire_loss > 10) + msg += "\n\t[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected." + if(oxy_loss > 10) + msg += "\n\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected." + if(tox_loss > 10) + msg += "\n\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected." + if(M.getStaminaLoss()) + msg += "\n\tSubject appears to be suffering from fatigue." + if(advanced) + msg += "\n\tFatigue Level: [M.getStaminaLoss()]%." + if (M.getCloneLoss()) + msg += "\n\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage." + if(advanced) + msg += "\n\tCellular Damage Level: [M.getCloneLoss()]." + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(advanced && H.has_dna()) + msg += "\n\tGenetic Stability: [H.dna.stability]%." + + to_chat(user, msg) + msg = "" + + // Body part damage report + var/list/dmgreport = list() + if(iscarbon(M) && mode == 1) + var/mob/living/carbon/C = M + var/list/damaged = C.get_damaged_bodyparts(1,1) + if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0) + dmgreport += "\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " + + for(var/o in damaged) + var/obj/item/bodypart/org = o //head, left arm, right arm, etc. + dmgreport += "\ + \ + " + dmgreport += "
Damage:BruteBurnToxinSuffocation
Overall:[brute_loss][fire_loss][tox_loss][oxy_loss]
[capitalize(org.name)]:[(org.brute_dam > 0) ? "[org.brute_dam]" : "0"][(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]
" + to_chat(user, dmgreport.Join()) + + + //Organ damages report + var/heart_ded = FALSE + if(iscarbon(M)) + var/mob/living/carbon/C = M + var/mob/living/carbon/human/H = M + for(var/organ in C.internal_organs) + var/temp_message + var/damage_message + var/obj/item/organ/O = organ + + //EYES + if(istype(O, /obj/item/organ/eyes)) + var/obj/item/organ/eyes/eyes = O + if(advanced) + if(HAS_TRAIT(C, TRAIT_BLIND)) + temp_message += " Subject is blind." + if(HAS_TRAIT(C, TRAIT_NEARSIGHT)) + temp_message += " Subject is nearsighted." + if(eyes.damage > 30) + damage_message += " Subject has severe eye damage." + else if(eyes.damage > 20) + damage_message += " Subject has significant eye damage." + else if(eyes.damage) + damage_message += " Subject has minor eye damage." + + + //EARS + else if(istype(O, /obj/item/organ/ears)) + var/obj/item/organ/ears/ears = O + if(advanced) + if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION)) + temp_message += " Subject is genetically deaf." + else if(HAS_TRAIT(C, TRAIT_DEAF)) + temp_message += " Subject is deaf." + else + if(ears.damage) + damage_message += " Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage." + if(ears.deaf) + damage_message += " Subject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf." + + + //BRAIN + else if(istype(O, /obj/item/organ/brain)) + if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200) + damage_message += " Subject's brain non-functional. Neurine injection recomended." + else if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120) + damage_message += " Severe brain damage detected. Subject likely to have mental traumas." + else if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45) + damage_message += " Brain damage detected." + if(advanced) + temp_message += " Brain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%." + + //TRAUMAS + if(LAZYLEN(C.get_traumas())) + var/list/trauma_text = list() + for(var/datum/brain_trauma/B in C.get_traumas()) + var/trauma_desc = "" + switch(B.resilience) + if(TRAUMA_RESILIENCE_SURGERY) + trauma_desc += "severe " + if(TRAUMA_RESILIENCE_LOBOTOMY) + trauma_desc += "deep-rooted " + if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE) + trauma_desc += "permanent " + trauma_desc += B.scan_desc + trauma_text += trauma_desc + temp_message += " Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)]." + if(C.roundstart_quirks.len) + temp_message += " Subject has the following physiological traits: [C.get_trait_string()]." + + if(ishuman(C) && advanced) + //MON PETIT CHAUFFEUR + if(H.hallucinating()) + temp_message += " Subject is hallucinating." + + //MKUltra + if(H.has_status_effect(/datum/status_effect/chem/enthrall)) + temp_message += " Subject has abnormal brain fuctions." + + //Astrogen shenanigans + if(H.reagents.has_reagent(/datum/reagent/fermi/astral)) + if(H.mind) + temp_message += " Warning: subject may be possesed." + else + temp_message += " Subject appears to be astrally projecting." + + + //LIVER + else if(istype(O, /obj/item/organ/liver)) + var/obj/item/organ/liver/L = O + if(L.organ_flags & ORGAN_FAILING && H.stat != DEAD) //might be depreciated + temp_message += "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!" + + //HEART + else if(ishuman(M) && (istype(O, /obj/item/organ/heart))) + var/obj/item/organ/heart/He = O + if(H.undergoing_cardiac_arrest() && H.stat != DEAD) + temp_message += " Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!" + if(He.organ_flags & ORGAN_FAILING) + heart_ded = TRUE + + //TONGUE + else if(istype(O, /obj/item/organ/tongue)) + var/obj/item/organ/tongue/T = O + if(T.name == "fluffy tongue") + temp_message += " Subject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant." + + //HECK + else if(istype(O, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = O + if(P.length>20) + temp_message += " Subject has a sizeable gentleman's organ at [P.length] inches." + + else if(istype(O, /obj/item/organ/genital/breasts)) + var/obj/item/organ/genital/breasts/Br = O + if(Br.cached_size>5) + temp_message += " Subject has a sizeable bosom with a [Br.size] cup." + + + + //GENERAL HANDLER + if(!damage_message) + if(O.organ_flags & ORGAN_FAILING) + damage_message += " Chronic [O.name] failure detected." + else if(O.damage > O.high_threshold) + damage_message += " Acute [O.name] failure detected." + else if(O.damage > O.low_threshold && advanced) + damage_message += " Minor [O.name] failure detected.
" + + if(temp_message || damage_message) + msg += "\t[uppertext(O.name)]:
[damage_message] [temp_message]\n" + + + + //END; LOOK FOR MISSING ORGANS? + var/breathes = TRUE + var/blooded = TRUE + if(C.dna && C.dna.species) + if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT)) + breathes = FALSE + if(NOBLOOD in C.dna.species.species_traits) + blooded = FALSE + var/has_liver = C.dna && !(NOLIVER in C.dna.species.species_traits) + var/has_stomach = C.dna && !(NOSTOMACH in C.dna.species.species_traits) + if(!M.getorganslot(ORGAN_SLOT_EYES)) + msg += "\tSubject does not have eyes.\n" + if(!M.getorganslot(ORGAN_SLOT_EARS)) + msg += "\tSubject does not have ears.\n" + if(!M.getorganslot(ORGAN_SLOT_BRAIN)) + msg += "\tSubject's brain function is non-existent!\n" + if(has_liver && !M.getorganslot(ORGAN_SLOT_LIVER)) + msg += "\tSubject's liver is missing!\n" + if(blooded && !M.getorganslot(ORGAN_SLOT_HEART)) + msg += "\tSubject's heart is missing!\n" + if(breathes && !M.getorganslot(ORGAN_SLOT_LUNGS)) + msg += "\tSubject's lungs have collapsed from trauma!\n" + if(has_stomach && !M.getorganslot(ORGAN_SLOT_STOMACH)) + msg += "\tSubject's stomach is missing!\n" + + + if(M.radiation) + msg += "\tSubject is irradiated.\n" + msg += "\tRadiation Level: [M.radiation] rad\n" + + + + // Species and body temperature + var/mob/living/carbon/human/H = M //Start to use human only stuff here + if(ishuman(M)) + var/datum/species/S = H.dna.species + var/mutant = FALSE + if (H.dna.check_mutation(HULK)) + mutant = TRUE + else if (S.mutantlungs != initial(S.mutantlungs)) + mutant = TRUE + else if (S.mutant_brain != initial(S.mutant_brain)) + mutant = TRUE + else if (S.mutant_heart != initial(S.mutant_heart)) + mutant = TRUE + else if (S.mutanteyes != initial(S.mutanteyes)) + mutant = TRUE + else if (S.mutantears != initial(S.mutantears)) + mutant = TRUE + else if (S.mutanthands != initial(S.mutanthands)) + mutant = TRUE + else if (S.mutanttongue != initial(S.mutanttongue)) + mutant = TRUE + else if (S.mutanttail != initial(S.mutanttail)) + mutant = TRUE + else if (S.mutantliver != initial(S.mutantliver)) + mutant = TRUE + else if (S.mutantstomach != initial(S.mutantstomach)) + mutant = TRUE + + msg += "\tReported Species: [H.dna.custom_species ? H.dna.custom_species : S.name]\n" + msg += "\tBase Species: [S.name]\n" + if(mutant) + msg += "\tSubject has mutations present.\n" + msg += "\tBody temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)\n" + + // Time of death + if(M.tod && (M.stat == DEAD || ((HAS_TRAIT(M, TRAIT_FAKEDEATH)) && !advanced))) + msg += "Time of Death: [M.tod]\n" + var/tdelta = round(world.time - M.timeofdeath) + if(tdelta < (DEFIB_TIME_LIMIT * 10)) + if(heart_ded) + msg += "Subject died [DisplayTimeText(tdelta)] ago, heart requires surgical intervention for defibrillation." + else + msg += "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!" + if(advanced) + if(H.get_ghost() || H.key || H.client)//Since it can last a while. + msg += " Intervention recommended.\n" + else + msg += "\n" + + for(var/thing in M.diseases) + var/datum/disease/D = thing + if(!(D.visibility_flags & HIDDEN_SCANNER)) + msg += "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]\n" + + // Blood Level + if(M.has_dna()) + var/mob/living/carbon/C = M + var/blood_typepath = C.get_blood_id() + if(blood_typepath) + if(ishuman(C)) + if(H.bleed_rate) + msg += "Subject is bleeding!\n" + var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100) + var/blood_type = C.dna.blood_type + if(!(blood_typepath in GLOB.blood_reagent_types)) + var/datum/reagent/R = GLOB.chemical_reagents_list[blood_typepath] + if(R) + blood_type = R.name + if(C.scan_blood_volume() <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.scan_blood_volume() > (BLOOD_VOLUME_OKAY*C.blood_ratio)) + msg += "LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + else if(C.scan_blood_volume() <= (BLOOD_VOLUME_OKAY*C.blood_ratio)) + msg += "CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + else + msg += "Blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + + var/cyberimp_detect + for(var/obj/item/organ/cyberimp/CI in C.internal_organs) + if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant) + cyberimp_detect += "[C.name] is modified with a [CI.name].
" + if(cyberimp_detect) + msg += "Detected cybernetic modifications:\n" + msg += "[cyberimp_detect]\n" + msg += "*---------*" + to_chat(user, msg) + SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE) + +/proc/chemscan(mob/living/user, mob/living/M) + if(istype(M)) + if(M.reagents) + var/msg = "*---------*\n" + if(M.reagents.reagent_list.len) + var/list/datum/reagent/reagents = list() + for(var/datum/reagent/R in M.reagents.reagent_list) + if(R.chemical_flags & REAGENT_INVISIBLE) + continue + reagents += R + + if(length(reagents)) + msg += "Subject contains the following reagents:\n" + for(var/datum/reagent/R in reagents) + msg += "[R.volume] units of [R.name][R.overdosed == 1 ? " - OVERDOSING" : "."]\n" + else + msg += "Subject contains no reagents.\n" + + else + msg += "Subject contains no reagents.\n" + if(M.reagents.addiction_list.len) + msg += "Subject is addicted to the following reagents:\n" + for(var/datum/reagent/R in M.reagents.addiction_list) + msg += "[R.name]\n" + else + msg += "Subject is not addicted to any reagents.\n" + + var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/datum/reagent/impure/fermiTox) + if(istype(F,/datum/reagent/impure/fermiTox)) + switch(F.volume) + if(5 to 10) + msg += "Subject contains a low amount of toxic isomers.\n" + if(10 to 25) + msg += "Subject contains toxic isomers.\n" + if(25 to 50) + msg += "Subject contains a substantial amount of toxic isomers.\n" + if(50 to 95) + msg += "Subject contains a high amount of toxic isomers.\n" + if(95 to INFINITY) + msg += "Subject contains a extremely dangerous amount of toxic isomers.\n" + + msg += "*---------*" + to_chat(user, msg) + +/obj/item/healthanalyzer/verb/toggle_mode() + set name = "Switch Verbosity" + set category = "Object" + + var/mob/living/L = usr + if(!istype(L) || !CHECK_MOBILITY(L, MOBILITY_USE)) + return + + mode = !mode + switch (mode) + if(1) + to_chat(usr, "The scanner now shows specific limb damage.") + if(0) + to_chat(usr, "The scanner no longer shows limb damage.") + +/obj/item/healthanalyzer/advanced + name = "advanced health analyzer" + icon_state = "health_adv" + desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy." + advanced = TRUE + +/obj/item/analyzer + desc = "A hand-held environmental scanner which reports current gas levels. Alt-Click to use the built in barometer function." + name = "analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "analyzer" + item_state = "analyzer" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + slot_flags = ITEM_SLOT_BELT + throwforce = 0 + throw_speed = 3 + throw_range = 7 + tool_behaviour = TOOL_ANALYZER + custom_materials = list(/datum/material/iron=30, /datum/material/glass=20) + grind_results = list(/datum/reagent/mercury = 5, /datum/reagent/iron = 5, /datum/reagent/silicon = 5) + var/cooldown = FALSE + var/cooldown_time = 250 + var/accuracy // 0 is the best accuracy. + +/obj/item/analyzer/examine(mob/user) + . = ..() + . += "Alt-click [src] to activate the barometer function." + +/obj/item/analyzer/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") + return BRUTELOSS + +/obj/item/analyzer/attack_self(mob/user) + add_fingerprint(user) + + if (user.stat || user.eye_blind) + return + + // Skyrat change: Functionality moved down to proc/scan_turf() + var/turf/location = get_turf(user) + if(!istype(location)) + return + + scan_turf(user, location) + +/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens + . = ..() + + if(user.canUseTopic(src)) + . = TRUE + if(cooldown) + to_chat(user, "[src]'s barometer function is preparing itself.") + return + + var/turf/T = get_turf(user) + if(!T) + return + + playsound(src, 'sound/effects/pop.ogg', 100) + var/area/user_area = T.loc + var/datum/weather/ongoing_weather = null + + if(!user_area.outdoors) + to_chat(user, "[src]'s barometer function won't work indoors!") + return + + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) + ongoing_weather = W + break + + if(ongoing_weather) + if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) + to_chat(user, "[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]") + return + + to_chat(user, "The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].") + if(ongoing_weather.aesthetic) + to_chat(user, "[src]'s barometer function says that the next storm will breeze on by.") + else + var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] + var/fixed = next_hit ? next_hit - world.time : -1 + if(fixed < 0) + to_chat(user, "[src]'s barometer function was unable to trace any weather patterns.") + else + to_chat(user, "[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].") + cooldown = TRUE + addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) + +/obj/item/analyzer/proc/ping() + if(isliving(loc)) + var/mob/living/L = loc + to_chat(L, "[src]'s barometer function is ready!") + playsound(src, 'sound/machines/click.ogg', 100) + cooldown = FALSE + +/obj/item/analyzer/proc/butchertime(amount) + if(!amount) + return + if(accuracy) + var/inaccurate = round(accuracy*(1/3)) + if(prob(50)) + amount -= inaccurate + if(prob(50)) + amount += inaccurate + return DisplayTimeText(max(1,amount)) + +/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src, visible = TRUE) + var/icon = target + if(visible) + user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(user))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].") + to_chat(user, "Results of analysis of [icon2html(icon, user)] [target].") + + var/list/airs = islist(mixture) ? mixture : list(mixture) + for(var/g in airs) + if(airs.len > 1) //not a unary gas mixture + to_chat(user, "Node [airs.Find(g)]") + var/datum/gas_mixture/air_contents = g + + var/total_moles = air_contents.total_moles() + var/pressure = air_contents.return_pressure() + var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess? + var/temperature = air_contents.temperature + var/cached_scan_results = air_contents.analyzer_results + + if(total_moles > 0) + to_chat(user, "Moles: [round(total_moles, 0.01)] mol") + to_chat(user, "Volume: [volume] L") + to_chat(user, "Pressure: [round(pressure,0.01)] kPa") + + var/list/cached_gases = air_contents.gases + for(var/id in cached_gases) + var/gas_concentration = cached_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)") + to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)") + + else + if(airs.len > 1) + to_chat(user, "This node is empty!") + else + to_chat(user, "[target] is empty!") + + if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected + var/fusion_power = round(cached_scan_results["fusion"], 0.01) + var/tier = fusionpower2text(fusion_power) + to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.") + to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.") + return + +// Skyrat change +/obj/item/analyzer/proc/scan_turf(mob/user, turf/location) + var/datum/gas_mixture/environment = location.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + var/cached_scan_results = environment.analyzer_results + + to_chat(user, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + else + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + if(total_moles) + var/list/env_gases = environment.gases + + var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles + var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles + var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles + var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles + + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + else + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + else + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + + if(co2_concentration > 0.01) + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + else + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + + if(plasma_concentration > 0.005) + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + else + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + + GAS_GARBAGE_COLLECT(environment.gases) + + for(var/id in env_gases) + if(id in GLOB.hardcoded_gases) + continue + var/gas_concentration = env_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)") + to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)") + + if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected + var/fusion_power = round(cached_scan_results["fusion"], 0.01) + var/tier = fusionpower2text(fusion_power) + to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.") + to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.") + +/obj/item/analyzer/ranged + desc = "A hand-held scanner which uses advanced spectroscopy and infrared readings to analyze gases as a distance. Alt-Click to use the built in barometer function." + name = "long-range analyzer" + icon = 'modular_skyrat/icons/obj/device.dmi' + icon_state = "ranged_analyzer" + +/obj/item/analyzer/ranged/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(target.tool_act(user, src, tool_behaviour)) + return + // Tool act didn't scan it, so let's get it's turf. + var/turf/location = get_turf(target) + scan_turf(user, location) +// End skyrat change + +//slime scanner + +/obj/item/slime_scanner + name = "slime scanner" + desc = "A device that analyzes a slime's internal composition and measures its stats." + icon = 'icons/obj/device.dmi' + icon_state = "adv_spectrometer" + item_state = "analyzer" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + throwforce = 0 + throw_speed = 3 + throw_range = 7 + custom_materials = list(/datum/material/iron=30, /datum/material/glass=20) + +/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) + if(user.stat || user.eye_blind) + return + if (!isslime(M)) + to_chat(user, "This device can only scan slimes!") + return + var/mob/living/simple_animal/slime/T = M + slime_scan(T, user) + +/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) + to_chat(user, "========================") + to_chat(user, "Slime scan results:") + to_chat(user, "[T.colour] [T.is_adult ? "adult" : "baby"] slime") + to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]") + if (T.nutrition < T.get_starve_nutrition()) + to_chat(user, "Warning: slime is starving!") + else if (T.nutrition < T.get_hunger_nutrition()) + to_chat(user, "Warning: slime is hungry") + to_chat(user, "Electric change strength: [T.powerlevel]") + to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%") + if (T.slime_mutation[4] == T.colour) + to_chat(user, "This slime does not evolve any further.") + else + if (T.slime_mutation[3] == T.slime_mutation[4]) + if (T.slime_mutation[2] == T.slime_mutation[1]) + to_chat(user, "Possible mutation: [T.slime_mutation[3]]") + to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + if (T.cores > 1) + to_chat(user, "Multiple cores detected") + to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]") + if(T.effectmod) + to_chat(user, "Core mutation in progress: [T.effectmod]") + to_chat(user, "Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]") + to_chat(user, "========================") + + +/obj/item/nanite_scanner + name = "nanite scanner" + icon = 'icons/obj/device.dmi' + icon_state = "nanite_scanner" + item_state = "nanite_remote" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + desc = "A hand-held body scanner able to detect nanites and their programming." + 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=200) + +/obj/item/nanite_scanner/attack(mob/living/M, mob/living/carbon/human/user) + user.visible_message("[user] has analyzed [M]'s nanites.") + + add_fingerprint(user) + + var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE) + if(!response) + to_chat(user, "No nanites detected in the subject.") + +/obj/item/sequence_scanner + name = "genetic sequence scanner" + icon = 'icons/obj/device.dmi' + icon_state = "gene" + item_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. Hold near 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=200) + var/list/discovered = list() //hit a dna console to update the scanners database + var/list/buffer + var/ready = TRUE + var/cooldown = 200 + +/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user) + add_fingerprint(user) + if (!HAS_TRAIT_NOT_FROM(M, TRAIT_RADIMMUNE,BLOODSUCKER_TRAIT)) //no scanning if its a husk or DNA-less Species + user.visible_message("[user] analyzes [M]'s genetic sequence.", \ + "You analyze [M]'s genetic sequence.") + gene_scan(M, user) + + else + user.visible_message("[user] failed to analyse [M]'s genetic sequence.", "[M] has no readable genetic sequence!") + +/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/O, mob/user, proximity) + . = ..() + if(!istype(O) || !proximity) + return + + if(istype(O, /obj/machinery/computer/scan_consolenew)) + var/obj/machinery/computer/scan_consolenew/C = O + if(C.stored_research) + to_chat(user, "[name] linked to central research database.") + discovered = C.stored_research.discovered_mutations + else + to_chat(user,"No database to update from.") + +/obj/item/sequence_scanner/proc/gene_scan(mob/living/carbon/C, mob/living/user) + if(!iscarbon(C) || !C.has_dna()) + return + buffer = C.dna.mutation_index + to_chat(user, "Subject [C.name]'s DNA sequence has been saved to buffer.") + if(LAZYLEN(buffer)) + for(var/A in buffer) + to_chat(user, "[get_display_name(A)]") + + +/obj/item/sequence_scanner/proc/display_sequence(mob/living/user) + if(!LAZYLEN(buffer) || !ready) + return + var/list/options = list() + for(var/A in buffer) + options += get_display_name(A) + + var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sortList(options) + if(answer && ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + var/sequence + for(var/A in buffer) //this physically hurts but i dont know what anything else short of an assoc list + if(get_display_name(A) == answer) + sequence = buffer[A] + 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, "[display]
") + + ready = FALSE + icon_state = "[icon_state]_recharging" + addtimer(CALLBACK(src, .proc/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/HM = GET_INITIALIZED_MUTATION(mutation) + if(!HM) + return "ERROR" + if(mutation in discovered) + return "[HM.name] ([HM.alias])" + else + return HM.alias diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 2011943bfb..86d1cc75e1 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -624,54 +624,13 @@ GENETICS SCANNER if (user.stat || user.eye_blind) return - var/turf/location = user.loc + //Functionality moved down to proc/scan_turf() + var/turf/location = get_turf(user) if(!istype(location)) return - - var/datum/gas_mixture/environment = location.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - to_chat(user, "Results:") - if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") - else - to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") - if(total_moles) - - var/o2_concentration = environment.get_moles(/datum/gas/oxygen)/total_moles - var/n2_concentration = environment.get_moles(/datum/gas/nitrogen)/total_moles - var/co2_concentration = environment.get_moles(/datum/gas/carbon_dioxide)/total_moles - var/plasma_concentration = environment.get_moles(/datum/gas/plasma)/total_moles - - if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/nitrogen), 0.01)] mol)") - else - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/nitrogen), 0.01)] mol)") - - if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/oxygen), 0.01)] mol)") - else - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/oxygen), 0.01)] mol)") - - if(co2_concentration > 0.01) - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/carbon_dioxide), 0.01)] mol)") - else - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/carbon_dioxide), 0.01)] mol)") - - if(plasma_concentration > 0.005) - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/plasma), 0.01)] mol)") - else - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/plasma), 0.01)] mol)") - - for(var/id in environment.get_gases()) - if(id in GLOB.hardcoded_gases) - continue - var/gas_concentration = environment.get_moles(id)/total_moles - to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(environment.get_moles(id), 0.01)] mol)") - to_chat(user, "Temperature: [round(environment.return_temperature()-T0C, 0.01)] °C ([round(environment.return_temperature(), 0.01)] K)") - + + scan_turf(user, location) + /obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens . = ..() @@ -749,8 +708,8 @@ GENETICS SCANNER var/total_moles = air_contents.total_moles() var/pressure = air_contents.return_pressure() - var/volume = air_contents.return_volume() - var/temperature = air_contents.return_temperature() + var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess? + var/temperature = air_contents.temperature var/cached_scan_results = air_contents.analyzer_results if(total_moles > 0) @@ -758,9 +717,10 @@ GENETICS SCANNER to_chat(user, "Volume: [volume] L") to_chat(user, "Pressure: [round(pressure,0.01)] kPa") - for(var/id in air_contents.get_gases()) - var/gas_concentration = air_contents.get_moles(id)/total_moles - to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)") + var/list/cached_gases = air_contents.gases + for(var/id in cached_gases) + var/gas_concentration = cached_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)") to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)") else @@ -776,6 +736,74 @@ GENETICS SCANNER to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.") return +/obj/item/analyzer/proc/scan_turf(mob/user, turf/location) + var/datum/gas_mixture/environment = location.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + var/cached_scan_results = environment.analyzer_results + + to_chat(user, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + else + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + if(total_moles) + var/list/env_gases = environment.gases + + var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles + var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles + var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles + var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles + + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + else + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + else + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + + if(co2_concentration > 0.01) + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + else + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + + if(plasma_concentration > 0.005) + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + else + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + + GAS_GARBAGE_COLLECT(environment.gases) + + for(var/id in env_gases) + if(id in GLOB.hardcoded_gases) + continue + var/gas_concentration = env_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)") + to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)") + + if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected + var/fusion_power = round(cached_scan_results["fusion"], 0.01) + var/tier = fusionpower2text(fusion_power) + to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.") + to_chat(user, "Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.") + +/obj/item/analyzer/ranged + desc = "A hand-held scanner which uses advanced spectroscopy and infrared readings to analyze gases as a distance. Alt-Click to use the built in barometer function." + name = "long-range analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "ranged_analyzer" + +/obj/item/analyzer/ranged/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(target.tool_act(user, src, tool_behaviour)) + return + // Tool act didn't scan it, so let's get it's turf. + var/turf/location = get_turf(target) + scan_turf(user, location) + //slime scanner /obj/item/slime_scanner @@ -966,4 +994,4 @@ GENETICS SCANNER #undef SCANMODE_CHEMICAL #undef SCANMODE_WOUND #undef SCANNER_CONDENSED -#undef SCANNER_VERBOSE +#undef SCANNER_VERBOSE \ No newline at end of file diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index f293e6579a..515f5715dd 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -131,6 +131,7 @@ /obj/item/flamethrower/analyzer_act(mob/living/user, obj/item/I) if(ptank) ptank.analyzer_act(user, I) + return TRUE /obj/item/flamethrower/attack_self(mob/user) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 52e8e9193f..347679e0a5 100755 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -83,7 +83,7 @@ new /obj/item/multitool(src) new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange")) new /obj/item/extinguisher/mini(src) - new /obj/item/analyzer(src) + new /obj/item/analyzer/ranged(src) //much roomier now that we've managed to remove two tools /obj/item/storage/belt/utility/full/PopulateContents() diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index 42e25273a2..2de5860794 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -123,6 +123,7 @@ /obj/item/tank/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(air_contents, user, src) + return TRUE /obj/item/tank/deconstruct(disassembled = TRUE) if(!disassembled) diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 91e8c49e5a..d39da2f543 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -90,6 +90,7 @@ /obj/item/crowbar/power/attack_self(mob/user) playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power(drop_location()) + cutjaws.name = name to_chat(user, "You attach the cutting jaws to [src].") qdel(src) user.put_in_active_hand(cutjaws) diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/wirecutters.dm index ac5a02b9fc..53a578a45d 100644 --- a/code/game/objects/items/tools/wirecutters.dm +++ b/code/game/objects/items/tools/wirecutters.dm @@ -117,6 +117,7 @@ /obj/item/wirecutters/power/attack_self(mob/user) playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power(drop_location()) + pryjaws.name = name to_chat(user, "You attach the pry jaws to [src].") qdel(src) user.put_in_active_hand(pryjaws) diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index 36c444f02d..9d7bae5c6d 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -62,6 +62,7 @@ /obj/item/onetankbomb/analyzer_act(mob/living/user, obj/item/I) bombtank.analyzer_act(user, I) + return TRUE /obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly bombassembly.attack_self(user, TRUE) diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm index dc7c106035..a8d9586fc4 100644 --- a/code/modules/atmospherics/machinery/components/components_base.dm +++ b/code/modules/atmospherics/machinery/components/components_base.dm @@ -170,3 +170,4 @@ /obj/machinery/atmospherics/components/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(airs, user, src) + return TRUE \ No newline at end of file diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm index 4a6170c251..23fd2292ff 100644 --- a/code/modules/atmospherics/machinery/pipes/pipes.dm +++ b/code/modules/atmospherics/machinery/pipes/pipes.dm @@ -64,6 +64,7 @@ /obj/machinery/atmospherics/pipe/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(parent.air, user, src) + return TRUE /obj/machinery/atmospherics/pipe/returnPipenet() return parent diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 445cc686f3..fa57e683c4 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -145,6 +145,7 @@ /obj/machinery/portable_atmospherics/analyzer_act(mob/living/user, obj/item/I) atmosanalyzer_scan(air_contents, user, src) + return TRUE /obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user, attackchain_flags = NONE, damage_multiplier = 1) if(I.force < 10 && !(stat & BROKEN)) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 4cc9cbe34f..256b13ee72 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -176,6 +176,7 @@ /obj/machinery/power/rad_collector/analyzer_act(mob/living/user, obj/item/I) if(loaded_tank) loaded_tank.analyzer_act(user, I) + return TRUE /obj/machinery/power/rad_collector/examine(mob/user) . = ..() diff --git a/code/modules/research/designs/tool_designs.dm b/code/modules/research/designs/tool_designs.dm index 551d6fa0e3..593fac50d4 100644 --- a/code/modules/research/designs/tool_designs.dm +++ b/code/modules/research/designs/tool_designs.dm @@ -91,6 +91,16 @@ build_path = /obj/item/construction/rld/mini category = list("Tool Designs") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO + + /datum/design/ranged_analyzer // Skyrat addition + name = "Long-range Analyzer" + desc = "A new advanced atmospheric analyzer design, capable of performing scans at long range." + id = "ranged_analyzer" + build_type = PROTOLATHE + materials = list(/datum/material/iron = 400, /datum/material/glass = 1000, /datum/material/uranium = 800, /datum/material/gold = 200, /datum/material/plastic = 200) + build_path = /obj/item/analyzer/ranged + category = list("Tool Designs") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING ///////////////////////////////////////// //////////////Alien Tools//////////////// diff --git a/code/modules/research/techweb/nodes/tools_nodes.dm b/code/modules/research/techweb/nodes/tools_nodes.dm index b084979116..180cdb5778 100644 --- a/code/modules/research/techweb/nodes/tools_nodes.dm +++ b/code/modules/research/techweb/nodes/tools_nodes.dm @@ -44,7 +44,7 @@ id = "exp_tools" display_name = "Experimental Tools" description = "Highly advanced construction tools." - design_ids = list("exwelder", "jawsoflife", "handdrill", "holosigncombifan") + design_ids = list("exwelder", "jawsoflife", "handdrill", "holosigncombifan", "ranged_analyzer") prereq_ids = list("adv_engi") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2750) diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index c27e03bb2a57f16d99442ddd2d2bbb33a38bec51..5a9e1e54b63cd31e3957d1d11fade35f3841a2c7 100644 GIT binary patch delta 14031 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 M>#CRDef;+S0FJ#u-T(jq delta 13227 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 A(*OVf