mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Fix the vending machine chef's compartment (#18626)
* Make the vending chef compartment work again * Fix vending load access check * Fix missing table wrapper * Apply eslint fixes & fix button disable check * Add icons to custom vending items * Appease the linter * Rename "dispense" to "vend_custom" * Refactor vending code out into procs Reduces code duplication significantly * Remove stray vend_ready set * Fix proper/improper names not dispensing * Remove empty stocks from listing Bibby --------- Co-authored-by: ynot01 <ynot000001@gmail.com>
This commit is contained in:
@@ -37,6 +37,26 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
|
|||||||
///List of items that have been returned to the vending machine.
|
///List of items that have been returned to the vending machine.
|
||||||
var/list/returned_products
|
var/list/returned_products
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # User-inserted custom product
|
||||||
|
* A datum that represents a custom product that is vendable
|
||||||
|
*/
|
||||||
|
/datum/data/vending_custom_product
|
||||||
|
///Name of the stored item
|
||||||
|
name = "generic"
|
||||||
|
///Unstripped name of the item, includes article
|
||||||
|
var/full_name = ""
|
||||||
|
///Icon of the item
|
||||||
|
var/asset = null
|
||||||
|
///How many are stored currently
|
||||||
|
var/amount = 0
|
||||||
|
|
||||||
|
/datum/data/vending_custom_product/New(obj/item/I)
|
||||||
|
name = format_text(I.name)
|
||||||
|
full_name = I.name
|
||||||
|
var/icon/icon = icon(I.icon, I.icon_state, SOUTH, 1)
|
||||||
|
asset = icon2base64(icon) // costly? probably. less costly than sending the entire spritesheet? also probably
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # vending machines
|
* # vending machines
|
||||||
*
|
*
|
||||||
@@ -136,6 +156,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
|
|||||||
var/obj/item/coin/coin
|
var/obj/item/coin/coin
|
||||||
///Bills we accept?
|
///Bills we accept?
|
||||||
var/obj/item/stack/spacecash/bill
|
var/obj/item/stack/spacecash/bill
|
||||||
|
///Custom item price
|
||||||
var/chef_price = 10
|
var/chef_price = 10
|
||||||
///Default price of items if not overridden
|
///Default price of items if not overridden
|
||||||
var/default_price = 25
|
var/default_price = 25
|
||||||
@@ -152,6 +173,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C
|
|||||||
///ID's that can load this vending machine wtih refills
|
///ID's that can load this vending machine wtih refills
|
||||||
var/list/canload_access_list
|
var/list/canload_access_list
|
||||||
|
|
||||||
|
///Custom item stock
|
||||||
var/list/vending_machine_input = list()
|
var/list/vending_machine_input = list()
|
||||||
///Display header on the input view
|
///Display header on the input view
|
||||||
var/input_display_header = "Custom Compartment"
|
var/input_display_header = "Custom Compartment"
|
||||||
@@ -601,10 +623,12 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
LAZYADD(product_datum.returned_products, I)
|
LAZYADD(product_datum.returned_products, I)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(vending_machine_input[format_text(I.name)])
|
var/name = format_text(I.name)
|
||||||
vending_machine_input[format_text(I.name)]++
|
if(!vending_machine_input[name])
|
||||||
else
|
vending_machine_input[name] = new /datum/data/vending_custom_product(I)
|
||||||
vending_machine_input[format_text(I.name)] = 1
|
|
||||||
|
var/datum/data/vending_custom_product/P = vending_machine_input[name]
|
||||||
|
P.amount++
|
||||||
loaded_items++
|
loaded_items++
|
||||||
|
|
||||||
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/W)
|
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/W)
|
||||||
@@ -669,6 +693,9 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
. = list()
|
. = list()
|
||||||
.["onstation"] = onstation
|
.["onstation"] = onstation
|
||||||
.["department"] = payment_department
|
.["department"] = payment_department
|
||||||
|
.["chef"] = list() // "chef compartment" i.e. player-added stock
|
||||||
|
.["chef"]["title"] = input_display_header
|
||||||
|
.["chef"]["price"] = chef_price
|
||||||
.["product_records"] = list()
|
.["product_records"] = list()
|
||||||
for (var/datum/data/vending_product/R in product_records)
|
for (var/datum/data/vending_product/R in product_records)
|
||||||
var/list/data = list(
|
var/list/data = list(
|
||||||
@@ -733,6 +760,11 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
|
for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records)
|
||||||
.["stock"][R.name] = R.amount
|
.["stock"][R.name] = R.amount
|
||||||
.["extended_inventory"] = extended_inventory
|
.["extended_inventory"] = extended_inventory
|
||||||
|
// extra items that have been placed in custom stock
|
||||||
|
.["custom_stock"] = list()
|
||||||
|
for (var/name in vending_machine_input)
|
||||||
|
var/datum/data/vending_custom_product/P = vending_machine_input[name]
|
||||||
|
.["custom_stock"][P.name] = list(amount = P.amount, img = P.asset)
|
||||||
|
|
||||||
/obj/machinery/vending/ui_act(action, params)
|
/obj/machinery/vending/ui_act(action, params)
|
||||||
. = ..()
|
. = ..()
|
||||||
@@ -770,6 +802,84 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
flick(icon_deny,src)
|
flick(icon_deny,src)
|
||||||
vend_ready = TRUE
|
vend_ready = TRUE
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if(coin_records.Find(R) || hidden_records.Find(R))
|
||||||
|
price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
|
||||||
|
|
||||||
|
if(LAZYLEN(R.returned_products))
|
||||||
|
price_to_use = 0 //returned items are free
|
||||||
|
|
||||||
|
if(!charge_user(price_to_use, R.name))
|
||||||
|
vend_ready = TRUE
|
||||||
|
return
|
||||||
|
|
||||||
|
if(onstation && ishuman(usr))
|
||||||
|
var/mob/living/carbon/human/H = usr
|
||||||
|
var/obj/item/card/id/C = H.get_idcard(TRUE)
|
||||||
|
// this should really be caught by charge_user above, just extra safety
|
||||||
|
if(!C)
|
||||||
|
vend_ready = TRUE
|
||||||
|
return
|
||||||
|
|
||||||
|
if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
|
||||||
|
say("You are not of legal age to purchase [R.name].")
|
||||||
|
if(!(usr in GLOB.narcd_underages))
|
||||||
|
alertradio.set_frequency(FREQ_SECURITY)
|
||||||
|
alertradio.talk_into(src, "SECURITY ALERT: Underaged crewmember [H] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY)
|
||||||
|
GLOB.narcd_underages += H
|
||||||
|
flick(icon_deny,src)
|
||||||
|
vend_ready = TRUE
|
||||||
|
return
|
||||||
|
|
||||||
|
thank_user("Thank you for shopping with [src]!")
|
||||||
|
finish_vend()
|
||||||
|
|
||||||
|
var/obj/item/vended_item
|
||||||
|
if(!LAZYLEN(R.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items
|
||||||
|
vended_item = new R.product_path(get_turf(src))
|
||||||
|
else
|
||||||
|
vended_item = LAZYACCESS(R.returned_products, LAZYLEN(R.returned_products)) //first in, last out
|
||||||
|
LAZYREMOVE(R.returned_products, vended_item)
|
||||||
|
vended_item.forceMove(get_turf(src))
|
||||||
|
R.amount--
|
||||||
|
SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
|
||||||
|
vend_ready = TRUE
|
||||||
|
if("vend_custom")
|
||||||
|
. = TRUE
|
||||||
|
if(!vend_ready)
|
||||||
|
return
|
||||||
|
if(panel_open)
|
||||||
|
to_chat(usr, span_warning("The vending machine cannot dispense products while its service panel is open!"))
|
||||||
|
return
|
||||||
|
var/N = params["item"]
|
||||||
|
var/datum/data/vending_custom_product/P = vending_machine_input[N]
|
||||||
|
if(!P || P.amount <= 0) // don't dispense none item with left beef
|
||||||
|
return
|
||||||
|
vend_ready = FALSE //One thing at a time!!
|
||||||
|
|
||||||
|
// Charge the user
|
||||||
|
if (!charge_user(chef_price, P.full_name))
|
||||||
|
vend_ready = TRUE
|
||||||
|
return
|
||||||
|
|
||||||
|
thank_user("Thank you for shopping local and buying \the [P.full_name]!")
|
||||||
|
finish_vend()
|
||||||
|
|
||||||
|
P.amount = max(P.amount - 1, 0)
|
||||||
|
for(var/obj/item/I in contents)
|
||||||
|
if(format_text(I.name) == N)
|
||||||
|
I.forceMove(get_turf(src))
|
||||||
|
break
|
||||||
|
if(P.amount <= 0) // If there's no more left, clear it from the records
|
||||||
|
vending_machine_input[N] = null
|
||||||
|
qdel(P)
|
||||||
|
vend_ready = TRUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charge the user during a vend
|
||||||
|
* Returns false if the user could not buy this item
|
||||||
|
*/
|
||||||
|
/obj/machinery/vending/proc/charge_user(price, item_name)
|
||||||
var/mob/living/L
|
var/mob/living/L
|
||||||
if(isliving(usr))
|
if(isliving(usr))
|
||||||
L = usr
|
L = usr
|
||||||
@@ -781,55 +891,41 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
if(!C)
|
if(!C)
|
||||||
say("No card found.")
|
say("No card found.")
|
||||||
flick(icon_deny,src)
|
flick(icon_deny,src)
|
||||||
vend_ready = TRUE
|
return FALSE
|
||||||
return
|
|
||||||
else if (!C.registered_account)
|
else if (!C.registered_account)
|
||||||
say("No account found.")
|
say("No account found.")
|
||||||
flick(icon_deny,src)
|
flick(icon_deny,src)
|
||||||
vend_ready = TRUE
|
return FALSE
|
||||||
return
|
|
||||||
else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
|
|
||||||
say("You are not of legal age to purchase [R.name].")
|
|
||||||
if(!(usr in GLOB.narcd_underages))
|
|
||||||
alertradio.set_frequency(FREQ_SECURITY)
|
|
||||||
alertradio.talk_into(src, "SECURITY ALERT: Underaged crewmember [H] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY)
|
|
||||||
GLOB.narcd_underages += H
|
|
||||||
flick(icon_deny,src)
|
|
||||||
vend_ready = TRUE
|
|
||||||
return
|
|
||||||
var/datum/bank_account/account = C.registered_account
|
var/datum/bank_account/account = C.registered_account
|
||||||
if(account.account_job && account.account_job.paycheck_department == payment_department)
|
if(account.account_job && account.account_job.paycheck_department == payment_department)
|
||||||
price_to_use = 0
|
price = 0
|
||||||
if(coin_records.Find(R) || hidden_records.Find(R))
|
if(price && !account.adjust_money(-price))
|
||||||
price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
|
say("You do not possess the funds to purchase \the [item_name].")
|
||||||
if(LAZYLEN(R.returned_products))
|
|
||||||
price_to_use = 0 //returned items are free
|
|
||||||
if(price_to_use && !account.adjust_money(-price_to_use))
|
|
||||||
say("You do not possess the funds to purchase [R.name].")
|
|
||||||
flick(icon_deny,src)
|
flick(icon_deny,src)
|
||||||
vend_ready = TRUE
|
return FALSE
|
||||||
return
|
|
||||||
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
|
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
|
||||||
if(D)
|
if(D)
|
||||||
D.adjust_money(price_to_use)
|
D.adjust_money(price)
|
||||||
|
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thank the user for the purchase
|
||||||
|
*/
|
||||||
|
/obj/machinery/vending/proc/thank_user(message)
|
||||||
if(last_shopper != usr || purchase_message_cooldown < world.time)
|
if(last_shopper != usr || purchase_message_cooldown < world.time)
|
||||||
say("Thank you for shopping with [src]!")
|
say(message)
|
||||||
purchase_message_cooldown = world.time + 5 SECONDS
|
purchase_message_cooldown = world.time + 5 SECONDS
|
||||||
last_shopper = usr
|
last_shopper = usr
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish a vend by consuming power, playing animations & playing sounds
|
||||||
|
*/
|
||||||
|
/obj/machinery/vending/proc/finish_vend()
|
||||||
use_power(5)
|
use_power(5)
|
||||||
if(icon_vend) //Show the vending animation if needed
|
if(icon_vend) //Show the vending animation if needed
|
||||||
flick(icon_vend,src)
|
flick(icon_vend,src)
|
||||||
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
|
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
|
||||||
var/obj/item/vended_item
|
|
||||||
if(!LAZYLEN(R.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items
|
|
||||||
vended_item = new R.product_path(get_turf(src))
|
|
||||||
else
|
|
||||||
vended_item = LAZYACCESS(R.returned_products, LAZYLEN(R.returned_products)) //first in, last out
|
|
||||||
LAZYREMOVE(R.returned_products, vended_item)
|
|
||||||
vended_item.forceMove(get_turf(src))
|
|
||||||
R.amount--
|
|
||||||
SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
|
|
||||||
vend_ready = TRUE
|
|
||||||
|
|
||||||
/obj/machinery/vending/process(delta_time)
|
/obj/machinery/vending/process(delta_time)
|
||||||
if(stat & (BROKEN|NOPOWER))
|
if(stat & (BROKEN|NOPOWER))
|
||||||
@@ -965,19 +1061,24 @@ GLOBAL_LIST_EMPTY(vending_products)
|
|||||||
if(!canload_access_list)
|
if(!canload_access_list)
|
||||||
return TRUE
|
return TRUE
|
||||||
else
|
else
|
||||||
var/do_you_have_access = FALSE
|
if((obj_flags & EMAGGED) || !scan_id)
|
||||||
var/req_access_txt_holder = req_access_txt
|
|
||||||
for(var/i in canload_access_list)
|
|
||||||
req_access_txt = i
|
|
||||||
if(!allowed(user) && !(obj_flags & EMAGGED) && scan_id)
|
|
||||||
continue
|
|
||||||
else
|
|
||||||
do_you_have_access = TRUE
|
|
||||||
break //you passed don't bother looping anymore
|
|
||||||
req_access_txt = req_access_txt_holder // revert to normal (before the proc ran)
|
|
||||||
if(do_you_have_access)
|
|
||||||
return TRUE
|
return TRUE
|
||||||
else
|
|
||||||
|
if(ishuman(user))
|
||||||
|
var/mob/living/carbon/human/H = user
|
||||||
|
var/obj/item/card/id/C = H.get_idcard(TRUE)
|
||||||
|
if(!C)
|
||||||
|
to_chat(user, span_warning("[src]'s input compartment blinks red: No card found."))
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
var/A = C.GetAccess()
|
||||||
|
// This is checking like `req_one_access` does (need any in list)
|
||||||
|
// The only thing that uses this is the chef compartment which only has one access in the list anyway
|
||||||
|
// If you add another, you may need to alter this behaviour
|
||||||
|
for(var/req in canload_access_list)
|
||||||
|
if(req in A)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
to_chat(user, span_warning("[src]'s input compartment blinks red: Access denied."))
|
to_chat(user, span_warning("[src]'s input compartment blinks red: Access denied."))
|
||||||
return FALSE
|
return FALSE
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,6 @@
|
|||||||
S.forceMove(get_turf(src))
|
S.forceMove(get_turf(src))
|
||||||
return ..()
|
return ..()
|
||||||
|
|
||||||
/obj/machinery/vending/snack/proc/food_load(obj/item/reagent_containers/food/snacks/S)
|
|
||||||
if(vending_machine_input[S.name])
|
|
||||||
vending_machine_input[S.name]++
|
|
||||||
else
|
|
||||||
vending_machine_input[S.name] = 1
|
|
||||||
sortList(vending_machine_input)
|
|
||||||
|
|
||||||
/obj/machinery/vending/snack/random
|
/obj/machinery/vending/snack/random
|
||||||
name = "\improper Random Snackies"
|
name = "\improper Random Snackies"
|
||||||
icon_state = "random_snack"
|
icon_state = "random_snack"
|
||||||
|
|||||||
@@ -24,12 +24,24 @@ const VendingRow = (props, context) => {
|
|||||||
|| data.ignores_capitalism
|
|| data.ignores_capitalism
|
||||||
// yogs end
|
// yogs end
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const customFree = (
|
||||||
|
!data.onstation
|
||||||
|
|| (
|
||||||
|
data.user
|
||||||
|
&& data.department
|
||||||
|
&& data.department === data.user.department
|
||||||
|
)
|
||||||
|
|| data.ignores_capitalism
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell collapsing>
|
<Table.Cell collapsing>
|
||||||
{product.base64 ? (
|
{product.base64 ? (
|
||||||
<img
|
<img
|
||||||
src={`data:image/jpeg;base64,${product.img}`}
|
src={`data:image/jpeg;base64,${product.img}`}
|
||||||
|
className="icon"
|
||||||
style={{
|
style={{
|
||||||
'vertical-align': 'middle',
|
'vertical-align': 'middle',
|
||||||
'horizontal-align': 'middle',
|
'horizontal-align': 'middle',
|
||||||
@@ -51,8 +63,7 @@ const VendingRow = (props, context) => {
|
|||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell collapsing textAlign="center">
|
<Table.Cell collapsing textAlign="center">
|
||||||
<Box
|
<Box
|
||||||
color={custom
|
color={custom ? (productStock > 0 ? 'good' : 'bad')
|
||||||
? 'good'
|
|
||||||
: productStock <= 0
|
: productStock <= 0
|
||||||
? 'bad'
|
? 'bad'
|
||||||
: productStock <= (product.max_amount / 2)
|
: productStock <= (product.max_amount / 2)
|
||||||
@@ -65,8 +76,13 @@ const VendingRow = (props, context) => {
|
|||||||
{custom && (
|
{custom && (
|
||||||
<Button
|
<Button
|
||||||
fluid
|
fluid
|
||||||
content={data.access ? 'FREE' : product.price + ' cr'}
|
disabled={(
|
||||||
onClick={() => act('dispense', {
|
productStock === 0
|
||||||
|
|| !data.user
|
||||||
|
|| (!free && product.price > data.user.cash)
|
||||||
|
)}
|
||||||
|
content={customFree ? 'FREE' : product.price + ' cr'}
|
||||||
|
onClick={() => act('vend_custom', {
|
||||||
'item': product.name,
|
'item': product.name,
|
||||||
})} />
|
})} />
|
||||||
) || (
|
) || (
|
||||||
@@ -93,13 +109,10 @@ export const Vending = (props, context) => {
|
|||||||
const { act, data } = useBackend(context);
|
const { act, data } = useBackend(context);
|
||||||
const {
|
const {
|
||||||
product_ad,
|
product_ad,
|
||||||
|
chef,
|
||||||
} = data;
|
} = data;
|
||||||
let inventory;
|
let inventory;
|
||||||
let custom = false;
|
if (data.extended_inventory) {
|
||||||
if (data.vending_machine_input) {
|
|
||||||
inventory = data.vending_machine_input;
|
|
||||||
custom = true;
|
|
||||||
} else if (data.extended_inventory) {
|
|
||||||
inventory = [
|
inventory = [
|
||||||
...data.product_records,
|
...data.product_records,
|
||||||
...data.coin_records,
|
...data.coin_records,
|
||||||
@@ -111,6 +124,17 @@ export const Vending = (props, context) => {
|
|||||||
...data.coin_records,
|
...data.coin_records,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customInventory = Object.keys(data.custom_stock).map(itemName => ({
|
||||||
|
product: {
|
||||||
|
name: itemName,
|
||||||
|
price: chef.price,
|
||||||
|
base64: true,
|
||||||
|
img: data.custom_stock[itemName].img,
|
||||||
|
},
|
||||||
|
productStock: data.custom_stock[itemName].amount,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Window
|
<Window
|
||||||
title="Vending Machine"
|
title="Vending Machine"
|
||||||
@@ -140,12 +164,24 @@ export const Vending = (props, context) => {
|
|||||||
)}
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
)}
|
)}
|
||||||
|
{!!customInventory.length &&
|
||||||
|
<Section title={chef.title} >
|
||||||
|
<Table>
|
||||||
|
{customInventory.map(customItem => (
|
||||||
|
<VendingRow
|
||||||
|
key={customItem.product.name}
|
||||||
|
custom
|
||||||
|
product={customItem.product}
|
||||||
|
productStock={customItem.productStock}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Table>
|
||||||
|
</Section>}
|
||||||
<Section title="Products" >
|
<Section title="Products" >
|
||||||
<Table>
|
<Table>
|
||||||
{inventory.map(product => (
|
{inventory.map(product => (
|
||||||
<VendingRow
|
<VendingRow
|
||||||
key={product.name}
|
key={product.name}
|
||||||
custom={custom}
|
|
||||||
product={product}
|
product={product}
|
||||||
productStock={data.stock[product.name]} />
|
productStock={data.stock[product.name]} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user