From c89f4fb977f225014dbb0f7161e332439ddfa3d1 Mon Sep 17 00:00:00 2001
From: Casper3667 <8396443+Casper3667@users.noreply.github.com>
Date: Thu, 26 Mar 2026 21:57:43 +0100
Subject: [PATCH] Combines the quikpay system into a datum, adds categories and
more commissary things (#22055)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This fixes a couple of issues with the quikpay program, and makes it
into a datum that can be put into anything.
The ordering terminal the galley has now uses the quikpay program as
well.
The commissary now has a wall terminal they can enable and disable at
need to allow people to purchase things while the shop is unattended.
Also a small expansion to the commissary.
Food and drink fridges can be renamed with a pen.
All commissary shelves now have a message when an item are taken from
them.
The frozen meal vending machine was also readded behind the café.
---
aurorastation.dme | 1 +
code/__HELPERS/price.dm | 73 ++-
code/datums/components/commissaryShop.dm | 434 +++++++++++++++
code/modules/cooking/machinery/commissary.dm | 380 ++++---------
code/modules/cooking/machinery/smartfridge.dm | 24 +-
code/modules/economy/OrderTerminal.dm | 165 +-----
code/modules/economy/quikpay.dm | 259 +--------
html/changelogs/CommissaryChanges.yml | 13 +
icons/obj/machinery/wall/terminals.dmi | Bin 42785 -> 48785 bytes
maps/sccv_horizon/sccv_horizon.dmm | 512 ++++++++++--------
tgui/packages/tgui/interfaces/QuikPay.tsx | 96 ++--
11 files changed, 1001 insertions(+), 956 deletions(-)
create mode 100644 code/datums/components/commissaryShop.dm
create mode 100644 html/changelogs/CommissaryChanges.yml
diff --git a/aurorastation.dme b/aurorastation.dme
index 611dbec244e..a8f148a0ceb 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -484,6 +484,7 @@
#include "code\datums\weakrefs.dm"
#include "code\datums\changelog\changelog.dm"
#include "code\datums\components\_component.dm"
+#include "code\datums\components\commissaryShop.dm"
#include "code\datums\components\connect_mob_behalf.dm"
#include "code\datums\components\drift.dm"
#include "code\datums\components\jukebox.dm"
diff --git a/code/__HELPERS/price.dm b/code/__HELPERS/price.dm
index 3a5331f3bed..50f53df1d76 100644
--- a/code/__HELPERS/price.dm
+++ b/code/__HELPERS/price.dm
@@ -11,35 +11,73 @@
/proc/read_paper_price_list(var/obj/item/paper/R)
var/text = R.info
- // Split on new line
+ // Split on line breaks
var/list/lines = splittext(text, "
")
var/list/result = list()
- // Skip a header line
+ // Skip header line
for(var/i = 2; i <= lines.len; i++)
- var/line = lines[i]
+ var/line = trim(lines[i])
if(!length(line))
continue
- // Split the name and price
var/list/split_input = splittext(line, ";")
- if(split_input.len < 2)
+ // category;name;price
+ if(length(split_input) >= 3)
+ var/category = trim(split_input[1])
+ var/name = trim(split_input[2])
+ var/price_text = trim(split_input[3])
+
+ if(!length(category))
+ category = "Uncategorized"
+ if(!length(name))
+ continue
+
+ var/price = text2num(price_text)
+
+ // Reject null prices
+ if(isnull(price))
+ continue
+
+ // Reject invalid prices
+ if(price == 0 && price_text != "0" && price_text != "0.0" && price_text != "0.00")
+ continue
+
+ result += list(list(
+ "name" = name,
+ "price" = price,
+ "category" = category
+ ))
continue
- var/name = split_input[1]
- var/price_text = split_input[2]
+ // name;price
+ if(length(split_input) >= 2)
+ var/name = trim(split_input[1])
+ var/price_text = trim(split_input[2])
- var/price = text2num(price_text)
+ if(!length(name))
+ continue
- // In case of invalid prices for some reason
- if(price == 0 && price_text != "0" && price_text != "0.0")
- continue
+ var/price = text2num(price_text)
- result += list(list("name" = name, "price" = price))
+ // Reject null prices
+ if(isnull(price))
+ continue
+
+ // Reject invalid prices
+ if(price == 0 && price_text != "0" && price_text != "0.0" && price_text != "0.00" && price != null)
+ continue
+
+ result += list(list(
+ "name" = name,
+ "price" = price,
+ "category" = "Uncategorized"
+ ))
return result
+
// Prints the prices from a register/quikpay into a list that can be used for read_paper_price_list()
/proc/print_price_to_paper(var/shop_name, var/list/items, var/paper_loc, mob/user)
if(!items || !items.len)
@@ -47,12 +85,17 @@
var/obj/item/paper/notepad/receipt/R = new(paper_loc)
var/title = "Price List: [shop_name]"
- var/text = "name;price
"
+ var/text = "category;name;price
"
for(var/list/L in items)
- var/item_name = L["name"]
+ var/item_category = sanitize_tg(L["category"])
+ var/item_name = sanitize_tg(L["name"])
var/item_price = L["price"]
- text += "[item_name];[round(item_price, 0.01)]
"
+
+ if(!length(item_category))
+ item_category = "Uncategorized"
+
+ text += "[item_category];[item_name];[round(item_price, 0.01)]
"
R.set_content(title, text)
diff --git a/code/datums/components/commissaryShop.dm b/code/datums/components/commissaryShop.dm
new file mode 100644
index 00000000000..6227cdcea27
--- /dev/null
+++ b/code/datums/components/commissaryShop.dm
@@ -0,0 +1,434 @@
+/datum/component/quikpay_shop
+ /// The id associated with the quikpay system. Automatically assigned.
+ var/machine_id = ""
+ /// The Items for sale, containing name and price
+ var/list/items = list()
+ /// The items in the purchase basket, containing name, price and amount
+ var/list/buying = list()
+ /// The input field for new item names
+ var/new_item = ""
+ /// The input field for new item prices
+ var/new_price = 0
+ /// The input field for new item categories
+ var/new_category = ""
+ /// The transaction total for a purchase, 0 when there is no transaction ongoing
+ var/sum = 0
+ /// If the shop can be changed or not, to add new items or change destination account
+ var/editmode = FALSE
+ /// Receipt printing info
+ var/receipt = ""
+ /// The account to receive funds from card transactions
+ var/destinationact = "Operations"
+ /// The credits within the shop object
+ var/credit = 100
+ /// The name used for the shop
+ var/shop_name = "Commissary"
+ /// The owner object, also known as parent. Defined to easily do obj specific procs
+ var/obj/owner
+ /// If it is possible to pay with physical credits or only card
+ var/can_use_credits = TRUE
+ /// The the longer version of the shop name
+ var/shop_long_name = "Idris Quik-Pay Register"
+ /// The access types that can configure the shop
+ var/req_one_access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO)
+
+/datum/component/quikpay_shop/quikpay
+ shop_name = "Quik-Pay"
+ destinationact = "Service"
+ shop_long_name = "Quik-Pay device"
+ can_use_credits = FALSE
+
+/// Add an item by clicking on it with the quikpay
+/datum/component/quikpay_shop/quikpay/proc/add_item(atom/target, mob/user)
+ if(!isobj(target))
+ return
+ if (!editmode)
+ to_chat(user, SPAN_NOTICE("Unlock \the [owner] to add items."))
+ return
+
+ var/obj/O = target
+ var/name_guess = O.name
+ var/price_guess = 0
+ var/category_guess = ""
+
+ price_guess = text2num(tgui_input_text(user, "Set price for [name_guess]:", "[shop_long_name]", 0, 10))
+ if(isnull(price_guess) || price_guess == 0)
+ return
+ price_guess = max(0.01, round(price_guess, 0.01))
+
+ category_guess = tgui_input_text(user, "Set category for [name_guess]:", "[shop_long_name]", "Uncategorized", 32)
+ if(isnull(category_guess) || !length(category_guess))
+ category_guess = "Uncategorized"
+
+ items += list(list(
+ "name" = "[name_guess]",
+ "price" = price_guess,
+ "category" = "[category_guess]"
+ ))
+
+ to_chat(user, SPAN_NOTICE("[owner]: added '[name_guess]' for [price_guess] in category '[category_guess]'."))
+ return TRUE
+
+
+/datum/component/quikpay_shop/Initialize(access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO), destination = "Operations")
+ . = ..()
+ machine_id = "[station_name()] [shop_long_name] #[SSeconomy.num_financial_terminals++]"
+ if(!isobj(parent))
+ return
+ req_one_access = access
+ destinationact = destination
+ owner = parent
+
+/// Put credits in or take them out, assuming the device has credits
+/datum/component/quikpay_shop/proc/take_give_credits(mob/user)
+ if(!can_use_credits)
+ return
+ var/item = user.get_active_hand()
+
+ if(istype(item, /obj/item/spacecash) && !istype(item, /obj/item/spacecash/ewallet))
+ var/obj/item/spacecash/cashmoney = item
+ credit += cashmoney.worth
+ user.drop_from_inventory(cashmoney,get_turf(owner))
+ user.visible_message("\The [user] inserts some credits into \the [owner]." )
+ qdel(cashmoney)
+ return
+
+ var/obj/item/card/id/I = user.GetIdCard()
+ if(istype(I) && has_access(req_one_access = src.req_one_access, accesses = I.access))
+ var/price_guess = text2num(tgui_input_text(user, "How much do you wish to withdraw? Remaining credits: [credit]电", "Quik-Pay", 0, 10))
+ if(isnull(price_guess) || price_guess == 0)
+ return
+ price_guess = max(0, round(price_guess, 0.01))
+ if(credit >= price_guess)
+ spawn_money(price_guess, owner.loc, user)
+ credit = max(0, credit - price_guess)
+ user.visible_message("\The [user] remove some credits from \the [owner].", "You hear a drawer being opened and the clinking of coins, followed by a drawer being closed." )
+
+/// Print out a relevant receipt
+/datum/component/quikpay_shop/proc/print_receipt()
+ var/obj/item/paper/notepad/receipt/R = new(owner.loc)
+ var/receiptname = "Receipt: [machine_id]"
+ R.set_content_unsafe(receiptname, receipt, sum)
+ stamp_receipt(R)
+ usr.put_in_any_hand_if_possible(R)
+
+/// Interact with an object. Papers or payment pethods
+/datum/component/quikpay_shop/proc/interact_object(obj/item/attacking_item, mob/user)
+ if(istype(attacking_item, /obj/item/paper))
+ read_paper_list(attacking_item, user)
+ return
+ if(sum == 0)
+ return
+ if (istype(attacking_item, /obj/item/spacecash/ewallet))
+ card_pay(attacking_item, user)
+ return
+ else if (istype(attacking_item, /obj/item/card/id))
+ ID_pay(attacking_item, user)
+ return
+ else if(istype(attacking_item, /obj/item/spacecash) && can_use_credits)
+ cash_pay(attacking_item, user)
+ return
+
+/// Paying with cash
+/datum/component/quikpay_shop/proc/cash_pay(obj/item/spacecash/cashmoney, mob/user)
+ if(!can_use_credits)
+ return
+ var/transaction_amount = sum
+ if(transaction_amount > cashmoney.worth)
+ to_chat(user, SPAN_WARNING("[icon2html(cashmoney, user)] That is not enough money."))
+ return FALSE
+ if(istype(cashmoney, /obj/item/spacecash/bundle))
+ user.visible_message(SPAN_INFO("\The [user] inserts some cash into \the [owner]."))
+ var/obj/item/spacecash/bundle/cashmoney_bundle = cashmoney
+ cashmoney_bundle.worth -= transaction_amount
+
+ if(cashmoney_bundle.worth <= 0)
+ usr.drop_from_inventory(cashmoney_bundle,get_turf(owner))
+ qdel(cashmoney_bundle)
+ else
+ cashmoney_bundle.update_icon()
+ else
+ user.visible_message(SPAN_INFO("\The [user] inserts a bill into \the [owner]."))
+ var/left = cashmoney.worth - transaction_amount
+ user.drop_from_inventory(cashmoney,get_turf(owner))
+ qdel(cashmoney)
+
+ if(left)
+ spawn_money(left, get_turf(user), user)
+ credit += transaction_amount
+ print_receipt()
+ clear_order()
+
+/// Paying with an id card
+/datum/component/quikpay_shop/proc/ID_pay(obj/item/attacking_item, mob/user)
+ var/obj/item/card/id/I = attacking_item.GetID()
+ var/transaction_amount = sum
+ var/transaction_purpose = "[destinationact] Payment"
+ var/transaction_terminal = machine_id
+
+ var/transaction = SSeconomy.transfer_money(I.associated_account_number, SSeconomy.get_department_account(destinationact)?.account_number,transaction_purpose,transaction_terminal,transaction_amount,null,usr)
+
+ if(transaction)
+ to_chat(user, SPAN_NOTICE("[icon2html(owner, user)][transaction]."))
+ else
+ end_transaction(user)
+
+/// Paying with a charge card
+/datum/component/quikpay_shop/proc/card_pay(obj/item/attacking_item, mob/user)
+ var/obj/item/spacecash/ewallet/E = attacking_item
+ var/transaction_amount = sum
+ var/transaction_purpose = "[destinationact] Payment"
+ var/transaction_terminal = machine_id
+
+ if(transaction_amount <= E.worth)
+ SSeconomy.charge_to_account(SSeconomy.get_department_account(destinationact)?.account_number, E.owner_name, transaction_purpose, transaction_terminal, transaction_amount)
+ E.worth -= transaction_amount
+
+ end_transaction(user)
+ else if (transaction_amount > E.worth)
+ to_chat(user, SPAN_WARNING("[icon2html(owner, user)]\The [E] doesn't have that much money!"))
+
+/// Read a paper to get data
+/datum/component/quikpay_shop/proc/read_paper_list(obj/item/paper/R, mob/user)
+ if(!editmode)
+ owner.balloon_alert(user, "device locked!")
+ return FALSE
+
+ var/result = read_paper_price_list(R)
+ for(var/item in result)
+ items += list(list(
+ "name" = item["name"],
+ "price" = item["price"],
+ "category" = item["category"] || "Uncategorized"
+ ))
+ owner.balloon_alert(user, "device set!")
+ return TRUE
+
+/// Handles receipt creation
+/datum/component/quikpay_shop/proc/buying_receipt(mob/user)
+ receipt = ""
+ sum = 0
+ var/obj/item/card/id/id_card = user.GetIdCard()
+ var/cashier = id_card ? id_card.registered_name : "Unknown"
+ receipt = "
[shop_name] receipt
Today's date: [worlddate2text()]
Cashier: [cashier]
Purchased items:"
+ for(var/list/bought_item in buying)
+ var/item_name = bought_item["name"]
+ var/item_amount = bought_item["amount"]
+ var/item_price = bought_item["price"]
+
+ receipt += "- [item_name]: [item_amount] x [item_price]电: [item_amount * item_price]电
"
+ sum += item_price * item_amount
+
+ receipt += "
Total: [sum]电
"
+
+/datum/component/quikpay_shop/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "QuikPay", "[shop_long_name]", 550, 550)
+ ui.open()
+
+/datum/component/quikpay_shop/ui_data(mob/user)
+ var/list/data = list()
+
+ data["items"] = items
+ data["buying"] = buying
+ data["sum"] = sum
+ data["new_item"] = new_item
+ data["new_price"] = new_price
+ data["new_category"] = new_category
+ data["editmode"] = editmode
+ data["destinationact"] = destinationact
+
+ return data
+
+/datum/component/quikpay_shop/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("add")
+ if(!editmode)
+ owner.balloon_alert(usr, "device locked!")
+ return FALSE
+
+ if(!length(new_item))
+ return FALSE
+
+ if(!length(new_category))
+ new_category = "Uncategorized"
+
+ for(var/list/L in items)
+ if(L["name"] == new_item)
+ return FALSE
+
+ items += list(list(
+ "name" = new_item,
+ "price" = new_price,
+ "category" = new_category
+ ))
+
+ new_item = ""
+ . = TRUE
+
+ if("remove")
+ if(!editmode)
+ owner.balloon_alert(usr, "device locked!")
+ return FALSE
+ var/index = 0
+ for(var/list/L in items)
+ index++
+ if(L["name"] == params["removing"])
+ items.Cut(index, index+1)
+ break
+ . = TRUE
+
+ if("set_new_price")
+ new_price = params["set_new_price"]
+ . = TRUE
+
+ if("set_new_item")
+ new_item = params["set_new_item"]
+ . = TRUE
+
+ if("set_new_category")
+ new_category = params["set_new_category"]
+ . = TRUE
+
+ if("clear")
+ clear_order()
+ . = TRUE
+
+ if("buy")
+ for(var/list/L in buying)
+ if(L["name"] == params["buying"])
+ L["amount"]++
+ return TRUE
+ buying += list(list("name" = sanitize_tg(params["buying"]), "amount" = params["amount"], "price" = params["price"]))
+
+ if("removal")
+ var/index = 0
+ for(var/list/L in buying)
+ index++
+ if(L["name"] == params["removal"])
+ if(L["amount"] > 1)
+ L["amount"]--
+ else
+ buying.Cut(index, index+1)
+ break
+ . = TRUE
+
+ if("confirm")
+ buying_receipt(usr)
+ playsound(owner, 'sound/machines/ping.ogg', 25, TRUE)
+ owner.audible_message(SPAN_NOTICE("[icon2html(owner, viewers(get_turf(owner)))] \The [owner] pings."))
+ . = TRUE
+
+ if("locking")
+ if(editmode)
+ editmode = FALSE
+ owner.balloon_alert(usr, "device locked!")
+ else
+ if(!editmode)
+ var/obj/item/card/id/I = usr.GetIdCard()
+ if(!istype(I))
+ return
+ if(!has_access(req_one_access = src.req_one_access, accesses = I.access))
+ owner.balloon_alert(usr, "no access!")
+ return
+ editmode = !editmode
+ owner.balloon_alert(usr, "device [editmode ? "un" : ""]locked")
+ . = TRUE
+
+ if("accountselect")
+ if(!editmode)
+ owner.balloon_alert(usr, "device locked!")
+ return FALSE
+
+ var/dest = tgui_input_list(usr, "What account would you like to select?", "Destination Account", assoc_to_keys(SSeconomy.department_accounts))
+ if(!dest)
+ return FALSE
+ destinationact = dest
+ . = TRUE
+
+ if("print_dsv")
+ if(!editmode)
+ owner.balloon_alert(usr, "device locked!")
+ return FALSE
+ print_price_to_paper(shop_name, items, owner.loc, usr)
+ . = TRUE
+
+/// Clear the order from the selection
+/datum/component/quikpay_shop/proc/clear_order()
+ buying.Cut()
+ sum = 0
+ receipt = ""
+
+/// Ends the transaction by using a card, giving a message
+/datum/component/quikpay_shop/proc/end_transaction(mob/user)
+ user.visible_message("\The [user] swipes a card on \the [owner]." )
+ owner.audible_message(SPAN_NOTICE("[icon2html(owner, viewers(get_turf(owner)))] \The [owner] chimes."))
+ playsound(owner, 'sound/machines/chime.ogg', 50, TRUE)
+ print_receipt()
+ to_chat(user, SPAN_NOTICE("Transaction completed, please return to the home screen."))
+ clear_order()
+
+/datum/component/quikpay_shop/orderterminal
+ shop_name = "Commissary"
+ shop_long_name = "Self-serve Shop Teller"
+ can_use_credits = FALSE
+
+/datum/component/quikpay_shop/orderterminal/food
+ var/ticket = ""
+ var/ticket_number = 1
+ shop_name = "Service terminal"
+ shop_long_name = "Idris Food Terminal"
+
+/datum/component/quikpay_shop/orderterminal/food/buying_receipt(mob/user)
+ ticket = ""
+ receipt = ""
+ sum = 0
+ receipt += "[shop_long_name] Receipt
"
+ ticket += "[shop_long_name] Ticket
"
+ for(var/list/bought_item in buying)
+ var/item_name = bought_item["name"]
+ var/item_amount = bought_item["amount"]
+ var/item_price = bought_item["price"]
+ sum += item_price * item_amount
+
+ receipt += "[item_name]: [item_amount] x [item_price]电: [item_amount * item_price]电
"
+ ticket += "[item_name]: [item_amount] x [item_price]电: [item_amount * item_price]电
"
+ receipt += "
Total: [sum]电"
+ ticket += "
Total: [sum]电"
+ sum = sum
+
+/// Print the receipt followed by the order ticket
+/datum/component/quikpay_shop/orderterminal/food/print_receipt()
+ var/obj/item/card/id/id_card
+ if(ishuman(usr))
+ id_card = usr.GetIdCard()
+ ticket += "
Customer: [id_card ? id_card.registered_name : "Unknown"]"
+ receipt += "
Customer: [id_card ? id_card.registered_name : "Unknown"]"
+ var/obj/item/paper/notepad/receipt/R = new(owner.loc)
+ var/receiptname = "Receipt: [machine_id]"
+ R.set_content_unsafe(receiptname, receipt, sum)
+ stamp_receipt(R)
+ usr.put_in_any_hand_if_possible(R)
+ // And now we do it but for the ticket.
+ var/obj/item/paper/notepad/receipt/T = new(owner.loc)
+ var/tickettname = "Order ticket: [ticket_number]"
+ ticket_number++
+ T.set_content_unsafe(tickettname, ticket, sum)
+ stamp_receipt(T)
+ usr.put_in_any_hand_if_possible(T)
+ ticket = ""
+ receipt = ""
+
+/datum/component/quikpay_shop/proc/stamp_receipt(obj/item/paper/R) // Stamps the papers, made into a proc to avoid copy pasting too much
+ var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
+ stampoverlay.icon_state = "paper_stamp-hop"
+ if(!R.stamped)
+ R.stamped = new
+ R.stamped += /obj/item/stamp
+ R.AddOverlays(stampoverlay)
+ R.stamps += "
This paper has been stamped by \the [owner]."
+ R.ripped = TRUE
diff --git a/code/modules/cooking/machinery/commissary.dm b/code/modules/cooking/machinery/commissary.dm
index dba823d2beb..bb89d7ca255 100644
--- a/code/modules/cooking/machinery/commissary.dm
+++ b/code/modules/cooking/machinery/commissary.dm
@@ -41,6 +41,7 @@
display_tiers = 5
display_tier_amt = 3
has_emissive = FALSE
+ visible_takeout = TRUE
/obj/machinery/smartfridge/tradeshelf/clothing
name = "clothing shelf"
@@ -87,6 +88,17 @@
display_tier_amt = 5
has_emissive = TRUE
+/obj/machinery/smartfridge/tradeshelf/food/attackby(obj/item/attacking_item, mob/user)
+ if(attacking_item.tool_behaviour == TOOL_PEN)
+ name_fridge(user)
+ return
+ . = ..()
+
+/obj/machinery/smartfridge/tradeshelf/food/proc/name_fridge(mob/user)
+ var/newname = tgui_input_text(user, "What would you like to rename the fridge? Note that fridge will be appended to the end of it", "Fridge name")
+ if(newname)
+ name = newname + " fridge"
+
/obj/machinery/smartfridge/tradeshelf/toy
name = "toy shelf"
desc = "A commercialized shelf for toys and associated items."
@@ -139,308 +151,119 @@
// -------------------------------------------------
/obj/structure/cash_register/commissary
- var/machine_id = ""
- var/list/items = list()
- var/list/items_to_price = list()
- var/list/buying = list()
- var/new_item = ""
- var/new_price = 0
- var/sum = 0
- var/editmode = FALSE
- var/receipt = ""
- var/destinationact = "Operations"
- var/credit = 100
- var/shop_name = "Commissary"
storage_type = null
+ req_one_access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO)
+ var/destination = "Operations"
/obj/structure/cash_register/commissary/mechanics_hints(mob/user, distance, is_adjacent)
. = list()
- . += "Alt click with a command id in hand, to gain command access."
- . += "Alt click with credits in hand, to deposit them."
- . += "Alt click while having operations access, to withdraw credits from it."
+ . += "Alt-click with credits in hand, to deposit them."
+ . += "Alt-click while having the proper access, to withdraw credits from it."
. += "Items can be paid for with id cards, charge cards or physical credits, and a receipt will be printed."
. += "The register can print a paper which can be used to quickly fill it out in the future by using it on the register."
/obj/structure/cash_register/commissary/Initialize()
. = ..()
- machine_id = "[station_name()] Idris Quik-Pay Register #[SSeconomy.num_financial_terminals++]"
+ src.LoadComponent(/datum/component/quikpay_shop, req_one_access, destination)
/obj/structure/cash_register/commissary/AltClick(var/mob/user)
- var/item = user.get_active_hand()
- var/obj/item/card/id/I = item
- if(istype(I) && (ACCESS_HEADS in I.access))
- if(ACCESS_HEADS in I.access)
- editmode = TRUE
- to_chat(user, SPAN_NOTICE("Command access granted."))
- SStgui.update_uis(src)
+ . = ..()
+ var/datum/component/quikpay_shop/qp_shop = src.GetComponent(/datum/component/quikpay_shop)
+ if(!qp_shop)
return
- if(istype(item, /obj/item/spacecash) && !istype(item, /obj/item/spacecash/ewallet))
- var/obj/item/spacecash/cashmoney = item
- credit += cashmoney.worth
- user.drop_from_inventory(cashmoney,get_turf(src))
- visible_message("\The [user] inserts some credits into \the [src]." )
- qdel(cashmoney)
- return
- I = user.GetIdCard()
- if(istype(I) && (ACCESS_CARGO in I.access))
- var/price_guess = text2num(sanitizeSafe( tgui_input_text(user, "How much do you wish to withdraw? Remaining credits: [credit]电", "QuikPay", 0, 10), 10))
- if(isnull(price_guess) || price_guess == 0)
- return
- price_guess = max(0, round(price_guess, 0.01))
- if(credit >= price_guess)
- spawn_money(price_guess, loc, user)
- credit -= price_guess
- visible_message("\The [user] remove some credits from \the [src]." )
- return
-
-/obj/structure/cash_register/commissary/proc/print_receipt()
- var/obj/item/paper/notepad/receipt/R = new(loc)
- var/receiptname = "Receipt: [machine_id]"
- R.set_content_unsafe(receiptname, receipt, sum)
-
- //stamp the paper
- var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
- stampoverlay.icon_state = "paper_stamp-hop"
- if(!R.stamped)
- R.stamped = new
- R.stamped += /obj/item/stamp
- R.AddOverlays(stampoverlay)
- R.stamps += "
This paper has been stamped by the Idris Quik-Pay Register."
- usr.put_in_any_hand_if_possible(R)
- R.ripped = TRUE
+ qp_shop.take_give_credits(user, loc)
/obj/structure/cash_register/commissary/attackby(obj/item/attacking_item, mob/user)
- if(istype(attacking_item, /obj/item/paper))
- read_paper_list(attacking_item, user)
+ . = ..()
+ var/datum/component/quikpay_shop/qp_shop = src.GetComponent(/datum/component/quikpay_shop)
+ if(!qp_shop)
return
- if(sum == 0)
- return
- if (istype(attacking_item, /obj/item/spacecash/ewallet))
- card_pay(attacking_item, user)
- return
- else if (istype(attacking_item, /obj/item/card/id))
- ID_pay(attacking_item, user)
- return
- else if(istype(attacking_item, /obj/item/spacecash))
- cash_pay(attacking_item, user)
- return
-
-/obj/structure/cash_register/commissary/proc/cash_pay(obj/item/spacecash/cashmoney, mob/user)
- var/transaction_amount = sum
- if(transaction_amount > cashmoney.worth)
- to_chat(user, SPAN_WARNING("[icon2html(cashmoney, user)] That is not enough money."))
- return 0
- if(istype(cashmoney, /obj/item/spacecash/bundle))
- visible_message(SPAN_INFO("\The [user] inserts some cash into \the [src]."))
- var/obj/item/spacecash/bundle/cashmoney_bundle = cashmoney
- cashmoney_bundle.worth -= transaction_amount
-
- if(cashmoney_bundle.worth <= 0)
- usr.drop_from_inventory(cashmoney_bundle,get_turf(src))
- qdel(cashmoney_bundle)
- else
- cashmoney_bundle.update_icon()
- else
- visible_message(SPAN_INFO("\The [user] inserts a bill into \the [src]."))
- var/left = cashmoney.worth - transaction_amount
- user.drop_from_inventory(cashmoney,get_turf(src))
- qdel(cashmoney)
-
- if(left)
- spawn_money(left, get_turf(user), user)
- credit += transaction_amount
- print_receipt()
- clear_order()
- return 1
-
-/obj/structure/cash_register/commissary/proc/ID_pay(obj/item/attacking_item, mob/user)
- var/obj/item/card/id/I = attacking_item.GetID()
- var/transaction_amount = sum
- var/transaction_purpose = "[destinationact] Payment"
- var/transaction_terminal = machine_id
-
- var/transaction = SSeconomy.transfer_money(I.associated_account_number, SSeconomy.get_department_account(destinationact)?.account_number,transaction_purpose,transaction_terminal,transaction_amount,null,usr)
-
- if(transaction)
- to_chat(user, SPAN_NOTICE("[icon2html(src, user)][transaction]."))
- else
- visible_message("\The [user] swipes a card on \the [src]." )
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] chimes."))
- playsound(src, 'sound/machines/chime.ogg', 50, 1)
- print_receipt()
- sum = 0
- receipt = ""
- to_chat(user, SPAN_NOTICE("Transaction completed, please return to the home screen."))
- clear_order()
-
-/obj/structure/cash_register/commissary/proc/card_pay(obj/item/attacking_item, mob/user)
- var/obj/item/spacecash/ewallet/E = attacking_item
- var/transaction_amount = sum
- var/transaction_purpose = "[destinationact] Payment"
- var/transaction_terminal = machine_id
-
- if(transaction_amount <= E.worth)
- SSeconomy.charge_to_account(SSeconomy.get_department_account(destinationact)?.account_number, E.owner_name, transaction_purpose, transaction_terminal, transaction_amount)
- E.worth -= transaction_amount
-
- visible_message("\The [user] swipes a card on \the [src]." )
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] chimes."))
- playsound(src, 'sound/machines/chime.ogg', 50, 1)
- print_receipt()
- sum = 0
- receipt = ""
- to_chat(user, SPAN_NOTICE("Transaction completed, please return to the home screen."))
- clear_order()
- else if (transaction_amount > E.worth)
- to_chat(user, SPAN_WARNING("[icon2html(src, user)]\The [E] doesn't have that much money!"))
- return
-
-/obj/structure/cash_register/commissary/proc/read_paper_list(obj/item/paper/R, mob/user)
- if(!editmode)
- balloon_alert(user, "device locked!")
- return FALSE
- var/result = read_paper_price_list(R)
- for(var/item in result)
- items += list(list("name" = item["name"], "price" = item["price"]))
- items_to_price[item["name"]] += item["price"]
-
-/obj/structure/cash_register/commissary/proc/print_price(mob/user)
- return print_price_to_paper(shop_name, items, loc, user)
+ qp_shop.interact_object(attacking_item, user)
/obj/structure/cash_register/commissary/attack_hand(mob/living/user)
. = ..()
- ui_interact(user)
-
-/obj/structure/cash_register/commissary/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "QuikPay", "Idris Quik-Pay Register", 550, 550)
- ui.open()
-
-/obj/structure/cash_register/commissary/ui_data(var/mob/user)
- var/list/data = list()
-
- data["items"] = items
- data["buying"] = buying
- data["sum"] = sum
- data["new_item"] = new_item
- data["new_price"] = new_price
- data["editmode"] = editmode
- data["destinationact"] = destinationact
-
- return data
-
-/obj/structure/cash_register/commissary/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
- . = ..()
- if(.)
+ var/datum/component/quikpay_shop/qp_shop = src.GetComponent(/datum/component/quikpay_shop)
+ if(!qp_shop)
return
- switch(action)
- if("add")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
+ qp_shop.ui_interact(user)
- items += list(list("name" = new_item, "price" = new_price))
- items_to_price[new_item] = new_price
- . = TRUE
+/obj/machinery/commissary_wall_shop
+ name = "self-serve shop teller"
+ desc = "An ordering terminal designed by Idris for quicker expedition."
+ icon = 'icons/obj/machinery/wall/terminals.dmi'
+ icon_state = "orderterminal"
+ idle_power_usage = 10
+ anchored = TRUE
+ var/turned_on = FALSE
+ req_one_access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO)
+ var/destination = "Operations"
- if("remove")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
- var/index = 0
- for(var/list/L in items)
- index++
- if(L["name"] == params["removing"])
- items.Cut(index, index+1)
- . = TRUE
+/obj/machinery/commissary_wall_shop/mechanics_hints(mob/user, distance, is_adjacent)
+ . += ..()
+ . += "Items can be paid for with id cards or charge cards, and a receipt will be printed."
+ . += "The terminal can print a paper which can be used to quickly fill it out in the future by using it on the register."
+ . += "With the proper access, ctrl-click to turn the machine on and off."
- if("set_new_price")
- new_price = params["set_new_price"]
- . = TRUE
+/obj/machinery/commissary_wall_shop/Initialize()
+ . = ..()
+ src.LoadComponent(/datum/component/quikpay_shop/orderterminal, req_one_access, destination)
+ update_icon()
- if("set_new_item")
- new_item = params["set_new_item"]
- . = TRUE
+/obj/machinery/commissary_wall_shop/attackby(obj/item/attacking_item, mob/user)
+ if(!turned_on)
+ balloon_alert(user, "turned off")
+ return
+ if(stat & NOPOWER)
+ balloon_alert(user, "no power")
+ return
+ var/datum/component/quikpay_shop/orderterminal/qp_shop = src.GetComponent(/datum/component/quikpay_shop/orderterminal)
+ if(!qp_shop)
+ return
+ qp_shop.interact_object(attacking_item, user)
- if("clear")
- clear_order()
- . = TRUE
+/obj/machinery/commissary_wall_shop/attack_hand(mob/living/user)
+ if(!turned_on)
+ balloon_alert(user, "turned off")
+ return
+ if(stat & NOPOWER)
+ balloon_alert(user, "no power")
+ return
+ var/datum/component/quikpay_shop/orderterminal/qp_shop = src.GetComponent(/datum/component/quikpay_shop/orderterminal)
+ if(!qp_shop)
+ return
+ qp_shop.ui_interact(user)
- if("buy")
- for(var/list/L in buying)
- if(L["name"] == params["buying"])
- L["amount"]++
- return TRUE
- buying += list(list("name" = params["buying"], "amount" = params["amount"], "price" = items_to_price[params["buying"]]))
+/obj/machinery/commissary_wall_shop/CtrlClick(mob/user)
+ var/obj/item/card/id/I = user.GetIdCard()
+ if(!istype(I))
+ balloon_alert(user, "no id!")
+ return
+ if(!has_access(req_one_access = src.req_one_access, accesses = I.access))
+ balloon_alert(user, "no access!")
+ return
+ turned_on = !turned_on
+ balloon_alert(user, "turned [turned_on ? "on" : "off"]")
+ update_icon()
- if("removal")
- var/index = 0
- for(var/list/L in buying)
- index++
- if(L["name"] == params["removal"])
- if(L["amount"] > 1)
- L["amount"]--
- else
- buying.Cut(index, index+1)
- . = TRUE
+/obj/machinery/commissary_wall_shop/power_change()
+ ..()
+ update_icon()
- if("confirm")
- // Ensuring it is clear, in case the button is clicked multiple times
- receipt = ""
- sum = 0
- var/obj/item/card/id/id_card = usr.GetIdCard()
- var/cashier = id_card? id_card.registered_name : "Unknown"
- receipt = "[shop_name] receipt
Today's date: [worlddate2text()]
Cashier: [cashier]
Purchased items:"
- for(var/list/bought_item in buying)
- var/item_name = bought_item["name"]
- var/item_amount = bought_item["amount"]
- var/item_price = items_to_price[item_name]
+/obj/machinery/commissary_wall_shop/update_icon()
+ ClearOverlays()
+ if(stat & NOPOWER || !turned_on)
+ set_light(FALSE)
+ return
- receipt += "- [item_name]: [item_amount] x [item_price]电: [item_amount * item_price]电
"
- sum += item_price * item_amount
+ var/mutable_appearance/screen_overlay = mutable_appearance(icon, "kitchenterminal-active", plane = ABOVE_LIGHTING_PLANE)
+ AddOverlays(screen_overlay)
+ set_light(1.4, 1, COLOR_CYAN)
- receipt += "
Total: [sum]电
"
- playsound(src, 'sound/machines/ping.ogg', 25, 1)
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] pings."))
- . = TRUE
-
- if("locking")
- if(editmode)
- editmode = FALSE
- balloon_alert(usr, "device locked!")
- else
- if(!editmode)
- var/obj/item/card/id/I = usr.GetIdCard()
- if(!istype(I))
- return
- if(check_access(I))
- editmode = !editmode
- balloon_alert(usr, "device [editmode ? "un" : ""]locked")
- . = TRUE
-
- if("accountselect")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
-
- var/dest = tgui_input_list(usr, "What account would you like to select?", "Destination Account", assoc_to_keys(SSeconomy.department_accounts))
- if(!dest)
- return FALSE
- destinationact = dest
- . = TRUE
-
- if("print_dsv")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
- print_price(usr)
- . = TRUE
-
-/obj/structure/cash_register/commissary/proc/clear_order()
- buying.Cut()
- sum = 0
- receipt = ""
+/obj/machinery/commissary_wall_shop/process()
+ if(stat & NOPOWER || !turned_on)
+ ClearOverlays()
+ set_light(FALSE)
+ return
/obj/item/commissary_restrock
name = "commissary cigarette restock pack"
@@ -701,6 +524,17 @@
desc = "A crate packed with boxes of various goods. Handle with care!"
/obj/structure/closet/crate/commissary/fill()
+ for(var/i = 1 to 8)
+ new /obj/item/storage/box/unique/papersack(src)
+ new /obj/item/storage/bag/plasticbag(src)
+ new /obj/item/storage/box/plasticbag(src)
+ new /obj/item/tape_roll(src)
+ new /obj/item/hand_labeler(src)
+ new /obj/item/storage/toolbox/mechanical(src)
+
+/obj/structure/closet/crate/commissary/resupply
+
+/obj/structure/closet/crate/commissary/resupply/fill()
new /obj/item/commissary_restrock(src)
new /obj/item/commissary_restrock/rollable(src)
new /obj/item/commissary_restrock/chewable(src)
diff --git a/code/modules/cooking/machinery/smartfridge.dm b/code/modules/cooking/machinery/smartfridge.dm
index 31957f5890e..19447520bc9 100644
--- a/code/modules/cooking/machinery/smartfridge.dm
+++ b/code/modules/cooking/machinery/smartfridge.dm
@@ -20,6 +20,7 @@
var/scan_id = 1
var/is_secure = 0
var/machineselect = 0
+ var/visible_takeout = FALSE
var/list/accepted_items = list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/mollusc)
/// List of items that the machine starts with upon spawn
@@ -536,15 +537,20 @@
var/i = amount
for(var/obj/O in contents)
- if(O.name == K)
- if(Adjacent(user))
- user.put_in_hands(O)
- else
- O.forceMove(loc)
- i--
- update_overlays()
- if(i <= 0)
- break
+ if(O.name != K)
+ continue
+ if(Adjacent(user))
+ user.put_in_hands(O)
+ if(visible_takeout)
+ user.visible_message(
+ "[SPAN_BOLD("[user]")] takes \a [O] from \the [src].",
+ SPAN_NOTICE("You take \a [O] from \the [src]."))
+ else
+ O.forceMove(loc)
+ i--
+ update_overlays()
+ if(i <= 0)
+ break
if(item_quants[K] <= 0)
update_static_data_for_all_viewers()
diff --git a/code/modules/economy/OrderTerminal.dm b/code/modules/economy/OrderTerminal.dm
index 9df4983d1a6..d5b1dd8779d 100644
--- a/code/modules/economy/OrderTerminal.dm
+++ b/code/modules/economy/OrderTerminal.dm
@@ -21,16 +21,16 @@
var/ticket = ""
var/destinationact = "Service"
var/ticket_number = 1
- req_one_access = list(ACCESS_BAR, ACCESS_GALLEY) // Access to change the menu
+ req_one_access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO) // Access to change the menu
/obj/machinery/orderterminal/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
- . += "To edit the menu, select 'Toggle Lock' while wearing an ID with galley access."
- . += "All credits from the machine will automatically go to the civilian account."
+ . += "Items can be paid for with id cards or charge cards, and a receipt will be printed."
+ . += "The terminal can print a paper which can be used to quickly fill it out in the future by using it on the register."
/obj/machinery/orderterminal/Initialize()
. = ..()
- machine_id = "Idris Ordering Terminal #[SSeconomy.num_financial_terminals++]"
+ src.LoadComponent(/datum/component/quikpay_shop/orderterminal/food, req_one_access, destinationact)
update_icon()
/obj/machinery/orderterminal/power_change()
@@ -54,152 +54,19 @@
return
/obj/machinery/orderterminal/attack_hand(var/mob/user)
- ui_interact(user)
-
-/obj/machinery/orderterminal/ui_interact(mob/user, var/datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "OrderTerminal", "Idris Ordering Terminal", 450, 450)
- ui.open()
-
-/obj/machinery/orderterminal/proc/print_receipt() // Print the receipt followed by the order ticket
- var/obj/item/paper/notepad/receipt/R = new(usr.loc)
- var/receiptname = "Receipt: [machine_id]"
- R.set_content_unsafe(receiptname, receipt, sum)
- stamp_receipt(R)
- // And now we do it but for the ticket.
- var/obj/item/paper/notepad/receipt/T = new(usr.loc)
- var/tickettname = "Order ticket: [ticket_number]"
- ticket_number++
- T.set_content_unsafe(tickettname, ticket, sum)
- stamp_receipt(T)
-
-/obj/machinery/orderterminal/proc/stamp_receipt(obj/item/paper/R) // Stamps the papers, made into a proc to avoid copy pasting too much
- var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
- stampoverlay.icon_state = "paper_stamp-hop"
- if(!R.stamped)
- R.stamped = new
- R.stamped += /obj/item/stamp
- R.AddOverlays(stampoverlay)
- R.stamps += "
This paper has been stamped by the Idris Ordering Terminal."
- R.ripped = TRUE
+ if(stat & NOPOWER)
+ balloon_alert(user, "no power")
+ return
+ var/datum/component/quikpay_shop/orderterminal/food/qp_shop = src.GetComponent(/datum/component/quikpay_shop/orderterminal/food)
+ if(!qp_shop)
+ return
+ qp_shop.ui_interact(user)
/obj/machinery/orderterminal/attackby(obj/item/attacking_item, mob/user)
- var/obj/item/card/id/I = attacking_item.GetID()
- if (!I)
+ if(stat & NOPOWER)
+ balloon_alert(user, "no power")
return
- if (!istype(attacking_item))
+ var/datum/component/quikpay_shop/orderterminal/food/qp_shop = src.GetComponent(/datum/component/quikpay_shop/orderterminal/food)
+ if(!qp_shop)
return
-
- else if (confirmorder)
- var/transaction_amount = sum
- var/transaction_purpose = "Idris Ordering Terminal order."
- var/transaction_terminal = machine_id
- if(sum > 0) // it will just get denied if the order is 0 credits. We still need the id regardless for the name
- var/transaction = SSeconomy.transfer_money(I.associated_account_number, SSeconomy.get_department_account(destinationact)?.account_number,transaction_purpose,transaction_terminal,transaction_amount,null,usr)
- if(transaction)
- to_chat(user,"[icon2html(src, user)][transaction].")
- else
- playsound(src, 'sound/machines/chime.ogg', 50, 1)
- src.visible_message("[icon2html(src, viewers(get_turf(src)))] \The [src] chimes.")
- ticket += "
Customer: [I.registered_name]"
- receipt += "
Customer: [I.registered_name]"
- print_receipt()
- clear_order()
-
-/obj/machinery/orderterminal/ui_data(mob/user)
- var/list/data = list()
-
- data["items"] = items
- data["buying"] = buying
- data["sum"] = sum
- data["new_item"] = new_item
- data["new_price"] = new_price
- data["editmode"] = editmode
- data["destinationact"] = destinationact
-
- return data
-
-/obj/machinery/orderterminal/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("add")
- if(!editmode)
- to_chat(usr, SPAN_NOTICE("You don't have access to use this option."))
- return FALSE
- items += list(list("name" = new_item, "price" = new_price))
- items_to_price[new_item] = new_price
- . = TRUE
-
- if("remove")
- if(!editmode)
- to_chat(usr, SPAN_NOTICE("You don't have access to use this option."))
- return FALSE
- items -= params["remove"]
- items_to_price -= params["remove"]
- . = TRUE
-
- if("buy")
- for(var/list/L in buying)
- if(L["name"] == params["buying"])
- L["amount"]++
- return TRUE
- buying += list(list("name" = params["buying"], "amount" = params["amount"]))
- . = TRUE
-
- if("removal")
- for(var/list/L in buying)
- if(L["name"] == params["removal"])
- if(L["amount"] > 1)
- L["amount"]--
- else
- buying -= L
- . = TRUE
-
- if("clear")
- clear_order()
- . = TRUE
-
- if("set_new_price")
- new_price = params["set_new_price"]
- . = TRUE
-
- if("set_new_item")
- new_item = params["set_new_item"]
- . = TRUE
-
- if("confirm")
- confirmorder = TRUE
- receipt += "Idris Food Terminal Receipt
"
- ticket += "Idris Food Terminal Ticket
"
- for(var/list/bought_item in buying)
- var/item_name = bought_item["name"]
- var/item_amount = bought_item["amount"]
- var/item_price = items_to_price[item_name]
- sum += item_price
-
- receipt += "[name]: [item_name] x[item_amount] at [item_price]电 each
"
- ticket += "[name]: [item_name] x[item_amount] at [item_price]电 each
"
- receipt += "
Total: [sum]电"
- ticket += "
Total: [sum]电"
- sum = sum
- . = TRUE
-
- if("locking")
- var/obj/item/card/id/I = usr.GetIdCard()
- if(!istype(I))
- return
- if(check_access(I))
- editmode = !editmode
- to_chat(usr, SPAN_NOTICE("Device [editmode ? "un" : ""]locked."))
- . = TRUE
-
-/obj/machinery/orderterminal/proc/clear_order()
- buying.Cut()
- sum = 0
- receipt = ""
- ticket = ""
- confirmorder = FALSE
+ qp_shop.interact_object(attacking_item, user)
diff --git a/code/modules/economy/quikpay.dm b/code/modules/economy/quikpay.dm
index a3c3c01c667..3e29362b030 100644
--- a/code/modules/economy/quikpay.dm
+++ b/code/modules/economy/quikpay.dm
@@ -8,36 +8,31 @@
item_state = "electronic"
w_class = WEIGHT_CLASS_SMALL
slot_flags = SLOT_BELT
- var/machine_id = ""
- var/list/items = list()
- var/list/items_to_price = list()
- var/list/buying = list()
- var/new_item = ""
- var/new_price = 0
- var/sum = 0
- var/editmode = FALSE
- var/receipt = ""
var/destinationact = "Service"
- var/shop_name = "Quikpay"
+ var/shop_name
+ req_one_access = list(ACCESS_BAR, ACCESS_GALLEY, ACCESS_CARGO)
+
+/obj/item/quikpay/mechanics_hints(mob/user, distance, is_adjacent)
+ . += ..()
+ . += "Items can be paid for with id cards or charge cards, and a receipt will be printed."
+ . += "The quikpay can print a paper which can be used to quickly fill it out in the future by using it on the register."
/obj/item/quikpay/Initialize()
. = ..()
- machine_id = "[station_name()] Idris Quik-Pay #[SSeconomy.num_financial_terminals++]"
+ src.LoadComponent(/datum/component/quikpay_shop/quikpay, req_one_access, destinationact)
//create a short manual as well
var/obj/item/paper/R = new(src.loc)
R.name = "Quik And Easy: How to make a transaction"
R.info += "Quik-Pay setup:
"
- R.info += "- Remember your access code included on the paper that is included with your device
"
R.info += "- Unlock it to be able to add items to the menu
"
- R.info += "- Add items to the menu by typing the item name and its price
"
+ R.info += "Add items to the menu by typing the item name and its price, optionally include a category"
R.info += "When starting a new transaction:
"
R.info += "- Have the customer enter the amount of the item they want and then confirm the purchase.
"
R.info += "- Allow them to review the sum.
"
R.info += "- Have them swipe their card to pay for the items.
"
-
//stamp the paper
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
stampoverlay.icon_state = "paper_stamp-cent"
@@ -50,238 +45,24 @@
R.AddOverlays(stampoverlay)
R.stamps += "
This paper has been stamped by the Executive Officer's desk."
-/obj/item/quikpay/AltClick(var/mob/user)
- var/obj/item/card/id/I = user.GetIdCard()
- if(istype(I) && (ACCESS_HEADS in I.access))
- editmode = TRUE
- to_chat(user, SPAN_NOTICE("Command access granted."))
- SStgui.update_uis(src)
-
-/obj/item/quikpay/proc/print_receipt()
- var/obj/item/paper/notepad/receipt/R = new(usr.loc)
- var/receiptname = "Receipt: [machine_id]"
- R.set_content_unsafe(receiptname, receipt, sum)
-
- //stamp the paper
- var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
- stampoverlay.icon_state = "paper_stamp-hop"
- if(!R.stamped)
- R.stamped = new
- R.stamped += /obj/item/stamp
- R.AddOverlays(stampoverlay)
- R.stamps += "
This paper has been stamped by the Quik-Pay device."
- R.ripped = TRUE
- usr.put_in_any_hand_if_possible(R)
-
/obj/item/quikpay/attackby(obj/item/attacking_item, mob/user)
- if(istype(attacking_item, /obj/item/paper))
- read_paper_list(attacking_item, user)
+ . = ..()
+ var/datum/component/quikpay_shop/quikpay/qp_shop = src.GetComponent(/datum/component/quikpay_shop/quikpay)
+ if(!qp_shop)
return
- if (istype(attacking_item, /obj/item/spacecash/ewallet))
- var/obj/item/spacecash/ewallet/E = attacking_item
- var/transaction_amount = sum
- var/transaction_purpose = "[destinationact] Payment"
- var/transaction_terminal = machine_id
-
- if(transaction_amount <= E.worth)
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] chimes."))
- playsound(src, 'sound/machines/chime.ogg', 50, 1)
-
- SSeconomy.charge_to_account(SSeconomy.get_department_account(destinationact)?.account_number, E.owner_name, transaction_purpose, transaction_terminal, transaction_amount)
- E.worth -= transaction_amount
- print_receipt()
- sum = 0
- receipt = ""
- to_chat(user, SPAN_NOTICE("Transaction completed, please return to the home screen."))
- else if (transaction_amount > E.worth)
- to_chat(user, SPAN_WARNING("[icon2html(src, user)]\The [E] doesn't have that much money!"))
- return
-
- var/obj/item/card/id/I = attacking_item.GetID()
- if (!istype(attacking_item))
- return
-
- var/transaction_amount = sum
- var/transaction_purpose = "[destinationact] Payment"
- var/transaction_terminal = machine_id
-
- var/transaction = SSeconomy.transfer_money(I.associated_account_number, SSeconomy.get_department_account(destinationact)?.account_number,transaction_purpose,transaction_terminal,transaction_amount,null,usr)
-
- if(transaction)
- to_chat(user, SPAN_NOTICE("[icon2html(src, user)][transaction]."))
- else
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] chimes."))
- playsound(src, 'sound/machines/chime.ogg', 50, 1)
- print_receipt()
- sum = 0
- receipt = ""
- to_chat(user, SPAN_NOTICE("Transaction completed, please return to the home screen."))
-
-/obj/item/quikpay/proc/read_paper_list(obj/item/paper/R, mob/user)
- if(!editmode)
- balloon_alert(user, "device locked!")
- return FALSE
- var/result = read_paper_price_list(R)
- for(var/item in result)
- items += list(list("name" = item["name"], "price" = item["price"]))
- items_to_price[item["name"]] += item["price"]
-
-/obj/item/quikpay/proc/print_price(mob/user)
- return print_price_to_paper(shop_name, items, loc, user)
+ qp_shop.interact_object(attacking_item, user)
/obj/item/quikpay/attack_self(var/mob/user)
- ui_interact(user)
-
-/obj/item/quikpay/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "QuikPay", "Idris Quik-Pay", 550, 550)
- ui.open()
-
-/obj/item/quikpay/ui_data(var/mob/user)
- var/list/data = list()
-
- data["items"] = items
- data["buying"] = buying
- data["sum"] = sum
- data["new_item"] = new_item
- data["new_price"] = new_price
- data["editmode"] = editmode
- data["destinationact"] = destinationact
-
- return data
-
-/obj/item/quikpay/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
- if(.)
+ var/datum/component/quikpay_shop/quikpay/qp_shop = src.GetComponent(/datum/component/quikpay_shop/quikpay)
+ if(!qp_shop)
return
-
- switch(action)
- if("add")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
-
- items += list(list("name" = new_item, "price" = new_price))
- items_to_price[new_item] = new_price
- . = TRUE
-
- if("remove")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
- var/index = 0
- for(var/list/L in items)
- index++
- if(L["name"] == params["removing"])
- items.Cut(index, index+1)
- . = TRUE
-
- if("set_new_price")
- new_price = params["set_new_price"]
- . = TRUE
-
- if("set_new_item")
- new_item = params["set_new_item"]
- . = TRUE
-
- if("clear")
- clear_order()
- . = TRUE
-
- if("buy")
- for(var/list/L in buying)
- if(L["name"] == params["buying"])
- L["amount"]++
- return TRUE
- buying += list(list("name" = params["buying"], "amount" = params["amount"], "price" = items_to_price[params["buying"]]))
-
- if("removal")
- var/index = 0
- for(var/list/L in buying)
- index++
- if(L["name"] == params["removal"])
- if(L["amount"] > 1)
- L["amount"]--
- else
- buying.Cut(index, index+1)
- . = TRUE
-
- if("confirm")
- // Ensuring it is clear, in case the button is clicked multiple times
- receipt = ""
- sum = 0
- var/obj/item/card/id/id_card = usr.GetIdCard()
- var/cashier = id_card? id_card.registered_name : "Unknown"
- receipt = "[shop_name] receipt
Today's date: [worlddate2text()]
Cashier: [cashier]
Purchased items:"
- for(var/list/bought_item in buying)
- var/item_name = bought_item["name"]
- var/item_amount = bought_item["amount"]
- var/item_price = items_to_price[item_name]
-
- receipt += "- [item_name]: [item_amount] x [item_price]电: [item_amount * item_price]电
"
- sum += item_price * item_amount
-
- receipt += "
Total: [sum]电
"
- playsound(src, 'sound/machines/ping.ogg', 25, 1)
- audible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] \The [src] pings."))
- . = TRUE
-
- if("locking")
- if(editmode)
- editmode = FALSE
- balloon_alert(usr, "device locked!")
- else
- if(!editmode)
- var/obj/item/card/id/I = usr.GetIdCard()
- if(!istype(I))
- return
- if(check_access(I))
- editmode = !editmode
- to_chat(usr, SPAN_NOTICE("Device [editmode ? "un" : ""]locked."))
- . = TRUE
-
- if("accountselect")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
-
- var/dest = tgui_input_list(usr, "What account would you like to select?", "Destination Account", assoc_to_keys(SSeconomy.department_accounts))
- if(!dest)
- return FALSE
- destinationact = dest
- . = TRUE
-
- if("print_dsv")
- if(!editmode)
- balloon_alert(usr, "device locked!")
- return FALSE
- print_price(usr)
- . = TRUE
-
-/obj/item/quikpay/proc/clear_order()
- buying.Cut()
- sum = 0
- receipt = ""
+ qp_shop.ui_interact(user)
/obj/item/quikpay/afterattack(atom/target, mob/user, proximity)
if (!proximity) return
- if (!istype(target, /obj))
+
+ var/datum/component/quikpay_shop/quikpay/qp_shop = src.GetComponent(/datum/component/quikpay_shop/quikpay)
+ if(!qp_shop)
return
- if (!editmode)
- to_chat(user, SPAN_NOTICE("Unlock \the [src] to add items."))
- return
-
- var/obj/O = target
- var/name_guess = O.name
- var/price_guess = 0
-
- price_guess = text2num(sanitizeSafe( tgui_input_text(user, "Set price for [name_guess]:", "QuikPay", 0, 10), 10))
- if(isnull(price_guess) || price_guess == 0)
- return
- price_guess = max(0, round(price_guess, 0.01))
-
- items += list(list("name" = "[name_guess]", "price" = price_guess))
- items_to_price[name_guess] = price_guess
-
- to_chat(user, SPAN_NOTICE("[src]: added '[name_guess]' for [price_guess]."))
+ qp_shop.add_item(target, user)
diff --git a/html/changelogs/CommissaryChanges.yml b/html/changelogs/CommissaryChanges.yml
new file mode 100644
index 00000000000..743518a6308
--- /dev/null
+++ b/html/changelogs/CommissaryChanges.yml
@@ -0,0 +1,13 @@
+author: TheGreyWolf
+
+delete-after: True
+
+changes:
+ - bugfix: "Fixed that the quikpay and cash register prices could be wrong."
+ - rscadd: "The commissary register now has categories."
+ - refactor: "Changed the commissary and quikpay to rely on a component, making them easier to maintan and ensuring they function almost identical."
+ - rscadd: "The commissary now has a wall terminal, that can be enabled by hangar technicians."
+ - bugfix: "The noticeboard in operations is now properly on one side of the wall, instead of on the wall turf itself."
+ - rscadd: "The food and drink fridges in the commissary can now be renamed."
+ - rscadd: "The shelves in the commissary now has a message when an item are taken from them."
+ - rscadd: "The quick-e-meals vendor was readded behind the café."
diff --git a/icons/obj/machinery/wall/terminals.dmi b/icons/obj/machinery/wall/terminals.dmi
index 41a5b5fe63b3462f6d8838f75e082a26bd509d92..08286b10cae377854c39d0667d2a9edd24666dcd 100644
GIT binary patch
literal 48785
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