Files
ArcaneMusic 652fd2633e Restocking a Vending machine no longer creates 0 credit holochips. (#96137)
## About The Pull Request

The post_restock proc did not have an appropriate check to confirm that
there was enough stored credits to produce a non-zero holochip. This
adds a quick check for the proc, preventing their creation.

## Why It's Good For The Game

One less silly runtime on live.

## Changelog

🆑
fix: Restocking a vending machine can no longer produce a zero credit
holochip.
/🆑
2026-05-19 08:50:14 +02:00

244 lines
8.6 KiB
Plaintext

//===============================HAND INTERACTION===================================
/obj/machinery/vending/interact(mob/user)
if(seconds_electrified && !(machine_stat & NOPOWER))
if(shock(user, 100))
return
if(tilted && !user.buckled)
to_chat(user, span_notice("You begin righting [src]."))
if(do_after(user, 5 SECONDS, target = src))
untilt(user)
return
return ..()
//================================TOOL ACTS==============================================
/obj/machinery/vending/crowbar_act(mob/living/user, obj/item/attack_item)
if(!component_parts)
return ITEM_INTERACT_FAILURE
return default_deconstruction_crowbar(user, attack_item)
/obj/machinery/vending/wrench_act(mob/living/user, obj/item/tool)
. = NONE
if(!panel_open)
return ITEM_INTERACT_FAILURE
if(default_unfasten_wrench(user, tool, time = 6 SECONDS))
unbuckle_all_mobs(TRUE)
return ITEM_INTERACT_SUCCESS
/obj/machinery/vending/screwdriver_act(mob/living/user, obj/item/attack_item)
if(anchored)
return default_deconstruction_screwdriver(user, attack_item)
to_chat(user, span_warning("You must first secure [src]."))
return ITEM_INTERACT_FAILURE
/obj/machinery/vending/on_set_panel_open(old_value)
update_appearance(UPDATE_OVERLAYS)
//=======================================RESTOCKING==========================================
/**
* Is the passed in user allowed to load this vending machines compartments? This only is ran if we are using a /obj/item/storage/bag to load the vending machine, and not a dedicated restocker.
*
* Arguments:
* * user - mob that is doing the loading of the vending machine
*/
/obj/machinery/vending/proc/compartmentLoadAccessCheck(mob/user)
PROTECTED_PROC(TRUE)
return !req_access || allowed(user) || (obj_flags & EMAGGED) || !scan_id
/**
* Are we able to load the item passed in
*
* Arguments:
* * loaded_item - the item being loaded
* * user - the user doing the loading
*/
/obj/machinery/vending/proc/canLoadItem(obj/item/loaded_item, mob/user, send_message = TRUE)
PROTECTED_PROC(TRUE)
if(!length(loaded_item.contents) && (products[loaded_item.type] || premium[loaded_item.type] || contraband[loaded_item.type]))
return TRUE
if(send_message)
to_chat(user, span_warning("[src] does not accept [loaded_item]!"))
return FALSE
/**
* Tries to insert the item into the vendor, and depending on whether the product is a part of the vendor's
* stock or not, increments an already present product entry's available amount or creates a new entry.
* arguments:
* inserted_item - the item we're trying to insert
* user - mob who's trying to insert the item
*/
/obj/machinery/vending/proc/loadingAttempt(obj/item/inserted_item, mob/user)
PROTECTED_PROC(TRUE)
. = TRUE
if(!canLoadItem(inserted_item, user))
return FALSE
for(var/datum/data/vending_product/product_datum in product_records + coin_records + hidden_records)
if(inserted_item.type != product_datum.product_path)
continue
if(product_datum.amount == product_datum.max_amount)
to_chat(user, span_warning("[src] can't accept any more [inserted_item.name][inserted_item.p_s()]!"))
return FALSE
if(!user.transferItemToLoc(inserted_item, src))
to_chat(user, span_warning("[inserted_item] is stuck in your hand!"))
return FALSE
product_datum.amount++
LAZYADD(product_datum.returned_products, inserted_item)
to_chat(user, span_notice("You insert [inserted_item] into [src]'s input compartment."))
break
/obj/machinery/vending/item_interaction(mob/living/user, obj/item/attack_item, list/modifiers)
. = ..()
if(. & ITEM_INTERACT_ANY_BLOCKER)
return .
if(panel_open && is_wire_tool(attack_item))
wires.interact(user)
return ITEM_INTERACT_SUCCESS
if(refill_canister && istype(attack_item, refill_canister))
. = ITEM_INTERACT_FAILURE
if (!panel_open)
to_chat(user, span_warning("You should probably unscrew the service panel first!"))
else if (!is_operational)
to_chat(user, span_warning("[src] does not respond."))
else
var/obj/item/vending_refill/canister = attack_item
if(canister.get_part_rating() == 0)
to_chat(user, span_warning("[canister] is empty!"))
else
post_restock(user, restock(canister))
return ITEM_INTERACT_SUCCESS
if(compartmentLoadAccessCheck(user) && !user.combat_mode)
. = ITEM_INTERACT_FAILURE
if (!is_operational)
to_chat(user, span_warning("[src] does not respond."))
else if(istype(attack_item, /obj/item/storage/bag)) //trays USUALLY
var/obj/item/storage/storage_item = attack_item
var/loaded = 0
var/denied_items = 0
for(var/obj/item/the_item in storage_item.contents)
if(loadingAttempt(the_item, user))
loaded++
else
denied_items++
if(denied_items)
to_chat(user, span_warning("[src] refuses some items!"))
if(loaded)
to_chat(user, span_notice("You insert [loaded] dishes into [src]'s compartment."))
return ITEM_INTERACT_SUCCESS
else
return loadingAttempt(attack_item, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_FAILURE
/**
* After-effects of refilling a vending machine from a refill canister
*
* This takes the amount of products restocked and gives the user our contained credits if needed,
* sending the user a fitting message.
*
* Arguments:
* * user - the user restocking us
* * restocked - the amount of items we've been refilled with
*/
/obj/machinery/vending/proc/post_restock(mob/living/user, restocked)
PROTECTED_PROC(TRUE)
if(!restocked)
to_chat(user, span_warning("There's nothing to restock!"))
return
to_chat(user, span_notice("You loaded [restocked] items in [src][credits_contained > 0 ? ", and are rewarded [credits_contained] [MONEY_NAME]." : "."]"))
var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo_account.adjust_money(round(credits_contained * 0.5), "Vending: Restock")
if(credits_contained >= 1)
var/obj/item/holochip/payday = new(src, credits_contained)
try_put_in_hand(payday, user)
credits_contained = 0
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/replacer)
if(!istype(replacer) || !component_parts || !refill_canister)
return FALSE
var/works_from_distance = istype(replacer, /obj/item/storage/part_replacer/bluespace)
if(!panel_open || works_from_distance)
to_chat(user, display_parts(user))
if(!panel_open && !works_from_distance)
return FALSE
var/restocked = 0
for(var/replacer_item in replacer)
if(istype(replacer_item, refill_canister))
restocked += restock(replacer_item)
post_restock(user, restocked)
if(restocked > 0)
replacer.play_rped_effect()
return TRUE
//=======================================ATTACKS================================================
/**
* Dispenses free items from the standard stock.
*
* Arguments:
* freebies - number of free items to vend
*/
/obj/machinery/vending/proc/freebie(freebies)
PRIVATE_PROC(TRUE)
visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"][credits_contained > 0 ? " and some [MONEY_NAME]" : ""]!"))
for(var/i in 1 to freebies)
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
for(var/datum/data/vending_product/record in shuffle(product_records))
if(record.amount <= 0) //Try to use a record that actually has something to dump.
continue
// Always give out new stuff that costs before free returned stuff, because of the risk getting gibbed involved
var/only_returned_left = (record.amount <= LAZYLEN(record.returned_products))
dispense(record, get_turf(src), silent = TRUE, dispense_returned = only_returned_left)
break
if(credits_contained > 0)
var/credits_to_remove = min(CREDITS_DUMP_THRESHOLD, round(credits_contained))
var/obj/item/holochip/holochip = new(loc, credits_to_remove)
playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE)
credits_contained = max(0, credits_contained - credits_to_remove)
SSblackbox.record_feedback("amount", "vending machine looted", holochip.credits)
/obj/machinery/vending/attacked_by(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
. = ..()
if(!tiltable || tilted || . <= 0)
return
if(isclosedturf(get_turf(user))) //If the attacker is inside of a wall, immediately fall in the other direction, with no chance for goodies.
tilt(get_turf(get_step(src, REVERSE_DIR(get_dir(src, user)))))
return
switch(rand(1, 100))
if(1 to 5)
freebie(3)
if(6 to 15)
freebie(2)
if(16 to 25)
freebie(1)
if(26 to 75)
pass()
if(76 to 100)
tilt(user)
/obj/machinery/vending/attack_tk_grab(mob/user)
to_chat(user, span_warning("[src] seems to resist your mental grasp!"))
/obj/machinery/vending/attack_robot_secondary(mob/user, list/modifiers)
. = ..()
if (!Adjacent(user, src))
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN