/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")