mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
[MIRROR] Convert ATM to TGUI (#9212)
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com> Co-authored-by: CHOMPStation2 <chompsation2@gmail.com>
This commit is contained in:
@@ -123,147 +123,176 @@ log transactions
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/atm/attack_hand(mob/user as mob)
|
||||
if(istype(user, /mob/living/silicon))
|
||||
to_chat (user, span_warning("A firewall prevents you from interfacing with this device!"))
|
||||
return
|
||||
if(get_dist(src,user) <= 1)
|
||||
/obj/machinery/atm/tgui_status(mob/user)
|
||||
. = ..()
|
||||
if(issilicon(user))
|
||||
return STATUS_CLOSE
|
||||
|
||||
//js replicated from obj/machinery/computer/card
|
||||
var/dat = "<h1>Automatic Teller Machine</h1>"
|
||||
dat += "For all your monetary needs!<br>"
|
||||
dat += "<i>This terminal is</i> [machine_id]. <i>Report this code when contacting IT Support</i><br/>"
|
||||
/obj/machinery/atm/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, custom_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "AutomatedTellerMachine", name)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/atm/tgui_static_data(mob/user)
|
||||
var/list/data = ..()
|
||||
data["machine_id"] = machine_id
|
||||
return data
|
||||
|
||||
/obj/machinery/atm/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
|
||||
var/list/data = ..()
|
||||
|
||||
data["emagged"] = emagged
|
||||
if(emagged > 0)
|
||||
dat += "Card: <span style='color: red;'>LOCKED</span><br><br><span style='color: red;'>Unauthorized terminal access detected! This ATM has been locked. Please contact IT Support.</span>"
|
||||
else
|
||||
dat += "Card: <a href='?src=\ref[src];choice=insert_card'>[held_card ? held_card.name : "------"]</a><br><br>"
|
||||
return data
|
||||
|
||||
data["held_card"] = held_card
|
||||
data["locked_down"] = ticks_left_locked_down
|
||||
if(ticks_left_locked_down > 0)
|
||||
dat += span_warning("Maximum number of pin attempts exceeded! Access to this ATM has been temporarily disabled.")
|
||||
else if(authenticated_account)
|
||||
if(authenticated_account.suspended)
|
||||
dat += span_red(span_bold("Access to this account has been suspended, and the funds within frozen."))
|
||||
else
|
||||
switch(view_screen)
|
||||
if(CHANGE_SECURITY_LEVEL)
|
||||
dat += "Select a new security level for this account:<br><hr>"
|
||||
var/text = "Zero - Either the account number or card is required to access this account. EFTPOS transactions will require a card and ask for a pin, but not verify the pin is correct."
|
||||
if(authenticated_account.security_level != 0)
|
||||
text = "<A href='?src=\ref[src];choice=change_security_level;new_security_level=0'>[text]</a>"
|
||||
dat += "[text]<hr>"
|
||||
text = "One - An account number and pin must be manually entered to access this account and process transactions."
|
||||
if(authenticated_account.security_level != 1)
|
||||
text = "<A href='?src=\ref[src];choice=change_security_level;new_security_level=1'>[text]</a>"
|
||||
dat += "[text]<hr>"
|
||||
text = "Two - In addition to account number and pin, a card is required to access this account and process transactions."
|
||||
if(authenticated_account.security_level != 2)
|
||||
text = "<A href='?src=\ref[src];choice=change_security_level;new_security_level=2'>[text]</a>"
|
||||
dat += "[text]<hr><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=0'>Back</a>"
|
||||
if(VIEW_TRANSACTION_LOGS)
|
||||
dat += "<b>Transaction logs</b><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=0'>Back</a>"
|
||||
dat += "<table border=1 style='width:100%'>"
|
||||
dat += "<tr>"
|
||||
dat += "<td><b>Date</b></td>"
|
||||
dat += "<td><b>Time</b></td>"
|
||||
dat += "<td><b>Target</b></td>"
|
||||
dat += "<td><b>Purpose</b></td>"
|
||||
dat += "<td><b>Value</b></td>"
|
||||
dat += "<td><b>Source terminal ID</b></td>"
|
||||
dat += "</tr>"
|
||||
for(var/datum/transaction/T in authenticated_account.transaction_log)
|
||||
dat += "<tr>"
|
||||
dat += "<td>[T.date]</td>"
|
||||
dat += "<td>[T.time]</td>"
|
||||
dat += "<td>[T.target_name]</td>"
|
||||
dat += "<td>[T.purpose]</td>"
|
||||
dat += "<td>$[T.amount]</td>"
|
||||
dat += "<td>[T.source_terminal]</td>"
|
||||
dat += "</tr>"
|
||||
dat += "</table>"
|
||||
dat += "<A href='?src=\ref[src];choice=print_transaction'>Print</a><br>"
|
||||
if(TRANSFER_FUNDS)
|
||||
dat += "<b>Account balance:</b> $[authenticated_account.money]<br>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=0'>Back</a><br><br>"
|
||||
dat += "<form name='transfer' action='?src=\ref[src]' method='get'>"
|
||||
dat += "<input type='hidden' name='src' value='\ref[src]'>"
|
||||
dat += "<input type='hidden' name='choice' value='transfer'>"
|
||||
dat += "Target account number: <input type='text' name='target_acc_number' value='' style='width:200px; background-color:white;'><br>"
|
||||
dat += "Funds to transfer: <input type='text' name='funds_amount' value='' style='width:200px; background-color:white;'><br>"
|
||||
dat += "Transaction purpose: <input type='text' name='purpose' value='Funds transfer' style='width:200px; background-color:white;'><br>"
|
||||
dat += "<input type='submit' value='Transfer funds'><br>"
|
||||
dat += "</form>"
|
||||
else
|
||||
dat += "Welcome, <b>[authenticated_account.owner_name].</b><br/>"
|
||||
dat += "<b>Account balance:</b> $[authenticated_account.money]"
|
||||
dat += "<form name='withdrawal' action='?src=\ref[src]' method='get'>"
|
||||
dat += "<input type='hidden' name='src' value='\ref[src]'>"
|
||||
dat += "<input type='radio' name='choice' value='withdrawal' checked> Cash <input type='radio' name='choice' value='e_withdrawal'> Chargecard<br>"
|
||||
dat += "<input type='text' name='funds_amount' value='' style='width:200px; background-color:white;'><input type='submit' value='Withdraw'>"
|
||||
dat += "</form>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=1'>Change account security level</a><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=2'>Make transfer</a><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=view_screen;view_screen=3'>View transaction log</a><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=balance_statement'>Print balance statement</a><br>"
|
||||
dat += "<A href='?src=\ref[src];choice=logout'>Logout</a><br>"
|
||||
else
|
||||
dat += "<form name='atm_auth' action='?src=\ref[src]' method='get'>"
|
||||
dat += "<input type='hidden' name='src' value='\ref[src]'>"
|
||||
dat += "<input type='hidden' name='choice' value='attempt_auth'>"
|
||||
dat += "<b>Account:</b> <input type='text' id='account_num' name='account_num' style='width:250px; background-color:white;'><br>"
|
||||
dat += "<b>PIN:</b> <input type='text' id='account_pin' name='account_pin' style='width:250px; background-color:white;'><br>"
|
||||
dat += "<input type='submit' value='Submit'><br>"
|
||||
dat += "</form>"
|
||||
return data
|
||||
|
||||
user << browse(dat,"window=atm;size=550x650")
|
||||
else
|
||||
user << browse(null,"window=atm")
|
||||
|
||||
/obj/machinery/atm/Topic(var/href, var/href_list)
|
||||
if(href_list["choice"])
|
||||
switch(href_list["choice"])
|
||||
if("transfer")
|
||||
data["authenticated_account"] = null
|
||||
data["suspended"] = FALSE
|
||||
if(authenticated_account)
|
||||
var/transfer_amount = text2num(href_list["funds_amount"])
|
||||
transfer_amount = round(transfer_amount, 0.01)
|
||||
if(transfer_amount <= 0)
|
||||
tgui_alert_async(usr, "That is not a valid amount.")
|
||||
else if(transfer_amount <= authenticated_account.money)
|
||||
var/target_account_number = text2num(href_list["target_acc_number"])
|
||||
var/transfer_purpose = href_list["purpose"]
|
||||
if(charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount))
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='info'>Funds transfer successful.</span>")
|
||||
authenticated_account.money -= transfer_amount
|
||||
if(authenticated_account.suspended)
|
||||
data["suspended"] = TRUE
|
||||
return data
|
||||
|
||||
//create an entry in the account transaction log
|
||||
var/datum/transaction/T = new()
|
||||
T.target_name = "Account #[target_account_number]"
|
||||
T.purpose = transfer_purpose
|
||||
T.source_terminal = machine_id
|
||||
T.date = current_date_string
|
||||
T.time = stationtime2text()
|
||||
T.amount = "([transfer_amount])"
|
||||
authenticated_account.transaction_log.Add(T)
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>Funds transfer failed.</span>")
|
||||
var/list/transactions = list()
|
||||
for(var/datum/transaction/T as anything in authenticated_account.transaction_log)
|
||||
UNTYPED_LIST_ADD(transactions, list(
|
||||
"date" = T.date,
|
||||
"time" = T.time,
|
||||
"target_name" = T.target_name,
|
||||
"purpose" = T.purpose,
|
||||
"amount" = T.amount,
|
||||
"source_terminal" = T.source_terminal
|
||||
))
|
||||
|
||||
data["authenticated_account"] = list(
|
||||
"owner_name" = authenticated_account.owner_name,
|
||||
"money" = authenticated_account.money,
|
||||
"security_level" = authenticated_account.security_level,
|
||||
"transactions" = transactions,
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/atm/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
// This is also a logout
|
||||
if("insert_card")
|
||||
if(held_card)
|
||||
release_held_id(usr)
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>You don't have enough funds to do that!</span>")
|
||||
if("view_screen")
|
||||
view_screen = text2num(href_list["view_screen"])
|
||||
if(emagged > 0)
|
||||
to_chat(usr, span_red("[icon2html(src, usr.client)] The ATM card reader rejected your ID because this machine has been sabotaged!"))
|
||||
else
|
||||
var/obj/item/I = usr.get_active_hand()
|
||||
if(istype(I, /obj/item/card/id))
|
||||
usr.drop_item(src)
|
||||
held_card = I
|
||||
. = TRUE
|
||||
|
||||
if("logout")
|
||||
if(held_card)
|
||||
release_held_id(usr)
|
||||
authenticated_account = null
|
||||
. = TRUE
|
||||
|
||||
// Balance statement
|
||||
if("balance_statement")
|
||||
if(!authenticated_account)
|
||||
return
|
||||
|
||||
var/obj/item/paper/R = new(loc)
|
||||
R.name = "Account balance: [authenticated_account.owner_name]"
|
||||
R.info = "<b>NT Automated Teller Account Statement</b><br><br>"
|
||||
R.info += "<i>Account holder:</i> [authenticated_account.owner_name]<br>"
|
||||
R.info += "<i>Account number:</i> [authenticated_account.account_number]<br>"
|
||||
R.info += "<i>Balance:</i> $[authenticated_account.money]<br>"
|
||||
R.info += "<i>Date and time:</i> [stationtime2text()], [current_date_string]<br><br>"
|
||||
R.info += "<i>Service terminal ID:</i> [machine_id]<br>"
|
||||
|
||||
//stamp the paper
|
||||
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
|
||||
stampoverlay.icon_state = "paper_stamp-cent"
|
||||
if(!R.stamped)
|
||||
R.stamped = new
|
||||
R.stamped += /obj/item/stamp
|
||||
R.add_overlay(stampoverlay)
|
||||
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
|
||||
|
||||
if(prob(50))
|
||||
playsound(src, 'sound/items/polaroid1.ogg', 50, 1)
|
||||
else
|
||||
playsound(src, 'sound/items/polaroid2.ogg', 50, 1)
|
||||
. = TRUE
|
||||
|
||||
// Transaction logs
|
||||
if("print_transaction")
|
||||
if(!authenticated_account)
|
||||
return
|
||||
|
||||
var/obj/item/paper/R = new(loc)
|
||||
R.name = "Transaction logs: [authenticated_account.owner_name]"
|
||||
R.info = "<b>Transaction logs</b><br>"
|
||||
R.info += "<i>Account holder:</i> [authenticated_account.owner_name]<br>"
|
||||
R.info += "<i>Account number:</i> [authenticated_account.account_number]<br>"
|
||||
R.info += "<i>Date and time:</i> [stationtime2text()], [current_date_string]<br><br>"
|
||||
R.info += "<i>Service terminal ID:</i> [machine_id]<br>"
|
||||
R.info += "<table border=1 style='width:100%'>"
|
||||
R.info += "<tr>"
|
||||
R.info += "<td><b>Date</b></td>"
|
||||
R.info += "<td><b>Time</b></td>"
|
||||
R.info += "<td><b>Target</b></td>"
|
||||
R.info += "<td><b>Purpose</b></td>"
|
||||
R.info += "<td><b>Value</b></td>"
|
||||
R.info += "<td><b>Source terminal ID</b></td>"
|
||||
R.info += "</tr>"
|
||||
for(var/datum/transaction/T in authenticated_account.transaction_log)
|
||||
R.info += "<tr>"
|
||||
R.info += "<td>[T.date]</td>"
|
||||
R.info += "<td>[T.time]</td>"
|
||||
R.info += "<td>[T.target_name]</td>"
|
||||
R.info += "<td>[T.purpose]</td>"
|
||||
R.info += "<td>$[T.amount]</td>"
|
||||
R.info += "<td>[T.source_terminal]</td>"
|
||||
R.info += "</tr>"
|
||||
R.info += "</table>"
|
||||
|
||||
//stamp the paper
|
||||
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
|
||||
stampoverlay.icon_state = "paper_stamp-cent"
|
||||
if(!R.stamped)
|
||||
R.stamped = new
|
||||
R.stamped += /obj/item/stamp
|
||||
R.add_overlay(stampoverlay)
|
||||
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
|
||||
|
||||
if(prob(50))
|
||||
playsound(src, 'sound/items/polaroid1.ogg', 50, 1)
|
||||
else
|
||||
playsound(src, 'sound/items/polaroid2.ogg', 50, 1)
|
||||
. = TRUE
|
||||
|
||||
if("change_security_level")
|
||||
if(authenticated_account)
|
||||
var/new_sec_level = max( min(text2num(href_list["new_security_level"]), 2), 0)
|
||||
var/new_sec_level = clamp(text2num(params["new_security_level"]), 0, 2)
|
||||
authenticated_account.security_level = new_sec_level
|
||||
. = TRUE
|
||||
|
||||
if("attempt_auth")
|
||||
if(!ticks_left_locked_down)
|
||||
var/tried_account_num = held_card ? held_card.associated_account_number : text2num(href_list["account_num"])
|
||||
var/tried_pin = text2num(href_list["account_pin"])
|
||||
if(ticks_left_locked_down)
|
||||
return
|
||||
var/tried_account_num = held_card ? held_card.associated_account_number : text2num(params["account_num"])
|
||||
var/tried_pin = text2num(params["account_pin"])
|
||||
|
||||
// check if they have low security enabled
|
||||
|
||||
if(!tried_account_num)
|
||||
scan_user(usr)
|
||||
else
|
||||
@@ -311,12 +340,48 @@ log transactions
|
||||
to_chat(usr, span_blue("[icon2html(src, usr.client)] Access granted. Welcome user '[authenticated_account.owner_name].'"))
|
||||
|
||||
previous_account_number = tried_account_num
|
||||
. = TRUE
|
||||
|
||||
if("transfer")
|
||||
if(!authenticated_account)
|
||||
return
|
||||
var/transfer_amount = text2num(params["funds_amount"])
|
||||
transfer_amount = round(transfer_amount, 0.01)
|
||||
if(transfer_amount <= 0)
|
||||
tgui_alert_async(usr, "That is not a valid amount.")
|
||||
else if(transfer_amount <= authenticated_account.money)
|
||||
var/target_account_number = text2num(params["target_acc_number"])
|
||||
var/transfer_purpose = params["purpose"]
|
||||
if(charge_to_account(target_account_number, authenticated_account.owner_name, transfer_purpose, machine_id, transfer_amount))
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='info'>Funds transfer successful.</span>")
|
||||
authenticated_account.money -= transfer_amount
|
||||
|
||||
//create an entry in the account transaction log
|
||||
var/datum/transaction/T = new()
|
||||
T.target_name = "Account #[target_account_number]"
|
||||
T.purpose = transfer_purpose
|
||||
T.source_terminal = machine_id
|
||||
T.date = current_date_string
|
||||
T.time = stationtime2text()
|
||||
T.amount = "([transfer_amount])"
|
||||
authenticated_account.transaction_log.Add(T)
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>Funds transfer failed.</span>")
|
||||
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>You don't have enough funds to do that!</span>")
|
||||
. = TRUE
|
||||
|
||||
if("e_withdrawal")
|
||||
var/amount = max(text2num(href_list["funds_amount"]),0)
|
||||
var/amount = max(text2num(params["funds_amount"]),0)
|
||||
amount = round(amount, 0.01)
|
||||
if(amount <= 0)
|
||||
tgui_alert_async(usr, "That is not a valid amount.")
|
||||
else if(authenticated_account && amount > 0)
|
||||
return
|
||||
|
||||
if(!authenticated_account)
|
||||
return
|
||||
|
||||
if(amount <= authenticated_account.money)
|
||||
playsound(src, 'sound/machines/chime.ogg', 50, 1)
|
||||
|
||||
@@ -337,12 +402,18 @@ log transactions
|
||||
authenticated_account.transaction_log.Add(T)
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>You don't have enough funds to do that!</span>")
|
||||
. = TRUE
|
||||
|
||||
if("withdrawal")
|
||||
var/amount = max(text2num(href_list["funds_amount"]),0)
|
||||
var/amount = max(text2num(params["funds_amount"]),0)
|
||||
amount = round(amount, 0.01)
|
||||
if(amount <= 0)
|
||||
tgui_alert_async(usr, "That is not a valid amount.")
|
||||
else if(authenticated_account && amount > 0)
|
||||
return
|
||||
|
||||
if(!authenticated_account)
|
||||
return
|
||||
|
||||
if(amount <= authenticated_account.money)
|
||||
playsound(src, 'sound/machines/chime.ogg', 50, 1)
|
||||
|
||||
@@ -362,91 +433,17 @@ log transactions
|
||||
authenticated_account.transaction_log.Add(T)
|
||||
else
|
||||
to_chat(usr, "[icon2html(src, usr.client)]<span class='warning'>You don't have enough funds to do that!</span>")
|
||||
if("balance_statement")
|
||||
if(authenticated_account)
|
||||
var/obj/item/paper/R = new(src.loc)
|
||||
R.name = "Account balance: [authenticated_account.owner_name]"
|
||||
R.info = "<b>NT Automated Teller Account Statement</b><br><br>"
|
||||
R.info += "<i>Account holder:</i> [authenticated_account.owner_name]<br>"
|
||||
R.info += "<i>Account number:</i> [authenticated_account.account_number]<br>"
|
||||
R.info += "<i>Balance:</i> $[authenticated_account.money]<br>"
|
||||
R.info += "<i>Date and time:</i> [stationtime2text()], [current_date_string]<br><br>"
|
||||
R.info += "<i>Service terminal ID:</i> [machine_id]<br>"
|
||||
. = TRUE
|
||||
|
||||
//stamp the paper
|
||||
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
|
||||
stampoverlay.icon_state = "paper_stamp-cent"
|
||||
if(!R.stamped)
|
||||
R.stamped = new
|
||||
R.stamped += /obj/item/stamp
|
||||
R.add_overlay(stampoverlay)
|
||||
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
|
||||
if(.)
|
||||
playsound(src, "keyboard", 50, TRUE)
|
||||
|
||||
if(prob(50))
|
||||
playsound(src, 'sound/items/polaroid1.ogg', 50, 1)
|
||||
else
|
||||
playsound(src, 'sound/items/polaroid2.ogg', 50, 1)
|
||||
if ("print_transaction")
|
||||
if(authenticated_account)
|
||||
var/obj/item/paper/R = new(src.loc)
|
||||
R.name = "Transaction logs: [authenticated_account.owner_name]"
|
||||
R.info = "<b>Transaction logs</b><br>"
|
||||
R.info += "<i>Account holder:</i> [authenticated_account.owner_name]<br>"
|
||||
R.info += "<i>Account number:</i> [authenticated_account.account_number]<br>"
|
||||
R.info += "<i>Date and time:</i> [stationtime2text()], [current_date_string]<br><br>"
|
||||
R.info += "<i>Service terminal ID:</i> [machine_id]<br>"
|
||||
R.info += "<table border=1 style='width:100%'>"
|
||||
R.info += "<tr>"
|
||||
R.info += "<td><b>Date</b></td>"
|
||||
R.info += "<td><b>Time</b></td>"
|
||||
R.info += "<td><b>Target</b></td>"
|
||||
R.info += "<td><b>Purpose</b></td>"
|
||||
R.info += "<td><b>Value</b></td>"
|
||||
R.info += "<td><b>Source terminal ID</b></td>"
|
||||
R.info += "</tr>"
|
||||
for(var/datum/transaction/T in authenticated_account.transaction_log)
|
||||
R.info += "<tr>"
|
||||
R.info += "<td>[T.date]</td>"
|
||||
R.info += "<td>[T.time]</td>"
|
||||
R.info += "<td>[T.target_name]</td>"
|
||||
R.info += "<td>[T.purpose]</td>"
|
||||
R.info += "<td>$[T.amount]</td>"
|
||||
R.info += "<td>[T.source_terminal]</td>"
|
||||
R.info += "</tr>"
|
||||
R.info += "</table>"
|
||||
|
||||
//stamp the paper
|
||||
var/image/stampoverlay = image('icons/obj/bureaucracy.dmi')
|
||||
stampoverlay.icon_state = "paper_stamp-cent"
|
||||
if(!R.stamped)
|
||||
R.stamped = new
|
||||
R.stamped += /obj/item/stamp
|
||||
R.add_overlay(stampoverlay)
|
||||
R.stamps += "<HR><i>This paper has been stamped by the Automatic Teller Machine.</i>"
|
||||
|
||||
if(prob(50))
|
||||
playsound(src, 'sound/items/polaroid1.ogg', 50, 1)
|
||||
else
|
||||
playsound(src, 'sound/items/polaroid2.ogg', 50, 1)
|
||||
|
||||
if("insert_card")
|
||||
if(!held_card)
|
||||
//this might happen if the user had the browser window open when somebody emagged it
|
||||
if(emagged > 0)
|
||||
to_chat(usr, span_red("[icon2html(src, usr.client)] The ATM card reader rejected your ID because this machine has been sabotaged!"))
|
||||
else
|
||||
var/obj/item/I = usr.get_active_hand()
|
||||
if (istype(I, /obj/item/card/id))
|
||||
usr.drop_item()
|
||||
I.loc = src
|
||||
held_card = I
|
||||
else
|
||||
release_held_id(usr)
|
||||
if("logout")
|
||||
authenticated_account = null
|
||||
//usr << browse(null,"window=atm")
|
||||
|
||||
src.attack_hand(usr)
|
||||
/obj/machinery/atm/attack_hand(mob/user as mob)
|
||||
if(istype(user, /mob/living/silicon))
|
||||
to_chat (user, span_warning("A firewall prevents you from interfacing with this device!"))
|
||||
return
|
||||
if(get_dist(src,user) <= 1)
|
||||
tgui_interact(user)
|
||||
|
||||
//stolen wholesale and then edited a bit from newscasters, which are awesome and by Agouri
|
||||
/obj/machinery/atm/proc/scan_user(mob/living/carbon/human/human_user as mob)
|
||||
|
||||
702
tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx
Normal file
702
tgui/packages/tgui/interfaces/AutomatedTellerMachine.tsx
Normal file
@@ -0,0 +1,702 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useBackend } from 'tgui/backend';
|
||||
import { Window } from 'tgui/layouts';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
DmIcon,
|
||||
Icon,
|
||||
Input,
|
||||
LabeledList,
|
||||
Section,
|
||||
Stack,
|
||||
Table,
|
||||
} from 'tgui-core/components';
|
||||
|
||||
export type Transaction = {
|
||||
date: string;
|
||||
time: number;
|
||||
target_name: string;
|
||||
purpose: string;
|
||||
amount: number;
|
||||
source_terminal: string;
|
||||
};
|
||||
|
||||
export type Account = {
|
||||
owner_name: string;
|
||||
money: number;
|
||||
security_level: number;
|
||||
transactions: Transaction[];
|
||||
};
|
||||
|
||||
export type AutomatedTellerMachineData = {
|
||||
machine_id: string;
|
||||
emagged: number;
|
||||
held_card: string | null;
|
||||
locked_down: number;
|
||||
suspended: boolean;
|
||||
authenticated_account: Account;
|
||||
};
|
||||
|
||||
export const AutomatedTellerMachine = (props) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
|
||||
return (
|
||||
<Window width={680} height={550}>
|
||||
<Window.Content>
|
||||
<Section title={'Automatic Teller Machine - ' + data.machine_id} fill>
|
||||
{data.authenticated_account ? (
|
||||
<AuthenticatedScreen />
|
||||
) : (
|
||||
<LoginScreen machine_id={data.machine_id} card={data.held_card} />
|
||||
)}
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
enum Menu {
|
||||
Main = 'main',
|
||||
Withdraw = 'withdraw',
|
||||
Balance = 'balance',
|
||||
Transfer = 'transfer',
|
||||
More = 'more',
|
||||
}
|
||||
|
||||
const AuthenticatedScreen = (props) => {
|
||||
const [menu, setMenu] = useState(Menu.Main);
|
||||
|
||||
switch (menu) {
|
||||
case Menu.Main: {
|
||||
return <MainMenu setMenu={setMenu} />;
|
||||
}
|
||||
case Menu.Withdraw: {
|
||||
return <WithdrawMenu setMenu={setMenu} />;
|
||||
}
|
||||
case Menu.Balance: {
|
||||
return <BalanceMenu setMenu={setMenu} />;
|
||||
}
|
||||
case Menu.Transfer: {
|
||||
return <TransferMenu setMenu={setMenu} />;
|
||||
}
|
||||
case Menu.More: {
|
||||
return <MoreMenu setMenu={setMenu} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MainMenu = (props: {
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { setMenu } = props;
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
|
||||
return (
|
||||
<Stack fill fontSize={2}>
|
||||
<Stack.Item grow>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => setMenu(Menu.Withdraw)}>
|
||||
<Icon name="money-bill-wave" width={2} mr={2} />
|
||||
Withdraw
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => setMenu(Menu.More)}>
|
||||
<Icon name="bars" width={2} mr={2} />
|
||||
More Services
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow textAlign="right">
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => setMenu(Menu.Balance)}>
|
||||
Balance
|
||||
<Icon name="dollar-sign" width={2} ml={2} />
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => setMenu(Menu.Transfer)}>
|
||||
Transfer
|
||||
<Icon name="money-check-alt" width={2} ml={2} />
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => act('logout')}>
|
||||
{data.held_card ? 'Return Card' : 'Logout'}
|
||||
<Icon name="sign-out-alt" width={2} ml={2} />
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const WithdrawMenu = (props: {
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
const { setMenu } = props;
|
||||
const [custom, setCustom] = useState(false);
|
||||
const [useCard, setUseCard] = useState(false);
|
||||
|
||||
const withdrawFunction = (val) => {
|
||||
if (useCard) {
|
||||
act('e_withdrawal', { funds_amount: val });
|
||||
} else {
|
||||
act('withdrawal', { funds_amount: val });
|
||||
}
|
||||
setMenu(Menu.Main);
|
||||
};
|
||||
|
||||
if (custom) {
|
||||
return (
|
||||
<CustomWithdrawal
|
||||
withdrawFunction={withdrawFunction}
|
||||
useCard={useCard}
|
||||
setCustom={setCustom}
|
||||
setMenu={setMenu}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Section
|
||||
title="Withdrawals"
|
||||
buttons={
|
||||
<Button icon="arrow-left" onClick={() => setMenu(Menu.Main)}>
|
||||
Back To Main Menu
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Box fontSize={2} mb={1}>
|
||||
Account Balance: ${data.authenticated_account.money}
|
||||
</Box>
|
||||
<Box fontSize={2} mb={4}>
|
||||
<Stack align="center">
|
||||
<Stack.Item grow>
|
||||
<Button
|
||||
fluid
|
||||
icon="money-bill-wave"
|
||||
selected={!useCard}
|
||||
onClick={() => setUseCard(false)}
|
||||
>
|
||||
Cash
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<Button
|
||||
fluid
|
||||
icon="credit-card"
|
||||
selected={useCard}
|
||||
onClick={() => setUseCard(true)}
|
||||
>
|
||||
Chargecard
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Stack fontSize={2}>
|
||||
<Stack.Item grow>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => withdrawFunction(10)}>
|
||||
$10
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => withdrawFunction(100)}>
|
||||
$100
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => withdrawFunction(500)}>
|
||||
$500
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item grow textAlign="right">
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => withdrawFunction(50)}>
|
||||
$50
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => withdrawFunction(200)}>
|
||||
$200
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button fluid onClick={() => setCustom(true)}>
|
||||
Other
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomWithdrawal = (props: {
|
||||
withdrawFunction: (val: number) => void;
|
||||
useCard: boolean;
|
||||
setCustom: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
const { withdrawFunction, useCard, setCustom, setMenu } = props;
|
||||
const [money, setMoney] = useState(1000);
|
||||
return (
|
||||
<Section
|
||||
title="Withdrawal"
|
||||
buttons={
|
||||
<Button icon="arrow-left" onClick={() => setCustom(false)}>
|
||||
Back To Withdrawals
|
||||
</Button>
|
||||
}
|
||||
fill
|
||||
>
|
||||
<Stack
|
||||
align="center"
|
||||
justify="center"
|
||||
fill
|
||||
fontSize={4}
|
||||
textAlign="center"
|
||||
>
|
||||
<Stack.Item>
|
||||
<Stack vertical>
|
||||
<Stack.Item fontSize={3}>
|
||||
Available: ${data.authenticated_account.money}
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Input
|
||||
fluid
|
||||
value={money}
|
||||
maxLength={10}
|
||||
onInput={(e, val) => {
|
||||
let value = parseInt(val, 10);
|
||||
if (isNaN(value)) {
|
||||
setMoney(0);
|
||||
} else {
|
||||
setMoney(value);
|
||||
}
|
||||
}}
|
||||
onChange={(e, val) => {
|
||||
let value = parseInt(val, 10);
|
||||
if (isNaN(value)) {
|
||||
setMoney(0);
|
||||
} else {
|
||||
setMoney(value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon={useCard ? 'credit-card' : 'money-bill-wave'}
|
||||
fluid
|
||||
fontSize={3}
|
||||
onClick={() => withdrawFunction(money)}
|
||||
>
|
||||
{useCard ? 'Load' : 'Withdraw'} ${money}
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const BalanceMenu = (props: {
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
const { setMenu } = props;
|
||||
return (
|
||||
<Section
|
||||
title="Balance"
|
||||
buttons={
|
||||
<Button icon="arrow-left" onClick={() => setMenu(Menu.Main)}>
|
||||
Back To Main Menu
|
||||
</Button>
|
||||
}
|
||||
fill
|
||||
>
|
||||
<Stack justify="space-between">
|
||||
<Stack.Item>
|
||||
<Box fontSize={1.5} mb={1}>
|
||||
Current Funds: ${data.authenticated_account.money}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="money-bill-alt"
|
||||
onClick={() => act('balance_statement')}
|
||||
>
|
||||
Print Balance Statement
|
||||
</Button>
|
||||
<Button icon="money-check" onClick={() => act('print_transaction')}>
|
||||
Print Transactions
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<TransactionLog transactions={data.authenticated_account.transactions} />
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const TransactionLog = (props: { transactions: Transaction[] }) => {
|
||||
const { transactions } = props;
|
||||
|
||||
return (
|
||||
<Section fill scrollable height="95%">
|
||||
<Table collapsing={false} className="AutomatedTellerMachine__Table">
|
||||
<Table.Row header>
|
||||
<Table.Cell header>Date</Table.Cell>
|
||||
<Table.Cell header>Time</Table.Cell>
|
||||
<Table.Cell header>Target</Table.Cell>
|
||||
<Table.Cell header>Purpose</Table.Cell>
|
||||
<Table.Cell header>Value</Table.Cell>
|
||||
<Table.Cell header>Source Terminal ID</Table.Cell>
|
||||
</Table.Row>
|
||||
{transactions.map((transaction, index) => (
|
||||
<Table.Row key={index}>
|
||||
<Table.Cell>{transaction.date}</Table.Cell>
|
||||
<Table.Cell>{transaction.time}</Table.Cell>
|
||||
<Table.Cell>{transaction.target_name}</Table.Cell>
|
||||
<Table.Cell>{transaction.purpose}</Table.Cell>
|
||||
<Table.Cell>${transaction.amount}</Table.Cell>
|
||||
<Table.Cell>{transaction.source_terminal}</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const MoreMenu = (props: {
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
const { setMenu } = props;
|
||||
return (
|
||||
<Section
|
||||
title="More Services"
|
||||
buttons={
|
||||
<Button icon="arrow-left" onClick={() => setMenu(Menu.Main)}>
|
||||
Back To Main Menu
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Box fontSize={2} mb={1}>
|
||||
Account Security Settings
|
||||
</Box>
|
||||
<Button
|
||||
fluid
|
||||
style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}
|
||||
selected={data.authenticated_account.security_level === 0}
|
||||
onClick={() => act('change_security_level', { new_security_level: 0 })}
|
||||
>
|
||||
Zero - Either the account number or card is required to access this
|
||||
account. EFTPOS transactions will require a card and ask for a pin, but
|
||||
not verify the pin is correct.
|
||||
</Button>
|
||||
<Button
|
||||
fluid
|
||||
style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}
|
||||
selected={data.authenticated_account.security_level === 1}
|
||||
onClick={() => act('change_security_level', { new_security_level: 1 })}
|
||||
>
|
||||
One - An account number and pin must be manually entered to access this
|
||||
account and process transactions.
|
||||
</Button>
|
||||
<Button
|
||||
fluid
|
||||
style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}
|
||||
selected={data.authenticated_account.security_level === 2}
|
||||
onClick={() => act('change_security_level', { new_security_level: 2 })}
|
||||
>
|
||||
Two - In addition to account number and pin, a card is required to
|
||||
access this account and process transactions.
|
||||
</Button>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const TransferMenu = (props: {
|
||||
setMenu: React.Dispatch<React.SetStateAction<Menu>>;
|
||||
}) => {
|
||||
const { act, data } = useBackend<AutomatedTellerMachineData>();
|
||||
const [accountNum, setAccountNum] = useState(100000);
|
||||
const updateAccountNum = (val) => {
|
||||
let newVal = parseInt(val, 10);
|
||||
if (isNaN(newVal)) {
|
||||
setAccountNum(100000);
|
||||
} else {
|
||||
setAccountNum(newVal);
|
||||
}
|
||||
};
|
||||
|
||||
const [money, setMoney] = useState(0);
|
||||
const updateMoney = (val) => {
|
||||
let newVal = parseInt(val, 10);
|
||||
if (isNaN(newVal)) {
|
||||
setMoney(0);
|
||||
} else {
|
||||
setMoney(newVal);
|
||||
}
|
||||
};
|
||||
|
||||
const [purpose, setPurpose] = useState('Funds transfer');
|
||||
|
||||
const { setMenu } = props;
|
||||
return (
|
||||
<Section
|
||||
title="Transfer Money"
|
||||
buttons={
|
||||
<Button icon="arrow-left" onClick={() => setMenu(Menu.Main)}>
|
||||
Back To Main Menu
|
||||
</Button>
|
||||
}
|
||||
fill
|
||||
textAlign="center"
|
||||
>
|
||||
<Box fontSize={2}>
|
||||
<Box fontSize={3} mb={2}>
|
||||
Available: ${data.authenticated_account.money}
|
||||
</Box>
|
||||
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Target Account Number">
|
||||
<Input
|
||||
fluid
|
||||
maxLength={6}
|
||||
value={accountNum}
|
||||
onChange={(e, val) => updateAccountNum(val)}
|
||||
onInput={(e, val) => updateAccountNum(val)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Funds To Transfer">
|
||||
<Input
|
||||
fluid
|
||||
maxLength={10}
|
||||
value={money}
|
||||
onChange={(e, val) => updateMoney(val)}
|
||||
onInput={(e, val) => updateMoney(val)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Transaction Purpose">
|
||||
<Input
|
||||
fluid
|
||||
maxLength={20}
|
||||
value={purpose}
|
||||
onChange={(e, val) => setPurpose(val)}
|
||||
onInput={(e, val) => setPurpose(val)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Button
|
||||
icon="exchange-alt"
|
||||
fluid
|
||||
mt={1}
|
||||
onClick={() =>
|
||||
act('transfer', {
|
||||
funds_amount: money,
|
||||
target_acc_number: accountNum,
|
||||
})
|
||||
}
|
||||
>
|
||||
Transfer Funds
|
||||
</Button>
|
||||
</Box>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const LoginScreen = (props: { machine_id: string; card: string | null }) => {
|
||||
const { act } = useBackend();
|
||||
const { machine_id, card } = props;
|
||||
|
||||
const [account, setAccount] = useState('');
|
||||
const [pin, setPin] = useState('');
|
||||
|
||||
return (
|
||||
<Stack align="flex-start" justify="space-between" fill>
|
||||
<Stack.Item grow height="100%">
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
fontSize={2}
|
||||
fluid
|
||||
height={8}
|
||||
verticalAlignContent="middle"
|
||||
onClick={() => act('insert_card')}
|
||||
>
|
||||
<Stack align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<Icon name="id-card" />
|
||||
</Stack.Item>
|
||||
<Stack.Item
|
||||
grow
|
||||
style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}
|
||||
textAlign="center"
|
||||
>
|
||||
{card ? card : 'Insert ID'}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
<Stack.Item fontSize={1.5} mt={10}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Account">
|
||||
<Input
|
||||
fluid
|
||||
value={account}
|
||||
onChange={(e, val) => setAccount(val)}
|
||||
onInput={(e, val) => setAccount(val)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="PIN">
|
||||
<Input
|
||||
fluid
|
||||
value={pin}
|
||||
onChange={(e, val) => setPin(val)}
|
||||
onInput={(e, val) => setPin(val)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Button
|
||||
fontSize={4}
|
||||
fluid
|
||||
mt={2}
|
||||
textAlign="center"
|
||||
onClick={() =>
|
||||
act('attempt_auth', {
|
||||
account_num: account,
|
||||
account_pin: pin,
|
||||
})
|
||||
}
|
||||
>
|
||||
<Stack>
|
||||
<Stack.Item>
|
||||
<Icon name="sign-in-alt" />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow textAlign="center">
|
||||
Log In
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item
|
||||
ml={-5}
|
||||
mr={-5}
|
||||
onClick={() => act('insert_card')}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<Stack align="center" justify="center" fill vertical>
|
||||
<Stack.Item>
|
||||
<Box height={10}>
|
||||
<CardSlot />
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<AnimatedIDCard />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const CardSlot = (props) => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 100 40">
|
||||
{/* Top */}
|
||||
<rect
|
||||
rx="5"
|
||||
x="0"
|
||||
y="0"
|
||||
width="100"
|
||||
height="20"
|
||||
strokeWidth={0}
|
||||
fill="grey"
|
||||
/>
|
||||
{/* Faceplate */}
|
||||
<rect x="0" y="10" width="100" height="30" fill="#aaa" />
|
||||
{/* Slot */}
|
||||
<rect
|
||||
rx="5"
|
||||
ry="5"
|
||||
x="10"
|
||||
y="25"
|
||||
width="80"
|
||||
height="10"
|
||||
fill="#000"
|
||||
stroke="#444"
|
||||
/>
|
||||
{/* Arrows */}
|
||||
<polyline transform="translate(30, 15)" points="0 5, 5 0, 10 5" />
|
||||
<polyline transform="translate(60, 15)" points="0 5, 5 0, 10 5" />
|
||||
{/* Card */}
|
||||
<g transform="translate(45, 11)">
|
||||
{/* Slot */}
|
||||
<rect rx="2" x="0" y="0" width="10" height="2.5" />
|
||||
{/* Card Outline */}
|
||||
<rect
|
||||
rx="1"
|
||||
x="2"
|
||||
y="2"
|
||||
width="6"
|
||||
height="8"
|
||||
fill="transparent"
|
||||
stroke="black"
|
||||
/>
|
||||
{/* Chip */}
|
||||
<rect rx="0.5" x="4" y="3" width="2" height="3" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const AnimatedIDCard = (props) => {
|
||||
const [glitch, setGlitch] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (Math.random() * 100 < 1) {
|
||||
// 1% chance
|
||||
setGlitch(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={
|
||||
glitch
|
||||
? 'AutomatedTellerMachine__Card--glitch'
|
||||
: 'AutomatedTellerMachine__Card'
|
||||
}
|
||||
mt={-8}
|
||||
>
|
||||
<DmIcon
|
||||
icon="icons/obj/card_new.dmi"
|
||||
icon_state="civilian-id"
|
||||
width={32}
|
||||
height={32}
|
||||
style={{
|
||||
transform: 'rotate(90deg)',
|
||||
transformOrigin: 'center',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
.AutomatedTellerMachine__Card {
|
||||
animation: cardAnim 0.75s linear 0s infinite normal both;
|
||||
}
|
||||
|
||||
@keyframes cardAnim {
|
||||
0% {
|
||||
transform: translate(0, 40px);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translate(0, -32px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.AutomatedTellerMachine__Card--glitch {
|
||||
animation: cardAnimGlitch 0.2s linear 0s infinite normal both;
|
||||
transform-origin: 'center';
|
||||
}
|
||||
|
||||
@keyframes cardAnimGlitch {
|
||||
0% {
|
||||
transform: translate(0, 40px) rotate(-45deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translate(0, -32px) rotate(45deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(0, 40px) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
.AutomatedTellerMachine__Table,
|
||||
.AutomatedTellerMachine__Table td {
|
||||
border: 4px solid #aaa;
|
||||
border-style: ridge;
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
@include meta.load-css('./components/Tooltip.scss');
|
||||
|
||||
// Interfaces
|
||||
@include meta.load-css('./interfaces/AutomatedTellerMachine.scss');
|
||||
@include meta.load-css('./interfaces/ListInput.scss');
|
||||
@include meta.load-css('./interfaces/InputModal.scss');
|
||||
@include meta.load-css('./interfaces/IntegratedCircuit.scss');
|
||||
|
||||
Reference in New Issue
Block a user