Files
JacquerelandGitHub 9fb7c20daa Cargo can buy more presents at Christmas time (#94251)
## About The Pull Request

Last Christmas we had and resolved this bug #88595 where via the
mechanic of buying random trash, Cargo could roll on the roulette to try
and get random Christmas presents and open them for random items.
This was removed because it's not supposed to work that way and was a
way of exploiting Christmas cheer for personal gain, which is grinchsome
behaviour.

That said, being able to order Christmas presents is also kind of
soulful, so this PR brings that mechanic back but on purpose this time.

During the Christmas season _only_, Cargo can spend 3000 credits of
their budget to order a "Surplus Christmas Gifts" crate which contains
4-6 presents _with pre-assigned recipients_.
In a similar system to addressed mail, only the person written on the
gift tag can open the present, so unless you're sufficiently scroogeous
to collect a huge pile of presents on the cargo room floor in hopes of
getting a couple with your name on (in my testing these contained raw
unbaked croissant dough, and a piece of paper from a space ruin) you
should mostly be using this to deliver generalised holiday cheer to the
crew rather than just yourself.



This PR also contains (maybe as more lines than the actual feature) a
refactor changing a bunch of boolean vars on `/datum/supply_pack` into
bitflags, because I needed to add one more and it seemed silly to have
so many booleans.

## Why It's Good For The Game

It allows those with the yuletide spirit to deliver some christmas cheer
to their fellow man, isn't that the season's reason?

## Changelog

🆑
add: When the game considers it to be Christmas, Cargo can order
additional Christmas gifts pre-addressed to random crew members.
/🆑
2025-12-12 02:10:31 +11:00

178 lines
7.0 KiB
Plaintext

/datum/supply_pack
/// The name of the supply pack, as listed on th cargo purchasing UI.
var/name = "Crate"
/// The group that the supply pack is sorted into within the cargo purchasing UI.
var/group = ""
/// Cost of the crate. DO NOT GO ANY LOWER THAN X1.4 the "CARGO_CRATE_VALUE" value if using regular crates, or infinite profit will be possible!
var/cost = CARGO_CRATE_VALUE * 1.4
/// What access is required to open the crate when spawned?
var/access = FALSE
/// Who can view this supply_pack and with what access.
var/access_view = FALSE
/// If someone with any of the following accesses in a list can open this cargo pack crate.
var/access_any = FALSE
/// A list of items that are spawned in the crate of the supply pack.
var/list/contains = null
/// What is the name of the crate that is spawned with the crate's contents??
var/crate_name = "crate"
/// When spawning a gas canistor, what kind of gas type are we spawning?
var/id
/// The description shown on the cargo purchasing UI. No desc by default.
var/desc = ""
/// What typepath of crate do you spawn?
var/crate_type = /obj/structure/closet/crate
/// If this pack comes shipped in a specific pod when launched from the express console
var/special_pod
/// Can coupons target this pack? If so, how rarely?
var/discountable = SUPPLY_PACK_NOT_DISCOUNTABLE
/// Is this supply pack considered unpredictable for the purposes of testing unit testing? Examples include the stock market, or miner supply crates. If true, exempts from unit testing
var/test_ignored = FALSE
/// Various properties for cargo order mostly used to determine which consoles can see it
var/order_flags = NONE
/datum/supply_pack/New()
id = type
/// Returns data used for cargo purchasing UI
/datum/supply_pack/proc/get_contents_ui_data()
var/list/data = list()
for(var/obj/item/item as anything in contains)
var/list/item_data = list(
"name" = item.name,
"icon" = item.greyscale_config ? null : item.icon,
"icon_state" = item.greyscale_config ? null : item.icon_state,
"amount" = contains[item]
)
UNTYPED_LIST_ADD(data, item_data)
return data
/**
* Proc that takes a given supply_pack, and attempts to create a crate containing the pack's contents as determined by fill()
*
* @ atom/A: The location or turf that the pack is being generated onto. Cargo shuttle provides an empty turf, other generate()s call this either null or otherwise.
* @ datum/bank_account/paying_account: The account to associate the supply pack with when going and generating the crate. Only the paying account can open said secure crate/case.
*/
/datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account)
var/obj/structure/closet/crate/C
if(paying_account)
C = new /obj/structure/closet/crate/secure/owned(A, paying_account)
C.name = "[crate_name] - Purchased by [paying_account.account_holder]"
else if(!crate_type)
CRASH("tried to generate a supply pack without a valid crate type")
else
C = new crate_type(A)
C.name = crate_name
if(access)
C.req_access = list(access)
if(access_any)
C.req_one_access = access_any
fill(C)
return C
/datum/supply_pack/proc/get_cost()
. = cost
. *= SSeconomy.pack_price_modifier
/datum/supply_pack/proc/fill(obj/structure/closet/crate/C)
for(var/item in contains)
if(!contains[item])
contains[item] = 1
for(var/iteration = 1 to contains[item])
var/atom/A = new item(C)
if(!(order_flags & ORDER_ADMIN_SPAWNED))
continue
A.flags_1 |= ADMIN_SPAWNED_1
/// For generating supply packs at runtime. Returns a list of supply packs to use instead of this one.
/datum/supply_pack/proc/generate_supply_packs()
return
///Easily send a supplypod to an area
/proc/send_supply_pod_to_area(contents, area_type, pod_type = /obj/structure/closet/supplypod)
var/list/areas = get_areas(area_type)
if(!LAZYLEN(areas))
return FALSE
var/list/open_turfs = list()
for(var/turf/open/floor/found_turf in get_area_turfs(pick(areas), subtypes = TRUE))
open_turfs += found_turf
if(!length(open_turfs))
return FALSE
new /obj/effect/pod_landingzone (pick(open_turfs), new pod_type (), contents)
return TRUE
/**
* Custom supply pack
* The contents are given on New rather than being static
* This is for adding custom orders to the Cargo console (like order consoles)
*/
/datum/supply_pack/custom
name = "mining order"
order_flags = ORDER_INVISIBLE
crate_name = "shaft mining delivery crate"
access = ACCESS_MINING
test_ignored = TRUE
/datum/supply_pack/custom/New(purchaser, cost, list/contains)
. = ..()
name = "[purchaser]'s Mining Order"
src.cost = cost
src.contains = contains
/datum/supply_pack/custom/minerals
name = "materials order"
crate_name = "galactic materials market delivery crate"
access = FALSE
crate_type = /obj/structure/closet/crate/cardboard
/datum/supply_pack/custom/minerals/New(purchaser, cost, list/contains)
. = ..()
name = "[purchaser]'s Materials Order"
src.cost = cost
src.contains = contains
///Alters material amrkey & adjust order quantities if they exceed whats on the market
/datum/supply_pack/custom/minerals/proc/adjust_market()
. = list()
for(var/obj/item/stack/sheet/possible_stack as anything in contains)
var/material_type = possible_stack.material_type
//in case we ordered more than what's in the market at the time due to market fluctuations
//we find the min of what was ordered & what's actually available in the market at this point of time
var/market_quantity = SSstock_market.materials_quantity[material_type]
var/available_quantity = contains[possible_stack]
if(available_quantity > market_quantity)
var/message = "[possible_stack::singular_name]: requested=[available_quantity] sheets, available=[market_quantity] sheets, adjusted=[market_quantity - available_quantity] sheets."
available_quantity = market_quantity
if(!available_quantity)
. += "[possible_stack::singular_name]: order cancelled due to insufficient sheets in the market."
contains -= possible_stack
continue
. += message
//adjust the order based ont the available quantity
contains[possible_stack] = available_quantity
//Prices go up as material quantity becomes scarce
var/fraction = available_quantity
if(market_quantity != available_quantity) //to avoid division by zero error
fraction /= (market_quantity - available_quantity)
SSstock_market.adjust_material_price(material_type, SSstock_market.materials_prices[material_type] * fraction)
//We decrease the quantity only after adjusting our prices for accurate values
SSstock_market.adjust_material_quantity(material_type, -available_quantity)
/datum/supply_pack/custom/minerals/fill(obj/structure/closet/crate/C)
for(var/obj/item/stack/sheet/possible_stack as anything in contains)
//spawn the ordered stack inside the crate
var/sheets_to_spawn = contains[possible_stack]
while(sheets_to_spawn)
var/spawn_quantity = min(sheets_to_spawn, MAX_STACK_SIZE)
var/obj/item/stack/sheet/ordered_stack = new possible_stack(C, spawn_quantity)
if(order_flags & ORDER_ADMIN_SPAWNED)
ordered_stack.flags_1 |= ADMIN_SPAWNED_1
sheets_to_spawn -= spawn_quantity