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 👍
Fixes #93090.

## Changelog
🆑
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.
/🆑
This commit is contained in:
_0Steven
2026-02-20 20:55:04 -05:00
committed by GitHub
parent 0e581d23bd
commit 2ff55b02cd
2 changed files with 47 additions and 70 deletions
+1 -1
View File
@@ -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
+46 -69
View File
@@ -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