mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
## About The Pull Request Puts a check on departamental budget cards to not be used with custom vendors ## Why It's Good For The Game Keeps those that manage to get their hands in departamental cards from draining it completly on custom vendors they can easily make (hell, on Meta you got the machine that sell the parts for it 5 tiles away). ## Did You Test Shit? <img width="912" height="695" alt="image" src="https://github.com/user-attachments/assets/56469110-6dcc-41c1-ae50-e6d28c439c82" /> <img width="1147" height="849" alt="image" src="https://github.com/user-attachments/assets/8c852d59-c324-4c59-a56e-9cdba6fd7ee6" /> <img width="778" height="881" alt="image" src="https://github.com/user-attachments/assets/2eae235f-cad5-4b80-9e47-e4600ae8fedf" /> ## Changelog 🆑 fix: Puts a check on departamental budget cards to not be used with custom vendors. /🆑 --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> Co-authored-by: Lucy <lucy@absolucy.moe>
354 lines
11 KiB
Plaintext
354 lines
11 KiB
Plaintext
///This unique key decides how items are stacked on the UI. We separate them based on name, price & type
|
|
#define ITEM_HASH(item)(sanitize_css_class_name("[item.name][item.custom_price][item.type]"))
|
|
|
|
/obj/machinery/vending/custom
|
|
name = "Custom Vendor"
|
|
icon_state = "custom"
|
|
icon_deny = "custom-deny"
|
|
max_integrity = 400
|
|
payment_department = NO_FREEBIES
|
|
light_mask = "custom-light-mask"
|
|
panel_type = "panel20"
|
|
refill_canister = /obj/item/vending_refill/custom
|
|
fish_source_path = /datum/fish_source/vending/custom
|
|
obj_flags = UNIQUE_RENAME
|
|
|
|
/// max number of items that the custom vendor can hold
|
|
var/max_loaded_items = 20
|
|
/// where the money is sent
|
|
VAR_PRIVATE/datum/bank_account/linked_account
|
|
/// Base64 cache of custom icons.
|
|
VAR_PRIVATE/static/list/base64_cache = list()
|
|
|
|
/obj/machinery/vending/custom/on_deconstruction(disassembled)
|
|
var/obj/item/vending_refill/custom/installed_refill = locate() in component_parts
|
|
|
|
if(linked_account)
|
|
//we delete the canister so players don't resell our products as their own
|
|
component_parts -= installed_refill
|
|
qdel(installed_refill)
|
|
|
|
//self destruct protocol for unauthorized destruction
|
|
explosion(get_turf(src), devastation_range = -1, light_impact_range = 3)
|
|
|
|
return
|
|
|
|
//copy product hash keys
|
|
installed_refill.products.Cut()
|
|
installed_refill.products += products
|
|
|
|
//move products to canister
|
|
for(var/obj/item/stored_item in contents - component_parts)
|
|
stored_item.forceMove(installed_refill)
|
|
|
|
/obj/machinery/vending/custom/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
if(panel_open && istype(held_item, refill_canister))
|
|
context[SCREENTIP_CONTEXT_LMB] = "Restock vending machine"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
if(isliving(user) && istype(held_item, /obj/item/card/id))
|
|
var/obj/item/card/id/card_used = held_item
|
|
if(card_used?.registered_account)
|
|
if(!linked_account)
|
|
context[SCREENTIP_CONTEXT_LMB] = "Link account"
|
|
return ITEM_INTERACT_SUCCESS
|
|
else if(linked_account == card_used.registered_account)
|
|
context[SCREENTIP_CONTEXT_LMB] = "Unlink account"
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/vending/custom/examine(mob/user)
|
|
. = ..()
|
|
if(linked_account)
|
|
. += span_warning("Machine is ID locked. Be sure to unlink before deconstructing the machine.")
|
|
|
|
/obj/machinery/vending/custom/Exited(obj/item/gone, direction)
|
|
. = ..()
|
|
|
|
var/hash_key = ITEM_HASH(gone)
|
|
if(products[hash_key])
|
|
var/new_amount = products[hash_key] - 1
|
|
if(!new_amount)
|
|
products -= hash_key
|
|
update_static_data_for_all_viewers()
|
|
else
|
|
products[hash_key] = new_amount
|
|
|
|
///Returns the number of products loaded in this machine
|
|
/obj/machinery/vending/custom/proc/loaded_items()
|
|
PRIVATE_PROC(TRUE)
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
. = 0
|
|
for(var/product_hash in products)
|
|
. += products[product_hash]
|
|
|
|
/obj/machinery/vending/custom/canLoadItem(obj/item/loaded_item, mob/user, send_message = TRUE)
|
|
if(loaded_item.flags_1 & HOLOGRAM_1)
|
|
if(send_message)
|
|
speak("This vendor cannot accept nonexistent items.")
|
|
return FALSE
|
|
if(isstack(loaded_item))
|
|
if(send_message)
|
|
speak("Loose items may cause problems, try to use it inside wrapping paper.")
|
|
return FALSE
|
|
if(!loaded_item.custom_price)
|
|
if(send_message)
|
|
speak("Item needs to have a custom price set.")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/custom/loadingAttempt(obj/item/inserted_item, mob/user)
|
|
if(!canLoadItem(inserted_item, user))
|
|
return FALSE
|
|
|
|
if(loaded_items() == max_loaded_items)
|
|
speak("There are too many items in stock.")
|
|
return FALSE
|
|
|
|
if(!user.transferItemToLoc(inserted_item, src))
|
|
to_chat(user, span_warning("[inserted_item] is stuck in your hand!"))
|
|
return FALSE
|
|
|
|
//the hash key decides how items stack in the UI. We diffrentiate them based on name & price
|
|
var/hash_key = ITEM_HASH(inserted_item)
|
|
if(products[hash_key])
|
|
products[hash_key]++
|
|
else
|
|
products[hash_key] = 1
|
|
update_static_data_for_all_viewers()
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/custom/RefreshParts()
|
|
SHOULD_CALL_PARENT(FALSE)
|
|
|
|
restock(locate(refill_canister) in component_parts)
|
|
|
|
/obj/machinery/vending/custom/restock(obj/item/vending_refill/canister)
|
|
. = 0
|
|
if(!canister.products?.len)
|
|
if(!canister.products)
|
|
canister.products = list()
|
|
return
|
|
|
|
var/update_static_data = FALSE
|
|
var/available_load = max_loaded_items - loaded_items()
|
|
for(var/product_hash in canister.products)
|
|
//get available space
|
|
var/load_count = min(canister.products[product_hash], available_load)
|
|
if(!load_count)
|
|
break
|
|
//update canister record
|
|
canister.products[product_hash] -= load_count
|
|
if(!canister.products[product_hash])
|
|
canister.products -= product_hash
|
|
//update vendor record
|
|
products[product_hash] += load_count
|
|
//reduce from available space
|
|
available_load -= load_count
|
|
|
|
//update product
|
|
for(var/obj/item/product in canister)
|
|
if(!load_count)
|
|
break
|
|
if(ITEM_HASH(product) == product_hash)
|
|
. += 1
|
|
product.forceMove(src)
|
|
load_count--
|
|
|
|
if(update_static_data)
|
|
update_static_data_for_all_viewers()
|
|
|
|
|
|
/obj/machinery/vending/custom/post_restock(mob/living/user, restocked)
|
|
if(!restocked)
|
|
to_chat(user, span_warning("There's nothing to restock!"))
|
|
return
|
|
|
|
to_chat(user, span_notice("You loaded [restocked] items in [src]"))
|
|
|
|
/obj/machinery/vending/custom/crowbar_act(mob/living/user, obj/item/attack_item)
|
|
if(linked_account)
|
|
visible_message(
|
|
span_warning("Security warning"),
|
|
span_warning("Unauthorized deconstruction of vending machine is prohibited. Please read the warning alert")
|
|
)
|
|
if(tgui_alert(user, "Vending machine is ID locked.\
|
|
Deconstruction will result in an catrostrophic self destruct.\
|
|
If you are the owner of this machine please unlink your account with an ID swipe before proceeding.\
|
|
Still proceed?",
|
|
"Vandalism protection protocol",
|
|
list("Yes", "No")) == "No")
|
|
return ITEM_INTERACT_FAILURE
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/vending/custom/compartmentLoadAccessCheck(mob/user)
|
|
. = FALSE
|
|
if(!isliving(user))
|
|
return FALSE
|
|
var/mob/living/living_user = user
|
|
var/obj/item/card/id/id_card = living_user.get_idcard(FALSE)
|
|
if(id_card?.registered_account && id_card.registered_account == linked_account)
|
|
return TRUE
|
|
|
|
/obj/machinery/vending/custom/item_interaction(mob/living/user, obj/item/attack_item, list/modifiers)
|
|
if(isliving(user) && istype(attack_item, /obj/item/card/id))
|
|
var/obj/item/card/id/card_used = attack_item
|
|
if(card_used?.registered_account)
|
|
if(!linked_account)
|
|
linked_account = card_used.registered_account
|
|
speak("\The [src] has been linked to [card_used].")
|
|
return ITEM_INTERACT_SUCCESS
|
|
else if(linked_account == card_used.registered_account)
|
|
linked_account = null
|
|
speak("account unlinked.")
|
|
return ITEM_INTERACT_SUCCESS
|
|
else
|
|
to_chat(user, "verification failed. unlinking process has been cancelled.")
|
|
return ITEM_INTERACT_FAILURE
|
|
return ..()
|
|
|
|
/obj/machinery/vending/custom/descformat(input, mob/living/user)
|
|
. = input
|
|
var/new_slogan = reject_bad_text(tgui_input_text(user, "Set slogan", "Slogan", "Epic", max_length = 60))
|
|
if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
|
|
return
|
|
if (new_slogan)
|
|
slogan_list += new_slogan
|
|
last_slogan = world.time + rand(0, slogan_delay)
|
|
|
|
/obj/machinery/vending/custom/collect_records_for_static_data(list/records, list/categories, premium)
|
|
. = list()
|
|
if(records != product_records) //no coin or hidden stuff only product records
|
|
return
|
|
|
|
categories["Products"] = list("icon" = "cart-shopping")
|
|
for(var/stocked_hash in products)
|
|
var/base64 = ""
|
|
var/obj/item/target = null
|
|
for(var/obj/item/stored_item in contents - component_parts)
|
|
if(ITEM_HASH(stored_item) == stocked_hash)
|
|
base64 = base64_cache[stocked_hash]
|
|
if(!base64) //generate an icon of the item to use in UI
|
|
base64 = icon2base64(getFlatIcon(stored_item, no_anim = TRUE))
|
|
base64_cache[stocked_hash] = base64
|
|
target = stored_item
|
|
break
|
|
|
|
. += list(list(
|
|
path = stocked_hash,
|
|
name = target.name,
|
|
price = target.custom_price,
|
|
category = "Products",
|
|
ref = stocked_hash,
|
|
colorable = FALSE,
|
|
image = base64
|
|
))
|
|
|
|
|
|
/obj/machinery/vending/custom/ui_interact(mob/user, datum/tgui/ui)
|
|
if(!linked_account)
|
|
balloon_alert(user, "no registered owner!")
|
|
return FALSE
|
|
return ..()
|
|
|
|
/obj/machinery/vending/custom/ui_data(mob/user)
|
|
. = ..()
|
|
|
|
var/is_owner = compartmentLoadAccessCheck(user)
|
|
|
|
.["stock"] = list()
|
|
for(var/stocked_hash in products)
|
|
.["stock"][stocked_hash] = list(
|
|
amount = products[stocked_hash],
|
|
free = is_owner
|
|
)
|
|
|
|
/obj/machinery/vending/custom/vend(list/params, mob/living/user, list/greyscale_colors)
|
|
. = FALSE
|
|
if(!isliving(user))
|
|
return
|
|
var/obj/item/dispensed_item = params["ref"]
|
|
for(var/obj/item/product in contents - component_parts)
|
|
if(ITEM_HASH(product) == dispensed_item)
|
|
dispensed_item = product
|
|
break
|
|
|
|
var/obj/item/card/id/id_card = user.get_idcard(TRUE)
|
|
if(QDELETED(id_card))
|
|
balloon_alert(user, "no card found!")
|
|
flick(icon_deny, src)
|
|
return
|
|
|
|
/// Charges the user if its not the owner
|
|
var/datum/bank_account/payee = id_card.registered_account
|
|
if(!compartmentLoadAccessCheck(user))
|
|
if(istype(id_card, /obj/item/card/id/departmental_budget))
|
|
balloon_alert(user, "invalid payment card")
|
|
to_chat(user, span_warning("You cannot use a departmental card for this."))
|
|
return
|
|
if(!payee.has_money(dispensed_item.custom_price))
|
|
balloon_alert(user, "insufficient funds!")
|
|
return
|
|
/// Make the transaction
|
|
payee.adjust_money(-dispensed_item.custom_price, , "Vending: [dispensed_item]")
|
|
linked_account.adjust_money(dispensed_item.custom_price, "Vending: [dispensed_item] Bought")
|
|
linked_account.bank_card_talk("[payee.account_holder] made a [dispensed_item.custom_price] \
|
|
[MONEY_SYMBOL] purchase at your custom vendor.")
|
|
/// Log the transaction
|
|
SSblackbox.record_feedback("amount", "vending_spent", dispensed_item.custom_price)
|
|
log_econ("[dispensed_item.custom_price] [MONEY_NAME] were spent on [src] buying a \
|
|
[dispensed_item] by [payee.account_holder], owned by [linked_account.account_holder].")
|
|
/// Make an alert
|
|
var/ref = REF(user)
|
|
if(last_shopper != ref || purchase_message_cooldown < world.time)
|
|
speak("Thank you for your patronage [user]!")
|
|
purchase_message_cooldown = world.time + 5 SECONDS
|
|
last_shopper = ref
|
|
|
|
/// Remove the item
|
|
use_energy(active_power_usage)
|
|
try_put_in_hand(dispensed_item, user)
|
|
return TRUE
|
|
|
|
/obj/item/vending_refill/custom
|
|
machine_name = "Custom Vendor"
|
|
icon_state = "refill_custom"
|
|
custom_premium_price = PAYCHECK_CREW
|
|
|
|
/obj/item/vending_refill/custom/get_part_rating()
|
|
. = 0
|
|
for(var/key in products)
|
|
. += products[key]
|
|
|
|
/obj/machinery/vending/custom/unbreakable
|
|
name = "Indestructible Vendor"
|
|
resistance_flags = INDESTRUCTIBLE
|
|
allow_custom = FALSE
|
|
|
|
/obj/machinery/vending/custom/greed //name and like decided by the spawn
|
|
icon_state = "greed"
|
|
icon_deny = "greed-deny"
|
|
panel_type = "panel4"
|
|
max_integrity = 700
|
|
max_loaded_items = 40
|
|
light_mask = "greed-light-mask"
|
|
allow_custom = FALSE
|
|
custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
|
|
|
|
/obj/machinery/vending/custom/greed/Initialize(mapload)
|
|
. = ..()
|
|
//starts in a state where you can move it
|
|
set_anchored(FALSE)
|
|
set_panel_open(TRUE)
|
|
//and references the deity
|
|
name = "[GLOB.deity]'s Consecrated Vendor"
|
|
desc = "A vending machine created by [GLOB.deity]."
|
|
slogan_list = list("[GLOB.deity] says: It's your divine right to buy!")
|
|
add_filter("vending_outline", 9, list("type" = "outline", "color" = COLOR_VERY_SOFT_YELLOW))
|
|
add_filter("vending_rays", 10, list("type" = "rays", "size" = 35, "color" = COLOR_VIVID_YELLOW))
|
|
|
|
#undef ITEM_HASH
|