mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-28 10:01:58 +00:00
Port of yogstation13/Yogstation-TG#2549 which is a port of Citadel-Station-13/Citadel-Station-13#7442 which is a port of OracleStation/OracleStation#50 This port visualizes items in all machines handled by _vending.dm, allowing a person to see what they're buying before buying it. This is very useful for clothing vendors for example, and much better than the previous system that had vending machines choose from three colors randomly. Credits to: * AndrewMontagne (Oracle, original implementation) * FlattestGuitar (Citadel) * nichlas0010 (Yogstation)
580 lines
18 KiB
Plaintext
580 lines
18 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
|
|
*/
|
|
|
|
/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??
|
|
|
|
// 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/dish_quants = list() //used by the snack machine's custom compartment to count dishes.
|
|
|
|
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
|
|
|
|
/obj/machinery/vending/proc/GetIconForProduct(datum/data/vending_product/P)
|
|
if(GLOB.vending_cache[P.product_path])
|
|
return GLOB.vending_cache[P.product_path]
|
|
|
|
var/product = new P.product_path()
|
|
GLOB.vending_cache[P.product_path] = icon2base64(getFlatIcon(product, no_anim = TRUE))
|
|
qdel(product)
|
|
return GLOB.vending_cache[P.product_path]
|
|
|
|
/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()
|
|
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
|
|
else
|
|
return ..()
|
|
|
|
/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/dat = ""
|
|
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>"
|
|
dat += "<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><img src='data:image/jpeg;base64,[GetIconForProduct(R)]'/></td>"
|
|
dat += "<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(istype(src, /obj/machinery/vending/snack))
|
|
dat += "<h3>Chef's Food Selection</h3>"
|
|
dat += "<div class='statusDisplay'>"
|
|
for (var/O in dish_quants)
|
|
if(dish_quants[O] > 0)
|
|
var/N = dish_quants[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.set_content(dat)
|
|
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(dish_quants[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)
|
|
|
|
dish_quants[N] = max(dish_quants[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)
|
|
say("Thank you for shopping with [src]!")
|
|
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/onTransitZ()
|
|
return
|