diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 14bc65811fa..5261fa38db0 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -183,6 +183,9 @@
///from base of [/datum/reagent/proc/expose_atom]: (/turf, reac_volume)
#define COMSIG_REAGENT_EXPOSE_TURF "reagent_expose_turf"
+///from base of [/datum/controller/subsystem/materials/proc/InitializeMaterial]: (/datum/material)
+#define COMSIG_MATERIALS_INIT_MAT "SSmaterials_init_mat"
+
///from base of [/datum/reagents/proc/add_reagent]: (/datum/reagent, amount, reagtemp, data, no_react)
#define COMSIG_REAGENTS_NEW_REAGENT "reagents_new_reagent"
///from base of [/datum/reagents/proc/add_reagent]: (/datum/reagent, amount, reagtemp, data, no_react)
diff --git a/code/__DEFINES/materials.dm b/code/__DEFINES/materials.dm
index 9a7bf2e468e..107e8c841ff 100644
--- a/code/__DEFINES/materials.dm
+++ b/code/__DEFINES/materials.dm
@@ -9,6 +9,8 @@
/// Used to make a material initialize at roundstart.
#define MATERIAL_INIT_MAPLOAD (1<<0)
+/// Used to make a material type able to be instantiated on demand after roundstart.
+#define MATERIAL_INIT_BESPOKE (1<<1)
//Material Container Flags.
///If the container shows the amount of contained materials on examine.
@@ -46,6 +48,9 @@
#define MATERIAL_NO_EFFECTS (1<<2)
#define MATERIAL_AFFECT_STATISTICS (1<<3)
+/// Wrapper for fetching material references. Exists exclusively so that people don't need to wrap everything in a list every time.
+#define GET_MATERIAL_REF(arguments...) SSmaterials._GetMaterialRef(list(##arguments))
+
#define MATERIAL_SOURCE(mat) "[mat.name]_material"
diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm
index 1fdc72cbfab..cebdcc9be5e 100644
--- a/code/controllers/subsystem/materials.dm
+++ b/code/controllers/subsystem/materials.dm
@@ -8,12 +8,16 @@ These materials call on_applied() on whatever item they are applied to, common e
SUBSYSTEM_DEF(materials)
name = "Materials"
flags = SS_NO_FIRE | SS_NO_INIT
- ///Dictionary of material.type || material ref
+ ///Dictionary of material.id || material ref
var/list/materials
+ ///Dictionary of type || list of material refs
+ var/list/materials_by_type
+ ///Dictionary of type || list of material ids
+ var/list/materialids_by_type
///Dictionary of category || list of material refs
var/list/materials_by_category
- ///Dictionary of category || list of material types, mostly used by rnd machines like autolathes.
- var/list/materialtypes_by_category
+ ///Dictionary of category || list of material ids, mostly used by rnd machines like autolathes.
+ var/list/materialids_by_category
///A cache of all material combinations that have been used
var/list/list/material_combos
///List of stackcrafting recipes for materials using base recipes
@@ -31,40 +35,131 @@ SUBSYSTEM_DEF(materials)
///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info)
/datum/controller/subsystem/materials/proc/InitializeMaterials()
materials = list()
+ materials_by_type = list()
+ materialids_by_type = list()
materials_by_category = list()
- materialtypes_by_category = list()
+ materialids_by_category = list()
material_combos = list()
for(var/type in subtypesof(/datum/material))
- var/datum/material/ref = type
- if(!(initial(ref.init_flags) & MATERIAL_INIT_MAPLOAD))
- continue // Do not initialize
+ var/datum/material/mat_type = type
+ if(!(initial(mat_type.init_flags) & MATERIAL_INIT_MAPLOAD))
+ continue // Do not initialize at mapload
+ InitializeMaterial(list(mat_type))
- ref = new ref
- materials[type] = ref
- for(var/c in ref.categories)
- materials_by_category[c] += list(ref)
- materialtypes_by_category[c] += list(type)
+/** Creates and caches a material datum.
+ *
+ * Arugments:
+ * - [arguments][/list]: The arguments to use to create the material datum
+ * - The first element is the type of material to initialize.
+ */
+/datum/controller/subsystem/materials/proc/InitializeMaterial(list/arguments)
+ var/datum/material/mat_type = arguments[1]
+ if(initial(mat_type.init_flags) & MATERIAL_INIT_BESPOKE)
+ arguments[1] = GetIdFromArguments(arguments)
-/datum/controller/subsystem/materials/proc/GetMaterialRef(datum/material/fakemat)
+ var/datum/material/mat_ref = new mat_type
+ if(!mat_ref.Initialize(arglist(arguments)))
+ return null
+
+ var/mat_id = mat_ref.id
+ materials[mat_id] = mat_ref
+ materials_by_type[mat_type] += list(mat_ref)
+ materialids_by_type[mat_type] += list(mat_id)
+ for(var/category in mat_ref.categories)
+ materials_by_category[category] += list(mat_ref)
+ materialids_by_category[category] += list(mat_id)
+
+ SEND_SIGNAL(src, COMSIG_MATERIALS_INIT_MAT, mat_ref)
+ return mat_ref
+
+/** Fetches a cached material singleton when passed sufficient arguments.
+ *
+ * Arguments:
+ * - [arguments][/list]: The list of arguments used to fetch the material ref.
+ * - The first element is a material datum, text string, or material type.
+ * - [Material datums][/datum/material] are assumed to be references to the cached datum and are returned
+ * - Text is assumed to be the text ID of a material and the corresponding material is fetched from the cache
+ * - A material type is checked for bespokeness:
+ * - If the material type is not bespoke the type is assumed to be the id for a material and the corresponding material is loaded from the cache.
+ * - If the material type is bespoke a text ID is generated from the arguments list and used to load a material datum from the cache.
+ * - The following elements are used to generate bespoke IDs
+ */
+/datum/controller/subsystem/materials/proc/_GetMaterialRef(list/arguments)
if(!materials)
InitializeMaterials()
- return materials[fakemat] || fakemat
-///Returns a list to be used as an object's custom_materials. Lists will be cached and re-used based on the parameters.
+ var/datum/material/key = arguments[1]
+ if(istype(key))
+ return key // We are assuming here that the only thing allowed to create material datums is [/datum/controller/subsystem/materials/proc/InitializeMaterial]
+
+ if(istext(key)) // Handle text id
+ . = materials[key]
+ if(!.)
+ WARNING("Attempted to fetch material ref with invalid text id '[key]'")
+ return
+
+ if(!ispath(key, /datum/material))
+ CRASH("Attempted to fetch material ref with invalid key [key]")
+
+ if(!(initial(key.init_flags) & MATERIAL_INIT_BESPOKE))
+ . = materials[key]
+ if(!.)
+ WARNING("Attempted to fetch reference to an abstract material with key [key]")
+ return
+
+ key = GetIdFromArguments(arguments)
+ return materials[key] || InitializeMaterial(arguments)
+
+/** I'm not going to lie, this was swiped from [SSdcs][/datum/controller/subsystem/processing/dcs].
+ * Credit does to ninjanomnom
+ *
+ * Generates an id for bespoke ~~elements~~ materials when given the argument list
+ * Generating the id here is a bit complex because we need to support named arguments
+ * Named arguments can appear in any order and we need them to appear after ordered arguments
+ * We assume that no one will pass in a named argument with a value of null
+ **/
+/datum/controller/subsystem/materials/proc/GetIdFromArguments(list/arguments)
+ var/datum/material/mattype = arguments[1]
+ var/list/fullid = list("[initial(mattype.id) || mattype]")
+ var/list/named_arguments = list()
+ for(var/i in 2 to length(arguments))
+ var/key = arguments[i]
+ var/value
+ if(istext(key))
+ value = arguments[key]
+ if(!(istext(key) || isnum(key)))
+ key = REF(key)
+ key = "[key]" // Key is stringified so numbers dont break things
+ if(!isnull(value))
+ if(!(istext(value) || isnum(value)))
+ value = REF(value)
+ named_arguments["[key]"] = value
+ else
+ fullid += "[key]"
+
+ if(length(named_arguments))
+ named_arguments = sortList(named_arguments)
+ fullid += named_arguments
+ return list2params(fullid)
+
+
+/// Returns a list to be used as an object's custom_materials. Lists will be cached and re-used based on the parameters.
/datum/controller/subsystem/materials/proc/FindOrCreateMaterialCombo(list/materials_declaration, multiplier)
+ if(!LAZYLEN(materials_declaration))
+ return null // If we get a null we pass it right back, we don't want to generate stack traces just because something is clearing out its materials list.
+
if(!material_combos)
InitializeMaterials()
var/list/combo_params = list()
for(var/x in materials_declaration)
var/datum/material/mat = x
- var/path_name = ispath(mat) ? "[mat]" : "[mat.type]"
- combo_params += "[path_name]=[materials_declaration[mat] * multiplier]"
+ combo_params += "[istype(mat) ? mat.id : mat]=[materials_declaration[mat] * multiplier]"
sortTim(combo_params, /proc/cmp_text_asc) // We have to sort now in case the declaration was not in order
var/combo_index = combo_params.Join("-")
var/list/combo = material_combos[combo_index]
if(!combo)
combo = list()
for(var/mat in materials_declaration)
- combo[GetMaterialRef(mat)] = materials_declaration[mat] * multiplier
+ combo[GET_MATERIAL_REF(mat)] = materials_declaration[mat] * multiplier
material_combos[combo_index] = combo
return combo
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index 70f13376639..cddebbab6a1 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -1,9 +1,15 @@
/datum/component/butchering
- var/speed = 80 //time in deciseconds taken to butcher something
- var/effectiveness = 100 //percentage effectiveness; numbers above 100 yield extra drops
- var/bonus_modifier = 0 //percentage increase to bonus item chance
- var/butcher_sound = 'sound/effects/butcher.ogg' //sound played when butchering
+ /// Time in deciseconds taken to butcher something
+ var/speed = 8 SECONDS
+ /// Percentage effectiveness; numbers above 100 yield extra drops
+ var/effectiveness = 100
+ /// Percentage increase to bonus item chance
+ var/bonus_modifier = 0
+ /// Sound played when butchering
+ var/butcher_sound = 'sound/effects/butcher.ogg'
+ /// Whether or not this component can be used to butcher currently. Used to temporarily disable butchering
var/butchering_enabled = TRUE
+ /// Whether or not this component is compatible with blunt tools.
var/can_be_blunt = FALSE
/datum/component/butchering/Initialize(_speed, _effectiveness, _bonus_modifier, _butcher_sound, disabled, _can_be_blunt)
@@ -77,7 +83,15 @@
screaming_through_a_slit_throat.apply_wound(slit_throat)
H.apply_status_effect(/datum/status_effect/neck_slice)
+/**
+ * Handles a user butchering a target
+ *
+ * Arguments:
+ * - [butcher][/mob/living]: The mob doing the butchering
+ * - [meat][/mob/living]: The mob being butchered
+ */
/datum/component/butchering/proc/Butcher(mob/living/butcher, mob/living/meat)
+ var/list/results = list()
var/turf/T = meat.drop_location()
var/final_effectiveness = effectiveness - meat.butcher_difficulty
var/bonus_chance = max(0, (final_effectiveness - 100) + bonus_modifier) //so 125 total effectiveness = 25% extra chance
@@ -88,20 +102,29 @@
if(!prob(final_effectiveness))
if(butcher)
to_chat(butcher, "You fail to harvest some of the [initial(bones.name)] from [meat].")
- else if(prob(bonus_chance))
+ continue
+
+ if(prob(bonus_chance))
if(butcher)
to_chat(butcher, "You harvest some extra [initial(bones.name)] from [meat]!")
- for(var/i in 1 to 2)
- new bones (T)
- else
- new bones (T)
+ results += new bones (T)
+ results += new bones (T)
+
meat.butcher_results.Remove(bones) //in case you want to, say, have it drop its results on gib
+
for(var/V in meat.guaranteed_butcher_results)
var/obj/sinew = V
var/amount = meat.guaranteed_butcher_results[sinew]
for(var/i in 1 to amount)
- new sinew (T)
+ results += new sinew (T)
meat.guaranteed_butcher_results.Remove(sinew)
+
+ for(var/obj/item/carrion in results)
+ var/list/meat_mats = carrion.has_material_type(/datum/material/meat)
+ if(!length(meat_mats))
+ continue
+ carrion.set_custom_materials((carrion.custom_materials - meat_mats) + list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, meat) = counterlist_sum(meat_mats)))
+
if(butcher)
butcher.visible_message("[butcher] butchers [meat].", \
"You butcher [meat].")
diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm
index 010500d4adf..83ec8d61194 100644
--- a/code/datums/components/material_container.dm
+++ b/code/datums/components/material_container.dm
@@ -10,42 +10,78 @@
*/
/datum/component/material_container
+ /// The total amount of materials this material container contains
var/total_amount = 0
+ /// The maximum amount of materials this material container can contain
var/max_amount
- var/sheet_type
+ /// Map of material ref -> amount
var/list/materials //Map of key = material ref | Value = amount
- var/disable_attackby
- var/list/allowed_typecache
+ /// The list of materials that this material container can accept
+ var/list/allowed_materials
+ /// The typecache of things that this material container can accept
+ var/list/allowed_item_typecache
+ /// The last main material that was inserted into this container
var/last_inserted_id
+ /// Whether or not this material container allows specific amounts from sheets to be inserted
var/precise_insertion = FALSE
+ /// A callback for checking wheter we can insert a material into this container
+ var/datum/callback/insertion_check
+ /// A callback invoked before materials are inserted into this container
var/datum/callback/precondition
+ /// A callback invoked after materials are inserted into this container
var/datum/callback/after_insert
- ///The material container flags. See __DEFINES/materials.dm.
+ /// The material container flags. See __DEFINES/materials.dm.
var/mat_container_flags
/// Sets up the proper signals and fills the list of materials with the appropriate references.
-/datum/component/material_container/Initialize(list/mat_list, max_amt = 0, _mat_container_flags=NONE, list/allowed_types, datum/callback/_precondition, datum/callback/_after_insert)
+/datum/component/material_container/Initialize(list/init_mats, max_amt = 0, _mat_container_flags=NONE, list/allowed_mats=init_mats, list/allowed_items, datum/callback/_insertion_check, datum/callback/_precondition, datum/callback/_after_insert)
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+
materials = list()
max_amount = max(0, max_amt)
mat_container_flags = _mat_container_flags
- if(allowed_types)
- if(ispath(allowed_types) && allowed_types == /obj/item/stack)
- allowed_typecache = GLOB.typecache_stack
+ allowed_materials = allowed_mats || list()
+ if(allowed_items)
+ if(ispath(allowed_items) && allowed_items == /obj/item/stack)
+ allowed_item_typecache = GLOB.typecache_stack
else
- allowed_typecache = typecacheof(allowed_types)
+ allowed_item_typecache = typecacheof(allowed_items)
+ insertion_check = _insertion_check
precondition = _precondition
after_insert = _after_insert
+ for(var/mat in init_mats) //Make the assoc list material reference -> amount
+ var/mat_ref = GET_MATERIAL_REF(mat)
+ if(isnull(mat_ref))
+ continue
+ var/mat_amt = init_mats[mat]
+ if(isnull(mat_amt))
+ mat_amt = 0
+ materials[mat_ref] += mat_amt
+
+/datum/component/material_container/Destroy(force, silent)
+ materials = null
+ allowed_materials = null
+ if(insertion_check)
+ QDEL_NULL(insertion_check)
+ if(precondition)
+ QDEL_NULL(precondition)
+ if(after_insert)
+ QDEL_NULL(after_insert)
+ return ..()
+
+
+/datum/component/material_container/RegisterWithParent()
+ . = ..()
+
if(!(mat_container_flags & MATCONTAINER_NO_INSERT))
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/on_attackby)
if(mat_container_flags & MATCONTAINER_EXAMINE)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
- for(var/mat in mat_list) //Make the assoc list ref | amount
- var/datum/material/M = SSmaterials.GetMaterialRef(mat)
- materials[M] = 0
/datum/component/material_container/vv_edit_var(var_name, var_value)
var/old_flags = mat_container_flags
@@ -75,7 +111,7 @@
/datum/component/material_container/proc/on_attackby(datum/source, obj/item/I, mob/living/user)
SIGNAL_HANDLER
- var/list/tc = allowed_typecache
+ var/list/tc = allowed_item_typecache
if(!(mat_container_flags & MATCONTAINER_ANY_INTENT) && user.a_intent != INTENT_HELP)
return
if(I.item_flags & ABSTRACT)
@@ -136,54 +172,91 @@
last_inserted_id = insert_item_materials(I, multiplier, breakdown_flags)
return material_amount
-/datum/component/material_container/proc/insert_item_materials(obj/item/I, multiplier = 1, breakdown_flags = mat_container_flags)
+/**
+ * Inserts the relevant materials from an item into this material container.
+ *
+ * Arguments:
+ * - [source][/obj/item]: The source of the materials we are inserting.
+ * - multiplier: The multiplier for the materials being inserted.
+ * - breakdown_flags: The breakdown bitflags that will be used to retrieve the materials from the source
+ */
+/datum/component/material_container/proc/insert_item_materials(obj/item/source, multiplier = 1, breakdown_flags = mat_container_flags)
var/primary_mat
var/max_mat_value = 0
- var/list/item_materials = I.get_material_composition(breakdown_flags)
- for(var/MAT in materials)
+ var/list/item_materials = source.get_material_composition(breakdown_flags)
+ for(var/MAT in item_materials)
+ if(!can_hold_material(MAT))
+ continue
materials[MAT] += item_materials[MAT] * multiplier
total_amount += item_materials[MAT] * multiplier
if(item_materials[MAT] > max_mat_value)
max_mat_value = item_materials[MAT]
primary_mat = MAT
+
return primary_mat
+/**
+ * The default check for whether we can add materials to this material container.
+ *
+ * Arguments:
+ * - [mat][/atom/material]: The material we are checking for insertability.
+ */
+/datum/component/material_container/proc/can_hold_material(datum/material/mat)
+ if(mat in allowed_materials)
+ return TRUE
+ if(istype(mat) && ((mat.id in allowed_materials) || (mat.type in allowed_materials)))
+ allowed_materials += mat // This could get messy with passing lists by ref... but if you're doing that the list expansion is probably being taken care of elsewhere anyway...
+ return TRUE
+ if(insertion_check?.Invoke(mat))
+ allowed_materials += mat
+ return TRUE
+ return FALSE
+
/// For inserting an amount of material
/datum/component/material_container/proc/insert_amount_mat(amt, datum/material/mat)
- if(!istype(mat))
- mat = SSmaterials.GetMaterialRef(mat)
- if(amt > 0 && has_space(amt))
- var/total_amount_saved = total_amount
- if(mat)
- materials[mat] += amt
- else
- for(var/i in materials)
- materials[i] += amt
- total_amount += amt
- return (total_amount - total_amount_saved)
- return FALSE
+ if(amt <= 0 || !has_space(amt))
+ return 0
+
+ var/total_amount_saved = total_amount
+ if(mat)
+ if(!istype(mat))
+ mat = GET_MATERIAL_REF(mat)
+ materials[mat] += amt
+ else
+ var/num_materials = length(materials)
+ if(!num_materials)
+ return 0
+
+ amt /= num_materials
+ for(var/i in materials)
+ materials[i] += amt
+ total_amount += amt
+ return (total_amount - total_amount_saved)
/// Uses an amount of a specific material, effectively removing it.
/datum/component/material_container/proc/use_amount_mat(amt, datum/material/mat)
if(!istype(mat))
- mat = SSmaterials.GetMaterialRef(mat)
+ mat = GET_MATERIAL_REF(mat)
+
+ if(!mat)
+ return 0
var/amount = materials[mat]
- if(mat)
- if(amount >= amt)
- materials[mat] -= amt
- total_amount -= amt
- return amt
- return FALSE
+ if(amount < amt)
+ return 0
+
+ materials[mat] -= amt
+ total_amount -= amt
+ return amt
/// Proc for transfering materials to another container.
/datum/component/material_container/proc/transer_amt_to(datum/component/material_container/T, amt, datum/material/mat)
if(!istype(mat))
- mat = SSmaterials.GetMaterialRef(mat)
+ mat = GET_MATERIAL_REF(mat)
if((amt==0)||(!T)||(!mat))
return FALSE
if(amt<0)
return T.transer_amt_to(src, -amt, mat)
- var/tr = min(amt, materials[mat],T.can_insert_amount_mat(amt, mat))
+ var/tr = min(amt, materials[mat], T.can_insert_amount_mat(amt, mat))
if(tr)
use_amount_mat(tr, mat)
T.insert_amount_mat(tr, mat)
@@ -191,14 +264,14 @@
return FALSE
/// Proc for checking if there is room in the component, returning the amount or else the amount lacking.
-/datum/component/material_container/proc/can_insert_amount_mat(amt, mat)
- if(amt && mat)
- var/datum/material/M = mat
- if(M)
- if((total_amount + amt) <= max_amount)
- return amt
- else
- return (max_amount-total_amount)
+/datum/component/material_container/proc/can_insert_amount_mat(amt, datum/material/mat)
+ if(!amt || !mat)
+ return 0
+
+ if((total_amount + amt) <= max_amount)
+ return amt
+ else
+ return (max_amount - total_amount)
/// For consuming a dictionary of materials. mats is the map of materials to use and the corresponding amounts, example: list(M/datum/material/glass =100, datum/material/iron=200)
@@ -211,7 +284,7 @@
for(var/x in mats) //Loop through all required materials
var/datum/material/req_mat = x
if(!istype(req_mat))
- req_mat = SSmaterials.GetMaterialRef(req_mat) //Get the ref if necesary
+ req_mat = GET_MATERIAL_REF(req_mat) //Get the ref if necesary
if(!materials[req_mat]) //Do we have the resource?
return FALSE //Can't afford it
var/amount_required = mats[x] * multiplier
@@ -228,24 +301,25 @@
return total_amount_save - total_amount
/// For spawning mineral sheets at a specific location. Used by machines to output sheets.
-/datum/component/material_container/proc/retrieve_sheets(sheet_amt, datum/material/M, target = null)
+/datum/component/material_container/proc/retrieve_sheets(sheet_amt, datum/material/M, atom/target = null)
if(!M.sheet_type)
return 0 //Add greyscale sheet handling here later
if(sheet_amt <= 0)
return 0
if(!target)
- target = get_turf(parent)
+ var/atom/parent_atom = parent
+ target = parent_atom.drop_location()
if(materials[M] < (sheet_amt * MINERAL_MATERIAL_AMOUNT))
sheet_amt = round(materials[M] / MINERAL_MATERIAL_AMOUNT)
var/count = 0
while(sheet_amt > MAX_STACK_SIZE)
- new M.sheet_type(target, MAX_STACK_SIZE)
+ new M.sheet_type(target, MAX_STACK_SIZE, null, list((M) = MINERAL_MATERIAL_AMOUNT))
count += MAX_STACK_SIZE
use_amount_mat(sheet_amt * MINERAL_MATERIAL_AMOUNT, M)
sheet_amt -= MAX_STACK_SIZE
if(sheet_amt >= 1)
- new M.sheet_type(target, sheet_amt)
+ new M.sheet_type(target, sheet_amt, null, list((M) = MINERAL_MATERIAL_AMOUNT))
count += sheet_amt
use_amount_mat(sheet_amt * MINERAL_MATERIAL_AMOUNT, M)
return count
@@ -272,7 +346,7 @@
var/datum/material/req_mat = x
if(!istype(req_mat))
if(ispath(req_mat)) //Is this an actual material, or is it a category?
- req_mat = SSmaterials.GetMaterialRef(req_mat) //Get the ref
+ req_mat = GET_MATERIAL_REF(req_mat) //Get the ref
else // Its a category. (For example MAT_CATEGORY_RIGID)
if(!has_enough_of_category(req_mat, mats[x], multiplier)) //Do we have enough of this category?
@@ -294,7 +368,6 @@
categories += x
return categories
-
/// Returns TRUE if you have enough of the specified material.
/datum/component/material_container/proc/has_enough_of_material(datum/material/req_mat, amount, multiplier=1)
if(!materials[req_mat]) //Do we have the resource?
@@ -331,12 +404,14 @@
return 0
var/material_amount = 0
var/list/item_materials = I.get_material_composition(breakdown_flags)
- for(var/MAT in materials)
+ for(var/MAT in item_materials)
+ if(!can_hold_material(MAT))
+ continue
material_amount += item_materials[MAT]
return material_amount
/// Returns the amount of a specific material in this container.
/datum/component/material_container/proc/get_material_amount(datum/material/mat)
if(!istype(mat))
- mat = SSmaterials.GetMaterialRef(mat)
- return(materials[mat])
+ mat = GET_MATERIAL_REF(mat)
+ return materials[mat]
diff --git a/code/datums/components/remote_materials.dm b/code/datums/components/remote_materials.dm
index f8ee6a830e9..566b3b5c1d3 100644
--- a/code/datums/components/remote_materials.dm
+++ b/code/datums/components/remote_materials.dm
@@ -72,7 +72,7 @@ handles linking back and forth.
/datum/material/plastic,
)
- mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, mat_container_flags, /obj/item/stack)
+ mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, mat_container_flags, allowed_items=/obj/item/stack)
/datum/component/remote_materials/proc/set_local_size(size)
local_size = size
diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm
index 3f55b6aa12b..f28e82c45af 100644
--- a/code/datums/materials/_material.dm
+++ b/code/datums/materials/_material.dm
@@ -6,8 +6,13 @@ Simple datum which is instanced once per type and is used for every object of sa
/datum/material
+ /// What the material is referred to as IC.
var/name = "material"
+ /// A short description of the material. Not used anywhere, yet...
var/desc = "its..stuff."
+ /// What the material is indexed by in the SSmaterials.materials list. Defaults to the type of the material.
+ var/id
+
///Base color of the material, is used for greyscale. Item isn't changed in color if this is null.
var/color
///Base alpha of the material, is used for greyscale icons.
@@ -39,12 +44,21 @@ Simple datum which is instanced once per type and is used for every object of sa
///What type of shard the material will shatter to
var/obj/item/shard_type
-/datum/material/New()
- . = ..()
+/** Handles initializing the material.
+ *
+ * Arugments:
+ * - _id: The ID the material should use. Overrides the existing ID.
+ */
+/datum/material/proc/Initialize(_id, ...)
+ if(_id)
+ id = _id
+ else if(isnull(id))
+ id = type
+
if(texture_layer_icon_state)
cached_texture_filter_icon = icon('icons/materials/composite.dmi', texture_layer_icon_state)
-
+ return TRUE
///This proc is called when the material is added to an object.
/datum/material/proc/on_applied(atom/source, amount, material_flags)
diff --git a/code/datums/materials/alloys.dm b/code/datums/materials/alloys.dm
index 68150493504..eec3809e110 100644
--- a/code/datums/materials/alloys.dm
+++ b/code/datums/materials/alloys.dm
@@ -16,7 +16,7 @@
. = list()
var/list/cached_comp = composition
for(var/comp_mat in cached_comp)
- var/datum/material/component_material = SSmaterials.GetMaterialRef(comp_mat)
+ var/datum/material/component_material = GET_MATERIAL_REF(comp_mat)
var/list/component_composition = component_material.return_composition(cached_comp[comp_mat], breakdown_flags)
for(var/comp_comp_mat in component_composition)
.[comp_comp_mat] += component_composition[comp_comp_mat] * amount
diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm
index 447b2db8ea5..ffa23b7a2ab 100644
--- a/code/datums/materials/meat.dm
+++ b/code/datums/materials/meat.dm
@@ -2,6 +2,7 @@
/datum/material/meat
name = "meat"
desc = "Meat"
+ id = /datum/material/meat // So the bespoke versions are categorized under this
color = rgb(214, 67, 67)
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
sheet_type = /obj/item/stack/sheet/meat
@@ -19,7 +20,6 @@
/datum/material/meat/on_applied_obj(obj/O, amount, material_flags)
. = ..()
- O.obj_flags |= UNIQUE_RENAME //So you can name it after the person its made from, a depressing comprimise.
make_edible(O, amount, material_flags)
/datum/material/meat/on_applied_turf(turf/T, amount, material_flags)
@@ -31,3 +31,23 @@
var/oil_count = 2 * (amount / MINERAL_MATERIAL_AMOUNT)
source.AddComponent(/datum/component/edible, list(/datum/reagent/consumable/nutriment = nutriment_count, /datum/reagent/consumable/cooking_oil = oil_count), null, RAW | MEAT | GROSS, null, 30, list("Fleshy"))
+
+/datum/material/meat/mob_meat
+ init_flags = MATERIAL_INIT_BESPOKE
+
+/datum/material/meat/mob_meat/Initialize(_id, mob/living/source)
+ if(!istype(source))
+ return FALSE
+
+ name = "[source?.name ? "[source.name]'s" : "mystery"] [initial(name)]"
+ return ..()
+
+/datum/material/meat/species_meat
+ init_flags = MATERIAL_INIT_BESPOKE
+
+/datum/material/meat/species_meat/Initialize(_id, datum/species/source)
+ if(!istype(source))
+ return FALSE
+
+ name = "[source?.name || "mystery"] [initial(name)]"
+ return ..()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index d2e89e0ac15..0176d01ce7e 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1576,11 +1576,11 @@
/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
-///Sets the custom materials for an item.
+/// Sets the custom materials for an item.
/atom/proc/set_custom_materials(list/materials, multiplier = 1)
if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways
for(var/i in custom_materials)
- var/datum/material/custom_material = SSmaterials.GetMaterialRef(i)
+ var/datum/material/custom_material = GET_MATERIAL_REF(i)
custom_material.on_removed(src, custom_materials[i], material_flags) //Remove the current materials
if(!length(materials))
@@ -1589,11 +1589,110 @@
if(!(material_flags & MATERIAL_NO_EFFECTS))
for(var/x in materials)
- var/datum/material/custom_material = SSmaterials.GetMaterialRef(x)
+ var/datum/material/custom_material = GET_MATERIAL_REF(x)
custom_material.on_applied(src, materials[x] * multiplier * material_modifier, material_flags)
custom_materials = SSmaterials.FindOrCreateMaterialCombo(materials, multiplier)
+/**
+ * Returns the material composition of the atom.
+ *
+ * Used when recycling items, specifically to turn alloys back into their component mats.
+ *
+ * Exists because I'd need to add a way to un-alloy alloys or otherwise deal
+ * with people converting the entire stations material supply into alloys.
+ *
+ * Arguments:
+ * - flags: A set of flags determining how exactly the materials are broken down.
+ */
+/atom/proc/get_material_composition(breakdown_flags=NONE)
+ . = list()
+ if(!(breakdown_flags & BREAKDOWN_INCLUDE_ALCHEMY) && HAS_TRAIT(src, TRAIT_MAT_TRANSMUTED))
+ return
+
+ var/list/cached_materials = custom_materials
+ for(var/mat in cached_materials)
+ var/datum/material/material = GET_MATERIAL_REF(mat)
+ var/list/material_comp = material.return_composition(cached_materials[mat], breakdown_flags)
+ for(var/comp_mat in material_comp)
+ .[comp_mat] += material_comp[comp_mat]
+
+/**
+ * Fetches a list of all of the materials this object has of the desired type
+ *
+ * Arguments:
+ * - [mat_type][/datum/material]: The type of material we are checking for
+ * - exact: Whether to search for the _exact_ material type
+ * - mat_amount: The minimum required amount of material
+ */
+/atom/proc/has_material_type(datum/material/mat_type, exact=FALSE, mat_amount=0)
+ var/list/cached_materials = custom_materials
+ if(!length(cached_materials))
+ return null
+
+ . = list()
+ for(var/m in cached_materials)
+ if(cached_materials[m] < mat_amount)
+ continue
+ var/datum/material/material = GET_MATERIAL_REF(m)
+ if(exact ? material.type != m : !istype(material, mat_type))
+ continue
+ .[material] = cached_materials[m]
+
+/**
+ * Fetches a list of all of the materials this object has with the desired material category.
+ *
+ * Arguments:
+ * - category: The category to check for
+ * - any_flags: Any bitflags that must be present for the category
+ * - all_flags: All bitflags that must be present for the category
+ * - no_flags: Any bitflags that must not be present for the category
+ * - mat_amount: The minimum amount of materials that must be present
+ */
+/atom/proc/has_material_category(category, any_flags=0, all_flags=0, no_flags=0, mat_amount=0)
+ var/list/cached_materials = custom_materials
+ if(!length(cached_materials))
+ return null
+
+ . = list()
+ for(var/m in cached_materials)
+ if(cached_materials[m] < mat_amount)
+ continue
+ var/datum/material/material = GET_MATERIAL_REF(m)
+ var/category_flags = material?.categories[category]
+ if(isnull(category_flags))
+ continue
+ if(any_flags && !(category_flags & any_flags))
+ continue
+ if(all_flags && (all_flags != (category_flags & all_flags)))
+ continue
+ if(no_flags && (category_flags & no_flags))
+ continue
+ .[material] = cached_materials[m]
+
+/**
+ * Gets the most common material in the object.
+ */
+/atom/proc/get_master_material()
+ var/list/cached_materials = custom_materials
+ if(!length(cached_materials))
+ return null
+
+ var/most_common_material = null
+ var/max_amount = 0
+ for(var/m in cached_materials)
+ if(cached_materials[m] > max_amount)
+ most_common_material = m
+ max_amount = cached_materials[m]
+
+ if(most_common_material)
+ return GET_MATERIAL_REF(most_common_material)
+
+/**
+ * Gets the total amount of materials in this atom.
+ */
+/atom/proc/get_custom_material_amount()
+ return isnull(custom_materials) ? 0 : counterlist_sum(custom_materials)
///Setter for the `base_pixel_x` variable to append behavior related to its changing.
/atom/proc/set_base_pixel_x(new_value)
@@ -1614,29 +1713,6 @@
pixel_y = pixel_y + base_pixel_y - .
-
-/**Returns the material composition of the atom.
- *
- * Used when recycling items, specifically to turn alloys back into their component mats.
- *
- * Exists because I'd need to add a way to un-alloy alloys or otherwise deal
- * with people converting the entire stations material supply into alloys.
- *
- * Arguments:
- * - flags: A set of flags determining how exactly the materials are broken down.
- */
-/atom/proc/get_material_composition(breakdown_flags=NONE)
- . = list()
- if(!(breakdown_flags & BREAKDOWN_INCLUDE_ALCHEMY) && HAS_TRAIT(src, TRAIT_MAT_TRANSMUTED))
- return
-
- var/list/cached_materials = custom_materials
- for(var/mat in cached_materials)
- var/datum/material/material = SSmaterials.GetMaterialRef(mat)
- var/list/material_comp = material.return_composition(cached_materials[material], breakdown_flags)
- for(var/comp_mat in material_comp)
- .[comp_mat] += material_comp[comp_mat]
-
/**
* Returns true if this atom has gravity for the passed in turf
*
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index c4da40b3b11..049e2274c8d 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -48,7 +48,7 @@
)
/obj/machinery/autolathe/Initialize()
- AddComponent(/datum/component/material_container, SSmaterials.materialtypes_by_category[MAT_CATEGORY_RIGID], 0, MATCONTAINER_EXAMINE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
+ AddComponent(/datum/component/material_container, SSmaterials.materials_by_category[MAT_CATEGORY_RIGID], 0, MATCONTAINER_EXAMINE, _after_insert = CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autolathe(src)
@@ -123,14 +123,13 @@
return ..()
-/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
+/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
use_power(MINERAL_MATERIAL_AMOUNT / 10)
- else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
- flick("autolathe_r",src)//plays glass insertion animation by default otherwise
+ else if(item_inserted.has_material_type(/datum/material/glass))
+ flick("autolathe_r", src)//plays glass insertion animation by default otherwise
else
- flick("autolathe_o",src)//plays metal insertion animation
-
+ flick("autolathe_o", src)//plays metal insertion animation
use_power(min(1000, amount_inserted / 100))
updateUsrDialog()
@@ -222,7 +221,7 @@
materials.use_materials(materials_used)
if(is_stack)
- var/obj/item/stack/N = new being_built.build_path(A, multiplier)
+ var/obj/item/stack/N = new being_built.build_path(A, multiplier, FALSE)
N.update_icon()
N.autolathe_crafted(src)
else
diff --git a/code/game/machinery/droneDispenser.dm b/code/game/machinery/droneDispenser.dm
index 4345c76a800..460363f8468 100644
--- a/code/game/machinery/droneDispenser.dm
+++ b/code/game/machinery/droneDispenser.dm
@@ -50,7 +50,7 @@
/obj/machinery/drone_dispenser/Initialize()
. = ..()
- var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(/datum/material/iron, /datum/material/glass), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_DRONE_DISPENSER, /obj/item/stack)
+ var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(/datum/material/iron, /datum/material/glass), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_DRONE_DISPENSER, allowed_items=/obj/item/stack)
materials.insert_amount_mat(starting_amount)
materials.precise_insertion = TRUE
using_materials = list(/datum/material/iron = metal_cost, /datum/material/glass = glass_cost)
diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm
index b530d2362be..b026ed542c7 100644
--- a/code/game/machinery/fat_sucker.dm
+++ b/code/game/machinery/fat_sucker.dm
@@ -179,10 +179,12 @@
C.put_in_hands(new /obj/item/food/cookie, del_on_fail = TRUE)
while(nutrients >= nutrient_to_meat)
nutrients -= nutrient_to_meat
- new C.type_of_meat (drop_location())
+ var/atom/meat = new C.type_of_meat (drop_location())
+ meat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, C) = MINERAL_MATERIAL_AMOUNT * 4))
while(nutrients >= nutrient_to_meat / 3)
nutrients -= nutrient_to_meat / 3
- new /obj/item/food/meat/rawcutlet/plain (drop_location())
+ var/atom/meat = new /obj/item/food/meat/rawcutlet/plain (drop_location())
+ meat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, C) = round(MINERAL_MATERIAL_AMOUNT * (4/3))))
nutrients = 0
/obj/machinery/fat_sucker/screwdriver_act(mob/living/user, obj/item/I)
diff --git a/code/game/machinery/sheetifier.dm b/code/game/machinery/sheetifier.dm
index d388564aaae..924b12e6b84 100644
--- a/code/game/machinery/sheetifier.dm
+++ b/code/game/machinery/sheetifier.dm
@@ -13,7 +13,8 @@
/obj/machinery/sheetifier/Initialize()
. = ..()
- AddComponent(/datum/component/material_container, list(/datum/material/meat), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_SHEETIFIER, /obj/item/food/meat/slab, CALLBACK(src, .proc/CanInsertMaterials), CALLBACK(src, .proc/AfterInsertMaterials))
+
+ AddComponent(/datum/component/material_container, list(/datum/material/meat), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_SHEETIFIER, typesof(/datum/material/meat), /obj/item/food/meat, null, CALLBACK(src, .proc/CanInsertMaterials), CALLBACK(src, .proc/AfterInsertMaterials))
/obj/machinery/sheetifier/update_overlays()
. = ..()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2a0354df308..7e059ca465d 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1065,7 +1065,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
found_mats++
//if there's glass in it and the glass is more than 60% of the item, then we can shatter it
- if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)] >= total_material_amount * 0.60)
+ if(custom_materials[GET_MATERIAL_REF(/datum/material/glass)] >= total_material_amount * 0.60)
if(prob(66)) //66% chance to break it
/// The glass shard that is spawned into the source item
var/obj/item/shard/broken_glass = new /obj/item/shard(loc)
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index f1dafbb75ab..b8c9a5b5181 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -161,7 +161,7 @@ RLD
return FALSE
var/list/materials = list()
- materials[SSmaterials.GetMaterialRef(/datum/material/iron)] = 500
+ materials[GET_MATERIAL_REF(/datum/material/iron)] = 500
silo_mats.mat_container.use_materials(materials, amount)
silo_mats.silo_log(src, "consume", -amount, "build", materials)
return TRUE
diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm
index 6eff92125fb..e9e9304dc06 100644
--- a/code/game/objects/items/apc_frame.dm
+++ b/code/game/objects/items/apc_frame.dm
@@ -66,8 +66,8 @@
if(iswallturf(T))
T.attackby(src, user, params)
- var/metal_amt = round(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)]/MINERAL_MATERIAL_AMOUNT) //Replace this shit later
- var/glass_amt = round(custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]/MINERAL_MATERIAL_AMOUNT) //Replace this shit later
+ var/metal_amt = round(custom_materials[GET_MATERIAL_REF(/datum/material/iron)]/MINERAL_MATERIAL_AMOUNT) //Replace this shit later
+ var/glass_amt = round(custom_materials[GET_MATERIAL_REF(/datum/material/glass)]/MINERAL_MATERIAL_AMOUNT) //Replace this shit later
if(W.tool_behaviour == TOOL_WRENCH && (metal_amt || glass_amt))
to_chat(user, "You dismantle [src].")
diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm
index 6634fc11e47..f529627524f 100644
--- a/code/game/objects/items/stacks/bscrystal.dm
+++ b/code/game/objects/items/stacks/bscrystal.dm
@@ -22,7 +22,7 @@
refined_type = null
merge_type = /obj/item/stack/ore/bluespace_crystal/refined
-/obj/item/stack/ore/bluespace_crystal/Initialize()
+/obj/item/stack/ore/bluespace_crystal/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
diff --git a/code/game/objects/items/stacks/cash.dm b/code/game/objects/items/stacks/cash.dm
index d49c8db08bf..73aa5eecb50 100644
--- a/code/game/objects/items/stacks/cash.dm
+++ b/code/game/objects/items/stacks/cash.dm
@@ -14,7 +14,7 @@
var/value = 0
grind_results = list(/datum/reagent/cellulose = 10)
-/obj/item/stack/spacecash/Initialize()
+/obj/item/stack/spacecash/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
update_desc()
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 89dd43a97e4..85e214f4bae 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -278,7 +278,7 @@
grind_results = list(/datum/reagent/medicine/spaceacillin = 2)
merge_type = /obj/item/stack/medical/mesh
-/obj/item/stack/medical/mesh/Initialize()
+/obj/item/stack/medical/mesh/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
if(amount == max_amount) //only seal full mesh packs
is_open = FALSE
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index e57cae20fad..6236538efc7 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -36,7 +36,7 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
user.visible_message("[user] begins to stuff \the [src] down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!")//it looks like theyre ur mum
return BRUTELOSS
-/obj/item/stack/rods/Initialize(mapload, new_amount, merge = TRUE)
+/obj/item/stack/rods/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
update_icon()
diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm
index 29741f3cd9c..0678745adab 100644
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ b/code/game/objects/items/stacks/sheets/leather.dm
@@ -153,7 +153,7 @@ GLOBAL_LIST_INIT(xeno_recipes, list ( \
/// Kelvin to start drying
var/drying_threshold_temperature = 500
-/obj/item/stack/sheet/wethide/Initialize(mapload, new_amount, merge)
+/obj/item/stack/sheet/wethide/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
AddElement(/datum/element/dryable, /obj/item/stack/sheet/leather)
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 0de720af211..b69815948b4 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -595,7 +595,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
. = ..()
. += GLOB.bronze_recipes
-/obj/item/stack/sheet/paperframes/Initialize()
+/obj/item/stack/sheet/paperframes/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = 0
pixel_y = 0
diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm
index 6d0daa37fa4..92b17d57606 100644
--- a/code/game/objects/items/stacks/sheets/sheets.dm
+++ b/code/game/objects/items/stacks/sheets/sheets.dm
@@ -16,7 +16,7 @@
///What type of wall does this sheet spawn
var/walltype
-/obj/item/stack/sheet/Initialize(mapload, new_amount, merge)
+/obj/item/stack/sheet/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = rand(-4, 4)
pixel_y = rand(-4, 4)
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 5393091e631..0745ab26a7b 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -40,7 +40,7 @@
/// Amount of matter for RCD
var/matter_amount = 0
-/obj/item/stack/Initialize(mapload, new_amount, merge = TRUE)
+/obj/item/stack/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
if(new_amount != null)
amount = new_amount
while(amount > max_amount)
@@ -49,7 +49,9 @@
if(!merge_type)
merge_type = type
- if(LAZYLEN(mats_per_unit))
+ if(LAZYLEN(mat_override))
+ set_mats_per_unit(mat_override, mat_amt)
+ else if(LAZYLEN(mats_per_unit))
set_mats_per_unit(mats_per_unit, 1)
else if(LAZYLEN(custom_materials))
set_mats_per_unit(custom_materials, amount ? 1/amount : 1)
@@ -62,7 +64,7 @@
var/list/temp_recipes = get_main_recipes()
recipes = temp_recipes.Copy()
if(material_type)
- var/datum/material/M = SSmaterials.GetMaterialRef(material_type) //First/main material
+ var/datum/material/M = GET_MATERIAL_REF(material_type) //First/main material
for(var/i in M.categories)
switch(i)
if(MAT_CATEGORY_BASE_RECIPES)
@@ -477,9 +479,8 @@
/obj/item/stack/proc/split_stack(mob/user, amount)
if(!use(amount, TRUE, FALSE))
return null
- var/obj/item/stack/F = new type(user? user : drop_location(), amount, FALSE)
+ var/obj/item/stack/F = new type(user? user : drop_location(), amount, FALSE, mats_per_unit)
. = F
- F.set_mats_per_unit(mats_per_unit, 1) // Required for greyscale sheets and tiles.
F.copy_evidences(src)
if(user)
if(!user.put_in_hands(F, merge_stacks = FALSE))
diff --git a/code/game/objects/items/stacks/tickets.dm b/code/game/objects/items/stacks/tickets.dm
index 45db72771ac..92a66471c6b 100644
--- a/code/game/objects/items/stacks/tickets.dm
+++ b/code/game/objects/items/stacks/tickets.dm
@@ -8,7 +8,7 @@
max_amount = 30
merge_type = /obj/item/stack/arcadeticket
-/obj/item/stack/arcadeticket/Initialize(mapload, new_amount, merge = TRUE)
+/obj/item/stack/arcadeticket/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
update_icon()
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 4c5cd820216..19384a21064 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -20,7 +20,7 @@
var/list/tile_reskin_types
-/obj/item/stack/tile/Initialize(mapload, amount)
+/obj/item/stack/tile/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = rand(-3, 3)
pixel_y = rand(-3, 3) //randomize a little
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index c616a78ff8b..30eec3b0212 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -320,7 +320,7 @@
if(remaining_mats)
for(var/M=1 to remaining_mats)
new stack_type(get_turf(loc))
- else if(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)])
+ else if(custom_materials[GET_MATERIAL_REF(/datum/material/iron)])
new /obj/item/stack/rods(get_turf(loc), 2)
qdel(src)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 96b4f9d8a5b..3b5e562cadd 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -200,7 +200,7 @@
var/turf/newturf = T.PlaceOnTop(/turf/closed/wall/material)
var/list/material_list = list()
if(S.material_type)
- material_list[SSmaterials.GetMaterialRef(S.material_type)] = MINERAL_MATERIAL_AMOUNT * 2
+ material_list[GET_MATERIAL_REF(S.material_type)] = MINERAL_MATERIAL_AMOUNT * 2
if(material_list)
newturf.set_custom_materials(material_list)
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 7c27b782cd7..1935e6692e5 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -52,7 +52,7 @@
if(!(flags_1 & NODECONSTRUCT_1))
var/amount_mod = disassembled ? 0 : -2
for(var/mat in custom_materials)
- var/datum/material/custom_material = SSmaterials.GetMaterialRef(mat)
+ var/datum/material/custom_material = GET_MATERIAL_REF(mat)
var/amount = max(0,round(custom_materials[mat]/MINERAL_MATERIAL_AMOUNT) + amount_mod)
if(amount > 0)
new custom_material.sheet_type(drop_location(),amount)
@@ -520,7 +520,7 @@ Moving interrupts
var/list/carving_cost = statue_costs[statue_path]
var/enough_materials = TRUE
for(var/required_material in carving_cost)
- if(!custom_materials[required_material] || custom_materials[required_material] < carving_cost[required_material])
+ if(!has_material_type(required_material, TRUE, carving_cost[required_material]))
enough_materials = FALSE
break
if(enough_materials)
diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm
index d4f4954f7a7..c429896ba10 100644
--- a/code/modules/antagonists/gang/gang.dm
+++ b/code/modules/antagonists/gang/gang.dm
@@ -545,6 +545,6 @@
return FALSE // didnt pass the bar check, no point in continuing to loop
var/obj/machinery/ore_silo/S = GLOB.ore_silo_default
var/datum/component/material_container/mat_container = S.GetComponent(/datum/component/material_container)
- if(mat_container.materials[SSmaterials.GetMaterialRef(/datum/material/gold)] >= 2000) // if theres at least 1 bar of gold left in the silo, they've failed to heist all of it
+ if(mat_container.materials[GET_MATERIAL_REF(/datum/material/gold)] >= 2000) // if theres at least 1 bar of gold left in the silo, they've failed to heist all of it
return FALSE
return TRUE
diff --git a/code/modules/cargo/exports/materials.dm b/code/modules/cargo/exports/materials.dm
index 1b2359cc853..123c2326dc5 100644
--- a/code/modules/cargo/exports/materials.dm
+++ b/code/modules/cargo/exports/materials.dm
@@ -16,10 +16,11 @@
var/obj/item/I = O
var/list/mat_comp = I.get_material_composition(BREAKDOWN_FLAGS_EXPORT)
- if(isnull(mat_comp[SSmaterials.GetMaterialRef(material_id)]))
+ var/datum/material/mat_ref = ispath(material_id) ? locate(material_id) in mat_comp : GET_MATERIAL_REF(material_id)
+ if(isnull(mat_comp[mat_ref]))
return 0
- var/amount = mat_comp[SSmaterials.GetMaterialRef(material_id)]
+ var/amount = mat_comp[mat_ref]
if(istype(I, /obj/item/stack/ore))
amount *= 0.8 // Station's ore redemption equipment is really goddamn good.
diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm
index 0d34acf075b..e98cef336a7 100644
--- a/code/modules/clothing/shoes/bananashoes.dm
+++ b/code/modules/clothing/shoes/bananashoes.dm
@@ -16,7 +16,7 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/ComponentInitialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
- AddComponent(/datum/component/material_container, list(/datum/material/bananium), 200000, MATCONTAINER_EXAMINE|MATCONTAINER_ANY_INTENT|MATCONTAINER_SILENT, /obj/item/stack)
+ AddComponent(/datum/component/material_container, list(/datum/material/bananium), 100 * MINERAL_MATERIAL_AMOUNT, MATCONTAINER_EXAMINE|MATCONTAINER_ANY_INTENT|MATCONTAINER_SILENT, allowed_items=/obj/item/stack)
AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75, falloff_exponent = 20)
/obj/item/clothing/shoes/clown_shoes/banana_shoes/step_action()
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index 0d45eb253c9..20764d3c590 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -69,7 +69,7 @@
*/
/obj/item/reagent_containers/food/drinks/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
if(isGlass && !custom_materials)
- set_custom_materials(list(SSmaterials.GetMaterialRef(/datum/material/glass) = 5))
+ set_custom_materials(list(GET_MATERIAL_REF(/datum/material/glass) = 5))
return ..()
/obj/item/reagent_containers/food/drinks/afterattack(obj/target, mob/user , proximity)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index 2c02c85a59e..8a198ca4391 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -139,6 +139,7 @@
if(!occupant)
audible_message("You hear a loud metallic grinding sound.")
return
+
use_power(1000)
audible_message("You hear a loud squelchy grinding sound.")
playsound(loc, 'sound/machines/juicer.ogg', 50, TRUE)
@@ -174,12 +175,14 @@
gibtype = C.gib_type
if(isalien(C))
typeofskin = /obj/item/stack/sheet/animalhide/xeno
+
var/occupant_volume
if(occupant?.reagents)
occupant_volume = occupant.reagents.total_volume
for (var/i=1 to meat_produced)
var/obj/item/food/meat/slab/newmeat = new typeofmeat
newmeat.name = "[sourcename] [newmeat.name]"
+ newmeat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, occupant) = 4 * MINERAL_MATERIAL_AMOUNT))
if(istype(newmeat))
newmeat.subjectname = sourcename
newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, sourcenutriment / meat_produced) // Thehehe. Fat guys go first
diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
index 37f37889eba..e10e4041409 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
@@ -319,9 +319,9 @@
var/metal = 0
for(var/obj/item/O in ingredients)
O.microwave_act(src)
- if(O.custom_materials && length(O.custom_materials))
- if(O.custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)])
- metal += O.custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)]
+ if(LAZYLEN(O.custom_materials))
+ if(O.custom_materials[GET_MATERIAL_REF(/datum/material/iron)])
+ metal += O.custom_materials[GET_MATERIAL_REF(/datum/material/iron)]
if(metal)
spark()
diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
index 73555c300dc..2b6eb0e7342 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
@@ -28,10 +28,15 @@
. += "The status display reads: Outputting [rating_amount] item(s) at [rating_speed*100]% speed."
/obj/machinery/processor/proc/process_food(datum/food_processor_process/recipe, atom/movable/what)
- if (recipe.output && loc && !QDELETED(src))
- for(var/i = 0, i < (rating_amount * recipe.multiplier), i++)
- new recipe.output(drop_location())
- if (isliving(what))
+ if(recipe.output && loc && !QDELETED(src))
+ var/list/cached_mats = recipe.preserve_materials && what.custom_materials
+ var/cached_multiplier = recipe.multiplier
+ for(var/i in 1 to cached_multiplier)
+ var/atom/processed_food = new recipe.output(drop_location())
+ if(cached_mats)
+ processed_food.set_custom_materials(cached_mats, 1 / cached_multiplier)
+
+ if(isliving(what))
var/mob/living/themob = what
themob.gib(TRUE,TRUE,TRUE)
else
diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm
index 54ec6e7ad1f..c135551db25 100644
--- a/code/modules/food_and_drinks/recipes/processor_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm
@@ -1,9 +1,16 @@
/datum/food_processor_process
+ /// What this recipe takes
var/input
+ /// What this recipe creates
var/output
+ /// The amount of time this recipe takes.
var/time = 40
+ /// The machine required to do this recipe
var/required_machine = /obj/machinery/processor
+ /// The number of products this recipe creates.
var/multiplier = 1 //This multiplies the number of products produced per object processed.
+ /// Whether to copy the materials from the input to the output
+ var/preserve_materials = TRUE
/datum/food_processor_process/meat
input = /obj/item/food/meat/slab
@@ -100,3 +107,4 @@
input = /obj/item/grown/log
output = /obj/item/popsicle_stick
multiplier = 3
+ preserve_materials = FALSE
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index b580fada7a3..fedd36ac1d1 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -188,13 +188,13 @@
update_icon()
/obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE)
- if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass))
+ if(materials.len != 1 || materials[1] != GET_MATERIAL_REF(/datum/material/biomass))
return FALSE
- if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points)
+ if (materials[GET_MATERIAL_REF(/datum/material/biomass)]*multiplier/efficiency > points)
return FALSE
else
if(remove_points)
- points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency
+ points -= materials[GET_MATERIAL_REF(/datum/material/biomass)]*multiplier/efficiency
update_icon()
return TRUE
@@ -297,7 +297,7 @@
cat["items"] += list(list(
"id" = D.id,
"name" = D.name,
- "cost" = D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency,
+ "cost" = D.materials[GET_MATERIAL_REF(/datum/material/biomass)]/efficiency,
))
data["categories"] += list(cat)
diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm
index 3222a3c0aa2..710572122a4 100644
--- a/code/modules/mining/equipment/marker_beacons.dm
+++ b/code/modules/mining/equipment/marker_beacons.dm
@@ -33,7 +33,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sortList(list(
/obj/item/stack/marker_beacon/thirty //and they're bought in stacks of 1, 10, or 30
amount = 30
-/obj/item/stack/marker_beacon/Initialize(mapload)
+/obj/item/stack/marker_beacon/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
update_icon()
diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm
index b484deb5a30..93175fbe18d 100644
--- a/code/modules/mining/machine_processing.dm
+++ b/code/modules/mining/machine_processing.dm
@@ -144,9 +144,9 @@
/datum/material/titanium,
/datum/material/bluespace
)
- AddComponent(/datum/component/material_container, allowed_materials, INFINITY, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_ORE_PROCESSOR, /obj/item/stack)
+ AddComponent(/datum/component/material_container, allowed_materials, INFINITY, MATCONTAINER_EXAMINE|BREAKDOWN_FLAGS_ORE_PROCESSOR, allowed_items=/obj/item/stack)
stored_research = new /datum/techweb/specialized/autounlocking/smelter
- selected_material = SSmaterials.GetMaterialRef(/datum/material/iron)
+ selected_material = GET_MATERIAL_REF(/datum/material/iron)
/obj/machinery/mineral/processing_unit/Destroy()
CONSOLE = null
diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm
index e1e9544fb7b..de05a2e612e 100644
--- a/code/modules/mining/machine_silo.dm
+++ b/code/modules/mining/machine_silo.dm
@@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
/datum/material/bluespace,
/datum/material/plastic,
)
- AddComponent(/datum/component/material_container, materials_list, INFINITY, MATCONTAINER_NO_INSERT, /obj/item/stack)
+ AddComponent(/datum/component/material_container, materials_list, INFINITY, MATCONTAINER_NO_INSERT, allowed_items=/obj/item/stack)
if (!GLOB.ore_silo_default && mapload && is_station_level(z))
GLOB.ore_silo_default = src
@@ -57,8 +57,8 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
if(!istype(I) || (I.flags_1 & HOLOGRAM_1) || (I.item_flags & NO_MAT_REDEMPTION))
to_chat(user, "[M] won't accept [I]!")
return
- var/item_mats = I.get_material_composition(breakdown_flags) & materials.materials
- if(!length(item_mats))
+ var/item_mats = materials.get_item_material_amount(I, breakdown_flags)
+ if(!item_mats)
to_chat(user, "[I] does not contain sufficient materials to be accepted by [M].")
return
// assumes unlimited space...
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index c5f97a6ba27..f6096c6edc0 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -326,7 +326,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
explosion(src,0,1,3,adminlog = notify_admins)
qdel(src)
-/obj/item/stack/ore/Initialize()
+/obj/item/stack/ore/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = base_pixel_x + rand(0, 16) - 8
pixel_y = base_pixel_y + rand(0, 8) - 8
diff --git a/code/modules/mob/living/simple_animal/hostile/goose.dm b/code/modules/mob/living/simple_animal/hostile/goose.dm
index 28284705cff..aa003c2ab64 100644
--- a/code/modules/mob/living/simple_animal/hostile/goose.dm
+++ b/code/modules/mob/living/simple_animal/hostile/goose.dm
@@ -60,7 +60,7 @@
/mob/living/simple_animal/hostile/retaliate/goose/vomit/handle_automated_action()
if(length(nummies))
var/obj/item/E = pick(nummies)
- if(!(E.custom_materials && E.custom_materials[SSmaterials.GetMaterialRef(/datum/material/plastic)]))
+ if(!E.has_material_type(/datum/material/plastic))
nummies -= E // remove non-plastic item from queue
E = locate(/obj/item/reagent_containers/food) in nummies // find food
if(E && E.loc == loc)
@@ -70,7 +70,7 @@
/mob/living/simple_animal/hostile/retaliate/goose/proc/feed(obj/item/suffocator)
if(stat == DEAD || choking) // plapatin I swear to god
return FALSE
- if(suffocator.custom_materials && suffocator.custom_materials[SSmaterials.GetMaterialRef(/datum/material/plastic)]) // dumb goose'll swallow food or drink with plastic in it
+ if(suffocator.has_material_type(/datum/material/plastic)) // dumb goose'll swallow food or drink with plastic in it
visible_message("[src] hungrily gobbles up \the [suffocator]! ")
visible_message("[src] is choking on \the [suffocator]! ")
suffocator.forceMove(src)
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index 060435232db..7ee0ecd6f4c 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -414,7 +414,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
var/obj/structure/cable/target_type = /obj/structure/cable
var/target_layer = CABLE_LAYER_2
-/obj/item/stack/cable_coil/Initialize(mapload, new_amount = null)
+/obj/item/stack/cable_coil/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
pixel_x = base_pixel_x + rand(-2, 2)
pixel_y = base_pixel_y + rand(-2, 2)
@@ -591,7 +591,7 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
icon_state = "coil2"
worn_icon_state = "coil"
-/obj/item/stack/cable_coil/cut/Initialize(mapload)
+/obj/item/stack/cable_coil/cut/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
if(!amount)
amount = rand(1,2)
. = ..()
diff --git a/code/modules/power/pipecleaners.dm b/code/modules/power/pipecleaners.dm
index 996a5e3e6d2..adecef56c63 100644
--- a/code/modules/power/pipecleaners.dm
+++ b/code/modules/power/pipecleaners.dm
@@ -81,9 +81,9 @@ By design, d1 is the smallest direction and d2 is the highest
d2 = text2num(copytext(icon_state, dash + length(icon_state[dash])))
if(d1)
- stored = new/obj/item/stack/pipe_cleaner_coil(null, 2, color)
+ stored = new/obj/item/stack/pipe_cleaner_coil(null, 2, null, null, null, color)
else
- stored = new/obj/item/stack/pipe_cleaner_coil(null, 1, color)
+ stored = new/obj/item/stack/pipe_cleaner_coil(null, 1, null, null, null, color)
color = param_color || color
if(!color)
@@ -234,10 +234,11 @@ By design, d1 is the smallest direction and d2 is the highest
user.visible_message("[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return(OXYLOSS)
-/obj/item/stack/pipe_cleaner_coil/Initialize(mapload, new_amount = null, param_color = null)
+/obj/item/stack/pipe_cleaner_coil/Initialize(mapload, new_amount = null, list/mat_override=null, mat_amt=1, param_color = null)
. = ..()
- color = param_color || color
+ if(param_color)
+ color = param_color
if(!color)
var/list/pipe_cleaner_colors = GLOB.pipe_cleaner_colors
var/random_color = pick(pipe_cleaner_colors)
diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
index 62e55cf75f7..9d9192a6693 100644
--- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm
+++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
@@ -35,14 +35,9 @@
/obj/item/ammo_box/Initialize()
. = ..()
- if (!bullet_cost)
- for (var/material in custom_materials)
- var/material_amount = custom_materials[material]
- LAZYSET(base_cost, material, (material_amount * 0.10))
-
- material_amount *= 0.90 // 10% for the container
- material_amount /= max_ammo
- LAZYSET(bullet_cost, material, material_amount)
+ if(!bullet_cost)
+ base_cost = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.1)
+ bullet_cost = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.9 / max_ammo)
if(!start_empty)
top_off(starting=TRUE)
update_icon()
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index ca440a8cb0a..eacddfc0fa1 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -139,7 +139,11 @@
iter_reagent.on_merge(data, amount)
if(reagtemp != cached_temp)
- set_temperature(((old_heat_capacity * cached_temp) + (iter_reagent.specific_heat * amount * reagtemp)) / heat_capacity())
+ var/new_heat_capacity = heat_capacity()
+ if(new_heat_capacity)
+ set_temperature(((old_heat_capacity * cached_temp) + (iter_reagent.specific_heat * amount * reagtemp)) / new_heat_capacity)
+ else
+ set_temperature(reagtemp)
SEND_SIGNAL(src, COMSIG_REAGENTS_ADD_REAGENT, iter_reagent, amount, reagtemp, data, no_react)
if(!no_react)
@@ -160,7 +164,11 @@
update_total()
if(reagtemp != cached_temp)
- set_temperature(((old_heat_capacity * cached_temp) + (new_reagent.specific_heat * amount * reagtemp)) / heat_capacity())
+ var/new_heat_capacity = heat_capacity()
+ if(new_heat_capacity)
+ set_temperature(((old_heat_capacity * cached_temp) + (new_reagent.specific_heat * amount * reagtemp)) / new_heat_capacity)
+ else
+ set_temperature(reagtemp)
SEND_SIGNAL(src, COMSIG_REAGENTS_NEW_REAGENT, new_reagent, amount, reagtemp, data, no_react)
if(!no_react)
@@ -978,6 +986,8 @@
*/
/datum/reagents/proc/adjust_thermal_energy(delta_energy, min_temp = 2.7, max_temp = 1000)
var/heat_capacity = heat_capacity()
+ if(!heat_capacity)
+ return // no div/0 please
set_temperature(clamp(chem_temp + (delta_energy / heat_capacity), min_temp, max_temp))
/// Applies heat to this holder
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 5f27bcbe39a..480f5f949f2 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -85,7 +85,7 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent())
addiction_type = type
if(material)
- material = SSmaterials.GetMaterialRef(material)
+ material = GET_MATERIAL_REF(material)
/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
. = ..()
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 49361566f7d..1a460dbf595 100755
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -125,7 +125,7 @@
*/
/obj/item/reagent_containers/glass/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
if(!custom_materials)
- set_custom_materials(list(SSmaterials.GetMaterialRef(/datum/material/glass) = 5))//sets it to glass so, later on, it gets picked up by the glass catch (hope it doesn't 'break' things lol)
+ set_custom_materials(list(GET_MATERIAL_REF(/datum/material/glass) = 5))//sets it to glass so, later on, it gets picked up by the glass catch (hope it doesn't 'break' things lol)
return ..()
/obj/item/reagent_containers/glass/beaker
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 21e5c69996d..a4fb1d63a32 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -175,7 +175,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
"You struggle to pry up \the [src] with \the [I].")
if(I.use_tool(src, user, 40, volume=40))
if(!(machine_stat & BROKEN))
- var/obj/item/stack/conveyor/C = new /obj/item/stack/conveyor(loc, 1, TRUE, id)
+ var/obj/item/stack/conveyor/C = new /obj/item/stack/conveyor(loc, 1, TRUE, null, null, id)
transfer_fingerprints_to(C)
to_chat(user, "You remove the conveyor belt.")
qdel(src)
@@ -425,7 +425,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
///id for linking
var/id = ""
-/obj/item/stack/conveyor/Initialize(mapload, new_amount, merge = TRUE, _id)
+/obj/item/stack/conveyor/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1, _id)
. = ..()
id = _id
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index f47744d6e8f..85477d2510f 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -21,23 +21,41 @@ other types of metals and chemistry for reagents).
//DO NOT REFERENCE OUTSIDE OF SSRESEARCH. USE THE PROCS IN SSRESEARCH TO OBTAIN A REFERENCE.
/datum/design //Datum for object designs, used in construction
- var/name = "Name" //Name of the created object.
- var/desc = "Desc" //Description of the created object.
- var/id = DESIGN_ID_IGNORE //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols
- var/build_type = null //Flag as to what kind machine the design is built in. See defines.
- var/list/materials = list() //List of materials. Format: "id" = amount.
- var/construction_time //Amount of time required for building the object
- var/build_path = null //The file path of the object that gets created
- var/list/make_reagents = list() //Reagents produced. Format: "id" = amount. Currently only supported by the biogenerator.
- var/list/category = null //Primarily used for Mech Fabricators, but can be used for anything
- var/list/reagents_list = list() //List of reagents. Format: "id" = amount.
+ /// Name of the created object
+ var/name = "Name"
+ /// Description of the created object
+ var/desc = "Desc"
+ /// The ID of the design. Used for quick reference. Alphanumeric, lower-case, no symbols
+ var/id = DESIGN_ID_IGNORE
+ /// Bitflags indicating what machines this design is compatable with. ([IMPRINTER]|[PROTOLATHE]|[AUTOLATHE]|[CRAFTLATHE]|[MECHFAB]|[BIOGENERATOR]|[LIMBGROWER]|[SMELTER]|[NANITE_COMPILER])
+ var/build_type = null
+ /// List of materials required to create one unit of the product. Format is (typepath or caregory) -> amount
+ var/list/materials = list()
+ /// The amount of time required to create one unit of the product.
+ var/construction_time
+ /// The typepath of the object produced by this design
+ var/build_path = null
+ /// List of reagents produced by this design. Currently only supported by the biogenerator.
+ var/list/make_reagents = list()
+ /// What category this design falls under. Used for sorting in production machines, mostly the mechfab.
+ var/list/category = null
+ /// List of reagents required to create one unit of the product.
+ var/list/reagents_list = list()
+ /// The maximum number of units of whatever is produced by this can be produced in one go.
var/maxstack = 1
- var/lathe_time_factor = 1 //How many times faster than normal is this to build on the protolathe
- var/dangerous_construction = FALSE //notify and log for admin investigations if this is printed.
- var/departmental_flags = ALL //bitflags for deplathes.
+ /// How many times faster than normal is this to build on the protolathe
+ var/lathe_time_factor = 1
+ /// If this is [TRUE] the admins get notified whenever anyone prints this. Currently only used by the BoH.
+ var/dangerous_construction = FALSE
+ /// Bitflags indicating what departmental lathes should be allowed to process this design.
+ var/departmental_flags = ALL
+ /// What techwebs nodes unlock this design. Constructed by SSresearch
var/list/datum/techweb_node/unlocked_by = list()
- var/research_icon //Replaces the item icon in the research console
+ /// Override for the automatic icon generation used for the research console.
+ var/research_icon
+ /// Override for the automatic icon state generation used for the research console.
var/research_icon_state
+ /// Appears to be unused.
var/icon_cache
/// Optional string that interfaces can use as part of search filters. See- item/borg/upgrade/ai and the Exosuit Fabs.
var/search_metadata
@@ -55,7 +73,7 @@ other types of metals and chemistry for reagents).
for(var/i in materials) //Go through all of our materials, get the subsystem instance, and then replace the list.
var/amount = materials[i]
if(!istext(i)) //Not a category, so get the ref the normal way
- var/datum/material/M = SSmaterials.GetMaterialRef(i)
+ var/datum/material/M = GET_MATERIAL_REF(i)
temp_list[M] = amount
else
temp_list[i] = amount
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index a79f539b2df..469ca211da0 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -83,19 +83,45 @@
I.set_custom_materials(matlist)
SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]"))
-/obj/machinery/rnd/production/proc/check_mat(datum/design/being_built, mat) // now returns how many times the item can be built with the material
- if (!materials.mat_container) // no connected silo
+/**
+ * Returns how many times over the given material requirement for the given design is satisfied.
+ *
+ * Arguments:
+ * - [being_built][/datum/design]: The design being referenced.
+ * - material: The material being checked.
+ */
+/obj/machinery/rnd/production/proc/check_material_req(datum/design/being_built, material)
+ if(!materials.mat_container) // no connected silo
return 0
- var/list/all_materials = being_built.reagents_list + being_built.materials
- var/A = materials.mat_container.get_material_amount(mat)
- if(!A)
- A = reagents.get_reagent_amount(mat)
+ var/mat_amt = materials.mat_container.get_material_amount(material)
+ if(!mat_amt)
+ return 0
// these types don't have their .materials set in do_print, so don't allow
// them to be constructed efficiently
- var/ef = efficient_with(being_built.build_path) ? efficiency_coeff : 1
- return round(A / max(1, all_materials[mat] / ef))
+ var/efficiency = efficient_with(being_built.build_path) ? efficiency_coeff : 1
+ return round(mat_amt / max(1, being_built.materials[material] / efficiency))
+
+/**
+ * Returns how many times over the given reagent requirement for the given design is satisfied.
+ *
+ * Arguments:
+ * - [being_built][/datum/design]: The design being referenced.
+ * - reagent: The reagent being checked.
+ */
+/obj/machinery/rnd/production/proc/check_reagent_req(datum/design/being_built, reagent)
+ if(!reagents) // no reagent storage
+ return 0
+
+ var/chem_amt = reagents.get_reagent_amount(reagent)
+ if(!chem_amt)
+ return 0
+
+ // these types don't have their .materials set in do_print, so don't allow
+ // them to be constructed efficiently
+ var/efficiency = efficient_with(being_built.build_path) ? efficiency_coeff : 1
+ return round(chem_amt / max(1, being_built.reagents_list[reagent] / efficiency))
/obj/machinery/rnd/production/proc/efficient_with(path)
return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal)
@@ -239,35 +265,47 @@
/obj/machinery/rnd/production/proc/design_menu_entry(datum/design/D, coeff)
if(!istype(D))
return
- if(!coeff)
- coeff = efficiency_coeff
if(!efficient_with(D.build_path))
coeff = 1
- var/list/l = list()
- var/temp_material
- var/c = 50
- var/t
- var/all_materials = D.materials + D.reagents_list
- for(var/M in all_materials)
- t = check_mat(D, M)
- temp_material += " | "
- if (t < 1)
- temp_material += "[all_materials[M]/coeff] [CallMaterialName(M)]"
- else
- temp_material += " [all_materials[M]/coeff] [CallMaterialName(M)]"
- c = min(c,t)
+ else if(!coeff)
+ coeff = efficiency_coeff
- if (c >= 1)
- l += "[D.name][RDSCREEN_NOBREAK]"
- if(c >= 5)
- l += "x5[RDSCREEN_NOBREAK]"
- if(c >= 10)
- l += "x10[RDSCREEN_NOBREAK]"
- l += "[temp_material][RDSCREEN_NOBREAK]"
+ var/list/entry_text = list()
+ var/temp_material
+ var/max_production = 50
+ var/list/cached_mats = D.materials
+ for(var/material in cached_mats)
+ var/enough_mats = check_material_req(D, material)
+ max_production = min(max_production, enough_mats)
+
+ temp_material += " | "
+ if (enough_mats < 1)
+ temp_material += "[cached_mats[material]/coeff] [CallMaterialName(material)]"
+ else
+ temp_material += " [cached_mats[material]/coeff] [CallMaterialName(material)]"
+
+ var/list/cached_reagents = D.reagents_list
+ for(var/reagent in cached_reagents)
+ var/enough_chems = check_reagent_req(D, reagent)
+ max_production = min(max_production, enough_chems)
+
+ temp_material += " | "
+ if (enough_chems < 1)
+ temp_material += "[cached_reagents[reagent]/coeff] [CallMaterialName(reagent)]"
+ else
+ temp_material += " [cached_reagents[reagent]/coeff] [CallMaterialName(reagent)]"
+
+ if (max_production >= 1)
+ entry_text += "[D.name][RDSCREEN_NOBREAK]"
+ if(max_production >= 5)
+ entry_text += "x5[RDSCREEN_NOBREAK]"
+ if(max_production >= 10)
+ entry_text += "x10[RDSCREEN_NOBREAK]"
+ entry_text += "[temp_material][RDSCREEN_NOBREAK]"
else
- l += "[D.name][temp_material][RDSCREEN_NOBREAK]"
- l += ""
- return l
+ entry_text += "[D.name][temp_material][RDSCREEN_NOBREAK]"
+ entry_text += ""
+ return entry_text
/obj/machinery/rnd/production/Topic(raw, ls)
if(..())
@@ -290,7 +328,11 @@
if(ls["category"])
selected_category = ls["category"]
if(ls["dispose"]) //Causes the protolathe to dispose of a single reagent (all of it)
- reagents.del_reagent(ls["dispose"])
+ var/reagent_path = text2path(ls["dispose"])
+ if(!ispath(reagent_path, /datum/reagent))
+ stack_trace("Invalid reagent typepath - [ls["dispose"]] - returned in reagent disposal topic call")
+ else
+ reagents.del_reagent(reagent_path)
if(ls["disposeall"]) //Causes the protolathe to dispose of all it's reagents.
reagents.clear_reagents()
if(ls["ejectsheet"]) //Causes the protolathe to eject a sheet of material
diff --git a/code/modules/swarmers/swarmer_act.dm b/code/modules/swarmers/swarmer_act.dm
index 0c73a66da4b..80c6def2a7e 100644
--- a/code/modules/swarmers/swarmer_act.dm
+++ b/code/modules/swarmers/swarmer_act.dm
@@ -55,7 +55,7 @@
/obj/item/integrate_amount() //returns the amount of resources gained when eating this item
var/list/mats = get_material_composition(ALL) // Ensures that items made from plasteel, and plas/titanium/plastitaniumglass get integrated correctly.
- if(length(mats) && (mats[SSmaterials.GetMaterialRef(/datum/material/iron)] || mats[SSmaterials.GetMaterialRef(/datum/material/glass)]))
+ if(length(mats) && (mats[GET_MATERIAL_REF(/datum/material/iron)] || mats[GET_MATERIAL_REF(/datum/material/glass)]))
return 1
return ..()
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index c606e031bc2..b5a9dd61f6e 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -1202,8 +1202,7 @@
to_chat(user, "You add [ammo_needed] [A.round_term][ammo_needed > 1?"s":""] to the [gun.name]")
A.rounds = A.rounds - ammo_needed
if(A.custom_materials)
- for(var/i in A.custom_materials)
- A.custom_materials[i] = A.custom_materials[i] * (A.rounds/initial(A.rounds))
+ A.set_custom_materials(A.custom_materials, A.rounds / initial(A.rounds))
A.update_name()
return TRUE