diff --git a/_maps/map_files/debug/runtimestation.dmm b/_maps/map_files/debug/runtimestation.dmm index 0148dd562c1..01019e27ead 100644 --- a/_maps/map_files/debug/runtimestation.dmm +++ b/_maps/map_files/debug/runtimestation.dmm @@ -535,12 +535,12 @@ /area/medical/chemistry) "bR" = ( /obj/machinery/camera/autoname, -/obj/machinery/chem_heater/withbuffer, /obj/machinery/power/apc{ dir = 1; pixel_y = 23 }, /obj/structure/cable, +/obj/machinery/chem_heater/debug, /turf/open/floor/iron/dark, /area/medical/chemistry) "bS" = ( @@ -2615,6 +2615,10 @@ /obj/machinery/nanite_chamber, /turf/open/floor/iron/dark, /area/science/nanite) +"Nc" = ( +/obj/machinery/chem_recipe_debug, +/turf/open/floor/iron, +/area/medical/chemistry) "Ns" = ( /obj/machinery/door/airlock/public/glass, /obj/structure/cable, @@ -7449,7 +7453,7 @@ aj hD cj ct -Ce +Nc cF bE bE diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index cf45ea48dca..21271fc7f58 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -135,6 +135,14 @@ #define SCREWYHUD_DEAD 2 #define SCREWYHUD_HEALTHY 3 +//Health doll screws for human mobs +#define SCREWYDOLL_HEAD /obj/item/bodypart/head +#define SCREWYDOLL_CHEST /obj/item/bodypart/chest +#define SCREWYDOLL_L_ARM /obj/item/bodypart/l_arm +#define SCREWYDOLL_R_ARM /obj/item/bodypart/r_arm +#define SCREWYDOLL_L_LEG /obj/item/bodypart/l_leg +#define SCREWYDOLL_R_LEG /obj/item/bodypart/r_leg + //Threshold levels for beauty for humans #define BEAUTY_LEVEL_HORRID -66 #define BEAUTY_LEVEL_BAD -33 diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index 1b0469b26e0..a5ecad2ef1d 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -55,6 +55,9 @@ ///Default pH for reagents datum #define CHEMICAL_NORMAL_PH 7.000 +///The default purity of all non reacted reagents +#define REAGENT_STANDARD_PUIRTY 0.75 + //reagent bitflags, used for altering how they works ///allows on_mob_dead() if present in a dead body #define REAGENT_DEAD_PROCESS (1<<0) @@ -82,6 +85,10 @@ #define REACTION_HEAT_ARBITARY (1<<4) ///Used to bypass the chem_master transfer block (This is needed for competitive reactions unless you have an end state programmed). More stuff might be added later. When defining this, please add in the comments the associated reactions that it competes with #define REACTION_COMPETITIVE (1<<5) +///Used to force pH changes to be constant regardless of volume +#define REACTION_PH_VOL_CONSTANT (1<<6) +///If a reaction will generate it's impure/inverse reagents in the middle of a reaction, as apposed to being determined on ingestion/on reaction completion +#define REACTION_REAL_TIME_SPLIT (1<<7) ///Used for overheat_temp - This sets the overheat so high it effectively has no overheat temperature. #define NO_OVERHEAT 99999 @@ -144,3 +151,8 @@ #define REACTION_TAG_PLANT (1<<20) /// This reaction is produces a product that affects plants #define REACTION_TAG_COMPETITIVE (1<<21) + +/// Below are defines used for reagent associated machines only +/// For the pH meter flashing method +#define ENABLE_FLASHING -1 +#define DISABLE_FLASHING 14 diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 73874b4a276..d170bc2aa12 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -309,4 +309,8 @@ DEFINE_BITFIELD(reaction_flags, list( "REACTION_CLEAR_INVERSE" = REACTION_CLEAR_INVERSE, "REACTION_CLEAR_RETAIN" = REACTION_CLEAR_RETAIN, "REACTION_INSTANT" = REACTION_INSTANT, + "REACTION_HEAT_ARBITARY" = REACTION_HEAT_ARBITARY, + "REACTION_COMPETITIVE" = REACTION_COMPETITIVE, + "REACTION_PH_VOL_CONSTANT" = REACTION_PH_VOL_CONSTANT, + "REACTION_REAL_TIME_SPLIT" = REACTION_REAL_TIME_SPLIT, )) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index d16673f008a..cce1743a2f9 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -21,6 +21,7 @@ GLOBAL_LIST(chemical_reactions_list) //list of all /datum/chemical_reaction datu GLOBAL_LIST(chemical_reactions_list_product_index) //list of all /datum/chemical_reaction datums. Used for the reaction lookup UI. Indexed by PRODUCT type GLOBAL_LIST(chemical_reagents_list) //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff GLOBAL_LIST(chemical_reactions_results_lookup_list) //List of all reactions with their associated product and result ids. Used for reaction lookups +GLOBAL_LIST(fake_reagent_blacklist) //List of all reagents that are parent types used to define a bunch of children - but aren't used themselves as anything. GLOBAL_LIST_EMPTY(tech_list) //list of all /datum/tech datums indexed by id. GLOBAL_LIST_EMPTY(surgeries_list) //list of all surgeries by name, associated with their path. GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes diff --git a/code/controllers/subsystem/processing/reagents.dm b/code/controllers/subsystem/processing/reagents.dm index 1a38ae4219a..1a129ace80c 100644 --- a/code/controllers/subsystem/processing/reagents.dm +++ b/code/controllers/subsystem/processing/reagents.dm @@ -14,10 +14,12 @@ PROCESSING_SUBSYSTEM_DEF(reagents) . = ..() //So our first step isn't insane previous_world_time = world.time + ///Blacklists these reagents from being added to the master list. the exact type only. Children are not blacklisted. + GLOB.fake_reagent_blacklist = list(/datum/reagent/medicine/c2, /datum/reagent/medicine, /datum/reagent/reaction_agent) //Build GLOB lists - see holder.dm build_chemical_reagent_list() build_chemical_reactions_lists() - return + return /datum/controller/subsystem/processing/reagents/fire(resumed = FALSE) if (!resumed) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 8158f5ccab6..140bb4e0c74 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -170,6 +170,8 @@ GENE SCANNER var/mob/living/carbon/human/H = M if(H.undergoing_cardiac_arrest() && H.stat != DEAD) render_list += "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!\n" + if(H.has_reagent(/datum/reagent/inverse/technetium)) + advanced = TRUE render_list += "Analyzing results for [M]:\nOverall status: [mob_status]\n" @@ -324,9 +326,16 @@ GENE SCANNER for(var/obj/item/organ/organ in H.internal_organs) var/status = "" - if (organ.organ_flags & ORGAN_FAILING) status = "Non-Functional" - else if (organ.damage > organ.high_threshold) status = "Severely Damaged" - else if (organ.damage > organ.low_threshold) status = "Mildly Damaged" + if(H.has_reagent(/datum/reagent/inverse/technetium)) + if(organ.damage) + status = " organ is [round((organ.damage/organ.maxHealth)*100, 1)]% damaged." + else + if (organ.organ_flags & ORGAN_FAILING) + status = "Non-Functional" + else if (organ.damage > organ.high_threshold) + status = "Severely Damaged" + else if (organ.damage > organ.low_threshold) + status = "Mildly Damaged" if (status != "") render = TRUE toReport += "[organ.name]:\ diff --git a/code/modules/actionspeed/modifiers/status_effects.dm b/code/modules/actionspeed/modifiers/status_effects.dm index 615346b7902..65153e08933 100644 --- a/code/modules/actionspeed/modifiers/status_effects.dm +++ b/code/modules/actionspeed/modifiers/status_effects.dm @@ -3,3 +3,6 @@ /datum/actionspeed_modifier/blunt_wound variable = TRUE + +/datum/actionspeed_modifier/nooartrium + multiplicative_slowdown = 0.5 diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 8014a9df27f..d3f64d0eb1d 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( /datum/hallucination/battle = 20, /datum/hallucination/dangerflash = 15, /datum/hallucination/hudscrew = 12, + /datum/hallucination/fake_health_doll = 12, /datum/hallucination/fake_alert = 12, /datum/hallucination/weird_sounds = 8, /datum/hallucination/stationmessage = 7, @@ -1152,6 +1153,62 @@ GLOBAL_LIST_INIT(hallucination_list, list( target.clear_alert(alert_type, clear_override = TRUE) qdel(src) +///Causes the target to see incorrect health damages on the healthdoll +/datum/hallucination/fake_health_doll + var/timer_id = null + +///Creates a specified doll hallucination, or picks one randomly +/datum/hallucination/fake_health_doll/New(mob/living/carbon/human/human_mob, forced = TRUE, specific_limb, severity, duration = 500) + . = ..() + if(!specific_limb) + specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) + if(!severity) + severity = rand(1, 5) + LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) + human_mob.update_health_hud() + + timer_id = addtimer(CALLBACK(src, .proc/cleanup), duration, TIMER_STOPPABLE) + +///Increments the severity of the damage seen on the doll +/datum/hallucination/fake_health_doll/proc/increment_fake_damage() + if(!ishuman(target)) + stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") + var/mob/living/carbon/human/human_mob = target + for(var/entry in human_mob.hal_screwydoll) + human_mob.hal_screwydoll[entry] = clamp(human_mob.hal_screwydoll[entry]+1, 1, 5) + human_mob.update_health_hud() + +///Adds a fake limb to the hallucination datum effect +/datum/hallucination/fake_health_doll/proc/add_fake_limb(specific_limb, severity) + if(!specific_limb) + specific_limb = pick(list(SCREWYDOLL_HEAD, SCREWYDOLL_CHEST, SCREWYDOLL_L_ARM, SCREWYDOLL_R_ARM, SCREWYDOLL_L_LEG, SCREWYDOLL_R_LEG)) + if(!severity) + severity = rand(1, 5) + var/mob/living/carbon/human/human_mob = target + LAZYSET(human_mob.hal_screwydoll, specific_limb, severity) + target.update_health_hud() + +/datum/hallucination/fake_health_doll/target_deleting() + if(isnull(timer_id)) + return + deltimer(timer_id) + timer_id = null + ..() + +///Cleans up the hallucinations - this deletes any overlap, but that shouldn't happen. +/datum/hallucination/fake_health_doll/proc/cleanup() + qdel(src) + +//So that the associated addition proc cleans it up correctly +/datum/hallucination/fake_health_doll/Destroy() + if(!ishuman(target)) + stack_trace("Somehow [target] managed to get a fake health doll hallucination, while not being a human mob.") + var/mob/living/carbon/human/human_mob = target + LAZYNULL(human_mob.hal_screwydoll) + human_mob.update_health_hud() + ..() + + /datum/hallucination/items/New(mob/living/carbon/C, forced = TRUE) set waitfor = FALSE ..() diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm index 96aadcbd5da..09208fd37c8 100644 --- a/code/modules/food_and_drinks/recipes/food_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm @@ -181,6 +181,18 @@ required_reagents = list(/datum/reagent/consumable/nutriment/protein = 0.5) required_catalysts = list(/datum/reagent/medicine/metafactor = 0.5) +/datum/chemical_reaction/food/failed_nutriconversion + results = list(/datum/reagent/peptides_failed = 0.5) + required_reagents = list(/datum/reagent/consumable/nutriment/ = 0.5) + required_catalysts = list(/datum/reagent/impurity/probital_failed = 0.5) + thermic_constant = 100 // a tell + +/datum/chemical_reaction/food/failed_protein_peptide + results = list(/datum/reagent/peptides_failed = 0.5) + required_reagents = list(/datum/reagent/consumable/nutriment/protein = 0.5) + required_catalysts = list(/datum/reagent/impurity/probital_failed = 0.5) + thermic_constant = 100 // a tell + /datum/chemical_reaction/food/bbqsauce results = list(/datum/reagent/consumable/bbqsauce = 5) required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/consumable/tomatojuice = 1, /datum/reagent/medicine/salglu_solution = 3, /datum/reagent/consumable/blackpepper = 1) diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 9656d6ae420..1b26beb385e 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -49,7 +49,7 @@ /mob/living/brain/get_ear_protection()//no ears return 2 -/mob/living/brain/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0) +/mob/living/brain/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) return // no eyes, no flashing /mob/living/brain/can_be_revived() diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 4d29301159d..0766a04ca71 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -597,7 +597,7 @@ return embeds -/mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0) +/mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) var/obj/item/organ/eyes/eyes = getorganslot(ORGAN_SLOT_EYES) if(!eyes) //can't flash what can't see! return diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 0a73f9050eb..e4069efa23c 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -148,8 +148,6 @@ if(get_bodypart(BODY_ZONE_HEAD) && !getorgan(/obj/item/organ/brain)) . += "It appears that [t_his] brain is missing..." - var/temp = getBruteLoss() //no need to calculate each of these twice - var/list/msg = list() var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) @@ -202,6 +200,11 @@ msg += "[t_He] [p_do()]n't seem all there.\n" if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy + var/temp + if(user == src && src.hal_screwyhud == SCREWYHUD_CRIT)//fake damage + temp = 50 + else + temp = getBruteLoss() if(temp) if(temp < 25) msg += "[t_He] [t_has] minor bruising.\n" diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index ac2d36b73f1..39f932eede0 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -879,11 +879,16 @@ hud_used.healthdoll.cut_overlays() if(stat != DEAD) hud_used.healthdoll.icon_state = "healthdoll_OVERLAY" - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - var/damage = BP.burn_dam + BP.brute_dam - var/comparison = (BP.max_damage/5) + for(var/obj/item/bodypart/body_part as anything in bodyparts) var/icon_num = 0 + //Hallucinations + if(body_part.type in hal_screwydoll) + icon_num = hal_screwydoll[body_part.type] + hud_used.healthdoll.add_overlay(mutable_appearance('icons/hud/screen_gen.dmi', "[body_part.body_zone][icon_num]")) + continue + //Not hallucinating + var/damage = body_part.burn_dam + body_part.brute_dam + var/comparison = (body_part.max_damage/5) if(damage) icon_num = 1 if(damage > (comparison)) @@ -897,7 +902,7 @@ if(hal_screwyhud == SCREWYHUD_HEALTHY) icon_num = 0 if(icon_num) - hud_used.healthdoll.add_overlay(mutable_appearance('icons/hud/screen_gen.dmi', "[BP.body_zone][icon_num]")) + hud_used.healthdoll.add_overlay(mutable_appearance('icons/hud/screen_gen.dmi', "[body_part.body_zone][icon_num]")) for(var/t in get_missing_limbs()) //Missing limbs hud_used.healthdoll.add_overlay(mutable_appearance('icons/hud/screen_gen.dmi', "[t]6")) for(var/t in get_disabled_limbs()) //Disabled limbs diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index b44200ff4c2..53da1a41f0a 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -688,18 +688,17 @@ var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - for(var/X in bodyparts) - var/obj/item/bodypart/LB = X - missing -= LB.body_zone - if(LB.is_pseudopart) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades + for(var/obj/item/bodypart/body_part as anything in bodyparts) + missing -= body_part.body_zone + if(body_part.is_pseudopart) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades continue var/self_aware = FALSE if(HAS_TRAIT(src, TRAIT_SELF_AWARE)) self_aware = TRUE - var/limb_max_damage = LB.max_damage + var/limb_max_damage = body_part.max_damage var/status = "" - var/brutedamage = LB.brute_dam - var/burndamage = LB.burn_dam + var/brutedamage = body_part.brute_dam + var/burndamage = body_part.burn_dam if(hallucination) if(prob(30)) brutedamage += rand(30,40) @@ -712,21 +711,24 @@ status = "no damage" else + if(body_part.type in hal_screwydoll)//Are we halucinating? + brutedamage = (hal_screwydoll[body_part.type] * 0.2)*limb_max_damage + if(brutedamage > 0) - status = LB.light_brute_msg + status = body_part.light_brute_msg if(brutedamage > (limb_max_damage*0.4)) - status = LB.medium_brute_msg + status = body_part.medium_brute_msg if(brutedamage > (limb_max_damage*0.8)) - status = LB.heavy_brute_msg + status = body_part.heavy_brute_msg if(brutedamage > 0 && burndamage > 0) status += " and " if(burndamage > (limb_max_damage*0.8)) - status += LB.heavy_burn_msg + status += body_part.heavy_burn_msg else if(burndamage > (limb_max_damage*0.2)) - status += LB.medium_burn_msg + status += body_part.medium_burn_msg else if(burndamage > 0) - status += LB.light_burn_msg + status += body_part.light_burn_msg if(status == "") status = "OK" @@ -734,45 +736,45 @@ if(status == "OK" || status == "no damage") no_damage = TRUE var/isdisabled = "" - if(LB.bodypart_disabled) + if(body_part.bodypart_disabled) isdisabled = " is disabled" if(no_damage) isdisabled += " but otherwise" else isdisabled += " and" - combined_msg += "\t Your [LB.name][isdisabled][self_aware ? " has " : " is "][status]." + combined_msg += "\t Your [body_part.name][isdisabled][self_aware ? " has " : " is "][status]." - for(var/thing in LB.wounds) + for(var/thing in body_part.wounds) var/datum/wound/W = thing var/msg switch(W.severity) if(WOUND_SEVERITY_TRIVIAL) //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]." //ORIGINAL - msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]." //SKYRAT EDIT CHANGE - MEDICAL + msg = "\t Your [body_part.name] is suffering [W.a_or_from] [W.get_topic_name(src)]." //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_MODERATE) //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" //ORIGINAL - msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL + msg = "\t Your [body_part.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_SEVERE) //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" //ORIGINAL - msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL + msg = "\t Your [body_part.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_CRITICAL) //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!!" //ORIGINAL - msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!!" //SKYRAT EDIT CHANGE - MEDICAL + msg = "\t Your [body_part.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!!" //SKYRAT EDIT CHANGE - MEDICAL combined_msg += msg - for(var/obj/item/I in LB.embedded_objects) + for(var/obj/item/I in body_part.embedded_objects) if(I.isEmbedHarmless()) - combined_msg += "\t There is \a [I] stuck to your [LB.name]!" + combined_msg += "\t There is \a [I] stuck to your [body_part.name]!" else - combined_msg += "\t There is \a [I] embedded in your [LB.name]!" + combined_msg += "\t There is \a [I] embedded in your [body_part.name]!" //SKYRAT EDIT ADDITION BEGIN - MEDICAL - if(LB.current_gauze) - var/datum/bodypart_aid/current_gauze = LB.current_gauze - combined_msg += "\t Your [LB.name] is [current_gauze.desc_prefix] with [current_gauze.get_description()]." - if(LB.current_splint) - var/datum/bodypart_aid/current_splint = LB.current_splint - combined_msg += "\t Your [LB.name] is [current_splint.desc_prefix] with [current_splint.get_description()]." + if(body_part.current_gauze) + var/datum/bodypart_aid/current_gauze = body_part.current_gauze + combined_msg += "\t Your [body_part.name] is [current_gauze.desc_prefix] with [current_gauze.get_description()]." + if(body_part.current_splint) + var/datum/bodypart_aid/current_splint = body_part.current_splint + combined_msg += "\t Your [body_part.name] is [current_splint.desc_prefix] with [current_splint.get_description()]." //SKYRAT EDIT END for(var/t in missing) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 4d80893735b..28478f44da7 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -77,3 +77,6 @@ ///Exposure to damaging heat levels increases stacks, stacks clean over time when temperatures are lower. Stack is consumed to add a wound. var/heat_exposure_stacks = 0 + + ///human specific screwyhuds from hallucinations (define key (bodypart) to int value (severity)) - see /datum/hallucination/fake_health_doll + var/hal_screwydoll diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 807f916c193..af46cff49e6 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -414,12 +414,12 @@ return TRUE //called when the mob receives a bright flash -/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash) +/mob/living/proc/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) if(HAS_TRAIT(src, TRAIT_NOFLASH)) return FALSE if(get_eye_protection() < intensity && (override_blindness_check || !is_blind())) overlay_fullscreen("flash", type) - addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25) + addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", length), length) return TRUE return FALSE diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm index 2ecdd6594fb..0ce126d945a 100644 --- a/code/modules/mob/living/silicon/ai/ai_defense.dm +++ b/code/modules/mob/living/silicon/ai/ai_defense.dm @@ -57,5 +57,5 @@ . = ..(Proj) updatehealth() -/mob/living/silicon/ai/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0) +/mob/living/silicon/ai/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) return // no eyes, no flashing diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index ed2cbbf8c60..9e59959f1f6 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -123,6 +123,6 @@ Proj.on_hit(src, 0, piercing_hit) return BULLET_ACT_HIT -/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash/static) +/mob/living/silicon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash/static, length = 25) if(affect_silicon) return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index b70a21eccc9..693b5e86078 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -302,7 +302,7 @@ if(cleared) to_chat(src, "--- [class] alarm in [A.name] has been cleared.") -/mob/living/simple_animal/drone/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0) +/mob/living/simple_animal/drone/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) if(affect_silicon) return ..() diff --git a/code/modules/movespeed/modifiers/reagent.dm b/code/modules/movespeed/modifiers/reagent.dm index d91eb917485..b7108582d93 100644 --- a/code/modules/movespeed/modifiers/reagent.dm +++ b/code/modules/movespeed/modifiers/reagent.dm @@ -33,3 +33,6 @@ /datum/movespeed_modifier/reagent/nuka_cola multiplicative_slowdown = -0.35 + +/datum/movespeed_modifier/reagent/nooartrium + multiplicative_slowdown = 2 diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index 5ff2bc68eac..a1cfbd4ec1a 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -44,6 +44,8 @@ var/thermic_mod = 1 ///Allow us to deal with lag by "charging" up our reactions to react faster over a period - this means that the reaction doesn't suddenly mass react - which can cause explosions var/time_deficit + ///Used to store specific data needed for a reaction, usually used to keep track of things between explosion calls. CANNOT be used as a part of chemical_recipe - those vars are static lookup tables. + var/data = list() /* * Creates and sets up a new equlibrium object @@ -203,16 +205,16 @@ * This was moved from the start, to the end - after a reaction, so post reaction temperature changes aren't ignored. * overheated() is first - so double explosions can't happen (i.e. explosions that blow up the holder) */ -/datum/equilibrium/proc/check_fail_states() +/datum/equilibrium/proc/check_fail_states(vol_added) //Are we overheated? if(reaction.is_cold_recipe) - if(holder.chem_temp < reaction.overheat_temp) //This is before the process - this is here so that overly_impure and overheated() share the same code location (and therefore vars) for calls. + if(holder.chem_temp < reaction.overheat_temp && reaction.overheat_temp != NO_OVERHEAT) //This is before the process - this is here so that overly_impure and overheated() share the same code location (and therefore vars) for calls. SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overheated reaction steps") - reaction.overheated(holder, src) + reaction.overheated(holder, src, vol_added) else if(holder.chem_temp > reaction.overheat_temp) SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overheated reaction steps") - reaction.overheated(holder, src) + reaction.overheated(holder, src, vol_added) //is our product too impure? for(var/product in reaction.results) @@ -221,7 +223,7 @@ continue if (reagent.purity < reaction.purity_min)//If purity is below the min, call the proc SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overly impure reaction steps") - reaction.overly_impure(holder, src) + reaction.overly_impure(holder, src, vol_added) //did we explode? if(!holder.my_atom || holder.reagent_list.len == 0) @@ -331,15 +333,40 @@ for(var/reagent in reaction.required_reagents) holder.remove_reagent(reagent, (delta_chem_factor * reaction.required_reagents[reagent]), safety = TRUE) //Apply pH changes - holder.adjust_specific_reagent_ph(reagent, (delta_chem_factor * reaction.required_reagents[reagent])*(reaction.H_ion_release*h_ion_mod)) + var/pH_adjust + if(reaction.reaction_flags & REACTION_PH_VOL_CONSTANT) + pH_adjust = ((delta_chem_factor * reaction.required_reagents[reagent])/target_vol)*(reaction.H_ion_release*h_ion_mod) + else //Default adds pH independant of volume + pH_adjust = (delta_chem_factor * reaction.required_reagents[reagent])*(reaction.H_ion_release*h_ion_mod) + holder.adjust_specific_reagent_ph(reagent, pH_adjust) var/step_add for(var/product in reaction.results) //create the products step_add = delta_chem_factor * reaction.results[product] - holder.add_reagent(product, step_add, null, cached_temp, purity, override_base_ph = TRUE) + //If we make purities in real time + if(reaction.reaction_flags & REACTION_REAL_TIME_SPLIT && purity < 1) + var/datum/reagent/product_ref = GLOB.chemical_reagents_list[product] + if(purity < reaction.purity_min && product_ref.failed_chem) //If we're failed + holder.add_reagent(product_ref.failed_chem, step_add, null, cached_temp, (1-purity), override_base_ph = TRUE) + else if(purity < product_ref.inverse_chem_val && product_ref.inverse_chem) //If we're inverse + holder.add_reagent(product_ref.inverse_chem, step_add, null, cached_temp, (1-purity), override_base_ph = TRUE) + else if(product_ref.impure_chem && product_ref.impure_chem) //if we're impure + holder.add_reagent(product*purity, step_add, null, cached_temp, purity, override_base_ph = TRUE) + holder.add_reagent(product_ref.impure_chem*(1-purity), step_add, null, cached_temp, (1-purity), override_base_ph = TRUE) + else //We can get here if the flag is set, but there's no associated impure_chem assigned. In some cases this is desired (i.e. multiver only wants to real time split it's inverse chem) + holder.add_reagent(product, step_add, null, cached_temp, purity, override_base_ph = TRUE) + //Default handiling + else + holder.add_reagent(product, step_add, null, cached_temp, purity, override_base_ph = TRUE) + //Apply pH changes - holder.adjust_specific_reagent_ph(product, step_add*reaction.H_ion_release) + var/pH_adjust + if(reaction.reaction_flags & REACTION_PH_VOL_CONSTANT) + pH_adjust = (step_add/target_vol)*(reaction.H_ion_release*h_ion_mod) + else + pH_adjust = step_add*(reaction.H_ion_release*h_ion_mod) + holder.adjust_specific_reagent_ph(product, pH_adjust) reacted_vol += step_add total_step_added += step_add @@ -367,7 +394,7 @@ reaction_quality = purity //post reaction checks - if(!(check_fail_states())) + if(!(check_fail_states(total_step_added))) to_delete = TRUE //end reactions faster so plumbing is faster diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 8b7b7abf619..f70c77e3b92 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -13,6 +13,8 @@ GLOB.chemical_reagents_list = list() for(var/path in paths) + if(path in GLOB.fake_reagent_blacklist) + continue var/datum/reagent/D = new path() GLOB.chemical_reagents_list[path] = D @@ -32,9 +34,9 @@ //Randomized need to go last since they need to check against conflicts with normal recipes var/paths = subtypesof(/datum/chemical_reaction) - typesof(/datum/chemical_reaction/randomized) + subtypesof(/datum/chemical_reaction/randomized) - GLOB.chemical_reactions_list = list() - GLOB.chemical_reactions_results_lookup_list = list() - GLOB.chemical_reactions_list_product_index = list() + GLOB.chemical_reactions_list = list() //reagents to reaction list + GLOB.chemical_reactions_results_lookup_list = list() //UI glob + GLOB.chemical_reactions_list_product_index = list() //product to reaction list for(var/path in paths) var/datum/chemical_reaction/D = new path() @@ -529,7 +531,6 @@ return if(amount < 0) return - var/cached_amount = amount if(get_reagent_amount(reagent) < amount) amount = get_reagent_amount(reagent) @@ -904,7 +905,6 @@ mix_message += temp_mix_message continue SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[equilibrium.reaction.type] total reaction steps") - if(num_reactions) SEND_SIGNAL(src, COMSIG_REAGENTS_REACTION_STEP, num_reactions, delta_time) @@ -1022,7 +1022,6 @@ target.previous_reagent_list = LAZYLISTDUPLICATE(previous_reagent_list) target.is_reacting = is_reacting - ///Checks to see if the reagents has a difference in reagents_list and previous_reagent_list (I.e. if there's a difference between the previous call and the last) ///Also checks to see if the saved reactions in failed_but_capable_reactions can start as a result of temp/pH change /datum/reagents/proc/has_changed_state() @@ -1126,7 +1125,6 @@ if (R.purity == 1) return if(R.chemical_flags & REAGENT_DONOTSPLIT) - R.purity = 1 return if(R.purity < 0) stack_trace("Purity below 0 for chem: [type]!") @@ -1145,7 +1143,7 @@ if(!(R.chemical_flags & REAGENT_SPLITRETAINVOL)) remove_reagent(R.type, impureVol, FALSE) add_reagent(R.impure_chem, impureVol, FALSE, added_purity = 1-R.creation_purity) - R.purity = 1 //prevent this process from repeating (this is why creation_purity exists) + R.chemical_flags |= REAGENT_DONOTSPLIT /// Updates [/datum/reagents/var/total_volume] /datum/reagents/proc/update_total() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 5f19e20a117..7dce0cf58b3 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -1,5 +1,3 @@ -#define ENABLE_FLASHING -1 - ///Tutorial states #define TUT_NO_BUFFER 50 #define TUT_START 1 @@ -26,7 +24,6 @@ var/heater_coefficient = 0.05 var/on = FALSE var/dispense_volume = 1 - //The list of active clients using this heater, so that we can update the UI on a reaction_step. I assume there are multiple clients possible. var/list/ui_client_list ///If the user has the tutorial enabled @@ -107,7 +104,6 @@ return if(beaker?.reagents.has_reagent(/datum/reagent/mercury, 10) || beaker?.reagents.has_reagent(/datum/reagent/chlorine, 10)) tutorial_state = TUT_HAS_REAGENTS - if(TUT_HAS_REAGENTS) if(!(beaker?.reagents.has_reagent(/datum/reagent/mercury, 9)) || !(beaker?.reagents.has_reagent(/datum/reagent/chlorine, 9))) tutorial_state = TUT_MISSING @@ -175,7 +171,6 @@ var/obj/item/reagent_containers/syringe/S = I S.afterattack(beaker, user, 1) return - return ..() /obj/machinery/chem_heater/on_deconstruction() @@ -253,7 +248,7 @@ data["beakerContents"] = beaker_contents var/list/active_reactions = list() - var/flashing = 14 //for use with alertAfter - since there is no alertBefore, I set the after to 0 if true, or to the max value if false + var/flashing = DISABLE_FLASHING //for use with alertAfter - since there is no alertBefore, I set the after to 0 if true, or to the max value if false for(var/_reaction in beaker?.reagents.reaction_list) var/datum/equilibrium/equilibrium = _reaction if(!length(beaker.reagents.reaction_list))//I'm not sure why when it explodes it causes the gui to fail (it's missing danger (?) ) @@ -275,7 +270,7 @@ if(equilibrium.reaction.optimal_ph_min > beaker?.reagents.ph || equilibrium.reaction.optimal_ph_max < beaker?.reagents.ph) flashing = ENABLE_FLASHING if(equilibrium.reaction.is_cold_recipe) - if(equilibrium.reaction.overheat_temp > beaker?.reagents.chem_temp) + if(equilibrium.reaction.overheat_temp > beaker?.reagents.chem_temp && equilibrium.reaction.overheat_temp != NO_OVERHEAT) danger = TRUE overheat = TRUE else @@ -494,3 +489,12 @@ To continue set your target temperature to 390K."} . = ..() reagents.add_reagent(/datum/reagent/reaction_agent/basic_buffer, 20) reagents.add_reagent(/datum/reagent/reaction_agent/acidic_buffer, 20) + +#undef TUT_NO_BUFFER +#undef TUT_START +#undef TUT_HAS_REAGENTS +#undef TUT_IS_ACTIVE +#undef TUT_IS_REACTING +#undef TUT_FAIL +#undef TUT_COMPLETE +#undef TUT_MISSING diff --git a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm index 30bdf93ef11..d8ca6c4b6e5 100644 --- a/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm +++ b/code/modules/reagents/chemistry/machinery/chem_recipe_debug.dm @@ -24,23 +24,41 @@ var/minorImpurity ///The count of reactions that resolve below 0.9 purity var/majorImpurity - ///If we failed to react this current chem so use a lower temp + ///If we failed to react this current chem so use a lower temp - all reactions only var/failed = 0 + ///If we're forcing optimal conditions + var/should_force_temp = FALSE + var/should_force_ph = FALSE + ///Forced values + var/force_temp = 300 + var/force_ph = 7 + ///Multiplier of product + var/vol_multiplier = 20 + ///If we're reacting + var/react = FALSE + ///Number of delta times taken to react + var/react_time = 0 + ///IF we're doing EVERY reaction + var/process_all = FALSE + ///The name + var/list/reaction_names = list() + ///If it's started + var/reaction_stated = FALSE + ///If we spawn a beaker at the end of a reaction or not + var/beaker_spawn = FALSE + ///If we force min temp on reaction setup + var/min_temp = FALSE ///Create reagents datum /obj/machinery/chem_recipe_debug/Initialize() . = ..() create_reagents(9000)//I want to make sure everything fits + end_processing() ///Enable the machine /obj/machinery/chem_recipe_debug/attackby(obj/item/I, mob/user, params) . = .() - if(processing) - say("currently processing reaction [index]: [cached_reactions[index]] of [cached_reactions.len]") - return - say("Starting processing") - setup_reactions() - begin_processing() + ui_interact(usr) ///Enable the machine /obj/machinery/chem_recipe_debug/AltClick(mob/living/user) @@ -48,6 +66,7 @@ if(processing) say("currently processing reaction [index]: [cached_reactions[index]] of [cached_reactions.len]") return + process_all = TRUE say("Starting processing") setup_reactions() begin_processing() @@ -55,10 +74,13 @@ ///Resets the index, and creates the cached_reaction list from all possible reactions /obj/machinery/chem_recipe_debug/proc/setup_reactions() cached_reactions = list() - for(var/V in GLOB.chemical_reactions_list) - if(is_type_in_list(GLOB.chemical_reactions_list[V], cached_reactions)) - continue - cached_reactions += GLOB.chemical_reactions_list[V] + if(process_all) + for(var/reaction in GLOB.chemical_reactions_list) + if(is_type_in_list(GLOB.chemical_reactions_list[reaction], cached_reactions)) + continue + cached_reactions += GLOB.chemical_reactions_list[reaction] + else + cached_reactions = reaction_names reagents.clear_reagents() index = 1 processing = TRUE @@ -70,60 +92,261 @@ /obj/machinery/chem_recipe_debug/process(delta_time) if(processing == FALSE) setup_reactions() + if(should_force_ph) + reagents.ph = force_ph + if(should_force_temp) + reagents.chem_temp = force_temp if(reagents.is_reacting == TRUE) + react_time += delta_time return - if(index >= cached_reactions.len) - say("Completed testing, missing reactions products (may have exploded) are:") - say("[problem_string]") - say("Problem with results are:") - say("[impure_string]") - say("Reactions with minor impurity: [minorImpurity], reactions with major impurity: [majorImpurity]") - processing = FALSE - end_processing() - if(reagents.reagent_list) - say("Reaction completed for [cached_reactions[index]] final temperature = [reagents.chem_temp], ph = [reagents.ph].") - var/datum/chemical_reaction/C = cached_reactions[index] - for(var/R in C.results) - var/datum/reagent/R2 = reagents.get_reagent(R) - if(!R2) - say("Unable to find product [R] in holder after reaction! reagents found are:") - for(var/R3 in reagents.reagent_list) - say("[R3]") - var/obj/item/reagent_containers/glass/beaker/bluespace/B = new /obj/item/reagent_containers/glass/beaker/bluespace(loc) - reagents.trans_to(B) - B.name = "[cached_reactions[index]]" - if(failed > 0) - problem_string += "[cached_reactions[index]] Unable to find product [R] in holder after reaction! index:[index]\n" - failed++ - continue - say("Reaction has a product [R] [R2.volume]u purity of [R2.purity]") - if(R2.purity < 0.9) - impure_string += "Reaction [cached_reactions[index]] has a product [R] [R2.volume]u purity of [R2.purity] index:[index]\n" - majorImpurity++ - else if (R2.purity < 1) - impure_string += "Reaction [cached_reactions[index]] has a product [R] [R2.volume]u purity of [R2.purity] index:[index]\n" - minorImpurity++ - if(R2.volume < C.results[R]) - impure_string += "Reaction [cached_reactions[index]] has a product [R] [R2.volume]u purity of [R2.purity] index:[index]\n" - reagents.clear_reagents() - if(failed > 1) - index++ - failed = 0 - if(failed == 0) - index++ - var/datum/chemical_reaction/C = cached_reactions[index] - if(!C) - say("Unable to find reaction on index: [index]") - for(var/R in C.required_reagents) - reagents.add_reagent(R, C.required_reagents[R]*20) - for(var/cat in C.required_catalysts) - reagents.add_reagent(cat, C.required_catalysts[cat]) - if(failed == 0) - reagents.chem_temp = C.optimal_temp - if(failed == 1) - reagents.chem_temp = C.required_temp+25 - failed++ - say("Reacting [cached_reactions[index]] starting pH: [reagents.ph] index [index] of [cached_reactions.len]") - if(C.reaction_flags & REACTION_INSTANT) - say("This reaction is instant") + if(reaction_stated == TRUE) + reaction_stated = FALSE + relay_ended_reaction() + if(index > cached_reactions.len) + relay_all_reactions() + return + setup_reaction() + reaction_stated = TRUE +/obj/machinery/chem_recipe_debug/proc/relay_all_reactions() + say("Completed testing, missing reactions products (may have exploded) are:") + say("[problem_string]") + say("Problem with results are:") + say("[impure_string]") + say("Reactions with minor impurity: [minorImpurity], reactions with major impurity: [majorImpurity]") + processing = FALSE + problem_string = null + impure_string = null + minorImpurity = null + majorImpurity = null + end_processing() + +/obj/machinery/chem_recipe_debug/proc/relay_ended_reaction() + if(reagents.reagent_list) + var/cached_purity + say("Reaction completed for [cached_reactions[index]] final temperature = [reagents.chem_temp], ph = [reagents.ph], time taken = [react_time]s.") + var/datum/chemical_reaction/reaction = cached_reactions[index] + for(var/reagent_type in reaction.results) + var/datum/reagent/reagent = reagents.get_reagent(reagent_type) + if(!reagent) + say("Unable to find product [reagent_type] in holder after reaction! reagents found are:") + for(var/other_reagent in reagents.reagent_list) + say("[other_reagent]") + var/obj/item/reagent_containers/glass/beaker/bluespace/beaker = new /obj/item/reagent_containers/glass/beaker/bluespace(loc) + reagents.trans_to(beaker) + beaker.name = "[cached_reactions[index]] failed" + if(!failed) + problem_string += "[cached_reactions[index]] Unable to find product [reagent_type] in holder after reaction! Trying alternative setup. index:[index]\n" + failed++ + return + say("Reaction has a product [reagent_type] [reagent.volume]u purity of [reagent.purity]") + if(reagent.purity < 0.9) + impure_string += "Reaction [cached_reactions[index]] has a product [reagent_type] [reagent.volume]u purity of [reagent.purity] index:[index]\n" + majorImpurity++ + else if (reagent.purity < 1) + impure_string += "Reaction [cached_reactions[index]] has a product [reagent_type] [reagent.volume]u purity of [reagent.purity] index:[index]\n" + minorImpurity++ + if(reagent.volume < reaction.results[reagent_type]) + impure_string += "Reaction [cached_reactions[index]] has a product [reagent_type] [reagent.volume]u purity of [reagent.purity] index:[index]\n" + cached_purity = reagent.purity + if(beaker_spawn && reagents.total_volume) + var/obj/item/reagent_containers/glass/beaker/bluespace/beaker = new /obj/item/reagent_containers/glass/beaker/bluespace(loc) + reagents.trans_to(beaker) + beaker.name = "[cached_reactions[index]] purity: [cached_purity]" + reagents.clear_reagents() + reagents.chem_temp = 300 + index++ + failed = 0 + else + say("No reagents left in beaker!") + index++ + +/obj/machinery/chem_recipe_debug/proc/setup_reaction() + react_time = 0 + if(!length(cached_reactions)) + return FALSE + var/datum/chemical_reaction/reaction = cached_reactions[index] + if(!reaction) + say("Unable to find reaction on index: [index]") + say("Using forced temperatures.") + if(reaction.reaction_flags & REACTION_INSTANT) + say("This reaction is instant") + for(var/reagent_type in reaction.required_reagents) + reagents.add_reagent(reagent_type, reaction.required_reagents[reagent_type]*vol_multiplier) + for(var/catalyst_type in reaction.required_catalysts) + reagents.add_reagent(catalyst_type, reaction.required_catalysts[catalyst_type]) + if(should_force_temp && !min_temp) + say("Using forced temperatures.") + reagents.chem_temp = force_temp ? force_temp : reaction.optimal_temp + if(should_force_ph) + say("Using forced pH.") + reagents.ph = force_ph ? force_ph : (reaction.optimal_ph_max + reaction.optimal_ph_min)/2 + if(failed == 0 && !should_force_temp) + reagents.chem_temp = reaction.optimal_temp + if(failed == 1 && !should_force_temp) + reagents.chem_temp = reaction.required_temp+25 + failed++ + if(min_temp) + say("Overriding temperature to required temp.") + reagents.chem_temp = reaction.is_cold_recipe ? reaction.required_temp - 1 : reaction.required_temp + 1 + say("Reacting [cached_reactions[index]] starting pH: [reagents.ph] index [index] of [cached_reactions.len]") + +/obj/machinery/chem_recipe_debug/ui_data(mob/user) + var/data = list() + data["targetTemp"] = force_temp + data["targatpH"] = force_ph + data["isActive"] = reagents.is_reacting + data["forcepH"] = should_force_ph + data["forceTemp"] = should_force_temp + data["targetVol"] = vol_multiplier + data["processAll"] = process_all + data["currentTemp"] = reagents.chem_temp + data["currentpH"] = round(reagents.ph, 0.01) + data["processing"] = processing + data["index"] = index + data["endIndex"] = cached_reactions.len + data["beakerSpawn"] = beaker_spawn + data["minTemp"] = min_temp + + var/list/beaker_contents = list() + for(var/datum/reagent/reagent as anything in reagents.reagent_list) + beaker_contents.len++ + beaker_contents[length(beaker_contents)] = list("name" = reagent.name, "volume" = round(reagent.volume, 0.01), "purity" = round(reagent.purity)) + data["chamberContents"] = beaker_contents + + var/list/queued_reactions = list() + for(var/datum/chemical_reaction/reaction as anything in reaction_names) + var/datum/reagent/reagent = find_reagent_object_from_type(reaction.results[1]) + queued_reactions.len++ + queued_reactions[length(queued_reactions)] = list("name" = reagent.name) + data["queuedReactions"] = queued_reactions + + var/list/active_reactions = list() + var/flashing = DISABLE_FLASHING //for use with alertAfter - since there is no alertBefore, I set the after to 0 if true, or to the max value if false + for(var/datum/equilibrium/equilibrium as anything in reagents.reaction_list) + if(!length(reagents.reaction_list))//I'm not sure why when it explodes it causes the gui to fail (it's missing danger (?) ) + stack_trace("Chem debug managed to find an equilibrium in a location where there should be none (skipping this entry and continuing). This is usually because of an ill timed explosion.") + continue + if(!equilibrium.reaction.results)//Incase of no result reactions + continue + var/datum/reagent/reagent = reagents.get_reagent(equilibrium.reaction.results[1]) //Reactions are named after their primary products + if(!reagent) + continue + var/overheat = FALSE + var/danger = FALSE + var/purity_alert = 2 //same as flashing + if(reagent.purity < equilibrium.reaction.purity_min) + purity_alert = ENABLE_FLASHING//Because 0 is seen as null + danger = TRUE + if(flashing != ENABLE_FLASHING)//So that the pH meter flashes for ANY reactions out of optimal + if(equilibrium.reaction.optimal_ph_min > reagents.ph || equilibrium.reaction.optimal_ph_max < reagents.ph) + flashing = ENABLE_FLASHING + if(equilibrium.reaction.is_cold_recipe) + if(equilibrium.reaction.overheat_temp > reagents.chem_temp && equilibrium.reaction.overheat_temp != NO_OVERHEAT) + danger = TRUE + overheat = TRUE + else + if(equilibrium.reaction.overheat_temp < reagents.chem_temp) + danger = TRUE + overheat = TRUE + if(equilibrium.reaction.reaction_flags & REACTION_COMPETITIVE) //We have a compeitive reaction - concatenate the results for the different reactions + for(var/entry in active_reactions) + if(entry["name"] == reagent.name) //If we have multiple reaction methods for the same result - combine them + entry["reactedVol"] = equilibrium.reacted_vol + entry["targetVol"] = round(equilibrium.target_vol, 1)//Use the first result reagent to name the reaction detected + entry["quality"] = (entry["quality"] + equilibrium.reaction_quality) /2 + continue + active_reactions.len++ + active_reactions[length(active_reactions)] = list("name" = reagent.name, "danger" = danger, "purityAlert" = purity_alert, "quality" = equilibrium.reaction_quality, "overheat" = overheat, "inverse" = reagent.inverse_chem_val, "minPure" = equilibrium.reaction.purity_min, "reactedVol" = equilibrium.reacted_vol, "targetVol" = round(equilibrium.target_vol, 1))//Use the first result reagent to name the reaction detected + data["activeReactions"] = active_reactions + data["isFlashing"] = flashing + + return data + +/obj/machinery/chem_recipe_debug/ui_act(action, params) + . = ..() + if(.) + return + switch(action) + if("power") + return + if("temperature") + var/target = params["target"] + if(text2num(target) != null) + target = text2num(target) + . = TRUE + if(.) + force_temp = clamp(target, 0, 1000) + if("pH") + var/target = params["target"] + if(text2num(target) != null) + target = text2num(target) + . = TRUE + if(.) + force_ph = target + if("forceTemp") + should_force_temp = ! should_force_temp + . = TRUE + if("forcepH") + should_force_ph = ! should_force_ph + . = TRUE + if("react") + react = TRUE + return TRUE + if("all") + process_all = !process_all + return TRUE + if("beakerSpawn") + beaker_spawn = !beaker_spawn + return TRUE + if("setTargetList") + var/text = stripped_input(usr,"List","Enter a list of Recipe product names separated by commas", "Recipe", MAX_MESSAGE_LEN) + reaction_names = list() + if(!text) + say("Could not find reaction") + var/list/names = splittext("[text]", ",") + for(var/name in names) + var/datum/reagent/reagent = find_reagent_object_from_type(get_chem_id(name)) + if(!reagent) + say("Could not find [name]") + continue + var/datum/chemical_reaction/reaction = GLOB.chemical_reactions_list_product_index[reagent.type] + if(!reaction) + say("Could not find [name] reaction!") + continue + reaction_names += reaction + if("vol") + var/target = params["target"] + if(text2num(target) != null) + target = text2num(target) + . = TRUE + if(.) + vol_multiplier = clamp(target, 1, 200) + if("start") + if(processing) + say("currently processing reaction [index]: [cached_reactions[index]] of [cached_reactions.len]") + return + say("Starting processing") + index = 1 + setup_reactions() + begin_processing() + return TRUE + if("stop") + relay_all_reactions() + if("minTemp") + min_temp = !min_temp + + +/obj/machinery/chem_recipe_debug/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemRecipeDebug", name) + ui.open() + +///Moves a type of buffer from the heater to the beaker, + +/obj/machinery/chem_recipe_debug/ui_status(mob/user) + return UI_INTERACTIVE + +/obj/machinery/chem_recipe_debug/ui_state(mob/user) + return GLOB.physical_state diff --git a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm index 74a0e149f36..677d612ebf4 100644 --- a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm +++ b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm @@ -11,6 +11,8 @@ var/static/list/shortcuts = list( "meth" = /datum/reagent/drug/methamphetamine ) + ///The purity of the created reagent in % (purity uses 0-1 values) + var/purity = 100 /obj/machinery/chem_dispenser/chem_synthesizer/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) @@ -39,7 +41,7 @@ return else if(!beaker.reagents && !QDELETED(beaker)) beaker.create_reagents(beaker.volume) - beaker.reagents.add_reagent(input_reagent, amount) + beaker.reagents.add_reagent(input_reagent, amount, added_purity = (purity/100)) if("makecup") if(beaker) return @@ -49,8 +51,17 @@ var/input = text2num(params["amount"]) if(input) amount = input + if("purity") + var/input = text2num(params["amount"]) + if(input) + purity = input update_appearance() +/obj/machinery/chem_dispenser/chem_synthesizer/ui_data(mob/user) + . = ..() + .["purity"] = purity + return . + /obj/machinery/chem_dispenser/chem_synthesizer/proc/find_reagent(input) . = FALSE if(GLOB.chemical_reagents_list[input]) //prefer IDs! diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 63061bf24cf..2d64e84eb0d 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -84,7 +84,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) /// If the impurity is below 0.5, replace ALL of the chem with inverse_chem upon metabolising var/inverse_chem_val = 0.25 /// What chem is metabolised when purity is below inverse_chem_val - var/inverse_chem = /datum/reagent/impurity/toxic + var/inverse_chem = /datum/reagent/inverse ///what chem is made at the end of a reaction IF the purity is below the recipies purity_min at the END of a reaction only var/failed_chem = /datum/reagent/consumable/failed_reaction ///Thermodynamic vars @@ -160,6 +160,7 @@ Primarily used in reagents/reaction_agents /// Called when this reagent is first added to a mob /datum/reagent/proc/on_mob_add(mob/living/L, amount) + overdose_threshold /= max(normalise_creation_purity(), 1) //Maybe??? Seems like it would help pure chems be even better but, if I normalised this to 1, then everything would take a 25% reduction return /// Called when this reagent is removed while inside a mob @@ -227,6 +228,20 @@ Primarily used in reagents/reaction_agents /datum/reagent/proc/get_taste_description(mob/living/taster) return list("[taste_description]" = 1) +/** + * Used when you want the default reagents purity to be equal to the normal effects + * (i.e. if default purity is 0.75, and your reacted purity is 1, then it will return 1.33) + * + * Arguments + * * normalise_num_to - what number/purity value you're normalising to. If blank it will default to the compile value of purity for this chem + * * creation_purity - creation_purity override, if desired. This is the purity of the reagent that you're normalising from. + */ +/datum/reagent/proc/normalise_creation_purity(normalise_num_to, creation_purity) + if(!normalise_num_to) + normalise_num_to = initial(purity) + if(!creation_purity) + creation_purity = src.creation_purity + return creation_purity / normalise_num_to /proc/pretty_string_from_reagent_list(list/reagent_list) //Convert reagent list to a printable string for logging etc @@ -235,3 +250,5 @@ Primarily used in reagents/reaction_agents rs += "[R.name], [R.volume]" return rs.Join(" | ") + + diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index 81d00ef20c7..46bf43739e0 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -2,6 +2,14 @@ /datum/reagent/medicine/c2 harmful = TRUE metabolization_rate = 0.5 * REAGENTS_METABOLISM + impure_chem = null //Very few of these have impure effects, they're all baked in by creation_purity + inverse_chem = null //Some of these use inverse chems - we're just defining them all to null here to avoid repetition, eventually this will be moved up to parent + creation_purity = REAGENT_STANDARD_PUIRTY//All sources by default are 0.75 - reactions are primed to resolve to roughly the same with no intervention for these. + purity = REAGENT_STANDARD_PUIRTY + inverse_chem_val = 0 + inverse_chem = null + failed_chem = /datum/reagent/impurity/healing/medicine_failure + chemical_flags = REAGENT_SPLITRETAINVOL /******BRUTE******/ /*Suffix: -bital*/ @@ -11,15 +19,19 @@ description = "Named after the norse goddess Hel, this medicine heals the patient's bruises the closer they are to death. Patients will find the medicine 'aids' their healing if not near death by causing asphyxiation." color = "#9400D3" taste_description = "cold and lifeless" + ph = 8 overdose_threshold = 35 reagent_state = SOLID + inverse_chem_val = 0.3 + inverse_chem = /datum/reagent/inverse/helgrasp + failed_chem = null var/helbent = FALSE var/reaping = FALSE chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/helbital/on_mob_life(mob/living/carbon/M, delta_time, times_fired) . = TRUE - var/death_is_coming = (M.getToxLoss() + M.getOxyLoss() + M.getFireLoss() + M.getBruteLoss()) + var/death_is_coming = (M.getToxLoss() + M.getOxyLoss() + M.getFireLoss() + M.getBruteLoss())*normalise_creation_purity() var/thou_shall_heal = 0 var/good_kind_of_healing = FALSE switch(M.stat) @@ -83,13 +95,15 @@ name = "Libital" description = "A bruise reliever. Does minor liver damage." color = "#ECEC8D" // rgb: 236 236 141 + ph = 8.2 taste_description = "bitter with a hint of alcohol" reagent_state = SOLID + impure_chem = /datum/reagent/impurity/libitoil chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/libital/on_mob_life(mob/living/carbon/M, delta_time, times_fired) M.adjustOrganLoss(ORGAN_SLOT_LIVER, 0.3 * REM * delta_time) - M.adjustBruteLoss(-3 * REM * delta_time) + M.adjustBruteLoss(-3 * REM * normalise_creation_purity() * delta_time) ..() return TRUE @@ -98,11 +112,15 @@ description = "Originally developed as a prototype-gym supliment for those looking for quick workout turnover, this oral medication quickly repairs broken muscle tissue but causes lactic acid buildup, tiring the patient. Overdosing can cause extreme drowsiness. An Influx of nutrients promotes the muscle repair even further." reagent_state = SOLID color = "#FFFF6B" + ph = 5.5 overdose_threshold = 20 + inverse_chem_val = 0.5//Though it's tough to get + inverse_chem = /datum/reagent/medicine/metafactor //Seems thematically intact + failed_chem = /datum/reagent/impurity/probital_failed chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/probital/on_mob_life(mob/living/carbon/M, delta_time, times_fired) - M.adjustBruteLoss(-2.25 * REM * delta_time, FALSE) + M.adjustBruteLoss(-2.25 * REM * normalise_creation_purity() * delta_time, FALSE) var/ooo_youaregettingsleepy = 3.5 switch(round(M.getStaminaLoss())) if(10 to 40) @@ -142,35 +160,32 @@ description = "Used to treat burns. Makes you move slower while it is in your system. Applies stomach damage when it leaves your system." reagent_state = LIQUID color = "#6171FF" - var/resetting_probability = 0 + ph = 4.7 + impure_chem = /datum/reagent/impurity/lentslurri + failed_chem = /datum/reagent/inverse/ichiyuri //I do hope cobby won't take this personally + var/resetting_probability = 0 //What are these for?? Can I remove them? var/spammer = 0 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/lenturi/on_mob_life(mob/living/carbon/M, delta_time, times_fired) - M.adjustFireLoss(-3 * REM * delta_time) + M.adjustFireLoss(-3 * REM * normalise_creation_purity() * delta_time) M.adjustOrganLoss(ORGAN_SLOT_STOMACH, 0.4 * REM * delta_time) ..() return TRUE -/datum/reagent/medicine/c2/lenturi/on_mob_metabolize(mob/living/carbon/M) - M.add_movespeed_modifier(/datum/movespeed_modifier/reagent/lenturi) - return ..() - -/datum/reagent/medicine/c2/lenturi/on_mob_end_metabolize(mob/living/carbon/M) - M.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/lenturi) - return ..() - /datum/reagent/medicine/c2/aiuri name = "Aiuri" description = "Used to treat burns. Does minor eye damage." reagent_state = LIQUID color = "#8C93FF" - var/resetting_probability = 0 + ph = 4 + impure_chem = /datum/reagent/impurity/aiuri //blurriness + var/resetting_probability = 0 //same with this? Old legacy vars that should be removed? var/message_cd = 0 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/aiuri/on_mob_life(mob/living/carbon/M, delta_time, times_fired) - M.adjustFireLoss(-2 * REM * delta_time) + M.adjustFireLoss(-2 * REM * normalise_creation_purity() * delta_time) M.adjustOrganLoss(ORGAN_SLOT_EYES, 0.25 * REM * delta_time) ..() return TRUE @@ -182,13 +197,16 @@ color = "#F7FFA5" overdose_threshold = 25 reagent_weight = 0.6 + ph = 8.9 + inverse_chem = /datum/reagent/inverse/hercuri + inverse_chem_val = 0.3 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/hercuri/on_mob_life(mob/living/carbon/M, delta_time, times_fired) if(M.getFireLoss() > 50) - M.adjustFireLoss(-2 * REM * delta_time, FALSE) + M.adjustFireLoss(-2 * REM * delta_time * normalise_creation_purity(), FALSE) else - M.adjustFireLoss(-1.25 * REM * delta_time, FALSE) + M.adjustFireLoss(-1.25 * REM * delta_time * normalise_creation_purity(), FALSE) M.adjust_bodytemperature(rand(-25,-5) * TEMPERATURE_DAMAGE_COEFFICIENT * REM * delta_time, 50) if(ishuman(M)) var/mob/living/carbon/human/humi = M @@ -226,13 +244,16 @@ reagent_state = LIQUID color = "#FF6464" overdose_threshold = 35 // at least 2 full syringes +some, this stuff is nasty if left in for long + ph = 5.6 + inverse_chem_val = 0.4 + inverse_chem = /datum/reagent/inverse/healing/convermol chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/convermol/on_mob_life(mob/living/carbon/human/M, delta_time, times_fired) var/oxycalc = 2.5 * REM * current_cycle if(!overdosed) oxycalc = min(oxycalc, M.getOxyLoss() + 0.5) //if NOT overdosing, we lower our toxdamage to only the damage we actually healed with a minimum of 0.1*current_cycle. IE if we only heal 10 oxygen damage but we COULD have healed 20, we will only take toxdamage for the 10. We would take the toxdamage for the extra 10 if we were overdosing. - M.adjustOxyLoss(-oxycalc * delta_time, 0) + M.adjustOxyLoss(-oxycalc * delta_time * normalise_creation_purity(), 0) M.adjustToxLoss(oxycalc * delta_time / CONVERMOL_RATIO, 0) if(DT_PROB(current_cycle / 2, delta_time) && M.losebreath) M.losebreath-- @@ -250,12 +271,16 @@ name = "Tirimol" description = "An oxygen deprivation medication that causes fatigue. Prolonged exposure causes the patient to fall asleep once the medicine metabolizes." color = "#FF6464" + ph = 5.6 + inverse_chem = /datum/reagent/inverse/healing/tirimol + inverse_chem_val = 0.4 /// A cooldown for spacing bursts of stamina damage COOLDOWN_DECLARE(drowsycd) chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + /datum/reagent/medicine/c2/tirimol/on_mob_life(mob/living/carbon/human/M, delta_time, times_fired) - M.adjustOxyLoss(-3 * REM * delta_time) + M.adjustOxyLoss(-3 * REM * delta_time * normalise_creation_purity()) M.adjustStaminaLoss(2 * REM * delta_time) if(drowsycd && COOLDOWN_FINISHED(src, drowsycd)) M.drowsyness += 10 @@ -277,6 +302,11 @@ name = "Seiver" description = "A medicine that shifts functionality based on temperature. Colder temperatures incurs radiation removal while hotter temperatures promote antitoxicity. Damages the heart." //CHEM HOLDER TEMPS, NOT AIR TEMPS var/radbonustemp = (T0C - 100) //being below this number gives you 10% off rads. + inverse_chem_val = 0.3 + ph = 3.7 + inverse_chem = /datum/reagent/inverse/technetium + inverse_chem_val = 0.45 + failed_chem = null chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/seiver/on_mob_metabolize(mob/living/carbon/human/M) @@ -289,9 +319,9 @@ var/healypoints = 0 //5 healypoints = 1 heart damage; 5 rads = 1 tox damage healed for the purpose of healypoints //you're hot - var/toxcalc = min(round(5 + ((chemtemp-1000)/175), 0.1), 5) * REM * delta_time //max 2.5 tox healing per second + var/toxcalc = min(round(5 + ((chemtemp-1000)/175), 0.1), 5) * REM * delta_time * normalise_creation_purity() //max 2.5 tox healing per second if(toxcalc > 0) - M.adjustToxLoss(-toxcalc) + M.adjustToxLoss(-toxcalc * delta_time * normalise_creation_purity()) healypoints += toxcalc //and you're cold @@ -302,7 +332,7 @@ M.radiation = round(M.radiation * (0.75**(REM * delta_time))) else if(chemtemp < radbonustemp)//else if you're under the chill-zone, it takes off 10% of your current rads M.radiation = round(M.radiation * (0.90**(REM * delta_time))) - M.radiation -= radcalc + M.radiation -= radcalc * normalise_creation_purity() healypoints += (radcalc / 5) //you're yes and... oh no! @@ -314,6 +344,10 @@ /datum/reagent/medicine/c2/multiver //enhanced with MULTIple medicines name = "Multiver" description = "A chem-purger that becomes more effective the more unique medicines present. Slightly heals toxicity but causes lung damage (mitigatable by unique medicines)." + inverse_chem = /datum/reagent/inverse/healing/monover + inverse_chem_val = 0.35 + failed_chem = null //Reaction uses a special method - so we don't want this for now. + ph = 9.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/multiver/on_mob_life(mob/living/carbon/human/M, delta_time, times_fired) @@ -322,7 +356,8 @@ var/datum/reagent/the_reagent = r if(istype(the_reagent, /datum/reagent/medicine)) medibonus += 1 - + if(creation_purity > 1) //Perfectly pure multivers gives a bonus of 2! + medibonus += 1 M.adjustToxLoss(-0.5 * min(medibonus, 3) * REM * delta_time) //not great at healing but if you have nothing else it will work M.adjustOrganLoss(ORGAN_SLOT_LUNGS, 0.5 * REM * delta_time) //kills at 40u for(var/r2 in M.reagents.reagent_list) @@ -330,7 +365,7 @@ if(the_reagent2 == src) continue var/amount2purge = 3 - if(medibonus >= 3 && istype(the_reagent2, /datum/reagent/medicine)) //3 unique meds (2+multiver) will make it not purge medicines + if(medibonus >= 3 && istype(the_reagent2, /datum/reagent/medicine)) //3 unique meds (2+multiver) | (1 + pure multiver) will make it not purge medicines continue M.reagents.remove_reagent(the_reagent2.type, amount2purge * REM * delta_time) ..() @@ -339,7 +374,7 @@ // Antitoxin binds plants pretty well. So the tox goes significantly down /datum/reagent/medicine/c2/multiver/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) . = ..() - mytray.adjustToxic(-round(chems.get_reagent_amount(type) * 2)) + mytray.adjustToxic(-(round(chems.get_reagent_amount(type) * 2)*normalise_creation_purity())) //0-2.66, 2 by default (0.75 purity). #define issyrinormusc(A) (istype(A,/datum/reagent/medicine/c2/syriniver) || istype(A,/datum/reagent/medicine/c2/musiver)) //musc is metab of syrin so let's make sure we're not purging either @@ -350,6 +385,8 @@ color = "#8CDF24" // heavy saturation to make the color blend better metabolization_rate = 0.75 * REAGENTS_METABOLISM overdose_threshold = 6 + impure_chem = /datum/reagent/inverse/healing/syriniver + ph = 8.6 var/conversion_amount chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -358,11 +395,11 @@ return var/mob/living/carbon/C = A if(trans_volume >= 0.6) //prevents cheesing with ultralow doses. - C.adjustToxLoss(-1.5 * min(2, trans_volume) * REM, 0) //This is to promote iv pole use for that chemotherapy feel. + C.adjustToxLoss((-1.5 * min(2, trans_volume) * REM) * normalise_creation_purity(), 0) //This is to promote iv pole use for that chemotherapy feel. var/obj/item/organ/liver/L = C.internal_organs_slot[ORGAN_SLOT_LIVER] if((L.organ_flags & ORGAN_FAILING) || !L) return - conversion_amount = trans_volume * (min(100 -C.getOrganLoss(ORGAN_SLOT_LIVER), 80) / 100) //the more damaged the liver the worse we metabolize. + conversion_amount = (trans_volume * (min(100 -C.getOrganLoss(ORGAN_SLOT_LIVER), 80) / 100)*normalise_creation_purity()) //the more damaged the liver the worse we metabolize. C.reagents.remove_reagent(/datum/reagent/medicine/c2/syriniver, conversion_amount) C.reagents.add_reagent(/datum/reagent/medicine/c2/musiver, conversion_amount) ..() @@ -392,12 +429,13 @@ color = "#DFD54E" metabolization_rate = 0.25 * REAGENTS_METABOLISM overdose_threshold = 25 + ph = 9.1 var/datum/brain_trauma/mild/muscle_weakness/U chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/musiver/on_mob_life(mob/living/carbon/M, delta_time, times_fired) M.adjustOrganLoss(ORGAN_SLOT_LIVER, 0.1 * REM * delta_time) - M.adjustToxLoss(-1 * REM * delta_time, 0) + M.adjustToxLoss(-1 * REM * delta_time * normalise_creation_purity(), 0) for(var/datum/reagent/R in M.reagents.reagent_list) if(issyrinormusc(R)) continue @@ -429,6 +467,7 @@ description = "Heals brute and burn damage at the cost of toxicity (66% of damage healed). 100u or more can restore corpses husked by burns. Touch application only." reagent_state = LIQUID color = "#FFEBEB" + ph = 7.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/synthflesh/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE) @@ -445,7 +484,7 @@ for(var/i in carbies.all_wounds) var/datum/wound/iter_wound = i iter_wound.on_synthflesh(reac_volume) - carbies.adjustToxLoss((harmies+burnies)*0.66) + carbies.adjustToxLoss((harmies+burnies)*(0.5 + (0.25*(1-creation_purity)))) //0.5 - 0.75 if(show_message) to_chat(carbies, "You feel your burns and bruises healing! It stings like hell!") SEND_SIGNAL(carbies, COMSIG_ADD_MOOD_EVENT, "painful_medicine", /datum/mood_event/painful_medicine) @@ -470,6 +509,10 @@ description = "An expensive medicine that aids with pumping blood around the body even without a heart, and prevents the heart from slowing down. Mixing it with epinephrine or atropine will cause an explosion." color = "#F5F5F5" overdose_threshold = 50 + ph = 12.7 + inverse_chem = /datum/reagent/inverse/penthrite + inverse_chem_val = 0.25 + failed_chem = null //We don't want to accidentally crash it out (see reaction) chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/c2/penthrite/on_mob_metabolize(mob/living/M) @@ -483,7 +526,7 @@ /datum/reagent/medicine/c2/penthrite/on_mob_life(mob/living/carbon/human/H, delta_time, times_fired) H.adjustStaminaLoss(-25 * REM) //SKYRAT EDIT ADDITION - COMBAT - makes your heart beat faster, fills you with energy. For miners H.adjustOrganLoss(ORGAN_SLOT_STOMACH, 0.25 * REM * delta_time) - if(H.health <= HEALTH_THRESHOLD_CRIT && H.health > (H.crit_threshold + HEALTH_THRESHOLD_FULLCRIT*2)) //we cannot save someone below our lowered crit threshold. + if(H.health <= HEALTH_THRESHOLD_CRIT && H.health > (H.crit_threshold + HEALTH_THRESHOLD_FULLCRIT * (2 * normalise_creation_purity()))) //we cannot save someone below our lowered crit threshold. H.adjustToxLoss(-2 * REM * delta_time, 0) H.adjustBruteLoss(-2 * REM * delta_time, 0) @@ -500,7 +543,7 @@ if(DT_PROB(18, delta_time)) to_chat(H,"Your body is trying to give up, but your heart is still beating!") - if(H.health <= (H.crit_threshold + HEALTH_THRESHOLD_FULLCRIT*2)) //certain death below this threshold + if(H.health <= (H.crit_threshold + HEALTH_THRESHOLD_FULLCRIT*(2*normalise_creation_purity()))) //certain death below this threshold REMOVE_TRAIT(H, TRAIT_STABLEHEART, type) //we have to remove the stable heart trait before we give them a heart attack to_chat(H,"You feel something rupturing inside your chest!") H.emote("scream") diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm old mode 100755 new mode 100644 index aced7fd7d5b..d0e2111e21b --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -13,7 +13,7 @@ taste_mult = 4 impure_chem = /datum/reagent/water inverse_chem_val = 0.1 - inverse_chem = /datum/reagent/water + inverse_chem = null failed_chem = /datum/reagent/consumable/nutriment /// How much nutrition this reagent supplies var/nutriment_factor = 1 * REAGENTS_METABOLISM @@ -871,6 +871,8 @@ nutriment_factor = 10 * REAGENTS_METABOLISM // 33% less than nutriment to reduce weight gain brute_heal = 3 burn_heal = 1 + inverse_chem = /datum/reagent/peptides_failed//should be impossible, but it's so it appears in the chemical lookup gui + inverse_chem_val = 0.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/consumable/caramel diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents.dm index bb526cd1ea8..51dea8b58dd 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents.dm @@ -7,27 +7,41 @@ name = "Chemical Isomers" description = "Impure chemical isomers made from inoptimal reactions. Causes mild liver damage" //by default, it will stay hidden on splitting, but take the name of the source on inverting. Cannot be fractioned down either if the reagent is somehow isolated. - chemical_flags = REAGENT_INVISIBLE | REAGENT_SNEAKYNAME | REAGENT_DONOTSPLIT | REAGENT_CAN_BE_SYNTHESIZED + chemical_flags = REAGENT_SNEAKYNAME | REAGENT_DONOTSPLIT | REAGENT_CAN_BE_SYNTHESIZED //impure can be synthed, and is one of the only ways to get almost pure impure ph = 3 - overdose_threshold = 0 //So that they're shown as a problem (?) + impure_chem = null + inverse_chem = null + failed_chem = null + metabolization_rate = 0.1 * REM //default impurity is 0.75, so we get 25% converted. Default metabolisation rate is 0.4, so we're 4 times slower. + var/liver_damage = 0.5 /datum/reagent/impurity/on_mob_life(mob/living/carbon/C, delta_time, times_fired) var/obj/item/organ/liver/L = C.getorganslot(ORGAN_SLOT_LIVER) if(!L)//Though, lets be safe C.adjustToxLoss(1 * REM * delta_time, FALSE)//Incase of no liver! return ..() - C.adjustOrganLoss(ORGAN_SLOT_LIVER, 0.5 * REM * delta_time) + C.adjustOrganLoss(ORGAN_SLOT_LIVER, liver_damage * REM * delta_time) return ..() -/datum/reagent/impurity/toxic - name = "Toxic sludge" - description = "Toxic chemical isomers made from impure reactions. Causes toxin damage" +//Basically just so people don't forget to adjust metabolization_rate +/datum/reagent/inverse + name = "Toxic monomers" + description = "Inverse reagents are created when a reagent's purity is below it's inverse threshold. The are created either during ingestion - which will then replace their associated reagent, or some can be created during the reaction process." ph = 2 + chemical_flags = REAGENT_SNEAKYNAME | REAGENT_DONOTSPLIT //Inverse generally cannot be synthed - they're difficult to get + //Mostly to be safe - but above flags will take care of this. Also prevents it from showing these on reagent lookups in the ui + impure_chem = null + inverse_chem = null + failed_chem = null + ///how much this reagent does for tox damage too + var/tox_damage = 1 -/datum/reagent/impurity/toxic/on_mob_life(mob/living/carbon/C, delta_time, times_fired) - C.adjustToxLoss(1 * REM * delta_time, FALSE) + +/datum/reagent/inverse/on_mob_life(mob/living/carbon/C, delta_time, times_fired) + C.adjustToxLoss(tox_damage * REM * delta_time, FALSE) return ..() +//Failed chems - generally use inverse if you want to use a impure subtype for it //technically not a impure chem, but it's here because it can only be made with a failed impure reaction /datum/reagent/consumable/failed_reaction name = "Viscous sludge" diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm new file mode 100644 index 00000000000..9b33c3fb897 --- /dev/null +++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm @@ -0,0 +1,521 @@ +//Reagents produced by metabolising/reacting fermichems inoptimally these specifically are for medicines +//Inverse = Splitting +//Invert = Whole conversion +//Failed = End reaction below purity_min + +//START SUBTYPES + +//We don't want these to hide - they're helpful! +/datum/reagent/impurity/healing + name = "Healing impure reagent" + description = "Not all impure reagents are bad! Sometimes you might want to specifically make these!" + chemical_flags = REAGENT_DONOTSPLIT + addiction_types = list(/datum/addiction/medicine = 3.5) + liver_damage = 0 + +/datum/reagent/inverse/healing + name = "Healing inverse reagent" + description = "Not all impure reagents are bad! Sometimes you might want to specifically make these!" + chemical_flags = REAGENT_DONOTSPLIT + addiction_types = list(/datum/addiction/medicine = 3) + tox_damage = 0 + +// END SUBTYPES + +////////////////////MEDICINES/////////////////////////// + +//Catch all failed reaction for medicines - supposed to be non punishing +/datum/reagent/impurity/healing/medicine_failure + name = "Insolvent medicinal precipitate" + description = "A viscous mess of various medicines. Will heal a damage type at random" + metabolization_rate = 1 * REM//This is fast + addiction_types = list(/datum/addiction/medicine = 7.5) + ph = 11 + +//Random healing of the 4 main groups +/datum/reagent/impurity/healing/medicine_failure/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + var/pick = pick("brute", "burn", "tox", "oxy") + switch(pick) + if("brute") + owner.adjustBruteLoss(-0.5) + if("burn") + owner.adjustFireLoss(-0.5) + if("tox") + owner.adjustToxLoss(-0.5) + if("oxy") + owner.adjustOxyLoss(-0.5) + ..() + +// C2 medications +// Helbital +//Inverse: +/datum/reagent/inverse/helgrasp + name = "Helgrasp" + description = "This rare and forbidden concoction is thought to bring you closer to the grasp of the Norse goddess Hel." + metabolization_rate = 1*REM //This is fast + tox_damage = 0.25 + ph = 14 + //Compensates for delta_time lag by spawning multiple hands at the end + var/lag_compensate = 0 + +//Warns you about the impenting hands +/datum/reagent/inverse/helgrasp/on_mob_add(mob/living/L, amount) + to_chat(L, "You hear laughter as malevolent hands apparate before you, eager to drag you down to hell...! Look out!") + playsound(L.loc, 'sound/chemistry/ahaha.ogg', 80, TRUE, -1) //Very obvious tell so people can be ready + . = ..() + +//Sends hands after you for your hubris +/datum/reagent/inverse/helgrasp/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + spawn_hands(owner) + lag_compensate += 1 - max(delta_time, 1) + . = ..() + +/datum/reagent/inverse/helgrasp/proc/spawn_hands(mob/living/carbon/owner) + if(!owner)//Unit test has a very strange interation where hands spawned will no longer have a target causing the beam to runtime + return + //Adapted from the end of the curse - but lasts a short time + var/grab_dir = turn(owner.dir, pick(-90, 90, 180, 180)) //grab them from a random direction other than the one faced, favoring grabbing from behind + var/turf/spawn_turf = get_ranged_target_turf(owner, grab_dir, 8)//Larger range so you have more time to dodge + if(!spawn_turf) + return + new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, owner.dir) + playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1) + var/obj/projectile/curse_hand/hel/hand = new (spawn_turf) + hand.preparePixelProjectile(owner, spawn_turf) + hand.fire() + +/datum/reagent/inverse/helgrasp/on_mob_delete(mob/living/owner) + var/hands = 0 + while(lag_compensate > hands) + spawn_hands(owner) + hands++ + . = ..() + +//libital +//Impure +//Simply reduces your alcohol tolerance, kinda simular to prohol +/datum/reagent/impurity/libitoil + name = "Libitoil" + description = "Temporarilly interferes a patient's ability to process alcohol." + chemical_flags = REAGENT_DONOTSPLIT + ph = 13.5 + liver_damage = 0.1 + addiction_types = list(/datum/addiction/medicine = 4) + +/datum/reagent/impurity/libitoil/on_mob_add(mob/living/L, amount) + . = ..() + var/mob/living/carbon/consumer = L + if(!consumer) + return + RegisterSignal(consumer, COMSIG_CARBON_LOSE_ORGAN, .proc/on_removed_organ) + var/obj/item/organ/liver/this_liver = consumer.getorganslot(ORGAN_SLOT_LIVER) + this_liver.alcohol_tolerance *= 2 + +/datum/reagent/impurity/libitoil/proc/on_removed_organ(mob/prev_owner, obj/item/organ/organ) + if(!istype(organ, /obj/item/organ/liver)) + return + var/obj/item/organ/liver/this_liver = organ + this_liver.alcohol_tolerance /= 2 + +/datum/reagent/impurity/libitoil/on_mob_delete(mob/living/L) + . = ..() + var/mob/living/carbon/consumer = L + UnregisterSignal(consumer, COMSIG_CARBON_LOSE_ORGAN) + var/obj/item/organ/liver/this_liver = consumer.getorganslot(ORGAN_SLOT_LIVER) + if(!this_liver) + return + this_liver.alcohol_tolerance /= 2 + + +//probital +/datum/reagent/impurity/probital_failed//Basically crashed out failed metafactor + name = "Metabolic Inhibition Factor" + description = "This enzyme catalyzes crashes the conversion of nutricious food into healing peptides." + metabolization_rate = 0.0625 * REAGENTS_METABOLISM //slow metabolism rate so the patient can self heal with food even after the troph has metabolized away for amazing reagent efficency. + reagent_state = SOLID + color = "#b3ff00" + overdose_threshold = 10 + ph = 1 + addiction_types = list(/datum/addiction/medicine = 5) + liver_damage = 0 + +/datum/reagent/impurity/probital_failed/overdose_start(mob/living/carbon/M) + metabolization_rate = 4 * REAGENTS_METABOLISM + ..() + +/datum/reagent/peptides_failed + name = "Prion peptides" + taste_description = "spearmint frosting" + description = "These inhibitory peptides cause cellular damage and cost nutrition to the patient!" + ph = 2.1 + +/datum/reagent/peptides_failed/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + owner.adjustCloneLoss(0.25 * delta_time) + owner.adjust_nutrition(-5 * REAGENTS_METABOLISM * delta_time) + . = ..() + +//Lenturi +//impure +/datum/reagent/impurity/lentslurri //Okay maybe I should outsource names for these + name = "Lentslurri"//This is a really bad name please replace + description = "A highly addicitive muscle relaxant that is made when Lenturi reactions go wrong." + addiction_types = list(/datum/addiction/medicine = 8) + liver_damage = 0 + +/datum/reagent/impurity/lentslurri/on_mob_metabolize(mob/living/carbon/owner) + owner.add_movespeed_modifier(/datum/movespeed_modifier/reagent/lenturi) + return ..() + +/datum/reagent/impurity/lentslurri/on_mob_end_metabolize(mob/living/carbon/owner) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/lenturi) + return ..() + +//failed +/datum/reagent/inverse/ichiyuri + name = "Ichiyuri" + description = "Prolonged exposure to this chemical can cause an overwhelming urge to itch oneself." + reagent_state = LIQUID + color = "#C8A5DC" + ph = 1.7 + addiction_types = list(/datum/addiction/medicine = 2.5) + tox_damage = 0.1 + ///Probability of scratch - increases as a function of time + var/resetting_probability = 0 + ///Prevents message spam + var/spammer = 0 + +//Just the removed itching mechanism - omage to it's origins. +/datum/reagent/inverse/ichiyuri/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + if(prob(resetting_probability) && !(HAS_TRAIT(owner, TRAIT_RESTRAINED) || owner.incapacitated())) + if(spammer < world.time) + to_chat(owner,"You can't help but itch yourself.") + spammer = world.time + (10 SECONDS) + var/scab = rand(1,7) + owner.adjustBruteLoss(scab*REM) + owner.bleed(scab) + resetting_probability = 0 + resetting_probability += (5*(current_cycle/10) * delta_time) // 10 iterations = >51% to itch + ..() + return TRUE + +//Aiuri +//impure +/datum/reagent/impurity/aiuri + name = "Aivime" + description = "This reagent is known to interfere with the eyesight of a patient." + ph = 3.1 + addiction_types = list(/datum/addiction/medicine = 1.5) + liver_damage = 0.1 + //blurriness at the start of taking the med + var/cached_blurriness + +/datum/reagent/impurity/aiuri/on_mob_add(mob/living/owner, amount) + . = ..() + cached_blurriness = owner.eye_blurry + owner.set_blurriness(((creation_purity*10)*(volume/metabolization_rate)) + cached_blurriness) + +/datum/reagent/impurity/aiuri/on_mob_delete(mob/living/owner, amount) + . = ..() + if(owner.eye_blurry <= cached_blurriness) + return + owner.set_blurriness(cached_blurriness) + +//Hercuri +//inverse +/datum/reagent/inverse/hercuri + name = "Herignis" + description = "This reagent causes a dramatic raise in a patient's body temperature." + ph = 0.8 + tox_damage = 0 + color = "#ff1818" + taste_description = "heat! Ouch!" + addiction_types = list(/datum/addiction/medicine = 2.5) + data = list("method" = TOUCH) + ///The method in which the reagent was exposed + var/method + +/datum/reagent/inverse/hercuri/expose_mob(mob/living/carbon/exposed_mob, methods=VAPOR, reac_volume) + method |= methods + data["method"] |= methods + ..() + +/datum/reagent/inverse/hercuri/on_new(data) + method |= data["method"] + ..() + +/datum/reagent/inverse/hercuri/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + var/heating = rand(creation_purity * REM * 3, creation_purity * REM * 6) + if(method & INGEST) + owner.reagents?.chem_temp += heating * REM * delta_time + if(method & VAPOR) + owner.adjust_bodytemperature(heating * REM * delta_time * TEMPERATURE_DAMAGE_COEFFICIENT, 50) + if(method & INJECT) + if(!ishuman(owner)) + return ..() + var/mob/living/carbon/human/human_mob = owner + human_mob.adjust_coretemperature(heating * REM * delta_time * TEMPERATURE_DAMAGE_COEFFICIENT, 50) + else + owner.adjust_fire_stacks(heating * 0.05) + ..() + +/datum/reagent/inverse/healing/tirimol + name = "Super Melatonin"//It's melatonin, but super! + description = "This will send the patient to sleep, adding a bonus to the efficacy of all reagents administered." + ph = 12.5 //sleeping is a basic need of all lifeformsa + self_consuming = TRUE //No pesky liver shenanigans + chemical_flags = REAGENT_DONOTSPLIT | REAGENT_DEAD_PROCESS + var/cached_reagent_list = list() + addiction_types = list(/datum/addiction/medicine = 5) + +//Makes patients fall asleep, then boosts the purirty of their medicine reagents if they're asleep +/datum/reagent/inverse/healing/tirimol/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + switch(current_cycle) + if(1 to 10)//same delay as chloral hydrate + if(prob(50)) + owner.emote("yawn") + if(10 to INFINITY) + owner.Sleeping(40) + . = 1 + if(owner.IsSleeping()) + for(var/datum/reagent/reagent as anything in owner.reagents.reagent_list) + if(reagent in cached_reagent_list) + continue + if(!istype(reagent, /datum/reagent/medicine)) + continue + reagent.creation_purity *= 1.25 + cached_reagent_list += reagent + + else if(!owner.IsSleeping() && length(cached_reagent_list)) + for(var/datum/reagent/reagent as anything in cached_reagent_list) + if(!reagent) + continue + reagent.creation_purity *= 0.8 + cached_reagent_list = list() + ..() + +/datum/reagent/inverse/healing/tirimol/on_mob_delete(mob/living/owner) + if(owner.IsSleeping()) + owner.visible_message("[icon2html(owner, viewers(DEFAULT_MESSAGE_RANGE, src))] [owner] lets out a hearty snore!")//small way of letting people know the supersnooze is ended + for(var/datum/reagent/reagent as anything in cached_reagent_list) + if(!reagent) + continue + reagent.creation_purity *= 0.8 + cached_reagent_list = list() + ..() + +//convermol +//inverse +/datum/reagent/inverse/healing/convermol + name = "Coveroli" + description = "This reagent is known to coat the inside of a patient's lungs, providing greater protection against hot or cold air." + ph = 3.82 + tox_damage = 0 + addiction_types = list(/datum/addiction/medicine = 2.3) + //The heat damage levels of lungs when added (i.e. heat_level_1_threshold on lungs) + var/cached_heat_level_1 + var/cached_heat_level_2 + var/cached_heat_level_3 + //The cold damage levels of lungs when added (i.e. cold_level_1_threshold on lungs) + var/cached_cold_level_1 + var/cached_cold_level_2 + var/cached_cold_level_3 + +/datum/reagent/inverse/healing/convermol/on_mob_add(mob/living/owner, amount) + . = ..() + RegisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN, .proc/on_removed_organ) + var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS) + if(!lungs) + return + cached_heat_level_1 = lungs.heat_level_1_threshold + cached_heat_level_2 = lungs.heat_level_2_threshold + cached_heat_level_3 = lungs.heat_level_3_threshold + cached_cold_level_1 = lungs.cold_level_1_threshold + cached_cold_level_2 = lungs.cold_level_2_threshold + cached_cold_level_3 = lungs.cold_level_3_threshold + //Heat threshold is increased + lungs.heat_level_1_threshold *= creation_purity * 1.5 + lungs.heat_level_2_threshold *= creation_purity * 1.5 + lungs.heat_level_3_threshold *= creation_purity * 1.5 + //Cold threshold is decreased + lungs.cold_level_1_threshold *= creation_purity * 0.5 + lungs.cold_level_2_threshold *= creation_purity * 0.5 + lungs.cold_level_3_threshold *= creation_purity * 0.5 + +/datum/reagent/inverse/healing/convermol/proc/on_removed_organ(mob/prev_owner, obj/item/organ/organ) + if(!istype(organ, /obj/item/organ/lungs)) + return + var/obj/item/organ/lungs/lungs = organ + restore_lung_levels(lungs) + +/datum/reagent/inverse/healing/convermol/proc/restore_lung_levels(obj/item/organ/lungs/lungs) + lungs.heat_level_1_threshold = cached_heat_level_1 + lungs.heat_level_2_threshold = cached_heat_level_2 + lungs.heat_level_3_threshold = cached_heat_level_3 + lungs.cold_level_1_threshold = cached_cold_level_1 + lungs.cold_level_2_threshold = cached_cold_level_2 + lungs.cold_level_3_threshold = cached_cold_level_3 + +/datum/reagent/inverse/healing/convermol/on_mob_delete(mob/living/owner) + . = ..() + UnregisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN) + var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS) + if(!lungs) + return + restore_lung_levels(lungs) + +//seiver +//Inverse +//Allows the scanner to detect organ health to the nearest 1% (similar use to irl) and upgrates the scan to advanced +/datum/reagent/inverse/technetium + name = "Technetium 99" + description = "A radioactive tracer agent that can improve a scanner's ability to detect internal organ damage. Will irradiate the patient when present very slowly, purging or using a low dose is recommended after use." + metabolization_rate = 0.3 * REM + chemical_flags = REAGENT_DONOTSPLIT //Do show this on scanner + tox_damage = 0 + ///Accumulates radiation, then adds it as a whole number to the owner + var/radiation_ticker + +/datum/reagent/inverse/technetium/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + //for some reason radiation doesn't work when small + owner.radiation += creation_purity * delta_time * 0.5 + ..() + +//Kind of a healing effect, Presumably you're using syrinver to purge so this helps that +/datum/reagent/inverse/healing/syriniver + name = "Syrinifergus" + description = "This reagent reduces the impurity of all non medicines within the patient, reducing their negative effects." + self_consuming = TRUE //No pesky liver shenanigans + chemical_flags = REAGENT_DONOTSPLIT | REAGENT_DEAD_PROCESS + ///The list of reagents we've affected + var/cached_reagent_list = list() + addiction_types = list(/datum/addiction/medicine = 1.75) + +/datum/reagent/inverse/healing/syriniver/on_mob_add(mob/living/living_mob) + if(!(iscarbon(living_mob))) + return ..() + var/mob/living/carbon/owner = living_mob + for(var/datum/reagent/reagent as anything in owner.reagents.reagent_list) + if(reagent in cached_reagent_list) + continue + if(istype(reagent, /datum/reagent/medicine)) + continue + reagent.creation_purity *= 0.8 + cached_reagent_list += reagent + ..() + +/datum/reagent/inverse/healing/syriniver/on_mob_delete(mob/living/living_mob) + . = ..() + if(!(iscarbon(living_mob))) + return + if(!cached_reagent_list) + return + for(var/datum/reagent/reagent as anything in cached_reagent_list) + if(!reagent) + continue + reagent.creation_purity *= 1.25 + cached_reagent_list = null + +//Multiver +//Inverse +//Reaction product when between 0.2 and 0.35 purity. +/datum/reagent/inverse/healing/monover + name = "Monover" + description = "A toxin treating reagent, that only is effective if it's the only reagent present in the patient." + ph = 0.5 + addiction_types = list(/datum/addiction/medicine = 3.5) + +//Heals toxins if it's the only thing present - kinda the oposite of multiver! Maybe that's why it's inverse! +/datum/reagent/inverse/healing/monover/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + if(length(owner.reagents.reagent_list) > 1) + owner.adjustOrganLoss(ORGAN_SLOT_LUNGS, 0.5 * delta_time) //Hey! It's everyone's favourite drawback from multiver! + return ..() + owner.adjustToxLoss(-2 * REM * creation_purity * delta_time, 0) + ..() + return TRUE + +///Can bring a corpse back to life temporarily (if heart is intact) +///Makes wounds bleed more, if it brought someone back, they take additional brute and heart damage +///They can't die during this, but if they're past crit then take increasing stamina damage +///If they're past fullcrit, their movement is slowed by half +///If they OD, their heart explodes (if they were brought back from the dead) +/datum/reagent/inverse/penthrite + name = "Nooartrium" + description = "A reagent that is known to stimulate the heart in a dead patient, temporarily bringing back recently dead patients at great cost to their heart." + ph = 14 + metabolization_rate = 0.05 * REM + addiction_types = list(/datum/addiction/medicine = 12) + overdose_threshold = 20 + self_consuming = TRUE //No pesky liver shenanigans + chemical_flags = REAGENT_DONOTSPLIT | REAGENT_DEAD_PROCESS + ///If we brought someone back from the dead + var/back_from_the_dead = FALSE + +/datum/reagent/inverse/penthrite/on_mob_dead(mob/living/carbon/owner) + var/obj/item/organ/heart/heart = owner.getorganslot(ORGAN_SLOT_HEART) + if(!heart || heart.organ_flags & ORGAN_FAILING) + return ..() + metabolization_rate = 0.35 + ADD_TRAIT(owner, TRAIT_STABLEHEART, type) + ADD_TRAIT(owner, TRAIT_NOHARDCRIT, type) + ADD_TRAIT(owner, TRAIT_NOSOFTCRIT, type) + ADD_TRAIT(owner, TRAIT_NOCRITDAMAGE, type) + ADD_TRAIT(owner, TRAIT_NODEATH, type) + owner.set_stat(CONSCIOUS) //This doesn't touch knocked out + owner.updatehealth() + REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, STAT_TRAIT) + REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) //Because these are normally updated using set_health() - but we don't want to adjust health, and the addition of NOHARDCRIT blocks it being added after, but doesn't remove it if it was added before + owner.set_resting(FALSE)//Please get up, no one wants a deaththrows juggernaught that lies on the floor all the time + owner.SetAllImmobility(0) + back_from_the_dead = TRUE + owner.emote("gasp") + owner.playsound_local(owner, 'sound/health/fastbeat.ogg', 65) + ..() + +/datum/reagent/inverse/penthrite/on_mob_life(mob/living/carbon/owner, delta_time, times_fired) + if(!back_from_the_dead) + return ..() + REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) + //Following is for those brought back from the dead only + for(var/datum/wound/iter_wound as anything in owner.all_wounds) + iter_wound.blood_flow += (1-creation_purity) + owner.adjustBruteLoss(5 * (1-creation_purity) * delta_time) + owner.adjustOrganLoss(ORGAN_SLOT_HEART, (1 + (1-creation_purity)) * delta_time) + if(owner.health < HEALTH_THRESHOLD_CRIT) + owner.add_movespeed_modifier(/datum/movespeed_modifier/reagent/nooartrium) + if(owner.health < HEALTH_THRESHOLD_FULLCRIT) + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/nooartrium) + var/obj/item/organ/heart/heart = owner.getorganslot(ORGAN_SLOT_HEART) + if(!heart || heart.organ_flags & ORGAN_FAILING) + remove_buffs(owner) + ..() + +/datum/reagent/inverse/penthrite/on_mob_delete(mob/living/carbon/owner) + remove_buffs(owner) + var/obj/item/organ/heart/heart = owner.getorganslot(ORGAN_SLOT_HEART) + if(owner.health < -500 || heart.organ_flags & ORGAN_FAILING)//Honestly commendable if you get -500 + explosion(owner, 0, 0, 1) + qdel(heart) + owner.visible_message("[owner]'s heart explodes!") + return ..() + +/datum/reagent/inverse/penthrite/overdose_start(mob/living/carbon/owner) + if(!back_from_the_dead) + return ..() + var/obj/item/organ/heart/heart = owner.getorganslot(ORGAN_SLOT_HEART) + if(!heart) //No heart? No life! + REMOVE_TRAIT(owner, TRAIT_NODEATH, type) + owner.stat = DEAD + return ..() + explosion(owner, 0, 0, 1) + qdel(heart) + owner.visible_message("[owner]'s heart explodes!") + return..() + +/datum/reagent/inverse/penthrite/proc/remove_buffs(mob/living/carbon/owner) + REMOVE_TRAIT(owner, TRAIT_STABLEHEART, type) + REMOVE_TRAIT(owner, TRAIT_NOHARDCRIT, type) + REMOVE_TRAIT(owner, TRAIT_NOSOFTCRIT, type) + REMOVE_TRAIT(owner, TRAIT_NOCRITDAMAGE, type) + REMOVE_TRAIT(owner, TRAIT_NODEATH, type) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/reagent/nooartrium) + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/nooartrium) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index e52ac513f92..c1b96bf67e1 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -870,7 +870,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED addiction_types = list(/datum/addiction/stimulants = 4) //0.8 per 2 seconds - /datum/reagent/medicine/stimulants/on_mob_metabolize(mob/living/L) ..() L.add_movespeed_modifier(/datum/movespeed_modifier/reagent/stimulants) @@ -1267,6 +1266,8 @@ reagent_state = SOLID color = "#FFBE00" overdose_threshold = 10 + inverse_chem_val = 0.1 //Shouldn't happen - but this is so looking up the chem will point to the failed type + inverse_chem = /datum/reagent/impurity/probital_failed chemical_flags = REAGENT_CAN_BE_SYNTHESIZED /datum/reagent/medicine/metafactor/overdose_start(mob/living/carbon/M) diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 29b9dc4dcbb..5e40775fa32 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -143,6 +143,8 @@ * * reagent - the target reagent to convert */ /datum/chemical_reaction/proc/convert_into_failed(datum/reagent/reagent, datum/reagents/holder) + if(!reagent.failed_chem) + return if(reagent.purity < purity_min) var/cached_volume = reagent.volume holder.remove_reagent(reagent.type, cached_volume, FALSE) @@ -164,17 +166,19 @@ return var/cached_volume = reagent.volume + var/cached_purity = reagent.purity if((reaction_flags & REACTION_CLEAR_INVERSE) && reagent.inverse_chem) if(reagent.inverse_chem_val > reagent.purity) holder.remove_reagent(reagent.type, cached_volume, FALSE) - holder.add_reagent(reagent.inverse_chem, cached_volume, FALSE, added_purity = 1) + holder.add_reagent(reagent.inverse_chem, cached_volume, FALSE, added_purity = 1-cached_purity) + return if((reaction_flags & REACTION_CLEAR_IMPURE) && reagent.impure_chem) var/impureVol = cached_volume * (1 - reagent.purity) holder.remove_reagent(reagent.type, (impureVol), FALSE) - holder.add_reagent(reagent.impure_chem, impureVol, FALSE, added_purity = 1) - reagent.creation_purity = reagent.purity - reagent.purity = 1 + holder.add_reagent(reagent.impure_chem, impureVol, FALSE, added_purity = 1-cached_purity) + reagent.creation_purity = cached_purity + reagent.chemical_flags = reagent.chemical_flags | REAGENT_DONOTSPLIT /** * Occurs when a reation is overheated (i.e. past it's overheatTemp) @@ -187,7 +191,7 @@ * * holder - the datum that holds this reagent, be it a beaker or anything else * * equilibrium - the equilibrium datum that contains the equilibrium reaction properties and methods */ -/datum/chemical_reaction/proc/overheated(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/proc/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) for(var/id in results) var/datum/reagent/reagent = holder.get_reagent(id) if(!reagent) @@ -205,7 +209,7 @@ * * holder - the datum that holds this reagent, be it a beaker or anything else * * equilibrium - the equilibrium datum that contains the equilibrium reaction properties and methods */ -/datum/chemical_reaction/proc/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/proc/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) var/affected_list = results + required_reagents for(var/_reagent in affected_list) var/datum/reagent/reagent = holder.get_reagent(_reagent) @@ -295,7 +299,7 @@ //Spews out the inverse of the chems in the beaker of the products/reactants only -/datum/chemical_reaction/proc/explode_invert_smoke(datum/reagents/holder, datum/equilibrium/equilibrium, clear_products = TRUE, clear_reactants = TRUE) +/datum/chemical_reaction/proc/explode_invert_smoke(datum/reagents/holder, datum/equilibrium/equilibrium, force_range = 0, clear_products = TRUE, clear_reactants = TRUE, accept_impure = TRUE) var/datum/reagents/invert_reagents = new (2100, NO_REACT)//I think the biggest size we can get is 2100? var/datum/effect_system/smoke_spread/chem/smoke = new() var/sum_volume = 0 @@ -307,11 +311,17 @@ invert_reagents.add_reagent(reagent.inverse_chem, reagent.volume, no_react = TRUE) holder.remove_reagent(reagent.type, reagent.volume) continue + else if (reagent.impure_chem && accept_impure) + invert_reagents.add_reagent(reagent.impure_chem, reagent.volume, no_react = TRUE) + holder.remove_reagent(reagent.type, reagent.volume) + continue invert_reagents.add_reagent(reagent.type, reagent.volume, added_purity = reagent.purity, no_react = TRUE) sum_volume += reagent.volume holder.remove_reagent(reagent.type, reagent.volume) + if(!force_range) + force_range = sum_volume/5 if(invert_reagents.reagent_list) - smoke.set_up(invert_reagents, (sum_volume/5), holder.my_atom) + smoke.set_up(invert_reagents, force_range, holder.my_atom) smoke.start() holder.my_atom.audible_message("The [holder.my_atom] suddenly explodes, launching the aerosolized reagents into the air!") if(clear_reactants) @@ -339,56 +349,174 @@ clear_products(holder) //Pushes everything out, and damages mobs with 10 brute damage. -/datum/chemical_reaction/proc/explode_shockwave(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/proc/explode_shockwave(datum/reagents/holder, datum/equilibrium/equilibrium, range = 3, damage = 5, sound_and_text = TRUE, implosion = FALSE) var/turf/this_turf = get_turf(holder.my_atom) - holder.my_atom.audible_message("The [holder.my_atom] suddenly explodes, sending a shockwave rippling through the air!") - playsound(this_turf, 'sound/chemistry/shockwave_explosion.ogg', 80, TRUE) + if(sound_and_text) + holder.my_atom.audible_message("The [holder.my_atom] suddenly explodes, sending a shockwave rippling through the air!") + playsound(this_turf, 'sound/chemistry/shockwave_explosion.ogg', 80, TRUE) //Modified goonvortex - for(var/atom/movable/movey in orange(3, this_turf)) - if(isliving(movey)) + for(var/atom/movable/movey as anything in orange(range, this_turf)) + if(!istype(movey, /atom/movable)) + continue + if(isliving(movey) && damage) var/mob/living/live = movey - live.apply_damage(5)//Since this can be called multiple times + live.apply_damage(damage)//Since this can be called multiple times if(movey.anchored) continue if(iseffect(movey) || iscameramob(movey) || isdead(movey)) continue - var/distance = get_dist(movey, this_turf) - var/moving_power = max(4 - distance, 1)//Make sure we're thrown out of range of the next one - var/atom/throw_target = get_edge_target_turf(movey, get_dir(movey, get_step_away(movey, this_turf))) - movey.throw_at(throw_target, moving_power, 1) + if(implosion) + var/distance = get_dist(movey, this_turf) + var/moving_power = max(4 - distance, 1) + var/turf/target = get_turf(holder.my_atom) + movey.throw_at(target, moving_power, 1) + else + var/distance = get_dist(movey, this_turf) + var/moving_power = max(3 - distance, 1)//Make sure we're thrown out of range of the next one + var/atom/throw_target = get_edge_target_turf(movey, get_dir(movey, get_step_away(movey, this_turf))) + movey.throw_at(throw_target, moving_power, 1) -//Creates a ring of fire in a set range around the beaker location -/datum/chemical_reaction/proc/explode_fire(datum/reagents/holder, datum/equilibrium/equilibrium, range) - explosion(holder.my_atom, 0, 0, 0, 0, flame_range = 3) +////////BEGIN FIRE BASED EXPLOSIONS + +//Calls the default explosion subsystem handiler to explode with fire (random firespots and noise) +/datum/chemical_reaction/proc/explode_fire(datum/reagents/holder, datum/equilibrium/equilibrium, range = 3) + explosion(holder.my_atom, 0, 0, 0, 0, flame_range = range) holder.my_atom.audible_message("The [holder.my_atom] suddenly errupts in flames!") -//Clears the beaker of the reagents only -/datum/chemical_reaction/proc/clear_reactants(datum/reagents/holder, volume = null) - if(!holder) - return FALSE - for(var/datum/reagent/reagent as anything in holder.reagent_list) - if(!(reagent.type in required_reagents)) - continue - if(!volume) - holder.remove_reagent(reagent.type, reagent.volume) +//Creates a ring of fire in a set range around the beaker location +/datum/chemical_reaction/proc/explode_fire_vortex(datum/reagents/holder, datum/equilibrium/equilibrium, x_offset = 1, y_offset = 1, reverse = FALSE, id = "f_vortex", ) + var/increment = reverse ? -1 : 1 + if(isnull(equilibrium.data["[id]_tar"])) + equilibrium.data = list("[id]_x" = x_offset, "[id]_y" = y_offset, "[id]_tar" = "[id]_y")//tar is the current movement direction the cyclone is moving in + if(equilibrium.data["[id]_tar"] == "[id]_x") + if(equilibrium.data["[id]_x"] >= x_offset) + equilibrium.data["[id]_tar"] = "[id]_y" + equilibrium.data["[id]_y"] += increment + else if(equilibrium.data["[id]_x"] <= -x_offset) + equilibrium.data["[id]_tar"] = "[id]_y" + equilibrium.data["[id]_y"] -= increment else - holder.remove_reagent(reagent.type, volume) + if(equilibrium.data["[id]_y"] < 0) + equilibrium.data["[id]_x"] += increment + else if(equilibrium.data["[id]_y"] > 0) + equilibrium.data["[id]_x"] -= increment -//Clears the beaker of the product only -/datum/chemical_reaction/proc/clear_products(datum/reagents/holder, volume = null) - if(!holder) - return FALSE - for(var/datum/reagent/reagent as anything in holder.reagent_list) - if(!(reagent.type in results)) - continue - if(!volume) - holder.remove_reagent(reagent.type, reagent.volume) + else if (equilibrium.data["[id]_tar"] == "[id]_y") + if(equilibrium.data["[id]_y"] >= y_offset) + equilibrium.data["[id]_tar"] = "[id]_x" + equilibrium.data["[id]_x"] -= increment + else if(equilibrium.data["[id]_y"] <= -y_offset) + equilibrium.data["[id]_tar"] = "[id]_x" + equilibrium.data["[id]_x"] += increment else - holder.remove_reagent(reagent.type, volume) + if(equilibrium.data["[id]_x"] < 0) + equilibrium.data["[id]_y"] -= increment + else if(equilibrium.data["[id]_x"] > 0) + equilibrium.data["[id]_y"] += increment + var/turf/holder_turf = get_turf(holder.my_atom) + var/turf/target = locate(holder_turf.x + equilibrium.data["[id]_x"], holder_turf.y + equilibrium.data["[id]_y"], holder_turf.z) + new /obj/effect/hotspot(target) + debug_world("X: [equilibrium.data["[id]_x"]], Y: [equilibrium.data["[id]_x"]]") -//Clears the beaker of ALL reagents inside -/datum/chemical_reaction/proc/clear_reagents(datum/reagents/holder, volume = null) +/* + * Creates a square of fire in a fire_range radius, + * fire_range = 0 will be on the exact spot of the holder, + * fire_range = 1 or more will be additional tiles around the holder. Every tile will be heated this way. + * How clf3 works, you know! + */ +/datum/chemical_reaction/proc/explode_fire_square(datum/reagents/holder, datum/equilibrium/equilibrium, fire_range = 1) + var/turf/location = get_turf(holder.my_atom) + if(fire_range == 0) + new /obj/effect/hotspot(location) + return + for(var/turf/turf as anything in range(fire_range, location)) + new /obj/effect/hotspot(turf) + +///////////END FIRE BASED EXPLOSIONS + +/* +* Freezes in a circle around the holder location +* Arguments: +* * temp - the temperature to set the air to +* * radius - the range of the effect +* * freeze_duration - how long the icey spots remain for +*/ +/datum/chemical_reaction/proc/freeze_radius(datum/reagents/holder, datum/equilibrium/equilibrium, temp, radius = 2, freeze_duration = 50) + for(var/any_turf in circlerangeturfs(center = get_turf(holder.my_atom), radius = radius)) + if(!istype(any_turf, /turf/open)) + continue + var/turf/open/open_turf = any_turf + open_turf.MakeSlippery(TURF_WET_PERMAFROST, 10, freeze_duration, freeze_duration) + open_turf.temperature = temp + +///Clears the beaker of the reagents only +///if volume is not set, it will remove all of the reactant +/datum/chemical_reaction/proc/clear_reactants(datum/reagents/holder, volume = 1000) if(!holder) return FALSE + for(var/reagent in required_reagents) + holder.remove_reagent(reagent, volume) + +///Clears the beaker of the product only +/datum/chemical_reaction/proc/clear_products(datum/reagents/holder, volume = 1000) + if(!holder) + return FALSE + for(var/reagent in results) + holder.remove_reagent(reagent, volume) + + +///Clears the beaker of ALL reagents inside +/datum/chemical_reaction/proc/clear_reagents(datum/reagents/holder, volume = 1000) + if(!holder) + return FALSE + if(!volume) + volume = holder.total_volume holder.remove_all(volume) + +/* +* "Attacks" all mobs within range with a specified reagent +* Will be blocked if they're wearing proper protective equipment unless disabled +* Arguments +* * reagent - the reagent typepath that will be added +* * vol - how much will be added +* * range - the range that this will affect mobs for +* * ignore_mask - if masks block the effect, making this true will affect someone regardless +* * ignore_eyes - if glasses block the effect, making this true will affect someone regardless +*/ +/datum/chemical_reaction/proc/explode_attack_chem(datum/reagents/holder, datum/equilibrium/equilibrium, reagent, vol, range = 3, ignore_mask = FALSE, ignore_eyes = FALSE) + if(istype(reagent, /datum/reagent)) + var/datum/reagent/temp_reagent = reagent + reagent = temp_reagent.type + for(var/mob/living/carbon/target in orange(range, get_turf(holder.my_atom))) + if(target.has_smoke_protection() && !ignore_mask) + continue + if(target.get_eye_protection() && !ignore_eyes) + continue + to_chat(target, "The [holder.my_atom.name] launches some of [holder.p_their()] contents at you!") + target.reagents.add_reagent(reagent, vol) + + +/* +* Applys a cooldown to the reaction +* Returns false if time is below required, true if it's above required +* Time is kept in eqilibrium data +* +* Arguments: +* * seconds - the amount of time in server seconds to delay between true returns, will ceiling to the nearest 0.25 +* * id - a string phrase so that multiple cooldowns can be applied if needed +* * initial_delay - The number of seconds of delay to add on creation +*/ +/datum/chemical_reaction/proc/off_cooldown(datum/reagents/holder, datum/equilibrium/equilibrium, seconds = 1, id = "default", initial_delay = 0) + id = id+"_cooldown" + if(isnull(equilibrium.data[id])) + equilibrium.data[id] = 0 + if(initial_delay) + equilibrium.data[id] += initial_delay + return FALSE + return TRUE//first time we know we can go + equilibrium.data[id] += equilibrium.time_deficit ? 0.5 : 0.25 //sync to lag compensator + if(equilibrium.data[id] >= seconds) + equilibrium.data[id] = 0 + return TRUE + return FALSE diff --git a/code/modules/reagents/chemistry/recipes/cat2_medicines.dm b/code/modules/reagents/chemistry/recipes/cat2_medicines.dm index 2072f1295ed..3c06badea28 100644 --- a/code/modules/reagents/chemistry/recipes/cat2_medicines.dm +++ b/code/modules/reagents/chemistry/recipes/cat2_medicines.dm @@ -1,79 +1,369 @@ /*****BRUTE*****/ +//oops no theme - standard reactions with no whistles /datum/chemical_reaction/medicine/helbital results = list(/datum/reagent/medicine/c2/helbital = 3) required_reagents = list(/datum/reagent/consumable/sugar = 1, /datum/reagent/fluorine = 1, /datum/reagent/carbon = 1) mix_message = "The mixture turns into a thick, yellow powder." + //FermiChem vars: + required_temp = 250 + optimal_temp = 1000 + overheat_temp = 550 + optimal_ph_min = 5 + optimal_ph_max = 9.5 + determin_ph_range = 4 + temp_exponent_factor = 1 + ph_exponent_factor = 4 + thermic_constant = 100 + H_ion_release = 4 + rate_up_lim = 55 + purity_min = 0.55 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE +/datum/chemical_reaction/medicine/helbital/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + explode_fire_vortex(holder, equilibrium, 1, 1, "impure") + holder.chem_temp += 2.5 + var/datum/reagent/helbital = holder.get_reagent(/datum/reagent/medicine/c2/helbital) + if(!helbital) + return + if(helbital.purity <= 0.25) + if(prob(25)) + new /obj/effect/hotspot(holder.my_atom.loc) + holder.remove_reagent(/datum/reagent/medicine/c2/helbital, 2) + holder.chem_temp += 5 + holder.my_atom.audible_message("[icon2html(holder.my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] The impurity of the reacting helbital is too great causing the [src] to let out a hearty burst of flame, evaporating part of the product!") + +/datum/chemical_reaction/medicine/helbital/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..()//drains product + explode_fire_vortex(holder, equilibrium, 2, 2, "overheat", TRUE) + +/datum/chemical_reaction/medicine/helbital/reaction_finish(datum/reagents/holder, react_vol) + . = ..() + var/datum/reagent/helbital = holder.get_reagent(/datum/reagent/medicine/c2/helbital) + if(!helbital) + return + if(helbital.purity <= 0.1) //So people don't ezmode this by keeping it at min + explode_fire(holder, null, 3) + clear_products(holder) + /datum/chemical_reaction/medicine/libital results = list(/datum/reagent/medicine/c2/libital = 3) required_reagents = list(/datum/reagent/phenol = 1, /datum/reagent/oxygen = 1, /datum/reagent/nitrogen = 1) + required_temp = 225 + optimal_temp = 700 + overheat_temp = 840 + optimal_ph_min = 6 + optimal_ph_max = 10 + determin_ph_range = 4 + temp_exponent_factor = 1.75 + ph_exponent_factor = 1 + thermic_constant = 75 + H_ion_release = -6.5 + rate_up_lim = 40 + purity_min = 0.2 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE /datum/chemical_reaction/medicine/probital results = list(/datum/reagent/medicine/c2/probital = 4) required_reagents = list(/datum/reagent/copper = 1, /datum/reagent/acetone = 2, /datum/reagent/phosphorus = 1) + required_temp = 225 + optimal_temp = 700 + overheat_temp = 750 + optimal_ph_min = 4.5 + optimal_ph_max = 12 + determin_ph_range = 2 + temp_exponent_factor = 0.75 + ph_exponent_factor = 4 + thermic_constant = 50 + H_ion_release = -2.5 + rate_up_lim = 30 + purity_min = 0.35//15% window + reaction_flags = REACTION_CLEAR_INVERSE | REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE /*****BURN*****/ +//These are all endothermic! +//This is a relatively simple demonstration have splitting negatives/having purity based negatives +//Since it requires silver - I don't want to make it too hard /datum/chemical_reaction/medicine/lenturi results = list(/datum/reagent/medicine/c2/lenturi = 5) required_reagents = list(/datum/reagent/ammonia = 1, /datum/reagent/silver = 1, /datum/reagent/sulfur = 1, /datum/reagent/oxygen = 1, /datum/reagent/chlorine = 1) + required_temp = 200 + optimal_temp = 300 + overheat_temp = 500 + optimal_ph_min = 6 + optimal_ph_max = 11 + determin_ph_range = 6 + temp_exponent_factor = 1 + ph_exponent_factor = 2 + thermic_constant = -175 //Though, it is a test in endothermicity + H_ion_release = -2.5 + rate_up_lim = 30 + purity_min = 0.25 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN /datum/chemical_reaction/medicine/aiuri results = list(/datum/reagent/medicine/c2/aiuri = 4) required_reagents = list(/datum/reagent/ammonia = 1, /datum/reagent/toxin/acid = 1, /datum/reagent/hydrogen = 2) + required_temp = 50 + optimal_temp = 300 + overheat_temp = 315 + optimal_ph_min = 4.8 + optimal_ph_max = 9 + determin_ph_range = 3 + temp_exponent_factor = 5 + ph_exponent_factor = 2 + thermic_constant = -400 + H_ion_release = 3 + rate_up_lim = 35 + purity_min = 0.25 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN +/datum/chemical_reaction/medicine/aiuri/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..() + for(var/mob/living/living_mob in orange(3, get_turf(holder.my_atom))) + if(living_mob.flash_act(1, length = 5)) + living_mob.set_blurriness(10) + holder.my_atom.audible_message("[icon2html(holder.my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] The [holder.my_atom] lets out a loud bang!") + playsound(holder.my_atom, 'sound/effects/explosion1.ogg', 50, 1) + /datum/chemical_reaction/medicine/hercuri results = list(/datum/reagent/medicine/c2/hercuri = 5) required_reagents = list(/datum/reagent/cryostylane = 3, /datum/reagent/bromine = 1, /datum/reagent/lye = 1) - reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN - required_temp = 47 is_cold_recipe = TRUE + required_temp = 47 optimal_temp = 10 overheat_temp = 5 - thermic_constant = -50 + optimal_ph_min = 6 + optimal_ph_max = 10 + determin_ph_range = 1 + temp_exponent_factor = 3 + thermic_constant = -40 + H_ion_release = 3.7 + rate_up_lim = 50 + purity_min = 0.15 + reaction_flags = REACTION_PH_VOL_CONSTANT | REACTION_CLEAR_INVERSE + reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN + +/datum/chemical_reaction/medicine/hercuri/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + if(off_cooldown(holder, equilibrium, 2, "hercuri_freeze")) + return + playsound(holder.my_atom, 'sound/magic/ethereal_exit.ogg', 50, 1) + holder.my_atom.visible_message("The reaction frosts over, releasing it's chilly contents!") + var/radius = max((equilibrium.step_target_vol/50), 1) + freeze_radius(holder, equilibrium, 200, radius, 50) //drying agent exists + explode_shockwave(holder, equilibrium, sound_and_text = FALSE) /*****OXY*****/ +//These react faster with optional oxygen, and have blastback effects! (the oxygen makes their fail states deadlier) /datum/chemical_reaction/medicine/convermol results = list(/datum/reagent/medicine/c2/convermol = 3) required_reagents = list(/datum/reagent/hydrogen = 1, /datum/reagent/fluorine = 1, /datum/reagent/fuel/oil = 1) required_temp = 370 mix_message = "The mixture rapidly turns into a dense pink liquid." + optimal_temp = 420 + overheat_temp = 570 //Ash will be created before this - so it's pretty rare that overheat is actually triggered + optimal_ph_min = 3.045 //Rigged to blow once without oxygen + optimal_ph_max = 8.5 + determin_ph_range = 2 + temp_exponent_factor = 0.75 + ph_exponent_factor = 1.25 + thermic_constant = 15 + H_ion_release = -1 + rate_up_lim = 50 + purity_min = 0.4 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_OXY +/datum/chemical_reaction/medicine/convermol/reaction_step(datum/equilibrium/reaction, datum/reagents/holder, delta_t, delta_ph, step_reaction_vol) + . = ..() + var/datum/reagent/oxy = holder.has_reagent(/datum/reagent/oxygen) + if(oxy) + holder.remove_reagent(/datum/reagent/oxygen, 0.25) + else + reaction.delta_t = delta_t/10 //slow without oxygen + +/datum/chemical_reaction/medicine/convermol/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, impure = FALSE) + var/range = impure ? 4 : 3 + if(holder.has_reagent(/datum/reagent/oxygen)) + explode_shockwave(holder, equilibrium, range) //damage 5 + else + explode_shockwave(holder, equilibrium, range, damage = 2) + +/datum/chemical_reaction/medicine/convermol/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..() + overheated(holder, equilibrium, impure = TRUE) + clear_reactants(holder, vol_added*2) + + /datum/chemical_reaction/medicine/tirimol results = list(/datum/reagent/medicine/c2/tirimol = 5) required_reagents = list(/datum/reagent/nitrogen = 3, /datum/reagent/acetone = 2) required_catalysts = list(/datum/reagent/toxin/acid = 1) + mix_message = "The mixture turns into a tired reddish pink liquid." + optimal_temp = 1 + optimal_temp = 900 + overheat_temp = 720 + optimal_ph_min = 2 + optimal_ph_max = 7.1 + determin_ph_range = 2 + temp_exponent_factor = 4 + ph_exponent_factor = 1.8 + thermic_constant = -20 + H_ion_release = 3 + rate_up_lim = 50 + purity_min = 0.2 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_OXY +/datum/chemical_reaction/medicine/tirimol/reaction_step(datum/equilibrium/reaction, datum/reagents/holder, delta_t, delta_ph, step_reaction_vol) + . = ..() + var/datum/reagent/oxy = holder.has_reagent(/datum/reagent/oxygen) + if(oxy) + holder.remove_reagent(/datum/reagent/oxygen, 0.25) + else + holder.adjust_all_reagents_ph(-0.05*step_reaction_vol)//pH drifts faster + +//Sleepytime for chem +/datum/chemical_reaction/medicine/tirimol/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, impure = FALSE) + var/bonus = impure ? 2 : 1 + if(holder.has_reagent(/datum/reagent/oxygen)) + explode_attack_chem(holder, equilibrium, /datum/reagent/inverse/healing/tirimol, 7.5*bonus, 2, ignore_eyes = TRUE) //since we're smoke/air based + clear_products(holder, 5)//since we attacked + explode_invert_smoke(holder, equilibrium, 3) + else + explode_invert_smoke(holder, equilibrium, 3) + +/datum/chemical_reaction/medicine/tirimol/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..() + overheated(holder, equilibrium, TRUE) + clear_reactants(holder, 2) + /*****TOX*****/ +//These all care about purity in their reactions /datum/chemical_reaction/medicine/seiver results = list(/datum/reagent/medicine/c2/seiver = 3) required_reagents = list(/datum/reagent/nitrogen = 1, /datum/reagent/potassium = 1, /datum/reagent/aluminium = 1) + mix_message = "The mixture gives out a goopy slorp." + is_cold_recipe = TRUE + required_temp = 320 + optimal_temp = 280 + overheat_temp = NO_OVERHEAT + optimal_ph_min = 5 + optimal_ph_max = 8 + determin_ph_range = 6 + temp_exponent_factor = 1 + ph_exponent_factor = 0.5 + thermic_constant = -500 + H_ion_release = -6 + rate_up_lim = 15 + purity_min = 0.2 + reaction_flags = REACTION_PH_VOL_CONSTANT | REACTION_CLEAR_INVERSE reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_TOXIN +/datum/chemical_reaction/medicine/seiver/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + if(off_cooldown(holder, equilibrium, 1, "seiver_rads")) + return + var/modifier = max((100 - holder.chem_temp)*0.025, 0)*vol_added //0 - 5 * volume based off temperature(colder is more) + radiation_pulse(holder.my_atom, modifier, 0.5, can_contaminate=FALSE) //Please advise on this, I don't have a good handle on the numbers + /datum/chemical_reaction/medicine/multiver results = list(/datum/reagent/medicine/c2/multiver = 2) required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/consumable/salt = 1) mix_message = "The mixture yields a fine black powder." required_temp = 380 + optimal_temp = 400 + overheat_temp = 410 + optimal_ph_min = 2.5 + optimal_ph_max = 7 + determin_ph_range = 4 + temp_exponent_factor = 0.5 + ph_exponent_factor = 1 + thermic_constant = 0 + H_ion_release = 0 + rate_up_lim = 25 + purity_min = 0.1 //Fire is our worry for now + reaction_flags = REACTION_REAL_TIME_SPLIT | REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_PLANT | REACTION_TAG_TOXIN +//You get nothing! I'm serious about staying under the heating requirements! +/datum/chemical_reaction/medicine/multiver/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..() + var/datum/reagent/monover = holder.has_reagent(/datum/reagent/inverse/healing/monover) + if(monover) + holder.remove_reagent(/datum/reagent/inverse/healing/monover, monover.volume) + holder.my_atom.audible_message("[icon2html(holder.my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] The Monover bursts into flames from the heat!") + explode_fire_square(holder, equilibrium, 1) + holder.my_atom.fire_act(holder.chem_temp, monover.volume)//I'm kinda banking on this setting the thing on fire. If you see this, then it didn't! + +/datum/chemical_reaction/medicine/multiver/reaction_step(datum/equilibrium/reaction, datum/reagents/holder, delta_t, delta_ph, step_reaction_vol) + . = ..() + if(delta_ph < 0.35) + //normalise delta_ph + var/norm_d_ph = 1-(delta_ph/0.35) + holder.chem_temp += norm_d_ph*12 //0 - 48 per second) + if(delta_ph < 0.1) + holder.my_atom.visible_message("[icon2html(holder.my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] The Monover begins to glow!") + /datum/chemical_reaction/medicine/syriniver results = list(/datum/reagent/medicine/c2/syriniver = 5) required_reagents = list(/datum/reagent/sulfur = 1, /datum/reagent/fluorine = 1, /datum/reagent/toxin = 1, /datum/reagent/nitrous_oxide = 2) + required_temp = 250 + optimal_temp = 310 + overheat_temp = NO_OVERHEAT + optimal_ph_min = 6.5 + optimal_ph_max = 9 + determin_ph_range = 6 + temp_exponent_factor = 2 + ph_exponent_factor = 0.5 + thermic_constant = -20 + H_ion_release = -5.5 + rate_up_lim = 20 //affected by pH too + purity_min = 0.3 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_TOXIN +/datum/chemical_reaction/medicine/syriniver/reaction_step(datum/equilibrium/reaction, datum/reagents/holder, delta_t, delta_ph, step_reaction_vol) + . = ..() + reaction.delta_t = delta_t * delta_ph + /datum/chemical_reaction/medicine/penthrite results = list(/datum/reagent/medicine/c2/penthrite = 3) required_reagents = list(/datum/reagent/pentaerythritol = 1, /datum/reagent/acetone = 1, /datum/reagent/toxin/acid/nitracid = 1 , /datum/reagent/wittel = 1) + required_temp = 255 + optimal_temp = 350 + overheat_temp = 450 + optimal_ph_min = 5 + optimal_ph_max = 9 + determin_ph_range = 3 + temp_exponent_factor = 1 + ph_exponent_factor = 1 + thermic_constant = 150 + H_ion_release = -0.5 + rate_up_lim = 15 + purity_min = 0.55 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_TOXIN + +//overheat beats like a heart! (or is it overbeat?) +/datum/chemical_reaction/medicine/penthrite/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + . = ..() + if(off_cooldown(holder, equilibrium, 1, "lub")) + explode_shockwave(holder, equilibrium, 3, 2) + playsound(holder.my_atom, 'sound/health/slowbeat.ogg', 50, 1) // this is 2 mintues long (!) cut it up! + if(off_cooldown(holder, equilibrium, 1, "dub", 0.5)) + explode_shockwave(holder, equilibrium, 3, 2, implosion = TRUE) + playsound(holder.my_atom, 'sound/health/slowbeat.ogg', 50, 1) + explode_fire_vortex(holder, equilibrium, 1, 1) + +//enabling hardmode +/datum/chemical_reaction/medicine/penthrite/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) + holder.chem_temp += 15 diff --git a/code/modules/reagents/chemistry/recipes/catalysts.dm b/code/modules/reagents/chemistry/recipes/catalysts.dm index 964971e66fa..c6e7e64a235 100644 --- a/code/modules/reagents/chemistry/recipes/catalysts.dm +++ b/code/modules/reagents/chemistry/recipes/catalysts.dm @@ -9,7 +9,7 @@ reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_UNIQUE | REACTION_TAG_CHEMICAL required_temp = 320 optimal_temp = 600 - overheat_temp = 800 + overheat_temp = 800 optimal_ph_min = 5 optimal_ph_max = 6 determin_ph_range = 5 @@ -20,8 +20,8 @@ rate_up_lim = 1 purity_min = 0 -/datum/chemical_reaction/medical_speed_catalyst/overheated(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/medical_speed_catalyst/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) explode_invert_smoke(holder, equilibrium) //Will be better when the inputs have proper invert chems -/datum/chemical_reaction/medical_speed_catalyst/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/medical_speed_catalyst/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) explode_invert_smoke(holder, equilibrium) diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index 46de29b8265..4bc7042b69e 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -22,19 +22,19 @@ required_reagents = list(/datum/reagent/medicine/ephedrine = 1, /datum/reagent/iodine = 1, /datum/reagent/phosphorus = 1, /datum/reagent/hydrogen = 1) required_temp = 372 optimal_temp = 376//Wow this is tight - overheat_temp = 380 + overheat_temp = 380 optimal_ph_min = 6.5 optimal_ph_max = 7.5 determin_ph_range = 5 temp_exponent_factor = 1 ph_exponent_factor = 1.4 thermic_constant = 0.1 //exothermic nature is equal to impurty - H_ion_release = -0.025 + H_ion_release = -0.025 rate_up_lim = 12.5 purity_min = 0.5 //100u will natrually just dip under this w/ no buffer reaction_flags = REACTION_HEAT_ARBITARY //Heating up is arbitary because of submechanics of this reaction. reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DRUG | REACTION_TAG_DANGEROUS - + //The less pure it is, the faster it heats up. tg please don't hate me for making your meth even more dangerous /datum/chemical_reaction/methamphetamine/reaction_step(datum/equilibrium/reaction, datum/reagents/holder, delta_t, delta_ph, step_reaction_vol) var/datum/reagent/meth = holder.get_reagent(/datum/reagent/drug/methamphetamine) @@ -43,11 +43,11 @@ return reaction.thermic_mod = (1-meth.purity)*5 -/datum/chemical_reaction/methamphetamine/overheated(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/methamphetamine/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) . = ..() temp_meth_explosion(holder, equilibrium.reacted_vol) -/datum/chemical_reaction/methamphetamine/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/methamphetamine/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) . = ..() temp_meth_explosion(holder, equilibrium.reacted_vol) diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index ae55d8dca86..6a813122a07 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -60,6 +60,19 @@ /datum/chemical_reaction/medicine/synthflesh results = list(/datum/reagent/medicine/c2/synthflesh = 3) required_reagents = list(/datum/reagent/blood = 1, /datum/reagent/carbon = 1, /datum/reagent/medicine/c2/libital = 1) + required_temp = 250 + optimal_temp = 310 + overheat_temp = 325 + optimal_ph_min = 5.5 + optimal_ph_max = 9.5 + determin_ph_range = 3 + temp_exponent_factor = 1 + ph_exponent_factor = 2 + thermic_constant = 10 + H_ion_release = -3.5 + rate_up_lim = 20 //affected by pH too + purity_min = 0.3 + reaction_flags = REACTION_PH_VOL_CONSTANT reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE | REACTION_TAG_BURN /datum/chemical_reaction/medicine/calomel diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index e2c3555aea0..0692983069f 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -151,7 +151,7 @@ rate_up_lim = 25 //Give a chance to pull back reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL -/datum/chemical_reaction/nitrous_oxide/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/nitrous_oxide/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) . = ..() var/turf/exposed_turf = get_turf(holder.my_atom) if(!exposed_turf) @@ -159,7 +159,7 @@ exposed_turf.atmos_spawn_air("n2o=[equilibrium.step_target_vol/2];TEMP=[holder.chem_temp]") clear_products(holder, equilibrium.step_target_vol) -/datum/chemical_reaction/nitrous_oxide/overheated(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/nitrous_oxide/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) return //This is empty because the explosion reaction will occur instead (see pyrotechnics.dm). This is just here to update the lookup ui. @@ -738,7 +738,7 @@ required_reagents = list(/datum/reagent/consumable/ice = 1) required_temp = 275 optimal_temp = 350 - overheat_temp = 99999 + overheat_temp = NO_OVERHEAT optimal_ph_min = 0 optimal_ph_max = 14 thermic_constant = 0 @@ -754,7 +754,7 @@ required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/consumable/ethanol = 1, /datum/reagent/iodine = 1) required_temp = 274 optimal_temp = 350 - overheat_temp = 99999 + overheat_temp = NO_OVERHEAT optimal_ph_min = 0 optimal_ph_max = 14 thermic_constant = 0 diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index 5a921f8dd86..392b09c06c2 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -466,7 +466,7 @@ required_reagents = list(/datum/reagent/stable_plasma = 1, /datum/reagent/uranium/radium = 1, /datum/reagent/phosphorus = 1) required_temp = 0 optimal_temp = 20 - overheat_temp = 9999//Replace with NO_OVERHEAT when part 2 is in + overheat_temp = NO_OVERHEAT temp_exponent_factor = 10 thermic_constant = 0 reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE diff --git a/code/modules/reagents/chemistry/recipes/reaction_agents.dm b/code/modules/reagents/chemistry/recipes/reaction_agents.dm index 833ad2cc52a..f99f891647e 100644 --- a/code/modules/reagents/chemistry/recipes/reaction_agents.dm +++ b/code/modules/reagents/chemistry/recipes/reaction_agents.dm @@ -86,13 +86,13 @@ holder.remove_reagent(/datum/reagent/bluespace, 1) reaction.delta_t *= 5 -/datum/chemical_reaction/prefactor_b/overheated(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/prefactor_b/overheated(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) . = ..() explode_shockwave(holder, equilibrium) var/vol = max(20, holder.total_volume/5) //Not letting you have more than 5 clear_reagents(holder, vol)//Lest we explode forever -/datum/chemical_reaction/prefactor_b/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium) +/datum/chemical_reaction/prefactor_b/overly_impure(datum/reagents/holder, datum/equilibrium/equilibrium, vol_added) explode_fire(holder, equilibrium) var/vol = max(20, holder.total_volume/5) //Not letting you have more than 5 clear_reagents(holder, vol) diff --git a/code/modules/reagents/withdrawal/_addiction.dm b/code/modules/reagents/withdrawal/_addiction.dm index e1a64fb12b3..1a45cc1ca4c 100644 --- a/code/modules/reagents/withdrawal/_addiction.dm +++ b/code/modules/reagents/withdrawal/_addiction.dm @@ -113,7 +113,7 @@ /// Called when addiction is in stage 2 every process /datum/addiction/proc/withdrawal_stage_2_process(mob/living/carbon/affected_carbon, delta_time) - if(DT_PROB(10, delta_time)) + if(DT_PROB(10, delta_time) ) to_chat(affected_carbon, "[withdrawal_stage_messages[2]]") diff --git a/code/modules/reagents/withdrawal/generic_addictions.dm b/code/modules/reagents/withdrawal/generic_addictions.dm index b163514e0d8..649b13963cb 100644 --- a/code/modules/reagents/withdrawal/generic_addictions.dm +++ b/code/modules/reagents/withdrawal/generic_addictions.dm @@ -154,3 +154,77 @@ REMOVE_TRAIT(affected_human, TRAIT_NIGHT_VISION, type) var/obj/item/organ/eyes/eyes = affected_human.getorgan(/obj/item/organ/eyes) eyes.refresh() + +///Makes you a hypochondriac - I'd like to call it hypochondria, but "I could use some hypochondria" doesn't work +/datum/addiction/medicine + name = "medicine" + withdrawal_stage_messages = list("", "", "") + var/datum/hallucination/fake_alert/hallucination + var/datum/hallucination/fake_health_doll/hallucination2 + +/datum/addiction/medicine/withdrawal_enters_stage_1(mob/living/carbon/affected_carbon) + . = ..() + if(!ishuman(affected_carbon)) + return + var/mob/living/carbon/human/human_mob = affected_carbon + hallucination2 = new(human_mob, TRUE, severity = 1, duration = 120 MINUTES) + +/datum/addiction/medicine/withdrawal_stage_1_process(mob/living/carbon/affected_carbon, delta_time) + . = ..() + if(DT_PROB(10, delta_time)) + affected_carbon.emote("cough") + +/datum/addiction/medicine/withdrawal_enters_stage_2(mob/living/carbon/affected_carbon) + . = ..() + var/list/possibilities = list() + if(!HAS_TRAIT(affected_carbon, TRAIT_RESISTHEAT)) + possibilities += "temphot" + if(!HAS_TRAIT(affected_carbon, TRAIT_RESISTCOLD)) + possibilities += "tempcold" + var/obj/item/organ/lungs/lungs = affected_carbon.getorganslot(ORGAN_SLOT_LUNGS) + if(lungs) + if(lungs.safe_oxygen_min) + possibilities += "not_enough_oxy" + if(lungs.safe_oxygen_max) + possibilities += "too_much_oxy" + var/type = pick(possibilities) + hallucination = new(affected_carbon, TRUE, type, 120 MINUTES)//last for a while basically + +/datum/addiction/medicine/withdrawal_stage_2_process(mob/living/carbon/affected_carbon, delta_time) + . = ..() + if(DT_PROB(10, delta_time)) + hallucination2.add_fake_limb(severity = 1) + return + if(DT_PROB(5, delta_time)) + hallucination2.increment_fake_damage() + +/datum/addiction/medicine/withdrawal_enters_stage_3(mob/living/carbon/affected_carbon) + . = ..() + affected_carbon.hal_screwyhud = SCREWYHUD_CRIT + +/datum/addiction/medicine/withdrawal_stage_3_process(mob/living/carbon/affected_carbon, delta_time) + . = ..() + if(DT_PROB(5, delta_time)) + hallucination2.increment_fake_damage() + return + if(DT_PROB(15, delta_time)) + affected_carbon.emote("cough") + return + if(DT_PROB(65, delta_time)) + return + var/obj/item/organ/organ = pick(affected_carbon.internal_organs) + if(organ.low_threshold) + to_chat(affected_carbon, organ.low_threshold_passed) + return + else if (organ.high_threshold_passed) + to_chat(affected_carbon, organ.high_threshold_passed) + return + to_chat(affected_carbon, "You feel a dull pain in your [organ.name].") + +/datum/addiction/medicine/lose_addiction(datum/mind/victim_mind) + . = ..() + if(iscarbon(victim_mind.current)) + var/mob/living/carbon/affected_carbon = victim_mind.current + affected_carbon.hal_screwyhud = SCREWYHUD_NONE + hallucination.cleanup() + QDEL_NULL(hallucination2) diff --git a/code/modules/unit_tests/metabolizing.dm b/code/modules/unit_tests/metabolizing.dm index 05091b7efa3..7e7263503e0 100644 --- a/code/modules/unit_tests/metabolizing.dm +++ b/code/modules/unit_tests/metabolizing.dm @@ -5,6 +5,8 @@ var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human) for (var/reagent_type in subtypesof(/datum/reagent)) + if(reagent_type in GLOB.fake_reagent_blacklist) + continue test_reagent(human, reagent_type) /datum/unit_test/metabolization/proc/test_reagent(mob/living/carbon/C, reagent_type) diff --git a/sound/chemistry/ahaha.ogg b/sound/chemistry/ahaha.ogg new file mode 100644 index 00000000000..0fedd32155a Binary files /dev/null and b/sound/chemistry/ahaha.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 6878aeb4026..c1a234ec613 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3015,6 +3015,7 @@ #include "code\modules\reagents\chemistry\reagents\pyrotechnic_reagents.dm" #include "code\modules\reagents\chemistry\reagents\reaction_agents_reagents.dm" #include "code\modules\reagents\chemistry\reagents\toxin_reagents.dm" +#include "code\modules\reagents\chemistry\reagents\impure_reagents\impure_medicine_reagents.dm" #include "code\modules\reagents\chemistry\recipes\cat2_medicines.dm" #include "code\modules\reagents\chemistry\recipes\catalysts.dm" #include "code\modules\reagents\chemistry\recipes\drugs.dm" diff --git a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js index 23786c9f4e3..e5b34f9913a 100644 --- a/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js +++ b/tgui/packages/tgui/interfaces/ChemDebugSynthesizer.js @@ -6,6 +6,7 @@ export const ChemDebugSynthesizer = (props, context) => { const { act, data } = useBackend(context); const { amount, + purity, beakerCurrentVolume, beakerMaxVolume, isBeakerLoaded, @@ -34,6 +35,16 @@ export const ChemDebugSynthesizer = (props, context) => { onChange={(e, value) => act('amount', { amount: value, })} /> + act('purity', { + amount: value, + })} />