* [READY] Several fixes/changes to mood, longterm mood effects, beauty component (#36344) cl Floyd / Qustinnus del: Removes short-term effects of mood add; Adds long-term effects of mood by implementing sanity which goes up with good mood, down with bad mood, but takes time to change. Your sanity can be seen as your average mood in the recent past. All effects of moods are now covered by this system add: Beauty component, currently only attached to cleanables, but you could attach it to any atom/movable and make them pretty/ugly, affecting mood of anyone in the room. refactor: Removes the original way of adding mood events, uses signals properly instead. fix: Cleanables "giving" area's free beauty during initialization fix: Fixes some events not clearing properly /cl Fixes #36444 From now on mood no longer affects you directly, instead it decides whether your sanity goes up or down, when your sanity gets too low you will get the effects of what mood did before. This means getting hit with bad moods due to being attacked while not mean you are doomed anymore, and you get a large timeframe to get away and just fix your mood later. I also added the beauty component, you could add this to any object and it would either make a room prettier or uglier, comparable to DF or Rimworld. You could add traits to make certain people ugly, for example. * [READY] Several fixes/changes to mood, longterm mood effects, beauty component
784 lines
24 KiB
Plaintext
784 lines
24 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/flags
|
|
|
|
/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
|
|
|
|
|
|
// 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++
|
|
switch(R.addiction_stage)
|
|
if(1 to 10)
|
|
need_mob_update += R.addiction_act_stage1(C)
|
|
if(10 to 20)
|
|
need_mob_update += R.addiction_act_stage2(C)
|
|
if(20 to 30)
|
|
need_mob_update += R.addiction_act_stage3(C)
|
|
if(30 to 40)
|
|
need_mob_update += R.addiction_act_stage4(C)
|
|
if(40 to INFINITY)
|
|
to_chat(C, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
|
|
C.SendSignal(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)
|
|
flags &= ~(REAGENT_NOREACT)
|
|
else
|
|
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()
|
|
var/list/cached_reagents = reagent_list
|
|
var/list/cached_reactions = GLOB.chemical_reactions_list
|
|
var/datum/cached_my_atom = my_atom
|
|
if(flags & REAGENT_NOREACT)
|
|
return //Yup, no reactions here. No siree.
|
|
|
|
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
|
|
|
|
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(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other && meets_temp_requirement)
|
|
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/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)
|
|
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
|
|
|
|
/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/list/cached_reagents = reagent_list
|
|
update_total()
|
|
if(total_volume + amount > maximum_volume)
|
|
amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen.
|
|
chem_temp = round(((amount * reagtemp) + (total_volume * chem_temp)) / (total_volume + amount)) //equalize with new chems
|
|
|
|
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
|
|
|
|
var/datum/reagent/D = GLOB.chemical_reagents_list[reagent]
|
|
if(D)
|
|
|
|
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)
|
|
|
|
update_total()
|
|
if(my_atom)
|
|
my_atom.on_reagent_change(ADD_REAGENT)
|
|
if(!no_react)
|
|
handle_reactions()
|
|
if(isliving(my_atom))
|
|
R.on_mob_add(my_atom)
|
|
return TRUE
|
|
|
|
else
|
|
WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])")
|
|
return FALSE
|
|
|
|
/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
|