": ""][highscore]")
if(highscore >= goal)
return TRUE
return FALSE
//needed for the vending part of it
/datum/data/bluespace_tap_product
var/product_name = "generic"
var/product_path = null
var/product_cost = 100 //cost in mining points to generate
/datum/data/bluespace_tap_product/New(name, path, cost)
product_name = name
product_path = path
product_cost = cost
/obj/item/circuitboard/machine/bluespace_tap
board_name = "Bluespace Harvester"
icon_state = "command"
build_path = /obj/machinery/power/bluespace_tap
origin_tech = "engineering=2;combat=2;bluespace=3"
req_components = list(
/obj/item/stock_parts/capacitor/quadratic = 5,//Probably okay, right?
/obj/item/stack/ore/bluespace_crystal = 5)
/obj/effect/spawner/lootdrop/bluespace_tap
name = "bluespace harvester reward spawner"
lootcount = 1
/obj/effect/spawner/lootdrop/bluespace_tap/hat
name = "exotic hat"
loot = list(
/obj/item/clothing/head/collectable/chef, //same weighing on all of them
/obj/item/clothing/head/collectable/paper,
/obj/item/clothing/head/collectable/tophat,
/obj/item/clothing/head/collectable/captain,
/obj/item/clothing/head/collectable/beret,
/obj/item/clothing/head/collectable/welding,
/obj/item/clothing/head/collectable/flatcap,
/obj/item/clothing/head/collectable/pirate,
/obj/item/clothing/head/collectable/kitty,
/obj/item/clothing/head/crown/fancy,
/obj/item/clothing/head/collectable/rabbitears,
/obj/item/clothing/head/collectable/wizard,
/obj/item/clothing/head/collectable/hardhat,
/obj/item/clothing/head/collectable/HoS,
/obj/item/clothing/head/collectable/thunderdome,
/obj/item/clothing/head/collectable/swat,
/obj/item/clothing/head/collectable/slime,
/obj/item/clothing/head/collectable/police,
/obj/item/clothing/head/collectable/slime,
/obj/item/clothing/head/collectable/xenom,
/obj/item/clothing/head/collectable/petehat
)
/obj/effect/spawner/lootdrop/bluespace_tap/cultural
name = "cultural artifacts"
loot = list(
/obj/vehicle/space/speedbike/red = 10,
/obj/item/grenade/clusterbuster/honk = 10,
/obj/item/toy/katana = 10,
/obj/item/stack/tile/brass/fifty = 20,
/obj/item/stack/sheet/mineral/abductor/fifty = 20,
/obj/item/sord = 20,
/obj/item/toy/syndicateballoon = 15,
/obj/item/lighter/zippo/gonzofist = 5,
/obj/item/lighter/zippo/engraved = 5,
/obj/item/lighter/zippo/nt_rep = 5,
/obj/item/gun/projectile/automatic/c20r/toy = 1,
/obj/item/gun/projectile/automatic/l6_saw/toy = 1,
/obj/item/gun/projectile/automatic/toy/pistol = 2,
/obj/item/gun/projectile/automatic/toy/pistol/enforcer = 1,
/obj/item/gun/projectile/shotgun/toy = 1,
/obj/item/gun/projectile/shotgun/toy/crossbow = 1,
/obj/item/gun/projectile/shotgun/toy/tommygun = 1,
/obj/item/gun/projectile/automatic/sniper_rifle/toy = 1,
/obj/item/dualsaber/toy = 5,
/obj/machinery/snow_machine = 10,
/obj/item/clothing/head/kitty = 5,
/obj/item/coin/antagtoken = 5,
/obj/item/toy/prizeball/figure = 15,
/obj/item/toy/prizeball/therapy = 10,
/obj/item/bedsheet/patriot = 2,
/obj/item/bedsheet/rainbow = 2,
/obj/item/bedsheet/captain = 2,
/obj/item/bedsheet/centcom = 1, //mythic rare rarity
/obj/item/bedsheet/syndie = 2,
/obj/item/bedsheet/cult = 2,
/obj/item/bedsheet/wiz = 2,
/obj/item/stack/sheet/mineral/tranquillite/fifty = 3,
/obj/item/clothing/gloves/combat = 5,
/obj/item/blank_tarot_card = 5,
/obj/item/tarot_card_pack = 5,
/obj/item/tarot_card_pack/jumbo = 3,
/obj/item/tarot_card_pack/mega = 2
)
/obj/effect/spawner/lootdrop/bluespace_tap/organic
name = "organic objects"
loot = list(
/obj/item/seeds/random/labelled = 50,
/obj/item/guardiancreator/biological = 5,
/obj/item/organ/internal/vocal_cords/adamantine = 15,
/obj/item/storage/pill_bottle/random_meds/labelled = 25,
/obj/item/reagent_containers/glass/bottle/reagent/omnizine = 15,
/obj/item/dnainjector/telemut = 5,
/obj/item/dnainjector/small_size = 5,
/obj/item/dnainjector/morph = 5,
/obj/item/dnainjector/regenerate = 5,
/mob/living/simple_animal/pet/dog/corgi/ = 5,
/mob/living/simple_animal/pet/cat = 5,
/mob/living/simple_animal/pet/dog/fox/ = 5,
/mob/living/simple_animal/pet/penguin/baby = 5,
/mob/living/simple_animal/pig = 5,
/obj/item/slimepotion/sentience = 5,
/obj/item/clothing/mask/cigarette/cigar/havana = 3,
/obj/item/stack/sheet/mineral/bananium/fifty = 2, //bananas are organic, clearly.
/obj/item/storage/box/monkeycubes = 5,
/obj/item/stack/tile/carpet/twenty = 10,
/obj/item/stack/tile/carpet/black/twenty = 10,
/obj/item/soap/deluxe = 5
)
/obj/effect/spawner/lootdrop/bluespace_tap/food
name = "fancy food"
lootcount = 3
loot = list(
/obj/item/food/wingfangchu,
/obj/item/food/hotdog,
/obj/item/food/sliceable/turkey,
/obj/item/food/plumphelmetbiscuit,
/obj/item/food/appletart,
/obj/item/food/sliceable/cheesecake,
/obj/item/food/sliceable/bananacake,
/obj/item/food/sliceable/chocolatecake,
/obj/item/food/soup/meatballsoup,
/obj/item/food/soup/mysterysoup,
/obj/item/food/soup/stew,
/obj/item/food/soup/hotchili,
/obj/item/food/burrito,
/obj/item/food/fishburger,
/obj/item/food/cubancarp,
/obj/item/food/fishandchips,
/obj/item/food/meatpie,
/obj/item/pizzabox/hawaiian, //it ONLY gives hawaiian. MUHAHAHA
/obj/item/food/sliceable/xenomeatbread //maybe add some dangerous/special food here, ie robobuger?
)
/// Points generated per cycle for each Watt of power consumption
#define POINTS_PER_W 4e-6
/// Amount of points generated per cycle per 50KW for the first 500KW
#define BASE_POINTS 2
/**
* # Bluespace Harvester
*
* A station goal that consumes enormous amounts of power to generate (mostly fluff) rewards
*
* A machine that takes power each tick, generates points based on it
* and lets you spend those points on rewards. A certain amount of points
* has to be generated for the station goal to count as completed.
*/
/obj/machinery/power/bluespace_tap
name = "Bluespace harvester"
icon = 'icons/obj/machines/bluespace_tap.dmi'
icon_state = "bluespace_tap"
base_icon_state = "bluespace_tap"
max_integrity = 300
pixel_x = -32 //shamelessly stolen from dna vault
pixel_y = -32
power_state = NO_POWER_USE // power usage is handelled manually
density = TRUE
interact_offline = TRUE
luminosity = 1
/// list of possible products
var/static/product_list = list(
new /datum/data/bluespace_tap_product("Unknown Exotic Hat", /obj/effect/spawner/lootdrop/bluespace_tap/hat, 5000),
new /datum/data/bluespace_tap_product("Unknown Snack", /obj/effect/spawner/lootdrop/bluespace_tap/food, 6000),
new /datum/data/bluespace_tap_product("Unknown Cultural Artifact", /obj/effect/spawner/lootdrop/bluespace_tap/cultural, 15000),
new /datum/data/bluespace_tap_product("Unknown Biological Artifact", /obj/effect/spawner/lootdrop/bluespace_tap/organic, 20000)
)
/// The amount of power being used for mining at the moment (Watts)
var/mining_power = 0
/// The power you WANT the machine to use for mining. It will try to match this. (Watts)
var/desired_mining_power = 0
/// Points mined this cycle
var/mined_points = 0
/// Available mining points
var/points = 0
/// The total points earned by this machine so far, for tracking station goal and highscore
var/total_points = 0
// Tweak these and active_power_consumption to balance power generation
/// Whether or not auto shutdown will engage when portals open
var/auto_shutdown = TRUE
/// Whether or not stabilizers will engage to prevent or reduce the chance of portals opening
var/stabilizers = TRUE
/// Amount of power the stabilizers consume (Watts)
var/stabilizer_power = 0
/// Whether or not mining power will be prevented from exceedingn stabilizer power
var/stabilizer_priority = TRUE
/// When event triggers this will hold references to all portals so we can fix the sprite after they're broken
var/list/active_nether_portals = list()
/// The amount of portals waiting to be spawned. Used mostly for UI and icon stuff.
var/spawning = 0
/obj/machinery/power/bluespace_tap/Initialize(mapload)
. = ..()
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/bluespace_tap()
for(var/i in 1 to 5) //five of each
component_parts += new /obj/item/stock_parts/capacitor/quadratic()
component_parts += new /obj/item/stack/ore/bluespace_crystal()
if(!powernet)
connect_to_network()
AddComponent(/datum/component/multitile, list(
list(1, 1, 1),
list(1, MACH_CENTER, 1),
list(1, 0, 1),
))
/obj/machinery/power/bluespace_tap/update_icon_state()
. = ..()
if(length(active_nether_portals))
icon_state = "cascade_tap"
return
if(get_available_power() <= 0)
icon_state = base_icon_state
else
icon_state = "[base_icon_state][get_icon_state_number()]"
/obj/machinery/power/bluespace_tap/update_overlays()
. = ..()
underlays.Cut()
if(length(active_nether_portals) || spawning)
. += "cascade"
set_light(15, 5, "#ff0000")
return
if(stat & (BROKEN|NOPOWER))
set_light(0)
else
set_light(1, 1, "#353535")
if(get_available_power())
. += "screen"
if(light)
underlays += emissive_appearance(icon, "light_mask")
/obj/machinery/power/bluespace_tap/proc/get_icon_state_number()
switch(mining_power)
if(50 KW to 3 MW)
return 1
if(3 MW to 8 MW)
return 2
if(8 MW to 11 MW)
return 3
if(11 MW to 15 MW)
return 4
if(15 MW to INFINITY)
return 5
else
return 0
/obj/machinery/power/bluespace_tap/power_change()
. = ..()
if(stat & (BROKEN|NOPOWER))
set_light(0)
else
set_light(1, 1, "#353535")
if(.)
update_icon()
/obj/machinery/power/bluespace_tap/connect_to_network()
. = ..()
if(.)
update_icon()
/obj/machinery/power/bluespace_tap/disconnect_from_network()
. = ..()
if(.)
update_icon()
/**
* Sets the desired mining power (Watts)
*
* Sets the desired mining power, that
* the machine tries to reach if there
* is enough power for it. Note that it does
* NOT change the actual mining power directly.
* Arguments:
* * t_power - The power we try to set it at, between 0 and max_level
*/
/obj/machinery/power/bluespace_tap/proc/set_power(t_power)
desired_mining_power = max(t_power, 0)
// Round down to nearest MW if above 1 MW
if(desired_mining_power > 1 MW)
desired_mining_power = desired_mining_power - (desired_mining_power % (1 MW))
/obj/machinery/power/bluespace_tap/process()
// Power we could mine with (Watts)
mining_power = get_surplus()
// Round down to the nearest MW if above a MW
if(mining_power > 1 MW)
mining_power = mining_power - mining_power % (1 MW)
// Always try to use all available power for mining if emagged
if(emagged)
desired_mining_power = mining_power
/*
* Stabilizers activate above 15MW of mining power
* Stabilizers consume up to 1MW for each 1MW of mining power, consuming less between 15 and 30MW of mining power
* If stabilizers have priority they will always consume enough power to stabilize the BSH, limiting mining
* Emagging disables stabilizers
*/
if(stabilizer_priority)
// stabilizer power is what we need to stabilize the current mining level, but no more than half the available power.
stabilizer_power = \
clamp(\
desired_mining_power - clamp(30 MW - desired_mining_power, 0, 15 MW), \
0, \
mining_power / 2) \
* (stabilizers && !emagged)
else
// stabilizer power is however much power we have left, but no more than we need to stabilize our desired mining power.
stabilizer_power = \
clamp(mining_power - desired_mining_power, \
0, \
desired_mining_power - clamp(30 MW - desired_mining_power, 0, 15 MW)) \
* (stabilizers && !emagged)
// Actual mining power is what the desired mining power we set, unless we don't have enough power to satisfty that.
mining_power = min(desired_mining_power, mining_power - stabilizer_power)
consume_direct_power(mining_power + stabilizer_power)
// 2 points per 50 KW up to 20 and 4 points per MW (or 5 when emmaged).
mined_points = min(BASE_POINTS * (mining_power / (50 KW)) , 20) + mining_power * (POINTS_PER_W + emagged / (1 MW))
points += mined_points
total_points += mined_points
update_icon()
/*
* Portal chance is 0.1% per cycle per difference of 1MW between the stabilizer and mining power
* This should translate to portals spawning every 33:20 minutes for 1 difference of 1MW
* And about every 3:20 minutes for a 10MW difference
* Emagging guarantees a chance of at least 5%
* Prob treats values less than 0 as 0.
* Emagging garuantees a chance of at least 5%.
* Prob treats values less than 0 as 0.
*/
if(prob((mining_power - clamp(30 MW - mining_power, 0, 15 MW) - stabilizer_power) / (10 MW)) + (emagged * 5))
var/area/our_area = get_area(src)
if((!spawning || !length(active_nether_portals)))
GLOB.major_announcement.Announce("Unexpected power spike during Bluespace Harvester Operation. Extra-dimensional intruder alert. Expected location: [our_area.name]. [emagged ? "DANGER: Emergency shutdown failed! Please proceed with manual shutdown." : auto_shutdown ? "Emergency shutdown initiated." : "Automatic shutdown disabled."]", "Bluespace Harvester Malfunction", 'sound/AI/harvester.ogg')
if(!emagged && auto_shutdown)
desired_mining_power = 0 //emergency shutdown unless it is disabled
// An extra portal for each 30MW above 15
start_nether_portaling(rand(1 , 3) + round(max((mining_power - 15 MW) / (30 MW) , 0)), TRUE)
/obj/machinery/power/bluespace_tap/proc/start_nether_portaling(amount, new_incursion = FALSE)
if(new_incursion)
spawning += amount
var/turf/location = locate(x + rand(-5, 5), y + rand(-5, 5), z)
var/obj/structure/spawner/nether/bluespace_tap/P = new /obj/structure/spawner/nether/bluespace_tap(location)
amount--
spawning--
active_nether_portals += P
P.linked_source_object = src
// 1 Extra mob for each 20 MW of mining power above 15MW.
P.max_mobs = 5 + max((mining_power - 15 MW) / (20 MW), 0)
update_icon()
if(amount)
addtimer(CALLBACK(src, PROC_REF(start_nether_portaling), amount), rand(3, 5) SECONDS)
/obj/machinery/power/bluespace_tap/ui_data(mob/user)
var/list/data = list()
data["desiredMiningPower"] = desired_mining_power
data["miningPower"] = mining_power
data["points"] = points
data["totalPoints"] = total_points
data["powerUse"] = mining_power + stabilizer_power
data["availablePower"] = get_surplus()
data["emagged"] = emagged
data["autoShutown"] = auto_shutdown
data["stabilizers"] = stabilizers
data["stabilizerPower"] = stabilizer_power
data["stabilizerPriority"] = stabilizer_priority
data["portaling"] = (length(active_nether_portals) || spawning)
/// A list of lists, each inner list equals a datum
var/list/listed_items = list()
for(var/key = 1 to length(product_list))
var/datum/data/bluespace_tap_product/A = product_list[key]
listed_items[++listed_items.len] = list(
"key" = key,
"name" = A.product_name,
"price" = A.product_cost)
data["product"] = listed_items
return data
/obj/machinery/power/bluespace_tap/attack_hand(mob/user)
add_fingerprint(user)
ui_interact(user)
/obj/machinery/power/bluespace_tap/attack_ghost(mob/user)
ui_interact(user)
/obj/machinery/power/bluespace_tap/attack_ai(mob/user) //this would be cool if we made unique TGUI for this
ui_interact(user)
/**
* Produces the product with the desired key and increases product cost accordingly
*/
/obj/machinery/power/bluespace_tap/proc/produce(key)
if(key <= 0 || key > length(product_list)) //invalid key
return
var/datum/data/bluespace_tap_product/A = product_list[key]
if(!A)
return
if(A.product_cost > points)
return
points -= A.product_cost
A.product_cost = round(1.2 * A.product_cost, 1)
playsound(src, 'sound/magic/blink.ogg', 50)
do_sparks(2, FALSE, src)
new A.product_path(get_turf(src))
flick_overlay_view(image(icon, src, "flash", FLY_LAYER))
//UI stuff below
/obj/machinery/power/bluespace_tap/ui_act(action, params)
if(..())
return
. = TRUE // we want to refresh in all the cases below
switch(action)
if("set")
set_power(text2num(params["set_power"]))
if("vend")//it's not really vending as producing, but eh
var/key = text2num(params["target"])
produce(key)
if("auto_shutdown")
auto_shutdown = !auto_shutdown
if("stabilizers")
stabilizers = !stabilizers
if("stabilizer_priority")
stabilizer_priority = !stabilizer_priority
/obj/machinery/power/bluespace_tap/ui_state(mob/user)
return GLOB.default_state
/obj/machinery/power/bluespace_tap/ui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "BluespaceTap", name)
ui.open()
//emaging provides slightly more points but at much greater risk
/obj/machinery/power/bluespace_tap/emag_act(mob/living/user as mob)
if(emagged)
return
emagged = TRUE
do_sparks(5, FALSE, src)
if(user)
user.visible_message("[user] disables the [src]'s safeties'.", "You disable the [src]'s safeties'.")
return TRUE
/obj/structure/spawner/nether/bluespace_tap
spawn_time = 30 SECONDS
max_mobs = 5 // Don't want them overrunning the station
max_integrity = 250
/// the BSH that spawned this portal
var/obj/machinery/power/bluespace_tap/linked_source_object
/obj/structure/spawner/nether/bluespace_tap/deconstruct(disassembled)
new /obj/item/stack/ore/bluespace_crystal(loc) //have a reward
return ..()
/obj/structure/spawner/nether/bluespace_tap/Destroy()
. = ..()
if(linked_source_object)
linked_source_object.active_nether_portals -= src
linked_source_object.update_icon()
/obj/item/paper/bluespace_tap
name = "paper- 'The Experimental NT Bluespace Harvester - Mining other universes for science and profit!'"
info = "Important Instructions!
Please follow all setup instructions to ensure proper operation.
\
1. Create a wire node with ample access to spare power. The device operates independently of APCs.
\
2. Create a machine frame as normal on the wire node, taking into account the device's dimensions (3 by 3 meters).
\
3. Insert wiring, circuit board and required components and finish construction according to NT engineering standards.
\
4. Ensure the device is connected to the proper power network and the network contains sufficient power.
\
5. Set machine to desired level. Check periodically on machine progress.
\
6. Optionally, spend earned points on fun and exciting rewards.
\
Operational Principles
\
The Bluespace Harvester is capable of accepting a nearly limitless amount of power to search other universes for valuables to recover. The speed of this search is controlled via the 'level' control of the device. \
While it can be run on a low level by almost any power generation system, higher levels require work by a dedicated engineering team to power. \
As we are interested in testing how the device performs under stress, we wish to encourage you to stress-test it and see how much power you can provide it. \
For this reason, total shift point production will be calculated and announced at shift end. High totals may result in bonus payments to members of the Engineering department.
\
NT Science Directorate, Extradimensional Exploitation Research Group
\
Device highly experimental. Not for sale. Do not operate near small children or vital NT assets. Do not tamper with machine. In case of existential dread, stop machine immediately. \
Please document any and all extradimensional incursions. In case of imminent death, please leave said documentation in plain sight for clean-up teams to recover.
"
#undef POINTS_PER_W
#undef BASE_POINTS
ER_W