From 2ff55b02cd8992e887a9c7a85f8a845e8d525659 Mon Sep 17 00:00:00 2001 From: _0Steven <42909981+00-Steven@users.noreply.github.com> Date: Sat, 21 Feb 2026 02:55:04 +0100 Subject: [PATCH] Fixes exporting stock blocks using its own elasticity value separate from the materials exports causing inexplicable stock block sell and elasticity values (#95139) ## About The Pull Request Tin. Or, less unhelpfully, this was two issues causes by the same thing: 1. Selling GMM stock blocks was resulting in inexplicably lower results despite it reporting the material being at 100% elasticity. 2. The GMM was inexplicably showing elasticity resetting or cratering whenever you sold a stock block for a different material. Both of these were essentially caused by us using a separate export datum for stock blocks, which had its own elasticity value independent of the actual materials it was representing: https://github.com/tgstation/tgstation/blob/3248f9b4e8bbdeef02ad9482ea80f628f730f10d/code/modules/cargo/exports/materials.dm#L195-L218 But then, how did it reset or especially crater other materials' elasticity values? Trick question! It didn't: https://github.com/tgstation/tgstation/blob/3248f9b4e8bbdeef02ad9482ea80f628f730f10d/code/modules/cargo/exports/materials.dm#L215-L218 https://github.com/tgstation/tgstation/blob/d0e7d3c858d6926f6a4a387b74e140e8ce9f645b/code/modules/cargo/materials_market.dm#L176-L179 In reality, it was setting the material ID for the stock block export at every sale, causing the GMM to pull its elasticity value for whatever material was last sold and use *that* because the stock block export datum is later in the list. So in this pr we fix it by... merging stock block exports with the materials exports again. Honestly, much easier than I expected, because the base materials export already *has* handling for getting the sheet amount from an item's custom materials. So really most of this was making it adjust for the value tracked on the block, and the rest is... cleanup. The static list used confused me upon first reading the code, so I added some more comments. We now also have a cached list per `abstract_type` under it rather than just the one, because we need a separate one for market materials but it's still shared between several datums. We intentionally add `/obj/item/stock_block` when generating the typecache rather than as a part of the base `export_types`, just so none of the subtypes need to manually include it (and easily forget it). We now use `use_shared_exports` instead of requiring all the subtypes that don't use the cache to re-implement `init_export_types` the exact same way. ## Why It's Good For The Game Fixes jank :+1: Fixes #93090. ## Changelog :cl: fix: Exporting stock blocks no longer uses its own elasticity value separate from the materials exports, and no longer erroneously shows the stock block elasticity instead of the material elasticity in the GMM for the material of the most recently sold stock block. /:cl: --- code/modules/cargo/exports.dm | 2 +- code/modules/cargo/exports/materials.dm | 115 ++++++++++-------------- 2 files changed, 47 insertions(+), 70 deletions(-) diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index 2c1a8e5274b..7fc606992b3 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -159,7 +159,7 @@ Then the player gets the profit from selling his own wasted time. /** - * Returns the cost of the xported item i.e. amount * base cost * elasticity if TRUE + * Returns the cost of the exported item i.e. amount * base cost * elasticity if TRUE * * Arguments * * obj/exported_item - the item we are trying to export diff --git a/code/modules/cargo/exports/materials.dm b/code/modules/cargo/exports/materials.dm index 5678fb4459f..3889946d5dd 100644 --- a/code/modules/cargo/exports/materials.dm +++ b/code/modules/cargo/exports/materials.dm @@ -9,10 +9,12 @@ /obj/item/stack/sheet/mineral, /obj/item/stack/tile/mineral, /obj/item/stack/ore, - /obj/item/coin + /obj/item/coin, ) - ///Material id we are trying to + /// Material id we are trying to export. var/datum/material/material_id = null + /// Whether we use the shared static export types or not. Set to FALSE when using different export types. + var/use_shared_exports = TRUE // Yes, it's a base type containing export_types. // But it has no material_id, so any applies_to check will return false, and these types reduce amount of copypasta a lot @@ -23,32 +25,41 @@ export_types = init_export_types(temp_exports) /** - * Inits an list of exports for this type. For performance this usually returns a static list + * Inits an list of exports for this type. + * For performance, unless use_shared_exports is FALSE, + * this returns a static list of types we check for export + * that is shared by everything under the same abstract type. * * Arguments * * export_data - exports whos type cache we are trying to create */ /datum/export/material/proc/init_export_types(export_data) PROTECTED_PROC(TRUE) + + if(!use_shared_exports) + return generate_export_typecache(export_data) - var/static/list/shared_exports = null - if(isnull(shared_exports)) - shared_exports = typecacheof(export_data, only_root_path = !include_subtypes) + var/static/list/shared_exports = list() + if(isnull(shared_exports[abstract_type])) + shared_exports[abstract_type] = generate_export_typecache(export_data) - return shared_exports + return shared_exports[abstract_type] -/datum/export/material/get_amount(obj/O) - if(!isitem(O)) +/datum/export/material/proc/generate_export_typecache(export_data) + return typecacheof(export_data, only_root_path = !include_subtypes) + +/datum/export/material/get_amount(obj/exported_item) + if(!isitem(exported_item)) return 0 - var/obj/item/I = O - var/list/mat_comp = I.get_material_composition() + var/obj/item/our_item = exported_item + var/list/mat_comp = our_item.get_material_composition() var/datum/material/mat_ref = ispath(material_id) ? locate(material_id) in mat_comp : GET_MATERIAL_REF(material_id) var/amount = mat_comp[mat_ref] if(!amount) return 0 - if(istype(I, /obj/item/stack/ore)) + if(istype(our_item, /obj/item/stack/ore)) amount *= 0.8 // Station's ore redemption equipment is really goddamn good. return round(amount / SHEET_MATERIAL_AMOUNT) @@ -91,49 +102,46 @@ message = "cm3 of Hot Ice" material_id = /datum/material/hot_ice export_types = /obj/item/stack/sheet/hot_ice - -/datum/export/material/hot_ice/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) + use_shared_exports = FALSE /datum/export/material/metal_hydrogen cost = CARGO_CRATE_VALUE * 1.05 message = "cm3 of metallic hydrogen" material_id = /datum/material/metalhydrogen export_types = /obj/item/stack/sheet/mineral/metal_hydrogen - -/datum/export/material/metal_hydrogen/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) + use_shared_exports = FALSE /datum/export/material/market abstract_type = /datum/export/material/market cost = 1 +/datum/export/material/market/generate_export_typecache(export_data) + . = ..() + // Always include the stock block for any market exports. + .[/obj/item/stock_block] = TRUE + return . + /datum/export/material/market/get_base_cost(obj/exported_obj) - return ..() * SSstock_market.materials_prices[material_id] + . = ..() + if(!istype(exported_obj, /obj/item/stock_block)) + return . * SSstock_market.materials_prices[material_id] -/** - * Returns number of sheets in this item - * - * Arguments - * * obj/sold_item - the item whos sheets we are computing -*/ -/datum/export/material/market/proc/get_sheets(obj/sold_item) - PROTECTED_PROC(TRUE) - - return get_amount(sold_item) + var/obj/item/stock_block/exported_block = exported_obj + return . * (exported_block.fluid ? SSstock_market.materials_prices[exported_block.custom_materials[1].type] : exported_block.export_value) /datum/export/material/market/sell_object(obj/sold_item, datum/export_report/report, dry_run, apply_elastic) . = ..() - var/sheets = get_sheets(sold_item) + if(dry_run) + return + var/sheets = get_amount(sold_item) if(!sheets) return //This formula should impact lower quantity materials greater, and higher quantity materials less. Still, it's a bit rough. Tweaking may be needed. - if(!dry_run) - //decrease the market price - SSstock_market.adjust_material_price(material_id, -SSstock_market.materials_prices[material_id] * (sheets / (sheets + SSstock_market.materials_quantity[material_id]))) - //increase the stock - SSstock_market.adjust_material_quantity(material_id, sheets) + //decrease the market price + SSstock_market.adjust_material_price(material_id, -SSstock_market.materials_prices[material_id] * (sheets / (sheets + SSstock_market.materials_quantity[material_id]))) + //increase the stock + SSstock_market.adjust_material_quantity(material_id, sheets) /datum/export/material/market/diamond material_id = /datum/material/diamond @@ -162,9 +170,7 @@ /obj/item/stack/sheet/bluespace_crystal, /obj/item/stack/ore/bluespace_crystal, ) - -/datum/export/material/market/bscrystal/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) + use_shared_exports = FALSE /datum/export/material/market/iron message = "cm3 of iron" @@ -176,9 +182,7 @@ /obj/item/stack/ore, /obj/item/coin, ) - -/datum/export/material/market/iron/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) + use_shared_exports = FALSE /datum/export/material/market/glass message = "cm3 of glass" @@ -188,31 +192,4 @@ /obj/item/stack/ore, /obj/item/shard, ) - -/datum/export/material/market/glass/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) - -/datum/export/material/market/stock_block - amount_report_multiplier = 1 - k_hit_percentile = 0.2 //20% hit per block stock which is synomonous to an full stack of sheets - message = "" - unit_name = "stock block" - export_types = list(/obj/item/stock_block) - -/datum/export/material/market/stock_block/init_export_types(export_data) - return typecacheof(export_data, only_root_path = !include_subtypes) - -/datum/export/material/market/stock_block/get_amount(obj/item/stock_block/block) - return 1 //sold as 1 stock block but we adjust the markets via get_sheets() - -/datum/export/material/market/stock_block/get_sheets(obj/item/stock_block/block) - return block.custom_materials[block.custom_materials[1]] / SHEET_MATERIAL_AMOUNT - -/datum/export/material/market/stock_block/get_base_cost(obj/item/stock_block/block) - - return (block.fluid ? SSstock_market.materials_prices[block.custom_materials[1].type] : block.export_value) * get_sheets(block) - -/datum/export/material/market/stock_block/sell_object(obj/item/stock_block/block, datum/export_report/report, dry_run, apply_elastic) - material_id = block.custom_materials[1].type - - return ..() + use_shared_exports = FALSE