diff --git a/code/__defines/_reagents.dm b/code/__defines/_reagents.dm
index d2257c07065..6bfb2457e62 100644
--- a/code/__defines/_reagents.dm
+++ b/code/__defines/_reagents.dm
@@ -83,7 +83,12 @@
#define REAGENT_ID_TUNGSTEN "tungsten"
#define REAGENT_NUTRIMENT "Nutriment"
#define REAGENT_ID_NUTRIMENT "nutriment"
-
+#define REAGENT_TITANIUM "Titanium"
+#define REAGENT_ID_TITANIUM "titanium"
+#define REAGENT_TITANIUMDIOX "Titanium dioxide"
+#define REAGENT_ID_TITANIUMDIOX "titanium_diox"
+#define REAGENT_TIN "Tin"
+#define REAGENT_ID_TIN "tin"
// Xeno chem react
#define XENO_CHEM_NUTRI "nutr"
diff --git a/code/__defines/refinery.dm b/code/__defines/refinery.dm
new file mode 100644
index 00000000000..221c6b44b70
--- /dev/null
+++ b/code/__defines/refinery.dm
@@ -0,0 +1,52 @@
+#define REFINERY_SINTERING_SMOKE "FLAG_SMOKE"
+#define REFINERY_SINTERING_EXPLODE "FLAG_EXPLODE"
+#define REFINERY_SINTERING_SPIDERS "FLAG_SPIDERS"
+
+#define REFINERY_DEFAULT_TRANSFER_AMOUNTS list(0,1,2,5,10,15,20,25,30,40,60,80,100,120)
+#define SHEET_TO_REAGENT_EQUIVILENT *(1/REAGENTS_PER_SHEET) // 0.05 normally 1/20
+// The tl;dr here is that the supply_conversion_value of the reagent should just match the per sheet value with the macro above.
+// That should keep everything inline and not cause any cargo exploits.
+
+#define REFINERY_TUTORIAL_INPUT (1 << 0)
+#define REFINERY_TUTORIAL_FILTER (1 << 1)
+#define REFINERY_TUTORIAL_HUB (1 << 2)
+#define REFINERY_TUTORIAL_NOPOWER (1 << 3)
+#define REFINERY_TUTORIAL_NOINPUT (1 << 4)
+#define REFINERY_TUTORIAL_NOOUTPUT (1 << 5)
+#define REFINERY_TUTORIAL_ALLIN (1 << 6)
+#define REFINERY_TUTORIAL_SINGLEOUTPUT (1 << 7)
+
+#define REAGENT_VAT_VOLUME 500
+#define CARGOTANKER_VOLUME 3000
+
+// Use these, unless it had a sheet analog to match, VAL * 0.05 * 2000 = per full tank points
+#define REFINERYEXPORT_VALUE_NO 0.1 SHEET_TO_REAGENT_EQUIVILENT // 10 per tank
+#define REFINERYEXPORT_VALUE_UNWANTED 0.5 SHEET_TO_REAGENT_EQUIVILENT // 50 per tank
+#define REFINERYEXPORT_VALUE_COMMON 1 SHEET_TO_REAGENT_EQUIVILENT // 100 per tank
+#define REFINERYEXPORT_VALUE_RARE 2 SHEET_TO_REAGENT_EQUIVILENT // 200 per tank
+#define REFINERYEXPORT_VALUE_PROCESSED 3 SHEET_TO_REAGENT_EQUIVILENT // 300 per tank
+#define REFINERYEXPORT_VALUE_HIGHREFINED 4 SHEET_TO_REAGENT_EQUIVILENT // 400 per tank
+#define REFINERYEXPORT_VALUE_MASSINDUSTRY 8 SHEET_TO_REAGENT_EQUIVILENT // 800 per tank
+#define REFINERYEXPORT_VALUE_PEAK 16 SHEET_TO_REAGENT_EQUIVILENT // 1600 per tank
+#define REFINERYEXPORT_VALUE_GODTIER 20 SHEET_TO_REAGENT_EQUIVILENT // 2000 per tank
+
+// Use these or you'll get weird endround lists
+#define REFINERYEXPORT_REASON_BIOHAZARD "unwanted biohazards"
+#define REFINERYEXPORT_REASON_RAW "raw industrial materials"
+#define REFINERYEXPORT_REASON_PHORON "advanced phoronics research"
+#define REFINERYEXPORT_REASON_DRUG "pharmaceutical drugs"
+#define REFINERYEXPORT_REASON_ILLDRUG "illegal drugs"
+#define REFINERYEXPORT_REASON_RECDRUG "widespread recreational drugs"
+#define REFINERYEXPORT_REASON_CLONEDRUG "vat cloning stablizers"
+#define REFINERYEXPORT_REASON_SPECIALDRUG "specialized high end drugs"
+#define REFINERYEXPORT_REASON_DIET "dietary supplements"
+#define REFINERYEXPORT_REASON_FOOD "culinary ingredients"
+#define REFINERYEXPORT_REASON_MATSCI "material science research"
+#define REFINERYEXPORT_REASON_MEDSCI "medical science research"
+#define REFINERYEXPORT_REASON_INDUSTRY "heavy industrial materials"
+#define REFINERYEXPORT_REASON_PRECURSOR "industrial precursor chemicals"
+#define REFINERYEXPORT_REASON_WEAPONS "chemical weaponry"
+#define REFINERYEXPORT_REASON_COMSTIM "combat stimulants"
+#define REFINERYEXPORT_REASON_COSMETIC "cosmetic coatings and drugs"
+#define REFINERYEXPORT_REASON_CLEAN "cleaning products"
+#define REFINERYEXPORT_REASON_LUBE "industrial lubricants"
diff --git a/code/__defines/research/techweb_nodes.dm b/code/__defines/research/techweb_nodes.dm
index 1abbf3eb83f..5326651ffba 100644
--- a/code/__defines/research/techweb_nodes.dm
+++ b/code/__defines/research/techweb_nodes.dm
@@ -40,6 +40,7 @@
#define TECHWEB_NODE_DONK_PRODUCTS "donk_products"
#define TECHWEB_NODE_ELECTRIC_WEAPONS "electric_weapons"
#define TECHWEB_NODE_ENERGY_MANIPULATION "energy_manipulation"
+#define TECHWEB_NODE_CHEM_REFINERY "chem_refinery"
#define TECHWEB_NODE_EXODRONE "exodrone"
#define TECHWEB_NODE_EXOTIC_AMMO "exotic_ammo"
#define TECHWEB_NODE_EXP_TOOLS "exp_tools"
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index ebe9304618c..03a0c033d27 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -1476,8 +1476,44 @@ GLOBAL_LIST_INIT(ore_reagents, list( //have a number of reageents divisible by R
/obj/item/ore/lead = list(REAGENT_ID_LEAD),
/obj/item/ore/hydrogen = list(REAGENT_ID_HYDROGEN),
/obj/item/ore/verdantium = list(REAGENT_ID_RADIUM,REAGENT_ID_PHORON,REAGENT_ID_NITROGEN,REAGENT_ID_PHOSPHORUS,REAGENT_ID_SODIUM), // Some fun stuff to be useful with
- /obj/item/ore/rutile = list(REAGENT_ID_TUNGSTEN,REAGENT_ID_OXYGEN) // Should be titanium
-))
+ /obj/item/ore/rutile = list(REAGENT_ID_TITANIUMDIOX,REAGENT_ID_OXYGEN),
+ /obj/item/ore/copper = list(REAGENT_ID_COPPER),
+ /obj/item/ore/tin = list(REAGENT_ID_TIN),
+ /obj/item/ore/void_opal = list(REAGENT_ID_SILICON,REAGENT_ID_SILICON,REAGENT_ID_OXYGEN,REAGENT_ID_WATER),
+ /obj/item/ore/painite = list(REAGENT_ID_CALCIUM,REAGENT_ID_ALUMINIUM,REAGENT_ID_OXYGEN,REAGENT_ID_OXYGEN),
+ /obj/item/ore/quartz = list(REAGENT_ID_SILICON,REAGENT_ID_OXYGEN),
+ /obj/item/ore/bauxite = list(REAGENT_ID_ALUMINIUM,REAGENT_ID_ALUMINIUM),
+ ))
+
+// Don't need a new list for every grinder in the game
+GLOBAL_LIST_INIT(reagent_sheets,list( // Recompressing reagents back into sheets
+ REAGENT_ID_COPPER = MAT_COPPER,
+ REAGENT_ID_TIN = MAT_TIN,
+ REAGENT_ID_WOODPULP = MAT_CARDBOARD,
+ REAGENT_ID_CARBON = MAT_GRAPHITE,
+ REAGENT_ID_ALUMINIUM = MAT_ALUMINIUM,
+ REAGENT_ID_TITANIUM = MAT_TITANIUM,
+ REAGENT_ID_IRON = MAT_IRON,
+ REAGENT_ID_LEAD = MAT_LEAD,
+ REAGENT_ID_URANIUM = MAT_URANIUM,
+ REAGENT_ID_GOLD = MAT_GOLD,
+ REAGENT_ID_SILVER = MAT_SILVER,
+ REAGENT_ID_PLATINUM = MAT_PLATINUM,
+ REAGENT_ID_SILICON = MAT_GLASS,
+ // Mostly harmless
+ REAGENT_ID_PROTEIN = REFINERY_SINTERING_SMOKE,
+ REAGENT_ID_TRIGLYCERIDE = REFINERY_SINTERING_SMOKE,
+ REAGENT_ID_SODIUM = REFINERY_SINTERING_SMOKE,
+ REAGENT_ID_PHOSPHORUS = REFINERY_SINTERING_SMOKE,
+ REAGENT_ID_ETHANOL = REFINERY_SINTERING_SMOKE,
+ // Extremely stupid ones
+ REAGENT_ID_OXYGEN = REFINERY_SINTERING_EXPLODE,
+ REAGENT_ID_HYDROGEN = REFINERY_SINTERING_EXPLODE,
+ REAGENT_ID_PHORON = REFINERY_SINTERING_EXPLODE,
+ REAGENT_ID_SUPERMATTER = REFINERY_SINTERING_EXPLODE,
+ // Nothing is funnier to me
+ REAGENT_ID_SPIDEREGG = REFINERY_SINTERING_SPIDERS,
+ ))
//List of the ammo types that can be used in game.
GLOBAL_LIST_INIT(global_ammo_types, list(
diff --git a/code/_helpers/refinery.dm b/code/_helpers/refinery.dm
new file mode 100644
index 00000000000..7d93b9190ea
--- /dev/null
+++ b/code/_helpers/refinery.dm
@@ -0,0 +1,55 @@
+/// Grinds down various objects into their reagent components. Returns true if any reagents were gained from the attempt.
+/proc/grind_items_to_reagents(var/list/holdingitems,var/datum/reagents/R)
+ var/start_volume = R.total_volume
+
+ for(var/obj/item/O in holdingitems)
+ var/remaining_volume = R.maximum_volume - R.total_volume
+ if(remaining_volume <= 0)
+ break
+
+ if(GLOB.sheet_reagents[O.type])
+ var/obj/item/stack/stack = O
+ if(istype(stack))
+ var/list/sheet_components = GLOB.sheet_reagents[stack.type]
+ var/amount_to_take = max(0,min(stack.get_amount(),round(remaining_volume/REAGENTS_PER_SHEET)))
+ if(amount_to_take)
+ stack.use(amount_to_take)
+ if(QDELETED(stack))
+ holdingitems -= stack
+ if(islist(sheet_components))
+ amount_to_take = (amount_to_take/(sheet_components.len))
+ for(var/i in sheet_components)
+ R.add_reagent(i, (amount_to_take*REAGENTS_PER_SHEET))
+ else
+ R.add_reagent(sheet_components, (amount_to_take*REAGENTS_PER_SHEET))
+ continue
+
+ else if(GLOB.ore_reagents[O.type])
+ var/obj/item/ore/D = O
+ if(istype(D))
+ var/list/ore_components = GLOB.ore_reagents[D.type]
+ if(remaining_volume >= REAGENTS_PER_ORE)
+ holdingitems -= D
+ qdel(D)
+ if(islist(ore_components))
+ var/amount_to_take = (REAGENTS_PER_ORE/(ore_components.len))
+ for(var/i in ore_components)
+ R.add_reagent(i, amount_to_take)
+ else
+ R.add_reagent(ore_components, REAGENTS_PER_ORE)
+ continue
+
+ else if(O.reagents && O.reagents.total_volume > 0)
+ O.reagents.trans_to_obj(R.my_atom, min(O.reagents.total_volume, remaining_volume))
+ if(O.reagents.total_volume == 0)
+ holdingitems -= O
+ qdel(O)
+ if (R.total_volume >= R.maximum_volume)
+ break
+
+ else
+ // Cronch
+ holdingitems -= O
+ qdel(O)
+
+ return (R.total_volume > start_volume)
diff --git a/code/controllers/subsystems/internal_wiki.dm b/code/controllers/subsystems/internal_wiki.dm
index 34429a27f43..bae4349bb5e 100644
--- a/code/controllers/subsystems/internal_wiki.dm
+++ b/code/controllers/subsystems/internal_wiki.dm
@@ -294,12 +294,10 @@ SUBSYSTEM_DEF(internal_wiki)
var/list/assemble_reaction = list()
assemble_reaction["temp_min"] = CR.temp_range[1]
assemble_reaction["temp_max"] = CR.temp_range[2]
- /* Downstream features
assemble_reaction["xgm_min"] = CR.minimum_xgm_pressure
assemble_reaction["xgm_max"] = CR.maximum_xgm_pressure
- assemble_reaction["require_xgm_gas"] = CR.require_xgm_gas
- assemble_reaction["rejects_xgm_gas"] = CR.rejects_xgm_gas
- */
+ assemble_reaction["require_xgm_gas"] = CR.require_xgm_gas ? GLOB.gas_data.name[CR.require_xgm_gas] : null
+ assemble_reaction["rejects_xgm_gas"] = CR.rejects_xgm_gas ? GLOB.gas_data.name[CR.rejects_xgm_gas] : null
var/list/reqs = list()
for(var/RQ in CR.required_reagents)
var/decl/chemical_reaction/r_RQ = SSchemistry.chemical_reagents[RQ]
@@ -431,6 +429,15 @@ SUBSYSTEM_DEF(internal_wiki)
return allergies
return null
+/datum/controller/subsystem/internal_wiki/proc/assemble_sintering(var/sinter)
+ if(sinter == REFINERY_SINTERING_EXPLODE)
+ return "violent detonation"
+ if(sinter == REFINERY_SINTERING_SMOKE)
+ return "toxic fumes"
+ if(sinter == REFINERY_SINTERING_SPIDERS)
+ return "OH GOD WHY!?"
+ return sinter
+
/datum/controller/subsystem/internal_wiki/proc/add_icon(var/list/data, var/ic, var/is, var/col)
var/load_data = list()
load_data["icon"] = ic // dmi path
@@ -1148,14 +1155,12 @@ SUBSYSTEM_DEF(internal_wiki)
data["addictive"] = 0
if(R.id in get_addictive_reagents(ADDICT_ALL))
data["addictive"] = TRUE
- /* Downstream features
data["industrial_use"] = R.industrial_use
data["supply_points"] = R.supply_conversion_value ? R.supply_conversion_value : 0
var/value = R.supply_conversion_value * REAGENTS_PER_SHEET * SSsupply.points_per_money
value = FLOOR(value * 100,1) / 100 // Truncate decimals
data["market_price"] = value
- data["sintering"] = global.reagent_sheets[R.id]
- */
+ data["sintering"] = SSinternal_wiki.assemble_sintering(GLOB.reagent_sheets[R.id])
data["overdose"] = R.overdose
data["flavor"] = R.taste_description
data["allergen"] = SSinternal_wiki.assemble_allergens(R.allergen_type)
@@ -1166,7 +1171,6 @@ SUBSYSTEM_DEF(internal_wiki)
body += "Description: [data["description"]]
"
if(data["addictive"])
body += "DANGER, addictive.
"
- /* Downstream features
if(data["industrial_use"])
body += "Industrial Use: [data["industrial_use"]]
"
var/tank_size = CARGOTANKER_VOLUME
@@ -1177,16 +1181,16 @@ SUBSYSTEM_DEF(internal_wiki)
if(data["sintering"])
var/mat_id = data["sintering"]
switch(mat_id)
- if("FLAG_SMOKE")
+ if(REFINERY_SINTERING_SMOKE)
body += "Sintering Results: COMBUSTION
"
- if("FLAG_EXPLODE")
+ if(REFINERY_SINTERING_EXPLODE)
body += "Sintering Results: DETONATION
"
- if("FLAG_SPIDERS")
+ if(REFINERY_SINTERING_SPIDERS)
body += "Sintering Results: DO NOT EVER
"
else
var/datum/material/C = get_material_by_name(data["sintering"])
- body += "Sintering Results: [C.display_name] [C.sheet_plural_name]
"
- */
+ if(C)
+ body += "Sintering Results: [C.display_name] [C.sheet_plural_name]
"
if(data["overdose"] > 0)
body += "Overdose: [data["overdose"]]u
"
body += "Flavor: [data["flavor"]]
"
@@ -1569,13 +1573,11 @@ SUBSYSTEM_DEF(internal_wiki)
else
body += "Potential Chemical breakdown [segment]:
"
segment++
- /* Downstream features
body += " -Temperature: [react["xgm_min"]]K - [react["xgm_max"]]K | ([react["xgm_min"] - T0C]C - [react["xgm_max"] - T0C]C)
"
if(react["require_xgm_gas"])
- body += " -Requires Gas: [react["require_xgm_gas"])]
"
+ body += " -Requires Gas: [react["require_xgm_gas"]]
"
if(react["rejects_xgm_gas"])
body += " -Rejects Gas: [react["rejects_xgm_gas"]]
"
- */
for(var/RQ in react["required"])
body += " -Component: [RQ]
"
for(var/IH in react["inhibitor"])
diff --git a/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm b/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm
index 38177dc9560..7dbab675632 100644
--- a/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm
+++ b/code/datums/components/antags/changeling/powers/epinephrine_overdose.dm
@@ -52,6 +52,8 @@
color = "#C8A5DC"
metabolism = REM * 2
wiki_flag = WIKI_SPOILER
+ supply_conversion_value = REFINERYEXPORT_VALUE_RARE
+ industrial_use = REFINERYEXPORT_REASON_MATSCI
/datum/reagent/epinephrine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(alien == IS_DIONA)
diff --git a/code/datums/components/reagent_hose/item.dm b/code/datums/components/reagent_hose/item.dm
index b250a077173..3151261b69b 100644
--- a/code/datums/components/reagent_hose/item.dm
+++ b/code/datums/components/reagent_hose/item.dm
@@ -103,6 +103,7 @@
return
else
- to_chat(user, span_notice("There are no available connectors on \the [target]. You wind \the [src] back up."))
+ if(remembered)
+ to_chat(user, span_notice("There are no available connectors on \the [target]. You wind \the [src] back up."))
remembered = null // Unintuitive if it does not reset state
..()
diff --git a/code/datums/elements/sellable.dm b/code/datums/elements/sellable.dm
index 23be2312c0b..1630072f9eb 100644
--- a/code/datums/elements/sellable.dm
+++ b/code/datums/elements/sellable.dm
@@ -122,3 +122,53 @@
/datum/element/sellable/vaccine/calculate_sell_value(obj/source)
return 5
+
+
+// Refinery chemical tanks
+/datum/element/sellable/trolley_tank
+ sale_info = "This can be sold on the cargo shuttle if filled with a single reagent."
+ needs_crate = FALSE
+
+/datum/element/sellable/trolley_tank/sell_error(obj/source)
+ var/obj/vehicle/train/trolley_tank/tank = source
+ if(!tank.reagents || tank.reagents.reagent_list.len == 0)
+ return "Error: Product was not filled with any reagents to sell. Payment rendered null under terms of agreement."
+ var/min_tank = (CARGOTANKER_VOLUME - 100)
+ if(tank.reagents.total_volume < min_tank)
+ return "Error: Product was improperly packaged. Send full tanks only (minimum [min_tank] units). Payment rendered null under terms of agreement."
+ if(tank.reagents.reagent_list.len > 1)
+ return "Error: Product was improperly refined. Send purified mixtures only (too many reagents in tank). Payment rendered null under terms of agreement."
+ return null
+
+/datum/element/sellable/trolley_tank/calculate_sell_value(obj/source)
+ var/obj/vehicle/train/trolley_tank/tank = source
+
+ // Update export values
+ var/datum/reagent/R = tank.reagents.reagent_list[1]
+ var/reagent_value = FLOOR(R.volume * R.supply_conversion_value, 1)
+
+ return reagent_value
+
+/datum/element/sellable/trolley_tank/calculate_sell_quantity(obj/source)
+ var/obj/vehicle/train/trolley_tank/tank = source
+ if(!tank.reagents || tank.reagents.reagent_list.len == 0)
+ return "0u "
+ var/datum/reagent/R = tank.reagents.reagent_list[1]
+ return "[R.name] [tank.reagents.total_volume]u "
+
+/datum/element/sellable/trolley_tank/sell(obj/source, var/datum/exported_crate/EC, var/in_crate)
+ . = ..()
+ var/obj/vehicle/train/trolley_tank/tank = source
+ if(. && tank.reagents?.reagent_list?.len)
+ // Update end round data, has nothing to do with actual cargo sales
+ var/datum/reagent/R = tank.reagents.reagent_list[1]
+ var/reagent_value = FLOOR(R.volume * R.supply_conversion_value, 1)
+ if(R.industrial_use)
+ if(isnull(GLOB.refined_chems_sold[R.industrial_use]))
+ var/list/data = list()
+ data["units"] = FLOOR(R.volume, 1)
+ data["value"] = reagent_value
+ GLOB.refined_chems_sold[R.industrial_use] = data
+ else
+ GLOB.refined_chems_sold[R.industrial_use]["units"] += FLOOR(R.volume, 1)
+ GLOB.refined_chems_sold[R.industrial_use]["value"] += reagent_value
diff --git a/code/datums/roundstats/roundstats.dm b/code/datums/roundstats/roundstats.dm
index 1b80cf31cad..64cb6d50059 100644
--- a/code/datums/roundstats/roundstats.dm
+++ b/code/datums/roundstats/roundstats.dm
@@ -26,7 +26,7 @@ GLOBAL_VAR_INIT(prey_absorbed_roundstat, 0) //VOREStation Edit - Obviously
GLOBAL_VAR_INIT(prey_digested_roundstat, 0) //VOREStation Edit - Obviously
GLOBAL_VAR_INIT(items_digested_roundstat, 0) //VOREStation Edit - Obviously
GLOBAL_LIST_EMPTY(security_printer_tickets) //VOREStation Edit
-
+GLOBAL_LIST_EMPTY(refined_chems_sold)
/hook/roundend/proc/RoundTrivia()//bazinga
var/list/valid_stats_list = list() //This is to be populated with the good shit
@@ -72,7 +72,6 @@ GLOBAL_LIST_EMPTY(security_printer_tickets) //VOREStation Edit
else
good_num = 0
- //VOREStation Add Start - Vore stats lets gooooo
if(GLOB.prey_eaten_roundstat > 0)
valid_stats_list.Add("A total of [GLOB.prey_eaten_roundstat] individuals were eaten today!")
if(GLOB.prey_digested_roundstat > 0)
@@ -81,7 +80,24 @@ GLOBAL_LIST_EMPTY(security_printer_tickets) //VOREStation Edit
valid_stats_list.Add("A total of [GLOB.prey_absorbed_roundstat] individuals were absorbed today!")
if(GLOB.items_digested_roundstat > 0)
valid_stats_list.Add("A total of [GLOB.items_digested_roundstat] items were digested today!")
- //VOREStation Add End
+
+ var/points = 0
+ var/units = 0
+ if(GLOB.refined_chems_sold && GLOB.refined_chems_sold.len > 0)
+ valid_stats_list.Add(span_warning("The station exported:"))
+
+ for(var/D in GLOB.refined_chems_sold)
+ units += GLOB.refined_chems_sold[D]["units"]
+ points += GLOB.refined_chems_sold[D]["value"]
+
+ if(GLOB.refined_chems_sold[D]["units"] >= 1000) // Don't spam the list
+ var/dols = GLOB.refined_chems_sold[D]["value"] * SSsupply.points_per_money
+ dols = FLOOR(dols * 100,1) / 100 // Truncate decimals
+ valid_stats_list.Add("[GLOB.refined_chems_sold[D]["units"]]u of [D], for [GLOB.refined_chems_sold[D]["value"]] points! A total of [dols] [dols > 1 ? "thalers" : "thaler"]")
+
+ var/end_dols = points * SSsupply.points_per_money
+ end_dols = FLOOR(end_dols * 100,1) / 100 // Truncate decimals
+ valid_stats_list.Add("For a total of: [points] points, or [end_dols] [end_dols > 1 ? "thalers" : "thaler"]!")
if(LAZYLEN(valid_stats_list))
to_world(span_world("Shift trivia!"))
diff --git a/code/datums/supplypacks/supply.dm b/code/datums/supplypacks/supply.dm
index e3f408ddd5d..aef9846d156 100644
--- a/code/datums/supplypacks/supply.dm
+++ b/code/datums/supplypacks/supply.dm
@@ -168,6 +168,20 @@
contains = list(/obj/vehicle/train/trolley)
cost = 15
+/datum/supply_pack/supply/cargotank
+ name = "Cargo Train Tanker"
+ desc = "A cargo train tanker. For selling large volumes of a reagent."
+ contains = list(/obj/vehicle/train/trolley_tank)
+ cost = 15
+
+/datum/supply_pack/supply/freezer
+ name = "Freezer crate"
+ desc = "An empty freezer for storing perishable items."
+ contains = list()
+ cost = 20
+ containertype = /obj/structure/closet/crate/freezer
+ containername = "Freezer crate"
+
/datum/supply_pack/explorergear
name= JOB_EXPLORER + " gear"
desc = "Standard issue equipment for Explorers. Requires EVA and Exploration access."
diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm
index d41491f22ff..89281eca65f 100644
--- a/code/game/machinery/frame.dm
+++ b/code/game/machinery/frame.dm
@@ -216,6 +216,52 @@ GLOBAL_LIST(construction_frame_floor)
circuit = /obj/machinery/atmospheric_field_generator
frame_size = 3
+// Refinery machines
+/datum/frame/frame_types/industrial_reagent_grinder
+ name = "Industrial Chemical Grinder"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_pump
+ name = "Industrial Chemical Pump"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_filter
+ name = "Industrial Chemical Filter"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_vat
+ name = "Industrial Chemical Vat"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_pipe
+ name = "Industrial Chemical Pipe"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_waste_processor
+ name = "Industrial Chemical Waste Processor"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_hub
+ name = "Industrial Chemical Hub"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_reactor
+ name = "Industrial Chemical Reactor"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
+/datum/frame/frame_types/industrial_reagent_furnace
+ name = "Industrial Chemical Sintering Furnace"
+ icon_override = 'icons/obj/stock_parts_refinery.dmi'
+ frame_class = FRAME_CLASS_MACHINE
+
//////////////////////////////
// Frame Object (Structure)
//////////////////////////////
diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm
index dcf8b91451e..e721c7de0a2 100644
--- a/code/game/objects/items/weapons/circuitboards/frame.dm
+++ b/code/game/objects/items/weapons/circuitboards/frame.dm
@@ -291,3 +291,108 @@
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stock_parts/console_screen = 1
)
+
+// Smart centrifuge
+/obj/item/circuitboard/smart_centrifuge
+ name = T_BOARD("smart centrifuge")
+ build_path = /obj/machinery/smart_centrifuge
+ board_type = new /datum/frame/frame_types/machine
+ matter = list(MAT_STEEL = 50, MAT_GLASS = 50)
+ origin_tech = list(TECH_MAGNET = 3, TECH_DATA = 2, TECH_MATERIAL = 3)
+ req_components = list(
+ /obj/item/stock_parts/scanning_module = 1,
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stock_parts/gear = 3,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+// Refinery machines
+/obj/item/circuitboard/industrial_reagent_grinder
+ name = T_BOARD("industrial chemical grinder")
+ build_path = /obj/machinery/reagent_refinery/grinder
+ board_type = new /datum/frame/frame_types/industrial_reagent_grinder
+ req_components = list(
+ /obj/item/stock_parts/capacitor = 1,
+ /obj/item/stock_parts/motor = 2,
+ /obj/item/stock_parts/gear = 2,
+ /obj/item/reagent_containers/glass/beaker/large = 1)
+
+/obj/item/circuitboard/industrial_reagent_pump
+ name = T_BOARD("industrial chemical pump")
+ build_path = /obj/machinery/reagent_refinery/pump
+ board_type = new /datum/frame/frame_types/industrial_reagent_pump
+ req_components = list(
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_filter
+ name = T_BOARD("industrial chemical filter")
+ build_path = /obj/machinery/reagent_refinery/filter
+ board_type = new /datum/frame/frame_types/industrial_reagent_filter
+ req_components = list(
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stock_parts/scanning_module = 1,
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stock_parts/gear = 1,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_vat
+ name = T_BOARD("industrial chemical vat")
+ build_path = /obj/machinery/reagent_refinery/vat
+ board_type = new /datum/frame/frame_types/industrial_reagent_vat
+ req_components = list(
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_pipe
+ name = T_BOARD("industrial chemical pipe")
+ build_path = /obj/machinery/reagent_refinery/pipe
+ board_type = new /datum/frame/frame_types/industrial_reagent_pipe
+ req_components = list( /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_waste_processor
+ name = T_BOARD("industrial chemical waste processor")
+ build_path = /obj/machinery/reagent_refinery/waste_processor
+ board_type = new /datum/frame/frame_types/industrial_reagent_waste_processor
+ req_components = list(
+ /obj/item/stock_parts/capacitor = 4,
+ /obj/item/stock_parts/micro_laser = 4,
+ /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_hub
+ name = T_BOARD("industrial chemical hub")
+ build_path = /obj/machinery/reagent_refinery/hub
+ board_type = new /datum/frame/frame_types/industrial_reagent_hub
+ req_components = list(
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stock_parts/manipulator = 1)
+
+/obj/item/circuitboard/industrial_reagent_reactor
+ name = T_BOARD("industrial chemical reactor")
+ build_path = /obj/machinery/reagent_refinery/reactor
+ board_type = new /datum/frame/frame_types/industrial_reagent_reactor
+ req_components = list(
+ /obj/item/stock_parts/motor = 1,
+ /obj/item/stock_parts/capacitor = 4,
+ /obj/item/stock_parts/micro_laser = 4,
+ /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stack/material/glass/reinforced = 1)
+
+/obj/item/circuitboard/industrial_reagent_furnace
+ name = T_BOARD("industrial chemical sintering furnace")
+ build_path = /obj/machinery/reagent_refinery/furnace
+ board_type = new /datum/frame/frame_types/industrial_reagent_furnace
+ req_components = list(
+ /obj/item/stock_parts/motor = 2,
+ /obj/item/stock_parts/capacitor = 4,
+ /obj/item/stock_parts/scanning_module = 1,
+ /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stock_parts/gear = 2)
+
+/obj/item/circuitboard/pump_relay
+ name = T_BOARD("pump relay")
+ build_path = /obj/machinery/pump_relay
+ board_type = new /datum/frame/frame_types/machine
+ req_components = list(
+ /obj/item/stock_parts/capacitor = 1,
+ /obj/item/stock_parts/motor = 1)
diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm
index b6ddf773d5a..530f73a0a16 100644
--- a/code/game/objects/items/weapons/manuals.dm
+++ b/code/game/objects/items/weapons/manuals.dm
@@ -1670,3 +1670,76 @@