diff --git a/code/_globalvars/lists/cargo.dm b/code/_globalvars/lists/cargo.dm index 1a41609d25c..21e15a6d765 100644 --- a/code/_globalvars/lists/cargo.dm +++ b/code/_globalvars/lists/cargo.dm @@ -1,12 +1,6 @@ GLOBAL_LIST_INIT(discountable_packs, init_discountable_packs()) -/proc/init_discountable_packs() - var/list/packs = list() - for(var/datum/supply_pack/prototype as anything in subtypesof(/datum/supply_pack)) - var/discountable = initial(prototype.discountable) - if(discountable) - LAZYADD(packs[discountable], prototype) - return packs +GLOBAL_LIST_INIT(exports_list, init_Exports()) GLOBAL_LIST_INIT(pack_discount_odds, list( SUPPLY_PACK_STD_DISCOUNTABLE = 45, @@ -15,3 +9,20 @@ GLOBAL_LIST_INIT(pack_discount_odds, list( )) GLOBAL_LIST_EMPTY(supplypod_loading_bays) + +/// Caled when the global discount pack list is initialized +/proc/init_discountable_packs() + var/list/packs = list() + for(var/datum/supply_pack/prototype as anything in subtypesof(/datum/supply_pack)) + var/discountable = initial(prototype.discountable) + if(discountable) + LAZYADD(packs[discountable], prototype) + return packs + +/// Called when the global exports_list is empty, and sets it up. +/proc/init_Exports() + var/list/exports = list() + for(var/datum/export/subtype as anything in subtypesof(/datum/export)) + if(subtype::abstract_type != subtype) + exports += new subtype + return exports diff --git a/code/controllers/subsystem/stock_market.dm b/code/controllers/subsystem/stock_market.dm index 1cce5a8c549..8ecd6cc8085 100644 --- a/code/controllers/subsystem/stock_market.dm +++ b/code/controllers/subsystem/stock_market.dm @@ -20,16 +20,9 @@ SUBSYSTEM_DEF(stock_market) /datum/controller/subsystem/stock_market/Initialize() for(var/datum/material/possible_market as anything in subtypesof(/datum/material)) // I need to make this work like this, but lets hardcode it for now if(possible_market.tradable) - materials_prices += possible_market materials_prices[possible_market] = possible_market.value_per_unit * SHEET_MATERIAL_AMOUNT - - materials_trends += possible_market materials_trends[possible_market] = rand(MARKET_TREND_DOWNWARD,MARKET_TREND_UPWARD) //aka -1 to 1 - - materials_trend_life += possible_market materials_trend_life[possible_market] = rand(1,3) - - materials_quantity += possible_market materials_quantity[possible_market] = possible_market.tradable_base_quantity + (rand(-(possible_market.tradable_base_quantity) * 0.5, possible_market.tradable_base_quantity * 0.5)) return SS_INIT_SUCCESS diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm index 51a7a5d055a..db02d51f65b 100644 --- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm +++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm @@ -143,8 +143,6 @@ /// Looks across the station for items that are pirate specific exports /obj/machinery/loot_locator/proc/find_random_loot() - if(!GLOB.exports_list.len) - setupExports() var/list/possible_loot = list() for(var/datum/export/pirate/possible_export in GLOB.exports_list) possible_loot += possible_export @@ -402,6 +400,7 @@ deltimer(sending_timer) /datum/export/pirate + abstract_type = /datum/export/pirate sales_market = EXPORT_MARKET_PIRACY /// Attempts to find the thing on station @@ -421,8 +420,7 @@ if(head_mobs.len) return pick(head_mobs) -/datum/export/pirate/ransom/get_cost(atom/movable/exported_item) - var/mob/living/carbon/human/ransomee = exported_item +/datum/export/pirate/ransom/get_base_cost(mob/living/carbon/human/ransomee) if(ransomee.stat != CONSCIOUS || !ransomee.mind || HAS_TRAIT(ransomee.mind, TRAIT_HAS_BEEN_KIDNAPPED)) //mint condition only return 0 else if(FACTION_PIRATE in ransomee.faction) //can't ransom your fellow pirates to CentCom! @@ -472,18 +470,20 @@ /datum/export/pirate/cash cost = 1 + k_hit_percentile = 0.1 / MAX_STACK_SIZE unit_name = "bill" export_types = list(/obj/item/stack/spacecash) -/datum/export/pirate/cash/get_cost(obj/exported_item) - var/obj/item/stack/spacecash/cash = exported_item - return cash.value * cash.amount +/datum/export/pirate/cash/get_amount(obj/item/stack/spacecash/cash) + return cash.amount + +/datum/export/pirate/cash/get_base_cost(obj/item/stack/spacecash/cash) + return cash.value /datum/export/pirate/holochip cost = 1 unit_name = "holochip" export_types = list(/obj/item/holochip) -/datum/export/pirate/holochip/get_cost(atom/movable/exported_item) - var/obj/item/holochip/chip = exported_item +/datum/export/pirate/holochip/get_base_cost(obj/item/holochip/chip) return chip.credits diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index 1e140d646df..03fcdcb9180 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -30,14 +30,6 @@ Then the player gets the profit from selling his own wasted time. ///set to false if any objects in a dry run were unscannable var/all_contents_scannable = TRUE -/// Makes sure the exports list is populated and that the report isn't null. -/proc/init_export(datum/export_report/external_report) - if(!length(GLOB.exports_list)) - setupExports() - if(isnull(external_report)) - external_report = new - return external_report - /* * Handles exporting a movable atom and its contents * Arguments: @@ -52,7 +44,8 @@ Then the player gets the profit from selling his own wasted time. if(!islist(export_markets)) export_markets = list(export_markets) - external_report = init_export(external_report) + if(!external_report) + external_report = new var/list/contents = exported_atom.get_all_contents_ignoring(ignore_typecache) @@ -73,7 +66,8 @@ Then the player gets the profit from selling his own wasted time. /// It works like export_item_and_contents(), however it ignores the contents. Meaning only `exported_atom` will be valued. /proc/export_single_item(atom/movable/exported_atom, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report, export_markets = list(EXPORT_MARKET_STATION)) - external_report = init_export(external_report) + if(!external_report) + external_report = new var/sold = _export_loop(exported_atom, apply_elastic, dry_run, external_report, export_markets) if(!dry_run && (sold || delete_unsold) && sold != EXPORT_SOLD_DONT_DELETE) @@ -95,23 +89,28 @@ Then the player gets the profit from selling his own wasted time. external_report.all_contents_scannable = FALSE break sold = export.sell_object(exported_atom, external_report, dry_run, apply_elastic) - external_report.exported_atoms += " [exported_atom.name]" break return sold /datum/export + abstract_type = /datum/export + /// Unit name. Only used in "Received [total_amount] [name]s [message]." var/unit_name = "" /// Message appended to the sale report var/message = "" - /// Cost of item, in cargo credits. Must not allow for infinite price dupes, see above. + + ///Price per unit of an exported item var/cost = 1 /// whether this export can have a negative impact on the cargo budget or not var/allow_negative_cost = FALSE - /// coefficient used in marginal price calculation that roughly corresponds to the inverse of price elasticity, or "quantity elasticity" - var/k_elasticity = 1/30 - /// Coefficient used in the recovery of elastic price calculation. See Process, this value and k_elasticity are multiplied together to form the exponent that returns the price to normal. - var/k_recovery_elasticity = 1/30 + ///The percentage of an items real price that is earned when selling that item on cargo + var/k_elasticity = 1 + ///The percentage decrease on this exports elasticity per unit of item sold + var/k_hit_percentile = 0.05 + ///Time taken for this exports elasticity to reach back to 100% + var/k_recovery_time = 10 MINUTES + /// The multiplier of the amount sold shown on the report. Useful for exports, such as material, which costs are not strictly per single units sold. var/amount_report_multiplier = 1 /// Type of the exported object. If none, the export datum is considered base type. @@ -125,46 +124,55 @@ Then the player gets the profit from selling his own wasted time. /// Export market that this export applies to. Defaults to EXPORT_MARKET_STATION for items sold to the standard supply shuttle, replacements exist for pirates, etc. var/sales_market = EXPORT_MARKET_STATION - /// cost includes elasticity, this does not. - var/init_cost - - - /datum/export/New() ..() - SSprocessing.processing += src - init_cost = cost - export_types = typecacheof(export_types, only_root_path = !include_subtypes, ignore_root_path = FALSE) + export_types = typecacheof(export_types, only_root_path = !include_subtypes) exclude_types = typecacheof(exclude_types) /datum/export/Destroy() - SSprocessing.processing -= src return ..() -/datum/export/process() - cost *= NUM_E**(k_elasticity * k_recovery_elasticity) - // A little note here based on the standard values for k_recovery_elasticity: 1/30 will result in a price that started at 20% to go back to 100% in around 20 minutes, ramping up over time. - if(cost > init_cost) - cost = init_cost +/datum/export/process(seconds_per_tick) + k_elasticity += seconds_per_tick / k_recovery_time + if(k_elasticity >= 1) + k_elasticity = 1 + return PROCESS_KILL -/// Checks the cost. 0 cost items are skipped in export. -/datum/export/proc/get_cost(obj/exported_item, apply_elastic = TRUE) - var/amount = get_amount(exported_item) - if(apply_elastic) - if(k_elasticity != 0) - return round((cost/k_elasticity) * (1 - NUM_E**(-1 * k_elasticity * amount))) //anti-derivative of the marginal cost function - else - return round(cost * amount) //alternative form derived from L'Hopital to avoid division by 0 - else - return round(init_cost * amount) +/** + * Returns the cost of 1 unit of the exported item + * + * Arguments + * * obj/exported_item - the item we whos base cost we are trying to compute +*/ +/datum/export/proc/get_base_cost(obj/exported_item) + return cost /* -* Checks the amount of exportable in object. Credits in the bill, sheets in the stack, etc. -* Usually acts as a multiplier for a cost, so item that has 0 amount will be skipped in export. + * Returns the amount of exportable in object. Credits in the bill, sheets in the stack, etc. + * Usually acts as a multiplier for a cost, so item that has 0 amount will be skipped in export. + * + * Arguments + * * obj/exported_item - the amount of units in this exported item */ /datum/export/proc/get_amount(obj/exported_item) return 1 + +/** + * Returns the cost of the xported item i.e. amount * base cost * elasticity if TRUE + * + * Arguments + * * obj/exported_item - the item we are trying to export + * * apply_elastic - should we use elasticity +*/ +/datum/export/proc/get_cost(obj/exported_item, apply_elastic = TRUE) + SHOULD_NOT_OVERRIDE(TRUE) + + var/total = get_base_cost(exported_item) * get_amount(exported_item) + if(apply_elastic && initial(k_elasticity) > 0) + total *= k_elasticity + return ROUND_UP(total) + /// Checks if the item is fit for export datum. /datum/export/proc/applies_to(obj/exported_item, apply_elastic = TRUE, export_markets) for(var/found_market in export_markets) @@ -206,10 +214,14 @@ Then the player gets the profit from selling his own wasted time. if(!(export_result & COMPONENT_STOP_EXPORT_REPORT)) report.total_value[src] += export_value report.total_amount[src] += export_amount * amount_report_multiplier + report.exported_atoms += "[sold_item.name]" if(!dry_run) - if(apply_elastic) - cost *= NUM_E**(-1 * k_elasticity * export_amount) //marginal cost modifier + if(apply_elastic && k_elasticity > 0) + k_elasticity -= export_amount * k_hit_percentile + if(k_elasticity < 0) + k_elasticity = 0 + SSprocessing.processing |= src SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[sold_item.type]", "[export_value]")) return EXPORT_SOLD @@ -242,11 +254,3 @@ Then the player gets the profit from selling his own wasted time. msg += "." return msg -GLOBAL_LIST_EMPTY(exports_list) - -/// Called when the global exports_list is empty, and sets it up. -/proc/setupExports() - for(var/subtype in subtypesof(/datum/export)) - var/datum/export/export_datum = new subtype - if(export_datum.export_types && export_datum.export_types.len) // Exports without a type are invalid/base types - GLOB.exports_list += export_datum diff --git a/code/modules/cargo/exports/civilain_bounty.dm b/code/modules/cargo/exports/civilain_bounty.dm index 05241e0ab75..b4066bacbb1 100644 --- a/code/modules/cargo/exports/civilain_bounty.dm +++ b/code/modules/cargo/exports/civilain_bounty.dm @@ -4,5 +4,5 @@ unit_name = "completed bounty cube" export_types = list(/obj/item/bounty_cube) -/datum/export/bounty_box/get_cost(obj/item/bounty_cube/cube, apply_elastic) +/datum/export/bounty_box/get_base_cost(obj/item/bounty_cube/cube) return cube.bounty_value + (cube.bounty_value * cube.speed_bonus) diff --git a/code/modules/cargo/exports/fish.dm b/code/modules/cargo/exports/fish.dm index 0714041d4b5..0ecf977e4cd 100644 --- a/code/modules/cargo/exports/fish.dm +++ b/code/modules/cargo/exports/fish.dm @@ -1,9 +1,8 @@ /datum/export/fish cost = 30 + k_hit_percentile = 0.03 unit_name = "fish" export_types = list(/obj/item/fish) -/datum/export/fish/get_cost(obj/item/fish/fish, apply_elastic) - var/elastic_cost = ..() - var/elastic_percent = elastic_cost / init_cost - return round(fish.get_export_price(elastic_cost, elastic_percent)) +/datum/export/fish/get_base_cost(obj/item/fish/fish) + return fish.get_export_price(..()) diff --git a/code/modules/cargo/exports/food_and_drink.dm b/code/modules/cargo/exports/food_and_drink.dm index b44c05f0c78..9fc99f8716f 100644 --- a/code/modules/cargo/exports/food_and_drink.dm +++ b/code/modules/cargo/exports/food_and_drink.dm @@ -10,13 +10,8 @@ include_subtypes = TRUE exclude_types = list(/obj/item/food/grown) -/datum/export/food/get_cost(obj/item/food/object, allowed_categories, apply_elastic) +/datum/export/food/get_base_cost(obj/item/food/object) if(HAS_TRAIT(object, TRAIT_FOOD_SILVER)) return FOOD_PRICE_WORTHLESS - var/elastic_cost = ..() - if(object.venue_value) - var/elastic_percent = elastic_cost / init_cost - return round(object.venue_value * elastic_percent) - - return elastic_cost + return object.venue_value ? object.venue_value : ..() diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm index c420191ee80..c55afb10f44 100644 --- a/code/modules/cargo/exports/gear.dm +++ b/code/modules/cargo/exports/gear.dm @@ -1,109 +1,107 @@ -/datum/export/gear - -/datum/export/gear/sec_helmet +/datum/export/sec_helmet cost = CARGO_CRATE_VALUE * 0.5 unit_name = "helmet" export_types = list(/obj/item/clothing/head/helmet/sec) -/datum/export/gear/sec_armor +/datum/export/sec_armor cost = CARGO_CRATE_VALUE * 0.5 unit_name = "armor vest" export_types = list(/obj/item/clothing/suit/armor/vest) -/datum/export/gear/riot_shield +/datum/export/riot_shield cost = CARGO_CRATE_VALUE * 0.5 unit_name = "riot shield" export_types = list(/obj/item/shield/riot) -/datum/export/gear/mask/breath +/datum/export/breath_mask cost = CARGO_CRATE_VALUE * 0.01 unit_name = "breath mask" export_types = list(/obj/item/clothing/mask/breath) -/datum/export/gear/mask/gas +/datum/export/gas_mask cost = CARGO_CRATE_VALUE * 0.05 unit_name = "gas mask" export_types = list(/obj/item/clothing/mask/gas) include_subtypes = FALSE -/datum/export/gear/space/helmet +/datum/export/helmet_space cost = CARGO_CRATE_VALUE * 0.15 unit_name = "space helmet" export_types = list(/obj/item/clothing/head/helmet/space, /obj/item/clothing/head/helmet/space/eva, /obj/item/clothing/head/helmet/space/nasavoid) include_subtypes = FALSE -/datum/export/gear/space/suit +/datum/export/suit_space cost = CARGO_CRATE_VALUE * 0.3 unit_name = "space suit" export_types = list(/obj/item/clothing/suit/space, /obj/item/clothing/suit/space/eva, /obj/item/clothing/suit/space/nasavoid) include_subtypes = FALSE -/datum/export/gear/space/syndiehelmet +/datum/export/syndiehelmet_space cost = CARGO_CRATE_VALUE * 0.3 unit_name = "Syndicate space helmet" export_types = list(/obj/item/clothing/head/helmet/space/syndicate) -/datum/export/gear/space/syndiesuit +/datum/export/syndiesuit_space cost = CARGO_CRATE_VALUE * 0.6 unit_name = "Syndicate space suit" export_types = list(/obj/item/clothing/suit/space/syndicate) -/datum/export/gear/radhelmet +/datum/export/radhelmet cost = CARGO_CRATE_VALUE * 0.1 unit_name = "radsuit hood" export_types = list(/obj/item/clothing/head/utility/radiation) -/datum/export/gear/radsuit +/datum/export/radsuit cost = CARGO_CRATE_VALUE * 0.2 unit_name = "radsuit" export_types = list(/obj/item/clothing/suit/utility/radiation) -/datum/export/gear/biohood +/datum/export/biohood cost = CARGO_CRATE_VALUE * 0.1 unit_name = "biosuit hood" export_types = list(/obj/item/clothing/head/bio_hood) -/datum/export/gear/biosuit +/datum/export/biosuit cost = CARGO_CRATE_VALUE * 0.2 unit_name = "biosuit" export_types = list(/obj/item/clothing/suit/bio_suit) -/datum/export/gear/bombhelmet +/datum/export/bombhelmet cost = CARGO_CRATE_VALUE * 0.1 unit_name = "bomb suit hood" export_types = list(/obj/item/clothing/head/utility/bomb_hood) -/datum/export/gear/bombsuit +/datum/export/bombsuit cost = CARGO_CRATE_VALUE * 0.2 unit_name = "bomb suit" export_types = list(/obj/item/clothing/suit/utility/bomb_suit) -/datum/export/gear/lizardboots +/datum/export/lizardboots cost = CARGO_CRATE_VALUE * 0.7 unit_name = "lizard skin boots" export_types = list(/obj/item/clothing/shoes/cowboy/lizard) include_subtypes = FALSE -/datum/export/gear/lizardmasterwork +/datum/export/lizardmasterwork cost = CARGO_CRATE_VALUE * 2 unit_name = "Hugs-the-Feet lizard boots" export_types = list(/obj/item/clothing/shoes/cowboy/lizard/masterwork) -/datum/export/gear/bilton +/datum/export/bilton cost = CARGO_CRATE_VALUE * 5 unit_name = "bilton wrangler boots" export_types = list(/obj/item/clothing/shoes/cowboy/fancy) -/datum/export/gear/ebonydie +/datum/export/ebonydie cost = CARGO_CRATE_VALUE unit_name = "ebony die" export_types = list(/obj/item/dice/d6/ebony) -/datum/export/gear/rare_lighter +/datum/export/rare_lighter cost = CARGO_CRATE_VALUE * 4 unit_name = "novelty lighter" export_types = list( diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm index ed65563b02c..95260cf084c 100644 --- a/code/modules/cargo/exports/large_objects.dm +++ b/code/modules/cargo/exports/large_objects.dm @@ -1,4 +1,4 @@ -/datum/export/large/crate +/datum/export/crate cost = CARGO_CRATE_VALUE k_elasticity = 0 unit_name = "crate" @@ -10,119 +10,114 @@ /obj/structure/closet/crate/wooden, ) -/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much. +/datum/export/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much. . = ..() if(. && notes) . += " Thanks for participating in Nanotrasen Crates Recycling Program." -/datum/export/large/crate/wooden - cost = CARGO_CRATE_VALUE/5 +/datum/export/crate/wooden + cost = CARGO_CRATE_VALUE / 5 unit_name = "large wooden crate" export_types = list(/obj/structure/closet/crate/large) exclude_types = list() -/datum/export/large/crate/wooden/ore +/datum/export/crate/wooden/ore unit_name = "ore box" export_types = list(/obj/structure/ore_box) -/datum/export/large/crate/wood +/datum/export/crate/wood cost = CARGO_CRATE_VALUE * 0.48 unit_name = "wooden crate" export_types = list(/obj/structure/closet/crate/wooden) exclude_types = list() -/datum/export/large/crate/coffin +/datum/export/crate/coffin cost = CARGO_CRATE_VALUE/2 //50 wooden crates cost 800 credits, and you can make 10 coffins in seconds with those planks. Each coffin selling for 100 means you can make a net gain of 200 credits for wasting your time making coffins. unit_name = "coffin" export_types = list(/obj/structure/closet/crate/coffin) -/datum/export/large/reagent_dispenser +/datum/export/reagent_dispenser + abstract_type = /datum/export/reagent_dispenser cost = CARGO_CRATE_VALUE * 0.5 // +0-400 depending on amount of reagents left + ///cost for an full holder of reagents var/contents_cost = CARGO_CRATE_VALUE * 0.8 -/datum/export/large/reagent_dispenser/get_cost(obj/structure/reagent_dispensers/dispenser) - var/ratio = dispenser.reagents.total_volume / dispenser.reagents.maximum_volume +/datum/export/reagent_dispenser/get_base_cost(obj/structure/reagent_dispensers/dispenser) + return ..() + round(contents_cost * (dispenser.reagents.total_volume / dispenser.reagents.maximum_volume)) - return ..() + round(contents_cost * ratio) - -/datum/export/large/reagent_dispenser/water +/datum/export/reagent_dispenser/water unit_name = "watertank" export_types = list(/obj/structure/reagent_dispensers/watertank) contents_cost = CARGO_CRATE_VALUE * 0.4 -/datum/export/large/reagent_dispenser/fuel +/datum/export/reagent_dispenser/fuel unit_name = "fueltank" export_types = list(/obj/structure/reagent_dispensers/fueltank) -/datum/export/large/reagent_dispenser/beer +/datum/export/reagent_dispenser/beer unit_name = "beer keg" contents_cost = CARGO_CRATE_VALUE * 3.5 export_types = list(/obj/structure/reagent_dispensers/beerkeg) -/datum/export/large/pipedispenser +/datum/export/pipedispenser cost = CARGO_CRATE_VALUE * 2.5 unit_name = "pipe dispenser" export_types = list(/obj/machinery/pipedispenser) -/datum/export/large/emitter +/datum/export/emitter cost = CARGO_CRATE_VALUE * 2.75 unit_name = "emitter" export_types = list(/obj/machinery/power/emitter) -/datum/export/large/field_generator +/datum/export/field_generator cost = CARGO_CRATE_VALUE * 2.75 unit_name = "field generator" export_types = list(/obj/machinery/field/generator) -/datum/export/large/tesla_coil +/datum/export/tesla_coil cost = CARGO_CRATE_VALUE * 2.25 unit_name = "tesla coil" export_types = list(/obj/machinery/power/energy_accumulator/tesla_coil) -/datum/export/large/supermatter +/datum/export/supermatter cost = CARGO_CRATE_VALUE * 16 unit_name = "supermatter shard" export_types = list(/obj/machinery/power/supermatter_crystal/shard) -/datum/export/large/grounding_rod +/datum/export/grounding_rod cost = CARGO_CRATE_VALUE * 1.2 unit_name = "grounding rod" export_types = list(/obj/machinery/power/energy_accumulator/grounding_rod) -/datum/export/large/iv +/datum/export/iv cost = CARGO_CRATE_VALUE * 0.25 unit_name = "iv drip" export_types = list(/obj/machinery/iv_drip) -/datum/export/large/barrier +/datum/export/barrier cost = CARGO_CRATE_VALUE * 0.25 unit_name = "security barrier" export_types = list(/obj/item/grenade/barrier, /obj/structure/barricade/security) -/** - * Gas canister exports. - * I'm going to put a quick aside here as this has been a pain to balance for several years now, and I'd like to at least break how to keep gas exports tame. - * So: Gasses are sold in canisters below, which have a variable amount of maximum pressure before they start to break. The largest of which is 9.2e13 kPa. - * This means we can determine a theoretical maximum value for gas sale prices using the ideal gas laws, as we know we have a minimum gas temperature of 2.7 kelvin. - * - * Additional note on base value. Gasses are soft capped to limit how much they're worth at large quantities, and time and time again players will find new ways to break your gasses. - * so please, *PLEASE* try not to go too much further past 10. +///Maximum number of credits you can earn from selling your gas canister cause its theoritically infinite +#define MAX_GAS_CREDITS 15000 - * * AUTHORS NOTE: This means the theoretical, insane madman number of moles of a single gas in a can sits at a horrifying 4,098,150,709.4 moles. - * * Use this as you will, and when someone makes a quinquadrillion credits using gas exports, use these metrics as a way to balance the bejesus out of them. - * * For more information, see code\modules\atmospherics\machinery\portable\canister.dm. - */ -/datum/export/large/gas_canister +/** + * Maximum pressure a canister can withstand is 9.2e13 kPa at a minimum of 2.7K which would contain a horrifying 4,098,150,709.4 moles. + * We don't want players making that much credits so we limit the total amount earned to MAX_GAS_CREDITS +*/ +/datum/export/gas_canister cost = CARGO_CRATE_VALUE * 0.05 //Base cost of canister. You get more for nice gases inside. unit_name = "Gas Canister" export_types = list(/obj/machinery/portable_atmospherics/canister) - k_elasticity = 0.00033 -/datum/export/large/gas_canister/get_cost(obj/machinery/portable_atmospherics/canister/canister) - var/worth = cost +/datum/export/gas_canister/get_base_cost(obj/machinery/portable_atmospherics/canister/canister) var/datum/gas_mixture/canister_mix = canister.return_air() + if(!canister_mix.total_moles()) + return 0 var/canister_gas = canister_mix.gases - var/list/gases_to_check = list( + + var/static/list/gases_to_check = list( /datum/gas/bz, /datum/gas/nitrium, /datum/gas/hypernoblium, @@ -139,14 +134,18 @@ /datum/gas/halon, ) + var/worth = cost for(var/gasID in gases_to_check) canister_mix.assert_gas(gasID) if(canister_gas[gasID][MOLES] > 0) worth += get_gas_value(gasID, canister_gas[gasID][MOLES]) + if(worth > MAX_GAS_CREDITS) + worth = MAX_GAS_CREDITS + break - canister_mix.garbage_collect() return worth -/datum/export/large/gas_canister/proc/get_gas_value(datum/gas/gasType, moles) - var/baseValue = initial(gasType.base_value) - return round((baseValue/k_elasticity) * (1 - NUM_E**(-1 * k_elasticity * moles))) +/datum/export/gas_canister/proc/get_gas_value(datum/gas/gasType, moles) + return ROUND_UP(initial(gasType.base_value) * moles) + +#undef MAX_GAS_CREDITS diff --git a/code/modules/cargo/exports/lavaland.dm b/code/modules/cargo/exports/lavaland.dm index a764866ad28..78f98cb04da 100644 --- a/code/modules/cargo/exports/lavaland.dm +++ b/code/modules/cargo/exports/lavaland.dm @@ -1,6 +1,7 @@ //Tendril chest artifacts and ruin loot. //Consumable or one-use items like the magic D20 and gluttony's blessing are omitted /datum/export/lavaland + abstract_type = /datum/export/lavaland unit_name = "lava planet artifact" /// Prefix to add to our unit name after generation var/prefix = null diff --git a/code/modules/cargo/exports/manifest.dm b/code/modules/cargo/exports/manifest.dm index 03fe33467fb..861dae87027 100644 --- a/code/modules/cargo/exports/manifest.dm +++ b/code/modules/cargo/exports/manifest.dm @@ -36,8 +36,7 @@ return TRUE return FALSE -/datum/export/manifest_error_denied/get_cost(obj/O) - var/obj/item/paper/fluff/jobs/cargo/manifest/M = O +/datum/export/manifest_error_denied/get_base_cost(obj/item/paper/fluff/jobs/cargo/manifest/M) return ..() + M.order_cost @@ -59,8 +58,7 @@ return TRUE return FALSE -/datum/export/manifest_error/get_cost(obj/O) - var/obj/item/paper/fluff/jobs/cargo/manifest/M = O +/datum/export/manifest_error/get_base_cost(obj/item/paper/fluff/jobs/cargo/manifest/M) return -min(M.order_cost * 0.5, MAX_MANIFEST_PENALTY) @@ -82,8 +80,7 @@ return TRUE return FALSE -/datum/export/manifest_correct_denied/get_cost(obj/O) - var/obj/item/paper/fluff/jobs/cargo/manifest/M = O +/datum/export/manifest_correct_denied/get_base_cost(obj/item/paper/fluff/jobs/cargo/manifest/M) return -min(M.order_cost * 0.5, MAX_MANIFEST_PENALTY) #undef MAX_MANIFEST_PENALTY diff --git a/code/modules/cargo/exports/materials.dm b/code/modules/cargo/exports/materials.dm index 890a93eebf4..d5c48706cdf 100644 --- a/code/modules/cargo/exports/materials.dm +++ b/code/modules/cargo/exports/materials.dm @@ -1,33 +1,55 @@ /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 - var/datum/material/material_id = null 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(!material_id) - return 0 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) - if(isnull(mat_comp[mat_ref])) + var/amount = mat_comp[mat_ref] + if(!amount) return 0 - 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. - 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 @@ -35,7 +57,6 @@ /datum/export/material/plasma cost = CARGO_CRATE_VALUE * 0.4 - k_elasticity = 0 material_id = /datum/material/plasma message = "cm3 of plasma" @@ -44,7 +65,7 @@ material_id = /datum/material/bananium message = "cm3 of bananium" -/datum/export/material/diamond +/datum/export/material/adamantine cost = CARGO_CRATE_VALUE material_id = /datum/material/adamantine message = "cm3 of adamantine" @@ -70,76 +91,48 @@ 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 - k_recovery_elasticity = 1/10 //Modeled such that a stack of materials, selling to drop the cost to ~20%, will recover fully in 8 minutes instead of 20. - export_types = list( - /obj/item/stack/sheet/mineral, - /obj/item/stack/tile/mineral, - /obj/item/stack/ore, - /obj/item/coin, - /obj/item/stock_block, - ) -/datum/export/material/market/applies_to(obj/exported_obj, apply_elastic) - . = ..() - if(istype(exported_obj, /obj/item/stock_block)) - var/obj/item/stock_block/block = exported_obj - if(!block.export_mat) - return FALSE - if(block.export_mat == material_id) - return TRUE - return FALSE +/datum/export/material/market/get_base_cost(obj/exported_obj) + return ..() * SSstock_market.materials_prices[material_id] -/datum/export/material/market/get_amount(obj/exported_obj) - if(istype(exported_obj, /obj/item/stock_block)) - var/obj/item/stock_block/block = exported_obj - return block.quantity - return ..() +/** + * 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) -/datum/export/material/market/get_cost(obj/exported_obj, apply_elastic = TRUE) - if(!material_id) - return 0 - - var/obj/item/exported_item = exported_obj - var/amount = get_amount(exported_item) - if(!amount) - return 0 - - var/obj/item/stock_block/block - if(istype(exported_item, /obj/item/stock_block)) - block = exported_item - if(block.export_mat != material_id) - return 0 - - var/material_value = 0 - if(block) - if(block.fluid) - material_value = SSstock_market.materials_prices[block.export_mat] * amount - else - material_value = block.export_value - else - material_value = SSstock_market.materials_prices[material_id] * amount - return (apply_elastic ? cost : init_cost) * material_value // Cost in this case is only serving as the elastic modifier, where material value is the raw value of the sheets sold. + return get_amount(sold_item) /datum/export/material/market/sell_object(obj/sold_item, datum/export_report/report, dry_run, apply_elastic) . = ..() - var/amount = get_amount(sold_item) - if(!amount) + 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] * (amount / (amount + SSstock_market.materials_quantity[material_id]))) + 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, amount) + SSstock_market.adjust_material_quantity(material_id, sheets) /datum/export/material/market/diamond material_id = /datum/material/diamond @@ -167,8 +160,10 @@ export_types = list( /obj/item/stack/sheet/bluespace_crystal, /obj/item/stack/ore/bluespace_crystal, - /obj/item/stock_block, - ) //For whatever reason, bluespace crystals are not a mineral + ) + +/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" @@ -179,9 +174,11 @@ /obj/item/stack/rods, /obj/item/stack/ore, /obj/item/coin, - /obj/item/stock_block, ) +/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 @@ -189,5 +186,32 @@ /obj/item/stack/sheet/glass, /obj/item/stack/ore, /obj/item/shard, - /obj/item/stock_block, ) + +/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 ..() diff --git a/code/modules/cargo/exports/mecha.dm b/code/modules/cargo/exports/mecha.dm index 06eb94401a6..085977f02b8 100644 --- a/code/modules/cargo/exports/mecha.dm +++ b/code/modules/cargo/exports/mecha.dm @@ -1,24 +1,24 @@ -/datum/export/mecha/ripley +/datum/export/ripley cost = CARGO_CRATE_VALUE * 13 unit_name = "APLU MK-II Ripley" export_types = list(/obj/vehicle/sealed/mecha/ripley/mk2) -/datum/export/mecha/clarke +/datum/export/clarke cost = CARGO_CRATE_VALUE * 16 unit_name = "Clarke" export_types = list(/obj/vehicle/sealed/mecha/clarke) -/datum/export/mecha/odysseus +/datum/export/odysseus cost = CARGO_CRATE_VALUE * 11 unit_name = "Odysseus" export_types = list(/obj/vehicle/sealed/mecha/odysseus) -/datum/export/mecha/gygax +/datum/export/gygax cost = CARGO_CRATE_VALUE * 28 unit_name = "Gygax" export_types = list(/obj/vehicle/sealed/mecha/gygax) -/datum/export/mecha/durand +/datum/export/durand cost = CARGO_CRATE_VALUE * 20 unit_name = "Durand" export_types = list(/obj/vehicle/sealed/mecha/durand) diff --git a/code/modules/cargo/exports/organs.dm b/code/modules/cargo/exports/organs.dm index b446337586d..6447d7d4627 100644 --- a/code/modules/cargo/exports/organs.dm +++ b/code/modules/cargo/exports/organs.dm @@ -2,14 +2,13 @@ #define CLIENT_ORGAN_MULT 10 /datum/export/organ + abstract_type = /datum/export/organ include_subtypes = FALSE //CentCom doesn't need organs from non-humans. -/datum/export/organ/get_cost(obj/exported_item, apply_elastic) - if(HAS_TRAIT(exported_item, TRAIT_CLIENT_STARTING_ORGAN)) - // Multiply value for organs that started in a player - // Unaffected by price elasticity as there's a limited amount of these in play - return round(init_cost * CLIENT_ORGAN_MULT) - return ..() +/datum/export/organ/get_base_cost(obj/exported_item) + // Multiply value for organs that started in a player + // Unaffected by price elasticity as there's a limited amount of these in play + return round(..() * HAS_TRAIT(exported_item, TRAIT_CLIENT_STARTING_ORGAN) ? CLIENT_ORGAN_MULT : 1) /datum/export/organ/heart cost = CARGO_CRATE_VALUE * 0.2 //For the man who has everything and nothing. @@ -46,18 +45,17 @@ unit_name = "humanoid tongue" export_types = list(/obj/item/organ/tongue) -/datum/export/organ/external/tail/lizard +/datum/export/organ/lizard_tail cost = CARGO_CRATE_VALUE * 1.25 unit_name = "lizard tail" export_types = list(/obj/item/organ/tail/lizard) - -/datum/export/organ/external/tail/cat +/datum/export/organ/cat_tail cost = CARGO_CRATE_VALUE * 1.5 unit_name = "cat tail" export_types = list(/obj/item/organ/tail/cat) -/datum/export/organ/ears/cat +/datum/export/organ/cat_ears cost = CARGO_CRATE_VALUE unit_name = "cat ears" export_types = list(/obj/item/organ/ears/cat) diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm index fc8c9656fea..d5d672425ba 100644 --- a/code/modules/cargo/exports/parts.dm +++ b/code/modules/cargo/exports/parts.dm @@ -1,34 +1,34 @@ // Circuit boards, spare parts, etc. -/datum/export/solar/assembly +/datum/export/solar_assembly cost = CARGO_CRATE_VALUE * 0.25 unit_name = "solar panel assembly" export_types = list(/obj/item/solar_assembly) -/datum/export/solar/tracker_board +/datum/export/tracker_board cost = CARGO_CRATE_VALUE * 0.5 unit_name = "solar tracker board" export_types = list(/obj/item/electronics/tracker) -/datum/export/solar/control_board +/datum/export/control_board cost = CARGO_CRATE_VALUE * 0.75 unit_name = "solar panel control board" export_types = list(/obj/item/circuitboard/computer/solar_control) //Data Disks -/datum/export/modular_part/portabledrive/advanced +/datum/export/advanced_disk cost = CARGO_CRATE_VALUE * 0.4 unit_name = "advanced data disk" export_types = list(/obj/item/computer_disk/advanced) include_subtypes = FALSE -/datum/export/modular_part/portabledrive/super +/datum/export/super_disk cost = CARGO_CRATE_VALUE * 0.6 unit_name = "super data disk" export_types = list(/obj/item/computer_disk/super) include_subtypes = FALSE -/datum/export/modular_part/portabledrive/standard +/datum/export/standard_disk cost = CARGO_CRATE_VALUE * 0.2 unit_name = "data disk" export_types = list(/obj/item/computer_disk) diff --git a/code/modules/cargo/exports/seeds.dm b/code/modules/cargo/exports/seeds.dm index 981e77c16b6..3aa33ea2cf6 100644 --- a/code/modules/cargo/exports/seeds.dm +++ b/code/modules/cargo/exports/seeds.dm @@ -1,38 +1,30 @@ /datum/export/seed cost = CARGO_CRATE_VALUE * 0.25 // Gets multiplied by potency - k_elasticity = 1 //price inelastic/quantity elastic, only need to export a few samples + k_elasticity = 0 //price inelastic/quantity elastic, only need to export a few samples unit_name = "new plant species sample" export_types = list(/obj/item/seeds) - var/needs_discovery = FALSE // Only for undiscovered species + // Only for undiscovered species + var/needs_discovery = FALSE + // Plants sold on the market var/static/list/discovered_plants = list() -/datum/export/seed/get_cost(obj/O) - var/obj/item/seeds/S = O +/datum/export/seed/get_base_cost(obj/item/seeds/S) if(!needs_discovery && (S.type in discovered_plants)) return 0 if(needs_discovery && !(S.type in discovered_plants)) return 0 return ..() * S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later. -/datum/export/seed/sell_object(obj/O, datum/export_report/report, dry_run, apply_elastic) +/datum/export/seed/sell_object(obj/item/seeds/S, datum/export_report/report, dry_run, apply_elastic) . = ..() if(. && !dry_run) - var/obj/item/seeds/S = O discovered_plants[S.type] = S.potency - /datum/export/seed/potency cost = CARGO_CRATE_VALUE * 0.0125 // Gets multiplied by potency and rarity. unit_name = "improved plant sample" export_types = list(/obj/item/seeds) needs_discovery = TRUE // Only for already discovered species -/datum/export/seed/potency/get_cost(obj/O) - var/obj/item/seeds/S = O - var/cost = ..() - if(!cost) - return 0 - - var/potDiff = (S.potency - discovered_plants[S.type]) - - return round(..() * potDiff) +/datum/export/seed/potency/get_base_cost(obj/item/seeds/S) + return ..() * (S.potency - discovered_plants[S.type]) diff --git a/code/modules/cargo/exports/sheets.dm b/code/modules/cargo/exports/sheets.dm index 53ca52ab27b..ebc821fcb4d 100644 --- a/code/modules/cargo/exports/sheets.dm +++ b/code/modules/cargo/exports/sheets.dm @@ -1,4 +1,5 @@ /datum/export/stack + abstract_type = /datum/export/stack unit_name = "sheet" /datum/export/stack/get_amount(obj/O) @@ -9,48 +10,48 @@ // Hides -/datum/export/stack/skin/monkey +/datum/export/stack/monkey cost = CARGO_CRATE_VALUE * 0.25 unit_name = "monkey hide" export_types = list(/obj/item/stack/sheet/animalhide/monkey) -/datum/export/stack/skin/human +/datum/export/stack/human cost = CARGO_CRATE_VALUE * 0.5 unit_name = "piece" message = "of human skin" export_types = list(/obj/item/stack/sheet/animalhide/human) -/datum/export/stack/skin/goliath_hide +/datum/export/stack/goliath_hide cost = CARGO_CRATE_VALUE unit_name = "goliath hide" export_types = list(/obj/item/stack/sheet/animalhide/goliath_hide) -/datum/export/stack/skin/cat +/datum/export/stack/cat cost = CARGO_CRATE_VALUE * 0.75 unit_name = "cat hide" export_types = list(/obj/item/stack/sheet/animalhide/cat) -/datum/export/stack/skin/corgi +/datum/export/stack/corgi cost = CARGO_CRATE_VALUE unit_name = "corgi hide" export_types = list(/obj/item/stack/sheet/animalhide/corgi) -/datum/export/stack/skin/lizard +/datum/export/stack/lizard cost = CARGO_CRATE_VALUE * 0.75 unit_name = "lizard hide" export_types = list(/obj/item/stack/sheet/animalhide/lizard) -/datum/export/stack/skin/gondola +/datum/export/stack/gondola cost = CARGO_CRATE_VALUE * 10 unit_name = "gondola hide" export_types = list(/obj/item/stack/sheet/animalhide/gondola) -/datum/export/stack/skin/xeno +/datum/export/stack/xeno cost = CARGO_CRATE_VALUE * 2.5 unit_name = "alien hide" export_types = list(/obj/item/stack/sheet/animalhide/xeno) -/datum/export/stack/skin/carp +/datum/export/stack/carp cost = CARGO_CRATE_VALUE * 0.5 unit_name = "carp skin" export_types = list(/obj/item/stack/sheet/animalhide/carp) diff --git a/code/modules/cargo/exports/stamped_paperwork.dm b/code/modules/cargo/exports/stamped_paperwork.dm index 6f2c0e47ee1..ee7a52f177a 100644 --- a/code/modules/cargo/exports/stamped_paperwork.dm +++ b/code/modules/cargo/exports/stamped_paperwork.dm @@ -5,15 +5,8 @@ exclude_types = list(/obj/item/paperwork/photocopy) //Has its own category allow_negative_cost = TRUE -/datum/export/paperwork/get_cost(obj/item/paperwork/sold_paperwork) - var/paperwork_cost = cost - - if(sold_paperwork.stamped) - paperwork_cost = ..() - else - paperwork_cost = -init_cost //Punishment for improperly filed paperwork. - - return paperwork_cost +/datum/export/paperwork/get_base_cost(obj/item/paperwork/sold_paperwork) + return sold_paperwork.stamped ? ..() : -cost /datum/export/photocopy cost = CARGO_CRATE_VALUE @@ -25,7 +18,7 @@ ///Used to track if a batch of photocopy exports has backfired var/backfired = FALSE -/datum/export/photocopy/get_cost(obj/item/paperwork/photocopy/sold_paperwork) +/datum/export/photocopy/get_base_cost(obj/item/paperwork/photocopy/sold_paperwork) if(sold_paperwork.stamped && !backfired) //Upon backfiring, no more photocopies are processed or sold until the next cargo shipment if(sold_paperwork.voided) return 0 //Voided photocopies do nothing @@ -34,12 +27,12 @@ if(prob(backfire_chance)) backfire_chance = 0 backfired = TRUE - return -init_cost * 4 //too high of an amount to allow for infinite money + return -cost * 4 //too high of an amount to allow for infinite money else return ..() else - return -init_cost + return -cost /datum/export/photocopy/total_printout(datum/export_report/ex, notes) . = ..() diff --git a/code/modules/cargo/exports/weapons.dm b/code/modules/cargo/exports/weapons.dm index bceeab64b4c..03928077325 100644 --- a/code/modules/cargo/exports/weapons.dm +++ b/code/modules/cargo/exports/weapons.dm @@ -1,6 +1,7 @@ // Weapon exports. Stun batons, disablers, etc. /datum/export/weapon + abstract_type = /datum/export/weapon include_subtypes = FALSE /datum/export/weapon/baton @@ -79,4 +80,4 @@ cost = CARGO_CRATE_VALUE * 1.5 unit_name = "WT-550 automatic rifle" export_types = list(/obj/item/gun/ballistic/automatic/wt550) - + diff --git a/code/modules/cargo/exports/xenobio.dm b/code/modules/cargo/exports/xenobio.dm index 4b83e97567e..77d4b845ec9 100644 --- a/code/modules/cargo/exports/xenobio.dm +++ b/code/modules/cargo/exports/xenobio.dm @@ -1,41 +1,39 @@ -/datum/export/slime - -/datum/export/slime/grey +/datum/export/slime_extract_grey cost = CARGO_CRATE_VALUE * 0.05 unit_name = "grey slime core" export_types = list(/obj/item/slime_extract/grey) -/datum/export/slime/common +/datum/export/slime_extract_common cost = CARGO_CRATE_VALUE * 0.12 unit_name = "common slime core" export_types = list(/obj/item/slime_extract/metal,/obj/item/slime_extract/orange,/obj/item/slime_extract/purple,/obj/item/slime_extract/blue) -/datum/export/slime/uncommon +/datum/export/slime_extract_uncommon cost = CARGO_CRATE_VALUE * 0.2 unit_name = "uncommon slime core" export_types = list(/obj/item/slime_extract/gold,/obj/item/slime_extract/green,/obj/item/slime_extract/red,/obj/item/slime_extract/pink) -/datum/export/slime/rare +/datum/export/slime_extract_rare cost = CARGO_CRATE_VALUE * 0.28 unit_name = "rare slime core" export_types = list(/obj/item/slime_extract/silver,/obj/item/slime_extract/darkblue,/obj/item/slime_extract/darkpurple,/obj/item/slime_extract/yellow) -/datum/export/slime/charged +/datum/export/slime_extract_charged cost = CARGO_CRATE_VALUE unit_name = "\improper EMP-proof slime core" export_types = list(/obj/item/stock_parts/power_store/cell/emproof/slime) -/datum/export/slime/hypercharged +/datum/export/slime_extract_hypercharged cost = CARGO_CRATE_VALUE * 1.2 unit_name = "hypercharged slime core" export_types = list(/obj/item/stock_parts/power_store/cell/high/slime_hypercharged) -/datum/export/slime/epic //EPIIIIIIC +/datum/export/slime_extract_epic //EPIIIIIIC cost = CARGO_CRATE_VALUE * 0.44 unit_name = "epic slime core" export_types = list(/obj/item/slime_extract/black,/obj/item/slime_extract/cerulean,/obj/item/slime_extract/oil,/obj/item/slime_extract/sepia,/obj/item/slime_extract/pyrite,/obj/item/slime_extract/adamantine,/obj/item/slime_extract/lightpink,/obj/item/slime_extract/bluespace) -/datum/export/slime/rainbow +/datum/export/slime_extract_rainbow cost = CARGO_CRATE_VALUE unit_name = "rainbow slime core" export_types = list(/obj/item/slime_extract/rainbow) diff --git a/code/modules/cargo/materials_market.dm b/code/modules/cargo/materials_market.dm index 43bf07f5628..019c8f94503 100644 --- a/code/modules/cargo/materials_market.dm +++ b/code/modules/cargo/materials_market.dm @@ -17,16 +17,6 @@ idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION light_power = 3 light_range = MINIMUM_USEFUL_LIGHT_RANGE - /// What items can be converted into a stock block? Must be a stack subtype based on current implementation. - var/static/list/exportable_material_items = list( - /obj/item/stack/sheet/iron, //God why are we like this - /obj/item/stack/sheet/glass, //No really, God why are we like this - /obj/item/stack/sheet/mineral, - /obj/item/stack/tile/mineral, - /obj/item/stack/ore, - /obj/item/stack/sheet/bluespace_crystal, - /obj/item/stack/rods - ) /// Are we ordering sheets from our own card balance or the cargo budget? var/ordering_private = TRUE @@ -55,47 +45,41 @@ if(default_deconstruction_crowbar(tool)) return ITEM_INTERACT_SUCCESS -/obj/machinery/materials_market/attackby(obj/item/markable_object, mob/user, list/modifiers, list/attack_modifiers) - if(is_type_in_list(markable_object, exportable_material_items)) - if(machine_stat & NOPOWER) - balloon_alert(user, "no power!") - return FALSE - var/material_to_export - var/obj/item/stack/exportable = markable_object - for(var/datum/material/mat as anything in SSstock_market.materials_prices) - if(exportable.has_material_type(mat)) - material_to_export = mat - break //This is only for trading non-alloys, so we can break here +/obj/machinery/materials_market/item_interaction(mob/living/user, obj/item/stack/exportable, list/modifiers) + . = NONE + if(!isstack(exportable)) + return + if(!is_operational) + balloon_alert(user, "no power!") + return ITEM_INTERACT_FAILURE - var/datum/export_report/report = export_item_and_contents(exportable, apply_elastic = FALSE, dry_run = TRUE) // We'll apply elastic price reduction when fully sold. - var/price = 0 - var/amount = 0 - for(var/exported_datum in report.total_amount) - price += report.total_value[exported_datum] - amount += report.total_amount[exported_datum] + var/list/datum/material/materials = exportable.custom_materials + if(materials.len != 1) + balloon_alert(user, "alloy stacks not allowed") + return ITEM_INTERACT_FAILURE - if(amount <= 1) - balloon_alert(user, "stack too small!") - return FALSE + var/price = SSstock_market.materials_prices[materials[1].type] + if(!price) + balloon_alert(user, "materials in stack are worthless") + return ITEM_INTERACT_FAILURE - if(price <= 0) - balloon_alert(user, "not valuable enough to sell!") - return FALSE + if(!user.transferItemToLoc(exportable, src)) + to_chat(user, span_warning("[exportable] is stuck in hand!")) + return ITEM_INTERACT_FAILURE - qdel(markable_object) - var/obj/item/stock_block/new_block = new /obj/item/stock_block(drop_location()) - new_block.export_value = price - new_block.export_mat = material_to_export - new_block.quantity = amount / SHEET_MATERIAL_AMOUNT - to_chat(user, span_notice("You have created a stock block worth [new_block.export_value] cr! Sell it before it becomes liquid!")) - playsound(src, 'sound/machines/synth/synth_yes.ogg', 50, FALSE) - return TRUE - return ..() + var/obj/item/stock_block/new_block = new /obj/item/stock_block(drop_location()) + new_block.export_value = price + new_block.set_custom_materials(materials) + to_chat(user, span_notice("You have created a stock block worth [new_block.export_value * exportable.amount] cr! Sell it before it becomes liquid!")) + playsound(src, 'sound/machines/synth/synth_yes.ogg', 50, FALSE) + qdel(exportable) + use_energy(active_power_usage) + return ITEM_INTERACT_SUCCESS /obj/machinery/materials_market/power_change() . = ..() - if(machine_stat & NOPOWER) + if(!is_operational) set_light(0, 0) else set_light(initial(light_range), initial(light_power)) @@ -192,7 +176,7 @@ //Pulling elastic modifier into data. for(var/datum/export/material/market/export_est in GLOB.exports_list) if(export_est.material_id == traded_mat) - elastic_mult = (export_est.cost / export_est.init_cost) * 100 + elastic_mult = export_est.k_elasticity * 100 material_data += list(list( "name" = initial(traded_mat.name), @@ -288,7 +272,6 @@ var/cost = SSstock_market.materials_prices[material_bought] * quantity var/list/things_to_order = list() - things_to_order += (sheet_to_buy) things_to_order[sheet_to_buy] = quantity // We want to count how many stacks of all sheets we're ordering to make sure they don't exceed the limit of 10 @@ -299,14 +282,14 @@ var/prior_sheets = current_order.pack.contains[sheet_to_buy] if(prior_sheets + quantity > SSstock_market.materials_quantity[material_bought] ) say("There aren't enough sheets on the market! Please wait for more sheets to be traded before adding more.") - playsound(usr, 'sound/machines/synth/synth_no.ogg', 35, FALSE) + playsound(living_user, 'sound/machines/synth/synth_no.ogg', 35, FALSE) return // Check if the order exceeded the purchase limit var/prior_stacks = ROUND_UP(prior_sheets / MAX_STACK_SIZE) if(prior_stacks >= MAX_STACK_LIMIT) say("There are already 10 stacks of sheets on order! Please wait for them to arrive before ordering more.") - playsound(usr, 'sound/machines/synth/synth_no.ogg', 35, FALSE) + playsound(living_user, 'sound/machines/synth/synth_no.ogg', 35, FALSE) return // Prevents you from ordering more than the available budget @@ -367,10 +350,6 @@ icon_state = "stock_block" /// How many credits was this worth when created? var/export_value = 0 - /// What is the name of the material this was made from? - var/datum/material/export_mat - /// Quantity of export material - var/quantity = 0 /// Is this stock block currently updating its value with the market (aka fluid)? var/fluid = FALSE @@ -381,7 +360,11 @@ /obj/item/stock_block/examine(mob/user) . = ..() - . += span_notice("\The [src] is worth [export_value] cr, from selling [quantity] sheets of [initial(export_mat?.name)].") + + var/datum/material/export_mat = custom_materials[1] + var/quantity = custom_materials[export_mat] / SHEET_MATERIAL_AMOUNT + . += span_notice("\The [src] is worth [quantity * export_value] cr, from selling [quantity] sheets of [export_mat.name].") + if(fluid) . += span_warning("\The [src] is currently liquid! Its value is based on the market price.") else @@ -393,9 +376,7 @@ update_appearance(UPDATE_ICON_STATE) /obj/item/stock_block/proc/update_value() - if(!SSstock_market.materials_prices[export_mat]) - return - export_value = quantity * SSstock_market.materials_prices[export_mat] + export_value = SSstock_market.materials_prices[custom_materials[1]] icon_state = "stock_block_liquid" update_appearance(UPDATE_ICON_STATE) visible_message(span_warning("\The [src] becomes liquid!")) diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm index f7f868f7ba9..71563699a40 100644 --- a/code/modules/cargo/packs/organic.dm +++ b/code/modules/cargo/packs/organic.dm @@ -32,28 +32,61 @@ crate_name = "beekeeping starter crate" crate_type = /obj/structure/closet/crate/hydroponics +/datum/supply_pack/organic/randomized + test_ignored = TRUE + crate_name = "food crate" + +/datum/supply_pack/organic/randomized/fill(obj/structure/closet/crate/C) + for(var/i in 1 to 15) + var/item = pick(contains) + new item(C) + /datum/supply_pack/organic/randomized/chef name = "Excellent Meat Crate" desc = "The best cuts in the whole galaxy. Contains a random assortment of exotic meats." cost = CARGO_CRATE_VALUE * 4 - contains = list(/obj/item/food/meat/slab/human/mutant/slime, - /obj/item/food/meat/slab/killertomato, - /obj/item/food/meat/slab/bear, - /obj/item/food/meat/slab/xeno, - /obj/item/food/meat/slab/spider, - /obj/item/food/meat/rawbacon, - /obj/item/food/meat/slab/penguin, - /obj/item/food/spiderleg, - /obj/item/food/fishmeat/carp, - /obj/item/food/meat/slab/human, - /obj/item/food/meat/slab/grassfed, - ) - crate_name = "food crate" + contains = list( + /obj/item/food/meat/slab/human/mutant/slime, + /obj/item/food/meat/slab/killertomato, + /obj/item/food/meat/slab/bear, + /obj/item/food/meat/slab/xeno, + /obj/item/food/meat/slab/spider, + /obj/item/food/meat/rawbacon, + /obj/item/food/meat/slab/penguin, + /obj/item/food/spiderleg, + /obj/item/food/fishmeat/carp, + /obj/item/food/meat/slab/human, + /obj/item/food/meat/slab/grassfed, + ) -/datum/supply_pack/organic/randomized/chef/fill(obj/structure/closet/crate/C) - for(var/i in 1 to 15) - var/item = pick(contains) - new item(C) +/datum/supply_pack/organic/randomized/fruits + name = "Fruit Crate" + desc = "Rich in vitamins. Contains a lime, orange, watermelon, apple, berries and a lemon." + cost = CARGO_CRATE_VALUE * 3 + contains = list( + /obj/item/food/grown/citrus/lime, + /obj/item/food/grown/citrus/orange, + /obj/item/food/grown/watermelon, + /obj/item/food/grown/apple, + /obj/item/food/grown/berries, + /obj/item/food/grown/citrus/lemon, + ) + +/datum/supply_pack/organic/randomized/vegetables + name = "Vegetables Crate" + desc = "Grown in vats. Contains a chili, corn, tomato, potato, carrot, chanterelle, onion, pumpkin, and cucumber." + cost = CARGO_CRATE_VALUE * 1.8 + contains = list( + /obj/item/food/grown/chili, + /obj/item/food/grown/corn, + /obj/item/food/grown/tomato, + /obj/item/food/grown/potato, + /obj/item/food/grown/carrot, + /obj/item/food/grown/mushroom/chanterelle, + /obj/item/food/grown/onion, + /obj/item/food/grown/pumpkin, + /obj/item/food/grown/cucumber, + ) /datum/supply_pack/organic/exoticseeds name = "Exotic Seeds Crate" @@ -98,20 +131,6 @@ ) crate_name = "food crate" -/datum/supply_pack/organic/randomized/chef/fruits - name = "Fruit Crate" - desc = "Rich in vitamins. Contains a lime, orange, watermelon, apple, \ - berries and a lemon." - cost = CARGO_CRATE_VALUE * 3 - contains = list(/obj/item/food/grown/citrus/lime, - /obj/item/food/grown/citrus/orange, - /obj/item/food/grown/watermelon, - /obj/item/food/grown/apple, - /obj/item/food/grown/berries, - /obj/item/food/grown/citrus/lemon, - ) - crate_name = "food crate" - /datum/supply_pack/organic/cream_piee name = "High-yield Clown-grade Cream Pie Crate" desc = "Designed by Aussec's Advanced Warfare Research Division, \ @@ -276,23 +295,6 @@ crate_name = "seeds crate" crate_type = /obj/structure/closet/crate/hydroponics -/datum/supply_pack/organic/randomized/chef/vegetables - name = "Vegetables Crate" - desc = "Grown in vats. Contains a chili, corn, tomato, potato, carrot, \ - chanterelle, onion, pumpkin, and cucumber." - cost = CARGO_CRATE_VALUE * 1.8 - contains = list(/obj/item/food/grown/chili, - /obj/item/food/grown/corn, - /obj/item/food/grown/tomato, - /obj/item/food/grown/potato, - /obj/item/food/grown/carrot, - /obj/item/food/grown/mushroom/chanterelle, - /obj/item/food/grown/onion, - /obj/item/food/grown/pumpkin, - /obj/item/food/grown/cucumber, - ) - crate_name = "food crate" - /datum/supply_pack/organic/grill name = "Grilling Starter Kit" desc = "Hey dad I'm Hungry. Hi Hungry I'm THE NEW GRILLING STARTER KIT \ @@ -331,6 +333,7 @@ ) crate_name = "\improper Tiziran Supply box" crate_type = /obj/structure/closet/crate/cardboard/tiziran + test_ignored = TRUE /datum/supply_pack/organic/mothic_supply name = "Mothic Supply Box" diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index cee4898000f..beddb14125c 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -1477,7 +1477,7 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( #define FISH_PRICE_SOFT_CAP_EXPONENT 0.86 ///Returns the price of this fish, for the fish export. -/obj/item/fish/proc/get_export_price(price, elasticity_percent) +/obj/item/fish/proc/get_export_price(price) var/size_weight_exponentation = (size * weight * FISH_PRICE_MULTIPLIER)^FISH_PRICE_CURVE_EXPONENT var/raw_price = price + size_weight_exponentation if(raw_price >= FISH_PRICE_SOFT_CAP_THRESHOLD + 1) @@ -1485,7 +1485,7 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( raw_price = FISH_PRICE_SOFT_CAP_THRESHOLD + soft_cap if(HAS_TRAIT(src, TRAIT_FISH_LOW_PRICE)) //Avoid printing money by simply ordering fish and sending it back. raw_price *= 0.05 - return raw_price * elasticity_percent + return raw_price #undef FISH_PRICE_MULTIPLIER #undef FISH_PRICE_CURVE_EXPONENT diff --git a/code/modules/shuttle/mobile_port/variants/supply.dm b/code/modules/shuttle/mobile_port/variants/supply.dm index a6c5030605a..077f9e733aa 100644 --- a/code/modules/shuttle/mobile_port/variants/supply.dm +++ b/code/modules/shuttle/mobile_port/variants/supply.dm @@ -282,9 +282,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( var/datum/bank_account/cargo_budget = SSeconomy.get_dep_account(ACCOUNT_CAR) var/presale_points = cargo_budget.account_balance - if(!GLOB.exports_list.len) // No exports list? Generate it! - setupExports() - var/msg = "" var/datum/export_report/report = new @@ -299,9 +296,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( continue export_item_and_contents(exporting_atom, apply_elastic = TRUE, dry_run = FALSE, external_report = report) - if(report.exported_atoms) - report.exported_atoms += "." //ugh - for(var/datum/export/exported_datum in report.total_amount) var/export_text = exported_datum.total_printout(report) if(!export_text) @@ -311,7 +305,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( cargo_budget.adjust_money(report.total_value[exported_datum]) SSshuttle.centcom_message = msg - investigate_log("contents sold for [cargo_budget.account_balance - presale_points] credits. Contents: [report.exported_atoms ? report.exported_atoms.Join(",") + "." : "none."] Message: [SSshuttle.centcom_message || "none."]", INVESTIGATE_CARGO) + if(report.exported_atoms.len) + investigate_log("contents sold for [cargo_budget.account_balance - presale_points] credits. Contents: [report.exported_atoms.Join(",")]. Message: [msg]", INVESTIGATE_CARGO) /* Generates a box of mail depending on our exports and imports. diff --git a/code/modules/unit_tests/cargo_selling.dm b/code/modules/unit_tests/cargo_selling.dm index 31d90505bdf..a8124947343 100644 --- a/code/modules/unit_tests/cargo_selling.dm +++ b/code/modules/unit_tests/cargo_selling.dm @@ -18,6 +18,16 @@ export_types = list(/obj/item/cargo_unit_test_content) /datum/unit_test/cargo_selling/Run() + for(var/datum/export/subtype as anything in subtypesof(/datum/export)) + if(subtype::abstract_type == subtype) + continue + if(subtype::k_recovery_time < SSprocessing.wait) + TEST_FAIL("[subtype] should have k_recovery time >= [SSprocessing.wait]") + var/datum/export/sell = new subtype + if(!length(sell.export_types)) + TEST_FAIL("[subtype] has no export types") + qdel(sell) + var/obj/item/cargo_unit_test_container/box = allocate(/obj/item/cargo_unit_test_container) var/obj/item/cargo_unit_test_container/box_skip_content = allocate(/obj/item/cargo_unit_test_container)