/proc/build_chemical_reagent_list() //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id if(GLOB.chemical_reagents_list) return var/paths = subtypesof(/datum/reagent) GLOB.chemical_reagents_list = list() for(var/path in paths) var/datum/reagent/D = new path() GLOB.chemical_reagents_list[D.id] = D /proc/build_chemical_reactions_list() //Chemical Reactions - Initialises all /datum/chemical_reaction into a list // It is filtered into multiple lists within a list. // For example: // chemical_reaction_list["plasma"] is a list of all reactions relating to plasma if(GLOB.chemical_reactions_list) return var/paths = subtypesof(/datum/chemical_reaction) GLOB.chemical_reactions_list = list() for(var/path in paths) var/datum/chemical_reaction/D = new path() var/list/reaction_ids = list() if(D.required_reagents && D.required_reagents.len) for(var/reaction in D.required_reagents) reaction_ids += reaction // Create filters based on each reagent id in the required reagents list for(var/id in reaction_ids) if(!GLOB.chemical_reactions_list[id]) GLOB.chemical_reactions_list[id] = list() GLOB.chemical_reactions_list[id] += D break // Don't bother adding ourselves to other reagent ids, it is redundant /////////////////////////////////////////////////////////////////////////////////// /datum/reagents var/list/datum/reagent/reagent_list = new/list() var/total_volume = 0 var/maximum_volume = 100 var/atom/my_atom = null var/chem_temp = 150 var/pH = REAGENT_NORMAL_PH var/last_tick = 1 var/addiction_tick = 1 var/list/datum/reagent/addiction_list = new/list() var/reagents_holder_flags var/targetVol = 0 var/reactedVol = 0 var/fermiIsReacting = FALSE var/fermiReactID = null /datum/reagents/New(maximum=100) maximum_volume = maximum //I dislike having these here but map-objects are initialised before world/New() is called. >_> if(!GLOB.chemical_reagents_list) build_chemical_reagent_list() if(!GLOB.chemical_reactions_list) build_chemical_reactions_list() /datum/reagents/Destroy() . = ..() var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent qdel(R) cached_reagents.Cut() cached_reagents = null if(my_atom && my_atom.reagents == src) my_atom.reagents = null my_atom = null // Used in attack logs for reagents in pills and such /datum/reagents/proc/log_list() if(!length(reagent_list)) return "no reagents" var/list/data = list() for(var/r in reagent_list) //no reagents will be left behind var/datum/reagent/R = r data += "[R.id] ([round(R.volume, 0.1)]u)" //Using IDs because SOME chemicals (I'm looking at you, chlorhydrate-beer) have the same names as other chemicals. return english_list(data) /datum/reagents/proc/remove_any(amount = 1) var/list/cached_reagents = reagent_list var/total_transfered = 0 var/current_list_element = 1 current_list_element = rand(1, cached_reagents.len) while(total_transfered != amount) if(total_transfered >= amount) break if(total_volume <= 0 || !cached_reagents.len) break if(current_list_element > cached_reagents.len) current_list_element = 1 var/datum/reagent/R = cached_reagents[current_list_element] remove_reagent(R.id, 1) current_list_element++ total_transfered++ update_total() handle_reactions() return total_transfered /datum/reagents/proc/remove_all(amount = 1) var/list/cached_reagents = reagent_list if(total_volume > 0) var/part = amount / total_volume for(var/reagent in cached_reagents) var/datum/reagent/R = reagent remove_reagent(R.id, R.volume * part) update_total() handle_reactions() return amount /datum/reagents/proc/get_master_reagent_name() var/list/cached_reagents = reagent_list var/name var/max_volume = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.volume > max_volume) max_volume = R.volume name = R.name return name /datum/reagents/proc/get_master_reagent_id() var/list/cached_reagents = reagent_list var/id var/max_volume = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.volume > max_volume) max_volume = R.volume id = R.id return id /datum/reagents/proc/get_master_reagent() var/list/cached_reagents = reagent_list var/datum/reagent/master var/max_volume = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.volume > max_volume) max_volume = R.volume master = R return master /datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1, no_react = 0)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred. var/list/cached_reagents = reagent_list if(!target || !total_volume) return if(amount < 0) return var/datum/reagents/R if(istype(target, /datum/reagents)) R = target else if(!target.reagents) return R = target.reagents amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) var/part = amount / src.total_volume var/trans_data = null for(var/reagent in cached_reagents) var/datum/reagent/T = reagent var/transfer_amount = T.volume * part if(preserve_data) trans_data = copy_data(T) //fermichem Added ph TODO: add T.purity R.add_reagent(T.id, transfer_amount * multiplier, trans_data, chem_temp, pH, no_react = TRUE) //we only handle reaction after every reagent has been transfered. //R.add_reagent(T.id, transfer_amount * multiplier, trans_data, chem_temp, pH, T.purity, no_react = TRUE) //we only handle reaction after every reagent has been transfered. remove_reagent(T.id, transfer_amount) update_total() R.update_total() if(!no_react) R.handle_reactions() src.handle_reactions() return amount /datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1) var/list/cached_reagents = reagent_list if(!target || !total_volume) return var/datum/reagents/R if(istype(target, /datum/reagents)) R = target else if(!target.reagents) return R = target.reagents if(amount < 0) return amount = min(min(amount, total_volume), R.maximum_volume-R.total_volume) var/part = amount / total_volume var/trans_data = null for(var/reagent in cached_reagents) var/datum/reagent/T = reagent var/copy_amount = T.volume * part if(preserve_data) trans_data = T.data R.add_reagent(T.id, copy_amount * multiplier, trans_data) src.update_total() R.update_total() R.handle_reactions() src.handle_reactions() return amount /datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N var/list/cached_reagents = reagent_list if (!target) return if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent)) return if(amount < 0) return var/datum/reagents/R = target.reagents if(src.get_reagent_amount(reagent)= R.overdose_threshold && !R.overdosed) R.overdosed = 1 need_mob_update += R.overdose_start(C) if(R.addiction_threshold) if(R.volume >= R.addiction_threshold && !is_type_in_list(R, cached_addictions)) var/datum/reagent/new_reagent = new R.type() cached_addictions.Add(new_reagent) if(R.overdosed) need_mob_update += R.overdose_process(C) if(is_type_in_list(R,cached_addictions)) for(var/addiction in cached_addictions) var/datum/reagent/A = addiction if(istype(R, A)) A.addiction_stage = -15 // you're satisfied for a good while. need_mob_update += R.on_mob_life(C) if(can_overdose) if(addiction_tick == 6) addiction_tick = 1 for(var/addiction in cached_addictions) var/datum/reagent/R = addiction if(C && R) R.addiction_stage++ if(1 <= R.addiction_stage && R.addiction_stage <= R.addiction_stage1_end) need_mob_update += R.addiction_act_stage1(C) else if(R.addiction_stage1_end <= R.addiction_stage && R.addiction_stage <= R.addiction_stage2_end) need_mob_update += R.addiction_act_stage2(C) else if(R.addiction_stage2_end <= R.addiction_stage && R.addiction_stage <= R.addiction_stage3_end) need_mob_update += R.addiction_act_stage3(C) else if(R.addiction_stage3_end <= R.addiction_stage && R.addiction_stage <= R.addiction_stage4_end) need_mob_update += R.addiction_act_stage4(C) else if(R.addiction_stage4_end <= R.addiction_stage) to_chat(C, "You feel like you've gotten over your need for [R.name].") SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "[R.id]_addiction") cached_addictions.Remove(R) addiction_tick++ if(C && need_mob_update) //some of the metabolized reagents had effects on the mob that requires some updates. C.updatehealth() C.update_canmove() C.update_stamina() update_total() /datum/reagents/proc/set_reacting(react = TRUE) if(react) reagents_holder_flags &= ~(REAGENT_NOREACT) else reagents_holder_flags |= REAGENT_NOREACT /datum/reagents/proc/conditional_update_move(atom/A, Running = 0) var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent R.on_move (A, Running) update_total() /datum/reagents/proc/conditional_update(atom/A) var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent R.on_update (A) update_total() /datum/reagents/proc/handle_reactions()//HERE EDIT HERE THE MAIN REACTION FERMICHEMS ASSEMBLE! I hope rp is similar var/list/cached_reagents = reagent_list //a list of the reagents? var/list/cached_reactions = GLOB.chemical_reactions_list //a list of the whole reactions? var/datum/cached_my_atom = my_atom //It says my atom, but I didn't bring one with me!! if(reagents_holder_flags & REAGENT_NOREACT) //Not sure on reagents_holder_flags, but I think it checks to see if theres a reaction with current stuff. return //Yup, no reactions here. No siree. var/reaction_occurred = 0 // checks if reaction, binary variable var/continue_reacting = FALSE //Helps keep track what kind of reaction is occuring; standard or fermi. if(fermiIsReacting == TRUE) if (reactedVol >= targetVol && targetVol != 0) STOP_PROCESSING(SSfastprocess, src) fermiIsReacting = FALSE message_admins("FermiChem processing stopped") reaction_occurred = 1 return do //What does do do in byond? It sounds very redundant? is it a while loop? var/list/possible_reactions = list() //init list reaction_occurred = 0 // sets it back to 0? for(var/reagent in cached_reagents) //for reagent in beaker/holder var/datum/reagent/R = reagent //check to make sure that reagent is there for the reaction list for(var/reaction in cached_reactions[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id if(!reaction) continue var/datum/chemical_reaction/C = reaction var/list/cached_required_reagents = C.required_reagents var/total_required_reagents = cached_required_reagents.len var/total_matching_reagents = 0 var/list/cached_required_catalysts = C.required_catalysts var/total_required_catalysts = cached_required_catalysts.len var/total_matching_catalysts= 0 var/matching_container = 0 var/matching_other = 0 var/required_temp = C.required_temp var/is_cold_recipe = C.is_cold_recipe var/meets_temp_requirement = 0 var/has_special_react = C.special_react var/can_special_react = 0 for(var/B in cached_required_reagents) if(!has_reagent(B, cached_required_reagents[B])) break total_matching_reagents++ for(var/B in cached_required_catalysts) if(!has_reagent(B, cached_required_catalysts[B])) break total_matching_catalysts++ if(cached_my_atom) if(!C.required_container) matching_container = 1 else if(cached_my_atom.type == C.required_container)//if the suspected container is a container matching_container = 1 if (isliving(cached_my_atom) && !C.mob_react) //Makes it so certain chemical reactions don't occur in mobs return if(!C.required_other)//Checks for other things required matching_other = 1//binary check passes else if(istype(cached_my_atom, /obj/item/slime_extract))//if the object is a slime_extract. This might be complicated as to not break them via fermichem var/obj/item/slime_extract/M = cached_my_atom if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this matching_other = 1 else if(!C.required_container)//I'm not sure why this is here twice, I think if it's not a beaker? Oh, cyro. matching_container = 1 if(!C.required_other) matching_other = 1 //FermiChem /* if (chem_temp > C.ExplodeTemp)//Check to see if reaction is too hot! if (C.FermiExplode == TRUE) //To be added! else FermiExplode() //explode function!! */ if(required_temp == 0 || (is_cold_recipe && chem_temp <= required_temp) || (!is_cold_recipe && chem_temp >= required_temp))//Temperature check!! meets_temp_requirement = 1//binary pass if(!has_special_react || C.check_special_react(src)) can_special_react = 1 if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other && meets_temp_requirement && can_special_react) possible_reactions += C if(possible_reactions.len)//does list exist? var/datum/chemical_reaction/selected_reaction = possible_reactions[1] //select the reaction with the most extreme temperature requirements for(var/V in possible_reactions)//why V, surely that would indicate volume? V is the reaction potential. var/datum/chemical_reaction/competitor = V //competitor? I think this is theres two of them. Troubling..! if(selected_reaction.is_cold_recipe) //if there are no recipe conflicts, everything in possible_reactions will have this same value for is_cold_reaction. warranty void if assumption not met. if(competitor.required_temp <= selected_reaction.required_temp)//only returns with lower if reaction "is cold" var. selected_reaction = competitor else if(competitor.required_temp >= selected_reaction.required_temp) //will return with the hotter reacting first. selected_reaction = competitor var/list/cached_required_reagents = selected_reaction.required_reagents//update reagents list var/list/cached_results = selected_reaction.results//resultant chemical list var/special_react_result = selected_reaction.check_special_react(src) var/list/multiplier = INFINITY //Wat for(var/B in cached_required_reagents) // multiplier = min(multiplier, round(get_reagent_amount(B) / cached_required_reagents[B]))//a simple one over the other? (Is this for multiplying end product? Useful for toxinsludge buildup) //Splits reactions into two types; FermiChem is advanced reaction mechanics, Other is default reaction. //FermiChem relies on two additional properties; pH and impurity //Temperature plays into a larger role too. //BRANCH HERE //if(selected_reaction) var/datum/chemical_reaction/C = selected_reaction if (C.FermiChem == TRUE && !continue_reacting) message_admins("FermiChem Proc'd") reaction_occurred = 1 for(var/P in selected_reaction.results) targetVol = cached_results[P]*multiplier if (chem_temp > C.OptimalTempMin)//To prevent pointless reactions if (reactedVol < targetVol) //reactedVol = FermiReact(selected_reaction, chem_temp, pH, multiplier, reactedVol, targetVol, cached_required_reagents, cached_results) START_PROCESSING(SSfastprocess, src) message_admins("FermiChem processing started") fermiIsReacting = TRUE fermiReactID = selected_reaction else fermiIsReacting = FALSE STOP_PROCESSING(SSfastprocess, src) else return SSblackbox.record_feedback("tally", "Fermi_chemical_reaction", reactedVol, C.id)//log //Standard reaction mechanics: else for(var/B in cached_required_reagents) // multiplier = min(multiplier, round(get_reagent_amount(B) / cached_required_reagents[B]))//a simple one over the other? (Is this for multiplying end product? Useful for toxinsludge buildup) for(var/B in cached_required_reagents) remove_reagent(B, (multiplier * cached_required_reagents[B]), safety = 1)//safety? removes reagents from beaker using remove function. for(var/P in selected_reaction.results)//Not sure how this works, what is selected_reaction.results? multiplier = max(multiplier, 1) //this shouldnt happen ... SSblackbox.record_feedback("tally", "chemical_reaction", cached_results[P]*multiplier, P)//log add_reagent(P, cached_results[P]*multiplier, null, chem_temp)//add reagent function!! I THINK I can do this: var/list/seen = viewers(4, get_turf(my_atom))//Sound and sight checkers var/iconhtml = icon2html(cached_my_atom, seen) if(cached_my_atom) if(!ismob(cached_my_atom)) // No bubbling mobs if(selected_reaction.mix_sound) playsound(get_turf(cached_my_atom), selected_reaction.mix_sound, 80, 1) for(var/mob/M in seen) to_chat(M, "[iconhtml] [selected_reaction.mix_message]") if(istype(cached_my_atom, /obj/item/slime_extract))//if there's an extract and it's used up. var/obj/item/slime_extract/ME2 = my_atom ME2.Uses-- if(ME2.Uses <= 0) // give the notification that the slime core is dead for(var/mob/M in seen) to_chat(M, "[iconhtml] \The [my_atom]'s power is consumed in the reaction.") ME2.name = "used slime extract" ME2.desc = "This extract has been used up." selected_reaction.on_reaction(src, multiplier, special_react_result) reaction_occurred = 1 continue_reacting = TRUE while(reaction_occurred)//while do nothing? update_total()//Don't know waht this does. return 0//end! /datum/reagents/process() if (chem_temp > C.OptimalTempMin)//To prevent pointless reactions if (reactedVol < targetVol) reactedVol = FermiReact(fermiReactID, chem_temp, pH, multiplier, reactedVol, targetVol, cached_required_reagents, cached_results) else STOP_PROCESSING(SSfastprocess, src) //handle_reactions() /datum/reagents/proc/FermiReact(selected_reaction, chem_temp, pH, multiplier, reactedVol, targetVol, cached_required_reagents, cached_results) var/datum/chemical_reaction/C = selected_reaction var/deltaT = 0 var/deltapH = 0 var/stepChemAmmount = 0 //var/ammoReacted = 0 //get purity from combined beaker reactant purities HERE. var/purity = 1 //var/tempVol = totalVol //var/list/multiplier = INFINITY message_admins("Loop beginning") //Begin Parse //Check extremes first if (chem_temp > C.ExplodeTemp) //go to explode proc message_admins("temperature is over limit: [C.ExplodeTemp] Current temperature: [chem_temp]") FermiExplode() if (pH > 14 || pH < 0) //Create chemical sludge eventually(for now just destroy the beaker I guess?) //TODO Strong acids eat glass, make it so you NEED plastic beakers for superacids(for some reactions) message_admins("pH is lover limit, cur pH: [pH]") FermiExplode() //For now, purity is handled elsewhere //Calculate DeltaT (Deviation of T from optimal) if (chem_temp < C.OptimalTempMax && chem_temp >= C.OptimalTempMin) deltaT = (((C.OptimalTempMin - chem_temp)**C.CurveSharpT)/((C.OptimalTempMax - C.OptimalTempMin)**C.CurveSharpT)) else if (chem_temp >= C.OptimalTempMax) deltaT = 1 else deltaT = 0 message_admins("calculating temperature factor, min: [C.OptimalTempMin], max: [C.OptimalTempMax], Exponential: [C.CurveSharpT], deltaT: [deltaT]") //Calculate DeltapH (Deviation of pH from optimal) //Lower range if (pH < C.OptimalpHMin) if (pH < (C.OptimalpHMin - C.ReactpHLim)) deltapH = 0 else deltapH = (((pH - (C.OptimalpHMin - C.ReactpHLim))**C.CurveSharppH)/(C.ReactpHLim**C.CurveSharppH)) //Upper range else if (pH > C.OptimalpHMin) if (pH > (C.OptimalpHMin + C.ReactpHLim)) deltapH = 0 else deltapH = ((C.ReactpHLim -(pH - (C.OptimalpHMax + C.ReactpHLim))+C.ReactpHLim)/(C.ReactpHLim**C.CurveSharppH)) //Within mid range else if (pH >= C.OptimalpHMin && pH <= C.OptimalpHMax) deltapH = 1 //This should never proc: else message_admins("Fermichem's pH broke!! Please let Fermis know!!") WARNING("[my_atom] attempted to determine FermiChem pH for '[C.id]' which broke for some reason! ([usr])") //TODO Add CatalystFact message_admins("calculating pH factor(purity), pH: [pH], min: [C.OptimalpHMin]-[C.ReactpHLim], max: [C.OptimalpHMax]+[C.ReactpHLim], deltapH: [deltapH]") stepChemAmmount = targetVol * deltaT if (stepChemAmmount > C.RateUpLim) stepChemAmmount = C.RateUpLim else if (stepChemAmmount < 0.01) stepChemAmmount = 0.01 if ((reactedVol + stepChemAmmount) > targetVol) stepChemAmmount = targetVol - reactedVol message_admins("target volume reached. Reaction should stop after this loop") if (reactedVol > 0) purity = ((purity * reactedVol) + (deltapH * stepChemAmmount)) /((reactedVol+ stepChemAmmount)) //This should add the purity to the product else purity = deltapH message_admins("purity: [purity], purity of beaker") message_admins("Temp before change: [chem_temp], pH after change: [pH]") //Apply pH changes and thermal output of reaction to beaker chem_temp += (C.ThermicConstant * stepChemAmmount) pH += (C.HIonRelease * stepChemAmmount) message_admins("Temp after change: [chem_temp], pH after change: [pH]") // End. /* for(var/B in cached_required_reagents) // tempVol = min(reactedVol, round(get_reagent_amount(B) / cached_required_reagents[B]))//a simple one over the other? (Is this for multiplying end product? Useful for toxinsludge buildup) */ message_admins("cached_results: [cached_results], reactedVol: [reactedVol], stepChemAmmount [stepChemAmmount]") for(var/B in cached_required_reagents) message_admins("cached_required_reagents(B): [cached_required_reagents[B]], reactedVol: [reactedVol], base stepChemAmmount [stepChemAmmount]") remove_reagent(B, (stepChemAmmount * cached_required_reagents[B]), safety = 1)//safety? removes reagents from beaker using remove function. for(var/P in cached_results)//Not sure how this works, what is selected_reaction.results? reactedVol = max(reactedVol, 1) //this shouldnt happen ... SSblackbox.record_feedback("tally", "chemical_reaction", cached_results[P]*stepChemAmmount, P)//log add_reagent(P, cached_results[P]*stepChemAmmount, null, chem_temp)//add reagent function!! I THINK I can do this: reactedVol = reactedVol + stepChemAmmount return //(reactedVol) /datum/reagents/proc/FermiExplode() return /datum/reagents/proc/isolate_reagent(reagent) var/list/cached_reagents = reagent_list for(var/_reagent in cached_reagents) var/datum/reagent/R = _reagent if(R.id != reagent) del_reagent(R.id) update_total() /datum/reagents/proc/del_reagent(reagent) var/list/cached_reagents = reagent_list for(var/_reagent in cached_reagents) var/datum/reagent/R = _reagent if(R.id == reagent) if(my_atom && isliving(my_atom)) var/mob/living/M = my_atom R.on_mob_delete(M) qdel(R) reagent_list -= R update_total() if(my_atom) my_atom.on_reagent_change(DEL_REAGENT) return 1 /datum/reagents/proc/update_total() var/list/cached_reagents = reagent_list total_volume = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.volume < 0.1) del_reagent(R.id) else total_volume += R.volume return 0 /datum/reagents/proc/clear_reagents() var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent del_reagent(R.id) return 0 /datum/reagents/proc/reaction(atom/A, method = TOUCH, volume_modifier = 1, show_message = 1) var/react_type if(isliving(A)) react_type = "LIVING" if(method == INGEST) var/mob/living/L = A L.taste(src) else if(isturf(A)) react_type = "TURF" else if(isobj(A)) react_type = "OBJ" else return var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent switch(react_type) if("LIVING") var/touch_protection = 0 if(method == VAPOR) var/mob/living/L = A touch_protection = L.get_permeability_protection() R.reaction_mob(A, method, R.volume * volume_modifier, show_message, touch_protection) if("TURF") R.reaction_turf(A, R.volume * volume_modifier, show_message) if("OBJ") R.reaction_obj(A, R.volume * volume_modifier, show_message) /datum/reagents/proc/holder_full() if(total_volume >= maximum_volume) return TRUE return FALSE //Returns the average specific heat for all reagents currently in this holder. /datum/reagents/proc/specific_heat() . = 0 var/cached_amount = total_volume //cache amount var/list/cached_reagents = reagent_list //cache reagents for(var/I in cached_reagents) var/datum/reagent/R = I . += R.specific_heat * (R.volume / cached_amount) /datum/reagents/proc/adjust_thermal_energy(J, min_temp = 2.7, max_temp = 1000) var/S = specific_heat() chem_temp = CLAMP(chem_temp + (J / (S * total_volume)), 2.7, 1000) /datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, pH = 7, no_react = 0)//EDIT HERE TOO ~FERMICHEM~ if(!isnum(amount) || !amount) return FALSE if(amount <= 0) return FALSE var/datum/reagent/D = GLOB.chemical_reagents_list[reagent] if(!D) WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") return FALSE update_total() var/cached_total = total_volume if(cached_total + amount > maximum_volume) amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. if(amount <= 0) return FALSE var/new_total = cached_total + amount var/cached_temp = chem_temp var/list/cached_reagents = reagent_list //Equalize temperature - Not using specific_heat() because the new chemical isn't in yet. var/specific_heat = 0 var/thermal_energy = 0 for(var/i in cached_reagents) var/datum/reagent/R = i specific_heat += R.specific_heat * (R.volume / new_total) thermal_energy += R.specific_heat * R.volume * cached_temp specific_heat += D.specific_heat * (amount / new_total) thermal_energy += D.specific_heat * amount * reagtemp chem_temp = thermal_energy / (specific_heat * new_total) //// //add the reagent to the existing if it exists for(var/A in cached_reagents) var/datum/reagent/R = A if (R.id == reagent) R.volume += amount update_total() if(my_atom) my_atom.on_reagent_change(ADD_REAGENT) R.on_merge(data, amount) if(!no_react) handle_reactions() return TRUE //otherwise make a new one var/datum/reagent/R = new D.type(data) cached_reagents += R R.holder = src R.volume = amount if(data) R.data = data R.on_new(data) if(isliving(my_atom)) R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete update_total() if(my_atom) my_atom.on_reagent_change(ADD_REAGENT) if(!no_react) handle_reactions() return TRUE /datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15) for(var/r_id in list_reagents) var/amt = list_reagents[r_id] add_reagent(r_id, amt, data) /datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to if(isnull(amount)) amount = 0 CRASH("null amount passed to reagent code") return FALSE if(!isnum(amount)) return FALSE if(amount < 0) return FALSE var/list/cached_reagents = reagent_list for(var/A in cached_reagents) var/datum/reagent/R = A if (R.id == reagent) //clamp the removal amount to be between current reagent amount //and zero, to prevent removing more than the holder has stored amount = CLAMP(amount, 0, R.volume) R.volume -= amount update_total() if(!safety)//So it does not handle reactions when it need not to handle_reactions() if(my_atom) my_atom.on_reagent_change(REM_REAGENT) return TRUE return FALSE /datum/reagents/proc/has_reagent(reagent, amount = -1) var/list/cached_reagents = reagent_list for(var/_reagent in cached_reagents) var/datum/reagent/R = _reagent if (R.id == reagent) if(!amount) return R else if(R.volume >= amount) return R else return 0 return 0 /datum/reagents/proc/get_reagent_amount(reagent) var/list/cached_reagents = reagent_list for(var/_reagent in cached_reagents) var/datum/reagent/R = _reagent if (R.id == reagent) return R.volume return 0 /datum/reagents/proc/get_reagents() var/list/names = list() var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent names += R.name return jointext(names, ",") /datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included. if(!isnum(amount)) return 1 var/list/cached_reagents = reagent_list var/has_removed_reagent = 0 for(var/reagent in cached_reagents) var/datum/reagent/R = reagent var/matches = 0 // Switch between how we check the reagent type if(strict) if(R.type == reagent_type) matches = 1 else if(istype(R, reagent_type)) matches = 1 // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type. if(matches) // Have our other proc handle removement has_removed_reagent = remove_reagent(R.id, amount, safety) return has_removed_reagent //two helper functions to preserve data across reactions (needed for xenoarch) /datum/reagents/proc/get_data(reagent_id) var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.id == reagent_id) return R.data /datum/reagents/proc/set_data(reagent_id, new_data) var/list/cached_reagents = reagent_list for(var/reagent in cached_reagents) var/datum/reagent/R = reagent if(R.id == reagent_id) R.data = new_data /datum/reagents/proc/copy_data(datum/reagent/current_reagent) if(!current_reagent || !current_reagent.data) return null if(!istype(current_reagent.data, /list)) return current_reagent.data var/list/trans_data = current_reagent.data.Copy() // We do this so that introducing a virus to a blood sample // doesn't automagically infect all other blood samples from // the same donor. // // Technically we should probably copy all data lists, but // that could possibly eat up a lot of memory needlessly // if most data lists are read-only. if(trans_data["viruses"]) var/list/v = trans_data["viruses"] trans_data["viruses"] = v.Copy() return trans_data /datum/reagents/proc/get_reagent(type) var/list/cached_reagents = reagent_list . = locate(type) in cached_reagents /datum/reagents/proc/generate_taste_message(minimum_percent=15) // the lower the minimum percent, the more sensitive the message is. var/list/out = list() var/list/tastes = list() //descriptor = strength if(minimum_percent <= 100) for(var/datum/reagent/R in reagent_list) if(!R.taste_mult) continue if(istype(R, /datum/reagent/consumable/nutriment)) var/list/taste_data = R.data for(var/taste in taste_data) var/ratio = taste_data[taste] var/amount = ratio * R.taste_mult * R.volume if(taste in tastes) tastes[taste] += amount else tastes[taste] = amount else var/taste_desc = R.taste_description var/taste_amount = R.volume * R.taste_mult if(taste_desc in tastes) tastes[taste_desc] += taste_amount else tastes[taste_desc] = taste_amount //deal with percentages // TODO it would be great if we could sort these from strong to weak var/total_taste = counterlist_sum(tastes) if(total_taste > 0) for(var/taste_desc in tastes) var/percent = tastes[taste_desc]/total_taste * 100 if(percent < minimum_percent) continue var/intensity_desc = "a hint of" if(percent > minimum_percent * 2 || percent == 100) intensity_desc = "" else if(percent > minimum_percent * 3) intensity_desc = "the strong flavor of" if(intensity_desc != "") out += "[intensity_desc] [taste_desc]" else out += "[taste_desc]" return english_list(out, "something indescribable") /datum/reagents/proc/expose_temperature(var/temperature, var/coeff=0.02) var/temp_delta = (temperature - chem_temp) * coeff if(temp_delta > 0) chem_temp = min(chem_temp + max(temp_delta, 1), temperature) else chem_temp = max(chem_temp + min(temp_delta, -1), temperature) chem_temp = round(chem_temp) handle_reactions() /////////////////////////////////////////////////////////////////////////////////// // Convenience proc to create a reagents holder for an atom // Max vol is maximum volume of holder /atom/proc/create_reagents(max_vol) if(reagents) qdel(reagents) reagents = new/datum/reagents(max_vol) reagents.my_atom = src /proc/get_random_reagent_id() // Returns a random reagent ID minus blacklisted reagents var/static/list/random_reagents = list() if(!random_reagents.len) for(var/thing in subtypesof(/datum/reagent)) var/datum/reagent/R = thing if(initial(R.can_synth)) random_reagents += initial(R.id) var/picked_reagent = pick(random_reagents) return picked_reagent