Files
Bubberstation/code/modules/uplink/uplink_items.dm
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

258 lines
10 KiB
Plaintext

// TODO: Work into reworked uplinks.
/// Selects a set number of unique items from the uplink, and deducts a percentage discount from them
/proc/create_uplink_sales(num, datum/uplink_category/category, limited_stock, list/sale_items)
var/list/sales = list()
var/list/per_category = list()
for (var/datum/uplink_item/possible_sale as anything in sale_items)
if (!(possible_sale.category in per_category))
per_category[possible_sale.category] = list()
per_category[possible_sale.category] += possible_sale
for (var/i in 1 to min(length(per_category), num))
var/datum/uplink_category/item_category = pick(per_category)
var/datum/uplink_item/taken_item = pick(per_category[item_category])
per_category -= item_category
var/datum/uplink_item/uplink_item = new taken_item.type()
var/discount = uplink_item.get_discount()
var/static/list/disclaimer = list(
"Void where prohibited.",
"Not recommended for children.",
"Contains small parts.",
"Check local laws for legality in region.",
"Do not taunt.",
"Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.",
"Keep away from fire or flames.",
"Product is provided \"as is\" without any implied or expressed warranties.",
"As seen on TV.",
"For recreational use only.",
"Use only as directed.",
"16% sales tax will be charged for orders originating within Space Nebraska.",
)
//We want to limit the purchase amount of some items without adjusting the pricing.
if (uplink_item.limited_discount_stock > 0) {
uplink_item.limited_stock = uplink_item.limited_discount_stock
}
//if stock limited is passed into the function, we'll override everything
if (limited_stock > 0) {
uplink_item.limited_stock = limited_stock
}
if(uplink_item.cost >= 20) //Tough love for nuke ops
discount *= 0.5
uplink_item.stock_key = WEAKREF(uplink_item)
uplink_item.category = category
uplink_item.cost = max(round(uplink_item.cost * (1 - discount)),1)
uplink_item.name += " ([round(((initial(uplink_item.cost)-uplink_item.cost)/initial(uplink_item.cost))*100)]% off!)"
uplink_item.desc += " Normally costs [initial(uplink_item.cost)] TC. All sales final. [pick(disclaimer)]"
uplink_item.item = taken_item.item
uplink_item.discounted = TRUE
sales += uplink_item
return sales
/**
* Uplink Items
*
* Items that can be spawned from an uplink. Can be limited by gamemode.
**/
/datum/uplink_item
/// Name of the uplink item
var/name = "item name"
/// Category of the uplink
var/datum/uplink_category/category
/// Description of the uplink
var/desc = "item description"
/// Path to the item to spawn.
var/item = null
/// Cost of the item.
var/cost = 0
/// Amount of TC to refund, in case there's a TC penalty for refunds.
var/refund_amount = 0
/// Whether this item is refundable or not.
var/refundable = FALSE
// Chance of being included in the surplus crate.
var/surplus = 100
/// Whether this can be discounted or not
var/cant_discount = FALSE
/// If discounted, is true. Used to send a signal to update reimbursement.
VAR_FINAL/discounted = FALSE
/// If this value is changed on two items they will share stock, defaults to not sharing stock with any other item
var/stock_key = UPLINK_SHARED_STOCK_UNIQUE
/// How many items of this stock can be purchased.
var/limited_stock = -1 //Setting this above zero limits how many times this item can be bought by the same traitor in a round, -1 is unlimited
/// How many items of this stock can be purchased from the discount tab.
var/limited_discount_stock = -1
/// A bitfield to represent what uplinks can purchase this item.
/// See [`code/__DEFINES/uplink.dm`].
var/purchasable_from = ALL
/// If this uplink item is only available to certain roles. Roles are dependent on the frequency chip or stored ID.
var/list/restricted_roles = list()
/// The species able to purchase this uplink item.
var/list/restricted_species = list()
/// The minimum amount of progression needed for this item to be added to uplinks.
var/progression_minimum = 0
/// The minimum number of joined players (so not observers) needed for this item to be added to uplinks.
var/population_minimum = 0
/// Whether this purchase is visible in the purchase log.
var/purchase_log_vis = TRUE // Visible in the purchase log?
/// Whether this purchase is restricted or not (VR/Events related)
var/restricted = FALSE
/// Flags related to if an item will provide illegal tech, or trips contraband detectors once spawned in as an item.
var/uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/// String to be shown instead of the price, e.g for the Random item.
var/cost_override_string = ""
/// Whether this item locks all other items from being purchased. Used by syndicate balloon and a few other purchases.
/// Can't be purchased if you've already bought other things
/// Uses the purchase log, so items purchased that are not visible in the purchase log will not count towards this.
/// However, they won't be purchasable afterwards.
var/lock_other_purchases = FALSE
/// A lazylist of typepaths to uplink items relevant to this this item
/// EX: a pistol would list its magazines or modifications here
var/list/relevant_child_items
/datum/uplink_item/New()
. = ..()
if(stock_key != UPLINK_SHARED_STOCK_UNIQUE)
return
stock_key = type
/datum/uplink_category
/// Name of the category
var/name
/// Weight of the category. Used to determine the positioning in the uplink. High weight = appears first
var/weight = 0
/// Returns by how much percentage do we reduce the price of the selected item
/datum/uplink_item/proc/get_discount()
var/static/list/discount_types = list(
TRAITOR_DISCOUNT_SMALL = 4,
TRAITOR_DISCOUNT_AVERAGE = 2,
TRAITOR_DISCOUNT_BIG = 1,
)
return get_discount_value(pick_weight(discount_types))
/// Receives a traitor discount type value, returns the amount by which we will reduce the price
/datum/uplink_item/proc/get_discount_value(discount_type)
switch(discount_type)
if(TRAITOR_DISCOUNT_BIG)
return 0.75
if(TRAITOR_DISCOUNT_AVERAGE)
return 0.5
else
return 0.25
/// Spawns an item and logs its purchase
/datum/uplink_item/proc/purchase(mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
var/atom/spawned_item = spawn_item(item, user, uplink_handler, source)
log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [source]'s uplink")
user.playsound_local(get_turf(user), 'sound/effects/kaching.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
if(purchase_log_vis && uplink_handler.purchase_log)
uplink_handler.purchase_log.LogPurchase(spawned_item, src, cost)
if(lock_other_purchases)
uplink_handler.shop_locked = TRUE
/// Spawns an item in the world
/datum/uplink_item/proc/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
if(!spawn_path)
return
var/atom/spawned_item
if(ispath(spawn_path))
spawned_item = new spawn_path(get_turf(user))
else
spawned_item = spawn_path
if(refundable)
spawned_item.AddElement(/datum/element/uplink_reimburse, (refund_amount ? refund_amount : cost))
if(uplink_item_flags & SYNDIE_TRIPS_CONTRABAND) // Ignore things that shouldn't be detectable as contraband on the station.
ADD_TRAIT(spawned_item, TRAIT_CONTRABAND, INNATE_TRAIT)
for(var/obj/contained as anything in spawned_item.get_all_contents())
ADD_TRAIT(contained, TRAIT_CONTRABAND, INNATE_TRAIT)
var/mob/living/carbon/human/human_user = user
if(istype(human_user) && isitem(spawned_item) && human_user.put_in_hands(spawned_item))
to_chat(human_user, span_boldnotice("[spawned_item] materializes into your hands!"))
else
to_chat(user, span_boldnotice("[spawned_item] materializes onto the floor!"))
SEND_SIGNAL(uplink_handler, COMSIG_ON_UPLINK_PURCHASE, spawned_item, user)
return spawned_item
/// Used to create the uplink's item for generic use, rather than use by a Syndie specifically
/// Can be used to "de-restrict" some items, such as Nukie guns spawning with Syndicate pins
/datum/uplink_item/proc/spawn_item_for_generic_use(mob/user)
var/atom/movable/created = new item(user.loc)
if(uplink_item_flags & SYNDIE_TRIPS_CONTRABAND) // Things that shouldn't be detectable as contraband on the station.
ADD_TRAIT(created, TRAIT_CONTRABAND, INNATE_TRAIT)
for(var/obj/contained as anything in created.get_all_contents())
ADD_TRAIT(contained, TRAIT_CONTRABAND, INNATE_TRAIT)
if(isgun(created))
replace_pin(created)
else if(istype(created, /obj/item/storage/toolbox/guncase))
for(var/obj/item/gun/gun in created)
replace_pin(gun)
if(isobj(created))
var/obj/created_obj = created
LAZYREMOVE(created_obj.req_access, ACCESS_SYNDICATE)
LAZYREMOVE(created_obj.req_one_access, ACCESS_SYNDICATE)
return created
/// Used by spawn_item_for_generic_use to replace the pin of a gun with a normal one
/datum/uplink_item/proc/replace_pin(obj/item/gun/gun_reward)
PRIVATE_PROC(TRUE)
if(!istype(gun_reward.pin, /obj/item/firing_pin/implant/pindicate))
return
QDEL_NULL(gun_reward.pin)
var/obj/item/firing_pin/pin = new
pin.gun_insert(new_gun = gun_reward, starting = TRUE)
///For special overrides if an item can be bought or not.
/datum/uplink_item/proc/can_be_bought(datum/uplink_handler/source)
return TRUE
/datum/uplink_category/discounts
name = "Discounted Gear"
weight = -1
/datum/uplink_category/discount_team_gear
name = "Discounted Team Gear"
weight = -1
/datum/uplink_category/limited_discount_team_gear
name = "Limited Stock Team Gear"
weight = -2
//Discounts (dynamically filled above)
/datum/uplink_item/discounts
category = /datum/uplink_category/discounts
purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY // Probably not necessary but just in case
/datum/uplink_category/objective_special
name = "Objective-Specific Equipment"
weight = -3
// Special equipment (Dynamically fills in uplink component)
/datum/uplink_item/special_equipment
category = /datum/uplink_category/objective_special
name = "Objective-Specific Equipment"
desc = "Equipment necessary for accomplishing specific objectives. If you are seeing this, something has gone wrong."
limited_stock = 1
uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY // Ditto
/datum/uplink_item/special_equipment/purchase(mob/user, datum/component/uplink/U)
..()
if(user?.mind?.failed_special_equipment)
user.mind.failed_special_equipment -= item
/// Code that enables the ability to have limited stock that is shared by different items
/datum/shared_uplink_stock