Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions code/__defines/computers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
#define NET_FEATURE_RECORDS BITFLAG(5) // Modifying accounts, viewing crew records etc.
#define NET_FEATURE_FILESYSTEM BITFLAG(6) // Accessing mainframe filesystems.
#define NET_FEATURE_DECK BITFLAG(7) // Control of docking beacons, supply, deck control.
#define NET_FEATURE_FINANCE BITFLAG(8) // Money transfers, other finance.

#define NET_ALL_FEATURES (NET_FEATURE_SOFTWAREDOWNLOAD|NET_FEATURE_COMMUNICATION|NET_FEATURE_SYSTEMCONTROL|NET_FEATURE_SECURITY|NET_FEATURE_ACCESS|NET_FEATURE_RECORDS|NET_FEATURE_FILESYSTEM|NET_FEATURE_DECK)
#define NET_ALL_FEATURES (NET_FEATURE_SOFTWAREDOWNLOAD|NET_FEATURE_COMMUNICATION|NET_FEATURE_SYSTEMCONTROL|NET_FEATURE_SECURITY|NET_FEATURE_ACCESS|NET_FEATURE_RECORDS|NET_FEATURE_FILESYSTEM|NET_FEATURE_DECK|NET_FEATURE_FINANCE)

// Transfer speeds, used when downloading/uploading a file/program.
#define NETWORK_SPEED_BASE 1/NETWORK_BASE_BROADCAST_STRENGTH // GQ/s transfer speed, multiplied by signal power
Expand Down Expand Up @@ -44,6 +45,7 @@
#define PROG_UTIL "Utility"
#define PROG_SEC "Security"
#define PROG_MONITOR "Monitoring"
#define PROG_FINANCE "Finances"

#define RECEIVER_WIRELESS 1
#define RECEIVER_STRONG_WIRELESS 2
Expand Down Expand Up @@ -74,4 +76,4 @@
#define OS_FILE_NO_WRITE -5
#define OS_HARDDRIVE_SPACE -6
#define OS_NETWORK_ERROR -7
#define OS_BAD_NAME -8
#define OS_BAD_NAME -8
1 change: 1 addition & 0 deletions code/__defines/machinery.dm
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,4 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
#define PART_SCANNER /obj/item/stock_parts/computer/scanner // One of several optional scanner attachments.
#define PART_D_SLOT /obj/item/stock_parts/computer/drive_slot // Portable drive slot.
#define PART_MSTICK /obj/item/stock_parts/computer/charge_stick_slot // Charge-slot component for transactions /w charge sticks.
#define PART_MPRINTER /obj/item/stock_parts/computer/money_printer
1 change: 1 addition & 0 deletions code/__defines/subsystem-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#define SS_PRIORITY_GHOST_IMAGES 10 // Updates ghost client images.
#define SS_PRIORITY_ZCOPY 10 // Builds appearances for Z-Mimic.
#define SS_PRIORITY_PROJECTILES 10 // Projectile processing!
#define SS_PRIORITY_MONEY_ACCOUNTS 10 // Money accounts processing.

// SS_BACKGROUND
#define SS_PRIORITY_OBJECTS 100 // processing_objects processing.
Expand Down
26 changes: 26 additions & 0 deletions code/controllers/configuration.dm
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ var/global/list/gamemode_cache = list()

var/dex_malus_brainloss_threshold = 30 //The threshold of when brainloss begins to affect dexterity.

// Economy variables
var/withdraw_period = 1 DAY
var/interest_period = 1 DAY

var/interest_mod_delay = 2 DAYS
var/withdraw_mod_delay = 3 DAYS
var/transaction_mod_delay = 2 DAYS
var/fractional_reserve_mod_delay = 3 DAYS
var/anti_tamper_mod_delay = 2 DAYS

var/static/list/protected_vars = list(
"comms_password",
"ban_comms_password",
Expand Down Expand Up @@ -905,6 +915,22 @@ var/global/list/gamemode_cache = list()
if("dexterity_malus_brainloss_threshold")
config.dex_malus_brainloss_threshold = text2num(value)

// Economy config.
if("withdraw_period")
config.withdraw_period = value DAYS
if("interest_period")
config.interest_period = value DAYS

if("interest_mod_delay")
config.interest_mod_delay = value DAYS
if("withdraw_mod_delay")
config.withdraw_mod_delay = value DAYS
if("transaction_mod_delay")
config.transaction_mod_delay = value DAYS
if("fractional_reserve_mod_delay")
config.fractional_reserve_mod_delay = value DAYS
if("anti_tamper_mod_delay")
config.anti_tamper_mod_delay = value DAYS
else
log_misc("Unknown setting in configuration: '[name]'")

Expand Down
73 changes: 73 additions & 0 deletions code/controllers/subsystems/accounts.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
SUBSYSTEM_DEF(money_accounts)
name = "Money Accounts"
wait = 5 MINUTES
priority = SS_PRIORITY_MONEY_ACCOUNTS
var/list/datum/money_account/all_accounts = list()

// Independent, globally accessible accounts not tied to a network
var/list/datum/money_account/all_glob_accounts = list()

var/list/datum/money_account/escrow/all_escrow_accounts = list()

var/list/processing_accounts

var/adjustment = 0

/datum/controller/subsystem/money_accounts/fire(resumed = FALSE)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gotta admit, I'm not all that fond of holding all account data in-game, and just looping through all of them constantly.
The way I handled them in my rework was via SQL tables and stored procedures. So DM had very little work to do.
But if you really wanna go that way I'm not gonna protest.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the benefit of that, but since this is looping relatively rarely and performing very few actions, I'm not that worried about performance. I'm also leery of having features which require a working database to function, and that probably wouldn't pass muster upstream.


if(!resumed)
processing_accounts = all_accounts.Copy()

var/current_time = REALTIMEOFDAY + adjustment
while(processing_accounts.len)
var/datum/money_account/curr_account = processing_accounts[processing_accounts.len]
processing_accounts.len--

if(length(curr_account.pending_modifications))
for(var/datum/account_modification/pending_mod in curr_account.pending_modifications)
if(pending_mod.start_time + pending_mod.mod_delay <= current_time)
pending_mod.modify_account()

if(istype(curr_account, /datum/money_account/child))
var/datum/money_account/child/curr_child = curr_account

if(curr_child.withdrawal_limit && (curr_child.last_withdraw_period + config.withdraw_period <= current_time))
curr_child.current_withdrawal = 0
curr_child.last_withdraw_period = current_time

if(curr_child.interest_rate && (curr_child.last_interest_period + config.interest_period <= current_time))
curr_child.accrue_interest()
curr_child.last_interest_period = current_time

if(MC_TICK_CHECK)
return

/datum/controller/subsystem/money_accounts/proc/accel_day()
adjustment += 1 DAY

/datum/controller/subsystem/money_accounts/proc/decel_day()
adjustment -= 1 DAY

/datum/controller/subsystem/money_accounts/proc/get_or_add_escrow(account_id, account_pin, account_provider)
var/datum/money_account/escrow/existing = get_escrow(account_id, account_pin, account_provider)
if(existing)
return existing

existing = new()
existing.account_id = account_id
existing.remote_access_pin = account_pin
existing.owner_name = account_provider

all_escrow_accounts |= existing
return existing

/datum/controller/subsystem/money_accounts/proc/get_escrow(account_id, account_pin, account_provider)
for(var/datum/money_account/escrow/e_account in all_escrow_accounts)
if((e_account.account_id == account_id) && e_account.remote_access_pin == account_pin)
if(!account_provider || e_account.owner_name == account_provider)
return e_account

/datum/controller/subsystem/money_accounts/proc/get_escrow_provider_ids()
. = list()
for(var/datum/money_account/escrow/e_account in all_escrow_accounts)
. |= e_account.owner_name
2 changes: 1 addition & 1 deletion code/controllers/subsystems/jobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ SUBSYSTEM_DEF(jobs)
var/datum/money_account/department_account = department_accounts[job.primary_department]

if(department_account)
remembered_info += "<b>Your department's account number is:</b> #[department_account.account_number]<br>"
remembered_info += "<b>Your department's account number is:</b> #[department_account.account_id]<br>"
remembered_info += "<b>Your department's account pin is:</b> [department_account.remote_access_pin]<br>"
remembered_info += "<b>Your department's account funds are:</b> [department_account.format_value_by_currency(department_account.money)]<br>"

Expand Down
8 changes: 4 additions & 4 deletions code/controllers/subsystems/ticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,10 @@ Helpers
if(dronecount)
to_world("<b>There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance drone\s at the end of this round.</b>")

if(all_money_accounts.len)
var/datum/money_account/max_profit = all_money_accounts[1]
var/datum/money_account/max_loss = all_money_accounts[1]
for(var/datum/money_account/D in all_money_accounts)
if(SSmoney_accounts.all_glob_accounts.len)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally use length() instead of len.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be fixed, as other instances.

var/datum/money_account/max_profit = SSmoney_accounts.all_glob_accounts[1]
var/datum/money_account/max_loss = SSmoney_accounts.all_glob_accounts[1]
for(var/datum/money_account/D in SSmoney_accounts.all_glob_accounts)
if(D == vendor_account) //yes we know you get lots of money
continue
var/saldo = D.get_balance()
Expand Down
2 changes: 1 addition & 1 deletion code/datums/outfits/jobs/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
return
if(H.mind)
if(H.mind.initial_account)
C.associated_account_number = H.mind.initial_account.account_number
C.associated_account_id = H.mind.initial_account.account_id
return C
8 changes: 4 additions & 4 deletions code/game/jobs/job/_job.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/datum/job
var/title // The name of the job
var/title // The name of the job
var/list/software_on_spawn = list() // Defines the software files that spawn on tablets and labtops
var/list/department_types = list() // What departments the job is in.
var/autoset_department = TRUE // If department list is empty, use map default.
Expand Down Expand Up @@ -150,14 +150,14 @@
return // You are too poor for an account.

//give them an account in the station database
var/datum/money_account/M = create_account("[H.real_name]'s account", H.real_name, money_amount)
var/datum/money_account/M = create_glob_account("[H.real_name]'s account", H.real_name, money_amount)
var/cash_on_hand = create_cash_on_hand(H, M)
// Store their financial info.
if(H.mind)
var/remembered_info = ""
remembered_info += "<b>Your account number is:</b> #[M.account_number]<br>"
remembered_info += "<b>Your account number is:</b> #[M.account_id]<br>"
remembered_info += "<b>Your account pin is:</b> [M.remote_access_pin]<br>"
remembered_info += "<b>Your account funds are:</b> [M.format_value_by_currency(M.money)]<br>"
remembered_info += "<b>Your account funds are:</b> [M.format_value_by_currency(M.get_balance())]<br>"
if(M.transaction_log.len)
var/datum/transaction/T = M.transaction_log[1]
remembered_info += "<b>Your account was created:</b> [T.time], [T.date] at [T.get_source_name()]<br>"
Expand Down
84 changes: 84 additions & 0 deletions code/game/machinery/_machines_base/stock_parts/money_printer.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/obj/item/stock_parts/computer/money_printer
name = "cryptographic micro-printer"
desc = "A micro-printer capable of scanning, recycling, and printing cryptographically secured bank notes on ultra thin plastic."
icon_state = "printer"
material = /decl/material/solid/plastic
matter = list(
/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT,
/decl/material/solid/silicon = MATTER_AMOUNT_REINFORCEMENT
)
max_health = ITEM_HEALTH_NO_DAMAGE
external_slot = TRUE

var/stored_plastic = 0 // This could be capped, but it'd place an annoying limit on the amount of money you can insert at a given time.

/obj/item/stock_parts/computer/money_printer/examine(mob/user)
. = ..()
if(Adjacent(user))
to_chat(user, "It has [round(stored_plastic) / SHEET_MATERIAL_AMOUNT] sheets of plastic in storage.")

/obj/item/stock_parts/computer/money_printer/attackby(obj/item/W, mob/user)
. = ..()
if(istype(W, /obj/item/stack/material) && W.get_material_type() == /decl/material/solid/plastic)
var/obj/item/stack/material/stack = W

stored_plastic += SHEET_MATERIAL_AMOUNT * stack.amount
to_chat(user, "You insert the plastic into \the [src]'s storage.")
user.drop_from_inventory(stack)
qdel(stack)

if(IS_SCREWDRIVER(W) && !istype(loc, /obj/machinery))
to_chat(user, "You pry out the plastic reserves of \the [src].")
SSmaterials.create_object(/decl/material/solid/plastic, get_turf(src), round(stored_plastic / SHEET_MATERIAL_AMOUNT))
stored_plastic = 0

if(istype(W, /obj/item/cash))
if(!loc)
return
var/datum/extension/interactive/os/current_os = get_extension(loc, /datum/extension/interactive/os)
if(!current_os)
to_chat(user, SPAN_WARNING("\The [src] must be installed before it can be used!"))
return
var/obj/item/cash/receiving = W
var/decl/currency/receiving_currency = GET_DECL(receiving.currency)
if(receiving_currency.material != /decl/material/solid/plastic)
to_chat(user, SPAN_WARNING("\The [src] cannot accept cash of this currency!"))
return

var/amount_taken = current_os.process_cash(receiving, usr)
if(!amount_taken)
return
else
playsound(get_turf(src), pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, 1)
to_chat(usr, SPAN_NOTICE("You insert [FLOOR(amount_taken / receiving_currency.absolute_value)] [receiving_currency.name] into \the [src]."))
receiving.adjust_worth(-amount_taken)

stored_plastic += amount_taken*max(1, round(SHEET_MATERIAL_AMOUNT/10))

return

/obj/item/stock_parts/computer/money_printer/proc/can_print(amount, currency_type)
// TODO: Support for non-plastic currencies
var/decl/currency/printed_currency = GET_DECL(currency_type)
if(printed_currency.material != /decl/material/solid/plastic)
return FALSE
return (stored_plastic >= amount*max(1, round(SHEET_MATERIAL_AMOUNT/10)))

// BRRRR
/obj/item/stock_parts/computer/money_printer/proc/print_money(amount, currency_type, mob/user)
if(!can_print(amount, currency_type))
return FALSE

stored_plastic -= amount*max(1, round(SHEET_MATERIAL_AMOUNT/10))

var/obj/item/cash/cash = new(get_turf(src))
cash.set_currency(currency_type)
cash.adjust_worth(amount)

if(user)
user.put_in_hands(cash)

return TRUE

/obj/item/stock_parts/computer/money_printer/filled
stored_plastic = 50*SHEET_MATERIAL_AMOUNT
2 changes: 1 addition & 1 deletion code/game/objects/items/weapons/cards_ids.dm
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ var/global/const/NO_EMAG_ACT = -50
slot_flags = SLOT_ID
var/list/access = list()
var/registered_name = "Unknown" // The name registered_name on the card
var/associated_account_number = 0
var/associated_account_id = 0

// Associated network account. For normal IDs this is simply informational, but for network enabled IDs this is used for group-based access.
var/list/associated_network_account = list("login" = "", "password" = "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,17 @@
/obj/item/stock_parts/scanning_module = 1,
/obj/item/stock_parts/subspace/transmitter = 1,
/obj/item/stock_parts/micro_laser/high = 1
)
)

/obj/item/stock_parts/circuitboard/banking_mainframe
name = "circuitboard (banking mainframe)"
build_path = /obj/machinery/network/bank
origin_tech = "{'programming':4,'magnets':3}"
req_components = list(
/obj/item/stock_parts/capacitor = 1,
/obj/item/stock_parts/scanning_module = 2
)

additional_spawn_components = list(
/obj/item/stock_parts/power/apc/buildable = 1
)
Loading