Fix vendor stock bug, make vendor dispensing behaviours apply more (#90266)

## About The Pull Request

So #90246 was caused purely because vendors don't actually reduce the
stock when dispensing returned items:

https://github.com/tgstation/tgstation/blob/c0204da3da7388d9a0b28523a66b83bcc7a29f23/code/modules/vending/_vending.dm#L1464-L1467
While this could've been a single line fix by just decrementing the
stock like how returned items are handled elsewhere, instead we collapse
all these cases into being handled by `dispense(...)` so it's harder for
this to happen again. We make this a parameter because different
dispensing methods have different priorities.
As a side, we make it so `on_dispense(...)` forwards whether or not it's
a returned item, because logically mothroach infestations would still
snack on your clothes even if they're just returned. I felt it best to
make them deal less damage to returned clothes in that case.
## Why It's Good For The Game

Fixes #90246.
Fixes #89104.
Good if the special vendor stuff gets applied when items are thrown too
Being able to cycle items through the mothroach clothing gnawing machine
is funny
## Changelog
🆑
fix: Dispensing a returned item from a vendor actually reduces the
amount of stock for that item.
fix: Special vendor dispensing behaviours get applied to thrown items.
fix: Special vendor dispensing behaviours actually get applied to
returned items where applicable.
/🆑
This commit is contained in:
_0Steven
2025-03-28 18:29:49 +01:00
committed by GitHub
parent 7f54573127
commit 30ff13a0f8
4 changed files with 44 additions and 41 deletions
+32 -37
View File
@@ -379,11 +379,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
found_anything = FALSE
for(var/datum/data/vending_product/record as anything in shuffle(product_records))
//first dump any of the items that have been returned, in case they contain the nuke disk or something
for(var/obj/returned_obj_to_dump in record.returned_products)
LAZYREMOVE(record.returned_products, returned_obj_to_dump)
returned_obj_to_dump.forceMove(get_turf(src))
for(var/i in 1 to LAZYLEN(record.returned_products))
var/obj/item/returned_obj_to_dump = dispense(record, get_turf(src), dispense_returned = TRUE)
step(returned_obj_to_dump, pick(GLOB.alldirs))
record.amount--
if(record.amount <= 0) //Try to use a record that actually has something to dump.
continue
@@ -788,13 +786,9 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
if(record.amount <= 0) //Try to use a record that actually has something to dump.
continue
if(record.amount > LAZYLEN(record.returned_products)) //always give out new stuff that costs before free returned stuff, because of the risk getting gibbed involved
dispense(record, get_turf(src), silent = TRUE)
else
var/obj/returned_obj_to_dump = LAZYACCESS(record.returned_products, LAZYLEN(record.returned_products)) //first in, last out
LAZYREMOVE(record.returned_products, returned_obj_to_dump)
returned_obj_to_dump.forceMove(get_turf(src))
record.amount--
// 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
deploy_credits()
@@ -1457,14 +1451,11 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
use_energy(active_power_usage)
if(icon_vend) //Show the vending animation if needed
flick(icon_vend,src)
var/obj/item/vended_item
if(!LAZYLEN(item_record.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items
vended_item = dispense(item_record, get_turf(src))
else
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
vended_item = LAZYACCESS(item_record.returned_products, LAZYLEN(item_record.returned_products)) //first in, last out
LAZYREMOVE(item_record.returned_products, vended_item)
vended_item.forceMove(get_turf(src))
// Always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items
var/returned_available = LAZYLEN(item_record.returned_products)
var/obj/item/vended_item = dispense(item_record, get_turf(src), dispense_returned = returned_available)
if(greyscale_colors)
vended_item.set_greyscale(colors=greyscale_colors)
if(usr.CanReach(src) && usr.put_in_hands(vended_item))
@@ -1475,19 +1466,27 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
vend_ready = TRUE
///Common proc that dispenses an item. Called when the item is vended, or gotten some other way.
/obj/machinery/vending/proc/dispense(datum/data/vending_product/item_record, atom/spawn_location, silent = FALSE)
/obj/machinery/vending/proc/dispense(datum/data/vending_product/item_record, atom/spawn_location, silent = FALSE, dispense_returned = FALSE)
SHOULD_CALL_PARENT(TRUE)
if(!silent)
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
var/obj/item/vended_item = new item_record.product_path (spawn_location)
if(vended_item.type in contraband)
ADD_TRAIT(vended_item, TRAIT_CONTRABAND, INNATE_TRAIT)
on_dispense(vended_item)
var/obj/item/vended_item
if(dispense_returned)
vended_item = LAZYACCESS(item_record.returned_products, LAZYLEN(item_record.returned_products)) //first in, last out
LAZYREMOVE(item_record.returned_products, vended_item)
vended_item.forceMove(spawn_location)
else
vended_item = new item_record.product_path(spawn_location)
if(vended_item.type in contraband)
ADD_TRAIT(vended_item, TRAIT_CONTRABAND, INNATE_TRAIT)
on_dispense(vended_item, dispense_returned)
item_record.amount--
return vended_item
///A proc meant to perform custom behavior on newly dispensed items.
/obj/machinery/vending/proc/on_dispense(obj/item/vended_item)
/obj/machinery/vending/proc/on_dispense(obj/item/vended_item, dispense_returned = FALSE)
return
/**
@@ -1578,32 +1577,28 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
* This item is then created and tossed out in front of us with a visible message
*/
/obj/machinery/vending/proc/throw_item()
var/obj/throw_item = null
var/mob/living/target = locate() in view(7,src)
if(!target)
return FALSE
var/obj/thrown_item
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
var/dump_path = record.product_path
if(!dump_path)
continue
if(record.amount > LAZYLEN(record.returned_products)) //always throw new stuff that costs before free returned stuff, because of the hacking effort and time between throws involved
throw_item = new dump_path(loc)
else
throw_item = LAZYACCESS(record.returned_products, LAZYLEN(record.returned_products)) //first in, last out
throw_item.forceMove(loc)
LAZYREMOVE(record.returned_products, throw_item)
record.amount--
// Always throw new stuff that costs before free returned stuff, because of the hacking effort and time between throws involved
var/only_returned_left = (record.amount <= LAZYLEN(record.returned_products))
thrown_item = dispense(record, get_turf(src), silent = TRUE, dispense_returned = only_returned_left)
break
if(!throw_item)
if(isnull(thrown_item))
return FALSE
pre_throw(throw_item)
pre_throw(thrown_item)
throw_item.throw_at(target, 16, 3)
visible_message(span_danger("[src] launches [throw_item] at [target]!"))
thrown_item.throw_at(target, 16, 3)
visible_message(span_danger("[src] launches [thrown_item] at [target]!"))
return TRUE
/**
+4 -1
View File
@@ -54,7 +54,10 @@
/datum/reagent/consumable/ethanol/neurotoxin = 1,
)
/obj/machinery/vending/cola/on_dispense(obj/item/vended_item)
/obj/machinery/vending/cola/on_dispense(obj/item/vended_item, dispense_returned = FALSE)
// Only apply to newly dispensed items
if(dispense_returned)
return
// 35% chance that your drink will be safe, as safe pure acid and sugar that these drinks probably are can be
if(!onstation || !HAS_TRAIT(SSstation, STATION_TRAIT_SPIKED_DRINKS) || !prob(65))
return
+4 -1
View File
@@ -51,6 +51,9 @@
icon_state = "refill_snack"
/// Cute little thing that sets it apart from the other food vending mahicnes. I mean, you don't find this every day.
/obj/machinery/vending/hotdog/on_dispense(obj/item/vended_item)
/obj/machinery/vending/hotdog/on_dispense(obj/item/vended_item, dispense_returned = FALSE)
// Only apply to newly dispensed items
if(dispense_returned)
return
if(istype(vended_item, /obj/item/food))
ADD_TRAIT(vended_item, TRAIT_FOOD_CHEF_MADE, VENDING_MACHINE_TRAIT)
+4 -2
View File
@@ -23,11 +23,13 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE)
GLOB.roaches_deployed = TRUE
/obj/machinery/vending/wardrobe/on_dispense(obj/item/clothing/food)
/obj/machinery/vending/wardrobe/on_dispense(obj/item/clothing/food, dispense_returned = FALSE)
if(!istype(food))
return
for(var/mob/living/basic/mothroach/roach in contents)
food.take_damage(food.get_integrity() * 0.5)
// Be slightly nicer on returned items
var/damage_mult = dispense_returned ? 0.1 : 0.5
food.take_damage(food.get_integrity() * damage_mult)
/obj/machinery/vending/wardrobe/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()