From 7ad7d5383dffe79a1c1829b95c2a8020c633afe9 Mon Sep 17 00:00:00 2001 From: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Date: Wed, 10 Apr 2024 03:28:39 -0400 Subject: [PATCH] Fix vendors (#10861) * 2019 shitcode, still around. Great * fixerino * prettierx * oh cool the nuke disk cant be moved off the zlevel by putting it in a vendor anymore * didnt mean to commit discounts * need to fix messed up credit rendering. Otherwise g2g * readding code from https://github.com/BeeStation/BeeStation-Hornet/pull/7559 * prettierx * fix balloon * revert * requested changes * progress * back * fix NaN * prettier --- code/__DEFINES/economy.dm | 1 - code/modules/vending/_vending.dm | 125 +++++++++++++++------- code/modules/vending/snack.dm | 66 ------------ tgui/packages/tgui/interfaces/Vending.tsx | 105 ++++++++++-------- 4 files changed, 144 insertions(+), 153 deletions(-) diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm index 972affb47c9da..ceee39ceca056 100644 --- a/code/__DEFINES/economy.dm +++ b/code/__DEFINES/economy.dm @@ -22,7 +22,6 @@ #define BUDGET_RATIO_TYPE_SINGLE 1 // For Service & Civilian budget #define BUDGET_RATIO_TYPE_DOUBLE 2 // and for the rest - #define ACCOUNT_CIV_ID "Civilian" #define ACCOUNT_CIV_NAME "Civil Budget" #define ACCOUNT_SRV_ID "Service" diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index cc928a429e305..c79d3550a5655 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -12,8 +12,6 @@ products = list() contraband = list() premium = list() - -IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm */ #define MAX_VENDING_INPUT_AMOUNT 30 @@ -26,19 +24,20 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C name = "generic" ///Typepath of the product that is created when this record "sells" var/product_path = null - ///How many of this product we currently have var/amount = 0 - ///How many we can store at maximum var/max_amount = 0 - ///Does the item have a custom price override var/custom_price - ///Does the item have a custom premium price override var/custom_premium_price - + /** GAGs recolorability + ///Whether the product can be recolored by the GAGS system + var/colorable + **/ + ///List of items that have been returned to the vending machine. + var/list/returned_products /// The category the product was in, if any. /// Sourced directly from product_categories. var/category @@ -164,8 +163,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C */ var/onstation = TRUE //if it doesn't originate from off-station during mapload, everything is free - var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. - ///A variable to change on a per instance basis on the map that allows the instance to force cost and ID requirements var/onstation_override = FALSE //change this on the object on the map to override the onstation check. DO NOT APPLY THIS GLOBALLY. @@ -203,6 +200,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C build_inv = TRUE . = ..() wires = new /datum/wires/vending(src) + if(build_inv) //non-constructable vending machine build_inventories() @@ -233,7 +231,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/machinery/vending/can_speak() return !shut_up -/obj/machinery/vending/RefreshParts() //Better would be to make constructable child +//Better would be to make constructable child +/obj/machinery/vending/RefreshParts() if(!component_parts) return @@ -260,12 +259,21 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C . = ..() if(!.) return + var/dump_amount = 0 var/found_anything = TRUE while (found_anything) found_anything = FALSE for(var/record in shuffle(product_records)) var/datum/data/vending_product/R = record + + //first dump any of the items that have been returned, in case they contain the nuke disk or something + for(var/obj/returned_obj_to_dump in R.returned_products) + LAZYREMOVE(R.returned_products, returned_obj_to_dump) + returned_obj_to_dump.forceMove(get_turf(src)) + step(returned_obj_to_dump, pick(GLOB.alldirs)) + R.amount-- + if(R.amount <= 0) //Try to use a record that actually has something to dump. continue var/dump_path = R.product_path @@ -275,8 +283,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C // busting open a vendor will destroy some of the contents if(found_anything && prob(80)) continue - var/obj/O = new dump_path(loc) - step(O, pick(GLOB.alldirs)) + + var/obj/obj_to_dump = new dump_path(loc) + step(obj_to_dump, pick(GLOB.alldirs)) found_anything = TRUE dump_amount++ if (dump_amount >= 16) @@ -313,6 +322,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C R.max_amount = amount R.custom_price = initial(temp.custom_price) R.custom_premium_price = initial(temp.custom_premium_price) + /* GAGS recolorability + //R.colorable = !!(initial(temp.greyscale_config) && initial(temp.greyscale_colors) && (initial(temp.flags_1) & IS_PLAYER_COLORABLE_1)) + */ R.category = product_to_category[typepath] recordlist += R @@ -354,7 +366,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C canister.contraband = contraband.Copy() if (!canister.premium) canister.premium = premium.Copy() + . = 0 + if (isnull(canister.product_categories) && !isnull(product_categories)) canister.product_categories = product_categories.Copy() @@ -404,6 +418,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C CRASH("Constructible vending machine did not have a refill canister") unbuild_inventory_into(product_records, R.products, R.product_categories) + R.contraband = unbuild_inventory(hidden_records) R.premium = unbuild_inventory(coin_records) @@ -468,10 +483,12 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/machinery/vending/wrench_act(mob/living/user, obj/item/I) ..() - if(panel_open) - default_unfasten_wrench(user, I, time = 6 SECONDS) + if(!panel_open) + return FALSE + if(default_unfasten_wrench(user, I, time = 6 SECONDS)) unbuckle_all_mobs(TRUE) - return TRUE + return TOOL_ACT_TOOLTYPE_SUCCESS + return FALSE /obj/machinery/vending/screwdriver_act(mob/living/user, obj/item/I) if(..()) @@ -481,7 +498,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C cut_overlays() if(panel_open) add_overlay("[initial(icon_state)]-panel") - updateUsrDialog() else to_chat(user, "You must first secure [src].") return TRUE @@ -490,6 +506,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(panel_open && is_wire_tool(I)) wires.interact(user) return + if(refill_canister && istype(I, refill_canister)) if (!panel_open) to_chat(user, "You should probably unscrew the service panel first.") @@ -511,7 +528,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(compartmentLoadAccessCheck(user) && user.a_intent != INTENT_HARM) if(canLoadItem(I)) loadingAttempt(I,user) - updateUsrDialog() //can't put this on the proc above because we spam it below + ui_update() if(istype(I, /obj/item/storage/bag)) //trays USUALLY var/obj/item/storage/T = I @@ -530,7 +547,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C to_chat(user, "[src] refuses some items!") if(loaded) to_chat(user, "You insert [loaded] dishes into [src]'s compartment.") - updateUsrDialog() + ui_update() else . = ..() if(tiltable && !tilted && I.force) @@ -560,12 +577,18 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/dump_path = R.product_path if(!dump_path) continue - + if(R.amount > LAZYLEN(R.returned_products)) //always give out new stuff that costs before free returned stuff, because of the risk getting gibbed involved + new dump_path(get_turf(src)) + else + var/obj/returned_obj_to_dump = LAZYACCESS(R.returned_products, LAZYLEN(R.returned_products)) //first in, last out + LAZYREMOVE(R.returned_products, returned_obj_to_dump) + returned_obj_to_dump.forceMove(get_turf(src)) R.amount-- - new dump_path(get_turf(src)) break /obj/machinery/vending/proc/tilt(mob/fatty, crit=FALSE) + if(QDELETED(src)) + return visible_message("[src] tips over!") tilted = TRUE layer = ABOVE_MOB_LAYER @@ -667,11 +690,18 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C . = TRUE if(!user.transferItemToLoc(I, src)) return FALSE + to_chat(user, "You insert [I] into [src]'s input compartment.") + + for(var/datum/data/vending_product/product_datum in product_records + coin_records + hidden_records) + if(ispath(I.type, product_datum.product_path)) + product_datum.amount++ + LAZYADD(product_datum.returned_products, I) + return + if(vending_machine_input[format_text(I.name)]) vending_machine_input[format_text(I.name)]++ else vending_machine_input[format_text(I.name)] = 1 - to_chat(user, "You insert [I] into [src]'s input compartment.") loaded_items++ /obj/machinery/vending/unbuckle_mob(mob/living/buckled_mob, force=FALSE) @@ -753,9 +783,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C get_asset_datum(/datum/asset/spritesheet_batched/vending), ) -/obj/machinery/vending/ui_state(mob/user) - return GLOB.default_state - /obj/machinery/vending/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) @@ -835,13 +862,16 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(R) .["user"]["job"] = R.fields["rank"] .["stock"] = list() + for (var/datum/data/vending_product/product_record in product_records + coin_records + hidden_records) var/list/product_data = list( name = product_record.name, amount = product_record.amount, + //colorable = product_record.colorable, ) .["stock"][product_record.name] = product_data + .["extended_inventory"] = extended_inventory /obj/machinery/vending/ui_act(action, params) @@ -851,8 +881,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C switch(action) if("vend") . = vend(params) - if("select_colors") - . = select_colors(params) + //if("select_colors") + // . = select_colors(params) /obj/machinery/vending/proc/can_vend(user, silent=FALSE) . = FALSE @@ -947,18 +977,18 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C flick(icon_deny, src) vend_ready = TRUE return - var/datum/bank_account/account = C.registered_account if(account.account_job && (account.active_departments & dept_req_for_free)) price_to_use = 0 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(price_to_use && !account.adjust_money(-price_to_use)) say("You do not possess the funds to purchase [R.name].") flick(icon_deny,src) vend_ready = TRUE return - if(price_to_use && seller_department) var/list/dept_list = SSeconomy.get_dept_id_by_bitflag(seller_department) if(length(dept_list)) @@ -967,15 +997,21 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(D) D.adjust_money(price_to_use) - if(last_shopper != usr || purchase_message_cooldown < world.time) + if(last_shopper != REF(usr) || purchase_message_cooldown < world.time) say("Thank you for shopping with [src]!") purchase_message_cooldown = world.time + 5 SECONDS - last_shopper = usr + last_shopper = REF(usr) use_power(5) if(icon_vend) //Show the vending animation if needed flick(icon_vend,src) playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) - var/obj/item/vended_item = new R.product_path(get_turf(src)) + 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)) if(greyscale_colors) vended_item.set_greyscale(colors=greyscale_colors) R.amount-- @@ -1037,7 +1073,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/obj/throw_item = null var/mob/living/target = locate() in view(7,src) if(!target || target.incorporeal_move >= INCORPOREAL_MOVE_BASIC) - return 0 + return FALSE for(var/datum/data/vending_product/R in shuffle(product_records)) if(R.amount <= 0) //Try to use a record that actually has something to dump. @@ -1045,19 +1081,23 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/dump_path = R.product_path if(!dump_path) continue - + if(R.amount > LAZYLEN(R.returned_products)) //always throw new stuff that costs before free returned stuff, because of the hacking effort and time between throws involved + throw_item = new dump_path(loc) + else + throw_item = LAZYACCESS(R.returned_products, LAZYLEN(R.returned_products)) //first in, last out + throw_item.forceMove(loc) + LAZYREMOVE(R.returned_products, throw_item) R.amount-- - throw_item = new dump_path(loc) break if(!throw_item) - return 0 + return FALSE pre_throw(throw_item) throw_item.throw_at(target, 16, 3) visible_message("[src] launches [throw_item] at [target]!") ui_update() - return 1 + return TRUE /** * A callback called before an item is tossed out * @@ -1097,6 +1137,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C * * user - the user doing the loading */ /obj/machinery/vending/proc/canLoadItem(obj/item/I, mob/user) + if((I.type in products) || (I.type in premium) || (I.type in contraband)) + return TRUE + balloon_alert(user, "[src] does not accept [I]!") return FALSE /obj/machinery/vending/onTransitZ() @@ -1144,7 +1187,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(loaded_items >= max_loaded_items) say("There are too many items in stock.") return - if(istype(I, /obj/item/stack)) + if(isstack(I)) say("Loose items may cause problems, try to use it inside wrapping paper.") return if(I.custom_price) @@ -1161,20 +1204,22 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/item_price = 0 var/item_path for(var/obj/T in contents) - if(T.name == O) + if(format_text(T.name) == O) item_price = T.custom_price item_path = T.type if(!base64) if(base64_cache["[T.icon]_[T.icon_state]"]) base64 = base64_cache["[T.icon]_[T.icon_state]"] else - base64 = icon2base64(icon(T.icon, T.icon_state, frame=1)) + base64 = icon2base64(getFlatIcon(T, no_anim=TRUE)) base64_cache["[T.icon]_[T.icon_state]"] = base64 break var/list/data = list( name = O, price = item_price, img = base64, + amount = vending_machine_input[O], + //colorable = FALSE, path = "[replacetext(replacetext("[item_path]", "/obj/item/", ""), "/", "-")]-[O]" ) .["stock"]["[replacetext(replacetext("[item_path]", "/obj/item/", ""), "/", "-")]-[O]"] = vending_machine_input[O] @@ -1196,7 +1241,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/obj/item/card/id/C = H.get_idcard(TRUE) for(var/obj/O in contents) - if(O.name == N) + if(format_text(O.name) == N) S = O break if(S) diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm index 4a1d2dca0c07e..66bdd88ec5488 100644 --- a/code/modules/vending/snack.dm +++ b/code/modules/vending/snack.dm @@ -24,72 +24,6 @@ /obj/item/vending_refill/snack machine_name = "Getmore Chocolate Corp" -/obj/machinery/vending/snack/attackby(obj/item/W, mob/user, params) - if(IS_EDIBLE(W)) - if(!compartment_access_check(user)) - return - var/obj/item/food/S = W - if(!S.junkiness) - if(!iscompartmentfull(user)) - if(!user.transferItemToLoc(W, src)) - return - food_load(W) - to_chat(user, "You insert [W] into [src]'s chef compartment.") - else - to_chat(user, "[src]'s chef compartment does not accept junk food.") - - else if(istype(W, /obj/item/storage/bag/tray)) - if(!compartment_access_check(user)) - return - var/obj/item/storage/T = W - var/loaded = 0 - var/denied_items = 0 - for(var/obj/item/food/S in T.contents) - if(iscompartmentfull(user)) - break - if(!S.junkiness) - SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, S, src, TRUE) - food_load(S) - loaded++ - else - denied_items++ - if(denied_items) - to_chat(user, "[src] refuses some items.") - if(loaded) - to_chat(user, "You insert [loaded] dishes into [src]'s chef compartment.") - updateUsrDialog() - return - - else - return ..() - -/obj/machinery/vending/snack/Destroy() - for(var/obj/item/food/S in contents) - S.forceMove(get_turf(src)) - return ..() - -/obj/machinery/vending/snack/proc/compartment_access_check(user) - req_access_txt = chef_compartment_access - if(!allowed(user) && !(obj_flags & EMAGGED) && scan_id) - to_chat(user, "[src]'s chef compartment blinks red: Access denied.") - req_access_txt = "0" - return 0 - req_access_txt = "0" - return 1 - -/obj/machinery/vending/snack/proc/iscompartmentfull(mob/user) - if(contents.len >= 30) // no more than 30 dishes can fit inside - to_chat(user, "[src]'s chef compartment is full.") - return 1 - return 0 - -/obj/machinery/vending/snack/proc/food_load(obj/item/food/S) - if(dish_quants[S.name]) - dish_quants[S.name]++ - else - dish_quants[S.name] = 1 - sort_list(dish_quants) - /obj/machinery/vending/snack/blue icon_state = "snackblue" diff --git a/tgui/packages/tgui/interfaces/Vending.tsx b/tgui/packages/tgui/interfaces/Vending.tsx index 360fdb84612d0..271d4acda2f2b 100644 --- a/tgui/packages/tgui/interfaces/Vending.tsx +++ b/tgui/packages/tgui/interfaces/Vending.tsx @@ -1,11 +1,12 @@ import { classes } from 'common/react'; import { useBackend, useLocalState } from 'tgui/backend'; -import { Box, Button, Icon, LabeledList, NoticeBox, Section, Stack, Table } from '../components'; -import { Window } from '../layouts'; +import { Box, Button, Icon, LabeledList, NoticeBox, Section, Stack, Table } from 'tgui/components'; +import { Window } from 'tgui/layouts'; type VendingData = { onstation: boolean; - department: string; + department_bitflag: string; + jobDiscount: number; product_records: ProductRecord[]; coin_records: CoinRecord[]; hidden_records: HiddenRecord[]; @@ -42,7 +43,7 @@ type UserData = { name: string; cash: number; job: string; - department: string; + department_bitflag: string; }; type StockItem = { @@ -94,6 +95,7 @@ export const Vending = (props, context) => { }); }) ); + return ( @@ -123,7 +125,7 @@ export const Vending = (props, context) => { }; /** Displays user details if an ID is present and the user is on the station */ -const UserDetails = (props, context) => { +export const UserDetails = (props, context) => { const { data } = useBackend(context); const { user } = data; @@ -160,6 +162,7 @@ const ProductDisplay = ( const { data } = useBackend(context); const { custom, inventory, selectedCategory } = props; const { stock, onstation, user } = data; + return (
{ const { data } = useBackend(context); const { custom, product, productStock } = props; - const { department, onstation, user } = data; - const free = !onstation || product.price === 0 || (!product.premium && department && user); + const { access, department_bitflag, jobDiscount, onstation, user } = data; + const free = !onstation || product.price === 0 || (!product.premium && department_bitflag === user?.department_bitflag); + const discount = !product.premium && department_bitflag === user?.department_bitflag; + const remaining = custom ? product.amount : productStock.amount; + const redPrice = Math.round(product.price * jobDiscount); + const disabled = + remaining === 0 || (onstation && !user) || (onstation && !access && (discount ? redPrice : product.price) > user?.cash); return ( @@ -206,12 +214,19 @@ const VendingRow = (props, context) => { {product.name.replace(/^\w/, (c) => c.toUpperCase())} - {!!productStock?.colorable && } + {!!productStock?.colorable && } - + - + ); @@ -226,6 +241,7 @@ const ProductImage = (props) => { src={`data:image/jpeg;base64,${product.img}`} style={{ 'vertical-align': 'middle', + 'text-align': 'center', }} /> ) : ( @@ -233,77 +249,74 @@ const ProductImage = (props) => { className={classes(['vending32x32', product.path])} style={{ 'vertical-align': 'middle', + 'text-align': 'center', }} /> ); }; -/** Displays a colored indicator for remaining stock */ -const ProductStock = (props) => { - const { custom, product, productStock } = props; - - return ( - - {custom ? product.amount : productStock.amount} left - - ); -}; - /** In the case of customizable items, ie: shoes, * this displays a color wheel button that opens another window. */ const ProductColorSelect = (props, context) => { - const { act, data } = useBackend(context); - const { user, onstation, department } = data; - const { product, productStock } = props; - const free = !onstation || product.price === 0 || (!product.premium && department && user); + const { act } = useBackend(context); + const { disabled, product } = props; return ( ) : ( ); };