Adds a slightly-more-useful (maybe too useful) reagent recipe browser to chem and drink dispensers (#93903)

This commit is contained in:
MrMelbert
2025-11-14 23:45:24 -07:00
committed by GitHub
parent 13c4e87b3c
commit 1cb280cbf1
20 changed files with 1124 additions and 541 deletions
+42
View File
@@ -205,6 +205,48 @@
#define REACTION_TAG_PLANT (1<<19)
/// This reaction is produces a product that affects plants
#define REACTION_TAG_COMPETITIVE (1<<20)
/// Reaction produces a reagent that is a common component for other reactions
#define REACTION_TAG_COMPONENT (1<<21)
/// Denotes reactions that will immediately do something on reaction, like an explosion, smoke, etc.
#define REACTION_TAG_ACTIVE (1<<22)
/// Readable list of reagent reaction tags (in the same order as they are defined!)
#define REACTION_TAG_READABLE list(\
"BRUTE" = REACTION_TAG_BRUTE,\
"BURN" = REACTION_TAG_BURN,\
"TOXIN" = REACTION_TAG_TOXIN,\
"OXY" = REACTION_TAG_OXY,\
"HEALING" = REACTION_TAG_HEALING,\
"DAMAGING" = REACTION_TAG_DAMAGING,\
"EXPLOSIVE" = REACTION_TAG_EXPLOSIVE,\
"OTHER" = REACTION_TAG_OTHER,\
"DANGEROUS" = REACTION_TAG_DANGEROUS,\
"EASY" = REACTION_TAG_EASY,\
"MODERATE" = REACTION_TAG_MODERATE,\
"HARD" = REACTION_TAG_HARD,\
"ORGAN" = REACTION_TAG_ORGAN,\
"DRINK" = REACTION_TAG_DRINK,\
"FOOD" = REACTION_TAG_FOOD,\
"SLIME" = REACTION_TAG_SLIME,\
"DRUG" = REACTION_TAG_DRUG,\
"UNIQUE" = REACTION_TAG_UNIQUE,\
"CHEMICAL" = REACTION_TAG_CHEMICAL,\
"PLANT" = REACTION_TAG_PLANT,\
"COMPETITIVE" = REACTION_TAG_COMPETITIVE,\
"COMPONENT" = REACTION_TAG_COMPONENT,\
"ACTIVE" = REACTION_TAG_ACTIVE,\
)
/// Reaction tags for basic damgae types
#define DAMAGE_HEALING_REACTION_TAGS (REACTION_TAG_BRUTE | REACTION_TAG_BURN | REACTION_TAG_TOXIN | REACTION_TAG_OXY)
/// Reaction tags for medication
#define MEDICATION_REACTION_TAGS (REACTION_TAG_HEALING | REACTION_TAG_DAMAGING | REACTION_TAG_ORGAN | REACTION_TAG_DRUG)
/// Reaction tags for things the chemist would make
#define CHEMIST_REACTION_TAGS (REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPETITIVE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_COMPONENT)
/// Reaction tags for botanist stuff
#define BOTANIST_REACTION_TAGS (REACTION_TAG_PLANT | REACTION_TAG_COMPONENT)
/// Reaction tags for food and drink mainly
#define KITCHEN_REACTION_TAGS (REACTION_TAG_FOOD | REACTION_TAG_DRINK | REACTION_TAG_COMPONENT)
//flags used by holder.dm to locate an reagent
///Direct type
+6 -1
View File
@@ -166,7 +166,12 @@ GLOBAL_LIST_INIT(plant_traits, init_plant_traits())
if(!is_type_in_typecache(reaction.type, blacklist))
//Master list of ALL reactions that is used in the UI lookup table. This is expensive to make, and we don't want to lag the server by creating it on UI request, so it's cached to send to UIs instantly.
GLOB.chemical_reactions_results_lookup_list += list(list("name" = product_name, "id" = reaction.type, "bitflags" = bitflags, "reactants" = reagents))
GLOB.chemical_reactions_results_lookup_list += list(list(
"name" = product_name,
"id" = reaction.type,
"bitflags" = bitflags,
"reactants" = reagents,
))
// Create filters based on each reagent id in the required reagents list - this is specifically for finding reactions from product(reagent) ids/typepaths.
for(var/id in product_ids)
@@ -314,7 +314,7 @@
required_reagents = list(/datum/reagent/consumable/liquidelectricity/enriched = 2, /datum/reagent/consumable/grounding_solution = 1)
mix_message = "The mixture lets off a sharp snap as the electricity discharges."
mix_sound = 'sound/items/weapons/taser.ogg'
reaction_flags = REACTION_INSTANT
reaction_flags = REACTION_INSTANT | REACTION_TAG_ACTIVE
/datum/chemical_reaction/food/martian_batter
results = list(/datum/reagent/consumable/martian_batter = 10)
@@ -257,27 +257,8 @@
//Use GLOB list - saves processing
data["master_reaction_list"] = GLOB.chemical_reactions_results_lookup_list
data["bitflags"] = list()
data["bitflags"]["BRUTE"] = REACTION_TAG_BRUTE
data["bitflags"]["BURN"] = REACTION_TAG_BURN
data["bitflags"]["TOXIN"] = REACTION_TAG_TOXIN
data["bitflags"]["OXY"] = REACTION_TAG_OXY
data["bitflags"]["HEALING"] = REACTION_TAG_HEALING
data["bitflags"]["DAMAGING"] = REACTION_TAG_DAMAGING
data["bitflags"]["EXPLOSIVE"] = REACTION_TAG_EXPLOSIVE
data["bitflags"]["OTHER"] = REACTION_TAG_OTHER
data["bitflags"]["DANGEROUS"] = REACTION_TAG_DANGEROUS
data["bitflags"]["EASY"] = REACTION_TAG_EASY
data["bitflags"]["MODERATE"] = REACTION_TAG_MODERATE
data["bitflags"]["HARD"] = REACTION_TAG_HARD
data["bitflags"]["ORGAN"] = REACTION_TAG_ORGAN
data["bitflags"]["DRINK"] = REACTION_TAG_DRINK
data["bitflags"]["FOOD"] = REACTION_TAG_FOOD
data["bitflags"]["SLIME"] = REACTION_TAG_SLIME
data["bitflags"]["DRUG"] = REACTION_TAG_DRUG
data["bitflags"]["UNIQUE"] = REACTION_TAG_UNIQUE
data["bitflags"]["CHEMICAL"] = REACTION_TAG_CHEMICAL
data["bitflags"]["PLANT"] = REACTION_TAG_PLANT
data["bitflags"]["COMPETITIVE"] = REACTION_TAG_COMPETITIVE
for(var/readable_flag, real_flag in REACTION_TAG_READABLE)
data["bitflags"][readable_flag] = real_flag
return data
@@ -407,5 +388,12 @@
if("toggle_tag_competitive")
ui_tags_selected = ui_tags_selected ^ REACTION_TAG_COMPETITIVE
return TRUE
if("toggle_tag_component")
ui_tags_selected = ui_tags_selected ^ REACTION_TAG_COMPONENT
return TRUE
if("toggle_tag_active")
ui_tags_selected = ui_tags_selected ^ REACTION_TAG_ACTIVE
return TRUE
if("update_ui")
return TRUE
@@ -23,7 +23,7 @@
/// If the UI has the pH meter shown
var/show_ph = TRUE
/// The overlay used to display the beaker on the machine
var/mutable_appearance/beaker_overlay
VAR_PRIVATE/mutable_appearance/beaker_overlay
/// Icon to display when the machine is powered
var/working_state = "dispenser_working"
/// Icon to display when the machine is not powered
@@ -41,9 +41,14 @@
/// Starting purity of the created reagents
var/base_reagent_purity = 1
/// Records the reagents dispensed by the user if this list is not null
var/list/recording_recipe
VAR_PRIVATE/list/recording_recipe
/// Saves all the recipes recorded by the machine
var/list/saved_recipes = list()
VAR_PRIVATE/list/saved_recipes = list()
/// Filters out all reactions that don't have any of these tags from the reaction list
var/shown_reaction_tags = DAMAGE_HEALING_REACTION_TAGS | MEDICATION_REACTION_TAGS | CHEMIST_REACTION_TAGS
/// Filters out all reactions that have any one of these tags from the reaction list
var/hidden_reaction_tags = REACTION_TAG_ACTIVE | REACTION_TAG_FOOD | REACTION_TAG_DRINK
/// The default list of dispensable_reagents
var/static/list/default_dispensable_reagents = list(
@@ -90,6 +95,7 @@
/datum/reagent/drug/space_drugs,
/datum/reagent/toxin
)
/obj/machinery/chem_dispenser/Initialize(mapload)
if(dispensable_reagents != null && !dispensable_reagents.len)
dispensable_reagents = default_dispensable_reagents
@@ -257,6 +263,18 @@
beaker_data["contents"] = beakerContents
.["beaker"] = beaker_data
/obj/machinery/chem_dispenser/ui_static_data(mob/user)
var/list/data = list()
data["reaction_list"] = get_reaction_list()
data["all_bitflags"] = list()
for(var/readable_flag, real_flag in REACTION_TAG_READABLE)
if((real_flag & hidden_reaction_tags) || !(real_flag & shown_reaction_tags))
continue
data["all_bitflags"][readable_flag] = real_flag
return data
/obj/machinery/chem_dispenser/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
@@ -504,6 +522,61 @@
/obj/machinery/chem_dispenser/attack_ai_secondary(mob/user, list/modifiers)
return attack_hand_secondary(user, modifiers)
/obj/machinery/chem_dispenser/proc/get_reaction_list()
var/static/list/reaction_list
if(reaction_list?[type])
return reaction_list[type]
reaction_list ||= list()
reaction_list[type] = list()
var/list/new_reaction_list = list()
for(var/result, reactions in GLOB.chemical_reactions_list_product_index - dispensable_reagents)
var/datum/reagent/result_datum = GLOB.chemical_reagents_list[result]
for(var/datum/chemical_reaction/reaction as anything in reactions)
if(!(reaction.reaction_tags & shown_reaction_tags))
continue
if(reaction.reaction_tags & hidden_reaction_tags)
continue
if(reaction.required_container)
continue
var/index = result_datum.name
var/list/new_info = get_reaction_info(reaction)
new_info["description"] = result_datum.description
new_info["color"] = result_datum.color
var/num_alts = 0
while(new_reaction_list[index])
num_alts++
index = "[result_datum.name] (Alt[num_alts == 1 ? "" : " #[num_alts]"])"
new_reaction_list[index] = new_info
reaction_list[type] = new_reaction_list
return reaction_list[type]
/obj/machinery/chem_dispenser/proc/get_reaction_info(datum/chemical_reaction/reaction)
var/list/info = list()
info["id"] = reaction.type
info["lower_temperature"] = reaction.required_temp
info["upper_temperature"] = reaction.optimal_temp
info["lower_ph"] = reaction.optimal_ph_min
info["upper_ph"] = reaction.optimal_ph_max
info["bitflags"] = reaction.reaction_tags
info["required_reagents"] = reagent_list_to_info(reaction.required_reagents)
info["required_catalysts"] = reagent_list_to_info(reaction.required_catalysts)
return info
/obj/machinery/chem_dispenser/proc/reagent_list_to_info(list/reagent_list)
var/list/info = list()
for(var/datum/reagent/reagent_typepath as anything in reagent_list)
info += list(list(
"name" = reagent_typepath::name,
"amount" = reagent_list[reagent_typepath],
"typepath" = reagent_typepath,
))
return info
/obj/machinery/chem_dispenser/drinks
name = "soda dispenser"
@@ -520,6 +593,8 @@
nopower_state = null
pass_flags = PASSTABLE
show_ph = FALSE
shown_reaction_tags = KITCHEN_REACTION_TAGS
hidden_reaction_tags = REACTION_TAG_ACTIVE
/// The default list of reagents dispensable by the soda dispenser
var/static/list/drinks_dispensable_reagents = list(
/datum/reagent/consumable/coffee,
@@ -676,6 +751,8 @@
name = "botanical chemical dispenser"
desc = "Creates and dispenses chemicals useful for botany."
circuit = /obj/item/circuitboard/machine/chem_dispenser/mutagensaltpeter
shown_reaction_tags = BOTANIST_REACTION_TAGS
hidden_reaction_tags = REACTION_TAG_ACTIVE
/// The default list of dispensable reagents available in the mutagensaltpeter chem dispenser
var/static/list/mutagensaltpeter_dispensable_reagents = list(
@@ -1422,7 +1422,7 @@
/datum/reagent/consumable/ethanol/neurotoxin
name = "Neurotoxin"
description = "A strong neurotoxin that puts the subject into a death-like state."
description = "A strong neurotoxin that puts the patient into a death-like state."
color = "#2E2E61" // rgb: 46, 46, 97
boozepwr = 50
quality = DRINK_VERYGOOD
@@ -1164,7 +1164,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/strawberry_banana
name = "strawberry banana smoothie"
name = "Strawberry Banana Smoothie"
description = "A classic smoothie made from strawberries and bananas."
color = "#FF9999"
nutriment_factor = 0
@@ -1172,7 +1172,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/berry_blast
name = "berry blast smoothie"
name = "Berry Blast Smoothie"
description = "A classic smoothie made from mixed berries."
color = "#A76DC5"
nutriment_factor = 0
@@ -1180,7 +1180,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/funky_monkey
name = "funky monkey smoothie"
name = "Funky Monkey Smoothie"
description = "A classic smoothie made from chocolate and bananas."
color = COLOR_BROWNER_BROWN
nutriment_factor = 0
@@ -1188,7 +1188,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/green_giant
name = "green giant smoothie"
name = "Green Giant Smoothie"
description = "A green vegetable smoothie, made without vegetables."
color = COLOR_VERY_DARK_LIME_GREEN
nutriment_factor = 0
@@ -1196,7 +1196,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/melon_baller
name = "melon baller smoothie"
name = "Melon Baller Smoothie"
description = "A classic smoothie made from melons."
color = "#D22F55"
nutriment_factor = 0
@@ -1204,7 +1204,7 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/consumable/vanilla_dream
name = "vanilla dream smoothie"
name = "Vanilla Dream Smoothie"
description = "A classic smoothie made from vanilla and fresh cream."
color = "#FFF3DD"
nutriment_factor = 0
@@ -983,7 +983,8 @@
/datum/reagent/medicine/epinephrine
name = "Epinephrine"
description = "Very minor boost to stun resistance. Slowly heals damage if a patient is in critical condition, as well as regulating oxygen loss. Overdose causes weakness and toxin damage."
description = "Stabilizes and slowly heals patients in critical condition, and slows suffocation. \
Also provides a very minor boost to stun resistance. Overdose causes weakness and toxin damage."
color = "#D2FFFA"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
overdose_threshold = 30
@@ -1080,7 +1080,7 @@
/datum/reagent/glycerol
name = "Glycerol"
description = "Glycerol is a simple polyol compound. Glycerol is sweet-tasting and of low toxicity."
description = "A simple polyol compound. Sweet-tasting and of low toxicity."
color = "#D3B913"
taste_description = "sweetness"
ph = 9
@@ -1404,7 +1404,7 @@
/datum/reagent/impedrezene
name = "Impedrezene"
description = "Impedrezene is a narcotic that impedes one's ability by slowing down the higher brain cell functions."
description = "A narcotic that impedes one's ability by slowing down the higher brain cell functions."
color = "#E07DDD" // pink = happy = dumb
taste_description = "numbness"
ph = 9.1
@@ -2074,21 +2074,21 @@
/datum/reagent/pentaerythritol
name = "Pentaerythritol"
description = "Slow down, it ain't no spelling bee!"
color = "#E66FFF"
description = "A crystalline compound used in the synthesis of explosives and other chemicals."
color = "#EEEEEF"
taste_description = "acid"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/acetaldehyde
name = "Acetaldehyde"
description = "Similar to plastic. Tastes like dead people."
description = "A colorless liquid with a strong smell. Used in the synthesis of other chemicals."
color = "#EEEEEF"
taste_description = "dead people" //made from formaldehyde, ya get da joke ?
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
/datum/reagent/acetone_oxide
name = "Acetone Oxide"
description = "Enslaved oxygen"
description = "A highly reactive compoud derived from acetone. Known to cause burns on contact. Used in the synthesis of various explosives."
color = "#966199cb"
taste_description = "acid"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
@@ -2115,7 +2115,7 @@
/datum/reagent/ash
name = "Ash"
description = "Supposedly phoenixes rise from these, but you've never seen it."
description = "A fine ash. Supposedly phoenixes rise from these, but you've never seen it."
color = "#515151"
taste_description = "ash"
ph = 6.5
@@ -2530,7 +2530,7 @@
/datum/reagent/plastic_polymers
name = "Plastic Polymers"
description = "the petroleum based components of plastic."
description = "Petroleum based components of plastic."
color = "#f7eded"
taste_description = "plastic"
ph = 6
@@ -42,7 +42,7 @@
/datum/reagent/clf3
name = "Chlorine Trifluoride"
description = "Makes a temporary 3x3 fireball when it comes into existence, so be careful when mixing. ClF3 applied to a surface burns things that wouldn't otherwise burn, sometimes through the very floors of the station and exposing it to the vacuum of space."
description = "A very flammable liquid capable of burning even through the hull of the station. Bursts into a fireball upon creation."
color = "#FFC8C8"
metabolization_rate = 10 * REAGENTS_METABOLISM
taste_description = "burning"
@@ -248,7 +248,7 @@
/datum/reagent/toxin/zombiepowder
name = "Zombie Powder"
description = "A strong neurotoxin that puts the subject into a death-like state."
description = "A strong neurotoxin that puts the patient into a death-like state."
silent_toxin = TRUE
creation_purity = REAGENT_STANDARD_PURITY
purity = REAGENT_STANDARD_PURITY
@@ -705,7 +705,8 @@
/datum/reagent/toxin/formaldehyde
name = "Formaldehyde"
description = "Formaldehyde, on its own, is a fairly weak toxin. It contains trace amounts of Histamine, very rarely making it decay into Histamine. When used in a dead body, will prevent organ decay."
description = "A fairly weak toxin that helps prevent organ decay in dead bodies. \
It will slowly decay into Histamine over time."
silent_toxin = TRUE
color = "#B4004B"
metabolization_rate = 0.5 * REAGENTS_METABOLISM
@@ -766,7 +767,7 @@
/datum/reagent/toxin/fentanyl
name = "Fentanyl"
description = "Fentanyl will inhibit brain function and cause toxin damage before eventually knocking out its victim."
description = "Inhibits brain function and causes toxin damage before eventually knocking out the patient."
color = "#64916E"
metabolization_rate = 0.5 * REAGENTS_METABOLISM
creation_purity = REAGENT_STANDARD_PURITY
@@ -1046,7 +1047,8 @@
/datum/reagent/toxin/heparin //Based on a real-life anticoagulant. I'm not a doctor, so this won't be realistic.
name = "Heparin"
description = "A powerful anticoagulant. All open cut wounds on the victim will open up and bleed much faster. It directly purges sanguirite, a coagulant."
description = "A powerful anticoagulant. All open cut wounds on the patient will open up and bleed much faster. \
Counters coagulants like Sanguirite, purging them."
silent_toxin = TRUE
creation_purity = REAGENT_STANDARD_PURITY
purity = REAGENT_STANDARD_PURITY
@@ -1064,7 +1066,7 @@
/datum/reagent/toxin/rotatium //Rotatium. Fucks up your rotation and is hilarious
name = "Rotatium"
description = "A constantly swirling, oddly colourful fluid. Causes the consumer's sense of direction and hand-eye coordination to become wild."
description = "A constantly swirling, oddly colourful fluid. Causes the patient's sense of direction and hand-eye coordination to become wild."
silent_toxin = TRUE
creation_purity = REAGENT_STANDARD_PURITY
purity = REAGENT_STANDARD_PURITY
@@ -1164,7 +1166,7 @@
/datum/reagent/toxin/acid/fluacid
name = "Fluorosulfuric Acid"
description = "Fluorosulfuric acid is an extremely corrosive chemical substance."
description = "An extremely corrosive chemical substance."
color = "#5050FF"
creation_purity = REAGENT_STANDARD_PURITY
purity = REAGENT_STANDARD_PURITY
@@ -1186,7 +1188,7 @@
/datum/reagent/toxin/acid/nitracid
name = "Nitric Acid"
description = "Nitric acid is an extremely corrosive chemical substance that violently reacts with living organic tissue."
description = "An extremely corrosive chemical substance that violently reacts with living organic tissue."
color = "#5050FF"
creation_purity = REAGENT_STANDARD_PURITY
purity = REAGENT_STANDARD_PURITY
@@ -26,7 +26,7 @@
rate_up_lim = 12.5
purity_min = 0.5 //100u will natrually just dip under this w/ no buffer
reaction_flags = REACTION_HEAT_ARBITARY //Heating up is arbitary because of submechanics of this reaction.
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DRUG | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DRUG | REACTION_TAG_DANGEROUS | REACTION_TAG_ORGAN
//The less pure it is, the faster it heats up. tg please don't hate me for making your meth even more dangerous
/datum/chemical_reaction/methamphetamine/reaction_step(datum/reagents/holder, datum/equilibrium/reaction, delta_t, delta_ph, step_reaction_vol)
@@ -160,7 +160,7 @@
required_reagents = list(/datum/reagent/kronkus_extract = 15, /datum/reagent/fuel = 5, /datum/reagent/ammonia = 3)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING | REACTION_TAG_ACTIVE
/datum/chemical_reaction/moon_rock/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -176,7 +176,7 @@
required_reagents = list(/datum/reagent/silver = 10, /datum/reagent/toxin/cyanide = 10, /datum/reagent/lye = 5)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING | REACTION_TAG_ACTIVE
/datum/chemical_reaction/blastoff_ampoule/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -189,7 +189,7 @@
required_reagents = list(/datum/reagent/lead = 5, /datum/reagent/consumable/nothing = 5, /datum/reagent/drug/maint/tar = 10)
mob_react = FALSE
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DRUG | REACTION_TAG_ORGAN | REACTION_TAG_DAMAGING | REACTION_TAG_ACTIVE
/datum/chemical_reaction/saturnx_glob/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -169,7 +169,7 @@
results = list(/datum/reagent/medicine/albuterol = 4, /datum/reagent/medicine/sal_acid = 0.5, /datum/reagent/ammonia = 0.5)
required_catalysts = list(/datum/reagent/toxin/acid = 1)
required_reagents = list(/datum/reagent/medicine/salbutamol = 5, /datum/reagent/medicine/c2/convermol = 1)
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_ORGAN | REACTION_TAG_OTHER
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_ORGAN | REACTION_TAG_OTHER | REACTION_TAG_ACTIVE
required_temp = 500
optimal_temp = 610
overheat_temp = 980
@@ -181,7 +181,7 @@
results = list(/datum/reagent/medicine/salbutamol = 2, /datum/reagent/ammonia = 1)
required_catalysts = list(/datum/reagent/toxin/acid = 1)
required_reagents = list(/datum/reagent/medicine/albuterol = 3, /datum/reagent/oxygen = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_ORGAN | REACTION_TAG_OTHER
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_ORGAN | REACTION_TAG_OTHER | REACTION_TAG_ACTIVE
required_temp = 300
optimal_temp = 500
overheat_temp = 800
@@ -191,7 +191,7 @@
results = list(/datum/reagent/inverse/healing/convermol = 1, /datum/reagent/lithium = 3, /datum/reagent/aluminium = 3, /datum/reagent/bromine = 3)
required_catalysts = list(/datum/reagent/toxin/acid/fluacid = 1)
required_reagents = list(/datum/reagent/medicine/albuterol = 5)
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_ORGAN | REACTION_TAG_OTHER
reaction_tags = REACTION_TAG_MODERATE | REACTION_TAG_ORGAN | REACTION_TAG_OTHER | REACTION_TAG_ACTIVE
required_temp = 900
optimal_temp = 920
overheat_temp = 990
@@ -401,7 +401,7 @@
/datum/chemical_reaction/medicine/medsuture
required_reagents = list(/datum/reagent/cellulose = 2, /datum/reagent/toxin/formaldehyde = 4, /datum/reagent/medicine/polypyr = 3) //This might be a bit much, reagent cost should be reviewed after implementation.
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE | REACTION_TAG_ACTIVE
/datum/chemical_reaction/medicine/medsuture/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
new /obj/item/stack/medical/suture/medicated(get_turf(holder.my_atom), round(created_volume * 4))
@@ -409,7 +409,7 @@
/datum/chemical_reaction/medicine/medmesh
required_reagents = list(/datum/reagent/cellulose = 2, /datum/reagent/consumable/aloejuice = 4, /datum/reagent/space_cleaner/sterilizine = 2)
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BURN | REACTION_TAG_ACTIVE
/datum/chemical_reaction/medicine/medmesh/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
new /obj/item/stack/medical/mesh/advanced(get_turf(holder.my_atom), round(created_volume * 3))
@@ -417,7 +417,7 @@
/datum/chemical_reaction/medicine/poultice
required_reagents = list(/datum/reagent/toxin/bungotoxin = 4, /datum/reagent/cellulose = 4, /datum/reagent/consumable/aloejuice = 4 )
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE | REACTION_TAG_BURN
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_HEALING | REACTION_TAG_BRUTE | REACTION_TAG_BURN | REACTION_TAG_ACTIVE
/datum/chemical_reaction/medicine/poultice/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
new /obj/item/stack/medical/poultice(get_turf(holder.my_atom), round(created_volume * 3))
@@ -42,7 +42,7 @@
/datum/chemical_reaction/sodiumchloride
results = list(/datum/reagent/consumable/salt = 2)
required_reagents = list(/datum/reagent/sodium = 1, /datum/reagent/chlorine = 1) // That's what I said! Sodium Chloride!
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_FOOD
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_FOOD | REACTION_TAG_COMPONENT
required_other = TRUE
/datum/chemical_reaction/sodiumchloride/pre_reaction_other_checks(datum/reagents/holder)
@@ -233,6 +233,7 @@
var/level_min = 1
var/level_max = 2
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_ACTIVE
/datum/chemical_reaction/mix_virus/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list
@@ -386,18 +387,18 @@
optimal_ph_min = 1 // Lets increase our range for this basic chem
optimal_ph_max = 12
H_ion_release = -0.02 //handmade is more neutral
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT | REACTION_TAG_COMPONENT
/datum/chemical_reaction/diethylamine
results = list(/datum/reagent/diethylamine = 2)
required_reagents = list (/datum/reagent/ammonia = 1, /datum/reagent/consumable/ethanol = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT | REACTION_TAG_COMPONENT
/datum/chemical_reaction/space_cleaner
results = list(/datum/reagent/space_cleaner = 2)
required_reagents = list(/datum/reagent/ammonia = 1, /datum/reagent/water = 1)
rate_up_lim = 40
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_COMPONENT
/datum/chemical_reaction/plantbgone
results = list(/datum/reagent/toxin/plantbgone = 5)
@@ -418,14 +419,14 @@
/datum/chemical_reaction/drying_agent
results = list(/datum/reagent/drying_agent = 3)
required_reagents = list(/datum/reagent/stable_plasma = 2, /datum/reagent/consumable/ethanol = 1, /datum/reagent/sodium = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE | REACTION_TAG_COMPONENT
//////////////////////////////////// Other goon stuff ///////////////////////////////////////////
/datum/chemical_reaction/acetone
results = list(/datum/reagent/acetone = 3)
required_reagents = list(/datum/reagent/fuel/oil = 1, /datum/reagent/fuel = 1, /datum/reagent/oxygen = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
/datum/chemical_reaction/carpet
results = list(/datum/reagent/carpet = 2)
@@ -526,18 +527,18 @@
/datum/chemical_reaction/oil
results = list(/datum/reagent/fuel/oil = 3)
required_reagents = list(/datum/reagent/fuel = 1, /datum/reagent/carbon = 1, /datum/reagent/hydrogen = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
/datum/chemical_reaction/phenol
results = list(/datum/reagent/phenol = 3)
required_reagents = list(/datum/reagent/water = 1, /datum/reagent/chlorine = 1, /datum/reagent/fuel/oil = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
/datum/chemical_reaction/ash
results = list(/datum/reagent/ash = 1)
required_reagents = list(/datum/reagent/fuel/oil = 1)
required_temp = 480
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT | REACTION_TAG_COMPONENT
/datum/chemical_reaction/colorful_reagent
results = list(/datum/reagent/colorful_reagent = 5)
@@ -643,19 +644,19 @@
/datum/chemical_reaction/electrolysis
results = list(/datum/reagent/oxygen = 2.5, /datum/reagent/hydrogen = 5)
required_reagents = list(/datum/reagent/consumable/liquidelectricity = 1, /datum/reagent/water = 5)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
/datum/chemical_reaction/electrolysis2
results = list(/datum/reagent/oxygen = 2.5, /datum/reagent/hydrogen = 5)
required_reagents = list(/datum/reagent/consumable/liquidelectricity/enriched = 1, /datum/reagent/water = 5)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
//salt electrolysis
/datum/chemical_reaction/saltelectrolysis
results = list(/datum/reagent/chlorine = 2.5, /datum/reagent/sodium = 2.5)
required_reagents = list(/datum/reagent/consumable/salt = 5)
required_catalysts = list(/datum/reagent/consumable/liquidelectricity = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
/datum/chemical_reaction/saltelectrolysis/enriched
required_catalysts = list(/datum/reagent/consumable/liquidelectricity/enriched = 1)
@@ -722,18 +723,18 @@
/datum/chemical_reaction/saltpetre
results = list(/datum/reagent/saltpetre = 3)
required_reagents = list(/datum/reagent/potassium = 1, /datum/reagent/nitrogen = 1, /datum/reagent/oxygen = 3)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_PLANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_PLANT | REACTION_TAG_COMPONENT
/datum/chemical_reaction/lye
results = list(/datum/reagent/lye = 3)
required_reagents = list(/datum/reagent/sodium = 1, /datum/reagent/hydrogen = 1, /datum/reagent/oxygen = 1)
required_temp = 10 //So hercuri still shows life.
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
/datum/chemical_reaction/lye2
results = list(/datum/reagent/lye = 2)
required_reagents = list(/datum/reagent/ash = 1, /datum/reagent/water = 1, /datum/reagent/carbon = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
/datum/chemical_reaction/royal_bee_jelly
results = list(/datum/reagent/royal_bee_jelly = 5)
@@ -901,7 +902,7 @@
purity_min = 0
mix_message = "The solution freezes up into ice!"
reaction_flags = REACTION_COMPETITIVE
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_DRINK
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_DRINK | REACTION_TAG_COMPONENT | REACTION_TAG_ACTIVE
/datum/chemical_reaction/water
results = list(/datum/reagent/water = 0.92)//rough density excahnge
@@ -1092,5 +1093,3 @@
glitter.data["colors"] = list("[accumulated_color]" = 100)
glitter.color = accumulated_color
@@ -11,7 +11,7 @@
var/strengthdiv = 10
var/modifier = 0
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EXPLOSIVE | REACTION_TAG_MODERATE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EXPLOSIVE | REACTION_TAG_MODERATE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
required_temp = 0 //Prevent impromptu RPGs
// Only clear mob reagents in special cases
var/clear_mob_reagents = FALSE
@@ -189,7 +189,7 @@
/datum/chemical_reaction/gunpowder
results = list(/datum/reagent/gunpowder = 3)
required_reagents = list(/datum/reagent/saltpetre = 1, /datum/reagent/medicine/c2/multiver = 1, /datum/reagent/sulfur = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL
/datum/chemical_reaction/reagent_explosion/gunpowder_explosion
required_reagents = list(/datum/reagent/gunpowder = 1)
@@ -208,7 +208,7 @@
/datum/chemical_reaction/emp_pulse
required_reagents = list(/datum/reagent/uranium = 1, /datum/reagent/iron = 1, /datum/reagent/aluminium = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/emp_pulse/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
//pretending this reaction took two ingredients and not three for its effects
@@ -228,7 +228,7 @@
/datum/chemical_reaction/beesplosion
required_reagents = list(/datum/reagent/consumable/honey = 1, /datum/reagent/medicine/strange_reagent = 1, /datum/reagent/uranium/radium = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/beesplosion/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = holder.my_atom.drop_location()
@@ -251,7 +251,7 @@
/datum/chemical_reaction/stabilizing_agent
results = list(/datum/reagent/stabilizing_agent = 3)
required_reagents = list(/datum/reagent/iron = 1, /datum/reagent/oxygen = 1, /datum/reagent/hydrogen = 1)
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_CHEMICAL | REACTION_TAG_PLANT | REACTION_TAG_COMPONENT
/datum/chemical_reaction/clf3
results = list(/datum/reagent/clf3 = 4)
@@ -300,7 +300,7 @@
/datum/chemical_reaction/sorium_vortex
required_reagents = list(/datum/reagent/sorium = 1)
required_temp = 474
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/sorium_vortex/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/turf/T = get_turf(holder.my_atom)
@@ -323,7 +323,7 @@
/datum/chemical_reaction/ldm_vortex
required_reagents = list(/datum/reagent/liquid_dark_matter = 1)
required_temp = 474
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/ldm_vortex/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/turf/T = get_turf(holder.my_atom)
@@ -356,7 +356,7 @@
/datum/chemical_reaction/flash_powder_flash
required_reagents = list(/datum/reagent/flash_powder = 1)
required_temp = 374
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/flash_powder_flash/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -397,7 +397,7 @@
required_temp = 374
mob_react = FALSE
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/smoke_powder_smoke/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -427,7 +427,7 @@
/datum/chemical_reaction/sonic_powder_deafen
required_reagents = list(/datum/reagent/sonic_powder = 1)
required_temp = 374
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_EXPLOSIVE | REACTION_TAG_DANGEROUS | REACTION_TAG_ACTIVE
/datum/chemical_reaction/sonic_powder_deafen/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
@@ -177,7 +177,7 @@
rate_up_lim = 10
purity_min = 0.7
reaction_flags = REACTION_PH_VOL_CONSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DAMAGING | REACTION_TAG_PLANT | REACTION_TAG_OTHER
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DAMAGING | REACTION_TAG_PLANT | REACTION_TAG_OTHER | REACTION_TAG_COMPONENT
/datum/chemical_reaction/lexorin
results = list(/datum/reagent/toxin/lexorin = 3)
@@ -204,7 +204,7 @@
required_reagents = list(/datum/reagent/toxin/hot_ice = 1)
required_temp = T0C + 30 //Don't burst into flames when you melt
thermic_constant = -200//Counter the heat
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DAMAGING | REACTION_TAG_CHEMICAL | REACTION_TAG_TOXIN
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_DAMAGING | REACTION_TAG_CHEMICAL | REACTION_TAG_TOXIN | REACTION_TAG_ACTIVE
/datum/chemical_reaction/chloralhydrate
results = list(/datum/reagent/toxin/chloralhydrate = 1)
+701 -204
View File
@@ -1,18 +1,24 @@
import { useState } from 'react';
import {
BlockQuote,
Box,
Button,
Collapsible,
Icon,
Input,
LabeledList,
NoticeBox,
ProgressBar,
Section,
Stack,
Tooltip,
} from 'tgui-core/components';
import type { BooleanLike } from 'tgui-core/react';
import { toTitleCase } from 'tgui-core/string';
import { createSearch, toTitleCase } from 'tgui-core/string';
import { useBackend } from '../backend';
import { useBackend, useSharedState } from '../backend';
import { Window } from '../layouts';
import { type Beaker, BeakerDisplay } from './common/BeakerDisplay';
import { bitflagInfo } from './Reagents/types';
type DispensableReagent = {
title: string;
@@ -26,6 +32,33 @@ type TransferableBeaker = Beaker & {
transferAmounts: number[];
};
type ReactionTypepath = string;
type ReagentTypepath = string;
type ReactionComponent = {
name: string;
amount: number;
id: ReagentTypepath;
};
type Reaction = {
id: ReactionTypepath;
bitflags: number;
lower_temperature: number;
upper_temperature: number;
lower_ph: number;
upper_ph: number;
required_reagents: ReactionComponent[];
required_catalysts: ReactionComponent[];
description: string;
color: string; // hex
};
type ReagentReaction = {
name: string;
reaction: Reaction;
};
type Data = {
showpH: BooleanLike;
amount: number;
@@ -39,13 +72,45 @@ type Data = {
recipeReagents: string[];
beaker: TransferableBeaker;
hasBeakerInHand: BooleanLike;
// static
reaction_list: Record<string, Reaction>;
all_bitflags: Record<string, number>;
};
function reagentListToArray(
reagentList: Record<string, Reaction>,
): ReagentReaction[] {
return Object.entries(reagentList).map(([name, reaction]) => ({
name: name,
reaction: reaction,
}));
}
export const ChemDispenser = (props) => {
const { act, data } = useBackend<Data>();
const recording = !!data.recordingRecipe;
const { recipeReagents = [], recipes = [], beaker, hasBeakerInHand } = data;
const [showPhCol, setShowPhCol] = useState(false);
const {
recipes = [],
beaker,
hasBeakerInHand,
reaction_list,
all_bitflags,
chemicals,
} = data;
const [showPhCol, setShowPhCol] = useSharedState('showbaseph', false);
const [showReactionList, setShowReactionList] = useSharedState(
'showreactions',
false,
);
const [searchTerm, setSearchTerm] = useSharedState('searchterm', '');
const [filterByBitflag, setFilterByBitflag] = useSharedState<number>(
'filterbitflag',
0,
);
const [pinnedReactions, setPinnedReactions] = useSharedState<string[]>(
'pinnedreactions',
[],
);
const beakerTransferAmounts = beaker ? beaker.transferAmounts : [];
const recordedContents =
@@ -56,213 +121,645 @@ export const ChemDispenser = (props) => {
volume: data.recordingRecipe[id],
}));
// convert reagent list record to list of ReagentReaction
const reactionReagentList = reagentListToArray(reaction_list);
const reactionSearch = createSearch(
searchTerm,
(reaction: ReagentReaction) => reaction.name,
);
// filter the reaction list first by whitelist bitflags, then by search term
const filteredReactions = reactionReagentList
.filter((reaction) => {
// filter by whitelist bitflags
if (
filterByBitflag !== 0 &&
(reaction.reaction.bitflags & filterByBitflag) !== filterByBitflag
)
return false;
// filter base reagents
if (chemicals.find((chem) => chem.title === reaction.name)) return false;
// filter by search term
return reactionSearch(reaction);
})
.sort((a, b) => (a.name > b.name ? 1 : -1))
.sort((a, b) => {
// pinned reactions go first
const aPinned = pinnedReactions.includes(a.name);
const bPinned = pinnedReactions.includes(b.name);
if (aPinned && !bPinned) return -1;
if (!aPinned && bPinned) return 1;
return 0;
});
const mainWidth = 565;
const reactionWidth = 245;
const windowWidth = mainWidth + (showReactionList ? reactionWidth : 0);
return (
<Window width={565} height={620}>
<Window width={windowWidth} height={620}>
<Window.Content scrollable>
<Section
title="Status"
buttons={
<>
{recording && (
<Box inline mx={1} color="red">
<Icon name="circle" mr={1} />
Recording
</Box>
)}
<Button
icon="book"
disabled={!beaker}
tooltip={
beaker
? 'Look up recipes and reagents!'
: 'Please insert a beaker!'
}
tooltipPosition="bottom-start"
onClick={() => act('reaction_lookup')}
>
Reaction search
</Button>
<Button
icon="cog"
tooltip="Color code the reagents by pH"
tooltipPosition="bottom-start"
selected={showPhCol}
onClick={() => setShowPhCol(!showPhCol)}
/>
</>
}
>
<LabeledList>
<LabeledList.Item label="Energy">
<ProgressBar value={data.energy / data.maxEnergy}>
{data.displayedUnits +
' / ' +
data.displayedMaxUnits +
' units'}
</ProgressBar>
</LabeledList.Item>
</LabeledList>
</Section>
<Section
title="Recipes"
buttons={
<>
{!recording && (
<Box inline mx={1}>
<Button
color="transparent"
onClick={() => act('clear_recipes')}
>
Clear recipes
</Button>
</Box>
)}
{!recording && (
<Button
icon="circle"
disabled={!beaker}
onClick={() => act('record_recipe')}
<Stack fill>
<Stack.Item width={mainWidth} gr>
<Stack vertical fill>
<Stack.Item>
<Section
title="Status"
buttons={
<>
{recording && (
<Box inline mx={1} color="red">
<Icon name="circle" mr={1} />
Recording
</Box>
)}
<Button
icon="cog"
tooltip="Color code the reagents by pH"
tooltipPosition="bottom-start"
selected={showPhCol}
onClick={() => setShowPhCol(!showPhCol)}
/>
<Button
icon="book"
disabled={!beaker}
tooltip={
beaker
? 'Look up recipes and reagents!'
: 'Please insert a beaker!'
}
tooltipPosition="bottom-start"
onClick={() => act('reaction_lookup')}
>
Reactions
</Button>
<Button
icon={showReactionList ? 'arrow-left' : 'arrow-right'}
tooltipPosition="bottom-start"
onClick={() => setShowReactionList(!showReactionList)}
>
Recipes
</Button>
</>
}
>
Record
</Button>
)}
{recording && (
<Button
icon="ban"
color="transparent"
onClick={() => act('cancel_recording')}
<LabeledList>
<LabeledList.Item label="Energy">
<ProgressBar value={data.energy / data.maxEnergy}>
{data.displayedUnits +
' / ' +
data.displayedMaxUnits +
' units'}
</ProgressBar>
</LabeledList.Item>
</LabeledList>
</Section>
</Stack.Item>
<Stack.Item>
<Section
title="Custom Recipes"
buttons={
<>
{!recording && (
<Box inline mx={1}>
<Button
color="transparent"
onClick={() => act('clear_recipes')}
>
Clear recipes
</Button>
</Box>
)}
{!recording && (
<Button
icon="circle"
disabled={!beaker}
onClick={() => act('record_recipe')}
>
Record
</Button>
)}
{recording && (
<Button
icon="ban"
color="transparent"
onClick={() => act('cancel_recording')}
>
Discard
</Button>
)}
{recording && (
<Button
icon="save"
color="green"
onClick={() => act('save_recording')}
>
Save
</Button>
)}
</>
}
>
Discard
</Button>
)}
{recording && (
<Button
icon="save"
color="green"
onClick={() => act('save_recording')}
<Box mr={-1}>
{Object.keys(recipes).map((recipe) => (
<Button
key={recipe}
icon="tint"
width="129.5px"
lineHeight={1.75}
onClick={() =>
act('dispense_recipe', {
recipe: recipe,
})
}
>
{recipe}
</Button>
))}
{recipes.length === 0 && (
<Box color="light-gray">No recipes.</Box>
)}
</Box>
</Section>
</Stack.Item>
<Stack.Item>
<Section
title="Dispense"
buttons={beakerTransferAmounts.map((amount) => (
<Button
key={amount}
icon="plus"
selected={amount === data.amount}
onClick={() =>
act('amount', {
target: amount,
})
}
>
{amount}
</Button>
))}
>
Save
</Button>
)}
</>
}
>
<Box mr={-1}>
{Object.keys(recipes).map((recipe) => (
<Button
key={recipe}
icon="tint"
width="129.5px"
lineHeight={1.75}
onClick={() =>
act('dispense_recipe', {
recipe: recipe,
})
}
>
{recipe}
</Button>
))}
{recipes.length === 0 && <Box color="light-gray">No recipes.</Box>}
</Box>
</Section>
<Section
title="Dispense"
buttons={beakerTransferAmounts.map((amount) => (
<Button
key={amount}
icon="plus"
selected={amount === data.amount}
onClick={() =>
act('amount', {
target: amount,
})
}
>
{amount}
</Button>
))}
>
<Box mr={-1}>
{data.chemicals.map((chemical) => (
<Button
key={chemical.id}
icon="tint"
textColor={showPhCol ? chemical.pHCol : chemical.color}
width="129.5px"
lineHeight={1.75}
tooltip={`pH: ${chemical.pH}`}
style={{
textShadow: '1px 1px 0 black',
}}
backgroundColor={
recipeReagents.includes(chemical.id) ? 'green' : 'default'
}
onClick={() =>
act('dispense', {
reagent: chemical.id,
})
}
>
<span
style={{
color: 'white',
textShadow: 'none',
}}
<Stack wrap>
{data.chemicals.map((chemical) => (
<Stack.Item width="24.5%" key={chemical.id} mr={-0.5}>
<ReagentDispenseButton
chemical={chemical}
showPhCol={showPhCol}
mainscreen={true}
/>
</Stack.Item>
))}
</Stack>
</Section>
</Stack.Item>
<Stack.Item grow>
<Section
fill
title="Beaker"
buttons={beakerTransferAmounts.map((amount) => (
<Button
key={amount}
icon="minus"
disabled={recording}
onClick={() => act('remove', { amount })}
>
{amount}
</Button>
))}
>
{chemical.title}
</span>
</Button>
))}
</Box>
</Section>
<Section
title="Beaker"
buttons={beakerTransferAmounts.map((amount) => (
<Button
key={amount}
icon="minus"
disabled={recording}
onClick={() => act('remove', { amount })}
>
{amount}
</Button>
))}
>
{beaker || recording ? (
<BeakerDisplay
beaker={beaker}
title_label={recording && 'Virtual beaker'}
replace_contents={recordedContents}
showpH={data.showpH}
/>
) : (
<Box
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Box color="label">No beaker loaded.</Box>
<Button
icon="eject"
onClick={() => act('insert')}
style={{
opacity: data.hasBeakerInHand ? 1 : 0.5,
}}
tooltip={
!data.hasBeakerInHand &&
'You need to hold a container in your hand'
}
tooltipPosition="left-start"
>
Insert
</Button>
</Box>
{beaker || recording ? (
<BeakerDisplay
beaker={beaker}
title_label={recording && 'Virtual beaker'}
replace_contents={recordedContents}
showpH={data.showpH}
/>
) : (
<Box
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<Box color="label">No beaker loaded.</Box>
<Button
icon="eject"
onClick={() => act('insert')}
disabled={!hasBeakerInHand}
tooltip={
!hasBeakerInHand &&
'You need to hold a container in your hand!'
}
tooltipPosition="left-start"
>
Insert
</Button>
</Box>
)}
</Section>
</Stack.Item>
</Stack>
</Stack.Item>
{showReactionList && (
<Stack.Item width={reactionWidth}>
<Section title="Recipes" fill>
<Stack vertical fill>
<Stack.Item>
<Stack>
<Stack.Item grow>
<Input
placeholder="Search reactions..."
value={searchTerm}
fluid
onChange={(value) => setSearchTerm(value)}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="times"
disabled={searchTerm === ''}
onClick={() => setSearchTerm('')}
/>
</Stack.Item>
<Stack.Item>
<Button
icon="thumbtack"
disabled={pinnedReactions.length === 0}
onClick={() => setPinnedReactions([])}
/>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Divider />
<Stack.Item>
<Stack wrap>
{Object.entries(all_bitflags).map(([readable, flag]) => (
<Stack.Item key={readable} grow>
<Button
fluid
align="center"
fontSize="0.9em"
onClick={() =>
setFilterByBitflag(flag ^ filterByBitflag)
}
tooltip={
<Box fontSize="0.9em">
{bitflagInfo.find((bf) => bf.flag === readable)
?.tooltip || ''}
</Box>
}
selected={(filterByBitflag & flag) !== 0}
>
{readable}
</Button>
</Stack.Item>
))}
</Stack>
</Stack.Item>
<Stack.Divider />
<Stack.Item grow mt={1}>
<Section scrollable fill>
<Stack vertical>
{filteredReactions.length > 0 ? (
filteredReactions.map((reaction) => (
<Stack.Item key={reaction.name}>
<ReactionDisplay
reaction={reaction}
pinnedReactions={pinnedReactions}
setPinnedReactions={setPinnedReactions}
setSearchTerm={setSearchTerm}
/>
</Stack.Item>
))
) : (
<NoticeBox>No reactions found.</NoticeBox>
)}
</Stack>
</Section>
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
)}
</Section>
</Stack>
</Window.Content>
</Window>
);
};
type ReagentDispenseButtonProps = {
chemical: DispensableReagent;
showPhCol?: boolean;
mainscreen?: boolean;
prefix?: string;
};
const ReagentDispenseButton = (props: ReagentDispenseButtonProps) => {
const { chemical, showPhCol, mainscreen, prefix } = props;
const { act, data } = useBackend<Data>();
const { recipeReagents = [] } = data;
return (
<Button
key={chemical.id}
icon="tint"
fluid
textColor={showPhCol ? chemical.pHCol : chemical.color}
lineHeight={1.75}
tooltip={mainscreen ? `pH: ${chemical.pH}` : undefined}
style={{
textShadow: '1px 1px 0 black',
textOverflow: 'ellipsis',
overflow: 'hidden',
}}
selected={mainscreen && recipeReagents.includes(chemical.id)}
onClick={() =>
act('dispense', {
reagent: chemical.id,
})
}
>
<span
style={{
color: 'white',
textShadow: 'none',
textOverflow: 'ellipsis',
overflow: 'hidden',
}}
>
{prefix}
{chemical.title}
</span>
</Button>
);
};
type ReactionDisplayProps = {
reaction: ReagentReaction;
pinnedReactions: ReactionTypepath[];
setPinnedReactions: (reactions: ReactionTypepath[]) => void;
setSearchTerm: (term: string) => void;
};
const ReactionDisplay = (props: ReactionDisplayProps) => {
const { reaction, pinnedReactions, setPinnedReactions } = props;
return (
<Stack
p={1}
style={{
borderRadius: '4px',
flexDirection: 'column',
backgroundColor: 'black',
}}
>
<Stack.Item>
<Stack align="center">
<Stack.Item>
<Button
icon="thumbtack"
onClick={() => {
if (pinnedReactions.includes(reaction.name)) {
setPinnedReactions(
pinnedReactions.filter((rid) => rid !== reaction.name),
);
} else {
setPinnedReactions([...pinnedReactions, reaction.name]);
}
}}
onContextMenu={() => {
setPinnedReactions([reaction.name]);
}}
selected={pinnedReactions.includes(reaction.name)}
/>
</Stack.Item>
<Stack.Item
grow
style={{
overflow: 'hidden',
whiteSpace: 'nowrap',
}}
>
<Tooltip
content={
<Box fontSize="0.9rem">{reaction.reaction.description}</Box>
}
position="top"
>
<Stack fill>
<Stack.Item
grow
style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
}}
>
{reaction.name}
</Stack.Item>
<Stack.Item
backgroundColor={reaction.reaction.color}
p={1.25}
style={{ borderRadius: '8px' }}
/>
</Stack>
</Tooltip>
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<Collapsible
title="Recipe"
open={pinnedReactions.includes(reaction.name)}
>
<BlockQuote>
<Stack vertical>
<Stack.Item>
<HorizontalBarWithText text="Formula" />
</Stack.Item>
{reaction.reaction.required_reagents.map((reagent) => (
<Stack.Item key={`${reaction.name}-${reagent.name}-req`}>
<ReactionComponentDisplay
reagentComponent={reagent}
setSearchTerm={props.setSearchTerm}
pinnedReactions={pinnedReactions}
setPinnedReactions={props.setPinnedReactions}
/>
</Stack.Item>
))}
{reaction.reaction.required_catalysts.length > 0 && (
<>
<Stack.Item>
<HorizontalBarWithText
text={`Catalyst${reaction.reaction.required_reagents.length === 1 ? '' : 's'}`}
/>
</Stack.Item>
{reaction.reaction.required_catalysts.map((catalyst) => (
<Stack.Item key={`${reaction.name}-${catalyst.name}-cat`}>
<ReactionComponentDisplay
reagentComponent={catalyst}
setSearchTerm={props.setSearchTerm}
pinnedReactions={pinnedReactions}
setPinnedReactions={props.setPinnedReactions}
/>
</Stack.Item>
))}
</>
)}
<Stack.Item>
<HorizontalBarWithText text="Optimal temperature" />
</Stack.Item>
<Stack.Item fontSize="0.9em">
{getTemperatureMessage(
reaction.reaction.lower_temperature,
reaction.reaction.upper_temperature,
)}
</Stack.Item>
<Stack.Item>
<HorizontalBarWithText text="Optimal pH range" />
</Stack.Item>
<Stack.Item fontSize="0.9em">
{getPHMessage(
reaction.reaction.lower_ph,
reaction.reaction.upper_ph,
)}
</Stack.Item>
</Stack>
</BlockQuote>
</Collapsible>
</Stack.Item>
</Stack>
);
};
// if lower and upper are the same, return "at X degrees"
// if lower and upper are >300, return "heat to between X and Y degrees"
// if lower and upper are <300, return "cool to between X and Y degrees"
// if lower is <300 and upper is >300, return "keep between X and Y degrees"
function getTemperatureMessage(lower: number, upper: number): string {
if (lower === upper) {
return `Forms at ${lower}°K`;
} else if (lower > 300 && upper > 300) {
return `Heat between ${lower}°K-${upper}°K`;
} else if (lower < 300 && upper < 300) {
return `Cool between ${Math.min(upper, lower)}°K-${Math.max(upper, lower)}°K`;
} else {
return `Keep between ${lower}°K-${upper}°K`;
}
}
// if lower and upper are the same, return "keep at pH X"
// else return "keep between pH X and Y"
function getPHMessage(lower: number, upper: number): string {
if (lower === upper) {
return `Keep at pH ${lower}`;
} else {
return `Keep between pH ${lower}-${upper}`;
}
}
type ReactionComponentDisplayProps = {
reagentComponent: ReactionComponent;
setSearchTerm: (term: string) => void;
pinnedReactions: ReactionTypepath[];
setPinnedReactions: (reactions: ReactionTypepath[]) => void;
};
// linkifies a reagent name in the reaction display
// if it's a base reagent, it will dispense it when clicked
// if it's another recipe, it will put that recipe in the search box
// if it's nothing, it's not a button
const ReactionComponentDisplay = (props: ReactionComponentDisplayProps) => {
const {
reagentComponent,
setSearchTerm,
pinnedReactions,
setPinnedReactions,
} = props;
const { data } = useBackend<Data>();
const { chemicals, reaction_list } = data;
// check if it's a base reagent
const baseReagent = chemicals.find(
(chem) => chem.title === reagentComponent.name,
);
if (baseReagent) {
return (
<ReagentDispenseButton
chemical={baseReagent}
prefix={formatReagentName(reagentComponent.amount)}
/>
);
}
const reactionReagentList = reagentListToArray(reaction_list);
// check if it's a recipe
const isRecipe = reactionReagentList
.filter((reaction) => {
return reaction.name === reagentComponent.name;
})
.find((reaction) => reaction.name === reagentComponent.name);
if (isRecipe) {
return (
<Button
icon="book"
fluid
ellipsis
backgroundColor="default"
onContextMenu={() => {
// put recipe name in search box
setSearchTerm(reagentComponent.name);
}}
onClick={() => {
// pin recipe
setPinnedReactions([...pinnedReactions, isRecipe.name]);
}}
selected={pinnedReactions.includes(isRecipe.name)}
tooltip={
<Stack vertical>
<Stack.Item fontSize="0.9em">
Left click to pin this recipe.
</Stack.Item>
<Stack.Item fontSize="0.9em">
Right click to search for this recipe.
</Stack.Item>
</Stack>
}
>
{formatReagentName(reagentComponent.amount, reagentComponent.name)}
</Button>
);
}
// otherwise, just display the name
return (
<Button fluid ellipsis disabled icon="question">
{formatReagentName(reagentComponent.amount, reagentComponent.name)}
</Button>
);
};
function formatReagentName(amount: number, name?: string) {
if (!name) return `${amount} part `;
return `${amount} part${amount === 1 ? '' : 's'} ${name}`;
}
const HorizontalBarWithText = (props: { text: string }) => {
const { text } = props;
return (
<Stack>
<Stack.Item grow>
<hr />
</Stack.Item>
<Stack.Item fontSize="0.95em">{text}</Stack.Item>
<Stack.Item grow>
<hr />
</Stack.Item>
</Stack>
);
};
@@ -9,12 +9,23 @@ import {
import { useBackend } from '../../backend';
import { bookmarkedReactions } from '.';
import type { ReagentsData, ReagentsProps } from './types';
import { bitflagInfo, type ReagentsData, type ReagentsProps } from './types';
function matchBitflag(a: number, b: number) {
return a & b && (a | b) === b;
}
function bitflagToIcon(flag: number, all_flags: Record<string, number>) {
for (const [readable, value] of Object.entries(all_flags)) {
for (const meta of bitflagInfo) {
if (meta.flag === readable && value === flag) {
return meta.icon;
}
}
}
return null;
}
export function RecipeLibrary(props: ReagentsProps) {
const { act, data } = useBackend<ReagentsData>();
@@ -58,30 +69,6 @@ export function RecipeLibrary(props: ReagentsProps) {
const pageIndexMax = Math.ceil(visibleReactions.length / 50);
const flagIcons = [
{ flag: bitflags.BRUTE, icon: 'gavel' },
{ flag: bitflags.BURN, icon: 'burn' },
{ flag: bitflags.TOXIN, icon: 'biohazard' },
{ flag: bitflags.OXY, icon: 'wind' },
{ flag: bitflags.HEALING, icon: 'medkit' },
{ flag: bitflags.DAMAGING, icon: 'skull-crossbones' },
{ flag: bitflags.EXPLOSIVE, icon: 'bomb' },
{ flag: bitflags.OTHER, icon: 'question' },
{ flag: bitflags.DANGEROUS, icon: 'exclamation-triangle' },
{ flag: bitflags.EASY, icon: 'chess-pawn' },
{ flag: bitflags.MODERATE, icon: 'chess-knight' },
{ flag: bitflags.HARD, icon: 'chess-queen' },
{ flag: bitflags.ORGAN, icon: 'brain' },
{ flag: bitflags.DRINK, icon: 'cocktail' },
{ flag: bitflags.FOOD, icon: 'drumstick-bite' },
{ flag: bitflags.SLIME, icon: 'microscope' },
{ flag: bitflags.DRUG, icon: 'pills' },
{ flag: bitflags.UNIQUE, icon: 'puzzle-piece' },
{ flag: bitflags.CHEMICAL, icon: 'flask' },
{ flag: bitflags.PLANT, icon: 'seedling' },
{ flag: bitflags.COMPETITIVE, icon: 'recycle' },
];
return (
<Section
fill
@@ -184,11 +171,10 @@ export function RecipeLibrary(props: ReagentsProps) {
))}
</Table.Cell>
<Table.Cell width="60px">
{flagIcons
.filter((meta) => reaction.bitflags & meta.flag)
.map((meta) => (
<Icon key={meta.flag} name={meta.icon} mr={1} />
))}
<Icon
name={bitflagToIcon(reaction.bitflags, bitflags) || 'question'}
mr={1}
/>
</Table.Cell>
<Table.Cell width="20px">
{!bookmarkMode ? (
+38 -217
View File
@@ -1,7 +1,7 @@
import { Button, LabeledList } from 'tgui-core/components';
import { useBackend } from '../../backend';
import type { ReagentsData, ReagentsProps } from './types';
import { bitflagInfo, type ReagentsData, type ReagentsProps } from './types';
export function TagBox(props: ReagentsProps) {
const { act, data } = useBackend<ReagentsData>();
@@ -9,224 +9,45 @@ export function TagBox(props: ReagentsProps) {
const [page, setPage] = props.pageState;
// first go through all bitflaginfo to find all unique categories
const allCategories: string[] = [];
for (const meta of bitflagInfo) {
if (!allCategories.includes(meta.category)) {
allCategories.push(meta.category);
}
}
// then fill each category with its respective bitflags
const categorizedBitflags: Record<string, typeof bitflagInfo> = {};
for (const category of allCategories) {
categorizedBitflags[category] = bitflagInfo.filter(
(meta) => meta.category === category,
);
}
return (
<LabeledList>
<LabeledList.Item label="Affects">
<Button
color={selectedBitflags & bitflags.BRUTE ? 'green' : 'red'}
icon="gavel"
onClick={() => {
act('toggle_tag_brute');
setPage(1);
}}
>
Brute
</Button>
<Button
color={selectedBitflags & bitflags.BURN ? 'green' : 'red'}
icon="burn"
onClick={() => {
act('toggle_tag_burn');
setPage(1);
}}
>
Burn
</Button>
<Button
color={selectedBitflags & bitflags.TOXIN ? 'green' : 'red'}
icon="biohazard"
onClick={() => {
act('toggle_tag_toxin');
setPage(1);
}}
>
Toxin
</Button>
<Button
color={selectedBitflags & bitflags.OXY ? 'green' : 'red'}
icon="wind"
onClick={() => {
act('toggle_tag_oxy');
setPage(1);
}}
>
Suffocation
</Button>
<Button
color={selectedBitflags & bitflags.ORGAN ? 'green' : 'red'}
icon="brain"
onClick={() => {
act('toggle_tag_organ');
setPage(1);
}}
>
Organ
</Button>
<Button
icon="flask"
color={selectedBitflags & bitflags.CHEMICAL ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_chemical');
setPage(1);
}}
>
Chemical
</Button>
<Button
icon="seedling"
color={selectedBitflags & bitflags.PLANT ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_plant');
setPage(1);
}}
>
Plants
</Button>
<Button
icon="question"
color={selectedBitflags & bitflags.OTHER ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_other');
setPage(1);
}}
>
Other
</Button>
</LabeledList.Item>
<LabeledList.Item label="Type">
<Button
color={selectedBitflags & bitflags.DRINK ? 'green' : 'red'}
icon="cocktail"
onClick={() => {
act('toggle_tag_drink');
setPage(1);
}}
>
Drink
</Button>
<Button
color={selectedBitflags & bitflags.FOOD ? 'green' : 'red'}
icon="drumstick-bite"
onClick={() => {
act('toggle_tag_food');
setPage(1);
}}
>
Food
</Button>
<Button
color={selectedBitflags & bitflags.HEALING ? 'green' : 'red'}
icon="medkit"
onClick={() => {
act('toggle_tag_healing');
setPage(1);
}}
>
Healing
</Button>
<Button
icon="skull-crossbones"
color={selectedBitflags & bitflags.DAMAGING ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_damaging');
setPage(1);
}}
>
Toxic
</Button>
<Button
icon="pills"
color={selectedBitflags & bitflags.DRUG ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_drug');
setPage(1);
}}
>
Drugs
</Button>
<Button
icon="microscope"
color={selectedBitflags & bitflags.SLIME ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_slime');
setPage(1);
}}
>
Slime
</Button>
<Button
icon="bomb"
color={selectedBitflags & bitflags.EXPLOSIVE ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_explosive');
setPage(1);
}}
>
Explosive
</Button>
<Button
icon="puzzle-piece"
color={selectedBitflags & bitflags.UNIQUE ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_unique');
setPage(1);
}}
>
Unique
</Button>
</LabeledList.Item>
<LabeledList.Item label="Difficulty">
<Button
icon="chess-pawn"
color={selectedBitflags & bitflags.EASY ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_easy');
setPage(1);
}}
>
Easy
</Button>
<Button
icon="chess-knight"
color={selectedBitflags & bitflags.MODERATE ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_moderate');
setPage(1);
}}
>
Moderate
</Button>
<Button
icon="chess-queen"
color={selectedBitflags & bitflags.HARD ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_hard');
setPage(1);
}}
>
Hard
</Button>
<Button
icon="exclamation-triangle"
color={selectedBitflags & bitflags.DANGEROUS ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_dangerous');
setPage(1);
}}
>
Dangerous
</Button>
<Button
icon="recycle"
color={selectedBitflags & bitflags.COMPETITIVE ? 'green' : 'red'}
onClick={() => {
act('toggle_tag_competitive');
setPage(1);
}}
>
Competitive
</Button>
</LabeledList.Item>
{Object.entries(categorizedBitflags).map(([category, metas]) => (
<LabeledList.Item label={category} key={category}>
{metas.map((meta) => {
const flag = bitflags[meta.flag];
return (
<Button
key={meta.flag}
selected={(selectedBitflags & flag) !== 0}
icon={meta.icon}
tooltip={meta.tooltip}
onClick={() => {
act(meta.toggle);
setPage(1);
}}
>
{meta.flag}
</Button>
);
})}
</LabeledList.Item>
))}
</LabeledList>
);
}
@@ -78,3 +78,168 @@ export type Reaction = {
name: string;
reactants: ReactionReagent[];
};
export const bitflagInfo = [
{
flag: 'BRUTE',
icon: 'gavel',
tooltip: 'Produces a reagent that heals or deals brute damage.',
category: 'Affects',
toggle: 'toggle_tag_brute', // future todo : just make this use ui state
},
{
flag: 'BURN',
icon: 'burn',
tooltip: 'Produces a reagent that heals or deals burn damage.',
category: 'Affects',
toggle: 'toggle_tag_burn',
},
{
flag: 'TOXIN',
icon: 'biohazard',
tooltip: 'Produces a reagent that heals or deals toxin damage.',
category: 'Affects',
toggle: 'toggle_tag_toxin',
},
{
flag: 'OXY',
icon: 'wind',
tooltip: 'Produces a reagent that heals or deals suffocation damage.',
category: 'Affects',
toggle: 'toggle_tag_oxy',
},
{
flag: 'HEALING',
icon: 'medkit',
tooltip: 'Produces a healing reagent.',
category: 'Type',
toggle: 'toggle_tag_healing',
},
{
flag: 'DAMAGING',
icon: 'skull-crossbones',
tooltip: 'Produces a damaging reagent.',
category: 'Type',
toggle: 'toggle_tag_damaging',
},
{
flag: 'EXPLOSIVE',
icon: 'bomb',
tooltip: 'Produces a reagent that explodes or explodes on reaction.',
category: 'Type',
toggle: 'toggle_tag_explosive',
},
{
flag: 'OTHER',
icon: 'question',
tooltip: 'Produces a reagent with some other side effect.',
category: 'Affects',
toggle: 'toggle_tag_other',
},
{
flag: 'DANGEROUS',
icon: 'exclamation-triangle',
tooltip: 'Reaction may have a dangerous immediate effect.',
category: 'Difficulty',
toggle: 'toggle_tag_dangerous',
},
{
flag: 'EASY',
icon: 'chess-pawn',
tooltip: 'Easy to perform reaction.',
category: 'Difficulty',
toggle: 'toggle_tag_easy',
},
{
flag: 'MODERATE',
icon: 'chess-knight',
tooltip: 'Moderate difficulty reaction.',
category: 'Difficulty',
toggle: 'toggle_tag_moderate',
},
{
flag: 'HARD',
icon: 'chess-queen',
tooltip: 'Hard to perform reaction.',
category: 'Difficulty',
toggle: 'toggle_tag_hard',
},
{
flag: 'ORGAN',
icon: 'brain',
tooltip: 'Produces a reagent that heals or deals organ damage.',
category: 'Affects',
toggle: 'toggle_tag_organ',
},
{
flag: 'DRINK',
icon: 'cocktail',
tooltip: 'Produces a drinkable reagent. Usually performed in the bar.',
category: 'Type',
toggle: 'toggle_tag_drink',
},
{
flag: 'FOOD',
icon: 'drumstick-bite',
tooltip: 'Produces a food. Usually performed in the kitchen.',
category: 'Type',
toggle: 'toggle_tag_food',
},
{
flag: 'SLIME',
icon: 'microscope',
tooltip: 'A reaction related to Xenobiology.',
category: 'Type',
toggle: 'toggle_tag_slime',
},
{
flag: 'DRUG',
icon: 'pills',
tooltip:
'Produces an addictive reagent with positive and negative effects.',
category: 'Type',
toggle: 'toggle_tag_drug',
},
{
flag: 'UNIQUE',
icon: 'puzzle-piece',
tooltip: 'A unique or special reaction.',
category: 'Type',
toggle: 'toggle_tag_unique',
},
{
flag: 'CHEMICAL',
icon: 'flask',
tooltip: 'Produces a reagent which alters other reactions.',
category: 'Affects',
toggle: 'toggle_tag_chemical',
},
{
flag: 'PLANT',
icon: 'seedling',
tooltip: 'Produces a reagent that can help or harm plants.',
category: 'Affects',
toggle: 'toggle_tag_plant',
},
{
flag: 'COMPETITIVE',
icon: 'recycle',
tooltip: 'A reaction that competes with other reactions.',
category: 'Difficulty',
toggle: 'toggle_tag_competitive',
},
{
flag: 'COMPONENT',
icon: 'question',
tooltip: 'Produces a reagent commonly used in other reactions.',
category: 'Type',
toggle: 'toggle_tag_component',
},
{
flag: 'ACTIVE',
icon: 'question',
tooltip: 'Reaction has an active, immediate effect.',
category: 'Type',
toggle: 'toggle_tag_active',
},
];