Files
Bubberstation/code/modules/modular_computers/file_system/programs/budgetordering.dm
T
Jacquerel 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

345 lines
14 KiB
Plaintext

/datum/computer_file/program/budgetorders
filename = "orderapp"
filedesc = "NT Shopping Network"
downloader_category = PROGRAM_CATEGORY_SUPPLY
program_open_overlay = "request"
extended_desc = "Nanotrasen Shopping Network interface for purchasing supplies from the cargo catalogue using a department budget account."
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
size = 10
tgui_id = "NtosCargo"
program_icon = FA_ICON_CART_FLATBED
///Are you actually placing orders with it?
var/requestonly = TRUE
///Can the tablet see or buy illegal stuff?
var/contraband = FALSE
///Is it being bought from a personal account, or is it being done via a budget/cargo?
var/self_paid = FALSE
///Can this console approve purchase requests?
var/can_approve_requests = FALSE
///What do we say when the shuttle moves with living beings on it.
var/safety_warning = "For safety and ethical reasons, the automated supply shuttle cannot transport live organisms, \
human remains, classified nuclear weaponry, mail, undelivered departmental order crates, syndicate bombs, \
homing beacons, unstable eigenstates, or machinery housing any form of artificial intelligence."
///If you're being raided by pirates, what do you tell the crew?
var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible."
///The name of the shuttle template being used as the cargo shuttle. 'cargo' is default and contains critical code. Don't change this unless you know what you're doing.
var/cargo_shuttle = "cargo"
///The docking port called when returning to the station.
var/docking_home = "cargo_home"
///The docking port called when leaving the station.
var/docking_away = "cargo_away"
///If this console can loan the cargo shuttle. Set to false to disable.
var/stationcargo = TRUE
///The account this console processes and displays. Independent from the account the shuttle processes.
var/cargo_account = ACCOUNT_CAR
/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband)
if(HAS_SILICON_ACCESS(user)) //Borgs can't buy things.
return FALSE
if(computer.obj_flags & EMAGGED)
return TRUE
else if(contraband) //Hide contrband when non-emagged.
return FALSE
if(!paccess_to_check) // No required_access, allow it.
return TRUE
if(isAdminGhostAI(user))
return TRUE
//Aquire access from the inserted ID card.
if(!length(access))
var/obj/item/card/id/D = computer?.stored_id?.GetID()
if(!D)
return FALSE
access = D.GetAccess()
if(paccess_to_check in access)
return TRUE
return FALSE
/datum/computer_file/program/budgetorders/ui_data(mob/user)
var/list/data = list()
data["location"] = SSshuttle.supply.getStatusText()
data["department"] = "Cargo"
var/datum/bank_account/buyer = SSeconomy.get_dep_account(cargo_account)
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card?.registered_account)
buyer = SSeconomy.get_dep_account(id_card?.registered_account.account_job.paycheck_department)
if((ACCESS_COMMAND in id_card.access))
requestonly = FALSE
can_approve_requests = TRUE
// If buyer is a departmental budget, replaces "Cargo" with that budget - we're not using the cargo budget here
data["department"] = "[buyer.account_holder] Requisitions"
else
requestonly = TRUE
can_approve_requests = FALSE
else
requestonly = TRUE
if(buyer)
data["points"] = buyer.account_balance
// To recap above because it's kind of a mess, here's all the options:
//Head of staff ID card: Can approve, buy, and make purchases using their own departmental budgets.
//ID card, not a head of staff: can request items from cargo using departmental budget.
//No ID card, can request items from cargo using the cargo budget.
//Otherwise static data, that is being applied in ui_data as the crates visible and buyable are not static, and are determined by inserted ID.
data["requestonly"] = requestonly
data["supplies"] = list()
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
if(P.order_flags & ORDER_INVISIBLE)
continue
if(!is_visible_pack(user, P.access_view , null, (P.order_flags & ORDER_CONTRABAND)) || (P.order_flags & ORDER_EMAG_ONLY))
continue
if(!data["supplies"][P.group])
data["supplies"][P.group] = list(
"name" = P.group,
"packs" = list()
)
if(((P.order_flags & ORDER_EMAG_ONLY) && ((P.order_flags & ORDER_CONTRABAND) && !contraband) || ((P.order_flags & ORDER_SPECIAL) && !(P.order_flags & ORDER_SPECIAL_ENABLED)) || (P.order_flags & ORDER_POD_ONLY)))
continue
var/obj/item/first_item = length(P.contains) > 0 ? P.contains[1] : null
data["supplies"][P.group]["packs"] += list(list(
"name" = P.name,
"cost" = P.get_cost(),
"id" = pack,
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
"first_item_icon" = first_item?.icon,
"first_item_icon_state" = first_item?.icon_state,
"goody" = P.order_flags & ORDER_GOODY,
"access" = P.access,
"contains" = P.get_contents_ui_data(),
))
//Data regarding the User's capability to buy things.
data["away"] = SSshuttle.supply.getDockedId() == docking_away
data["self_paid"] = self_paid
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
data["loan"] = !!SSshuttle.shuttle_loan
data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched
data["can_send"] = FALSE //There is no situation where I want the app to be able to send the shuttle AWAY from the station, but conversely is fine.
data["can_approve_requests"] = can_approve_requests
data["app_cost"] = TRUE
var/message = "Remember to stamp and send back the supply manifests."
if(SSshuttle.centcom_message)
message = SSshuttle.centcom_message
if(SSshuttle.supply_blocked)
message = blockade_warning
data["message"] = message
var/cart_list = list()
for(var/datum/supply_order/order in SSshuttle.shopping_list)
if(cart_list[order.pack.name])
cart_list[order.pack.name][1]["amount"]++
cart_list[order.pack.name][1]["cost"] += order.get_final_cost()
if(order.department_destination)
cart_list[order.pack.name][1]["dep_order"]++
if(!isnull(order.paying_account))
cart_list[order.pack.name][1]["paid"]++
continue
cart_list[order.pack.name] = list(list(
"cost_type" = order.cost_type,
"object" = order.pack.name,
"cost" = order.get_final_cost(),
"id" = order.id,
"amount" = 1,
"orderer" = order.orderer,
"paid" = !isnull(order.paying_account), //number of orders purchased privatly
"dep_order" = !!order.department_destination, //number of orders purchased by a department
"can_be_cancelled" = order.can_be_cancelled,
))
data["cart"] = list()
for(var/item_id in cart_list)
data["cart"] += cart_list[item_id]
data["requests"] = list()
for(var/datum/supply_order/order in SSshuttle.request_list)
var/datum/supply_pack/pack = order.pack
data["requests"] += list(list(
"object" = pack.name,
"cost" = pack.get_cost(),
"orderer" = order.orderer,
"reason" = order.reason,
"id" = order.id,
"account" = order.paying_account ? order.paying_account.account_holder : "Cargo Department"
))
return data
/datum/computer_file/program/budgetorders/ui_static_data(mob/user)
var/list/data = list()
data["max_order"] = CARGO_MAX_ORDER
return data
/datum/computer_file/program/budgetorders/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
. = ..()
var/mob/user = ui.user
switch(action)
if("send")
if(!SSshuttle.supply.canMove())
computer.say(safety_warning)
return
if(SSshuttle.supply_blocked)
computer.say(blockade_warning)
return
if(SSshuttle.supply.getDockedId() == docking_home)
SSshuttle.moveShuttle(cargo_shuttle, docking_away, TRUE)
computer.say("The supply shuttle is departing.")
user.investigate_log("sent the supply shuttle away.", INVESTIGATE_CARGO)
else
user.investigate_log("called the supply shuttle.", INVESTIGATE_CARGO)
computer.say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minute\s.")
SSshuttle.moveShuttle(cargo_shuttle, docking_home, TRUE)
. = TRUE
if("loan")
if(!SSshuttle.shuttle_loan)
return
if(SSshuttle.supply_blocked)
computer.say(blockade_warning)
return
else if(SSshuttle.supply.mode != SHUTTLE_IDLE)
return
else if(SSshuttle.supply.getDockedId() != docking_away)
return
else if(stationcargo != TRUE)
return
else
SSshuttle.shuttle_loan.loan_shuttle()
computer.say("The supply shuttle has been loaned to CentCom.")
user.investigate_log("accepted a shuttle loan event.", INVESTIGATE_CARGO)
user.log_message("accepted a shuttle loan event.", LOG_GAME)
. = TRUE
if("add")
var/id = text2path(params["id"])
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
if(!istype(pack))
return
if((pack.order_flags & (ORDER_EMAG_ONLY | ORDER_POD_ONLY | ORDER_CONTRABAND)) || ((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)))
return
var/name = "*None Provided*"
var/rank = "*None Provided*"
var/ckey = user.ckey
var/mob/living/carbon/human/hwoman
if(ishuman(user))
hwoman = user
rank = hwoman.get_assignment(hand_first = TRUE)
else if(issilicon(user))
name = user.real_name
rank = "Silicon"
// Our account that we want to end up paying with.
var/datum/bank_account/account
// Our ID card that we want to pull from for identification. Modifies either name, account, or neither depending on function.
var/obj/item/card/id/id_card_customer = computer.stored_id?.GetID()
if(!id_card_customer)
id_card_customer = hwoman?.get_idcard(TRUE) //Grab from hands/mob if there's no id_card slot to prioritize.
name = id_card_customer?.registered_account.account_holder
if(self_paid)
if(!istype(id_card_customer))
computer.say("No ID card detected.")
return
if(IS_DEPARTMENTAL_CARD(id_card_customer))
computer.say("[id_card_customer] cannot be used to make purchases.")
return
account = id_card_customer.registered_account
name = id_card_customer.registered_account.account_holder
if(!istype(account))
computer.say("Invalid bank account.")
return
var/reason = ""
var/datum/bank_account/personal_department
if((requestonly && !self_paid) || !(computer.stored_id?.GetID()))
reason = tgui_input_text(user, "Reason", name, max_length = MAX_MESSAGE_LEN)
if(isnull(reason) || ..())
return
if(id_card_customer?.registered_account?.account_job && !self_paid) //Find a budget to pull from
personal_department = SSeconomy.get_dep_account(id_card_customer.registered_account.account_job.paycheck_department)
if(!(personal_department.account_holder == "Cargo Budget"))
var/dept_choice = tgui_alert(user, "Which department are you requesting this for?", "Choose request department", list("Cargo Budget", "[personal_department.account_holder]"))
if(!dept_choice)
return
if(dept_choice == "Cargo Budget")
personal_department = null
if((pack.order_flags & ORDER_GOODY) && !self_paid)
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
computer.say("ERROR: Small crates may only be purchased by private accounts.")
return
if(SSshuttle.supply.get_order_count(pack) == OVER_ORDER_LIMIT)
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
computer.say("ERROR: No more then [CARGO_MAX_ORDER] of any pack may be ordered at once")
return
if(!self_paid)
account = personal_department
var/turf/T = get_turf(computer)
var/datum/supply_order/SO = new(pack, name, rank, ckey, reason, account)
SO.generateRequisition(T)
if((requestonly && !self_paid) || !(computer.stored_id?.GetID()))
SSshuttle.request_list += SO
else
SSshuttle.shopping_list += SO
if(self_paid)
computer.say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.")
playsound(computer, 'sound/effects/coin2.ogg', 40, TRUE)
. = TRUE
if("remove")
var/id = text2num(params["id"])
for(var/datum/supply_order/SO in SSshuttle.shopping_list)
if(SO.id == id)
SSshuttle.shopping_list -= SO
. = TRUE
break
if("clear")
for(var/datum/supply_order/cancelled_order in SSshuttle.shopping_list)
if(cancelled_order.department_destination || cancelled_order.can_be_cancelled)
continue //don't cancel other department's orders or orders that can't be cancelled
SSshuttle.shopping_list -= cancelled_order
. = TRUE
if("approve")
var/id = text2num(params["id"])
for(var/datum/supply_order/SO in SSshuttle.request_list)
if(SO.id == id)
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card && id_card?.registered_account)
SO.paying_account = SSeconomy.get_dep_account(id_card?.registered_account?.account_job.paycheck_department)
SSshuttle.request_list -= SO
SSshuttle.shopping_list += SO
. = TRUE
break
if("deny")
var/id = text2num(params["id"])
for(var/datum/supply_order/SO in SSshuttle.request_list)
if(SO.id == id)
SSshuttle.request_list -= SO
. = TRUE
break
if("denyall")
SSshuttle.request_list.Cut()
. = TRUE
if("toggleprivate")
self_paid = !self_paid
. = TRUE
if(.)
post_signal(cargo_shuttle)
/datum/computer_file/program/budgetorders/proc/post_signal(command)
var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
if(!frequency)
return
var/datum/signal/status_signal = new(list("command" = command))
frequency.post_signal(src, status_signal)