/datum/reagent
var/name = "Reagent"
var/id = "reagent"
var/description = ""
var/datum/reagents/holder = null
var/reagent_state = SOLID
var/list/data = null
var/volume = 0
var/metabolization_rate = REAGENTS_METABOLISM
var/color = "#000000" // rgb: 0, 0, 0 (does not support alpha channels - yet!)
var/shock_reduction = 0
var/heart_rate_increase = 0
var/heart_rate_decrease = 0
var/heart_rate_stop = 0
var/penetrates_skin = FALSE //Whether or not a reagent penetrates the skin
//Processing flags, defines the type of mobs the reagent will affect
//By default, all reagents will ONLY affect organics, not synthetics. Re-define in the reagent's definition if the reagent is meant to affect synths
var/process_flags = ORGANIC
var/harmless = FALSE //flag used for attack logs
var/can_synth = TRUE //whether or not a mech syringe gun and synthesize this reagent
var/overdose_threshold = 0
var/addiction_chance = 0
var/addiction_chance_additional = 100 // If we want to lower the chance of addiction even more, set this
var/addiction_threshold = 0 // How much of a chem do we have to absorb before we can start rolling for its ill effects?
var/minor_addiction = FALSE
var/addiction_stage = 1
var/last_addiction_dose = 0
var/overdosed = FALSE // You fucked up and this is now triggering it's overdose effects, purge that shit quick.
var/current_cycle = 1
var/drink_icon = null
var/drink_name = "Glass of ..what?"
var/drink_desc = "You can't really tell what this is."
var/taste_mult = 1 //how easy it is to taste - the more the easier
var/taste_description = "metaphorical salt"
/datum/reagent/Destroy()
. = ..()
holder = null
if(islist(data))
data.Cut()
data = null
/datum/reagent/proc/reaction_temperature(exposed_temperature, exposed_volume) //By default we do nothing.
return
/datum/reagent/proc/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = TRUE) //Some reagents transfer on touch, others don't; dependent on if they penetrate the skin or not.
if(!holder) //for catching rare runtimes
return
if(method == REAGENT_TOUCH && penetrates_skin)
var/block = M.get_permeability_protection()
var/amount = round(volume * (1 - block), 0.1)
if(M.reagents)
if(amount >= 1)
M.reagents.add_reagent(id, amount)
if(method == REAGENT_INGEST) //Yes, even Xenos can get addicted to drugs.
var/can_become_addicted = M.reagents.reaction_check(M, src)
if(can_become_addicted)
if(is_type_in_list(src, M.reagents.addiction_list))
to_chat(M, "You feel slightly better, but for how long?") //sate_addiction handles this now, but kept this for the feed back.
var/mob/living/carbon/C = M
if(method == REAGENT_INGEST && istype(C) && C.get_blood_id() == id)
if(id == "blood" && !(data?["blood_type"] in get_safe_blood(C.dna?.blood_type)))
C.reagents.add_reagent("toxin", volume * 0.5)
else
C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL)
// This does not absorb the blood we are getting in *this* reagent transfer operation,
// (because the actual transfer has not happened yet. Because reasons) but it does process
// the blood already in the mob.
// This one only matters if the mob is dead.
M.absorb_blood()
/datum/reagent/proc/reaction_obj(obj/O, volume)
return
/datum/reagent/proc/reaction_turf(turf/T, volume, color)
return
/datum/reagent/proc/on_mob_life(mob/living/M)
current_cycle++
var/total_depletion_rate = metabolization_rate * M.metabolism_efficiency * M.digestion_ratio // Cache it
handle_addiction(M, total_depletion_rate)
sate_addiction(M)
holder.remove_reagent(id, total_depletion_rate) //By default it slowly disappears.
return STATUS_UPDATE_NONE
/datum/reagent/proc/handle_addiction(mob/living/M, consumption_rate)
if(addiction_chance && !is_type_in_list(src, M.reagents.addiction_list))
M.reagents.addiction_threshold_accumulated[id] += consumption_rate
var/current_threshold_accumulated = M.reagents.addiction_threshold_accumulated[id]
if(addiction_threshold < current_threshold_accumulated && prob(addiction_chance) && prob(addiction_chance_additional))
to_chat(M, "You suddenly feel invigorated and guilty...")
var/datum/reagent/new_reagent = new type()
new_reagent.last_addiction_dose = world.timeofday
M.reagents.addiction_list.Add(new_reagent)
/datum/reagent/proc/sate_addiction(mob/living/M) //reagents sate their own withdrawals
if(is_type_in_list(src, M.reagents.addiction_list))
for(var/A in M.reagents.addiction_list)
var/datum/reagent/AD = A
if(AD && istype(AD, src))
AD.last_addiction_dose = world.timeofday
AD.addiction_stage = 1
/datum/reagent/proc/on_mob_death(mob/living/M) //use this to have chems have a "death-triggered" effect
return
/**
* Flashfire is a proc used to log fire causing chemical reactions.
*
* Call this whenever you have a chemical reaction that makes fire flashes.
* Arguments:
* * holder: the beaker that the reagent is in
* * name: name of the reagent / reaction
*/
/proc/fire_flash_log(datum/reagents/holder, name)
if(!holder.my_atom)
return
if(holder.my_atom.fingerprintslast)
var/mob/M = get_mob_by_key(holder.my_atom.fingerprintslast)
add_attack_logs(M, COORD(holder.my_atom.loc), "Caused a flashfire reaction of [name]. Last associated key is [holder.my_atom.fingerprintslast]", ATKLOG_FEW)
log_game("Flashfire reaction ([holder.my_atom], reagent type: [name]) at [COORD(holder.my_atom.loc)]. Last touched by: [holder.my_atom.fingerprintslast ? "[holder.my_atom.fingerprintslast]" : "*null*"].")
holder.my_atom.investigate_log("A Flashfire reaction, (reagent type [name]) last touched by [holder.my_atom.fingerprintslast ? "[holder.my_atom.fingerprintslast]" : "*null*"], triggered at [COORD(holder.my_atom.loc)].", INVESTIGATE_BOMB)
// Called when this reagent is first added to a mob
/datum/reagent/proc/on_mob_add(mob/living/L)
return
// Called when this reagent is removed while inside a mob
/datum/reagent/proc/on_mob_delete(mob/living/M)
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)
return
/datum/reagent/proc/on_update(atom/A)
return
// Called every time reagent containers process.
/datum/reagent/process()
if(!holder || holder.flags & REAGENT_NOREACT)
return FALSE
return TRUE
// 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, severity)
var/effect = rand(1, 100) - severity
var/update_flags = STATUS_UPDATE_NONE
if(effect <= 8)
update_flags |= (M.adjustToxLoss(severity, FALSE) ? STATUS_UPDATE_HEALTH : STATUS_UPDATE_NONE)
return list(effect, update_flags)
/datum/reagent/proc/overdose_start(mob/living/M)
return
/datum/reagent/proc/addiction_act_stage1(mob/living/M)
return STATUS_UPDATE_NONE
/datum/reagent/proc/addiction_act_stage2(mob/living/M)
if(minor_addiction)
if(prob(4))
to_chat(M, "You briefly think about getting some more [name].")
else
if(prob(8))
M.emote("shiver")
if(prob(8))
M.emote("sneeze")
if(prob(4))
to_chat(M, "You feel a dull headache.")
return STATUS_UPDATE_NONE
/datum/reagent/proc/addiction_act_stage3(mob/living/M)
if(minor_addiction)
if(prob(4))
to_chat(M, "You could really go for some [name] right now.")
else
if(prob(8))
M.emote("twitch_s")
if(prob(8))
M.emote("shiver")
if(prob(4))
to_chat(M, "Your head hurts.")
if(prob(4))
to_chat(M, "You begin craving [name]!")
return STATUS_UPDATE_NONE
/datum/reagent/proc/addiction_act_stage4(mob/living/M)
if(minor_addiction)
if(prob(8))
to_chat(M, "You could really go for some [name] right now.")
else
if(prob(8))
M.emote("twitch")
if(prob(4))
to_chat(M, "You have a pounding headache.")
if(prob(4))
to_chat(M, "You have the strong urge for some [name]!")
else if(prob(4))
to_chat(M, "You REALLY crave some [name]!")
return STATUS_UPDATE_NONE
/datum/reagent/proc/addiction_act_stage5(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
if(minor_addiction)
if(prob(8))
to_chat(M, "You can't stop thinking about [name]...")
if(prob(4))
M.emote(pick("twitch"))
else
if(prob(6))
to_chat(M, "Your stomach lurches painfully!")
M.visible_message("[M] gags and retches!")
M.Weaken(rand(4 SECONDS, 8 SECONDS))
if(prob(8))
M.emote(pick("twitch", "twitch_s", "shiver"))
if(prob(4))
to_chat(M, "Your head is killing you!")
if(prob(5))
to_chat(M, "You feel like you can't live without [name]!")
else if(prob(5))
to_chat(M, "You would DIE for some [name] right now!")
return update_flags
/datum/reagent/proc/fakedeath(mob/living/M)
if(HAS_TRAIT(M, TRAIT_FAKEDEATH))
return
if(!(M.status_flags & CANPARALYSE))
return
M.emote("deathgasp")
ADD_TRAIT(M, TRAIT_FAKEDEATH, id)
M.updatehealth("fakedeath reagent")
/datum/reagent/proc/fakerevive(mob/living/M)
if(!HAS_TRAIT_FROM(M, TRAIT_FAKEDEATH, id))
return
if(IS_HORIZONTAL(M))
M.stand_up()
REMOVE_TRAIT(M, TRAIT_FAKEDEATH, id)
if(M.healthdoll)
M.healthdoll.cached_healthdoll_overlays.Cut()
M.updatehealth("fakedeath reagent end")