mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-23 08:31:57 +00:00
VueUI vending machines: buying space cola with aesthetics (#10197)
* Port vending machines to Vue UI, implement ordering GUI * changelog and whitespace * handle coins, fix lint, remove comments * add reset button
This commit is contained in:
@@ -9,14 +9,16 @@
|
||||
var/price = 0 // Price to buy one
|
||||
var/display_color = null // Display color for vending machine listing
|
||||
var/category = CAT_NORMAL // CAT_HIDDEN for contraband, CAT_COIN for premium
|
||||
var/icon/product_icon = null
|
||||
var/icon/product_icon_greyscale = null
|
||||
|
||||
/datum/data/vending_product/New(var/path, var/name = null, var/amount = 1, var/price = 0, var/color = null, var/category = CAT_NORMAL)
|
||||
..()
|
||||
|
||||
src.product_path = path
|
||||
var/atom/tmp = new path(null)
|
||||
|
||||
if(!name)
|
||||
var/atom/tmp = path
|
||||
src.product_name = initial(tmp.name)
|
||||
else
|
||||
src.product_name = name
|
||||
@@ -25,6 +27,10 @@
|
||||
src.price = price
|
||||
src.display_color = color
|
||||
src.category = category
|
||||
src.product_icon = new /icon(tmp.icon, tmp.icon_state)
|
||||
src.product_icon_greyscale = new /icon(tmp.icon, tmp.icon_state)
|
||||
src.product_icon_greyscale.GrayScale()
|
||||
QDEL_NULL(tmp)
|
||||
|
||||
/**
|
||||
* A vending machine
|
||||
@@ -51,6 +57,7 @@
|
||||
var/active = 1 //No sales pitches if off!
|
||||
var/vend_ready = 1 //Are we ready to vend?? Is it time??
|
||||
var/vend_delay = 10 //How long does it take to vend?
|
||||
var/vending = FALSE
|
||||
|
||||
var/categories = CAT_NORMAL // Bitmask of cats we're currently showing
|
||||
var/datum/data/vending_product/currently_vending = null // What we're requesting payment for right now
|
||||
@@ -99,6 +106,7 @@
|
||||
var/restock_items = 0 //If items can be restocked into the vending machine
|
||||
var/list/restock_blocked_items = list() //Items that can not be restocked if restock_items is enabled
|
||||
var/random_itemcount = 1 //If the number of items should be randomized
|
||||
var/sel_key = 0
|
||||
|
||||
var/temperature_setting = 0 //-1 means cooling, 1 means heating, 0 means doing nothing.
|
||||
|
||||
@@ -252,10 +260,11 @@
|
||||
handled = 1
|
||||
|
||||
if(paid)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
src.vend(currently_vending, usr)
|
||||
return
|
||||
else if(handled)
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
return // don't smack that machine with your 2 credits
|
||||
|
||||
if (I || istype(W, /obj/item/spacecash))
|
||||
@@ -268,7 +277,6 @@
|
||||
add_screen_overlay()
|
||||
if(src.panel_open)
|
||||
add_overlay("[initial(icon_state)]-panel")
|
||||
SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI
|
||||
return
|
||||
else if(W.ismultitool()||W.iswirecutter())
|
||||
if(src.panel_open)
|
||||
@@ -279,7 +287,7 @@
|
||||
coin = W
|
||||
categories |= CAT_COIN
|
||||
to_chat(user, "<span class='notice'>You insert \the [W] into \the [src].</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
return
|
||||
else if(W.iswrench())
|
||||
if(!can_move)
|
||||
@@ -484,6 +492,7 @@
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/vending/attack_hand(mob/user as mob)
|
||||
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
|
||||
@@ -491,27 +500,41 @@
|
||||
if(src.shock(user, 100))
|
||||
return
|
||||
|
||||
if (panel_open)
|
||||
wires.Interact(user)
|
||||
else
|
||||
ui_interact(user)
|
||||
|
||||
/**
|
||||
* Display the NanoUI window for the vending machine.
|
||||
*
|
||||
* See NanoUI documentation for details.
|
||||
*/
|
||||
/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
// VueUI implementation of vending machines.
|
||||
/obj/machinery/vending/ui_interact(mob/user, var/datum/topic_state/state = default_state)
|
||||
|
||||
user.set_machine(src)
|
||||
|
||||
var/list/data = list()
|
||||
if(currently_vending)
|
||||
var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
|
||||
if(!ui)
|
||||
ui = new(user, src, "machinery-vending", 400, 500, capitalize(name), state=state)
|
||||
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/vending/vueui_data_change(list/data, mob/user, datum/vueui/ui)
|
||||
|
||||
data = list()
|
||||
|
||||
if(currently_vending || !vend_ready)
|
||||
data["mode"] = 1
|
||||
data["product"] = currently_vending.product_name
|
||||
data["price"] = currently_vending.price
|
||||
data["message_err"] = 0
|
||||
data["sel_key"] = sel_key
|
||||
data["sel_name"] = capitalize(currently_vending.product_name)
|
||||
data["sel_price"] = currently_vending.price
|
||||
data["message"] = src.status_message
|
||||
data["message_err"] = src.status_error
|
||||
else
|
||||
data["mode"] = 0
|
||||
data["sel_key"] = 0
|
||||
data["sel_name"] = 0
|
||||
data["sel_price"] = 0
|
||||
data["message"] = ""
|
||||
data["message_err"] = 0
|
||||
|
||||
var/list/listed_products = list()
|
||||
|
||||
for(var/key = 1 to src.product_records.len)
|
||||
@@ -521,16 +544,29 @@
|
||||
continue
|
||||
|
||||
listed_products.Add(list(list(
|
||||
"key" = key,
|
||||
"name" = I.product_name,
|
||||
"key" = num2text(key),
|
||||
"name" = capitalize(I.product_name),
|
||||
"price" = I.price,
|
||||
"color" = I.display_color,
|
||||
"amount" = I.amount)))
|
||||
|
||||
var/icon/item_icon
|
||||
|
||||
if(I.amount > 0)
|
||||
item_icon = I.product_icon
|
||||
ui.add_asset(num2text(key), item_icon)
|
||||
ui.send_asset(num2text(key))
|
||||
else
|
||||
var/icon/grey_icon = I.product_icon_greyscale
|
||||
ui.add_asset(num2text(key) + "g", grey_icon)
|
||||
ui.send_asset(num2text(key) + "g")
|
||||
ui.push_change(null)
|
||||
|
||||
data["products"] = listed_products
|
||||
|
||||
if(src.coin)
|
||||
data["coin"] = src.coin.name
|
||||
if(coin)
|
||||
data["coin"] = coin.name
|
||||
else
|
||||
data["coin"] = null
|
||||
|
||||
if(src.panel_open)
|
||||
data["panel"] = 1
|
||||
@@ -538,13 +574,10 @@
|
||||
else
|
||||
data["panel"] = 0
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "vending_machine.tmpl", src.name, 440, 600)
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
return data
|
||||
|
||||
/obj/machinery/vending/Topic(href, href_list)
|
||||
|
||||
var/datum/money_account/vendor_account = SSeconomy.get_department_account("Vendor")
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
@@ -576,6 +609,7 @@
|
||||
return
|
||||
|
||||
var/key = text2num(href_list["vendItem"])
|
||||
sel_key = href_list["vendItem"]
|
||||
var/datum/data/vending_product/R = product_records[key]
|
||||
|
||||
// This should not happen unless the request from NanoUI was bad
|
||||
@@ -583,6 +617,7 @@
|
||||
return
|
||||
|
||||
if(R.price <= 0)
|
||||
src.currently_vending = R
|
||||
src.vend(R, usr)
|
||||
else if(istype(usr,/mob/living/silicon)) //If the item is not free, provide feedback if a synth is trying to buy something.
|
||||
to_chat(usr, "<span class='danger'>Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled.</span>")
|
||||
@@ -599,13 +634,19 @@
|
||||
else if (href_list["cancelpurchase"])
|
||||
src.currently_vending = null
|
||||
|
||||
else if (href_list["reset"])
|
||||
// reset button that nobody should ever (hopefully) see
|
||||
src.currently_vending = null
|
||||
src.vend_ready = 1
|
||||
|
||||
else if ((href_list["togglevoice"]) && (src.panel_open))
|
||||
src.shut_up = !src.shut_up
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
|
||||
/obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user)
|
||||
|
||||
if (!R || R.amount < 1)
|
||||
return
|
||||
|
||||
@@ -622,7 +663,7 @@
|
||||
src.vend_ready = 0 //One thing at a time!!
|
||||
src.status_message = "Vending..."
|
||||
src.status_error = 0
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
|
||||
if (R.category & CAT_COIN)
|
||||
if(!coin)
|
||||
@@ -661,6 +702,7 @@
|
||||
addtimer(CALLBACK(src, .proc/vend_product, R, user), vend_delay)
|
||||
|
||||
/obj/machinery/vending/proc/vend_product(var/datum/data/vending_product/R, mob/user)
|
||||
|
||||
var/vending_usr_dir = get_dir(src, user)
|
||||
var/obj/vended = new R.product_path(get_step(src, vending_usr_dir))
|
||||
if(Adjacent(user))
|
||||
@@ -669,7 +711,7 @@
|
||||
src.status_error = 0
|
||||
src.vend_ready = 1
|
||||
currently_vending = null
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
if(istype(vended,/obj/item/reagent_containers/))
|
||||
var/obj/item/reagent_containers/RC = vended
|
||||
if(RC.reagents)
|
||||
@@ -680,10 +722,11 @@
|
||||
use_power(RC.reagents.set_temperature(heating_temperature))
|
||||
|
||||
/obj/machinery/vending/proc/stock(var/datum/data/vending_product/R, var/mob/user)
|
||||
|
||||
to_chat(user, "<span class='notice'>You insert \the [R.product_name] in the product receptor.</span>")
|
||||
R.amount++
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
SSvueui.check_uis_for_change(src)
|
||||
|
||||
/obj/machinery/vending/machinery_process()
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
|
||||
6
html/changelogs/johnwildkins-vending_ui.yml
Normal file
6
html/changelogs/johnwildkins-vending_ui.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
author: JohnWildkins
|
||||
|
||||
delete-after: True
|
||||
|
||||
changes:
|
||||
- rscadd: "Vending machines now have an icon-based item purchasing interface, based in VueUI."
|
||||
184
vueui/src/components/view/machinery/vending.vue
Normal file
184
vueui/src/components/view/machinery/vending.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="mode == 0 || sel_price == 0">
|
||||
<div class="cancel-button">
|
||||
<vui-button v-if="coin" :params="{ remove_coin: 1 }" icon="sign-out-alt">{{ coin }}</vui-button>
|
||||
</div>
|
||||
<div class="t-parent">
|
||||
<vui-button :class="in_stock(vend_item.amount)" class="t-child tooltip" :disabled="vend_item.amount == 0 || mode == 1" push-state :params="{ vendItem: vend_item.key }" v-for="vend_item in products" :key="vend_item.key">
|
||||
<div class="t-container">
|
||||
<vui-img :class="in_stock(vend_item.amount)" class="food-icon" :name="getImage(vend_item)"/>
|
||||
<span v-if="vend_item.price > 0" class="cart-icon fas ic-shopping-cart"/>
|
||||
<span v-if="vend_item.price > 0" class="price">{{ vend_item.price }}电</span>
|
||||
<span class="qty" :class="in_stock(vend_item.amount)">(x{{ vend_item.amount }})</span>
|
||||
</div>
|
||||
<span class="tooltiptext">{{ vend_item.name }}</span>
|
||||
</vui-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="sel_name && sel_price > 0">
|
||||
<div class="t-parent">
|
||||
<p>Purchasing<vui-img class="purchase-icon" v-if="$root.$data.assets[sel_key]" :name="sel_key" />{{sel_name}} for {{sel_price}}电:</p>
|
||||
<p>Swipe ID or insert credits to purchase.</p>
|
||||
<p v-if="message_err == 1" class="danger">{{message}}</p>
|
||||
<div class="cancel-button">
|
||||
<vui-button :params="{ cancelpurchase: 1 }" icon="undo">Cancel Transaction</vui-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<vui-button class="cancel-button danger" :params="{ reset: 1}" icon="undo">Reset Machine</vui-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return this.$root.$data.state;
|
||||
},
|
||||
methods: {
|
||||
in_stock: function(amt) {
|
||||
if (amt <= 0) {
|
||||
return "no-stock";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
getImage: function(i) {
|
||||
if (i.amount == 0) {
|
||||
return i.key + "g"
|
||||
} else {
|
||||
return i.key
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.t-parent > p {
|
||||
text-align: center;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
p.danger {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.purchase-icon {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.food-icon {
|
||||
height: 75%;
|
||||
}
|
||||
|
||||
.t-parent {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
outline-style: ridge;
|
||||
outline-color: black;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.t-child {
|
||||
width: 22.5%;
|
||||
height: auto;
|
||||
white-space: normal;
|
||||
box-sizing: border-box;
|
||||
margin: 4px 4px 4px 4px;
|
||||
background-color: rgba(64, 98, 138, 0.4);
|
||||
}
|
||||
|
||||
.t-container {
|
||||
height: 100px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.statusValue {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.no-stock {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.cart-icon {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.price {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
left: 0;
|
||||
right: 15px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.qty {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.no-stock.button {
|
||||
background: inherit;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Tooltip container */
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: #202020;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
border-radius: 6px;
|
||||
opacity: 0;
|
||||
transition: opacity 1s;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -60px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: #202020 transparent transparent transparent;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user