#define REM REAGENTS_EFFECT_MULTIPLIER GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) /proc/build_name2reagent() . = list() for (var/t in subtypesof(/datum/reagent)) var/datum/reagent/R = t if (length(initial(R.name))) .[ckey(initial(R.name))] = t //Various reagents //Toxin & acid reagents //Hydroponics stuff /datum/reagent var/name = "Reagent" var/description = "" var/specific_heat = SPECIFIC_HEAT_DEFAULT //J/(K*mol) var/taste_description = "metaphorical salt" var/taste_mult = 1 //how this taste compares to others. Higher values means it is more noticable var/glass_name = "glass of ...what?" // use for specialty drinks. var/glass_desc = "You can't really tell what this is." var/glass_icon_state = null // Otherwise just sets the icon to a normal glass with the mixture of the reagents in the glass. var/shot_glass_icon_state = null var/datum/reagents/holder = null var/reagent_state = LIQUID var/list/data var/current_cycle = 0 var/volume = 0 //pretend this is moles var/color = "#000000" // rgb: 0, 0, 0 var/can_synth = TRUE // can this reagent be synthesized? (for example: odysseus syringe gun) var/metabolization_rate = REAGENTS_METABOLISM //how fast the reagent is metabolized by the mob var/overrides_metab = 0 var/overdose_threshold = 0 var/addiction_threshold = 0 var/addiction_stage = 0 var/addiction_stage1_end = 10 var/addiction_stage2_end = 20 var/addiction_stage3_end = 30 var/addiction_stage4_end = 40 var/overdosed = 0 // You fucked up and this is now triggering its overdose effects, purge that shit quick. var/self_consuming = FALSE //I think this uhhh, makes weird stuff happen when metabolising, but... doesn't seem to do what I think, so I'm gonna leave it. //Fermichem vars: var/purity = 1 //How pure a chemical is from 0 - 1. var/cached_purity = 1 var/turf/loc = null //Should be the creation location! var/pH = 7 //pH of the specific reagent, used for calculating the sum pH of a holder. //var/SplitChem = FALSE //If the chem splits on metabolism var/impure_chem // What chemical is metabolised with an inpure reaction var/inverse_chem_val = 0 // If the impurity is below 0.5, replace ALL of the chem with inverse_chemupon metabolising var/inverse_chem // What chem is metabolised when purity is below inverse_chem_val, this shouldn't be made, but if it does, well, I guess I'll know about it. var/metabolizing = FALSE var/chemical_flags // See fermi/readme.dm REAGENT_DEAD_PROCESS, REAGENT_DONOTSPLIT, REAGENT_ONLYINVERSE, REAGENT_ONMOBMERGE, REAGENT_INVISIBLE, REAGENT_FORCEONNEW, REAGENT_SNEAKYNAME var/value = REAGENT_VALUE_NONE //How much does it sell for in cargo? var/datum/material/material //are we made of material? /datum/reagent/New() . = ..() if(material) material = SSmaterials.GetMaterialRef(material) /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references . = ..() holder = null /datum/reagent/proc/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1, touch_protection = 0) if(!istype(M)) return 0 if(method == VAPOR) //smoke, foam, spray if(M.reagents) var/modifier = clamp((1 - touch_protection), 0, 1) var/amount = round(reac_volume*modifier, 0.1) if(amount >= 0.5) M.reagents.add_reagent(type, amount) return 1 /datum/reagent/proc/reaction_obj(obj/O, volume) return /datum/reagent/proc/reaction_turf(turf/T, volume) return /datum/reagent/proc/on_mob_life(mob/living/carbon/M) current_cycle++ if(holder) holder.remove_reagent(type, metabolization_rate * M.metabolism_efficiency) //By default it slowly disappears. //called when a mob processes chems when dead. /datum/reagent/proc/on_mob_dead(mob/living/carbon/M) if(!(chemical_flags & REAGENT_DEAD_PROCESS)) //justincase return current_cycle++ if(holder) holder.remove_reagent(type, metabolization_rate * M.metabolism_efficiency) //By default it slowly disappears. // Called when this reagent is first added to a mob /datum/reagent/proc/on_mob_add(mob/living/L, amount) if(!iscarbon(L)) return var/mob/living/carbon/M = L var/turf/T = get_turf(M) log_reagent("MOB ADD: on_mob_add(): [key_name(M)] at [AREACOORD(T)] - [volume] of [type] with [purity] purity") if (purity == 1) return if(cached_purity == 1) cached_purity = purity else if(purity < 0) CRASH("Purity below 0 for chem: [type], Please let Fermis Know!") if(chemical_flags & REAGENT_DONOTSPLIT) return if ((inverse_chem_val > purity) && (inverse_chem))//Turns all of a added reagent into the inverse chem M.reagents.remove_reagent(type, amount, FALSE) M.reagents.add_reagent(inverse_chem, amount, FALSE, other_purity = 1-cached_purity) var/datum/reagent/R = M.reagents.has_reagent(inverse_chem) if(R.chemical_flags & REAGENT_SNEAKYNAME) R.name = name//Negative effects are hidden if(R.chemical_flags & REAGENT_INVISIBLE) R.chemical_flags |= (REAGENT_INVISIBLE) log_reagent("MOB ADD: on_mob_add() (impure): merged [volume] of [inverse_chem]") else if (impure_chem) var/impureVol = amount * (1 - purity) //turns impure ratio into impure chem if(!(chemical_flags & REAGENT_SPLITRETAINVOL)) M.reagents.remove_reagent(type, (impureVol), FALSE) M.reagents.add_reagent(impure_chem, impureVol, FALSE, other_purity = 1-cached_purity) log_reagent("MOB ADD: on_mob_add() (mixed purity): merged [volume - impureVol] of [type] and [volume] of [impure_chem]") // Called when this reagent is removed while inside a mob /datum/reagent/proc/on_mob_delete(mob/living/L) var/turf/T = get_turf(L) log_reagent("MOB DELETE: on_mob_delete: [key_name(L)] at [AREACOORD(T)] - [type]") // Called when this reagent first starts being metabolized by a liver /datum/reagent/proc/on_mob_metabolize(mob/living/L) return // Called when this reagent stops being metabolized by a liver /datum/reagent/proc/on_mob_end_metabolize(mob/living/L) return /datum/reagent/proc/on_move(mob/M) return // Called after add_reagents creates a new reagent. /datum/reagent/proc/on_new(data) return // Called when two reagents of the same are mixing. /datum/reagent/proc/on_merge(data, amount, mob/living/carbon/M, purity) if(!iscarbon(M)) return var/turf/T = get_turf(M) log_reagent("MOB ADD: on_merge(): [key_name(M)] at [AREACOORD(T)] - [volume] of [type] with [purity] purity") if (purity == 1) return cached_purity = purity //purity SHOULD be precalculated from the add_reagent, update cache. if (purity < 0) CRASH("Purity below 0 for chem: [type], Please let Fermis Know!") if(chemical_flags & REAGENT_DONOTSPLIT) return if ((inverse_chem_val > purity) && (inverse_chem)) //INVERT M.reagents.remove_reagent(type, amount, FALSE) M.reagents.add_reagent(inverse_chem, amount, FALSE, other_purity = 1-cached_purity) var/datum/reagent/R = M.reagents.has_reagent(inverse_chem) if(R.chemical_flags & REAGENT_SNEAKYNAME) R.name = name//Negative effects are hidden if(R.chemical_flags & REAGENT_INVISIBLE) R.chemical_flags |= (REAGENT_INVISIBLE) log_reagent("MOB ADD: on_merge() (impure): merged [volume] of [inverse_chem]") else if (impure_chem) //SPLIT var/impureVol = amount * (1 - purity) if(!(chemical_flags & REAGENT_SPLITRETAINVOL)) M.reagents.remove_reagent(type, impureVol, FALSE) M.reagents.add_reagent(impure_chem, impureVol, FALSE, other_purity = 1-cached_purity) log_reagent("MOB ADD: on_merge() (mixed purity): merged [volume - impureVol] of [type] and [volume] of [impure_chem]") /datum/reagent/proc/on_update(atom/A) return // Called when the reagent container is hit by an explosion /datum/reagent/proc/on_ex_act(severity) return // Called if the reagent has passed the overdose threshold and is set to be triggering overdose effects /datum/reagent/proc/overdose_process(mob/living/M) return /datum/reagent/proc/overdose_start(mob/living/M) to_chat(M, "You feel like you took too much of [name]!") SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[type]_overdose", /datum/mood_event/overdose, name) /datum/reagent/proc/addiction_act_stage1(mob/living/M) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[type]_overdose", /datum/mood_event/withdrawal_light, name) if(prob(30)) to_chat(M, "You feel like having some [name] right about now.") /datum/reagent/proc/addiction_act_stage2(mob/living/M) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[type]_overdose", /datum/mood_event/withdrawal_medium, name) if(prob(30)) to_chat(M, "You feel like you need [name]. You just can't get enough.") /datum/reagent/proc/addiction_act_stage3(mob/living/M) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[type]_overdose", /datum/mood_event/withdrawal_severe, name) if(prob(30)) to_chat(M, "You have an intense craving for [name].") /datum/reagent/proc/addiction_act_stage4(mob/living/M) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[type]_overdose", /datum/mood_event/withdrawal_critical, name) if(prob(30)) to_chat(M, "You're not feeling good at all! You really need some [name].") /proc/pretty_string_from_reagent_list(list/reagent_list) //Convert reagent list to a printable string for logging etc var/list/rs = list() for (var/datum/reagent/R in reagent_list) rs += "[R.name], [R.volume]" return rs.Join(" | ") //For easy bloodsucker disgusting and blood removal /datum/reagent/proc/disgust_bloodsucker(mob/living/carbon/C, disgust, blood_change, blood_puke = TRUE, force) if(AmBloodsucker(C)) var/datum/antagonist/bloodsucker/bloodsuckerdatum = C.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) if(disgust) bloodsuckerdatum.handle_eat_human_food(disgust, blood_puke, force) if(blood_change) bloodsuckerdatum.AddBloodVolume(blood_change)