mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-01 12:31:32 +00:00
About The Pull Request I didn't like how the wardrobe replaced lockers but you couldn't clean up after yourself say once you switched to the nurse's outfit as a doctor. Now, anyone can put clothes in a wardrobe so long as that clothing is a vendable product of said wardrobe (No engineering jumpsuits in science vendor, etc.). Building off the previous snack machine vendor, this lays the framework for ALL vendors to allow all sorts of items to be inputted into vendors, all you have to do is change canLoadItem(obj/item/I,mob/user) to TRUE for the items you want the vendor to accept! It also has an option to restrict loading by changing canload_access_list. NOTE: having any of the access permits input instead of all access is needed to input (important distinction!) ECONOMY: This will make it so any clothes you put in becomes a sellable product. It does NOT make it free unless you can already access the vendor's contents for free. Code improvement + minor QoL with minute balance implications. If you want to discuss how making it easier to clean up your unused clothes makes it more difficult for antags to sneak then I'm in trouble lol. Why It's Good For The Game Changelog cl ExcessiveUseOfVending tweak: Wardrobe Vendors will now accept clothing types they sell. Now you can clean up after getting that cool alternate uniform! code: see PR #43964 on how to easily setup a vending machine to accept items! /cl
641 lines
20 KiB
Plaintext
641 lines
20 KiB
Plaintext
/*
|
|
* Vending machine types - Can be found under /code/modules/vending/
|
|
*/
|
|
|
|
/*
|
|
|
|
/obj/machinery/vending/[vendors name here] // --vending machine template :)
|
|
name = ""
|
|
desc = ""
|
|
icon = ''
|
|
icon_state = ""
|
|
products = list()
|
|
contraband = list()
|
|
premium = list()
|
|
|
|
IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY CANISTER CHARGES in vending_items.dm
|
|
*/
|
|
|
|
#define MAX_VENDING_INPUT_AMOUNT 30
|
|
|
|
/datum/data/vending_product
|
|
name = "generic"
|
|
var/product_path = null
|
|
var/amount = 0
|
|
var/max_amount = 0
|
|
var/custom_price
|
|
var/custom_premium_price
|
|
|
|
/obj/machinery/vending
|
|
name = "\improper Vendomat"
|
|
desc = "A generic vending machine."
|
|
icon = 'icons/obj/vending.dmi'
|
|
icon_state = "generic"
|
|
layer = BELOW_OBJ_LAYER
|
|
density = TRUE
|
|
verb_say = "beeps"
|
|
verb_ask = "beeps"
|
|
verb_exclaim = "beeps"
|
|
max_integrity = 300
|
|
integrity_failure = 100
|
|
armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70)
|
|
circuit = /obj/item/circuitboard/machine/vendor
|
|
payment_department = ACCOUNT_SRV
|
|
var/active = 1 //No sales pitches if off!
|
|
var/vend_ready = 1 //Are we ready to vend?? Is it time??
|
|
var/purchase_message_cooldown
|
|
var/last_shopper
|
|
|
|
// To be filled out at compile time
|
|
var/list/products = list() //For each, use the following pattern:
|
|
var/list/contraband = list() //list(/type/path = amount, /type/path2 = amount2)
|
|
var/list/premium = list() //No specified amount = only one in stock
|
|
|
|
var/product_slogans = "" //String of slogans separated by semicolons, optional
|
|
var/product_ads = "" //String of small ad messages in the vending screen - random chance
|
|
var/list/product_records = list()
|
|
var/list/hidden_records = list()
|
|
var/list/coin_records = list()
|
|
var/list/slogan_list = list()
|
|
var/list/small_ads = list() //Small ad messages in the vending screen - random chance of popping up whenever you open it
|
|
var/vend_reply //Thank you for shopping!
|
|
var/last_reply = 0
|
|
var/last_slogan = 0 //When did we last pitch?
|
|
var/slogan_delay = 6000 //How long until we can pitch again?
|
|
var/icon_vend //Icon_state when vending!
|
|
var/icon_deny //Icon_state when vending!
|
|
var/seconds_electrified = MACHINE_NOT_ELECTRIFIED //Shock customers like an airlock.
|
|
var/shoot_inventory = 0 //Fire items at customers! We're broken!
|
|
var/shoot_inventory_chance = 2
|
|
var/shut_up = 0 //Stop spouting those godawful pitches!
|
|
var/extended_inventory = 0 //can we access the hidden inventory?
|
|
var/scan_id = 1
|
|
var/obj/item/coin/coin
|
|
var/obj/item/stack/spacecash/bill
|
|
var/chef_price = 10
|
|
var/default_price = 25
|
|
var/extra_price = 50
|
|
var/onstation = TRUE //if it doesn't originate from off-station during mapload, everything is free
|
|
var/list/canload_access_list
|
|
|
|
var/list/vending_machine_input = list()
|
|
var/input_display_header = "Custom Compartment"
|
|
|
|
var/obj/item/vending_refill/refill_canister = null //The type of refill canisters used by this machine.
|
|
|
|
/obj/item/circuitboard
|
|
var/onstation = TRUE //if the circuit board originated from a vendor off station or not.
|
|
|
|
/obj/machinery/vending/Initialize(mapload)
|
|
var/build_inv = FALSE
|
|
if(!refill_canister)
|
|
circuit = null
|
|
build_inv = TRUE
|
|
. = ..()
|
|
wires = new /datum/wires/vending(src)
|
|
if(build_inv) //non-constructable vending machine
|
|
build_inventory(products, product_records)
|
|
build_inventory(contraband, hidden_records)
|
|
build_inventory(premium, coin_records)
|
|
|
|
slogan_list = splittext(product_slogans, ";")
|
|
// So not all machines speak at the exact same time.
|
|
// The first time this machine says something will be at slogantime + this random value,
|
|
// so if slogantime is 10 minutes, it will say it at somewhere between 10 and 20 minutes after the machine is crated.
|
|
last_slogan = world.time + rand(0, slogan_delay)
|
|
power_change()
|
|
|
|
if(mapload) //check if it was initially created off station during mapload.
|
|
if(!is_station_level(z))
|
|
onstation = FALSE
|
|
if(circuit)
|
|
circuit.onstation = onstation //sync up the circuit so the pricing schema is carried over if it's reconstructed.
|
|
else if(circuit && (circuit.onstation != onstation)) //check if they're not the same to minimize the amount of edited values.
|
|
onstation = circuit.onstation //if it was constructed outside mapload, sync the vendor up with the circuit's var so you can't bypass price requirements by moving / reconstructing it off station.
|
|
|
|
/obj/machinery/vending/Destroy()
|
|
QDEL_NULL(wires)
|
|
QDEL_NULL(coin)
|
|
QDEL_NULL(bill)
|
|
return ..()
|
|
|
|
/obj/machinery/vending/can_speak()
|
|
return !shut_up
|
|
|
|
/obj/machinery/vending/RefreshParts() //Better would be to make constructable child
|
|
if(!component_parts)
|
|
return
|
|
|
|
product_records = list()
|
|
hidden_records = list()
|
|
coin_records = list()
|
|
build_inventory(products, product_records, start_empty = TRUE)
|
|
build_inventory(contraband, hidden_records, start_empty = TRUE)
|
|
build_inventory(premium, coin_records, start_empty = TRUE)
|
|
for(var/obj/item/vending_refill/VR in component_parts)
|
|
restock(VR)
|
|
|
|
/obj/machinery/vending/deconstruct(disassembled = TRUE)
|
|
if(!refill_canister) //the non constructable vendors drop metal instead of a machine frame.
|
|
if(!(flags_1 & NODECONSTRUCT_1))
|
|
new /obj/item/stack/sheet/metal(loc, 3)
|
|
qdel(src)
|
|
else
|
|
..()
|
|
|
|
/obj/machinery/vending/obj_break(damage_flag)
|
|
if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1))
|
|
stat |= BROKEN
|
|
icon_state = "[initial(icon_state)]-broken"
|
|
|
|
var/dump_amount = 0
|
|
var/found_anything = TRUE
|
|
while (found_anything)
|
|
found_anything = FALSE
|
|
for(var/record in shuffle(product_records))
|
|
var/datum/data/vending_product/R = record
|
|
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)
|
|
continue
|
|
R.amount--
|
|
// busting open a vendor will destroy some of the contents
|
|
if(found_anything && prob(80))
|
|
continue
|
|
|
|
var/obj/O = new dump_path(loc)
|
|
step(O, pick(GLOB.alldirs))
|
|
found_anything = TRUE
|
|
dump_amount++
|
|
if (dump_amount >= 16)
|
|
return
|
|
|
|
GLOBAL_LIST_EMPTY(vending_products)
|
|
|
|
/obj/machinery/vending/proc/build_inventory(list/productlist, list/recordlist, start_empty = FALSE)
|
|
for(var/typepath in productlist)
|
|
var/amount = productlist[typepath]
|
|
if(isnull(amount))
|
|
amount = 0
|
|
|
|
var/atom/temp = typepath
|
|
var/datum/data/vending_product/R = new /datum/data/vending_product()
|
|
GLOB.vending_products[typepath] = 1
|
|
R.name = initial(temp.name)
|
|
R.product_path = typepath
|
|
if(!start_empty)
|
|
R.amount = amount
|
|
R.max_amount = amount
|
|
R.custom_price = initial(temp.custom_price)
|
|
R.custom_premium_price = initial(temp.custom_premium_price)
|
|
recordlist += R
|
|
|
|
/obj/machinery/vending/proc/restock(obj/item/vending_refill/canister)
|
|
if (!canister.products)
|
|
canister.products = products.Copy()
|
|
if (!canister.contraband)
|
|
canister.contraband = contraband.Copy()
|
|
if (!canister.premium)
|
|
canister.premium = premium.Copy()
|
|
. = 0
|
|
. += refill_inventory(canister.products, product_records)
|
|
. += refill_inventory(canister.contraband, hidden_records)
|
|
. += refill_inventory(canister.premium, coin_records)
|
|
|
|
/obj/machinery/vending/proc/refill_inventory(list/productlist, list/recordlist)
|
|
. = 0
|
|
for(var/R in recordlist)
|
|
var/datum/data/vending_product/record = R
|
|
var/diff = min(record.max_amount - record.amount, productlist[record.product_path])
|
|
if (diff)
|
|
productlist[record.product_path] -= diff
|
|
record.amount += diff
|
|
. += diff
|
|
|
|
/obj/machinery/vending/proc/update_canister()
|
|
if (!component_parts)
|
|
return
|
|
|
|
var/obj/item/vending_refill/R = locate() in component_parts
|
|
if (!R)
|
|
CRASH("Constructible vending machine did not have a refill canister")
|
|
return
|
|
|
|
R.products = unbuild_inventory(product_records)
|
|
R.contraband = unbuild_inventory(hidden_records)
|
|
R.premium = unbuild_inventory(coin_records)
|
|
|
|
/obj/machinery/vending/proc/unbuild_inventory(list/recordlist)
|
|
. = list()
|
|
for(var/R in recordlist)
|
|
var/datum/data/vending_product/record = R
|
|
.[record.product_path] += record.amount
|
|
|
|
/obj/machinery/vending/crowbar_act(mob/living/user, obj/item/I)
|
|
if(!component_parts)
|
|
return FALSE
|
|
default_deconstruction_crowbar(I)
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/wrench_act(mob/living/user, obj/item/I)
|
|
if(panel_open)
|
|
default_unfasten_wrench(user, I, time = 60)
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/screwdriver_act(mob/living/user, obj/item/I)
|
|
if(..())
|
|
return TRUE
|
|
if(anchored)
|
|
default_deconstruction_screwdriver(user, icon_state, icon_state, I)
|
|
cut_overlays()
|
|
if(panel_open)
|
|
add_overlay("[initial(icon_state)]-panel")
|
|
updateUsrDialog()
|
|
else
|
|
to_chat(user, "<span class='warning'>You must first secure [src].</span>")
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/attackby(obj/item/I, mob/user, params)
|
|
if(panel_open && is_wire_tool(I))
|
|
wires.interact(user)
|
|
return
|
|
if(refill_canister && istype(I, refill_canister))
|
|
if (!panel_open)
|
|
to_chat(user, "<span class='notice'>You should probably unscrew the service panel first.</span>")
|
|
else if (stat & (BROKEN|NOPOWER))
|
|
to_chat(user, "<span class='notice'>[src] does not respond.</span>")
|
|
else
|
|
//if the panel is open we attempt to refill the machine
|
|
var/obj/item/vending_refill/canister = I
|
|
if(canister.get_part_rating() == 0)
|
|
to_chat(user, "<span class='notice'>[canister] is empty!</span>")
|
|
else
|
|
// instantiate canister if needed
|
|
var/transferred = restock(canister)
|
|
if(transferred)
|
|
to_chat(user, "<span class='notice'>You loaded [transferred] items in [src].</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>There's nothing to restock!</span>")
|
|
return
|
|
if(compartmentLoadAccessCheck(user))
|
|
if(canLoadItem(I))
|
|
loadingAttempt(I,user)
|
|
updateUsrDialog() //can't put this on the proc above because we spam it below
|
|
|
|
if(istype(I, /obj/item/storage/bag)) //trays USUALLY
|
|
var/obj/item/storage/T = I
|
|
var/loaded = 0
|
|
var/denied_items = 0
|
|
for(var/obj/item/the_item in T.contents)
|
|
if(contents.len >= MAX_VENDING_INPUT_AMOUNT) // no more than 30 item can fit inside, legacy from snack vending although not sure why it exists
|
|
to_chat(user, "<span class='warning'>[src]'s chef compartment is full.</span>")
|
|
break
|
|
if(loadingAttempt(the_item,user))
|
|
SEND_SIGNAL(T, COMSIG_TRY_STORAGE_TAKE, the_item, src, TRUE)
|
|
loaded++
|
|
else
|
|
denied_items++
|
|
if(denied_items)
|
|
to_chat(user, "<span class='notice'>[src] refuses some items.</span>")
|
|
if(loaded)
|
|
to_chat(user, "<span class='notice'>You insert [loaded] dishes into [src]'s chef compartment.</span>")
|
|
updateUsrDialog()
|
|
else
|
|
..()
|
|
|
|
/obj/machinery/vending/proc/loadingAttempt(obj/item/I,mob/user)
|
|
. = TRUE
|
|
if(!user.transferItemToLoc(I, src))
|
|
return FALSE
|
|
if(vending_machine_input[I.name])
|
|
vending_machine_input[I.name]++
|
|
else
|
|
vending_machine_input[I.name] = 1
|
|
to_chat(user, "<span class='notice'>You insert [I] into [src]'s input compartment.</span>")
|
|
|
|
|
|
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/W)
|
|
if(!istype(W))
|
|
return FALSE
|
|
if((flags_1 & NODECONSTRUCT_1) && !W.works_from_distance)
|
|
return FALSE
|
|
if(!component_parts || !refill_canister)
|
|
return FALSE
|
|
|
|
var/moved = 0
|
|
if(panel_open || W.works_from_distance)
|
|
if(W.works_from_distance)
|
|
display_parts(user)
|
|
for(var/I in W)
|
|
if(istype(I, refill_canister))
|
|
moved += restock(I)
|
|
else
|
|
display_parts(user)
|
|
if(moved)
|
|
to_chat(user, "[moved] items restocked.")
|
|
W.play_rped_sound()
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/on_deconstruction()
|
|
update_canister()
|
|
. = ..()
|
|
|
|
/obj/machinery/vending/emag_act(mob/user)
|
|
if(obj_flags & EMAGGED)
|
|
return
|
|
obj_flags |= EMAGGED
|
|
to_chat(user, "<span class='notice'>You short out the product lock on [src].</span>")
|
|
|
|
/obj/machinery/vending/_try_interact(mob/user)
|
|
if(seconds_electrified && !(stat & NOPOWER))
|
|
if(shock(user, 100))
|
|
return
|
|
return ..()
|
|
|
|
/obj/machinery/vending/ui_interact(mob/user)
|
|
var/list/dat = list()
|
|
var/datum/bank_account/account
|
|
var/mob/living/carbon/human/H
|
|
var/obj/item/card/id/C
|
|
if(ishuman(user))
|
|
H = user
|
|
C = H.get_idcard(TRUE)
|
|
|
|
if(!C)
|
|
dat += "<font color = 'red'><h3>No ID Card detected!</h3></font>"
|
|
else if (!C.registered_account)
|
|
dat += "<font color = 'red'><h3>No account on registered ID card!</h3></font>"
|
|
if(onstation && C && C.registered_account)
|
|
account = C.registered_account
|
|
dat += {"<h3>Select an item</h3>
|
|
<div class='statusDisplay'>"}
|
|
if(!product_records.len)
|
|
dat += "<font color = 'red'>No product loaded!</font>"
|
|
else
|
|
var/list/display_records = product_records + coin_records
|
|
if(extended_inventory)
|
|
display_records = product_records + coin_records + hidden_records
|
|
dat += "<table>"
|
|
for (var/datum/data/vending_product/R in display_records)
|
|
var/price_listed = "$[default_price]"
|
|
var/is_hidden = hidden_records.Find(R)
|
|
if(is_hidden && !extended_inventory)
|
|
continue
|
|
if(R.custom_price)
|
|
price_listed = "$[R.custom_price]"
|
|
if(!onstation || account && account.account_job && account.account_job.paycheck_department == payment_department)
|
|
price_listed = "FREE"
|
|
if(coin_records.Find(R) || is_hidden)
|
|
price_listed = "$[R.custom_premium_price ? R.custom_premium_price : extra_price]"
|
|
dat += {"<tr><td><span class="vending32x32 [replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-")]"></td>
|
|
<td style=\"width: 100%\"><b>[sanitize(R.name)] ([price_listed])</b></td>"}
|
|
if(R.amount > 0 && ((C && C.registered_account && onstation) || (!onstation && isliving(user))))
|
|
dat += "<td align='right'><b>[R.amount] </b><a href='byond://?src=[REF(src)];vend=[REF(R)]'>Vend</a></td>"
|
|
else
|
|
dat += "<td align='right'><span class='linkOff'>Not Available</span></td>"
|
|
dat += "</tr>"
|
|
dat += "</table>"
|
|
dat += "</div>"
|
|
if(onstation && C && C.registered_account)
|
|
dat += "<b>Balance: $[account.account_balance]</b>"
|
|
if(vending_machine_input.len)
|
|
dat += "<h3>[input_display_header]</h3>"
|
|
dat += "<div class='statusDisplay'>"
|
|
for (var/O in vending_machine_input)
|
|
if(vending_machine_input[O] > 0)
|
|
var/N = vending_machine_input[O]
|
|
dat += "<a href='byond://?src=[REF(src)];dispense=[sanitize(O)]'>Dispense</A> "
|
|
dat += "<B>[capitalize(O)] ($[default_price]): [N]</B><br>"
|
|
dat += "</div>"
|
|
|
|
var/datum/browser/popup = new(user, "vending", (name))
|
|
popup.add_stylesheet(get_asset_datum(/datum/asset/spritesheet/vending))
|
|
popup.set_content(dat.Join(""))
|
|
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
|
popup.open()
|
|
|
|
/obj/machinery/vending/Topic(href, href_list)
|
|
if(..())
|
|
return
|
|
|
|
usr.set_machine(src)
|
|
|
|
if((href_list["dispense"]) && (vend_ready))
|
|
var/N = href_list["dispense"]
|
|
if(vending_machine_input[N] <= 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible.
|
|
return
|
|
vend_ready = 0
|
|
if(ishuman(usr) && onstation)
|
|
var/mob/living/carbon/human/H = usr
|
|
var/obj/item/card/id/C = H.get_idcard(TRUE)
|
|
|
|
if(!C)
|
|
say("No card found.")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
else if (!C.registered_account)
|
|
say("No account found.")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
var/datum/bank_account/account = C.registered_account
|
|
if(!account.adjust_money(-chef_price))
|
|
say("You do not possess the funds to purchase this meal.")
|
|
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_SRV)
|
|
if(D)
|
|
D.adjust_money(chef_price)
|
|
use_power(5)
|
|
|
|
vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
|
|
for(var/obj/O in contents)
|
|
if(O.name == N)
|
|
say("Thank you for supporting your local kitchen and purchasing [O]!")
|
|
O.forceMove(drop_location())
|
|
break
|
|
vend_ready = 1
|
|
updateUsrDialog()
|
|
return
|
|
|
|
if((href_list["vend"]) && (vend_ready))
|
|
if(panel_open)
|
|
to_chat(usr, "<span class='notice'>The vending machine cannot dispense products while its service panel is open!</span>")
|
|
return
|
|
|
|
vend_ready = 0 //One thing at a time!!
|
|
|
|
var/datum/data/vending_product/R = locate(href_list["vend"])
|
|
var/list/record_to_check = product_records + coin_records
|
|
if(extended_inventory)
|
|
record_to_check = product_records + coin_records + hidden_records
|
|
if(!R || !istype(R) || !R.product_path)
|
|
vend_ready = 1
|
|
return
|
|
var/price_to_use = default_price
|
|
if(R.custom_price)
|
|
price_to_use = R.custom_price
|
|
if(R in hidden_records)
|
|
if(!extended_inventory)
|
|
vend_ready = 1
|
|
return
|
|
|
|
else if (!(R in record_to_check))
|
|
vend_ready = 1
|
|
message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!")
|
|
return
|
|
if (R.amount <= 0)
|
|
say("Sold out of [R.name].")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
if(onstation && ishuman(usr))
|
|
var/mob/living/carbon/human/H = usr
|
|
var/obj/item/card/id/C = H.get_idcard(TRUE)
|
|
|
|
if(!C)
|
|
say("No card found.")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
else if (!C.registered_account)
|
|
say("No account found.")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
var/datum/bank_account/account = C.registered_account
|
|
if(account.account_job && account.account_job.paycheck_department == payment_department)
|
|
price_to_use = 0
|
|
if(coin_records.Find(R) || hidden_records.Find(R))
|
|
price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
|
|
if(price_to_use && !account.adjust_money(-price_to_use))
|
|
say("You do not possess the funds to purchase [R.name].")
|
|
flick(icon_deny,src)
|
|
vend_ready = 1
|
|
return
|
|
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
|
|
if(D)
|
|
D.adjust_money(price_to_use)
|
|
if(last_shopper != usr || purchase_message_cooldown < world.time)
|
|
say("Thank you for shopping with [src]!")
|
|
purchase_message_cooldown = world.time + 5 SECONDS
|
|
last_shopper = usr
|
|
use_power(5)
|
|
if(icon_vend) //Show the vending animation if needed
|
|
flick(icon_vend,src)
|
|
new R.product_path(get_turf(src))
|
|
R.amount--
|
|
SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
|
|
vend_ready = 1
|
|
|
|
else if(href_list["togglevoice"] && panel_open)
|
|
shut_up = !shut_up
|
|
|
|
updateUsrDialog()
|
|
|
|
|
|
/obj/machinery/vending/process()
|
|
if(stat & (BROKEN|NOPOWER))
|
|
return PROCESS_KILL
|
|
if(!active)
|
|
return
|
|
|
|
if(seconds_electrified > MACHINE_NOT_ELECTRIFIED)
|
|
seconds_electrified--
|
|
|
|
//Pitch to the people! Really sell it!
|
|
if(last_slogan + slogan_delay <= world.time && slogan_list.len > 0 && !shut_up && prob(5))
|
|
var/slogan = pick(slogan_list)
|
|
speak(slogan)
|
|
last_slogan = world.time
|
|
|
|
if(shoot_inventory && prob(shoot_inventory_chance))
|
|
throw_item()
|
|
|
|
/obj/machinery/vending/proc/speak(message)
|
|
if(stat & (BROKEN|NOPOWER))
|
|
return
|
|
if(!message)
|
|
return
|
|
|
|
say(message)
|
|
|
|
/obj/machinery/vending/power_change()
|
|
if(stat & BROKEN)
|
|
icon_state = "[initial(icon_state)]-broken"
|
|
else
|
|
if(powered())
|
|
icon_state = initial(icon_state)
|
|
stat &= ~NOPOWER
|
|
START_PROCESSING(SSmachines, src)
|
|
else
|
|
icon_state = "[initial(icon_state)]-off"
|
|
stat |= NOPOWER
|
|
|
|
//Somebody cut an important wire and now we're following a new definition of "pitch."
|
|
/obj/machinery/vending/proc/throw_item()
|
|
var/obj/throw_item = null
|
|
var/mob/living/target = locate() in view(7,src)
|
|
if(!target)
|
|
return 0
|
|
|
|
for(var/datum/data/vending_product/R in shuffle(product_records))
|
|
if(R.amount <= 0) //Try to use a record that actually has something to dump.
|
|
continue
|
|
var/dump_path = R.product_path
|
|
if(!dump_path)
|
|
continue
|
|
|
|
R.amount--
|
|
throw_item = new dump_path(loc)
|
|
break
|
|
if(!throw_item)
|
|
return 0
|
|
|
|
pre_throw(throw_item)
|
|
|
|
throw_item.throw_at(target, 16, 3)
|
|
visible_message("<span class='danger'>[src] launches [throw_item] at [target]!</span>")
|
|
return 1
|
|
|
|
/obj/machinery/vending/proc/pre_throw(obj/item/I)
|
|
return
|
|
|
|
/obj/machinery/vending/proc/shock(mob/user, prb)
|
|
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
|
return FALSE
|
|
if(!prob(prb))
|
|
return FALSE
|
|
do_sparks(5, TRUE, src)
|
|
var/check_range = TRUE
|
|
if(electrocute_mob(user, get_area(src), src, 0.7, check_range))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/obj/machinery/vending/proc/canLoadItem(obj/item/I,mob/user)
|
|
return FALSE
|
|
|
|
/obj/machinery/vending/proc/compartmentLoadAccessCheck(mob/user)
|
|
if(!canload_access_list)
|
|
return TRUE
|
|
else
|
|
var/do_you_have_access = FALSE
|
|
var/req_access_txt_holder = req_access_txt
|
|
for(var/i in canload_access_list)
|
|
req_access_txt = i
|
|
if(!allowed(user) && !(obj_flags & EMAGGED) && scan_id)
|
|
continue
|
|
else
|
|
do_you_have_access = TRUE
|
|
break //you passed don't bother looping anymore
|
|
req_access_txt = req_access_txt_holder // revert to normal (before the proc ran)
|
|
if(do_you_have_access)
|
|
return TRUE
|
|
else
|
|
to_chat(user, "<span class='warning'>[src]'s input compartment blinks red: Access denied.</span>")
|
|
return FALSE
|
|
|
|
/obj/machinery/vending/onTransitZ()
|
|
return
|