From 71f0806bbc3c5984270a8f065dc27240ab5e51f4 Mon Sep 17 00:00:00 2001
From: Drathek <76988376+Drulikar@users.noreply.github.com>
Date: Wed, 15 May 2024 06:19:10 -0700
Subject: [PATCH] Med Vendor Rebalance (#6103)
# About the pull request
This PR is a follow up to #5677 and does many things:
- ~~Disables splitting of medical items.~~
- ~~In the case of splints, to avoid splitting them via applying and
removing them, recovered splints are considered contaminated. However,
you can get them to not be deemed contaminated if you form a full stack
with them.~~
- Increases the starting stock of several medical vendors, also shifting
more of the bottles to chem vendors.
- Adds periodic automatic restocking to med linked medical vendors if
they are operable.
- Initializes groundside medical vendors with randomly reduced stock
amounts and reagents (if not on WO mode).
- Disables refilling medical items (as in use 3 splints on a vendor and
get upgraded to 5). Some conveyed to me they would rather lose the
refilling mechanic over the ability to split stacks.
- Autoinjectors and bottles can still be refilled using now internal
reagents tanks in the vendor. If the vendor has a medlink this will
periodically recharge. The UI also displays the reagent amount much like
the chem dispenser does.
- Adds the ability to bulk restock vendors with storage items. Click
drag the pack onto the vendor and all items that can restock the vendor
will be placed in the vendor.
- Adds the ability to bulk restock medical vendors with restock carts
ordered from requisitions. There are two variants: one that restocks the
items and another that restocks the internal reagent tanks. They can be
disassembled with a wrench.
- Adds the ability to restock partial stacks.
- Doubles the starting energy amount for chemical dispensers by
increasing the max energy amount in chem_storage and increasing the
boost gained when a chemical dispenser is connected to the chem_storage.
- Discounts all requisitions medical supplies.
- Enables initial population scaling for blood bags (of note it wasn't
intended these could restock automatically with a medlink; the supply
restock cart does work on them though and I intend to keep this)
This PR also fixes some miscellaneous things:
- Using a item on a medvendor will now try to fill the vendor with that
item (rather than only performing this action with a mouse drop).
- Fixes some of the grammar issues regarding restocking.
- Fixes some unused squad prep vendors that were not properly adding
headsets to their list of items.
- Fixes removing nano splints creating splints with 0 amount.
- Fixes xenos being able to restock vendors.
- Tweaks the alignment of the images of items in vendors to be more
centered vertically.
- Tweaks the widths of tables in vendors.
- Fixes USCM theme vendors not showing alternating backgrounds for odd
rows
**Of note: The restock cart sprites are temporary.**
# Explain why it's good for the game
The ultimate goal of this PR is to remove the infinite quantity of
medical items groundside so there is more pressure on requisitions to
keep groundside supplied. See
https://github.com/orgs/cmss13-devs/projects/6/views/1?pane=issue&itemId=23005516
for a broader picture of the changes that this PR is helping push
towards. I will finalize a design document once the remaining aspects
for requisitions are decided.
It also is a little goofy that a colony that got decimated by a
xenomorph infestation would have fully stocked medical supplies.
# Testing Photographs and Procedure
Screenshots & Videos
Original implementation: https://youtu.be/6ES88Zre6Gg
Revised implementation: https://youtu.be/7Iyy3eKx3Ng
Bulk restocking using inventories:
https://github.com/cmss13-devs/cmss13/assets/76988376/b9c33693-fe95-410e-8841-d962b052a5fb
Restock carts (temporary sprites for now):
![image](https://github.com/cmss13-devs/cmss13/assets/76988376/1e3e537c-f3a8-4e02-8e55-c97db707748b)
UI polishing:
![image](https://github.com/cmss13-devs/cmss13/assets/76988376/087f2bf2-9b2a-41e2-9786-4e4c6464a397)
![image](https://github.com/cmss13-devs/cmss13/assets/76988376/9bc03db2-f244-47aa-90f9-0ccdf398acf7)
# Changelog
:cl: Drathek
balance: Increased the starting stock of most medical vendors but shifts
some reagent bottles from weymeds to weychems
balance: Med linked medical vendors will now automatically restock items
(requires 20 minutes from round start) and reagents (no time
requirement) periodically if operable
balance: Groundside medical vendors will now have random stock and
reagents missing if its not WO
balance: Partial medical item stacks can no longer be refilled at
vendors (autoinjectors and bottles can still be refilled pulling from
internal reagent tanks)
balance: Doubles the starting energy for chemical dispensers
balance: Discounts all requisitions medical supplies
ui: Added reagent amount display to vendors that have internal reagent
tanks and tweaked the icon positioning of items slightly
ui: Tweaked table widths in vendors
ui: Fixes USCM theme vendors not showing different backgrounds for odd
rows
qol: Restocking a medvendor manually can now be done with just a click
rather than only via mouse drop
qol: Restocking a vendor can now be performed in bulk from a storage
inventory - click drag the inventory to the vendor and you will restock
all the items you can
add: Vendors can accept partial stacks when restocking - when an item
has a partial quantity for a stack it is denoted with an asterisk
add: Added two restock carts for bulk restocking medical vendors, one
for items and the other for reagents (purchasable from requisitions -
disassemble with a wrench - temporary sprites for now)
fix: Fixed some currently unused squad prep vendors
fix: Removing nano splints on a person no longer creates a 0 amount
normal splint
spellcheck: Tweaked some restock messages and vendor descriptions
/:cl:
---
code/datums/supply_packs/medical.dm | 32 +-
code/game/machinery/vending/cm_vending.dm | 217 ++++--
.../machinery/vending/vendor_types/medical.dm | 693 +++++++++++++-----
.../vendor_types/squad_prep/squad_prep.dm | 4 +
code/game/objects/items/stacks/stack.dm | 66 +-
code/modules/mob/living/carbon/human/human.dm | 77 +-
.../chemical_research/Chemical-Research.dm | 4 +-
.../chemistry_machinery/chem_storage.dm | 2 +-
.../vehicles/interior/interactable/vendors.dm | 13 -
.../tgui/interfaces/VendingSorted.tsx | 45 +-
.../tgui/styles/interfaces/VendingSorted.scss | 9 +-
tgui/packages/tgui/styles/themes/uscm.scss | 5 +
12 files changed, 827 insertions(+), 340 deletions(-)
diff --git a/code/datums/supply_packs/medical.dm b/code/datums/supply_packs/medical.dm
index 097642eb163f..acfb9fe1793d 100644
--- a/code/datums/supply_packs/medical.dm
+++ b/code/datums/supply_packs/medical.dm
@@ -17,11 +17,31 @@
/obj/item/storage/pill_bottle/peridaxon,
/obj/item/storage/box/pillbottles,
)
- cost = 20
+ cost = 15
containertype = /obj/structure/closet/crate/medical
containername = "medical crate"
group = "Medical"
+/datum/supply_packs/medical_restock_cart
+ name = "medical restock cart"
+ contains = list(
+ /obj/structure/restock_cart/medical,
+ )
+ cost = 20
+ containertype = null
+ containername = "medical restock cart"
+ group = "Medical"
+
+/datum/supply_packs/medical_reagent_cart
+ name = "medical reagent restock cart"
+ contains = list(
+ /obj/structure/restock_cart/medical/reagent,
+ )
+ cost = 20
+ containertype = null
+ containername = "medical reagent restock cart"
+ group = "Medical"
+
/datum/supply_packs/pillbottle
name = "pill bottle crate (x2 each)"
contains = list(
@@ -42,7 +62,7 @@
/obj/item/storage/box/pillbottles,
/obj/item/storage/box/pillbottles,
)
- cost = 20
+ cost = 15
containertype = /obj/structure/closet/crate/medical
containername = "medical crate"
group = "Medical"
@@ -61,7 +81,7 @@
/obj/item/storage/firstaid/adv,
/obj/item/storage/firstaid/adv,
)
- cost = 16
+ cost = 11
containertype = /obj/structure/closet/crate/medical
containername = "medical crate"
group = "Medical"
@@ -74,7 +94,7 @@
/obj/item/storage/box/bodybags,
/obj/item/storage/box/bodybags,
)
- cost = 12
+ cost = 7
containertype = /obj/structure/closet/crate/medical
containername = "body bag crate"
group = "Medical"
@@ -86,7 +106,7 @@
/obj/item/bodybag/cryobag,
/obj/item/bodybag/cryobag,
)
- cost = 20
+ cost = 15
containertype = /obj/structure/closet/crate/medical
containername = "stasis bag crate"
group = "Medical"
@@ -101,7 +121,7 @@
/obj/item/storage/box/masks,
/obj/item/storage/box/gloves,
)
- cost = 30
+ cost = 25
containertype = /obj/structure/closet/crate/secure/surgery
containername = "surgery crate"
access = ACCESS_MARINE_MEDBAY
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index ff218dac89c0..6c55ce8c7946 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -46,7 +46,11 @@
/// Direction to adjacent user from which we're allowed to do offset vending
var/list/vend_dir_whitelist
+ /// The actual inventory for this vendor as a list of lists
+ /// 1: name 2: amount 3: type 4: flag
var/list/listed_products = list()
+ /// Partial stacks to hold on to as an associated list of type : amount
+ var/list/partial_product_stacks = list()
// Are points associated with this vendor tied to its instance?
var/instanced_vendor_points = FALSE
@@ -900,9 +904,17 @@ GLOBAL_LIST_EMPTY(vending_products)
///this here is made to provide ability to restock vendors with different subtypes of same object, like handmade and manually filled ammo boxes.
var/list/corresponding_types_list
- ///If using [VEND_STOCK_DYNAMIC], assoc list of product entry to list of (product multiplier, awarded objects) - as seen in [/obj/structure/machinery/cm_vending/sorted/proc/populate_product_list]
- ///This allows us to backtrack and refill the stocks when new players latejoin
+ /**
+ * If using [VEND_STOCK_DYNAMIC], assoc list of product entry to list of (1.0 scale product multiplier, awarded objects) - as seen in [/obj/structure/machinery/cm_vending/sorted/proc/populate_product_list]
+ * This allows us to backtrack and refill the stocks when new players latejoin.
+ *
+ * If NOT using [VEND_STOCK_DYNAMIC], assoc list of product entry to list of (estimated 1.0 scale product multiplier, scaled product multiplier) - as seen in [/obj/structure/machinery/cm_vending/sorted/proc/populate_product_list]
+ * This allows us to know the original amounts to know if the vendor is full of an item.
+ * The 1.0 scale is estimated because it is a divided by the scale rather than repopulating the list at 1.0 scale - anything that is a fixed amount won't necessarily be correct.
+ */
var/list/list/dynamic_stock_multipliers
+ ///indicates someone is performing a restock that isn't instant
+ var/being_restocked = FALSE
/obj/structure/machinery/cm_vending/sorted/Initialize()
. = ..()
@@ -917,22 +929,33 @@ GLOBAL_LIST_EMPTY(vending_products)
///this proc, well, populates product list based on roundstart amount of players
/obj/structure/machinery/cm_vending/sorted/proc/populate_product_list_and_boxes(scale)
+ dynamic_stock_multipliers = list()
if(vend_flags & VEND_STOCK_DYNAMIC)
populate_product_list(1.0)
- dynamic_stock_multipliers = list()
for(var/list/vendspec in listed_products)
var/multiplier = vendspec[2]
if(multiplier > 0)
- var/awarded = round(vendspec[2] * scale, 1) // Starting amount
+ var/awarded = round(multiplier * scale, 1) // Starting amount
//Record the multiplier and how many have actually been given out
- dynamic_stock_multipliers[vendspec] = list(vendspec[2], awarded)
+ dynamic_stock_multipliers[vendspec] = list(multiplier, awarded)
vendspec[2] = awarded // Override starting amount
else
populate_product_list(scale)
+ for(var/list/vendspec in listed_products)
+ var/amount = vendspec[2]
+ if(amount > -1)
+ var/multiplier = ceil(amount / scale)
+ //Record the multiplier and how many have actually been given out
+ dynamic_stock_multipliers[vendspec] = list(multiplier, amount)
if(vend_flags & VEND_LOAD_AMMO_BOXES)
populate_ammo_boxes()
- return
+
+ partial_product_stacks = list()
+ for(var/list/vendspec in listed_products)
+ var/current_type = vendspec[3]
+ if(ispath(current_type, /obj/item/stack))
+ partial_product_stacks[current_type] = 0
///Updates the vendor stock when the [/datum/game_mode/var/marine_tally] has changed and we're using [VEND_STOCK_DYNAMIC]
///Assumes the scale can only increase!!! Don't take their items away!
@@ -979,42 +1002,78 @@ GLOBAL_LIST_EMPTY(vending_products)
.["displayed_categories"] = vendor_user_inventory_list(user, null, 4)
/obj/structure/machinery/cm_vending/sorted/MouseDrop_T(atom/movable/A, mob/user)
-
if(inoperable())
return
-
if(user.stat || user.is_mob_restrained())
return
-
if(get_dist(user, src) > 1 || get_dist(src, A) > 1)
return
+ if(!ishuman(user))
+ return
+
+ // Try to bulk restock using a container
+ if(istype(A, /obj/item/storage))
+ var/obj/item/storage/container = A
+ if(!length(container.contents))
+ return
+ if(being_restocked)
+ to_chat(user, SPAN_WARNING("[src] is already being restocked, you will get in the way!"))
+ return
+
+ user.visible_message(SPAN_NOTICE("[user] starts stocking a bunch of supplies into [src]."), \
+ SPAN_NOTICE("You start stocking a bunch of supplies into [src]."))
+ being_restocked = TRUE
+
+ for(var/obj/item/item in container.contents)
+ if(!do_after(user, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_GENERIC, src))
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] stopped stocking [src] with supplies."), \
+ SPAN_NOTICE("You stop stocking [src] with supplies."))
+ return
+ if(QDELETED(item) || item.loc != container)
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] stopped stocking [src] with supplies."), \
+ SPAN_NOTICE("You stop stocking [src] with supplies."))
+ return
+ stock(item, user)
+
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] finishes stocking [src] with supplies."), \
+ SPAN_NOTICE("You finish stocking [src] with supplies."))
+ return
if(istype(A, /obj/item))
- var/obj/item/I = A
- stock(I, user)
+ stock(A, user)
/obj/structure/machinery/cm_vending/sorted/proc/stock(obj/item/item_to_stock, mob/user)
- var/list/R
+ if(istype(item_to_stock, /obj/item/storage))
+ return FALSE
+
var/list/stock_listed_products = get_listed_products(user)
- for(R in (stock_listed_products))
- if(item_to_stock.type == R[3] && !istype(item_to_stock,/obj/item/storage))
+ for(var/list/vendspec as anything in stock_listed_products)
+ if(item_to_stock.type == vendspec[3])
+ var/partial_stacks = 0
if(istype(item_to_stock, /obj/item/device/defibrillator))
- var/obj/item/device/defibrillator/D = item_to_stock
- if(!D.dcell)
- to_chat(user, SPAN_WARNING("\The [item_to_stock] needs a cell in it to be restocked!"))
- return
- if(D.dcell.charge < D.dcell.maxcharge)
- to_chat(user, SPAN_WARNING("\The [item_to_stock] needs to be fully charged to restock it!"))
- return
+ var/obj/item/device/defibrillator/defib = item_to_stock
+ if(!defib.dcell)
+ to_chat(user, SPAN_WARNING("[item_to_stock] needs a cell in it to be restocked!"))
+ return FALSE
+ if(defib.dcell.charge < defib.dcell.maxcharge)
+ to_chat(user, SPAN_WARNING("[item_to_stock] needs to be fully charged to restock it!"))
+ return FALSE
else if(istype(item_to_stock, /obj/item/cell))
- var/obj/item/cell/C = item_to_stock
- if(C.charge < C.maxcharge)
- to_chat(user, SPAN_WARNING("\The [item_to_stock] needs to be fully charged to restock it!"))
- return
+ var/obj/item/cell/cell = item_to_stock
+ if(cell.charge < cell.maxcharge)
+ to_chat(user, SPAN_WARNING("[item_to_stock] needs to be fully charged to restock it!"))
+ return FALSE
- else if(!additional_restock_checks(item_to_stock, user))
+ else if(istype(item_to_stock, /obj/item/stack))
+ var/obj/item/stack/item_stack = item_to_stock
+ partial_stacks = item_stack.amount % item_stack.max_amount
+
+ if(!additional_restock_checks(item_to_stock, user, vendspec))
// the error message needs to go in the proc
return FALSE
@@ -1024,19 +1083,44 @@ GLOBAL_LIST_EMPTY(vending_products)
user.temp_drop_inv_item(item_to_stock)
if(isstorage(item_to_stock.loc)) //inside a storage item
- var/obj/item/storage/S = item_to_stock.loc
- S.remove_from_storage(item_to_stock, user.loc)
+ var/obj/item/storage/container = item_to_stock.loc
+ container.remove_from_storage(item_to_stock, user.loc)
qdel(item_to_stock)
- user.visible_message(SPAN_NOTICE("[user] stocks [src] with \a [R[1]]."),
- SPAN_NOTICE("You stock [src] with \a [R[1]]."))
- R[2]++
- update_derived_ammo_and_boxes_on_add(R)
+ user.visible_message(SPAN_NOTICE("[user] stocks [src] with \a [vendspec[1]]."), \
+ SPAN_NOTICE("You stock [src] with \a [vendspec[1]]."))
+ if(partial_stacks)
+ var/obj/item/stack/item_stack = item_to_stock
+ var/existing_stacks = partial_product_stacks[item_to_stock.type]
+ var/combined_stacks = existing_stacks + partial_stacks
+ if(existing_stacks == 0 || combined_stacks > item_stack.max_amount)
+ vendspec[2]++
+ partial_product_stacks[item_to_stock.type] = combined_stacks % item_stack.max_amount
+ else
+ vendspec[2]++
+ update_derived_ammo_and_boxes_on_add(vendspec)
updateUsrDialog()
- return //We found our item, no reason to go on.
+ return TRUE //We found our item, no reason to go on.
+
+ return FALSE
/// additional restocking checks for individual vendor subtypes. Parse in item, do checks, return FALSE to fail. Include error message.
-/obj/structure/machinery/cm_vending/sorted/proc/additional_restock_checks(obj/item/item_to_stock, mob/user)
+/obj/structure/machinery/cm_vending/sorted/proc/additional_restock_checks(obj/item/item_to_stock, mob/user, list/vendspec)
+ var/dynamic_metadata = dynamic_stock_multipliers[vendspec]
+ if(dynamic_metadata)
+ if(vendspec[2] >= dynamic_metadata[2])
+ if(!istype(item_to_stock, /obj/item/stack))
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
+ return FALSE
+ var/obj/item/stack/item_stack = item_to_stock
+ if(partial_product_stacks[item_to_stock.type] == 0)
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
+ return FALSE // No partial stack to fill
+ if((partial_product_stacks[item_to_stock.type] + item_stack.amount) > item_stack.max_amount)
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
+ return FALSE // Exceeds partial stack to fill
+ else
+ stack_trace("[src] could not find dynamic_stock_multipliers for [vendspec[1]]!")
return TRUE
//sending an /empty ammo box type path here will return corresponding regular (full) type of this box
@@ -1243,14 +1327,20 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
/obj/structure/machinery/cm_vending/proc/vendor_inventory_ui_data(mob/user)
. = list()
- var/list/ui_listed_products = get_listed_products(user)
- var/list/ui_categories = list()
-
- for (var/i in 1 to length(ui_listed_products))
- var/list/myprod = ui_listed_products[i] //we take one list from listed_products
- var/p_amount = myprod[2] //amount left
- ui_categories += list(p_amount)
- .["stock_listing"] = ui_categories
+ var/list/products = get_listed_products(user)
+ var/list/product_amounts = list()
+ var/list/product_partials = list()
+
+ for(var/i in 1 to length(products))
+ var/list/cur_prod = products[i] //we take one list from listed_products
+ product_amounts += list(cur_prod[2]) //amount left
+ var/cur_type = cur_prod[3]
+ var/cur_amount_partial = 0
+ if(cur_type in partial_product_stacks)
+ cur_amount_partial = partial_product_stacks[cur_type]
+ product_partials += list(cur_amount_partial)
+ .["stock_listing"] = product_amounts
+ .["stock_listing_partials"] = product_partials
/obj/structure/machinery/cm_vending/proc/vendor_successful_vend(list/itemspec, mob/living/carbon/human/user)
if(stat & IN_USE)
@@ -1267,19 +1357,23 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
sleep(vend_delay)
var/prod_type = itemspec[3]
+ var/stack_amount = 0
+ if(vend_flags & VEND_LIMITED_INVENTORY)
+ itemspec[2]--
+ if(itemspec[2] == 0)
+ stack_amount = partial_product_stacks[prod_type]
+ partial_product_stacks[prod_type] = 0
+ if(vend_flags & VEND_LOAD_AMMO_BOXES)
+ update_derived_ammo_and_boxes(itemspec)
+
if(islist(prod_type))
for(var/each_type in prod_type)
- vendor_successful_vend_one(each_type, user, target_turf, itemspec[4] == MARINE_CAN_BUY_UNIFORM)
+ vendor_successful_vend_one(each_type, user, target_turf, itemspec[4] == MARINE_CAN_BUY_UNIFORM, stack_amount)
SEND_SIGNAL(src, COMSIG_VENDOR_SUCCESSFUL_VEND, src, itemspec, user)
else
- vendor_successful_vend_one(prod_type, user, target_turf, itemspec[4] == MARINE_CAN_BUY_UNIFORM)
+ vendor_successful_vend_one(prod_type, user, target_turf, itemspec[4] == MARINE_CAN_BUY_UNIFORM, stack_amount)
SEND_SIGNAL(src, COMSIG_VENDOR_SUCCESSFUL_VEND, src, itemspec, user)
- if(vend_flags & VEND_LIMITED_INVENTORY)
- itemspec[2]--
- if(vend_flags & VEND_LOAD_AMMO_BOXES)
- update_derived_ammo_and_boxes(itemspec)
-
else
to_chat(user, SPAN_WARNING("ERROR: itemspec is missing. Please report this to admins."))
sleep(15)
@@ -1288,7 +1382,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
icon_state = initial(icon_state)
update_icon()
-/obj/structure/machinery/cm_vending/proc/vendor_successful_vend_one(prod_type, mob/living/carbon/human/user, turf/target_turf, insignas_override)
+/obj/structure/machinery/cm_vending/proc/vendor_successful_vend_one(prod_type, mob/living/carbon/human/user, turf/target_turf, insignas_override, stack_amount)
var/obj/item/new_item
if(ispath(prod_type, /obj/item))
if(ispath(prod_type, /obj/item/weapon/gun))
@@ -1298,7 +1392,11 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
prod_type = headset_type
else if(prod_type == /obj/item/clothing/gloves/marine)
prod_type = gloves_type
- new_item = new prod_type(target_turf)
+ if(stack_amount > 0 && ispath(prod_type, /obj/item/stack))
+ new_item = new prod_type(target_turf, stack_amount)
+ else
+ new_item = new prod_type(target_turf)
+
new_item.add_fingerprint(user)
else
new_item = new prod_type(target_turf)
@@ -1354,17 +1452,22 @@ GLOBAL_LIST_INIT(cm_vending_gear_corresponding_types_list, list(
while(i <= length(products))
sleep(0.5)
var/list/itemspec = products[i]
+ var/itemspec_item = itemspec[3]
if(!itemspec[2] || itemspec[2] <= 0)
i++
continue
- itemspec[2] -= 1
+ itemspec[2]--
var/list/spawned = list()
- if(islist(itemspec[3]))
- for(var/path in itemspec[3])
+ if(islist(itemspec_item))
+ for(var/path in itemspec_item)
spawned += new path(loc)
- else if(itemspec[3])
- var/path = itemspec[3]
- spawned += new path(loc)
+ else if(itemspec_item)
+ if(itemspec[2] == 0 && partial_product_stacks[itemspec_item] > 0 && ispath(itemspec_item, /obj/item/stack))
+ var/stack_amount = partial_product_stacks[itemspec_item]
+ partial_product_stacks[itemspec_item] = 0
+ spawned += new itemspec_item(loc, stack_amount)
+ else
+ spawned += new itemspec_item(loc)
if(throw_objects)
for(var/atom/movable/spawned_atom in spawned)
INVOKE_ASYNC(spawned_atom, TYPE_PROC_REF(/atom/movable, throw_atom), pick(orange(src, 4)), 4, SPEED_FAST)
diff --git a/code/game/machinery/vending/vendor_types/medical.dm b/code/game/machinery/vending/vendor_types/medical.dm
index 84528b44fa18..ab1df0b2abb7 100644
--- a/code/game/machinery/vending/vendor_types/medical.dm
+++ b/code/game/machinery/vending/vendor_types/medical.dm
@@ -1,4 +1,4 @@
-//------------SUPPLY LINK FOR MEDICAL VENDORS---------------
+//------------SUPPLY LINK FOR MEDICAL VENDORS------------
/obj/structure/medical_supply_link
name = "medilink supply port"
@@ -6,12 +6,13 @@
icon = 'icons/effects/warning_stripes.dmi'
icon_state = "medlink_unclamped"
var/base_state = "medlink"
+ plane = FLOOR_PLANE
+ layer = ABOVE_TURF_LAYER //It's the floor, man
+
anchored = TRUE
density = FALSE
unslashable = TRUE
unacidable = TRUE
- plane = FLOOR_PLANE
- layer = ABOVE_TURF_LAYER //It's the floor, man
/obj/structure/medical_supply_link/ex_act(severity, direction)
return FALSE
@@ -43,16 +44,178 @@
else
icon_state = "[base_state]_unclamped"
-// --- Green
/obj/structure/medical_supply_link/green
icon_state = "medlink_green_unclamped"
base_state = "medlink_green"
-//------------SORTED MEDICAL VENDORS---------------
+
+//------------RESTOCK CARTS FOR MEDICAL VENDORS------------
+
+/obj/structure/restock_cart
+ name = "restock cart"
+ desc = "A rather heavy cart filled with various supplies to restock a vendor with."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "tank_normal" // Temporary
+ var/overlay_color = rgb(252, 186, 3) // Temporary
+
+ density = TRUE
+ anchored = FALSE
+ drag_delay = 2
+ health = 100 // Can be destroyed in 2-4 slashes.
+ unslashable = FALSE
+
+ ///The quantity of things this can restock
+ var/supplies_remaining = 20
+ ///The max quantity of things this can restock
+ var/supplies_max = 20
+ ///The descriptor for the kind of things being restocked
+ var/supply_descriptor = "supplies"
+ ///The sound to play when attacked
+ var/attacked_sound = 'sound/effects/metalhit.ogg'
+ ///The sound to play when destroyed
+ var/destroyed_sound = 'sound/effects/metalhit.ogg'
+ ///Random loot to spawn if destroyed as assoc list of type_path = max_quantity
+ var/list/destroyed_loot = list(
+ /obj/item/stack/sheet/metal = 2
+ )
+
+/obj/structure/restock_cart/medical
+ name = "\improper Wey-Yu restock cart"
+ desc = "A rather heavy cart filled with various supplies to restock a vendor with. Provided by Wey-Yu Pharmaceuticals Division(TM)."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "tank_normal" // Temporary
+
+ supplies_remaining = 20
+ supplies_max = 20
+ supply_descriptor = "sets of medical supplies"
+ destroyed_loot = list(
+ /obj/item/stack/medical/advanced/ointment = 3,
+ /obj/item/stack/medical/advanced/bruise_pack = 2,
+ /obj/item/stack/medical/ointment = 3,
+ /obj/item/stack/medical/bruise_pack = 2,
+ /obj/item/stack/medical/splint = 2,
+ /obj/item/device/healthanalyzer = 1,
+ )
+
+/obj/structure/restock_cart/medical/reagent
+ name = "\improper Wey-Yu reagent restock cart"
+ desc = "A rather heavy cart filled with various reagents to restock a vendor with. Provided by Wey-Yu Pharmaceuticals Division(TM)."
+ icon_state = "tank_normal" // Temporary
+ overlay_color = rgb(252, 115, 3) // Temporary
+
+ supplies_remaining = 1200
+ supplies_max = 1200
+ supply_descriptor = "units of medical reagents"
+ destroyed_sound = 'sound/effects/slosh.ogg'
+ destroyed_loot = list()
+
+/obj/structure/restock_cart/Initialize(mapload, ...)
+ . = ..()
+ supplies_remaining = min(supplies_remaining, supplies_max)
+ update_icon()
+
+/obj/structure/restock_cart/update_icon()
+ . = ..()
+ var/image/overlay_image = image(icon, icon_state = "tn_color") // Temporary
+ overlay_image.color = overlay_color
+ overlays += overlay_image
+
+/obj/structure/restock_cart/get_examine_text(mob/user)
+ . = ..()
+ if(get_dist(user, src) > 2 && user != loc)
+ return
+ . += SPAN_NOTICE("It contains:")
+ if(supplies_remaining)
+ . += SPAN_NOTICE(" [supplies_remaining] [supply_descriptor].")
+ else
+ . += SPAN_NOTICE(" Nothing.")
+
+/obj/structure/restock_cart/deconstruct(disassembled)
+ if(!disassembled)
+ playsound(loc, destroyed_sound, 35, 1)
+ visible_message(SPAN_NOTICE("[src] falls apart as its contents spill everywhere!"))
+
+ // Assumption: supplies_max is > 0
+ if(supplies_remaining > 0 && length(destroyed_loot))
+ var/spawned_any = FALSE
+ var/probability = (supplies_remaining / supplies_max) * 100
+ for(var/type_path in destroyed_loot)
+ if(prob(probability))
+ for(var/amount in 1 to rand(1, destroyed_loot[type_path]))
+ new type_path(loc)
+ spawned_any = TRUE
+ if(!spawned_any) // It wasn't empty so atleast drop something
+ var/type_path = pick(destroyed_loot)
+ for(var/amount in 1 to rand(1, destroyed_loot[type_path]))
+ new type_path(loc)
+
+ return ..()
+
+/obj/structure/restock_cart/attackby(obj/item/W, mob/user)
+ if(HAS_TRAIT(W, TRAIT_TOOL_WRENCH))
+ if(user.action_busy)
+ return
+ playsound(src, 'sound/items/Ratchet.ogg', 25, 1)
+ user.visible_message(SPAN_NOTICE("[user] starts to deconstruct [src]."), \
+ SPAN_NOTICE("You start deconstructing [src]."))
+ if(!do_after(user, 5 SECONDS, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD, src))
+ return
+ user.visible_message(SPAN_NOTICE("[user] deconstructs [src]."), \
+ SPAN_NOTICE("You deconstruct [src]."))
+ playsound(src, 'sound/items/Crowbar.ogg', 25, 1)
+ new /obj/item/stack/sheet/metal(loc)
+ if(supplies_remaining)
+ msg_admin_niche("[key_name(user)] deconstructed [src] with [supplies_remaining] [supply_descriptor] remaining in [get_area(src)] [ADMIN_JMP(loc)]", loc.x, loc.y, loc.z)
+ deconstruct(TRUE)
+ return
+
+ return ..()
+
+/obj/structure/restock_cart/proc/healthcheck(mob/user)
+ if(health <= 0)
+ if(supplies_remaining && ishuman(user))
+ msg_admin_niche("[key_name(user)] destroyed [src] with [supplies_remaining] [supply_descriptor] remaining in [get_area(src)] [ADMIN_JMP(loc)]", loc.x, loc.y, loc.z)
+ deconstruct(FALSE)
+
+/obj/structure/restock_cart/bullet_act(obj/projectile/Proj)
+ health -= Proj.damage
+ playsound(src, attacked_sound, 25, 1)
+ healthcheck(Proj.firer)
+ return TRUE
+
+/obj/structure/restock_cart/attack_alien(mob/living/carbon/xenomorph/user)
+ if(unslashable)
+ return XENO_NO_DELAY_ACTION
+ user.animation_attack_on(src)
+ health -= (rand(user.melee_damage_lower, user.melee_damage_upper))
+ playsound(src, attacked_sound, 25, 1)
+ user.visible_message(SPAN_DANGER("[user] slashes [src]!"), \
+ SPAN_DANGER("You slash [src]!"), null, 5, CHAT_TYPE_XENO_COMBAT)
+ healthcheck(user)
+ return XENO_ATTACK_ACTION
+
+/obj/structure/restock_cart/ex_act(severity)
+ if(indestructible)
+ return
+
+ switch(severity)
+ if(0 to EXPLOSION_THRESHOLD_LOW)
+ if(prob(5))
+ deconstruct(FALSE)
+ return
+ if(EXPLOSION_THRESHOLD_LOW to EXPLOSION_THRESHOLD_MEDIUM)
+ if(prob(50))
+ deconstruct(FALSE)
+ return
+ if(EXPLOSION_THRESHOLD_MEDIUM to INFINITY)
+ deconstruct(FALSE)
+ return
+
+//------------SORTED MEDICAL VENDORS------------
/obj/structure/machinery/cm_vending/sorted/medical
name = "\improper Wey-Med Plus"
- desc = "Medical Pharmaceutical dispenser. Provided by Wey-Yu Pharmaceuticals Division(TM)."
+ desc = "Medical pharmaceutical dispenser. Provided by Wey-Yu Pharmaceuticals Division(TM)."
icon_state = "med"
req_access = list(ACCESS_MARINE_MEDBAY)
@@ -64,12 +227,18 @@
vendor_theme = VENDOR_THEME_COMPANY
vend_delay = 0.5 SECONDS
- /// sets vendor to require a medlink to be able to resupply
- var/requires_supply_link_port = TRUE
+ /// Whether the vendor can use a medlink to be able to resupply automatically
+ var/allow_supply_link_restock = TRUE
+ /// Whether this vendor supports health scanning the user via mouse drop
+ var/healthscan = TRUE
var/datum/health_scan/last_health_display
- var/healthscan = TRUE
+ /// The starting volume of the chem refill tank
+ var/chem_refill_volume = 600
+ /// The maximum volume of the chem refill tank
+ var/chem_refill_volume_max = 600
+ /// A list of item types that allow reagent refilling
var/list/chem_refill = list(
/obj/item/reagent_container/hypospray/autoinjector/bicaridine,
/obj/item/reagent_container/hypospray/autoinjector/dexalinp,
@@ -79,6 +248,7 @@
/obj/item/reagent_container/hypospray/autoinjector/oxycodone,
/obj/item/reagent_container/hypospray/autoinjector/tramadol,
/obj/item/reagent_container/hypospray/autoinjector/tricord,
+
/obj/item/reagent_container/hypospray/autoinjector/skillless,
/obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol,
@@ -95,14 +265,7 @@
/obj/item/reagent_container/glass/bottle/oxycodone,
/obj/item/reagent_container/glass/bottle/peridaxon,
/obj/item/reagent_container/glass/bottle/tramadol,
- )
- var/list/stack_refill = list(
- /obj/item/stack/medical/advanced/ointment,
- /obj/item/stack/medical/advanced/bruise_pack,
- /obj/item/stack/medical/ointment,
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/splint
- )
+ )
/obj/structure/machinery/cm_vending/sorted/medical/Destroy()
QDEL_NULL(last_health_display)
@@ -111,7 +274,13 @@
/obj/structure/machinery/cm_vending/sorted/medical/get_examine_text(mob/living/carbon/human/user)
. = ..()
if(healthscan)
- . += SPAN_NOTICE("The [src.name] offers assisted medical scan, for ease of usage with minimal training. Present the target in front of the scanner to scan.")
+ . += SPAN_NOTICE("[src] offers assisted medical scans, for ease of use with minimal training. Present the target in front of the scanner to scan.")
+
+/obj/structure/machinery/cm_vending/sorted/medical/ui_data(mob/user)
+ . = ..()
+ if(LAZYLEN(chem_refill))
+ .["reagents"] = chem_refill_volume
+ .["reagents_max"] = chem_refill_volume_max
/// checks if there is a supply link in our location and we are anchored to it
/obj/structure/machinery/cm_vending/sorted/medical/proc/get_supply_link()
@@ -122,29 +291,105 @@
return FALSE
return TRUE
-/obj/structure/machinery/cm_vending/sorted/medical/additional_restock_checks(obj/item/item_to_stock, mob/user)
- if(istype(item_to_stock, /obj/item/reagent_container/hypospray/autoinjector) || istype(item_to_stock, /obj/item/reagent_container/glass/bottle))
- if(requires_supply_link_port && !get_supply_link())
- var/obj/item/reagent_container/container = item_to_stock
- if(container.reagents.total_volume < container.reagents.maximum_volume)
- if(user)
- to_chat(user, SPAN_WARNING("[src] makes a buzzing noise as it rejects [container.name]. Looks like this vendor cannot refill these without a connected supply link."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
+/obj/structure/machinery/cm_vending/sorted/medical/additional_restock_checks(obj/item/item_to_stock, mob/user, list/vendspec)
+ var/dynamic_metadata = dynamic_stock_multipliers[vendspec]
+ if(dynamic_metadata)
+ if(vendspec[2] >= dynamic_metadata[2] && (!allow_supply_link_restock || !get_supply_link()))
+ if(!istype(item_to_stock, /obj/item/stack))
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
return FALSE
+ var/obj/item/stack/item_stack = item_to_stock
+ if(partial_product_stacks[item_to_stock.type] == 0)
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
+ return FALSE // No partial stack to fill
+ if((partial_product_stacks[item_to_stock.type] + item_stack.amount) > item_stack.max_amount)
+ to_chat(user, SPAN_WARNING("[src] is already full of [vendspec[1]]!"))
+ return FALSE // Exceeds partial stack to fill
+ else
+ stack_trace("[src] could not find dynamic_stock_multipliers for [vendspec[1]]!")
- //stacked items handling if the vendor cannot restock partial stacks
- else if(istype(item_to_stock, /obj/item/stack))
- if(requires_supply_link_port && !get_supply_link())
- var/obj/item/stack/restock_stack = item_to_stock
- if(restock_stack.amount < restock_stack.max_amount) // if the stack is not full
- if(user)
- to_chat(user, SPAN_WARNING("[src] makes a buzzing noise as it rejects [restock_stack]. Looks like this vendor cannot restock these without a connected supply link."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
+ if(istype(item_to_stock, /obj/item/reagent_container))
+ if(istype(item_to_stock, /obj/item/reagent_container/syringe) || istype(item_to_stock, /obj/item/reagent_container/dropper))
+ var/obj/item/reagent_container/container = item_to_stock
+ if(container.reagents.total_volume != 0)
+ to_chat(user, SPAN_WARNING("[item_to_stock] needs to be empty to restock it!"))
return FALSE
+ else
+ return try_deduct_chem(item_to_stock, user)
+
return TRUE
+/// Attempts to consume our reagents needed for the container (doesn't actually change the container)
+/// Will return TRUE if reagents were deducated or no reagents were needed
+/obj/structure/machinery/cm_vending/sorted/medical/proc/try_deduct_chem(obj/item/reagent_container/container, mob/user)
+ var/missing_reagents = container.reagents.maximum_volume - container.reagents.total_volume
+ if(missing_reagents <= 0)
+ return TRUE
+ if(!LAZYLEN(chem_refill) || !(container.type in chem_refill))
+ if(container.reagents.total_volume == initial(container.reagents.total_volume))
+ return TRUE
+ to_chat(user, SPAN_WARNING("[src] cannot refill [container]."))
+ return FALSE
+ if(chem_refill_volume < missing_reagents)
+ var/auto_refill = allow_supply_link_restock && get_supply_link()
+ to_chat(user, SPAN_WARNING("[src] blinks red and makes a buzzing noise as it rejects [container]. Looks like it doesn't have enough reagents [auto_refill ? "yet" : "left"]."))
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
+ return FALSE
+
+ chem_refill_volume -= missing_reagents
+ to_chat(user, SPAN_NOTICE("[src] makes a whirring noise as it refills your [container.name]."))
+ playsound(src, 'sound/effects/refill.ogg', 10, 1, 3)
+ return TRUE
+
+/// Performs automatic restocking via medical cart - will set being_restocked true during the action
+/obj/structure/machinery/cm_vending/sorted/medical/proc/cart_restock(obj/structure/restock_cart/medical/cart, mob/user)
+ if(cart.supplies_remaining <= 0)
+ to_chat(user, SPAN_WARNING("[cart] is empty!"))
+ return
+ if(being_restocked)
+ to_chat(user, SPAN_WARNING("[src] is already being restocked, you will get in the way!"))
+ return
+
+ var/restocking_reagents = istype(cart, /obj/structure/restock_cart/medical/reagent)
+ if(restocking_reagents && !LAZYLEN(chem_refill))
+ to_chat(user, SPAN_WARNING("[src] doesn't use [cart.supply_descriptor]!"))
+ return
+
+ user.visible_message(SPAN_NOTICE("[user] starts stocking [cart.supply_descriptor] supplies into [src]."), \
+ SPAN_NOTICE("You start stocking [cart.supply_descriptor] into [src]."))
+ being_restocked = TRUE
+
+ while(cart.supplies_remaining > 0)
+ if(!do_after(user, 1 SECONDS, INTERRUPT_ALL, BUSY_ICON_GENERIC, src))
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] stopped stocking [src] with [cart.supply_descriptor]."), \
+ SPAN_NOTICE("You stop stocking [src] with [cart.supply_descriptor]."))
+ return
+ if(QDELETED(cart) || get_dist(user, cart) > 1)
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] stopped stocking [src] with [cart.supply_descriptor]."), \
+ SPAN_NOTICE("You stop stocking [src] with [cart.supply_descriptor]."))
+ return
+
+ if(restocking_reagents)
+ var/reagent_added = restock_reagents(min(cart.supplies_remaining, 100))
+ if(reagent_added <= 0 || chem_refill_volume == chem_refill_volume_max)
+ break // All done
+ cart.supplies_remaining -= reagent_added
+ else
+ if(!restock_supplies(prob_to_skip = 0, can_remove = FALSE))
+ break // All done
+ cart.supplies_remaining--
+
+ being_restocked = FALSE
+ user.visible_message(SPAN_NOTICE("[user] finishes stocking [src] with [cart.supply_descriptor]."), \
+ SPAN_NOTICE("You finish stocking [src] with [cart.supply_descriptor]."))
+
/obj/structure/machinery/cm_vending/sorted/medical/attackby(obj/item/I, mob/user)
- if(stat == WORKING && LAZYLEN(chem_refill) && (istype(I, /obj/item/reagent_container/hypospray/autoinjector) || istype(I, /obj/item/reagent_container/glass/bottle))) // only if we are completely fine and working
+ if(stat != WORKING)
+ return ..()
+
+ if(istype(I, /obj/item/reagent_container))
if(!hacked)
if(!allowed(user))
to_chat(user, SPAN_WARNING("Access denied."))
@@ -154,54 +399,37 @@
to_chat(user, SPAN_WARNING("This machine isn't for you."))
return
- var/obj/item/reagent_container/C = I
- if(!(C.type in chem_refill))
- to_chat(user, SPAN_WARNING("[src] cannot refill the [C.name]."))
+ var/obj/item/reagent_container/container = I
+ if(istype(I, /obj/item/reagent_container/syringe) || istype(I, /obj/item/reagent_container/dropper))
+ if(!stock(container, user))
+ return ..()
return
- if(C.reagents.total_volume == C.reagents.maximum_volume)
- to_chat(user, SPAN_WARNING("[src] makes a warning noise. The [C.name] is currently full."))
+ if(container.reagents.total_volume == container.reagents.maximum_volume)
+ if(!stock(container, user))
+ return ..()
return
- if(requires_supply_link_port && !get_supply_link())
- to_chat(user, SPAN_WARNING("[src] makes a buzzing noise as it rejects [C.name]. Looks like this vendor cannot refill these without a connected supply link."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
- return
+ if(!try_deduct_chem(container, user))
+ return ..()
- to_chat(user, SPAN_NOTICE("[src] makes a whirring noise as it refills your [C.name]."))
// Since the reagent is deleted on use it's easier to make a new one instead of snowflake checking
- var/obj/item/reagent_container/new_container = new C.type(src)
- qdel(C)
+ var/obj/item/reagent_container/new_container = new container.type(src)
+ qdel(container)
user.put_in_hands(new_container)
- else if(stat == WORKING && LAZYLEN(stack_refill) && (istype(I, /obj/item/stack)))
- if(!hacked)
- if(!allowed(user))
- to_chat(user, SPAN_WARNING("Access denied."))
- return
-
- if(LAZYLEN(vendor_role) && !vendor_role.Find(user.job))
- to_chat(user, SPAN_WARNING("This machine isn't for you."))
- return
-
- var/obj/item/stack/S = I
- if(!(S.type in stack_refill))
- to_chat(user, SPAN_WARNING("[src] cannot restock the [S.name]."))
- return
+ return
- if(S.amount == S.max_amount)
- to_chat(user, SPAN_WARNING("[src] makes a warning noise. The [S.name] is currently fully stacked."))
+ if(ishuman(user) && istype(I, /obj/item/grab))
+ var/obj/item/grab/grabbed = I
+ if(istype(grabbed.grabbed_thing, /obj/structure/restock_cart/medical))
+ cart_restock(grabbed.grabbed_thing, user)
return
- if(requires_supply_link_port && !get_supply_link())
- to_chat(user, SPAN_WARNING("[src] makes a buzzing noise as it rejects [S.name]. Looks like this vendor cannot restock these without a connected supply link."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 15, TRUE)
+ if(hacked || (allowed(user) && (!LAZYLEN(vendor_role) || vendor_role.Find(user.job))))
+ if(stock(I, user))
return
- to_chat(user, SPAN_NOTICE("[src] makes a whirring noise as it restocks your [S.name]."))
- S.amount = S.max_amount
- S.update_icon()
- else
- . = ..()
+ return ..()
/obj/structure/machinery/cm_vending/sorted/medical/MouseDrop(obj/over_object as obj)
if(stat == WORKING && over_object == usr && CAN_PICKUP(usr, src))
@@ -211,7 +439,7 @@
return
if(!healthscan)
- to_chat(user, SPAN_WARNING("\The [src] does not have health scanning function."))
+ to_chat(user, SPAN_WARNING("[src] does not have health scanning function."))
return
if (!last_health_display)
@@ -222,63 +450,182 @@
last_health_display.look_at(user, DETAIL_LEVEL_HEALTHANALYSER, bypass_checks = TRUE)
return
+/obj/structure/machinery/cm_vending/sorted/medical/MouseDrop_T(atom/movable/A, mob/user)
+ if(inoperable())
+ return
+ if(user.stat || user.is_mob_restrained())
+ return
+ if(get_dist(user, src) > 1 || get_dist(user, A) > 1) // More lenient
+ return
+ if(!ishuman(user))
+ return
+
+ if(istype(A, /obj/structure/restock_cart/medical))
+ cart_restock(A, user)
+ return
+
+ return ..()
+
/obj/structure/machinery/cm_vending/sorted/medical/populate_product_list(scale)
listed_products = list(
list("FIELD SUPPLIES", -1, null, null),
- list("Burn Kit", floor(scale * 6), /obj/item/stack/medical/advanced/ointment, VENDOR_ITEM_REGULAR),
- list("Trauma Kit", floor(scale * 6), /obj/item/stack/medical/advanced/bruise_pack, VENDOR_ITEM_REGULAR),
- list("Ointment", floor(scale * 6), /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
- list("Roll of Gauze", floor(scale * 6), /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
- list("Splints", floor(scale * 6), /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
+ list("Burn Kit", floor(scale * 10), /obj/item/stack/medical/advanced/ointment, VENDOR_ITEM_REGULAR),
+ list("Trauma Kit", floor(scale * 10), /obj/item/stack/medical/advanced/bruise_pack, VENDOR_ITEM_REGULAR),
+ list("Ointment", floor(scale * 10), /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
+ list("Roll of Gauze", floor(scale * 10), /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
+ list("Splints", floor(scale * 10), /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
list("AUTOINJECTORS", -1, null, null),
- list("Autoinjector (Bicaridine)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/bicaridine, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Dexalin+)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/dexalinp, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Epinephrine)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/adrenaline, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Inaprovaline)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/inaprovaline, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Kelotane)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/kelotane, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Oxycodone)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/oxycodone, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Tramadol)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/tramadol, VENDOR_ITEM_REGULAR),
- list("Autoinjector (Tricord)", floor(scale * 3), /obj/item/reagent_container/hypospray/autoinjector/tricord, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Bicaridine)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/bicaridine, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Dexalin+)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/dexalinp, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Epinephrine)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/adrenaline, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Inaprovaline)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/inaprovaline, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Kelotane)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/kelotane, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Oxycodone)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/oxycodone, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Tramadol)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/tramadol, VENDOR_ITEM_REGULAR),
+ list("Autoinjector (Tricord)", floor(scale * 5), /obj/item/reagent_container/hypospray/autoinjector/tricord, VENDOR_ITEM_REGULAR),
list("LIQUID BOTTLES", -1, null, null),
- list("Bottle (Bicaridine)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/bicaridine, VENDOR_ITEM_REGULAR),
- list("Bottle (Dylovene)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/antitoxin, VENDOR_ITEM_REGULAR),
- list("Bottle (Dexalin)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/dexalin, VENDOR_ITEM_REGULAR),
- list("Bottle (Inaprovaline)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/inaprovaline, VENDOR_ITEM_REGULAR),
- list("Bottle (Kelotane)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/kelotane, VENDOR_ITEM_REGULAR),
- list("Bottle (Oxycodone)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/oxycodone, VENDOR_ITEM_REGULAR),
- list("Bottle (Peridaxon)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/peridaxon, VENDOR_ITEM_REGULAR),
- list("Bottle (Tramadol)", floor(scale * 4), /obj/item/reagent_container/glass/bottle/tramadol, VENDOR_ITEM_REGULAR),
+ list("Bottle (Bicaridine)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/bicaridine, VENDOR_ITEM_REGULAR),
+ list("Bottle (Dylovene)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/antitoxin, VENDOR_ITEM_REGULAR),
+ list("Bottle (Dexalin)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/dexalin, VENDOR_ITEM_REGULAR),
+ list("Bottle (Inaprovaline)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/inaprovaline, VENDOR_ITEM_REGULAR),
+ list("Bottle (Kelotane)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/kelotane, VENDOR_ITEM_REGULAR),
+ list("Bottle (Oxycodone)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/oxycodone, VENDOR_ITEM_REGULAR),
+ list("Bottle (Peridaxon)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/peridaxon, VENDOR_ITEM_REGULAR),
+ list("Bottle (Tramadol)", floor(scale * 3), /obj/item/reagent_container/glass/bottle/tramadol, VENDOR_ITEM_REGULAR),
list("PILL BOTTLES", -1, null, null),
- list("Pill Bottle (Bicaridine)", floor(scale * 2), /obj/item/storage/pill_bottle/bicaridine, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Dexalin)", floor(scale * 2), /obj/item/storage/pill_bottle/dexalin, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Dylovene)", floor(scale * 2), /obj/item/storage/pill_bottle/antitox, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Inaprovaline)", floor(scale * 2), /obj/item/storage/pill_bottle/inaprovaline, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Kelotane)", floor(scale * 2), /obj/item/storage/pill_bottle/kelotane, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Peridaxon)", floor(scale * 1), /obj/item/storage/pill_bottle/peridaxon, VENDOR_ITEM_REGULAR),
- list("Pill Bottle (Tramadol)", floor(scale * 2), /obj/item/storage/pill_bottle/tramadol, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Bicaridine)", floor(scale * 4), /obj/item/storage/pill_bottle/bicaridine, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Dexalin)", floor(scale * 4), /obj/item/storage/pill_bottle/dexalin, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Dylovene)", floor(scale * 4), /obj/item/storage/pill_bottle/antitox, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Inaprovaline)", floor(scale * 4), /obj/item/storage/pill_bottle/inaprovaline, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Kelotane)", floor(scale * 4), /obj/item/storage/pill_bottle/kelotane, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Peridaxon)", floor(scale * 3), /obj/item/storage/pill_bottle/peridaxon, VENDOR_ITEM_REGULAR),
+ list("Pill Bottle (Tramadol)", floor(scale * 4), /obj/item/storage/pill_bottle/tramadol, VENDOR_ITEM_REGULAR),
list("MEDICAL UTILITIES", -1, null, null),
list("Emergency Defibrillator", floor(scale * 3), /obj/item/device/defibrillator, VENDOR_ITEM_REGULAR),
list("Surgical Line", floor(scale * 2), /obj/item/tool/surgery/surgical_line, VENDOR_ITEM_REGULAR),
list("Synth-Graft", floor(scale * 2), /obj/item/tool/surgery/synthgraft, VENDOR_ITEM_REGULAR),
- list("Hypospray", floor(scale * 2), /obj/item/reagent_container/hypospray/tricordrazine, VENDOR_ITEM_REGULAR),
+ list("Hypospray", floor(scale * 3), /obj/item/reagent_container/hypospray/tricordrazine, VENDOR_ITEM_REGULAR),
list("Health Analyzer", floor(scale * 5), /obj/item/device/healthanalyzer, VENDOR_ITEM_REGULAR),
list("M276 Pattern Medical Storage Rig", floor(scale * 2), /obj/item/storage/belt/medical, VENDOR_ITEM_REGULAR),
list("Medical HUD Glasses", floor(scale * 3), /obj/item/clothing/glasses/hud/health, VENDOR_ITEM_REGULAR),
- list("Stasis Bag", floor(scale * 2), /obj/item/bodybag/cryobag, VENDOR_ITEM_REGULAR),
+ list("Stasis Bag", floor(scale * 3), /obj/item/bodybag/cryobag, VENDOR_ITEM_REGULAR),
list("Syringe", floor(scale * 7), /obj/item/reagent_container/syringe, VENDOR_ITEM_REGULAR)
)
+/obj/structure/machinery/cm_vending/sorted/medical/populate_product_list_and_boxes(scale)
+ . = ..()
+
+ // If this is groundside and isn't dynamically changing we will spawn with stock randomly removed from it
+ if(vend_flags & VEND_STOCK_DYNAMIC)
+ return
+ if(Check_WO())
+ return
+ var/turf/location = get_turf(src)
+ if(location && is_ground_level(location.z))
+ random_unstock()
+
+/obj/structure/machinery/cm_vending/sorted/medical/Initialize()
+ . = ..()
+
+ // If this is a medlinked vendor (that needs a link) and isn't dynamically changing it will periodically restock itself
+ if(vend_flags & VEND_STOCK_DYNAMIC)
+ return
+ if(!allow_supply_link_restock)
+ return
+ if(!get_supply_link())
+ return
+ START_PROCESSING(SSslowobj, src)
+
+/obj/structure/machinery/cm_vending/sorted/medical/toggle_anchored(obj/item/W, mob/user)
+ . = ..()
+
+ // If the anchor state changed, this is a vendor that needs a link, and isn't dynamically changing, update whether we automatically restock
+ if(. && !(vend_flags & VEND_STOCK_DYNAMIC) && allow_supply_link_restock)
+ if(get_supply_link())
+ START_PROCESSING(SSslowobj, src)
+ else
+ STOP_PROCESSING(SSslowobj, src)
+
+/obj/structure/machinery/cm_vending/sorted/medical/process()
+ if(!get_supply_link())
+ STOP_PROCESSING(SSslowobj, src)
+ return // Somehow we lost our link
+ if(inoperable())
+ return
+ if(world.time - SSticker.mode.round_time_lobby > 20 MINUTES)
+ restock_supplies()
+ restock_reagents()
+
+/// Randomly (based on prob_to_skip) adjusts all amounts of listed_products towards their desired values by 1
+/// Returns the quantity of items added
+/obj/structure/machinery/cm_vending/sorted/medical/proc/restock_supplies(prob_to_skip = 80, can_remove = TRUE)
+ . = 0
+ for(var/list/vendspec as anything in listed_products)
+ if(vendspec[2] < 0)
+ continue // It's a section title, not an actual entry
+ var/dynamic_metadata = dynamic_stock_multipliers[vendspec]
+ if(!dynamic_metadata)
+ stack_trace("[src] could not find dynamic_stock_multipliers for [vendspec[1]]!")
+ continue
+ var/cur_type = vendspec[3]
+ if(vendspec[2] == dynamic_metadata[2])
+ if((cur_type in partial_product_stacks) && partial_product_stacks[cur_type] > 0)
+ partial_product_stacks[cur_type] = 0
+ .++
+ continue // Already at desired value
+ if(vendspec[2] > dynamic_metadata[2])
+ if(can_remove)
+ vendspec[2]--
+ if(cur_type in partial_product_stacks)
+ partial_product_stacks[cur_type] = 0
+ if(vend_flags & VEND_LOAD_AMMO_BOXES)
+ update_derived_ammo_and_boxes(vendspec)
+ continue // Returned some items to the void
+ if(prob(prob_to_skip))
+ continue // 20% chance to restock per entry by default
+ vendspec[2]++
+ if(vend_flags & VEND_LOAD_AMMO_BOXES)
+ update_derived_ammo_and_boxes_on_add(vendspec)
+ .++
+
+/// Refills reagents towards chem_refill_volume_max
+/// Returns the quantity of reagents added
+/obj/structure/machinery/cm_vending/sorted/medical/proc/restock_reagents(additional_volume = 125)
+ var/old_value = chem_refill_volume
+ chem_refill_volume = min(chem_refill_volume + additional_volume, chem_refill_volume_max)
+ return chem_refill_volume - old_value
+
+/// Randomly removes amounts of listed_products and reagents
+/obj/structure/machinery/cm_vending/sorted/medical/proc/random_unstock()
+ // Random interval of 25 for reagents
+ chem_refill_volume = rand(0, chem_refill_volume_max * 0.04) * 25
+
+ for(var/list/vendspec as anything in listed_products)
+ var/amount = vendspec[2]
+ if(amount <= 0)
+ continue
+
+ // Chance to just be empty
+ if(prob(25))
+ vendspec[2] = 0
+ continue
+
+ // Otherwise its some amount between 1 and the original amount
+ vendspec[2] = rand(1, amount)
+
/obj/structure/machinery/cm_vending/sorted/medical/chemistry
name = "\improper Wey-Chem Plus"
desc = "Medical chemistry dispenser. Provided by Wey-Yu Pharmaceuticals Division(TM)."
icon_state = "chem"
req_access = list(ACCESS_MARINE_CHEMISTRY)
-
healthscan = FALSE
+
+ chem_refill_volume = 1200
+ chem_refill_volume_max = 1200
chem_refill = list(
/obj/item/reagent_container/glass/bottle/bicaridine,
/obj/item/reagent_container/glass/bottle/antitoxin,
@@ -289,19 +636,18 @@
/obj/item/reagent_container/glass/bottle/peridaxon,
/obj/item/reagent_container/glass/bottle/tramadol,
)
- stack_refill = null
/obj/structure/machinery/cm_vending/sorted/medical/chemistry/populate_product_list(scale)
listed_products = list(
list("LIQUID BOTTLES", -1, null, null),
- list("Bicaridine Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/bicaridine, VENDOR_ITEM_REGULAR),
- list("Dylovene Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/antitoxin, VENDOR_ITEM_REGULAR),
- list("Dexalin Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/dexalin, VENDOR_ITEM_REGULAR),
- list("Inaprovaline Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/inaprovaline, VENDOR_ITEM_REGULAR),
- list("Kelotane Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/kelotane, VENDOR_ITEM_REGULAR),
- list("Oxycodone Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/oxycodone, VENDOR_ITEM_REGULAR),
- list("Peridaxon Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/peridaxon, VENDOR_ITEM_REGULAR),
- list("Tramadol Bottle", floor(scale * 5), /obj/item/reagent_container/glass/bottle/tramadol, VENDOR_ITEM_REGULAR),
+ list("Bicaridine Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/bicaridine, VENDOR_ITEM_REGULAR),
+ list("Dylovene Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/antitoxin, VENDOR_ITEM_REGULAR),
+ list("Dexalin Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/dexalin, VENDOR_ITEM_REGULAR),
+ list("Inaprovaline Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/inaprovaline, VENDOR_ITEM_REGULAR),
+ list("Kelotane Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/kelotane, VENDOR_ITEM_REGULAR),
+ list("Oxycodone Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/oxycodone, VENDOR_ITEM_REGULAR),
+ list("Peridaxon Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/peridaxon, VENDOR_ITEM_REGULAR),
+ list("Tramadol Bottle", floor(scale * 6), /obj/item/reagent_container/glass/bottle/tramadol, VENDOR_ITEM_REGULAR),
list("MISCELLANEOUS", -1, null, null),
list("Beaker (60 Units)", floor(scale * 3), /obj/item/reagent_container/glass/beaker, VENDOR_ITEM_REGULAR),
@@ -324,13 +670,13 @@
name = "\improper Medical Equipment Vendor"
desc = "A vending machine dispensing various pieces of medical equipment."
req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL)
- requires_supply_link_port = FALSE
req_access = null
vendor_theme = VENDOR_THEME_CLF
+ allow_supply_link_restock = FALSE
/obj/structure/machinery/cm_vending/sorted/medical/marinemed
name = "\improper ColMarTech MarineMed"
- desc = "Medical Pharmaceutical dispenser with basic medical supplies for marines."
+ desc = "Medical pharmaceutical dispenser with basic medical supplies for marines."
icon_state = "marinemed"
req_access = list()
req_one_access = list()
@@ -340,11 +686,6 @@
/obj/item/reagent_container/hypospray/autoinjector/skillless,
/obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol,
)
- stack_refill = list(
- /obj/item/stack/medical/ointment,
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/splint,
- )
/obj/structure/machinery/cm_vending/sorted/medical/marinemed/populate_product_list(scale)
listed_products = list(
@@ -357,80 +698,80 @@
list("FIELD SUPPLIES", -1, null, null),
list("Fire Extinguisher (portable)", 5, /obj/item/tool/extinguisher/mini, VENDOR_ITEM_REGULAR),
- list("Ointment", floor(scale * 7), /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
- list("Roll of Gauze", floor(scale * 7), /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
- list("Splints", floor(scale * 7), /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR)
+ list("Ointment", floor(scale * 8), /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
+ list("Roll of Gauze", floor(scale * 8), /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
+ list("Splints", floor(scale * 8), /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR)
)
/obj/structure/machinery/cm_vending/sorted/medical/marinemed/antag
name = "\improper Basic Medical Supplies Vendor"
desc = "A vending machine dispensing basic medical supplies."
req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL)
- requires_supply_link_port = FALSE
req_access = null
vendor_theme = VENDOR_THEME_CLF
+ allow_supply_link_restock = FALSE
/obj/structure/machinery/cm_vending/sorted/medical/blood
name = "\improper MM Blood Dispenser"
- desc = "The Marine Med Brand Blood Pack Dispensary is the premier, top-of-the-line blood dispenser of 2105! Get yours today!" //Don't update this year, the joke is it's old.
+ desc = "The MarineMed brand blood dispensary is the premier, top-of-the-line blood dispenser of 2105! Get yours today!" //Don't update this year, the joke is it's old.
icon_state = "blood"
wrenchable = TRUE
hackable = TRUE
-
- listed_products = list(
- list("BLOOD PACKS", -1, null, null),
- list("A+ Blood Pack", 5, /obj/item/reagent_container/blood/APlus, VENDOR_ITEM_REGULAR),
- list("A- Blood Pack", 5, /obj/item/reagent_container/blood/AMinus, VENDOR_ITEM_REGULAR),
- list("B+ Blood Pack", 5, /obj/item/reagent_container/blood/BPlus, VENDOR_ITEM_REGULAR),
- list("B- Blood Pack", 5, /obj/item/reagent_container/blood/BMinus, VENDOR_ITEM_REGULAR),
- list("O+ Blood Pack", 5, /obj/item/reagent_container/blood/OPlus, VENDOR_ITEM_REGULAR),
- list("O- Blood Pack", 5, /obj/item/reagent_container/blood/OMinus, VENDOR_ITEM_REGULAR),
-
- list("MISCELLANEOUS", -1, null, null),
- list("Empty Blood Pack", 5, /obj/item/reagent_container/blood, VENDOR_ITEM_REGULAR)
- )
-
healthscan = FALSE
+ allow_supply_link_restock = FALSE
chem_refill = null
- stack_refill = null
/obj/structure/machinery/cm_vending/sorted/medical/blood/bolted
wrenchable = FALSE
/obj/structure/machinery/cm_vending/sorted/medical/blood/populate_product_list(scale)
- return
+ listed_products = list(
+ list("BLOOD PACKS", -1, null, null),
+ list("A+ Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/APlus, VENDOR_ITEM_REGULAR),
+ list("A- Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/AMinus, VENDOR_ITEM_REGULAR),
+ list("B+ Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/BPlus, VENDOR_ITEM_REGULAR),
+ list("B- Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/BMinus, VENDOR_ITEM_REGULAR),
+ list("O+ Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/OPlus, VENDOR_ITEM_REGULAR),
+ list("O- Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood/OMinus, VENDOR_ITEM_REGULAR),
+
+ list("MISCELLANEOUS", -1, null, null),
+ list("Empty Blood Pack", floor(scale * 5), /obj/item/reagent_container/blood, VENDOR_ITEM_REGULAR)
+ )
/obj/structure/machinery/cm_vending/sorted/medical/blood/antag
req_one_access = list(ACCESS_ILLEGAL_PIRATE, ACCESS_UPP_GENERAL, ACCESS_CLF_GENERAL)
- requires_supply_link_port = FALSE
req_access = null
vendor_theme = VENDOR_THEME_CLF
+ allow_supply_link_restock = FALSE
+
+
+//------------WALL MED VENDORS------------
/obj/structure/machinery/cm_vending/sorted/medical/wall_med
name = "\improper NanoMed"
- desc = "Wall-mounted Medical Equipment Dispenser."
+ desc = "A wall-mounted medical equipment dispenser."
icon_state = "wallmed"
- vend_delay = 0.7 SECONDS
- requires_supply_link_port = FALSE
-
+ appearance_flags = TILE_BOUND
req_access = list()
-
density = FALSE
wrenchable = FALSE
+ vend_delay = 0.7 SECONDS
+ allow_supply_link_restock = FALSE
+
listed_products = list(
list("SUPPLIES", -1, null, null),
- list("First-Aid Autoinjector", 1, /obj/item/reagent_container/hypospray/autoinjector/skillless, VENDOR_ITEM_REGULAR),
- list("Pain-Stop Autoinjector", 1, /obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol, VENDOR_ITEM_REGULAR),
- list("Roll Of Gauze", 2, /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
- list("Ointment", 2, /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
- list("Medical Splints", 1, /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
+ list("First-Aid Autoinjector", 2, /obj/item/reagent_container/hypospray/autoinjector/skillless, VENDOR_ITEM_REGULAR),
+ list("Pain-Stop Autoinjector", 2, /obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol, VENDOR_ITEM_REGULAR),
+ list("Roll Of Gauze", 4, /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
+ list("Ointment", 4, /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
+ list("Medical Splints", 4, /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
list("UTILITY", -1, null, null),
- list("HF2 Health Analyzer", 1, /obj/item/device/healthanalyzer, VENDOR_ITEM_REGULAR)
+ list("HF2 Health Analyzer", 2, /obj/item/device/healthanalyzer, VENDOR_ITEM_REGULAR)
)
- appearance_flags = TILE_BOUND
-
+ chem_refill_volume = 250
+ chem_refill_volume_max = 250
chem_refill = list(
/obj/item/reagent_container/hypospray/autoinjector/skillless,
/obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol,
@@ -439,29 +780,27 @@
/obj/item/reagent_container/hypospray/autoinjector/kelotane/skillless,
/obj/item/reagent_container/hypospray/autoinjector/tramadol/skillless,
)
- stack_refill = list(
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/splint,
- /obj/item/stack/medical/ointment,
- )
/obj/structure/machinery/cm_vending/sorted/medical/wall_med/limited
- desc = "Wall-mounted Medical Equipment Dispenser. This version is more limited than standard USCM NanoMeds."
+ desc = "A wall-mounted medical equipment dispenser. This version is more limited than standard USCM NanoMeds."
+ chem_refill_volume = 150
+ chem_refill_volume_max = 150
chem_refill = list(
/obj/item/reagent_container/hypospray/autoinjector/skillless,
/obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol,
)
- stack_refill = list(
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/ointment,
- )
/obj/structure/machinery/cm_vending/sorted/medical/wall_med/lifeboat
name = "Lifeboat Medical Cabinet"
icon = 'icons/obj/structures/machinery/lifeboat.dmi'
icon_state = "medcab"
desc = "A wall-mounted cabinet containing medical supplies vital to survival. While better equipped, it can only refill basic supplies."
+ unacidable = TRUE
+ unslashable = TRUE
+ wrenchable = FALSE
+ hackable = FALSE
+
listed_products = list(
list("AUTOINJECTORS", -1, null, null),
list("First-Aid Autoinjector", 8, /obj/item/reagent_container/hypospray/autoinjector/skillless, VENDOR_ITEM_REGULAR),
@@ -477,16 +816,9 @@
list("Roll of Gauze", 8, /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
list("Splints", 8, /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR)
)
- stack_refill = list(
- /obj/item/stack/medical/ointment,
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/splint,
- )
- unacidable = TRUE
- unslashable = TRUE
- wrenchable = FALSE
- hackable = FALSE
+ chem_refill_volume = 500
+ chem_refill_volume_max = 500
/obj/structure/machinery/cm_vending/sorted/medical/wall_med/populate_product_list(scale)
return
@@ -494,18 +826,19 @@
/obj/structure/machinery/cm_vending/sorted/medical/wall_med/souto
name = "\improper SoutoMed"
desc = "In Soutoland (Trademark pending), one is never more than 6ft away from canned Havana goodness. Drink a Souto today! For a full selection of Souto products please visit a licensed retailer or vending machine. Also doubles as basic first aid station."
- icon_state = "soutomed"
icon = 'icons/obj/structures/souto_land.dmi'
+ icon_state = "soutomed"
+
listed_products = list(
list("FIRST AID SUPPLIES", -1, null, null),
- list("First-Aid Autoinjector", 1, /obj/item/reagent_container/hypospray/autoinjector/skillless, VENDOR_ITEM_REGULAR),
- list("Pain-Stop Autoinjector", 1, /obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol, VENDOR_ITEM_REGULAR),
- list("Roll Of Gauze", 2, /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
- list("Ointment", 2, /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
- list("Medical Splints", 1, /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
+ list("First-Aid Autoinjector", 2, /obj/item/reagent_container/hypospray/autoinjector/skillless, VENDOR_ITEM_REGULAR),
+ list("Pain-Stop Autoinjector", 2, /obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol, VENDOR_ITEM_REGULAR),
+ list("Roll Of Gauze", 4, /obj/item/stack/medical/bruise_pack, VENDOR_ITEM_REGULAR),
+ list("Ointment", 4, /obj/item/stack/medical/ointment, VENDOR_ITEM_REGULAR),
+ list("Medical Splints", 4, /obj/item/stack/medical/splint, VENDOR_ITEM_REGULAR),
list("UTILITY", -1, null, null),
- list("HF2 Health Analyzer", 1, /obj/item/device/healthanalyzer, VENDOR_ITEM_REGULAR),
+ list("HF2 Health Analyzer", 2, /obj/item/device/healthanalyzer, VENDOR_ITEM_REGULAR),
list("SOUTO", -1, null, null),
list("Souto Classic", 1, /obj/item/reagent_container/food/drinks/cans/souto/classic, VENDOR_ITEM_REGULAR),
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
index 5ba8904069c3..276f0b760842 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
@@ -207,6 +207,7 @@
req_one_access = list(ACCESS_MARINE_ALPHA, ACCESS_MARINE_DATABASE, ACCESS_MARINE_CARGO)
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep/alpha/populate_product_list(scale)
+ ..()
listed_products += list(
list("HEADSET", -1, null, null),
list("Marine Alpha Radio Headset", 10, /obj/item/device/radio/headset/almayer/marine/alpha, VENDOR_ITEM_REGULAR),
@@ -217,6 +218,7 @@
req_one_access = list(ACCESS_MARINE_BRAVO, ACCESS_MARINE_DATABASE, ACCESS_MARINE_CARGO)
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep/bravo/populate_product_list(scale)
+ ..()
listed_products += list(
list("HEADSET", -1, null, null),
list("Marine Bravo Radio Headset", 10, /obj/item/device/radio/headset/almayer/marine/bravo, VENDOR_ITEM_REGULAR),
@@ -227,6 +229,7 @@
req_one_access = list(ACCESS_MARINE_CHARLIE, ACCESS_MARINE_DATABASE, ACCESS_MARINE_CARGO)
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep/charlie/populate_product_list(scale)
+ ..()
listed_products += list(
list("HEADSET", -1, null, null),
list("Marine Charlie Radio Headset", 10, /obj/item/device/radio/headset/almayer/marine/charlie, VENDOR_ITEM_REGULAR),
@@ -237,6 +240,7 @@
req_one_access = list(ACCESS_MARINE_DELTA, ACCESS_MARINE_DATABASE, ACCESS_MARINE_CARGO)
/obj/structure/machinery/cm_vending/sorted/uniform_supply/squad_prep/delta/populate_product_list(scale)
+ ..()
listed_products += list(
list("HEADSET", -1, null, null),
list("Marine Delta Radio Headset", 10, /obj/item/device/radio/headset/almayer/marine/delta, VENDOR_ITEM_REGULAR),
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 4138cb4311ac..5d38f238023f 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -15,10 +15,14 @@
var/list/datum/stack_recipe/recipes
var/singular_name
var/amount = 1
- var/max_amount //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount
- var/stack_id //used to determine if two stacks are of the same kind.
- var/amount_sprites = FALSE //does it have sprites for extra amount, like metal, plasteel, or wood
- var/display_maptext = TRUE //does it show amount on top of the icon
+ ///also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount
+ var/max_amount
+ ///used to determine if two stacks are of the same kind.
+ var/stack_id
+ ///does it have sprites for extra amount, like metal, plasteel, or wood
+ var/amount_sprites = FALSE
+ ///does it show amount on top of the icon
+ var/display_maptext = TRUE
//Coords for contents display, to make it play nice with inventory borders.
maptext_x = 4
maptext_y = 3
@@ -330,48 +334,48 @@ Also change the icon to reflect the amount of sheets, if possible.*/
return
if(!use(desired))
return
- var/obj/item/stack/newstack = new src.type(user, desired)
+ var/obj/item/stack/newstack = new type(user, desired)
transfer_fingerprints_to(newstack)
user.put_in_hands(newstack)
- src.add_fingerprint(user)
+ add_fingerprint(user)
newstack.add_fingerprint(user)
- if(src && usr.interactee==src)
- INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), usr)
+ if(!QDELETED(src) && user.interactee == src)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), user)
return TRUE
- else
- return ..()
+
+ return ..()
/obj/item/stack/attack_hand(mob/user as mob)
- if (user.get_inactive_hand() == src)
- var/obj/item/stack/F = new src.type(user, 1)
- transfer_fingerprints_to(F)
- user.put_in_hands(F)
- src.add_fingerprint(user)
- F.add_fingerprint(user)
+ if(user.get_inactive_hand() == src)
+ var/obj/item/stack/new_stack = new type(user, 1)
+ transfer_fingerprints_to(new_stack)
+ user.put_in_hands(new_stack)
+ add_fingerprint(user)
+ new_stack.add_fingerprint(user)
use(1)
- if (src && usr.interactee==src)
- INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), usr)
- else
- ..()
- return
+ if(!QDELETED(src) && user.interactee == src)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), user)
+ return
+
+ return ..()
/obj/item/stack/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/stack))
- var/obj/item/stack/S = W
- if(S.stack_id == stack_id) //same stack type
- if(S.amount >= max_amount)
+ var/obj/item/stack/other_stack = W
+ if(other_stack.stack_id == stack_id) //same stack type
+ if(other_stack.amount >= max_amount)
to_chat(user, SPAN_WARNING("The stack is full!"))
return TRUE
- var/to_transfer = min(src.amount, S.max_amount-S.amount)
+ var/to_transfer = min(amount, other_stack.max_amount - other_stack.amount)
if(to_transfer <= 0)
return
to_chat(user, SPAN_INFO("You transfer [to_transfer] between the stacks."))
- S.add(to_transfer)
- if (S && usr.interactee==S)
- INVOKE_ASYNC(S, TYPE_PROC_REF(/obj/item/stack, interact), usr)
- src.use(to_transfer)
- if (src && usr.interactee==src)
- INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), usr)
+ other_stack.add(to_transfer)
+ if(other_stack && user.interactee == other_stack)
+ INVOKE_ASYNC(other_stack, TYPE_PROC_REF(/obj/item/stack, interact), user)
+ use(to_transfer)
+ if(!QDELETED(src) && user.interactee == src)
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/obj/item/stack, interact), user)
user.next_move = world.time + 0.3 SECONDS
return TRUE
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index d03b154feb6b..1dc1b1e08282 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1364,30 +1364,29 @@
remove_splints()
// target = person whose splints are being removed
-// source = person removing the splints
-/mob/living/carbon/human/proc/remove_splints(mob/living/carbon/human/source)
- var/mob/living/carbon/human/HT = src
- var/mob/living/carbon/human/HS = source
-
- if(!istype(HS))
- HS = src
- if(!istype(HS) || !istype(HT))
+// user = person removing the splints
+/mob/living/carbon/human/proc/remove_splints(mob/living/carbon/human/user)
+ var/mob/living/carbon/human/target = src
+
+ if(!istype(user))
+ user = src
+ if(!istype(user) || !istype(target))
return
var/cur_hand = "l_hand"
- if(!HS.hand)
+ if(!user.hand)
cur_hand = "r_hand"
- if(!HS.action_busy)
+ if(!user.action_busy)
var/list/obj/limb/to_splint = list()
var/same_arm_side = FALSE // If you are trying to splint yourself, need opposite hand to splint an arm/hand
- if(HS.get_limb(cur_hand).status & LIMB_DESTROYED)
- to_chat(HS, SPAN_WARNING("You cannot remove splints without a hand."))
+ if(user.get_limb(cur_hand).status & LIMB_DESTROYED)
+ to_chat(user, SPAN_WARNING("You cannot remove splints without a hand."))
return
for(var/bodypart in list("l_leg","r_leg","l_arm","r_arm","r_hand","l_hand","r_foot","l_foot","chest","head","groin"))
- var/obj/limb/l = HT.get_limb(bodypart)
+ var/obj/limb/l = target.get_limb(bodypart)
if(l && (l.status & LIMB_SPLINTED))
- if(HS == HT)
+ if(user == target)
if((bodypart in list("l_arm", "l_hand")) && (cur_hand == "l_hand"))
same_arm_side = TRUE
continue
@@ -1398,43 +1397,45 @@
var/msg = "" // Have to use this because there are issues with the to_chat macros and text macros and quotation marks
if(to_splint.len)
- if(do_after(HS, HUMAN_STRIP_DELAY * HS.get_skill_duration_multiplier(SKILL_MEDICAL), INTERRUPT_ALL, BUSY_ICON_GENERIC, HT, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
+ if(do_after(user, HUMAN_STRIP_DELAY * user.get_skill_duration_multiplier(SKILL_MEDICAL), INTERRUPT_ALL, BUSY_ICON_GENERIC, target, INTERRUPT_MOVED, BUSY_ICON_GENERIC))
var/can_reach_splints = TRUE
var/amount_removed = 0
if(wear_suit && istype(wear_suit,/obj/item/clothing/suit/space))
- var/obj/item/clothing/suit/space/suit = HT.wear_suit
+ var/obj/item/clothing/suit/space/suit = target.wear_suit
if(suit.supporting_limbs && suit.supporting_limbs.len)
- msg = "[HS == HT ? "your":"\proper [HT]'s"]"
- to_chat(HS, SPAN_WARNING("You cannot remove the splints, [msg] [suit] is supporting some of the breaks."))
+ msg = "[user == target ? "your":"\proper [target]'s"]"
+ to_chat(user, SPAN_WARNING("You cannot remove the splints, [msg] [suit] is supporting some of the breaks."))
can_reach_splints = FALSE
if(can_reach_splints)
- var/obj/item/stack/W = new /obj/item/stack/medical/splint(HS.loc)
- W.amount = 0 //we checked that we have at least one bodypart splinted, so we can create it no prob. Also we need amount to be 0
- W.add_fingerprint(HS)
- for(var/obj/limb/l in to_splint)
+ var/obj/item/stack/medical/splint/new_splint = new(user.loc)
+ new_splint.amount = 0 //we checked that we have at least one bodypart splinted, so we can create it no prob. Also we need amount to be 0
+ new_splint.add_fingerprint(user)
+ for(var/obj/limb/cur_limb in to_splint)
amount_removed++
- l.status &= ~LIMB_SPLINTED
+ cur_limb.status &= ~LIMB_SPLINTED
pain.recalculate_pain()
- if(l.status & LIMB_SPLINTED_INDESTRUCTIBLE)
- new /obj/item/stack/medical/splint/nano(HS.loc, 1)
- l.status &= ~LIMB_SPLINTED_INDESTRUCTIBLE
- else if(!W.add(1))
- W = new /obj/item/stack/medical/splint(HS.loc)//old stack is dropped, time for new one
- W.amount = 0
- W.add_fingerprint(HS)
- W.add(1)
- msg = "[HS == HT ? "their own":"\proper [HT]'s"]"
- HT.visible_message(SPAN_NOTICE("[HS] removes [msg] [amount_removed>1 ? "splints":"splint"]."), \
+ if(cur_limb.status & LIMB_SPLINTED_INDESTRUCTIBLE)
+ new /obj/item/stack/medical/splint/nano(user.loc, 1)
+ cur_limb.status &= ~LIMB_SPLINTED_INDESTRUCTIBLE
+ else if(!new_splint.add(1))
+ new_splint = new(user.loc)//old stack is dropped, time for new one
+ new_splint.amount = 0
+ new_splint.add_fingerprint(user)
+ new_splint.add(1)
+ if(new_splint.amount == 0)
+ qdel(new_splint) //we only removed nano splints
+ msg = "[user == target ? "their own":"\proper [target]'s"]"
+ target.visible_message(SPAN_NOTICE("[user] removes [msg] [amount_removed>1 ? "splints":"splint"]."), \
SPAN_NOTICE("Your [amount_removed>1 ? "splints are":"splint is"] removed."))
- HT.update_med_icon()
+ target.update_med_icon()
else
- msg = "[HS == HT ? "your":"\proper [HT]'s"]"
- to_chat(HS, SPAN_NOTICE("You stop trying to remove [msg] splints."))
+ msg = "[user == target ? "your":"\proper [target]'s"]"
+ to_chat(user, SPAN_NOTICE("You stop trying to remove [msg] splints."))
else
if(same_arm_side)
- to_chat(HS, SPAN_WARNING("You need to use the opposite hand to remove the splints on your arm and hand!"))
+ to_chat(user, SPAN_WARNING("You need to use the opposite hand to remove the splints on your arm and hand!"))
else
- to_chat(HS, SPAN_WARNING("There are no splints to remove."))
+ to_chat(user, SPAN_WARNING("There are no splints to remove."))
/mob/living/carbon/human/yautja/Initialize(mapload)
. = ..(mapload, new_species = "Yautja")
diff --git a/code/modules/reagents/chemical_research/Chemical-Research.dm b/code/modules/reagents/chemical_research/Chemical-Research.dm
index e66cb474df50..2050e7e8e607 100644
--- a/code/modules/reagents/chemical_research/Chemical-Research.dm
+++ b/code/modules/reagents/chemical_research/Chemical-Research.dm
@@ -115,7 +115,7 @@ GLOBAL_DATUM_INIT(chemical_data, /datum/chemical_data, new)
return FALSE
//Make the chem storage scale with number of dispensers
storage.recharge_rate += 5
- storage.max_energy += 50
+ storage.max_energy += 100
storage.energy = storage.max_energy
return storage
@@ -125,7 +125,7 @@ GLOBAL_DATUM_INIT(chemical_data, /datum/chemical_data, new)
return FALSE
//Make the chem storage scale with number of dispensers
storage.recharge_rate -= 5
- storage.max_energy -= 50
+ storage.max_energy -= 100
storage.energy = storage.max_energy
return TRUE
diff --git a/code/modules/reagents/chemistry_machinery/chem_storage.dm b/code/modules/reagents/chemistry_machinery/chem_storage.dm
index fa4f2f5c6c5a..a5196147febe 100644
--- a/code/modules/reagents/chemistry_machinery/chem_storage.dm
+++ b/code/modules/reagents/chemistry_machinery/chem_storage.dm
@@ -12,7 +12,7 @@
var/recharge_cooldown = 15
var/recharge_rate = 10
var/energy = 50
- var/max_energy = 50
+ var/max_energy = 100
unslashable = TRUE
unacidable = TRUE
diff --git a/code/modules/vehicles/interior/interactable/vendors.dm b/code/modules/vehicles/interior/interactable/vendors.dm
index e8b03402f5d3..8069c8ba71e4 100644
--- a/code/modules/vehicles/interior/interactable/vendors.dm
+++ b/code/modules/vehicles/interior/interactable/vendors.dm
@@ -17,10 +17,6 @@
/obj/item/reagent_container/hypospray/autoinjector/skillless,
/obj/item/reagent_container/hypospray/autoinjector/skillless/tramadol,
)
- stack_refill = list(
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/ointment,
- )
//MED APC version of WY Med, provides resupply for basic stuff. Provides a decent amount of cryobags for evacuating hugged marines.
/obj/structure/machinery/cm_vending/sorted/medical/vehicle
@@ -57,14 +53,6 @@
/obj/item/reagent_container/hypospray/autoinjector/tricord/skillless,
)
- stack_refill = list(
- /obj/item/stack/medical/advanced/ointment,
- /obj/item/stack/medical/advanced/bruise_pack,
- /obj/item/stack/medical/ointment,
- /obj/item/stack/medical/bruise_pack,
- /obj/item/stack/medical/splint,
- )
-
/obj/structure/machinery/cm_vending/sorted/medical/vehicle/populate_product_list(scale)
listed_products = list(
list("FIELD SUPPLIES", -1, null, null),
@@ -125,7 +113,6 @@
wrenchable = FALSE
hackable = FALSE
density = FALSE
- var/being_restocked = FALSE
vend_flags = VEND_CLUTTER_PROTECTION | VEND_LIMITED_INVENTORY | VEND_TO_HAND | VEND_LOAD_AMMO_BOXES
diff --git a/tgui/packages/tgui/interfaces/VendingSorted.tsx b/tgui/packages/tgui/interfaces/VendingSorted.tsx
index f72ddb2c0a31..76f32cfa6fae 100644
--- a/tgui/packages/tgui/interfaces/VendingSorted.tsx
+++ b/tgui/packages/tgui/interfaces/VendingSorted.tsx
@@ -1,10 +1,11 @@
import { KEY_ESCAPE } from 'common/keycodes';
import { useBackend, useLocalState } from '../backend';
-import { Button, Section, Flex, Box, Tooltip, Input, NoticeBox, Icon } from '../components';
+import { Button, Section, Flex, Box, Tooltip, Input, NoticeBox, Icon, ProgressBar } from '../components';
import { Window } from '../layouts';
import { classes } from 'common/react';
import { BoxProps } from '../components/Box';
import { Table, TableCell, TableRow } from '../components/Table';
+import { toFixed } from 'common/math';
const THEME_COMP = 0;
const THEME_USCM = 1;
@@ -35,8 +36,11 @@ interface VendingData {
theme: string;
displayed_categories: VendingCategory[];
stock_listing: Array;
+ stock_listing_partials?: Array;
show_points?: boolean;
current_m_points?: number;
+ reagents?: number;
+ reagents_max?: number;
}
interface VenableItem {
@@ -113,20 +117,24 @@ const VendableItemRow = (props: VenableItem) => {
const quantity = data.stock_listing[record.prod_index - 1];
const available = quantity > 0;
+ const partial_quantity =
+ data.stock_listing_partials?.[record.prod_index - 1] ?? 0;
+ const partialDesignation = partial_quantity > 0 ? '*' : '';
const isMandatory = record.prod_color === VENDOR_ITEM_MANDATORY;
const isRecommended = record.prod_color === VENDOR_ITEM_RECOMMENDED;
return (
<>
-
+
-
+
{quantity}
+ {partialDesignation}
@@ -142,7 +150,10 @@ const VendableItemRow = (props: VenableItem) => {
-
+
>
@@ -164,7 +175,7 @@ const VendableClothingItemRow = (props: {
return (
<>
-
+
@@ -192,7 +203,7 @@ const VendableClothingItemRow = (props: {
@@ -256,6 +267,7 @@ export const ViewVendingCategory = (props: VendingCategoryProps) => {
return (
{
const { data, act } = useBackend();
if (data === undefined) {
return (
-
+
no data!
);
@@ -302,8 +314,10 @@ export const VendingSorted = () => {
const isEmpty = categories.length === 0;
const show_points = data.show_points ?? false;
const points = data.current_m_points ?? 0;
+ const reagents = data.reagents ?? 0;
+ const reagents_max = data.reagents_max ?? 0;
return (
-
+
{
/>
+ {reagents_max > 0 && (
+
+
+ Reagents
+
+
+
+ {toFixed(reagents) + ' units'}
+
+
+
+ )}
)}
diff --git a/tgui/packages/tgui/styles/interfaces/VendingSorted.scss b/tgui/packages/tgui/styles/interfaces/VendingSorted.scss
index 780b2b22711e..8135619f0fdd 100644
--- a/tgui/packages/tgui/styles/interfaces/VendingSorted.scss
+++ b/tgui/packages/tgui/styles/interfaces/VendingSorted.scss
@@ -135,10 +135,6 @@ $mandatory-color: rgb(128, 83, 0);
width: 100%;
}
- .ItemTable {
- width: 95vw;
- }
-
.IconCell {
width: 32px;
}
@@ -172,6 +168,11 @@ $mandatory-color: rgb(128, 83, 0);
color: white;
}
+ .SmallIcon {
+ min-width: 20px;
+ max-width: 20px;
+ }
+
.MandatoryItemText {
color: rgb(255, 200, 90);
}
diff --git a/tgui/packages/tgui/styles/themes/uscm.scss b/tgui/packages/tgui/styles/themes/uscm.scss
index 99fd2be0b670..edfd444ff4fe 100644
--- a/tgui/packages/tgui/styles/themes/uscm.scss
+++ b/tgui/packages/tgui/styles/themes/uscm.scss
@@ -26,4 +26,9 @@
background-position: right 40px top 50%;
background-repeat: no-repeat;
}
+ .Vendor {
+ .VendingFlexAlt {
+ background-color: rgba(#0c0e1e, 1);
+ }
+ }
}