mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
813 lines
25 KiB
Plaintext
813 lines
25 KiB
Plaintext
|
|
/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/last_tick = 1
|
|
var/addiction_tick = 1
|
|
var/list/datum/reagent/addiction_list = new/list()
|
|
var/reagents_holder_flags
|
|
|
|
/datum/reagents/New(maximum=100, new_flags)
|
|
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()
|
|
|
|
reagents_holder_flags = new_flags
|
|
|
|
/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)
|
|
R.add_reagent(T.id, transfer_amount * multiplier, trans_data, chem_temp, no_react = 1) //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)<amount)
|
|
amount = src.get_reagent_amount(reagent)
|
|
amount = min(amount, R.maximum_volume-R.total_volume)
|
|
var/trans_data = null
|
|
for (var/CR in cached_reagents)
|
|
var/datum/reagent/current_reagent = CR
|
|
if(current_reagent.id == reagent)
|
|
if(preserve_data)
|
|
trans_data = current_reagent.data
|
|
R.add_reagent(current_reagent.id, amount, trans_data, src.chem_temp)
|
|
remove_reagent(current_reagent.id, amount, 1)
|
|
break
|
|
|
|
src.update_total()
|
|
R.update_total()
|
|
R.handle_reactions()
|
|
return amount
|
|
|
|
/datum/reagents/proc/metabolize(mob/living/carbon/C, can_overdose = FALSE, liverless = FALSE)
|
|
var/list/cached_reagents = reagent_list
|
|
var/list/cached_addictions = addiction_list
|
|
if(C)
|
|
expose_temperature(C.bodytemperature, 0.25)
|
|
var/need_mob_update = 0
|
|
for(var/reagent in cached_reagents)
|
|
var/datum/reagent/R = reagent
|
|
if(QDELETED(R.holder))
|
|
continue
|
|
if(liverless && !R.self_consuming) //need to be metabolized
|
|
continue
|
|
if(!C)
|
|
C = R.holder.my_atom
|
|
if(C && R)
|
|
if(C.reagent_check(R) != 1)
|
|
if(can_overdose)
|
|
if(R.overdose_threshold)
|
|
if(R.volume > 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, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
|
|
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/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()
|
|
if(reagents_holder_flags & NO_REACT)
|
|
return //Yup, no reactions here. No siree.
|
|
var/list/cached_reagents = reagent_list
|
|
var/list/cached_reactions = GLOB.chemical_reactions_list
|
|
var/datum/cached_my_atom = my_atom
|
|
|
|
var/reaction_occurred = 0
|
|
do
|
|
var/list/possible_reactions = list()
|
|
reaction_occurred = 0
|
|
for(var/reagent in cached_reagents)
|
|
var/datum/reagent/R = reagent
|
|
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)
|
|
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)
|
|
matching_other = 1
|
|
|
|
else if(istype(cached_my_atom, /obj/item/slime_extract))
|
|
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)
|
|
matching_container = 1
|
|
if(!C.required_other)
|
|
matching_other = 1
|
|
|
|
if(required_temp == 0 || (is_cold_recipe && chem_temp <= required_temp) || (!is_cold_recipe && chem_temp >= required_temp))
|
|
meets_temp_requirement = 1
|
|
|
|
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)
|
|
var/datum/chemical_reaction/selected_reaction = possible_reactions[1]
|
|
//select the reaction with the most extreme temperature requirements
|
|
for(var/V in possible_reactions)
|
|
var/datum/chemical_reaction/competitor = V
|
|
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)
|
|
selected_reaction = competitor
|
|
else
|
|
if(competitor.required_temp >= selected_reaction.required_temp)
|
|
selected_reaction = competitor
|
|
var/list/cached_required_reagents = selected_reaction.required_reagents
|
|
var/list/cached_results = selected_reaction.results
|
|
var/special_react_result = selected_reaction.check_special_react(src)
|
|
var/list/multiplier = INFINITY
|
|
for(var/B in cached_required_reagents)
|
|
multiplier = min(multiplier, round(get_reagent_amount(B) / cached_required_reagents[B]))
|
|
|
|
for(var/B in cached_required_reagents)
|
|
remove_reagent(B, (multiplier * cached_required_reagents[B]), safety = 1)
|
|
|
|
for(var/P in selected_reaction.results)
|
|
multiplier = max(multiplier, 1) //this shouldnt happen ...
|
|
SSblackbox.record_feedback("tally", "chemical_reaction", cached_results[P]*multiplier, P)
|
|
add_reagent(P, cached_results[P]*multiplier, null, chem_temp)
|
|
|
|
var/list/seen = viewers(4, get_turf(my_atom))
|
|
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, "<span class='notice'>[iconhtml] [selected_reaction.mix_message]</span>")
|
|
|
|
if(istype(cached_my_atom, /obj/item/slime_extract))
|
|
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, "<span class='notice'>[iconhtml] \The [my_atom]'s power is consumed in the reaction.</span>")
|
|
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
|
|
|
|
while(reaction_occurred)
|
|
update_total()
|
|
return 0
|
|
|
|
/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, no_react = 0)
|
|
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, flags)
|
|
if(reagents)
|
|
qdel(reagents)
|
|
reagents = new/datum/reagents(max_vol, flags)
|
|
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
|