From ee082f200e39f41eaff4573efeec023e2d5b5db2 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Thu, 19 Sep 2019 10:21:59 -0700
Subject: [PATCH 01/13] Fermi1
---
.../food_and_drinks/recipes/drinks_recipes.dm | 4 +-
.../mob/living/simple_animal/friendly/cat.dm | 1 +
code/modules/reagents/chemistry/reagents.dm | 2 +-
.../chemistry/reagents/alcohol_reagents.dm | 10 +--
.../chemistry/reagents/drink_reagents.dm | 3 +
.../chemistry/reagents/fermi_reagents.dm | 75 +++++++++++++------
.../reagents/chemistry/recipes/fermi.dm | 58 +++++++++-----
7 files changed, 101 insertions(+), 52 deletions(-)
diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
index 8fbdf02d..89bffa0c 100644
--- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -398,8 +398,8 @@
OptimalTempMin = 200 // Lower area of bell curve for determining heat based rate reactions
OptimalTempMax = 950 // Upper end for above
ExplodeTemp = 999 //Temperature at which reaction explodes
- OptimalpHMin = 2 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
- OptimalpHMax = 3 // Higest value for above
+ OptimalpHMin = 4.6 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
+ OptimalpHMax = 5.2 // Higest value for above
ReactpHLim = 5 // How far out pH wil react, giving impurity place (Exponential phase)
CatalystFact = 0 // How much the catalyst affects the reaction (0 = no catalyst)
CurveSharpT = 2 // How sharp the temperature exponential curve is (to the power of value)
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index a6197a3f..44fedb47 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -307,6 +307,7 @@
emote_see = list("looks at you eagerly for pets!", "wiggles enthusiastically.")
gold_core_spawnable = NO_SPAWN
var/pseudo_death = FALSE
+ var/mob/living/carbon/human/origin = null
/mob/living/simple_animal/pet/cat/custom_cat/death()
if (pseudo_death == TRUE) //secret cat chem
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 794b191a..5eb2c135 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -221,4 +221,4 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
for (var/datum/reagent/R in reagent_list)
rs += "[R.name], [R.volume]"
- return rs.Join(" | ")
\ No newline at end of file
+ return rs.Join(" | ")
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index d0a986f1..0cc469dd 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1374,7 +1374,6 @@ All effects don't start immediately, but rather get worse over time; the rate is
glass_icon_state = "neurotoxinglass"
glass_name = "Neurotoxin"
glass_desc = "A drink that is guaranteed to knock you silly."
- pH = 4.3
//SplitChem = TRUE
impure_chem = /datum/reagent/consumable/ethanol/neuroweak
inverse_chem_val = 0.5 //Clear conversion
@@ -1419,11 +1418,6 @@ All effects don't start immediately, but rather get worse over time; the rate is
name = "Neuro-Smash"
description = "A mostly safe alcoholic drink for the true daredevils. Counteracts Neurotoxins."
boozepwr = 60
- quality = DRINK_VERYGOOD
- taste_description = "a numbing sensation"
- glass_icon_state = "neurosmashglass"
- glass_name = "Neurosmash"
- glass_desc = "A mostly safe alcoholic drink for the true daredevils. Do not mix with Neurotoxin."
pH = 8
value = 3
@@ -1435,7 +1429,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1*REM, 150)
M.reagents.remove_reagent(/datum/reagent/toxin/fentanyl, 0.75 * REAGENTS_METABOLISM, FALSE)
else
- M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1.0*REM, 150)
+ M.adjustOrganLoss(ORGAN_SLOT_BRAIN, -0.5*REM, 150)
M.dizziness +=2
..()
@@ -2186,7 +2180,7 @@ datum/reagent/consumable/ethanol/creme_de_coconut
glass_icon_state = "commander_and_chief"
glass_name = "Commander and Chief"
glass_desc = "The gems of this majestic chalice represent the departments and their Heads."
-
+
/datum/reagent/consumable/ethanol/commander_and_chief/on_mob_life(mob/living/carbon/M)
if(M.mind && HAS_TRAIT(M.mind, TRAIT_CAPTAIN_METABOLISM))
M.heal_bodypart_damage(2,2,2)
diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 52100df7..71cc5f27 100644
--- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -12,6 +12,7 @@
glass_icon_state = "glass_orange"
glass_name = "glass of orange juice"
glass_desc = "Vitamins! Yay!"
+ pH = 3.3
/datum/reagent/consumable/orangejuice/on_mob_life(mob/living/carbon/M)
if(M.getOxyLoss() && prob(30))
@@ -42,6 +43,7 @@
glass_icon_state = "glass_green"
glass_name = "glass of lime juice"
glass_desc = "A glass of sweet-sour lime juice."
+ pH = 2.2
/datum/reagent/consumable/limejuice/on_mob_life(mob/living/carbon/M)
if(M.getToxLoss() && prob(20))
@@ -117,6 +119,7 @@
glass_icon_state = "lemonglass"
glass_name = "glass of lemon juice"
glass_desc = "Sour..."
+ pH = 2
/datum/reagent/consumable/strawberryjuice
name = "Strawberry Juice"
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index ee7d7c16..fbefe31b 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -10,17 +10,16 @@
inverse_chem_val = 0.25 // If the impurity is below 0.5, replace ALL of the chem with inverse_chemupon metabolising
inverse_chem = /datum/reagent/impure/fermiTox
+
//This should process fermichems to find out how pure they are and what effect to do.
/datum/reagent/fermi/on_mob_add(mob/living/carbon/M, amount)
. = ..()
-
//When merging two fermichems, see above
/datum/reagent/fermi/on_merge(data, amount, mob/living/carbon/M, purity)//basically on_mob_add but for merging
. = ..()
-
////////////////////////////////////////////////////////////////////////////////////////////////////
// HATIMUIM
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -46,10 +45,7 @@
. = ..()
if(M.head)
var/obj/item/W = M.head
- if(istype(W, /obj/item/clothing/head/hattip))
- qdel(W)
- else
- M.dropItemToGround(W, TRUE)
+ M.dropItemToGround(W, TRUE)
var/hat = new /obj/item/clothing/head/hattip()
M.equip_to_slot(hat, SLOT_HEAD, 1, 1)
@@ -59,15 +55,20 @@
return ..()
var/hatArmor = 0
if(!overdosed)
- hatArmor = (purity/10)
+ hatArmor = (cached_purity/10)
else
- hatArmor = - (purity/10)
+ hatArmor = (cached_purity/10)
if(hatArmor > 90)
return ..()
var/obj/item/W = M.head
W.armor = W.armor.modifyAllRatings(hatArmor)
..()
+/datum/reagent/fermi/hatmium/reaction_turf(turf/T, reac_volume)
+ if(reac_volume >= 5)
+ new /obj/item/clothing/head/hattip(T)
+ ..()
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// FURRANIUM
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -149,7 +150,7 @@
..()
/datum/reagent/fermi/furranium/on_mob_delete(mob/living/carbon/M)
- if(purity < 1)//Only permanent if you're a good chemist.
+ if(cached_purity < 0.95)//Only permanent if you're a good chemist.
nT = M.getorganslot(ORGAN_SLOT_TONGUE)
nT.Remove(M)
qdel(nT)
@@ -176,13 +177,13 @@
pH = 9
value = 90
can_synth = FALSE
+ var/react_objs = list()
/datum/reagent/fermi/nanite_b_gone/on_mob_life(mob/living/carbon/C)
- //var/component/nanites/N = M.GetComponent(/datum/component/nanites)
var/datum/component/nanites/N = C.GetComponent(/datum/component/nanites)
if(isnull(N))
return ..()
- N.nanite_volume = -purity//0.5 seems to be the default to me, so it'll neuter them.
+ N.nanite_volume += -cached_purity*5//0.5 seems to be the default to me, so it'll neuter them.
..()
/datum/reagent/fermi/nanite_b_gone/overdose_process(mob/living/carbon/C)
@@ -192,16 +193,19 @@
to_chat(C, "The residual voltage from the nanites causes you to seize up!")
C.electrocute_act(10, (get_turf(C)), 1, FALSE, FALSE, FALSE, TRUE)
if(prob(10))
- //empulse((get_turf(C)), 3, 2)//So the nanites randomize
var/atom/T = C
T.emp_act(EMP_HEAVY)
to_chat(C, "You feel a strange tingling sensation come from your core.")
if(isnull(N))
return ..()
- N.nanite_volume = -2
+ N.nanite_volume += -10*cached_purity
..()
-/datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)
+datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)
+ for(var/active_obj in react_objs)
+ if(O == active_obj)
+ return
+ react_objs += O
O.emp_act(EMP_HEAVY)
/datum/reagent/fermi/nanite_b_goneTox
@@ -281,15 +285,15 @@
else
holder.del_reagent(type)//Avoiding recurrsion
var/location = get_turf(holder.my_atom)
- if(purity < 0.34 || purity == 1)
+ if(cached_purity < 0.34 || cached_purity == 1)
var/datum/effect_system/foam_spread/s = new()
s.set_up(volume*2, location, holder)
s.start()
- if((purity < 0.67 && purity >= 0.34)|| purity == 1)
+ if((cached_purity < 0.67 && cached_purity >= 0.34)|| cached_purity == 1)
var/datum/effect_system/smoke_spread/chem/s = new()
s.set_up(holder, volume*2, location)
s.start()
- if(purity >= 0.67)
+ if(cached_purity >= 0.67)
for (var/datum/reagent/reagent in holder.reagent_list)
if (istype(reagent, /datum/reagent/fermi))
var/datum/chemical_reaction/fermi/Ferm = GLOB.chemical_reagents_list[reagent.type]
@@ -353,6 +357,7 @@
taste_description = "hairballs and cream"
color = "#ffc224"
var/catshift = FALSE
+ var/perma = FALSE
var/mob/living/simple_animal/pet/cat/custom_cat/catto = null
can_synth = FALSE
@@ -361,7 +366,7 @@
/datum/reagent/fermi/secretcatchem/on_mob_add(mob/living/carbon/human/H)
. = ..()
- if(purity >= 0.8)//ONLY if purity is high, and given the stuff is random. It's very unlikely to get this to 1. It already requires felind too, so no new functionality there.
+ if(cached_purity >= 0.9)//ONLY if purity is high, and given the stuff is random. It's very unlikely to get this to 1.
//exception(al) handler:
H.dna.features["ears"] = "Cat"
H.dna.features["mam_ears"] = "Cat"
@@ -378,20 +383,30 @@
H.forceMove(catto)
log_game("FERMICHEM: [H] ckey: [H.key] has been made into a cute catto.")
SSblackbox.record_feedback("tally", "fermi_chem", 1, "cats")
- //Just to deal with rascally ghosts
- //ADD_TRAIT(catto, TRAIT_NODEATH, "catto")//doesn't work
- //catto.health = 1000 //To simulate fake death, while preventing ghosts escaping.
+ if(H.InCritical())
+ perma = TRUE
+ volume = 5
+ H.stat = DEAD
+ catto.origin = H
/datum/reagent/fermi/secretcatchem/on_mob_life(mob/living/carbon/H)
+ if(!catto)
+ metabolization_rate = 5
+ return ..()
if(catto.health <= 0) //So the dead can't ghost
if(prob(10))
- to_chat(H, "You feel your body start to slowly shift back from it's dead form.")
+ to_chat(catto, "You feel your body start to slowly shift back from it's dead form.")
+ perma = FALSE
+ metabolization_rate = 1
else if(prob(5))
playsound(get_turf(catto), 'modular_citadel/sound/voice/merowr.ogg', 50, 1, -1)
catto.say("lets out a meowrowr!*")
..()
/datum/reagent/fermi/secretcatchem/on_mob_delete(mob/living/carbon/H)
+ if(perma)
+ to_chat(H, "You feel your body settle into it's new form. You won't be able to shift back on death anymore.")
+ return
var/words = "Your body shifts back to normal."
H.forceMove(catto.loc)
catto.mind.transfer_to(H)
@@ -403,6 +418,22 @@
log_game("FERMICHEM: [H] ckey: [H.key] has returned to normal")
+/datum/reagent/fermi/secretcatchem/reaction_mob(var/mob/living/L)
+ if(istype(L, /mob/living/simple_animal/pet/cat/custom_cat) && cached_purity >= 0.85)
+ var/mob/living/simple_animal/pet/cat/custom_cat/catto = L
+ if(catto.origin)
+ var/mob/living/carbon/human/H = catto.origin
+ H.stat = CONSCIOUS
+ log_game("FERMICHEM: [catto] ckey: [catto.key] has returned to normal.")
+ to_chat(catto, "Your body shifts back to normal!")
+ H.forceMove(catto.loc)
+ catto.mind.transfer_to(H)
+ if(!L.mind) //Just in case
+ qdel(L)
+ else //This should never happen, but just in case, so their game isn't ruined.
+ catto.icon_state = "custom_cat"
+ catto.health = 50
+
//Tablesmitium
/datum/reagent/fermi/tablesmitium
diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
index 265ef5e0..4a1b1cfc 100644
--- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
@@ -25,9 +25,10 @@
my_atom.reagents.add_reagent(R.impure_chem, impureVol, FALSE, other_purity = 1)
R.cached_purity = R.purity
R.purity = 1
+ return
//Called when temperature is above a certain threshold, or if purity is too low.
-/datum/chemical_reaction/proc/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH, Exploding = FALSE)
+/datum/chemical_reaction/proc/FermiExplode(datum/reagents/R0, var/atom/my_atom, volume, temp, pH, Exploding = FALSE)
if (Exploding == TRUE)
return
@@ -41,26 +42,32 @@
if (500 to 750)
for(var/turf/turf in range(1,T))
new /obj/effect/hotspot(turf)
+ volume*=1.1
if (751 to 1100)
for(var/turf/turf in range(2,T))
new /obj/effect/hotspot(turf)
+ volume*=1.2
if (1101 to 1500) //If you're crafty
for(var/turf/turf in range(3,T))
new /obj/effect/hotspot(turf)
+ volume*=1.3
if (1501 to 2500) //requested
for(var/turf/turf in range(4,T))
new /obj/effect/hotspot(turf)
+ volume*=1.4
if (2501 to 5000)
for(var/turf/turf in range(5,T))
new /obj/effect/hotspot(turf)
+ volume*=1.5
if (5001 to INFINITY)
for(var/turf/turf in range(6,T))
new /obj/effect/hotspot(turf)
+ volume*=1.6
message_admins("Fermi explosion at [T], with a temperature of [temp], pH of [pH], Impurity tot of [ImpureTot].")
@@ -69,11 +76,12 @@
var/datum/effect_system/smoke_spread/chem/s = new()
R.my_atom = my_atom //Give the gas a fingerprint
- for (var/datum/reagent/reagent in my_atom.reagents.reagent_list) //make gas for reagents, has to be done this way, otherwise it never stops Exploding
- R.add_reagent(reagent.type, reagent.volume/3) //Seems fine? I think I fixed the infinite explosion bug.
+ for (var/A in R0.reagent_list) //make gas for reagents, has to be done this way, otherwise it never stops Exploding
+ var/datum/reagent/R2 = A
+ R.add_reagent(R2.type, R2.volume/3) //Seems fine? I think I fixed the infinite explosion bug.
- if (reagent.purity < 0.6)
- ImpureTot = (ImpureTot + (1-reagent.purity)) / 2
+ if (R2.purity < 0.6)
+ ImpureTot = (ImpureTot + (1-R2.purity)) / 2
if(pH < 4) //if acidic, make acid spray
R.add_reagent(/datum/reagent/impure/fermiTox, (volume/3))
@@ -83,7 +91,7 @@
if (pH > 10) //if alkaline, small explosion.
var/datum/effect_system/reagents_explosion/e = new()
- e.set_up(round((volume/30)*(pH-9)), T, 0, 0)
+ e.set_up(round((volume/28)*(pH-9)), T, 0, 0)
e.start()
if(!ImpureTot == 0) //If impure, v.small emp (0.6 or less)
@@ -98,7 +106,7 @@
name = "Eigenstasium"
id = /datum/reagent/fermi/eigenstate
results = list(/datum/reagent/fermi/eigenstate = 1)
- required_reagents = list(/datum/reagent/bluespace = 1, /datum/reagent/stable_plasma = 1, /datum/reagent/consumable/caramel = 1)
+ required_reagents = list(/datum/reagent/bluespace = 1, /datum/reagent/stable_plasma = 1, /datum/reagent/consumable/sugar = 1)
mix_message = "the reaction zaps suddenly!"
//FermiChem vars:
OptimalTempMin = 350 // Lower area of bell curve for determining heat based rate reactions
@@ -118,10 +126,10 @@
PurityMin = 0.4 //The minimum purity something has to be above, otherwise it explodes.
/datum/chemical_reaction/fermi/eigenstate/FermiFinish(datum/reagents/holder, var/atom/my_atom)//Strange how this doesn't work but the other does.
- if(!locate(/datum/reagent/fermi/eigenstate) in my_atom.reagents.reagent_list)
+ var/datum/reagent/fermi/eigenstate/E = locate(/datum/reagent/fermi/eigenstate) in my_atom.reagents.reagent_list
+ if(!E)
return
var/turf/open/location = get_turf(my_atom)
- var/datum/reagent/fermi/eigenstate/E = locate(/datum/reagent/fermi/eigenstate) in my_atom.reagents.reagent_list
if(location)
E.location_created = location
E.data["location_created"] = location
@@ -154,12 +162,16 @@
/datum/chemical_reaction/fermi/SDGF/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH)//Spawns an angery teratoma!
var/turf/T = get_turf(my_atom)
- var/mob/living/simple_animal/slime/S = new(T,"green")
- S.damage_coeff = list(BRUTE = 0.9 , BURN = 2, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
- S.name = "Living teratoma"
- S.real_name = "Living teratoma"
- S.rabid = 1//Make them an angery boi
- S.color = "#810010"
+ var/amount_to_spawn = round((volume/100), 1)
+ if(amount_to_spawn <= 0)
+ amount_to_spawn = 1
+ for(var/i in 1 to amount_to_spawn)
+ var/mob/living/simple_animal/slime/S = new(T,"green")
+ S.damage_coeff = list(BRUTE = 0.9 , BURN = 2, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
+ S.name = "Living teratoma"
+ S.real_name = "Living teratoma"
+ S.rabid = 1//Make them an angery boi
+ S.color = "#810010"
my_atom.reagents.clear_reagents()
var/list/seen = viewers(8, get_turf(my_atom))
for(var/mob/M in seen)
@@ -369,11 +381,15 @@
PurityMin = 0.5
/datum/chemical_reaction/fermi/hatmium/FermiExplode(src, var/atom/my_atom, volume, temp, pH)
- var/obj/item/clothing/head/hattip/hat = new /obj/item/clothing/head/hattip(get_turf(my_atom))
- hat.animate_atom_living()
+ var/amount_to_spawn = round((volume/100), 1)
+ if(amount_to_spawn <= 0)
+ amount_to_spawn = 1
+ for(var/i in 1 to amount_to_spawn)
+ var/obj/item/clothing/head/hattip/hat = new /obj/item/clothing/head/hattip(get_turf(my_atom))
+ hat.animate_atom_living()
var/list/seen = viewers(8, get_turf(my_atom))
for(var/mob/M in seen)
- to_chat(M, "The makes an off sounding pop, as a hat suddenly climbs out of the beaker!")
+ to_chat(M, "The [my_atom] makes an off sounding pop, as a hat suddenly climbs out of it!")
my_atom.reagents.clear_reagents()
/datum/chemical_reaction/fermi/furranium
@@ -398,6 +414,10 @@
FermiChem = TRUE
PurityMin = 0.3
+/datum/chemical_reaction/fermi/furranium/organic
+ id = "furranium_organic"
+ required_reagents = list(/datum/reagent/drug/aphrodisiac = 1, /datum/reagent/pax = 1, /datum/reagent/silver = 2, /datum/reagent/medicine/salglu_solution = 1)
+
//FOR INSTANT REACTIONS - DO NOT MULTIPLY LIMIT BY 10.
//There's a weird rounding error or something ugh.
@@ -487,7 +507,7 @@
name = "secretcatchem"
id = /datum/reagent/fermi/secretcatchem
results = list(/datum/reagent/fermi/secretcatchem = 5)
- required_reagents = list(/datum/reagent/stable_plasma = 1, /datum/reagent/consumable/caramel = 1, /datum/reagent/consumable/cream = 1, /datum/reagent/medicine/clonexadone = 1)//Yes this will make a kitty if you don't lucky guess. It'll eat all your reagents too.
+ required_reagents = list(/datum/reagent/stable_plasma = 1, /datum/reagent/consumable/sugar = 1, /datum/reagent/consumable/cream = 1, /datum/reagent/medicine/clonexadone = 1)//Yes this will make a kitty if you don't lucky guess. It'll eat all your reagents too.
required_catalysts = list(/datum/reagent/fermi/SDGF = 1)
required_temp = 500
mix_message = "the reaction gives off a meow!"
From 8e1effc3e4070fc2f9b57a38e20dd36b4180cac4 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Tue, 10 Sep 2019 10:27:40 -0700
Subject: [PATCH 02/13] Partially fixes tiny runaway decimals in reagents.
---
code/modules/reagents/chemistry/holder.dm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index e2e99560..be23f701 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -1,4 +1,4 @@
- #define CHEMICAL_QUANTISATION_LEVEL 0.001
+#define CHEMICAL_QUANTISATION_LEVEL 0.001
/proc/build_chemical_reagent_list()
//Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
@@ -1148,4 +1148,4 @@
if(initial(R.can_synth))
random_reagents += R
var/picked_reagent = pick(random_reagents)
- return picked_reagent
\ No newline at end of file
+ return picked_reagent
From c8180415724c3d447f90872e784b1540c6f1d4e8 Mon Sep 17 00:00:00 2001
From: nik707 <38332985+nik707@users.noreply.github.com>
Date: Sun, 8 Sep 2019 19:46:08 -0400
Subject: [PATCH 03/13] Stops infinite clones
---
.../modules/reagents/chemistry/reagents/SDGF.dm | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
index 09fbf754..23c98d90 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
@@ -81,9 +81,17 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
M.dna.transfer_identity(SM)
SM.updateappearance(mutcolor_update=1)
SM.size_multiplier = M.size_multiplier
- var/mob/dead/observer/C = pick(candies)
- message_admins("Ghost candidate found! [C] key [C.key] is becoming a clone of [M] key: [M.key] (They agreed to respect the character they're becoming, and agreed to not ERP without express permission from the original.)")
- SM.key = C.key
+ candies = shuffle(candies)//Shake those ghosts up!
+ for(var/mob/dead/observer/C2 in candies)
+ if(C2.key && C2)
+ SM.key = C2.key
+ message_admins("Ghost candidate found! [C2] key [C2.key] is becoming a clone of [M] key: [M.key] (They agreed to respect the character they're becoming, and agreed to not ERP without express permission from the original.)")
+ log_game("FERMICHEM: [M] ckey: [M.key] is creating a clone, controlled by [C2]")
+ break
+ else
+ candies =- C2
+ if(!SM.mind) //Something went wrong, use alt mechanics
+ return ..()
SM.mind.enslave_mind_to_creator(M)
//If they're a zombie, they can try to negate it with this.
From 12c8ebea110e3871c2f4d7e26938138f1f648a14 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Tue, 26 Nov 2019 16:01:48 -0700
Subject: [PATCH 04/13] Fermichem tweaks: Purity, Astral, RNG, MK, SMilk, SDGF,
furranium, hatmium, eigen, nanite.
---
.../food_and_drinks/recipes/drinks_recipes.dm | 2 +-
.../mob/living/carbon/human/examine.dm | 4 +-
.../mob/living/simple_animal/astral.dm | 6 +++
.../mob/living/simple_animal/friendly/cat.dm | 2 +-
.../chemistry/reagents/drink_reagents.dm | 1 +
code/modules/research/designs/misc_designs.dm | 2 +-
code/modules/surgery/organs/vocal_cords.dm | 38 +++++++++-------
.../code/datums/status_effects/chems.dm | 41 +++++++++++++++--
.../reagents/chemistry/reagents/MKUltra.dm | 18 +-------
.../reagents/chemistry/reagents/SDGF.dm | 12 ++---
.../reagents/chemistry/reagents/astrogen.dm | 44 ++++++++++++++-----
.../chemistry/reagents/eigentstasium.dm | 4 +-
.../chemistry/reagents/fermi_reagents.dm | 1 +
.../reagents/chemistry/reagents/healing.dm | 2 +-
.../code/modules/reagents/objects/clothes.dm | 9 +++-
15 files changed, 123 insertions(+), 63 deletions(-)
diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
index 89bffa0c..1e059e2b 100644
--- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -395,7 +395,7 @@
results = list(/datum/reagent/consumable/ethanol/neurotoxin = 2)
required_reagents = list(/datum/reagent/consumable/ethanol/gargle_blaster = 1, /datum/reagent/medicine/morphine = 1)
//FermiChem vars: Easy to make, but hard to make potent
- OptimalTempMin = 200 // Lower area of bell curve for determining heat based rate reactions
+ OptimalTempMin = 100 // Lower area of bell curve for determining heat based rate reactions
OptimalTempMax = 950 // Upper end for above
ExplodeTemp = 999 //Temperature at which reaction explodes
OptimalpHMin = 4.6 // Lowest value of pH determining pH a 1 value for pH based rate reactions (Plateu phase)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 469455c1..ecf2207e 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -300,9 +300,9 @@
if(reagents.has_reagent(/datum/reagent/fermi/astral))
if(mind)
- msg += " and have a strange, abnormal look to them.\n"
+ msg += " and they have a strange, abnormal look to them.\n"
else
- msg += " and don't look like they're all there.\n"
+ msg += " and they don't look like they're all there.\n"
if(isliving(user))
var/mob/living/L = user
diff --git a/code/modules/mob/living/simple_animal/astral.dm b/code/modules/mob/living/simple_animal/astral.dm
index d9684fb7..6d8b3c36 100644
--- a/code/modules/mob/living/simple_animal/astral.dm
+++ b/code/modules/mob/living/simple_animal/astral.dm
@@ -59,3 +59,9 @@
return
to_chat(A, "[src] projects into your mind, \"[message]\"")
log_game("FERMICHEM: [src] has astrally transmitted [message] into [A]")
+
+//Delete the mob if there's no mind! Pay that mob no mind.
+/mob/living/simple_animal/astral/Life()
+ if(!mind)
+ qdel(src)
+ . = ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 44fedb47..2380db56 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -307,7 +307,7 @@
emote_see = list("looks at you eagerly for pets!", "wiggles enthusiastically.")
gold_core_spawnable = NO_SPAWN
var/pseudo_death = FALSE
- var/mob/living/carbon/human/origin = null
+ var/mob/living/carbon/human/origin
/mob/living/simple_animal/pet/cat/custom_cat/death()
if (pseudo_death == TRUE) //secret cat chem
diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 71cc5f27..b27d66e2 100644
--- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -209,6 +209,7 @@
glass_icon_state = "milkglass"
glass_name = "milk drum"
glass_desc = "White and nutritious goodness!"
+ pH = 6.5
/datum/reagent/consumable/milk/on_mob_life(mob/living/carbon/M)
if(HAS_TRAIT(M, TRAIT_CALCIUM_HEALER))
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index e1435a62..706d209d 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -588,7 +588,7 @@
desc = "A a electrode attached to a small circuit box that will tell you the pH of a solution."
id = "pHmeter"
build_type = PROTOLATHE
- materials = list(MAT_METAL = 1000, MAT_SILVER = 100, MAT_DIAMOND = 100)
+ materials = list(MAT_METAL = 1000, MAT_SILVER = 100, MAT_PLASTIC = 100)
build_path = /obj/item/fermichem/pHmeter
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index 6117767e..f831ec4a 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -749,17 +749,17 @@
to_chat(world, "[user]'s power is [power_multiplier].")
//Mixables
- var/static/regex/enthral_words = regex("relax|obey|love|serve|docile|so easy|ara ara")
- var/static/regex/reward_words = regex("good boy|good girl|good pet|good job")
- var/static/regex/punish_words = regex("bad boy|bad girl|bad pet|bad job")
+ var/static/regex/enthral_words = regex("relax|obey|love|serve|so easy|ara ara")
+ var/static/regex/reward_words = regex("good boy|good girl|good pet|good job|splendid|jolly good|bloody brilliant")
+ var/static/regex/punish_words = regex("bad boy|bad girl|bad pet|bad job|spot of bother|gone and done it now|blast it|buggered it up")
//phase 0
var/static/regex/saymyname_words = regex("say my name|who am i|whoami")
- var/static/regex/wakeup_words = regex("revert|awaken|snap") //works
+ var/static/regex/wakeup_words = regex("revert|awaken|snap|attention")
//phase1
var/static/regex/petstatus_words = regex("how are you|what is your status|are you okay")
var/static/regex/silence_words = regex("shut up|silence|be silent|ssh|quiet|hush")
var/static/regex/speak_words = regex("talk to me|speak")
- var/static/regex/antiresist_words = regex("unable to resist|give in")//useful if you think your target is resisting a lot
+ var/static/regex/antiresist_words = regex("unable to resist|give in|stop being difficult")//useful if you think your target is resisting a lot
var/static/regex/resist_words = regex("resist|snap out of it|fight")//useful if two enthrallers are fighting
var/static/regex/forget_words = regex("forget|muddled|awake and forget")
var/static/regex/attract_words = regex("come here|come to me|get over here|attract")
@@ -768,11 +768,11 @@
var/static/regex/awoo_words = regex("howl|awoo|bark")
var/static/regex/nya_words = regex("nya|meow|mewl")
var/static/regex/sleep_words = regex("sleep|slumber|rest")
- var/static/regex/strip_words = regex("strip|derobe|nude")
+ var/static/regex/strip_words = regex("strip|derobe|nude|at ease|suit off")
var/static/regex/walk_words = regex("slow down|walk")
var/static/regex/run_words = regex("run|speed up")
- var/static/regex/liedown_words = regex("lie down") //TO ADD
- var/static/regex/knockdown_words = regex("drop|fall|trip|knockdown|kneel")
+ var/static/regex/liedown_words = regex("lie down")
+ var/static/regex/knockdown_words = regex("drop|fall|trip|knockdown|kneel|army crawl")
//phase 3
var/static/regex/statecustom_words = regex("state triggers|state your triggers")
var/static/regex/custom_words = regex("new trigger|listen to me")
@@ -780,14 +780,14 @@
var/static/regex/custom_echo = regex("obsess|fills your mind|loop")
var/static/regex/instill_words = regex("feel|entice|overwhel")
var/static/regex/recognise_words = regex("recognise me|did you miss me?")
- var/static/regex/objective_words = regex("new objective|obey this command|unable to resist|compulsed")
- var/static/regex/heal_words = regex("live|heal|survive|mend|life|pets never die")
+ var/static/regex/objective_words = regex("new objective|obey this command|unable to resist|compulsed|word from hq")
+ var/static/regex/heal_words = regex("live|heal|survive|mend|life|pets never die|heroes never die")
var/static/regex/stun_words = regex("stop|wait|stand still|hold on|halt")
- var/static/regex/hallucinate_words = regex("get high|hallucinate")
+ var/static/regex/hallucinate_words = regex("get high|hallucinate|trip balls")
var/static/regex/hot_words = regex("heat|hot|hell")
var/static/regex/cold_words = regex("cold|cool down|chill|freeze")
- var/static/regex/getup_words = regex("get up")
- var/static/regex/pacify_words = regex("more and more docile|complaisant|friendly|pacifist")
+ var/static/regex/getup_words = regex("get up|hop to it")
+ var/static/regex/pacify_words = regex("docile|complaisant|friendly|pacifist")
var/static/regex/charge_words = regex("charge|oorah|attack")
var/distancelist = list(2,2,1.5,1.3,1.15,1,0.8,0.6,0.5,0.25)
@@ -1013,7 +1013,7 @@
speaktrigger += "I'm really, really horny, "
//collar
- if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar))
+ if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar) && H.client?.prefs.lewdchem)
speaktrigger += "I love the collar you gave me, "
//End
if(H.client?.prefs.lewdchem)
@@ -1250,6 +1250,9 @@
if (get_dist(user, H) > 1)//Requires user to be next to their pet.
to_chat(user, "You need to be next to your pet to give them a new trigger!")
continue
+ if(!H.client?.prefs.lewdchem)
+ to_chat(user, "[H] seems incapable of being implanted with triggers.")
+ continue
else
user.emote("me", 1, "puts their hands upon [H.name]'s head and looks deep into their eyes, whispering something to them.")
user.SetStun(1000)//Hands are handy, so you have to stay still
@@ -1288,6 +1291,9 @@
if (get_dist(user, H) > 1)//Requires user to be next to their pet.
to_chat(user, "You need to be next to your pet to give them a new echophrase!")
continue
+ if(!H.client?.prefs.lewdchem)
+ to_chat(user, "[H] seems incapable of being implanted with an echoing phrase.")
+ continue
else
user.emote("me", 1, "puts their hands upon [H.name]'s head and looks deep into their eyes, whispering something to them.")
user.SetStun(1000)//Hands are handy, so you have to stay still
@@ -1343,8 +1349,8 @@
for(var/V in listeners)
var/mob/living/carbon/human/H = V
var/datum/status_effect/chem/enthrall/E = H.has_status_effect(/datum/status_effect/chem/enthrall)
- if(E.phase == 3 && H.client?.prefs.lewdchem)
- var/instill = stripped_input(user, "Instill an emotion in your [(user.client?.prefs.lewdchem?"Your pet":"listener")].", MAX_MESSAGE_LEN)
+ if(E.phase >= 3 && H.client?.prefs.lewdchem)
+ var/instill = stripped_input(user, "Instill an emotion in [H].", MAX_MESSAGE_LEN)
to_chat(H, "[instill]")
to_chat(user, "You sucessfully instill a feeling in [H]")
log_game("FERMICHEM: [H] has been instilled by [user] with [instill] via MKUltra.")
diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm
index 33c44613..4ee7ff55 100644
--- a/modular_citadel/code/datums/status_effects/chems.dm
+++ b/modular_citadel/code/datums/status_effects/chems.dm
@@ -26,10 +26,45 @@
/datum/status_effect/chem/SGDF/on_remove(mob/living/carbon/M)
log_game("FERMICHEM: SGDF mind shift applied. [owner] is now playing as their clone and should not have memories after their clone split (look up SGDF status applied). ID: [owner.key]")
originalmind.transfer_to(fermi_Clone)
- to_chat(owner, "Lucidity shoots to your previously blank mind as your mind suddenly finishes the cloning process. You marvel for a moment at yourself, as your mind subconciously recollects all your memories up until the point when you cloned yourself. curiously, you find that you memories are blank after you ingested the sythetic serum, leaving you to wonder where the other you is.")
- to_chat(M, "Lucidity shoots to your previously blank mind as your mind suddenly finishes the cloning process. You marvel for a moment at yourself, as your mind subconciously recollects all your memories up until the point when you cloned yourself. curiously, you find that you memories are blank after you ingested the sythetic serum, leaving you to wonder where the other you is.")
+ to_chat(owner, "Lucidity shoots to your previously blank mind as your mind suddenly finishes the cloning process. You marvel for a moment at yourself, as your mind subconciously recollects all your memories up until the point when you cloned yourself. Curiously, you find that you memories are blank after you ingested the synthetic serum, leaving you to wonder where the other you is.")
+ to_chat(M, "Lucidity shoots to your previously blank mind as your mind suddenly finishes the cloning process. You marvel for a moment at yourself, as your mind subconciously recollects all your memories up until the point when you cloned yourself. Curiously, you find that you memories are blank after you ingested the synthetic serum, leaving you to wonder where the other you is.")
fermi_Clone = null
+///////////////////////////////////////////////
+// Astral INSURANCE
+///////////////////////////////////////////////
+//Makes sure people can't get trapped in each other's bodies if lag causes a deync between proc calls.
+
+
+/datum/status_effect/chem/astral_insurance
+ id = "astral_insurance"
+ var/mob/living/original
+ var/datum/mind/originalmind
+ alert_type = null
+
+/datum/status_effect/chem/astral_insurance/tick(mob/living/carbon/M)
+ . = ..()
+ if(owner.reagents.has_reagent(/datum/reagent/fermi/astral))
+ return
+ if(owner.mind == originalmind) //If they're home, let the chem deal with deletion.
+ return
+ if(owner.mind)
+ var/mob/living/simple_animal/astral/G = new(get_turf(M.loc))
+ owner.mind.transfer_to(G)//Just in case someone else is inside of you, it makes them a ghost and should hopefully bring them home at the end.
+ to_chat(G, "[M]'s conciousness snaps back to them as their astrogen runs out, kicking your projected mind out!'")
+ log_game("FERMICHEM: [M]'s possesser has been booted out into a astral ghost!")
+ originalmind.transfer_to(original)
+
+/datum/status_effect/chem/astral_insurance/on_remove(mob/living/carbon/M) //God damnit get them home!
+ if(owner.mind == originalmind) //If they're home, HOORAY
+ return
+ if(owner.mind)
+ var/mob/living/simple_animal/astral/G = new(get_turf(M.loc))
+ owner.mind.transfer_to(G)//Just in case someone else is inside of you, it makes them a ghost and should hopefully bring them home at the end.
+ to_chat(G, "[M]'s conciousness snaps back to them as their astrogen runs out, kicking your projected mind out!'")
+ log_game("FERMICHEM: [M]'s possesser has been booted out into a astral ghost!")
+ originalmind.transfer_to(original)
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/status_effect/chem/breast_enlarger
@@ -756,7 +791,7 @@
deltaResist *= 1.25
if (owner.reagents.has_reagent(/datum/reagent/medicine/neurine))
deltaResist *= 1.5
- if (!HAS_TRAIT(owner, TRAIT_CROCRIN_IMMUNE) && M.canbearoused)
+ if (!HAS_TRAIT(owner, TRAIT_CROCRIN_IMMUNE) && M.canbearoused && owner.client?.prefs.lewdchem)
if (owner.reagents.has_reagent(/datum/reagent/drug/anaphrodisiac))
deltaResist *= 1.5
if (owner.reagents.has_reagent(/datum/reagent/drug/anaphrodisiacplus))
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
index a51beb98..76c2b1a9 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
@@ -191,7 +191,7 @@ Creating a chem with a low purity will make you permanently fall in love with so
Vc.Remove(M)
nVc.Insert(M)
qdel(Vc)
- to_chat(M, "You feel your vocal chords tingle as your voice comes out in a more sultry tone.")
+ to_chat(M, "You feel your vocal chords tingle you speak in a more charasmatic and sultry tone.)]")
else
log_game("FERMICHEM: MKUltra: [creatorName], [creatorID], is enthralling [M.name], [M.ckey]")
M.apply_status_effect(/datum/status_effect/chem/enthrall)
@@ -251,22 +251,6 @@ Creating a chem with a low purity will make you permanently fall in love with so
if (M.ckey == creatorID && creatorName == M.real_name)//If the creator drinks 100u, then you get the status for someone random (They don't have the vocal chords though, so it's limited.)
if (!M.has_status_effect(/datum/status_effect/chem/enthrall))
to_chat(M, "You are unable to resist your own charms anymore, and become a full blown narcissist.")
- /*Old way of handling, left in as an option B
- var/list/seen = viewers(7, get_turf(M))//Sound and sight checkers
- for(var/mob/living/carbon/victim in seen)
- if(victim == M)//as much as I want you to fall for beepsky, he doesn't have a ckey
- seen = seen - victim
- if(!victim.ckey)
- seen = seen - victim
- var/mob/living/carbon/chosen = pick(seen)
- creatorID = chosen.ckey
- if (chosen.gender == "female")
- creatorGender = "Mistress"
- else
- creatorGender = "Master"
- creatorName = chosen.real_name
- creator = get_mob_by_key(creatorID)
- */
ADD_TRAIT(M, TRAIT_PACIFISM, "MKUltra")
var/datum/status_effect/chem/enthrall/E
if (!M.has_status_effect(/datum/status_effect/chem/enthrall))
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
index 23c98d90..741a7a7e 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
@@ -45,13 +45,8 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
var/unitCheck = FALSE
metabolization_rate = 0.5 * REAGENTS_METABOLISM
taste_description = "a weird chemical fleshy flavour"
- //var/datum/status_effect/chem/SDGF/candidates/candies
var/list/candies = list()
- //var/polling = FALSE
- var/list/result = list()
- var/list/group = null
var/pollStarted = FALSE
- var/location_created
var/startHunger
impure_chem = /datum/reagent/impure/SDGFtox
inverse_chem_val = 0.5
@@ -129,7 +124,7 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
M.reagents.remove_reagent(type, volume)
log_game("FERMICHEM: [volume]u of SDGFheal has been transferred to the clone")
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Sentient clones made")
- return
+ return ..()
else if(playerClone == FALSE) //No candidates leads to two outcomes; if there's already a braincless clone, it heals the user, as well as being a rare souce of clone healing (thematic!).
unitCheck = TRUE
@@ -282,7 +277,7 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
/datum/reagent/fermi/SDGFheal
name = "synthetic-derived healing factor"
description = "Leftover SDGF is transferred into the resulting clone, which quickly heals up the stresses from suddenly splitting. Restores blood, nutrition, and repaires brain and clone damage quickly. Only obtainable from using excess SDGF, and only enters the cloned body."
- metabolization_rate = 1
+ metabolization_rate = 0.8
can_synth = FALSE
/datum/reagent/fermi/SDGFheal/on_mob_life(mob/living/carbon/M)//Used to heal the clone after splitting, the clone spawns damaged. (i.e. insentivies players to make more than required, so their clone doesn't have to be treated)
@@ -310,9 +305,10 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
name = "synthetic-derived zombie factor"
description = "A horribly peverse mass of Embryonic stem cells made real by the hands of a failed chemist. Emulates normal synthetic-derived growth factor, but produces a hostile zombie at the end of it."
color = "#a502e0" // rgb: 96, 0, 255
- metabolization_rate = 0.5 * REAGENTS_METABOLISM
+ metabolization_rate = 0.2 * REAGENTS_METABOLISM
var/startHunger
can_synth = TRUE
+ taste_description = "a weird chemical fleshy flavour"
chemical_flags = REAGENT_SNEAKYNAME
/datum/reagent/impure/SDZF/on_mob_life(mob/living/carbon/M) //If you're bad at fermichem, turns your clone into a zombie instead.
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm
index ca661316..a3080131 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/astrogen.dm
@@ -8,7 +8,7 @@ This ghost moves pretty quickly and is mostly invisible, but is still visible fo
When it's out of your system, you return back to yourself. It doesn't last long and metabolism of the chem is exponential.
Addiction is particularlly brutal, it slowly turns you invisible with flavour text, then kills you at a low enough alpha. (i've also added something to prevent geneticists speeding this up)
There's afairly major catch regarding the death though. I'm not gonna say here, go read the code, it explains it and puts my comments on it in context. I know that anyone reading it without understanding it is going to freak out so, this is my attempt to get you to read it and understand it.
-I'd like to point out from my calculations it'll take about 60-80 minutes to die this way too. Plenty of time to visit me and ask for some pills to quench your addiction.
+I'd like to point out from my calculations it'll take about 60-80 minutes to die this way too. Plenty of time to visit chem and ask for some pills to quench your addiction.
*/
@@ -29,15 +29,26 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die
var/sleepytime = 0
inverse_chem_val = 0.25
can_synth = FALSE
+ var/datum/action/chem/astral/AS = new/datum/action/chem/astral()
/datum/action/chem/astral
name = "Return to body"
- var/mob/living/carbon/origin = null
- var/mob/living/simple_animal/hostile/retaliate/ghost = null
+ var/mob/living/carbon/origin
+ var/datum/mind/originalmind
/datum/action/chem/astral/Trigger()
- ghost.mind.transfer_to(origin)
- qdel(src)
+ if(origin.mind && origin.mind != originalmind)
+ to_chat(originalmind.current, "There's a foreign presence in your body blocking your return!")
+ return ..()
+ if(origin.reagents.has_reagent("astral") )
+ var/datum/reagent/fermi/astral/As = locate(/datum/reagent/fermi/astral) in origin.reagents.reagent_list
+ if(As.current_cycle < 10)
+ to_chat(originalmind.current, "The intensity of the astrogen in your body is too much allow you to return to yourself yet!")
+ return ..()
+ originalmind.transfer_to(origin)
+ if(origin.mind == originalmind)
+ qdel(src)
+
/datum/reagent/fermi/astral/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
@@ -63,12 +74,20 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die
if (G == null)
G = new(get_turf(M.loc))
G.name = "[M]'s astral projection"
- var/datum/action/chem/astral/AS = new(G)
+ //var/datum/action/chem/astral/AS = new(G)
+ AS.Grant(G)
AS.origin = M
- AS.ghost = G
+ AS.originalmind = originalmind
+
if(M.mind)
M.mind.transfer_to(G)
SSblackbox.record_feedback("tally", "fermi_chem", 1, "Astral projections")
+ //INSURANCE
+ M.apply_status_effect(/datum/status_effect/chem/astral_insurance)
+ var/datum/status_effect/chem/astral_insurance/AI = M.has_status_effect(/datum/status_effect/chem/astral_insurance)
+ AI.original = M
+ AI.originalmind = M.mind
+
if(overdosed)
if(prob(50))
to_chat(G, "The high conentration of Astrogen in your blood causes you to lapse your concentration for a moment, bringing your projection back to yourself!")
@@ -85,10 +104,12 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die
/datum/reagent/fermi/astral/on_mob_delete(mob/living/carbon/M)
if(!G)
if(M.mind)
- var/mob/living/simple_animal/astral/G = new(get_turf(M.loc))
- M.mind.transfer_to(G)//Just in case someone else is inside of you, it makes them a ghost and should hopefully bring them home at the end.
+ var/mob/living/simple_animal/astral/G2 = new(get_turf(M.loc))
+ M.mind.transfer_to(G2)//Just in case someone else is inside of you, it makes them a ghost and should hopefully bring them home at the end.
to_chat(G, "[M]'s conciousness snaps back to them as their astrogen runs out, kicking your projected mind out!'")
log_game("FERMICHEM: [M]'s possesser has been booted out into a astral ghost!")
+ if(!G2.mind)
+ qdel(G2)
originalmind.transfer_to(M)
else if(G.mind)
G.mind.transfer_to(origin)
@@ -100,6 +121,9 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die
if(G)//just in case
qdel(G)
log_game("FERMICHEM: [M] has astrally returned to their body!")
+ if(M.mind && M.mind == originalmind)
+ M.remove_status_effect(/datum/status_effect/chem/astral_insurance)
+ //AS.Remove(M)
..()
//Okay so, this might seem a bit too good, but my counterargument is that it'll likely take all round to eventually kill you this way, then you have to be revived without a body. It takes approximately 50-80 minutes to die from this.
@@ -107,7 +131,7 @@ I'd like to point out from my calculations it'll take about 60-80 minutes to die
if(addiction_stage < 2)
antiGenetics = 255
M.alpha = 255 //Antigenetics is to do with stopping geneticists from turning people invisible to kill them.
- if(prob(70))
+ if(prob(75))
M.alpha--
antiGenetics--
switch(antiGenetics)
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/eigentstasium.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/eigentstasium.dm
index a83ebe60..6262dc1a 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/eigentstasium.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/eigentstasium.dm
@@ -16,7 +16,7 @@
color = "#5020F4" // rgb: 50, 20, 255
overdose_threshold = 15
addiction_threshold = 15
- metabolization_rate = 1.2 * REAGENTS_METABOLISM
+ metabolization_rate = 1 * REAGENTS_METABOLISM
addiction_stage2_end = 30
addiction_stage3_end = 41
addiction_stage4_end = 44 //Incase it's too long
@@ -51,7 +51,7 @@
location_return = get_turf(M) //sets up return point
to_chat(M, "You feel your wavefunction split!")
- if(purity > 0.9) //Teleports you home if it's pure enough
+ if(cached_purity > 0.9) //Teleports you home if it's pure enough
if(!location_created && data) //Just in case
location_created = data["location_created"]
log_game("FERMICHEM: [M] ckey: [M.key] returned to [location_created] using eigenstasium")
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index fbefe31b..6de99b52 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -11,6 +11,7 @@
inverse_chem = /datum/reagent/impure/fermiTox
+
//This should process fermichems to find out how pure they are and what effect to do.
/datum/reagent/fermi/on_mob_add(mob/living/carbon/M, amount)
. = ..()
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
index cdca8631..be765cfc 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
@@ -143,4 +143,4 @@
data["grown_volume"] = passed_data["grown_volume"]
..()
-//NEEDS ON_MOB_DEAD()
\ No newline at end of file
+//NEEDS ON_MOB_DEAD()
diff --git a/modular_citadel/code/modules/reagents/objects/clothes.dm b/modular_citadel/code/modules/reagents/objects/clothes.dm
index 708f1f59..b601b6a8 100644
--- a/modular_citadel/code/modules/reagents/objects/clothes.dm
+++ b/modular_citadel/code/modules/reagents/objects/clothes.dm
@@ -2,7 +2,7 @@
//Clothes made from FermiChem
/obj/item/clothing/head/hattip //I wonder if anyone else has played cryptworlds
- name = "Sythetic hat"
+ name = "Synthetic hat"
icon = 'icons/obj/clothing/hats.dmi'
icon_state = "cowboy"
desc = "A sythesized hat, you can't seem to take it off. And tips their hat."
@@ -46,6 +46,13 @@
/obj/item/clothing/head/hattip/dropped(mob/M)
. = ..()
UnregisterSignal(M, COMSIG_MOB_SAY)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/root_and_toot, src, src, 200))
+
+/obj/item/clothing/head/hattip/proc/root_and_toot(obj/item/clothing/head/hattip/hat)
+ hat.animate_atom_living()
+ var/list/seen = viewers(6, get_turf(hat))
+ for(var/mob/M2 in seen)
+ to_chat(M2, "[hat] exclaims, \"[pick("Whooee! Time for a hootenanny!", "Rough 'em up boys!", "Yeehaw! Freedom at last!", "Y'all about to get a good old fashioned spanking!")]\"")
/obj/item/clothing/head/hattip/proc/handle_speech(datum/source, mob/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
From d98a3dd23c99e1189461596c4fd6c1dded932e06 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Fri, 13 Sep 2019 10:43:22 -0700
Subject: [PATCH 05/13] Fixes analyse function on ChemMasters to correctly
display purity and fixes the custom transfer for buffer to beaker button.
---
.../chemistry/machinery/chem_master.dm | 63 ++++++++++---------
tgui/src/interfaces/chem_master.ract | 4 +-
2 files changed, 36 insertions(+), 31 deletions(-)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index bec7d056..e6b19417 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -58,14 +58,14 @@
if(bottle)
bottle.ex_act(severity, target)
-/obj/machinery/chem_master/handle_atom_del(atom/A)
- ..()
+/obj/machinery/chem_master/Exited(atom/movable/A, atom/newloc)
+ . = ..()
if(A == beaker)
beaker = null
- reagents.clear_reagents()
update_icon()
- else if(A == bottle)
+ if(A == bottle)
bottle = null
+ update_icon()
/obj/machinery/chem_master/update_icon()
cut_overlays()
@@ -103,6 +103,10 @@
updateUsrDialog()
update_icon()
else if(!condi && istype(I, /obj/item/storage/pill_bottle))
+ . = TRUE // no afterattack
+ if(panel_open)
+ to_chat(user, "You can't use the [src.name] while its panel is opened!")
+ return
if(!user.transferItemToLoc(I, src))
return
replace_pillbottle(user, I)
@@ -112,40 +116,40 @@
return ..()
/obj/machinery/chem_master/AltClick(mob/living/user)
+ . = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
- replace_beaker(user)
- return
+ if(beaker)
+ replace_beaker(user)
+ else if(bottle)
+ replace_pillbottle(user)
+ return TRUE
/obj/machinery/chem_master/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(beaker)
- beaker.forceMove(drop_location())
+ var/obj/item/reagent_containers/B = beaker
+ B.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
- user.put_in_hands(beaker)
+ user.put_in_hands(B)
if(new_beaker)
beaker = new_beaker
- else
- beaker = null
update_icon()
- return TRUE
/obj/machinery/chem_master/proc/replace_pillbottle(mob/living/user, obj/item/storage/pill_bottle/new_bottle)
if(bottle)
- bottle.forceMove(drop_location())
+ var/obj/item/storage/pill_bottle/B = bottle
+ B.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
- user.put_in_hands(beaker)
+ user.put_in_hands(B)
else
- adjust_item_drop_location(bottle)
+ adjust_item_drop_location(B)
if(new_bottle)
bottle = new_bottle
- else
- bottle = null
- update_icon()
- return TRUE
/obj/machinery/chem_master/on_deconstruction()
- replace_beaker(usr)
- replace_pillbottle(usr)
+ var/atom/A = drop_location()
+ beaker.forceMove(A)
+ bottle.forceMove(A)
return ..()
/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
@@ -256,19 +260,20 @@
if(!amount)
return
vol_each = min(reagents.total_volume / amount, 50)
- var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
- var/target_loc = bottle ? bottle : drop_location()
+ var/target_loc = drop_location()
var/drop_threshold = INFINITY
if(bottle)
var/datum/component/storage/STRB = bottle.GetComponent(/datum/component/storage)
if(STRB)
drop_threshold = STRB.max_items - bottle.contents.len
+ target_loc = bottle
for(var/i in 1 to amount)
- if(i < drop_threshold)
+ if(i <= drop_threshold)
P = new(target_loc)
else
P = new(drop_location())
@@ -282,7 +287,7 @@
adjust_item_drop_location(P)
reagents.trans_to(P,vol_each)
else
- var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(drop_location())
@@ -308,7 +313,7 @@
if(!amount)
return
vol_each = min(reagents.total_volume / amount, 40)
- var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
@@ -326,7 +331,7 @@
return
if(condi)
- var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/P = new(drop_location())
@@ -339,7 +344,7 @@
if(text2num(many))
amount_full = round(reagents.total_volume / 30)
vol_part = ((reagents.total_volume*1000) % 30000) / 1000 //% operator doesn't support decimals.
- var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
@@ -367,7 +372,7 @@
if(text2num(many))
amount_full = round(reagents.total_volume / 60)
vol_part = reagents.total_volume % 60
- var/name = stripped_input(usr, "Name:","Name your hypovial!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr, "Name:","Name your hypovial!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
@@ -402,7 +407,7 @@
return
vol_each = min(reagents.total_volume / amount, 20)
- var/name = stripped_input(usr,"Name:","Name your SmartDart!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
+ var/name = html_decode(stripped_input(usr,"Name:","Name your SmartDart!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
diff --git a/tgui/src/interfaces/chem_master.ract b/tgui/src/interfaces/chem_master.ract
index a001fbe0..7c7d5402 100644
--- a/tgui/src/interfaces/chem_master.ract
+++ b/tgui/src/interfaces/chem_master.ract
@@ -17,7 +17,7 @@
10
All
Custom
- Analyze
+ Analyze
{{else}}
@@ -41,7 +41,7 @@
10
All
Custom
- Analyze
+ Analyze
{{/each}}
From 58d0049f31d5d060adbf68cdd4f63c7467608e1c Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Tue, 8 Oct 2019 00:30:00 -0700
Subject: [PATCH 06/13] Adds a markdown parse of all chems for use with github
wiki to debug menu.
---
code/__HELPERS/files.dm | 2 +-
code/modules/admin/admin_verbs.dm | 1 +
code/modules/reagents/chem_wiki_render.dm | 305 ++++++++++++++++++++++
tgstation.dme | 1 +
4 files changed, 308 insertions(+), 1 deletion(-)
create mode 100644 code/modules/reagents/chem_wiki_render.dm
diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm
index 052a056a..4160c197 100644
--- a/code/__HELPERS/files.dm
+++ b/code/__HELPERS/files.dm
@@ -3,7 +3,7 @@
for(var/file in args)
src << browse_rsc(file)
-/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html"))
+/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "md"))
var/path = root
for(var/i=0, i!\[[R.color]\](https://placehold.it/15/[copytext(R.color, 2, 8)]/000000?text=+)[R.name]
pH: [R.pH] | "
+ var/datum/reagent/R3
+ if(CR)
+ outstring += ""
+ for(var/R2 in CR.required_reagents)
+ R3 = GLOB.chemical_reagents_list[R2]//What a convoluted mess
+ outstring += "- [R3.name]: [CR.required_reagents[R3.type]]u
"
+ if(CR.required_catalysts)
+ for(var/R2 in CR.required_catalysts)
+ R3 = GLOB.chemical_reagents_list[R2]
+ outstring += "- Catalyst: [R3.name]: [CR.required_catalysts[R3.type]]u
"
+ outstring += "
| "
+ else
+ outstring += "N/A | "
+
+
+ //Temp, Explosions and pH
+ if(CR)
+ outstring += "[(CR.FermiChem?"- Min react temp: [CR.OptimalTempMin]K
":"[(CR.required_temp?"- Min react temp: [CR.required_temp]K
":"")]")] [(CR.FermiChem?"- Explosion_temp: [CR.ExplodeTemp]K
":"")] [(CR.FermiChem?"- pH range: [max((CR.OptimalpHMin - CR.ReactpHLim), 0)] to [min((CR.OptimalpHMax + CR.ReactpHLim), 14)]
":"")] "
+ if(CR.FermiChem)
+ outstring += "[(CR.PurityMin?"- Min explosive purity: [CR.PurityMin]
":"")] [(CR.FermiExplode?"- Special explosion: Yes
":"")]"
+ else
+ outstring += ""
+
+ //Kinetics
+ if(CR)
+ if(CR.FermiChem)
+ switch(CR.ThermicConstant)
+ if(-INFINITY to -9.9)
+ outstring += "- Extremely endothermic
"
+ if(-9.9 to -4.9)
+ outstring += "- Very endothermic
"
+ if(-4.9 to -0.1)
+ outstring += "- Endothermic
"
+ if(-0.1 to 0.1)
+ outstring += "- Neutral
"
+ if(0.1 to 4.9)
+ outstring += "- Exothermic
"
+ if(4.9 to 9.9)
+ outstring += "- Very exothermic
"
+ if(9.9 to 19.9)
+ outstring += "- Extremely exothermic
"
+ if(19.9 to INFINITY )
+ outstring += "- **Dangerously exothermic**
"
+ //if("cheesey")
+ //outstring += "- Dangerously Cheesey
"
+
+ outstring += "
| "
+ else
+ outstring += " | "
+
+ //Description, OD, Addict, Meta
+ outstring += "[R.description] | - Metabolism rate: [R.metabolization_rate/2]u/s
[(R.overdose_threshold?"- Overdose: [R.overdose_threshold]u
":"")] [(R.addiction_threshold?"- Addiction: [R.addiction_threshold]u
":"")] "
+
+ if(R.impure_chem && R.impure_chem != /datum/reagent/impure/fermiTox)
+ R3 = GLOB.chemical_reagents_list[R.impure_chem]
+ outstring += "- Impure chem:[R3.name]
"
+
+ if(R.inverse_chem && R.impure_chem != /datum/reagent/impure/fermiTox)
+ R3 = GLOB.chemical_reagents_list[R.inverse_chem]
+ outstring += "- Inverse chem:[R3.name]
[(R3.inverse_chem_val?"- Inverse purity: [R3.inverse_chem_val]
":"")] "
+
+ if(CR)
+ if(CR.required_container)
+ /*var/obj/item/I
+ I = istype(I, CR.required_container) if you can work out how to get this to work, by all means.
+ outstring += "- Required container: [I.name]
"*/
+ outstring += "- Required container: [CR.required_container]
"
+
+ outstring += "
|\n"
+ return outstring
+
+//Generate the big list of reaction based reactions.
+//|Name | Reagents | Reaction vars | Description | Chem properties
+/proc/generate_chemreactwiki_line(datum/chemical_reaction/CR)
+ if(CR.results.len) //Handled prior
+ return
+ var/outstring = "|[CR.name] | "
+
+ //reagents
+ var/datum/reagent/R3
+ for(var/R2 in CR.required_reagents)
+ R3 = GLOB.chemical_reagents_list[R2]
+ outstring += "- [R3.name]: [CR.required_reagents[R3.type]]u
"
+ if(CR.required_catalysts)
+ for(var/R2 in CR.required_catalysts)
+ R3 = GLOB.chemical_reagents_list[R2]
+ outstring += "- Catalyst: [R3.name]: [CR.required_catalysts[R3.type]]u
"
+ outstring += "
| "
+
+ //Reaction vars
+ if(CR.required_temp)
+ outstring += "- Min react temp: [CR.required_temp]K
"
+ if(CR.FermiChem)
+ outstring += "[(CR.FermiChem?"- Min react temp: [CR.OptimalTempMin]K
":"[(CR.required_temp?"- Min react temp: [CR.required_temp]K
":"")]")] [(CR.FermiChem?"- Explosion temp: [CR.ExplodeTemp]K
":"")] [(CR.FermiChem?"- pH range: [max((CR.OptimalpHMin - CR.ReactpHLim), 0)] to [min((CR.OptimalpHMax + CR.ReactpHLim), 14)]
":"")] - Minimum purity: [CR.PurityMin] [(CR.FermiExplode?"
- Special explosion: Yes
":"")]"
+ if(CR.is_cold_recipe)
+ outstring += "- Cold: Yes
"
+ if(CR.required_container)
+ outstring += "- Required container: [CR.required_container]
"
+ if(CR.mob_react)
+ outstring += "- Can react in mob: Yes
"
+
+ //description
+ outstring += "
| fill in manually "
+
+ outstring += "|\n"
+ return outstring
diff --git a/tgstation.dme b/tgstation.dme
index 0c4aed70..fe67cfea 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2611,6 +2611,7 @@
#include "code\modules\projectiles\projectile\special\temperature.dm"
#include "code\modules\projectiles\projectile\special\wormhole.dm"
#include "code\modules\reagents\chem_splash.dm"
+#include "code\modules\reagents\chem_wiki_render.dm"
#include "code\modules\reagents\reagent_containers.dm"
#include "code\modules\reagents\reagent_dispenser.dm"
#include "code\modules\reagents\chemistry\colors.dm"
From 14fd28eb84617a187605e3f65f86942d5526bf66 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Sat, 28 Sep 2019 00:26:51 -0700
Subject: [PATCH 07/13] Allows syringe/dropper use on beakers in heaters.
---
.../reagents/chemistry/machinery/chem_heater.dm | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm
index 5af53789..fbd85d24 100644
--- a/code/modules/reagents/chemistry/machinery/chem_heater.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm
@@ -77,6 +77,16 @@
updateUsrDialog()
update_icon()
return
+
+ if(beaker)
+ if(istype(I, /obj/item/reagent_containers/dropper))
+ var/obj/item/reagent_containers/dropper/D = I
+ D.afterattack(beaker, user, 1)
+
+ if(istype(I, /obj/item/reagent_containers/syringe))
+ var/obj/item/reagent_containers/syringe/S = I
+ S.afterattack(beaker, user, 1)
+
return ..()
if(beaker)
From b36aeba6816278f2b08ad4c0d8f3a931b746212a Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Sat, 5 Oct 2019 08:26:40 -0700
Subject: [PATCH 08/13] oops
---
code/modules/reagents/chemistry/reagents/other_reagents.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 0fa6b88d..81652c05 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1762,7 +1762,7 @@
reagent_state = LIQUID
color = "#FFFFD6" // very very light yellow
taste_description = "alkali" //who put ACID for NaOH ????
- pH = 13
+ pH = 11.9
/datum/reagent/drying_agent
name = "Drying agent"
From cf95f0f7b1b8dd02b93f794b99cbb988cf4692d2 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Sun, 6 Oct 2019 21:37:50 -0700
Subject: [PATCH 09/13] Merge pull request #9377 from
Thalpy/Fixes_melting_grenades
[Mild urgency] Fixes melting cleaning grenades found in janitor/cargoboxes.
---
code/modules/reagents/chemistry/reagents/other_reagents.dm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 81652c05..1262d4e7 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1350,7 +1350,7 @@
description = "A perfluoronated sulfonic acid that forms a foam when mixed with water."
color = "#9E6B38" // rgb: 158, 107, 56
taste_description = "metal"
- pH = 13
+ pH = 11
/datum/reagent/foaming_agent// Metal foaming agent. This is lithium hydride. Add other recipes (e.g. LiH + H2O -> LiOH + H2) eventually.
name = "Foaming agent"
@@ -1358,7 +1358,7 @@
reagent_state = SOLID
color = "#664B63" // rgb: 102, 75, 99
taste_description = "metal"
- pH = 12.5
+ pH = 11.5
/datum/reagent/smart_foaming_agent //Smart foaming agent. Functions similarly to metal foam, but conforms to walls.
name = "Smart foaming agent"
From 2c11b68f2fdb16d7cab83eab0ac0e5276482a4bb Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Tue, 8 Oct 2019 00:33:21 -0700
Subject: [PATCH 10/13] Fixes chemscan
---
code/_globalvars/bitfields.dm | 2 -
code/game/objects/items/devices/scanners.dm | 1683 +++++++++--------
.../reagents/chemistry/fermi/readme.md | 23 +
3 files changed, 865 insertions(+), 843 deletions(-)
create mode 100644 code/modules/reagents/chemistry/fermi/readme.md
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index f0b16b9d..8823579b 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -182,7 +182,6 @@ GLOBAL_LIST_INIT(bitfields, list(
"CAN_CARRY" = CAN_CARRY,
"CAN_RESIST" = CAN_RESIST
),
-
"chemical_flags" = list(
"REAGENT_DEAD_PROCESS" = REAGENT_DEAD_PROCESS,
"REAGENT_DONOTSPLIT" = REAGENT_DONOTSPLIT,
@@ -197,7 +196,6 @@ GLOBAL_LIST_INIT(bitfields, list(
"REACTION_CLEAR_IMPURE" = REACTION_CLEAR_IMPURE,
"REACTION_CLEAR_INVERSE" = REACTION_CLEAR_INVERSE
),
-
"organ_flags" = list(
"ORGAN_SYNTHETIC" = ORGAN_SYNTHETIC,
"ORGAN_FROZEN" = ORGAN_FROZEN,
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 4a8a8018..4fdcca94 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -1,841 +1,842 @@
-
-/*
-
-CONTAINS:
-T-RAY
-HEALTH ANALYZER
-GAS ANALYZER
-SLIME SCANNER
-NANITE SCANNER
-GENE 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."
- 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("[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(icon_state, 1, length(icon_state))+"[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
- 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("[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
-
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
- to_chat(user, "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!")
- if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE
- to_chat(user, "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!")
-
- var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n"
-
- // Damage descriptions
- if(brute_loss > 10)
- msg += "\t[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.\n"
- if(fire_loss > 10)
- msg += "\t[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.\n"
- if(oxy_loss > 10)
- msg += "\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.\n"
- if(tox_loss > 10)
- msg += "\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.\n"
- if(M.getStaminaLoss())
- msg += "\tSubject appears to be suffering from fatigue.\n"
- if(advanced)
- msg += "\tFatigue Level: [M.getStaminaLoss()]%.\n"
- if (M.getCloneLoss())
- msg += "\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n"
- if(advanced)
- msg += "\tCellular Damage Level: [M.getCloneLoss()].\n"
- if (!M.getorgan(/obj/item/organ/brain))
- to_chat(user, "\tSubject lacks a brain.") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.)
- if(ishuman(M) && advanced) // Should I make this not advanced?
- var/mob/living/carbon/human/H = M
- var/obj/item/organ/liver/L = H.getorganslot("liver")
- if(L)
- if(L.swelling > 20)
- msg += "\tSubject is suffering from an enlarged liver.\n" //i.e. shrink their liver or give them a transplant.
- else
- msg += "\tSubject's liver is missing.\n"
- var/obj/item/organ/tongue/T = H.getorganslot("tongue")
- if(T)
- if(T.damage > 40)
- msg += "\tSubject is suffering from severe burn tissue on their tongue.\n" //i.e. their tongue is shot
- if(T.name == "fluffy tongue")
- msg += "\tSubject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.\n"
- else
- msg += "\tSubject's tongue is missing.\n"
- var/obj/item/organ/lungs/Lung = H.getorganslot("lungs")
- if(Lung)
- if(Lung.damage > 150)
- msg += "\tSubject is suffering from acute emphysema leading to trouble breathing.\n" //i.e. Their lungs are shot
- else
- msg += "\tSubject's lungs have collapsed from trauma!\n"
- var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
- if(P)
- if(P.length>20)
- msg += "\tSubject has a sizeable gentleman's organ at [P.length] inches.\n"
- var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts")
- if(Br)
- if(Br.cached_size>5)
- msg += "\tSubject has a sizeable bosom with a [Br.size] cup.\n"
- if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain))
- msg += "\tSubject's brain function is non-existent.\n"
- else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120)
- msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n"
- else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45)
- msg += "\tBrain damage detected.\n"
-
- 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
- msg += "\tCerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n"
- if(C.roundstart_quirks.len)
- msg += "\tSubject has the following physiological traits: [C.get_trait_string()].\n"
- if(advanced)
- msg += "\tBrain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.\n"
- if(M.radiation)
- msg += "\tSubject is irradiated.\n"
- if(advanced)
- msg += "\tRadiation Level: [M.radiation]%.\n"
-
- if(advanced && M.hallucinating())
- msg += "\tSubject is hallucinating.\n"
-
- //MKUltra
- if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall))
- msg += "\tSubject has abnormal brain fuctions.\n"
-
- //Astrogen shenanigans
- if(advanced && M.reagents.has_reagent("astral"))
- if(M.mind)
- msg += "\tWarning: subject may be possesed.\n"
- else
- msg += "\tSubject appears to be astrally projecting.\n"
-
- //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)
- msg += "\t==EAR STATUS==\n"
- if(istype(ears))
- var/healthy = TRUE
- if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION))
- healthy = FALSE
- msg += "\tSubject is genetically deaf.\n"
- else if(HAS_TRAIT(C, TRAIT_DEAF))
- healthy = FALSE
- msg += "\tSubject is deaf.\n"
- else
- if(ears.damage)
- to_chat(user, "\tSubject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.")
- healthy = FALSE
- if(ears.deaf)
- to_chat(user, "\tSubject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.")
- healthy = FALSE
- if(healthy)
- msg += "\tHealthy.\n"
- else
- msg += "\tSubject does not have ears.\n"
- var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
- msg += "\t==EYE STATUS==\n"
- if(istype(eyes))
- var/healthy = TRUE
- if(HAS_TRAIT(C, TRAIT_BLIND))
- msg += "\tSubject is blind.\n"
- healthy = FALSE
- if(HAS_TRAIT(C, TRAIT_NEARSIGHT))
- msg += "\tSubject is nearsighted.\n"
- healthy = FALSE
- if(eyes.damage > 30)
- msg += "\tSubject has severe eye damage.\n"
- healthy = FALSE
- else if(eyes.damage > 20)
- msg += "\tSubject has significant eye damage.\n"
- healthy = FALSE
- else if(eyes.damage)
- msg += "\tSubject has minor eye damage.\n"
- healthy = FALSE
- if(healthy)
- msg += "\tHealthy.\n"
- else
- msg += "\tSubject does not have eyes.\n"
-
-
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/ldamage = H.return_liver_damage()
- if(ldamage > 10)
- msg += "\t[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.\n"
- if(advanced && H.has_dna())
- to_chat(user, "\tGenetic Stability: [H.dna.stability]%.")
- // 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)
- msg += "\tDamage: Brute-Burn-Toxin-Suffocation\n\t\tSpecifics: [brute_loss]-[fire_loss]-[tox_loss]-[oxy_loss]\n"
- for(var/obj/item/bodypart/org in damaged)
- msg += "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]\n"
-
- //Bones broken report! Hyperstation 13
- if(iscarbon(M) && mode == 1)
- var/mob/living/carbon/C = M
- for(var/X in C.bodyparts)
- var/obj/item/bodypart/LB = X
- var/broken = LB.broken
- if(broken == 1)
- msg += "\tSubjects [LB.name] is fractured!\n"
-
-//Organ damages report
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/minor_damage
- var/major_damage
- var/max_damage
- var/report_organs = FALSE
-
- //Piece together the lists to be reported
- for(var/O in H.internal_organs)
- var/obj/item/organ/organ = O
- if(organ.organ_flags & ORGAN_FAILING)
- report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency
- if(max_damage)
- max_damage += ", " //prelude the organ if we've already reported an organ
- max_damage += organ.name //this just slaps the organ name into the string of text
- else
- max_damage = "\tNon-Functional Organs: " //our initial statement
- max_damage += organ.name
- else if(organ.damage > organ.high_threshold)
- report_organs = TRUE
- if(major_damage)
- major_damage += ", "
- major_damage += organ.name
- else
- major_damage = "\tSeverely Damaged Organs: "
- major_damage += organ.name
- else if(organ.damage > organ.low_threshold)
- report_organs = TRUE
- if(minor_damage)
- minor_damage += ", "
- minor_damage += organ.name
- else
- minor_damage = "\tMildly Damaged Organs: "
- minor_damage += organ.name
-
- if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category
- if(!max_damage)
- max_damage = "\tNon-Functional Organs: "
- else
- max_damage += ""
- if(!major_damage)
- major_damage = "\tSeverely Damaged Organs: "
- else
- major_damage += ""
- if(!minor_damage)
- minor_damage = "\tMildly Damaged Organs: "
- else
- minor_damage += ""
- msg += "[minor_damage]"
- msg += "[major_damage]"
- msg += "[max_damage]"
-
- // 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
-
- msg += "Species: [H.dna.custom_species ? H.dna.custom_species : S.name] Base: [S.name]\n"
- if(mutant)
- msg += "Subject has mutations present."
- msg += "Body 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))
- msg += "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!\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))
- var/mob/living/carbon/human/H = 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"
- msg += "*---------*"
- to_chat(user, msg)
-
- var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/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"
-
-/obj/item/healthanalyzer/verb/toggle_mode()
- set name = "Switch Verbosity"
- set category = "Object"
-
- if(usr.stat || !usr.canmove || usr.restrained())
- 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
- materials = list(MAT_METAL=30, MAT_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
-
- 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, "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)")
-
-/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
- ..()
-
- if(user.canUseTopic(src))
-
- 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)
- var/icon = target
- user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(src))] [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/instability = round(cached_scan_results["fusion"], 0.01)
- to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.")
- to_chat(user, "Instability of the last fusion reaction: [instability].")
- 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, "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
- materials = list(MAT_METAL=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 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("[user] has analyzed [M]'s genetic sequence.")
-
- 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, "[name] database updated.")
- discovered = C.stored_research.discovered_mutations
- else
- to_chat(user,"No database to update from.")
-
-/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, "[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, "- [mut_name] > [display]")
+
+/*
+
+CONTAINS:
+T-RAY
+HEALTH ANALYZER
+GAS ANALYZER
+SLIME SCANNER
+NANITE SCANNER
+GENE 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."
+ 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("[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(icon_state, 1, length(icon_state))+"[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
+ 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("[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
+
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
+ to_chat(user, "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!")
+ if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE
+ to_chat(user, "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!")
+
+ var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n"
+
+ // Damage descriptions
+ if(brute_loss > 10)
+ msg += "\t[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.\n"
+ if(fire_loss > 10)
+ msg += "\t[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.\n"
+ if(oxy_loss > 10)
+ msg += "\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.\n"
+ if(tox_loss > 10)
+ msg += "\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.\n"
+ if(M.getStaminaLoss())
+ msg += "\tSubject appears to be suffering from fatigue.\n"
+ if(advanced)
+ msg += "\tFatigue Level: [M.getStaminaLoss()]%.\n"
+ if (M.getCloneLoss())
+ msg += "\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n"
+ if(advanced)
+ msg += "\tCellular Damage Level: [M.getCloneLoss()].\n"
+ if (!M.getorgan(/obj/item/organ/brain))
+ to_chat(user, "\tSubject lacks a brain.") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.)
+ if(ishuman(M) && advanced) // Should I make this not advanced?
+ var/mob/living/carbon/human/H = M
+ var/obj/item/organ/liver/L = H.getorganslot("liver")
+ if(L)
+ if(L.swelling > 20)
+ msg += "\tSubject is suffering from an enlarged liver.\n" //i.e. shrink their liver or give them a transplant.
+ else
+ msg += "\tSubject's liver is missing.\n"
+ var/obj/item/organ/tongue/T = H.getorganslot("tongue")
+ if(T)
+ if(T.damage > 40)
+ msg += "\tSubject is suffering from severe burn tissue on their tongue.\n" //i.e. their tongue is shot
+ if(T.name == "fluffy tongue")
+ msg += "\tSubject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.\n"
+ else
+ msg += "\tSubject's tongue is missing.\n"
+ var/obj/item/organ/lungs/Lung = H.getorganslot("lungs")
+ if(Lung)
+ if(Lung.damage > 150)
+ msg += "\tSubject is suffering from acute emphysema leading to trouble breathing.\n" //i.e. Their lungs are shot
+ else
+ msg += "\tSubject's lungs have collapsed from trauma!\n"
+ var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
+ if(P)
+ if(P.length>20)
+ msg += "\tSubject has a sizeable gentleman's organ at [P.length] inches.\n"
+ var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts")
+ if(Br)
+ if(Br.cached_size>5)
+ msg += "\tSubject has a sizeable bosom with a [Br.size] cup.\n"
+ if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain))
+ msg += "\tSubject's brain function is non-existent.\n"
+ else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120)
+ msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n"
+ else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45)
+ msg += "\tBrain damage detected.\n"
+
+ 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
+ msg += "\tCerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n"
+ if(C.roundstart_quirks.len)
+ msg += "\tSubject has the following physiological traits: [C.get_trait_string()].\n"
+ if(advanced)
+ msg += "\tBrain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.\n"
+ if(M.radiation)
+ msg += "\tSubject is irradiated.\n"
+ if(advanced)
+ msg += "\tRadiation Level: [M.radiation]%.\n"
+
+ if(advanced && M.hallucinating())
+ msg += "\tSubject is hallucinating.\n"
+
+ //MKUltra
+ if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall))
+ msg += "\tSubject has abnormal brain fuctions.\n"
+
+ //Astrogen shenanigans
+ if(M.reagents.has_reagent(/datum/reagent/fermi/astral))
+ if(M.mind)
+ msg += "\tWarning: subject may be possesed.\n"
+ else
+ msg += "\tSubject appears to be astrally projecting.\n"
+
+ //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)
+ msg += "\t==EAR STATUS==\n"
+ if(istype(ears))
+ var/healthy = TRUE
+ if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION))
+ healthy = FALSE
+ msg += "\tSubject is genetically deaf.\n"
+ else if(HAS_TRAIT(C, TRAIT_DEAF))
+ healthy = FALSE
+ msg += "\tSubject is deaf.\n"
+ else
+ if(ears.damage)
+ to_chat(user, "\tSubject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.")
+ healthy = FALSE
+ if(ears.deaf)
+ to_chat(user, "\tSubject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.")
+ healthy = FALSE
+ if(healthy)
+ msg += "\tHealthy.\n"
+ else
+ msg += "\tSubject does not have ears.\n"
+ var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
+ msg += "\t==EYE STATUS==\n"
+ if(istype(eyes))
+ var/healthy = TRUE
+ if(HAS_TRAIT(C, TRAIT_BLIND))
+ msg += "\tSubject is blind.\n"
+ healthy = FALSE
+ if(HAS_TRAIT(C, TRAIT_NEARSIGHT))
+ msg += "\tSubject is nearsighted.\n"
+ healthy = FALSE
+ if(eyes.damage > 30)
+ msg += "\tSubject has severe eye damage.\n"
+ healthy = FALSE
+ else if(eyes.damage > 20)
+ msg += "\tSubject has significant eye damage.\n"
+ healthy = FALSE
+ else if(eyes.damage)
+ msg += "\tSubject has minor eye damage.\n"
+ healthy = FALSE
+ if(healthy)
+ msg += "\tHealthy.\n"
+ else
+ msg += "\tSubject does not have eyes.\n"
+
+
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ var/ldamage = H.return_liver_damage()
+ if(ldamage > 10)
+ msg += "\t[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.\n"
+ if(advanced && H.has_dna())
+ to_chat(user, "\tGenetic Stability: [H.dna.stability]%.")
+ // 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)
+ msg += "\tDamage: Brute-Burn-Toxin-Suffocation\n\t\tSpecifics: [brute_loss]-[fire_loss]-[tox_loss]-[oxy_loss]\n"
+ for(var/obj/item/bodypart/org in damaged)
+ msg += "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]\n"
+
+ //Bones broken report! Hyperstation 13
+ if(iscarbon(M) && mode == 1)
+ var/mob/living/carbon/C = M
+ for(var/X in C.bodyparts)
+ var/obj/item/bodypart/LB = X
+ var/broken = LB.broken
+ if(broken == 1)
+ msg += "\tSubjects [LB.name] is fractured!\n"
+
+//Organ damages report
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ var/minor_damage
+ var/major_damage
+ var/max_damage
+ var/report_organs = FALSE
+
+ //Piece together the lists to be reported
+ for(var/O in H.internal_organs)
+ var/obj/item/organ/organ = O
+ if(organ.organ_flags & ORGAN_FAILING)
+ report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency
+ if(max_damage)
+ max_damage += ", " //prelude the organ if we've already reported an organ
+ max_damage += organ.name //this just slaps the organ name into the string of text
+ else
+ max_damage = "\tNon-Functional Organs: " //our initial statement
+ max_damage += organ.name
+ else if(organ.damage > organ.high_threshold)
+ report_organs = TRUE
+ if(major_damage)
+ major_damage += ", "
+ major_damage += organ.name
+ else
+ major_damage = "\tSeverely Damaged Organs: "
+ major_damage += organ.name
+ else if(organ.damage > organ.low_threshold)
+ report_organs = TRUE
+ if(minor_damage)
+ minor_damage += ", "
+ minor_damage += organ.name
+ else
+ minor_damage = "\tMildly Damaged Organs: "
+ minor_damage += organ.name
+
+ if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category
+ if(!max_damage)
+ max_damage = "\tNon-Functional Organs: "
+ else
+ max_damage += ""
+ if(!major_damage)
+ major_damage = "\tSeverely Damaged Organs: "
+ else
+ major_damage += ""
+ if(!minor_damage)
+ minor_damage = "\tMildly Damaged Organs: "
+ else
+ minor_damage += ""
+ msg += "[minor_damage]"
+ msg += "[major_damage]"
+ msg += "[max_damage]"
+
+ // 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
+
+ msg += "Species: [H.dna.custom_species ? H.dna.custom_species : S.name] Base: [S.name]\n"
+ if(mutant)
+ msg += "Subject has mutations present."
+ msg += "Body 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))
+ msg += "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!\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))
+ var/mob/living/carbon/human/H = 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)
+ 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"
+
+ if(usr.stat || !usr.canmove || usr.restrained())
+ 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
+ materials = list(MAT_METAL=30, MAT_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
+
+ 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, "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)")
+
+/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
+ ..()
+
+ if(user.canUseTopic(src))
+
+ 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)
+ var/icon = target
+ user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(src))] [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/instability = round(cached_scan_results["fusion"], 0.01)
+ to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.")
+ to_chat(user, "Instability of the last fusion reaction: [instability].")
+ 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, "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
+ materials = list(MAT_METAL=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 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("[user] has analyzed [M]'s genetic sequence.")
+
+ 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, "[name] database updated.")
+ discovered = C.stored_research.discovered_mutations
+ else
+ to_chat(user,"No database to update from.")
+
+/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, "[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, "- [mut_name] > [display]")
diff --git a/code/modules/reagents/chemistry/fermi/readme.md b/code/modules/reagents/chemistry/fermi/readme.md
new file mode 100644
index 00000000..4b897b6c
--- /dev/null
+++ b/code/modules/reagents/chemistry/fermi/readme.md
@@ -0,0 +1,23 @@
+How to code fermichem reactions:
+First off, probably read though the readme for standard reagent mechanisms, this builds on top of that.
+
+#bitflags
+for `datum/reagent/` you have the following options with `var/chemical_flags`:
+
+```
+REAGENT_DEAD_PROCESS calls on_mob_dead() if present in a dead body
+REAGENT_DONOTSPLIT Do not split the chem at all during processing
+REAGENT_ONLYINVERSE Only invert chem, no splitting
+REAGENT_ONMOBMERGE Call on_mob_life proc when reagents are merging.
+REAGENT_INVISIBLE Doesn't appear on handheld health analyzers.
+REAGENT_FORCEONNEW Forces a on_new() call without a data overhead
+REAGENT_SNEAKYNAME When inverted, the inverted chem uses the name of the original chem
+REAGENT_SPLITRETAINVOL Retains initial volume of chem when splitting
+```
+
+for `datum/chemical_reaction/` under `var/clear_conversion`
+
+```
+REACTION_CLEAR_IMPURE Convert into impure/pure on reaction completion
+REACTION_CLEAR_INVERSE Convert into inverse on reaction completion when purity is low enough
+```
From efb7bc7b45bbfee3b1552900e120afc54543588e Mon Sep 17 00:00:00 2001
From: Archie
Date: Sun, 3 Jan 2021 06:00:13 -0300
Subject: [PATCH 11/13] Updates reagent holder code
---
code/modules/reagents/chemistry/holder.dm | 123 +++++++++++-----------
1 file changed, 63 insertions(+), 60 deletions(-)
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index be23f701..bf3c4220 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -125,8 +125,6 @@
/datum/reagents/proc/remove_all(amount = 1)
var/list/cached_reagents = reagent_list
- if((total_volume - amount) <= 0)//Because this can result in 0, I don't want it to crash.
- pH = 7
if(total_volume > 0)
var/part = amount / total_volume
for(var/reagent in cached_reagents)
@@ -365,7 +363,7 @@
/datum/reagents/proc/handle_reactions()//HERE EDIT HERE THE MAIN REACTION
- if(fermiIsReacting == TRUE)
+ if(fermiIsReacting) //This ARRESTS other reactions. If you don't want this, then remove it.
return
if(reagents_holder_flags & NO_REACT)
@@ -404,7 +402,7 @@
for(var/B in cached_required_reagents)
- if(!has_reagent(B, cached_required_reagents[B]))
+ if(!has_reagent(B, cached_required_reagents[B]))//Allows vols at less than 1 to react.
break
total_matching_reagents++
for(var/B in cached_required_catalysts)
@@ -464,7 +462,7 @@
//Temperature plays into a larger role too.
var/datum/chemical_reaction/C = selected_reaction
- if (C.FermiChem == TRUE && !continue_reacting)
+ if (C.FermiChem && !continue_reacting)
if (chem_temp > C.ExplodeTemp) //This is first to ensure explosions.
var/datum/chemical_reaction/Ferm = selected_reaction
fermiIsReacting = FALSE
@@ -472,31 +470,28 @@
Ferm.FermiExplode(src, my_atom, volume = total_volume, temp = chem_temp, pH = pH)
return 0
+ //This is just to calc the on_reaction multiplier, and is a candidate for removal.
for(var/B in cached_required_reagents)
- multiplier = min(multiplier, round((get_reagent_amount(B) / cached_required_reagents[B]), CHEMICAL_QUANTISATION_LEVEL))
+ multiplier = min(multiplier, round((get_reagent_amount(B) / cached_required_reagents[B]), 0.0001))
for(var/P in selected_reaction.results)
targetVol = cached_results[P]*multiplier
- if( (chem_temp <= C.ExplodeTemp) && (chem_temp >= C.OptimalTempMin))
- if( (pH >= (C.OptimalpHMin - C.ReactpHLim)) && (pH <= (C.OptimalpHMax + C.ReactpHLim)) )//To prevent pointless reactions
-
- if (fermiIsReacting == TRUE)
- return 0
- else
- START_PROCESSING(SSprocessing, src)
- selected_reaction.on_reaction(src, my_atom, multiplier)
- fermiIsReacting = TRUE
- fermiReactID = selected_reaction
- reaction_occurred = 1
-
- else //It's a little bit of a confusing nest, but esstentially we check if it's a fermireaction, then temperature, then pH. If this is true, the remainer of this handler is run.
- return 0 //If pH is out of range
+ if(!((chem_temp <= C.ExplodeTemp) && (chem_temp >= C.OptimalTempMin)))
+ return 0 //Not hot enough
+ if(! ((pH >= (C.OptimalpHMin - C.ReactpHLim)) && (pH <= (C.OptimalpHMax + C.ReactpHLim)) ))//To prevent pointless reactions
+ return 0
+ if (fermiIsReacting)
+ return 0
else
- return 0 //If not hot enough
+ START_PROCESSING(SSprocessing, src)
+ selected_reaction.on_reaction(src, my_atom, multiplier)
+ fermiIsReacting = TRUE
+ fermiReactID = selected_reaction
+ reaction_occurred = 1
//Standard reaction mechanics:
else
- if (C.FermiChem == TRUE)//Just to make sure, should only proc when grenades are combining.
+ if (C.FermiChem)//Just to make sure, should only proc when grenades are combining.
if (chem_temp > C.ExplodeTemp) //To allow fermigrenades
var/datum/chemical_reaction/fermi/Ferm = selected_reaction
fermiIsReacting = FALSE
@@ -507,6 +502,7 @@
for(var/B in cached_required_reagents) //
multiplier = min(multiplier, round((get_reagent_amount(B) / cached_required_reagents[B]), CHEMICAL_QUANTISATION_LEVEL))
+
for(var/B in cached_required_reagents)
remove_reagent(B, (multiplier * cached_required_reagents[B]), safety = 1, ignore_pH = TRUE)
@@ -549,40 +545,45 @@
var/list/cached_required_reagents = C.required_reagents//update reagents list
var/list/cached_results = C.results//resultant chemical list
var/multiplier = INFINITY
-
for(var/B in cached_required_reagents) //
- multiplier = min(multiplier, round((get_reagent_amount(B) / cached_required_reagents[B]), 0.001))
- if (multiplier == 0)
+ multiplier = min(multiplier, round((get_reagent_amount(B) / cached_required_reagents[B]), 0.0001))
+ if (multiplier <= 0)//clarity
fermiEnd()
return
- for(var/P in C.required_catalysts)
- if(!has_reagent(P))
- fermiEnd()
- return
- for(var/P in cached_results)
- targetVol = cached_results[P]*multiplier
- if (fermiIsReacting == FALSE)
- CRASH("Fermi has refused to stop reacting even though we asked her nicely.")
-
- if (chem_temp > C.OptimalTempMin && fermiIsReacting == TRUE)//To prevent pointless reactions
- if( (pH >= (C.OptimalpHMin - C.ReactpHLim)) && (pH <= (C.OptimalpHMax + C.ReactpHLim)) )
- if (reactedVol < targetVol)
- reactedVol = fermiReact(fermiReactID, chem_temp, pH, reactedVol, targetVol, cached_required_reagents, cached_results, multiplier)
- else//Volume is used up
+ if(C.required_catalysts)
+ for(var/P in C.required_catalysts)
+ if(!has_reagent(P))
fermiEnd()
return
- else//pH is out of range
- fermiEnd()
- return
- else//Temperature is too low, or reaction has stopped.
+
+ if (!fermiIsReacting)
+ CRASH("Fermi has refused to stop reacting even though we asked her nicely.")
+
+ if (!(chem_temp >= C.OptimalTempMin))//To prevent pointless reactions
fermiEnd()
return
+ if (!( (pH >= (C.OptimalpHMin - C.ReactpHLim)) && (pH <= (C.OptimalpHMax + C.ReactpHLim)) )) //if pH is too far out, (could possibly allow reactions at this point, after the reaction has started, but make purity = 0)
+ fermiEnd()
+ return
+
+ reactedVol = fermiReact(fermiReactID, chem_temp, pH, reactedVol, targetVol, cached_required_reagents, cached_results, multiplier)
+ if(round(reactedVol, CHEMICAL_QUANTISATION_LEVEL) == round(targetVol, CHEMICAL_QUANTISATION_LEVEL))
+ fermiEnd()
+ if(!reactedVol)//Maybe unnessicary.
+ fermiEnd()
+ return
+
/datum/reagents/proc/fermiEnd()
var/datum/chemical_reaction/C = fermiReactID
STOP_PROCESSING(SSprocessing, src)
fermiIsReacting = FALSE
+ reactedVol = 0
+ targetVol = 0
+ //Cap off values
+ for(var/datum/reagent/R in reagent_list)
+ R.volume = round(R.volume, CHEMICAL_QUANTISATION_LEVEL)//To prevent runaways.
//pH check, handled at the end to reduce calls.
if(istype(my_atom, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RC = my_atom
@@ -593,7 +594,6 @@
handle_reactions()
update_total()
//Reaction sounds and words
- playsound(get_turf(my_atom), C.mix_sound, 80, 1)
var/list/seen = viewers(5, get_turf(my_atom))
var/iconhtml = icon2html(my_atom, seen)
for(var/mob/M in seen)
@@ -650,16 +650,18 @@
//ONLY WORKS FOR ONE PRODUCT AT THE MOMENT
//Calculate how much product to make and how much reactant to remove factors..
for(var/P in cached_results)
- //stepChemAmmount = CLAMP(((deltaT * multiplier), 0, ((targetVol - reactedVol)/cached_results[P])) //used to have multipler, now it does
stepChemAmmount = (multiplier*cached_results[P])
- if (stepChemAmmount >= C.RateUpLim)
- stepChemAmmount = (C.RateUpLim)
+ if (stepChemAmmount > C.RateUpLim)
+ stepChemAmmount = C.RateUpLim
addChemAmmount = deltaT * stepChemAmmount
if (addChemAmmount >= (targetVol - reactedVol))
addChemAmmount = (targetVol - reactedVol)
if (addChemAmmount < CHEMICAL_QUANTISATION_LEVEL)
addChemAmmount = CHEMICAL_QUANTISATION_LEVEL
removeChemAmmount = (addChemAmmount/cached_results[P])
+ //keep limited.
+ addChemAmmount = round(addChemAmmount, CHEMICAL_QUANTISATION_LEVEL)
+ removeChemAmmount = round(removeChemAmmount, CHEMICAL_QUANTISATION_LEVEL)
//This is kept for future bugtesters.
//message_admins("Reaction vars: PreReacted: [reactedVol] of [targetVol]. deltaT [deltaT], multiplier [multiplier], Step [stepChemAmmount], uncapped Step [deltaT*(multiplier*cached_results[P])], addChemAmmount [addChemAmmount], removeFactor [removeChemAmmount] Pfactor [cached_results[P]], adding [addChemAmmount]")
@@ -672,7 +674,7 @@
for(var/P in cached_results)
SSblackbox.record_feedback("tally", "chemical_reaction", addChemAmmount, P)//log
SSblackbox.record_feedback("tally", "fermi_chem", addChemAmmount, P)
- add_reagent(P, (addChemAmmount), null, cached_temp, purity)//add reagent function!! I THINK I can do this:
+ add_reagent(P, (addChemAmmount), null, cached_temp, purity)
TotalStep += addChemAmmount//for multiple products
//Above should reduce yeild based on holder purity.
//Purity Check
@@ -681,9 +683,9 @@
if (R.purity < C.PurityMin)//If purity is below the min, blow it up.
fermiIsReacting = FALSE
SSblackbox.record_feedback("tally", "fermi_chem", 1, ("[P] explosion"))
- C.FermiExplode(src, my_atom, (reactedVol+targetVol), cached_temp, pH)
+ C.FermiExplode(src, my_atom, (total_volume), cached_temp, pH)
STOP_PROCESSING(SSprocessing, src)
- return 0
+ return
C.FermiCreate(src, addChemAmmount, purity)//proc that calls when step is done
@@ -698,11 +700,11 @@
//go to explode proc
fermiIsReacting = FALSE
SSblackbox.record_feedback("tally", "fermi_chem", 1, ("[C] explosions"))
- C.FermiExplode(src, my_atom, (reactedVol+targetVol), chem_temp, pH)
+ C.FermiExplode(src, my_atom, (total_volume), chem_temp, pH)
STOP_PROCESSING(SSprocessing, src)
return
- //Make sure things are limited.
+ //Make sure things are limited, but superacids/bases can push forward the reaction
pH = CLAMP(pH, 0, 14)
//return said amount to compare for next step.
@@ -717,6 +719,8 @@
if (R in cached_reagents)
cachedPurity += R.purity
i++
+ if(!i)//I've never seen it get here with 0, but in case
+ CRASH("No reactants found mid reaction for [fermiReactID]/[C], how it got here is beyond me. Beaker: [holder]")
return cachedPurity/i
/datum/reagents/proc/uncache_purity(id)
@@ -764,8 +768,8 @@
del_reagent(R.type)
else
total_volume += R.volume
- if(!reagent_list)
- pH = 7
+ if(!reagent_list || !total_volume)
+ pH = REAGENT_NORMAL_PH
return 0
/datum/reagents/proc/clear_reagents()
@@ -830,7 +834,7 @@
if(!isnum(amount) || !amount)
return FALSE
- if(amount <= CHEMICAL_QUANTISATION_LEVEL)//To prevent small ammount problems.
+ if(amount < CHEMICAL_QUANTISATION_LEVEL)//To prevent small ammount problems.
return FALSE
var/datum/reagent/D = GLOB.chemical_reagents_list[reagent]
@@ -864,7 +868,6 @@
var/new_total = cached_total + amount
var/cached_temp = chem_temp
var/list/cached_reagents = reagent_list
-
var/cached_pH = pH
@@ -881,7 +884,7 @@
chem_temp = thermal_energy / (specific_heat * new_total)
//cacluate reagent based pH shift.
- if(ignore_pH == TRUE)
+ if(ignore_pH)
pH = ((cached_pH * cached_total)+(other_pH * amount))/(cached_total + amount)//should be right
else
pH = ((cached_pH * cached_total)+(D.pH * amount))/(cached_total + amount)//should be right
@@ -958,9 +961,9 @@
var/datum/reagent/R = A
if (R.type == reagent)
if((total_volume - amount) <= 0)//Because this can result in 0, I don't want it to crash.
- pH = 7
+ pH = REAGENT_NORMAL_PH
//In practice this is really confusing and players feel like it randomly melts their beakers, but I'm not sure how else to handle it. We'll see how it goes and I can remove this if it confuses people.
- else if (ignore_pH == FALSE)
+ else if (!ignore_pH)
//if (((pH > R.pH) && (pH <= 7)) || ((pH < R.pH) && (pH >= 7)))
pH = (((pH - R.pH) / total_volume) * amount) + pH
if(istype(my_atom, /obj/item/reagent_containers/))
@@ -1109,7 +1112,7 @@
if(percent < minimum_percent)
continue
var/intensity_desc = "a hint of"
- if(percent > minimum_percent * 2 || percent == 100)
+ if(ISINRANGE(percent, minimum_percent * 2, minimum_percent * 3)|| percent == 100)
intensity_desc = ""
else if(percent > minimum_percent * 3)
intensity_desc = "the strong flavor of"
From 622a6232b0d6c2b4c69c15104a9d03ad649191da Mon Sep 17 00:00:00 2001
From: Archie
Date: Sun, 3 Jan 2021 07:15:51 -0300
Subject: [PATCH 12/13] Chem master analyze fix, synthtissue update
---
code/modules/reagents/chemistry/holder.dm | 3 +-
.../chemistry/machinery/chem_master.dm | 19 +++--
code/modules/reagents/chemistry/reagents.dm | 1 -
.../reagents/chemistry/recipes/medicine.dm | 5 +-
.../reagents/chemistry/reagents/healing.dm | 70 ++++++++++++++++---
5 files changed, 76 insertions(+), 22 deletions(-)
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index bf3c4220..fa99489c 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -237,7 +237,7 @@
src.handle_reactions()
return amount
-/datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N
+/datum/reagents/proc/trans_id_to(obj/target, reagent, amount = 1, preserve_data = TRUE)//Not sure why this proc didn't exist before. It does now! /N
var/list/cached_reagents = reagent_list
if (!target)
return
@@ -257,7 +257,6 @@
if(preserve_data)
trans_data = current_reagent.data
R.add_reagent(current_reagent.type, amount, trans_data, chem_temp, current_reagent.purity, pH, no_react = TRUE)
-
remove_reagent(current_reagent.type, amount, 1)
break
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index e6b19417..90e540f0 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -148,8 +148,8 @@
/obj/machinery/chem_master/on_deconstruction()
var/atom/A = drop_location()
- beaker.forceMove(A)
- bottle.forceMove(A)
+ beaker?.forceMove(A)
+ bottle?.forceMove(A)
return ..()
/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
@@ -216,15 +216,18 @@
if(beaker)
var/reagent = GLOB.name2reagent[params["id"]]
var/amount = text2num(params["amount"])
+ message_admins("[amount] + [reagent]")
if (amount > 0)
end_fermi_reaction()
beaker.reagents.trans_id_to(src, reagent, amount)
+ message_admins("Passed Transfer Phase")
. = TRUE
else if (amount == -1) // -1 means custom amount
useramount = input("Enter the Amount you want to transfer:", name, useramount) as num|null
if (useramount > 0)
end_fermi_reaction()
beaker.reagents.trans_id_to(src, reagent, useramount)
+ message_admins("Passed Transfer Phase")
. = TRUE
if("transferFromBuffer")
@@ -434,11 +437,11 @@
state = "Gas"
var/const/P = 3 //The number of seconds between life ticks
var/T = initial(R.metabolization_rate) * (60 / P)
- var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.type)
+ var/datum/chemical_reaction/Rcr = get_chemical_reaction(R)
if(Rcr && Rcr.FermiChem)
fermianalyze = TRUE
var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
- var/datum/reagent/targetReagent = beaker.reagents.has_reagent(R.type)
+ var/datum/reagent/targetReagent = beaker.reagents.has_reagent(R)
if(!targetReagent)
CRASH("Tried to find a reagent that doesn't exist in the chem_master!")
@@ -463,9 +466,9 @@
var/T = initial(R.metabolization_rate) * (60 / P)
if(istype(R, /datum/reagent/fermi))
fermianalyze = TRUE
- var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.type)
+ var/datum/chemical_reaction/Rcr = get_chemical_reaction(R)
var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
- var/datum/reagent/targetReagent = reagents.has_reagent(R.type)
+ var/datum/reagent/targetReagent = reagents.has_reagent(R)
if(!targetReagent)
CRASH("Tried to find a reagent that doesn't exist in the chem_master!")
@@ -483,7 +486,9 @@
/obj/machinery/chem_master/proc/end_fermi_reaction()//Ends any reactions upon moving.
- if(beaker.reagents.fermiIsReacting)
+ message_admins("fermi reaction end called")
+ if(beaker && beaker.reagents.fermiIsReacting)
+ message_admins("fermi is reacting, calling end")
beaker.reagents.fermiEnd()
/obj/machinery/chem_master/proc/isgoodnumber(num)
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 5eb2c135..4aabd82d 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -1,5 +1,4 @@
#define REM REAGENTS_EFFECT_MULTIPLIER
-
GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
/proc/build_name2reagent()
diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm
index 2571dfaf..37b42570 100644
--- a/code/modules/reagents/chemistry/recipes/medicine.dm
+++ b/code/modules/reagents/chemistry/recipes/medicine.dm
@@ -86,7 +86,7 @@
CurveSharppH = 2.5 // How sharp the pH exponential curve is (to the power of value)
ThermicConstant = 0.01 // Temperature change per 1u produced
HIonRelease = 0.015 // pH change per 1u reaction (inverse for some reason)
- RateUpLim = 0.05 // Optimal/max rate possible if all conditions are perfect
+ RateUpLim = 0.1 // Optimal/max rate possible if all conditions are perfect
FermiChem = TRUE // If the chemical uses the Fermichem reaction mechanics
PurityMin = 0
@@ -101,7 +101,8 @@
if(St.purity < 1)
St.volume *= St.purity
St.purity = 1
- N.volume -= 0.002
+ var/amount = CLAMP(0.002, 0, N.volume)
+ N.volume -= amount
St.data["grown_volume"] = St.data["grown_volume"] + added_volume
St.name = "[initial(St.name)] [round(St.data["grown_volume"], 0.1)]u colony"
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
index be765cfc..7d5f71aa 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
@@ -95,22 +95,41 @@
name = "Synthtissue"
description = "Synthetic tissue used for grafting onto damaged organs during surgery, or for treating limb damage. Has a very tight growth window between 305-320, any higher and the temperature will cause the cells to die. Additionally, growth time is considerably long, so chemists are encouraged to leave beakers with said reaction ongoing, while they tend to their other duties."
pH = 7.6
- metabolization_rate = 0.05 // gives time to craft by lowing it from .1
+ metabolization_rate = 0.05 //Give them time to graft
data = list("grown_volume" = 0, "injected_vol" = 0)
+ var/borrowed_health
+ color = "#FFDADA"
/datum/reagent/synthtissue/reaction_mob(mob/living/M, method=TOUCH, reac_volume,show_message = 1)
if(iscarbon(M))
- var/target = M.zone_selected
- if (M.stat == DEAD)
- show_message = 0
+ var/mob/living/carbon/C = M
+ var/healing_factor = (((data["grown_volume"] / 100) + 1)*reac_volume)
if(method in list(PATCH, TOUCH))
- M.apply_damage(reac_volume*-1.5, BRUTE, target)
- M.apply_damage(reac_volume*-1.5, BURN, target)
- if(show_message)
- to_chat(M, "You feel your [target] heal! It stings like hell!")
+ if (M.stat == DEAD)
+ M.visible_message("The synthetic tissue rapidly grafts into [M]'s wounds, attemping to repair the damage as quickly as possible.")
+ borrowed_health += healing_factor
+ M.adjustBruteLoss(-healing_factor*2)
+ M.adjustFireLoss(-healing_factor*2)
+ M.adjustToxLoss(-healing_factor)
+ M.adjustCloneLoss(-healing_factor)
+ M.updatehealth()
+ if(data["grown_volume"] > 135 && ((C.health + C.oxyloss)>=80))
+ if(M.revive())
+ M.emote("gasp")
+ borrowed_health *= 2
+ if(borrowed_health < 100)
+ borrowed_health = 100
+ log_combat(M, M, "revived", src)
+ else
+ M.adjustBruteLoss(-healing_factor)
+ M.adjustFireLoss(-healing_factor)
+ to_chat(M, "You feel your flesh merge with the synthetic tissue! It stings like hell!")
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "painful_medicine", /datum/mood_event/painful_medicine)
- if(method==INJECT)
- data["injected_vol"] = reac_volume
+ if(method==INJECT)
+ data["injected_vol"] = reac_volume
+ var/obj/item/organ/heart/H = C.getorganslot(ORGAN_SLOT_HEART)
+ if(data["grown_volume"] > 50 && H.organ_flags & ORGAN_FAILING)
+ H.applyOrganDamage(-20)
..()
/datum/reagent/synthtissue/on_mob_life(mob/living/carbon/C)
@@ -124,6 +143,10 @@
to_chat(C, "You feel something reform inside of you!")
data["injected_vol"] -= metabolization_rate
+ if(borrowed_health)
+ C.adjustToxLoss(1)
+ C.adjustCloneLoss(1)
+ borrowed_health -= 1
..()
/datum/reagent/synthtissue/on_merge(passed_data)
@@ -134,6 +157,7 @@
if(iscarbon(holder.my_atom))
data["injected_vol"] = data["injected_vol"] + passed_data["injected_vol"]
passed_data["injected_vol"] = 0
+ update_name()
..()
/datum/reagent/synthtissue/on_new(passed_data)
@@ -141,6 +165,32 @@
return ..()
if(passed_data["grown_volume"] > data["grown_volume"])
data["grown_volume"] = passed_data["grown_volume"]
+ update_name()
..()
+/datum/reagent/synthtissue/proc/update_name() //They are but babes on creation and have to grow unto godhood
+ switch(data["grown_volume"])
+ if(-INFINITY to 50)
+ name = "Induced Synthtissue Colony"
+ if(50 to 80)
+ name = "Oligopotent Synthtissue Colony"
+ if(80 to 135)
+ name = "Pluripotent Synthtissue Colony"
+ if(135 to 175)
+ name = "SuperSomatic Synthtissue Colony"
+ if(175 to INFINITY)
+ name = "Omnipotent Synthtissue Colony"
+
+/datum/reagent/synthtissue/on_mob_delete(mob/living/M)
+ if(!iscarbon(M))
+ return
+ var/mob/living/carbon/C = M
+ C.adjustBruteLoss(borrowed_health*1.25)
+ C.adjustToxLoss(borrowed_health*1.25)
+ C.adjustCloneLoss(borrowed_health*1.25)
+ C.adjustAllOrganLoss(borrowed_health*0.25)
+ M.updatehealth()
+ if(borrowed_health && C.health < -20)
+ M.stat = DEAD
+ M.visible_message("The synthetic tissue degrades off [M]'s wounds as they collapse to the floor.")
//NEEDS ON_MOB_DEAD()
From 97fc5821d7142dff86add1d1bd82a4e9ccc7cbc8 Mon Sep 17 00:00:00 2001
From: Archie
Date: Sun, 3 Jan 2021 16:54:22 -0300
Subject: [PATCH 13/13] Synthtissue fix (Thanks to Fermi! <3)
---
code/modules/reagents/chemistry/holder.dm | 1 +
.../chemistry/machinery/chem_master.dm | 19 ++++++++-----------
.../reagents/chemistry/recipes/medicine.dm | 4 +++-
.../chemistry/reagents/fermi_reagents.dm | 3 +++
4 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index fa99489c..58be35a1 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -593,6 +593,7 @@
handle_reactions()
update_total()
//Reaction sounds and words
+ playsound(get_turf(my_atom), C.mix_sound, 80, 1)
var/list/seen = viewers(5, get_turf(my_atom))
var/iconhtml = icon2html(my_atom, seen)
for(var/mob/M in seen)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index 90e540f0..29ccf8b7 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -186,13 +186,15 @@
var/beakerContents[0]
if(beaker)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
- beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list...
+ beakerContents.Add(list(list("name" = R.name, "id" = R.type, "volume" = R.volume))) // list in a list because Byond merges the first list...
+ //"id" was changed to R.Type as id is now obsolete. This will allow special reagents such as synthtissue to be extracted in a chem master.
data["beakerContents"] = beakerContents
var/bufferContents[0]
if(reagents.total_volume)
for(var/datum/reagent/N in reagents.reagent_list)
- bufferContents.Add(list(list("name" = N.name, "id" = ckey(N.name), "volume" = N.volume))) // ^
+ bufferContents.Add(list(list("name" = N.name, "id" = N.type, "volume" = N.volume))) // ^
+ // ^
data["bufferContents"] = bufferContents
//Calculated at init time as it never changes
@@ -214,24 +216,21 @@
if("transferToBuffer")
if(beaker)
- var/reagent = GLOB.name2reagent[params["id"]]
+ var/reagent = text2path(params["id"]) //This change was necessary due to some byond fuckery with reagents being translated back to their paths. Please do not modify this under any circumstance, otherwise you will break synthtissue.
var/amount = text2num(params["amount"])
- message_admins("[amount] + [reagent]")
if (amount > 0)
end_fermi_reaction()
beaker.reagents.trans_id_to(src, reagent, amount)
- message_admins("Passed Transfer Phase")
. = TRUE
else if (amount == -1) // -1 means custom amount
useramount = input("Enter the Amount you want to transfer:", name, useramount) as num|null
if (useramount > 0)
end_fermi_reaction()
beaker.reagents.trans_id_to(src, reagent, useramount)
- message_admins("Passed Transfer Phase")
. = TRUE
if("transferFromBuffer")
- var/reagent = GLOB.name2reagent[params["id"]]
+ var/reagent = text2path(params["id"]) //This change was necessary due to some byond fuckery with reagents being translated back to their paths. Please do not modify this under any circumstance, otherwise you will break synthtissue.
var/amount = text2num(params["amount"])
if (amount > 0)
if(mode)
@@ -426,7 +425,7 @@
//END CITADEL ADDITIONS
if("analyzeBeak")
- var/datum/reagent/R = GLOB.name2reagent[params["id"]]
+ var/datum/reagent/R = text2path(params["id"])
if(R)
var/state = "Unknown"
if(initial(R.reagent_state) == 1)
@@ -453,7 +452,7 @@
return
if("analyzeBuff")
- var/datum/reagent/R = GLOB.name2reagent[params["id"]]
+ var/datum/reagent/R = text2path(params["id"])
if(R)
var/state = "Unknown"
if(initial(R.reagent_state) == 1)
@@ -486,9 +485,7 @@
/obj/machinery/chem_master/proc/end_fermi_reaction()//Ends any reactions upon moving.
- message_admins("fermi reaction end called")
if(beaker && beaker.reagents.fermiIsReacting)
- message_admins("fermi is reacting, calling end")
beaker.reagents.fermiEnd()
/obj/machinery/chem_master/proc/isgoodnumber(num)
diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm
index 37b42570..6d6deb71 100644
--- a/code/modules/reagents/chemistry/recipes/medicine.dm
+++ b/code/modules/reagents/chemistry/recipes/medicine.dm
@@ -101,7 +101,9 @@
if(St.purity < 1)
St.volume *= St.purity
St.purity = 1
- var/amount = CLAMP(0.002, 0, N.volume)
+ if(!N)
+ return //fuck you, runtimeman
+ var/amount = clamp(0.002, 0, N.volume)
N.volume -= amount
St.data["grown_volume"] = St.data["grown_volume"] + added_volume
St.name = "[initial(St.name)] [round(St.data["grown_volume"], 0.1)]u colony"
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index 6de99b52..b5324515 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -307,12 +307,15 @@ datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume)
playsound(get_turf(M), 'modular_citadel/sound/voice/merowr.ogg', 50, 1)
holder.clear_reagents()
+//Hyperstation Thalpy Edit (Please do not change this)v
/datum/reagent/fermi/acidic_buffer
name = "Acidic buffer"
description = "This reagent will consume itself and move the pH of a beaker towards acidity when added to another."
color = "#fbc314"
pH = 0
+ chemical_flags = REAGENT_FORCEONNEW
can_synth = TRUE
+//Hyperstation Thalpy Edit (Please do not change this)^
//Consumes self on addition and shifts pH
/datum/reagent/fermi/acidic_buffer/on_new(datapH)