diff --git a/code/__DEFINES/blackmarket.dm b/code/__DEFINES/blackmarket.dm new file mode 100644 index 000000000000..875c74ac16ac --- /dev/null +++ b/code/__DEFINES/blackmarket.dm @@ -0,0 +1,9 @@ + +// Shipping methods + +// The BEST way of shipping items: accurate, "undetectable" +#define SHIPPING_METHOD_LTSRBT "LTSRBT" +// Picks a random area to teleport the item to and gives you a minute to get there before it is sent. +#define SHIPPING_METHOD_TELEPORT "Teleport" +// Throws the item from somewhere at the station. +#define SHIPPING_METHOD_LAUNCH "Launch" diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm index ff6e104e7e41..13391b6e0bb8 100644 --- a/code/_globalvars/lists/maintenance_loot.dm +++ b/code/_globalvars/lists/maintenance_loot.dm @@ -620,7 +620,8 @@ GLOBAL_LIST_INIT(maintenance_loot_makeshift,list( /obj/item/stack/spacecash/c200 = W_RARE, /obj/item/stack/spacecash/c50 = W_UNCOMMON, /obj/item/stack/spacecash/c500 = W_MYTHICAL, - /obj/item/stock_parts/cell/potato = W_UNCOMMON + /obj/item/stock_parts/cell/potato = W_UNCOMMON, + /obj/item/blackmarket_uplink = W_UNCOMMON )) //Has minor mechanical usage; stuff you'd usually only find in a lathe, through crafting, or through a vendor. diff --git a/code/controllers/subsystem/blackmarket.dm b/code/controllers/subsystem/blackmarket.dm new file mode 100644 index 000000000000..ff3a3b75433d --- /dev/null +++ b/code/controllers/subsystem/blackmarket.dm @@ -0,0 +1,116 @@ +SUBSYSTEM_DEF(blackmarket) + name = "Blackmarket" + flags = SS_BACKGROUND + init_order = INIT_ORDER_DEFAULT + + /// Descriptions for each shipping methods. + var/shipping_method_descriptions = list( + SHIPPING_METHOD_LAUNCH="Launches the item at the station from space, cheap but you might not recieve your item at all.", + SHIPPING_METHOD_LTSRBT="Long-To-Short-Range-Bluespace-Transceiver, a machine that recieves items outside the station and then teleports them to the location of the uplink.", + SHIPPING_METHOD_TELEPORT="Teleports the item in a random area in the station, you get 60 seconds to get there first though." + ) + + /// List of all existing markets. + var/list/datum/blackmarket_market/markets = list() + /// List of existing ltsrbts. + var/list/obj/machinery/ltsrbt/telepads = list() + /// Currently queued purchases. + var/list/queued_purchases = list() + +/datum/controller/subsystem/blackmarket/Initialize(timeofday) + for(var/market in subtypesof(/datum/blackmarket_market)) + markets[market] += new market + + for(var/item in subtypesof(/datum/blackmarket_item)) + var/datum/blackmarket_item/I = new item() + if(!I.item) + continue + + for(var/M in I.markets) + if(!markets[M]) + stack_trace("SSblackmarket: Item [I] available in market that does not exist.") + continue + markets[M].add_item(item) + qdel(I) + . = ..() + return SS_INIT_SUCCESS + +/datum/controller/subsystem/blackmarket/fire(resumed) + while(length(queued_purchases)) + var/datum/blackmarket_purchase/purchase = queued_purchases[1] + queued_purchases.Cut(1,2) + + // Uh oh, uplink is gone. We will just keep the money and you will not get your order. + if(!purchase.uplink || QDELETED(purchase.uplink)) + queued_purchases -= purchase + qdel(purchase) + continue + + switch(purchase.method) + // Find a ltsrbt pad and make it handle the shipping. + if(SHIPPING_METHOD_LTSRBT) + if(!telepads.len) + continue + // Prioritize pads that don't have a cooldown active. + var/free_pad_found = FALSE + for(var/obj/machinery/ltsrbt/pad in telepads) + if(pad.recharge_cooldown) + continue + pad.add_to_queue(purchase) + queued_purchases -= purchase + free_pad_found = TRUE + break + + if(free_pad_found) + continue + + var/obj/machinery/ltsrbt/pad = pick(telepads) + + to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being processed by [pad].")) + + queued_purchases -= purchase + pad.add_to_queue(purchase) + // Get random area, throw it somewhere there. + if(SHIPPING_METHOD_TELEPORT) + var/turf/targetturf = get_safe_random_station_turf() + // This shouldn't happen. + if (!targetturf) + continue + + to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds.")) + + // do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks. + addtimer(CALLBACK(src, /datum/controller/subsystem/blackmarket/proc/fake_teleport, purchase.entry.spawn_item(), targetturf), 60 SECONDS) + queued_purchases -= purchase + qdel(purchase) + // Get the current location of the uplink if it exists, then throws the item from space at the station from a random direction. + if(SHIPPING_METHOD_LAUNCH) + var/startSide = pick(GLOB.cardinals) + var/turf/T = get_turf(purchase.uplink) + var/pickedloc = spaceDebrisStartLoc(startSide, T.z) + + var/atom/movable/item = purchase.entry.spawn_item(pickedloc) + item.throw_at(purchase.uplink, 3, 3, spin = FALSE) + + to_chat(recursive_loc_check(purchase.uplink.loc, /mob), span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)].")) + + queued_purchases -= purchase + qdel(purchase) + + if(MC_TICK_CHECK) + break + +/// Used to make a teleportation effect as do_teleport does not like moving items from nullspace. +/datum/controller/subsystem/blackmarket/proc/fake_teleport(atom/movable/item, turf/target) + item.forceMove(target) + var/datum/effect_system/spark_spread/sparks = new + sparks.set_up(5, 1, target) + sparks.attach(item) + sparks.start() + +/// Used to add /datum/blackmarket_purchase to queued_purchases var. Returns TRUE when queued. +/datum/controller/subsystem/blackmarket/proc/queue_item(datum/blackmarket_purchase/P) + if(P.method == SHIPPING_METHOD_LTSRBT && !telepads.len) + return FALSE + queued_purchases += P + return TRUE diff --git a/code/modules/cargo/blackmarket/blackmarket_item.dm b/code/modules/cargo/blackmarket/blackmarket_item.dm new file mode 100644 index 000000000000..4725677818d6 --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_item.dm @@ -0,0 +1,76 @@ +/datum/blackmarket_item + /// Name for the item entry used in the uplink. + var/name + /// Description for the item entry used in the uplink. + var/desc + /// The category this item belongs to, should be already declared in the market that this item is accessible in. + var/category + /// "/datum/blackmarket_market"s that this item should be in, used by SSblackmarket on init. + var/list/markets = list(/datum/blackmarket_market/blackmarket) + + /// Price for the item, if not set creates a price according to the *_min and *_max vars. + var/price + /// How many of this type of item is available, if not set creates a price according to the *_min and *_max vars. + var/stock + + /// Path to or the item itself what this entry is for, this should be set even if you override spawn_item to spawn your item. + var/item + + /// Minimum price for the item if generated randomly. + var/price_min = 0 + /// Maximum price for the item if generated randomly. + var/price_max = 0 + /// Minimum amount that there should be of this item in the market if generated randomly. This defaults to 1 as most items will have it as 1. + var/stock_min = 1 + /// Maximum amount that there should be of this item in the market if generated randomly. + var/stock_max = 0 + /// Probability for this item to be available. Used by SSblackmarket on init. + var/availability_prob = 0 + +/datum/blackmarket_item/New() + if(isnull(price)) + price = rand(price_min, price_max) + if(isnull(stock)) + stock = rand(stock_min, stock_max) + +/// Used for spawning the wanted item, override if you need to do something special with the item. +/datum/blackmarket_item/proc/spawn_item(loc) + return new item(loc) + +/// Buys the item and makes SSblackmarket handle it. +/datum/blackmarket_item/proc/buy(obj/item/blackmarket_uplink/uplink, mob/buyer, shipping_method) + // Sanity + if(!istype(uplink) || !istype(buyer)) + return FALSE + + // This shouldn't be able to happen unless there was some manipulation or admin fuckery. + if(!item || stock <= 0) + return FALSE + + // Alright, the item has been purchased. + var/datum/blackmarket_purchase/purchase = new(src, uplink, shipping_method) + + // SSblackmarket takes care of the shipping. + if(SSblackmarket.queue_item(purchase)) + stock-- + log_game("[ADMIN_LOOKUPFLW(buyer)] has successfully purchased [name] using [shipping_method] for shipping.") + return TRUE + return FALSE + +// This exists because it is easier to keep track of all the vars this way. +/datum/blackmarket_purchase + /// The entry being purchased. + var/datum/blackmarket_item/entry + /// Instance of the item being sent. + var/item + /// The uplink where this purchase was done from. + var/obj/item/blackmarket_uplink/uplink + /// Shipping method used to buy this item. + var/method + +/datum/blackmarket_purchase/New(_entry, _uplink, _method) + entry = _entry + if(!ispath(entry.item)) + item = entry.item + uplink = _uplink + method = _method diff --git a/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm b/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm new file mode 100644 index 000000000000..19eb73734a05 --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_items/clothing.dm @@ -0,0 +1,59 @@ +/datum/blackmarket_item/clothing + category = "Clothing" + +/datum/blackmarket_item/clothing/ninja_mask + name = "Space Ninja Mask" + desc = "Apart from being acid, lava, fireproof and being hard to take off someone it does nothing special on it's own." + item = /obj/item/clothing/mask/gas/space_ninja + + price_min = 200 + price_max = 500 + stock_max = 3 + availability_prob = 40 + +/datum/blackmarket_item/clothing/durathread_vest + name = "Durathread Vest" + desc = "Dont let them tell you this stuff is \"Like asbestos\" or \"Pulled from the market for safety concerns\". It could be the difference between a robusting and a retaliation." + item = /obj/item/clothing/suit/armor/vest/durathread + + price_min = 200 + price_max = 400 + stock_max = 4 + availability_prob = 50 + +/datum/blackmarket_item/clothing/durathread_helmet + name = "Durathread Helmet" + desc = "Customers ask why it's called a helmet when it's just made from armoured fabric and I always say the same thing: No refunds." + item = /obj/item/clothing/head/helmet/durathread + + price_min = 100 + price_max = 200 + stock_max = 4 + availability_prob = 50 + +/datum/blackmarket_item/clothing/full_spacesuit_set + name = "Nanotrasen Branded Spacesuit Box" + desc = "A few boxes of \"Old Style\" space suits fell off the back of a space truck." + item = /obj/item/storage/box + price_min = 1500 + price_max = 4000 + stock_max = 3 + availability_prob = 30 + +/datum/blackmarket_item/clothing/full_spacesuit_set/spawn_item(loc) + var/obj/item/storage/box/B = ..() + B.name = "Spacesuit Box" + B.desc = "It has a NT logo on it." + new /obj/item/clothing/suit/space(B) + new /obj/item/clothing/head/helmet/space(B) + return B + +/datum/blackmarket_item/clothing/chameleon_hat + name = "Chameleon Hat" + desc = "Pick any hat you want with this Handy device. Not Quality Tested." + item = /obj/item/clothing/head/chameleon/broken + + price_min = 100 + price_max = 200 + stock_max = 2 + availability_prob = 70 diff --git a/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm b/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm new file mode 100644 index 000000000000..9c64d75fbd4d --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_items/consumables.dm @@ -0,0 +1,62 @@ +/datum/blackmarket_item/consumable + category = "Consumables" + +/datum/blackmarket_item/consumable/clown_tears + name = "Bowl of Clown's Tears" + desc = "Guaranteed fresh from Weepy Boggins Tragic Kitchen" + item = /obj/item/reagent_containers/food/snacks/soup/clownstears + stock = 1 + + price_min = 520 + price_max = 600 + availability_prob = 10 + +/datum/blackmarket_item/consumable/donk_pocket_box + name = "Box of Donk Pockets" + desc = "A well packaged box containing the favourite snack of every spacefarer." + item = /obj/item/storage/box/donkpockets + + stock_min = 2 + stock_max = 5 + price_min = 325 + price_max = 400 + availability_prob = 80 + +/datum/blackmarket_item/consumable/suspicious_pills + name = "Bottle of Suspicious Pills" + desc = "A random cocktail of luxury drugs that are sure to put a smile on your face!" + item = /obj/item/storage/pill_bottle + + stock_min = 2 + stock_max = 3 + price_min = 400 + price_max = 700 + availability_prob = 50 + +/datum/blackmarket_item/consumable/suspicious_pills/spawn_item(loc) + var/pillbottle = pick(list(/obj/item/storage/pill_bottle/zoom, + /obj/item/storage/pill_bottle/happy, + /obj/item/storage/pill_bottle/lsd, + /obj/item/storage/pill_bottle/aranesp, + /obj/item/storage/pill_bottle/stimulant)) + return new pillbottle(loc) + +/datum/blackmarket_item/consumable/floor_pill + name = "Strange Pill" + desc = "The Russian Roulette of the Maintenance Tunnels." + item = /obj/item/reagent_containers/pill/floorpill + + stock_min = 5 + stock_max = 35 + price_min = 10 + price_max = 60 + availability_prob = 50 + +/datum/blackmarket_item/consumable/pumpup + name = "Maintenance Pump-Up" + desc = "Resist any Baton stun with this handy device!" + item = /obj/item/reagent_containers/autoinjector/medipen/pumpup + stock_max = 3 + price_min = 50 + price_max = 150 + availability_prob = 90 diff --git a/code/modules/cargo/blackmarket/blackmarket_items/misc.dm b/code/modules/cargo/blackmarket/blackmarket_items/misc.dm new file mode 100644 index 000000000000..13b14009ddda --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_items/misc.dm @@ -0,0 +1,66 @@ +/datum/blackmarket_item/misc + category = "Miscellaneous" + +/datum/blackmarket_item/misc/jade_Lantern + name = "Jade Lantern" + desc = "Found in a box labeled 'Danger: Radioactive'. Probably safe." + item = /obj/item/flashlight/lantern/jade + price_min = 150 + price_max = 500 + stock_max = 2 + availability_prob = 45 + +/datum/blackmarket_item/misc/cap_gun + name = "Cap Gun" + desc = "Prank your friends with this harmless gun! Harmlessness guranteed." + item = /obj/item/toy/gun + + price_min = 50 + price_max = 200 + stock_max = 6 + availability_prob = 80 + +/datum/blackmarket_item/misc/shoulder_holster + name = "Shoulder holster" + desc = "Yeehaw, hardboiled friends! This holster is the first step in your dream of becoming a detective and being allowed to shoot real guns!" + item = /obj/item/storage/belt/holster + + price_min = 400 + price_max = 800 + stock_max = 8 + availability_prob = 60 + +/datum/blackmarket_item/misc/holywater + name = "Flask of holy water" + desc = "Father Lootius' own brand of ready-made holy water." + item = /obj/item/reagent_containers/food/drinks/bottle/holywater + price_min = 400 + price_max = 600 + stock_max = 3 + availability_prob = 40 + +/datum/blackmarket_item/misc/holywater/spawn_item(loc) + if (prob(6.66)) + return new /obj/item/reagent_containers/glass/beaker/unholywater(loc) + return ..() + +/datum/blackmarket_item/misc/strange_seed + name = "Strange Seeds" + desc = "An Exotic Variety of seed that can contain anything from glow to acid." + item = /obj/item/seeds/random + + price_min = 320 + price_max = 360 + stock_min = 2 + stock_max = 5 + availability_prob = 50 + +/datum/blackmarket_item/misc/smugglers_satchel + name = "Smuggler's Satchel" + desc = "This easily hidden satchel can become a versatile tool to anybody with the desire to keep certain items out of sight and out of mind." + item = /obj/item/storage/backpack/satchel/flat/empty + + price_min = 750 + price_max = 1000 + stock_max = 2 + availability_prob = 30 diff --git a/code/modules/cargo/blackmarket/blackmarket_items/tools.dm b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm new file mode 100644 index 000000000000..0b2cb16aa827 --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_items/tools.dm @@ -0,0 +1,82 @@ +/datum/blackmarket_item/tool + category = "Tools" + +/datum/blackmarket_item/tool/caravan_wrench + name = "Experimental Wrench" + desc = "The extra fast and handy wrench you always wanted!" + item = /obj/item/wrench/caravan + stock = 1 + + price_min = 400 + price_max = 800 + availability_prob = 20 + +/datum/blackmarket_item/tool/caravan_wirecutters + name = "Experimental Wirecutters" + desc = "The extra fast and handy wirecutters you always wanted!" + item = /obj/item/wirecutters/caravan + stock = 1 + + price_min = 400 + price_max = 800 + availability_prob = 20 + +/datum/blackmarket_item/tool/caravan_screwdriver + name = "Experimental Screwdriver" + desc = "The extra fast and handy screwdriver you always wanted!" + item = /obj/item/screwdriver/caravan + stock = 1 + + price_min = 400 + price_max = 800 + availability_prob = 20 + +/datum/blackmarket_item/tool/caravan_crowbar + name = "Experimental Crowbar" + desc = "The extra fast and handy crowbar you always wanted!" + item = /obj/item/crowbar/red/caravan + stock = 1 + + price_min = 400 + price_max = 800 + availability_prob = 20 + +/datum/blackmarket_item/tool/binoculars + name = "Binoculars" + desc = "Increase your sight by 150% with this handy Tool!" + item = /obj/item/binoculars + stock = 1 + + price_min = 400 + price_max = 960 + availability_prob = 30 + +/datum/blackmarket_item/tool/riot_shield + name = "Riot Shield" + desc = "Protect yourself from an unexpected Riot at your local Police department!" + item = /obj/item/shield/riot + + price_min = 450 + price_max = 650 + stock_max = 2 + availability_prob = 50 + +/datum/blackmarket_item/tool/thermite_bottle + name = "Thermite Bottle" + desc = "30u of Thermite to assist in creating a quick access point or get away!" + item = /obj/item/reagent_containers/glass/bottle/thermite + + price_min = 500 + price_max = 1500 + stock_max = 3 + availability_prob = 30 + +/datum/blackmarket_item/tool/science_goggles + name = "Science Goggles" + desc = "These glasses scan the contents of containers and projects their contents to the user in an easy to read format." + item = /obj/item/clothing/glasses/science + + price_min = 150 + price_max = 200 + stock_max = 3 + availability_prob = 50 diff --git a/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm b/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm new file mode 100644 index 000000000000..458fce7dfdcd --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_items/weapons.dm @@ -0,0 +1,45 @@ +/datum/blackmarket_item/weapon + category = "Weapons" + +/datum/blackmarket_item/weapon/bear_trap + name = "Bear Trap" + desc = "Get the janitor back at his own game with this affordable prank kit." + item = /obj/item/restraints/legcuffs/beartrap + + price_min = 300 + price_max = 550 + stock_max = 3 + availability_prob = 40 + +/datum/blackmarket_item/weapon/shotgun_dart + name = "Shotgun Dart" + desc = "These handy darts can be filled up with any chemical and be shot with a shotgun! \ + Prank your friends by shooting them with laughter! \ + Not recommended for comercial use." + item = /obj/item/ammo_casing/shotgun/dart + + price_min = 10 + price_max = 50 + stock_min = 10 + stock_max = 60 + availability_prob = 40 + +/datum/blackmarket_item/weapon/bone_spear + name = "Bone Spear" + desc = "Authentic tribal spear, made from real bones! A steal at any price, especially if you're a caveman." + item = /obj/item/melee/spear/bonespear + + price_min = 200 + price_max = 300 + stock_max = 3 + availability_prob = 60 + +/datum/blackmarket_item/weapon/emp_grenade + name = "EMP Grenade" + desc = "Use this grenade for SHOCKING results!" + item = /obj/item/grenade/empgrenade + + price_min = 100 + price_max = 400 + stock_max = 2 + availability_prob = 50 diff --git a/code/modules/cargo/blackmarket/blackmarket_market.dm b/code/modules/cargo/blackmarket/blackmarket_market.dm new file mode 100644 index 000000000000..dea1eb9109cd --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_market.dm @@ -0,0 +1,53 @@ +/datum/blackmarket_market + /// Name for the market. + var/name = "huh?" + + /// Available shipping methods and prices, just leave the shipping method out that you don't want to have. + var/list/shipping + + + // Automatic vars, do not touch these. + /// Items available from this market, populated by SSblackmarket on initialization. + var/list/available_items = list() + /// Item categories available from this market, only items which are in these categories can be gotten from this market. + var/list/categories = list() + +/// Adds item to the available items and add it's category if it is not in categories yet. +/datum/blackmarket_market/proc/add_item(datum/blackmarket_item/item) + if(!prob(initial(item.availability_prob))) + return FALSE + + if(ispath(item)) + item = new item() + + if(!(item.category in categories)) + categories += item.category + available_items[item.category] = list() + + available_items[item.category] += item + return TRUE + +/// Handles buying the item, this is mainly for future use and moving the code away from the uplink. +/datum/blackmarket_market/proc/purchase(item, category, method, obj/item/blackmarket_uplink/uplink, user) + if(!istype(uplink) || !(method in shipping)) + return FALSE + + for(var/datum/blackmarket_item/I in available_items[category]) + if(I.type != item) + continue + var/price = I.price + shipping[method] + // I can't get the price of the item and shipping in a clean way to the UI, so I have to do this. + if(uplink.money < price) + to_chat(span_warning("You don't have enough credits in [uplink] for [I] with [method] shipping.")) + return FALSE + + if(I.buy(uplink, user, method)) + uplink.money -= price + return TRUE + return FALSE + +/datum/blackmarket_market/blackmarket + name = "Black Market" + shipping = list(SHIPPING_METHOD_LTSRBT =50, + SHIPPING_METHOD_LAUNCH =10, + SHIPPING_METHOD_TELEPORT=75) diff --git a/code/modules/cargo/blackmarket/blackmarket_telepad.dm b/code/modules/cargo/blackmarket/blackmarket_telepad.dm new file mode 100644 index 000000000000..ef4273d193a4 --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_telepad.dm @@ -0,0 +1,120 @@ +/obj/item/circuitboard/machine/ltsrbt + name = "LTSRBT (Machine Board)" + icon_state = "bluespacearray" + build_path = /obj/machinery/ltsrbt + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 2, + /obj/item/stock_parts/subspace/ansible = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/scanning_module = 2) + def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial) + +/obj/machinery/ltsrbt + name = "Long-To-Short-Range-Bluespace-Transciever" + desc = "The LTSRBT is a compact teleportation machine for recieving and sending items outside the station and inside the station.\nUsing teleportation frequencies stolen from NT it is near undetectable.\nEssential for any illegal market operations on NT stations.\n" + icon_state = "exonet_node" + circuit = /obj/item/circuitboard/machine/ltsrbt + density = TRUE + + idle_power_usage = 200 + + /// Divider for power_usage_per_teleport. + var/power_efficiency = 1 + /// Power used per teleported which gets divided by power_efficiency. + var/power_usage_per_teleport = 10000 + /// The time it takes for the machine to recharge before being able to send or recieve items. + var/recharge_time = 0 + /// Current recharge progress. + var/recharge_cooldown = 0 + /// Base recharge time which is used to get recharge_time. + var/base_recharge_time = 100 + /// Current /datum/blackmarket_purchase being recieved. + var/recieving + /// Current /datum/blackmarket_purchase being sent to the target uplink. + var/transmitting + /// Queue for purchases that the machine should recieve and send. + var/list/datum/blackmarket_purchase/queue = list() + +/obj/machinery/ltsrbt/Initialize(mapload) + . = ..() + SSblackmarket.telepads += src + +/obj/machinery/ltsrbt/examine(mob/user) + . = ..() + if(queue.len) + . += span_notice("It is currently recharging. Progress: [recharge_cooldown]%") + +/obj/machinery/ltsrbt/Destroy() + SSblackmarket.telepads -= src + // Bye bye orders. + if(SSblackmarket.telepads.len) + for(var/datum/blackmarket_purchase/P in queue) + SSblackmarket.queue_item(P) + . = ..() + +/obj/machinery/ltsrbt/RefreshParts() + recharge_time = base_recharge_time + // On tier 4 recharge_time should be 20 and by default it is 80 as scanning modules should be tier 1. + for(var/obj/item/stock_parts/scanning_module/scan in component_parts) + recharge_time -= scan.rating * 10 + recharge_cooldown = recharge_time + + power_efficiency = 0 + for(var/obj/item/stock_parts/micro_laser/laser in component_parts) + power_efficiency += laser.rating + // Shouldn't happen but you never know. + if(!power_efficiency) + power_efficiency = 1 + +/// Adds /datum/blackmarket_purchase to queue unless the machine is free, then it sets the purchase to be instantly recieved +/obj/machinery/ltsrbt/proc/add_to_queue(datum/blackmarket_purchase/purchase) + if(!recharge_cooldown && !recieving && !transmitting) + recieving = purchase + return + queue += purchase + +/obj/machinery/ltsrbt/process() + if(stat & NOPOWER) + return + + if(recharge_cooldown) + recharge_cooldown-- + return + + var/turf/T = get_turf(src) + if(recieving) + var/datum/blackmarket_purchase/P = recieving + + if(!P.item || ispath(P.item)) + P.item = P.entry.spawn_item(T) + else + var/atom/movable/M = P.item + M.forceMove(T) + + use_power(power_usage_per_teleport / power_efficiency) + var/datum/effect_system/spark_spread/sparks = new + sparks.set_up(5, 1, get_turf(src)) + sparks.attach(P.item) + sparks.start() + + recieving = null + transmitting = P + + recharge_cooldown = recharge_time + return + else if(transmitting) + var/datum/blackmarket_purchase/P = transmitting + if(!P.item) + QDEL_NULL(transmitting) + if(!(P.item in T.contents)) + QDEL_NULL(transmitting) + return + do_teleport(P.item, get_turf(P.uplink)) + use_power(power_usage_per_teleport / power_efficiency) + QDEL_NULL(transmitting) + + recharge_cooldown = recharge_time + return + + if(queue.len) + recieving = pick_n_take(queue) diff --git a/code/modules/cargo/blackmarket/blackmarket_uplink.dm b/code/modules/cargo/blackmarket/blackmarket_uplink.dm new file mode 100644 index 000000000000..b0228de2f328 --- /dev/null +++ b/code/modules/cargo/blackmarket/blackmarket_uplink.dm @@ -0,0 +1,181 @@ +/obj/item/blackmarket_uplink + name = "Black Market Uplink" + icon = 'icons/obj/blackmarket.dmi' + icon_state = "uplink" + + // UI variables. + var/ui_x = 720 + var/ui_y = 480 + var/viewing_category + var/viewing_market + var/selected_item + var/buying + + /// How much money is inserted into the uplink. + var/money = 0 + /// List of typepaths for "/datum/blackmarket_market"s that this uplink can access. + var/list/accessible_markets = list(/datum/blackmarket_market/blackmarket) + +/obj/item/blackmarket_uplink/Initialize(mapload) + . = ..() + // We don't want to go through this at mapload because the SSblackmarket isn't initialized yet. + if(mapload) + return + + update_viewing_category() + +/// Simple internal proc for updating the viewing_category variable. +/obj/item/blackmarket_uplink/proc/update_viewing_category() + if(accessible_markets.len) + viewing_market = accessible_markets[1] + var/list/categories = SSblackmarket.markets[viewing_market].categories + if(categories && categories.len) + viewing_category = categories[1] + +/obj/item/blackmarket_uplink/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/holochip) || istype(I, /obj/item/stack/spacecash) || istype(I, /obj/item/coin)) + var/worth = I.get_item_credit_value() + if(!worth) + to_chat(user, span_warning("[I] doesn't seem to be worth anything!")) + money += worth + to_chat(user, span_notice("You slot [I] into [src] and it reports a total of [money] credits inserted.")) + qdel(I) + return + . = ..() + +/obj/item/blackmarket_uplink/AltClick(mob/user) + if(!isliving(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + + var/amount_to_remove = FLOOR(input(user, "How much do you want to withdraw? Current Amount: [money]", "Withdraw Funds", 5) as num|null, 1) + if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return + + if(!amount_to_remove || amount_to_remove < 0) + return + if(amount_to_remove > money) + to_chat(user, span_warning("There is only [money] credits in [src]")) + return + + var/obj/item/holochip/holochip = new (user.drop_location(), amount_to_remove) + money -= amount_to_remove + holochip.name = "washed " + holochip.name + user.put_in_hands(holochip) + to_chat(user, span_notice("You withdraw [amount_to_remove] credits into a holochip.")) + +/obj/item/blackmarket_uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) + if(!viewing_category) + update_viewing_category() + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "BlackMarketUplink", "Black Market Uplink") + ui.open() + ui.set_autoupdate(TRUE) + +/obj/item/blackmarket_uplink/ui_data(mob/user) + var/list/data = list() + var/datum/blackmarket_market/market = viewing_market ? SSblackmarket.markets[viewing_market] : null + data["categories"] = market ? market.categories : null + data["delivery_methods"] = list() + if(market) + for(var/delivery in market.shipping) + data["delivery_methods"] += list(list("name" = delivery, "price" = market.shipping[delivery])) + data["money"] = money + data["buying"] = buying + data["items"] = list() + data["viewing_category"] = viewing_category + data["viewing_market"] = viewing_market + if(viewing_category && market) + if(market.available_items[viewing_category]) + for(var/datum/blackmarket_item/I in market.available_items[viewing_category]) + data["items"] += list(list( + "id" = I.type, + "name" = I.name, + "cost" = I.price, + "amount" = I.stock, + "desc" = I.desc || I.name + )) + return data + +/obj/item/blackmarket_uplink/ui_static_data(mob/user) + var/list/data = list() + data["delivery_method_description"] = SSblackmarket.shipping_method_descriptions + data["ltsrbt_built"] = SSblackmarket.telepads.len + data["markets"] = list() + for(var/M in accessible_markets) + var/datum/blackmarket_market/BM = SSblackmarket.markets[M] + data["markets"] += list(list( + "id" = M, + "name" = BM.name + )) + return data + +/obj/item/blackmarket_uplink/ui_act(action, params) + if(..()) + return + switch(action) + if("set_category") + if(isnull(params["category"])) + return + if(isnull(viewing_market)) + return + if(!(params["category"] in SSblackmarket.markets[viewing_market].categories)) + return + viewing_category = params["category"] + . = TRUE + if("set_market") + if(isnull(params["market"])) + return + var/market = text2path(params["market"]) + if(!(market in accessible_markets)) + return + + viewing_market = market + + var/list/categories = SSblackmarket.markets[viewing_market].categories + if(categories && categories.len) + viewing_category = categories[1] + else + viewing_category = null + . = TRUE + if("select") + if(isnull(params["item"])) + return + var/item = text2path(params["item"]) + selected_item = item + buying = TRUE + . = TRUE + if("cancel") + selected_item = null + buying = FALSE + . = TRUE + if("buy") + if(isnull(params["method"])) + return + if(isnull(selected_item)) + buying = FALSE + return + var/datum/blackmarket_market/market = SSblackmarket.markets[viewing_market] + market.purchase(selected_item, viewing_category, params["method"], src, usr) + + buying = FALSE + selected_item = null + +/datum/crafting_recipe/blackmarket_uplink + name = "Black Market Uplink" + result = /obj/item/blackmarket_uplink + time = 30 + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER, TOOL_MULTITOOL) + reqs = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/assembly/signaler = 1, + /obj/item/stack/cable_coil = 15, + /obj/item/radio = 1, + /obj/item/analyzer = 1 + ) + category = CAT_MISC + +/datum/crafting_recipe/blackmarket_uplink/New() + ..() + blacklist |= subtypesof(/obj/item/radio/) diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index c901c950016a..081af527df8c 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -3330,3 +3330,16 @@ cost = 2000 crate_name = "liquid pump crate" contains = list(/obj/structure/liquid_pump) + +/datum/supply_pack/misc/blackmarket_telepad + name = "Black Market LTSRBT" + desc = "Need a faster and better way of transporting your illegal goods from and to the station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) is here to help. Contains a LTSRBT circuit, two bluespace crystals, and one ansible." + cost = 10000 + contraband = TRUE + contains = list( + /obj/item/circuitboard/machine/ltsrbt, + /obj/item/stack/ore/bluespace_crystal/artificial, + /obj/item/stack/ore/bluespace_crystal/artificial, + /obj/item/stock_parts/subspace/ansible + ) + crate_name = "crate" diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 75a5a5a8aa27..9c4cdc881970 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -466,6 +466,10 @@ icon_state = reagents.total_volume ? base_icon_state : "[base_icon_state]_e" return ..() +/obj/item/reagent_containers/glass/bottle/thermite + name = "thermite bottle" + list_reagents = list(/datum/reagent/thermite = 30) + //Yogs: Vials /obj/item/reagent_containers/glass/bottle/vial diff --git a/icons/obj/blackmarket.dmi b/icons/obj/blackmarket.dmi new file mode 100644 index 000000000000..99f4811ea6b9 Binary files /dev/null and b/icons/obj/blackmarket.dmi differ diff --git a/tgui/packages/tgui/interfaces/BlackMarketUplink.js b/tgui/packages/tgui/interfaces/BlackMarketUplink.js index 8cfcff5756aa..449d7b2dcc6c 100644 --- a/tgui/packages/tgui/interfaces/BlackMarketUplink.js +++ b/tgui/packages/tgui/interfaces/BlackMarketUplink.js @@ -1,5 +1,5 @@ import { useBackend } from '../backend'; -import { AnimatedNumber, Box, Button, Flex, Modal, Section, Tabs } from '../components'; +import { AnimatedNumber, Box, Button, Modal, Section, Stack, Tabs } from '../components'; import { formatMoney } from '../format'; import { Window } from '../layouts'; @@ -15,7 +15,7 @@ export const BlackMarketUplink = (props, context) => { } = data; return ( @@ -42,8 +42,8 @@ export const BlackMarketUplink = (props, context) => { ))} - - + + {categories.map(category => ( { ))} - - + + {items.map(item => ( - - + + {item.name} - - + + {item.amount ? item.amount + " in stock" : "Out of stock"} - - + + {formatMoney(item.cost) + ' cr'} - - + +