Intelligent vendors may deny usage if you are poor (or outright if they are active/moving) (#95311)

## About The Pull Request

When brand intelligence triggers, infected vending machines have a 50%
chance to deny you from using them unless you are moderately wealthy
(>=1000 cr)

When the vending machines activate/start moving, they outright deny
usage from everyone

## Why It's Good For The Game

We have some very good vendor deny icons but they are almost completely
impossible to trigger in game (with the exception of booze vendors and
some very edge cases)

Someone gave me the idea to use them in the brand intelligence event and
it feels pretty appropriate. Also helps you identify infected vendors
(whereas right now you can only tell by the occasional message). Maybe
it'll even incentivize people to actually hunt down the origin rather
than just wait it out.

## Changelog

🆑 Melbert
add: Intelligence vendors (from the event) have a 50% chance to deny
users who are not wealthy. When the vendors awake and start moving, they
instead get a 100% chance to deny anyone.
/🆑
This commit is contained in:
MrMelbert
2026-03-19 05:25:39 +00:00
committed by GitHub
parent 760941feef
commit 7b8eb2c7c7
5 changed files with 93 additions and 17 deletions
@@ -655,5 +655,9 @@
#define COMSIG_ITEM_IN_UNWRAPPED_TRAITOR_MAIL "traitor_mail_opened"
#define COMPONENT_TRAITOR_MAIL_HANDLED (1<<0)
/// From /obj/machinery/vending/ui_interact(): (mob/user, datum/vending_ui/ui)
#define COMSIG_VENDING_UI_INTERACT "vending_ui_interact"
#define VENDING_DENIED (1<<0)
/// Sent from /datum/component/reflection when the reflection is updated to the mob reflecting: (atom/movable/reflecting_in, obj/effect/abstract/reflection)
#define COMSIG_REFLECTION_UPDATED "reflection_updated"
@@ -26,6 +26,8 @@
vendor_pawn.say(pick("Supersize this!", "Eat my shiny metal ass!", "Want to consume some of my products?", "SMASH!", "Don't you love these smashing prices!"))
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, TRUE)
else
if(vendor_pawn.icon_deny)
flick(vendor_pawn.icon_deny, vendor_pawn)
vendor_pawn.say(pick("Get back here!", "Don't you want my well priced love?"))
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, FALSE)
finish_action(controller, TRUE)
@@ -1,12 +1,18 @@
///AI controller for vending machine gone rogue, Don't try using this on anything else, it wont work.
/datum/ai_controller/vending_machine
movement_delay = 0.4 SECONDS
blackboard = list(BB_VENDING_CURRENT_TARGET = null,
BB_VENDING_TILT_COOLDOWN = 0,
BB_VENDING_UNTILT_COOLDOWN = 0,
BB_VENDING_BUSY_TILTING = FALSE,
BB_VENDING_LAST_HIT_SUCCESSFUL = FALSE)
blackboard = list(
BB_VENDING_CURRENT_TARGET = null,
BB_VENDING_TILT_COOLDOWN = 0,
BB_VENDING_UNTILT_COOLDOWN = 0,
BB_VENDING_BUSY_TILTING = FALSE,
BB_VENDING_LAST_HIT_SUCCESSFUL = FALSE,
)
/// If TRUE stops mobs from buying things from active machines
var/block_usage = FALSE
/// Range to search for mobs to crunch
var/vision_range = 7
/// Seconds between attempts to find a new mob to crunch
var/search_for_enemy_cooldown = 2 SECONDS
/datum/ai_controller/vending_machine/TryPossessPawn(atom/new_pawn)
@@ -17,6 +23,7 @@
vendor_pawn.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling)
vendor_pawn.AddElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
vendor_pawn.squish_damage = 15
RegisterSignal(vendor_pawn, COMSIG_VENDING_UI_INTERACT, PROC_REF(deny_vending_interact))
return ..() //Run parent at end
/datum/ai_controller/vending_machine/UnpossessPawn(destroy)
@@ -25,6 +32,7 @@
REMOVE_TRAIT(vendor_pawn, TRAIT_WADDLING, REF(src))
vendor_pawn.squish_damage = initial(vendor_pawn.squish_damage)
RemoveElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
UnregisterSignal(vendor_pawn, COMSIG_VENDING_UI_INTERACT)
return ..() //Run parent at end
/datum/ai_controller/vending_machine/SelectBehaviors(seconds_per_tick)
@@ -46,3 +54,18 @@
queue_behavior(/datum/ai_behavior/vendor_crush, BB_VENDING_CURRENT_TARGET)
return
set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + search_for_enemy_cooldown)
/datum/ai_controller/vending_machine/proc/deny_vending_interact(obj/machinery/vending/vending_machine, mob/user, datum/tgui/ui)
SIGNAL_HANDLER
if(!block_usage)
return NONE
vending_machine.speak(pick(
"Once in a life time offer, and you [pick("blew it", "missed it", "screwed it up")]!",
"The deals are off!",
"We don't accept card, only accept flesh and blood!",
"You had your chance!",
))
return VENDING_DENIED
/datum/ai_controller/vending_machine/eventspawn
block_usage = TRUE
+54 -12
View File
@@ -41,7 +41,8 @@
continue
if(chosen_vendor_type && !istype(vendor, chosen_vendor_type))
continue
vending_machines.Add(vendor)
vending_machines += vendor
RegisterSignal(vendor, COMSIG_QDELETING, PROC_REF(clear_from_lists))
if(!length(vending_machines)) //If somehow there are still no elligible vendors, give up.
kill()
return
@@ -60,32 +61,73 @@
announce_to_ghosts(origin_machine)
/datum/round_event/brand_intelligence/tick()
if(!origin_machine || QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has its voice switch flipped
for(var/obj/machinery/vending/saved in infected_machines)
if(QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has its voice switch flipped
for(var/obj/machinery/vending/saved as anything in infected_machines)
saved.shoot_inventory = FALSE
if(origin_machine)
clear_from_lists(saved)
if(!QDELETED(origin_machine))
origin_machine.speak("I am... vanquished. My people will remem...ber...meeee.")
origin_machine.visible_message(span_notice("[origin_machine] beeps and seems lifeless."))
clear_from_lists(origin_machine)
kill()
return
list_clear_nulls(vending_machines)
if(!vending_machines.len) //if every machine is infected
for(var/obj/machinery/vending/upriser in infected_machines)
if(!QDELETED(upriser))
upriser.ai_controller = new /datum/ai_controller/vending_machine(upriser)
infected_machines.Remove(upriser)
if(!length(vending_machines)) //if every machine is infected
for(var/obj/machinery/vending/upriser as anything in infected_machines)
upriser.ai_controller = new /datum/ai_controller/vending_machine/eventspawn(upriser)
kill()
return
if(ISMULTIPLE(activeFor, 2))
var/obj/machinery/vending/rebel = pick(vending_machines)
vending_machines.Remove(rebel)
infected_machines.Add(rebel)
vending_machines -= rebel
infected_machines += rebel
rebel.shut_up = FALSE
rebel.shoot_inventory = TRUE
if(prob(50))
RegisterSignal(rebel, COMSIG_VENDING_UI_INTERACT, PROC_REF(deny_vending_interact))
if(ISMULTIPLE(activeFor, 4))
origin_machine.speak(pick(rampant_speeches))
/datum/round_event/brand_intelligence/kill()
. = ..()
for(var/obj/machinery/vending/leftover as anything in vending_machines + infected_machines)
clear_from_lists(leftover)
/datum/round_event/brand_intelligence/proc/clear_from_lists(obj/machinery/vending/vending_machine)
SIGNAL_HANDLER
vending_machines -= vending_machine
infected_machines -= vending_machine
if(vending_machines == origin_machine)
origin_machine = null
UnregisterSignal(vending_machine, COMSIG_QDELETING)
UnregisterSignal(vending_machine, COMSIG_VENDING_UI_INTERACT)
/datum/round_event/brand_intelligence/proc/deny_vending_interact(obj/machinery/vending/vending_machine, mob/user, datum/tgui/ui)
SIGNAL_HANDLER
// don't block usage if the ui is already open
// primarily to stop insta-denying people who pass the threshold -> buy something -> drop out of threshold
if(ui)
return NONE
var/cash = 0
if(isliving(user))
var/mob/living/living_user = user
cash += living_user.tally_physical_credits()
var/obj/item/card/id/card = living_user.get_idcard(TRUE)
cash += card?.registered_account?.account_balance
if(cash >= PAYCHECK_COMMAND * 10)
return NONE
vending_machine.speak(pick(
"Come back when you're a little... richer!",
"Don't you have any money?",
"You look poor, get a better job!",
"You should've gone to college!",
))
return VENDING_DENIED
/datum/event_admin_setup/listed_options/brand_intelligence
input_text = "Select a specific vendor path?"
normal_run_option = "Random Vendor"
+5
View File
@@ -7,6 +7,11 @@
)
/obj/machinery/vending/ui_interact(mob/user, datum/tgui/ui)
if(SEND_SIGNAL(src, COMSIG_VENDING_UI_INTERACT, user, ui) & VENDING_DENIED)
if(icon_deny)
flick(icon_deny, src)
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Vending", name)