[MIRROR] Produce consoles now grey out their buttons if the cost criteria aren't met & have dynamic window sizes. [MDB IGNORE] (#20653)

* Produce consoles now grey out their buttons if the cost criteria aren't met & have dynamic window sizes. (#74773)

## About The Pull Request
Fixes #74645
Fixes #72536

If your total order is less than 200 credits/mining points then the
purchase button is greyed out telling you just that
![Screenshot
(169)](https://user-images.githubusercontent.com/110812394/232320295-bb8a3c14-1f4d-4823-ba7d-3557011166fe.png)

Bonus. If your cart is empty then the express button is also greyed out
telling you to order atleast 1 item
![Screenshot
(170)](https://user-images.githubusercontent.com/110812394/232320326-8fd59734-c3fe-4a5e-9655-bdf62288456d.png)

Even more bonus, the width of the window is dynamically adjusted based
on the number of order categories so golem mining console is no longer
obscured
![Screenshot
(171)](https://user-images.githubusercontent.com/110812394/232320401-f6ac65f6-0f93-4426-9ca6-7b4398c613f0.png)

Code changes
1. `var/cargo_cost_multiplier` : as the name implies is now the cost
applied to all stuff ordered through cargo. it takes top
priority(because we assume it will always be cheaper than express) and
all item costs are multiplied with this before being displayed on the
UI. So mining console has this value as 0.65 as intended
2. `proc/retrive_points()` : is used to retrieve the type of points this
console is dealing with from the mobs id card, so for mining it returns
the card's mining point's, for others it returns the card's cash, if you
want to introduce a new currency type and make your own console for it,
make sure you override this proc
3. `proc/subtract_points()` : is used to subtract these points(money,
mining points) from the id card after the order is confirmed, return
true if it was successful, false otherwise to cancel the order

## Changelog
🆑
fix: order consoles cancelling order's less than 200 but still
subtracting money, mining points from the player
code: multiplier for all shipments made through cargo
refactor: 2 new procs retrive_points() & subtract_points() to dela with
different types
/🆑

* Produce consoles now grey out their buttons if the cost criteria aren't met & have dynamic window sizes.

---------

Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
This commit is contained in:
SkyratBot
2023-04-20 02:25:55 +01:00
committed by GitHub
co-authored by SyncIt21
parent 06d163083f
commit a05eb41126
3 changed files with 119 additions and 77 deletions
@@ -1,5 +1,3 @@
#define MINING_SHIPPING_MULTIPLIER 0.65
#define GET_MINING_SHIPPING_MULTIPLIER(cost) round(cost * MINING_SHIPPING_MULTIPLIER, 5)
#define CREDIT_TYPE_MINING "mp"
/obj/machinery/computer/order_console/mining
@@ -10,14 +8,13 @@
icon_keyboard = null
icon_screen = null
circuit = /obj/item/circuitboard/computer/order_console/mining
cooldown_time = 10 SECONDS //just time to let you know your order went through.
cargo_cost_multiplier = 0.65
express_cost_multiplier = 1
purchase_tooltip = @{"Your purchases will arrive at cargo,
and hopefully get delivered by them.
35% cheaper than express delivery."}
express_tooltip = @{"Sends your purchases instantly."}
credit_type = CREDIT_TYPE_MINING
order_categories = list(
@@ -28,15 +25,10 @@
)
blackbox_key = "mining"
/obj/machinery/computer/order_console/mining/purchase_items(obj/item/card/id/card, express = FALSE)
var/final_cost = get_total_cost()
var/failure_message = "Sorry, but you do not have enough mining points."
if(!express)
final_cost = GET_MINING_SHIPPING_MULTIPLIER(final_cost)
/obj/machinery/computer/order_console/mining/subtract_points(final_cost, obj/item/card/id/card)
if(final_cost <= card.registered_account.mining_points)
card.registered_account.mining_points -= final_cost
return TRUE
say(failure_message)
return FALSE
/obj/machinery/computer/order_console/mining/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries)
@@ -67,33 +59,14 @@
radio.talk_into(src, "A shaft miner has ordered equipment which will arrive on the cargo shuttle! Please make sure it gets to them as soon as possible!", radio_channel)
SSshuttle.shopping_list += new_order
/obj/machinery/computer/order_console/mining/ui_data(mob/user)
var/list/data = ..()
var/cost = get_total_cost()
data["total_cost"] = "[GET_MINING_SHIPPING_MULTIPLIER(cost)] (Express: [cost]) "
if(!isliving(user))
return data
var/mob/living/living_user = user
var/obj/item/card/id/id_card = living_user.get_idcard(TRUE)
if(id_card)
data["points"] = id_card.registered_account.mining_points
return data
/obj/machinery/computer/order_console/mining/retrive_points(obj/item/card/id/id_card)
return FLOOR(id_card.registered_account.mining_points, 1)
/obj/machinery/computer/order_console/mining/ui_act(action, params)
. = ..()
if(!.)
flick("mining-deny", src)
/obj/machinery/computer/order_console/mining/ui_static_data(mob/user)
var/list/data = ..()
for(var/list/order in data["order_datums"])
var/cost = order["cost"]
if(isnull(cost)) //sanity check
continue
order["cost"] = GET_MINING_SHIPPING_MULTIPLIER(cost) // change costs to reflect mining shipping instead
return data
/obj/machinery/computer/order_console/mining/attackby(obj/item/weapon, mob/user, params)
if(istype(weapon, /obj/item/mining_voucher))
redeem_voucher(weapon, user)
@@ -215,5 +188,3 @@
#undef CREDIT_TYPE_MINING
#undef TO_POINT_CARD
#undef TO_USER_ID
#undef MINING_SHIPPING_MULTIPLIER
#undef GET_MINING_SHIPPING_MULTIPLIER
@@ -28,7 +28,9 @@ GLOBAL_LIST_EMPTY(order_console_products)
var/credit_type = CREDIT_TYPE_CREDIT
///Whether the console can only use express mode ONLY
var/forced_express = FALSE
///Multiplied cost to use express mode
///Multiplied cost to use for cargo mode
var/cargo_cost_multiplier = 1
///Multiplied cost to use for express mode
var/express_cost_multiplier = 2
///The categories of orderable items this console can view and purchase.
var/list/order_categories = list()
@@ -69,10 +71,17 @@ GLOBAL_LIST_EMPTY(order_console_products)
ui = new(user, src, "ProduceConsole", name)
ui.open()
/**
* points is any type of currency this machine accepts(money, mining points etc) which is displayed on the ui
* Args:
* card - The ID card we retrive these points from
*/
/obj/machinery/computer/order_console/proc/retrive_points(obj/item/card/id/id_card)
return FLOOR(id_card.registered_account?.account_balance, 1)
/obj/machinery/computer/order_console/ui_data(mob/user)
var/list/data = list()
var/cost = get_total_cost()
data["total_cost"] = "[cost] (Express: [cost * express_cost_multiplier])"
data["total_cost"] = get_total_cost()
data["off_cooldown"] = COOLDOWN_FINISHED(src, order_cooldown)
if(!isliving(user))
@@ -80,7 +89,7 @@ GLOBAL_LIST_EMPTY(order_console_products)
var/mob/living/living_user = user
var/obj/item/card/id/id_card = living_user.get_idcard(TRUE)
if(id_card)
data["points"] = id_card.registered_account?.account_balance
data["points"] = retrive_points(id_card)
for(var/datum/orderable_item/item as anything in GLOB.order_console_products)
if(!(item.category_index in order_categories))
continue
@@ -97,17 +106,21 @@ GLOBAL_LIST_EMPTY(order_console_products)
data["express_tooltip"] = express_tooltip
data["purchase_tooltip"] = purchase_tooltip
data["forced_express"] = forced_express
data["cargo_value"] = CARGO_CRATE_VALUE
data["cargo_cost_multiplier"] = cargo_cost_multiplier
data["express_cost_multiplier"] = express_cost_multiplier
data["order_categories"] = order_categories
data["order_datums"] = list()
for(var/datum/orderable_item/item as anything in GLOB.order_console_products)
if(!(item.category_index in order_categories))
continue
data["order_datums"] += list(list(
"name" = item.name,
"desc" = item.desc,
"cat" = item.category_index,
"ref" = REF(item),
"cost" = item.cost_per_order,
"cost" = FLOOR(item.cost_per_order * cargo_cost_multiplier, 1),
"product_icon" = icon2base64(getFlatIcon(image(icon = initial(item.item_path.icon), icon_state = initial(item.item_path.icon_state)), no_anim=TRUE))
))
return data
@@ -136,21 +149,20 @@ GLOBAL_LIST_EMPTY(order_console_products)
grocery_list[wanted_item] = clamp(params["amt"], 0, 20)
if(!grocery_list[wanted_item])
grocery_list -= wanted_item
if("purchase", "ltsrbt_deliver")
if("purchase")
if(!grocery_list.len || !COOLDOWN_FINISHED(src, order_cooldown))
return
if(forced_express)
return ui_act(action = "express")
//So miners cant spam buy crates for a very low price
if(get_total_cost() < CARGO_CRATE_VALUE)
return
var/obj/item/card/id/used_id_card = living_user.get_idcard(TRUE)
if(!used_id_card || !used_id_card.registered_account)
say("No bank account detected!")
return
if(!purchase_items(used_id_card))
return
//So miners cant spam buy crates for a very low price
if(get_total_cost() < CARGO_CRATE_VALUE)
say("For the delivery order needs to cost more or equal to [CARGO_CRATE_VALUE] points!")
return
if(blackbox_key)
SSblackbox.record_feedback("tally", "non_express_[blackbox_key]_order", 1, name)
order_groceries(living_user, used_id_card, grocery_list)
@@ -197,16 +209,30 @@ GLOBAL_LIST_EMPTY(order_console_products)
* returns TRUE if we can afford, FALSE otherwise.
*/
/obj/machinery/computer/order_console/proc/purchase_items(obj/item/card/id/card, express = FALSE)
var/final_cost = get_total_cost()
var/failure_message = "Sorry, but you do not have enough money."
if(express)
final_cost *= express_cost_multiplier
failure_message += " Remember, Express upcharges the cost!"
if(card.registered_account.adjust_money(-final_cost, "[name]: Purchase"))
var/final_cost = get_total_cost() * (express ? express_cost_multiplier : cargo_cost_multiplier)
var/failure_message = !express ? "Sorry, but you do not have enough [credit_type]." : " Remember, Express upcharges the cost!"
if(subtract_points(final_cost, card))
return TRUE
say(failure_message)
return FALSE
/**
* whatever type of points was retrived in retrive_points() subtract those type of points from the card upon confirming order
* Args:
* final_cost - amount of points to subtract from this card
* card - The ID card to subtract these points from
* returns TRUE if successfull
*/
/obj/machinery/computer/order_console/proc/subtract_points(final_cost, obj/item/card/id/card)
return card.registered_account.adjust_money(-final_cost, "[name]: Purchase")
/**
* start of the shipment of your order
* Args:
* purchaser - The mob who is making this purchase
* card - The card used to place this order
* groceries - the list of orders to be placed
*/
/obj/machinery/computer/order_console/proc/order_groceries(mob/living/purchaser, obj/item/card/id/card, list/groceries)
return
@@ -1,3 +1,4 @@
import { BooleanLike } from 'common/react';
import { capitalize, createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Dimmer, Divider, Icon, Input, NumberInput, Section, Stack, Tabs } from '../components';
@@ -5,6 +6,36 @@ import { Window } from '../layouts';
const buttonWidth = 2;
type OrderDatum = {
name: string;
desc: number;
cat: string;
ref: string;
cost: number;
product_icon: string;
};
type Item = {
name: string;
amt: number;
};
type Data = {
credit_type: string;
off_cooldown: BooleanLike;
points: number;
express_tooltip: string;
purchase_tooltip: string;
forced_express: string;
cargo_value: number;
cargo_cost_multiplier: number;
express_cost_multiplier: number;
order_categories: string[];
order_datums: OrderDatum[];
item_amts: Item[];
total_cost: number;
};
const TAB2NAME = [
{
component: () => ShoppingTab,
@@ -20,34 +51,41 @@ const findAmount = (item_amts, name) => {
};
const ShoppingTab = (props, context) => {
const { data, act } = useBackend(context);
const { data, act } = useBackend<Data>(context);
const { credit_type, order_categories, order_datums, item_amts } = data;
const [shopIndex, setShopIndex] = useLocalState(context, 'shop-index', 1);
const [condensed, setCondensed] = useLocalState(context, 'condensed', false);
const [shopCategory, setShopCategory] = useLocalState(
context,
'shopCategory',
order_categories[0]
);
const [condensed] = useLocalState(context, 'condensed', false);
const [searchItem, setSearchItem] = useLocalState(context, 'searchItem', '');
const search = createSearch(searchItem, (order_datums) => order_datums.name);
const search = createSearch<OrderDatum>(
searchItem,
(order_datums) => order_datums.name
);
let goods =
searchItem.length > 0
? data.order_datums.filter(search)
: order_datums.filter((item) => item && item.cat === shopIndex);
? order_datums.filter((item) => search(item) && item.cat === shopCategory)
: order_datums.filter((item) => item && item.cat === shopCategory);
return (
<Stack fill vertical>
<Section mb={-1}>
<Stack.Item>
<Tabs>
{order_categories.map((item, key) => (
{order_categories.map((category) => (
<Tabs.Tab
key={item.id}
selected={item === shopIndex}
key={category}
selected={category === shopCategory}
onClick={() => {
setShopIndex(item);
setShopCategory(category);
if (searchItem.length > 0) {
setSearchItem('');
}
}}>
{item}
{category}
</Tabs.Tab>
))}
<Stack.Item grow>
@@ -60,10 +98,6 @@ const ShoppingTab = (props, context) => {
value={searchItem}
onInput={(e, value) => {
setSearchItem(value);
if (value.length > 0) {
setShopIndex(1);
}
}}
fluid
/>
@@ -75,8 +109,8 @@ const ShoppingTab = (props, context) => {
<Section fill scrollable>
<Stack vertical mt={-2}>
<Divider />
{goods.map((item) => (
<Stack.Item key={item}>
{goods.map((item, key) => (
<Stack.Item key={key}>
<Stack>
<span
style={{
@@ -154,17 +188,20 @@ const ShoppingTab = (props, context) => {
};
const CheckoutTab = (props, context) => {
const { data, act } = useBackend(context);
const { data, act } = useBackend<Data>(context);
const {
credit_type,
purchase_tooltip,
express_tooltip,
forced_express,
cargo_value,
order_datums,
total_cost,
cargo_cost_multiplier,
express_cost_multiplier,
item_amts,
} = data;
const total_cargo_cost = Math.floor(total_cost * cargo_cost_multiplier);
const checkout_list = order_datums.filter(
(food) => food && (findAmount(item_amts, food.name) || 0)
);
@@ -187,8 +224,8 @@ const CheckoutTab = (props, context) => {
</>
)}
<Stack.Item grow>
{checkout_list.map((item) => (
<Stack.Item key={item}>
{checkout_list.map((item, key) => (
<Stack.Item key={key}>
<Stack>
<Stack.Item>{capitalize(item.name)}</Stack.Item>
<Stack.Item grow mt={-1} color="label" fontSize="10px">
@@ -228,7 +265,8 @@ const CheckoutTab = (props, context) => {
<Section>
<Stack>
<Stack.Item grow mt={0.5}>
Total Cost: {total_cost}
Total Cost:{total_cargo_cost}&#40;Express:
{total_cost * express_cost_multiplier}&#41;
</Stack.Item>
{!forced_express && (
<Stack.Item grow textAlign="center">
@@ -236,7 +274,12 @@ const CheckoutTab = (props, context) => {
fluid
icon="plane-departure"
content="Purchase"
tooltip={purchase_tooltip}
disabled={total_cargo_cost < cargo_value}
tooltip={
total_cargo_cost < cargo_value
? `Total Cost must be above or equal to ${cargo_value}`
: purchase_tooltip
}
tooltipPosition="top"
onClick={() => act('purchase')}
/>
@@ -248,7 +291,10 @@ const CheckoutTab = (props, context) => {
icon="parachute-box"
color="yellow"
content="Express"
tooltip={express_tooltip}
disabled={total_cost <= 0}
tooltip={
total_cost <= 0 ? 'Order atleast 1 item' : express_tooltip
}
tooltipPosition="top-start"
onClick={() => act('express')}
/>
@@ -261,7 +307,6 @@ const CheckoutTab = (props, context) => {
};
const OrderSent = (props, context) => {
const { act, data } = useBackend(context);
return (
<Dimmer>
<Stack vertical>
@@ -277,13 +322,13 @@ const OrderSent = (props, context) => {
};
export const ProduceConsole = (props, context) => {
const { act, data } = useBackend(context);
const { points, off_cooldown } = data;
const { data } = useBackend<Data>(context);
const { points, off_cooldown, order_categories } = data;
const [tabIndex, setTabIndex] = useLocalState(context, 'tab-index', 1);
const [condensed, setCondensed] = useLocalState(context, 'condensed', false);
const TabComponent = TAB2NAME[tabIndex - 1].component();
return (
<Window width={500} height={400}>
<Window width={Math.max(order_categories.length * 125, 500)} height={400}>
<Window.Content>
{!off_cooldown && <OrderSent />}
<Stack vertical fill>