diff --git a/code/WorkInProgress/Railgun.dm b/code/WorkInProgress/Railgun.dm
index 4b35ff0a..f8b4e9bf 100644
--- a/code/WorkInProgress/Railgun.dm
+++ b/code/WorkInProgress/Railgun.dm
@@ -404,8 +404,6 @@ proc/line_ReturnEfficDir(Angle)
ReturnedDir = WEST
return ReturnedDir
-
-
//This returns the tangent reciprocal of x, for use with East and West straights.
proc/tanR(x)
return (cos(x)/sin(x))
diff --git a/code/datums/mining/cloud_storage.dm b/code/datums/mining/cloud_storage.dm
new file mode 100644
index 00000000..f85ba2dd
--- /dev/null
+++ b/code/datums/mining/cloud_storage.dm
@@ -0,0 +1,245 @@
+#define ROCKBOX_STANDARD_FEE 5
+var/global/rockbox_client_fee_min = 1
+var/global/rockbox_client_fee_pct = 10
+var/global/rockbox_premium_purchased = 0
+
+/obj/machinery/ore_cloud_storage_container
+ name = "Rockbox™ Ore Cloud Storage Container"
+ desc = "This thing stores ore in \"the cloud\" for the station to use. Best not to think about it too hard."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "ore_storage_unit"
+ density = 1
+ anchored = 1
+ var/base_material_class = /obj/item/raw_material/
+ var/list/sell_price = list()
+ var/list/for_sale = list()
+ var/list/ores = list()
+ var/list/sellable_ores = list()
+ var/health = 100
+ var/broken = 0
+
+ MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
+
+ if (!O || !user)
+ return
+
+ if(!istype(user,/mob/living/))
+ boutput(user, "Only living mobs are able to use the storage container's quick-load feature.")
+ return
+
+ if (!istype(O,/obj/))
+ boutput(user, "You can't quick-load that.")
+ return
+
+ if(get_dist(O,user) > 1)
+ boutput(user, "You are too far away!")
+ return
+
+ else if (istype(O, /obj/storage/crate/) && src.accept_loading(user,1))
+ if (O:welded || O:locked)
+ boutput(user, "You cannot load from a crate that cannot open!")
+ return
+
+ user.visible_message("[user] uses [src]'s automatic loader on [O]!", "You use [src]'s automatic loader on [O].")
+ var/amtload = 0
+ for (var/obj/item/M in O.contents)
+ if (!istype(M,src.base_material_class))
+ continue
+ src.load_item(M)
+ amtload++
+ if (amtload) boutput(user, "[amtload] materials loaded from [O]!")
+ else boutput(user, "No material loaded!")
+
+ else if (istype(O, /obj/item/) && src.accept_loading(user,1))
+ user.visible_message("[user] begins quickly stuffing materials into [src]!")
+ var/staystill = user.loc
+ for(var/obj/item/M in view(1,user))
+ if (!O)
+ continue
+ if (!istype(M,O.type))
+ continue
+ if (!istype(M,src.base_material_class))
+ continue
+ if (O.loc == user)
+ continue
+ if (O in user.contents)
+ continue
+ src.load_item(M)
+ sleep(0.5)
+ if (user.loc != staystill) break
+ boutput(user, "You finish stuffing materials into [src]!")
+ src.update_ores()
+
+ else ..()
+
+ src.updateUsrDialog()
+
+ attackby(obj/item/W as obj, mob/user as mob)
+ if (istype(W, src.base_material_class) && src.accept_loading(user))
+ user.visible_message("[user] loads [W] into the [src].", "You load [W] into the [src].")
+ src.load_item(W,user)
+ src.update_ores()
+
+ else
+ src.health = max(src.health-W.force,0)
+ src.check_health()
+ ..()
+
+ proc/check_health()
+ if(!src.health)
+ src.broken = 1
+ src.icon_state = "ore_storage_unit-broken"
+ src.update_sellable()
+
+ proc/load_item(var/obj/item/O,var/mob/living/user)
+ if (!O)
+ return
+ if(O.amount == 1)
+ O.set_loc(src)
+ if (user && O)
+ user.u_equip(O)
+ O.dropped()
+ else if(O.amount>1)
+ O.set_loc(src)
+ for(O.amount,O.amount > 0, O.amount--)
+ new O.type(src)
+ if (user && O)
+ user.u_equip(O)
+ O.dropped()
+ qdel(O)
+ else
+ return // uhhhhhh
+
+
+ proc/accept_loading(var/mob/user,var/allow_silicon = 0)
+ if (!user)
+ return 0
+ if (src.stat & BROKEN || src.stat & NOPOWER)
+ return 0
+ if (!istype(user, /mob/living/))
+ return 0
+ if (istype(user, /mob/living/silicon) && !allow_silicon)
+ return 0
+ var/mob/living/L = user
+ if (L.stat || L.transforming)
+ return 0
+ return 1
+
+ proc/update_ores()
+ src.ores.len = 0
+ for(var/obj/item/raw_material/R in src.contents)
+ if(!(R.material_name in ores))
+ ores += R.material_name
+ ores[R.material_name] = 1
+
+ else
+ ores[R.material_name]++
+ src.update_sellable()
+ return
+
+ proc/update_sellable()
+ src.sellable_ores.len = 0
+ if(src.broken)
+ return
+ for(var/ore in src.ores)
+ if(ore in src.for_sale)
+ if(for_sale[ore])
+ if(!(ore in sellable_ores))
+ sellable_ores += ore
+ sellable_ores[ore] = ores[ore]
+ else
+ continue
+ else
+ continue
+
+ attack_hand(var/mob/user as mob)
+
+ var/list/ores = src.ores
+
+ user.machine = src
+ var/dat = "[src.name]"
+
+ dat += "
"
+
+ if (stat & BROKEN || stat & NOPOWER)
+ dat = "The screen is blank."
+ user << browse(dat, "window=mining_dropbox;size=400x500")
+ onclose(user, "mining_dropbox")
+ return
+
+ if(ores.len)
+ for(var/ore in ores)
+ var/sellable = 0
+ var/price = 0
+ if(src.sell_price[ore] != null)
+ price = sell_price[ore]
+ if(src.for_sale[ore] != null)
+ sellable = src.for_sale[ore]
+ dat += "[ore]: [ores[ore]] ([sellable ? "For Sale" : "Not For Sale"]) ($[price] per ore) (Eject)
"
+ else
+ dat += "No ores currently loaded.
"
+
+ user << browse(dat, "window=mining_dropbox;size=450x500")
+ onclose(user, "mining_dropbox")
+
+
+
+ Topic(href, href_list)
+
+ if(stat & BROKEN || stat & NOPOWER)
+ return
+
+ if(usr.stat || usr.restrained())
+ return
+
+ if ((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))))
+ usr.machine = src
+
+ if (href_list["eject"])
+ var/ore = href_list["eject"]
+ var/turf/ejectturf = get_turf(usr)
+
+ src.eject_ores(ore,ejectturf,0,0,usr)
+
+ if (href_list["price"])
+ var/ore = href_list["price"]
+ var/new_price = null
+ new_price = input(usr,"What price would you like to set? (Min 0)","Set Sale Price",null) as num
+ new_price = max(0,new_price)
+ if(src.sell_price[ore])
+ sell_price[ore] = new_price
+ else
+ sell_price += ore
+ sell_price[ore] = new_price
+
+ if (href_list["sellable"])
+ var/ore = href_list["sellable"]
+ if(src.for_sale[ore])
+ for_sale[ore] = !for_sale[ore]
+ else
+ for_sale += ore
+ for_sale[ore] = 1
+ update_sellable()
+
+ src.updateUsrDialog()
+ return
+
+ proc/eject_ores(var/ore, var/turf/ejectturf, var/ejectamt, var/transmit = 0, var/user as mob)
+ for(var/obj/item/raw_material/R in src.contents)
+ if (R.material_name == ore)
+ if (!ejectamt)
+ ejectamt = input(usr,"How many ores do you want to eject?","Eject Ores") as num
+ if ((ejectamt <= 0 || get_dist(src, user) > 1) && !transmit)
+ break
+ if (!ejectturf)
+ break
+ R.set_loc(ejectturf)
+ ejectamt--
+ if (ejectamt <= 0)
+ break
+ if(transmit)
+ flick("ore_storage_unit-transmit",src)
+ showswirl(ejectturf)
+ leaveresidual(ejectturf)
+
+ src.update_ores()
\ No newline at end of file
diff --git a/code/obj/machinery/computer/QM_order.dm b/code/obj/machinery/computer/QM_order.dm
index f3b8ee95..29f4ca02 100644
--- a/code/obj/machinery/computer/QM_order.dm
+++ b/code/obj/machinery/computer/QM_order.dm
@@ -35,6 +35,12 @@
account = FindBankAccountByName(src.scan.registered)
if(account)
dat += "Credits on Account: [account.fields["current_money"]] Credits
"
+
+ dat += "Rockbox™ Ore Cloud Storage Service Settings:
"
+ dat += "Rockbox™ Fees: $[!rockbox_premium_purchased ? ROCKBOX_STANDARD_FEE : 0] per ore [!rockbox_premium_purchased ? "(Purchase our Premium Service to remove this fee!)" : ""]
"
+ dat += "Client Quartermaster Transaction Fee: [rockbox_client_fee_pct]%
"
+ dat += "Client Quartermaster Transaction Fee Per Ore Minimum: $[rockbox_client_fee_min]
"
+ dat += "
"
dat += {"View Requests
Request Items
Purchase Supply Points
@@ -212,6 +218,28 @@
else if (href_list["mainmenu"])
src.temp = null
- src.add_fingerprint(usr)
+
+ else if (href_list["fee_pct"])
+ var/fee_pct = null
+ fee_pct = input(usr,"What fee percent would you like to set? (Min 0)","Fee Percent per Transaction:",null) as num
+ fee_pct = max(0,fee_pct)
+ rockbox_client_fee_pct = fee_pct
+
+ else if (href_list["fee_min"])
+ var/fee_min = null
+ fee_min = input(usr,"What fee min would you like to set? (Min 0)","Minimum Fee per Transaction in Credits:",) as num
+ fee_min = max(0,fee_min)
+ rockbox_client_fee_min = fee_min
+
+ else if (href_list["premium_service"])
+ var/response = ""
+ response = alert(usr,"Would you like to purchase the Rockbox™ Premium Service for 10000 Credits?",,"Yes","No")
+ if(response == "Yes")
+ if(wagesystem.shipping_budget >= 10000)
+ wagesystem.shipping_budget -= 10000
+ rockbox_premium_purchased = 1
+ else
+ boutput(usr,"Not enough money in the budget!")
+
src.updateUsrDialog()
return
\ No newline at end of file
diff --git a/code/obj/machinery/manufacturer.dm b/code/obj/machinery/manufacturer.dm
index 4f895a61..31886c5a 100644
--- a/code/obj/machinery/manufacturer.dm
+++ b/code/obj/machinery/manufacturer.dm
@@ -61,6 +61,8 @@
var/static/list/text_flipout_adjective = list("an awful","a terrible","a loud","a horrible","a nasty","a horrendous")
var/static/list/text_flipout_noun = list("noise","racket","ruckus","clatter","commotion","din")
var/list/text_bad_output_adjective = list("janky","crooked","warped","shoddy","shabby","lousy","crappy","shitty")
+ var/obj/item/card/id/scan = null
+ var/temp = null
#define WIRE_EXTEND 1
#define WIRE_POWER 2
@@ -276,6 +278,34 @@
dat += "
"
dat += build_material_list()
+ dat += "
"
+
+ dat +="Scanned Card: ([src.scan])
"
+ if(scan)
+ var/datum/data/record/account = null
+ account = FindBankAccountByName(src.scan.registered)
+ if (account)
+ dat+="Current Funds: [account.fields["current_money"]] Credits
"
+ dat+= src.temp
+ dat+="
"
+ else
+ dat+= src.temp
+ dat+="
"
+ else
+ dat+= src.temp
+ dat+="
"
+
+
+
+ dat += "Ores Available for Purchase:
"
+ for(var/obj/machinery/ore_cloud_storage_container/S in world)
+ var/list/ores = S.sellable_ores
+ if(ores.len)
+ dat += "[S.name] at [get_turf(S).loc.name]:
"
+ for(var/ore in ores)
+ var/taxes = round(max(rockbox_client_fee_min,abs(S.sell_price[ore]*rockbox_client_fee_pct/100)),0.01) //transaction taxes for the station budget
+ dat += "[ore]: [ores[ore]] ($[S.sell_price[ore]+taxes+(!rockbox_premium_purchased ? ROCKBOX_STANDARD_FEE : 0)]/ore) (Purchase)
"
+
dat += "
"
if (!page)
@@ -589,6 +619,91 @@
src.pulse(twire)
src.build_icon()
+ if (href_list["card"])
+ if (src.scan) src.scan = null
+ else
+ var/obj/item/I = usr.equipped()
+ if (istype(I, /obj/item/card/id))
+ boutput(usr, "You swipe the ID card in the card reader.")
+ var/datum/data/record/account = null
+ account = FindBankAccountByName(I:registered)
+ if(account)
+ var/enterpin = input(usr, "Please enter your PIN number.", "Card Reader", 0) as null|num
+ if (enterpin == I:pin)
+ boutput(usr, "Card authorized.")
+ src.scan = I
+ else
+ boutput(usr, "Pin number incorrect.")
+ src.scan = null
+ else
+ boutput(usr, "No bank account associated with this ID found.")
+ src.scan = null
+
+ if (href_list["purchase"])
+ var/obj/machinery/ore_cloud_storage_container/storage = locate(href_list["storage"])
+ var/ore = href_list["ore"]
+ var/list/ores = storage.sellable_ores
+ var/price = storage.sell_price[ore]
+ var/taxes = round(max(rockbox_client_fee_min,abs(price*rockbox_client_fee_pct/100)),0.01) //transaction taxes for the station budget
+
+ if(!scan)
+ src.temp = {"You have to scan a card in first.
"}
+ src.updateUsrDialog()
+ return
+ else
+ src.temp = null
+ if (src.scan.registered in FrozenAccounts)
+ boutput(usr, "Your account cannot currently be liquidated due to active borrows.")
+ return
+ var/datum/data/record/account = null
+ account = FindBankAccountByName(src.scan.registered)
+ if (account)
+ var/quantity = 1
+ quantity = input("How many units do you want to purchase?", "Ore Purchase", null, null) as num
+
+ ////////////
+
+ if(ores[ore] >= quantity)
+ var/subtotal = round(price * quantity)
+ var/sum_taxes = round(taxes * quantity)
+ var/rockbox_fees = (!rockbox_premium_purchased ? ROCKBOX_STANDARD_FEE : 0) * quantity
+ var/total = subtotal + sum_taxes + rockbox_fees
+ if(account.fields["current_money"] >= total)
+ account.fields["current_money"] -= total
+ storage.eject_ores(ore, get_turf(usr), quantity, transmit=1, user=usr)
+
+ // This next bit is stolen from PTL Code
+ var/list/accounts = list()
+ for(var/datum/data/record/t in data_core.bank)
+ if(t.fields["job"] == "Chief Engineer")
+ accounts += t
+ accounts += t //fuck it x2
+ else if(t.fields["job"] == "Miner")
+ accounts += t
+
+
+ //any non-divisible amounts go to the shipping budget
+ var/leftovers = 0
+ if(accounts.len)
+ leftovers = subtotal%accounts.len
+ var/divisible_amount = subtotal - leftovers
+ if(divisible_amount)
+ for(var/datum/data/record/t in accounts)
+ t.fields["current_money"] += divisible_amount/accounts.len
+ else
+ leftovers = subtotal
+ wagesystem.shipping_budget += (leftovers + sum_taxes)
+
+
+
+ src.temp = {"Enjoy your purchase!
"}
+ else
+ src.temp = {"You don't have enough dosh, bucko.
"}
+ else
+ src.temp = {"I don't have that many for sale, champ.
"}
+ else
+ src.temp = {"That card doesn't have an account anymore, you might wanna get that checked out.
"}
+
src.updateUsrDialog()
return
@@ -831,6 +946,7 @@
return
MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob)
+
if (!O || !user)
return
diff --git a/goonstation.dme b/goonstation.dme
index c49da5f1..3ef2236c 100644
--- a/goonstation.dme
+++ b/goonstation.dme
@@ -464,6 +464,7 @@ var/datum/preMapLoad/preMapLoad = new
#include "code\datums\hydroponics\plants_herb.dm"
#include "code\datums\hydroponics\plants_veg.dm"
#include "code\datums\hydroponics\plants_weed.dm"
+#include "code\datums\mining\cloud_storage.dm"
#include "code\datums\mining\mining_encounters.dm"
#include "code\datums\mining\ore.dm"
#include "code\datums\mining\tile_events.dm"
@@ -1199,7 +1200,6 @@ var/datum/preMapLoad/preMapLoad = new
#include "libs\Base64\hexadecimal.dm"
#include "libs\Base64\text.dm"
#include "maps\_disposaltester.dm"
-#include "maps\adv_test.dmm"
#include "maps\config\map.dm"
#include "maps\placeholders\map_placeholders.dm"
// END_INCLUDE
diff --git a/goonstation.int b/goonstation.int
index 1930186e..42ce1519 100644
--- a/goonstation.int
+++ b/goonstation.int
@@ -1,9 +1,12 @@
// BEGIN_INTERNALS
/*
-LAST_COMPILE_TIME: 1457219709
-DIR: code code\chui\example code\datums\buildmodes code\datums\buildmodes\adventure code\datums\buildmodes\adventure\elements code\datums\chemistry code\datums\chemistry\tools code\datums\critter_mobs\health code\datums\gamemodes code\datums\genetics
+MAP_ZOOM: 1.000
+LAST_COMPILE_TIME: 1576914694
+DIR: browserassets browserassets\images browserassets\images\genetics browserassets\images\traders code code\chui\example code\datums code\datums\buildmodes code\datums\buildmodes\adventure code\datums\buildmodes\adventure\elements code\datums\chemistry code\datums\genetics code\datums\mining code\obj code\obj\item code\obj\machinery code\WorkInProgress code\WorkInProgress\griffening code\WorkInProgress\Materials_Crafting icons icons\obj libs maps maps\config sound sound\vox
AUTO_FILE_DIR: OFF
MAP_ICON_TYPE: 0
-LAST_COMPILE_VERSION: 510.1327
+LAST_COMPILE_VERSION: 513.1501
+WINDOW: code\datums\mining\ore.dm;code\datums\mining\cloud_storage.dm;icons\obj\mining.dmi;maps\config\standard.dm;code\obj\machinery\manufacturer.dm;code\obj\item\material.dm;maps\oretest.dmm;code\WorkInProgress\Materials_Crafting\Mat_RawMaterials.dm;code\WorkInProgress\Materials_Crafting\Mat_ProcsDefines.dm;code\WorkInProgress\Materials_Crafting\Mat_Materials.dm
+FILE: code\datums\mining\cloud_storage.dm
*/
// END_INTERNALS
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index cc3c3b65..3c935174 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ