Custom, crew-buildable vendors (#28009)

* Buildable vendors.

* Lint.

* I think this is right?

* It's already IN their hands.

* Lint, re-check item after prompting for price.

* Fixes.

* Review fixes, working dissassembly again.

* Apply suggestions from code review

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Signed-off-by: Charlie Nolan <funnyman3595@gmail.com>

* Restrict appearance to buildable vendor sprites.

* tyop fix

---------

Signed-off-by: Charlie Nolan <funnyman3595@gmail.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
Charlie Nolan
2025-01-28 22:00:14 +00:00
committed by GitHub
co-authored by Burzah
parent 0c5de8b955
commit a6d347e14a
11 changed files with 325 additions and 116 deletions
+6 -2
View File
@@ -318,7 +318,8 @@ to destroy them and players will be able to make replacements.
"HydroDrobe" = /obj/machinery/economy/vending/hydrodrobe,
"JaniDrobe" = /obj/machinery/economy/vending/janidrobe,
"LawDrobe" = /obj/machinery/economy/vending/lawdrobe,
"TrainDrobe" = /obj/machinery/economy/vending/traindrobe)
"TrainDrobe" = /obj/machinery/economy/vending/traindrobe,
"CrewVend 3000" = /obj/machinery/economy/vending/custom)
var/static/list/unique_vendors = list(
"ShadyCigs Ultra" = /obj/machinery/economy/vending/cigarette/beach,
"SyndiMed Plus" = /obj/machinery/economy/vending/wallmed/syndicate)
@@ -338,7 +339,10 @@ to destroy them and players will be able to make replacements.
build_path = typepath
board_name = "[type] Vendor"
format_board_name()
req_components = list(initial(typepath.refill_canister) = 1)
if(initial(typepath.refill_canister))
req_components = list(initial(typepath.refill_canister) = 1)
else
req_components = list()
/obj/item/circuitboard/slot_machine
board_name = "Slot Machine"
+84
View File
@@ -0,0 +1,84 @@
/obj/machinery/economy/vending/custom
name = "\improper CrewVend 3000"
refill_canister = null
always_deconstruct = TRUE
var/obj/item/eftpos/linked_pos
/obj/machinery/economy/vending/custom/Destroy()
if(!isnull(linked_pos))
linked_pos.linked_vendors -= src
linked_pos = null
return ..()
/obj/machinery/economy/vending/custom/locked()
return isnull(linked_pos) || linked_pos.transaction_locked
/obj/machinery/economy/vending/custom/get_vendor_account()
return linked_pos?.linked_account || ..()
/obj/machinery/economy/vending/custom/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(istype(used, /obj/item/eftpos))
visible_message("<span class='notice'>[src] beeps as [user] links it to [used].</span>", "<span class='notice'>You hear something beep.</span>")
if(!isnull(linked_pos))
linked_pos.linked_vendors -= src
linked_pos = used
var/obj/item/eftpos/pos = used
pos.linked_vendors += src
return ITEM_INTERACT_COMPLETE
else if(isnull(linked_pos))
to_chat(user, "<span class='warning'>You need to link a point of sale device first!</span>")
return ITEM_INTERACT_COMPLETE
else if(locked())
return ..()
if(!user.canUnEquip(used, FALSE))
to_chat(user, "<span class='warning'>\The [used] is stuck to your hand!</span>")
return ITEM_INTERACT_COMPLETE
for(var/datum/data/vending_product/physical/record in physical_product_records)
if(record.get_amount_left() == 0)
physical_product_records -= record
qdel(record)
else if(isitem(record.items[1]))
var/obj/item/existing = record.items[1]
if(existing.should_stack_with(used))
record.items += used
user.unequip(used)
used.moveToNullspace()
user.visible_message("<span class='notice'>[user] puts [used] into [src].</span>", "<span class='notice>'You put [used] into [src].</span>")
return ITEM_INTERACT_COMPLETE
var/price = tgui_input_number(user, "How much do you want to sell [used] for?")
if(!isnum(price))
return ITEM_INTERACT_COMPLETE
if(!Adjacent(user))
to_chat(user, "<span class='warning'>You can't reach [src] from here!</span>")
return ITEM_INTERACT_COMPLETE
if(!user.is_holding(used))
to_chat(user, "<span class='warning'>\The [used] isn't in your hand anymore!</span>")
return ITEM_INTERACT_COMPLETE
if(!user.canUnEquip(used, FALSE))
to_chat(user, "<span class='warning'>\The [used] is stuck to your hand!</span>")
return ITEM_INTERACT_COMPLETE
var/datum/data/vending_product/physical/record = new(used.name, used.icon, used.icon_state)
record.items += used
record.price = price
physical_product_records += record
SStgui.update_uis(src, TRUE)
user.unequip(used)
used.moveToNullspace()
user.visible_message("[user] puts [used] into [src].", "You put [used] into [src].")
return ITEM_INTERACT_COMPLETE
/obj/machinery/economy/vending/custom/crowbar_act(mob/user, obj/item/I)
if(!isnull(linked_pos) && linked_pos.transaction_locked)
user.visible_message("<span class='notice'>[user] tries to pry [src] apart, but fails.</span>", "<span class='notice'>The lock on [src] resists your efforts to pry it apart.</span>")
return TRUE
return ..()
/obj/machinery/economy/vending/custom/delayed_vend(datum/data/vending_product/R, mob/user)
. = ..()
if(istype(R, /datum/data/vending_product/physical) && R.get_amount_left() == 0)
physical_product_records -= R
physical_hidden_records -= R
SStgui.update_uis(src, TRUE)
+3 -18
View File
@@ -156,30 +156,15 @@
return TRUE
/obj/machinery/economy/vending/coffee/do_vend(datum/data/vending_product/R, mob/user)
if(..())
var/obj/item/reagent_containers/drinks/vended = ..()
if(!istype(vended))
return
var/obj/item/reagent_containers/drinks/vended = new R.product_path()
if(istype(vended, /obj/item/reagent_containers/drinks/mug))
var/put_on_turf = TRUE
if(user && iscarbon(user) && user.Adjacent(src))
if(user.put_in_hands(vended))
put_on_turf = FALSE
if(put_on_turf)
var/turf/T = get_turf(src)
vended.forceMove(T)
return
vended.reagents.trans_to(inserted_item, vended.reagents.total_volume)
if(vended.reagents.total_volume)
var/put_on_turf = TRUE
if(user && iscarbon(user) && user.Adjacent(src))
if(user.put_in_hands(vended))
put_on_turf = FALSE
if(put_on_turf)
var/turf/T = get_turf(src)
vended.forceMove(T)
else
if(!vended.reagents.total_volume)
qdel(vended)
+179 -95
View File
@@ -2,15 +2,98 @@
* Datum used to hold information about a product in a vending machine
*/
/datum/data/vending_product
name = "generic"
name = "the idea of vending something"
/// Price to buy one
var/price = 0
/datum/data/vending_product/proc/get_amount_full()
CRASH("Abstract vending product used.")
/datum/data/vending_product/proc/get_amount_left()
CRASH("Abstract vending product used.")
/datum/data/vending_product/proc/get_icon()
CRASH("Abstract vending product used.")
/datum/data/vending_product/proc/get_icon_state()
CRASH("Abstract vending product used.")
/datum/data/vending_product/proc/get_name()
CRASH("Abstract vending product used.")
/datum/data/vending_product/proc/vend(turf/where)
CRASH("Abstract vending product used.")
/datum/data/vending_product/from_path
name = "a magically-created vending product"
///Typepath of the product that is created when this record "sells"
var/product_path = null
var/atom/movable/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
/// Price to buy one
var/price = 0
/datum/data/vending_product/from_path/get_amount_full()
return max_amount
/datum/data/vending_product/from_path/get_amount_left()
return amount
/datum/data/vending_product/from_path/get_icon()
return initial(product_path.icon)
/datum/data/vending_product/from_path/get_icon_state()
return initial(product_path.icon_state)
/datum/data/vending_product/from_path/get_name()
return initial(product_path.name)
/datum/data/vending_product/from_path/vend(turf/where)
amount--
return new product_path(where)
/datum/data/vending_product/physical
name = "a physical vending product"
/// The items available.
var/list/items = list()
/// What should they be called?
var/display_name
/// What icon should they use?
var/display_icon
/// What icon state should they use?
var/display_icon_state
/datum/data/vending_product/physical/New(name, icon, icon_state)
display_name = name
display_icon = icon
display_icon_state = icon_state
/datum/data/vending_product/physical/get_amount_full()
return 1
/datum/data/vending_product/physical/get_amount_left()
return length(items)
/datum/data/vending_product/physical/get_icon()
return display_icon
/datum/data/vending_product/physical/get_icon_state()
return display_icon_state
/datum/data/vending_product/physical/get_name()
return display_name
/datum/data/vending_product/physical/vend(turf/where)
var/atom/movable/vended = items[length(items)]
items.len--
vended.forceMove(where)
return vended
/datum/data/vending_product/physical/proc/on_deconstruct(turf/where)
while(length(items))
vend(where)
/obj/machinery/economy/vending
name = "\improper Vendomat"
@@ -63,7 +146,9 @@
// List of vending_product items available.
var/list/product_records = list()
var/list/physical_product_records = list()
var/list/hidden_records = list()
var/list/physical_hidden_records = list()
/// Unimplemented list of ads that are meant to show up somewhere, but don't.
var/list/ads_list = list()
@@ -82,6 +167,8 @@
//The type of refill canisters used by this machine.
var/obj/item/vending_refill/refill_canister
/// Should we always be able to deconstruct this into components, even if it has no refill_canister?
var/always_deconstruct = FALSE
// Things that can go wrong
/// Allows people to access a vendor that's normally access restricted.
@@ -110,9 +197,6 @@
/// do I look unpowered, even when powered?
var/force_no_power_icon_state = FALSE
///the money account that is tethered to this vendor
var/datum/money_account/vendor_account
/// If this vending machine can be tipped or not
var/tiltable = TRUE
/// If this vendor is currently tipped
@@ -149,6 +233,10 @@
var/build_inv = FALSE
if(!refill_canister)
build_inv = TRUE
if(always_deconstruct)
var/obj/item/circuitboard/vendor/V = new
V.set_type(replacetext(initial(name), "\improper", ""))
component_parts = list(V)
else
component_parts = list()
var/obj/item/circuitboard/vendor/V = new
@@ -168,9 +256,6 @@
// so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is created.
last_slogan = world.time + rand(0, slogan_delay)
if(account_database)
vendor_account = account_database.vendor_account
update_icon(UPDATE_OVERLAYS)
reconnect_database()
power_change()
@@ -263,7 +348,7 @@
amount = 0
var/atom/temp = typepath
var/datum/data/vending_product/R = new /datum/data/vending_product()
var/datum/data/vending_product/from_path/R = new /datum/data/vending_product/from_path()
R.name = initial(temp.name)
R.product_path = typepath
if(!start_empty)
@@ -296,7 +381,7 @@
*/
/obj/machinery/economy/vending/proc/refill_inventory(list/productlist, list/recordlist)
. = 0
for(var/datum/data/vending_product/record as anything in recordlist)
for(var/datum/data/vending_product/from_path/record as anything in recordlist)
var/diff = min(record.max_amount - record.amount, productlist[record.product_path])
if(diff)
productlist[record.product_path] -= diff
@@ -308,7 +393,7 @@
* This is used when the machine is deconstructed, so the items aren't "lost"
*/
/obj/machinery/economy/vending/proc/update_canister()
if(!component_parts)
if(isnull(refill_canister))
return
var/obj/item/vending_refill/R = locate() in component_parts
@@ -323,12 +408,14 @@
*/
/obj/machinery/economy/vending/proc/unbuild_inventory(list/recordlist)
. = list()
for(var/datum/data/vending_product/record as anything in recordlist)
for(var/datum/data/vending_product/from_path/record in recordlist)
.[record.product_path] += record.amount
/obj/machinery/economy/vending/deconstruct(disassembled = TRUE)
eject_item()
if(!refill_canister) //the non constructable vendors drop metal instead of a machine frame.
for(var/datum/data/vending_product/physical/R in physical_product_records + physical_hidden_records)
R.on_deconstruct(loc)
if(!component_parts) //the non constructable vendors drop metal instead of a machine frame.
new /obj/item/stack/sheet/metal(loc, 3)
qdel(src)
else
@@ -371,9 +458,6 @@
return ITEM_INTERACT_COMPLETE
/obj/machinery/economy/vending/attacked_by(obj/item/attacker, mob/living/user)
if(..())
return FINISH_ATTACK
if(tiltable && !tilted && attacker.force)
if(resistance_flags & INDESTRUCTIBLE)
// no goodies, but also no tilts
@@ -404,16 +488,12 @@
visible_message("<span class='notice'>[num_freebies] free goodie\s tumble[num_freebies > 1 ? "" : "s"] out of [src]!</span>")
for(var/i in 1 to num_freebies)
for(var/datum/data/vending_product/R in shuffle(product_records))
for(var/datum/data/vending_product/R in shuffle(product_records + physical_product_records))
if(R.amount <= 0)
if(R.get_amount_left() <= 0)
continue
var/dump_path = R.product_path
if(!dump_path)
continue
new dump_path(get_turf(src))
R.amount--
R.vend(loc)
break
/obj/machinery/economy/vending/HasProximity(atom/movable/AM)
@@ -609,6 +689,8 @@
/obj/machinery/economy/vending/ui_data(mob/user)
var/list/data = list()
data["locked"] = locked()
data["chargesMoney"] = locked()
data["usermoney"] = 0
data["inserted_cash"] = cash_transaction
data["user"] = null
@@ -629,8 +711,8 @@
data["user"]["job"] = C.rank ? C.rank : "No Job"
data["stock"] = list()
for(var/datum/data/vending_product/R in product_records + hidden_records)
data["stock"][R.name] = R.amount
for(var/datum/data/vending_product/R in product_records + physical_product_records + hidden_records + physical_hidden_records)
data["stock"][R.get_name()] = R.get_amount_left()
data["extended_inventory"] = extended_inventory
data["vend_ready"] = vend_ready
data["panel_open"] = panel_open ? TRUE : FALSE
@@ -642,30 +724,27 @@
/obj/machinery/economy/vending/ui_static_data(mob/user)
var/list/data = list()
data["chargesMoney"] = length(prices) > 0 ? TRUE : FALSE
data["product_records"] = list()
var/i = 1
for(var/datum/data/vending_product/R in product_records)
var/obj/item = R.product_path
for(var/datum/data/vending_product/R in product_records + physical_product_records)
var/list/data_pr = list(
name = R.name,
price = (item in prices) ? prices[item] : 0,
icon = item.icon,
icon_state = item.icon_state,
max_amount = R.max_amount,
name = R.get_name(),
price = R.price,
icon = R.get_icon(),
icon_state = R.get_icon_state(),
max_amount = R.get_amount_full(),
is_hidden = FALSE,
inum = i++
)
data["product_records"] += list(data_pr)
data["hidden_records"] = list()
for(var/datum/data/vending_product/R in hidden_records)
var/obj/item = R.product_path
for(var/datum/data/vending_product/R in hidden_records + physical_hidden_records)
var/list/data_hr = list(
name = R.name,
price = (item in prices) ? prices[item] : 0,
icon = item.icon,
icon_state = item.icon_state,
max_amount = R.max_amount,
name = R.get_name(),
price = R.price,
icon = R.get_icon(),
icon_state = R.get_icon_state(),
max_amount = R.get_amount_full(),
is_hidden = TRUE,
inum = i++,
premium = TRUE
@@ -697,6 +776,27 @@
if("vend")
var/key = text2num(params["inum"])
try_vend(key, user)
if("rename")
if(!locked())
var/new_name = tgui_input_text(user, "Rename the vendor to what?", name)
if(!isnull(new_name))
name = new_name
if("change_appearance")
if(locked())
return
var/possible_icons = list()
var/icon_lookup = list()
var/buildable_vendors = /obj/item/circuitboard/vendor::station_vendors
for(var/vendor_name in buildable_vendors)
var/obj/machinery/economy/vending/vendor_type = buildable_vendors[vendor_name]
possible_icons[vendor_name] = image(icon = initial(vendor_type.icon), icon_state = initial(vendor_type.icon_state))
icon_lookup[vendor_name] = list(initial(vendor_type.icon), initial(vendor_type.icon_state))
var/choice = show_radial_menu(user, src, possible_icons, radius=48)
if(!choice || !(choice in icon_lookup) || user.stat || !in_range(user, src) || QDELETED(src))
return
icon = icon_lookup[choice][1]
icon_state = icon_lookup[choice][2]
if(.)
add_fingerprint(user)
@@ -708,9 +808,9 @@
to_chat(user, "<span class='warning'>The vending machine cannot dispense products while its service panel is open!</span>")
return
var/list/display_records = product_records
var/list/display_records = product_records + physical_product_records
if(extended_inventory)
display_records = product_records + hidden_records
display_records = product_records + physical_product_records + hidden_records + physical_hidden_records
if(key < 1 || key > length(display_records))
log_debug("invalid inum passed to a [name] vendor.</span>")
return
@@ -718,29 +818,25 @@
if(!istype(R))
log_debug("player attempted to access an unknown vending_product at a [name] vendor.</span>")
return
var/list/record_to_check = product_records
var/list/record_to_check = product_records + physical_product_records
if(extended_inventory)
record_to_check = product_records + hidden_records
if(!R.product_path)
log_debug("player attempted to access an unknown product record at a [name] vendor.</span>")
record_to_check = product_records + physical_product_records + hidden_records + physical_hidden_records
if(!extended_inventory && ((R in hidden_records) || (R in physical_hidden_records)))
// Exploit prevention, stop the user purchasing hidden stuff if they haven't hacked the machine.
log_debug("player attempted to access a [name] vendor extended inventory when it was not allowed.</span>")
return
if(R in hidden_records)
if(!extended_inventory)
// Exploit prevention, stop the user purchasing hidden stuff if they haven't hacked the machine.
log_debug("player attempted to access a [name] vendor extended inventory when it was not allowed.</span>")
return
else if(!(R in record_to_check))
// Exploit prevention, stop the user
message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(user)]!")
return
if(R.amount <= 0)
to_chat(user, "Sold out of [R.name].")
if(R.get_amount_left() <= 0)
to_chat(user, "Sold out of [R.get_name()].")
flick(icon_deny, src)
return
vend_ready = FALSE // From this point onwards, vendor is locked to performing this transaction only, until it is resolved.
if(!ishuman(user) || R.price <= 0)
if(!ishuman(user) || R.price <= 0 || !locked())
// Either the purchaser is not human, or the item is free.
// Skip all payment logic.
vend(R, user)
@@ -757,19 +853,20 @@
currently_vending = R
var/paid = FALSE
if(cash_transaction < currently_vending.price && GLOB.station_money_database.vendor_account?.suspended)
to_chat(user, "Vendor account offline. Unable to process transaction.")
var/datum/money_account/vendor_account = get_vendor_account()
if(cash_transaction < currently_vending.price && (isnull(vendor_account) || vendor_account.suspended))
to_chat(user, "<span class='warning'>Vendor account offline. Unable to process transaction.</span>")
flick(icon_deny, src)
vend_ready = TRUE
return
if(cash_transaction >= currently_vending.price)
paid = pay_with_cash(currently_vending.price, "Vendor Transaction", name, user, GLOB.station_money_database.vendor_account)
paid = pay_with_cash(currently_vending.price, "Vendor Transaction", name, user, vendor_account)
else if(istype(C, /obj/item/card))
// Because this uses H.get_idcard(TRUE), it will attempt to use:
// active hand, inactive hand, pda.id, and then wear_id ID in that order
// this is important because it lets people buy stuff with someone else's ID by holding it while using the vendor
paid = pay_with_card(C, currently_vending.price, "Vendor transaction", name, user, GLOB.station_money_database.vendor_account)
paid = pay_with_card(C, currently_vending.price, "Vendor transaction", name, user, vendor_account)
else if(user.can_advanced_admin_interact())
to_chat(user, "<span class='notice'>Vending object due to admin interaction.</span>")
paid = TRUE
@@ -794,15 +891,13 @@
vend_ready = TRUE
return
if(!R.amount)
if(R.get_amount_left() <= 0)
to_chat(user, "<span class='warning'>The vending machine has ran out of that product.</span>")
vend_ready = TRUE
return
vend_ready = FALSE //One thing at a time!!
R.amount--
if(last_reply + vend_delay + 200 <= world.time && vend_reply)
speak(vend_reply)
last_reply = world.time
@@ -819,25 +914,17 @@
currently_vending = null
//override this proc to add handling for what to do with the vended product when you have a inserted item and remember to include a parent call for this generic handling
/obj/machinery/economy/vending/proc/do_vend(datum/data/vending_product/R, mob/user)
if(!item_slot || !inserted_item)
var/put_on_turf = TRUE
var/obj/vended = new R.product_path()
if(user && iscarbon(user) && user.Adjacent(src))
if(user.put_in_hands(vended))
put_on_turf = FALSE
if(put_on_turf)
var/turf/T = get_turf(src)
vended.forceMove(T)
vended.scatter_atom()
return TRUE
return FALSE
/obj/machinery/economy/vending/proc/do_vend(datum/data/vending_product/R, mob/user, put_in_hands = TRUE)
var/vended = R.vend(loc)
if(put_in_hands && isliving(user) && istype(vended, /obj/item) && Adjacent(user))
user.put_in_hands(vended)
return vended
/* Example override for do_vend proc:
/obj/machinery/economy/vending/example/do_vend(datum/data/vending_product/R)
if(..())
var/obj/item/vended = ..()
if(!vended)
return
var/obj/item/vended = new R.product_path()
if(inserted_item.force == initial(inserted_item.force)
inserted_item.force += vended.force
inserted_item.damtype = vended.damtype
@@ -903,20 +990,15 @@
var/found_anything = TRUE
while(found_anything)
found_anything = FALSE
for(var/record in shuffle(product_records))
var/datum/data/vending_product/R = record
if(R.amount <= 0) //Try to use a record that actually has something to dump.
for(var/datum/data/vending_product/record in shuffle(product_records))
if(record.get_amount_left() <= 0) //Try to use a record that actually has something to dump.
continue
var/dump_path = R.product_path
if(!dump_path)
continue
R.amount--
var/atom/movable/thing = record.vend()
// busting open a vendor will destroy some of the contents
if(found_anything && prob(80))
continue
qdel(thing)
var/obj/O = new dump_path(loc)
step(O, pick(GLOB.alldirs))
step(thing, pick(GLOB.alldirs))
found_anything = TRUE
dump_amount++
if(dump_amount >= 16)
@@ -933,15 +1015,11 @@
if(!target)
return 0
for(var/datum/data/vending_product/R in product_records)
if(R.amount <= 0) //Try to use a record that actually has something to dump.
continue
var/dump_path = R.product_path
if(!dump_path)
for(var/datum/data/vending_product/R in product_records + physical_product_records)
if(R.get_amount_left() <= 0) //Try to use a record that actually has something to dump.
continue
R.amount--
throw_item = new dump_path(loc)
throw_item = R.vend(loc)
break
if(!throw_item)
return
@@ -1012,6 +1090,12 @@
mob_hurt = TRUE
return ..()
/obj/machinery/economy/vending/proc/locked()
return TRUE
/obj/machinery/economy/vending/proc/get_vendor_account()
return GLOB.station_money_database.vendor_account
/*
* Vending machine types
*/
+4
View File
@@ -705,6 +705,7 @@
/obj/item/clothing/accessory/armband/service = 3,
/obj/item/reagent_containers/glass/rag = 3,
/obj/item/storage/box/dish_drive = 1,
/obj/item/storage/box/crewvend = 1,
/obj/item/clothing/head/helmet/space/plasmaman/chef = 2,
/obj/item/clothing/under/plasmaman/chef = 2)
@@ -724,6 +725,7 @@
/obj/item/clothing/accessory/armband/service = 20,
/obj/item/reagent_containers/glass/rag = 5,
/obj/item/storage/box/dish_drive = 100,
/obj/item/storage/box/crewvend = 100,
/obj/item/clothing/head/helmet/space/plasmaman/chef = 60,
/obj/item/clothing/under/plasmaman/chef = 60)
@@ -751,6 +753,7 @@
/obj/item/clothing/accessory/armband/service = 3,
/obj/item/reagent_containers/glass/rag = 3,
/obj/item/storage/box/dish_drive = 1,
/obj/item/storage/box/crewvend = 1,
/obj/item/clothing/head/helmet/space/plasmaman/white = 2,
/obj/item/clothing/under/plasmaman/enviroslacks = 2)
@@ -768,6 +771,7 @@
/obj/item/clothing/accessory/armband/service = 20,
/obj/item/reagent_containers/glass/rag = 5,
/obj/item/storage/box/dish_drive = 100,
/obj/item/storage/box/crewvend = 100,
/obj/item/clothing/head/helmet/space/plasmaman/white = 60,
/obj/item/clothing/under/plasmaman/enviroslacks = 60)
@@ -1246,6 +1246,17 @@
new /obj/item/stock_parts/matter_bin(src)
new /obj/item/screwdriver(src)
/obj/item/storage/box/crewvend
name = "CrewVend 3000 Kit"
desc = "Contains everything you need to build your own vending machine!"
/obj/item/storage/box/crewvend/populate_contents()
new /obj/item/stack/sheet/metal/(src, 5)
new /obj/item/stack/cable_coil/five(src)
var/obj/item/circuitboard/vendor/board = new /obj/item/circuitboard/vendor(src)
board.set_type("CrewVend 3000")
new /obj/item/screwdriver(src)
/obj/item/storage/box/hardmode_box
name = "box of HRD-MDE project box"
desc = "Contains everything needed to get yourself killed for a medal."
@@ -7,6 +7,7 @@
desc = "A base economy machine."
anchored = TRUE
density = TRUE
new_attack_chain = TRUE
///Can this machine access restricted money accounts?
var/restricted_bypass = FALSE
@@ -26,6 +26,9 @@
///Is this a portable unit that you can offer with *payme?
var/can_offer = TRUE
///The vendors that are linked to this EFTPOS.
var/list/linked_vendors = list()
/obj/item/eftpos/Initialize(mapload)
machine_name = "EFTPOS #[rand(101, 999)]"
access_code = rand(1000, 9999)
@@ -35,6 +38,16 @@
print_reference()
return ..()
/obj/item/eftpos/Destroy()
account_database = null
linked_account = null
for(var/obj/machinery/economy/vending/custom/vendor in linked_vendors)
if(vendor.linked_pos == src)
vendor.linked_pos = null
linked_vendors.Cut()
return ..()
/obj/item/eftpos/proc/reconnect_database()
account_database = GLOB.station_money_database
@@ -138,6 +151,9 @@
transaction_paid = FALSE
else if(linked_account)
transaction_locked = TRUE
for(var/obj/machinery/economy/vending/custom/vendor in linked_vendors)
if(vendor.linked_pos == src)
SStgui.update_uis(vendor, TRUE)
else
to_chat(user, "[bicon(src)]<span class='warning'>No account connected to send transactions to.</span>")
if("reset")
+1
View File
@@ -966,6 +966,7 @@
#include "code\game\machinery\tcomms\tcomms_core.dm"
#include "code\game\machinery\tcomms\tcomms_presets.dm"
#include "code\game\machinery\vendors\contraband_vendors.dm"
#include "code\game\machinery\vendors\custom_vendors.dm"
#include "code\game\machinery\vendors\departmental_vendors.dm"
#include "code\game\machinery\vendors\generic_vendors.dm"
#include "code\game\machinery\vendors\tilt_crits.dm"
+19
View File
@@ -66,6 +66,7 @@ export const Vending = (props, context) => {
inserted_item_name,
panel_open,
speaker,
locked,
} = data;
let inventory;
@@ -83,6 +84,24 @@ export const Vending = (props, context) => {
>
<Window.Content scrollable>
<Stack fill vertical>
{!locked && (
<Stack.Item>
<Section title="Configuration">
<Stack horizontal>
<Stack.Item>
<Button icon="pen-to-square" content="Rename Vendor" onClick={() => act('rename', {})} />
</Stack.Item>
<Stack.Item>
<Button
icon="pen-to-square"
content="Change Vendor Appearance"
onClick={() => act('change_appearance', {})}
/>
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
)}
{!!chargesMoney && (
<Stack.Item>
<Section
File diff suppressed because one or more lines are too long