diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index eebe775d479..d2705a33259 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -482,13 +482,20 @@
///from [/obj/structure/closet/supplypod/proc/endlaunch]:
#define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom"
+// /obj signals for economy
+///called when the payment component tries to charge an account.
+#define COMSIG_OBJ_ATTEMPT_CHARGE "obj_attempt_simple_charge"
+ #define COMPONENT_OBJ_CANCEL_CHARGE (1<<0)
+///Called when a payment component changes value
+#define COMSIG_OBJ_ATTEMPT_CHARGE_CHANGE "obj_attempt_simple_charge_change"
+
// /obj/item signals for economy
///called when an item is sold by the exports subsystem
#define COMSIG_ITEM_SOLD "item_sold"
///called when a wrapped up structure is opened by hand
#define COMSIG_STRUCTURE_UNWRAPPED "structure_unwrapped"
-#define COMSIG_ITEM_UNWRAPPED "item_unwrapped"
///called when a wrapped up item is opened by hand
+#define COMSIG_ITEM_UNWRAPPED "item_unwrapped"
#define COMSIG_ITEM_SPLIT_VALUE (1<<0)
///called when getting the item's exact ratio for cargo's profit.
#define COMSIG_ITEM_SPLIT_PROFIT "item_split_profits"
diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm
index 28d68ec6df3..11d724d93be 100644
--- a/code/__DEFINES/economy.dm
+++ b/code/__DEFINES/economy.dm
@@ -46,3 +46,8 @@
#define CIV_JOB_MINE 10
#define CIV_JOB_MED 11
#define CIV_JOB_GROW 12
+
+//These defines are to be used to with the payment component, determines which lines will be used during a transaction. If in doubt, go with clinical.
+#define PAYMENT_CLINICAL "clinical"
+#define PAYMENT_FRIENDLY "friendly"
+#define PAYMENT_ANGRY "angry"
diff --git a/code/datums/components/payment.dm b/code/datums/components/payment.dm
new file mode 100644
index 00000000000..d2238fffd12
--- /dev/null
+++ b/code/datums/components/payment.dm
@@ -0,0 +1,71 @@
+
+/**
+ * Handles simple payment operations where the cost of the object in question doesn't change.
+ *
+ * What this is useful for:
+ * Basic forms of vending.
+ * Objects that can drain the owner's money linearly.
+ * What this is not useful for:
+ * Things where the seller may want to fluxuate the price of the object.
+ * Improving standardizing every form of payment handing, as some custom handling is specific to that object.
+ **/
+/datum/component/payment
+ ///Standardized of operation.
+ var/cost = 10
+ ///Flavor style for handling cash (Friendly? Hostile? etc.)
+ var/transaction_style = "Clinical"
+ ///Who's getting paid?
+ var/datum/bank_account/target_acc
+
+/datum/component/payment/Initialize(_cost, _target, _style)
+ target_acc = _target
+ if(!target_acc)
+ target_acc = SSeconomy.get_dep_account(ACCOUNT_CIV)
+
+ cost = _cost
+ transaction_style = _style
+ RegisterSignal(parent, COMSIG_OBJ_ATTEMPT_CHARGE, .proc/attempt_charge)
+ RegisterSignal(parent, COMSIG_OBJ_ATTEMPT_CHARGE_CHANGE, .proc/change_cost)
+
+/datum/component/payment/proc/attempt_charge(datum/source, atom/movable/target, var/extra_fees = 0)
+ if(!cost) //In case a free variant of anything is made it'll skip charging anyone.
+ return
+ if(!ismob(target))
+ return COMPONENT_OBJ_CANCEL_CHARGE
+ var/mob/user = target
+ var/obj/item/card/id/card = user.get_idcard(TRUE)
+ if(!card)
+ switch(transaction_style)
+ if(PAYMENT_FRIENDLY)
+ to_chat(user, "ID not detected, sorry [user]!")
+ if(PAYMENT_ANGRY)
+ to_chat(user, "WHERE IS YOUR GOD DAMN CARD! GOD DAMNIT!")
+ if(PAYMENT_CLINICAL)
+ to_chat(user, "ID card not present. Aborting.")
+ return COMPONENT_OBJ_CANCEL_CHARGE
+ if(!card.registered_account)
+ switch(transaction_style)
+ if(PAYMENT_FRIENDLY)
+ to_chat(user, "There's no account detected on your ID, how mysterious!")
+ if(PAYMENT_ANGRY)
+ to_chat(user, "ARE YOU JOKING. YOU DON'T HAVE A BANK ACCOUNT ON YOUR ID YOU IDIOT.")
+ if(PAYMENT_CLINICAL)
+ to_chat(user, "ID Card lacks a bank account. Aborting.")
+ return COMPONENT_OBJ_CANCEL_CHARGE
+ if(!(card.registered_account.has_money(cost + extra_fees)))
+ switch(transaction_style)
+ if(PAYMENT_FRIENDLY)
+ to_chat(user, "I'm so sorry... You don't seem to have enough money.")
+ if(PAYMENT_ANGRY)
+ to_chat(user, "YOU MORON. YOU ABSOLUTE BAFOON. YOU INSUFFERABLE TOOL. YOU ARE POOR.")
+ if(PAYMENT_CLINICAL)
+ to_chat(user, "ID Card lacks funds. Aborting.")
+ return COMPONENT_OBJ_CANCEL_CHARGE
+ target_acc.transfer_money(card.registered_account, cost)
+ card.registered_account.bank_card_talk("[cost] credits deducted from your account.")
+ playsound(src, 'sound/effects/cashregister.ogg', 20, TRUE)
+
+/datum/component/payment/proc/change_cost(datum/source, var/new_cost)
+ if(!isnum(new_cost))
+ CRASH("change_cost called with variable new_cost as not a number.")
+ cost = new_cost
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 03486f0e995..32a0368c2c7 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1431,3 +1431,11 @@
*/
/atom/proc/setClosed()
return
+
+/**
+ * Used to attempt to charge an object with a payment component.
+ *
+ * Use this if an atom needs to attempt to charge another atom.
+ */
+/atom/proc/attempt_charge(var/atom/sender, var/atom/target, var/extra_fees = 0)
+ return SEND_SIGNAL(sender, COMSIG_OBJ_ATTEMPT_CHARGE, target, extra_fees)
diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm
index b474a80d7fe..77f35944433 100644
--- a/code/game/machinery/medical_kiosk.dm
+++ b/code/game/machinery/medical_kiosk.dm
@@ -29,35 +29,23 @@
/obj/machinery/medical_kiosk/Initialize() //loaded subtype for mapping use
. = ..()
+ AddComponent(/datum/component/payment, active_price, SSeconomy.get_dep_account(ACCOUNT_MED), PAYMENT_FRIENDLY)
scanner_wand = new/obj/item/scanner_wand(src)
/obj/machinery/medical_kiosk/proc/inuse() //Verifies that the user can use the interface, followed by showing medical information.
- if (pandemonium == TRUE)
- active_price += (rand(10,30)) //The wheel of capitalism says health care ain't cheap.
- if(!istype(C))
- say("No ID card detected.") // No unidentified crew.
- return
- if(C.registered_account)
+ if(C?.registered_account)
account = C.registered_account
- else
- say("No account detected.") //No homeless crew.
- return
if(account?.account_job?.paycheck_department == payment_department)
use_power(20)
paying_customer = TRUE
say("Hello, esteemed medical staff!")
RefreshParts()
return
- if(!account.has_money(active_price))
- say("You do not possess the funds to purchase this.") //No jobless crew, either.
+ var/bonus_fee = pandemonium ? rand(10,30) : 0
+ if(attempt_charge(src, H, bonus_fee) & COMPONENT_OBJ_CANCEL_CHARGE )
return
- else
- account.adjust_money(-active_price)
- var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_MED)
- if(D)
- D.adjust_money(active_price)
- use_power(20)
- paying_customer = TRUE
+ use_power(20)
+ paying_customer = TRUE
icon_state = "kiosk_active"
say("Thank you for your patronage!")
RefreshParts()
@@ -332,22 +320,26 @@
return
switch(action)
if("beginScan_1")
- inuse()
+ if(!scan_active_1)
+ inuse()
if(paying_customer == TRUE)
scan_active_1 = TRUE
paying_customer = FALSE
if("beginScan_2")
- inuse()
+ if(!scan_active_2)
+ inuse()
if(paying_customer == TRUE)
scan_active_2 = TRUE
paying_customer = FALSE
if("beginScan_3")
- inuse()
+ if(!scan_active_3)
+ inuse()
if(paying_customer == TRUE)
scan_active_3 = TRUE
paying_customer = FALSE
if("beginScan_4")
- inuse()
+ if(!scan_active_4)
+ inuse()
if(paying_customer == TRUE)
scan_active_4 = TRUE
paying_customer = FALSE
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 9a375f09724..2ca575b5d7a 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -29,6 +29,10 @@
var/mob/living/ass //i can't believe i didn't write a stupid-ass comment about this var when i first coded asscopy.
var/busy = FALSE
+/obj/machinery/photocopier/Initialize()
+ . = ..()
+ AddComponent(/datum/component/payment, 5, SSeconomy.get_dep_account(ACCOUNT_CIV), PAYMENT_CLINICAL)
+
/obj/machinery/photocopier/ui_interact(mob/user)
. = ..()
var/list/dat = list("Photocopier
")
@@ -56,8 +60,12 @@
return
if(href_list["copy"])
if(copy)
+ if(busy)
+ return
for(var/i = 0, i < copies, i++)
- if(toner > 0 && !busy && copy)
+ if(toner > 0 && copy)
+ if(attempt_charge(src, usr) & COMPONENT_OBJ_CANCEL_CHARGE)
+ return
var/copy_as_paper = 1
if(istype(copy, /obj/item/paper/contract/employment))
var/obj/item/paper/contract/employment/E = copy
@@ -89,16 +97,24 @@
break
updateUsrDialog()
else if(photocopy)
+ if(busy)
+ return
for(var/i = 0, i < copies, i++)
- if(toner >= 5 && !busy && photocopy) //Was set to = 0, but if there was say 3 toner left and this ran, you would get -2 which would be weird for ink
+ if(attempt_charge(src, usr) & COMPONENT_OBJ_CANCEL_CHARGE)
+ return
+ if(toner >= 5 && photocopy) //Was set to = 0, but if there was say 3 toner left and this ran, you would get -2 which would be weird for ink
new /obj/item/photo (loc, photocopy.picture.Copy(greytoggle == "Greyscale"? TRUE : FALSE))
busy = TRUE
addtimer(CALLBACK(src, .proc/reset_busy), 1.5 SECONDS)
else
break
else if(doccopy)
+ if(busy)
+ return
for(var/i = 0, i < copies, i++)
- if(toner > 5 && !busy && doccopy)
+ if(attempt_charge(src, usr) & COMPONENT_OBJ_CANCEL_CHARGE)
+ return
+ if(toner > 5 && doccopy)
new /obj/item/documents/photocopy(loc, doccopy)
toner-= 6 // the sprite shows 6 papers, yes I checked
busy = TRUE
@@ -107,7 +123,11 @@
break
updateUsrDialog()
else if(ass) //ASS COPY. By Miauw
+ if(busy)
+ return
for(var/i = 0, i < copies, i++)
+ if(attempt_charge(src, usr) & COMPONENT_OBJ_CANCEL_CHARGE)
+ return
var/icon/temp_img
if(ishuman(ass) && (ass.get_item_by_slot(ITEM_SLOT_ICLOTHING) || ass.get_item_by_slot(ITEM_SLOT_OCLOTHING)))
to_chat(usr, "You feel kind of silly, copying [ass == usr ? "your" : ass][ass == usr ? "" : "\'s"] ass with [ass == usr ? "your" : "[ass.p_their()]"] clothes on." )
diff --git a/tgstation.dme b/tgstation.dme
index 87181078c6e..f416b29f1cf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -426,6 +426,7 @@
#include "code\datums\components\omen.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\paintable.dm"
+#include "code\datums\components\payment.dm"
#include "code\datums\components\pellet_cloud.dm"
#include "code\datums\components\pricetag.dm"
#include "code\datums\components\punchcooldown.dm"