Files
Bubberstation/code/__HELPERS/reagents.dm
Rhials 45174ce0e3 New maintenance hazard: Smokey Remains (#86016)
## About The Pull Request

![tboi
unlock](https://github.com/user-attachments/assets/c1cc3d10-85b1-450b-b404-1b00eeb5715b)

This implements a new rare maintenance spawn, smokey remains. Piles of
bones that used to be Nanotrasen employees, still packed with whatever
reagent finally did them in.

Rarely replacing a crate spawn (7/1007 chance per crate spawn if I'm
doing the math right), these act as landmines for uncareful travelers in
maintenance. When you get too close, one will release a cloud of a
randomly generated reagents into the air (the same reagent every time,
unique to the instance of remains). You can (mostly) avoid this by
walking instead of running. These will "re-arm" themselves after 4-6
minutes. They're decals, so you can just sweep them away if they're a
problem.

This also slightly tweaks the original iteration of this, which I used
in my Smoking Room ruin. Don't worry about it.

I'm open to adjusting the rarity, cooldown, and selection of reagents if
need be.
## Why It's Good For The Game

Makes maintenance a bit more spicy. Maybe dangerous, maybe beneficial,
maybe it's baldium. Who knows! Better be careful just in case!

There's plenty of boring chemicals that could roll, but an interesting
one could be a nice injection of uniqueness into an otherwise dull
round. It's an infinite supply, but can only be dispensed infrequently
(at a fixed location) and would be extremely difficult to harvest.

The rarity, combined with the variety of reagents that can be picked,
make it unlikely that these will have any serious impact on a round
(it's still possible, but rarely, which is good).
## Changelog
🆑 Rhials
add: Smokey remains have appeared in maintenance. Make sure to walk when
near them!
/🆑
2024-08-28 21:50:40 +02:00

233 lines
8.2 KiB
Plaintext

/proc/chem_recipes_do_conflict(datum/chemical_reaction/r1, datum/chemical_reaction/r2)
//We have to check to see if either is competitive so can ignore it (competitive reagents are supposed to conflict)
if((r1.reaction_flags & REACTION_COMPETITIVE) || (r2.reaction_flags & REACTION_COMPETITIVE))
return FALSE
//do the non-list tests first, because they are cheaper
if(r1.required_container != r2.required_container)
return FALSE
if(r1.is_cold_recipe == r2.is_cold_recipe)
if(r1.required_temp != r2.required_temp)
//one reaction requires a more extreme temperature than the other, so there is no conflict
return FALSE
else
var/datum/chemical_reaction/cold_one = r1.is_cold_recipe ? r1 : r2
var/datum/chemical_reaction/warm_one = r1.is_cold_recipe ? r2 : r1
if(cold_one.required_temp < warm_one.required_temp)
//the range of temperatures does not overlap, so there is no conflict
return FALSE
//find the reactions with the shorter and longer required_reagents list
var/datum/chemical_reaction/long_req
var/datum/chemical_reaction/short_req
if(r1.required_reagents.len > r2.required_reagents.len)
long_req = r1
short_req = r2
else if(r1.required_reagents.len < r2.required_reagents.len)
long_req = r2
short_req = r1
else
//if they are the same length, sort instead by the length of the catalyst list
//this is important if the required_reagents lists are the same
if(r1.required_catalysts.len > r2.required_catalysts.len)
long_req = r1
short_req = r2
else
long_req = r2
short_req = r1
//check if the shorter reaction list is a subset of the longer one
var/list/overlap = r1.required_reagents & r2.required_reagents
if(overlap.len != short_req.required_reagents.len)
//there is at least one reagent in the short list that is not in the long list, so there is no conflict
return FALSE
//check to see if the shorter reaction's catalyst list is also a subset of the longer reaction's catalyst list
//if the longer reaction's catalyst list is a subset of the shorter ones, that is fine
//if the reaction lists are the same, the short reaction will have the shorter required_catalysts list, so it will register as a conflict
var/list/short_minus_long_catalysts = short_req.required_catalysts - long_req.required_catalysts
if(short_minus_long_catalysts.len)
//there is at least one unique catalyst for the short reaction, so there is no conflict
return FALSE
//if we got this far, the longer reaction will be impossible to create if the shorter one is earlier in GLOB.chemical_reactions_list_reactant_index, and will require the reagents to be added in a particular order otherwise
return TRUE
/proc/get_chemical_reaction(id)
if(!GLOB.chemical_reactions_list_reactant_index)
return
for(var/reagent in GLOB.chemical_reactions_list_reactant_index)
for(var/R in GLOB.chemical_reactions_list_reactant_index[reagent])
var/datum/reac = R
if(reac.type == id)
return R
/proc/remove_chemical_reaction(datum/chemical_reaction/R)
if(!GLOB.chemical_reactions_list_reactant_index || !R)
return
for(var/rid in R.required_reagents)
GLOB.chemical_reactions_list_reactant_index[rid] -= R
//see build_chemical_reactions_list in holder.dm for explanations
/proc/add_chemical_reaction(datum/chemical_reaction/add)
if(!GLOB.chemical_reactions_list_reactant_index || !add.required_reagents || !add.required_reagents.len)
return
var/rand_reagent = pick(add.required_reagents)
if(!GLOB.chemical_reactions_list_reactant_index[rand_reagent])
GLOB.chemical_reactions_list_reactant_index[rand_reagent] = list()
GLOB.chemical_reactions_list_reactant_index[rand_reagent] += add
//Creates foam from the reagent. Metaltype is for metal foam, notification is what to show people in textbox
/datum/reagents/proc/create_foam(foamtype, foam_volume, result_type = null, notification = null, log = FALSE)
var/location = get_turf(my_atom)
var/datum/effect_system/fluid_spread/foam/foam = new foamtype()
foam.set_up(amount = foam_volume, holder = my_atom, location = location, carry = src, result_type = result_type)
foam.start(log = log)
clear_reagents()
if(!notification)
return
for(var/mob/M in viewers(5, location))
to_chat(M, notification)
///Converts the pH into a tgui readable color - i.e. white and black text is readable over it. This is NOT the colourwheel for pHes however.
/proc/convert_ph_to_readable_color(pH)
switch(pH)
if(-INFINITY to 1)
return "red"
if(1 to 2)
return "orange"
if(2 to 3)
return "average"
if(3 to 4)
return "yellow"
if(4 to 5)
return "olive"
if(5 to 6)
return "good"
if(6 to 8)
return "green"
if(8 to 9.5)
return "teal"
if(9.5 to 11)
return "blue"
if(11 to 12.5)
return "violet"
if(12.5 to INFINITY)
return "purple"
///Converts pH to universal indicator colours. This is the colorwheel for pHes
#define CONVERT_PH_TO_COLOR(pH, color) \
switch(pH) {\
if(14 to INFINITY)\
{ color = "#462c83" }\
if(13 to 14)\
{ color = "#63459b" }\
if(12 to 13)\
{ color = "#5a51a2" }\
if(11 to 12)\
{ color = "#3853a4" }\
if(10 to 11)\
{ color = "#3f93cf" }\
if(9 to 10)\
{ color = "#0bb9b7" }\
if(8 to 9)\
{ color = "#23b36e" }\
if(7 to 8)\
{ color = "#3aa651" }\
if(6 to 7)\
{ color = "#4cb849" }\
if(5 to 6)\
{ color = "#b5d335" }\
if(4 to 5)\
{ color = "#f7ec1e" }\
if(3 to 4)\
{ color = "#fbc314" }\
if(2 to 3)\
{ color = "#f26724" }\
if(1 to 2)\
{ color = "#ef1d26" }\
if(-INFINITY to 1)\
{ color = "#c6040c" }\
}
///Returns a list of chemical_reaction datums that have the input STRING as a product
/proc/get_reagent_type_from_product_string(string)
var/input_reagent = replacetext(LOWER_TEXT(string), " ", "") //95% of the time, the reagent id is a lowercase/no spaces version of the name
if (isnull(input_reagent))
return
var/list/shortcuts = list("meth" = /datum/reagent/drug/methamphetamine)
if(shortcuts[input_reagent])
input_reagent = shortcuts[input_reagent]
else
input_reagent = find_reagent(input_reagent)
return input_reagent
///Returns reagent datum from typepath
/proc/find_reagent(input)
. = FALSE
if(GLOB.chemical_reagents_list[input]) //prefer IDs!
return input
else
return get_chem_id(input)
/proc/find_reagent_object_from_type(input)
if(GLOB.chemical_reagents_list[input]) //prefer IDs!
return GLOB.chemical_reagents_list[input]
else
return null
///Returns a random reagent object, with the option to blacklist reagents.
/proc/get_random_reagent_id(list/blacklist)
var/static/list/reagent_static_list = list() //This is static, and will be used by default if a blacklist is not passed.
var/list/reagent_list_to_process
if(blacklist) //If we do have a blacklist, we recompile a new list with the excluded reagents not present and pick from there.
reagent_list_to_process = list()
else
reagent_list_to_process = reagent_static_list
if(!reagent_list_to_process.len)
for(var/datum/reagent/reagent_path as anything in subtypesof(/datum/reagent))
if(is_path_in_list(reagent_path, blacklist))
continue
if(initial(reagent_path.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED)
reagent_list_to_process += reagent_path
var/picked_reagent = pick(reagent_list_to_process)
return picked_reagent
///Returns a random reagent consumable ethanol object minus blacklisted reagents
/proc/get_random_drink_id()
var/static/list/random_drinks = list()
if(!random_drinks.len)
for(var/datum/reagent/drink_path as anything in subtypesof(/datum/reagent/consumable/ethanol))
if(initial(drink_path.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED)
random_drinks += drink_path
var/picked_drink = pick(random_drinks)
return picked_drink
///Returns reagent datum from reagent name string
/proc/get_chem_id(chem_name)
for(var/X in GLOB.chemical_reagents_list)
var/datum/reagent/R = GLOB.chemical_reagents_list[X]
if(ckey(chem_name) == ckey(LOWER_TEXT(R.name)))
return X
///Takes a type in and returns a list of associated recipes
/proc/get_recipe_from_reagent_product(input_type)
if(!input_type)
return
var/list/matching_reactions = GLOB.chemical_reactions_list_product_index[input_type]
return matching_reactions
/proc/reagent_paths_list_to_text(list/reagents, addendum)
var/list/temp = list()
for(var/datum/reagent/R as anything in reagents)
temp |= initial(R.name)
if(addendum)
temp += addendum
return jointext(temp, ", ")