Files
749643cddb Reworks cargo market elasticity (#93235)
## About The Pull Request
What it says on the tin. Cargo now has new math for market elasticity,
each parameter and what it does is explained below

**1. Refactor**
1. **k_elasticity**: This is the elasticity as before applied on all
exports except now this is a floating-point value that swings from
`0->1` instead of 10e-10 like is the current case. This in laymen terms
is the percentile of an items actual cost that is sold on cargo [0 means
you get no credits, 0.5 means you get 50% of the items cost, 1 means you
get 100% of the items cost]. Whenever an item is sold on cargo this
value decreases by an amount that is determined by the next variable

2. **k_hit_percentile(default 5%)**: This is the value by which an
export `k_elasticity` decreases whenever an export is successful for
every unit of an item sold. The real formulae by which the exports
elasticity decreases is dependent on the total amount sold and is as
follows
<pre>k_elasticity -= amount sold (1 for most cases except stacks) *
k_hit_percentile</pre>
So subsequent exports yield lesser profits cause the elasticity
decreases. Now the rate at which the elasticity recovers is as follows

3. **k_recovery_time(default 10 minutes)**: This is the time (minimum
unit should be seconds) it takes for the elasticity to rebound back to
100% after it has reached full 0 but recovery process starts immediately
if it decreases at any point. So if elasticity becomes say 50% it means
it would take 5 minutes to reach 100% again

**2. Some Balance changes**
1. Profits earned from exporting gas is linear per mole sold so the more
gas you put in the more profits you get HOWEVER the max number of
credits you can make per canister is 15000 cr
2. Selling fish yields higher prices because it's no longer subject to
the old elasticity formula
3. Selling 50 sheets of anything will decrease future sale price by 10%
and will take 8 minutes to rebound back to 100% if it reaches 0

**3. Improvements:**
- `SSProcessing` subsystem no longer processes more than 180+ export
datums from round start itself but now starts out empty. It instead
processes only those exports whose elasticity has been impacted and
later cancels it after elasticity has reached 100% so performance of
this subsystem has been drastically improved
- export datums now respect `abstract_type` meaning they won't be
created and can be used as a skeleton body for subtypes. So datums like
`datum/material` & `datum/material/market` are not created anymore but
only their subtypes are so we save processing power & memory
 - Shaved of a lot of dead exports that went unused

## Changelog
🆑
balance: cargo exports now have different prices with applied elasticity
code: Improved performance of export code
qol: stock blocks can be recycled for materials & show up as stock
blocks in order console sold items
refactor: refactored cargo export code in whole. Report bugs on GitHub
/🆑

---------

Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
2025-10-18 04:36:09 -04:00

218 lines
7.2 KiB
Plaintext

/datum/export/material
abstract_type = /datum/export/material
cost = 5 // Cost per SHEET_MATERIAL_AMOUNT, which is 100cm3 as of May 2023.
k_hit_percentile = 0.2 / MAX_STACK_SIZE //Meaning selling 1 full stack of materials will decrease subsequent sales by 20%
k_recovery_time = 8 MINUTES
message = "cm3 of developer's tears. Please, report this on github"
amount_report_multiplier = SHEET_MATERIAL_AMOUNT
export_types = list(
/obj/item/stack/sheet/mineral,
/obj/item/stack/tile/mineral,
/obj/item/stack/ore,
/obj/item/coin
)
///Material id we are trying to
var/datum/material/material_id = null
// 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
/datum/export/material/New()
var/temp_exports = export_types
export_types = null
. = ..()
export_types = init_export_types(temp_exports)
/**
* Inits an list of exports for this type. For performance this usually returns a static list
*
* Arguments
* * export_data - exports whos type cache we are trying to create
*/
/datum/export/material/proc/init_export_types(export_data)
PROTECTED_PROC(TRUE)
var/static/list/shared_exports = null
if(isnull(shared_exports))
shared_exports = typecacheof(export_data, only_root_path = !include_subtypes)
return shared_exports
/datum/export/material/get_amount(obj/O)
if(!isitem(O))
return 0
var/obj/item/I = O
var/list/mat_comp = I.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))
amount *= 0.8 // Station's ore redemption equipment is really goddamn good.
return round(amount / SHEET_MATERIAL_AMOUNT)
// Materials. Static materials exist as parent types, while materials subject to the stock market have a fluid cost as determined by material/market types
// If you're adding a new material to the stock market, make sure its export type is added here.
/datum/export/material/plasma
cost = CARGO_CRATE_VALUE * 0.4
material_id = /datum/material/plasma
message = "cm3 of plasma"
/datum/export/material/bananium
cost = CARGO_CRATE_VALUE * 2
material_id = /datum/material/bananium
message = "cm3 of bananium"
/datum/export/material/adamantine
cost = CARGO_CRATE_VALUE
material_id = /datum/material/adamantine
message = "cm3 of adamantine"
/datum/export/material/mythril
cost = CARGO_CRATE_VALUE * 3
material_id = /datum/material/mythril
message = "cm3 of mythril"
/datum/export/material/plastic
cost = CARGO_CRATE_VALUE * 0.05
message = "cm3 of plastic"
material_id = /datum/material/plastic
/datum/export/material/runite
cost = CARGO_CRATE_VALUE * 1.2
message = "cm3 of runite"
material_id = /datum/material/runite
/datum/export/material/hot_ice
cost = CARGO_CRATE_VALUE * 0.8
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)
/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)
/datum/export/material/market
abstract_type = /datum/export/material/market
cost = 1
/datum/export/material/market/get_base_cost(obj/exported_obj)
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)
/datum/export/material/market/sell_object(obj/sold_item, datum/export_report/report, dry_run, apply_elastic)
. = ..()
var/sheets = get_sheets(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)
/datum/export/material/market/diamond
material_id = /datum/material/diamond
message = "cm3 of diamonds"
/datum/export/material/market/uranium
material_id = /datum/material/uranium
message = "cm3 of uranium"
/datum/export/material/market/gold
material_id = /datum/material/gold
message = "cm3 of gold"
/datum/export/material/market/silver
material_id = /datum/material/silver
message = "cm3 of silver"
/datum/export/material/market/titanium
material_id = /datum/material/titanium
message = "cm3 of titanium"
/datum/export/material/market/bscrystal
message = "of bluespace crystals"
material_id = /datum/material/bluespace
export_types = list(
/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)
/datum/export/material/market/iron
message = "cm3 of iron"
material_id = /datum/material/iron
export_types = list(
/obj/item/stack/sheet/iron,
/obj/item/stack/tile/iron,
/obj/item/stack/rods,
/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)
/datum/export/material/market/glass
message = "cm3 of glass"
material_id = /datum/material/glass
export_types = list(
/obj/item/stack/sheet/glass,
/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 ..()