29ecfa47bf
GENETICS
The random hexadecimal rng game has been replaced with gene sequencing from goon.
Adds mutation activators and mutators
You can now store mutations
Everyone now has their own set of unique mutations
Limited mutations per person to 8 (including one always being monkey)
Adds race specific mutations (See fire breathing for lizads)
You can inspect discovered mutations, undiscovered mutations use an alias to recognize them by
Adds a sequence analyzer. Can be used to scan someones genes sequence. Useful for determing what mutations they can safely have and or collecting data for very difficult sequences
Adds mutation combining. It's currently only RADIOACTIVE + STRONG = HULK (So yes you will now need 2 mutations for to get hulk)
Adds several other mutations. Telepathy, firebreath, glowy, radioactive and strength
cl Time-Green
add: Goon genetics!
add: More mutations! Fire breath for lizards! Radioactive! Telepathy! Glowy! Strength, though its cosmetic and should be combined with radioactivity instead! Fiery sweat!
add: Adds void magnet mutation by @tralezab !
/cl
715 lines
30 KiB
Plaintext
715 lines
30 KiB
Plaintext
|
|
/*
|
|
|
|
CONTAINS:
|
|
T-RAY
|
|
HEALTH ANALYZER
|
|
GAS ANALYZER
|
|
SLIME SCANNER
|
|
NANITE 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 = 10
|
|
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'
|
|
materials = list(MAT_METAL=150)
|
|
|
|
/obj/item/t_scanner/suicide_act(mob/living/carbon/user)
|
|
user.visible_message("<span class='suicide'>[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
|
return TOXLOSS
|
|
|
|
/obj/item/t_scanner/proc/toggle_on()
|
|
on = !on
|
|
icon_state = copytext(icon_state, 1, length(icon_state))+"[on]"
|
|
if(on)
|
|
START_PROCESSING(SSobj, src)
|
|
else
|
|
STOP_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/t_scanner/attack_self(mob/user)
|
|
toggle_on()
|
|
|
|
/obj/item/t_scanner/cyborg_unequip(mob/user)
|
|
if(!on)
|
|
return
|
|
toggle_on()
|
|
|
|
/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
|
|
materials = list(MAT_METAL=200)
|
|
var/mode = 1
|
|
var/scanmode = 0
|
|
var/advanced = FALSE
|
|
|
|
/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user)
|
|
user.visible_message("<span class='suicide'>[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!</span>")
|
|
return BRUTELOSS
|
|
|
|
/obj/item/healthanalyzer/attack_self(mob/user)
|
|
if(!scanmode)
|
|
to_chat(user, "<span class='notice'>You switch the health analyzer to scan chemical contents.</span>")
|
|
scanmode = 1
|
|
else
|
|
to_chat(user, "<span class='notice'>You switch the health analyzer to check physical health.</span>")
|
|
scanmode = 0
|
|
|
|
/obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user)
|
|
|
|
// Clumsiness/brain damage check
|
|
if ((user.has_trait(TRAIT_CLUMSY) || user.has_trait(TRAIT_DUMB)) && prob(50))
|
|
to_chat(user, "<span class='notice'>You stupidly try to analyze the floor's vitals!</span>")
|
|
user.visible_message("<span class='warning'>[user] has analyzed the floor's vitals!</span>")
|
|
to_chat(user, "<span class='info'>Analyzing results for The floor:\n\tOverall status: <b>Healthy</b>")
|
|
to_chat(user, "<span class='info'>Key: <font color='blue'>Suffocation</font>/<font color='green'>Toxin</font>/<font color='#FF8000'>Burn</font>/<font color='red'>Brute</font></span>")
|
|
to_chat(user, "<span class='info'>\tDamage specifics: <font color='blue'>0</font>-<font color='green'>0</font>-<font color='#FF8000'>0</font>-<font color='red'>0</font></span>")
|
|
to_chat(user, "<span class='info'>Body temperature: ???</span>")
|
|
return
|
|
|
|
user.visible_message("<span class='notice'>[user] has analyzed [M]'s vitals.</span>")
|
|
|
|
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 ? "<span class='alert'><b>Deceased</b></span>" : "<b>[round(M.health/M.maxHealth,0.01)*100] % healthy</b>")
|
|
|
|
if(M.has_trait(TRAIT_FAKEDEATH) && !advanced)
|
|
mob_status = "<span class='alert'><b>Deceased</b></span>"
|
|
oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss
|
|
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
|
|
to_chat(user, "<span class='danger'>Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!</span>")
|
|
if(H.undergoing_liver_failure() && H.stat != DEAD)
|
|
to_chat(user, "<span class='danger'>Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!</span>")
|
|
|
|
to_chat(user, "<span class='info'>Analyzing results for [M]:\n\tOverall status: [mob_status]</span>")
|
|
|
|
// Damage descriptions
|
|
if(brute_loss > 10)
|
|
to_chat(user, "\t<span class='alert'>[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.</span>")
|
|
if(fire_loss > 10)
|
|
to_chat(user, "\t<span class='alert'>[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.</span>")
|
|
if(oxy_loss > 10)
|
|
to_chat(user, "\t<span class='info'><span class='alert'>[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.</span>")
|
|
if(tox_loss > 10)
|
|
to_chat(user, "\t<span class='alert'>[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.</span>")
|
|
if(M.getStaminaLoss())
|
|
to_chat(user, "\t<span class='alert'>Subject appears to be suffering from fatigue.</span>")
|
|
if(advanced)
|
|
to_chat(user, "\t<span class='info'>Fatigue Level: [M.getStaminaLoss()]%.</span>")
|
|
if (M.getCloneLoss())
|
|
to_chat(user, "\t<span class='alert'>Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.</span>")
|
|
if(advanced)
|
|
to_chat(user, "\t<span class='info'>Cellular Damage Level: [M.getCloneLoss()].</span>")
|
|
if (M.getBrainLoss() >= 200 || !M.getorgan(/obj/item/organ/brain))
|
|
to_chat(user, "\t<span class='alert'>Subject's brain function is non-existent.</span>")
|
|
else if (M.getBrainLoss() >= 120)
|
|
to_chat(user, "\t<span class='alert'>Severe brain damage detected. Subject likely to have mental traumas.</span>")
|
|
else if (M.getBrainLoss() >= 45)
|
|
to_chat(user, "\t<span class='alert'>Brain damage detected.</span>")
|
|
if(iscarbon(M))
|
|
var/mob/living/carbon/C = M
|
|
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
|
|
to_chat(user, "\t<span class='alert'>Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].</span>")
|
|
if(C.roundstart_quirks.len)
|
|
to_chat(user, "\t<span class='info'>Subject has the following physiological traits: [C.get_trait_string()].</span>")
|
|
if(advanced)
|
|
to_chat(user, "\t<span class='info'>Brain Activity Level: [(200 - M.getBrainLoss())/2]%.</span>")
|
|
if (M.radiation)
|
|
to_chat(user, "\t<span class='alert'>Subject is irradiated.</span>")
|
|
if(advanced)
|
|
to_chat(user, "\t<span class='info'>Radiation Level: [M.radiation]%.</span>")
|
|
|
|
if(advanced && M.hallucinating())
|
|
to_chat(user, "\t<span class='info'>Subject is hallucinating.</span>")
|
|
|
|
//Eyes and ears
|
|
if(advanced)
|
|
if(iscarbon(M))
|
|
var/mob/living/carbon/C = M
|
|
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
|
to_chat(user, "\t<span class='info'><b>==EAR STATUS==</b></span>")
|
|
if(istype(ears))
|
|
var/healthy = TRUE
|
|
if(C.has_trait(TRAIT_DEAF, GENETIC_MUTATION))
|
|
healthy = FALSE
|
|
to_chat(user, "\t<span class='alert'>Subject is genetically deaf.</span>")
|
|
else if(C.has_trait(TRAIT_DEAF))
|
|
healthy = FALSE
|
|
to_chat(user, "\t<span class='alert'>Subject is deaf.</span>")
|
|
else
|
|
if(ears.ear_damage)
|
|
to_chat(user, "\t<span class='alert'>Subject has [ears.ear_damage > UNHEALING_EAR_DAMAGE? "permanent ": "temporary "]hearing damage.</span>")
|
|
healthy = FALSE
|
|
if(ears.deaf)
|
|
to_chat(user, "\t<span class='alert'>Subject is [ears.ear_damage > UNHEALING_EAR_DAMAGE ? "permanently ": "temporarily "] deaf.</span>")
|
|
healthy = FALSE
|
|
if(healthy)
|
|
to_chat(user, "\t<span class='info'>Healthy.</span>")
|
|
else
|
|
to_chat(user, "\t<span class='alert'>Subject does not have ears.</span>")
|
|
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
|
|
to_chat(user, "\t<span class='info'><b>==EYE STATUS==</b></span>")
|
|
if(istype(eyes))
|
|
var/healthy = TRUE
|
|
if(C.has_trait(TRAIT_BLIND))
|
|
to_chat(user, "\t<span class='alert'>Subject is blind.</span>")
|
|
healthy = FALSE
|
|
if(C.has_trait(TRAIT_NEARSIGHT))
|
|
to_chat(user, "\t<span class='alert'>Subject is nearsighted.</span>")
|
|
healthy = FALSE
|
|
if(eyes.eye_damage > 30)
|
|
to_chat(user, "\t<span class='alert'>Subject has severe eye damage.</span>")
|
|
healthy = FALSE
|
|
else if(eyes.eye_damage > 20)
|
|
to_chat(user, "\t<span class='alert'>Subject has significant eye damage.</span>")
|
|
healthy = FALSE
|
|
else if(eyes.eye_damage)
|
|
to_chat(user, "\t<span class='alert'>Subject has minor eye damage.</span>")
|
|
healthy = FALSE
|
|
if(healthy)
|
|
to_chat(user, "\t<span class='info'>Healthy.</span>")
|
|
else
|
|
to_chat(user, "\t<span class='alert'>Subject does not have eyes.</span>")
|
|
|
|
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = M
|
|
var/ldamage = H.return_liver_damage()
|
|
if(ldamage > 10)
|
|
to_chat(user, "\t<span class='alert'>[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.</span>")
|
|
|
|
// Body part damage report
|
|
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)
|
|
to_chat(user, "<span class='info'>\tDamage: <span class='info'><font color='red'>Brute</font></span>-<font color='#FF8000'>Burn</font>-<font color='green'>Toxin</font>-<font color='blue'>Suffocation</font>\n\t\tSpecifics: <font color='red'>[brute_loss]</font>-<font color='#FF8000'>[fire_loss]</font>-<font color='green'>[tox_loss]</font>-<font color='blue'>[oxy_loss]</font></span>")
|
|
for(var/obj/item/bodypart/org in damaged)
|
|
to_chat(user, "\t\t<span class='info'>[capitalize(org.name)]: [(org.brute_dam > 0) ? "<font color='red'>[org.brute_dam]</font></span>" : "<font color='red'>0</font>"]-[(org.burn_dam > 0) ? "<font color='#FF8000'>[org.burn_dam]</font>" : "<font color='#FF8000'>0</font>"]")
|
|
|
|
// Species and body temperature
|
|
if(ishuman(M))
|
|
var/mob/living/carbon/human/H = 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
|
|
|
|
to_chat(user, "<span class='info'>Species: [S.name][mutant ? "-derived mutant" : ""]</span>")
|
|
to_chat(user, "<span class='info'>Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)</span>")
|
|
|
|
// Time of death
|
|
if(M.tod && (M.stat == DEAD || ((M.has_trait(TRAIT_FAKEDEATH)) && !advanced)))
|
|
to_chat(user, "<span class='info'>Time of Death:</span> [M.tod]")
|
|
var/tdelta = round(world.time - M.timeofdeath)
|
|
if(tdelta < (DEFIB_TIME_LIMIT * 10))
|
|
to_chat(user, "<span class='danger'>Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!</span>")
|
|
|
|
for(var/thing in M.diseases)
|
|
var/datum/disease/D = thing
|
|
if(!(D.visibility_flags & HIDDEN_SCANNER))
|
|
to_chat(user, "<span class='alert'><b>Warning: [D.form] detected</b>\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]</span>")
|
|
|
|
// Blood Level
|
|
if(M.has_dna())
|
|
var/mob/living/carbon/C = M
|
|
var/blood_id = C.get_blood_id()
|
|
if(blood_id)
|
|
if(ishuman(C))
|
|
var/mob/living/carbon/human/H = C
|
|
if(H.bleed_rate)
|
|
to_chat(user, "<span class='danger'>Subject is bleeding!</span>")
|
|
var/blood_percent = round((C.blood_volume / BLOOD_VOLUME_NORMAL)*100)
|
|
var/blood_type = C.dna.blood_type
|
|
if(blood_id != "blood")//special blood substance
|
|
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
|
|
if(R)
|
|
blood_type = R.name
|
|
else
|
|
blood_type = blood_id
|
|
if(C.blood_volume <= BLOOD_VOLUME_SAFE && C.blood_volume > BLOOD_VOLUME_OKAY)
|
|
to_chat(user, "<span class='danger'>LOW blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>")
|
|
else if(C.blood_volume <= BLOOD_VOLUME_OKAY)
|
|
to_chat(user, "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>")
|
|
else
|
|
to_chat(user, "<span class='info'>Blood level [blood_percent] %, [C.blood_volume] cl, type: [blood_type]</span>")
|
|
|
|
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].<br>"
|
|
if(cyberimp_detect)
|
|
to_chat(user, "<span class='notice'>Detected cybernetic modifications:</span>")
|
|
to_chat(user, "<span class='notice'>[cyberimp_detect]</span>")
|
|
SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE)
|
|
|
|
/proc/chemscan(mob/living/user, mob/living/M)
|
|
if(istype(M))
|
|
if(M.reagents)
|
|
if(M.reagents.reagent_list.len)
|
|
to_chat(user, "<span class='notice'>Subject contains the following reagents:</span>")
|
|
for(var/datum/reagent/R in M.reagents.reagent_list)
|
|
to_chat(user, "<span class='notice'>[R.volume] units of [R.name][R.overdosed == 1 ? "</span> - <span class='boldannounce'>OVERDOSING</span>" : ".</span>"]")
|
|
else
|
|
to_chat(user, "<span class='notice'>Subject contains no reagents.</span>")
|
|
if(M.reagents.addiction_list.len)
|
|
to_chat(user, "<span class='boldannounce'>Subject is addicted to the following reagents:</span>")
|
|
for(var/datum/reagent/R in M.reagents.addiction_list)
|
|
to_chat(user, "<span class='danger'>[R.name]</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>Subject is not addicted to any reagents.</span>")
|
|
|
|
/obj/item/healthanalyzer/verb/toggle_mode()
|
|
set name = "Switch Verbosity"
|
|
set category = "Object"
|
|
|
|
if(usr.incapacitated())
|
|
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"
|
|
custom_price = 10
|
|
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
|
|
materials = list(MAT_METAL=30, MAT_GLASS=20)
|
|
grind_results = list("mercury" = 5, "iron" = 5, "silicon" = 5)
|
|
var/cooldown = FALSE
|
|
var/cooldown_time = 250
|
|
var/accuracy // 0 is the best accuracy.
|
|
|
|
/obj/item/analyzer/examine(mob/user)
|
|
. = ..()
|
|
to_chat(user, "<span class='notice'>Alt-click [src] to activate the barometer function.</span>")
|
|
|
|
/obj/item/analyzer/suicide_act(mob/living/carbon/user)
|
|
user.visible_message("<span class='suicide'>[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!</span>")
|
|
return BRUTELOSS
|
|
|
|
/obj/item/analyzer/attack_self(mob/user)
|
|
add_fingerprint(user)
|
|
|
|
if (user.stat || user.eye_blind)
|
|
return
|
|
|
|
var/turf/location = user.loc
|
|
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, "<span class='info'><B>Results:</B></span>")
|
|
if(abs(pressure - ONE_ATMOSPHERE) < 10)
|
|
to_chat(user, "<span class='info'>Pressure: [round(pressure, 0.01)] kPa</span>")
|
|
else
|
|
to_chat(user, "<span class='alert'>Pressure: [round(pressure, 0.01)] kPa</span>")
|
|
if(total_moles)
|
|
var/list/env_gases = environment.gases
|
|
|
|
environment.assert_gases(arglist(GLOB.hardcoded_gases))
|
|
var/o2_concentration = env_gases[/datum/gas/oxygen][MOLES]/total_moles
|
|
var/n2_concentration = env_gases[/datum/gas/nitrogen][MOLES]/total_moles
|
|
var/co2_concentration = env_gases[/datum/gas/carbon_dioxide][MOLES]/total_moles
|
|
var/plasma_concentration = env_gases[/datum/gas/plasma][MOLES]/total_moles
|
|
|
|
if(abs(n2_concentration - N2STANDARD) < 20)
|
|
to_chat(user, "<span class='info'>Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen][MOLES], 0.01)] mol)</span>")
|
|
else
|
|
to_chat(user, "<span class='alert'>Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen][MOLES], 0.01)] mol)</span>")
|
|
|
|
if(abs(o2_concentration - O2STANDARD) < 2)
|
|
to_chat(user, "<span class='info'>Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen][MOLES], 0.01)] mol)</span>")
|
|
else
|
|
to_chat(user, "<span class='alert'>Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen][MOLES], 0.01)] mol)</span>")
|
|
|
|
if(co2_concentration > 0.01)
|
|
to_chat(user, "<span class='alert'>CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide][MOLES], 0.01)] mol)</span>")
|
|
else
|
|
to_chat(user, "<span class='info'>CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide][MOLES], 0.01)] mol)</span>")
|
|
|
|
if(plasma_concentration > 0.005)
|
|
to_chat(user, "<span class='alert'>Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma][MOLES], 0.01)] mol)</span>")
|
|
else
|
|
to_chat(user, "<span class='info'>Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma][MOLES], 0.01)] mol)</span>")
|
|
|
|
environment.garbage_collect()
|
|
|
|
for(var/id in env_gases)
|
|
if(id in GLOB.hardcoded_gases)
|
|
continue
|
|
var/gas_concentration = env_gases[id][MOLES]/total_moles
|
|
to_chat(user, "<span class='alert'>[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id][MOLES], 0.01)] mol)</span>")
|
|
to_chat(user, "<span class='info'>Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)</span>")
|
|
|
|
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
|
|
..()
|
|
|
|
if(user.canUseTopic(src))
|
|
|
|
if(cooldown)
|
|
to_chat(user, "<span class='warning'>[src]'s barometer function is preparing itself.</span>")
|
|
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, "<span class='warning'>[src]'s barometer function won't work indoors!</span>")
|
|
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, "<span class='warning'>[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]</span>")
|
|
return
|
|
|
|
to_chat(user, "<span class='notice'>The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].</span>")
|
|
if(ongoing_weather.aesthetic)
|
|
to_chat(user, "<span class='warning'>[src]'s barometer function says that the next storm will breeze on by.</span>")
|
|
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, "<span class='warning'>[src]'s barometer function was unable to trace any weather patterns.</span>")
|
|
else
|
|
to_chat(user, "<span class='warning'>[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].</span>")
|
|
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, "<span class='notice'>[src]'s barometer function is ready!</span>")
|
|
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)
|
|
var/icon = target
|
|
user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(src))] [target].", "<span class='notice'>You use the analyzer on [icon2html(icon, user)] [target].</span>")
|
|
to_chat(user, "<span class='boldnotice'>Results of analysis of [icon2html(icon, user)] [target].</span>")
|
|
|
|
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, "<span class='boldnotice'>Node [airs.Find(g)]</span>")
|
|
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, "<span class='notice'>Moles: [round(total_moles, 0.01)] mol</span>")
|
|
to_chat(user, "<span class='notice'>Volume: [volume] L</span>")
|
|
to_chat(user, "<span class='notice'>Pressure: [round(pressure,0.01)] kPa</span>")
|
|
|
|
var/list/cached_gases = air_contents.gases
|
|
for(var/id in cached_gases)
|
|
var/gas_concentration = cached_gases[id][MOLES]/total_moles
|
|
to_chat(user, "<span class='notice'>[cached_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id][MOLES], 0.01)] mol)</span>")
|
|
to_chat(user, "<span class='notice'>Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)</span>")
|
|
|
|
else
|
|
if(airs.len > 1)
|
|
to_chat(user, "<span class='notice'>This node is empty!</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>[target] is empty!</span>")
|
|
|
|
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, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
|
|
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
|
|
return
|
|
|
|
//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
|
|
materials = list(MAT_METAL=30, MAT_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, "<span class='warning'>This device can only scan slimes!</span>")
|
|
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, "<b>Slime scan results:</b>")
|
|
to_chat(user, "<span class='notice'>[T.colour] [T.is_adult ? "adult" : "baby"] slime</span>")
|
|
to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]")
|
|
if (T.nutrition < T.get_starve_nutrition())
|
|
to_chat(user, "<span class='warning'>Warning: slime is starving!</span>")
|
|
else if (T.nutrition < T.get_hunger_nutrition())
|
|
to_chat(user, "<span class='warning'>Warning: slime is hungry</span>")
|
|
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, "<span class='notice'>Core mutation in progress: [T.effectmod]</span>")
|
|
to_chat(user, "<span_class = 'notice'>Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]</span>")
|
|
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
|
|
materials = list(MAT_METAL=200)
|
|
|
|
/obj/item/nanite_scanner/attack(mob/living/M, mob/living/carbon/human/user)
|
|
user.visible_message("<span class='notice'>[user] has analyzed [M]'s nanites.</span>")
|
|
|
|
add_fingerprint(user)
|
|
|
|
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
|
|
if(!response)
|
|
to_chat(user, "<span class='info'>No nanites detected in the subject.</span>")
|
|
|
|
/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 able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their 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
|
|
materials = list(MAT_METAL=200)
|
|
var/list/discovered = list() //hit a dna console to update the scanners database
|
|
|
|
/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user)
|
|
user.visible_message("<span class='notice'>[user] has analyzed [M]'s genetic sequence.</span>")
|
|
|
|
add_fingerprint(user)
|
|
|
|
gene_scan(M, user, src)
|
|
|
|
/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, "<span class='notice'>[name] database updated.</span>")
|
|
discovered = C.stored_research.discovered_mutations
|
|
else
|
|
to_chat(user,"<span class='warning'>No database to update from.</span>")
|
|
|
|
/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G)
|
|
if(!iscarbon(C) || !C.has_dna())
|
|
return
|
|
to_chat(user, "<span class='notice'>[C.name]'s potential mutations.")
|
|
for(var/A in C.dna.mutation_index)
|
|
var/datum/mutation/human/HM = get_initialized_mutation(A)
|
|
var/mut_name
|
|
if(G && (A in G.discovered))
|
|
mut_name = "[HM.name] ([HM.alias])"
|
|
else
|
|
mut_name = HM.alias
|
|
var/temp = get_gene_string(HM.type, C.dna)
|
|
var/display
|
|
for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1)
|
|
if(i)
|
|
display += "-"
|
|
display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
|
|
|
|
|
|
to_chat(user, "<span class='boldnotice'>- [mut_name] > [display]</span>")
|